import cPickle as pickle from visitor import Visitor class ConstructorsVisitor(Visitor): def __init__(self, args): Visitor.__init__(self, args) self.constructors = [] self.free_id = 0 def dump(self): return self.constructors # return pickle.dumps(self.constructors, pickle.HIGHEST_PROTOCOL) # return '\n'.join([repr(x) for x in 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("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"]) last = tail[-1] for child in tail[:-1]: if child.head == "return": 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 != "return": 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 = '"%s"' % symbol.name self.add_constructors('"global"', symbol.node) else: symbol.node = self.free_id self.add_constructors('"declare"', self.free_id) self.free_id += 1 def visit_vardecl(self, tree): symbol = self.get_symbol(tree) self.declare(symbol) return True def visit_assignment(self, tree): self.add_constructors('"assign"') self.visit(tree.get_tail()[0]) self.visit(tree.get_tail()[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": "%s"}' % (tree.get_text())) def visit_actionname(self, tree): self.add_constructors('"const"', '{"value": "%s"}' % (tree.get_tail()[1].get_text())) def visit_string(self, tree): self.visit_literal(tree) # there is no such rule in the grammar, we just avoid code duplicates def visit_literal(self, tree): self.add_constructors('"const"', tree.get_text()) def visit_integer(self, tree): self.visit_literal(tree) def visit_float(self, tree): self.visit_literal(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()[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.add_constructors(str(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("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("expression") blocks = tree.get_children("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("expression")) self.visit(tree.get_child("block")) return True def visit_block(self, tree): tail = tree.get_tail_without(["newline", "indent"]) last = tail[-1] for child in tail[:-1]: if child.head == "return": 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 != "return": 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") symbol = self.get_symbol(tree) symbol.node = '"%s"' % symbol.name return False def visit_funcdecl(self, tree): func_body = tree.get_child("func_body") symbol = self.get_symbol(tree) if func_body: if symbol.name in ["input", "output"]: return False self.add_constructors('"funcdef"', symbol.node, str(len(symbol.params))) for p in tree.get_children("parameter"): self.visit(p) self.visit(func_body) return True elif tree.get_child("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 = tree.get_child("ANYTHING").get_text() # pathmv is needed in visit_func_call(self, tree) symbol.pathmv = '"{}"'.format(pathmv) 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): self.add_constructors(self.free_id) symbol = self.get_symbol(tree) symbol.node = self.free_id self.free_id += 1 def visit_return(self, tree): self.add_constructors('"return"') if len(tree.get_tail()) > 1: self.add_constructors('true') self.visit(tree.get_tail()[1]) else: self.add_constructors('false') return True def visit_bool(self, tree): if tree.get_text() == "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) symbol.node = '"%s"' % symbol.name # Now generate constructors self.add_constructors('"global"', '"%s"' % symbol.name) # Determine whether it is just a constant, or a deref atom = tree.get_child("atomvalue") if atom.get_child("deref"): # Deref dest = atom.get_child("deref").get_child("ANYTHING") if dest is None: # Just an empty questionmark! self.add_constructors('"empty"') else: self.add_constructors('"deref"', '"%s"' % dest.get_text()) else: # Constant self.visit(atom) return True