def test_completeness_test(self) -> None: in_var = SequenceVariable() f = StackEffect( [in_var, object_type, object_type], [in_var, object_type, object_type], ) self.assertFalse(f.can_be_complete_program())
def _generate_module_type(components: Sequence[str], _full_name: Optional[str] = None, source_dir='.') -> ObjectType: if _full_name is None: _full_name = '.'.join(components) if len(components) > 1: module_t = ObjectType( IndividualVariable(), { components[1]: _generate_module_type(components[1:], _full_name, source_dir)[_seq_var, ], }, nominal_supertypes=[module_type], ) effect = StackEffect([_seq_var], [_seq_var, module_type]) return ObjectType(IndividualVariable(), { '__call__': effect, }, [_seq_var]) else: innermost_type = _generate_type_of_innermost_module( _full_name, source_dir) return ObjectType(IndividualVariable(), { '__call__': innermost_type, }, [_seq_var])
def to_type(self, env: Environment) -> Tuple[StackEffect, Environment]: a_bar = SequenceVariable() b_bar = a_bar new_env = env.copy() if self.input_sequence_variable is not None: if self.input_sequence_variable in new_env: a_bar = cast( SequenceVariable, new_env[self.input_sequence_variable], ) new_env[self.input_sequence_variable] = a_bar if self.output_sequence_variable is not None: if self.output_sequence_variable in new_env: b_bar = cast( SequenceVariable, new_env[self.output_sequence_variable], ) else: b_bar = SequenceVariable() new_env[self.output_sequence_variable] = b_bar in_types = [] for item in self.input: type, new_env = _ensure_type(item[1], new_env, item[0]) in_types.append(type) out_types = [] for item in self.output: type, new_env = _ensure_type(item[1], new_env, item[0]) out_types.append(type) return StackEffect([a_bar, *in_types], [b_bar, *out_types]), new_env
def _generate_type_of_innermost_module(qualified_name: str, source_dir) -> StackEffect: # We resolve imports as if we are the source file. sys.path, old_path = [source_dir, *sys.path], sys.path try: module = importlib.import_module(qualified_name) except ModuleNotFoundError: raise TypeError( 'module {} not found during type checking'.format(qualified_name)) finally: sys.path = old_path module_attributes = {} for name in dir(module): attribute_type = object_type if isinstance(getattr(module, name), int): attribute_type = int_type elif callable(getattr(module, name)): attribute_type = py_function_type module_attributes[name] = attribute_type module_t = ObjectType( IndividualVariable(), module_attributes, nominal_supertypes=[module_type], ) return StackEffect([_seq_var], [_seq_var, module_type])
def test_add_operator_inference(self, a: int, b: int) -> None: try_prog = '{!r} {!r} +\n'.format(a, b) tree = parse(try_prog) _, type = concat.typecheck.infer( concat.typecheck.Environment(), tree.children, is_top_level=True, ) note(str(type)) self.assertEqual(type, StackEffect([], [int_type]))
def test_cast_word(self) -> None: """Test that the type checker properly checks casts.""" tree = parse('"str" cast (int)') _, type = concat.typecheck.infer( Environment(concat.typecheck.preamble_types.types), tree.children, is_top_level=True, ) self.assertEqual(type, StackEffect([], [int_type]))
def test_slice_inference(self) -> None: slice_prog = '[1, 2, 3, 4, 5, 6, 7, 8] $[1:None:2]\n' tree = parse(slice_prog) _, type = concat.typecheck.infer( Environment(concat.typecheck.preamble_types.types), tree.children, is_top_level=True, ) self.assertEqual(type, StackEffect([], [list_type[int_type,]]))
def test_call_inference(self) -> None: try_prog = '$(42) call\n' tree = parse(try_prog) _, type = concat.typecheck.infer( concat.typecheck.Environment( concat.typecheck.preamble_types.types ), tree.children, is_top_level=True, ) self.assertEqual(type, StackEffect([], [int_type]))
def test_read_quot(self) -> None: stack = [] seq_var = SequenceVariable() # Like in Factor, read_quot will search its caller's scope for objects. some, words, here = object(), object(), object() with replace_stdin(io.StringIO('some words here')): concat.stdlib.repl.read_quot( stack, [], extra_env=concat.typecheck.Environment( { 'some': StackEffect([seq_var], []), 'words': StackEffect([], []), 'here': StackEffect([], []), } ), ) self.assertEqual( stack, [concat.stdlib.types.Quotation([some, words, here])], msg='read_quot has incorrect stack effect', )
def test_with_word_and_funcdef_inference(self) -> None: wth = 'def fn(f:object -- n:int): drop 0 ~\n$fn {"file": "a_file"} open with' tree = parse(wth) in_var = SequenceVariable() _, type = concat.typecheck.infer( Environment( { **concat.typecheck.preamble_types.types, 'drop': concat.typecheck.types.ForAll( [in_var], StackEffect([in_var, object_type], [in_var]) ), 'open': concat.typecheck.types.ForAll( [in_var], # FIXME: dict_type should be a type constructor StackEffect([in_var, dict_type], [in_var, file_type]), ), } ), tree.children, initial_stack=TypeSequence([in_var]), ) self.assertEqual(type, StackEffect([in_var], [in_var, int_type]))
def test_attribute_word(self, attr_word) -> None: _, type = concat.typecheck.infer( concat.typecheck.Environment(), [attr_word], initial_stack=TypeSequence( [ ObjectType( IndividualVariable(), {attr_word.value: StackEffect([], [int_type]),}, ), ] ), ) self.assertEqual(list(type.output), [int_type])
def test_with_word(self) -> None: wth = '$() ctxmgr with\n' tree = parse(wth) a_bar = SequenceVariable() self.assertRaises( concat.typecheck.TypeError, concat.typecheck.infer, concat.typecheck.Environment( { 'ctxmgr': concat.typecheck.types.ForAll( [a_bar], StackEffect([a_bar], [a_bar, object_type]) ) } ), tree.children, )
class TestStackEffectParser(unittest.TestCase): _a_bar = concat.typecheck.SequenceVariable() _d_bar = concat.typecheck.SequenceVariable() _b = concat.typecheck.IndividualVariable() _c = concat.typecheck.IndividualVariable() examples: Dict[str, StackEffect] = { 'a b -- b a': StackEffect([_a_bar, _b, _c], [_a_bar, _c, _b]), 'a -- a a': StackEffect([_a_bar, _b], [_a_bar, _b, _b]), 'a --': StackEffect([_a_bar, _b], [_a_bar]), 'a:object b:object -- b a': StackEffect( [_a_bar, object_type, object_type,], [_a_bar, *[object_type] * 2], ), 'a:`t -- a a': StackEffect([_a_bar, _b], [_a_bar, _b, _b]), '*i -- *i a': StackEffect([_a_bar], [_a_bar, _b]), '*i fun:(*i -- *o) -- *o': StackEffect( [_a_bar, StackEffect([_a_bar], [_d_bar])], [_d_bar], ), } def test_examples(self) -> None: for example in self.examples: with self.subTest(example=example): tokens = lex_string(example) # exclude ENCODING, NEWLINE and ENDMARKER tokens = tokens[1:-2] try: effect = build_parsers()['stack-effect-type'].parse(tokens) except parsy.ParseError as e: self.fail('could not parse {}\n{}'.format(example, e)) env = Environment(concat.typecheck.preamble_types.types) actual = effect.to_type(env)[0].generalized_wrt(env) expected = self.examples[example].generalized_wrt(env) print(actual) print(expected) self.assertEqual( actual, expected, )
def test_class_subtype_of_stack_effect(self, effect) -> None: x = IndividualVariable() # NOTE: self-last convention is modelled after Factor. unbound_effect = StackEffect([*effect.input, x], effect.output) cls = ClassType(x, {'__init__': unbound_effect}) self.assertLessEqual(cls, effect)
def test_stack_effect_subtyping(self, type1, type2) -> None: fun1 = StackEffect([type1], [type2]) fun2 = StackEffect([no_return_type], [object_type]) self.assertLessEqual(fun1, fun2)
def infer( gamma: Environment, e: 'concat.astutils.WordsOrStatements', extensions: Optional[Tuple[Callable]] = None, is_top_level=False, source_dir='.', initial_stack: Optional[TypeSequence] = None, ) -> Tuple[Substitutions, StackEffect]: """The infer function described by Kleffner.""" e = list(e) current_subs = Substitutions() if initial_stack is None: initial_stack = TypeSequence( [] if is_top_level else [SequenceVariable()]) current_effect = StackEffect(initial_stack, initial_stack) for node in e: try: S, (i, o) = current_subs, current_effect if isinstance(node, concat.operators.AddWordNode): # rules: # require object_type because the methods should return # NotImplemented for most types # FIXME: Make the rules safer... somehow # ... a b => (... {__add__(object) -> s} t) # --- # a b + => (... s) # ... a b => (... t {__radd__(object) -> s}) # --- # a b + => (... s) *rest, type1, type2 = current_effect.output try_radd = False try: add_type = type1.get_type_of_attribute('__add__') except AttributeError: try_radd = True else: if not isinstance(add_type, ObjectType): raise TypeError( '__add__ method of type {} is not of an object type, instead has type {}' .format(type1, add_type)) if add_type.head != py_function_type: raise TypeError( '__add__ method of type {} is not a Python function, instead it has type {}' .format(type1, add_type)) if [*add_type.type_arguments[0]] != [object_type]: raise TypeError( '__add__ method of type {} does not have type (object) -> `t, instead it has type {}' .format(type1, add_type)) current_effect = StackEffect( current_effect.input, [*rest, add_type.output], ) if try_radd: radd_type = type2.get_type_of_attribute('__radd__') if (not isinstance(radd_type, ObjectType) or radd_type.head != py_function_type or [*radd_type.type_arguments[0]] != [object_type]): raise TypeError( '__radd__ method of type {} does not have type (object) -> `t, instead it has type {} (left operand is of type {})' .format(type2, radd_type, type1)) current_effect = StackEffect( current_effect.input, [*rest, radd_type.output], ) elif isinstance(node, concat.parse.PushWordNode): S1, (i1, o1) = S, (i, o) # special case for pushing an attribute accessor child = node.children[0] if isinstance(child, concat.parse.AttributeWordNode): top = o1[-1] attr_type = top.get_type_of_attribute(child.value) rest_types = o1[:-1] current_subs, current_effect = ( S1, StackEffect(i1, [*rest_types, attr_type]), ) # special case for name words elif isinstance(child, concat.parse.NameWordNode): if child.value not in gamma: raise NameError(child) name_type = gamma[child.value].instantiate() current_effect = StackEffect( current_effect.input, [*current_effect.output, current_subs(name_type)], ) elif isinstance(child, concat.parse.SliceWordNode): sliceable_object_type = o[-1] # This doesn't match the evaluation order used by the # transpiler. # FIXME: Change the transpiler to fit the type checker. sub1, start_effect = infer( gamma, list(child.start_children), extensions=extensions, source_dir=source_dir, initial_stack=TypeSequence(o[:-1]), ) start_type = start_effect.output[-1] o = tuple(start_effect.output[:-1]) sub2, stop_effect = infer( sub1(gamma), list(child.stop_children), extensions=extensions, source_dir=source_dir, initial_stack=TypeSequence(o), ) stop_type = stop_effect.output[-1] o = tuple(stop_effect.output[:-1]) sub3, step_effect = infer( sub2(sub1(gamma)), list(child.step_children), extensions=extensions, source_dir=source_dir, initial_stack=TypeSequence(o), ) step_type = step_effect.output[-1] o = tuple(step_effect.output[:-1]) this_slice_type = slice_type[start_type, stop_type, step_type] getitem_type = sliceable_object_type.get_type_of_attribute( '__getitem__') getitem_type = getitem_type.get_type_of_attribute( '__call__') getitem_type = getitem_type.instantiate() if (not isinstance(getitem_type, PythonFunctionType) or len(getitem_type.input) != 1): raise TypeError( '__getitem__ method of {} has incorrect type {}'. format(node, getitem_type)) getitem_type, overload_subs = getitem_type.select_overload( (this_slice_type, )) result_type = getitem_type.output current_subs = overload_subs(sub3(sub2( sub1(current_subs)))) current_effect = current_subs( StackEffect(i, [*o, result_type])) # special case for subscription words elif isinstance(child, concat.parse.SubscriptionWordNode): S2, (i2, o2) = infer( current_subs(gamma), child.children, extensions=extensions, is_top_level=False, source_dir=source_dir, initial_stack=current_effect.output, ) # FIXME: Should be generic subscriptable_interface = subscriptable_type[ int_type, IndividualVariable(), ] rest_var = SequenceVariable() expected_o2 = TypeSequence([ rest_var, subscriptable_interface, int_type, ]) o2[-1].constrain(int_type) getitem_type = (o2[-2].get_type_of_attribute( '__getitem__').instantiate().get_type_of_attribute( '__call__').instantiate()) if not isinstance(getitem_type, PythonFunctionType): raise TypeError( '__getitem__ of type {} is not a Python function (has type {})' .format(o2[-2], getitem_type)) getitem_type, overload_subs = getitem_type.select_overload( [int_type]) current_subs = overload_subs(S2(current_subs)) current_effect = current_subs( StackEffect( current_effect.input, [*o2[:-2], getitem_type.output], )) else: if (isinstance(child, concat.parse.QuoteWordNode) and child.input_stack_type is not None): input_stack, _ = child.input_stack_type.to_type(gamma) else: # The majority of quotations I've written don't comsume # anything on the stack, so make that the default. input_stack = TypeSequence([SequenceVariable()]) S2, fun_type = infer( S1(gamma), child.children, extensions=extensions, source_dir=source_dir, initial_stack=input_stack, ) current_subs, current_effect = ( S2(S1), StackEffect( S2(TypeSequence(i1)), [*S2(TypeSequence(o1)), QuotationType(fun_type)], ), ) elif isinstance(node, concat.parse.WithWordNode): a_bar, b_bar = SequenceVariable(), SequenceVariable() body_type = StackEffect([a_bar, object_type], [b_bar]) phi = current_effect.output.constrain_and_bind_supertype_variables( TypeSequence([a_bar, body_type, context_manager_type]), set(), ) assert b_bar in phi current_subs, current_effect = ( phi(current_subs), phi( StackEffect(current_effect.input, TypeSequence([b_bar]))), ) elif isinstance(node, concat.parse.TryWordNode): a_bar, b_bar = SequenceVariable(), SequenceVariable() phi = TypeSequence(o).constrain_and_bind_supertype_variables( TypeSequence([ a_bar, iterable_type[StackEffect([a_bar], [b_bar]), ], StackEffect([a_bar], [b_bar]), ]), set(), ) assert b_bar in phi current_subs, current_effect = ( phi(S), phi(StackEffect(i, [b_bar])), ) elif isinstance(node, concat.parse.DictWordNode): phi = current_subs collected_type = current_effect.output for key, value in node.dict_children: phi1, (i1, o1) = infer( phi(gamma), key, extensions=extensions, source_dir=source_dir, initial_stack=collected_type, ) phi = phi1(phi) collected_type = phi(o1) # drop the top of the stack to use as the key collected_type, key_type = ( collected_type[:-1], collected_type.as_sequence()[-1], ) phi2, (i2, o2) = infer( phi(gamma), value, extensions=extensions, source_dir=source_dir, initial_stack=collected_type, ) phi = phi2(phi) collected_type = phi(o2) # drop the top of the stack to use as the value collected_type, value_type = ( collected_type[:-1], collected_type.as_sequence()[-1], ) current_subs, current_effect = ( phi, phi( StackEffect(current_effect.input, [*collected_type, dict_type])), ) elif isinstance(node, concat.parse.ListWordNode): phi = S collected_type = TypeSequence(o) element_type: IndividualType = object_type for item in node.list_children: phi1, fun_type = infer( phi(gamma), item, extensions=extensions, source_dir=source_dir, initial_stack=collected_type, ) collected_type = fun_type.output # FIXME: Infer the type of elements in the list based on # ALL the elements. if element_type == object_type: assert isinstance(collected_type[-1], IndividualType) element_type = collected_type[-1] # drop the top of the stack to use as the item collected_type = collected_type[:-1] phi = phi1(phi) current_subs, current_effect = ( phi, phi( StackEffect( i, [*collected_type, list_type[element_type, ]])), ) elif isinstance(node, concat.operators.InvertWordNode): out_types = current_effect.output[:-1] invert_attr_type = current_effect.output[ -1].get_type_of_attribute('__invert__') if not isinstance(invert_attr_type, PythonFunctionType): raise TypeError( '__invert__ of type {} must be a Python function'. format(current_effect.output[-1])) result_type = invert_attr_type.output current_effect = StackEffect(current_effect.input, [*out_types, result_type]) elif isinstance(node, concat.parse.NoneWordNode): current_effect = StackEffect(i, [*o, none_type]) elif isinstance(node, concat.parse.NotImplWordNode): current_effect = StackEffect(i, [*o, not_implemented_type]) elif isinstance(node, concat.parse.EllipsisWordNode): current_effect = StackEffect(i, [*o, ellipsis_type]) elif isinstance(node, concat.parse.SliceWordNode): sliceable_object_type = o[-1] # This doesn't match the evaluation order used by the # transpiler. # FIXME: Change the transpiler to fit the type checker. sub1, start_effect = infer( gamma, list(node.start_children), source_dir=source_dir, initial_stack=TypeSequence(o[:-1]), ) start_type = start_effect.output[-1] o = tuple(start_effect.output[:-1]) sub2, stop_effect = infer( sub1(gamma), list(node.stop_children), source_dir=source_dir, initial_stack=TypeSequence(o), ) stop_type = stop_effect.output[-1] o = tuple(stop_effect.output[:-1]) sub3, step_effect = infer( sub2(sub1(gamma)), list(node.step_children), source_dir=source_dir, initial_stack=TypeSequence(o), ) step_type = step_effect.output[-1] o = tuple(step_effect.output[:-1]) this_slice_type = slice_type[start_type, stop_type, step_type] getitem_type = sliceable_object_type.get_type_of_attribute( '__getitem__') getitem_type = getitem_type.get_type_of_attribute('__call__') getitem_type = getitem_type.instantiate() if (not isinstance(getitem_type, PythonFunctionType) or len(getitem_type.input) != 1): raise TypeError( '__getitem__ method of {} has incorrect type {}'. format(node, getitem_type)) getitem_type, overload_subs = getitem_type.select_overload( (this_slice_type, )) result_type = getitem_type.output current_subs = overload_subs(sub3(sub2(sub1(current_subs)))) current_effect = overload_subs( StackEffect(i, [*o, result_type])) elif isinstance(node, concat.parse.FromImportStatementNode): imported_name = node.asname or node.imported_name # mutate type environment gamma[imported_name] = object_type # We will try to find a more specific type. sys.path, old_path = [source_dir, *sys.path], sys.path module = importlib.import_module(node.value) sys.path = old_path # For now, assume the module's written in Python. try: gamma[imported_name] = current_subs( getattr(module, '@@types')[node.imported_name]) except (KeyError, builtins.AttributeError): # attempt introspection to get a more specific type if callable(getattr(module, node.imported_name)): args_var = SequenceVariable() gamma[imported_name] = ObjectType( IndividualVariable(), { '__call__': py_function_type[TypeSequence([args_var]), object_type], }, type_parameters=[args_var], nominal=True, ) elif isinstance(node, concat.parse.ImportStatementNode): # TODO: Support all types of import correctly. if node.asname is not None: gamma[node.asname] = current_subs( _generate_type_of_innermost_module( node.value, source_dir).generalized_wrt(current_subs(gamma))) else: imported_name = node.value # mutate type environment components = node.value.split('.') # FIXME: This replaces whatever was previously imported. I really # should implement namespaces properly. gamma[components[0]] = current_subs( _generate_module_type(components, source_dir=source_dir)) elif isinstance(node, concat.parse.SubscriptionWordNode): seq = current_effect.output[:-1] index_type_var = IndividualVariable() result_type_var = IndividualVariable() subscriptable_interface = subscriptable_type[index_type_var, result_type_var] ( index_subs, (index_input, index_output), ) = infer( gamma, node.children, initial_stack=current_effect.output, extensions=extensions, ) index_output_typeseq = index_output subs = index_output_typeseq[ -2].constrain_and_bind_supertype_variables( subscriptable_interface, set(), )(index_subs(current_subs)) subs(index_output_typeseq[-1]).constrain(subs(index_type_var)) result_type = subs(result_type_var).get_type_of_attribute( '__call__') if not isinstance(result_type, StackEffect): raise TypeError( 'result of subscription is not callable as a Concat function (has type {})' .format(result_type)) subs = index_output_typeseq[: -2].constrain_and_bind_supertype_variables( result_type.input, set())(subs) current_subs, current_effect = ( subs, subs(StackEffect(current_effect.input, result_type.output)), ) elif isinstance(node, concat.operators.SubtractWordNode): # FIXME: We should check if the other operand supports __rsub__ if the # first operand doesn't support __sub__. other_operand_type_var = IndividualVariable() result_type_var = IndividualVariable() subtractable_interface = subtractable_type[ other_operand_type_var, result_type_var] seq_var = SequenceVariable() final_subs = current_effect.output.constrain_and_bind_supertype_variables( TypeSequence([ seq_var, subtractable_interface, other_operand_type_var, ]), set(), ) assert seq_var in final_subs current_subs, current_effect = ( final_subs(current_subs), final_subs( StackEffect(current_effect.input, [seq_var, result_type_var])), ) elif isinstance(node, concat.parse.FuncdefStatementNode): S = current_subs f = current_effect name = node.name declared_type: Optional[StackEffect] if node.stack_effect: declared_type, _ = node.stack_effect.to_type(S(gamma)) declared_type = S(declared_type) else: # NOTE: To continue the "bidirectional" bent, we will require a # type annotation. # TODO: Make the return types optional? # FIXME: Should be a parse error. raise TypeError( 'must have type annotation on function definition') recursion_env = gamma.copy() recursion_env[name] = declared_type.generalized_wrt(S(gamma)) phi1, inferred_type = infer( S(recursion_env), node.body, is_top_level=False, extensions=extensions, initial_stack=declared_type.input, ) # We want to check that the inferred outputs are subtypes of # the declared outputs. Thus, inferred_type.output should be a subtype # declared_type.output. try: inferred_type.output.constrain(declared_type.output) except TypeError: message = ( 'declared function type {} is not compatible with ' 'inferred type {}') raise TypeError( message.format(declared_type, inferred_type)) effect = declared_type # we *mutate* the type environment gamma[name] = effect.generalized_wrt(S(gamma)) elif isinstance(node, concat.operators.GreaterThanOrEqualToWordNode): a_type, b_type = current_effect.output[-2:] try: ge_type = a_type.get_type_of_attribute('__ge__') if not isinstance(ge_type, PythonFunctionType): raise TypeError( 'method __ge__ of type {} should be a Python function' .format(ge_type)) _, current_subs = ge_type.select_overload([b_type]) except TypeError: le_type = b_type.get_type_of_attribute('__le__') if not isinstance(le_type, PythonFunctionType): raise TypeError( 'method __le__ of type {} should be a Python function' .format(le_type)) _, current_subs = le_type.select_overload([a_type]) current_subs, current_effect = ( current_subs, StackEffect( current_effect.input, TypeSequence([*current_effect.output[:-2], bool_type]), ), ) elif isinstance( node, ( concat.operators.IsWordNode, concat.operators.AndWordNode, concat.operators.OrWordNode, concat.operators.EqualToWordNode, ), ): # TODO: I should be more careful here, since at least __eq__ can be # deleted, if I remember correctly. if not isinstance(current_effect.output[-1], IndividualType) or not isinstance( current_effect.output[-2], IndividualType): raise StackMismatchError( TypeSequence(current_effect.output), TypeSequence([object_type, object_type]), ) current_effect = StackEffect( current_effect.input, TypeSequence([*current_effect.output[:-2], bool_type]), ) elif isinstance(node, concat.parse.NumberWordNode): if isinstance(node.value, int): current_effect = StackEffect(i, [*o, int_type]) else: raise UnhandledNodeTypeError elif isinstance(node, concat.parse.NameWordNode): (i1, o1) = current_effect if node.value not in current_subs(gamma): raise NameError(node) type_of_name = current_subs(gamma)[node.value].instantiate() type_of_name = type_of_name.get_type_of_attribute('__call__') if not isinstance(type_of_name, StackEffect): raise UnhandledNodeTypeError( 'name {} of type {} (repr {!r})'.format( node.value, type_of_name, type_of_name)) constraint_subs = o1.constrain_and_bind_supertype_variables( type_of_name.input, set()) current_subs = constraint_subs(current_subs) current_effect = current_subs( StackEffect(i1, type_of_name.output)) elif isinstance(node, concat.parse.QuoteWordNode): quotation = cast(concat.parse.QuoteWordNode, node) # make sure any annotation matches the current stack if quotation.input_stack_type is not None: input_stack, _ = quotation.input_stack_type.to_type(gamma) S = TypeSequence(o).constrain_and_bind_supertype_variables( input_stack, set(), )(S) else: input_stack = TypeSequence(o) S1, (i1, o1) = infer( gamma, [*quotation.children], extensions=extensions, source_dir=source_dir, initial_stack=input_stack, ) current_subs, current_effect = ( S1(S), S1(StackEffect(i, o1)), ) elif isinstance(node, concat.parse.StringWordNode): current_subs, current_effect = ( S, StackEffect( current_effect.input, [*current_effect.output, str_type], ), ) elif isinstance(node, concat.parse.AttributeWordNode): stack_top_type = o[-1] out_types = o[:-1] attr_function_type = stack_top_type.get_type_of_attribute( node.value).instantiate() if not isinstance(attr_function_type, StackEffect): raise UnhandledNodeTypeError( 'attribute {} of type {} (repr {!r})'.format( node.value, attr_function_type, attr_function_type)) R = TypeSequence( out_types).constrain_and_bind_supertype_variables( attr_function_type.input, set(), ) current_subs, current_effect = ( R(S), R(StackEffect(i, attr_function_type.output)), ) elif isinstance(node, concat.parse.CastWordNode): new_type, _ = node.type.to_type(gamma) rest = current_effect.output[:-1] current_effect = current_subs( StackEffect(current_effect.input, [*rest, new_type])) else: raise UnhandledNodeTypeError( "don't know how to handle '{}'".format(node)) except TypeError as e: e.set_location_if_missing(node.location) raise return current_subs, current_effect
Mapping, Optional, Sequence, Sized, Union, cast, ) _stack_type_var = SequenceVariable() _rest_var = SequenceVariable() _rest_var_2 = SequenceVariable() _rest_var_3 = SequenceVariable() globals()['@@types'] = { 'to_str': StackEffect( TypeSequence([_stack_type_var, object_type, object_type, object_type]), TypeSequence([_stack_type_var, str_type]), ), 'to_py_function': ForAll( [_rest_var, _rest_var_2, _rest_var_3], StackEffect( [_rest_var, StackEffect([_rest_var_2], [_rest_var_3])], [_rest_var, py_function_type], ), ), } def to_py_function(stack: List[object], stash: List[object]) -> None: func = cast(Callable[[List[object], List[object]], None], stack.pop()) def py_func(*args: object) -> object:
_seq_var = SequenceVariable() _stack_var = SequenceVariable() _stack_type_var = SequenceVariable() _a_var = IndividualVariable() _b_var = IndividualVariable() _c_var = IndividualVariable() _x = IndividualVariable() types = { 'py_call': ForAll( [_rest_var, _seq_var, _a_var], StackEffect( [ _rest_var, iterable_type[object_type,], iterable_type[object_type,], py_function_type[TypeSequence([_seq_var]), _a_var], ], [_rest_var, _a_var], ), ), 'swap': ForAll( [_rest_var, _a_var, _b_var], StackEffect([_rest_var, _a_var, _b_var], [_rest_var, _b_var, _a_var]), ), 'pick': ForAll( [_rest_var, _a_var, _b_var, _c_var], StackEffect( [_rest_var, _a_var, _b_var, _c_var], [_rest_var, _a_var, _b_var, _c_var, _a_var], ),