def chain_comp(self, tree: TypeTree): symbol_to_data = { "<=": "less_equals", ">=": "greater_equals", "<": "less", ">": "greater", } # Separate expressions and comparison operators. expressions = tree.children[::2] comparisons = tree.children[1::2] # Generate comparison trees. comp_trees = [ TypeTree(symbol_to_data[symbol], [a, b], tree.meta) for symbol, a, b in zip(comparisons, expressions[:-1], expressions[1:]) ] # Build up nested tree. prev_tree = comp_trees[0] for comp_tree in comp_trees[1:]: prev_tree = TypeTree("logic_and", [prev_tree, comp_tree], tree.meta) # Override this node with last. tree.data = prev_tree.data tree.children = prev_tree.children self.visit(tree)
def __init__(self, tree: TypeTree, scope: Callable, args=None, stack_trace: Optional[List[TypeTree]] = None): self.scope = scope self.local_scope = dict() self.used = set() self.stack_trace: List[ TypeTree] = [] if stack_trace is None else stack_trace if args is not None: assign(self, self.local_scope, args[0], args[1]) if tree.data != "block": tree.children = [TypeTree(tree.data, tree.children, tree.meta)] tree.data = "block" tree.children[-1] = TypeTree("return_n", [tree.children[-1]], tree.meta) def ret(value_type): if tree.return_type is None: tree.return_type = value_type self.assert_(tree.return_type == value_type, "block has different return types", tree) self.ret = ret self.visit_children(tree) # sets tree.return_type
def math_assign(self, tree: TypeTree): symbol = tree.children[0] expression = tree.children[1] # Make the current node an assign. operation = tree.data.replace("_assign", "") tree.data = "assign" # Make a child node with the math operation. tree.children[1] = TypeTree(operation, [symbol, expression], tree.meta) return self.visit(tree)
def string(self, tree: TypeTree): tree.data = "array" # Evaluate string using Python try: text = eval(tree.children[0]) except SyntaxError as err: raise VolpeError(err.msg, tree, self.stack_trace) self.assert_(is_ascii(text), "strings can only have ascii characters", tree) tree.children = [] for eval_character in text: tree.children.append( TypeTree("character", [Token("CHARACTER", "'" + eval_character + "'")], tree.meta)) self.visit_children(tree) return VolpeArray(char, len(tree.children))
def parse_trees(file_path: str, imports: Dict): if file_path in imports: return with open(file_path) as vlp_file: try: tree = imports[file_path] = volpe_parser.parse(vlp_file.read()) except UnexpectedEOF: raise VolpeError( "unexpected end of input (did you return from main?)") except UnexpectedCharacters as err: # Return cursor to start of file. vlp_file.seek(0) line = vlp_file.readlines()[err.line - 1] symbol = line[err.column - 1] # Print the line. error_message = f"unexpected symbol '{symbol}'" error_message += f"\n{err.line}| {line}" # Add the cursor. padding = " " * (len(str(err.line)) + err.column) error_message += f"\n{padding} ^" raise VolpeError(error_message) for subtree in imports[file_path].iter_subtrees(): subtree.meta.file_path = file_path if subtree.data == "import_": directory = path.dirname(file_path) import_path = path.join( directory, *[child.value for child in subtree.children]) + ".vlp" parse_trees(import_path, imports) obj_tree = TypeTree("object", [], subtree.meta) subtree.data = "func_call" subtree.children = [ TypeTree("func", [obj_tree, imports[import_path]], subtree.meta), obj_tree ] return tree
def volpe_llvm(tree: TypeTree, verbose=False, more_verbose=False, console=False): if more_verbose: print(tree.pretty()) arg_scope = {} def scope(name, local_tree: TypeTree): if name in arg_scope: return arg_scope[name] raise VolpeError(f"variable `{name}` not found", local_tree) AnnotateScope(tree, scope) if verbose: print(tree.pretty()) module = ir.Module("program") module.func_count = itertools.count() run_func = ir.Function( module, ir.FunctionType(unknown, [unwrap(tree.return_type).as_pointer()]), "run") with build_func(run_func) as (b, args): arg_scope = {} def scope(name): return arg_scope[name] def ret(value): b.store(value, args[0], 8) b.ret_void() LLVMScope(b, tree, scope, ret, None) return str(module)
def assign(self, scope: dict, tree: TypeTree, value): if tree.data == "object": self.assert_(isinstance(value, VolpeObject), "can only destructure object", tree) self.assert_( len(tree.children) == len(value.type_dict), "only full deconstruction is allowed", tree) used = set() for i, child in enumerate(tree.children): key, attribute = get_obj_key_value(child, i) self.assert_(key in value.type_dict, f"object doesn't have attribute {key}", child) self.assert_(key not in used, f"{key} has already been used", child) used.add(key) assign(self, scope, attribute, value.type_dict[key]) elif tree.data == "attribute": self.assert_( self.visit(tree) == value, "wrong type in attribute assignment", tree) elif tree.data == "array": self.assert_(isinstance(value, VolpeArray), "can only destructure array", tree) self.assert_(value.count == len(tree.children), "array has wrong length", tree) for child in tree.children: assign(self, scope, child, value.element) elif tree.data == "array_index": self.assert_( self.visit(tree) == value, "wrong type in array assignment", tree) else: self.assert_(tree.data == "symbol", f"cannot assign to {tree.data}", tree) scope[tree.children[0].value] = value tree.return_type = value
def escaped_character(tree: TypeTree): # let Python parse the escaped character (guaranteed ascii by lark) evaluated = eval(f"{tree.children[0]}") return tree.return_type(ord(evaluated))
def character(tree: TypeTree): return tree.return_type(ord(tree.children[0].value[1]))
def integer(tree: TypeTree): return tree.return_type(int(tree.children[0].value))
def func(self, tree: TypeTree): tree.children = [TypeTree("inst", tree.children, tree.meta)] tree.instances = dict() return VolpeClosure(tree=tree, scope=self.get_scope())
def visit(self, tree: TypeTree): tree.return_type = getattr(self, tree.data)(tree) if tree.return_type is None: tree.return_type = int1 return tree.return_type
def if_then(self, tree: TypeTree): tree.data = "implication" tree.children[1] = TypeTree("return_n", [tree.children[1]], tree.meta) return self.visit(tree)
def constant_array_like(self, tree: TypeTree): tree.data = "constant_array" element_type, parent_array = self.visit_children(tree) self.assert_(isinstance(parent_array, VolpeArray), "can only get size of arrays", tree) return VolpeArray(element_type, parent_array.count)