def resolve_mutation(self, t, tree): """ Resolve a mutation on t with the MutationTable, instantiate it and check the caller arguments. """ # a mutation on 'object' returns 'any' (for now) if t == ObjectType.instance(): return AnyType.instance() name = tree.mutation_fragment.child(0).value args = self.build_arguments(tree.mutation_fragment, name, fn_type='Mutation') # a mutation on 'any' returns 'any' overloads = self.mutation_table.resolve(t, name) tree.expect(overloads is not None, 'mutation_invalid_name', name=name) ms = overloads.match(args.keys()) if ms is None: # if there's only one overload, use this for a better error # message single = overloads.single() if single is None: self.show_available_mutation_overloads(tree, overloads) else: ms = [single] if len(ms) > 1: # a mutation on any might have matched multiple overloads return AnyType.instance() else: assert len(ms) == 1 m = ms[0] m = m.instantiate(t) m.check_call(tree.mutation_fragment, args) return m.output()
def implicit_type_cast(tree, source_type, target_type, fn_type, fn_name, arg_name, arg_node): """ Checks whether an implicit cast from source_type to target type is allowed. If it is possible the implicit cast is performed on AST subtree. Args: tree: AST subtree which holds the args source_type: instance of one of the suitable type class for argument target_type: instance of one of the suitable type class for parameter fn_type: string specifying if the args are for a function, mutation or a service fn_name: string specifying name of the function, or a service arg_name: string specifying name of the argument arg_node: Tree node pointing to the actual argument node inside the tree in question """ type_cast_result = target_type.can_be_assigned(source_type) tree.expect(type_cast_result or source_type == AnyType.instance(), 'param_arg_type_mismatch', fn_type=fn_type, name=fn_name, arg_name=arg_name, target=target_type, source=source_type) if target_type != AnyType.instance() and type_cast_result != source_type: # We don't emit a type cast if: # * Target type is AnyType (AnyType can represent anything) # * Target and Source type are the same. arg_node.children[1] = SymbolExpressionVisitor.\ type_cast_expression(arg_node.children[1], target_type)
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_function_insert(): fn = FunctionTable() fn.insert("foo", {}, AnyType.instance()) fn_b = FunctionTable() fn_b.insert("bar", {}, AnyType.instance()) fn_b.insert_fn_table(fn) assert list(fn_b.functions.keys()) == ["bar", "foo"]
def map(self, tree): assert tree.data == 'map' keys = [] values = [] for i, item in enumerate(tree.children): assert isinstance(item, Tree) key_child = item.child(0) if key_child.data == 'string': new_key = self.string(key_child).type() elif key_child.data == 'number': new_key = self.number(key_child).type() elif key_child.data == 'boolean': new_key = self.boolean(key_child).type() else: assert key_child.data == 'path' new_key = self.path(key_child).type() keys.append(new_key) values.append(self.base_expression(item.child(1)).type()) # check all keys - even if they don't match key_child.expect(new_key.hashable(), 'type_key_not_hashable', key=new_key) key = None value = None for i, p in enumerate(zip(keys, values)): new_key, new_value = p if i >= 1: # type mismatch in the list if key != new_key: key = AnyType.instance() break if value != new_value: value = AnyType.instance() break else: key = new_key value = new_value if not self.with_as: tree.expect(key is not None, 'map_type_no_any') tree.expect(value is not None, 'map_type_no_any') if key is None: key = AnyType.instance() if value is None: value = AnyType.instance() return base_symbol(MapType(key, value))
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 = AnyType.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) 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.function_table.resolve(function_name) is None, 'function_redeclaration', name=function_name) output = tree.function_output if output is not None: output = self.resolver.types(output.types).type() self.function_table.insert(function_name, args, output) if tree.function_output: return_type = self.resolver.types( tree.function_output.types).type() return scope, return_type
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 test_mutation_pretty_b(): args = { "a": Symbol("a", IntType.instance()), "b": Symbol("b", StringType.instance()), } fn = MutationFunction("foo", args, AnyType.instance(), desc="") assert fn.pretty() == "foo(a:`int` b:`string`)"
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 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 test_mutation_pretty_b(): args = { 'a': Symbol('a', IntType.instance()), 'b': Symbol('b', StringType.instance()) } fn = MutationFunction('foo', args, AnyType.instance()) assert fn.pretty() == 'foo a:`int` b:`string`'
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 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 values(self, tree): """ Parses a values subtree """ subtree = tree.child(0) if hasattr(subtree, 'data'): if subtree.data == 'string': return self.string(subtree) elif subtree.data == 'boolean': return self.boolean(subtree) elif subtree.data == 'list': return self.list(subtree) elif subtree.data == 'number': return self.number(subtree) elif subtree.data == 'time': return self.time(subtree) elif subtree.data == 'map': return self.map(subtree) elif subtree.data == 'regular_expression': return self.regular_expression(subtree) else: assert subtree.data == 'void' return base_symbol(AnyType.instance()) assert subtree.type == 'NAME' return self.path(tree)
def implicit_cast(self, tree, val, values): """ Creates an AST with the cast. This boils down to - finding the right level to insert the new pow_expression with its respective as_operator - building a correct tree cascade in pow_expression to the old value """ if val == AnyType.instance(): return for i, v in enumerate(values): if i > 0: # ignore the arith_operator tree child i += 1 # check whether a tree child needs casting if v != val: element = tree.children[i] casted_type = self.type_to_tree(element, val) element = Tree('expression', [ element, Tree('as_operator', [ Tree('types', [ casted_type ]) ]) ]) element.kind = 'as_expression' tree.children[i] = element
def list(self, tree): assert tree.data == 'list' value = None for i, c in enumerate(tree.children[1:]): if not isinstance(c, Tree): continue val = self.base_expression(c).type() if i >= 1: # type mismatch in the list if val != value: value = AnyType.instance() break else: value = val if value is None: value = AnyType.instance() return base_symbol(ListType(value))
def _resolve_any(self, muts, name): """ Searches for all potential type overloads on mutation. """ mo = MutationOverloads(name, AnyType.instance()) for overloads in muts.values(): mo.add_overloads(overloads) return mo
def get_type_instance(var, obj=None): """ Returns the correctly mapped type class instance of the given type. Params: var: A Symbol from which type could be retrieved. object: In case the Symbol is of type Object, the object that should be wrapped inside the ObjectType instance. """ type_class = TypeMappings.type_class_mapping(var.type()) if type_class == ObjectType: output_type = ObjectType(obj=obj) elif type_class == ListType: output_type = ListType(AnyType.instance()) elif type_class == MapType: output_type = MapType(AnyType.instance(), AnyType.instance()) else: assert type_class in (AnyType, BooleanType, FloatType, IntType, StringType) output_type = type_class.instance() return output_type
def _check_arg_types(self, tree, args): """ Checks that for each argument its type can be implicitly converted to the respective function argument. """ for k, argument in zip(args.keys(), tree.children[1:]): target = self._args[k].type() t = args[k].type() type_cast_result = target.can_be_assigned(t) tree.expect(type_cast_result or t == AnyType.instance(), 'function_arg_type_mismatch', fn_type=self.fn_type, name=self._name, arg_name=k, target=target, source=t) if target != AnyType.instance() and type_cast_result != t: # We don't emit a type cast if: # * Target type is AnyType (AnyType can represent anything) # * Target and Source type are the same. argument.children[1] = SymbolExpressionVisitor.\ type_cast_expression(argument.children[1], target)
def nary_args_implicit_cast(self, tree, target_type, source_types): """ Cast nary_expression arguments implicitly. """ if target_type == AnyType.instance(): return for i, t in enumerate(source_types): if i > 0: # ignore the arith_operator tree child i += 1 # check whether a tree child needs casting if t != target_type: tree.children[i] = self.type_cast_expression( tree.children[i], target_type)
def service(self, tree): # unknown for now if tree.service_fragment.output is not None: tree.service_fragment.output.expect( 0, 'service_no_inline_output') try: # check whether variable exists t = self.path(tree.path) service_to_mutation(tree) return self.resolve_mutation(t, tree) except CompilerError as e: # ignore only invalid variables (must be services) if e.error == 'var_not_defined': return AnyType.instance() raise e
def _check_arg_types(self, tree, args): """ Checks that for each argument its type can be implicitly converted to the respective function argument. """ for k in args.keys(): target = self._args[k].type() t = args[k].type() tree.expect(target.can_be_assigned(t) or t == AnyType.instance(), 'function_arg_type_mismatch', fn_type=self.fn_type, name=self._name, arg_name=k, target=target, source=t)
def service(self, tree): # unknown for now if tree.service_fragment.output is not None: tree.service_fragment.output.expect(0, 'service_no_inline_output') t = None try: # check whether variable exists t = self.path(tree.path) except CompilerError: # ignore invalid variables (not existent or invalid) # -> must be a service return base_symbol(AnyType.instance()) # variable exists -> mutation service_to_mutation(tree) return self.resolve_mutation(t, tree)
def resolve(self, type_, name): """ Returns the mutation `name` or `None`. """ muts = self.mutations.get(name, None) if muts is None: return None if type_ == AnyType.instance(): return self._resolve_any(muts, name) t = self.type_key(type(type_)) overloads = muts.get(t, None) if overloads is None: return None mo = MutationOverloads(name, type_) mo.add_overloads(overloads) return mo
def implicit_cast(self, tree, val, values): """ Creates an AST with the cast. This boils down to - finding the right level to insert the new pow_expression with its respective as_operator - building a correct tree cascade in pow_expression to the old value """ if val == AnyType.instance(): return insert_tree_name = tree.data for i, v in enumerate(values): if i > 0: # ignore the arith_operator tree child i += 1 # check whether a tree child needs casting if v != val: element = tree.children[i] casted_type = self.type_to_tree(element, val) if i != 0: element = Tree(insert_tree_name, [element]) if element.data == 'mul_expression': element = Tree('arith_expression', [element]) if element.data == 'arith_expression': element = Tree('cmp_expression', [element]) tree.children[i] = Tree('unary_expression', [ Tree('pow_expression', [ Tree('primary_expression', [ Tree('or_expression', [ Tree('and_expression', [element]), ]), ]), Tree('as_operator', [Tree('types', [casted_type])]) ]) ]) for e in ['mul_expression', 'arith_expression']: if e == insert_tree_name: break else: tree.children[i] = Tree(e, [tree.children[i]]) if i == 0: tree.children[0] = Tree(insert_tree_name, [tree.children[0]])
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_)
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() def test_none_assign():
def test_none_eq(): assert NoneType.instance() == NoneType.instance() assert NoneType.instance() != IntType.instance() assert NoneType.instance() != AnyType.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_explicit_from(): assert NoneType.instance().explicit_from(IntType.instance()) is None assert NoneType.instance().explicit_from(AnyType.instance()) is None