Browse Source

Allow multi-function function definitions (not completely tested; backwards-compatible)

Yentl Van Tendeloo 8 years ago
parent
commit
1242a84626
39 changed files with 159 additions and 140 deletions
  1. 71 61
      bootstrap/constructors.alc
  2. 0 2
      interface/HUTN/includes/constructors.alh
  3. 0 0
      interface/HUTN/test/grammar_action_language/code/accumulate.alc
  4. 0 0
      interface/HUTN/test/grammar_action_language/code/action.alc
  5. 0 0
      interface/HUTN/test/grammar_action_language/code/assign.alc
  6. 0 0
      interface/HUTN/test/grammar_action_language/code/assign_to_constant.alc
  7. 0 0
      interface/HUTN/test/grammar_action_language/code/assign_to_func_result.alc
  8. 0 0
      interface/HUTN/test/grammar_action_language/code/assign_to_statement.alc
  9. 0 0
      interface/HUTN/test/grammar_action_language/code/dictionary_selection.alc
  10. 0 0
      interface/HUTN/test/grammar_action_language/code/empty.alc
  11. 0 0
      interface/HUTN/test/grammar_action_language/code/fibonacci.alc
  12. 0 0
      interface/HUTN/test/grammar_action_language/code/full_empty.alc
  13. 0 0
      interface/HUTN/test/grammar_action_language/code/funcdef_empty_body.alc
  14. 0 0
      interface/HUTN/test/grammar_action_language/code/global.alc
  15. 0 0
      interface/HUTN/test/grammar_action_language/code/if.alc
  16. 0 0
      interface/HUTN/test/grammar_action_language/code/if_empty_body.alc
  17. 0 0
      interface/HUTN/test/grammar_action_language/code/if_empty_else.alc
  18. 0 0
      interface/HUTN/test/grammar_action_language/code/if_no_condition.alc
  19. 0 0
      interface/HUTN/test/grammar_action_language/code/if_only_else.alc
  20. 0 4
      interface/HUTN/test/grammar_action_language/code/include.al
  21. 4 0
      interface/HUTN/test/grammar_action_language/code/include.alc
  22. 0 0
      interface/HUTN/test/grammar_action_language/code/include_late.alc
  23. 9 0
      interface/HUTN/test/grammar_action_language/code/nested.alc
  24. 0 0
      interface/HUTN/test/grammar_action_language/code/primitives.alc
  25. 0 0
      interface/HUTN/test/grammar_action_language/code/return_in_assign.alc
  26. 0 0
      interface/HUTN/test/grammar_action_language/code/types.alc
  27. 0 0
      interface/HUTN/test/grammar_action_language/code/vardecl.alc
  28. 0 0
      interface/HUTN/test/grammar_action_language/code/while.alc
  29. 0 0
      interface/HUTN/test/grammar_action_language/code/while_empty_body.alc
  30. 0 0
      interface/HUTN/test/grammar_action_language/code/while_no_condition.alc
  31. 2 0
      interface/HUTN/test/grammar_action_language/test_invalids.py
  32. 13 16
      interface/HUTN/test/grammar_action_language/test_valids.py
  33. 6 4
      kernel/modelverse_jit/bytecode_interpreter.py
  34. 6 0
      kernel/modelverse_jit/bytecode_to_cfg.py
  35. 15 12
      kernel/modelverse_jit/bytecode_to_tree.py
  36. 5 3
      kernel/modelverse_kernel/legacy.py
  37. 5 3
      kernel/modelverse_kernel/main.py
  38. 22 34
      kernel/modelverse_kernel/request_handler.py
  39. 1 1
      models/architecture.mvc

+ 71 - 61
bootstrap/constructors.alc

@@ -7,21 +7,68 @@ include "modelling.alh"
 Element while_stack = ?
 Element variable_map = ?
 
-Element function construct_top():
+Element function construct_function():
 	String command
-	while (True):
+	Element result
+	Element main_function
+	Boolean continue
+	Element prev_element
+	Element first_element
+
+	// Initialize variables
+	prev_element = read_root()
+	main_function = read_root()
+
+	// Clear global variables
+	while_stack = create_node()
+	variable_map = create_node()
+
+	continue = True
+	while (continue):
 		command = input()
 		if (command == "global"):
-			return construct_global()!
+			result = construct_global()
 		elif (command == "funcdef"):
-			return construct_top_funcdef(False)!
+			result = construct_funcdef(False)
 		elif (command == "mutable_funcdef"):
-			return construct_top_funcdef(True)!
+			result = construct_funcdef(True)
 		else:
 			log("ERROR (1): did not understand command " + cast_e2s(command))
 			output("ERROR: compiled code not understood: " + cast_e2s(command))
 			return read_root()!
 
+		continue = input()
+		if (prev_element != read_root()):
+			dict_add_fast(prev_element, "next", result["start"])
+		else:
+			first_element = result["start"]
+
+		if (bool_and(element_eq(main_function, read_root()), command != "global")):
+			// In case no main function is defined, it is the first defined function
+			// This is mostly there to ensure backwards compatibility
+			main_function = result["instruction"]
+
+		prev_element = result["end"]
+
+		if (value_eq(result["name"], "main")):
+			// It was the function that we want to call
+			main_function = result["instruction"]
+
+	if (element_eq(main_function, read_root())):
+		log("ERROR (2): no main function found")
+		output("ERROR: no main function found")
+		return read_root()!
+
+	// Overwrite the main function with our declaration function
+	prev_element = main_function["body"]
+	log("Got main function V: " + cast_e2s(main_function))
+	log("Got main function S: " + dict_to_string(main_function))
+	dict_delete(main_function, "body")
+	dict_add_fast(main_function, "body", first_element)
+	dict_add_fast(result["end"], "next", prev_element)
+	
+	return main_function!
+
 Action function construct_global():
 	Action this_element
 	String declared_element
@@ -58,21 +105,25 @@ Action function construct_global():
 		dict_add_fast(value, "node", input())
 	dict_add_fast(assign, "value", value)
 
-	if (input()):
-		dict_add_fast(assign, "next", construct_top())
-	return this_element!
+	Element result
+	result = create_node()
+	dict_add_fast(result, "name", "")
+	dict_add_fast(result, "instruction", this_element)
+	dict_add_fast(result, "start", this_element)
+	dict_add_fast(result, "end", assign)
+	return result!
 
-Action function construct_top_funcdef(mutable : Boolean):
+Action function construct_funcdef(mutable : Boolean):
 	Action assign
 	Action resolve
 	Action constant
 	Element formal
 	Element func
 	Element params
-	Action global
+	Action declare
 	String name
 
-	global = create_value(!global)
+	declare = create_value(!global)
 	assign = create_value(!assign)
 	resolve = create_value(!resolve)
 	constant = create_value(!constant)
@@ -83,8 +134,8 @@ Action function construct_top_funcdef(mutable : Boolean):
 		formal = name
 	func = create_node()
 	params = create_node()
-	dict_add_fast(global, "var", formal)
-	dict_add_fast(global, "next", assign)
+	dict_add_fast(declare, "var", formal)
+	dict_add_fast(declare, "next", assign)
 	dict_add_fast(assign, "var", resolve)
 	dict_add_fast(assign, "value", constant)
 	dict_add_fast(resolve, "var", formal)
@@ -114,10 +165,13 @@ Action function construct_top_funcdef(mutable : Boolean):
 	// Now add the body
 	dict_add_fast(func, "body", construct_unknown())
 
-	if (input()):
-		dict_add_fast(assign, "next", construct_top())
-
-	return global!
+	Element result
+	result = create_node()
+	dict_add_fast(result, "name", name)
+	dict_add_fast(result, "instruction", func)
+	dict_add_fast(result, "start", declare)
+	dict_add_fast(result, "end", assign)
+	return result!
 
 Element function construct_unknown():
 	String elem
@@ -309,47 +363,3 @@ Action function construct_continue():
 	this_element = create_value(!continue)
 	dict_add_fast(this_element, "while", while_stack[list_len(while_stack) - 1])
 	return this_element!
-
-Action function construct_function():
-	Action func
-	Integer nrParams
-	Integer counter
-	Element param
-	Element params
-	String arg_names_decl
-	String inp
-
-	variable_map = create_node()
-
-	inp = input()
-	while (bool_and(inp != "funcdef", inp != "mutable_funcdef")):
-		// We skip over everything that is not a funcdef, as these are all just definitions of global stuff
-		inp = input()
-
-	// Consume the name
-	input()
-
-	params = create_node()
-	nrParams = input()
-	counter = 0
-	func = create_node()
-	arg_names_decl = "abcdefghijklmnopqrstuvwxyz"
-	dict_add_fast(func, "params", params)
-
-	if (inp == "mutable_funcdef"):
-		dict_add_fast(func, "mutable", create_node())
-
-	while (counter < nrParams):
-		param = create_node()
-		dict_add_fast(params, string_get(arg_names_decl, counter), param)
-		dict_add_fast(variable_map, input(), param)
-		// Output each parameter in turn
-		counter = counter + 1
-
-	// Now add the body
-	dict_add_fast(func, "body", construct_unknown())
-
-	// Consume the final 'false', to indicate that no additional code will come
-	input()
-
-	return func!

+ 0 - 2
interface/HUTN/includes/constructors.alh

@@ -1,3 +1 @@
-Action function construct_top()
-Action function construct_unknown()
 Action function construct_function()

interface/HUTN/test/grammar_action_language/code/accumulate.al → interface/HUTN/test/grammar_action_language/code/accumulate.alc


interface/HUTN/test/grammar_action_language/code/action.al → interface/HUTN/test/grammar_action_language/code/action.alc


interface/HUTN/test/grammar_action_language/code/assign.al → interface/HUTN/test/grammar_action_language/code/assign.alc


interface/HUTN/test/grammar_action_language/code/assign_to_constant.al → interface/HUTN/test/grammar_action_language/code/assign_to_constant.alc


interface/HUTN/test/grammar_action_language/code/assign_to_func_result.al → interface/HUTN/test/grammar_action_language/code/assign_to_func_result.alc


interface/HUTN/test/grammar_action_language/code/assign_to_statement.al → interface/HUTN/test/grammar_action_language/code/assign_to_statement.alc


interface/HUTN/test/grammar_action_language/code/dictionary_selection.al → interface/HUTN/test/grammar_action_language/code/dictionary_selection.alc


interface/HUTN/test/grammar_action_language/code/empty.al → interface/HUTN/test/grammar_action_language/code/empty.alc


interface/HUTN/test/grammar_action_language/code/fibonacci.al → interface/HUTN/test/grammar_action_language/code/fibonacci.alc


interface/HUTN/test/grammar_action_language/code/full_empty.al → interface/HUTN/test/grammar_action_language/code/full_empty.alc


interface/HUTN/test/grammar_action_language/code/funcdef_empty_body.al → interface/HUTN/test/grammar_action_language/code/funcdef_empty_body.alc


interface/HUTN/test/grammar_action_language/code/global.al → interface/HUTN/test/grammar_action_language/code/global.alc


interface/HUTN/test/grammar_action_language/code/if.al → interface/HUTN/test/grammar_action_language/code/if.alc


interface/HUTN/test/grammar_action_language/code/if_empty_body.al → interface/HUTN/test/grammar_action_language/code/if_empty_body.alc


interface/HUTN/test/grammar_action_language/code/if_empty_else.al → interface/HUTN/test/grammar_action_language/code/if_empty_else.alc


interface/HUTN/test/grammar_action_language/code/if_no_condition.al → interface/HUTN/test/grammar_action_language/code/if_no_condition.alc


interface/HUTN/test/grammar_action_language/code/if_only_else.al → interface/HUTN/test/grammar_action_language/code/if_only_else.alc


+ 0 - 4
interface/HUTN/test/grammar_action_language/code/include.al

@@ -1,4 +0,0 @@
-include "test/grammar_action_language/code/primitives.al"
-
-Void function main():
-	integer_addition(1, 2)

+ 4 - 0
interface/HUTN/test/grammar_action_language/code/include.alc

@@ -0,0 +1,4 @@
+include "test/grammar_action_language/code/primitives.alc"
+
+Void function main():
+	integer_addition(1, 2)

interface/HUTN/test/grammar_action_language/code/include_late.al → interface/HUTN/test/grammar_action_language/code/include_late.alc


+ 9 - 0
interface/HUTN/test/grammar_action_language/code/nested.alc

@@ -0,0 +1,9 @@
+Void function run_1():
+	Integer function fibonacci(n : Integer):
+		if (n > 2):
+			return (fibonacci(n-1) + fibonacci(n-2))!
+		else:
+			return 1!
+
+	log(fibonacci(5))
+	return!

interface/HUTN/test/grammar_action_language/code/primitives.al → interface/HUTN/test/grammar_action_language/code/primitives.alc


interface/HUTN/test/grammar_action_language/code/return_in_assign.al → interface/HUTN/test/grammar_action_language/code/return_in_assign.alc


interface/HUTN/test/grammar_action_language/code/types.al → interface/HUTN/test/grammar_action_language/code/types.alc


interface/HUTN/test/grammar_action_language/code/vardecl.al → interface/HUTN/test/grammar_action_language/code/vardecl.alc


interface/HUTN/test/grammar_action_language/code/while.al → interface/HUTN/test/grammar_action_language/code/while.alc


interface/HUTN/test/grammar_action_language/code/while_empty_body.al → interface/HUTN/test/grammar_action_language/code/while_empty_body.alc


interface/HUTN/test/grammar_action_language/code/while_no_condition.al → interface/HUTN/test/grammar_action_language/code/while_no_condition.alc


+ 2 - 0
interface/HUTN/test/grammar_action_language/test_invalids.py

@@ -50,3 +50,5 @@ class TestValids(unittest.TestCase):
     def test_include_late(self):
         self.assertTrue(parse_file("include_late.al"))
 
+    def test_nested(self):
+        self.assertTrue(parse_file("nested.alc"))

+ 13 - 16
interface/HUTN/test/grammar_action_language/test_valids.py

@@ -4,42 +4,39 @@ import util
 from hutn_compiler.compiler import main
 
 def parse_file(filename):
-    try:
-        main(util.get_code_path(filename), "grammars/actionlanguage.g", "N", [])
-        return True
-    except:
-        return False
+    main(util.get_code_path(filename), "grammars/actionlanguage.g", "N", [])
+    return True
 
 class TestValids(unittest.TestCase):
     def test_empty(self):
-        self.assertTrue(parse_file("empty.al"))
+        self.assertTrue(parse_file("empty.alc"))
 
     def test_assign(self):
-        self.assertTrue(parse_file("assign.al"))
+        self.assertTrue(parse_file("assign.alc"))
 
     def test_variable_declare(self):
-        self.assertTrue(parse_file("vardecl.al"))
+        self.assertTrue(parse_file("vardecl.alc"))
 
     def test_accumulate(self):
-        self.assertTrue(parse_file("accumulate.al"))
+        self.assertTrue(parse_file("accumulate.alc"))
 
     def test_fibonacci(self):
-        self.assertTrue(parse_file("fibonacci.al"))
+        self.assertTrue(parse_file("fibonacci.alc"))
 
     def test_while(self):
-        self.assertTrue(parse_file("while.al"))
+        self.assertTrue(parse_file("while.alc"))
 
     def test_if(self):
-        self.assertTrue(parse_file("if.al"))
+        self.assertTrue(parse_file("if.alc"))
 
     def test_action(self):
-        self.assertTrue(parse_file("action.al"))
+        self.assertTrue(parse_file("action.alc"))
 
     def test_type(self):
-        self.assertTrue(parse_file("types.al"))
+        self.assertTrue(parse_file("types.alc"))
 
     def test_global(self):
-        self.assertTrue(parse_file("global.al"))
+        self.assertTrue(parse_file("global.alc"))
 
     def test_include(self):
-        self.assertTrue(parse_file("include.al"))
+        self.assertTrue(parse_file("include.alc"))

+ 6 - 4
kernel/modelverse_jit/bytecode_interpreter.py

@@ -208,11 +208,13 @@ class InterpreterState(object):
         var_name = instruction.variable.name
         task_root = self.get_task_root()
         _globals, = yield [("RD", [task_root, "globals"])]
-        global_var, = yield [("RD", [_globals, var_name])]
+        global_var, = yield [("RDE", [_globals, var_name])]
 
-        if global_var is None:
-            global_var, = yield [("CN", [])]
-            yield [("CD", [_globals, var_name, global_var])]
+        if global_var is not None:
+            yield [("DE", [global_var])]
+
+        global_var, = yield [("CN", [])]
+        yield [("CD", [_globals, var_name, global_var])]
 
         self.update_result(global_var)
         yield [("CE", [self.gc_root_node, global_var])]

+ 6 - 0
kernel/modelverse_jit/bytecode_to_cfg.py

@@ -283,6 +283,11 @@ class AnalysisState(object):
 
     def analyze_global(self, instruction):
         """Analyzes a 'global' instruction."""
+
+        return self.current_block.append_definition(
+            cfg_ir.DeclareGlobal(instruction.variable))
+
+        """
         resolved_global = self.current_block.append_definition(
             cfg_ir.ResolveGlobal(instruction.variable))
         nothing = self.current_block.append_definition(cfg_ir.Literal(None))
@@ -294,6 +299,7 @@ class AnalysisState(object):
             self.current_block.append_definition(
                 cfg_ir.DeclareGlobal(instruction.variable)),
             lambda: resolved_global)
+        """
 
     def analyze_assign(self, instruction):
         """Analyzes an 'assign' instruction."""

+ 15 - 12
kernel/modelverse_jit/bytecode_to_tree.py

@@ -502,9 +502,10 @@ class AnalysisState(object):
         #     _globals, = yield [("RD", [task_root, "globals"])]
         #     global_var, = yield [("RD", [_globals, var_name])]
         #
-        #     if global_var is None:
-        #         global_var, = yield [("CN", [])]
-        #         yield [("CD", [_globals, var_name, global_var])]
+        #     if global_var is not None:
+        #         yield [("DE", [global_var])]
+        #     global_var, = yield [("CN", [])]
+        #     yield [("CD", [_globals, var_name, global_var])]
         #
         #     tmp = global_var
 
@@ -517,7 +518,7 @@ class AnalysisState(object):
 
         global_var = tree_ir.StoreLocalInstruction(
             'global_var',
-            tree_ir.ReadDictionaryValueInstruction(
+            tree_ir.ReadDictionaryEdgeInstruction(
                 _globals.create_load(),
                 tree_ir.LiteralInstruction(instruction.variable.name)))
 
@@ -530,17 +531,19 @@ class AnalysisState(object):
                     tree_ir.SelectInstruction(
                         tree_ir.BinaryInstruction(
                             global_var.create_load(),
-                            'is',
+                            'is not',
                             tree_ir.LiteralInstruction(None)),
                         tree_ir.create_block(
-                            global_var.create_store(
-                                tree_ir.CreateNodeInstruction()),
-                            tree_ir.CreateDictionaryEdgeInstruction(
-                                _globals.create_load(),
-                                tree_ir.LiteralInstruction(
-                                    instruction.variable.name),
+                            tree_ir.DeleteEdgeInstruction(
                                 global_var.create_load())),
-                        tree_ir.EmptyInstruction())),
+                        tree_ir.EmptyInstruction()),
+                    global_var.create_store(
+                        tree_ir.CreateNodeInstruction()),
+                    tree_ir.CreateDictionaryEdgeInstruction(
+                        _globals.create_load(),
+                        tree_ir.LiteralInstruction(
+                            instruction.variable.name),
+                        global_var.create_load())),
                 global_var.create_load()))
 
     def analyze_assign(self, instruction):

+ 5 - 3
kernel/modelverse_kernel/legacy.py

@@ -1049,10 +1049,12 @@ class ModelverseKernel(object):
                               ]
 
         value, =        yield [("RV", [new_var])]
-        exists, =       yield [("RD", [global_symbols, value])]
+        exists, =       yield [("RDE", [global_symbols, value])]
 
-        if exists is None:
-            yield [("CD", [global_symbols, value, empty_node])]
+        if exists is not None:
+            yield [("DE", [exists])]
+        
+        yield [("CD", [global_symbols, value, empty_node])]
 
         _, _ =          yield [("CD", [task_frame, "phase", new_phase]),
                                ("DE", [phase_link])

+ 5 - 3
kernel/modelverse_kernel/main.py

@@ -1171,10 +1171,12 @@ class ModelverseKernel(object):
                               ]
 
         value, =        yield [("RV", [new_var])]
-        exists, =       yield [("RD", [global_symbols, value])]
+        exists, =       yield [("RDE", [global_symbols, value])]
 
-        if exists is None:
-            yield [("CD", [global_symbols, value, empty_node])]
+        if exists is not None:
+            yield [("DE", [exists])]
+        
+        yield [("CD", [global_symbols, value, empty_node])]
 
         _, _ =          yield [("CD", [task_frame, "phase", new_phase]),
                                ("DE", [phase_link])

+ 22 - 34
kernel/modelverse_kernel/request_handler.py

@@ -54,7 +54,7 @@ class RequestHandler(object):
 
     def handle_request(self, reply):
         """Replies to a request from the top-of-stack generator, and returns a new request."""
-        if not self.is_active():
+        if not self.generator_stack:
             raise ValueError('handle_request cannot be called with an empty generator stack.')
 
         # Append the server's replies to the list of replies.
@@ -68,20 +68,19 @@ class RequestHandler(object):
             # Silence pylint's warning about catching Exception.
             # pylint: disable=I0011,W0703
             try:
-                while self.generator_stack[-1]["finished_requests"]:
+                if self.generator_stack[-1]["finished_requests"]:
                     gen = self.generator_stack[-1]
                     gen["pending_requests"] = gen["generator"].send(gen["replies"])
                     gen["finished_requests"] = False
                     gen["replies"] = None
-                else:
-                    return self.pop_requests()
+                return self.pop_requests()
 
             except KnownRequestHandled:
                 pass
             except StopIteration:
                 # Done, so remove the generator
                 self.pop_generator()
-                if self.is_active():
+                if self.generator_stack:
                     # This generator was called from another generator.
                     # Append 'None' to the caller's list of replies.
                     self.append_reply(None)
@@ -91,7 +90,7 @@ class RequestHandler(object):
             except primitive_functions.PrimitiveFinished as ex:
                 # Done, so remove the generator
                 self.pop_generator()
-                if self.is_active():
+                if self.generator_stack:
                     # This generator was called from another generator.
                     # Append the callee's result to the caller's list of replies.
                     self.append_reply(ex.result)
@@ -111,10 +110,6 @@ class RequestHandler(object):
         """Sets the finished_requests flag in the top-of-stack tuple."""
         self.generator_stack[-1]["finished_requests"] = True
 
-    def has_pending_requests(self):
-        """Tests if the top-of-stack generator has pending requests."""
-        return not self.generator_stack[-1]["finished_requests"]
-
     def push_generator(self, gen):
         """Pushes a new generator onto the stack."""
         dd = defaultdict(lambda : None)
@@ -130,9 +125,8 @@ class RequestHandler(object):
         # print('Popped generator %s. Generator count: %d' % (gen, len(self.generator_stack)))
         # Pop any exception handlers defined by the generator.
         top_of_stack_index = len(self.generator_stack)
-        while len(self.exception_handlers) > 0:
-            stack_index, _ = self.exception_handlers[-1]
-            if stack_index == top_of_stack_index:
+        while self.exception_handlers:
+            if self.exception_handlers[-1][0] == top_of_stack_index:
                 # Pop exception handlers until exception_handlers is empty or until
                 # we find an exception handler that is not associated with the popped
                 # generator.
@@ -152,7 +146,7 @@ class RequestHandler(object):
         """Handles the given exception. A Boolean is returned that tells if
            the exception was handled."""
         # print('Exception thrown from %s: %s' % (str(self.generator_stack[-1]), str(exception)))
-        while len(self.exception_handlers) > 0:
+        while self.exception_handlers:
             # Pop the top-of-stack exception handler.
             stack_index, handlers = self.exception_handlers.pop()
 
@@ -193,7 +187,7 @@ class RequestHandler(object):
 
         # Then pop every generator from the stack and make it crash.
         stack_trace = []
-        while len(self.generator_stack) > 0:
+        while self.generator_stack:
             top_entry = self.generator_stack.pop()
             if top_entry["function_origin"] is None:
                 # Skip this function.
@@ -224,32 +218,26 @@ class RequestHandler(object):
            A list of requests and a Boolean are returned. The latter is True
            if there are no more requests to process, and false otherwise."""
         requests = self.generator_stack[-1]["pending_requests"]
-        if requests is None or len(requests) == 0:
-            # Couldn't find a request for the state to handle.
-            self.set_finished_requests_flag()
-            return requests
-
-        for i, elem in enumerate(requests):
-            if elem[0] in self.handlers:
-                # The kernel should handle known requests.
-                if i > 0:
-                    # Handle any requests that precede the known request first.
-                    pre_requests = requests[:i]
-                    del requests[:i]
-                    return pre_requests
-
-                # The known request must be the first element in the list. Pop it.
-                requests.pop(0)
+        if requests:
+            if requests[0][0] in self.handlers:
+                # First element is a known request
+                elem = requests.pop(0)
 
                 # The list of requests might be empty now. If so, then flag this
                 # batch of requests as finished.
-                if len(requests) == 0:
+                if not requests:
                     self.set_finished_requests_flag()
 
                 # Handle the request.
-                _, request_args = elem
-                self.handlers[elem[0]](request_args)
+                self.handlers[elem[0]](elem[1])
                 raise KnownRequestHandled()
+            else:
+                for i, elem in enumerate(requests):
+                    if elem[0] in self.handlers:
+                        # Handle any requests that precede the known request first.
+                        pre_requests = requests[:i]
+                        del requests[:i]
+                        return pre_requests
 
         # We couldn't find a known request in the batch of requests, so we might as well
         # handle them all at once then.

+ 1 - 1
models/architecture.mvc

@@ -3,7 +3,7 @@ include "primitives.alh"
 SimpleClassDiagram Architecture{
     SimpleAttribute String{
         constraint = $
-            String function constraint(model : Element, name : String):
+            String function main(model : Element, name : String):
                 if (is_physical_string(model["model"][name])):
                     return "OK"!
                 else: