def create_scope(self): self._scope_num += 1 if self._scope_num == 0: scope = Scope(self._scope_num, None) else: scope = Scope(self._scope_num, self._scope_array[self._scope_num-1]) self._scope_array.append(scope)
def visit_Block(self, node): self.current_scope = Scope(self.current_scope) self.visit(node.stmts) return None
def __init__(self): self.current_scope = Scope() self.loop_count = 0
class TypeChecker(NodeVisitor): def __init__(self): self.current_scope = Scope() self.loop_count = 0 def visit_IntNum(self, node): return IntT def visit_FloatNum(self, node): return FloatT def visit_String(self, node): return StringT def visit_Block(self, node): self.current_scope = Scope(self.current_scope) self.visit(node.stmts) return None def visit_FnCall(self, node): self.visit(node.args) if node.fn in ['zeros', 'eye', 'ones']: if not 1 <= len(node.args) <= 2: print(f"Line {node.line}: {node.fn} requires 1 or 2 arguments") if all(isinstance(arg, AST.IntNum) for arg in node.args): if len(node.args) == 2: return ArrayT(2, FloatT, tuple(arg.value for arg in node.args)) elif len(node.args) == 1: return ArrayT(2, FloatT, (node.args[0].value, node.args[0].value)) return ArrayT(2, FloatT, tuple(arg.value for arg in node.args)) return ArrayT(2, FloatT, (None, None)) return AnyT def visit_Print(self, node): self.visit(node.args) return None def visit_Transposition(self, node): type1 = self.current_scope.get(node.target) if not isinstance(type1, ArrayT): print( f"Line {node.line}: Can not transpose {type1}, type mismatch") return ArrayT(2, AnyT, (None, None)) if type1.dims != 2: print( f"Line {node.line}: Can not transpose {type1}, dimension mismatch" ) return ArrayT(2, type1.eltype, (None, None)) m, n = type1.size return ArrayT(2, type1.eltype, (n, m)) def visit_UnaryMinus(self, node): type1 = self.visit(node.expr) if type1 not in [FloatT, IntT]: # or not isinstance(type1, ArrayT) print(f'Line {node.line}: Can not apply unary minus to {type1}') return type1 def visit_BinExpr(self, node): type1 = self.visit(node.left) type2 = self.visit(node.right) op = node.op if op[0] == '.': op = op[1:] if isinstance(type1, ArrayT) and isinstance(type2, ArrayT): if type1.size != type2.size: print(f"Line {node.line}: Size mismatch during .{op}") type3 = aaa[op][type1.eltype][type2.eltype] if type3 == AnyT: print( f'Line {node.line}: Can not apply {op} for {type1.eltype} and {type2.eltype}, expression will result in any type' ) return ArrayT(type1.dims, type3, type1.size) elif isinstance(type1, ArrayT): type3 = aaa[op][type1.eltype][type2] if type3 == AnyT: print( f'Line {node.line}: Can not apply {op} for {type1.eltype} and {type2}, expression will result in any type' ) return ArrayT(type1.dims, type3, type1.size) elif isinstance(type2, ArrayT): type3 = aaa[op][type1][type2.eltype] if type3 == AnyT: print( f'Line {node.line}: Can not apply {op} for {type1} and {type2.eltype}, expression will result in any type' ) return ArrayT(type2.dims, type3, type2.size) else: print( f'Line {node.line}: Cannnot apply {op} for {type1} and {type2}, at least one argument must be array' ) return AnyT else: type3 = aaa[op][type1][type2] if type3 == AnyT: print( f'Line {node.line}: Can not apply {op} for {type1} and {type2}, expression will result in any type' ) return type3 def visit_Id(self, node): type1 = self.current_scope.get(node.id) if type1 == None: print( f'Line {node.line}: Variable can not be found in current scope' ) return AnyT return type1 def visit_AssignExpr(self, node): type1 = self.visit(node.value) if isinstance(node.id, AST.Ref): # todo A[1,2] = type2 = self.current_scope.get(node.id.target.id) if type1 != type2.eltype: print( f"Line {node.line}: Ref assigment type mismatch {type2.eltype} and {type1}" ) return type1 else: self.current_scope.put(node.id.id, type1) return type1 def visit_IfStmt(self, node): condt = self.visit(node.cond) if condt != BoolT: print( f'Line {node.line}: If must have condition resolving to boolean value, but got {condt}' ) self.push_scope() self.visit(node.positive) self.pop_scope() self.push_scope() self.visit(node.negative) # default is Block([]) self.pop_scope() return None def visit_ForLoop(self, node): self.loop_count += 1 type1 = self.visit(node.range) if type1 != 'range': print( f'Line {node.line}: For loop must be iterating over range, but got {type1}' ) self.push_scope() self.current_scope.put(node.id, IntT) self.visit(node.stmt) self.pop_scope() self.loop_count -= 1 return None def visit_WhileLoop(self, node): self.loop_count += 1 condt = self.visit(node.cond) if condt != BoolT: print( f'Line {node.line}: While loop must have condition resolving to boolean value, but got {condt}' ) self.push_scope() self.visit(node.stmt) self.pop_scope() self.loop_count -= 1 return None def visit_Range(self, node): if not self.visit(node.min) == self.visit(node.max) == IntT: print(f"Line {node.line}: Range extremas must be integers") return RangeT def visit_Vector(self, node): types = list(map(self.visit, node.values)) eltype = types[0] if any(eltype != t for t in types): if isinstance(eltype, ArrayT): print( f"Line {node.line}: Inconsistant vector lengths, choosing first length to fit dimension" ) return ArrayT(eltype.dims + 1, eltype.eltype, (len(types), ) + eltype.size) print( f'Line {node.line}: Inconsistant vector value types, choosing any as vector base type' ) return ArrayT(1, AnyT, (len(types), )) if isinstance(eltype, ArrayT): return ArrayT(eltype.dims + 1, eltype.eltype, (len(types), ) + eltype.size) return ArrayT(1, eltype, (len(types), )) def visit_Break(self, node): if self.loop_count == 0: print( f"Line {node.line}: Line {node.line}: Using break outside of loop" ) return None def visit_Continue(self, node): if self.loop_count == 0: print(f"Line {node.line}: Using continue outside of loop") return None def visit_Ref(self, node): targett = self.current_scope.get(node.target.id) if isinstance( node.target, AST.Id) else self.visit(node.target) if targett == StringT and len(node.indices) != 1: print( f"Line {node.line}: Indexing string with {len(node.indices)} dimensions" ) return IntT if isinstance(targett, ArrayT): if len(node.indices) != targett.dims: print( f"Line {node.line}: Indexing {targett.dims}d array with {len(node.indices)} dimensions" ) indices = [i.value for i in node.indices] if not all(m is None or 1 <= i <= m for i, m in zip(indices, targett.size)): print(f"Line {node.line}: Index out of range") return targett.eltype return targett.eltype print(f"Line {node.line}: {targett} is not indexable") return AnyT def visit_Return(self, node): self.visit(node.expr) print(f"Line {node.line}: return stmt without function definition") return None def push_scope(self): self.current_scope = Scope(self.current_scope) def pop_scope(self): self.current_scope
def push_scope(self): self.current_scope = Scope(self.current_scope)