@@ -203,6 +203,7 @@ class TypecheckError(BaseLocatedError):
203203_typecheck_dispatch : dict [type [AstNode ], NodeTypecheckFn ] = {}
204204
205205
206+ # TODO: output typed AST
206207class Typechecker :
207208 _curr_scope : Scope
208209
@@ -320,6 +321,83 @@ def _typecheck_define(self, n: AstDefine):
320321 finally :
321322 self ._curr_scope = old_scope
322323
324+ @_node_typechecker (AstNumber )
325+ def _typecheck_number (self , _n : AstNumber ):
326+ return ValType ()
327+
328+ @_node_typechecker (AstString )
329+ def _typecheck_string (self , _n : AstString ):
330+ return ValType ()
331+
332+ @_node_typechecker (AstListLiteral )
333+ def _typecheck_list (self , n : AstListLiteral ):
334+ for item in n .items :
335+ if self ._typecheck (item ) != ValType ():
336+ raise self .err ("Can only have ValType()s in list" , item )
337+ return ListType ()
338+
339+ @_node_typechecker (AstIdent )
340+ def _typecheck_ident (self , n : AstIdent ):
341+ return self ._curr_scope .used [n .id ].tp_info
342+
343+ @_node_typechecker (AstAttrName )
344+ def _typecheck_attr_name (self , _n : AstAttrName ):
345+ assert 0 , "AstAttrName has no type, cannot be checked on its own"
346+
347+ @_node_typechecker (AstAttribute )
348+ def _typecheck_attribute (self , n : AstAttribute ):
349+ # TODO: implement this properly, with better types and stuff
350+ raise self .err ("Attributes are not implemented yet" , n )
351+
352+ @_node_typechecker (AstItem )
353+ def _typecheck_item (self , n : AstItem ):
354+ # TODO: this will require different intrinsics for string vs list getitem
355+ container_tp = self ._typecheck (n .obj )
356+ if container_tp not in (ListType (), ValType ()):
357+ raise self .err (f"Cannot get item of { container_tp } " , n )
358+ self .expect_type (self ._typecheck (n .index ), ValType (), n .index )
359+
360+ @_node_typechecker (AstCall )
361+ def _typecheck_call (self , n : AstCall ):
362+ called_tp = self ._typecheck (n .obj )
363+ if not isinstance (called_tp , FunctionType ):
364+ raise self .err (f"Cannot call { called_tp } " , n .obj )
365+ if len (called_tp .arg_types ) != len (n .args ):
366+ if n .args and len (n .args ) > len (called_tp .arg_types ):
367+ region = n .args [- 1 ].region # Highlight extraneous arg
368+ else :
369+ region = n .region
370+ raise self .err (f"Incorrect number of arguments, expected "
371+ f"{ len (called_tp .arg_types )} , got { len (n .args )} " ,
372+ region )
373+ for decl_t , arg_node in zip (called_tp .arg_types , n .args ):
374+ self .expect_type (self ._typecheck (arg_node ), decl_t , arg_node )
375+ return called_tp .ret_type
376+
377+ _BINARY_OP_TYPES = dict .fromkeys ([
378+ * '+-*/%' , '**' , '..' , '==' , '!=' , '<' , '>' , '<=' , '>='
379+ ], ValType ()) | dict .fromkeys ([
380+ '&&' , '||'
381+ ], BoolType ())
382+
383+ _UNARY_OP_TYPES = dict .fromkeys ([
384+ * '+-'
385+ ], ValType ()) | dict .fromkeys ([
386+ '!'
387+ ], BoolType ())
388+
389+ # TODO: allow casting bool to val? - auto-cast or explicit?
390+ @_node_typechecker (AstBinOp )
391+ def _typecheck_bin_op (self , n : AstBinOp ):
392+ expect_tp = self ._BINARY_OP_TYPES [n .op ]
393+ self .expect_type (self ._typecheck (n .left ), expect_tp , n .left )
394+ self .expect_type (self ._typecheck (n .right ), expect_tp , n .right )
395+
396+ @_node_typechecker (AstUnaryOp )
397+ def _typecheck_unary_op (self , n : AstUnaryOp ):
398+ expect_tp = self ._UNARY_OP_TYPES [n .op ]
399+ self .expect_type (self ._typecheck (n .operand ), expect_tp , n .operand )
400+
323401 def _resolve_scope (self , scope_tp : VarDeclScope ):
324402 return self .top_scope if scope_tp == VarDeclScope .GLOBAL else self ._curr_scope
325403
0 commit comments