Example #1
0
  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
Example #2
0
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
Example #3
0
 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
Example #4
0
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)
Example #5
0
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()
Example #6
0
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
Example #7
0
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
Example #8
0
 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)