Пример #1
0
def generate_parameter(decl_params):
    # holds working parameters
    param = {}
    for item in decl_params.content:
        if isinstance(item, ASTNode):
            # handle parameter prefix
            if item.name == 'func_param_prefix':
                # add passing by reference
                if item.content[0].type == 'AMP':
                    param['reference'] = True
                # add constant parameters
                elif item.content[0].type == '@':
                    param['const'] = True
            # specify type
            elif item.name == 'extension':
                param['data_type'] = generate_type(item.content[-1])
            # handle initializer
            elif item.name == 'func_initializer':
                param['default_value'] = generate_expr(item.content[-1])
                param['data_type'] = param['default_value'].data_type
                param['optional'] = True
        else:
            # handle indefinite params
            if item.type == '...':
                param['indefinite'] = True
            # add name
            elif item.type == 'IDENTIFIER':
                param['name'] = item.value
    # generate parameter object
    return type('Object', (), param)
Пример #2
0
def generate_statement(stmt, context: Context):
    return {
        # handle return
        'return_stmt': generate_return,
        # handle yield
        # yield uses same function, b/c it differentiates
        'yield_stmt': generate_return,
        # generate break statement and check context
        'break_stmt': lambda s, a: StatementNode('Break') if context.break_context else errormodule.throw('semantic_error', 'Invalid context for break statement',
                                                                                                           stmt),
        # generate continue statement and check context
        'continue_stmt': lambda s, a: StatementNode('Continue') if context.continue_context else errormodule.throw('semantic_error', 'Invalid context for continue statement',
                                                                                                                    stmt),
        # generate throw statement
        'throw_stmt': lambda s: StatementNode('Throw', generate_expr(s.content[1])),
        # generate variable with no modifiers
        'variable_declaration': lambda s: generate_variable_declaration(s, []),
        # generate variable with external modifier
        'external_stmt': lambda s: generate_variable_declaration(s.content[1].content[0], [Modifiers.EXTERNAL]),
        # generate variable with volatile and possibly external modifiers
        'lock_stmt': lambda s: generate_variable_declaration(s.content[-1], [Modifiers.LOCK] if s.content[1].name != 'extern' else [Modifiers.LOCK,
                                                                                                                                        Modifiers.EXTERNAL]),
        # generate assignment / function call statement
        'assignment': generate_assignment,
        # generate delete statement
        'delete_stmt': generate_delete
    # subscript to get statement name and then call function, pass in context if necessary
    }[stmt.name](*([stmt, context] if stmt.name in {'yield_stmt', 'return_stmt', 'break_stmt', 'continue_stmt'} else [stmt]))
Пример #3
0
def compile_parameters(param_ast):
    params = []
    expr = ''

    if not isinstance(param_ast, ASTNode):
        return params

    for item in param_ast.content:
        if isinstance(item, ASTNode):
            if item.name == 'expr':
                expr = item
            elif item.name == 'named_param':
                ue = unparse(expr)
                if len(ue) > 1 or isinstance(
                        ue[0], ASTNode) or ue[0].type != 'IDENTIFIER':
                    errormodule.throw(
                        'semantic_error', 'Invalid parameter name', param_ast.
                        content[0 if isinstance(param_ast.content[0], ASTNode
                                                ) else 1])
                expr = (ue[0].value, generate_expr(item.content[1]))
            elif item.name == 'n_param':
                params += compile_parameters(item)
    return [generate_expr(expr) if isinstance(expr, ASTNode) else expr
            ] + params
Пример #4
0
 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]
Пример #5
0
 def generate_variable_dict(ast):
     for item in ast.content:
         # if is an AST
         if isinstance(item, ASTNode):
             # add extension
             if item.name == 'extension':
                 variable['extension'] = generate_type(item.content[-1])
             # add initializer and constexpr
             elif item.name == 'initializer':
                 variable['initializer'] = generate_expr(item.content[-1])
                 # check for constexpr from initializer operator
                 variable['constexpr'] = item.content[0].type == ':='
                 # perform constexpr check if is constexpr
                 if variable['constexpr']:
                     if not check_constexpr(variable['initializer']):
                         errormodule.throw('semantic_error', 'Expected constexpr', item.content[-1])
             # recur and continue building variable dictionary
             elif item.name == 'multi_var':
                 add_final_variable()
                 generate_variable_dict(item)
         # otherwise assume token and check if it identifier
         elif item.type == 'IDENTIFIER':
             variable['name'] = item
Пример #6
0
def generate_type(ext):
    pointers = 0
    # handle std types (from extension)
    if ext.name == 'types':
        if ext.content[0].name == 'deref_op':
            # extract data type pointers
            pointers = len(unparse(ext.content[0]))
        # update ext to simple types
        ext = ext.content[-1]  # selects last element (always simple types)
    # if it is token, assume array, list or dict
    if isinstance(ext.content[0], Token):
        # assume array
        if ext.content[0].type == 'ARRAY_TYPE':
            # ext.content[1].content[1] == pure_types -> array_modifier -> types
            et = generate_type(ext.content[1].content[1])
            # extract count value
            # ext.content[1].content[1] == pure_types -> array_modifiers -> expr
            error_ast = ext.content[1].content[3]
            try:
                count = get_array_bound(
                    generate_expr(ext.content[1].content[3]))
            except IndexError:
                errormodule.throw('semantic_error', 'Index out of range',
                                  error_ast)
                return
            if not count and count != 0:
                errormodule.throw('semantic_error',
                                  'Non-constexpr array bound', error_ast)
            elif type(count) == bool:
                errormodule.throw('semantic_error',
                                  'Invalid value for array bound', error_ast)
            try:
                _ = count < 0
                count = float(count)
            except (ValueError, TypeError):
                errormodule.throw('semantic_error',
                                  'Invalid value for array bound', error_ast)
            if not count.is_integer():
                errormodule.throw('semantic_error',
                                  'Invalid value for array bound', error_ast)
            elif count < 1:
                errormodule.throw('semantic_error',
                                  'Invalid value for array bound', error_ast)
            return types.ArrayType(et, int(count), pointers)
        # assume list
        elif ext.content[0].type == 'LIST_TYPE':
            # ext.content[1].content[1] == pure_types -> list_modifier -> types
            return types.ListType(generate_type(ext.content[1].content[1]),
                                  pointers)
        # assume function
        elif ext.content[0].type in {'FUNC', 'ASYNC'}:
            params, return_types = None, None
            for item in ext.content[1].content:
                if not isinstance(item, Token):
                    if item.name == 'func_params_decl':
                        params = generate_parameter_list(item)
                    elif item.name == 'rt_type':
                        return_types = get_return_from_type(item)
            return types.Function(params, return_types, 0,
                                  ext.content[0].value == 'ASYNC', False)
        # assume dict
        else:
            # ext.content[1].content[1] == pure_types -> dict_modifier -> types
            kt, vt = generate_type(ext.content[1].content[1]), generate_type(
                ext.content[1].content[3])
            # check mutability
            if types.mutable(kt):
                errormodule.throw('semantic_error',
                                  'Invalid key type for dictionary',
                                  ext.content[1].content[1])
            # compile dictionary type
            return types.MapType(kt, vt, pointers)
    else:
        if ext.content[0].name == 'pure_types':
            # data type literal
            if ext.content[0].content[0].type == 'DATA_TYPE':
                return types.DataTypeLiteral(types.DataTypes.DATA_TYPE)
            # return matched pure types
            return types.DataType({
                'INT_TYPE': types.DataTypes.INT,
                'BOOL_TYPE': types.DataTypes.BOOL,
                'BYTE_TYPE': types.DataTypes.BYTE,
                'FLOAT_TYPE': types.DataTypes.FLOAT,
                'LONG_TYPE': types.DataTypes.LONG,
                'COMPLEX_TYPE': types.DataTypes.COMPLEX,
                'STRING_TYPE': types.DataTypes.STRING,
                'CHAR_TYPE': types.DataTypes.CHAR,
                'OBJECT_TYPE': types.OBJECT_TEMPLATE,
            }[ext.content[0].content[0].type], 0)
        else:
            # get root symbol
            name = ext.content[0].content[0].value
            if name.type == 'THIS':
                sym = modules.get_instance()
            else:
                sym = util.symbol_table.look_up(name)
            # hold previous symbol ASTNode for error messages
            prev_sym = ext.content[0].content[0]
            # extract outer symbols if necessary
            if len(ext.content[0].content) > 1:
                content = ext.content[0].content[1:]
                for item in content:
                    if isinstance(item, Token):
                        if item.type == 'IDENTIFIER':
                            # make sure symbol is a custom type
                            if not isinstance(sym, types.CustomType):
                                errormodule.throw(
                                    'semantic_error',
                                    'Object is not a valid is a data type',
                                    prev_sym)
                            identifier = item.value
                            # get member for modules
                            if sym.data_type.data_type == types.DataTypes.MODULE:
                                # get and check property
                                prop = modules.get_property(
                                    sym.data_type, identifier)
                                if not prop:
                                    errormodule.throw(
                                        'semantic_error',
                                        'Object has no member \'%s\'' %
                                        identifier, item)
                                # update previous symbol
                                prev_sym = item
                                # update symbol
                                sym = prop
                            # invalid get member on interfaces
                            elif sym.data_type.data_type == types.DataTypes.INTERFACE:
                                errormodule.throw(
                                    'semantic_error',
                                    '\'.\' is not valid for this object', item)
                            # assume struct or enum
                            else:
                                for member in sym.data_type.members:
                                    # if there is a match, extract value
                                    if member.name == identifier:
                                        prev_sym = item
                                        sym = member
                                        # break to prevent else condition
                                        break
                                # if there is no match, throw error
                                else:
                                    errormodule.throw(
                                        'semantic_error',
                                        'Object has no member \'%s\'' %
                                        identifier, item)
                    # continue recursive for loop
                    elif item.name == 'dot_id':
                        content.extend(item.content)
            # final check for invalid data types
            if not isinstance(sym, types.CustomType):
                errormodule.throw('semantic_error',
                                  'Object is not a valid is a data type',
                                  prev_sym)
            # add instance marker if necessary
            if sym.data_type.data_type != types.DataTypes.INTERFACE:
                sym.data_type.instance = True
            return sym.data_type
Пример #7
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)
Пример #8
0
def generate_return(stmt, context):
    # generate return or yield if possible
    if context.return_context:
        return StatementNode('Return' if stmt.name == 'return_stmt' else 'Yield', generate_expr(stmt.content[1]))
    else:
        errormodule.throw('semantic_error', 'Invalid context for ' + ('yield statement' if stmt.name == 'yield_stmt' else 'return statement'), stmt)
Пример #9
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)