def handle_line(line: str, stmt_index: int): """Runs a single user-provided line as a REPL input.""" fn_name = f'repl_{stmt_index}' module_text = f""" import std fn {fn_name}() -> () {{ {line} }} """ # For error reporting we use a helper that puts this into a fake filesystem # location. def make_fakefs_open(): fs = fake_filesystem.FakeFilesystem() fs.CreateFile(FILENAME, module_text) return fake_filesystem.FakeFileOpen(fs) import_cache = import_routines.ImportCache() while True: try: fake_module = parser.Parser(scanner.Scanner(FILENAME, module_text), fn_name).parse_module() except span.PositionalError as e: parser_helpers.pprint_positional_error(e, fs_open=make_fakefs_open()) return # First attempt at type checking, we expect this may fail the first time # around and we'll substitute the real return type we observe. try: f_import = functools.partial(import_routines.do_import, cache=import_cache) type_info = typecheck.check_module(fake_module, f_import=f_import) except xls_type_error.XlsTypeError as e: # We use nil as a placeholder, and swap it with the type that was expected # and retry once we determine what that should be. if e.rhs_type == concrete_type_mod.ConcreteType.NIL: module_text = module_text.replace(' -> ()', ' -> ' + str(e.lhs_type)) continue # Any other errors are likely real type errors in the code and we should # report them. parser_helpers.pprint_positional_error(e, fs_open=make_fakefs_open()) return # It type checked ok, and we can proceed. break # Interpret the line and print the result. # TODO(leary): 2020-06-20 No let bindings for the moment, just useful for # evaluating expressions -- could put them into the module scope as consts. interpreter = interpreter_mod.Interpreter(fake_module, type_info, f_import=f_import, trace_all=False) result = interpreter.run_function(fn_name, args=()) print(result) return result
def parse_text(text: Text, name: Text, print_on_error: bool, filename: Text, *, fs_open=None) -> ast.Module: """Returns a parsed module from DSL program text. Pretty-prints error information to stderr if an error is encountered, before re-raising. Args: text: The text to parse. name: Name that should be given to the resulting module. print_on_error: Whether to print to stderr when an error occurs -- if false, the error is simply raised and nothing is printed. filename: Filename that "text" orginates from. fs_open: Lets the user substitute their own filesystem open; e.g. if the program is not materialized on the real filesystem. Raises: CppParseError: When a parsing error occurs. ScanError: When a scanning error occurs. """ try: s = scanner.Scanner(filename, text) return parser.Parser(s, name).parse_module() except (parser.CppParseError, scanner.ScanError) as e: if print_on_error: pprint_positional_error(e, fs_open=fs_open) raise
def test_parse_error_get_span(self): s = scanner.Scanner(self.fake_filename, '+') p = parser.Parser(s, 'test_module') try: p.parse_expression(None) except parser.CppParseError as e: pos = Pos(self.fake_filename, 0, 0) want = Span(pos, pos.bump_col()) self.assertEqual(parser.get_parse_error_span(str(e)), want) else: raise AssertionError
def _parse_internal( self, program: Text, bindings: Optional[parser.Bindings], fparse: Callable[[parser.Parser, parser.Bindings], TypeVar('T')] ) -> TypeVar('T'): with fakefs_test_util.scoped_fakefs(self.fake_filename, program): s = scanner.Scanner(self.fake_filename, program) b = bindings or parser.Bindings(None) try: e = fparse(parser.Parser(s, 'test_module'), b) except parser.CppParseError as e: parser_helpers.pprint_positional_error(e) raise self.assertTrue(s.at_eof()) return e