예제 #1
0
def generate_types():
    """
    Generates and writes the C++ code for internal thing types, such as the text and number classes
    Additionally, writes the symbol maps for the generated types
    """

    for name, path in definitions.INTERNAL_SOURCES.items():
        ast = pipeline.preprocess(SourceContext(path))
        symbol_map = SymbolMapper(ast)[name]
        symbol_map.convention = Symbol.INTERNAL

        text_cast = CastTag(Identifier('text'))
        if text_cast not in symbol_map:
            symbol_map.lookup[text_cast] = Symbol.noop(text_cast,
                                                       Identifier('text'),
                                                       implicit=True)

        for symbol in symbol_map:
            symbol.convention = Symbol.INTERNAL

        write_if_changed(
            os.path.join(SYMBOLS_TARGET, f'{name}.thingsymbols'),
            json.dumps(symbol_map.serialize(),
                       cls=JSONSerializer,
                       indent=4,
                       sort_keys=True))
예제 #2
0
def test_parameterization_propagation():
    symbols = get_symbols(SOURCE_FULL)
    generic_type = symbols[Identifier('Person')][Identifier(
        'favorite_numbers')].type
    parametrized_type = symbols[generic_type]
    assert parametrized_type.name == GenericIdentifier(
        Identifier('Pair'), (Identifier('number'), ))
예제 #3
0
 def entry(self) -> Reference:
     """
     Get the index of the program's entry point
     """
     return self.resolve(
         NamedAccess([Identifier('Program'),
                      Identifier.constructor()]), {})
예제 #4
0
def validate_thing_definition(node, name, extends=None, generics=None):
    assert isinstance(node, ThingDefinition)
    assert node.name == (name
                         if isinstance(name, Identifier) else Identifier(name))
    assert node.extends == (Identifier(extends) if extends else None)
    assert node.generics == ([Identifier(x)
                              for x in generics] if generics else None)
예제 #5
0
def test_method_parameterization():
    pair_number = get_parametrized()
    validate_method(pair_number[Identifier('set_values')], 'number',
                    ['number'] * 2, 1)
    validate_method(pair_number[Identifier('nested_param')],
                    GenericIdentifier.wrap('list', 'number'),
                    [GenericIdentifier.wrap('list', 'number')], 2)
예제 #6
0
def finalize_buffer(buffer: str, terminating_char, entity_class,
                    source_ref) -> LexicalToken:
    """
    Finalize a character buffer into a lexical token
    :param buffer: the characters collected thus far
    :param terminating_char: the character which caused the buffer termination (not included in buffer)
    :param entity_class: the current entity being collected (generally, a type of quote, or none)
    :param source_ref: a reference to the source from which these tokens were derived
    """
    if buffer in KEYWORDS:
        return KEYWORDS[buffer](
            buffer, source_ref) if KEYWORDS[buffer].EMITTABLE else None

    if buffer.isdigit():
        return NumericValue(buffer, source_ref)

    if terminating_char == '"':
        if entity_class is not LexicalQuote:
            raise ValueError("Unexpected end of string")
        return InlineString(buffer, source_ref)

    if terminating_char == '`':
        if entity_class is not LexicalBacktick:
            raise ValueError("Unexpected end of inline code")
        return InlineString(buffer, source_ref)

    if Identifier.validate(buffer):
        if entity_class in (LexicalQuote, LexicalBacktick):
            raise ValueError("String was not closed")
        return Identifier(buffer, source_ref)

    if buffer:
        raise ValueError('Lexer: cannot terminate group "{}" (at {})'.format(
            buffer, source_ref))
예제 #7
0
def test_iteration_loop_parsing():
    loop = parse_local('for number n in numbers')

    assert isinstance(loop, IterationLoop)
    assert loop.target == Identifier('n')
    assert loop.target_type == Identifier('number')
    assert loop.collection == Identifier('numbers')
예제 #8
0
def internal_call(target):
    return OpcodeCallInternal.from_reference(
        SYMBOL_MAPPER.resolve_named([
            CastTag(Identifier(x[3:]))
            if x.startswith('as ') else Identifier(x)
            for x in target.split('.')
        ],
                                    generic_validation=False))
예제 #9
0
def test_person_member_symbol_description():
    symbols = get_symbols(SOURCE_PERSON)
    person = symbol_map_sanity(symbols, 'Person',
                               ('name', 'age', 'location', 'walk_to',
                                'say_hello', 'shout', 'favorite_numbers'))

    validate_member(person[Identifier('name')], Identifier('text'), 0)
    validate_member(person[Identifier('location')], Identifier('Location'), 2)
예제 #10
0
    def finalize(self):
        super().finalize()

        if Identifier.constructor() not in self.names:  # Add implicit constructor
            self.children.insert(0, MethodDefinition.empty_constructor(self))

        if self.extends and self.extends.untyped in definitions.INTERNAL_SOURCES:
            self.children.insert(0, MemberDefinition(Identifier.super(), self.extends).deriving_from(self))
예제 #11
0
def validate_method(method: Symbol, type, arguments, index, static=False):
    assert method.type == (Identifier(type) if isinstance(type, str) else type), (method.type, type)
    assert method.index == index
    assert method.arguments == [Identifier(x) if isinstance(x, str) else x for x in arguments]
    assert method.static is static

    assert method.kind is Symbol.METHOD
    assert method.visibility is Symbol.PUBLIC
예제 #12
0
def test_chained_call():
    call = parse_local('counter.increment().add(10)')
    validate_types(call.arguments, [NumericValue])
    assert call.target == NamedAccess([
        MethodCall(NamedAccess([
            Identifier('counter'), Identifier('increment')
        ])),
        Identifier('add')
    ])
def validate_assignment(node, type, name, value):
    assert isinstance(node, AssignmentOperation)
    assert node.name == Identifier(name)
    assert node.name.type == (Identifier(type) if type else None)
    assert node.intent == (AssignmentOperation.DECELERATION
                           if type else AssignmentOperation.REASSIGNMENT)

    if value in (MethodCall, BinaryOperation):
        assert isinstance(node.value, value)
예제 #14
0
def test_generic_parsing():
    node = parse_local('list<number> l = [1, 2, 3]')

    assert isinstance(node, AssignmentOperation)
    assert isinstance(node.name, Identifier)
    assert isinstance(node.name.type, GenericIdentifier)
    assert node.name == Identifier('l')
    assert node.name.type.value == Identifier('list')
    assert node.name.type.generics == (Identifier('number'), )
예제 #15
0
 def load_identifier(value):
     """
     Parse a generic identifier
     """
     if isinstance(value, str):
         return Identifier(value)
     elif isinstance(value, list):
         return GenericIdentifier(Identifier(value[0]), tuple(Identifier(x) for x in value[1]))
     elif isinstance(value, dict) and value['intent'] == 'cast':
         return CastTag(Symbol.load_identifier(value['type']))
예제 #16
0
def test_nested_member_parameterization():
    pair_number = get_parametrized()

    validate_member(pair_number[Identifier('parts')],
                    GenericIdentifier.wrap('list', 'number'), 2)
    validate_member(
        pair_number[Identifier('nested')],
        GenericIdentifier.wrap(
            'list',
            GenericIdentifier.wrap('list',
                                   GenericIdentifier.wrap('list', 'number'))),
        3)
예제 #17
0
def test_argument_count_mismatch():
    with pytest.raises(NoMatchingOverload) as e:
        pipeline.compile(
            SourceContext.wrap(BASE.format(code="self.no_args(1)")))

    assert e.value.methods[0].name == Identifier(
        'no_args') and e.value.arguments == [NumericValue(1)]

    with pytest.raises(NoMatchingOverload) as e:
        pipeline.compile(
            SourceContext.wrap(BASE.format(code="self.two_args(1)")))

    assert e.value.methods[0].name == Identifier(
        'two_args') and e.value.arguments == [NumericValue(1)]
예제 #18
0
    def compile(self, context: CompilationBuffer):
        if not self.values:
            return

        buffer = context.optional()
        ref = self[0].compile(buffer)  # TODO: remove unnecessary recompilation of first element (used to infer type)

        list_type = GenericIdentifier(Identifier('list'), (ref.type,))
        last_call = MethodCall(NamedAccess([list_type, Identifier.constructor()])).deriving_from(self)

        for value in self:  # TODO: validate list is homogeneous, and descend to lowest common type
            last_call = MethodCall(NamedAccess([last_call, Identifier("append")]), ArgumentList([value])).deriving_from(self)

        return last_call.compile(context)
예제 #19
0
def get_selection(*target_types):
    selector = BASE.element.selector(CONTEXT)

    for target_type in target_types:
        selector.constraint(Reference(Identifier(target_type)))

    return selector.disambiguate(None)
예제 #20
0
def validate_member(member: Symbol, type, index, static=False):
    assert member.type == (Identifier(type) if isinstance(type, str) else type)
    assert member.index == index
    assert member.static is static

    assert member.kind is Symbol.MEMBER
    assert member.visibility is Symbol.PUBLIC
예제 #21
0
    def resolve(
        self,
        target: Union[Identifier, NamedAccess],
        method_locals: dict = (),
        current_generics: list = ()
    ) -> Reference:
        """
        Resolve a reference into a Reference object
        :param target: the reference being resolved (can be either an identifier or access object)
        :param method_locals: the locals of the method being compiled
        :return: new Reference
        """
        assert not target.STATIC

        if isinstance(target, Identifier):
            if target in current_generics:
                return Reference(Identifier.object())
            elif target.untyped in self:  # TODO: verify name collisions
                return Reference(target)
            else:
                local = method_locals[target]
                if not local.allowed:
                    raise SelfInStaticMethod(target)
                if local.type not in current_generics and self[
                        local.
                        type].generics and local.source != 'self':  # Check if any generic type parameters have been left unfilled
                    raise UnfilledGenericParameters(target, self[local.type],
                                                    None)
                return LocalReference(method_locals[target], target)
        elif isinstance(target, NamedAccess):
            return self.resolve_named(target, method_locals, current_generics)

        raise Exception("Unknown reference type {}".format(target))
예제 #22
0
    def resolve_named(self,
                      target: Sequence,
                      method_locals=(),
                      current_generics=(),
                      generic_validation=True) -> ElementReference:
        """
        Resolves an identifier pair (a.b) into an element reference
        :param target: the Access object to resolve
        :param method_locals: the current method's locals
        :return: new ElementReference
        """
        assert len(target) == 2

        first, second, local = target[0], target[1], None

        if first.STATIC:
            container = self[first.type]
        elif isinstance(first, IndexedAccess):
            container = self.resolve_indexed(first, method_locals)
        elif first.untyped in current_generics:  # TODO: what about name collisions?
            container = self[Identifier.object(
            )]  # TODO: implement `with type T implement Interface`
        elif first.untyped in self.maps:
            container = self[first]
        elif first in method_locals:
            local = method_locals[first]
            if not local.allowed:
                raise SelfInStaticMethod(target)
            container = self[local.type]
        else:
            raise Exception(
                'Cannot resolve first level access {} (on {}) from {}'.format(
                    first, first.source_ref, method_locals))

        container, element = self.pull(container, second, target)

        remaining_generics = set(container.generics) - set(
            current_generics
        )  # TODO: are we sure generic name conflicts don't prevent this validation?
        if generic_validation and not element.is_complete({
                x: Identifier.invalid()
                for x in remaining_generics
        }):  # Check if any generic type parameters have been left unfilled
            raise UnfilledGenericParameters(target, container, element)

        return ElementReference(container, self.index(container), element,
                                local)
예제 #23
0
 def from_serialized(cls, code, argument_names, argument_types):
     ast = preprocess.preprocess(SourceContext.wrap(code))
     return cls(
         ast.children,
         ArgumentList([
             Identifier(name, type_name=arg_type)
             for name, arg_type in zip(argument_names, argument_types)
         ]))
예제 #24
0
 def compile(
     self, context: CompilationBuffer
 ):  # TODO: we should probably reparse the maps into identifiers
     method_call = MethodCall(
         NamedAccess.extend(self.lhs,
                            Identifier(self.operator.serialize())),
         [self.rhs])
     return method_call.compile(context)
예제 #25
0
    def __init__(self, target: Identifier, target_type: Identifier,
                 collection: ValueType):
        super().__init__(None, (target, target_type, collection))

        self.target, self.target_type, self.collection = target, target_type, collection
        self.iterator_id = next(IterationLoop.TRANSIENT_COUNTER)

        self.iterator = self.iterator_container_name[0]
        self.continuation_check = MethodCall(
            NamedAccess.extend(self.iterator,
                               Identifier('has_next'))).deriving_from(self)
        self.continuation_next = MethodCall(
            NamedAccess.extend(self.iterator,
                               Identifier('next'))).deriving_from(self)

        self.value = self.continuation_check

        if isinstance(self.collection, MethodCall):
            self.collection.is_captured = True
예제 #26
0
파일: indexer.py 프로젝트: ytanay/thinglang
    def __init__(self, method: MethodDefinition, thing: ThingDefinition):
        super(IndexerContext, self).__init__()

        self.current_method = method
        self.locals = OrderedDict({Identifier.self(): LocalMember(thing.name, 0, 'self', not method.static)})

        # Note: this keeps the first slot reserved for self, even in a static method

        for arg in method.arguments:
            self.locals[arg] = LocalMember(arg.type, len(self.locals), 'argument', True)
예제 #27
0
def normalize_id(param):
    if isinstance(param, str):
        return Identifier(param)

    if isinstance(param, int):
        return NumericValue(param)

    if isinstance(param, (tuple, list)):
        return [normalize_id(x) for x in param]

    return param
def validate_method_definition(node,
                               name,
                               expected_arguments=(),
                               return_type=None):
    assert isinstance(node, MethodDefinition)
    assert node.name == (name
                         if isinstance(name, Identifier) else Identifier(name))
    assert node.return_type == return_type
    for actual_argument, expected_argument in zip(node.arguments,
                                                  expected_arguments):
        assert actual_argument.value == expected_argument[0]
        assert actual_argument.type.value == expected_argument[1]
예제 #29
0
    def finalize(self):
        if not self.is_constructor():
            return super().finalize()

        for descendant in self.descendants:
            if isinstance(
                    descendant,
                    MethodCall) and descendant.target[0] == Identifier.super():
                descendant.replace(
                    AssignmentOperation(
                        AssignmentOperation.REASSIGNMENT,
                        NamedAccess([Identifier.self(),
                                     Identifier.super()]),
                        MethodCall(NamedAccess(
                            [self.parent.extends,
                             Identifier.constructor()]),
                                   descendant.arguments,
                                   is_captured=True).deriving_from(
                                       self)).deriving_from(descendant))

        super().finalize()
예제 #30
0
    def compile(self, context: CompilationBuffer):
        iterator_name, iterator_type = self.iterator_container_name
        AssignmentOperation(
            AssignmentOperation.REASSIGNMENT,
            iterator_name,
            MethodCall(NamedAccess.extend(self.collection,
                                          Identifier('iterator')),
                       is_captured=True).deriving_from(self),
            iterator_type,
        ).deriving_from(self).compile(context)

        super().compile(context)