예제 #1
0
    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()),
        )
예제 #2
0
    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])
예제 #3
0
 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
예제 #4
0
 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
예제 #5
0
 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)
예제 #6
0
 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())
예제 #7
0
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
예제 #9
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_)
예제 #10
0
 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
예제 #11
0
    ('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()]),
예제 #12
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]",
        ),
        (NullType.instance(), "null"),
    ],
)
def test_boolean_str(type_, expected):
    assert str(type_) == expected
예제 #13
0
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()
예제 #14
0
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",
예제 #15
0
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')
예제 #16
0
    ],
)
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",
예제 #17
0
    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)
예제 #18
0
 def boolean(self, tree):
     """
     Compiles a boolean tree.
     """
     assert tree.data == 'boolean'
     return base_symbol(BooleanType.instance())
예제 #19
0
    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={}),
        ),
        (