def assignment(self, tree, scope): target_symbol = self.path_resolver.path(tree.path) tree.expect(target_symbol.can_write(), 'readonly_type_assignment', left=target_symbol.name()) frag = tree.assignment_fragment expr_sym = self.resolver.base_expression(frag.base_expression) expr_type = expr_sym.type() storage_class = expr_sym._storage_class if expr_sym.can_write(): storage_class = self.storage_class_scope token = frag.child(0) tree.expect('/' not in token.value, 'variables_backslash', token=token) tree.expect('/' not in token.value, 'variables_dash', token=token) if target_symbol.type() == NoneType.instance(): if not target_symbol.is_internal(): frag.base_expression.expect(expr_type != NoneType.instance(), 'assignment_type_none') sym = Symbol(target_symbol.name(), expr_type, storage_class=storage_class) scope.symbols().insert(sym) else: tree.expect(target_symbol.type().can_be_assigned(expr_type), 'type_assignment_different', target=target_symbol._type, source=expr_type) self.visit_children(tree, scope)
def return_statement(self, tree, scope): assert tree.data == "return_statement" with self.scope(tree.scope): obj = tree.base_expression if obj is None: return base_symbol(NoneType.instance()), tree return self.resolver.base_expression(tree.base_expression), obj
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 return_statement(self, tree, scope): assert tree.data == 'return_statement' self.symbol_resolver.update_scope(scope) obj = tree.base_expression if obj is None: return base_symbol(NoneType.instance()), tree return self.resolver.base_expression(tree.base_expression), obj
def function_block(self, tree, scope): if tree.function_statement.function_output: if not self.has_return(tree): t = tree.function_statement.function_output raise CompilerError('return_required', tree=t) for ret in tree.find_data('return_statement'): ret_sym, obj = self.return_statement(ret, scope) ret_type = ret_sym.type() obj.expect(ret_sym.can_write(), 'return_type_readonly', source=ret_type) node = obj # obj might not have any tokens, e.g. {} if obj.line() is None: node = ret node.expect( self.return_type.can_be_assigned(ret_type), 'return_type_differs', target=self.return_type, source=ret_type ) else: # function has no return output, so only `return` may be used for ret in tree.find_data('return_statement'): ret_sym, obj = self.return_statement(ret, scope) ret_type = ret_sym.type() # obj might not have any tokens, e.g. {} obj.expect( ret_type == NoneType.instance(), 'function_without_output_return', return_type=ret_type )
def test_none_op(): none = NoneType.instance() assert none.binary_op(none, Token("MINUS", "-")) is None assert none.binary_op(IntType.instance(), Token("MINUS", "-")) is None assert none.binary_op(IntType.instance(), Token("PLUS", "+")) is None assert none.binary_op(StringType.instance(), Token("PLUS", "+")) is None assert none.binary_op(AnyType.instance(), None) is None
def function_statement(self, tree, scope): """ Create a new scope _without_ a parent scope for this function. Prepopulate the scope with symbols from the function arguments. """ scope = Scope.root() return_type = NoneType.instance() args = {} for c in tree.children[2:]: if isinstance(c, Tree) and c.data == "typed_argument": name = c.child(0) e_sym = self.resolver.types(c.types) c.expect( e_sym.type() != ObjectType.instance(), "arg_type_no_object", arg=name, type=e_sym.type(), ) sym = Symbol.from_path(name, e_sym.type()) scope.insert(sym) args[sym.name()] = sym # add function to the function table function_name = tree.child(1).value tree.expect( self.module.function_table.resolve(function_name) is None, "function_redeclaration", name=function_name, ) output = tree.function_output if output is not None: return_type = self.resolver.types(output.types).type() tree.expect(return_type != ObjectType.instance(), "return_type_no_object") self.module.function_table.insert(function_name, args, return_type) return scope, return_type
def test_none_op(): none = NoneType.instance() assert none.binary_op(none, Token('MINUS', '-')) is None assert none.binary_op(IntType.instance(), Token('MINUS', '-')) is None assert none.binary_op(IntType.instance(), Token('PLUS', '+')) is None assert none.binary_op(StringType.instance(), Token('PLUS', '+')) is None assert none.binary_op(AnyType.instance(), None) is None
def test_service_output_none(patch, magic): patch.init(ServiceTyping) patch.object(TypeMappings, "get_type_instance") action = magic() action.output.return_value = None typings = ServiceTyping() res = typings.get_service_output(action) TypeMappings.get_type_instance.assert_not_called() assert res == NoneType.instance()
def insert(self, name, args, output=None): """ Insert a new function into the function table. """ assert name not in self.functions if output is None: output = NoneType.instance() fn = Function(name, args, output) self.functions[name] = fn
def assignment(self, tree, scope): lhs_node = tree.path target_symbol = self.path_resolver.path(lhs_node) # allow rebindable assignments here lhs_node.expect( target_symbol.can_assign(), "readonly_type_assignment", left=target_symbol.name(), ) frag = tree.assignment_fragment expr_sym = self.resolver.base_expression(frag.base_expression) expr_type = expr_sym.type() storage_class = expr_sym._storage_class if expr_sym.can_write(): storage_class = self.storage_class_scope token = frag.child(0) tree.expect("/" not in token.value, "variables_backslash", token=token) tree.expect("/" not in token.value, "variables_dash", token=token) if target_symbol.type() == NoneType.instance(): can_assign = self.storage_class_scope.can_assign() storage_class = storage_class.declaration_from_symbol( rebindable=can_assign) if not target_symbol.is_internal(): frag.base_expression.expect(expr_type != NoneType.instance(), "assignment_type_none") sym = Symbol(target_symbol.name(), expr_type, storage_class=storage_class) scope.insert(sym) else: tree.expect( target_symbol.type().can_be_assigned(expr_type), "type_assignment_different", target=target_symbol._type, source=expr_type, ) self.visit_children(tree, scope)
def resolve(self, tree, paths): val = paths[0] if isinstance(val, Token): val = val.value assert isinstance(val, str) symbol = self.scope.resolve(val) if self._check_variable_existence: tree.expect(symbol is not None, 'var_not_defined', name=paths[0]) else: if symbol is None: tree.expect(len(paths) == 1, 'var_not_defined', name=paths[0]) return Symbol(val, NoneType.instance()) return symbol.index(paths[1:], tree)
def resolve(self, tree, val, paths): assert isinstance(val, str) symbol = self.scope.resolve(val) if self._check_variable_existence: tree.expect(symbol is not None, 'var_not_defined', name=val) else: if symbol is None: tree.expect(len(paths) == 0, 'var_not_defined', name=val) return Symbol(val, NoneType.instance()) for p in paths: symbol = symbol.index(tree, name=p.value, type_=p.type, kind=p.kind) return symbol
def function_block(self, tree, scope): if tree.function_statement.function_output: if not self.has_return(tree): t = tree.function_statement.function_output raise CompilerError('return_required', tree=t) for ret in tree.find_data('return_statement'): ret_type, obj = self.return_statement(ret, scope) obj.expect(self.return_type.can_be_assigned(ret_type), 'return_type_differs', target=self.return_type, source=ret_type) else: # function has no return output, so only `return` may be used for ret in tree.find_data('return_statement'): ret_type, obj = self.return_statement(ret, scope) obj.expect( ret_type == NoneType.instance(), 'function_without_output_return', )
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 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() assert NoneType.instance() != AnyType.instance()
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_none_eq():
def test_null_explicit_from(): assert NullType.instance().explicit_from(NoneType.instance()) is None assert (NullType.instance().explicit_from(AnyType.instance()) is NullType.instance()) assert (NullType.instance().explicit_from(IntType.instance()) is NullType.instance())
('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()]), ('Map[int,string]', MapGenericType, [IntType.instance(), StringType.instance()]), ('List[A]', ListGenericType, [TypeSymbol('A')]),
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={}), ), ( MapType(IntType.instance(), StringType.instance()), OutputMap(OutputInt(data={}), OutputString(data={}), data={}), ),
def test_none_explicit_from(): assert NoneType.instance().explicit_from(IntType.instance()) is None assert NoneType.instance().explicit_from(AnyType.instance()) is None
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", [ ("List[int]", ListGenericType, [IntType.instance()]), ("List[float]", ListGenericType, [FloatType.instance()]),
def test_none_assign(): assert not NoneType.instance().can_be_assigned(IntType.instance()) assert not NoneType.instance().can_be_assigned(AnyType.instance())
def test_none_eq(): assert NoneType.instance() == NoneType.instance() assert NoneType.instance() != IntType.instance() assert NoneType.instance() != AnyType.instance()