class ASTWalker(NodeVisitor): def visit_Assignment(self, n): if self.st.get_function(n.var): raise SemanticError("function '%s' is not assignable" % n.var) left = self.st.get_identifier(n.var) if left is None: raise SemanticError("use of undeclared identifier '%s'" % n.var) right = self.visit(n.expr) if left.type != right.type: raise SemanticError("assigning to '%s' from incompatible type '%s'" % (left.type, right.type)) srcId = None if isinstance(n.expr, (Assignment, Identifier, Constant)): self.st.update_variable(n.var, left.type, right.value) srcId = right.name outasm = [] add(outasm, right.asm) add(outasm, asm.AssignToVariable(n.var, srcId, self.st)) return Info(name=n.var, type=left.type, value=right.value, asm=outasm) def visit_BinaryOp(self, n): left = self.visit(n.lexpr) right = self.visit(n.rexpr) id1 = left.name if isinstance(n.lexpr, (Assignment, Identifier, Constant)) else None id2 = right.name if isinstance(n.rexpr, (Assignment, Identifier, Constant)) else None operators = { "+": asm.Plus, "-": asm.Minus, "*": asm.Times, "/": asm.Divide, "%": asm.Modulo, "!": asm.Not, "&&": asm.And, "||": asm.Or, "<": asm.Lesser, ">": asm.Greater, "<=": asm.LesserOrEqual, ">=": asm.GreaterOrEqual, "==": asm.Equal, "!=": asm.NotEqual, } if left.type in [T.Integer, T.Character, T.String] and right.type in [T.Integer, T.Character, T.String] and left.type != right.type: raise SemanticError("invalid types of operands to binary expression '%s' ('%s' and '%s')" % (n.op, left.type, right.type)) outasm = [] add(outasm, left.asm) add(outasm, right.asm) add(outasm, operators[n.op](id1, id2, self.st)) return Info(type=T.Integer, asm=outasm) def visit_Break(self, n): if not self.st.in_loop(): raise SemanticError("'break' statement not in loop statement") return Info(asm=asm.Break()) def visit_Cast(self, n, internal=False): cast = self.visit(n.type) expr = self.visit(n.expr) id = expr.name if isinstance(n.expr, (Assignment, Identifier, Constant)) else None if expr.type in [T.Character] and cast.type in [T.Integer, T.Character, T.String]: pass elif expr.type in [T.Integer] and cast.type in [T.Integer, T.Character]: pass elif expr.type in [T.String] and cast.type in [T.String]: pass elif internal and expr.type in [T.String] and cast.type in [T.Integer]: pass else: raise SemanticError("cast from '%s' to '%s' is not allowed" % (expr.type, cast.type)) outasm = [] add(outasm, expr.asm) add(outasm, asm.Cast(id, cast.type, self.st)) return Info(type=cast.type, value=expr.value, asm=outasm) def visit_Compound(self, n): output = [] type = None for ch in n: ch_visited = self.visit(ch) add(output, ch_visited.asm) if isinstance(ch, Return): type = ch_visited.type return Info(type=type, asm=asm.Compound(output)) def visit_Constant(self, n): def is_int32(value): return int(value) >= INT.minint and int(value) <= INT.maxint type = self.visit(n.type) if type.type in [T.Integer] and not is_int32(n.value): raise SemanticError("integer constant '%s' is too large for its type" % n.value) name = self.st.insert_to_global(n.value, type.type) return Info(name=name, type=type.type, value=n.value) def visit_Continue(self, n): if not self.st.in_loop(): raise SemanticError("'continue' statement not in loop statement") return Info(asm=asm.Continue()) def visit_Empty(self, n): return Info(asm=asm.Empty()) def visit_For(self, n): scope = self.st.insert_nonfunction_scope() self.st.enter_scope(scope) self.st.enter_loop() start = self.visit(UnaryOp('+', n.start)) if isinstance(n.start, (Constant, Identifier)) else self.visit(n.start) test = self.visit_Cast(Cast(Type('int'), n.test), True) if isinstance(n.test, (Assignment, Constant, Identifier)) else self.visit(n.test) if test.type not in [T.Integer, T.Character, T.String]: raise SemanticError("statement requires expression of scalar type ('%s' invalid)" % test.type) next = self.visit(UnaryOp('+', n.next)) if isinstance(n.next, (Constant, Identifier)) else self.visit(n.next) body = self.visit(n.body) self.st.leave_loop() self.st.leave_scope() start_asm = [] add(start_asm, start.asm) test_asm = [] add(test_asm, test.asm) next_asm = [] add(next_asm, next.asm) return Info(asm=asm.For(start_asm, test_asm, next_asm, body.asm, self.st)) def visit_FunctionCall(self, n): def fce_param_types(fce_scope): types = [] for param in sorted(fce_scope): if param.startswith("#arg#"): types.append(fce_scope[param].type) return types def arg_param_types(args): types = [] for param in args: types.append(param.type) return types if self.st.get_identifier(n.name): raise SemanticError("called variable '%s' is not a function" % n.name) fce = self.st.get_function(n.name) if fce is None: raise SemanticError("implicit declaration of function '%s' is invalid in VYPe13" % n.name) fce_types = fce_param_types(fce.scope) arg_list = [(arg, self.visit(arg)) for arg in n.args] arg_types = arg_param_types([arg[1] for arg in arg_list]) if n.name == "print": if len(n.args) == 0: raise SemanticError("calling function '%s' with incompatible parameter types" % (n.name)) else: if fce_types != arg_types: if fce_types == ['void'] and arg_types == [None]: pass else: raise SemanticError("calling function '%s' with incompatible parameter types" % (n.name)) call_list = [] call_name = [] for arg in arg_list: add(call_list, arg[1].asm) if isinstance(arg[0], (Assignment, Identifier, Constant)): call_name.append(arg[1].name) else: call_name.append(None) add(call_list, asm.Call(n.name, call_name, self.st)) return Info(name=n.name, type=fce.type, asm=call_list) def visit_FunctionDefinition(self, n): def fce_param_types(fce_scope): types = [] for param in sorted(fce_scope): if param.startswith("#arg#"): types.append(fce_scope[param].type) return types def def_param_types(param_list): types = [] for param in param_list: types.append(self.visit(param.type).type) return types def def_param_names(param_list): names = [] for param in param_list: if param.name is not None: names.append(param.name) return names if self.st.get_identifier(n.name): raise SemanticError("redefinition of variable '%s' as a function" % n.name) fce = self.st.get_function(n.name) type = self.visit(n.type) if n.name == "main" and type.type != T.Integer: raise SemanticError("return type of '%s' is not '%s' but '%s'" % (n.name, T.Integer, type.type)) param_names = def_param_names(n.args) if fce is None: self.st.insert_function(n.name, type.type) self.st.enter_scope(n.name) for arg, i in zip(n.args, range(len(n.args))): self.visit_FunctionParam(arg, i) param_names = asm.GetParams(param_names, self.st) self.st.set_function_def(n.name, False) if n.code is not None: code = self.visit(n.code) self.st.set_function_def(n.name, True) self.st.leave_scope() else: if fce.scope is None: raise SemanticError("redefinition of '%s' as different kind of symbol" % (n.name)) self.st.enter_scope(n.name) fce_types = fce_param_types(fce.scope) def_types = def_param_types(n.args) if fce_types != def_types or fce.type != type.type: raise SemanticError("conflicting types for function '%s'" % (n.name)) if n.code is None: raise SemanticError("redeclaration of function '%s'" % (n.name)) elif fce.defined is False and n.code is not None: for arg, i in zip(n.args, range(len(n.args))): self.visit_FunctionParam(arg, i) param_names = asm.GetParams(param_names, self.st) code = self.visit(n.code) self.st.set_function_def(n.name, True) elif fce.defined is True and n.code is not None: raise SemanticError("redefinition of function '%s'" % (n.name)) self.st.leave_scope() if n.code is not None and code.type != type.type: if code.type is not None: raise SemanticError("returning '%s' from function '%s' with result type '%s'" % (code.type, n.name, type.type)) if n.code is not None: return Info(name=n.name, type=type.type, asm=asm.FunctionDefinition(n.name, param_names, code.asm, self.st)) else: return Info(name=n.name, type=type.type, asm=asm.Empty()) def visit_FunctionParam(self, n, pos = None): type = self.visit(n.type) self.st.insert_arg_type(type.type, pos) if n.name is not None: self.st.insert_variable(n.name, type.type, position = pos) return Info(name=n.name, type=type.type) def visit_Identifier(self, n): id = self.st.get_identifier(n.name) if id is None: raise SemanticError("use of undeclared identifier '%s'" % n.name) return Info(name=n.name, type=id.type, value=id.value) def visit_If(self, n): test = self.visit_Cast(Cast(Type('int'), n.test), True) if isinstance(n.test, (Assignment, Constant, Identifier)) else self.visit(n.test) if test.type not in [T.Integer, T.Character, T.String]: raise SemanticError("statement requires expression of scalar type ('%s' invalid)" % test.type) scope = self.st.insert_nonfunction_scope() self.st.enter_scope(scope) true = self.visit(n.true) self.st.leave_scope() scope = self.st.insert_nonfunction_scope() self.st.enter_scope(scope) false = self.visit(n.false) self.st.leave_scope() test_asm = [] add(test_asm, test.asm) if isinstance(n.false, Empty): return Info(asm=asm.IfOnly(test_asm, true.asm, self.st)) else: return Info(asm=asm.IfElse(test_asm, true.asm, false.asm, self.st)) def visit_Program(self, n): self.st = SymbolTable() outasm = [self.visit(ch).asm for ch in n] if self.st.get_function('main') is None: raise SemanticError("undefined reference to 'main'") return Info(asm=asm.Program(outasm, self.st.get_globals(finalize=True))) def visit_Return(self, n): if n.expr is not None: expr = self.visit(n.expr) id = expr.name if isinstance(n.expr, (Assignment, Identifier, Constant)) else None outasm = [] add(outasm, expr.asm) add(outasm, asm.Return(id, self.st)) return Info(type=expr.type, value=expr.value, asm=outasm) else: return Info(type=T.Void, asm=asm.ReturnEmpty()) def visit_Type(self, n): if n == Type("int"): type = T.Integer elif n == Type("char"): type = T.Character elif n == Type("string"): type = T.String elif n == Type("void"): type = T.Void else: type = T.Nothing return Info(type=type) def visit_UnaryOp(self, n): expr = self.visit(n.expr) id = expr.name if isinstance(n.expr, (Assignment, Identifier, Constant)) else None outasm = [] add(outasm, expr.asm) if n.op == '-': add(outasm, asm.UnaryMinus(id, self.st)) elif n.op == '!': add(outasm, asm.Not(id, self.st)) elif n.op == '+': add(outasm, asm.UnaryPlus(id, self.st)) return Info(type=expr.type, asm=outasm) def visit_VariableDeclaration(self, n): type = self.visit(n.type) vars = [] for i in n.vardecls: vars.append(self.visit_VariableInitialize(i, type.type)) vars_asm = [] for var in vars: add(vars_asm, var.asm) return Info(type=type.type, asm=vars_asm) def visit_VariableInitialize(self, n, type): if self.st.get_function(n.name) or self.st.insert_variable(n.name, type) is False: raise SemanticError("redefinition of function '%s' as a variable" % (n.name)) value = None opasm = [] add(opasm, asm.DeclareVariable(n.name, self.st)) expr = self.visit(n.value) add(opasm, expr.asm) if n.value is not None: if expr.type != type: raise SemanticError("initializing '%s' with an expression of incompatible type '%s'" % (type, expr.type)) if isinstance(n.value, (Identifier, Constant)): self.st.update_variable(n.name, type, expr.value) value = expr.value if isinstance(n.value, Constant): add(opasm, asm.InitialiseVariable(n.name, expr.name, self.st)) elif isinstance(n.value, Identifier): add(opasm, asm.InitialiseVariable(n.name, expr.name, self.st)) add(opasm, asm.AssignToVariable(n.name, expr.name, self.st)) else: add(opasm, asm.AssignToVariable(n.name, None, self.st)) else: add(opasm, asm.InitialiseVariable(n.name, None, self.st)) return Info(name=n.name, type=type, value=value, asm=opasm) def visit_While(self, n): scope = self.st.insert_nonfunction_scope() self.st.enter_scope(scope) self.st.enter_loop() test = self.visit_Cast(Cast(Type('int'), n.test), True) if isinstance(n.test, (Assignment, Constant, Identifier)) else self.visit(n.test) body = self.visit(n.body) self.st.leave_loop() self.st.leave_scope() if test.type not in [T.Integer, T.Character, T.String]: raise SemanticError("statement requires expression of scalar type ('%s' invalid)" % test.type) test_asm = [] add(test_asm, test.asm) return Info(asm=asm.While(test_asm, body.asm, self.st))
def visit_Program(self, n): self.st = SymbolTable() outasm = [self.visit(ch).asm for ch in n] if self.st.get_function('main') is None: raise SemanticError("undefined reference to 'main'") return Info(asm=asm.Program(outasm, self.st.get_globals(finalize=True)))