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 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;
PROCEDURE ConstDecl (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;
PROCEDURE TypeDecl (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;
PROCEDURE VarDecl (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;
PROCEDURE ProcDecl (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;
PROCEDURE ProcBody (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;
PROCEDURE Reveal (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;
PROCEDURE ExceptDecl (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;
------------------------------------------------------------ statements ---
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 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;
PROCEDURE EmptyStmts (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;
PROCEDURE AssignStmt (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;
PROCEDURE AssertStmt (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;
PROCEDURE CaseStmt (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;
PROCEDURE CaseBranch (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;
PROCEDURE ExitStmt (VAR s: State) RAISES {Error} =
BEGIN
Match (s, TK_Exit);
EVAL AddOp (s, OP_Exit);
END ExitStmt;
PROCEDURE EvalStmt (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;
PROCEDURE ForStmt (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;
PROCEDURE IfStmt (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;
PROCEDURE LockStmt (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;
PROCEDURE LoopStmt (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;
PROCEDURE RaiseStmt (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;
PROCEDURE RepeatStmt (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;
PROCEDURE ReturnStmt (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;
PROCEDURE TryStmt (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;
PROCEDURE TryHandler (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;
PROCEDURE TypeCaseStmt (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;
PROCEDURE TypeCaseArm (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;
PROCEDURE WhileStmt (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;
PROCEDURE WithStmt (VAR s: State) RAISES {Error} =
BEGIN
Match (s, TK_With);
WithTail (s);
END WithStmt;
PROCEDURE WithTail (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;
----------------------------------------------------------------- types ---
*** 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 Expr (VAR s: State) RAISES {Error} =
BEGIN
E0 (s, FALSE);
END Expr;
PROCEDURE E0 (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;
PROCEDURE E1 (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;
PROCEDURE E2 (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};
PROCEDURE E3 (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};
PROCEDURE E4 (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};
PROCEDURE E5 (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};
PROCEDURE E6 (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;
PROCEDURE E7 (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;
PROCEDURE E8 (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;
PROCEDURE ArgList (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;
PROCEDURE Actual (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;
PROCEDURE ConsList (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;
PROCEDURE Constructor (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;
--------------------------------------------------------------- pragmas ---
TYPE
DeclAttributes = RECORD
gotSome : BOOLEAN;
isInline : BOOLEAN;
isExternal : BOOLEAN;
isUnused : BOOLEAN;
isObsolete : BOOLEAN;
alias : M3ID.T;
callingConv : M3ID.T;
END;
PROCEDURE 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;
PROCEDURE ExternalPragma (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;
PROCEDURE GenAttributes (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;
PROCEDURE FatalPragma (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;
------------------------------------------------------- token utilities ---
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.