def get_type_instance(cls, ty): """ Maps a type class from the hub SDK to its corresponding TypeClass in the compiler. """ assert isinstance(ty, OutputBase), ty if isinstance(ty, OutputBoolean): return BooleanType.instance() if isinstance(ty, OutputInt): return IntType.instance() if isinstance(ty, OutputFloat): return FloatType.instance() if isinstance(ty, OutputString): return StringType.instance() if isinstance(ty, OutputAny): return AnyType.instance() if isinstance(ty, OutputObject): return ObjectType({ k: cls.get_type_instance(v) for k, v in ty.properties().items() }) if isinstance(ty, OutputList): return ListType(cls.get_type_instance(ty.elements()), ) if isinstance(ty, OutputNone): return NoneType.instance() if isinstance(ty, OutputRegex): return RegExpType.instance() if isinstance(ty, OutputEnum): return StringType.instance() assert isinstance(ty, OutputMap), f"Unknown Hub Type: {ty!r}" return MapType( cls.get_type_instance(ty.keys()), cls.get_type_instance(ty.values()), )
def type_to_tree(tree, t): """ Converts a type to its respective AST Tree representation. """ if isinstance(t, ListType): inner = SymbolExpressionVisitor.type_to_tree(tree, t.inner) return Tree('list_type', [Tree('types', [inner])]) if isinstance(t, MapType): key = SymbolExpressionVisitor.type_to_tree(tree, t.key) value = SymbolExpressionVisitor.type_to_tree(tree, t.value) return Tree('map_type', [ key, Tree('types', [value]), ]) if t == BooleanType.instance(): base_type = tree.create_token('BOOLEAN_TYPE', 'boolean') elif t == IntType.instance(): base_type = tree.create_token('INTEGER_TYPE', 'int') elif t == FloatType.instance(): base_type = tree.create_token('FLOAT_TYPE', 'float') elif t == StringType.instance(): base_type = tree.create_token('STRING_TYPE', 'string') elif t == TimeType.instance(): base_type = tree.create_token('TIME_TYPE', 'time') elif t == RegExpType.instance(): base_type = tree.create_token('REGEXP_TYPE', 'regex') else: assert t == AnyType.instance() base_type = tree.create_token('ANY_TYPE', 'any') return Tree('base_type', [base_type])
def names(self, tree): """ Extracts names from a path tree """ assert tree.data == 'path' main_name = tree.child(0).value names = [NamedPath(main_name, main_name, IndexKind.FIRST)] for fragment in tree.children[1:]: child = fragment.child(0) kind = IndexKind.INDEX if isinstance(child, Tree): if child.data == 'string': type_ = StringType.instance() value = child.child(0).value elif child.data == 'boolean': type_ = BooleanType.instance() value = child.child(0).value elif child.data == 'range': type_ = RangeType.instance() value = 'range' elif child.data == 'number': type_ = self.number(child) value = child.child(0).value else: assert child.data == 'path' type_ = self.path(child) value = child.child(0).value else: assert child.type == 'NAME' kind = IndexKind.DOT value = child.value type_ = StringType.instance() names.append(NamedPath(value, type_, kind)) return names
def names(self, tree): """ Extracts names from a path tree """ assert tree.data == 'path' main_name = tree.child(0).value names = [(main_name, tree.child(0).value)] for fragment in tree.children[1:]: child = fragment.child(0) name = None if isinstance(child, Tree): if child.data == 'string': value = StringType.instance() elif child.data == 'boolean': value = BooleanType.instance() else: assert child.data == 'path' value = self.path(child) else: name = child.value if child.type == 'INT': value = IntType.instance() else: assert child.type == 'NAME' value = StringType.instance() names.append((name, value)) return names
def ensure_boolean_expression(self, tree, expr): """ Ensures that the expression resolves to a boolean. """ t = self.resolver.base_expression(expr).type() expr.expect(t == BooleanType.instance(), 'if_expression_boolean', type=t)
def base_type(self, tree): """ Resolves a base type expression to a type """ assert tree.data == 'base_type' tok = tree.first_child() if tok.type == 'BOOLEAN_TYPE': return base_symbol(BooleanType.instance()) elif tok.type == 'INT_TYPE': return base_symbol(IntType.instance()) elif tok.type == 'FLOAT_TYPE': return base_symbol(FloatType.instance()) elif tok.type == 'STRING_TYPE': return base_symbol(StringType.instance()) elif tok.type == 'ANY_TYPE': return base_symbol(AnyType.instance()) elif tok.type == 'OBJECT_TYPE': return base_symbol(ObjectType.instance()) elif tok.type == 'FUNCTION_TYPE': return base_symbol(AnyType.instance()) elif tok.type == 'TIME_TYPE': return base_symbol(TimeType.instance()) else: assert tok.type == 'REGEXP_TYPE' return base_symbol(RegExpType.instance())
def test_type_to_tree_map(magic, patch): tree = magic() mt = MapType(IntType.instance(), BooleanType.instance()) type_to_tree = SymbolExpressionVisitor.type_to_tree patch.object(SymbolExpressionVisitor, "type_to_tree") se = type_to_tree(tree, mt) assert SymbolExpressionVisitor.type_to_tree.call_args_list == [ call(tree, IntType.instance()), call(tree, BooleanType.instance()), ] assert se == Tree( "map_type", [ SymbolExpressionVisitor.type_to_tree(), Tree("types", [SymbolExpressionVisitor.type_to_tree()]), ], )
def nary_expression(self, tree, op, values): if self.op_returns_boolean(op.type): assert len(values) <= 2 # e.g. a < b if self.is_cmp(op.type): tree.expect(values[0].cmp(values[1]), 'type_operation_cmp_incompatible', left=values[0], right=values[1]) return BooleanType.instance() # e.g. a == b if self.is_equal(op.type): tree.expect(values[0].equal(values[1]), 'type_operation_equal_incompatible', left=values[0], right=values[1]) return BooleanType.instance() # e.g. a and b, a or b, !a tree.expect(values[0].has_boolean(), 'type_operation_boolean_incompatible', val=values[0]) if len(values) == 2: tree.expect(values[1].has_boolean(), 'type_operation_boolean_incompatible', val=values[1]) return BooleanType.instance() is_arithmetic = self.is_arithmetic_operator(op.type) tree.expect(is_arithmetic, 'compiler_error_no_operator', operator=op.type) val = values[0] for c in values[1:]: new_val = val.binary_op(c, op) tree.expect(new_val is not None, 'type_operation_incompatible', left=val, right=c, op=op.value) val = new_val return val
def parse_type(type_): """ Parses a type string and returns its parsed type which can be a: - BaseType (e.g. `IntType`) - TypeSymbol (e.g. `TypeSymbol(A)`) - GenericType (e.g. `ListGenericType(TypeSymbol(A))`) """ assert len(type_) > 0 type_ = type_.strip() if type_ == "boolean": return BooleanType.instance() if type_ == "int": return IntType.instance() if type_ == "float": return FloatType.instance() if type_ == "string": return StringType.instance() if type_ == "time": return TimeType.instance() if type_ == "none": return NoneType.instance() if type_ == "regexp": return RegExpType.instance() if type_ == "any": return AnyType.instance() if "[" in type_: types = [] for t in parse_type_inner(type_).split(","): t2 = parse_type(t) types.append(t2) if type_.startswith("List["): return ListGenericType(types) else: assert type_.startswith("Map[") return MapGenericType(types) assert "]" not in type_ return TypeSymbol(type_)
def names(self, tree): """ Extracts names from a path tree """ assert tree.data == "path" main_name = tree.child(0).value names = [NamedPath(main_name, main_name, IndexKind.FIRST)] for fragment in tree.children[1:]: child = fragment.child(0) kind = IndexKind.INDEX if isinstance(child, Tree): child.expect(child.data != "null", "path_index_no_null") if child.data == "string": type_ = StringType.instance() value = child.child(0).value elif child.data == "boolean": type_ = BooleanType.instance() value = child.child(0).value elif child.data == "range": self.module.storycontext.deprecate(child, "no_range") type_ = RangeType.instance() value = "range" elif child.data == "number": type_ = self.number(child) value = child.child(0).value else: assert child.data == "path" type_ = self.path(child) value = child.child(0).value else: assert child.type == "NAME" kind = IndexKind.DOT value = child.value type_ = StringType.instance() names.append(NamedPath(value, type_, kind)) return names
('foo[a,b]', ['foo[a,b]']), ('foo[a, b] arg:[]', ['foo[a, b]', 'arg:[]']), ('foo[a, b] arg:[]', ['foo[a, b]', 'arg:[]']), ('foo[a, b] arg: []', ['foo[a, b]', 'arg: []']), ('foo[a, b] arg:list[int] a2:bar', ['foo[a, b]', 'arg:list[int]', 'a2:bar']), ('foo[a, b] arg:foo[bar]', ['foo[a, b]', 'arg:foo[bar]']), ('foo[bar[abc]] arg:foo[bar]', ['foo[bar[abc]]', 'arg:foo[bar]']), ]) def test_split_args(text, expected): assert [*split_type_arguments(text)] == expected @mark.parametrize('text,expected', [ ('any', AnyType.instance()), ('boolean', BooleanType.instance()), ('float', FloatType.instance()), ('int', IntType.instance()), ('none', NoneType.instance()), ('string', StringType.instance()), ('time', TimeType.instance()), ('A', TypeSymbol('A')), ]) def test_parse_type_base(text, expected): t = parse_type(text) assert str(t) == str(expected) @mark.parametrize('text,expected_type,expected_symbols', [ ('List[int]', ListGenericType, [IntType.instance()]), ('List[float]', ListGenericType, [FloatType.instance()]),
def test_fn(): nonlocal c c = c + 1 return c single_fn = singleton(test_fn) assert single_fn() == 1 assert single_fn() == 1 assert c == 1 @mark.parametrize( "type_,expected", [ (BooleanType.instance(), "boolean"), (IntType.instance(), "int"), (FloatType.instance(), "float"), (NoneType.instance(), "none"), (AnyType.instance(), "any"), (RegExpType.instance(), "regexp"), (ListType(AnyType.instance()), "List[any]"), ( MapType(IntType.instance(), StringType.instance()), "Map[int,string]", ), (NullType.instance(), "null"), ], ) def test_boolean_str(type_, expected): assert str(type_) == expected
def test_singleton(): c = 0 def test_fn(): nonlocal c c = c + 1 return c single_fn = singleton(test_fn) assert single_fn() == 1 assert single_fn() == 1 assert c == 1 @mark.parametrize('type_,expected', [ (BooleanType.instance(), 'boolean'), (IntType.instance(), 'int'), (FloatType.instance(), 'float'), (NoneType.instance(), 'none'), (AnyType.instance(), 'any'), (RegExpType.instance(), 'regexp'), (ListType(AnyType.instance()), 'List[any]'), (MapType(IntType.instance(), StringType.instance()), 'Map[int,string]'), ]) def test_boolean_str(type_, expected): assert str(type_) == expected def test_none_eq(): assert NoneType.instance() == NoneType.instance() assert NoneType.instance() != IntType.instance()
from storyscript.compiler.semantics.functions.HubMutations import Hub from storyscript.compiler.semantics.functions.MutationTable import ( MutationTable, ) from storyscript.compiler.semantics.types.Types import BooleanType, IntType @mark.parametrize( "type_,expected", [ ( IntType.instance(), ["absolute", "decrement", "increment", "isEven", "isOdd"], ), (BooleanType.instance(), []), ], ) def test_resolve_by_type(type_, expected): muts = MutationTable.instance().resolve_by_type(type_) muts_names = [m.name() for m in muts] assert sorted(muts_names) == expected def test_desc(mocker): builtins = [ { "name": "increment", "input_type": "int", "return_type": "int", "desc": "returns the number + 1",
def test_type_to_tree_boolean(magic): tree = magic() bt = BooleanType.instance() se = SymbolExpressionVisitor.type_to_tree(tree, bt) assert se.data == 'base_type' tree.create_token.assert_called_with('BOOLEAN_TYPE', 'boolean')
], ) def test_parse_end(text, expected): assert parse_type_inner(text) == expected def test_parse_end_error(): with raises(AssertionError): parse_type_inner("foo[bar") @mark.parametrize( "text,expected", [ ("any", AnyType.instance()), ("boolean", BooleanType.instance()), ("float", FloatType.instance()), ("int", IntType.instance()), ("none", NoneType.instance()), ("string", StringType.instance()), ("time", TimeType.instance()), ("A", TypeSymbol("A")), ], ) def test_parse_type_base(text, expected): t = parse_type(text) assert str(t) == str(expected) @mark.parametrize( "text,expected_type,expected_symbols",
def nary_expression(self, tree, op, values): types = [v.type() for v in values] if self.op_returns_boolean(op.type): assert len(types) <= 2 # e.g. a < b if self.is_cmp(op.type): target_type = types[0].cmp(types[1]) tree.expect( target_type, "type_operation_cmp_incompatible", left=types[0], right=types[1], ) self.nary_args_implicit_cast(tree, target_type, types) return base_symbol(BooleanType.instance()) # e.g. a == b if self.is_equal(op.type): target_type = types[0].equal(types[1]) tree.expect( target_type, "type_operation_equal_incompatible", left=types[0], right=types[1], ) self.nary_args_implicit_cast(tree, target_type, types) return base_symbol(BooleanType.instance()) # e.g. a and b, a or b, !a child_node = 0 if len(types) == 1: child_node = 1 tree.child(child_node).expect( types[0].has_boolean(), "type_operation_boolean_incompatible", val=types[0], ) if len(types) == 2: tree.child(2).expect( types[1].has_boolean(), "type_operation_boolean_incompatible", val=types[1], ) return base_symbol(BooleanType.instance()) is_arithmetic = self.is_arithmetic_operator(op.type) tree.expect( is_arithmetic, "compiler_error_no_operator", operator=op.type ) target_type = types[0] for t in types[1:]: new_target_type = target_type.binary_op(t, op) tree.expect( new_target_type is not None, "type_operation_incompatible", left=target_type, right=t, op=op.value, ) target_type = new_target_type # add implicit casts if tree.kind == "pow_expression": return base_symbol(target_type) if tree.kind == "mul_expression": self.nary_args_implicit_cast(tree, target_type, types) else: assert tree.kind == "arith_expression" self.nary_args_implicit_cast(tree, target_type, types) return base_symbol(target_type)
def boolean(self, tree): """ Compiles a boolean tree. """ assert tree.data == 'boolean' return base_symbol(BooleanType.instance())
FloatType, IntType, ListType, MapType, NoneType, ObjectType, RegExpType, StringType, ) from storyscript.hub.TypeMappings import TypeMappings @mark.parametrize( "expected,hub", [ (BooleanType.instance(), OutputBoolean(data={})), (IntType.instance(), OutputInt(data={})), (FloatType.instance(), OutputFloat(data={})), (NoneType.instance(), OutputNone(data={})), (AnyType.instance(), OutputAny(data={})), (RegExpType.instance(), OutputRegex(data={})), (StringType.instance(), OutputEnum(data={})), ( ListType(AnyType.instance()), OutputList(OutputAny.create(), data={}), ), ( ListType(IntType.instance()), OutputList(OutputInt(data={}), data={}), ), (