def run(self, source, errors): #print("source : \n{}".format(source)) tokens = [] statements = [] scan_tokens(source, { 'start': 0, 'current': 0, 'line': 1 }, tokens, errors) if len(tokens) is 1: if tokens[0].type is Tk.EOF: LoxError.error(tokens[0], "Source file is empty.") parser = Parser(tokens) # print("Tokens:\n") # for t in tokens: # print(t) #print("Lox: ready to parse") statements = parser.parse() #print("Lox: ready to resolve") resolver = Resolver(self.interpreter) resolver.resolvelist(statements) if LoxError.haderror: print("Syntax errors detected during compilation") return #print("Lox: ready to interpret") self.interpreter.interpret(statements)
def visitclass(self, var_class): self.declare(var_class.name) self.define(var_class.name) if var_class.superclass is not None: if var_class.superclass.name.lexeme is var_class.name.lexeme: LoxError.error( var_class.name, "Class cannot have the same name as super class.") self.resolve(var_class.superclass) previous_class = self.current_class self.current_class = ClassType.CLASS # add the scope for the super keyword if var_class.superclass is not None: self.current_class = ClassType.SUBCLASS self.beginscope() self.scopes[-1][Tk.lexeme_from_type[Tk.SUPER]] = True # Define the 'this' self.beginscope() self.scopes[-1][Tk.lexeme_from_type[Tk.THIS]] = True for method in var_class.methods: self.resolve(method) self.endscope() if var_class.superclass is not None: self.endscope() self.current_class = previous_class
def visitreturn(self, var_return): if self.current_function == FunctionType.NONE: LoxError.error(var_return.keyword, "Cannot return from top-level code.") if var_return.value is not None: if self.current_function is FunctionType.INIT: LoxError.error(var_return.keyword, "Cannot return value from an initializer.") self.resolve(var_return.value)
def visitsuper(self, var_super): if self.current_class is ClassType.NONE: LoxError.error(var_super.keyword, "Cannot use 'super' outside of a class.") if self.current_class is ClassType.CLASS: LoxError.error( var_super.keyword, "Cannot use 'super' in a class with no superclass.") self.resolvelocal(var_super, var_super.keyword)
def visitvariable(self, variable): """Resolve Variable expression. Do not allow the variable to initialize with itself.""" if self.scopes and variable.name.lexeme in self.scopes[-1]: if self.scopes[-1][variable.name.lexeme] == False: LoxError.error( variable.name, "Cannot use local variable in its own initializer.") self.resolvelocal(variable, variable.name)
def finishcall(self, callee: Expr) -> Expr: arguments = [] if not self.check(Tk.RIGHT_PAREN): while "let's parse arguments as long as we have commas": arguments.append(self.expression()) if not self.match(Tk.COMMA): break if len(arguments) > LoxConstant.max_param: LoxError.error(self.peek(), "function cannot have more than 8 arguments") call_left_paren = self.consume( Tk.RIGHT_PAREN, "expect a ')' at the end of a function call.") return Call(callee, call_left_paren, arguments)
def string(source, positions): """Scan strings.""" while not is_at_end(source, positions) and peek(source, positions) != '"': if peek(source, positions) == '\n': positions['line'] += 1 advance(source, positions) if is_at_end(source, positions): LoxError.error(positions['line'], "String is not ended with quotes.") return None # Closing quote else: advance(source, positions) # return string without quotes return source[positions['start'] + 1:positions['current'] - 1]
def declare(self, name: LoxToken): """Declare a variable in the current scope and mark it non initialized.""" if not self.scopes: return # we retrieve the in-work scope scope = self.scopes[-1] # If the variable is already there, send an error if name.lexeme in scope: LoxError.error( name, "A variable with this name has already been declared in the same scope." ) return # variable is marked False as it is not initialized yet scope[name.lexeme] = False
def functionbody(self, kind: FunctionType): funcid = None functiontype = kind if kind is FunctionType.FUNCTION: funcid = self.consume(Tk.IDENTIFIER, "Expect a function name after 'fun'.") self.consume(Tk.LEFT_PAREN, "expect a '(' after function name.") elif kind is FunctionType.LAMBDA: self.consume(Tk.LEFT_PAREN, "expect a '(' after 'fun' for lambda.") elif kind is FunctionType.METHOD: funcid = self.consume(Tk.IDENTIFIER, "Expect a method name.") if funcid is LoxConstant.init_method: functiontype = FunctionType.INIT self.consume(Tk.LEFT_PAREN, "expect a '(' after method name.") else: raise ParserError(self.previous(), "Unexpected function type in parser.") parameters = [] # All parameters should be identifiers if not self.check(Tk.RIGHT_PAREN): while "we have comma after the parameter": parameters.append( self.consume(Tk.IDENTIFIER, "expect identifiers as function parameters.")) if len(parameters) > LoxConstant.max_param: LoxError.error( self.previous(), "function can take at most " + LoxConstant.max_param + " parameters.") if not self.match(Tk.COMMA): break self.consume(Tk.RIGHT_PAREN, "expect a ) at the end of the function parameters.") # Parse the body of the function self.consume(Tk.LEFT_BRACE, "expect a { after function parameters list.") body = self.blockstatement() return FunctionExp(funcid, parameters, body, functiontype)
def visitthis(self, this): if self.current_class is ClassType.NONE: LoxError.error(this.keyword, "Cannot use 'this' outside of a class.") self.resolvelocal(this, this.keyword)
def __init__(self): self.interpreter = Interpreter() self.error = LoxError()