def parse_text( text: Text, name: Text, print_on_error: bool, *, import_cache: ImportCache, additional_search_paths: Tuple[str, ...], filename: Text, fs_open: Callable[[Text], io.IOBase] = None, ) -> Tuple[ast.Module, type_info_mod.TypeInfo]: """Parses text into a module with name "name" and typechecks it.""" logging.vlog(1, 'Parsing text; name: %r', name) module = parser_helpers.parse_text(text, name, print_on_error=print_on_error, filename=filename, fs_open=fs_open) logging.vlog(1, 'Parsed text; name: %r', name) try: type_info = cpp_typecheck.check_module(module, import_cache, additional_search_paths) except (XlsTypeError, TypeInferenceError) as e: if print_on_error: # If the typecheck fails, pretty-print the error. parser_helpers.pprint_positional_error(e, fs_open=fs_open) raise return module, type_info
def _typecheck(self, text: Text, error: Optional[Text] = None, error_type=XlsTypeError): """Checks the first function in "text" for type errors. Args: text: Text to parse. error: Whether it is expected that the text will cause a type error. error_type: Type of error to check for, if "error" is given. """ filename = '/fake/test_module.x' with fakefs_test_util.scoped_fakefs(filename, text): try: m = parser_helpers.parse_text(text, 'test_module', print_on_error=True, filename=filename) except cpp_parser.CppParseError as e: parser_helpers.pprint_positional_error(e) raise if error: with self.assertRaises(error_type) as cm: typecheck.check_module(m, f_import=None) self.assertIn(error, str(cm.exception)) else: try: typecheck.check_module(m, f_import=None) except (span.PositionalError, cpp_parser.CppParseError) as e: parser_helpers.pprint_positional_error(e) raise
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 main(argv): binary = os.path.basename(argv[0]) if len(argv) < 2: raise app.UsageError('Wrong number of command-line arguments; ' 'expect %s <input-file>' % binary) path = argv[1] with open(path, 'r') as f: text = f.read() name = os.path.basename(path) name, _ = os.path.splitext(name) module = parser_helpers.parse_text(text, name, print_on_error=True, filename=path) import_cache = {} f_import = functools.partial(import_routines.do_import, cache=import_cache) try: node_to_type = typecheck.check_module(module, f_import) if FLAGS.entry: print( ir_converter.convert_one_function(module, FLAGS.entry, node_to_type)) else: print(ir_converter.convert_module(module, node_to_type)) except PositionalError as e: parser_helpers.pprint_positional_error(e) if FLAGS.raise_exception: raise else: sys.exit(1)
def _run_typecheck(module: ast.Module, print_on_error: bool, f_import: ImportFn, fs_open: Callable[[Text], io.IOBase]) -> deduce.NodeToType: try: return typecheck.check_module(module, f_import) except XlsTypeError as e: if print_on_error: # If the typecheck fails, pretty-print the error. parser_helpers.pprint_positional_error(e, fs_open=fs_open) raise
def _run_typecheck( module: ast.Module, print_on_error: bool, f_import: ImportFn, fs_open: Callable[[Text], io.IOBase]) -> type_info_mod.TypeInfo: try: return typecheck.check_module(module, f_import) except (XlsTypeError, TypeInferenceError) as e: if print_on_error: # If the typecheck fails, pretty-print the error. parser_helpers.pprint_positional_error(e, fs_open=fs_open) raise
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
def test_generates_valid_functions(self): g = ast_generator.AstGenerator( random.Random(0), ast_generator.AstGeneratorOptions()) for i in range(32): print('Generating sample', i) _, m = g.generate_function_in_module('main', 'test') text = str(m) filename = '/fake/test_sample.x' with fakefs_test_util.scoped_fakefs(filename, text): try: module = parser_helpers.parse_text( text, name='test_sample', print_on_error=True, filename=filename) typecheck.check_module(module, f_import=None) except PositionalError as e: parser_helpers.pprint_positional_error(e) raise
def main(argv): binary = os.path.basename(argv[0]) if len(argv) < 2: raise app.UsageError('Wrong number of command-line arguments; ' 'expect %s <input-file>' % binary) init_xls.init_xls(sys.argv) path = argv[1] with open(path, 'r') as f: text = f.read() name = os.path.basename(path) name, _ = os.path.splitext(name) module = parser_helpers.parse_text(text, name, print_on_error=True, filename=path) importer = import_helpers.Importer(tuple(FLAGS.dslx_path)) type_info = None try: type_info = cpp_typecheck.check_module( module, importer.cache, importer.additional_search_paths) if FLAGS.entry: print( ir_converter.convert_one_function(module, FLAGS.entry, type_info)) else: print(ir_converter.convert_module(module, type_info)) except (PositionalError, cpp_parser.CppParseError) as e: parser_helpers.pprint_positional_error(e) if FLAGS.raise_exception: raise else: sys.exit(1) finally: if type_info is not None: type_info.clear_type_info_refs_for_gc()
def test_pprint_parse_error(self): output = io.StringIO() filename = '/fake/test_file.x' text = 'oh\nwhoops\nI did an\nerror somewhere\nthat is bad' with fakefs_util.scoped_fakefs(filename, text): pos = scanner.Pos(filename, lineno=2, colno=0) span = Span(pos, pos.bump_col()) error = parser.ParseError(span, 'This is bad') parser_helpers.pprint_positional_error(error, output=cast( io.IOBase, output), color=False, error_context_line_count=3) expected = textwrap.dedent("""\ /fake/test_file.x:2-4 0002: whoops * 0003: I did an ^^ This is bad @ /fake/test_file.x:3:1 0004: error somewhere """) self.assertMultiLineEqual(expected, output.getvalue())
def parse_and_test(program: Text, name: Text, *, filename: Text, raise_on_error: bool = True, test_filter: Optional[Text] = None, trace_all: bool = False, compare_jit: bool = True, seed: Optional[int] = None) -> bool: """Parses program and run all tests contained inside. Args: program: The program text to parse. name: Name for the module. filename: The filename from which "program" text originates. raise_on_error: When true, raises exceptions that happen in tests; otherwise, simply returns a boolean to the caller when all test have run. test_filter: Test filter specification (e.g. as passed from bazel test environment). trace_all: Whether or not to trace all expressions. compare_jit: Whether or not to assert equality between interpreted and JIT'd function return values. seed: Seed for QuickCheck random input stimulus. Returns: Whether or not an error occurred during parsing/testing. Raises: ScanError, ParseError: In case of front-end errors. TypeInferenceError, TypeError: In case of type errors. EvaluateError: In case of a runtime failure. """ did_fail = False test_name = None import_cache = import_routines.ImportCache() f_import = functools.partial(import_routines.do_import, cache=import_cache) type_info = None try: module = Parser(Scanner(filename, program), name).parse_module() type_info = typecheck.check_module(module, f_import) ir_package = (ir_converter.convert_module_to_package( module, type_info, traverse_tests=True) if compare_jit else None) interpreter = Interpreter(module, type_info, f_import, trace_all=trace_all, ir_package=ir_package) for test_name in module.get_test_names(): if not _matches(test_name, test_filter): continue print('[ RUN UNITTEST ]', test_name, file=sys.stderr) interpreter.run_test(test_name) print('[ OK ]', test_name, file=sys.stderr) if ir_package and module.get_quickchecks(): if seed is None: # We want to guarantee non-determinism by default. See # https://abseil.io/docs/cpp/guides/random#stability-of-generated-sequences # for rationale. seed = int(os.getpid() * time.time()) print(f'[ SEED: {seed} ]') for quickcheck in module.get_quickchecks(): test_name = quickcheck.f.name.identifier print('[ RUN QUICKCHECK ]', test_name, file=sys.stderr) interpreter.run_quickcheck(quickcheck, seed=seed) print('[ OK ]', test_name, file=sys.stderr) except PositionalError as e: did_fail = True parser_helpers.pprint_positional_error(e) if test_name: print('[ FAILED ]', test_name, e.__class__.__name__, file=sys.stderr) if raise_on_error: raise finally: if type_info is not None: type_info.clear_type_info_refs_for_gc() return did_fail