Ejemplo n.º 1
0
 def add_final_variable():
     # holding dictionary
     final_variable = {}
     # add extension if it exists
     if 'extension' in variable:
         final_variable['data_type'] = variable['extension']
     # if the variable has an initializer
     if 'initializer' in variable:
         # if it has a data type, type check the initializer
         if 'data_type' in final_variable:
             if not types.coerce(final_variable['data_type'], variable['initializer'].data_type):
                 errormodule.throw('semantic_error', 'Variable type extension and initializer data types do not match', variable['name'])
         else:
             # else infer from initializer
             final_variable['data_type'] = variable['initializer'].data_type
         # add initializer to variable
         final_variable['initializer'] = variable['initializer']
         # add constexpr designation
         final_variable['constexpr'] = variable['constexpr']
     # add synthesized object to variables
     variables[variable['name']] = type('Object', (), final_variable)
Ejemplo n.º 2
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])
Ejemplo n.º 3
0
def check_operands(dt1, dt2, operator, ast):
    # check for custom type mismatch
    if not isinstance(dt1, types.CustomType) and isinstance(
            dt2, types.CustomType):
        errormodule.throw('semantic_error',
                          'Invalid type match up for numeric operator', ast)
    # check for invalid pointer arithmetic
    if dt1.pointers > 0:
        if isinstance(dt2, types.DataType):
            if dt2.data_type == types.DataTypes.INT and dt2.pointers == 0:
                return dt1
        errormodule.throw(
            'semantic_error',
            'Pointer arithmetic can only be performed between an integer and a pointer',
            ast)
    # check addition operator
    if operator == '+':
        # numeric addition
        if types.numeric(dt1) and types.numeric(dt2):
            if types.coerce(dt1, dt2):
                return dt2
            return dt1
        # list / string concatenation
        elif types.enumerable(dt1) and types.enumerable(dt2):
            if dt1 == dt2:
                return dt1
            # check arrays and lists
            elif (isinstance(dt1, types.ArrayType) or isinstance(dt1, types.ListType)) and \
                    (isinstance(dt2, types.ArrayType) or isinstance(dt2, types.ListType)):
                if dt1.element_type == dt2.element_type:
                    return dt1
            errormodule.throw(
                'semantic_error',
                'Unable to apply operator to dissimilar enumerable types', ast)
        errormodule.throw('semantic_error',
                          'Invalid type(s) for operator \'%s\'' % operator,
                          ast)
    # check multiply operator
    elif operator == '*':
        # numeric multiplication
        if types.numeric(dt1) and types.numeric(dt2):
            if types.coerce(dt1, dt2):
                return dt2
            return dt1
        # string multiplication
        if isinstance(dt1, types.DataType) and isinstance(dt2, types.DataType):
            # we can assume val1's pointers
            if dt2.pointers == 0:
                if dt1.data_type == types.DataTypes.STRING and dt2.data_type == types.DataTypes.INT:
                    return dt1
        errormodule.throw('semantic_error',
                          'Invalid type(s) for operator \'%s\'' % operator,
                          ast)
    # check all other operators
    else:
        if types.numeric(dt1) and types.numeric(dt2):
            if types.coerce(dt1, dt2):
                return dt2
            return dt1
        errormodule.throw('semantic_error',
                          'Invalid type(s) for operator \'%s\'' % operator,
                          ast)
Ejemplo n.º 4
0
def get_return_type(function_body):
    def generate_returns(rt_expr):
        # hold return types
        rt_types = []
        for elem in rt_expr.content:
            if isinstance(elem, ASTNode):
                # if it is an expr, assume it is part of return
                if elem.name == 'expr':
                    # add to return types
                    rt_types.append(generate_expr(elem))
                # if there are more/multiple return expression, add those to the return type list as well
                elif elem.name == 'n_rt_expr':
                    # get a set of return types and decide how to add them to the list
                    n_types = generate_returns(elem)
                    if isinstance(n_types, list):
                        rt_types += n_types
                    else:
                        rt_types.append(n_types)
        # if there are no returns, return None
        if len(rt_types) == 0:
            return
        # return a list if there are multiple or just one if there is only 1
        return rt_types if len(rt_types) > 1 else rt_types[0]

    # return type holder
    rt_type = None
    # if it has encountered a return value
    is_rt_type = False
    # if it is a generator
    generator = False
    for item in function_body.content:
        if isinstance(item, ASTNode):
            if item.name == 'return_stmt':
                if len(item.content) > 1:
                    # generate new type from return expr
                    n_type = generate_returns(item.content[1])
                    # if they are not equal and there is a return type
                    if n_type != rt_type and is_rt_type and not coerce(
                            rt_type, n_type):
                        errormodule.throw('semantic_error',
                                          'Inconsistent return type', item)
                    else:
                        rt_type = n_type
                else:
                    # if the there is an rt type and it is not null
                    if is_rt_type and rt_type:
                        errormodule.throw('semantic_error',
                                          'Inconsistent return type', item)
                        # no need to update as it is already null
                is_rt_type = True
            elif item.name == 'yield_stmt':
                if len(item.content) > 1:
                    # generate new type from return expr
                    n_type = generate_returns(item.content[1])
                    # if they are not equal and there is a return type
                    if n_type != rt_type and is_rt_type and not coerce(
                            rt_type, n_type):
                        errormodule.throw('semantic_error',
                                          'Inconsistent return type', item)
                    else:
                        rt_type = n_type
                else:
                    # if the there is an rt type and it is not null
                    if is_rt_type and rt_type:
                        errormodule.throw('semantic_error',
                                          'Inconsistent return type', item)
                        # no need to update as it is already null
                is_rt_type = True
                generator = True
            else:
                # get type from rest of function
                temp_type = get_return_type(item)
                # if the types are inconsistent
                if is_rt_type and temp_type != rt_type:
                    errormodule.throw('semantic_error',
                                      'Inconsistent return type', item)
                # otherwise update return type
                else:
                    rt_type = temp_type
    # since rt_type will be evaluated on the basis of not being a direct data type
    # return None is ok
    return rt_type[0].data_type if isinstance(rt_type,
                                              tuple) else rt_type, generator
Ejemplo n.º 5
0
def generate_variable_declaration(stmt, modifiers):
    # is constant
    constant = False
    # for multideclaration: holds set of variables
    variables = {}
    # main type extension
    overall_type = None
    # main variable initializer
    initializer = None
    # is marked constexpr
    constexpr = False
    # iterate to generate statement components
    for item in stmt.content:
        if isinstance(item, ASTNode):
            # generate variable if name given
            if item.name == 'var':
                variables = generate_var(item)
            # set overall type
            elif item.name == 'extension':
                overall_type = generate_type(item.content[1])
            # add initializer
            elif item.name == 'initializer':
                initializer = generate_expr(item.content[1])
                # set constexpr if := operator used
                constexpr = item.content[0].type == ':='
        # set constant if @ operator used instead of $
        elif item.type == '@':
            constant = True
    # add constant to modifiers if variable is marked constant
    if constant:
        modifiers.append(Modifiers.CONSTANT)
    # all constexpr variables must also be constant
    if constexpr and not constant:
        errormodule.throw('semantic_error', 'Declaration of constexpr on non-constant', stmt)
    # if multi-declaration was used
    if isinstance(variables, dict):
        # position in variable set
        pos = 0
        # iterate through variable dictionary
        # k = identifier token, v = generated variable object
        for k, v in variables.items():
            # handle variables marked constexpr in non constant environment
            if v.constexpr and not constant:
                errormodule.throw('semantic_error', 'Declaration of constexpr on non-constant', stmt)
            # if there is a global initializer
            if initializer:
                # if it is tuple based initializer
                if isinstance(initializer.data_type, types.Tuple):
                    # if the variable is still within the domain of the global initializer
                    if pos < len(initializer.data_type.values):
                        # if is doesn't have a data type, add the one provided by the initializer
                        if not hasattr(v, 'data_type'):
                            setattr(v, 'data_type', initializer.data_type.values[pos].data_type)
                        # handle invalid double initializers, ie $(x = 2, y) = tupleFunc();
                        elif hasattr(v, 'initializer'):
                            errormodule.throw('semantic_error', '%s cannot have two initializers' % ('constant' if constant else 'variable'), k)
                        # if there is a type mismatch between the type extension and the given initializer value
                        elif not types.coerce(v.data_type, initializer.data_type.values[pos].data_type):
                            errormodule.throw('semantic_error', 'Variable type extension and initializer data types do not match', k)
                # otherwise, it is invalid
                else:
                    errormodule.throw('semantic_error', 'Multi-%s declaration cannot have single global initializer' % ('constant' if constant else 'variable'), stmt)
            # constexpr value (constexpr(s) store value so they can be evaluated at compile-time)
            val = None
            if v.constexpr:
                val = v.initializer
            # generate symbol object
            # identifier name, data type, modifiers, value (if constexpr)
            sym = Symbol(k.value, v.data_type if hasattr(v, 'data_type') else overall_type, modifiers + [Modifiers.CONSTEXPR] if v.constexpr else modifiers, value=val)
            # if the symbol lacks a data type
            if not sym.data_type:
                errormodule.throw('semantic_error', 'Unable to infer data type of variable', k)
            # if there is a null declared variable (x = null)
            elif not overall_type and isinstance(sym.data_type, types.DataType) and sym.data_type.data_type == types.DataTypes.NULL:
                errormodule.throw('semantic_error', 'Unable to infer data type of variable', k)
            # add variable to symbol table
            util.symbol_table.add_variable(sym, k)
            pos += 1
        # statement name
        name = 'DeclareConstants' if constant else 'DeclareVariables'
        # if there is an initializer, add it to final statement
        if initializer:
            return StatementNode(name, overall_type, dict(zip([k.value for k in variables.keys()], variables.values())), modifiers, initializer)
        return StatementNode(name, overall_type, dict(zip([k.value for k in variables.keys()], variables.values())), modifiers)
    # if only normal declaration was used
    else:
        # handle null declarations (no type extension or initializer)
        if not overall_type and not initializer:
            errormodule.throw('semantic_error', 'Unable to infer data type of variable', stmt)
        # handle null declarations (no type extension and null initializer)
        if not overall_type and isinstance(initializer.data_type, types.DataType) and initializer.data_type.data_type == types.DataTypes.NULL:
            errormodule.throw('semantic_error', 'Unable to infer data type of variable', stmt)
        # check for type extension and initializer mismatch
        if overall_type and initializer and not types.coerce(overall_type, initializer.data_type):
            errormodule.throw('semantic_error', 'Variable type extension and initializer data types do not match', stmt)
        # add constexpr if marked as such
        if constexpr:
            modifiers.append(Modifiers.CONSTEXPR)
            # assume initializer exists
            if not check_constexpr(initializer):
                errormodule.throw('semantic_error', 'Expected constexpr', stmt)
        # add to symbol table
        util.symbol_table.add_variable(Symbol(variables.value, overall_type if overall_type else initializer.data_type, modifiers, None if not constexpr else initializer), stmt)
        # return generated statement node
        return StatementNode('DeclareConstant' if constant else 'DeclareVariable', overall_type, variables.value, initializer, modifiers)
Ejemplo n.º 6
0
def generate_assignment_expr(root, assign_expr):
    # check for traditional assignment
    if isinstance(assign_expr.content[0], ASTNode):
        # NOTE all upper level values are ASTNodes
        # value is used to determine where to begin generating assignment expr
        is_n_assign = int(assign_expr.content[0].name != 'n_assignment')
        # variables is the collection of variables used in assignment (assign vars)
        # initializers is the matching initializer set
        variables, initializers = [root], [generate_expr(assign_expr.content[2 - is_n_assign])]
        # if there are multiple variables
        if assign_expr.content[0].name == 'n_assignment':
            # holding content used in recursive for loop
            assign_content = assign_expr.content[0].content
            for item in assign_content:
                # ignore commas
                if isinstance(item, ASTNode):
                    # check for the assignment variable
                    if item.name == 'assign_var':
                        # add generated assignment variable
                        variables.append(generate_assign_var(item))
                    elif item.name == 'n_assignment':
                        # recur
                        assign_content = item.content
        # if there are multiple expressions
        if assign_expr.content[-1].name == 'n_list':
            # holding content used in recursive for loop
            expressions = assign_expr.content[-1].content
            for item in expressions:
                # ignore commas
                if isinstance(item, ASTNode):
                    # check for expression (initializer)
                    if item.name == 'expr':
                        # generate initializer expression
                        expr = generate_expr(item)
                        # if it is a tuple (multiple values stored in a single expression)
                        if isinstance(expr.data_type, types.Tuple):
                            # add each value to expression set (de-tuple)
                            for elem in expr.data_type.values:
                                initializers.append(elem)
                        # else add raw expr to list
                        else:
                            initializers.append(expr)
                    elif item.name == 'n_list':
                        # recur
                        expressions = item.content
        # check for matching assignment properties (unmodified variables)
        if len(variables) != len(initializers):
            errormodule.throw('semantic_error', 'Assignment value counts don\'t match', assign_expr)
        # get the assignment operator used
        # use offset to calculate it
        op = assign_expr.content[1 - is_n_assign].content[0]
        # iterate through variables and initializers together
        for var, expr in zip(variables, initializers):
            # if the variable is not modifiable
            if not modifiable(var):
                errormodule.throw('semantic_error', 'Unable to modify unmodifiable l-value', assign_expr)
            # if there is a type mismatch
            if not types.coerce(var.data_type, expr.data_type):
                errormodule.throw('semantic_error', 'Variable type and reassignment type do not match', assign_expr)
            # if there is a compound operator
            if op.type != '=' and not types.numeric(var.data_type):
                # all compound operators only work on numeric types
                errormodule.throw('semantic_error', 'Compound assignment operator invalid for non-numeric type', op)

        # return generate statement
        return StatementNode('Assign', op.type, dict(zip(variables, initializers)))
    # else assume increment and decrement
    else:
        # holds whether or not it is increment of decrement
        increment = assign_expr.content[0].type == '+'
        # check if the root is modifiable
        if not modifiable(root):
            errormodule.throw('semantic_error', 'Unable to modify unmodifiable l-value', assign_expr)
        # check if the root is numeric (as only numeric types accept these operators)
        if not types.numeric(root.data_type):
            errormodule.throw('semantic_error', 'Unable to %s non numeric value' % 'increment' if increment else 'decrement', assign_expr)
        # generate statement
        return StatementNode('Increment' if increment else 'Decrement', root)