def parseTopLevel(mod, source, forJIT=False): # Classify the block try: blockHead = lexer.lex(source.peek(), False) except Exception: # Clear the bad line from the buffer source.getLine() raise if blockHead[0].name == 'def': # Determine function name and return type blockHead = lexer.lex(source.getLine(), mod.debugLexer) dtype = dtypes.getType(blockHead[1].data) funcName = blockHead[2].data[0] args = [[dtypes.getType(i.data), j.data] for i, j in ast.splitArguments(blockHead[2].data[1])] if funcName in mod.userFunctions.keys(): raise ValueError( "ERROR: Function {} is already defined.".format(funcName)) # Handle function arguments mod.body += [ "define {} @{}({})".format( dtype.irname, funcName, ",".join( [i.irname + " %arg_" + j for i, j in args])) + "{" ] mod.body += ["entry:"] for argType, argName in args: mem = mod.newVariable(argName, argType) mod.body += [ "store {} {}, {}* {}".format(argType.irname, "%arg_" + argName, mem.irname, mem.addr) ] # Read in the function body output = None if source.end(1): raise ValueError( "ERROR: Expected a block (maybe you forgot to indent?)") while not source.end(1): result = parseBlock(mod, source, 1) if result is not None: output = result # Check that the return type is correct and end the function definition if output is None or (dtype.name != output.name): raise ValueError( "ERROR: Return type {} does not match declaration {}.".format( output.name, dtype.name)) mod.userFunctions[funcName] = (dtype, args) mod.alreadyDeclared.append(funcName) mod.endScope() mod.body += ["ret {} {}".format(output.irname, output.addr)] mod.body += ["}"] else: # Top-level statement mod.isGlobal = forJIT mod.out = mod.main mod.lastOutput = parseBlock(mod, source, 0, forJIT) mod.out = mod.body mod.isGlobal = False return
def resolveTyping(self, name=None, catalog=builtins.catalog): if name is None: name = self.token.name # Handle untyped options if name in catalog.keys() and type(catalog[name]) is not dict: self.evaluator = catalog[name] return argTypes = [i.dtype for i in self.children] best, bestArgs, cost = None, [], 9999999 for candidate in catalog[name]: candidateCost = 0 candidateArgs = candidate.split(' ') if len(candidateArgs) != len(argTypes): continue try: for a, ca in zip(argTypes, candidateArgs): candidateCost += a.casting.index(ca) except ValueError: continue if candidateCost < cost: best, bestArgs, cost = candidate, candidateArgs, candidateCost elif candidateCost == cost: raise ValueError( "ERROR: Tie for overload resolution of token '{}' with types {}" .format(self.token.name, argTypes)) if best is None: raise ValueError( "No valid candidates for token '{}' with types {}".format( name, [i.name for i in argTypes])) # We've found the best candidate, record findings to self self.evaluator, self.dtype = catalog[name][best] self.children = [ i.castTo(dtypes.getType(j)) for i, j in zip(self.children, bestArgs) ] return
def __init__(self, tokens, module): # Keep reference to parent module self.module = module self.children = [] self.dtype = None if tokens is None: return # Find the token of lowest precedence index, self.token = min(enumerate(tokens[::-1]), key=lambda t: t[1].precedence) index = len(tokens) - index - 1 leftTokens, rightTokens = tokens[:index], tokens[index + 1:] if module.debugAST: sys.stderr.write("AST: " + self.token.name + ', ' + str(self.token.data) + "\n") # Now, construct the node according to the operator found if self.token.name == "print": assertEmpty(leftTokens) self.children = [ASTNode(rightTokens, self.module)] elif self.token.name in [ '=', '+=', '-=', '/=', '//=', '**=', '*=', '%=' ]: # Assignment operator and variants if self.token.name != '=': # Handle extra operation if type(self.token.data) is list: left = self.token.data else: left = [lexer.NameToken('name', self.token.data)] # TODO: replace this ugly hack with a proper builtin function TokenType = { '+=': lexer.BinaryPlusToken, '-=': lexer.BinaryMinusToken, '/=': lexer.MultiplyFamilyToken, '//=': lexer.MultiplyFamilyToken, '**=': lexer.ExponentToken, '*=': lexer.MultiplyFamilyToken, '%=': lexer.MultiplyFamilyToken }[self.token.name] rightTokens = left + [ TokenType(self.token.name[:-1]), lexer.ParensToken('()', rightTokens) ] self.token.name = '=' if type(self.token.data) is list: # Array indexing assignment self.children = [ ASTNode(self.token.data[:-1], self.module), ASTNode(self.token.data[-1].data, self.module).castTo(dtypes.Int), ASTNode(rightTokens, self.module) ] self.children[-1] = self.children[-1].castTo( self.children[0].dtype.subtype) self.token.name = "index=" else: # Regular old variable assignment self.children = [ASTNode(rightTokens, self.module)] elif self.token.name in [ 'and', 'or', 'xor', '-', '+', '%', '*', '//', '/', '**', '<=', '>=', '<', '>', '!=', '==' ]: # Binary operators! self.children = [ ASTNode(t, self.module) for t in [leftTokens, rightTokens] ] elif self.token.name in ["unary +", "unary -"]: assertEmpty(leftTokens) self.children = [ASTNode(rightTokens, self.module)] self.dtype = self.children[0].dtype elif self.token.name == "literal": assertEmpty(leftTokens, rightTokens) self.dtype = self.token.data[0] elif self.token.name == "function": assertEmpty(leftTokens, rightTokens) caller, data = self.token.data if caller in builtins.catalog.keys(): # Known builtin disguised as a function self.children = [ ASTNode(t, self.module) for t in splitArguments(data) ] self.dtype = self.children[0].dtype self.resolveTyping(caller, builtins.catalog) return elif caller in self.module.userFunctions.keys(): # Known, user-defined function self.dtype, args = self.module.userFunctions[caller] self.children = [ ASTNode(t, self.module).castTo(a[0]) for t, a in zip(splitArguments(data), args) ] elif caller in dtypes.baseTypes: # Explicit type cast self.token.name = "()" self.children = [ ASTNode(data, self.module).castTo(dtypes.getType(caller), True) ] self.dtype = self.children[0].dtype else: # Unknown function, assume it's declared somewhere else self.children = [ASTNode(self.token.data[1], self.module)] self.dtype = self.children[0].dtype self.token.data = [caller, self.dtype] elif self.token.name == '()': assertEmpty(leftTokens, rightTokens) self.token.name = "()" self.children = [ASTNode(self.token.data, self.module)] self.dtype = self.children[0].dtype elif self.token.name == "name": assertEmpty(leftTokens, rightTokens) self.dtype = type(self.module.getVariable(self.token.data, True)) elif self.token.name == "indexing": assertEmpty(rightTokens) self.children = [ ASTNode(i, self.module) for i in [leftTokens, self.token.data] ] elif self.token.name == "array": assertEmpty(leftTokens, rightTokens) self.children = [ASTNode(self.token.data[1], self.module)] elif self.token.name == "literalArray": self.children = [ ASTNode(t, self.module) for t in splitArguments(self.token.data) ] self.dtype = self.children[0].dtype self.children = [i.castTo(self.dtype) for i in self.children] self.dtype = dtypes.Array(self.dtype) else: raise ValueError("ERROR: Can't parse:'{}'.\n".format( self.token.name)) # Resolve the types given the child types and available builtins. self.resolveTyping() return
def createArray(inputs, token, mod): dtype = dtypes.Array(dtypes.getType(token.data[0])) addr, allocID = mod.allocate(dtype.subtype, inputs[0].addr) result = dtype(addr) result.allocID = allocID return result
# For type-dependent builtins, it also says the accepted and return types. catalog = { # Untyped builtins 'function': callFunction, 'literal': literal, 'name': name, "()": parentheses, 'print': printStatement, 'literalArray': literalArray, '=': assignment, 'index=': indexingAssignment, 'free': freeMemory, 'array': createArray, # Typed name:{"Arg1Type Args2Type":[function,retType]} 'indexing': { "Array:{} Int".format(i): [indexArray, dtypes.getType(i)] for i in ["Real", "Int"] }, 'unary -': {i: [unaryPlusMinus, dtypes.getType(i)] for i in ['Real', 'Int']}, 'unary +': {i: [unaryPlusMinus, dtypes.getType(i)] for i in ['Real', 'Int']}, '**': { "Real Real": [power, dtypes.Real] }, '/': { "Real Real": [simpleBinary, dtypes.Real] }, '//': {