def scan_token(self): c = self.advance() if c == "(": self.add_token(TokenType.LEFT_PAREN) elif c == ")": self.add_token(TokenType.RIGHT_PAREN) elif c == "{": self.add_token(TokenType.LEFT_BRACE) elif c == "}": self.add_token(TokenType.RIGHT_BRACE) elif c == ",": self.add_token(TokenType.COMMA) elif c == ".": self.add_token(TokenType.DOT) elif c == "-": self.add_token(TokenType.MINUS) elif c == "+": self.add_token(TokenType.PLUS) elif c == ";": self.add_token(TokenType.SEMICOLON) elif c == "*": self.add_token(TokenType.STAR) elif c == "!": self.add_token(TokenType.BANG_EQUAL if self.match("=") else TokenType.BANG) elif c == "=": self.add_token( TokenType.EQUAL_EQUAL if self.match("=") else TokenType.EQUAL ) elif c == "<": self.add_token(TokenType.LESS_EQUAL if self.match("=") else TokenType.LESS) elif c == ">": self.add_token( TokenType.GREATER_EQUAL if self.match("=") else TokenType.GREATER ) elif c == "/": if self.match("/"): # a comment, consume till \n while not self.at_end() and self.peek() != "\n": self.advance() else: self.add_token(TokenType.SLASH) elif c == " ": pass elif c == "\r": pass elif c == "\t": pass elif c == "\n": self.line += 1 elif c == '"': self.string() elif c.isdigit(): self.number() elif self.isalpha(c): self.identifier() else: Lox.error(self.line, f"Unexpected character '{c}'.")
def string(self): while not self.at_end() and self.peek() != '"': if self.peek() == "\n": # multi line comments self.line += 1 self.advance() if self.at_end(): Lox.error(line, "Unterminated string.") return self.advance() # final " literal = self.source[self.start + 1 : self.current - 1] self.add_token(TokenType.STRING, literal)
def run_prompt(iptr: lox.Lox, parser: argparse.ArgumentParser): """Creates a prompt (REPL) for executing code. Args: iptr (lox.Lox): Interpreter to pass code into. parser (ArgumentParser): ArgumentParser of the executable, used to output exec-level errors (e.g. file not found). """ print("pylox version alpha") while True: try: line = input(">>> ") iptr.run(line) except KeyboardInterrupt: sys.exit(0)
def run_file(filename: str, iptr: lox.Lox, parser: argparse.ArgumentParser): """Executes code from a file. Args: filename (str): Filename of code to execute. iptr (Lox): Interpreter to pass code into. parser (ArgumentParser): ArgumentParser of the executable, used to output exec-level errors (e.g. file not found). """ try: with open(filename) as source: iptr.run(source.read()) if iptr.error: sys.exit(2) except FileNotFoundError: parser.error("File not found: {0}".format(filename))
def run_test(test, verbose=True): with open(test, "r", encoding="utf-8") as f: source = f.read() # get expected output if exists expected = "\n".join([ line.split("// expect: ")[1] for line in source.split("\n") if "// expect:" in line ]) # use this to change acceptance conditions expected_type = OUTPUT # get token error if exists if not expected: for line in source.split("\n"): search = TOKEN_REGEX.search(line) if search: expected_type = TOKEN_ERROR expected += search.group(0).strip() + "\n" # get runtime error if not expected: for line in source.split("\n"): search = RUNTIME_REGEX.search(line) if search: expected_type = RUNTIME_ERROR expected += search.group(0).strip() + "\n" # actually run the thing captured_stdout = io.StringIO() with redirect_stdout(captured_stdout): Lox(test=True).run(source=source) # process + compare output to expected actual = captured_stdout.getvalue()[:-1] if expected_type == TOKEN_ERROR: actual = "\n".join([ TOKEN_REGEX.search(line).group(0).strip() for line in actual.split("\n") ]) if expected_type == RUNTIME_ERROR: actual = "\n".join( [actual.split("[")[0].strip() for line in actual.split("\n")]) if actual.strip() == expected.strip(): return 1 else: print(f"{test} failed") if verbose: print(f"Expected:\n{expected}\n") print(f"Actual:\n{actual}\n\n") return 0
"""Run the interpreter""" from lox import Lox if __name__ == "__main__": Lox()
def _error(self, token, msg): from lox import Lox Lox.error_token(token, msg) return ParseError()
import argparse from lox import Lox parser = argparse.ArgumentParser() parser.add_argument('path', nargs='?', default=None) args = parser.parse_args() lox = Lox() if args.path is not None: lox.run_file(args.path) else: lox.run_prompt()
def error(self, token, msg): Lox.parsing_error(token, msg) return ParseError()
def interpret(self, statements): try: for statement in statements: self.execute(statement) except RunTimeError as ex: Lox.runtime_error(ex)
def _Lox_error(self, message): from lox import Lox Lox.error_line(self.line, message)