class Analyzer(): ''' Analyzer class Attributes: symboltable (SymbolTable) success (Bool) ''' def __init__(self): self.symboltable = SymbolTable() self.success = True def __scan_tree(self, node): ''' scan tree on pre-order and analyze each childen Args: node (AnyTree's Node): Node to start the scan ''' flags = self.__analyze(node) if (not flags["goDeep"]): return for child in node.children: self.__scan_tree(child) if (flags["newContext"]): self.symboltable.end_contex() if (flags["isFunction"]): line = self.symboltable.get_global_last_line() _type = line["type"] if (_type != "" and not self.symboltable.check_return()): self.success = False error("Function " + line["name"] + " must have a retorno") def __analyze_var(self, var): ''' analyze var's node Args: var (Anytree's Node): var node ''' dimension = 0 if (len(var.children) > 1): list_index = var.children[1] for child in list_index.children: if (child.value != "[" and child.value != "]"): _type = self.__analyze_expression(child) if (_type and _type != "inteiro"): self.success = False error("Index must be an Inteiro at line " + str(var.children[0].line) + "." + str(var.children[0].pos)) dimension += 1 return dimension def __crete_var(self, var, _type): ''' Add a new var to symbol table Args: var (Anytree's Node): var node _type (str): var's type ''' dimension = self.__analyze_var(var) status = self.symboltable.insert({ "name": var.children[0].value, "type": _type, "used": False, "symbol_type": "var", "initialized": False, "dimension": dimension, "line": var.children[0].line, "pos": var.children[0].pos, "value": None }) line = self.symboltable.lookup(var.children[0].value) var.children[0].table_pointer = line if (not status): self.success = False def __analyze_var_declaration(self, node): ''' analyze var declaration Args: node (Anytree's Node): var_declaration's node ''' children = node.children _type = children[0].value for var in children[1:]: self.__crete_var(var, _type) def __analyze_params_list(self, node): ''' analyze params list and add all params to symbol table Args: node (Anytree's Node): var_declaration's node ''' params = node.children for param in params: _type = param.children[0].value par_name = param.children[1].value self.symboltable.insert({ "name": par_name, "type": _type, "used": False, "initialized": True, "symbol_type": "par", "dimension": int(len(param.children[2:]) / 2), "line": param.children[0].line, "pos": param.children[0].pos }) line = self.symboltable.lookup(par_name) param.children[1].table_pointer = line def __analyze_function_declaration(self, node): ''' add function declaration to symbol table Args: node (Anytree's Node): function_declaration's node ''' params = [] _type = None if (len(node.children) == 4): _type = node.children[0].value name = node.children[1].value par_list = node.children[2] else: name = node.children[0].value par_list = node.children[1] for par in par_list.children: params.append({ "type": par.children[0].value, "vet": 0 if len(par.children) == 2 else int( (len(par.children) - 2) / 2) }) status = self.symboltable.insert({ "name": name, "type": _type if _type else "", "used": False, "initialized": True, "symbol_type": "func", "dimension": 0, "params": params, "line": node.children[0].line, "pos": node.children[0].pos }) line = self.symboltable.lookup(name, used=False) if (len(node.children) == 4): node.children[1].table_pointer = line else: node.children[0].table_pointer = line if (not status): self.success = False self.symboltable.add_contex(name) def __analyze_function_call(self, node): ''' ana;yze function call Args: node (Anytree's Node): function_call's node ''' funtion_line = self.get_table_line_by_node_type(node, False) node.table_pointer = funtion_line if funtion_line: arg_list = node.children[-1] par = funtion_line["params"] given_args = [] for exp in arg_list.children: arg = {} _type = self.__analyze_expression(exp) _type_tok = _type.split(" ") arg["type"] = _type_tok[0] arg["vet"] = int(_type_tok[1]) if len(_type_tok) == 2 else 0 given_args.append(arg) if (len(par) != len(given_args)): self.success = False error("Function \"" + funtion_line["name"] + "\" takes " + str(len(par)) + " argments but " + str(len(given_args)) + " were given at line " + str(node.line) + "." + str(node.pos)) elif (par != given_args): warning("Implicit type conversion at line " + str(node.line) + "." + str(node.pos)) def get_table_line_by_node_type(self, _type, show_error=True, used=True, initialized=False): ''' get table line Args: _type (AnyTree's Node): type's Node show_error=True (Bool): show if found some error used=True (Bool): set line searched used with true initialized=False (Bool): set line searched initialized with false ''' aux = _type.children[0].value line = self.symboltable.lookup(aux, used=used, initialized=initialized) _type.table_pointer = line if (not line): self.success = False if (show_error): self.success = False error(("Variable " if (_type.value == "var") else "Function ") + aux + " not declared on line " + str(_type.children[0].line) + "." + str(_type.children[0].pos)) return line if line else None def __analyze_single_expression(self, node): """ Return type: "bool" "inteiro" "flutuante" "inteiro dim" "flutuante dim" """ children = node.children # factor and other variations if (len(children) == 1): # just factor _type = children[0].children[0] # if is function_call, num or var else: # factor and some other op op = children[0].value _type = children[1].children[0] # if is function_call, num or var if (op == "!"): # if op is ! then is a bool.. if (_type.value == "expression"): self.__analyze_expression(_type) elif (_type.value != "num"): # just search for the line for mark as used line = self.get_taget_table_line_by_node_typeble_line_by_node_type( _type) return "bool" # if not a bool... just do the regular verification if (_type.value == "num"): num = _type.children[0].value return "inteiro" if (type(num) is int) else "flutuante" elif (_type.value == "expression"): return self.__analyze_expression(_type) else: line = self.get_table_line_by_node_type(_type) if (line and (line["symbol_type"] == "var" or line["symbol_type"] == "par")): dimension = line["dimension"] if (dimension != 0): real_dimension = len(_type.children) - 1 if (dimension - real_dimension != 0): return line["type"] + " " + str(dimension - real_dimension) if (_type.value == "function_call"): self.__analyze_function_call(_type) return line["type"] if line else None def __analyze_expression(self, node): # if is single_expression, just go all deep and return the type if (node.value == "expression"): return self.__analyze_expression(node.children[0]) if (node.value == "single_expression"): return self.__analyze_single_expression(node) type1 = self.__analyze_expression(node.children[0]) type2 = self.__analyze_expression(node.children[1]) if (node.value in RELATIONAL_OP): if (not type1 or not type2 or (len(type1.split(" ")) == 2 or len(type2.split(" ")) == 2)): self.success = False error("Invalid type at line " + str(node.line) + "." + str(node.pos)) return "bool" if (type1 == type2): return type1 elif (type1 in TYPE and type2 in TYPE): # one is inteiro other is fluatuante return "flutuante" return None def __analyze_add_new_contex(self, node): new_contex = node.value self.symboltable.add_contex(new_contex) def __analyze_assignment(self, node): var = node.children[0] exp = node.children[1] self.__analyze_var(var) line = self.get_table_line_by_node_type(var, initialized=True, used=False) _type = "inteiro" if (line): _type = line["type"] exp_type = self.__analyze_expression(exp) if (exp_type == "bool"): error("Invalid type association at line " + str(var.line) + "." + str(var.pos)) self.success = False elif (_type != exp_type): warning("Implicit type conversion at line " + str(var.line) + "." + str(var.pos)) def __analyze_return(self, node): exp = node.children[0] self.symboltable.set_return() _type = self.__analyze_expression(exp) line = self.symboltable.get_global_last_line() if (line["type"] not in TYPE or _type not in TYPE): self.success = False error("Invalid return at line " + str(node.line) + "." + str(node.pos)) elif (line["type"] != _type): warning("Implicit type conversion at line " + str(node.line) + "." + str(node.pos)) def __analyze_body(self, node): for child in node.children: if (child.value == "expression"): self.__analyze_expression(child) def __analyze_leia(self, node): var = node.children[0] line = self.get_table_line_by_node_type(var, initialized=True) var.children[0].table_pointer = line def __analyze(self, node): if (node.value == "var_declaration"): self.__analyze_var_declaration(node) return { "goDeep": False, "newContext": False, "isFunction": False, } # dont go deep, dont change context elif (node.value == "params_list"): self.__analyze_params_list(node) return { "goDeep": False, "newContext": False, "isFunction": False, } elif (node.value == "assignment"): self.__analyze_assignment(node) return { "goDeep": True, "newContext": False, "isFunction": False, } elif (node.value == "body"): self.__analyze_body(node) return { "goDeep": True, "newContext": False, "isFunction": False, } elif (node.value == "retorna"): self.__analyze_return(node) return { "goDeep": False, "newContext": False, "isFunction": False, } elif (node.value == "func_declaration"): self.__analyze_function_declaration(node) return { "goDeep": True, "newContext": True, "isFunction": True, } # go deep, and i change the context elif (node.value == "repita" or node.value == "se" or node.value == "senão"): self.__analyze_add_new_contex(node) # Just to analyze the expression if (node.value == "repita"): for child in node.children: if (child.value == "expression"): self.__analyze_expression(child) return { "goDeep": True, "newContext": True, "isFunction": False, } elif (node.value == "conditional"): # Just to analyze the expression for child in node.children: if (child.value == "expression"): self.__analyze_expression(child) return { "goDeep": True, "newContext": False, "isFunction": False, } elif (node.value == "leia"): self.__analyze_leia(node) elif (node.value == "function_call"): self.__analyze_function_call(node) elif (node.value == "escreva" or node.value == "escreva"): self.__analyze_expression(node.children[0]) return { "goDeep": False, "newContext": False, "isFunction": False, } return { "goDeep": True, "newContext": False, "isFunction": False, } def verify_warnings(self): ''' verify and print warnings ''' for line in self.symboltable.get_uninitialized(): warning("Identifier \"" + line["name"] + "\" not initialized at line: " + str(line["line"]) + "." + str(line["pos"])) for line in self.symboltable.get_unused(): if (line["name"] == "principal"): continue warning("Unused identifier \"" + line["name"] + "\" at line: " + str(line["line"]) + "." + str(line["pos"])) def verify_principal(self): ''' Verify principal function ''' line = self.symboltable.has_principal() if (line and line["used"]): error("\"principal\" function shouldn't be called") self.success = False elif (not line): error("Program don't have a \"principal\" function") self.success = False def analyze(self, node): ''' Analyze node and set attribute success with True or False Args: node (AnyTree's Node): node to start the analyze ''' self.__scan_tree(node)