Exemple #1
0
def generate_assignment(assignment):
    # await = if 'await' is present, same for new, root is root variable
    await, new, root = False, False, None
    for item in assignment.content:
        # NOTE assumes everything is an AST
        # mark await if it is encountered
        if item.name == 'await':
            await = True
        # mark new if it is encounted
        elif item.name == 'new':
            new = True
        # generate the assignment variable if the given AST is encountered
        elif item.name == 'assign_var':
            root = generate_assign_var(item)
        # add trailer if it detected
        elif item.name == 'trailer':
            root = add_trailer(root, item)
        # handle extended assignment expression
        elif item.name == 'assignment_expr':
            # await and new are invalid on assignment
            if await or new:
                errormodule.throw('semantic_error', 'Invalid operands for %s operator' % 'await' if await else 'new', item)
            root = generate_assignment_expr(root, item)
    # check await
    if await:
        # if it is not returning and Incomplete Type, an async function was not called
        if not isinstance(root.data_type, types.Future):
            errormodule.throw('semantic_error', 'Unable to await object', assignment)
        root = StatementNode('Expr', ExprNode('Await', root.data_type.data_type, root))
    # check new
    if new:
        expr = None
        # if it is not a structure of a group
        if root.data_type != types.DataTypes.MODULE:
            # if it is a data type
            if isinstance(root.data_type, types.DataTypeLiteral):
                # get new pointer type
                dt = copy(root.data_type)
                dt.pointers += 1
                # return memory allocation with size of type
                expr = ExprNode('Malloc', dt, ExprNode('SizeOf', types.DataType(types.DataTypes.INT, 1), root))
            else:
                # if it is not an integer
                if root.data_type != types.DataType(types.DataTypes.INT, 0):
                    # all tests failed, not allocatable
                    errormodule.throw('semantic_error', 'Unable to dynamically allocate memory for object', assignment)
                else:
                    # malloc for just int size
                    expr = ExprNode('Malloc', types.VOID_PTR, root)
        else:
            dt = copy(root.data_type)
            dt.instance = True
            # return object instance
            expr = ExprNode('CreateObjectInstance', dt, root)
        root = StatementNode('Expr', expr)
    # return compiled root
    return root
Exemple #2
0
def generate_expr(expr):
    # hold the resulting expr
    root = None
    for item in expr.content:
        # only possible value is ASTNode
        # if it is a root ASTNode, generate it
        if item.name == 'or':
            root = generate_logical(item)
        # if the root has a continued expr
        elif item.name == 'n_expr':
            # if it is InlineIf
            if item.content[0].type == '?':
                # root ? val1 : val2
                val1 = generate_logical(item.content[1])
                val2 = generate_logical(item.content[3])
                # ensure root is boolean
                if not types.boolean(root.data_type):
                    errormodule.throw(
                        'semantic_error',
                        'Comparison expression of inline comparison must be a boolean',
                        expr.content[0])
                # get the dominant resulting type
                dt = types.dominant(val1.data_type, val2.data_type)
                if not dt:
                    dt = types.dominant(val2.data_type, val1.data_type)
                    # if neither can be overruled by each other, throw error
                    if not dt:
                        errormodule.throw(
                            'semantic_error',
                            'Types of inline comparison must be similar', item)
                return ExprNode('InlineCompare', dt, root, val1, val2)
            # otherwise, it is Null Coalescence
            else:
                # recursive while loop
                n_expr = item
                while n_expr.name == 'n_expr':
                    # get root expression
                    logical = generate_logical(n_expr.content[1])
                    # ensure the root and logical are coercible / equivalent
                    if types.coerce(root.data_type, logical.data_type):
                        root = ExprNode('NullCoalesce', root.data_type, root,
                                        logical)
                    # otherwise it is an invalid null coalescence
                    else:
                        errormodule.throw(
                            'semantic_error',
                            'Types of null coalescing must be similar', expr)
                    # recur
                    n_expr = n_expr.content[-1]
    # return final expr
    return root
Exemple #3
0
def generate_assign_var(assign_var):
    # generate identifier
    def generate_id_type(id_type):
        # handle this token
        # uses Module.get_instance()
        if id_type.content[0].type == 'THIS':
            return get_instance()
        # otherwise look up symbol and return identifier
        else:
            sym = util.symbol_table.look_up(id_type.content[0].value)
            if not sym:
                errormodule.throw('semantic_error', 'Variable \'%s\' not defined' % id_type.content[0].value, id_type)
            return Identifier(sym.name, sym.data_type, Modifiers.CONSTANT in sym.modifiers)

    # if there is single ASTNode, assume the it is id_types
    if isinstance(assign_var.content[0], ASTNode):
        # return generate id type
        return generate_id_type(assign_var.content[0])
    # there is a dereference operator
    else:
        # check if there is an id type after the dereference operator
        if isinstance(assign_var.content[-1].content[0], ASTNode):
            root = generate_id_type(assign_var.content[-1].content[0])
        # otherwise generate sub var
        else:
            root = generate_assign_var(assign_var.content[-1].content[1])
            # check for trailer
            if len(assign_var.content[-1].content) > 3:
                root = add_trailer(root, assign_var.content[-1].content[2])
        # calculate the dereference count
        deref_count = 1 if len(assign_var.content) == 2 else len(unparse(assign_var.content[1])) + 1
        # check for non-pointer dereference
        if deref_count > root.data_type.pointers:
            errormodule.throw('semantic_error', 'Unable to dereference a non-pointers', assign_var)
        dt = copy(root.data_type)
        dt.pointers -= deref_count
        return ExprNode('Dereference', dt, deref_count, root)
Exemple #4
0
def generate_logical(logical):
    # if it is a comparison, pass it on to the next generator
    if logical.name == 'comparison':
        return generate_comparison(logical)
    # unpack tree if it exists
    if len(logical.content) > 1:
        # hold the unpacked tree
        unpacked_tree = logical.content[:]
        # semi-recursive for loop to unpack tree
        for item in unpacked_tree:
            # if it is an ASTNode
            if isinstance(item, ASTNode):
                # and falls into the 'n' categories
                if item.name.startswith('n'):
                    # unpack it
                    unpacked_tree += unpacked_tree.pop().content
        # root holds the next level downward, op = operator being used
        root, op = generate_logical(unpacked_tree.pop(0)), None
        # iterate through unpacked tree
        for item in unpacked_tree:
            # main tree
            if isinstance(item, ASTNode):
                # get next comparison tree if it is a comparison otherwise get next logical tree
                tree = generate_comparison(
                    item) if item.name == 'comparison' else generate_logical(
                        item)
                # if both are simple data types
                if isinstance(tree.data_type, types.DataType) and isinstance(
                        root.data_type, types.DataType):
                    # check booleans and generate boolean operators
                    if tree.data_type.data_type == types.DataTypes.BOOL and tree.data_type.pointers == 0 and \
                                    root.data_type.data_type == types.DataTypes.BOOL and root.data_type.pointers == 0:
                        root = ExprNode(
                            op, types.DataType(types.DataTypes.BOOL, 0), root,
                            tree)
                        continue
                    # generate bitwise operators
                    else:
                        # extract dominant type and if there is not one, throw error
                        dom = types.dominant(root.data_type, tree.data_type)
                        if dom:
                            if not types.coerce(
                                    types.DataType(types.DataTypes.INT, 0),
                                    dom):
                                errormodule.throw(
                                    'semantic_error',
                                    'Unable to apply bitwise %s to object' %
                                    op.lower(), logical)
                            root = ExprNode('Bitwise' + op, dom, root, tree)
                        else:
                            if not types.coerce(
                                    types.DataType(types.DataTypes.INT, 0),
                                    tree.data_type):
                                errormodule.throw(
                                    'semantic_error',
                                    'Unable to apply bitwise %s to object' %
                                    op.lower(), logical)
                            root = ExprNode('Bitwise' + op, tree.data_type,
                                            root, tree)
                # handle operator overloading
                elif isinstance(root.data_type, types.CustomType):
                    # get and check method
                    method = modules.get_property(tree.data_type,
                                                  '__%s__' % op.lower())
                    functions.check_parameters(method, [tree], item)
                    if method:
                        root = ExprNode('Call', method.data_type.return_type,
                                        method, tree)
                    else:
                        errormodule.throw(
                            'semantic_error',
                            'Object has no method \'__%s__\'' % op.lower(),
                            logical)
                else:
                    errormodule.throw(
                        'semantic_error',
                        'Unable to apply bitwise operator to object', logical)
            # only token is operator
            else:
                name = item.type.lower()
                op = name[0].upper() + name[1:]
        return root
    # otherwise recur to next level down
    else:
        return generate_logical(logical.content[0])
Exemple #5
0
def generate_unary_atom(u_atom):
    # generate hold atom
    atom = generate_atom(u_atom.content[-1])
    if len(u_atom.content) > 1:
        # check for packages
        if isinstance(atom, Package):
            errormodule.throw('semantic_error',
                              'Unable to apply operator to package', u_atom)
            return
        # check for tuples
        if isinstance(atom.data_type, types.Tuple):
            errormodule.throw('semantic_error',
                              'Unable to apply operator to multiple values',
                              u_atom)
            return
        # check data type literal
        if isinstance(atom.data_type, types.DataTypeLiteral):
            errormodule.throw('semantic_error',
                              'Unable to apply operator to Data Type literal',
                              u_atom)
            return
        # check for template
        if isinstance(atom.data_type, types.Template):
            errormodule.throw('semantic_error',
                              'Unable to apply operator to template', u_atom)
            return
        prefix = u_atom.content[0].content[0]
        # handle sine change
        if prefix.type == '-':
            # test for numericality
            if types.numeric(atom.data_type):
                # handle modules
                if isinstance(atom.data_type, types.CustomType):
                    invert_method = modules.get_property(
                        atom.data_type, '__invert__')
                    return ExprNode('Call',
                                    invert_method.data_type.return_type,
                                    invert_method)
                # change the sine of an element
                else:
                    return ExprNode('ChangeSine', atom.data_type, atom)
            else:
                # throw error
                errormodule.throw(
                    'semantic_error',
                    'Unable to change sine on non-numeric type.', u_atom)
        elif prefix.type == 'AMP':
            dt = copy(atom.data_type)
            # create pointer
            dt.pointers += 1
            # reference pointer
            return ExprNode('Reference', dt, atom)
        # handle deref op
        elif prefix.type == '*':
            do = len(unparse(u_atom.content[0].content[1])) + 1 if len(
                u_atom.content[0].content) > 1 else 1
            # handle pointer error
            # < because that means there is more dereferencing than there are references to dereference
            if atom.data_type.pointers < do:
                errormodule.throw('semantic_error',
                                  'Unable to dereference a non-pointer',
                                  u_atom.content[0])
            elif isinstance(atom.data_type, types.VoidPointer):
                if atom.data_type.pointers <= do:
                    errormodule.throw('semantic_error',
                                      'Unable to dereference void pointer',
                                      u_atom.content[0])
                vp = copy(atom.data_type)
                vp.pointers -= 1
                return ExprNode('Dereference', vp, do, atom)
            else:
                dt = copy(atom.data_type)
                dt.pointers -= do
                # return dereference with count
                return ExprNode('Dereference', dt, do, atom)

    else:
        return atom
Exemple #6
0
def generate_arithmetic(ari):
    # names of overload methods
    method_names = {
        '+': '__add__',
        '-': '__sub__',
        '*': '__mul__',
        '/': '__div__',
        '%': '__mod__',
        '^': '__pow__'
    }
    # unpack tree in operator sets
    unpacked_tree = ari.content[:]
    while unpacked_tree[-1].name in {'add_sub_op', 'mul_div_mod_op', 'exp_op'}:
        unpacked_tree += unpacked_tree.pop().content
    # root = base tree, op = operator, tree = resulting tree
    root, op, tree = None, None, None
    for item in unpacked_tree:
        # ast => expression
        if isinstance(item, ASTNode):
            # if there is an operator, generate arithmetic tree
            if op:
                # generate next value
                val = generate_unary_atom(
                    item
                ) if item.name == 'unary_atom' else generate_arithmetic(item)
                # check operands and extract data type
                dt = check_operands(root.data_type, val.data_type, op, ari)
                if tree:
                    # operator overloading
                    if isinstance(tree.data_type, types.CustomType):
                        # convert to method name
                        name = method_names[op]
                        # get method and check parameters
                        method = modules.get_property(tree.data_type, name)
                        # assume method exists
                        functions.check_parameters(method.data_type, [val],
                                                   item)
                        tree = ExprNode('Call', method.data_type.return_type,
                                        method, [tree])
                    # add arguments to previous tree (allow for multiple items)
                    elif tree.name == op:
                        tree.arguments.append(val)
                    # create expression node from tree
                    else:
                        tree = ExprNode(op, dt, tree, val)
                # create root tree
                else:
                    # operator overloading
                    if isinstance(root.data_type, types.CustomType):
                        # convert to method name
                        name = method_names[op]
                        # get method and check parameters
                        method = modules.get_property(root.data_type, name)
                        # assume method exists
                        functions.check_parameters(method.data_type, [val],
                                                   item)
                        tree = ExprNode('Call', method.data_type.return_type,
                                        method, [root])
                    # default generation
                    else:
                        tree = ExprNode(op, dt, root, val)
                op, root, = None, tree
            # otherwise, generate root
            else:
                root = generate_unary_atom(
                    item
                ) if item.name == 'unary_atom' else generate_arithmetic(item)
        # token => operator
        else:
            op = item.value
    # ensure tree is initialized
    if not tree:
        tree = root
    return tree
Exemple #7
0
def generate_shift(shift):
    # if there is a shift performed
    if len(shift.content) > 1:
        # extract operator-expr list
        unpacked_tree = shift.content[:]
        while unpacked_tree[-1].name == 'n_shift':
            unpacked_tree += unpacked_tree.pop().content
        # get first expression
        root = generate_arithmetic(unpacked_tree.pop(0))
        # check for overload flag
        overload = False
        # check root data type
        if not isinstance(root.data_type, types.DataType):
            if isinstance(root.data_type, types.CustomType):
                overload = True
            else:
                errormodule.throw(
                    'semantic_error',
                    'Invalid type for left operand of binary shift',
                    shift.content[0])
        # operator used
        op = ''
        for item in unpacked_tree:
            # ast node => arithmetic expr
            if isinstance(item, ASTNode):
                # generate next tree element
                tree = generate_arithmetic(item)
                # check for overloads
                if overload:
                    method = modules.get_property(root.data_type,
                                                  '__%s__' % op.lower())
                    if not method:
                        errormodule.throw(
                            'semantic_error',
                            'Invalid type for left operand of binary shift',
                            shift.content[0])
                    functions.check_parameters(method.data_type, [tree], item)
                    overload = isinstance(method.data_type.return_type,
                                          types.CustomType)
                    root = ExprNode('Call', method.data_type.return_type,
                                    method, [tree])

                # type check element
                if isinstance(tree.data_type, types.DataType):
                    # ensure it is an integer (binary shifts can only be continued by integers)
                    if tree.data_type.data_type == types.DataTypes.INT and tree.data_type.pointers == 0:
                        root = ExprNode(op, root.data_type, root, tree)
                        continue
                errormodule.throw(
                    'semantic_error',
                    'Invalid type for right operand of binary shift', item)
            # token => operator
            else:
                if item.type == '<<':
                    op = 'Lshift'
                elif item.type == '>>':
                    op = 'ARshift'
                else:
                    op = 'LRshift'
        return root
    # otherwise, pass on to arithmetic parser
    else:
        return generate_arithmetic(shift.content[0])
Exemple #8
0
def generate_comparison(comparison):
    # generate shift if it is a shift tree (because the comparison generator is recursive)
    if comparison.name == 'shift':
        return generate_shift(comparison)
    # evaluate comparison if there is one
    if len(comparison.content) > 1:
        # generate inversion operator
        if comparison.name == 'not':
            # generate base tree to invert
            tree = generate_shift(comparison.content[1])
            # check for data types
            if isinstance(tree.data_type, types.DataType):
                # unable to ! pointers
                if tree.data_type.pointers > 0:
                    errormodule.throw(
                        'semantic_error',
                        'The \'!\' operator is not applicable to pointer',
                        comparison)
                # generate normal not operator
                return ExprNode('Not', tree.data_type, tree)
            # generate overloaded not operator
            elif isinstance(tree.data_type, types.CustomType):
                # get and check overloaded not method
                not_method = modules.get_property(tree.data_type, '__not__')
                functions.check_parameters(not_method, tree, comparison)
                if not_method:
                    return ExprNode('Call', not_method.data_type.return_type,
                                    not_method, tree)
                else:
                    errormodule.throw('semantic_error',
                                      'Object has no method \'__not__\'',
                                      comparison)
            else:
                errormodule.throw(
                    'semantic_error',
                    'The \'!\' operator is not applicable to object',
                    comparison)
        # otherwise generate normal comparison operator
        else:
            # comparison op method dictionary
            comparison_methods = {
                '<=': '__lteq__',
                '>=': '__gteq__',
                "<": '__lt__',
                '>': '__gt__',
                '==': '__eq__',
                '!=': '__neq__',
                '===': '__seq__',
                '!==': '__sneq__'
            }
            # unpack tree into expressions and operators
            unpacked_tree = comparison.content[:]
            for item in unpacked_tree:
                if item.name == 'n_comparison':
                    unpacked_tree += unpacked_tree.pop().content
            # root = first element in unpacked tree, op = operator
            root, op = generate_comparison(unpacked_tree.pop(0)), None
            for item in unpacked_tree:
                # all elements are ASTs
                # not is base expression
                if item.name == 'not':
                    # extract next operator tree
                    n_tree = generate_comparison(item)
                    # check for overloads
                    if isinstance(root.data_type, types.CustomType):
                        method = modules.get_property(root.data_type,
                                                      comparison_methods[op])
                        if not method:
                            if op in {'<=', '>=', '<', '>'}:
                                errormodule.throw(
                                    'semantic_error',
                                    'Unable to use numeric comparison with non-numeric type',
                                    comparison)
                        else:
                            functions.check_parameters(method.data_type,
                                                       [n_tree], comparison)
                            root = ExprNode('Call',
                                            method.data_type.return_type,
                                            method, [n_tree])
                    # check numeric comparison
                    if op in {'<=', '>=', '<', '>'}:
                        # check invalid overloads
                        if isinstance(n_tree.data_type, types.CustomType):
                            errormodule.throw(
                                'semantic_error',
                                'Invalid type match up for numeric comparison',
                                comparison)
                        if types.numeric(n_tree.data_type) and types.numeric(
                                root.data_type):
                            root = ExprNode(
                                op, types.DataType(types.DataTypes.BOOL, 0),
                                root, n_tree)
                        else:
                            errormodule.throw(
                                'semantic_error',
                                'Unable to use numeric comparison with non-numeric type',
                                comparison)
                    # generate standard comparison
                    elif op in {'==', '!=', '===', '!=='}:
                        root = ExprNode(
                            op, types.DataType(types.DataTypes.BOOL, 0), root,
                            n_tree)
                # if it is not a base expression, it is an operators
                elif item.name == 'comparison_op':
                    op = item.content[0].value
            return root
    # otherwise recur
    else:
        return generate_comparison(comparison.content[0])