from hutn_compiler.visitor import Visitor from hutn_compiler.hutnparser import Tree class ConstructorsVisitor(Visitor): def __init__(self, args): Visitor.__init__(self, args) self.constructors = [] self.free_id = 0 def dump(self): return [len(self.constructors)] + self.constructors def add_constructors(self, *constructors): self.constructors.extend(constructors) # a visit_* method for each non-terminal in the grammar def visit_start(self, tree): # declare all functions for child in Tree.get_children(tree, "funcdecl"): if self.pre_visit_funcdecl(child): self.add_constructors(True) tail = Tree.get_tail_without(tree, ["newline"]) last = tail[-1] for child in tail[:-1]: 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' 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' self.constructors.pop() if new_constructors_were_added: if last['head'] not in ["return", "break", "continue"]: self.add_constructors(False) elif self.constructors: self.constructors.pop() # pop 'true' self.add_constructors(False) def declare(self, symbol): if symbol['is_global']: symbol['node'] = symbol['name'] self.add_constructors("global", symbol['node']) else: symbol['node'] = str(self.free_id) self.add_constructors("declare", symbol['node']) self.free_id += 1 def visit_assignment(self, tree): self.add_constructors("assign") self.visit(Tree.get_tail(tree)[0]) self.visit(Tree.get_tail(tree)[2]) return True def visit_expression(self, tree): self.visit_children(tree) def visit_binary_operation(self, tree): self.visit_children(tree) def visit_disjunction(self, tree): self.visit_children(tree) def visit_conjunction(self, tree): self.visit_children(tree) def visit_comparison(self, tree): self.visit_children(tree) def visit_relation(self, tree): self.visit_children(tree) def visit_sum(self, tree): self.visit_children(tree) def visit_term(self, tree): self.visit_children(tree) def visit_factor(self, tree): self.visit_children(tree) def visit_primary(self, tree): self.visit_children(tree) def visit_parenthesized(self, tree): self.visit_children(tree) def visit_atomvalue(self, tree): self.visit_children(tree) def visit_type_specifier(self, tree): self.add_constructors("const", {"value": Tree.get_text(tree)}) def visit_actionname(self, tree): self.add_constructors("const", {"value": Tree.get_text(Tree.get_tail(tree)[0])[1:]}) def visit_string(self, tree): 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(tree))) def visit_float(self, tree): self.add_constructors("const", float(Tree.get_text(tree))) def visit_rvalue(self, tree): self.add_constructors("access") self.visit_lvalue(tree) def visit_lvalue(self, tree): symbol = self.get_symbol(tree) # TODO: split visit_funcdecl in pre_visit_funcdecl and visit_funcdecl if symbol['node'] is None: raise Exception("Undefined variable: %s" % (symbol['name'])) self.add_constructors("resolve", symbol['node']) def visit_func_call(self, tree): symbol = self.get_symbol(Tree.get_tail(tree)[0]) self.add_constructors("call") if "pathmv" in symbol: self.add_constructors("deref", symbol['pathmv']) else: 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) self.add_constructors(False) return True def visit_input(self, tree): self.add_constructors("input") return True def visit_output(self, tree): self.add_constructors("output") self.visit(Tree.get_child(tree, "expression")) return True def visit_dictionary(self, tree): pass # TODO: list and dictionary def visit_list(self, tree): pass # TODO: list and dictionary def visit_dict_item(self, tree): pass # TODO: list and dictionary def visit_ifelse(self, tree): self.add_constructors("if") expressions = Tree.get_children(tree, "expression") blocks = Tree.get_children(tree, "block") self.visit(expressions[0]) # condition self.visit(blocks[0]) # then-clause for e, b in zip(expressions[1:], blocks[1:]): self.add_constructors(True, "if") # else-if-clause self.visit(e) self.visit(b) if len(expressions) != len(blocks): self.add_constructors(True) # else-clause self.visit(blocks[-1]) else: self.add_constructors(False) # no else_clause for i in range(len(expressions)-1): self.add_constructors(False) # no next return True def visit_while(self, tree): self.add_constructors("while") 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(tree, ["newline", "indent"]) last = tail[-1] for child in tail[:-1]: if child['head'] in ["return", "break", "continue"]: last = child break else: self.visit(child) if child['head'] == "func_call": # pop 'false' self.constructors.pop() self.add_constructors(True) self.visit(last) if last['head'] == "func_call": # pop 'false' self.constructors.pop() 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(tree, "func_body") symbol = self.get_symbol(tree) symbol['node'] = symbol['name'] return False def visit_funcdecl(self, tree): 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(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(tree, "parameter"): self.visit(p) self.visit(func_body) return True 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_text(Tree.get_child(tree, "ANYTHING")) self.add_constructors("global", symbol['node'], "deref", symbol['pathmv']) # reason: "X function f(Y) = Z" adds no constructors return True else: # Just a declaration, so skip return False def visit_parameter(self, tree): symbol = self.get_symbol(tree) symbol['node'] = str(self.free_id) self.add_constructors(symbol['node']) self.free_id += 1 def visit_continue(self, tree): self.add_constructors("continue") return True def visit_break(self, tree): self.add_constructors("break") return True def visit_return(self, tree): self.add_constructors("return") if len(Tree.get_tail(tree)) > 2: self.add_constructors(True) self.visit(Tree.get_tail(tree)[1]) else: self.add_constructors(False) return True def visit_bool(self, tree): if Tree.get_text(tree) == "True": self.add_constructors("const", True) else: self.add_constructors("const", False) def visit_definition(self, tree): # First declare it symbol = self.get_symbol(tree) self.declare(symbol) if Tree.get_children(tree, "ASSIGN"): # Determine whether it is just a constant, or a deref atom = Tree.get_child(tree, "atomvalue") if Tree.get_child(atom, "deref"): # Deref 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", Tree.get_text(dest)) else: # Constant self.visit(atom) else: self.add_constructors("none") return True