def validateReturnType(self): for statement in self.content.statements: # find return statement if statement.type is SmallCParser.RETURNSTATEMENT: # validate functions with void as return type if isinstance(self.return_type, VoidType): raise C2PException("return-statement with a value, in function '" + self.identifier + "' returning 'void'") from_type = statement.expression.result_type.getCSymbol() if statement.expression.result_type.is_pointer: from_type += "*" to_type = self.return_type.getCSymbol() if self.return_type.is_pointer: to_type += "*" elif self.return_type.is_reference: to_type += "&" # validate expected return types (basic) if self.return_type.getName() != statement.expression.result_type.getName(): raise C2PException("In return statement of functon '" + self.identifier + "' invalid conversion from " + from_type + " to " + to_type) else: # validate pointers if self.return_type.is_pointer: if statement.expression.result_type.is_pointer and not statement.expression.indirection: return if not statement.expression.result_type.is_pointer and statement.expression.address_of: return raise C2PException("Function '" + self.identifier + "' should return " + to_type + " instead of " + from_type)
def __init__(self, environment, name, indirection, address_of, array_size): super().__init__(environment) self.type = SmallCParser.ID symbol = environment.symbol_table.getSymbol(name) self.name = name self.indirection = indirection self.address_of = address_of if symbol is not None: self.operand_type = symbol.type self.result_type = self.operand_type else: raise C2PException("use of undeclared identifier '" + self.name + "'") if self.operand_type.isArray(): self.array_size = array_size else: self.array_size = 0 self.address = symbol.address self.depth = symbol.getRelativeDepth(environment.call_stack) if self.indirection and not self.operand_type.is_pointer: raise C2PException( "'" + self.name + "' is not a pointer and therefore can not be dereferenced")
def checkFunctionSignature(self, name, parameters, return_type=None): arg_types = [] # prepare `arg_types` with Type objects from parameter (declaration) # list if isinstance(parameters, ParameterList): # function call for arg in parameters.arguments: arg_types.append(arg.result_type) elif isinstance(parameters, ParameterDeclarationList): # function definition if parameters is not None: for param_decl in parameters.parameter_declarations: if isinstance(param_decl, TypeSpecifier): arg_types.append(param_decl.type_object) else: arg_types.append(param_decl) else: raise C2PException("Unexpected parameters type for function '" + name + "'") # try to match function signature in our symbol table for scope in reversed(self.functions): if name in scope: # we start by checking the return type of the function if return_type is not None: if return_type.getName( ) != scope[name].return_type.getName(): continue # check whether amount of arguments is the same if len(arg_types) != len(scope[name].arg_types): continue isTotalMatching = True for i in range(len(arg_types)): # match actual Type of arguments if arg_types[i].getName( ) == scope[name].arg_types[i].getName(): # check that both are either pointers or none of them # are if not (arg_types[i].is_pointer ^ scope[name].arg_types[i].is_pointer): # this doesn't check possible indirection or references, # it is left out in this stage else it would over # complicate things continue isTotalMatching = False break else: isTotalMatching = False break # as long as something went wrong, keep looking up in outer # scope if isTotalMatching: return scope[name] raise C2PException("Calling an undefined function '" + name + "'")
def __init__(self, environment, typename, var_decl_list): super().__init__(environment, SmallCParser.VARIABLEDECLARATION) if isinstance(typename, VoidType): raise C2PException("variable has incomplete type 'void'") self.typename = typename self.variable_identifiers = var_decl_list for var in self.variable_identifiers.variable_ids: if var.identifier not in environment.symbol_table.stack[-1]: var.setType(typename) self.addChild(var) else: raise C2PException("redefinition of '" + var.identifier + "'")
def __init__(self, environment, value): super().__init__(environment) self.type = SmallCParser.PRIMARY if isinstance(value, int): self.value = value self.operand_type = IntegerType() self.result_type = self.operand_type elif isinstance(value, float): self.value = value self.operand_type = FloatType() self.result_type = self.operand_type elif isinstance(value, bool): if value: self.value = 1 else: self.value = 0 self.operand_type = IntegerType() self.result_type = BooleanType() elif isinstance(value, str): self.value = "'" + value + "'" self.operand_type = CharacterType() self.result_type = self.operand_type else: raise C2PException("use of unrecognized primary value " + str(value))
def generateCode(self, out): # Get p-code datatype for this expression p_type = self.operand_type.getPSymbol() # Get the first operand on top of the stack self.term.generateCode(out) self.cast(self.term, out) # Execute the relevant operation on the operands ('*' and '/' load # the factor in the condition to permit '%' to execute it later) if self.operator == "*": self.factor.generateCode(out) self.cast(self.factor, out) self.writeInstruction("mul " + p_type, out) elif self.operator == "/": self.factor.generateCode(out) self.cast(self.factor, out) self.writeInstruction("div " + p_type, out) elif self.operator == "%": # Duplicate the result of the term first, instead of recomputing it # This way we effectively cut the cost of the second computation # OPTIMALIZATION: try to find a way to duplicate the factor as well, instead of # computing it twice self.writeInstruction("dpl " + p_type, out) self.factor.generateCode(out) self.cast(self.factor, out) self.writeInstruction("div " + p_type, out) self.factor.generateCode(out) self.cast(self.factor, out) self.writeInstruction("mul " + p_type, out) self.writeInstruction("sub " + p_type, out) else: raise C2PException(self.operator + " is not supported")
def addFunction(self, name, return_type, parameter_decl_list, address, depth): try: self.checkFunctionSignature(name, parameter_decl_list, return_type) except C2PException: self.functions[len(self.functions) - 1][name] = Function( return_type, parameter_decl_list, address, depth) return raise C2PException("redefinition of existing function '" + name + "'")
def __init__(self, environment, identifier, expression): super().__init__(environment) self.type = SmallCParser.ASSIGNMENT self.identifier = identifier self.expression = expression self.addChild(self.identifier) self.addChild(self.expression) symbol = environment.symbol_table.getSymbol(self.identifier.name) if symbol is None: raise C2PException("Can't assign to undeclared variable '" + self.identifier.name + "'") self.expression.result_type = symbol.type self.address = symbol.address self.depth = symbol.getRelativeDepth(environment.call_stack) self.expression.operand_type = self.expression.result_type if self.expression.result_type.is_const: raise C2PException("Can't assign to const variable '" + self.identifier.name + "'")
def generateCode(self, out): # first get the operand on top of the stack self.factor.generateCode(out) self.cast(self.factor, out) # execute the relevant operation on the operand if self.operator == "-": self.writeInstruction("neg " + self.operand_type.getPSymbol(), out) elif self.operator == "!": self.writeInstruction("not ", out) else: raise C2PException(self.operator + " is not supported")
def generateCode(self, out): # Get p-code datatype for this expression p_type = self.operand_type.getPSymbol() # First get the operands on top of the stack self.relation1.generateCode(out) self.cast(self.relation1, out) self.relation2.generateCode(out) self.cast(self.relation2, out) # Execute the operation on the operands if self.operator == "==": self.writeInstruction("equ " + p_type, out) elif self.operator == "!=": self.writeInstruction("neq " + p_type, out) else: raise C2PException(self.operator, " is not supported")
def generateCode(self, out): # Get p-code datatype for this expression p_type = self.operand_type.getPSymbol() # First get the operands on top of the stack self.equation1.generateCode(out) self.cast(self.equation1, out) self.equation2.generateCode(out) self.cast(self.equation2, out) # Execute the relevant operation on the operands if self.operator is "<": self.writeInstruction("les " + p_type, out) elif self.operator is ">": self.writeInstruction("grt " + p_type, out) else: raise C2PException(self.operator + " is not supported.")
def generateCode(self, out): # Get p-code datatype for this expression p_type = self.operand_type.getPSymbol() # First get the operands on top of the stack self.equation.generateCode(out) self.cast(self.equation, out) # Then get the right side of the equation on top of the stack self.term.generateCode(out) self.cast(self.term, out) # Execute the relevant operation on the operands if self.operator == "+": self.writeInstruction("add " + p_type, out) elif self.operator == "-": self.writeInstruction("sub " + p_type, out) else: raise C2PException(self.operator + " is not supported")
def setType(self, typename): self.typename = typename self.typename.is_pointer = self.is_pointer self.typename.array_size = self.array_size if self.expression is not None: if isinstance(self.expression, Primary): self.value = self.expression.value if self.typename.getName() != self.expression.result_type.getName( ): raise C2PException("identifier '" + self.identifier + "' is assigned a value of type " + self.expression.result_type.getCSymbol() + ", while " + self.typename.getCSymbol() + " is expected") if self.is_pointer: if not self.expression.result_type.is_pointer and not self.expression.address_of: raise C2PException( "identifier '" + self.identifier + "' is assigned a value of type " + self.expression.result_type.getCSymbol() + ", while " + self.typename.getCSymbol() + "* is expected") else: if self.is_pointer: raise C2PException( "variable '" + self.identifier + "' is of type " + self.typename.getCSymbol() + "*, can't initialize pointer elements with default value.") else: # determine default value for uninitialized variable c_type = self.typename.getCSymbol() if c_type == "int": self.value = 0 elif c_type == "float": self.value = 0.0 elif c_type == "char": self.value = '0' elif c_type == "bool": self.value = False if not self.typename.isArray(): # initialize variable of basic type self.addChild(Primary(self.environment, self.value)) else: for element in self.array_elements: self.addChild(Primary(self.environment, element)) if len(self.array_elements) > self.array_size: raise C2PException("Array '" + self.identifier + "' has size " + str(self.array_size) + ". You cannot fill it with " + str(len(self.array_elements)) + " elements.") if len(self.array_elements) < self.array_size: print( "Warning: array '", self.identifier, "' has size ", self.array_size, ". Remaining elements will be filled with default values." ) # initialize array of basic type while (len(self.array_elements) != self.array_size): self.array_elements.append(self.value) self.addChild(Primary(self.environment, self.value)) self.allocate()
def getRelativeDepth(self, call_stack): stack_depth = call_stack.getNestingDepth() if stack_depth < self.depth: raise C2PException( "scope of function is larger than stack's depth") return stack_depth - self.depth
parser = SmallCParser(stream) parser.removeErrorListeners() parser.addErrorListener(MyErrorListener()) parsetree = parser.smallc_program() environment = Environment() ast = ASTGenerator(environment, parsetree).generate() if os.path.isfile(output): # empty the file so only new code is saved open(output, 'w').close() ast.generateCode(output) if saveast: ast.storeASTToDisk() draw(ast) if __name__ == '__main__': try: if len(sys.argv) == 3: run(sys.argv[1], sys.argv[2], False) elif len(sys.argv) == 4 and sys.argv[3] == "-saveast": run(sys.argv[1], sys.argv[2], True) else: raise C2PException("ERROR: minimum 2 arguments needed: `input.c` \ and `output.p`, with an optional -saveast as last argument" ) except C2PException as error: print(error)