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 import_cache = ImportCache() additional_search_paths = () if error: with self.assertRaises(error_type) as cm: cpp_typecheck.check_module(m, import_cache, additional_search_paths) self.assertIn(error, str(cm.exception)) else: try: cpp_typecheck.check_module(m, import_cache, additional_search_paths) except (span.PositionalError, cpp_parser.CppParseError) as e: parser_helpers.pprint_positional_error(e) raise
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 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' import_cache = ImportCache() additional_search_paths = () with fakefs_test_util.scoped_fakefs(filename, text): try: module = parser_helpers.parse_text( text, name='test_sample', print_on_error=True, filename=filename) cpp_typecheck.check_module(module, import_cache, additional_search_paths) except PositionalError as e: parser_helpers.pprint_positional_error(e) raise
def sign_convert_args_batch(f: ast.Function, m: ast.Module, args_batch: sample.ArgsBatch) -> sample.ArgsBatch: """Sign-converts ArgsBatch to match the signedness of function arguments.""" f = m.get_function('main') import_cache = ImportCache() additional_search_paths = () type_info = cpp_typecheck.check_module(m, import_cache, additional_search_paths) arg_types = tuple(type_info.get_type(p.type_) for p in f.params) converted_batch = [] for args in args_batch: assert len(arg_types) == len(args) converted_batch.append( tuple(sign_convert_value(t, a) for t, a in zip(arg_types, args))) return tuple(converted_batch)
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 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) importer = import_helpers.Importer() 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: type_info = cpp_typecheck.check_module( fake_module, importer.cache, importer.additional_search_paths) except 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, importer.typecheck, import_cache=importer.cache, additional_search_paths=(), trace_all=False) result = interpreter.run_function(fn_name, args=()) print(result) type_info.clear_type_info_refs_for_gc() return result
def parse_and_test(program: Text, name: Text, *, filename: Text, additional_search_paths: Tuple[str, ...] = (), 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. additional_search_paths: Additional paths at which we search for imported module files. 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 type_info = None importer = import_helpers.Importer(additional_search_paths) ran = 0 try: module = Parser(Scanner(filename, program), name).parse_module() type_info = cpp_typecheck.check_module( module, importer.cache, importer.additional_search_paths) ir_package = (ir_converter.convert_module_to_package( module, type_info, traverse_tests=True) if compare_jit else None) interpreter = Interpreter(module, type_info, importer.typecheck, importer.additional_search_paths, importer.cache, trace_all=trace_all, ir_package=ir_package) for test_name in module.get_test_names(): if not _matches(test_name, test_filter): continue ran += 1 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) quickcheck_helpers.run_quickcheck(interpreter, ir_package, quickcheck, seed=seed) print('[ OK ]', test_name, file=sys.stderr) except (PositionalError, FailureError, CppParseError, ScanError, TypeInferenceError, XlsTypeError) as e: did_fail = True parser_helpers.pprint_positional_error(e, output=cast( io.IOBase, sys.stderr)) 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() print('[==================]', ran, 'test(s) ran.', file=sys.stderr) return did_fail
def typecheck(self, module: ast.Module) -> type_info_mod.TypeInfo: """Typechecks 'module' (using this Importer's cache and search paths).""" return cpp_typecheck.check_module(module, self.cache, self.additional_search_paths)