def __init__(self, node: ast.AstNode, suffix: str = ''): assert isinstance(node, ast.AstNode), repr(node) message = 'Missing type for AST node: {node}{suffix}'.format( node=node, suffix=' :: ' + suffix if suffix else '') # We don't know the real span of the user, we rely on the appropriate caller # to catch the error and populate this field properly. fake_span = Span(Pos('<fake>', 0, 0), Pos('<fake>', 0, 0)) super(TypeMissingError, self).__init__(message, fake_span) self.node = node self.suffix = suffix self.user = None
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 test_stringify(self): # Test without a suffix. t = BitsType(signed=False, size=3) fake_pos = Pos('<fake>', 9, 10) fake_span = Span(fake_pos, fake_pos) e = XlsTypeError(fake_span, t, t) self.assertEndsWith(str(e), '@ <fake>:10:11-10:11')
class ParametricExpressionTest(absltest.TestCase): fake_pos = Pos('<fake>', 0, 0) fake_span = Span(fake_pos, fake_pos) def test_sample_evaluation(self): param_0 = interp_value.Value.make_ubits(32, 0) param_1 = interp_value.Value.make_ubits(32, 1) param_2 = interp_value.Value.make_ubits(32, 2) param_3 = interp_value.Value.make_ubits(32, 3) param_6 = interp_value.Value.make_ubits(32, 6) param_12 = interp_value.Value.make_ubits(32, 12) e = ParametricMul( ParametricConstant(param_3), ParametricAdd(ParametricSymbol('M', self.fake_span), ParametricSymbol('N', self.fake_span))) self.assertEqual(e, e) self.assertEqual('(bits[32]:0x3*(M+N))', str(e)) self.assertEqual(param_6, e.evaluate(dict(N=param_2, M=param_0))) self.assertEqual(param_12, e.evaluate(dict(N=param_1, M=param_3))) self.assertEqual(set(['N', 'M']), e.get_freevars()) def test_non_identity_equality(self): s0 = ParametricSymbol('s', self.fake_span) s1 = ParametricSymbol('s', self.fake_span) self.assertEqual(s0, s1) self.assertEqual(repr(s0), 'ParametricSymbol("s")') add = ParametricAdd(s0, s1) self.assertEqual( repr(add), 'ParametricAdd(ParametricSymbol("s"), ParametricSymbol("s"))')
class ParametricExpressionTest(absltest.TestCase): fake_pos = Pos('<fake>', 0, 0) fake_span = Span(fake_pos, fake_pos) def test_sample_evaluation(self): e = ParametricMul( ParametricConstant(3), ParametricAdd( ParametricSymbol('M', self.fake_span), ParametricSymbol('N', self.fake_span))) self.assertEqual(e, e) self.assertEqual('(3*(M+N))', str(e)) self.assertEqual(6, e.evaluate(dict(N=2, M=0))) self.assertEqual(12, e.evaluate(dict(N=1, M=3))) self.assertEqual(set(['N', 'M']), e.get_freevars()) def test_non_identity_equality(self): s0 = ParametricSymbol('s', self.fake_span) s1 = ParametricSymbol('s', self.fake_span) self.assertEqual(s0, s1) self.assertEqual(repr(s0), 'ParametricSymbol("s")') add = ParametricAdd(s0, s1) self.assertEqual( repr(add), 'ParametricAdd(ParametricSymbol("s"), ParametricSymbol("s"))')
def test_equality(self): fake_pos = Pos('<fake>', 0, 0) fake_span = Span(fake_pos, fake_pos) p = BitsType(signed=False, size=ParametricSymbol('N', fake_span)) c = BitsType(signed=False, size=32) self.assertTrue(p.__ne__(c)) self.assertFalse(p.__eq__(c))
def test_simple_folding(self): fake_pos = Pos('<fake>', 0, 0) fake_span = Span(fake_pos, fake_pos) n = ParametricSymbol('N', fake_span) self.assertEqual(n, 0 + n) self.assertEqual(n, 1 * n) self.assertNotEqual(n, 0 * n)
def test_let_destructure_nested(self): e = self.parse_expression( 'let (w, (x, (y)), z): (u32,(u32,(u32)),u32) = (1, (2, (3,)), 4); y') self.assertIsInstance(e.rhs, ast.XlsTuple) # Three top-level members. self.assertLen(e.rhs.members, 3) # The middle one has two members. self.assertLen(e.rhs.members[1], 2) # The second one of those has one member. self.assertLen(e.rhs.members[1].members[1], 1) self.assertEqual( e.name_def_tree.span, Span(Pos('/fake/fake.x', 0, 4), Pos('/fake/fake.x', 0, 20))) self.assertEqual( e.name_def_tree.tree[1].span, Span(Pos('/fake/fake.x', 0, 8), Pos('/fake/fake.x', 0, 16)))
def test_parse_name_def_tree(self): text = '(a, (b, (c, d), e), f)' fparse = lambda p, b: p._parse_name_def_tree(b) m = ast.Module('test') bindings = parser.Bindings(m) ndt = self._parse_internal(text, bindings, fparse) self.assertIsInstance(ndt, ast.NameDefTree) self.assertLen(ndt.tree, 3) self.assertIsInstance(ndt.tree[0], ast.NameDefTree) self.assertTrue(ndt.tree[0].is_leaf()) self.assertIsInstance(ndt.tree[2], ast.NameDefTree) self.assertTrue(ndt.tree[2].is_leaf()) self.assertEqual( ndt.tree[0].span, Span(Pos(self.fake_filename, 0, 1), Pos(self.fake_filename, 0, 2))) self.assertEqual( ndt.tree[2].span, Span(Pos(self.fake_filename, 0, 20), Pos(self.fake_filename, 0, 21))) self.assertNotEqual(ndt.tree[2].span, ndt.tree[0].span)
def test_sample_evaluation(self): fake_pos = Pos('<fake>', 0, 0) fake_span = Span(fake_pos, fake_pos) e = ParametricMul( ParametricConstant(3), ParametricAdd(ParametricSymbol('M', fake_span), ParametricSymbol('N', fake_span))) self.assertEqual(e, e) self.assertEqual('(3)*((M)+(N))', str(e)) self.assertEqual(6, e.evaluate(dict(N=2, M=0))) self.assertEqual(12, e.evaluate(dict(N=1, M=3))) self.assertEqual(set(['N', 'M']), e.get_freevars())
def test_parse_name_def_tree(self): text = 'let (a, (b, (c, d), e), f) = x; a' m = ast.Module('test') bindings = parser.Bindings() bindings.add('x', ast.BuiltinNameDef(m, 'x')) let = self.parse_expression(text, bindings) self.assertIsInstance(let, ast.Let) ndt = let.name_def_tree self.assertIsInstance(ndt, ast.NameDefTree) self.assertLen(ndt.tree, 3) self.assertIsInstance(ndt.tree[0], ast.NameDefTree) self.assertTrue(ndt.tree[0].is_leaf()) self.assertIsInstance(ndt.tree[2], ast.NameDefTree) self.assertTrue(ndt.tree[2].is_leaf()) self.assertEqual( ndt.tree[0].span, Span(Pos(self.fake_filename, 0, 5), Pos(self.fake_filename, 0, 6))) self.assertEqual( ndt.tree[2].span, Span(Pos(self.fake_filename, 0, 24), Pos(self.fake_filename, 0, 25))) self.assertNotEqual(ndt.tree[2].span, ndt.tree[0].span)
def test_named_tuple_vs_tuple_compatibility(self): u32 = ConcreteType.U32 u8 = ConcreteType.U8 m = ast.Module('test') fake_pos = Pos('fake.x', 0, 0) fake_span = Span(fake_pos, fake_pos) name_def = ast.NameDef(m, fake_span, 'fake') s = ast.Struct(m, fake_span, name_def, (), (), False) named = TupleType((('x', u32), ('y', u8)), struct=s) unnamed = TupleType((u32, u8)) self.assertTrue(named.compatible_with(unnamed)) self.assertNotEqual(named, unnamed) self.assertEqual(named.tuple_names, ('x', 'y'))
def test_import(self): program = """ import thing """ m = ast.Module('test') bindings = parser.Bindings(None) fparse = lambda p, bindings: p.parse_module(bindings) m = self._parse_internal(program, bindings, fparse) self.assertIsInstance(m.top[0], ast.Import) fake_pos = Pos(self.fake_filename, 0, 0) fake_span = Span(fake_pos, fake_pos) self.assertIsInstance(bindings.resolve_node(m, 'thing', fake_span), ast.Import)
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_test_util.scoped_fakefs(filename, text): pos = 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-3:2 0004: error somewhere """) self.assertMultiLineEqual(expected, output.getvalue())
def __init__(self, rng: Random, options: AstGeneratorOptions, codegen_ops_only: bool = True): self.options = options self.rng = rng # Should we only generate ops that can be codegenned? self._codegen_ops_only = codegen_ops_only self.fake_pos = Pos('<fake>', 0, 0) self.fake_span = Span(self.fake_pos, self.fake_pos) self.name_generator = self._name_generator() if options.binop_allowlist: assert all( binop in ast_helpers.BINOP_SAME_TYPE_KIND_LIST for binop in options.binop_allowlist ), 'Contains invalid TokenKinds for same-type binop allowlist: {}'.format( options.binop_allowlist) self._binops = options.binop_allowlist else: self._binops = list(ast_helpers.BINOP_SAME_TYPE_KIND_LIST) if options.disallow_divide: self._binops.remove(ast.BinopKind.DIV) type_kws = set(scanner.TYPE_KEYWORD_STRINGS) - set(['bits', 'uN', 'sN']) if not options.emit_signed_types: type_kws = {kw for kw in type_kws if not kw.startswith('s')} def kw_width(kw): if kw == 'bool': return 1 # Keyword should be of uN or sN form. return int(kw[1:]) type_kws = { kw for kw in type_kws if kw_width(kw) <= self.options.max_width_bits_types } self._kw_identifiers = sorted(list(type_kws)) # Set of functions created during generation. self._functions = [] # Set of types defined during module generation. self._type_defs = [] # Set of constants defined during module generation. self._constants = collections.OrderedDict() # Widths of the aggregate types, indexed by str(TypeAnnotation). self._type_bit_counts = {}
def test_for_freevars(self): program = """for (i, accum): (u32, u32) in range(4) { let new_accum: u32 = accum + i + j; new_accum }(u32:0)""" m = ast.Module('test') b = parser.Bindings(None) b.add('range', ast.BuiltinNameDef(m, 'range')) b.add('j', ast.BuiltinNameDef(m, 'j')) e = self.parse_expression(program, bindings=b) self.assertIsInstance(e, ast.For) self.assertEqual(e.span.start, Pos(self.fake_filename, lineno=0, colno=3)) freevars = e.get_free_variables(e.span.start) self.assertCountEqual(freevars.keys(), ['j', 'range'])
def test_bindings_stack(self): m = ast.Module('test') top = parser.Bindings(None) leaf0 = parser.Bindings(top) leaf1 = parser.Bindings(top) a = ast.BuiltinNameDef(m, 'a') b = ast.BuiltinNameDef(m, 'b') c = ast.BuiltinNameDef(m, 'c') top.add('a', a) leaf0.add('b', b) leaf1.add('c', c) pos = Pos(self.fake_filename, lineno=0, colno=0) span = Span(pos, pos) self.assertEqual(leaf0.resolve(m, 'a', span), a) self.assertEqual(leaf1.resolve(m, 'a', span), a) self.assertEqual(top.resolve(m, 'a', span), a) with self.assertRaises(CppParseError): top.resolve(m, 'b', span) with self.assertRaises(CppParseError): leaf1.resolve(m, 'b', span) with self.assertRaises(CppParseError): leaf0.resolve(m, 'c', span) self.assertEqual(leaf0.resolve(m, 'b', span), b) self.assertEqual(leaf1.resolve(m, 'c', span), c)
def fake_pos(self) -> Pos: return Pos('<fake>', 0, 0)
def make_type_ref_type_annotation( owner: ast.AstNodeOwner, span: Span, type_ref: ast.TypeRef, dims: Tuple[ast.Expr, ...], parametrics: Optional[Tuple[ast.Expr, ...]] = None) -> ast.TypeAnnotation: """Creates a type ref annotation that may be wrapped in array dimensions.""" assert dims is not None, dims elem_type = ast.TypeRefTypeAnnotation(owner, span, type_ref, parametrics) for dim in dims: elem_type = ast.ArrayTypeAnnotation(owner, span, elem_type, dim) return elem_type _FAKE_POS = Pos('<no-file>', 0, 0) _FAKE_SPAN = Span(_FAKE_POS, _FAKE_POS) def get_span_or_fake(n: ast.AstNode) -> Span: return getattr(n, 'span', _FAKE_SPAN) def _get_value_as_int(s: str) -> int: if s in ('true', 'false'): return int(s == 'true') if s.startswith(('0x', '-0x')): return int(s.replace('_', ''), 16) if s.startswith(('0b', '-0b')): return int(s.replace('_', ''), 2) return int(s.replace('_', ''))
def test_stringify_single_member_tuple(self): fake_pos = Pos('<fake>', 0, 0) fake_span = Span(fake_pos, fake_pos) t = ast.XlsTuple(self.m, fake_span, (self.five, )) self.assertEqual('(5,)', str(t))