@@ -552,6 +552,73 @@ and parsePrimary = (state: parserState): option<expr> => {
552552 }
553553}
554554
555+ // ============================================
556+ // Type Annotation Parsing
557+ // ============================================
558+
559+ // Consume a closing '>' for a type-argument list. Because the lexer greedily
560+ // produces `>>` as a single `GreaterGreater` token, a closing angle that sits
561+ // directly against an enclosing one (e.g. `Echo<Array<Int>>`) arrives fused.
562+ // We split it in place: consume one '>' worth and leave a `Greater` behind for
563+ // the enclosing type to close against.
564+ let rec expectCloseAngle = (state : parserState ): unit => {
565+ switch peek (state ) {
566+ | Some ({type_ : Greater }) => advance (state )-> ignore
567+ | Some ({type_ : GreaterGreater , loc }) =>
568+ state .tokens [state .pos ] = {type_ : Greater , lexeme : ">" , loc }
569+ | Some (tok ) => addDiagnostic (state , E0006 , "Expected '>' to close type arguments" , tok .loc )
570+ | None => ()
571+ }
572+ }
573+
574+ // Parse an optional `<A>` or `<A, B>` argument list. Returns (first, second).
575+ and parseTypeArgs = (state : parserState ): (option <typeExpr >, option <typeExpr >) => {
576+ if check (state , Less ) {
577+ advance (state )-> ignore
578+ let first = parseTypeExpr (state )
579+ let second = if match_ (state , [Comma ]) {
580+ parseTypeExpr (state )
581+ } else {
582+ None
583+ }
584+ expectCloseAngle (state )
585+ (first , second )
586+ } else {
587+ (None , None )
588+ }
589+ }
590+
591+ // Parse a type expression: primitives, Array<T>, Echo / Echo<A> / Echo<A, B>,
592+ // EchoR / EchoR<A> / EchoR<A, B>, or a named identifier type.
593+ and parseTypeExpr = (state : parserState ): option <typeExpr > => {
594+ switch peek (state ) {
595+ | Some ({type_ : TInt }) => { advance (state )-> ignore ; Some (TyInt ) }
596+ | Some ({type_ : TFloat }) => { advance (state )-> ignore ; Some (TyFloat ) }
597+ | Some ({type_ : TString }) => { advance (state )-> ignore ; Some (TyString ) }
598+ | Some ({type_ : TBool }) => { advance (state )-> ignore ; Some (TyBool ) }
599+ | Some ({type_ : TArray }) => {
600+ advance (state )-> ignore
601+ switch parseTypeArgs (state ) {
602+ | (Some (inner ), _ ) => Some (TyArray (inner ))
603+ // Bare `Array` with no parameter: default the element type to `Any`.
604+ | (None , _ ) => Some (TyArray (TyIdent ("Any" )))
605+ }
606+ }
607+ | Some ({type_ : TEcho }) => {
608+ advance (state )-> ignore
609+ let (a , b ) = parseTypeArgs (state )
610+ Some (TyEcho (a , b ))
611+ }
612+ | Some ({type_ : TEchoR }) => {
613+ advance (state )-> ignore
614+ let (a , b ) = parseTypeArgs (state )
615+ Some (TyEchoResidue (a , b ))
616+ }
617+ | Some ({type_ : Identifier (name )}) => { advance (state )-> ignore ; Some (TyIdent (name )) }
618+ | _ => None
619+ }
620+ }
621+
555622// ============================================
556623// Statement Parsing
557624// ============================================
@@ -567,12 +634,17 @@ let parseStatement = (state: parserState): option<stmt> => {
567634 switch peek (state ) {
568635 | Some ({type_ : Identifier (name )}) => {
569636 advance (state )-> ignore
570- // Optional type annotation (skip for now)
637+ // Optional type annotation: `let x: Type = ...`
638+ let type_ = if match_ (state , [Colon ]) {
639+ parseTypeExpr (state )
640+ } else {
641+ None
642+ }
571643 expect (state , Equal , "Expected '=' after variable name" )-> ignore
572644 switch parseExpression (state ) {
573645 | Some (value ) => {
574646 let loc = {start : startLoc .start , end_ : currentLoc (state ).end_ , file : state .file }
575- Some (LetStmt ({mutable_ , name , type_ : None , value , loc }))
647+ Some (LetStmt ({mutable_ , name , type_ , value , loc }))
576648 }
577649 | None => None
578650 }
@@ -809,14 +881,15 @@ let parseDeclaration = (state: parserState): option<decl> => {
809881 advance (state )-> ignore
810882 expect (state , LParen , "Expected '(' after function name" )-> ignore
811883
812- // Parse parameters
884+ // Parse parameters (each with an optional `: Type` annotation)
813885 let params = ref ([])
814886 skipNewlines (state )
815887 if ! check (state , RParen ) {
816888 switch peek (state ) {
817889 | Some ({type_ : Identifier (pname ), loc : ploc }) => {
818890 advance (state )-> ignore
819- params := Array .concat (params .contents , [{name : pname , type_ : None , loc : ploc }])
891+ let ptype = if match_ (state , [Colon ]) { parseTypeExpr (state ) } else { None }
892+ params := Array .concat (params .contents , [{name : pname , type_ : ptype , loc : ploc }])
820893 }
821894 | _ => ()
822895 }
@@ -825,20 +898,26 @@ let parseDeclaration = (state: parserState): option<decl> => {
825898 switch peek (state ) {
826899 | Some ({type_ : Identifier (pname ), loc : ploc }) => {
827900 advance (state )-> ignore
828- params := Array .concat (params .contents , [{name : pname , type_ : None , loc : ploc }])
901+ let ptype = if match_ (state , [Colon ]) { parseTypeExpr (state ) } else { None }
902+ params := Array .concat (params .contents , [{name : pname , type_ : ptype , loc : ploc }])
829903 }
830904 | _ => ()
831905 }
832906 }
833907 }
834908 expect (state , RParen , "Expected ')' after parameters" )-> ignore
835909
836- // Optional return type (skip for now)
910+ // Optional return type: `function f(...) -> Type`
911+ let returnType = if match_ (state , [Arrow ]) {
912+ parseTypeExpr (state )
913+ } else {
914+ None
915+ }
837916 skipNewlines (state )
838917
839918 let body = parseBlock (state )
840919 let loc = {start : startLoc .start , end_ : currentLoc (state ).end_ , file : state .file }
841- Some (FunctionDecl ({name , params : params .contents , returnType : None , body , loc }))
920+ Some (FunctionDecl ({name , params : params .contents , returnType , body , loc }))
842921 }
843922 | _ => {
844923 addDiagnostic (state , E0001 , "Expected function name" , currentLoc (state ))
@@ -869,29 +948,10 @@ let parseDeclaration = (state: parserState): option<decl> => {
869948 | Some ({type_ : Identifier (fname )}) => {
870949 advance (state )-> ignore
871950 expect (state , Colon , "Expected ':' after field name" )-> ignore
872- // Simple type parsing
873- switch peek (state ) {
874- | Some ({type_ : TInt }) => {
875- advance (state )-> ignore
876- fields := Array .concat (fields .contents , [(fname , TyInt )])
877- }
878- | Some ({type_ : TFloat }) => {
879- advance (state )-> ignore
880- fields := Array .concat (fields .contents , [(fname , TyFloat )])
881- }
882- | Some ({type_ : TString }) => {
883- advance (state )-> ignore
884- fields := Array .concat (fields .contents , [(fname , TyString )])
885- }
886- | Some ({type_ : TBool }) => {
887- advance (state )-> ignore
888- fields := Array .concat (fields .contents , [(fname , TyBool )])
889- }
890- | Some ({type_ : Identifier (tname )}) => {
891- advance (state )-> ignore
892- fields := Array .concat (fields .contents , [(fname , TyIdent (tname ))])
893- }
894- | _ => ()
951+ // Full type parsing (primitives, Array<T>, Echo<A, B>, EchoR<A, B>, idents)
952+ switch parseTypeExpr (state ) {
953+ | Some (ty ) => fields := Array .concat (fields .contents , [(fname , ty )])
954+ | None => ()
895955 }
896956 }
897957 | _ => ()
0 commit comments