MODULE----------------------------------------------------- compilation units ---M3Parse EXPORTSM3AST ; IMPORT Target, TInt, MxConfig; IMPORT M3ID, Text; IMPORT M3Lexer; FROM M3Scanner IMPORT TK_Comment, TK_EOF, TK_Error, (* literals *) TK_Ident, TK_Card_const, TK_Real_const, TK_Longreal_const, TK_Extended_const, TK_Char_const, TK_Text_const, (* operators *) TK_Plus, TK_Minus, TK_Asterisk, TK_Slash, TK_Assign, TK_Ampersand, TK_Dot, TK_Comma, TK_Semi, TK_L_paren, TK_L_bracket, TK_L_brace, TK_Arrow, TK_Equal, TK_Sharp, TK_Less, TK_Greater, TK_Ls_equal, TK_Gr_equal, TK_Dot_dot, TK_Colon, TK_R_paren, TK_R_bracket, TK_R_brace, TK_Bar, TK_Subtype, TK_Implies, TK_End_pragma, (* reserved words *) TK_And, TK_Any, TK_Array, TK_As, TK_Begin, TK_Bits, TK_Branded, TK_By, TK_Case, TK_Const, TK_Div, TK_Do, TK_Else, TK_Elsif, TK_End, TK_Eval, TK_Except, TK_Exception, TK_Exit, TK_Exports, TK_Finally, TK_For, TK_From, TK_Generic, TK_If, TK_Import, TK_In, TK_Interface, TK_Lock, TK_Loop, TK_Methods, TK_Mod, TK_Module, TK_Not, TK_Object, TK_Of, TK_Or, TK_Overrides, TK_Procedure, TK_Raise, TK_Raises, TK_Readonly, TK_Record, TK_Ref, TK_Repeat, TK_Return, TK_Reveal, TK_Set, TK_Then, TK_To, TK_Try, TK_Type, TK_Typecase, TK_Unsafe, TK_Until, TK_Untraced, TK_Value, TK_Var, TK_While, TK_With; FROM M3Lexer IMPORT TK, (* pragmas *) TK_Inline, TK_External, TK_Assert, TK_Unused, TK_Obsolete, <*NOWARN*>TK_Trace, TK_CallConv, TK_Fatal; TYPE TKSet = SET OF TK; TYPE State = RECORD scan : M3Lexer.T; err : ErrorHandler; ast : T; head : Chunk; tail : Chunk; n_ops : CARDINAL; (* next available node index *) base : CARDINAL; (* first node index in tail *) cur : CARDINAL; (* next available node slot in tail *) tok : TK; n_texts : INTEGER; n_ints : INTEGER; n_floats : INTEGER; END; TYPE Chunk = REF RECORD next : Chunk := NIL; nodes : ARRAY [0..999] OF Node; END; EXCEPTION Error; (* => early bail out requested by client *) PROCEDUREParse (scan: M3Lexer.T; err: ErrorHandler): T = VAR s: State; BEGIN s.scan := scan; s.err := err; s.ast := NEW (T); s.head := NEW (Chunk); s.tail := s.head; s.n_ops := 0; s.base := 0; s.cur := 0; s.n_texts := 0; s.n_ints := 0; s.n_floats := 0; s.ast.nodes := NIL; s.ast.safe := TRUE; s.ast.interface := FALSE; s.ast.texts := NIL; s.ast.ints := NIL; s.ast.floats := NIL; TRY InitTarget (s); GetToken (s); Unit (s); EXCEPT Error => (* early bail out... *) END; s.ast.nodes := FlattenChunks (s); (* make sure the collector has a chance... *) s.head := NIL; s.tail := NIL; s.scan := NIL; RETURN s.ast; END Parse; PROCEDUREInitTarget (VAR s: State) RAISES {Error} = VAR sys: TEXT; BEGIN IF Target.System_name = NIL THEN sys := MxConfig.Get ("TARGET"); IF (sys = NIL) THEN Err (s, "unknown target architecture"); ELSIF NOT Target.Init (sys) THEN Err (s, "unsupported target architecture: ", sys); END; END; END InitTarget;
PROCEDURE---------------------------------------------------------- declarations ---Unit (VAR s: State) RAISES {Error} = VAR id1, id2: M3ID.T; z: CARDINAL; id: M3ID.T; BEGIN IF (s.tok = TK_External) THEN s.ast.external := TRUE; ExternalPragma (s, id, s.ast.module_cc); IF (id # M3ID.NoID) THEN Err (s, "<*EXTERNAL*> module name ignored: ", M3ID.ToText (id)); END; END; IF (s.tok = TK_Generic) THEN GetToken (s); (* GENERIC *) UnitKind (s); (* INTERFACE / MODULE *) id1 := MatchID (s); z := AddOp (s, OP_Generic, id1); GenericArgs (s); Match (s, TK_Semi); UnitBody (s); FixWidth (s, z); ELSE IF (s.tok = TK_Unsafe) THEN s.ast.safe := FALSE; GetToken (s); END; UnitKind (s); (* INTERFACE / MODULE *) id1 := MatchID (s); z := AddOp (s, OP_Unit, id1); IF NOT s.ast.interface THEN Exports (s); END; IF (s.tok = TK_Semi) THEN GetToken (s); (* ; *) UnitBody (s); ELSIF (s.tok = TK_Equal) THEN GetToken (s); (* = *) FixOp (s, z, OP_GenInstance); EVAL AddOp (s, OP_Id, MatchID (s)); GenericArgs (s); Match (s, TK_End); ELSE Err (s, "expected ';' or '=', found ", TokName (s)); END; FixWidth (s, z); END; id2 := MatchID (s); IF (id1 # id2) THEN Err (s, "initial unit name \"", M3ID.ToText (id1), "\" doesn't match final name \"", M3ID.ToText (id2) & "\""); END; Match (s, TK_Dot); Match (s, TK_EOF); END Unit; PROCEDUREUnitKind (VAR s: State) RAISES {Error} = BEGIN IF (s.tok = TK_Interface) THEN s.ast.interface := TRUE; GetToken (s); (* INTERFACE *) ELSIF (s.tok = TK_Module) THEN s.ast.interface := FALSE; GetToken (s); (* MODULE *) ELSE Err (s, "expected INTERFACE or MODULE keyword, found ", TokName (s)); END; END UnitKind; PROCEDUREUnitBody (VAR s: State) RAISES {Error} = VAR z: CARDINAL; BEGIN Imports (s); IF s.ast.interface THEN z := AddOp (s, OP_Block); Decls (s); FixWidth (s, z); Match (s, TK_End); ELSE Block (s); END; END UnitBody; PROCEDUREGenericArgs (VAR s: State) RAISES {Error} = BEGIN Match (s, TK_L_paren); WHILE (s.tok = TK_Ident) DO EVAL AddOp (s, OP_GenericArg, MatchID (s)); IF (s.tok # TK_Comma) THEN EXIT; END; GetToken (s); (* , *) END; Match (s, TK_R_paren); END GenericArgs; PROCEDUREExports (VAR s: State) RAISES {Error} = BEGIN IF (s.tok = TK_Exports) THEN GetToken (s); (* EXPORTS *) EVAL AddOp (s, OP_Export, MatchID (s)); WHILE (s.tok = TK_Comma) DO GetToken (s); (* , *) EVAL AddOp (s, OP_Export, MatchID (s)); END; END; END Exports; PROCEDUREImports (VAR s: State) RAISES {Error} = VAR id, id2: M3ID.T; z: CARDINAL; BEGIN LOOP IF (s.tok = TK_Import) THEN GetToken (s); (* IMPORT *) WHILE (s.tok = TK_Ident) DO id := MatchID (s); IF (s.tok = TK_As) THEN GetToken (s); (* AS *) id2 := MatchID (s); z := AddOp (s, OP_ImportAs, id2); EVAL AddOp (s, OP_Id, id); FixWidth (s, z); ELSE EVAL AddOp (s, OP_Import, id); END; IF (s.tok # TK_Comma) THEN EXIT; END; GetToken (s); (* , *) END; Match (s, TK_Semi); ELSIF (s.tok = TK_From) THEN GetToken (s); (* FROM *) id := MatchID (s); Match (s, TK_Import); WHILE (s.tok = TK_Ident) DO id2 := MatchID (s); z := AddOp (s, OP_FromImport, id2); EVAL AddOp (s, OP_Id, id); FixWidth (s, z); IF (s.tok # TK_Comma) THEN EXIT; END; GetToken (s); (* , *) END; Match (s, TK_Semi); ELSE EXIT; END; END; END Imports; PROCEDUREBlock (VAR s: State) RAISES {Error} = VAR z := AddOp (s, OP_Block); BEGIN Decls (s); Match (s, TK_Begin); Stmt (s); Match (s, TK_End); FixWidth (s, z); END Block;
CONST DeclStart = TKSet {TK_Const, TK_Type, TK_Reveal, TK_Var, TK_External, TK_Inline, TK_Unused, TK_Obsolete, TK_Exception, TK_Procedure, TK_Fatal, TK_CallConv}; PROCEDURE------------------------------------------------------------ statements ---Decls (VAR s: State) RAISES {Error} = VAR att: DeclAttributes; BEGIN WHILE (s.tok IN DeclStart) DO DeclPragmas (s, att); CASE s.tok OF | TK_Const => ConstDecl (s, att); | TK_Type => TypeDecl (s, att); | TK_Var => VarDecl (s, att); | TK_Procedure => ProcDecl (s, att); | TK_Reveal => Reveal (s, att); | TK_Exception => ExceptDecl (s, att); | TK_Fatal => FatalPragma (s, att); ELSE IF att.gotSome THEN Err (s, "declaration pragma not followed by a declaration"); END; END; END; END Decls; PROCEDUREConstDecl (VAR s: State; READONLY att: DeclAttributes) RAISES {Error} = VAR id: M3ID.T; z: CARDINAL; BEGIN Match (s, TK_Const); WHILE (s.tok = TK_Ident) DO id := MatchID (s); z := AddOp (s, OP_ConstDecl, id); IF (s.tok = TK_Colon) THEN GetToken (s); (* : *) Type (s); ELSE EVAL AddOp (s, OP_Empty); END; Match (s, TK_Equal); Expr (s); GenAttributes (s, att); FixWidth (s, z); Match (s, TK_Semi); END; END ConstDecl; PROCEDURETypeDecl (VAR s: State; READONLY att: DeclAttributes) RAISES {Error} = VAR id: M3ID.T; z: CARDINAL; BEGIN Match (s, TK_Type); WHILE (s.tok = TK_Ident) DO id := MatchID (s); z := AddOp (s, OP_TypeDecl, id); IF (s.tok = TK_Equal) THEN GetToken (s); (* = *) ELSIF (s.tok = TK_Subtype) THEN GetToken (s); (* <: *) FixOp (s, z, OP_OpaqueDecl); ELSE Err (s, "expected '=' or '<:', found ", TokName (s)); END; Type (s); GenAttributes (s, att); FixWidth (s, z); Match (s, TK_Semi); END; END TypeDecl; PROCEDUREVarDecl (VAR s: State; READONLY att: DeclAttributes) RAISES {Error} = VAR id: M3ID.T; z: CARDINAL; BEGIN Match (s, TK_Var); WHILE (s.tok = TK_Ident) DO z := AddOp (s, OP_VarDecl); WHILE (s.tok = TK_Ident) DO id := MatchID (s); EVAL AddOp (s, OP_VarDefn, id); IF (s.tok # TK_Comma) THEN EXIT; END; GetToken (s); (* , *) END; IF (s.tok = TK_Colon) THEN GetToken (s); (* : *) Type (s); ELSE EVAL AddOp (s, OP_Empty); END; IF (s.tok = TK_Assign) THEN GetToken (s); (* := *) Expr (s); ELSE EVAL AddOp (s, OP_Empty); END; GenAttributes (s, att); FixWidth (s, z); Match (s, TK_Semi); END; END VarDecl; PROCEDUREProcDecl (VAR s: State; READONLY att: DeclAttributes) RAISES {Error} = VAR id: M3ID.T; z: CARDINAL; BEGIN Match (s, TK_Procedure); id := MatchID (s); z := AddOp (s, OP_ProcDecl, id); ProcSignature (s, att.callingConv); IF (s.ast.interface) THEN IF (s.tok = TK_Equal) THEN Err (s, "procedure body is not allowed in an interface"); GetToken (s); (* = *) ProcBody (s, id); END; ELSE (* NOT interface *) Match (s, TK_Equal); ProcBody (s, id); END; Match (s, TK_Semi); GenAttributes (s, att); FixWidth (s, z); END ProcDecl; PROCEDUREProcBody (VAR s: State; proc_id: M3ID.T) RAISES {Error} = VAR end_id: M3ID.T; BEGIN Block (s); end_id := MatchID (s); IF (proc_id # end_id) THEN Err (s, "initial procedure name \"", M3ID.ToText (proc_id), "\" doesn't match final name \"", M3ID.ToText (end_id) & "\""); END; END ProcBody; PROCEDUREReveal (VAR s: State; READONLY att: DeclAttributes) RAISES {Error} = VAR z: CARDINAL; BEGIN Match (s, TK_Reveal); WHILE (s.tok = TK_Ident) DO z := AddOp (s, OP_Reveal); QID (s); IF (s.tok = TK_Equal) THEN GetToken (s); (* = *) Type (s); ELSIF (s.tok = TK_Subtype) THEN FixOp (s, z, OP_RevealPartial); GetToken (s); (* <: *) Type (s); ELSE Err (s, "expected '=' or '<:', found ", TokName (s)); END; GenAttributes (s, att); FixWidth (s, z); Match (s, TK_Semi); END; END Reveal; PROCEDUREExceptDecl (VAR s: State; READONLY att: DeclAttributes) RAISES {Error} = VAR id: M3ID.T; z: CARDINAL; BEGIN Match (s, TK_Exception); WHILE (s.tok = TK_Ident) DO id := MatchID (s); z := AddOp (s, OP_ExceptDecl, id); IF (s.tok = TK_L_paren) THEN GetToken (s); (* ( *) Type (s); Match (s, TK_R_paren); ELSE EVAL AddOp (s, OP_Empty); END; GenAttributes (s, att); FixWidth (s, z); Match (s, TK_Semi); END; END ExceptDecl;
CONST StmtStart = TKSet {TK_Case, TK_Exit, TK_Eval, TK_For, TK_If, TK_Lock, TK_Loop, TK_Raise, TK_Repeat, TK_Return, TK_Try, TK_Typecase, TK_While, TK_With, TK_Begin, TK_Assert, TK_Ident, TK_L_paren, TK_Array, TK_Record} + DeclStart; PROCEDURE----------------------------------------------------------------- types ---Stmt (VAR s: State) RAISES {Error} = VAR z := AddOp (s, OP_StmtList); BEGIN LOOP CASE s.tok OF | TK_Const, TK_Type, TK_Reveal, TK_Var, TK_External, TK_Inline, TK_Unused, TK_Obsolete, TK_Exception, TK_CallConv, TK_Procedure, TK_Fatal, TK_Begin => Block (s); | TK_Ident, TK_L_paren, TK_Array, TK_Record => AssignStmt (s); | TK_Assert => AssertStmt (s); | TK_Case => CaseStmt (s); | TK_Exit => ExitStmt (s); | TK_Eval => EvalStmt (s); | TK_For => ForStmt (s); | TK_If => IfStmt (s); | TK_Lock => LockStmt (s); | TK_Loop => LoopStmt (s); | TK_Raise => RaiseStmt (s); | TK_Repeat => RepeatStmt (s); | TK_Return => ReturnStmt (s); | TK_Try => TryStmt (s); | TK_Typecase => TypeCaseStmt (s); | TK_While => WhileStmt (s); | TK_With => WithStmt (s); ELSE EXIT; END; IF (s.tok = TK_Semi) THEN GetToken (s); (* ; *) EmptyStmts (s); ELSIF (s.tok IN StmtStart) THEN (* assume the simple mistake and keep going *) Err (s, "expected ';', found ", TokName (s)); ELSE EXIT; END; END; FixWidth (s, z); END Stmt; PROCEDUREEmptyStmts (VAR s: State) RAISES {Error} = (* try to handle empty statements gracefully *) VAR err_line := -1; BEGIN WHILE (s.tok = TK_Semi) DO IF (err_line # s.scan.line) THEN Err (s, "empty statement, ignored"); err_line := s.scan.line; END; GetToken (s); (* ; *) END; END EmptyStmts; PROCEDUREAssignStmt (VAR s: State) RAISES {Error} = VAR z: CARDINAL; BEGIN z := AddOp (s, OP_Assign); Expr (s); IF (s.tok = TK_Assign) THEN GetToken (s); (* := *) Expr (s); ELSE FixOp (s, z, OP_CallStmt); END; FixWidth (s, z); END AssignStmt; PROCEDUREAssertStmt (VAR s: State) RAISES {Error} = VAR z: CARDINAL; BEGIN Match (s, TK_Assert); z := AddOp (s, OP_Assert); Expr (s); IF (s.tok # TK_End_pragma) THEN Err (s, "expected '*>', found ", TokName (s)); ELSE s.tok := TK_Semi; (* fake out the Stmt() parser *) END; FixWidth (s, z); END AssertStmt; PROCEDURECaseStmt (VAR s: State) RAISES {Error} = VAR z, zz: CARDINAL; bar: TK; BEGIN Match (s, TK_Case); z := AddOp (s, OP_Case); Expr (s); Match (s, TK_Of); bar := s.tok; IF (bar = TK_Bar) THEN GetToken (s); (* | *) END; WHILE (s.tok # TK_Else) AND (s.tok # TK_End) DO CaseBranch (s); bar := s.tok; IF (bar # TK_Bar) THEN EXIT END; GetToken (s); (* | *) END; IF (bar = TK_Bar) THEN Err (s, "missing case branch"); END; IF (s.tok = TK_Else) THEN GetToken (s); (* ELSE *) zz := AddOp (s, OP_CaseElse); Stmt (s); FixWidth (s, zz); END; Match (s, TK_End); FixWidth (s, z); END CaseStmt; PROCEDURECaseBranch (VAR s: State) RAISES {Error} = VAR z, zz: CARDINAL; BEGIN z := AddOp (s, OP_CaseBranch); (* read the labels *) LOOP zz := AddOp (s, OP_CaseLabel); Expr (s); IF (s.tok = TK_Dot_dot) THEN FixOp (s, z, OP_CaseRange); GetToken (s); (* .. *) Expr (s); END; FixWidth (s, zz); IF (s.tok # TK_Comma) THEN EXIT END; GetToken (s); (* , *) END; Match (s, TK_Implies); Stmt (s); FixWidth (s, z); END CaseBranch; PROCEDUREExitStmt (VAR s: State) RAISES {Error} = BEGIN Match (s, TK_Exit); EVAL AddOp (s, OP_Exit); END ExitStmt; PROCEDUREEvalStmt (VAR s: State) RAISES {Error} = VAR z: CARDINAL; BEGIN Match (s, TK_Eval); z := AddOp (s, OP_Eval); Expr (s); FixWidth (s, z); END EvalStmt; PROCEDUREForStmt (VAR s: State) RAISES {Error} = VAR z: CARDINAL; id: M3ID.T; BEGIN Match (s, TK_For); id := MatchID (s); z := AddOp (s, OP_For1, id); Match (s, TK_Assign); Expr (s); Match (s, TK_To); Expr (s); IF (s.tok = TK_By) THEN FixOp (s, z, OP_ForN); GetToken (s); (* BY *) Expr (s); END; Match (s, TK_Do); Stmt (s); FixWidth (s, z); Match (s, TK_End); END ForStmt; PROCEDUREIfStmt (VAR s: State) RAISES {Error} = VAR z, zz: CARDINAL; BEGIN Match (s, TK_If); z := AddOp (s, OP_If); zz := AddOp (s, OP_IfClause); Expr (s); Match (s, TK_Then); Stmt (s); FixWidth (s, zz); WHILE (s.tok = TK_Elsif) DO GetToken (s); (* ELSIF *) zz := AddOp (s, OP_IfClause); Expr (s); Match (s, TK_Then); Stmt (s); FixWidth (s, zz); END; IF (s.tok = TK_Else) THEN GetToken (s); (* ELSE *) zz := AddOp (s, OP_IfElse); Stmt (s); FixWidth (s, zz); END; FixWidth (s, z); Match (s, TK_End); END IfStmt; PROCEDURELockStmt (VAR s: State) RAISES {Error} = VAR z: CARDINAL; BEGIN Match (s, TK_Lock); z := AddOp (s, OP_Lock); Expr (s); Match (s, TK_Do); Stmt (s); FixWidth (s, z); Match (s, TK_End); END LockStmt; PROCEDURELoopStmt (VAR s: State) RAISES {Error} = VAR z: CARDINAL; BEGIN Match (s, TK_Loop); z := AddOp (s, OP_Loop); Stmt (s); FixWidth (s, z); Match (s, TK_End); END LoopStmt; PROCEDURERaiseStmt (VAR s: State) RAISES {Error} = VAR z: CARDINAL; BEGIN Match (s, TK_Raise); z := AddOp (s, OP_Raise); QID (s); IF (s.tok = TK_L_paren) THEN FixOp (s, z, OP_RaiseValue); GetToken (s); (* ( *) Expr (s); Match (s, TK_R_paren); END; FixWidth (s, z); END RaiseStmt; PROCEDURERepeatStmt (VAR s: State) RAISES {Error} = VAR z: CARDINAL; BEGIN Match (s, TK_Repeat); z := AddOp (s, OP_Repeat); Stmt (s); Match (s, TK_Until); Expr (s); FixWidth (s, z); END RepeatStmt; PROCEDUREReturnStmt (VAR s: State) RAISES {Error} = VAR z: CARDINAL; BEGIN Match (s, TK_Return); z := AddOp (s, OP_Return); IF (s.tok IN ExprStart) THEN FixOp (s, z, OP_ReturnValue); Expr (s); END; FixWidth (s, z); END ReturnStmt; PROCEDURETryStmt (VAR s: State) RAISES {Error} = VAR z, zz: CARDINAL; bar: TK; BEGIN Match (s, TK_Try); z := AddOp (s, OP_TryFinally); Stmt (s); IF (s.tok = TK_Finally) THEN GetToken (s); (* FINALLY *) Stmt (s); ELSE FixOp (s, z, OP_TryExcept); Match (s, TK_Except); bar := s.tok; IF (bar = TK_Bar) THEN GetToken (s); (* | *) END; WHILE (s.tok # TK_Else) AND (s.tok # TK_End) DO TryHandler (s); bar := s.tok; IF (bar # TK_Bar) THEN EXIT END; GetToken (s); (* | *) END; IF (bar = TK_Bar) THEN Err (s, "missing TRY EXCEPT handler"); END; IF (s.tok = TK_Else) THEN GetToken (s); (* ELSE *) zz := AddOp (s, OP_TryElse); Stmt (s); FixWidth (s, zz); END; END; FixWidth (s, z); Match (s, TK_End); END TryStmt; PROCEDURETryHandler (VAR s: State) RAISES {Error} = VAR z: CARDINAL; id: M3ID.T; BEGIN z := AddOp (s, OP_TryHandler); LOOP QID (s); IF (s.tok # TK_Comma) THEN EXIT; END; GetToken (s); (* , *) END; IF (s.tok = TK_L_paren) THEN GetToken (s); (* ( *) id := MatchID (s); FixOpInfo (s, z, OP_TryHandlerVar, id); Match (s, TK_R_paren); END; Match (s, TK_Implies); Stmt (s); FixWidth (s, z); END TryHandler; PROCEDURETypeCaseStmt (VAR s: State) RAISES {Error} = VAR z, zz: CARDINAL; bar: TK; BEGIN Match (s, TK_Typecase); z := AddOp (s, OP_TypeCase); Expr (s); Match (s, TK_Of); bar := s.tok; IF (bar = TK_Bar) THEN GetToken (s); (* | *) END; WHILE (s.tok # TK_Else) AND (s.tok # TK_End) DO TypeCaseArm (s); bar := s.tok; IF (bar # TK_Bar) THEN EXIT; END; GetToken (s); (* | *) END; IF (bar = TK_Bar) THEN Err (s, "missing TYPECASE arm"); END; IF (s.tok = TK_Else) THEN GetToken (s); (* ELSE *) zz := AddOp (s, OP_TypeCaseElse); Stmt (s); FixWidth (s, zz); END; FixWidth (s, z); Match (s, TK_End); END TypeCaseStmt; PROCEDURETypeCaseArm (VAR s: State) RAISES {Error} = VAR z: CARDINAL; id: M3ID.T; BEGIN z := AddOp (s, OP_TypeCaseArm); LOOP Type (s); IF (s.tok # TK_Comma) THEN EXIT; END; GetToken (s); (* , *) END; IF (s.tok = TK_L_paren) THEN GetToken (s); (* ( *) id := MatchID (s); FixOpInfo (s, z, OP_TypeCaseVar, id); Match (s, TK_R_paren); END; Match (s, TK_Implies); Stmt (s); FixWidth (s, z); END TypeCaseArm; PROCEDUREWhileStmt (VAR s: State) RAISES {Error} = VAR z: CARDINAL; BEGIN Match (s, TK_While); z := AddOp (s, OP_While); Expr (s); Match (s, TK_Do); Stmt (s); FixWidth (s, z); Match (s, TK_End); END WhileStmt; PROCEDUREWithStmt (VAR s: State) RAISES {Error} = BEGIN Match (s, TK_With); WithTail (s); END WithStmt; PROCEDUREWithTail (VAR s: State) RAISES {Error} = VAR z: CARDINAL; id: M3ID.T; BEGIN id := MatchID (s); z := AddOp (s, OP_With, id); Match (s, TK_Equal); Expr (s); IF (s.tok = TK_Comma) THEN GetToken (s); (* , *) WithTail (s); ELSE Match (s, TK_Do); Stmt (s); Match (s, TK_End); END; FixWidth (s, z); END WithTail;
*** CONST TypeStart = TKSet {TK_Ident, TK_Array, TK_Bits, TK_Branded, TK_L_brace, TK_Untraced, TK_Object, TK_Procedure, TK_Record, TK_Ref, TK_Set, TK_L_bracket, TK_L_paren}; **
PROCEDURE----------------------------------------------------------- expressions ---Type (VAR s: State) RAISES {Error} = VAR z: CARDINAL; BEGIN CASE s.tok OF | TK_Ident => NamedType (s); | TK_Array => ArrayType (s); | TK_Bits => PackedType (s); | TK_Branded => RefType (s); | TK_L_brace => EnumType (s); | TK_Untraced => RefType (s); | TK_Object => ObjectType (s); | TK_CallConv => ProcType (s); | TK_Procedure => ProcType (s); | TK_Record => RecordType (s); | TK_Ref => RefType (s); | TK_Set => SetType (s); | TK_L_bracket => SubrangeType (s); | TK_L_paren => z := s.n_ops; GetToken (s); (* ( *) Type (s); Match (s, TK_R_paren); IF (s.tok = TK_Branded) OR (s.tok = TK_Object) THEN ObjectTail (s, z); END; ELSE Err (s, "bad type expression"); END; END Type; PROCEDUREArrayType (VAR s: State) RAISES {Error} = VAR z: CARDINAL; BEGIN Match (s, TK_Array); IF (s.tok = TK_Of) THEN GetToken (s); (* OF *) z := AddOp (s, OP_OpenArray); Type (s); FixWidth (s, z); ELSE ArrayTail (s, AddOp (s, OP_Array)); END; END ArrayType; PROCEDUREArrayTail (VAR s: State; head: CARDINAL) RAISES {Error} = BEGIN Type (s); IF (s.tok = TK_Comma) THEN GetToken (s); (* , *) ArrayTail (s, AddOp (s, OP_Array)); ELSE Match (s, TK_Of); Type (s); END; FixWidth (s, head); END ArrayTail; PROCEDUREEnumType (VAR s: State) RAISES {Error} = VAR z: CARDINAL; BEGIN Match (s, TK_L_brace); z := AddOp (s, OP_Enum); IF (s.tok = TK_Ident) THEN LOOP EVAL AddOp (s, OP_EnumDefn, MatchID (s)); IF (s.tok # TK_Comma) THEN EXIT; END; GetToken (s); (* , *) END; END; FixWidth (s, z); Match (s, TK_R_brace); END EnumType; PROCEDURENamedType (VAR s: State) RAISES {Error} = VAR z := AddOp (s, OP_NamedType); BEGIN QID (s); FixWidth (s, z); IF (s.tok = TK_Branded) OR (s.tok = TK_Object) THEN ObjectTail (s, z); END; END NamedType; PROCEDUREObjectType (VAR s: State) RAISES {Error} = VAR z := AddOp (s, OP_Object); BEGIN ObjectBody (s, z); FixWidth (s, z); END ObjectType; PROCEDUREObjectTail (VAR s: State; super: CARDINAL) RAISES {Error} = BEGIN InsertOp (s, super, OP_Object); Brand (s); ObjectBody (s, super); FixWidth (s, super); END ObjectTail; PROCEDUREBrand (VAR s: State) RAISES {Error} = BEGIN IF (s.tok = TK_Branded) THEN GetToken (s); (* BRANDED *) IF (s.tok IN ExprStart) THEN Expr (s); ELSE EVAL AddOp (s, OP_DefaultBrand); END; ELSE EVAL AddOp (s, OP_NoBrand); END; END Brand; PROCEDUREObjectBody (VAR s: State; head: CARDINAL) RAISES {Error} = BEGIN Match (s, TK_Object); Fields (s); IF (s.tok = TK_Methods) THEN GetToken (s); (* METHODS *) Methods (s); END; IF (s.tok = TK_Overrides) THEN GetToken (s); (* OVERRIDES *) Overrides (s); END; Match (s, TK_End); IF (s.tok = TK_Branded) OR (s.tok = TK_Object) THEN ObjectTail (s, head); END; END ObjectBody; PROCEDUREFields (VAR s: State) RAISES {Error} = VAR z: CARDINAL; BEGIN WHILE (s.tok = TK_Ident) DO z := AddOp (s, OP_Field); FieldDecls (s, OP_FieldDefn); FixWidth (s, z); IF (s.tok # TK_Semi) THEN EXIT; END; GetToken (s); (* ; *) END; END Fields; PROCEDUREFieldDecls (VAR s: State; defn_op: OP) RAISES {Error} = BEGIN LOOP EVAL AddOp (s, defn_op, MatchID (s)); IF (s.tok # TK_Comma) THEN EXIT; END; GetToken (s); (* , *) END; IF (s.tok = TK_Colon) THEN GetToken (s); (* : *) Type (s); ELSE EVAL AddOp (s, OP_Empty); END; IF (s.tok = TK_Assign) THEN GetToken (s); (* := *) Expr (s); ELSE EVAL AddOp (s, OP_Empty); END; END FieldDecls; PROCEDUREMethods (VAR s: State) RAISES {Error} = VAR z: CARDINAL; id: M3ID.T; BEGIN WHILE (s.tok = TK_Ident) DO id := MatchID (s); z := AddOp (s, OP_Method, id); ProcSignature (s, M3ID.NoID); IF (s.tok = TK_Assign) THEN GetToken (s); (* := *) Expr (s); ELSE EVAL AddOp (s, OP_Empty); END; FixWidth (s, z); IF (s.tok # TK_Semi) THEN EXIT; END; GetToken (s); (* ; *) END; END Methods; PROCEDUREOverrides (VAR s: State) RAISES {Error} = VAR z: CARDINAL; id: M3ID.T; BEGIN WHILE (s.tok = TK_Ident) DO id := MatchID (s); z := AddOp (s, OP_Override, id); Match (s, TK_Assign); Expr (s); FixWidth (s, z); IF (s.tok # TK_Semi) THEN EXIT; END; GetToken (s); (* ; *) END; END Overrides; PROCEDUREPackedType (VAR s: State) RAISES {Error} = VAR z := AddOp (s, OP_Packed); BEGIN Match (s, TK_Bits); Expr (s); Match (s, TK_For); Type (s); FixWidth (s, z); END PackedType; PROCEDUREProcType (VAR s: State) RAISES {Error} = VAR cc: M3ID.T := M3ID.NoID; BEGIN IF (s.tok = TK_CallConv) THEN cc := s.scan.id; GetToken (s); (* calling convention *) Match (s, TK_End_pragma); END; Match (s, TK_Procedure); ProcSignature (s, cc); END ProcType; CONST FormalStart = TKSet {TK_Value, TK_Var, TK_Readonly, TK_Ident, TK_Unused}; PROCEDUREProcSignature (VAR s: State; cc: M3ID.T) RAISES {Error} = VAR z := AddOp (s, OP_ProcType, cc); BEGIN Match (s, TK_L_paren); WHILE (s.tok IN FormalStart) DO Formal (s); IF (s.tok # TK_Semi) THEN EXIT END; GetToken (s); (* ; *) END; Match (s, TK_R_paren); IF (s.tok = TK_Colon) THEN GetToken (s); (* : *) Type (s); ELSE EVAL AddOp (s, OP_Empty); END; Raises (s); FixWidth (s, z); END ProcSignature; PROCEDUREFormal (VAR s: State) RAISES {Error} = VAR z: CARDINAL; mode: INTEGER; BEGIN IF (s.tok = TK_Value) THEN mode := 0; GetToken (s); ELSIF (s.tok = TK_Var) THEN mode := 1; GetToken (s); ELSIF (s.tok = TK_Readonly) THEN mode := 2; GetToken (s); ELSE mode := 0; END; z := AddOp (s, OP_Formal, mode); FieldDecls (s, OP_FormalDefn); FixWidth (s, z); END Formal; PROCEDURERaises (VAR s: State) RAISES {Error} = VAR z: CARDINAL; BEGIN IF (s.tok = TK_Raises) THEN GetToken (s); (* RAISES *) IF (s.tok = TK_Any) THEN EVAL AddOp (s, OP_RaisesAny); ELSE z := AddOp (s, OP_Raises); Match (s, TK_L_brace); IF (s.tok = TK_Ident) THEN LOOP QID (s); IF (s.tok # TK_Comma) THEN EXIT END; GetToken (s); (* , *) END; END; Match (s, TK_R_brace); FixWidth (s, z); END; ELSE EVAL AddOp (s, OP_Raises); END; END Raises; PROCEDURERecordType (VAR s: State) RAISES {Error} = VAR z := AddOp (s, OP_Record); BEGIN Match (s, TK_Record); Fields (s); Match (s, TK_End); FixWidth (s, z); END RecordType; VAR root_id := M3ID.NoID; PROCEDURERefType (VAR s: State) RAISES {Error} = VAR z := s.n_ops; id: M3ID.T; BEGIN IF (s.tok = TK_Untraced) THEN GetToken (s); (* UNTRACED *) IF (s.tok = TK_Ident) THEN id := MatchID (s); IF (root_id = M3ID.NoID) THEN root_id := M3ID.Add ("ROOT"); END; IF (id # root_id) THEN Err (s, "expected UNTRACED ROOT, found ", M3ID.ToText (id)); END; ObjectTail (s, AddOp (s, OP_UntracedRoot)); RETURN; END; z := AddOp (s, OP_UntracedRef); ELSE z := AddOp (s, OP_Ref); END; Brand (s); IF (s.tok = TK_Ref) THEN GetToken (s); (* REF *) Type (s); FixWidth (s, z); ELSE (* must be: BRANDED "foo" OBJECT ... *) InsertOp (s, z, OP_Root); (* before the brand *) InsertOp (s, z, OP_Object); (* before ROOT *) ObjectBody (s, z); FixWidth (s, z); END; END RefType; PROCEDURESetType (VAR s: State) RAISES {Error} = VAR z := AddOp (s, OP_Set); BEGIN Match (s, TK_Set); Match (s, TK_Of); Type (s); FixWidth (s, z); END SetType; PROCEDURESubrangeType (VAR s: State) RAISES {Error} = VAR z := AddOp (s, OP_Subrange); BEGIN Match (s, TK_L_bracket); Expr (s); Match (s, TK_Dot_dot); Expr (s); Match (s, TK_R_bracket); FixWidth (s, z); END SubrangeType;
CONST ExprStart = TKSet {TK_Not, TK_Plus, TK_Minus, TK_Ident, TK_Card_const, TK_Longreal_const, TK_Real_const, TK_Extended_const, TK_Char_const, TK_Text_const, TK_L_paren, TK_Array, TK_Bits, TK_Record, TK_Set}; PROCEDURE--------------------------------------------------------------- pragmas ---Expr (VAR s: State) RAISES {Error} = BEGIN E0 (s, FALSE); END Expr; PROCEDUREE0 (VAR s: State; types: BOOLEAN) RAISES {Error} = VAR z := s.n_ops; BEGIN E1 (s, types); WHILE (s.tok = TK_Or) DO GetToken (s); (* OR *) InsertOp (s, z, OP_Or); E1 (s, FALSE); FixWidth (s, z); END; END E0; PROCEDUREE1 (VAR s: State; types: BOOLEAN) RAISES {Error} = VAR z := s.n_ops; BEGIN E2 (s, types); WHILE (s.tok = TK_And) DO GetToken (s); (* AND *) InsertOp (s, z, OP_And); E2 (s, FALSE); FixWidth (s, z); END; END E1; PROCEDUREE2 (VAR s: State; types: BOOLEAN) RAISES {Error} = VAR n := 0; z := s.n_ops; BEGIN WHILE (s.tok = TK_Not) DO GetToken (s); (* NOT *) EVAL AddOp (s, OP_Not); INC (n); END; E3 (s, types AND (n = 0)); WHILE n > 0 DO FixWidth (s, z); INC (z); DEC (n); END; END E2; CONST RelOps = TKSet {TK_Equal, TK_Sharp, TK_Less, TK_Ls_equal, TK_Greater, TK_Gr_equal, TK_In}; PROCEDUREE3 (VAR s: State; types: BOOLEAN) RAISES {Error} = VAR z := s.n_ops; op: OP; BEGIN E4 (s, types); WHILE (s.tok IN RelOps) DO CASE s.tok OF | TK_Equal => op := OP_EQ; | TK_Sharp => op := OP_NE; | TK_Less => op := OP_LT; | TK_Ls_equal => op := OP_LE; | TK_Greater => op := OP_GT; | TK_Gr_equal => op := OP_GE; | TK_In => op := OP_Member; ELSE <*ASSERT FALSE*> END; GetToken (s); (* operator *) InsertOp (s, z, op); E4 (s, FALSE); FixWidth (s, z); END; END E3; CONST AddOps = TKSet {TK_Plus, TK_Minus, TK_Ampersand}; PROCEDUREE4 (VAR s: State; types: BOOLEAN) RAISES {Error} = VAR z := s.n_ops; op: OP; BEGIN E5 (s, types); WHILE (s.tok IN AddOps) DO CASE s.tok OF | TK_Plus => op := OP_Add; | TK_Minus => op := OP_Subtract; | TK_Ampersand => op := OP_Concat; ELSE <*ASSERT FALSE*> END; GetToken (s); (* operator *) InsertOp (s, z, op); E5 (s, FALSE); FixWidth (s, z); END; END E4; CONST MulOps = TKSet {TK_Asterisk, TK_Slash, TK_Div, TK_Mod}; PROCEDUREE5 (VAR s: State; types: BOOLEAN) RAISES {Error} = VAR z := s.n_ops; op: OP; BEGIN E6 (s, types); WHILE (s.tok IN MulOps) DO CASE s.tok OF | TK_Asterisk => op := OP_Multiply; | TK_Slash => op := OP_Divide; | TK_Div => op := OP_Div; | TK_Mod => op := OP_Mod; ELSE <*ASSERT FALSE*> END; GetToken (s); (* operator *) InsertOp (s, z, op); E6 (s, FALSE); FixWidth (s, z); END; END E5; CONST SelectStart = TKSet {TK_Arrow, TK_Dot, TK_L_bracket, TK_L_paren, TK_L_brace, TK_Branded, TK_Object}; PROCEDUREE6 (VAR s: State; types: BOOLEAN) RAISES {Error} = VAR cnt := 0; z := s.n_ops; BEGIN LOOP IF (s.tok = TK_Plus) THEN GetToken (s); INC (cnt); EVAL AddOp (s, OP_UnaryPlus); ELSIF (s.tok = TK_Minus) THEN GetToken (s); INC (cnt); EVAL AddOp (s, OP_UnaryMinus); ELSE EXIT; END; END; E7 (s, types AND (cnt = 0)); WHILE (cnt > 0) DO FixWidth (s, z); INC (z); DEC (cnt); END; END E6; PROCEDUREE7 (VAR s: State; types: BOOLEAN) RAISES {Error} = VAR z := s.n_ops; BEGIN E8 (s, types); WHILE (s.tok IN SelectStart) DO CASE s.tok OF | TK_Arrow => GetToken (s); (* ^ *) InsertOp (s, z, OP_Deref); FixWidth (s, z); | TK_Dot => GetToken (s); (* . *) InsertOp (s, z, OP_Qualify); FixOpInfo (s, z, OP_Qualify, MatchID (s)); FixWidth (s, z); | TK_L_bracket => GetToken (s); (* [ *) LOOP InsertOp (s, z, OP_Subscript); E0 (s, FALSE); FixWidth (s, z); IF (s.tok # TK_Comma) THEN EXIT END; GetToken (s); (* , *) END; Match (s, TK_R_bracket); | TK_L_paren => GetToken (s); (* ( *) InsertOp (s, z, OP_CallExpr); ArgList (s); Match (s, TK_R_paren); FixWidth (s, z); | TK_L_brace => GetToken (s); (* { *) InsertOp (s, z, OP_ConsExpr); ConsList (s); Match (s, TK_R_brace); FixWidth (s, z); | TK_Branded, TK_Object => IF (types) THEN ObjectTail (s, z); END; EXIT; ELSE Err (s, "unrecognized selector ", TokName (s)); END; END; END E7; PROCEDUREE8 (VAR s: State; types: BOOLEAN) RAISES {Error} = BEGIN CASE s.tok OF | TK_Ident => EVAL AddOp (s, OP_Id, s.scan.id); GetToken (s); | TK_Char_const => EVAL AddOp (s, OP_Char, s.scan.char); GetToken (s); | TK_Card_const => ScanInt (s); | TK_Text_const => ScanText (s); | TK_Real_const => ScanFloat (s); | TK_Longreal_const=> ScanFloat (s); | TK_Extended_const=> ScanFloat (s); | TK_L_paren => GetToken (s); (* ( *) E0 (s, types); Match (s, TK_R_paren); | TK_Array, TK_Bits, TK_Record, TK_Set => Type (s); IF (NOT types) AND (s.tok # TK_L_brace) THEN Err (s, "expected a constructor, found ", TokName (s)); END; | TK_Branded, TK_L_brace, TK_Untraced, TK_Object, TK_Procedure, TK_Ref, TK_L_bracket, TK_CallConv => IF NOT types THEN Err (s, "unexpected type expression") END; Type (s); ELSE Err (s, "unrecognized expression"); EVAL AddOp (s, OP_Int, 0); END; END E8; PROCEDUREArgList (VAR s: State) RAISES {Error} = BEGIN IF (s.tok # TK_R_paren) THEN LOOP Actual (s); IF (s.tok # TK_Comma) THEN EXIT END; GetToken (s); (* , *) END; END; END ArgList; PROCEDUREActual (VAR s: State) RAISES {Error} = VAR z := s.n_ops; BEGIN E0 (s, TRUE); IF (s.tok = TK_Assign) THEN GetToken (s); (* := *) InsertOp (s, z, OP_NameBind); E0 (s, FALSE); FixWidth (s, z); END; END Actual; PROCEDUREConsList (VAR s: State) RAISES {Error} = BEGIN IF (s.tok # TK_R_brace) THEN LOOP IF (s.tok = TK_Dot_dot) THEN (* must be the end of an array constructor *) GetToken (s); (* .. *) EVAL AddOp (s, OP_Etc); EXIT; END; Constructor (s); IF (s.tok # TK_Comma) THEN EXIT END; GetToken (s); (* , *) END; END; END ConsList; PROCEDUREConstructor (VAR s: State) RAISES {Error} = VAR z := s.n_ops; BEGIN E0 (s, FALSE); IF (s.tok = TK_Dot_dot) THEN GetToken (s); (* .. *) InsertOp (s, z, OP_RangeExpr); E0 (s, FALSE); FixWidth (s, z); ELSIF (s.tok = TK_Assign) THEN GetToken (s); (* := *) InsertOp (s, z, OP_NameBind); E0 (s, FALSE); FixWidth (s, z); END; END Constructor;
TYPE DeclAttributes = RECORD gotSome : BOOLEAN; isInline : BOOLEAN; isExternal : BOOLEAN; isUnused : BOOLEAN; isObsolete : BOOLEAN; alias : M3ID.T; callingConv : M3ID.T; END; PROCEDURE------------------------------------------------------- token utilities ---DeclPragmas (VAR s: State; VAR att: DeclAttributes) RAISES {Error} = BEGIN att.gotSome := FALSE; att.isInline := FALSE; att.isExternal := FALSE; att.isUnused := FALSE; att.isObsolete := FALSE; att.alias := M3ID.NoID; att.callingConv := M3ID.NoID; LOOP CASE s.tok OF | TK_External => IF NOT s.ast.interface THEN Err (s, "External declarations only allowed in interfaces"); END; ExternalPragma (s, att.alias, att.callingConv); att.isExternal := TRUE; att.gotSome := TRUE; | TK_Inline => att.isInline := TRUE; GetToken (s); (* INLINE *) Match (s, TK_End_pragma); att.gotSome := TRUE; | TK_Unused => att.isUnused := TRUE; GetToken (s); (* UNUSED *) Match (s, TK_End_pragma); att.gotSome := TRUE; | TK_Obsolete => att.isObsolete := TRUE; GetToken (s); (* OBSOLETE *) Match (s, TK_End_pragma); att.gotSome := TRUE; | TK_CallConv => att.callingConv := s.scan.id; GetToken (s); (* convention name *) Match (s, TK_End_pragma); att.gotSome := TRUE; ELSE EXIT; END; END; END DeclPragmas; PROCEDUREExternalPragma (VAR s: State; VAR(*OUT*) alias: M3ID.T; VAR(*OUT*) cc: M3ID.T) RAISES {Error} = BEGIN alias := M3ID.NoID; (* default => use the Modula-3 name *) cc := M3ID.NoID; Match (s, TK_External); IF (s.tok = TK_Ident) OR (s.tok = TK_Text_const) THEN IF (s.tok = TK_Ident) THEN alias := s.scan.id; ELSE alias := M3ID.Add (s.scan.text); END; GetToken (s); (* Ident or Text_const *) IF (s.tok = TK_Colon) THEN GetToken (s); (* : *) IF (s.tok = TK_Ident) THEN cc := s.scan.id; ELSIF (s.tok = TK_Text_const) THEN cc := M3ID.Add (s.scan.text); ELSE cc := M3ID.NoID; END; IF (cc # M3ID.NoID) THEN IF Target.FindConvention (M3ID.ToText (cc)) = NIL THEN Err (s, "unsupported language or calling convention: ", M3ID.ToText (cc)); END; GetToken (s); (* Ident or Text_const *) ELSE Err (s, "missing language after ':' in <*EXTERNAL*> pragma"); END; END; END; Match (s, TK_End_pragma); END ExternalPragma; PROCEDUREGenAttributes (VAR s: State; READONLY att: DeclAttributes) = VAR z: CARDINAL; BEGIN IF att.gotSome THEN z := AddOp (s, OP_Attributes); IF att.isInline THEN EVAL AddOp (s, OP_Inline); END; IF att.isUnused THEN EVAL AddOp (s, OP_Unused); END; IF att.isObsolete THEN EVAL AddOp (s, OP_Obsolete); END; IF att.isExternal THEN EVAL AddOp (s, OP_External); END; IF att.alias # M3ID.NoID THEN EVAL AddOp (s, OP_Alias, att.alias); END; IF att.callingConv # M3ID.NoID THEN EVAL AddOp (s, OP_CallConv, att.callingConv); END; FixWidth (s, z); END; END GenAttributes; PROCEDUREFatalPragma (VAR s: State; READONLY att: DeclAttributes) RAISES {Error} = VAR any := FALSE; started := FALSE; z: CARDINAL; BEGIN IF (att.gotSome) THEN Err (s, "cannot attach pragma attributes to <*FATAL*> declaration"); END; Match (s, TK_Fatal); LOOP IF (s.tok = TK_Any) THEN GetToken (s); (* ANY *) any := TRUE; ELSIF (s.tok = TK_Ident) THEN IF NOT started THEN z := AddOp (s, OP_Fatal); started := TRUE; END; QID (s); ELSE EXIT; END; IF (s.tok # TK_Comma) THEN EXIT; END; GetToken (s); (* , *) END; IF (started) THEN FixWidth (s, z); END; IF (any) THEN EVAL AddOp (s, OP_FatalAny); END; IF NOT (started OR any) THEN Err (s, "missing exception list or ANY in <*FATAL*> pragma"); END; Match (s, TK_End_pragma); END FatalPragma;
PROCEDURE------------------------------------------------------- Chunk utilities ---ScanText (VAR s: State) RAISES {Error} = VAR index := s.n_texts; BEGIN IF (s.ast.texts = NIL) THEN s.ast.texts := NEW (REF ARRAY OF TEXT, 16); ELSIF (index >= NUMBER (s.ast.texts^)) THEN ExpandTexts (s); END; s.ast.texts [index] := s.scan.text; INC (s.n_texts); EVAL AddOp (s, OP_Text, index); GetToken (s); END ScanText; PROCEDUREExpandTexts (VAR s: State) = VAR n := NUMBER (s.ast.texts^); xx := NEW (REF ARRAY OF TEXT, n+n); BEGIN SUBARRAY (xx^, 0, n) := s.ast.texts^; s.ast.texts := xx; END ExpandTexts; PROCEDUREScanInt (VAR s: State) RAISES {Error} = VAR index := s.n_ints; val: INTEGER; BEGIN IF TInt.ToInt (s.scan.int, val) THEN EVAL AddOp (s, OP_Int, val); ELSE IF (s.ast.ints = NIL) THEN s.ast.ints := NEW (REF ARRAY OF Target.Int, 16); ELSIF (index >= NUMBER (s.ast.ints^)) THEN ExpandInts (s); END; s.ast.ints [index] := s.scan.int; INC (s.n_ints); EVAL AddOp (s, OP_BigInt, index); END; GetToken (s); END ScanInt; PROCEDUREExpandInts (VAR s: State) = VAR n := NUMBER (s.ast.ints^); xx := NEW (REF ARRAY OF Target.Int, n+n); BEGIN SUBARRAY (xx^, 0, n) := s.ast.ints^; s.ast.ints := xx; END ExpandInts; PROCEDUREScanFloat (VAR s: State) RAISES {Error} = VAR index := s.n_floats; op: OP; BEGIN IF (s.tok = TK_Real_const) THEN op := OP_Real; ELSIF (s.tok = TK_Longreal_const) THEN op := OP_LReal; ELSIF (s.tok = TK_Extended_const) THEN op := OP_EReal; ELSE <*ASSERT FALSE*> END; IF (s.ast.floats = NIL) THEN s.ast.floats := NEW (REF ARRAY OF Target.Float, 16); ELSIF (s.n_floats >= NUMBER (s.ast.floats^)) THEN ExpandFloats (s); END; s.ast.floats [index] := s.scan.float; INC (s.n_floats); EVAL AddOp (s, op, index); GetToken (s); END ScanFloat; PROCEDUREExpandFloats (VAR s: State) = VAR n := NUMBER (s.ast.floats^); xx := NEW (REF ARRAY OF Target.Float, n+n); BEGIN SUBARRAY (xx^, 0, n) := s.ast.floats^; s.ast.floats := xx; END ExpandFloats; PROCEDUREQID (VAR s: State) RAISES {Error} = VAR id1, id2: M3ID.T; z: CARDINAL; BEGIN id1 := MatchID (s); IF (s.tok = TK_Dot) THEN GetToken (s); (* . *) id2 := MatchID (s); z := AddOp (s, OP_Qualify, id2); EVAL AddOp (s, OP_Id, id1); FixWidth (s, z); ELSE EVAL AddOp (s, OP_Id, id1); END; END QID; PROCEDUREMatchID (VAR s: State): M3ID.T RAISES {Error} = VAR id: M3ID.T; BEGIN IF (s.tok # TK_Ident) THEN Err (s, "expected an identifier, but found ", TokName (s)); END; id := s.scan.id; GetToken (s); RETURN id; END MatchID; PROCEDUREMatch (VAR s: State; tk: TK) RAISES {Error} = BEGIN IF (s.tok # tk) THEN Err (s, "expected ", s.scan.className (tk), ", but found ", TokName (s)); END; GetToken (s); END Match; PROCEDUREGetToken (VAR s: State) RAISES {Error} = BEGIN REPEAT s.scan.next (); s.tok := s.scan.token; UNTIL (s.tok # TK_Comment); IF (s.tok = TK_Error) THEN Err (s, "unrecognized input token: ", TokName (s)); END; END GetToken; PROCEDURETokName (VAR s: State): TEXT = VAR txt := s.scan.toText (); BEGIN <*ASSERT txt # NIL*> IF Text.Length (txt) > 27 THEN txt := Text.Sub (txt, 0, 24) & "..."; END; RETURN txt; END TokName; PROCEDUREErr (VAR s: State; a, b, c, d: TEXT := NIL) RAISES {Error} = VAR msg := ""; BEGIN IF (a # NIL) THEN msg := msg & a; END; IF (b # NIL) THEN msg := msg & b; END; IF (c # NIL) THEN msg := msg & c; END; IF (d # NIL) THEN msg := msg & d; END; IF s.err (msg, s.scan) THEN RAISE Error; END; END Err;
PROCEDUREAddOp (VAR s: State; op: OP; info := 0): CARDINAL = BEGIN IF (s.cur > LAST (s.tail.nodes)) THEN s.tail.next := NEW (Chunk); s.tail := s.tail.next; INC (s.base, NUMBER (s.tail.nodes)); s.cur := 0; END; WITH n = s.tail.nodes[s.cur] DO n.op := op; n.info := info; n.width := 1; (* self *) END; INC (s.n_ops); INC (s.cur); RETURN s.n_ops - 1; END AddOp; PROCEDUREFixOp (VAR s: State; n: CARDINAL; op: OP) = VAR c := s.tail; BEGIN IF (n >= s.base) THEN DEC (n, s.base); ELSE c := s.head; WHILE (n > LAST (c.nodes)) DO c := c.next; DEC (n, NUMBER (c.nodes)); END; END; c.nodes[n].op := op; END FixOp; PROCEDUREFixOpInfo (VAR s: State; n: CARDINAL; op: OP; info: INTEGER) = VAR c := s.tail; BEGIN IF (n >= s.base) THEN DEC (n, s.base); ELSE c := s.head; WHILE (n > LAST (c.nodes)) DO c := c.next; DEC (n, NUMBER (c.nodes)); END; END; WITH z = c.nodes[n] DO z.op := op; z.info := info; END; END FixOpInfo; PROCEDUREFixWidth (VAR s: State; n: CARDINAL) = VAR c := s.tail; width := s.n_ops - n; BEGIN IF (n >= s.base) THEN DEC (n, s.base); ELSE c := s.head; WHILE (n > LAST (c.nodes)) DO c := c.next; DEC (n, NUMBER (c.nodes)); END; END; <*ASSERT width > 0 *> c.nodes[n].width := width; END FixWidth; PROCEDUREInsertOp (VAR s: State; n: CARDINAL; op: OP) = BEGIN EVAL AddOp (s, OP_Empty); (* make room for a new node *) OpenGap (s, n); FixOp (s, n, op); END InsertOp; PROCEDUREOpenGap (VAR s: State; n: CARDINAL) = VAR c := s.tail; cnt := s.n_ops - n - 1; tmp, tmp2: Node; BEGIN (* find the 'n'th node *) IF (n >= s.base) THEN DEC (n, s.base); ELSE c := s.head; WHILE (n > LAST (c.nodes)) DO c := c.next; DEC (n, NUMBER (c.nodes)); END; END; tmp.op := OP_Empty; tmp.width := 1; tmp.info := 0; tmp.client := 0; WHILE (cnt > 0) DO WITH z = c.nodes[n] DO tmp2 := z; z := tmp; tmp := tmp2; END; DEC (cnt); INC (n); IF (n >= NUMBER (c.nodes)) THEN c := c.next; n := 0; END; END; c.nodes[n] := tmp; END OpenGap; PROCEDUREFlattenChunks (VAR s: State): REF ARRAY OF Node = (* build the final, flat list of nodes *) VAR nn := NEW (REF ARRAY OF Node, s.base + s.cur); c := s.head; x := 0; BEGIN WHILE c # s.tail DO SUBARRAY (nn^, x, NUMBER (c.nodes)) := c.nodes; c := c.next; INC (x, NUMBER (c.nodes)); END; IF (s.cur > 0) THEN SUBARRAY (nn^, x, s.cur) := SUBARRAY (c.nodes, 0, s.cur); END; RETURN nn; END FlattenChunks; BEGIN END M3Parse.