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)
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])
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)
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
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)
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)