瀏覽代碼

Completely rewrite the internal data structure of the HUTN compiler

Yentl Van Tendeloo 7 年之前
父節點
當前提交
d73ace55d0

+ 1 - 0
interface/HUTN/hutn_compiler/bootstrap_visitor.py

@@ -1,4 +1,5 @@
 import string
+from hutn_compiler.hutnparser import Tree
 from hutn_compiler.primitives_visitor import PrimitivesVisitor, Action
 
 class BootstrapVisitor(PrimitivesVisitor):

+ 14 - 15
interface/HUTN/hutn_compiler/compiler.py

@@ -1,4 +1,3 @@
-
 try:
     import cPickle as pickle
 except ImportError:
@@ -127,41 +126,41 @@ def do_compile(inputfile, grammarfile, visitors=[], include_paths = [], mode="")
         msg = "%s:%s:%s: %s\nContext:\n%s" % (inputfile, result["line"], result["column"], result["text"], lines)
         raise Exception(msg)
     else:
-        for child in result["tree"].tail:
-            child.inputfile = inputfile
+        for child in result["tree"]['tail']:
+            child['inputfile'] = inputfile
         included = set()
         while True:
-            for i, v in enumerate(result["tree"].tail):
-                if v.head == "include":
+            for i, v in enumerate(result["tree"]['tail']):
+                if v['head'] == "include":
                     # Expand this node
-                    for j in v.tail:
-                        if j.head == "STRVALUE":
-                            f = str(j.tail[0])[1:-1]
+                    for j in v['tail']:
+                        if j['head'] == "STRVALUE":
+                            f = str(j['tail'][0])[1:-1]
                             if f in included:
                                 subtree = []
                             else:
-                                name = str(j.tail[0])[1:-1]
-                                subtree = do_parse(find_file(name, include_paths), grammarfile)["tree"].tail
+                                name = str(j['tail'][0])[1:-1]
+                                subtree = do_parse(find_file(name, include_paths), grammarfile)["tree"]['tail']
                                 if subtree is None:
                                     raise Exception("Parsing error for included file %s" % find_file(name, include_paths))
 
                                 for t in subtree:
-                                    t.inputfile = name
+                                    t['inputfile'] = name
                                 included.add(f)
                             # Found the string value, so break from the inner for ("searching for element")
                             break
 
                     # Merge all nodes in
-                    before = result["tree"].tail[:i]
-                    after = result["tree"].tail[i+1:]
-                    result["tree"].tail = before + subtree + after
+                    before = result["tree"]['tail'][:i]
+                    after = result["tree"]['tail'][i+1:]
+                    result["tree"]['tail'] = before + subtree + after
                     # Found an include node, but to prevent corruption of the tree, we need to start over again, so break from the outer for loop
                     break
             else:
                 # The outer for finally finished, so there were no includes remaining, thus terminate the infinite while loop
                 break
 
-    result["tree"].fix_tracability(inputfile)
+    Tree.fix_tracability(result['tree'], inputfile)
 
     for visitor in visitors:
         visitor.visit(result["tree"])

+ 41 - 50
interface/HUTN/hutn_compiler/constructors_visitor.py

@@ -1,10 +1,5 @@
-
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle as pickle
-
 from hutn_compiler.visitor import Visitor
+from hutn_compiler.hutnparser import Tree
 
 class ConstructorsVisitor(Visitor):
     def __init__(self, args):
@@ -21,30 +16,28 @@ class ConstructorsVisitor(Visitor):
     # a visit_* method for each non-terminal in the grammar
     def visit_start(self, tree):
         # declare all functions
-        for child in tree.get_children("funcdecl"):
+        for child in Tree.get_children(tree, "funcdecl"):
             if self.pre_visit_funcdecl(child):
                 self.add_constructors(True)
 
-        tail = tree.get_tail_without(["newline"])
-        # tail = tree.get_children("funcdecl") +\
-        #     tree.get_tail_without(["newline", "funcdecl"])
+        tail = Tree.get_tail_without(tree, ["newline"])
         last = tail[-1]
         for child in tail[:-1]:
-            if child.head in ["return", "break", "continue"]:
+            if child['head'] in ["return", "break", "continue"]:
                 last = child
                 break
             else:
                 # funcdecl may add no constructors
                 new_constructors_were_added = self.visit(child)
-                if child.head == "func_call":  # pop 'false'
+                if child['head'] == "func_call":  # pop 'false'
                     self.constructors.pop()
                 if new_constructors_were_added:
                     self.add_constructors(True)
         new_constructors_were_added = self.visit(last)
-        if last.head == "func_call":  # pop 'false'
+        if last['head'] == "func_call":  # pop 'false'
             self.constructors.pop()
         if new_constructors_were_added:
-            if last.head not in ["return", "break", "continue"]:
+            if last['head'] not in ["return", "break", "continue"]:
                 self.add_constructors(False)
         elif self.constructors:
             self.constructors.pop()  # pop 'true'
@@ -61,8 +54,8 @@ class ConstructorsVisitor(Visitor):
 
     def visit_assignment(self, tree):
         self.add_constructors("assign")
-        self.visit(tree.get_tail()[0])
-        self.visit(tree.get_tail()[2])
+        self.visit(Tree.get_tail(tree)[0])
+        self.visit(Tree.get_tail(tree)[2])
         return True
 
     def visit_expression(self, tree):
@@ -102,25 +95,23 @@ class ConstructorsVisitor(Visitor):
         self.visit_children(tree)
 
     def visit_type_specifier(self, tree):
-        self.add_constructors("const",
-            {"value": tree.get_text()})
+        self.add_constructors("const", {"value": Tree.get_text(tree)})
 
     def visit_actionname(self, tree):
-        self.add_constructors("const",
-            {"value": tree.get_tail()[0].get_text()[1:]})
+        self.add_constructors("const", {"value": Tree.get_text(Tree.get_tail(tree)[0])[1:]})
 
     def visit_string(self, tree):
-        v = tree.get_text()[1:-1]
+        v = Tree.get_text(tree)[1:-1]
         v = v.replace("\\n", "\n")
         v = v.replace("\\t", "\t")
         v = v.replace("\\\"", "\"")
         self.add_constructors("const", v)
 
     def visit_integer(self, tree):
-        self.add_constructors("const", int(tree.get_text()))
+        self.add_constructors("const", int(Tree.get_text(tree)))
 
     def visit_float(self, tree):
-        self.add_constructors("const", float(tree.get_text()))
+        self.add_constructors("const", float(Tree.get_text(tree)))
 
     def visit_rvalue(self, tree):
         self.add_constructors("access")
@@ -134,13 +125,13 @@ class ConstructorsVisitor(Visitor):
         self.add_constructors("resolve", symbol.node)
 
     def visit_func_call(self, tree):
-        symbol = self.get_symbol(tree.get_tail()[0])
+        symbol = self.get_symbol(Tree.get_tail(tree)[0])
         self.add_constructors("call")
         if hasattr(symbol, "pathmv"):
             self.add_constructors("deref", symbol.pathmv)
         else:
-            self.visit(tree.get_tail()[0])
-        expressions = tree.get_children("expression")
+            self.visit(Tree.get_tail(tree)[0])
+        expressions = Tree.get_children(tree, "expression")
         self.add_constructors(len(expressions))
         for expression in expressions:
             self.visit(expression)
@@ -153,7 +144,7 @@ class ConstructorsVisitor(Visitor):
 
     def visit_output(self, tree):
         self.add_constructors("output")
-        self.visit(tree.get_child("expression"))
+        self.visit(Tree.get_child(tree, "expression"))
         return True
 
     def visit_dictionary(self, tree):
@@ -167,8 +158,8 @@ class ConstructorsVisitor(Visitor):
 
     def visit_ifelse(self, tree):
         self.add_constructors("if")
-        expressions = tree.get_children("expression")
-        blocks = tree.get_children("block")
+        expressions = Tree.get_children(tree, "expression")
+        blocks = Tree.get_children(tree, "block")
         self.visit(expressions[0])  # condition
         self.visit(blocks[0])  # then-clause
 
@@ -190,59 +181,59 @@ class ConstructorsVisitor(Visitor):
 
     def visit_while(self, tree):
         self.add_constructors("while")
-        self.visit(tree.get_child("expression"))
-        self.visit(tree.get_child("block"))
+        self.visit(Tree.get_child(tree, "expression"))
+        self.visit(Tree.get_child(tree, "block"))
         return True
 
     def visit_block(self, tree):
-        tail = tree.get_tail_without(["newline", "indent"])
+        tail = Tree.get_tail_without(tree, ["newline", "indent"])
         last = tail[-1]
         for child in tail[:-1]:
-            if child.head in ["return", "break", "continue"]:
+            if child['head'] in ["return", "break", "continue"]:
                 last = child
                 break
             else:
                 self.visit(child)
-                if child.head == "func_call":  # pop 'false'
+                if child['head'] == "func_call":  # pop 'false'
                     self.constructors.pop()
                 self.add_constructors(True)
         self.visit(last)
-        if last.head == "func_call":  # pop 'false'
+        if last['head'] == "func_call":  # pop 'false'
             self.constructors.pop()
-        if last.head not in ["return", "break", "continue"]:
+        if last['head'] not in ["return", "break", "continue"]:
             self.add_constructors(False)
 
     def visit_func_body(self, tree):
         self.visit_children(tree)
 
     def pre_visit_funcdecl(self, tree):
-        func_body = tree.get_child("func_body")
+        func_body = Tree.get_child(tree, "func_body")
         symbol = self.get_symbol(tree)
         symbol.node = symbol.name
         return False
 
     def visit_funcdecl(self, tree):
-        func_body = tree.get_child("func_body")
+        func_body = Tree.get_child(tree, "func_body")
         symbol = self.get_symbol(tree)
         if func_body:
             if symbol.name in ["input", "output"]:
                 return False
-            if tree.get_children("MUTABLE"):
+            if Tree.get_children(tree, "MUTABLE"):
                 self.add_constructors("mutable_funcdef")
             else:
                 self.add_constructors("funcdef")
             self.add_constructors(symbol.node,
                                   len(symbol.params))
-            for p in tree.get_children("parameter"):
+            for p in Tree.get_children(tree, "parameter"):
                 self.visit(p)
             self.visit(func_body)
             return True
-        elif tree.get_child("ASSIGN"):
+        elif Tree.get_child(tree, "ASSIGN"):
             # TODO: fix funcdecl special case: "X function f(...) = ..."
             # Note: replicates "Element x; x = ?primiteves/a" behavior
             # Dangerous: SemanticsVisitor handles it as a function
             # pathmv is needed in visit_func_call(self, tree)
-            symbol.pathmv = tree.get_child("ANYTHING").get_text()
+            symbol.pathmv = Tree.get_text(Tree.get_child(tree, "ANYTHING"))
 
             self.add_constructors("global", symbol.node, "deref", symbol.pathmv)
 
@@ -268,15 +259,15 @@ class ConstructorsVisitor(Visitor):
         
     def visit_return(self, tree):
         self.add_constructors("return")
-        if len(tree.get_tail()) > 2:
+        if len(Tree.get_tail(tree)) > 2:
             self.add_constructors(True)
-            self.visit(tree.get_tail()[1])
+            self.visit(Tree.get_tail(tree)[1])
         else:
             self.add_constructors(False)
         return True
 
     def visit_bool(self, tree):
-        if tree.get_text() == "True":
+        if Tree.get_text(tree) == "True":
             self.add_constructors("const", True)
         else:
             self.add_constructors("const", False)
@@ -286,17 +277,17 @@ class ConstructorsVisitor(Visitor):
         symbol = self.get_symbol(tree)
         self.declare(symbol)
 
-        if tree.get_children("ASSIGN"):
+        if Tree.get_children(tree, "ASSIGN"):
             # Determine whether it is just a constant, or a deref
-            atom = tree.get_child("atomvalue")
-            if atom.get_child("deref"):
+            atom = Tree.get_child(tree, "atomvalue")
+            if Tree.get_child(atom, "deref"):
                 # Deref
-                dest = atom.get_child("deref").get_child("ANYTHING")
+                dest = Tree.get_child(Tree.get_child(atom, "deref"), "ANYTHING")
                 if dest is None:
                     # Just an empty questionmark!
                     self.add_constructors("empty")
                 else:
-                    self.add_constructors("deref", dest.get_text())
+                    self.add_constructors("deref", Tree.get_text(dest))
             else:
                 # Constant
                 self.visit(atom)

+ 10 - 10
interface/HUTN/hutn_compiler/declare_functions_visitor.py

@@ -1,7 +1,7 @@
 import hutn_compiler.symbol_table as st
 import hutn_compiler.types_mv as types_mv
 from hutn_compiler.visitor import Visitor
-
+from hutn_compiler.hutnparser import Tree
 
 # Declare the function but do not visit its body
 class DeclareFunctionsVisitor(Visitor):
@@ -12,18 +12,18 @@ class DeclareFunctionsVisitor(Visitor):
 
     def compute_parameter_types(self, tree):
         parameter_types = []
-        for parameter in tree.get_children('parameter'):
-            type_specifier = parameter.get_children("type_specifier")[0].get_text()
+        for parameter in Tree.get_children(tree, 'parameter'):
+            type_specifier = Tree.get_text(Tree.get_children(parameter, "type_specifier")[0])
             parameter_types.append(types_mv.string_to_type(type_specifier))
         return parameter_types
 
     def compute_func_type(self, tree):
-        self.visit(tree.get_tail()[0])
-        func_type = self.get_type(tree.get_tail()[0])
+        self.visit(Tree.get_tail(tree)[0])
+        func_type = self.get_type(Tree.get_tail(tree)[0])
         return func_type
 
     def visit_funcdecl(self, tree):
-        func_name = tree.get_children("func_name")[0].get_text()
+        func_name = Tree.get_text(Tree.get_children(tree, "func_name")[0])
         func_type = self.compute_func_type(tree)
         parameter_types = self.compute_parameter_types(tree)
         self.set_type(tree, func_type)
@@ -33,12 +33,12 @@ class DeclareFunctionsVisitor(Visitor):
             s = st.Symbol(func_name, func_type, is_global=True, params=parameter_types)
             self.symbol_table.add(s)
         except Exception:
-            if "COLON" in tree.get_tail() or "ASSIGN" in tree.get_tail():
+            if "COLON" in Tree.get_tail(tree) or "ASSIGN" in Tree.get_tail(tree):
                 raise RuntimeError(
                     "{}:{}:{}: error: redeclaration of '{}'".format(
-                        self.inputfiles[0], tree.startpos['line'],
-                        tree.startpos['column'], func_name))
+                        self.inputfiles[0], tree['startpos']['line'],
+                        tree['startpos']['column'], func_name))
         self.set_symbol(tree, s)
 
     def visit_func_type(self, tree):
-        self.set_type(tree, types_mv.string_to_type(tree.get_text()))
+        self.set_type(tree, types_mv.string_to_type(Tree.get_text(tree)))

+ 33 - 33
interface/HUTN/hutn_compiler/grammar_compiler_visitor.py

@@ -69,21 +69,21 @@ class Visitor(object):
     """
     def isTree(self, item):
         ret = False
-        if isinstance(item, Tree):
-            if len(item.tail) > 1:
+        if isinstance(item, dict):
+            if len(item['tail']) > 1:
                 ret = True
-            elif len(item.tail) == 1 and isinstance(item.tail[0], Tree):
+            elif len(item['tail']) == 1 and isinstance(item['tail'][0], dict):
                 ret = True
         return ret
 
     def isToken(self,item):
         ret = False
-        if isinstance(item, Tree):
+        if isinstance(item, dict):
             try:
-                if len(item.tail) == 1 and isinstance(item.tail[0], basestring):
+                if len(item['tail']) == 1 and isinstance(item['tail'][0], basestring):
                     ret = True
             except NameError:
-                if len(item.tail) == 1 and isinstance(item.tail[0], str):
+                if len(item['tail']) == 1 and isinstance(item['tail'][0], str):
                     ret = True
         return ret
 
@@ -92,8 +92,8 @@ class Visitor(object):
         # place_name : LOWER_LETTER
         #and item is place_name than the value is at the bottom of place_name -> LOWER_LETTER -> value
         #USE ONLY WHEN SURE YOU ARE EXPECTING A TOKEN VALUE
-        while item and isinstance(item, Tree):
-            item = item.tail[0]
+        while item and isinstance(item, dict):
+            item = item['tail'][0]
         return str(item)
 
 
@@ -106,9 +106,9 @@ class GrammarCompilerVisitor(Visitor):
 
     def visit(self, tree):
         if self.isTree(tree):
-            if tree.head == START_RULE:
-                for item in tree.tail:
-                    if self.isTree(item) and item.head == GRAMMAR_RULE:
+            if tree['head'] == START_RULE:
+                for item in tree['tail']:
+                    if self.isTree(item) and item['head'] == GRAMMAR_RULE:
                         self.visitGrammar(item)
                     #elif self.isTree(item, Tree) and item.head == MAPPER_RULE:
                     #    self.visitMapper(item)
@@ -122,9 +122,9 @@ class GrammarCompilerVisitor(Visitor):
 
     def visitGrammar(self,tree):
         if self.isTree(tree):
-            for child in tree.tail:
+            for child in tree['tail']:
                 if self.isTree(child):
-                    rule = child.head
+                    rule = child['head']
                     if rule == PROD_RULE: #a grammar consists of prod rules and a prod rule can be a rule or token
                         self.visitGrammar(child)
                     elif rule == RULE_DEF: #top level rule definition
@@ -140,9 +140,9 @@ class GrammarCompilerVisitor(Visitor):
             body = ''
             msg = ''
             rm = False
-            for child in tree.tail:
+            for child in tree['tail']:
                 if self.isTree(child):
-                    rule = child.head
+                    rule = child['head']
                     if rule == RULE_NAME:
                        name = self.getTokenValue(child)
                        # print name
@@ -152,7 +152,7 @@ class GrammarCompilerVisitor(Visitor):
                         #TODO this will not work for the moment due to having an extra parent
                         #the child  0 is @Msg or Message
                         #child 1 is a token of type REGEXP and his tail contains the value
-                        msg = self.getTokenValue(child.tail[1])
+                        msg = self.getTokenValue(child['tail'][1])
                     elif rule == REMOVE:
                         rm = True
                     else:
@@ -184,8 +184,8 @@ class GrammarCompilerVisitor(Visitor):
         allowUnpack = True #allow unpacking in upperlevel of recursion
         operator = '.' #we always assume it is a sequence we can change it later if we are wrong
         rhs = []
-        for item in tree.tail:
-            head = item.head
+        for item in tree['tail']:
+            head = item['head']
             if self.isTree(item):
                 if head == TOKEN_NAME: # a reference to a token
                     tok =  '$' + self.getTokenValue(item)
@@ -207,10 +207,10 @@ class GrammarCompilerVisitor(Visitor):
                     allowUnpack = False
                     operator = '#'
                     extra = '('
-                    for child in item.tail:
-                        if child.head == MINUS:
+                    for child in item['tail']:
+                        if child['head'] == MINUS:
                             extra += '-'
-                        if child.head == INT:
+                        if child['head'] == INT:
                             extra += self.getTokenValue(child) + ')'
                             operator += extra
 
@@ -228,10 +228,10 @@ class GrammarCompilerVisitor(Visitor):
                     else:
                         rhs.append(r)
                 else:
-                    print('Encountered unexpected rule type in tree RHS with head: ' + str(item.head))
+                    print('Encountered unexpected rule type in tree RHS with head: ' + str(item['head']))
             elif self.isToken(item):
                 #print "TOKEN INNER in rule:",  tree.head, "with name", item.head, " value", self.getTokenValue(item)
-                head = item.head
+                head = item['head']
                 if head == OPER:
                     operator = self.getTokenValue(item)
                     allowUnpack = False
@@ -280,17 +280,17 @@ class GrammarCompilerVisitor(Visitor):
 
     def visitTokens(self,tree):
         if self.isTree(tree):
-            for child in tree.tail:
+            for child in tree['tail']:
                 if self.isTree(child):
-                    rule = child.head
+                    rule = child['head']
                     if rule == TOKEN_DEF:
                         self.fillTokenInfo(child)
                     elif rule == TOKEN_SUB_COLLECTION:
                         #a collection is 0 type 1: 2 name 3 {  4 and further token_defs last }
-                        colType  = self.getTokenValue(child.tail[0])
-                        colName = self.getTokenValue(child.tail[2])
-                        for item in child.tail[4:]:
-                            if self.isTree(item) and item.head == TOKEN_DEF:
+                        colType  = self.getTokenValue(child['tail'][0])
+                        colName = self.getTokenValue(child['tail'][2])
+                        for item in child['tail'][4:]:
+                            if self.isTree(item) and item['head'] == TOKEN_DEF:
                                 self.fillTokenInfo(item,colType, colName)
                     else: #token_collection_content is the  parent of both token def and token sub collection
                         self.visitTokens(child)
@@ -300,9 +300,9 @@ class GrammarCompilerVisitor(Visitor):
         val = ''
         msg = ''
         rm = False
-        for item in tree.tail:
+        for item in tree['tail']:
             if self.isTree(item):
-                head = item.head
+                head = item['head']
                 if head == TOKEN_NAME:
                     #roken name contains a child of type token uppercase and this child contains the actual value
                     name =  self.getTokenValue(item)
@@ -311,11 +311,11 @@ class GrammarCompilerVisitor(Visitor):
                 elif head == MESSAGE:
                     #the child  0 is @Msg or Message
                     #child 1 is a token of type REGEXP and his tail contains the value
-                    msg = self.getTokenValue(item.tail[1])
+                    msg = self.getTokenValue(item['tail'][1])
                 elif head == MODIFIER:
                     #the tail is the token, the head gives us the name we don't need the actual value
                     #especially since the actual value might change
-                    if item.tail[0].head == IMPL_MOD:
+                    if item['tail'][0]['head'] == IMPL_MOD:
                         self.implicit.append( '$' + name)
                     #else:
                         #pass

+ 90 - 99
interface/HUTN/hutn_compiler/hutnparser.py

@@ -39,34 +39,29 @@ def get_buffer(base, start):
         return buffer(base, start)
 
 class Tree(object):
-    def __init__(self, head, tail, startpos, endpos, inputfile = None):
-        self.head = head
-        self.tail = tail
-        self.startpos = startpos
-        self.endpos = endpos
-        self._tail = None
-        self.inputfile = inputfile
-        # IMPORTANT: self.replaced: replace_child defines self.replaced
-
-    def is_rule(self):
-        return self.head.islower()
-
-    def is_token(self):
-        return not self.is_rule()
-
-    def get_tail(self):
-        if self.is_rule():
-            if not self._tail:
-                self._tail = [t for t in self.get_raw_tail()
-                              if not t.head.startswith("implicit_autogenerated_")]
-            return self._tail
+    @staticmethod
+    def is_rule(tree):
+        return tree['head'].islower()
+
+    @staticmethod
+    def is_token(tree):
+        return not Tree.is_rule(tree)
+
+    @staticmethod
+    def get_tail(tree):
+        if Tree.is_rule(tree):
+            if not tree.get("_tail", None):
+                tree['_tail'] = [t for t in Tree.get_raw_tail(tree) if not t['head'].startswith("implicit_autogenerated_")]
+            return tree['_tail']
         else:
-            return self.get_raw_tail()
+            return tree['tail']
 
-    def get_raw_tail(self):
-        return self.tail
+    @staticmethod
+    def get_raw_tail(tree):
+        return tree['tail']
 
-    def get_text(self, with_implicit=False):
+    @staticmethod
+    def get_text(tree, with_implicit=False):
         parts = []
 
         if with_implicit:
@@ -76,62 +71,60 @@ class Tree(object):
 
         def post_order(tree):
             for child in tail(tree):
-                if hasattr(child, "replaced"):
-                    child = child.replaced
-                if isinstance(child, Tree):
+                if "replaced" in child:
+                    child = child['replaced']
+                if isinstance(child, dict):
                     post_order(child)
                 else:
                     parts.append(child)
 
-        post_order(self)
+        post_order(tree)
 
         return ''.join(parts)
 
-    def get_child(self, name):
-        for child in self.get_tail():
-            if child.head == name:
+    @staticmethod
+    def get_child(tree, name):
+        for child in Tree.get_tail(tree):
+            if child['head'] == name:
                 return child
         return None
 
-    def get_children(self, name):
+    @staticmethod
+    def get_children(tree, name):
         children = []
-        for child in self.get_tail():
-            if child.head == name:
+        for child in Tree.get_tail(tree):
+            if child['head'] == name:
                 children.append(child)
         return children
 
-    def replace_child(self, old_child, new_child):
-        new_child.replaced = old_child
+    @staticmethod
+    def replace_child(tree, old_child, new_child):
+        new_child['replaced'] = old_child
 
-        i = self.get_raw_tail().index(old_child)
-        self.get_raw_tail()[i] = new_child
+        i = Tree.get_raw_tail(tree).index(old_child)
+        Tree.get_raw_tail(tree)[i] = new_child
 
-        i = self.get_tail().index(old_child)
-        self.get_tail()[i] = new_child
+        i = Tree.get_tail(tree).index(old_child)
+        Tree.get_tail(tree)[i] = new_child
 
-    def get_tail_without(self, names):
-        if self.is_rule():
-            return [t for t in self.get_tail() if not t.head in names]
+    @staticmethod
+    def get_tail_without(tree, names):
+        if Tree.is_rule(tree):
+            return [t for t in Tree.get_tail(tree) if not t['head'] in names]
         else:
-            return self.get_raw_tail()
+            return Tree.get_raw_tail(tree)
 
-    def __str__(self):
-        return "(%s, %s) [%s]" % (
-            self.head, str((self.startpos, self.endpos)),
-            ", ".join([str(i) for i in self.get_raw_tail()]))
+    @staticmethod
+    def get_reference_line(tree):
+        return "%s:%s:%s-%s" % (tree['inputfile'], tree['startpos']["line"], tree['startpos']["column"], tree['endpos']["column"])
 
-    def get_reference_line(self):
-        return "%s:%s:%s-%s" % (self.inputfile, self.startpos["line"], self.startpos["column"], self.endpos["column"])
-
-    def fix_tracability(self, inputfile):
-        if self.inputfile is None:
-            self.inputfile = inputfile
-        for f in self.tail:
-            if isinstance(f, Tree):
-                f.fix_tracability(self.inputfile)
-
-    def pretty_print(self, i=0):
-        return "\t" * i + str(self.head) + "\n" + "\n".join([("\t" * (i+1) + st) if isinstance(st, str) else st.pretty_print(i+1) for st in self.get_tail()])
+    @staticmethod
+    def fix_tracability(tree, inputfile):
+        if 'inputfile' not in tree:
+            tree['inputfile'] = inputfile
+        for t in tree['tail']:
+            if isinstance(t, dict):
+                Tree.fix_tracability(t, tree['inputfile'])
 
 class Parser(object):
     class Constants(object):
@@ -329,8 +322,8 @@ class Parser(object):
         elif len(results) == 1:
             result = results[0]
             result.update({'status': Parser.Constants.Success})
-            if result['tree'].head != 'start':
-                result['tree'] = Tree('start', [result['tree']], result['tree'].startpos, result['tree'].endpos)
+            if result['tree']['head'] != 'start':
+                result['tree'] = {"head": "start", "tail": [result['tree']], "startpos": result['tree']['startpos'], "endpos": result['tree']['endpos']}
             result['tree'] = IgnorePostProcessor(self.rules, self.tokens).visit(result['tree'])
             if self.linePosition: #Added by Daniel
                 result['tree'] = Parser.PositionPostProcessor(self.convertToLineColumn).visit(result['tree'])
@@ -592,10 +585,9 @@ class Parser(object):
             results = self.eval_body(rulename, rule['body'], j)
             for r in results:
                 if (r['tree']):
-                    head = r['tree'].head
+                    head = r['tree']['head']
                     if(head == '*' or head == '+' or head == '?' or head == '|' or head == '.'):
-                        newr = {'tree': Tree(rulename, [r['tree']], r['startpos'], r['endpos']), 'startpos': r['startpos'],
-                         'endpos': r['endpos']}
+                        newr = {"tree": {"head": rulename, "tail": [r['tree']], "startpos": r['startpos'], 'endpos': r['endpos']}, "startpos": r['startpos'], "endpos": r["endpos"]}
                         r = newr
                 newresults.append(r)
         elif (self.isType(rule, Parser.Constants.Token)):
@@ -663,7 +655,7 @@ class Parser(object):
             # this is a failure! nice to register!
             self.failure.update({rulename: {'startpos': j, 'text': self.tokens[rulename]['errortext']}})
             return []
-        return [{'tree': Tree(rulename, [mobj.group()], j, j + mobj.end()), 'startpos': j, 'endpos': j + mobj.end()}]
+        return [{'tree': {"head": rulename, "tail": [mobj.group()], "startpos": j, "endpos": j + mobj.end()}, 'startpos': j, 'endpos': j + mobj.end()}]
 
     def anonTerm(self, term, j):
         """
@@ -677,12 +669,12 @@ class Parser(object):
             self.failure.update({ name : {'startpos': j, 'text': name}})
             return []
 
-        mobj = re.match(term, self.input[j:])
+        mobj = re.match(term, get_buffer(self.input, j))
         if (not mobj):
             # this is a failure! nice to register!
             self.failure.update({ name : {'startpos': j, 'text': name }})
             return []
-        return [{'tree': Tree(name , [mobj.group()], j, j + mobj.end()), 'startpos': j, 'endpos': j + mobj.end()}]
+        return [{'tree': {"head": name, "tail": [mobj.group()], "startpos": j, "endpos": j + mobj.end()}, 'startpos': j, 'endpos': j + mobj.end()}]
 
     def many(self, rulename, ls, j):
 
@@ -701,10 +693,10 @@ class Parser(object):
         overall_results = []
         for r in results:
             if (r['tree']):
-                if (len(r['tree'].tail) > 1):
-                    left = r['tree'].tail[0]
-                    right = r['tree'].tail[1].tail
-                    r['tree'].tail = [left] + right
+                if (len(r['tree']['tail']) > 1):
+                    left = r['tree']['tail'][0]
+                    right = r['tree']['tail'][1]['tail']
+                    r['tree']['tail'] = [left] + right
                 overall_results.append(r)
 
 
@@ -727,10 +719,10 @@ class Parser(object):
         overall_results = []
         for r in results:
             if (r['tree']):
-                if (len(r['tree'].tail) > 1):
-                    left = r['tree'].tail[0]
-                    right = r['tree'].tail[1].tail
-                    r['tree'].tail = [left] + right
+                if (len(r['tree']['tail']) > 1):
+                    left = r['tree']['tail'][0]
+                    right = r['tree']['tail'][1]['tail']
+                    r['tree']['tail'] = [left] + right
                 overall_results.append(r)
 
         return overall_results
@@ -864,11 +856,10 @@ class Parser(object):
         for elem_n in newres:
             tail = []
             if ('tree' in elem_p and elem_p['tree']):
-                tail += elem_p['tree'].tail
+                tail += elem_p['tree']['tail']
             if ('tree' in elem_n and elem_n['tree']):
                 tail.append(elem_n['tree'])
-            value = {'tree': Tree(rulename, tail, elem_p['startpos'], elem_n['endpos']), 'startpos': elem_p['startpos'],
-                     'endpos': elem_n['endpos']}
+            value = {'tree': {"head": rulename, "tail": tail, "startpos": elem_p['startpos'], "endpos": elem_n["endpos"]}, 'startpos': elem_p['startpos'], 'endpos': elem_n['endpos']}
             results += [value]
         return results
 
@@ -910,12 +901,12 @@ class Parser(object):
             self.calcPosMethod = method
 
         def inner_visit(self,tree):
-            startDic = self.calcPosMethod(tree.startpos)
-            endDic = self.calcPosMethod(tree.endpos)
-            tree.startpos = Position(startDic["line"], startDic["column"])
-            tree.endpos =  Position(endDic["line"], endDic["column"])
-            for item in tree.tail:
-                if (isinstance(item, Tree)):
+            startDic = self.calcPosMethod(tree['startpos'])
+            endDic = self.calcPosMethod(tree['endpos'])
+            tree['startpos'] = Position(startDic["line"], startDic["column"])
+            tree['endpos'] =  Position(endDic["line"], endDic["column"])
+            for item in tree['tail']:
+                if (isinstance(item, dict)):
                     self.inner_visit(item)
 
         def visit(self, tree):
@@ -930,8 +921,8 @@ class Parser(object):
             self.output = output
 
         def inner_visit(self, tree):
-            for item in tree.tail:
-                if (isinstance(item, Tree)):
+            for item in tree['tail']:
+                if (isinstance(item, dict)):
                     self.inner_visit(item)
                 else:
                     self.outputStream += item
@@ -956,9 +947,9 @@ class Parser(object):
         def inner_visit(self, tree):
             self.tabcount += 1
             self.outputStream += self.tab()
-            self.outputStream += 'node ' + tree.head + ':\n'
-            for item in tree.tail:
-                if (isinstance(item, Tree)):
+            self.outputStream += 'node ' + tree['head'] + ':\n'
+            for item in tree['tail']:
+                if (isinstance(item, dict)):
                     self.inner_visit(item)
                 else:
                     self.tabcount += 1
@@ -979,20 +970,20 @@ class IgnorePostProcessor(object):
 
     def inner_visit(self, tree):
         results = []
-        if (isinstance(tree, Tree)):
-            if (self.isHidden(tree.head)):
-                for item in tree.tail:
+        if (isinstance(tree, dict)):
+            if (self.isHidden(tree['head'])):
+                for item in tree['tail']:
                     ivlist = []
                     ivresult = self.inner_visit(item)
                     for elem in ivresult:
-                        if (isinstance(elem, Tree)):
+                        if (isinstance(elem, dict)):
                             ivlist += [elem]
                     results += ivlist
             else:
                 tlist = []
-                for item in tree.tail:
+                for item in tree['tail']:
                     tlist += self.inner_visit(item)
-                tree.tail = tlist
+                tree['tail'] = tlist
                 results += [tree]
             return results
 
@@ -1001,9 +992,9 @@ class IgnorePostProcessor(object):
     def visit(self, tree):
         # start cannot be hidden
         tlist = []
-        for item in tree.tail:
+        for item in tree['tail']:
             tlist += self.inner_visit(item)
-        tree.tail = tlist
+        tree['tail'] = tlist
         return tree
 
     def isHidden(self, head):

+ 50 - 49
interface/HUTN/hutn_compiler/model_bootstrap_visitor.py

@@ -1,4 +1,5 @@
 from hutn_compiler.visitor import Visitor
+from hutn_compiler.hutnparser import Tree
 from hutn_compiler.compiler import main as do_compile
 import os
 import uuid
@@ -27,43 +28,43 @@ class ModelBootstrapVisitor(Visitor):
             raise AttributeError()
 
     def visit_start(self, tree):
-        for t in tree.get_tail():
+        for t in Tree.get_tail(tree):
             self.visit(t)
         self.code += '\tdict_overwrite(%s, "types", get_type_mapping(%s))\n' % (self.current_model, self.current_model)
 
     def visit_include_files(self, tree):
-        self.includes.append('include %s' % tree.get_children("STRVALUE")[0].get_text())
+        self.includes.append('include %s' % Tree.get_text(Tree.get_children(tree, "STRVALUE")[0]))
 
     def visit_import(self, tree):
-        url = tree.get_children("MV_URL")[0]
-        target = tree.get_children("MODEL_ID")[0]
+        url = Tree.get_children(tree, "MV_URL")[0]
+        target = Tree.get_children(tree, "MODEL_ID")[0]
         #self.constructors.extend(["import_node", url.get_text(), target.get_text()])
-        self.code += '\tElement %s\n' % target.get_text()
-        self.code += '\t%s = import_node("%s")\n' % (target.get_text(), url.get_text())
+        self.code += '\tElement %s\n' % Tree.get_text(target)
+        self.code += '\t%s = import_node("%s")\n' % (Tree.get_text(target), Tree.get_text(url))
 
     def visit_export(self, tree):
-        url = tree.get_children("MV_URL")[0]
-        target = tree.get_children("MODEL_ID")[0]
+        url = Tree.get_children(tree, "MV_URL")[0]
+        target = Tree.get_children(tree, "MODEL_ID")[0]
         #self.constructors.extend(["export_node", target.get_text(), url.get_text()])
-        self.code += '\texport_node("%s", %s)\n' % (url.get_text(), target.get_text())
+        self.code += '\texport_node("%s", %s)\n' % (Tree.get_text(url), Tree.get_text(target))
 
     def visit_model(self, tree):
-        children = tree.get_children("MODEL_ID")
-        model_type = children[0].get_text()
-        model_name = children[-1].get_text()
+        children = Tree.get_children(tree, "MODEL_ID")
+        model_type = Tree.get_text(children[0])
+        model_name = Tree.get_text(children[-1])
         #self.constructors.extend(["instantiate_model", model_type, model_name])
         self.code += "\tElement %s\n" % model_name
         self.code += '\t%s = instantiate_model(%s)\n' % (model_name, model_type)
         self.current_model = model_name
         self.names = set()
-        for element in tree.get_children("model_element"):
+        for element in Tree.get_children(tree, "model_element"):
             self.visit(element)
 
     def visit_model_element(self, tree):
-        children = tree.get_children("MODEL_ID")
-        element_type = children[0].get_text()
+        children = Tree.get_children(tree, "MODEL_ID")
+        element_type = Tree.get_text(children[0])
         if len(children) == 2 or len(children) == 4:
-            element_name = children[1].get_text()
+            element_name = Tree.get_text(children[1])
         else:
             element_name = "__%s" % self.free_id
             self.free_id += 1
@@ -73,8 +74,8 @@ class ModelBootstrapVisitor(Visitor):
 
         if len(children) > 2:
             # So we have a source and target; but aren't sure which is which, because the name is optional!
-            source_name = children[-2].get_text()
-            target_name = children[-1].get_text()
+            source_name = Tree.get_text(children[-2])
+            target_name = Tree.get_text(children[-1])
             if source_name not in self.names:
                 raise Exception("Source of link %s unknown: %s" % (element_name, source_name))
             if target_name not in self.names:
@@ -89,10 +90,10 @@ class ModelBootstrapVisitor(Visitor):
         self.names.add(element_name)
         self.current_element.append(element_name)
 
-        if tree.get_children("inheritance"):
-            self.visit(tree.get_children("inheritance")[0])
+        if Tree.get_children(tree, "inheritance"):
+            self.visit(Tree.get_children(tree, "inheritance")[0])
             
-        for attr in tree.get_children("model_attribute"):
+        for attr in Tree.get_children(tree, "model_attribute"):
             self.visit(attr)
 
         self.current_element.pop()
@@ -100,8 +101,8 @@ class ModelBootstrapVisitor(Visitor):
         return element_name
 
     def visit_inheritance(self, tree):
-        for token in tree.get_children("MODEL_ID"):
-            superclass = token.get_text()
+        for token in Tree.get_children(tree, "MODEL_ID"):
+            superclass = Tree.get_text(token)
             if superclass not in self.names:
                 raise Exception("Superclass %s is undefined" % superclass)
 
@@ -110,15 +111,15 @@ class ModelBootstrapVisitor(Visitor):
             self.names.add("%s_inherits_from_%s" % (self.current_element[-1], superclass))
 
     def visit_model_attribute(self, tree):
-        children = tree.get_children("MODEL_ID")
-        is_definition = bool(tree.get_children("COLON"))
-        is_assign = bool(tree.get_children("model_attr_instance"))
-        is_nested = bool(tree.get_children("model_element"))
+        children = Tree.get_children(tree, "MODEL_ID")
+        is_definition = bool(Tree.get_children(tree, "COLON"))
+        is_assign = bool(Tree.get_children(tree, "model_attr_instance"))
+        is_nested = bool(Tree.get_children(tree, "model_element"))
 
         if is_definition:
-            attr_name = children[0].get_text()
-            attr_optional = len(tree.get_children("OPTIONAL")) > 0
-            attr_type = children[1].get_text()
+            attr_name = Tree.get_text(children[0])
+            attr_optional = len(Tree.get_children(tree, "OPTIONAL")) > 0
+            attr_type = Tree.get_text(children[1])
             #self.constructors.extend(["model_define_attribute", self.current_model, self.current_element[-1], attr_name, attr_optional, attr_type])
             self.code += '\tmodel_define_attribute(%s, "%s", "%s", %s, "%s")\n' % (self.current_model, self.current_element[-1], attr_name, attr_optional, attr_type)
             full_attribute_name = self.current_element[-1] + "_" + attr_name
@@ -127,18 +128,18 @@ class ModelBootstrapVisitor(Visitor):
                 # There are also some attributes to set!
                 self.current_element.append(full_attribute_name)
 
-                for f in tree.get_children("model_attr_instance"):
+                for f in Tree.get_children(tree, "model_attr_instance"):
                     self.visit(f)
 
                 self.current_element.pop()
         elif is_assign:
-            self.visit(tree.get_children("model_attr_instance")[0])
+            self.visit(Tree.get_children(tree, "model_attr_instance")[0])
         elif is_nested:
-            if tree.get_children("MODEL_ID"):
-                contains_link = tree.get_children("MODEL_ID")[0].get_text()
+            if Tree.get_children(tree, "MODEL_ID"):
+                contains_link = Tree.get_text(Tree.get_children(tree, "MODEL_ID")[0])
             else:
                 contains_link = ""
-            entry = self.visit(tree.get_children("model_element")[0])
+            entry = self.visit(Tree.get_children(tree, "model_element")[0])
             #self.constructors.extend(["instantiate_link", self.current_model, contains_link, "__%s" % self.free_id, self.current_element[-1], entry])
             self.code += '\tinstantiate_link(%s, "%s", "__%s", "%s", "%s")\n' % (self.current_model, contains_link, self.free_id, self.current_element[-1], entry)
             self.names.add("__%s" % self.free_id)
@@ -161,31 +162,31 @@ class ModelBootstrapVisitor(Visitor):
             compiled = do_compile(".code.alc", directory + "/../grammars/actionlanguage.g", "CS")
             return compiled
 
-        children = tree.get_children("MODEL_ID")
-        attr_name = children[0].get_text()
-        if tree.get_children("value"):
+        children = Tree.get_children(tree, "MODEL_ID")
+        attr_name = Tree.get_text(children[0])
+        if Tree.get_children(tree, "value"):
             # Value attribute
-            attr_value = tree.get_children("value")[0].get_tail()[0]
-            if attr_value.head == "STRVALUE":
-                attr_value = '"%s"' %  attr_value.get_text()[1:-1]
-            elif attr_value.head == "TRUE":
+            attr_value = Tree.get_tail(Tree.get_children(tree, "value")[0])[0]
+            if attr_value['head'] == "STRVALUE":
+                attr_value = '"%s"' %  Tree.get_text(attr_value)[1:-1]
+            elif attr_value['head'] == "TRUE":
                 attr_value = True
-            elif attr_value.head == "FALSE":
+            elif attr_value['head'] == "FALSE":
                 attr_value = False
-            elif attr_value.head == "DEC_NUMBER":
-                attr_value = int(attr_value.get_text())
+            elif attr_value['head'] == "DEC_NUMBER":
+                attr_value = int(Tree.get_text(attr_value))
             elif attr_value.head == "FLOAT_NUMBER":
-                attr_value = float(attr_value.get_text())
+                attr_value = float(Tree.get_text(attr_value))
             else:
-                raise Exception(attr_value.head)
+                raise Exception(attr_value['head'])
             #self.constructors.extend(["instantiate_attribute", self.current_model, self.current_element[-1], attr_name, attr_value])
             self.code += '\tinstantiate_attribute(%s, "%s", "%s", %s)\n' % (self.current_model, self.current_element[-1], attr_name, attr_value)
-        elif tree.get_children("DOLLAR"):
+        elif Tree.get_children(tree, "DOLLAR"):
             # Coded attribute
             raise Exception("Code is no longer allowed in bootstrap files, as the HUTN parser is not initialized yet")
             #self.constructors.extend(["instantiate_attribute_code", self.current_model, self.current_element[-1], attr_name])
             #self.constructors.extend(constructors_compile(tree.get_children("ANYTHING_EXCEPT_DOLLAR")[0].get_text()))
-            code = tree.get_children("ANYTHING_EXCEPT_DOLLAR")[0].get_text()
+            code = Tree.get_text(Tree.get_children(tree, "ANYTHING_EXCEPT_DOLLAR")[0])
 
             code_fragments = code.split("\n")
             code_fragments = [i for i in code_fragments if i.strip() != ""]

+ 41 - 40
interface/HUTN/hutn_compiler/model_visitor.py

@@ -1,5 +1,6 @@
 from hutn_compiler.visitor import Visitor
 from hutn_compiler.compiler import main as do_compile
+from hutn_compiler.hutnparser import Tree
 import os
 
 def empty(s):
@@ -24,17 +25,17 @@ class ModelVisitor(Visitor):
             raise AttributeError()
 
     def visit_start(self, tree):
-        for t in tree.get_tail():
+        for t in Tree.get_tail(tree):
             self.visit(t)
 
     def visit_include_files(self, tree):
-        self.includes.append('include %s' % tree.get_children("STRVALUE")[0].get_text())
+        self.includes.append('include %s' % Tree.get_text(Tree.get_children(tree, "STRVALUE")[0]))
 
     def visit_model_element(self, tree):
-        children = tree.get_children("MODEL_ID")
-        element_type = children[0].get_text()
+        children = Tree.get_children(tree, "MODEL_ID")
+        element_type = Tree.get_text(children[0])
         if len(children) == 2 or len(children) == 4:
-            element_name = children[1].get_text()
+            element_name = Tree.get_text(children[1])
         else:
             element_name = "__%s" % self.free_id
             self.free_id += 1
@@ -44,8 +45,8 @@ class ModelVisitor(Visitor):
 
         if len(children) > 2:
             # So we have a source and target; but aren't sure which is which, because the name is optional!
-            source_name = children[-2].get_text()
-            target_name = children[-1].get_text()
+            source_name = Tree.get_text(children[-2])
+            target_name = Tree.get_text(children[-1])
             if source_name not in self.names:
                 raise Exception("Source of link %s unknown: %s" % (element_name, source_name))
             if target_name not in self.names:
@@ -58,10 +59,10 @@ class ModelVisitor(Visitor):
         self.names.add(element_name)
         self.current_element.append(element_name)
 
-        if tree.get_children("inheritance"):
-            self.visit(tree.get_children("inheritance")[0])
+        if Tree.get_children(tree, "inheritance"):
+            self.visit(Tree.get_children(tree, "inheritance")[0])
             
-        for attr in tree.get_children("model_attribute"):
+        for attr in Tree.get_children(tree, "model_attribute"):
             self.visit(attr)
 
         self.current_element.pop()
@@ -69,8 +70,8 @@ class ModelVisitor(Visitor):
         return element_name
 
     def visit_inheritance(self, tree):
-        for token in tree.get_children("MODEL_ID"):
-            superclass = token.get_text()
+        for token in Tree.get_children(tree, "MODEL_ID"):
+            superclass = Tree.get_text(token)
             if superclass not in self.names:
                 raise Exception("Superclass %s is undefined" % superclass)
 
@@ -78,15 +79,15 @@ class ModelVisitor(Visitor):
             self.names.add("%s_inherits_from_%s" % (self.current_element[-1], superclass))
 
     def visit_model_attribute(self, tree):
-        children = tree.get_children("MODEL_ID")
-        is_definition = bool(tree.get_children("COLON"))
-        is_assign = bool(tree.get_children("model_attr_instance"))
-        is_nested = bool(tree.get_children("model_element"))
+        children = Tree.get_children(tree, "MODEL_ID")
+        is_definition = bool(Tree.get_children(tree, "COLON"))
+        is_assign = bool(Tree.get_children(tree, "model_attr_instance"))
+        is_nested = bool(Tree.get_children(tree, "model_element"))
 
         if is_definition:
-            attr_name = children[0].get_text()
-            attr_optional = len(tree.get_children("OPTIONAL")) > 0
-            attr_type = children[1].get_text()
+            attr_name = Tree.get_text(children[0])
+            attr_optional = len(Tree.get_children(tree, "OPTIONAL")) > 0
+            attr_type = Tree.get_text(children[1])
             if attr_type not in self.names:
                 raise Exception("Unknown Attribute type!")
             self.constructors.extend(["model_define_attribute", self.current_element[-1], attr_name, attr_optional, attr_type])
@@ -96,18 +97,18 @@ class ModelVisitor(Visitor):
                 # There are also some attributes to set!
                 self.current_element.append(full_attribute_name)
 
-                for f in tree.get_children("model_attr_instance"):
+                for f in Tree.get_children(tree, "model_attr_instance"):
                     self.visit(f)
 
                 self.current_element.pop()
         elif is_assign:
-            self.visit(tree.get_children("model_attr_instance")[0])
+            self.visit(Tree.get_children(tree, "model_attr_instance")[0])
         elif is_nested:
-            if tree.get_children("MODEL_ID"):
-                contains_link = tree.get_children("MODEL_ID")[0].get_text()
+            if Tree.get_children(tree, "MODEL_ID"):
+                contains_link = Tree.get_text(Tree.get_children(tree, "MODEL_ID")[0])
             else:
                 contains_link = ""
-            entry = self.visit(tree.get_children("model_element")[0])
+            entry = self.visit(Tree.get_children(tree, "model_element")[0])
             self.constructors.extend(["instantiate_link", contains_link, "__%s" % self.free_id, self.current_element[-1], entry])
             self.names.add("__%s" % self.free_id)
             self.free_id += 1
@@ -129,27 +130,27 @@ class ModelVisitor(Visitor):
             compiled = do_compile(".code.alc", directory + "/../grammars/actionlanguage.g", "CS")
             return compiled + [code]
 
-        children = tree.get_children("MODEL_ID")
-        attr_name = children[0].get_text()
-        if tree.get_children("value"):
+        children = Tree.get_children(tree, "MODEL_ID")
+        attr_name = Tree.get_text(children[0])
+        if Tree.get_children(tree, "value"):
             # Value attribute
-            attr_value = tree.get_children("value")[0].get_tail()[0]
-            if attr_value.head == "STRVALUE":
-                attr_value = attr_value.get_text()[1:-1]
-            elif attr_value.head == "LONG_STR":
-                attr_value = attr_value.get_text()[3:-3]
-            elif attr_value.head == "TRUE":
+            attr_value = Tree.get_tail(Tree.get_children(tree, "value")[0])[0]
+            if attr_value['head'] == "STRVALUE":
+                attr_value = Tree.get_text(attr_value)[1:-1]
+            elif attr_value['head'] == "LONG_STR":
+                attr_value = Tree.get_text(attr_value)[3:-3]
+            elif attr_value['head'] == "TRUE":
                 attr_value = True
-            elif attr_value.head == "FALSE":
+            elif attr_value['head'] == "FALSE":
                 attr_value = False
-            elif attr_value.head == "DEC_NUMBER":
-                attr_value = int(attr_value.get_text())
+            elif attr_value['head'] == "DEC_NUMBER":
+                attr_value = int(Tree.get_text(attr_value))
             elif attr_value.head == "FLOAT_NUMBER":
-                attr_value = float(attr_value.get_text())
+                attr_value = float(Tree.get_text(attr_value))
             else:
-                raise Exception(attr_value.head)
+                raise Exception(attr_value['head'])
             self.constructors.extend(["instantiate_attribute", self.current_element[-1], attr_name, attr_value])
-        elif tree.get_children("DOLLAR"):
+        elif Tree.get_children(tree, "DOLLAR"):
             # Coded attribute
             self.constructors.extend(["instantiate_attribute_code", self.current_element[-1], attr_name])
-            self.constructors.extend(constructors_compile(tree.get_children("ANYTHING_EXCEPT_DOLLAR")[0].get_text()))
+            self.constructors.extend(constructors_compile(Tree.get_text(Tree.get_children(tree, "ANYTHING_EXCEPT_DOLLAR")[0])))

+ 1 - 1
interface/HUTN/hutn_compiler/position.py

@@ -70,4 +70,4 @@ class Position(object):
             raise AttributeError("'Position' object has no attribute '"+str(item)+"'")
 
     def deepcopy(self):
-        pass
+        pass

+ 10 - 10
interface/HUTN/hutn_compiler/prettyprint_visitor.py

@@ -8,9 +8,9 @@ class PrintVisitor(Visitor):
         pass
 
     def visit(self, tree, level=0):
-        if isinstance(tree, Tree):
-            self.result += '\n' + '\t' * level + tree.head
-            for child in tree.tail:
+        if isinstance(tree, dict):
+            self.result += '\n' + '\t' * level + tree['head']
+            for child in tree['tail']:
                 self.visit(child, level+1)
         else:
             self.result += '(' + repr(tree) + ')'
@@ -24,15 +24,15 @@ class PrettyPrintVisitor(Visitor):
         self.result = ""
 
     def visit(self, tree, level=0):
-        if isinstance(tree, Tree):
-            if tree.is_rule() and len(tree.get_tail()) > 1:
-                self.result += '\n' + '\t' * level + tree.head
-                if not tree.head.startswith("implicit_autogenerated_"):
-                    for child in tree.tail:
+        if isinstance(tree, dict):
+            if tree.is_rule() and len(Tree.get_tail(tree)) > 1:
+                self.result += '\n' + '\t' * level + tree['head']
+                if not tree['head'].startswith("implicit_autogenerated_"):
+                    for child in tree['tail']:
                         self.visit(child, level+1)
             else:
-                if not tree.head.startswith("implicit_autogenerated_"):
-                    for child in tree.tail:
+                if not tree['head'].startswith("implicit_autogenerated_"):
+                    for child in tree['tail']:
                         self.visit(child, level)
         else:
             self.result += '\n' + '\t' * level + '(' + repr(tree) + ')'

+ 38 - 37
interface/HUTN/hutn_compiler/primitives_visitor.py

@@ -1,5 +1,6 @@
 import string
 from hutn_compiler.visitor import Visitor
+from hutn_compiler.hutnparser import Tree
 import json
 
 class Action():
@@ -17,7 +18,7 @@ class PrimitivesVisitor(Visitor):
 
     def debug(self, node, tree, msg=""):
         if self.debug_symbols:
-            self.dict(node, "__debug", self.value("[%s] %s" % (tree.get_reference_line(), msg)))
+            self.dict(node, "__debug", self.value("[%s] %s" % (Tree.get_reference_line(tree), msg)))
 
     def node(self):
         self.output.append(("N", self.free_id))
@@ -71,24 +72,24 @@ class PrimitivesVisitor(Visitor):
         return "".join(output)
 
     def set_primitive(self, tree, primitive):
-        tree.primitive = primitive
+        tree['primitive'] = primitive
 
     def get_primitive(self, tree):
-        return getattr(tree, 'primitive', None)
+        return tree.get('primitive', None)
 
     def forward_primitive_of_child(self, tree, i):
         self.visit_children(tree)
-        self.set_primitive(tree, self.get_primitive(tree.get_tail()[i]))
+        self.set_primitive(tree, self.get_primitive(Tree.get_tail(tree)[i]))
 
     # a visit_* method for each non-terminal in the grammar
     def visit_start(self, tree):
         primitives = []
-        for child in tree.get_children("funcdecl"):
+        for child in Tree.get_children(tree, "funcdecl"):
             p = self.pre_visit_funcdecl(child)
             if p:
                 # funcdecl returns a (global, assign) pair
                 primitives.extend(p)
-        for child in tree.get_tail():
+        for child in Tree.get_tail(tree):
             self.visit(child)
             p = self.get_primitive(child)
             if p is not None:
@@ -97,7 +98,7 @@ class PrimitivesVisitor(Visitor):
                     primitives.extend(p)
                 else:
                     primitives.append(p)
-                if child.head == "return":
+                if child['head'] == "return":
                     break
         self.first = primitives[0]
         for i in range(len(primitives)-1):
@@ -108,8 +109,8 @@ class PrimitivesVisitor(Visitor):
         self.visit_children(tree)
 
         a = self.value(Action("assign"))
-        var = self.get_primitive(tree.get_tail()[0])
-        value = self.get_primitive(tree.get_tail()[-1])
+        var = self.get_primitive(Tree.get_tail(tree)[0])
+        value = self.get_primitive(Tree.get_tail(tree)[-1])
         self.dict(a, "var", var)
         self.dict(a, "value", value)
         self.debug(a, tree)
@@ -156,8 +157,8 @@ class PrimitivesVisitor(Visitor):
         self.visit_literal(tree)
 
     def visit_actionname(self, tree):
-        self.visit_literal(tree.get_tail()[0])
-        self.set_primitive(tree, self.get_primitive(tree.get_tail()[0]))
+        self.visit_literal(Tree.get_tail(tree)[0])
+        self.set_primitive(tree, self.get_primitive(Tree.get_tail(tree)[0]))
 
     def visit_string(self, tree):
         self.visit_literal(tree)
@@ -170,10 +171,10 @@ class PrimitivesVisitor(Visitor):
         #     self.set_id(tree, n)
         # else:
         c = self.value(Action("constant"))
-        if tree.get_text()[0] == "!":
-            v = tree.get_text()[1:]
+        if Tree.get_text(tree)[0] == "!":
+            v = Tree.get_text(tree)[1:]
         else:
-            v = tree.get_text()
+            v = Tree.get_text(tree)
         #NOTE Wrap this in an Action, even though it might not be an action: this has to be seen directly in the Mv without additional wrapping
         n = self.value(Action(v))
         self.dict(c, "node", n)
@@ -210,9 +211,9 @@ class PrimitivesVisitor(Visitor):
     def visit_func_call(self, tree):
         self.visit_children(tree)
 
-        symbol = self.get_symbol(tree.get_tail()[0])
+        symbol = self.get_symbol(Tree.get_tail(tree)[0])
 
-        expressions = tree.get_children("expression")
+        expressions = Tree.get_children(tree, "expression")
         arg_nodes_reversed = []
 
         for i in reversed(range(len(expressions))):
@@ -233,7 +234,7 @@ class PrimitivesVisitor(Visitor):
             arg_nodes_reversed.append(arg_node)
 
         c = self.value(Action("call"))
-        a = self.get_primitive(tree.get_tail()[0])
+        a = self.get_primitive(Tree.get_tail(tree)[0])
         self.dict(c, "func", a)
         self.debug(c, tree)
 
@@ -253,15 +254,15 @@ class PrimitivesVisitor(Visitor):
         self.visit_children(tree)
         v = self.value(Action("output"))
         self.debug(v, tree)
-        value = self.get_primitive(tree.get_child("expression"))
+        value = self.get_primitive(Tree.get_child(tree, "expression"))
         self.dict(v, "value", value)
         self.set_primitive(tree, v)
 
     def visit_ifelse(self, tree):
         self.visit_children(tree)
         expressions = [self.get_primitive(e) for e in
-                       tree.get_children("expression")]
-        blocks = [self.get_primitive(b) for b in tree.get_children("block")]
+                       Tree.get_children(tree, "expression")]
+        blocks = [self.get_primitive(b) for b in Tree.get_children(tree, "block")]
 
         first = None
         prev = None
@@ -299,8 +300,8 @@ class PrimitivesVisitor(Visitor):
         self.while_stack.pop()
 
         self.debug(w, tree)
-        c = self.get_primitive(tree.get_child("expression"))
-        b = self.get_primitive(tree.get_child("block"))
+        c = self.get_primitive(Tree.get_child(tree, "expression"))
+        b = self.get_primitive(Tree.get_child(tree, "block"))
 
         self.dict(w, "cond", c)
         self.dict(w, "body", b)
@@ -310,16 +311,16 @@ class PrimitivesVisitor(Visitor):
     def visit_block(self, tree):
         self.visit_children(tree)
         primitives = []
-        for child in tree.get_tail():
+        for child in Tree.get_tail(tree):
             p = self.get_primitive(child)
             if p:
                 primitives.append(p)
-                if child.head == "return":
+                if child['head'] == "return":
                     break
         for i in range(len(primitives)-1):
             self.dict(primitives[i], "next", primitives[i+1])
         if len(primitives) == 0:
-            raise Exception("Block with no body found at %s" % tree.get_reference_line())
+            raise Exception("Block with no body found at %s" % Tree.get_reference_line(tree))
         self.set_primitive(tree, primitives[0])
 
     def visit_func_body(self, tree):
@@ -333,7 +334,7 @@ class PrimitivesVisitor(Visitor):
         # TODO: fix funcdecl special case: "X function f(...) = ..."
         # Note: replicates "Element x; x = ?primiteves/a" behavior
         # Dangerous: SemanticsVisitor handles it as a function
-        if not tree.get_child("func_body"):
+        if not Tree.get_child(tree, "func_body"):
             return
 
         new_value = self.node()
@@ -361,11 +362,11 @@ class PrimitivesVisitor(Visitor):
         if symbol.name in ["__input", "__output"]:
             return
 
-        func_body = tree.get_child("func_body")
+        func_body = Tree.get_child(tree, "func_body")
         if func_body:
             self.visit_children(tree)
             vf = self.function_values[symbol.name]
-            parameters = tree.get_children("parameter")
+            parameters = Tree.get_children(tree, "parameter")
             if parameters:
                 ps = self.node()
                 self.dict(vf, "params", ps)
@@ -377,7 +378,7 @@ class PrimitivesVisitor(Visitor):
                     self.dict(n, "name", self.value(string.ascii_lowercase[i]))
             b = self.get_primitive(func_body)
 
-            if tree.get_children("MUTABLE"):
+            if Tree.get_children(tree, "MUTABLE"):
                 self.dict(vf, "mutable", self.node())
 
             self.dict(vf, "body", b)
@@ -388,9 +389,9 @@ class PrimitivesVisitor(Visitor):
             root = self.value(symbol.name)
             symbol.node = root
 
-            if tree.get_child("ASSIGN"):
-                new_value = "?" + tree.get_child("ANYTHING").get_text()
-            elif tree.get_child("ASSIGN") and not tree.get_child("ANYTHING"):
+            if Tree.get_child(tree, "ASSIGN"):
+                new_value = "?" + Tree.get_text(Tree.get_child(tree, "ANYTHING"))
+            elif Tree.get_child(tree, "ASSIGN") and not Tree.get_child(tree, "ANYTHING"):
                 new_value = self.node()
             else:
                 return
@@ -426,15 +427,15 @@ class PrimitivesVisitor(Visitor):
 
         symbol.node = root
 
-        if tree.get_children("ASSIGN"):
+        if Tree.get_children(tree, "ASSIGN"):
             resolve = self.value(Action("resolve"))
             self.dict(resolve, "var", root)
 
             assign = self.value(Action("assign"))
             self.dict(assign, "var", resolve)
 
-            self.visit_atomvalue(tree.get_tail()[-1])
-            value = self.get_primitive(tree.get_tail()[-1])
+            self.visit_atomvalue(Tree.get_tail(tree)[-1])
+            value = self.get_primitive(Tree.get_tail(tree)[-1])
 
             if value is None:
                 call = self.value(Action("call"))
@@ -462,8 +463,8 @@ class PrimitivesVisitor(Visitor):
         self.visit_children(tree)
         r = self.value(Action("return"))
         self.debug(r, tree)
-        if len(tree.get_tail()) > 2:
-            v = self.get_primitive(tree.get_tail()[1])
+        if len(Tree.get_tail(tree)) > 2:
+            v = self.get_primitive(Tree.get_tail(tree)[1])
             self.dict(r, "value", v)
         self.set_primitive(tree, r)
 

+ 137 - 134
interface/HUTN/hutn_compiler/semantics_visitor.py

@@ -3,6 +3,7 @@ import hutn_compiler.symbol_table as st
 import sys
 import hutn_compiler.types_mv as types_mv
 from hutn_compiler.declare_functions_visitor import DeclareFunctionsVisitor
+from hutn_compiler.hutnparser import Tree
 from hutn_compiler.visitor import Visitor
 
 
@@ -21,8 +22,7 @@ class SemanticsVisitor(Visitor):
         # to ensure that (returned type == declared type)
         self.current_funcdecl = None
 
-        self.declare_functions_visitor =\
-            DeclareFunctionsVisitor(self.symbol_table, self.inputfiles)
+        self.declare_functions_visitor = DeclareFunctionsVisitor(self.symbol_table, self.inputfiles)
 
     @staticmethod
     def incompatible_types(l_type, r_type):
@@ -41,34 +41,34 @@ class SemanticsVisitor(Visitor):
             raise RuntimeError(
                 "{}:{}:{}: error: invalid operands to binary operator "
                 "(have {} and {})".format(self.inputfiles[0],
-                                          l.startpos['line'],
-                                          l.startpos['column'],
+                                          l['startpos']['line'],
+                                          l['startpos']['column'],
                                           str(l_type),
                                           str(r_type)))
 
     def check_binary_ops_arithmetic(self, tree):
-        l, r = tree.get_tail()[0], tree.get_tail()[2]
+        l, r = Tree.get_tail(tree)[0], Tree.get_tail(tree)[2]
         self.do_check_binary_ops_arithmetic(l, r)
 
     def generalize_binary_ops_arithmetic(self, tree):
-        l, r = tree.get_tail()[0], tree.get_tail()[2]
+        l, r = Tree.get_tail(tree)[0], Tree.get_tail(tree)[2]
         l_type, r_type = self.get_type(l), self.get_type(r)
         return types_mv.generalize_arithmetic(l_type, r_type)
 
     def check_unary_ops_arithmetic(self, tree, operator_name):
-        l = tree.get_tail()[1]
+        l = Tree.get_tail(tree)[1]
         l_type = self.get_type(l)
         if l_type.isNotNumber():
             raise RuntimeError(
                 "{}:{}:{}: error: wrong type argument to unary {} "
                 "({})".format(self.inputfiles[0],
-                              l.startpos['line'],
-                              l.startpos['column'],
+                              l['startpos']['line'],
+                              l['startpos']['column'],
                               operator_name,
                               str(l_type)))
 
     def promote_unary_ops_arithmetic(self, tree):
-        l = tree.get_tail()[1]
+        l = Tree.get_tail(tree)[1]
         l_type = self.get_type(l)
         try:
             return types_mv.promote_arithmetic(l_type)
@@ -87,20 +87,20 @@ class SemanticsVisitor(Visitor):
             raise RuntimeError("{}:{}:{}: error: cannot assign a value of "
                                "type '{}' to a variable of type '{}'"
                 .format(self.inputfiles[0],
-                        l.startpos['line'],
-                        l.startpos['column'],
+                        l['startpos']['line'],
+                        l['startpos']['column'],
                         str(r_type),
                         str(l_type)))
 
     def check_assignment(self, tree):
-        l, r = tree.get_tail()[0], tree.get_tail()[2]
+        l, r = Tree.get_tail(tree)[0], Tree.get_tail(tree)[2]
         self.do_check_assignment(l, r)
 
     def check_return(self, tree):
         l = self.current_funcdecl
 
-        if len(tree.get_tail()) > 2:
-            r = tree.get_tail()[1]
+        if len(Tree.get_tail(tree)) > 2:
+            r = Tree.get_tail(tree)[1]
             r_type = None
         else:
             r = None
@@ -112,8 +112,8 @@ class SemanticsVisitor(Visitor):
             raise RuntimeError(
                 "{}:{}:{}: error: 'return' is used outside of a function"
                 .format(self.inputfiles[0],
-                        tree.startpos['line'],
-                        tree.startpos['column']))
+                        tree['startpos']['line'],
+                        tree['startpos']['column']))
 
     def check_predicate(self, tree):
         if isinstance(self.get_type(tree), types_mv.Element):
@@ -122,18 +122,18 @@ class SemanticsVisitor(Visitor):
             raise RuntimeError(
                 "{}:{}:{}: error: predicates of type '{}' are not allowed"
                 .format(self.inputfiles[0],
-                        tree.startpos['line'],
-                        tree.startpos['column'],
+                        tree['startpos']['line'],
+                        tree['startpos']['column'],
                         self.get_type(tree)))
 
     def replace_child_binary_op_with_call(self, tree, i=0):
         if i == -1:
             child = tree
         else:
-            child = tree.get_tail()[i]
-        if len(child.get_tail()) > 1:
+            child = Tree.get_tail(tree)[i]
+        if len(Tree.get_tail(child)) > 1:
             try:
-                l, op, r = child.get_tail()
+                l, op, r = Tree.get_tail(child)
             except:
                 # Something went wrong... this code is severely broken
                 return
@@ -143,8 +143,8 @@ class SemanticsVisitor(Visitor):
                 raise RuntimeError(
                     "{}:{}:{}: error: children were not casted".format(
                         self.inputfiles[0],
-                        tree.startpos['line'],
-                        tree.startpos['column']
+                        tree['startpos']['line'],
+                        tree['startpos']['column']
                     ))
             call_name = SemanticsVisitor.call_name_binary(l_type, op)
             call_tree = self.func_call(call_name, [l, r], tree)
@@ -157,24 +157,24 @@ class SemanticsVisitor(Visitor):
                     "{}:{}:{}: error: cannot perform {}: function '{}' is "
                     "not found".format(
                         self.inputfiles[0],
-                        tree.startpos['line'],
-                        tree.startpos['column'],
-                        child.head,
+                        tree['startpos']['line'],
+                        tree['startpos']['column'],
+                        child['head'],
                         call_signature))
             if i == -1:
-                tree.head = call_tree.head
-                tree.tail = call_tree.tail
-                tree._tail = None
+                tree['head'] = call_tree['head']
+                tree['tail'] = call_tree['tail']
+                tree['_tail'] = None
             else:
-                tree.replace_child(child, call_tree)
-        self.set_type(tree, self.get_type(tree.get_tail()[i]))
+                Tree.replace_child(tree, child, call_tree)
+        self.set_type(tree, self.get_type(Tree.get_tail(tree)[i]))
 
     def replace_child_unary_op_with_call(self, tree):
-        child = tree.get_tail()[0]
-        if child.head == "keep_sign":
-            tree.replace_child(child, child.get_tail()[1])
+        child = Tree.get_tail(tree)[0]
+        if child['head'] == "keep_sign":
+            Tree.replace_child(tree, child, Tree.get_tail(child)[1])
         else:
-            op, l = child.get_tail()
+            op, l = Tree.get_tail(child)
             l_type = self.get_type(l)
             call_name = SemanticsVisitor.call_name_unary(l_type, op)
             call_tree = self.func_call(call_name, [l], tree)
@@ -187,15 +187,15 @@ class SemanticsVisitor(Visitor):
                     "{}:{}:{}: error: cannot perform {}: function '{}' is "
                     "not found".format(
                         self.inputfiles[0],
-                        tree.startpos['line'],
-                        tree.startpos['column'],
-                        child.head,
+                        tree['startpos']['line'],
+                        tree['startpos']['column'],
+                        child['head'],
                         call_signature))
-            tree.replace_child(child, call_tree)
-        self.set_type(tree, self.get_type(tree.get_tail()[0]))
+            Tree.replace_child(tree, child, call_tree)
+        self.set_type(tree, self.get_type(Tree.get_tail(tree)[0]))
 
     def cast_binary_ops_arithmetic(self, tree):
-        l, op, r = tree.get_tail()
+        l, op, r = Tree.get_tail(tree)
         l_type, r_type = self.get_type(l), self.get_type(r)
         if type(l_type) != type(r_type):  # if two different numeric types
             g_type = types_mv.generalize_arithmetic(l_type, r_type)
@@ -203,42 +203,46 @@ class SemanticsVisitor(Visitor):
             self.perform_implicit_cast(tree, r, r_type, g_type)
 
     def cast_binary_ops_logical(self, tree):
-        l, op, r = tree.get_tail()
+        l, op, r = Tree.get_tail(tree)
         l_type, r_type = self.get_type(l), self.get_type(r)
         self.perform_implicit_cast(tree, l, l_type, types_mv.Boolean())
         self.perform_implicit_cast(tree, r, r_type, types_mv.Boolean())
 
     def cast_unary_ops_arithmetic(self, tree):
-        l = tree.get_tail()[1]
+        l = Tree.get_tail(tree)[1]
         l_type = self.get_type(l)
         p_type = self.promote_unary_ops_arithmetic(tree)
         self.perform_implicit_cast(tree, l, l_type, p_type)
 
     def func_call(self, name, params, old_tree):
-        startpos = old_tree.startpos
-        endpos = old_tree.endpos
-        inputfile = old_tree.inputfile
-
-        tree = hp.Tree(
-            "func_call",
-            [
-                hp.Tree("rvalue",
-                        [
-                            hp.Tree("ID", [name], startpos, endpos, inputfile)
-                        ],
-                        startpos, endpos, inputfile),
-                # Tokens have no impact on visit_func_call. So leave them out.
-            ],
-            startpos, endpos, inputfile)
+        startpos = old_tree['startpos']
+        endpos = old_tree['endpos']
+        inputfile = old_tree['inputfile']
+
+        tree = {"head": "func_call",
+                "tail": [
+                    {"head": "rvalue",
+                     "tail": [
+                        {"head": "ID",
+                         "tail": [name],
+                         "startpos": startpos,
+                         "endpos": endpos,
+                         "inputfile": inputfile}],
+                     "startpos": startpos,
+                     "endpos": endpos,
+                     "inputfile": inputfile}],
+                "startpos": startpos,
+                "endpos": endpos,
+                "inputfile": inputfile}
 
         for p in params:
             self.replace_child_binary_op_with_call(p, -1)
 
-        params = [hp.Tree("expression", [p], startpos, endpos, inputfile) for p in params]
+        params = [{"head": "expression", "tail": [p], "startpos": startpos, "endpos": endpos, "inputfile": inputfile} for p in params]
 
-        tree.tail.extend(params)
+        tree['tail'].extend(params)
 
-        return hp.Tree("expression", [tree], startpos, endpos, inputfile)
+        return {"head": "expression", "tail": [tree], "startpos": startpos, "endpos": endpos, "inputfile": inputfile}
 
     @staticmethod
     def cast_name(from_type, to_type):
@@ -254,8 +258,8 @@ class SemanticsVisitor(Visitor):
             "{}:{}:{}: error: cannot perform implicit cast from '{}'"
             " to '{}': function '{}' is not found".format(
                 self.inputfiles[0],
-                tree.startpos['line'],
-                tree.startpos['column'],
+                tree['startpos']['line'],
+                tree['startpos']['column'],
                 str(to_type), str(from_type),
                 cast_signature))
 
@@ -270,7 +274,7 @@ class SemanticsVisitor(Visitor):
             self.visit(cast_tree)
         except RuntimeError:
             self.raise_implicit_cast_error(from_type, to_type, child)
-        tree.replace_child(child, cast_tree)
+        Tree.replace_child(tree, child, cast_tree)
 
     types = {
         "Integer": "integer",
@@ -306,38 +310,38 @@ class SemanticsVisitor(Visitor):
     def call_name_binary(operand_type, operator):
         # String joins should also be possible
         if str(operand_type) == "String":
-            if operator.head == "PLUS":
+            if operator['head'] == "PLUS":
                 return "string_join"
 
-        if operator.head == "EQ":
+        if operator['head'] == "EQ":
             return "value_eq"
-        elif operator.head == "NEQ":
+        elif operator['head'] == "NEQ":
             return "value_neq"
 
         call_name = "{}_{}".format(SemanticsVisitor.types[str(operand_type)],
-                                   SemanticsVisitor.binary_ops[operator.head])
+                                   SemanticsVisitor.binary_ops[operator['head']])
         return call_name
 
     @staticmethod
     def call_name_unary(operand_type, operator):
         call_name = "{}_{}".format(SemanticsVisitor.types[str(operand_type)],
-                                   SemanticsVisitor.unary_ops[operator.head])
+                                   SemanticsVisitor.unary_ops[operator['head']])
 
         return call_name
 
     def dump(self):
-        return self.tree.get_text(with_implicit=True)
+        return Tree.get_text(self.tree, with_implicit=True)
         # return "No code generation here"
 
     # a visit_* method for each non-terminal in the grammar
     def visit_start(self, tree):
         self.symbol_table.open_scope()
-        self.inputfiles.append(tree.inputfile)
-        for child in tree.get_tail():
-            self.inputfiles[0] = child.inputfile
+        self.inputfiles.append(tree['inputfile'])
+        for child in Tree.get_tail(tree):
+            self.inputfiles[0] = child['inputfile']
             self.declare_functions_visitor.visit(child)
-        for child in tree.get_tail():
-            self.inputfiles[0] = child.inputfile
+        for child in Tree.get_tail(tree):
+            self.inputfiles[0] = child['inputfile']
             self.visit(child)
         self.inputfiles.pop()
         self.symbol_table.close_scope()
@@ -350,22 +354,21 @@ class SemanticsVisitor(Visitor):
         self.visit_vardecl(tree)
 
     def visit_vardecl(self, tree):
-        type_spec = tree.get_child("type_specifier")
-        var_id = tree.get_child("ID")
+        type_spec = Tree.get_child(tree, "type_specifier")
+        var_id = Tree.get_child(tree, "ID")
 
-        var_type = types_mv.string_to_type(type_spec.get_text())
-        var_name = var_id.get_text()
+        var_type = types_mv.string_to_type(Tree.get_text(type_spec))
+        var_name = Tree.get_text(var_id)
 
-        symbol = st.Symbol(var_name, var_type,
-                        is_global=self.current_funcdecl is None)
+        symbol = st.Symbol(var_name, var_type, is_global=self.current_funcdecl is None)
 
         try:
             self.symbol_table.add(symbol)
         except Exception:
             raise RuntimeError(
                 "{}:{}:{}: error: redeclaration of '{}'".format(
-                    self.inputfiles[0], tree.startpos['line'],
-                    tree.startpos['column'], var_name))
+                    self.inputfiles[0], tree['startpos']['line'],
+                    tree['startpos']['column'], var_name))
 
         self.set_symbol(tree, symbol)
 
@@ -375,7 +378,7 @@ class SemanticsVisitor(Visitor):
 
     def visit_expression(self, tree):
         self.visit_children(tree)
-        self.set_type(tree, self.get_type(tree.get_tail()[0]))
+        self.set_type(tree, self.get_type(Tree.get_tail(tree)[0]))
 
     def visit_binary_operation(self, tree):
         self.visit_children(tree)
@@ -383,7 +386,7 @@ class SemanticsVisitor(Visitor):
 
     def visit_disjunction(self, tree):
         self.visit_children(tree)
-        if len(tree.get_tail()) == 1:
+        if len(Tree.get_tail(tree)) == 1:
             self.replace_child_binary_op_with_call(tree)
         else:
             self.replace_child_binary_op_with_call(tree, 2)
@@ -392,7 +395,7 @@ class SemanticsVisitor(Visitor):
 
     def visit_conjunction(self, tree):
         self.visit_children(tree)
-        if len(tree.get_tail()) == 1:
+        if len(Tree.get_tail(tree)) == 1:
             self.replace_child_binary_op_with_call(tree)
         else:
             self.replace_child_binary_op_with_call(tree, 2)
@@ -401,7 +404,7 @@ class SemanticsVisitor(Visitor):
 
     def visit_comparison(self, tree):
         self.visit_children(tree)
-        if len(tree.get_tail()) == 1:
+        if len(Tree.get_tail(tree)) == 1:
             self.replace_child_binary_op_with_call(tree)
         else:
             self.replace_child_binary_op_with_call(tree, 2)
@@ -411,7 +414,7 @@ class SemanticsVisitor(Visitor):
 
     def visit_relation(self, tree):
         self.visit_children(tree)
-        if len(tree.get_tail()) == 1:
+        if len(Tree.get_tail(tree)) == 1:
             self.replace_child_binary_op_with_call(tree)
         else:
             self.replace_child_binary_op_with_call(tree, 2)
@@ -421,40 +424,40 @@ class SemanticsVisitor(Visitor):
 
     def visit_sum(self, tree):
         self.visit_children(tree)
-        if len(tree.get_tail()) == 1:
+        if len(Tree.get_tail(tree)) == 1:
             self.replace_child_binary_op_with_call(tree)
         else:
             self.replace_child_binary_op_with_call(tree, 2)
             self.check_binary_ops_arithmetic(tree)
             self.cast_binary_ops_arithmetic(tree)
             # after the cast both parameters have the same (generalized) type:
-            self.set_type(tree, self.get_type(tree.get_tail()[0]))
+            self.set_type(tree, self.get_type(Tree.get_tail(tree)[0]))
 
     def visit_term(self, tree):
         self.visit_children(tree)
-        if len(tree.get_tail()) == 1:
-            self.set_type(tree, self.get_type(tree.get_tail()[0]))
+        if len(Tree.get_tail(tree)) == 1:
+            self.set_type(tree, self.get_type(Tree.get_tail(tree)[0]))
         else:
             self.check_binary_ops_arithmetic(tree)
             self.cast_binary_ops_arithmetic(tree)
             # after the cast both parameters have the same (generalized) type:
-            self.set_type(tree, self.get_type(tree.get_tail()[0]))
+            self.set_type(tree, self.get_type(Tree.get_tail(tree)[0]))
 
     def visit_factor(self, tree):
         self.visit_children(tree)
-        if tree.get_child("primary") is not None:
-            self.set_type(tree, self.get_type(tree.get_tail()[0]))
+        if Tree.get_child(tree, "primary") is not None:
+            self.set_type(tree, self.get_type(Tree.get_tail(tree)[0]))
         else:
             self.replace_child_unary_op_with_call(tree)
 
     def visit_logical_not(self, tree):
         self.visit_children(tree)
 
-        l = tree.get_tail()[1]
+        l = Tree.get_tail(tree)[1]
         l_type = self.get_type(l)
         self.perform_implicit_cast(tree, l, l_type, types_mv.Boolean())
 
-        self.set_type(tree, self.get_type(tree.get_tail()[1]))
+        self.set_type(tree, self.get_type(Tree.get_tail(tree)[1]))
 
     def visit_invert_sign(self, tree):
         self.visit_children(tree)
@@ -462,7 +465,7 @@ class SemanticsVisitor(Visitor):
         self.check_unary_ops_arithmetic(tree, "minus")
         self.cast_unary_ops_arithmetic(tree)
 
-        self.set_type(tree, self.get_type(tree.get_tail()[1]))
+        self.set_type(tree, self.get_type(Tree.get_tail(tree)[1]))
 
     def visit_keep_sign(self, tree):
         self.visit_children(tree)
@@ -470,19 +473,19 @@ class SemanticsVisitor(Visitor):
         self.check_unary_ops_arithmetic(tree, "plus")
         self.cast_unary_ops_arithmetic(tree)
 
-        self.set_type(tree, self.get_type(tree.get_tail()[1]))
+        self.set_type(tree, self.get_type(Tree.get_tail(tree)[1]))
 
     def visit_primary(self, tree):
         self.visit_children(tree)
-        self.set_type(tree, self.get_type(tree.get_tail()[0]))
+        self.set_type(tree, self.get_type(Tree.get_tail(tree)[0]))
 
     def visit_parenthesized(self, tree):
         self.visit_children(tree)
-        self.set_type(tree, self.get_type(tree.get_tail()[1]))
+        self.set_type(tree, self.get_type(Tree.get_tail(tree)[1]))
 
     def visit_atomvalue(self, tree):
         self.visit_children(tree)
-        self.set_type(tree, self.get_type(tree.get_tail()[0]))
+        self.set_type(tree, self.get_type(Tree.get_tail(tree)[0]))
 
     def visit_type_specifier(self, tree):
         self.set_type(tree, types_mv.Type())
@@ -501,29 +504,29 @@ class SemanticsVisitor(Visitor):
 
     # there is no such rule in the grammar, we just avoid code duplicates
     def visit_id(self, tree):
-        name = tree.get_text()
+        name = Tree.get_text(tree)
         #TODO this is set to the function returnvalue, even if we use the function pointer...
         try:
             symbol = self.symbol_table.get(name)
         except KeyError:
             raise RuntimeError("{}:{}:{}: error: '{}' is not declared".format(
-                self.inputfiles[0], tree.startpos['line'],
-                tree.startpos['column'], name))
+                self.inputfiles[0], tree['startpos']['line'],
+                tree['startpos']['column'], name))
         self.set_type(tree, symbol.type)
         self.set_symbol(tree, symbol)
 
     def visit_rvalue(self, tree):
-        if len(tree.get_tail()) > 1:
+        if len(Tree.get_tail(tree)) > 1:
             # Complex: dict_read operation needed
-            child = tree.get_tail()[0]
-            node = tree.get_child("rvalue")
-            expression = tree.get_child("expression")
+            child = Tree.get_tail(tree)[0]
+            node = Tree.get_child(tree, "rvalue")
+            expression = Tree.get_child(tree, "expression")
             operation = "dict_read"
             call_tree = self.func_call(operation, [node, expression], tree)
             self.visit(call_tree)
-            tree.head = call_tree.head
-            tree._tail = call_tree.tail
-            tree.tail = call_tree.tail
+            tree['head'] = call_tree['head']
+            tree['_tail'] = call_tree['tail']
+            tree['tail'] = call_tree['tail']
             self.set_type(tree, self.get_type(node))
         else:
             # Simple
@@ -535,7 +538,7 @@ class SemanticsVisitor(Visitor):
     def visit_func_call(self, tree):
         self.visit_children(tree)
 
-        symbol = self.get_symbol(tree.get_tail()[0])
+        symbol = self.get_symbol(Tree.get_tail(tree)[0])
         self.set_type(tree, symbol.type)
 
         if not symbol.is_func():
@@ -549,18 +552,18 @@ class SemanticsVisitor(Visitor):
             raise RuntimeError(
                 "{}:{}:{}: error: '{}' is a variable of type '{}', not a "
                 "function".format(self.inputfiles[0],
-                                  tree.startpos['line'],
-                                  tree.startpos['column'],
+                                  tree['startpos']['line'],
+                                  tree['startpos']['column'],
                                   symbol.name,
                                   symbol.type))
 
-        expressions = tree.get_children("expression")
+        expressions = Tree.get_children(tree, "expression")
         if len(expressions) != len(symbol.params):
             raise RuntimeError(
                 "{}:{}:{}: error: wrong number of arguments to "
                 "function '{}'".format(self.inputfiles[0],
-                                       tree.startpos['line'],
-                                       tree.startpos['column'],
+                                       tree['startpos']['line'],
+                                       tree['startpos']['column'],
                                        symbol.signature()))
 
         """
@@ -584,9 +587,9 @@ class SemanticsVisitor(Visitor):
         """
 
         if symbol.name == "__input":
-            tree.head = "input"
+            tree['head'] = "input"
         elif symbol.name == "__output":
-            tree.head = "output"
+            tree['head'] = "output"
 
     def visit_input(self, tree):
         pass  # no need to visit it again
@@ -605,7 +608,7 @@ class SemanticsVisitor(Visitor):
 
     def visit_ifelse(self, tree):
         self.visit_children(tree)
-        expressions = tree.get_children("expression")
+        expressions = Tree.get_children(tree, "expression")
         for expression in expressions:
             self.check_predicate(expression)
 
@@ -613,7 +616,7 @@ class SemanticsVisitor(Visitor):
         self.while_counter += 1
         self.visit_children(tree)
         self.while_counter -= 1
-        expression = tree.get_child("expression")
+        expression = Tree.get_child(tree, "expression")
         self.check_predicate(expression)
 
     def visit_block(self, tree):
@@ -627,7 +630,7 @@ class SemanticsVisitor(Visitor):
     def visit_funcdecl(self, tree):
         # here we only visit the body cause the declaration is already done
         # by declare_functions_visitor
-        if tree.get_child('func_body') is not None:
+        if Tree.get_child(tree, 'func_body') is not None:
             self.current_funcdecl = tree
 
             self.symbol_table.open_scope()
@@ -637,11 +640,11 @@ class SemanticsVisitor(Visitor):
             self.current_funcdecl = None
 
     def visit_parameter(self, tree):
-        param_id = tree.get_child("ID")
-        type_spec = tree.get_child("type_specifier")
+        param_id = Tree.get_child(tree, "ID")
+        type_spec = Tree.get_child(tree, "type_specifier")
 
-        param_type = types_mv.string_to_type(type_spec.get_text())
-        param_name = param_id.get_text()
+        param_type = types_mv.string_to_type(Tree.get_text(type_spec))
+        param_name = Tree.get_text(param_id)
 
         symbol = st.Symbol(param_name, param_type, is_global=False)
 
@@ -650,8 +653,8 @@ class SemanticsVisitor(Visitor):
         except Exception:
             raise RuntimeError(
                 "{}:{}:{}: error: redeclaration of '{}'".format(
-                    self.inputfiles[0], tree.startpos['line'],
-                    tree.startpos['column'], param_name))
+                    self.inputfiles[0], tree['startpos']['line'],
+                    tree['startpos']['column'], param_name))
 
         self.set_symbol(tree, symbol)
 
@@ -666,12 +669,12 @@ class SemanticsVisitor(Visitor):
         if self.while_counter == 0:
             raise RuntimeError(
                 "{}:{}:{}: error: break outside of while".format(
-                    self.inputfiles[0], tree.startpos['line'],
-                    tree.startpos['column']))
+                    self.inputfiles[0], tree['startpos']['line'],
+                    tree['startpos']['column']))
 
     def visit_continue(self, tree):
         if self.while_counter == 0:
             raise RuntimeError(
                 "{}:{}:{}: error: continue outside of while".format(
-                    self.inputfiles[0], tree.startpos['line'],
-                    tree.startpos['column']))
+                    self.inputfiles[0], tree['startpos']['line'],
+                    tree['startpos']['column']))

+ 9 - 8
interface/HUTN/hutn_compiler/visitor.py

@@ -1,4 +1,5 @@
 import hutn_compiler.symbol_table as st
+from hutn_compiler.hutnparser import Tree
 
 
 class Visitor(object):
@@ -9,27 +10,27 @@ class Visitor(object):
         return ""
 
     def visit(self, tree):
-        if not tree.head.startswith("implicit_autogenerated_"):
-            val = tree.head
+        if not tree['head'].startswith("implicit_autogenerated_"):
+            val = tree['head']
             visit_val = getattr(self, 'visit_' + val)
             return visit_val(tree)
 
     def visit_children(self, tree):
-        for child in tree.get_tail():
-            if child.head.islower():  # visit only non-terminals
+        for child in Tree.get_tail(tree):
+            if child['head'].islower():  # visit only non-terminals
                 self.visit(child)
 
     def set_type(self, tree, type):
-        tree.type = type
+        tree['type'] = type
 
     def get_type(self, tree):
-        return getattr(tree, 'type', None)
+        return tree.get('type', None)
 
     def set_symbol(self, tree, symbol):
-        tree.symbol = symbol
+        tree['symbol'] = symbol
 
     def get_symbol(self, tree):
-        return getattr(tree, 'symbol', None)
+        return tree.get('symbol', None)
 
     @staticmethod
     def print_tree(tree):