(* ppg.mod master source file of the ebnf parser generator. Copyright (C) 2003-2023 Free Software Foundation, Inc. Contributed by Gaius Mulley . This file is part of GNU Modula-2. GNU Modula-2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Modula-2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Modula-2; see the file COPYING3. If not see . *) MODULE ppg ; FROM PushBackInput IMPORT WarnError, WarnString, GetColumnPosition, GetCurrentLine ; FROM bnflex IMPORT IsSym, SymIs, TokenType, GetCurrentToken, GetCurrentTokenType, GetChar, PutChar, SkipWhite, SkipUntilEoln, AdvanceToken, IsReserved, OpenSource, CloseSource, PushBackToken, SetDebugging ; FROM StrLib IMPORT StrCopy, StrEqual, StrLen, StrConCat ; FROM Storage IMPORT ALLOCATE, DEALLOCATE ; FROM NameKey IMPORT Name, MakeKey, WriteKey, LengthKey, GetKey, KeyToCharStar, NulName ; FROM NumberIO IMPORT CardToStr, WriteCard ; FROM SymbolKey IMPORT InitTree, SymbolTree, PutSymKey, GetSymKey, ForeachNodeDo, ContainsSymKey, NulKey ; FROM Lists IMPORT InitList, IsItemInList, IncludeItemIntoList, RemoveItemFromList, KillList, List ; FROM DynamicStrings IMPORT String, InitString, KillString, ConCat, Mark, ConCatChar, InitStringCharStar, char, Length ; FROM ASCII IMPORT nul, lf, tab ; FROM StrIO IMPORT WriteString, WriteLn ; FROM StdIO IMPORT Write ; FROM Debug IMPORT Halt ; FROM Args IMPORT GetArg, Narg ; FROM SYSTEM IMPORT WORD ; FROM libc IMPORT exit ; IMPORT Output ; CONST MaxCodeHunkLength = 8192 ; MaxFileName = 8192 ; MaxString = 8192 ; DefaultRecovery = TRUE ; (* default is to generate a parser which will recover from errors. *) MaxElementsInSet = 32 ; (* formatting constants *) BaseRightLimit = 75 ; BaseRightMargin = 50 ; BaseNewLine = 3 ; TYPE ElementType = (idel, tokel, litel) ; m2condition = (m2none, m2if, m2elsif, m2while) ; TraverseResult = (unknown, true, false) ; IdentDesc = POINTER TO RECORD definition: ProductionDesc ; (* where this idents production is defined *) name : Name ; line : CARDINAL ; END ; SetDesc = POINTER TO RECORD next : SetDesc ; CASE type: ElementType OF idel : ident : IdentDesc | tokel, litel : string: Name END END ; (* note that epsilon refers to whether we can satisfy this component part of a sentance without consuming a token. Reachend indicates we can get to the end of the sentance without consuming a token. For expression, statement, productions, terms: the epsilon value should equal the reachend value but for factors the two may differ. *) FollowDesc = POINTER TO RECORD calcfollow : BOOLEAN ; (* have we solved the follow set yet? *) follow : SetDesc ; (* the follow set *) reachend : TraverseResult ; (* can we see the end of the sentance (due to multiple epsilons) *) epsilon : TraverseResult ; (* potentially no token may be consumed within this component of the sentance *) line : CARDINAL ; END ; TermDesc = POINTER TO termdesc ; ExpressionDesc = POINTER TO RECORD term : TermDesc ; followinfo: FollowDesc ; line : CARDINAL ; END ; StatementDesc = POINTER TO RECORD ident : IdentDesc ; expr : ExpressionDesc ; followinfo : FollowDesc ; line : CARDINAL ; END ; CodeHunk = POINTER TO RECORD codetext : ARRAY [0..MaxCodeHunkLength] OF CHAR ; next : CodeHunk ; END ; CodeDesc = POINTER TO RECORD code : CodeHunk ; indent : CARDINAL ; (* column of the first % *) line : CARDINAL ; END ; FactorType = (id, lit, sub, opt, mult, m2) ; FactorDesc = POINTER TO RECORD followinfo: FollowDesc ; next : FactorDesc ; (* chain of successive factors *) line : CARDINAL ; pushed : FactorDesc ; (* chain of pushed code factors *) CASE type: FactorType OF id : ident : IdentDesc | lit : string: Name | sub, opt, mult: expr : ExpressionDesc | m2 : code : CodeDesc ; END END ; termdesc = RECORD factor : FactorDesc ; next : TermDesc ; (* chain of alternative terms *) followinfo: FollowDesc ; line : CARDINAL ; END ; ProductionDesc = POINTER TO RECORD next : ProductionDesc ; (* the chain of productions *) statement : StatementDesc ; first : SetDesc ; (* the first set *) firstsolved : BOOLEAN ; followinfo : FollowDesc ; line : CARDINAL ; description : Name ; END ; DoProcedure = PROCEDURE (ProductionDesc) ; VAR LastLineNo : CARDINAL ; Finished, SuppressFileLineTag, KeywordFormatting, PrettyPrint, EmitCode, Texinfo, Sphinx, FreeDocLicense, Debugging, WasNoError : BOOLEAN ; LinePrologue, LineEpilogue, LineDeclaration : CARDINAL ; CodePrologue, CodeEpilogue, CodeDeclaration : CodeHunk ; CurrentProduction, TailProduction, HeadProduction : ProductionDesc ; CurrentExpression : ExpressionDesc ; CurrentTerm : TermDesc ; CurrentFactor : FactorDesc ; CurrentIdent : IdentDesc ; CurrentStatement : StatementDesc ; CurrentSetDesc : SetDesc ; ReverseValues, Values, (* tree of tokens and their ORD value *) ReverseAliases, Aliases : SymbolTree ; ModuleName : Name ; LastLiteral : Name ; LastIdent : Name ; SymIsProc, (* the name of the SymIs function tests and consumes token *) TokenTypeProc, (* the name of the function which yields the current token type *) ErrorProcArray, ErrorProcString : Name ; (* the name of the error procedures *) ArgName, FileName : ARRAY [0..MaxFileName] OF CHAR ; OnLineStart, BeginningOfLine : BOOLEAN ; Indent : CARDINAL ; EmittedVar : BOOLEAN ; (* have we written VAR yet? *) ErrorRecovery : BOOLEAN ; (* do we want to recover from parsing errors? *) LargestValue : CARDINAL ; (* the number of tokens we are using. *) InitialElement : BOOLEAN ; (* used to determine whether we are writing *) (* the first element of a case statement. *) ParametersUsed : BITSET ; (* which parameters have been used? *) (* % declaration *) (* AddEntry - adds an entry into, t, containing [def:value]. *) PROCEDURE AddEntry (VAR t: SymbolTree; def, value: Name) ; BEGIN IF ContainsSymKey(t, def) THEN WarnError1("already seen a definition for token '%s'", def) ELSE PutSymKey(t, def, value) END END AddEntry ; (* Format1 - converts string, src, into, dest, together with encapsulated entity, n. It only formats the first %s or %d with n. *) PROCEDURE Format1 (src: ARRAY OF CHAR; n: WORD; VAR dest: ARRAY OF CHAR) ; VAR HighSrc, HighDest, i, j : CARDINAL ; str : ARRAY [0..MaxString] OF CHAR ; BEGIN HighSrc := StrLen(src) ; HighDest := HIGH(dest) ; i := 0 ; j := 0 ; WHILE (i0 DO Output.Write(' ') ; DEC(n) END ; OnLineStart := FALSE END WriteIndent ; (* CheckWrite - *) PROCEDURE CheckWrite (ch: CHAR; VAR curpos: CARDINAL; left: CARDINAL; VAR seentext: BOOLEAN) ; BEGIN IF ch=lf THEN NewLine(left) ; curpos := 0 ; seentext := FALSE ELSE Output.Write(ch) ; INC(curpos) END END CheckWrite ; (* WriteStringIndent - writes a string but it will try and remove upto indent spaces if they exist. *) PROCEDURE WriteStringIndent (a: ARRAY OF CHAR; indent: CARDINAL; VAR curpos: CARDINAL; left: CARDINAL; VAR seentext: BOOLEAN) ; VAR l, i: CARDINAL ; BEGIN i := 0 ; l := StrLen(a) ; WHILE i=indent THEN WriteIndent(curpos-indent) END ; seentext := TRUE ; CheckWrite(a[i], curpos, left, seentext) END END ; INC(i) END END WriteStringIndent ; (* WriteCodeHunkListIndent - writes the CodeHunk list in the correct order but it removes up to indent spaces if they exist. *) PROCEDURE WriteCodeHunkListIndent (l: CodeHunk; indent: CARDINAL; VAR curpos: CARDINAL; left: CARDINAL; VAR seentext: BOOLEAN) ; BEGIN IF l#NIL THEN (* recursion *) WITH l^ DO WriteCodeHunkListIndent(next, indent, curpos, left, seentext) ; WriteStringIndent(codetext, indent, curpos, left, seentext) END END END WriteCodeHunkListIndent ; (* Add - adds a character to a code hunk and creates another code hunk if necessary. *) PROCEDURE Add (VAR p: CodeHunk; ch: CHAR; VAR i: CARDINAL) : CodeHunk ; VAR q: CodeHunk ; BEGIN IF (p=NIL) OR (i>MaxCodeHunkLength) THEN NEW(q) ; q^.next := p ; q^.codetext[0] := ch ; i := 1 ; RETURN( q ) ELSE p^.codetext[i] := ch ; INC(i) ; RETURN( p ) END END Add ; (* ConsHunk - combine two possible code hunks. *) PROCEDURE ConsHunk (VAR p: CodeHunk; q: CodeHunk) ; VAR r: CodeHunk ; BEGIN IF p#NIL THEN r := q ; WHILE r^.next#NIL DO r := r^.next END ; r^.next := p ; END ; p := q END ConsHunk ; (* GetName - returns the next symbol which is checked for a legal name. *) PROCEDURE GetName () : Name ; VAR name: Name ; BEGIN IF IsReserved(GetCurrentToken()) THEN WarnError('expecting a name and found a reserved word') ; AdvanceToken ; (* move on to another token *) RETURN( NulName ) ELSE name := GetCurrentToken() ; AdvanceToken ; RETURN( name ) END END GetName ; (* % rules *) (* Note that all the code from here down to the end of the module as delimited by the comment will all be hidden when the buildpg script is invoked. Also be careful not to duplicate or remove these critical comments below.. Check buildpg for sed details. *) (* StartNonErrorChecking *) (* actually these two are not strictly rules but hand built primitives *) (* Ident - non error checking varient of Ident *) PROCEDURE Ident () : BOOLEAN ; BEGIN IF GetCurrentTokenType()=identtok THEN NEW(CurrentIdent) ; WITH CurrentIdent^ DO definition := NIL ; name := GetName() ; line := GetCurrentLine() END ; RETURN( TRUE ) ELSE RETURN( FALSE ) END END Ident ; (* Modula2Code - non error checking varient of Modula2Code *) PROCEDURE Modula2Code () : BOOLEAN ; VAR p : CodeHunk ; i : CARDINAL ; quote : BOOLEAN ; line, position: CARDINAL ; BEGIN line := GetCurrentLine() ; PushBackToken(GetCurrentToken()) ; position := GetColumnPosition() ; p := NIL ; SkipWhite ; WHILE (PutChar(GetChar())#'%') AND (PutChar(GetChar())#nul) DO IF PutChar(GetChar())='"' THEN REPEAT p := Add(p, GetChar(), i) UNTIL (PutChar(GetChar())='"') OR (PutChar(GetChar())=nul) ; p := Add(p, '"', i) ; IF (PutChar(GetChar())='"') AND (GetChar()='"') THEN END ELSIF PutChar(GetChar())="'" THEN REPEAT p := Add(p, GetChar(), i) UNTIL (PutChar(GetChar())="'") OR (PutChar(GetChar())=nul) ; p := Add(p, "'", i) ; IF (PutChar(GetChar())="'") AND (GetChar()="'") THEN END ELSIF (PutChar(GetChar())='\') AND (GetChar()='\') THEN p := Add(p, GetChar(), i) ELSIF PutChar(GetChar())#'%' THEN p := Add(p, GetChar(), i) END END ; p := Add(p, nul, i) ; WITH CurrentFactor^ DO type := m2 ; code := NewCodeDesc() ; WITH code^ DO code := p ; indent := position END END ; IF PutChar(' ')=' ' THEN END ; AdvanceToken ; (* read the next token ready for the parser *) IF NOT WasNoError THEN WarnError1('error probably occurred before the start of inline code on line %d', line) END ; RETURN( TRUE ) END Modula2Code ; (* StartModName := % ModuleName := GetName() ; (* ignore begintok *) CodeFragmentPrologue % =: *) PROCEDURE StartModName () : BOOLEAN ; BEGIN ModuleName := GetName() ; CodeFragmentPrologue ; RETURN( TRUE ) END StartModName ; (* EndModName := *) PROCEDURE EndModName () : BOOLEAN ; BEGIN IF ModuleName#GetName() THEN WarnError('expecting same module name at end as beginning') END ; (* ignore endtok as it consumes the token afterwards *) CodeFragmentEpilogue ; RETURN( TRUE ) END EndModName ; (* DoDeclaration := % CodeFragmentDeclaration % =: *) PROCEDURE DoDeclaration () : BOOLEAN ; BEGIN IF ModuleName#GetName() THEN WarnError('expecting same module name in declaration as in the beginning') END ; (* ignore begintok as it consumes the token afterwards *) CodeFragmentDeclaration ; RETURN( TRUE ) END DoDeclaration ; (* EndNonErrorChecking now for the real ebnf rules *) TYPE SetOfStop = SET OF TokenType ; (* ************************************************************************** E r r o r R e c o v e r y I d e n t & M o d u l a 2 C o d e ************************************************************************** (* StartErrorChecking *) (* SyntaxError - after a syntax error we skip all tokens up until we reach a stop symbol. *) PROCEDURE SyntaxError (stop: SetOfStop) ; BEGIN DescribeError ; IF Debugging THEN WriteLn ; WriteString('skipping token *** ') END ; WHILE NOT (GetCurrentTokenType() IN stop) DO AdvanceToken END ; IF Debugging THEN WriteString(' ***') ; WriteLn END ; WasNoError := FALSE END SyntaxError ; (* SyntaxCheck - *) PROCEDURE SyntaxCheck (stop: SetOfStop) ; BEGIN IF NOT (GetCurrentTokenType() IN stop) THEN SyntaxError(stop) END END SyntaxCheck ; (* Expect - *) PROCEDURE Expect (t: TokenType; stop: SetOfStop) ; BEGIN IF GetCurrentTokenType()=t THEN AdvanceToken ELSE SyntaxError(stop) END ; SyntaxCheck(stop) END Expect ; (* Ident - error checking varient of Ident *) PROCEDURE Ident (stop: SetOfStop) ; BEGIN IF GetCurrentTokenType()=identtok THEN NEW(CurrentIdent) ; WITH CurrentIdent^ DO definition := NIL ; name := GetName() ; line := GetCurrentLine() END ; END END Ident ; (* Modula2Code - error checking varient of Modula2Code *) PROCEDURE Modula2Code (stop: SetOfStop) ; VAR p : CodeHunk ; i : CARDINAL ; quote : BOOLEAN ; line, position: CARDINAL ; BEGIN line := GetCurrentLine() ; PushBackToken(GetCurrentToken()) ; position := GetColumnPosition() ; p := NIL ; SkipWhite ; WHILE (PutChar(GetChar())#'%') AND (PutChar(GetChar())#nul) DO IF PutChar(GetChar())='"' THEN REPEAT p := Add(p, GetChar(), i) UNTIL (PutChar(GetChar())='"') OR (PutChar(GetChar())=nul) ; p := Add(p, '"', i) ; IF (PutChar(GetChar())='"') AND (GetChar()='"') THEN END ELSIF PutChar(GetChar())="'" THEN REPEAT p := Add(p, GetChar(), i) UNTIL (PutChar(GetChar())="'") OR (PutChar(GetChar())=nul) ; p := Add(p, "'", i) ; IF (PutChar(GetChar())="'") AND (GetChar()="'") THEN END ELSIF (PutChar(GetChar())='\') AND (GetChar()='\') THEN p := Add(p, GetChar(), i) ELSIF PutChar(GetChar())#'%' THEN p := Add(p, GetChar(), i) END END ; p := Add(p, nul, i) ; WITH CurrentFactor^ DO type := m2 ; code := NewCodeDesc() ; WITH code^ DO code := p ; indent := position END END ; IF PutChar(' ')=' ' THEN END ; AdvanceToken ; (* read the next token ready for the parser *) IF NOT WasNoError THEN WarnError1('error probably occurred before the start of inline code on line %d', line) END END Modula2Code ; (* StartModName := % ModuleName := GetName() ; (* ignore begintok *) CodeFragmentPrologue % =: *) PROCEDURE StartModName (stop: SetOfStop) ; BEGIN ModuleName := GetName() ; CodeFragmentPrologue END StartModName ; (* EndModName := *) PROCEDURE EndModName (stop: SetOfStop) ; BEGIN IF ModuleName#GetName() THEN WarnError('expecting same module name at end as beginning') END ; (* ignore endtok as it consumes the token afterwards *) CodeFragmentEpilogue END EndModName ; (* DoDeclaration := % CodeFragmentDeclaration % =: *) PROCEDURE DoDeclaration (stop: SetOfStop) ; BEGIN IF ModuleName#GetName() THEN WarnError('expecting same module name in declaration as in the beginning') END ; (* ignore begintok as it consumes the token afterwards *) CodeFragmentDeclaration END DoDeclaration ; (* EndErrorChecking now for the real ebnf rules *) ***************************************************************** l e a v e a b o v e c o d e a l o n e (f o r S E D) ***************************************************************** *) (* this code below will be recreated by ppg *) PROCEDURE DescribeError ; BEGIN WarnError('syntax error') END DescribeError ; PROCEDURE Main () : BOOLEAN ; BEGIN IF Header() THEN IF Decls() THEN IF Footer() THEN IF Rules() THEN RETURN( TRUE ) END END END END ; RETURN( FALSE ) END Main ; PROCEDURE Header () : BOOLEAN ; BEGIN IF SymIs(codetok) THEN IF SymIs(moduletok) THEN ModuleName := GetName() ; (* ignore the begintok as we are looking one symbol ahead and we dont want to move over MODULE *) CodeFragmentPrologue ; RETURN( TRUE ) ELSE WarnError('expecting module') END END ; RETURN( FALSE ) END Header ; PROCEDURE Footer () : BOOLEAN ; BEGIN IF SymIs(codetok) THEN IF SymIs(moduletok) THEN IF ModuleName#GetName() THEN WarnError('expecting same module name at end as beginning') END ; (* ignore endtok as it consumes the token afterwards *) CodeFragmentEpilogue ; RETURN( TRUE ) ELSE WarnError('expecting module') END END ; RETURN( FALSE ) END Footer ; PROCEDURE Decls () : BOOLEAN ; BEGIN IF SymIs(codetok) THEN IF SymIs(declarationtok) THEN RETURN( DoDeclaration() ) ELSE WarnError('expecting declaration') END END ; RETURN( FALSE ) END Decls ; (* Rules := " % " " rules " { Defs } ExtBNF =: *) PROCEDURE Rules () : BOOLEAN ; BEGIN IF SymIs(codetok) THEN IF SymIs(rulestok) THEN WHILE Defs() DO END ; IF ExtBNF() THEN RETURN( TRUE ) ELSE WarnError('expecting some BNF rules to be present') END END END ; RETURN( FALSE ) END Rules ; (* Defs := " special " Special | " token " Token | " error " ErrorProcedures | "tokenfunc" TokenProcedure =: *) PROCEDURE Defs () : BOOLEAN ; BEGIN IF SymIs(specialtok) THEN IF Special() THEN RETURN( TRUE ) END ELSIF SymIs(tokentok) THEN IF Token() THEN RETURN( TRUE ) END ELSIF SymIs(errortok) THEN IF ErrorProcedures() THEN RETURN( TRUE ) END ELSIF SymIs(tfunctok) THEN IF TokenProcedure() THEN RETURN( TRUE ) END ELSIF SymIs(symfunctok) THEN IF SymProcedure() THEN RETURN( TRUE ) END END ; RETURN( FALSE ) END Defs ; (* Special := Name First Follow [ "epsilon" ] =: *) PROCEDURE Special () : BOOLEAN ; VAR p: ProductionDesc ; BEGIN IF Ident() THEN p := NewProduction() ; p^.statement := NewStatement() ; p^.statement^.followinfo^.calcfollow := TRUE ; p^.statement^.followinfo^.epsilon := false ; p^.statement^.followinfo^.reachend := false ; p^.statement^.ident := CurrentIdent ; p^.statement^.expr := NIL ; p^.firstsolved := TRUE ; p^.followinfo^.calcfollow := TRUE ; p^.followinfo^.epsilon := false ; p^.followinfo^.reachend := false ; IF First() THEN IF Follow() THEN IF SymIs(epsilontok) THEN p^.statement^.followinfo^.epsilon := true ; (* these are not used - but they are displayed when debugging *) p^.statement^.followinfo^.reachend := true ; p^.followinfo^.epsilon := true ; p^.followinfo^.reachend := true END ; IF Literal() THEN p^.description := LastLiteral END ; RETURN( TRUE ) ELSE WarnError('Follow - expected') ; RETURN( FALSE ) END ; ELSE WarnError('First - expected') ; RETURN( FALSE ) END ELSE RETURN( FALSE ) END END Special ; (* First := 'first' '{' { LitOrTokenOrIdent % WITH LastSetDesc^ DO next := HeadProduction^.first ; END ; TailProduction^.first := LastSetDesc ; % } '}' *) PROCEDURE First () : BOOLEAN ; BEGIN IF SymIs(firsttok) THEN IF SymIs(lcparatok) THEN WHILE LitOrTokenOrIdent() DO WITH CurrentSetDesc^ DO next := TailProduction^.first ; END ; TailProduction^.first := CurrentSetDesc END ; (* while *) IF SymIs(rcparatok) THEN RETURN( TRUE ) ELSE WarnError("'}' - expected") ; RETURN( FALSE ) END ; ELSE WarnError("'{' - expected") ; RETURN( FALSE ) END ; ELSE RETURN( FALSE ) END ; END First ; (* Follow := 'follow' '{' { LitOrTokenOrIdent } '}' *) PROCEDURE Follow () : BOOLEAN ; BEGIN IF SymIs(followtok) THEN IF SymIs(lcparatok) THEN WHILE LitOrTokenOrIdent() DO WITH CurrentSetDesc^ DO next := TailProduction^.followinfo^.follow ; END ; TailProduction^.followinfo^.follow := CurrentSetDesc END ; (* while *) IF SymIs(rcparatok) THEN RETURN( TRUE ) ELSE WarnError("'}' - expected") ; RETURN( FALSE ) END ; ELSE WarnError("'{' - expected") ; RETURN( FALSE ) END ; ELSE RETURN( FALSE ) END ; END Follow ; (* LitOrTokenOrIdent := Literal % CurrentSetDesc := NewSetDesc() ; WITH CurrentSetDesc^ DO type := litel ; string := LastLiteral ; END ; % | '<' % CurrentSetDesc := NewSetDesc() ; WITH CurrentSetDesc^ DO type := tokel ; string := GetCurrentToken() ; END ; AdvanceToken() ; % '>' | Ident % CurrentSetDesc := NewSetDesc() ; WITH CurrentSetDesc^ DO type := idel ; ident := CurrentIdent ; END ; % *) PROCEDURE LitOrTokenOrIdent () : BOOLEAN ; BEGIN IF Literal() THEN CurrentSetDesc := NewSetDesc() ; WITH CurrentSetDesc^ DO type := litel ; string := LastLiteral END ; RETURN( TRUE ) ELSIF SymIs(lesstok) THEN CurrentSetDesc := NewSetDesc() ; WITH CurrentSetDesc^ DO type := tokel ; string := GetCurrentToken() ; END ; IF GetSymKey(Aliases, GetCurrentToken())=NulKey THEN (* PutSymKey(Values, GetCurrentToken(), LargestValue) ; PutSymKey(Aliases, GetCurrentToken(), GetCurrentToken()) ; PutSymKey(ReverseAliases, GetCurrentToken(), GetCurrentToken()) ; INC(LargestValue) ; *) END ; AdvanceToken() ; IF SymIs(gretok) THEN RETURN( TRUE ) ELSE WarnError("'>' - expected") ; RETURN( FALSE ) END ; ELSIF Ident() THEN CurrentSetDesc := NewSetDesc() ; WITH CurrentSetDesc^ DO type := idel ; ident := CurrentIdent ; END ; RETURN( TRUE ) ELSE RETURN( FALSE ) END ; (* elsif *) END LitOrTokenOrIdent ; (* Literal - *) PROCEDURE Literal () : BOOLEAN ; BEGIN IF SymIs(squotetok) THEN LastLiteral := GetCurrentToken() ; AdvanceToken ; IF SymIs(squotetok) THEN RETURN( TRUE ) END ELSIF SymIs(dquotetok) THEN LastLiteral := GetCurrentToken() ; AdvanceToken ; IF SymIs(dquotetok) THEN RETURN( TRUE ) END END ; RETURN( FALSE ) END Literal ; (* Token := Literal % VAR l: CARDINAL ; l := GetCurrentToken() ; % Name % PutSymKey(Aliases, l, GetCurrentToken()) ; % =: *) PROCEDURE Token () : BOOLEAN ; BEGIN IF Literal() THEN AddEntry(Aliases, LastLiteral, GetCurrentToken()) ; AddEntry(ReverseAliases, GetCurrentToken(), LastLiteral) ; AddEntry(Values, GetCurrentToken(), LargestValue) ; AddEntry(ReverseValues, Name(LargestValue), GetCurrentToken()) ; INC(LargestValue) ; AdvanceToken ; RETURN( TRUE ) ELSE RETURN( FALSE ) END END Token ; (* ErrorProcedures := Literal % ErrorProcArray := LastLiteral % Literal % ErrorProcString := LastLiteral % =: *) PROCEDURE ErrorProcedures () : BOOLEAN ; BEGIN IF Literal() THEN ErrorProcArray := LastLiteral ; IF Literal() THEN ErrorProcString := LastLiteral ; RETURN( TRUE ) END END ; RETURN( FALSE ) END ErrorProcedures ; (* TokenProcedure := Literal % TokenTypeProc := LastLiteral % =: *) PROCEDURE TokenProcedure () : BOOLEAN ; BEGIN IF Literal() THEN TokenTypeProc := LastLiteral ; RETURN( TRUE ) ELSE RETURN( FALSE ) END END TokenProcedure ; (* SymProcedure := Literal % SymIsProc := LastLiteral % =: *) PROCEDURE SymProcedure () : BOOLEAN ; BEGIN IF Literal() THEN SymIsProc := LastLiteral ; RETURN( TRUE ) ELSE RETURN( FALSE ) END END SymProcedure ; (* ExtBNF := " BNF " { Production } " FNB " =: *) PROCEDURE ExtBNF () : BOOLEAN ; BEGIN IF SymIs(BNFtok) THEN WHILE Production() DO END ; IF SymIs(FNBtok) THEN RETURN( TRUE ) END END ; RETURN( FALSE ) END ExtBNF ; (* Production := Statement =: *) PROCEDURE Production () : BOOLEAN ; BEGIN IF Statement() THEN RETURN( TRUE ) END ; RETURN( FALSE ) END Production ; (* Statement := % VAR i: IdentDesc ; % Ident % i := CurrentIdent ; % " := " % VAR e: ExpressionDesc ; e := NewExpression() ; % Expression % WITH CurrentStatement^ DO ident := i ; expr := e ; first := NIL ; END ; % " =: " =: *) PROCEDURE Statement () : BOOLEAN ; VAR i: IdentDesc ; s: StatementDesc ; e: ExpressionDesc ; p: ProductionDesc ; BEGIN IF Ident() THEN p := FindDefinition(CurrentIdent^.name) ; IF p=NIL THEN p := NewProduction() ELSE IF NOT ((p^.statement=NIL) OR (p^.statement^.expr=NIL)) THEN WarnError1('already declared rule %s', CurrentIdent^.name) END END ; i := CurrentIdent ; IF SymIs(lbecomestok) THEN e := NewExpression() ; CurrentExpression := e ; s := NewStatement() ; WITH s^ DO ident := i ; expr := e ; END ; IF Expression() THEN p^.statement := s ; IF SymIs(rbecomestok) THEN RETURN( TRUE ) END END END END ; RETURN( FALSE ) END Statement ; (* Expression := % CurrentTerm := NIL % Term { " | " % CurrentTerm := NewTerm() % Term } =: *) PROCEDURE Expression () : BOOLEAN ; VAR t1, t2: TermDesc ; e : ExpressionDesc ; BEGIN e := CurrentExpression ; t1 := NewTerm() ; CurrentTerm := t1 ; IF Term() THEN e^.term := t1 ; WHILE SymIs(bartok) DO t2 := NewTerm() ; CurrentTerm := t2 ; IF Term() THEN t1^.next := t2 ; t1 := t2 ELSE WarnError('term expected') END END ; RETURN( TRUE ) ELSE (* DISPOSE(t1) ; *) RETURN( FALSE ) END END Expression ; (* Term := Factor { Factor } =: *) PROCEDURE Term () : BOOLEAN ; VAR t1: TermDesc ; f1, f2: FactorDesc ; BEGIN CurrentFactor := NewFactor() ; f1 := CurrentFactor ; t1 := CurrentTerm ; IF Factor() THEN t1^.factor := f1 ; f2 := NewFactor() ; CurrentFactor := f2 ; WHILE Factor() DO f1^.next := f2 ; f1 := f2 ; f2 := NewFactor() ; CurrentFactor := f2 ; END ; (* DISPOSE(f2) ; *) RETURN( TRUE ) ELSE (* DISPOSE(f1) ; *) RETURN( FALSE ) END END Term ; (* Factor := " % " Modula2Code " % " % AssignCode ; % | ( Ident | Literal | " { " Expression " } " | " [ " Expression " ] " | " ( " Expression " ) " ) =: *) PROCEDURE Factor () : BOOLEAN ; BEGIN IF SymIs(codetok) THEN IF Modula2Code() THEN IF SymIs(codetok) THEN RETURN( TRUE ) END END ELSE IF Ident() THEN WITH CurrentFactor^ DO type := id ; ident := CurrentIdent END ; RETURN( TRUE ) ELSIF Literal() THEN WITH CurrentFactor^ DO type := lit ; string := LastLiteral ; IF GetSymKey(Aliases, LastLiteral)=NulKey THEN WarnError1('no token defined for literal %s', LastLiteral) END END ; RETURN( TRUE ) ELSIF SymIs(lcparatok) THEN WITH CurrentFactor^ DO type := mult ; expr := NewExpression() ; CurrentExpression := expr ; IF Expression() THEN IF SymIs(rcparatok) THEN RETURN( TRUE ) ELSE WarnError('} expected') END END END ELSIF SymIs(lsparatok) THEN WITH CurrentFactor^ DO type := opt ; expr := NewExpression() ; CurrentExpression := expr ; IF Expression() THEN IF SymIs(rsparatok) THEN RETURN( TRUE ) ELSE WarnError('] expected') END END END ELSIF SymIs(lparatok) THEN WITH CurrentFactor^ DO type := sub ; expr := NewExpression() ; CurrentExpression := expr ; IF Expression() THEN IF SymIs(rparatok) THEN RETURN( TRUE ) ELSE WarnError(') expected') END END END END END ; RETURN( FALSE ) END Factor ; (* % module pg end *) (* GetDefinitionName - returns the name of the rule inside, p. *) PROCEDURE GetDefinitionName (p: ProductionDesc) : Name ; BEGIN IF p#NIL THEN WITH p^ DO IF (statement#NIL) AND (statement^.ident#NIL) THEN RETURN( statement^.ident^.name ) END END END ; RETURN( NulName ) END GetDefinitionName ; (* FindDefinition - searches and returns the rule which defines, n. *) PROCEDURE FindDefinition (n: Name) : ProductionDesc ; VAR p, f: ProductionDesc ; BEGIN p := HeadProduction ; f := NIL ; WHILE p#NIL DO IF GetDefinitionName(p)=n THEN IF f=NIL THEN f := p ELSE WriteString('multiple definition for rule: ') ; WriteKey(n) ; WriteLn END END ; p := p^.next END ; RETURN( f ) END FindDefinition ; (* BackPatchIdent - found an ident, i, we must look for the corresponding rule and set the definition accordingly. *) PROCEDURE BackPatchIdent (i: IdentDesc) ; BEGIN IF i#NIL THEN WITH i^ DO definition := FindDefinition(name) ; IF definition=NIL THEN WarnError1('unable to find production %s', name) ; WasNoError := FALSE END END END END BackPatchIdent ; (* BackPatchFactor - runs through the factor looking for an ident *) PROCEDURE BackPatchFactor (f: FactorDesc) ; BEGIN WHILE f#NIL DO WITH f^ DO CASE type OF id : BackPatchIdent(ident) | sub , opt , mult: BackPatchExpression(expr) ELSE END END ; f := f^.next END END BackPatchFactor ; (* BackPatchTerm - runs through all terms to find idents. *) PROCEDURE BackPatchTerm (t: TermDesc) ; BEGIN WHILE t#NIL DO BackPatchFactor(t^.factor) ; t := t^.next END END BackPatchTerm ; (* BackPatchExpression - runs through the term to find any idents. *) PROCEDURE BackPatchExpression (e: ExpressionDesc) ; BEGIN IF e#NIL THEN BackPatchTerm(e^.term) END END BackPatchExpression ; (* BackPatchSet - *) PROCEDURE BackPatchSet (s: SetDesc) ; BEGIN WHILE s#NIL DO WITH s^ DO CASE type OF idel: BackPatchIdent(ident) ELSE END END ; s := s^.next END END BackPatchSet ; (* BackPatchIdentToDefinitions - search through all the rules and add a link from any ident to the definition. *) PROCEDURE BackPatchIdentToDefinitions (d: ProductionDesc) ; BEGIN IF (d#NIL) AND (d^.statement#NIL) THEN BackPatchExpression(d^.statement^.expr) END END BackPatchIdentToDefinitions ; (* CalculateFirstAndFollow - *) PROCEDURE CalculateFirstAndFollow (p: ProductionDesc) ; BEGIN IF Debugging THEN WriteLn ; WriteKey(p^.statement^.ident^.name) ; WriteLn ; WriteString(' calculating first') END ; CalcFirstProduction(p, p, p^.first) ; BackPatchSet(p^.first) ; IF Debugging THEN WriteString(' calculating follow set') END ; IF p^.followinfo^.follow=NIL THEN CalcFollowProduction(p) END ; BackPatchSet(p^.followinfo^.follow) END CalculateFirstAndFollow ; (* ForeachRuleDo - *) PROCEDURE ForeachRuleDo (p: DoProcedure) ; BEGIN CurrentProduction := HeadProduction ; WHILE CurrentProduction#NIL DO p(CurrentProduction) ; CurrentProduction := CurrentProduction^.next END END ForeachRuleDo ; (* WhileNotCompleteDo - *) PROCEDURE WhileNotCompleteDo (p: DoProcedure) ; BEGIN REPEAT Finished := TRUE ; ForeachRuleDo(p) ; UNTIL Finished END WhileNotCompleteDo ; (* NewLine - generate a newline and indent. *) PROCEDURE NewLine (Left: CARDINAL) ; BEGIN Output.WriteLn ; BeginningOfLine := TRUE ; Indent := 0 ; WHILE IndentBaseRightMargin THEN NewLine(Left) END END CheckNewLine ; (* IndentString - writes out a string with a preceeding indent. *) PROCEDURE IndentString (a: ARRAY OF CHAR) ; VAR i: CARDINAL ; BEGIN i := 0 ; WHILE i', followinfo) END ; f := f^.next END END PrettyCommentFactor ; (* PeepTerm - returns the length of characters in term. *) PROCEDURE PeepTerm (t: TermDesc) : CARDINAL ; VAR l: CARDINAL ; BEGIN l := 0 ; WHILE t#NIL DO INC(l, PeepFactor(t^.factor)) ; IF t^.next#NIL THEN INC(l, 3) END ; t := t^.next END ; RETURN( l ) END PeepTerm ; (* PeepExpression - returns the length of the expression. *) PROCEDURE PeepExpression (e: ExpressionDesc) : CARDINAL ; BEGIN IF e=NIL THEN RETURN( 0 ) ELSE RETURN( PeepTerm(e^.term) ) END END PeepExpression ; (* PeepFactor - returns the length of character in the factor *) PROCEDURE PeepFactor (f: FactorDesc) : CARDINAL ; VAR l: CARDINAL ; BEGIN l := 0 ; WHILE f#NIL DO WITH f^ DO CASE type OF id : INC(l, LengthKey(ident^.name)+1) | lit : INC(l, LengthKey(string)+3) | opt , mult, sub : INC(l, PeepExpression(expr)) | m2 : (* empty *) ELSE END END ; f := f^.next END ; RETURN( l ) END PeepFactor ; (* PrettyCommentTerm - *) PROCEDURE PrettyCommentTerm (t: TermDesc; Left: CARDINAL) ; BEGIN WHILE t#NIL DO CheckNewLine(Left) ; PrettyCommentFactor(t^.factor, Left) ; IF t^.next#NIL THEN Output.WriteString(' | ') ; INC(Indent, 3) ; IF PeepFactor(t^.factor)+Indent>BaseRightMargin THEN NewLine(Left) END END ; PrettyFollow('', t^.followinfo) ; t := t^.next END END PrettyCommentTerm ; (* PrettyCommentExpression - *) PROCEDURE PrettyCommentExpression (e: ExpressionDesc; Left: CARDINAL) ; BEGIN IF e#NIL THEN PrettyCommentTerm(e^.term, Left) ; PrettyFollow('', e^.followinfo) END END PrettyCommentExpression ; (* PrettyCommentStatement - *) PROCEDURE PrettyCommentStatement (s: StatementDesc; Left: CARDINAL) ; BEGIN IF s#NIL THEN PrettyCommentExpression(s^.expr, Left) ; PrettyFollow('', s^.followinfo) END END PrettyCommentStatement ; (* PrettyCommentProduction - generates the comment for rule, p. *) PROCEDURE PrettyCommentProduction (p: ProductionDesc) ; VAR to: SetDesc ; BEGIN IF p#NIL THEN BeginningOfLine := TRUE ; Indent := 0 ; Output.WriteString('(*') ; NewLine(3) ; Output.WriteKey(GetDefinitionName(p)) ; Output.WriteString(' := ') ; INC(Indent, LengthKey(GetDefinitionName(p))+4) ; PrettyCommentStatement(p^.statement, Indent) ; NewLine(0) ; IF ErrorRecovery THEN NewLine(3) ; Output.WriteString('first symbols:') ; EmitSet(p^.first, 0, 0) ; NewLine(3) ; PrettyFollow('', p^.followinfo) ; NewLine(3) ; CASE GetReachEnd(p^.followinfo) OF true : Output.WriteString('reachend') | false : Output.WriteString('cannot reachend') | unknown: Output.WriteString('unknown...') ELSE END ; NewLine(0) END ; Output.WriteString('*)') ; NewLine(0) ; END END PrettyCommentProduction ; (* PrettyPrintProduction - pretty prints the ebnf rule, p. *) PROCEDURE PrettyPrintProduction (p: ProductionDesc) ; VAR to: SetDesc ; BEGIN IF p#NIL THEN BeginningOfLine := TRUE ; Indent := 0 ; IF Texinfo THEN Output.WriteString('@example') ; NewLine(0) ELSIF Sphinx THEN Output.WriteString('.. code-block:: ebnf') ; NewLine(0) END ; Output.WriteKey(GetDefinitionName(p)) ; Output.WriteString(' := ') ; INC(Indent, LengthKey(GetDefinitionName(p))+4) ; PrettyCommentStatement(p^.statement, Indent) ; IF p^.description#NulName THEN Output.WriteKey(p^.description) END ; NewLine(0) ; WriteIndent(LengthKey(GetDefinitionName(p))+1) ; Output.WriteString(' =: ') ; NewLine(0) ; IF Texinfo THEN Output.WriteString('@findex ') ; Output.WriteKey(GetDefinitionName(p)) ; Output.WriteString(' (ebnf)') ; NewLine(0) ; Output.WriteString('@end example') ; NewLine(0) ELSIF Sphinx THEN Output.WriteString('.. index::') ; NewLine(0) ; Output.WriteString(' pair: ') ; Output.WriteKey(GetDefinitionName(p)) ; Output.WriteString('; (ebnf)') ; NewLine(0) END ; NewLine(0) END END PrettyPrintProduction ; (* EmitFileLineTag - emits a line and file tag using the C preprocessor syntax. *) PROCEDURE EmitFileLineTag (line: CARDINAL) ; BEGIN IF (NOT SuppressFileLineTag) AND (line#LastLineNo) THEN LastLineNo := line ; IF NOT OnLineStart THEN Output.WriteLn END ; Output.WriteString('# ') ; Output.WriteCard(line, 0) ; Output.WriteString(' "') ; Output.WriteString(FileName) ; Output.Write('"') ; Output.WriteLn ; OnLineStart := TRUE END END EmitFileLineTag ; (* EmitRule - generates a comment and code for rule, p. *) PROCEDURE EmitRule (p: ProductionDesc) ; BEGIN IF PrettyPrint THEN PrettyPrintProduction(p) ELSE PrettyCommentProduction(p) ; IF ErrorRecovery THEN RecoverProduction(p) ELSE CodeProduction(p) END END END EmitRule ; (* CodeCondition - *) PROCEDURE CodeCondition (m: m2condition) ; BEGIN CASE m OF m2if, m2none : IndentString('IF ') | m2elsif: IndentString('ELSIF ') | m2while: IndentString('WHILE ') ELSE Halt('unrecognised m2condition', __FILE__, __FUNCTION__, __LINE__) END END CodeCondition ; (* CodeThenDo - codes a "THEN" or "DO" depending upon, m. *) PROCEDURE CodeThenDo (m: m2condition) ; BEGIN CASE m OF m2if, m2none, m2elsif: IF LastLineNo=0 THEN Output.WriteLn END ; IndentString('THEN') ; Output.WriteLn | m2while: Output.WriteString(' DO') ; Output.WriteLn ELSE Halt('unrecognised m2condition', __FILE__, __FUNCTION__, __LINE__) END ; OnLineStart := TRUE END CodeThenDo ; (* CodeElseEnd - builds an ELSE END statement using string, end. *) PROCEDURE CodeElseEnd (end: ARRAY OF CHAR; consumed: BOOLEAN; f: FactorDesc; inopt: BOOLEAN) ; BEGIN Output.WriteLn ; OnLineStart := TRUE ; EmitFileLineTag(f^.line) ; IF NOT inopt THEN IndentString('ELSE') ; WriteLn ; INC(Indent, 3) ; IF consumed THEN IndentString('') ; Output.WriteKey(ErrorProcArray) ; Output.Write('(') ; WITH f^ DO CASE type OF id : Output.Write("'") ; Output.WriteKey(ident^.name) ; Output.WriteString(' - expected') ; Output.WriteString("') ;") | lit : IF MakeKey("'")=string THEN Output.Write('"') ; KeyWord(string) ; Output.WriteString(' - expected') ; Output.WriteString('") ;') ELSIF MakeKey('"')=string THEN Output.Write("'") ; KeyWord(string) ; Output.WriteString(' - expected') ; Output.WriteString("') ;") ELSE Output.Write('"') ; Output.Write("'") ; KeyWord(string) ; Output.WriteString("' - expected") ; Output.WriteString('") ;') END ELSE END END ; Output.WriteLn END ; IndentString('RETURN( FALSE )') ; DEC(Indent, 3) ; Output.WriteLn END ; IndentString(end) ; Output.WriteLn ; OnLineStart := TRUE END CodeElseEnd ; (* CodeEnd - codes a "END" depending upon, m. *) PROCEDURE CodeEnd (m: m2condition; t: TermDesc; consumed: BOOLEAN; f: FactorDesc; inopt: BOOLEAN) ; BEGIN DEC(Indent, 3) ; Output.WriteLn ; OnLineStart := TRUE ; CASE m OF m2none : IF t=NIL THEN CodeElseEnd('END ;', consumed, f, inopt) END | m2if : IF t=NIL THEN CodeElseEnd('END ; (* if *)', consumed, f, inopt) END | m2elsif: IF t=NIL THEN CodeElseEnd('END ; (* elsif *)', consumed, f, inopt) END | m2while: IndentString('END ; (* while *)') ELSE Halt('unrecognised m2condition', __FILE__, __FUNCTION__, __LINE__) END ; OnLineStart := FALSE END CodeEnd ; (* EmitNonVarCode - writes out, code, providing it is not a variable declaration. *) PROCEDURE EmitNonVarCode (code: CodeDesc; curpos, left: CARDINAL) ; VAR i : CARDINAL ; t : CodeHunk ; seentext: BOOLEAN ; BEGIN t := code^.code ; IF (NOT FindStr(t, i, 'VAR')) AND EmitCode THEN seentext := FALSE ; curpos := 0 ; EmitFileLineTag(code^.line) ; IndentString('') ; WriteCodeHunkListIndent(code^.code, code^.indent, curpos, left, seentext) ; Output.WriteString(' ;') ; Output.WriteLn ; OnLineStart := TRUE END END EmitNonVarCode ; (* ChainOn - *) PROCEDURE ChainOn (codeStack, f: FactorDesc) : FactorDesc ; VAR s: FactorDesc ; BEGIN f^.pushed := NIL ; IF codeStack=NIL THEN RETURN( f ) ELSE s := codeStack ; WHILE s^.pushed#NIL DO s := s^.pushed END ; s^.pushed := f ; RETURN( codeStack ) END END ChainOn ; (* FlushCode - *) PROCEDURE FlushCode (VAR codeStack: FactorDesc) ; BEGIN IF codeStack#NIL THEN NewLine(Indent) ; Output.WriteString('(* begin flushing code *)') ; OnLineStart := FALSE ; WHILE codeStack#NIL DO NewLine(Indent) ; EmitNonVarCode(codeStack^.code, 0, Indent) ; NewLine(Indent) ; codeStack := codeStack^.pushed ; IF codeStack#NIL THEN Output.WriteString(' (* again flushing code *)') ; Output.WriteLn ; OnLineStart := TRUE END END ; NewLine(Indent) ; Output.WriteString('(* end flushing code *)') ; OnLineStart := FALSE END END FlushCode ; (* CodeFactor - *) PROCEDURE CodeFactor (f: FactorDesc; t: TermDesc; l, n: m2condition; inopt, inwhile, consumed: BOOLEAN; codeStack: FactorDesc) ; BEGIN IF f=NIL THEN IF (* ((l=m2elsif) OR (l=m2if) OR (l=m2none)) AND *) (NOT inwhile) AND (NOT inopt) THEN Output.WriteLn ; IndentString('RETURN( TRUE )') ; OnLineStart := FALSE END ELSE WITH f^ DO EmitFileLineTag(line) ; CASE type OF id : FlushCode(codeStack) ; CodeCondition(n) ; Output.WriteKey(ident^.name) ; Output.WriteString('()') ; CodeThenDo(n) ; INC(Indent, 3) ; CodeFactor(f^.next, NIL, n, m2none, inopt, inwhile, TRUE, NIL) ; CodeEnd(n, t, consumed, f, inopt) | lit : FlushCode(codeStack) ; CodeCondition(n) ; Output.WriteKey(SymIsProc) ; Output.Write('(') ; Output.WriteKey(GetSymKey(Aliases, string)) ; Output.Write(')') ; CodeThenDo(n) ; INC(Indent, 3) ; CodeFactor(f^.next, NIL, n, m2none, inopt, inwhile, TRUE, NIL) ; CodeEnd(n, t, consumed, f, inopt) | sub: FlushCode(codeStack) ; CodeExpression(expr, m2none, inopt, inwhile, consumed, NIL) ; IF f^.next#NIL THEN (* * the test above makes sure that we don't emit a RETURN( TRUE ) * after a subexpression. Remember sub expressions are not conditional *) CodeFactor(f^.next, t, n, m2none, inopt, inwhile, TRUE, NIL) END | opt: FlushCode(codeStack) ; CodeExpression(expr, m2if, TRUE, inwhile, FALSE, NIL) ; CodeFactor(f^.next, t, n, m2none, inopt, inwhile, consumed, NIL) | mult: FlushCode(codeStack) ; CodeExpression(expr, m2while, FALSE, TRUE, consumed, NIL) ; CodeFactor(f^.next, t, n, m2none, inopt, inwhile, consumed, NIL) | m2 : codeStack := ChainOn(codeStack, f) ; IF consumed OR (f^.next=NIL) THEN FlushCode(codeStack) END ; CodeFactor(f^.next, t, n, m2none, inopt, inwhile, consumed, codeStack) ELSE END END END END CodeFactor ; (* CodeTerm - *) PROCEDURE CodeTerm (t: TermDesc; m: m2condition; inopt, inwhile, consumed: BOOLEAN; codeStack: FactorDesc) ; VAR l: m2condition ; BEGIN l := m ; WHILE t#NIL DO EmitFileLineTag(t^.line) ; IF (t^.factor^.type=m2) AND (m=m2elsif) THEN m := m2if ; IndentString('ELSE') ; Output.WriteLn ; OnLineStart := TRUE ; INC(Indent, 3) ; CodeFactor(t^.factor, t^.next, m2none, m2none, inopt, inwhile, consumed, codeStack) ; DEC(Indent, 3) ; IndentString('END ;') ; Output.WriteLn ; OnLineStart := TRUE ELSE CodeFactor(t^.factor, t^.next, m2none, m, inopt, inwhile, consumed, codeStack) END ; l := m ; IF t^.next#NIL THEN m := m2elsif END ; t := t^.next END END CodeTerm ; (* CodeExpression - *) PROCEDURE CodeExpression (e: ExpressionDesc; m: m2condition; inopt, inwhile, consumed: BOOLEAN; codeStack: FactorDesc) ; BEGIN IF e#NIL THEN EmitFileLineTag(e^.line) ; CodeTerm(e^.term, m, inopt, inwhile, consumed, codeStack) END END CodeExpression ; (* CodeStatement - *) PROCEDURE CodeStatement (s: StatementDesc; m: m2condition) ; BEGIN IF s#NIL THEN EmitFileLineTag(s^.line) ; CodeExpression(s^.expr, m, FALSE, FALSE, FALSE, NIL) END END CodeStatement ; (* CodeProduction - only encode grammer rules which are not special. *) PROCEDURE CodeProduction (p: ProductionDesc) ; BEGIN IF (p#NIL) AND ((NOT p^.firstsolved) OR ((p^.statement#NIL) AND (p^.statement^.expr#NIL))) THEN BeginningOfLine := TRUE ; Indent := 0 ; Output.WriteLn ; EmitFileLineTag(p^.line) ; IndentString('PROCEDURE ') ; Output.WriteKey(GetDefinitionName(p)) ; Output.WriteString(' () : BOOLEAN ;') ; VarProduction(p) ; Output.WriteLn ; OnLineStart := TRUE ; EmitFileLineTag(p^.line) ; IndentString('BEGIN') ; WriteLn ; OnLineStart := FALSE ; EmitFileLineTag(p^.line) ; Indent := 3 ; CodeStatement(p^.statement, m2none) ; Output.WriteLn ; Indent := 0 ; IndentString('END ') ; WriteKey(GetDefinitionName(p)) ; Output.WriteString(' ;') ; Output.WriteLn ; Output.WriteLn ; Output.WriteLn END END CodeProduction ; (* and now for the production of code which will recover from syntax errors *) (* RecoverCondition - *) PROCEDURE RecoverCondition (m: m2condition) ; BEGIN CASE m OF m2if : IndentString('IF ') | m2none : IndentString('IF ') | m2elsif: IndentString('ELSIF ') | m2while: IndentString('WHILE ') ELSE Halt('unrecognised m2condition', __FILE__, __FUNCTION__, __LINE__) END END RecoverCondition ; (* ConditionIndent - returns the number of spaces indentation created via, m. *) PROCEDURE ConditionIndent (m: m2condition) : CARDINAL ; BEGIN CASE m OF m2if : RETURN( 3 ) | m2none : RETURN( 3 ) | m2elsif: RETURN( 6 ) | m2while: RETURN( 6 ) ELSE Halt('unrecognised m2condition', __FILE__, __FUNCTION__, __LINE__) END END ConditionIndent ; (* WriteGetTokenType - writes out the method of determining the token type. *) PROCEDURE WriteGetTokenType ; BEGIN Output.WriteKey(TokenTypeProc) END WriteGetTokenType ; (* NumberOfElements - returns the number of elements in set, to, which lie between low..high *) PROCEDURE NumberOfElements (to: SetDesc; low, high: WORD) : CARDINAL ; VAR n: CARDINAL ; BEGIN n := 0 ; WHILE to#NIL DO WITH to^ DO CASE type OF tokel: IF (high=0) OR IsBetween(string, low, high) THEN INC(n) END | litel: IF (high=0) OR IsBetween(GetSymKey(Aliases, string), low, high) THEN INC(n) END | idel : WarnError('not expecting ident in first symbol list') ; WasNoError := FALSE ELSE WarnError('unknown enuneration element') ; WasNoError := FALSE END END ; to := to^.next ; END ; RETURN( n ) END NumberOfElements ; (* WriteElement - writes the literal name for element, e. *) PROCEDURE WriteElement (e: WORD) ; BEGIN Output.WriteKey(GetSymKey(ReverseValues, e)) END WriteElement ; (* EmitIsInSet - writes out the equivalent of GetTokenType() IN { toset } *) PROCEDURE EmitIsInSet (to: SetDesc; low, high: Name) ; BEGIN IF NumberOfElements(to, low, high)=1 THEN WriteGetTokenType ; Output.Write('=') ; EmitSet(to, low, high) ELSE WriteGetTokenType ; Output.WriteString(' IN SetOfStop') ; IF LargestValue > MaxElementsInSet THEN Output.WriteCard(CARDINAL(low) DIV MaxElementsInSet, 0) END ; Output.WriteString(' {') ; EmitSet(to, low, high) ; Output.WriteString('}') END END EmitIsInSet ; (* EmitIsInSubSet - writes out a test to see whether GetTokenype() is in { subset } *) PROCEDURE EmitIsInSubSet (to: SetDesc; low, high: WORD) ; BEGIN IF NumberOfElements(to, low, high)=1 THEN Output.Write('(') ; EmitIsInSet(to, low, high) ; Output.Write(')') ELSIF low=0 THEN (* no need to check whether GetTokenType > low *) Output.WriteString('((') ; WriteGetTokenType ; Output.Write('<') ; WriteElement(INTEGER(high)+1) ; Output.WriteString(') AND (') ; EmitIsInSet(to, low, high) ; Output.WriteString('))') ELSIF CARDINAL(high)>LargestValue THEN (* no need to check whether GetTokenType < high *) Output.WriteString('((') ; WriteGetTokenType ; Output.WriteString('>=') ; WriteElement(low) ; Output.WriteString(') AND (') ; EmitIsInSet(to, low, high) ; Output.WriteString('))') ELSE Output.WriteString('((') ; WriteGetTokenType ; Output.WriteString('>=') ; WriteElement(low) ; Output.WriteString(') AND (') ; WriteGetTokenType ; Output.Write('<') ; WriteElement(INTEGER(high)+1) ; Output.WriteString(') AND (') ; EmitIsInSet(to, low, high) ; Output.WriteString('))') END END EmitIsInSubSet ; (* EmitIsInFirst - *) PROCEDURE EmitIsInFirst (to: SetDesc; m: m2condition) ; VAR i : CARDINAL ; first: BOOLEAN ; BEGIN IF NumberOfElements(to, 0, 0)=1 THEN (* only one element *) WriteGetTokenType ; Output.Write('=') ; EmitSet(to, 0, 0) ELSE IF LargestValue<=MaxElementsInSet THEN Output.Write('(') ; WriteGetTokenType ; Output.WriteString(' IN ') ; EmitSetAsParameters(to) ; Output.WriteString(')') ELSE i := 0 ; first := TRUE ; REPEAT IF NOT IsEmptySet(to, i*MaxElementsInSet, (i+1)*MaxElementsInSet-1) THEN IF NOT first THEN Output.WriteString(' OR') ; NewLine(Indent+ConditionIndent(m)) ; DEC(Indent, ConditionIndent(m)) END ; EmitIsInSubSet(to, i*MaxElementsInSet, (i+1)*MaxElementsInSet-1) ; first := FALSE END ; INC(i) ; UNTIL i*MaxElementsInSet>LargestValue END END END EmitIsInFirst ; (* FlushCode - *) PROCEDURE FlushRecoverCode (VAR codeStack: FactorDesc) ; BEGIN IF codeStack#NIL THEN WHILE codeStack#NIL DO EmitNonVarCode(codeStack^.code, 0, Indent) ; codeStack := codeStack^.pushed END END END FlushRecoverCode ; (* RecoverFactor - *) PROCEDURE RecoverFactor (f: FactorDesc; m: m2condition; codeStack: FactorDesc) ; VAR to: SetDesc ; BEGIN IF f=NIL THEN ELSE EmitFileLineTag(f^.line) ; WITH f^ DO CASE type OF id : to := NIL ; CalcFirstFactor(f, NIL, to) ; IF (to#NIL) AND (m#m2none) THEN RecoverCondition(m) ; EmitIsInFirst(to, m) ; CodeThenDo(m) ; INC(Indent, 3) END ; FlushRecoverCode(codeStack) ; IndentString('') ; Output.WriteKey(ident^.name) ; Output.Write('(') ; EmitStopParametersAndFollow(f, m) ; Output.WriteString(') ;') ; Output.WriteLn ; RecoverFactor(f^.next, m2none, codeStack) ; IF (to#NIL) AND (m#m2none) THEN DEC(Indent, 3) END | lit : IF m=m2none THEN FlushRecoverCode(codeStack) ; IndentString('Expect(') ; Output.WriteKey(GetSymKey(Aliases, string)) ; Output.WriteString(', ') ; EmitStopParametersAndFollow(f, m) ; Output.WriteString(') ;') ; Output.WriteLn ; RecoverFactor(f^.next, m2none, codeStack) ELSE RecoverCondition(m) ; WriteGetTokenType ; Output.Write('=') ; Output.WriteKey(GetSymKey(Aliases, string)) ; CodeThenDo(m) ; INC(Indent, 3) ; IndentString('Expect(') ; Output.WriteKey(GetSymKey(Aliases, string)) ; Output.WriteString(', ') ; EmitStopParametersAndFollow(f, m) ; Output.WriteString(') ;') ; Output.WriteLn ; FlushRecoverCode(codeStack) ; RecoverFactor(f^.next, m2none, codeStack) ; DEC(Indent, 3) END | sub: FlushRecoverCode(codeStack) ; RecoverExpression(expr, m2none, m) ; RecoverFactor(f^.next, m2none, codeStack) | opt: FlushRecoverCode(codeStack) ; IF OptExpSeen(f) THEN to := NIL ; CalcFirstExpression(expr, NIL, to) ; RecoverCondition(m) ; EmitIsInFirst(to, m) ; CodeThenDo(m) ; INC(Indent, 3) ; IndentString('(* seen optional [ | ] expression *)') ; Output.WriteLn ; stop(); RecoverExpression(expr, m2none, m2if) ; IndentString('(* end of optional [ | ] expression *)') ; Output.WriteLn ; DEC(Indent, 3) ; IndentString('END ;') ; Output.WriteLn ELSE RecoverExpression(expr, m2if, m) END ; RecoverFactor(f^.next, m2none, codeStack) | mult: FlushRecoverCode(codeStack) ; IF OptExpSeen(f) OR (m=m2if) OR (m=m2elsif) THEN to := NIL ; CalcFirstExpression(expr, NIL, to) ; RecoverCondition(m) ; EmitIsInFirst(to, m) ; CodeThenDo(m) ; INC(Indent, 3) ; IndentString('(* seen optional { | } expression *)') ; Output.WriteLn ; RecoverCondition(m2while) ; EmitIsInFirst(to, m2while) ; CodeThenDo(m2while) ; INC(Indent, 3) ; RecoverExpression(expr, m2none, m2while) ; IndentString('(* end of optional { | } expression *)') ; Output.WriteLn ; DEC(Indent, 3) ; IndentString('END ;') ; Output.WriteLn ; DEC(Indent, 3) ; IF m=m2none THEN IndentString('END ;') ; Output.WriteLn ; DEC(Indent, 3) END ELSE RecoverExpression(expr, m2while, m) END ; RecoverFactor(f^.next, m2none, codeStack) | m2 : codeStack := ChainOn(codeStack, f) ; IF f^.next=NIL THEN FlushRecoverCode(codeStack) ELSE RecoverFactor(f^.next, m, codeStack) (* was m2none *) END ELSE END END END END RecoverFactor ; (* OptExpSeen - returns TRUE if we can see an optional expression in the factor. This is not the same as epsilon. Example { '+' } matches epsilon as well as { '+' | '-' } but OptExpSeen returns TRUE in the second case and FALSE in the first. *) PROCEDURE OptExpSeen (f: FactorDesc) : BOOLEAN ; BEGIN IF f=NIL THEN RETURN( FALSE ) ELSE WITH f^ DO CASE type OF id , lit : RETURN( FALSE ) | sub : RETURN( FALSE ) | (* is this correct? *) opt , mult: RETURN( (expr#NIL) AND (expr^.term#NIL) AND (expr^.term^.next#NIL) ) | m2 : RETURN( TRUE ) ELSE END END END ; WarnError('all cases were not handled') ; WasNoError := FALSE END OptExpSeen ; (* RecoverTerm - *) PROCEDURE RecoverTerm (t: TermDesc; new, old: m2condition) ; VAR LastWasM2Only, (* does the factor only contain inline code? *) alternative : BOOLEAN ; to : SetDesc ; BEGIN LastWasM2Only := (t^.factor^.type = m2) AND (t^.factor^.next = NIL) ; to := NIL ; CalcFirstTerm(t, NIL, to) ; alternative := FALSE ; IF t^.next#NIL THEN new := m2if END ; WHILE t#NIL DO EmitFileLineTag(t^.line) ; LastWasM2Only := (t^.factor^.type = m2) AND (t^.factor^.next = NIL) ; IF (t^.factor^.type=m2) AND (new=m2elsif) THEN new := m2if ; IndentString('ELSE') ; Output.WriteLn ; INC(Indent, 3) ; RecoverFactor(t^.factor, m2none, NIL) ; alternative := FALSE ELSE RecoverFactor(t^.factor, new, NIL) END ; IF t^.next#NIL THEN new := m2elsif ; alternative := TRUE END ; t := t^.next END ; IF (new=m2if) OR (new=m2elsif) THEN IF alternative AND (old#m2while) THEN IndentString('ELSE') ; Output.WriteLn ; INC(Indent, 3) ; IndentString('') ; Output.WriteKey(ErrorProcArray) ; Output.WriteString("('expecting one of: ") ; EmitSetName(to, 0, 0) ; Output.WriteString("')") ; Output.WriteLn ; DEC(Indent, 3) ELSIF LastWasM2Only THEN DEC(Indent, 3) END ; IndentString('END ;') ; Output.WriteLn ELSIF new=m2while THEN IndentString('END (* while *) ;') ; Output.WriteLn ELSIF LastWasM2Only THEN DEC(Indent, 3) END END RecoverTerm ; (* RecoverExpression - *) PROCEDURE RecoverExpression (e: ExpressionDesc; new, old: m2condition) ; BEGIN IF e#NIL THEN EmitFileLineTag(e^.line) ; RecoverTerm(e^.term, new, old) END END RecoverExpression ; (* RecoverStatement - *) PROCEDURE RecoverStatement (s: StatementDesc; m: m2condition) ; BEGIN IF s#NIL THEN EmitFileLineTag(s^.line) ; RecoverExpression(s^.expr, m, m2none) END END RecoverStatement ; (* EmitFirstFactor - generate a list of all first tokens between the range: low..high. *) PROCEDURE EmitFirstFactor (f: FactorDesc; low, high: CARDINAL) ; BEGIN END EmitFirstFactor ; (* EmitUsed - *) PROCEDURE EmitUsed (wordno: CARDINAL) ; BEGIN IF NOT (wordno IN ParametersUsed) THEN Output.WriteString (" (* <* unused *> *) ") END END EmitUsed ; (* EmitStopParameters - generate the stop set. *) PROCEDURE EmitStopParameters (FormalParameters: BOOLEAN) ; VAR i: CARDINAL ; BEGIN IF LargestValue<=MaxElementsInSet THEN Output.WriteString('stopset') ; IF FormalParameters THEN Output.WriteString(': SetOfStop') ; EmitUsed (0) ELSE INCL (ParametersUsed, 0) END ELSE i := 0 ; REPEAT Output.WriteString('stopset') ; Output.WriteCard(i, 0) ; IF FormalParameters THEN Output.WriteString(': SetOfStop') ; Output.WriteCard(i, 0) ; EmitUsed (i) ELSE INCL (ParametersUsed, i) END ; INC (i) ; IF i*MaxElementsInSet=LargestValue ; END END EmitStopParameters ; (* IsBetween - returns TRUE if the value of the token, string, is in the range: low..high *) PROCEDURE IsBetween (string: Name; low, high: WORD) : BOOLEAN ; BEGIN RETURN( (GetSymKey(Values, string) >= low) AND (GetSymKey(Values, string) <= high) ) END IsBetween ; (* IsEmptySet - returns TRUE if no elements exist in set, to, with values, low..high. *) PROCEDURE IsEmptySet (to: SetDesc; low, high: WORD) : BOOLEAN ; BEGIN WHILE to#NIL DO WITH to^ DO CASE type OF tokel: IF IsBetween(string, low, high) THEN RETURN( FALSE ) END | litel: IF IsBetween(GetSymKey(Aliases, string), low, high) THEN RETURN( FALSE ) END | idel : WarnError('not expecting ident in first symbol list') ; WasNoError := FALSE ELSE WarnError('unknown enuneration element') ; WasNoError := FALSE END END ; to := to^.next ; END ; RETURN( TRUE ) END IsEmptySet ; (* EmitSet - emits the tokens in the set, to, which have values low..high *) PROCEDURE EmitSet (to: SetDesc; low, high: WORD) ; VAR first: BOOLEAN ; BEGIN first := TRUE ; WHILE to#NIL DO WITH to^ DO CASE type OF tokel: IF (high=0) OR IsBetween(string, low, high) THEN IF NOT first THEN Output.WriteString(', ') END ; Output.WriteKey(string) ; first := FALSE END | litel: IF (high=0) OR IsBetween(GetSymKey(Aliases, string), low, high) THEN IF NOT first THEN Output.WriteString(', ') END ; Output.WriteKey(GetSymKey(Aliases, string)) ; first := FALSE END | idel : WarnError('not expecting ident in first symbol list') ; WasNoError := FALSE ELSE WarnError('unknown enuneration element') ; WasNoError := FALSE END END ; to := to^.next END END EmitSet ; (* EmitSetName - emits the tokens in the set, to, which have values low..high, using their names. *) PROCEDURE EmitSetName (to: SetDesc; low, high: WORD) ; BEGIN WHILE to#NIL DO WITH to^ DO CASE type OF tokel: IF (high=0) OR IsBetween(string, low, high) THEN IF MakeKey("'")=GetSymKey(ReverseAliases, string) THEN Output.WriteString('single quote') ELSE KeyWord(GetSymKey(ReverseAliases, string)) END END | litel: IF (high=0) OR IsBetween(GetSymKey(Aliases, string), low, high) THEN Output.WriteKey(string) END | idel : WarnError('not expecting ident in first symbol list') ; WasNoError := FALSE ELSE WarnError('unknown enuneration element') ; WasNoError := FALSE END END ; to := to^.next ; IF to#NIL THEN Output.Write(' ') END END END EmitSetName ; (* EmitStopParametersAndSet - generates the stop parameters together with a set inclusion of all the symbols in set, to. *) PROCEDURE EmitStopParametersAndSet (to: SetDesc) ; VAR i : CARDINAL ; BEGIN IF LargestValue<=MaxElementsInSet THEN Output.WriteString('stopset') ; INCL (ParametersUsed, 0) ; IF (to#NIL) AND (NumberOfElements(to, 0, MaxElementsInSet-1)>0) THEN Output.WriteString(' + SetOfStop') ; Output.Write('{') ; EmitSet(to, 0, MaxElementsInSet-1) ; Output.Write('}') END ELSE i := 0 ; REPEAT Output.WriteString('stopset') ; Output.WriteCard(i, 0) ; INCL (ParametersUsed, i) ; IF (to#NIL) AND (NumberOfElements(to, i*MaxElementsInSet, (i+1)*MaxElementsInSet-1)>0) THEN Output.WriteString(' + SetOfStop') ; Output.WriteCard(i, 0) ; Output.Write('{') ; EmitSet(to, i*MaxElementsInSet, (i+1)*MaxElementsInSet-1) ; Output.Write('}') END ; INC(i) ; IF i*MaxElementsInSet=LargestValue END END EmitStopParametersAndSet ; (* EmitSetAsParameters - generates the first symbols as parameters to a set function. *) PROCEDURE EmitSetAsParameters (to: SetDesc) ; VAR i : CARDINAL ; BEGIN IF LargestValue<=MaxElementsInSet THEN Output.Write('{') ; EmitSet(to, 0, MaxElementsInSet-1) ELSE i := 0 ; REPEAT Output.Write('{') ; EmitSet(to, i*MaxElementsInSet, (i+1)*MaxElementsInSet-1) ; INC(i) ; IF (i+1)*MaxElementsInSet>LargestValue THEN Output.WriteString('}, ') END UNTIL (i+1)*MaxElementsInSet>=LargestValue ; END ; Output.Write('}') END EmitSetAsParameters ; (* EmitStopParametersAndFollow - generates the stop parameters together with a set inclusion of all the follow symbols for subsequent sentances. *) PROCEDURE EmitStopParametersAndFollow (f: FactorDesc; m: m2condition) ; VAR to: SetDesc ; BEGIN to := NIL ; (* IF m=m2while THEN CalcFirstFactor(f, NIL, to) END ; *) CollectFollow(to, f^.followinfo) ; EmitStopParametersAndSet(to) ; IF Debugging THEN Output.WriteLn ; Output.WriteString('factor is: ') ; PrettyCommentFactor(f, StrLen('factor is: ')) ; Output.WriteLn ; Output.WriteString('follow set:') ; EmitSet(to, 0, 0) ; Output.WriteLn END END EmitStopParametersAndFollow ; (* EmitFirstAsParameters - *) PROCEDURE EmitFirstAsParameters (f: FactorDesc) ; VAR to: SetDesc ; BEGIN to := NIL ; CalcFirstFactor(f, NIL, to) ; EmitSetAsParameters(to) END EmitFirstAsParameters ; (* RecoverProduction - only encode grammer rules which are not special. Generate error recovery code. *) PROCEDURE RecoverProduction (p: ProductionDesc) ; VAR s: String ; BEGIN IF (p#NIL) AND ((NOT p^.firstsolved) OR ((p^.statement#NIL) AND (p^.statement^.expr#NIL))) THEN BeginningOfLine := TRUE ; Indent := 0 ; Output.WriteLn ; OnLineStart := FALSE ; EmitFileLineTag(p^.line) ; IndentString('PROCEDURE ') ; Output.WriteKey(GetDefinitionName(p)) ; Output.WriteString(' (') ; ParametersUsed := {} ; Output.StartBuffer ; Output.WriteString(') ;') ; VarProduction(p) ; Output.WriteLn ; OnLineStart := FALSE ; EmitFileLineTag(p^.line) ; Indent := 0 ; IndentString('BEGIN') ; Output.WriteLn ; OnLineStart := FALSE ; EmitFileLineTag(p^.line) ; Indent := 3 ; RecoverStatement(p^.statement, m2none) ; Indent := 0 ; IndentString('END ') ; Output.WriteKey(GetDefinitionName(p)) ; Output.WriteString(' ;') ; Output.WriteLn ; Output.WriteLn ; Output.WriteLn ; s := Output.EndBuffer () ; EmitStopParameters (TRUE) ; Output.KillWriteS (s) END END RecoverProduction ; (* IsWhite - returns TRUE if, ch, is a space or a tab. *) PROCEDURE IsWhite (ch: CHAR) : BOOLEAN ; BEGIN RETURN( (ch=' ') OR (ch=tab) OR (ch=lf) ) END IsWhite ; (* FindStr - returns TRUE if, str, was seen inside the code hunk *) PROCEDURE FindStr (VAR code: CodeHunk; VAR i: CARDINAL; str: ARRAY OF CHAR) : BOOLEAN ; VAR j, k: CARDINAL ; t : CodeHunk ; BEGIN t := code ; k := StrLen(code^.codetext)+1 ; WHILE t#NIL DO REPEAT WHILE (k>0) AND IsWhite(t^.codetext[k-1]) DO DEC(k) END ; IF k=0 THEN t := t^.next ; k := MaxCodeHunkLength+1 END UNTIL (t=NIL) OR (NOT IsWhite(t^.codetext[k-1])) ; (* found another word check it *) IF t#NIL THEN j := StrLen(str) ; i := k ; WHILE (t#NIL) AND (j>0) AND ((str[j-1]=t^.codetext[k-1]) OR (IsWhite(str[j-1]) AND IsWhite(t^.codetext[k-1]))) DO DEC(j) ; DEC(k) ; IF j=0 THEN (* found word remember position *) code := t END ; IF k=0 THEN t := t^.next ; k := MaxCodeHunkLength+1 END END ; IF k>0 THEN DEC(k) ELSE t := t^.next END END ; END ; RETURN( (t=NIL) AND (j=0) ) END FindStr ; (* WriteUpto - *) PROCEDURE WriteUpto (code, upto: CodeHunk; limit: CARDINAL) ; BEGIN IF code#upto THEN WriteUpto(code^.next, upto, limit) ; Output.WriteString(code^.codetext) ELSE WHILE (limit<=MaxCodeHunkLength) AND (code^.codetext[limit]#nul) DO Output.Write(code^.codetext[limit]) ; INC(limit) END END END WriteUpto ; (* CheckForVar - checks for any local variables which need to be emitted during this production. *) PROCEDURE CheckForVar (code: CodeHunk) ; VAR i: CARDINAL ; t: CodeHunk ; BEGIN t := code ; IF FindStr(t, i, 'VAR') AND EmitCode THEN IF NOT EmittedVar THEN Output.WriteLn ; Indent := 0 ; IndentString('VAR') ; INC(Indent, 3) ; Output.WriteLn ; EmittedVar := TRUE ; END ; WriteUpto(code, t, i) END END CheckForVar ; (* VarFactor - *) PROCEDURE VarFactor (f: FactorDesc) ; BEGIN WHILE f#NIL DO WITH f^ DO CASE type OF id : | lit : | sub , opt , mult: VarExpression(expr) | m2 : CheckForVar(code^.code) ELSE END END ; f := f^.next END END VarFactor ; (* VarTerm - *) PROCEDURE VarTerm (t: TermDesc) ; BEGIN WHILE t#NIL DO VarFactor(t^.factor) ; t := t^.next END END VarTerm ; (* VarExpression - *) PROCEDURE VarExpression (e: ExpressionDesc) ; BEGIN IF e#NIL THEN VarTerm(e^.term) END END VarExpression ; (* VarStatement - *) PROCEDURE VarStatement (s: StatementDesc) ; BEGIN IF s#NIL THEN VarExpression(s^.expr) END END VarStatement ; (* VarProduction - writes out all variable declarations. *) PROCEDURE VarProduction (p: ProductionDesc) ; BEGIN EmittedVar := FALSE ; IF p#NIL THEN VarStatement(p^.statement) END END VarProduction ; (* In - returns TRUE if token, s, is already in the set, to. *) PROCEDURE In (to: SetDesc; s: Name) : BOOLEAN ; BEGIN WHILE to#NIL DO WITH to^ DO CASE type OF idel : IF s=ident^.name THEN RETURN( TRUE ) END | tokel, litel : IF s=string THEN RETURN( TRUE ) END ELSE WarnError('internal error CASE type not known') ; WasNoError := FALSE END END ; to := to^.next END ; RETURN( FALSE ) END In ; (* IntersectionIsNil - given two set lists, s1, s2, return TRUE if the s1 * s2 = {} *) PROCEDURE IntersectionIsNil (s1, s2: SetDesc) : BOOLEAN ; BEGIN WHILE s1#NIL DO WITH s1^ DO CASE type OF idel : IF In(s2, ident^.name) THEN RETURN( FALSE ) END | tokel, litel: IF In(s2, string) THEN RETURN( FALSE ) END ELSE WarnError('internal error CASE type not known') ; WasNoError := FALSE END END ; s1 := s1^.next END ; RETURN( TRUE ) END IntersectionIsNil ; (* AddSet - adds a first symbol to a production. *) PROCEDURE AddSet (VAR to: SetDesc; s: Name) ; VAR d: SetDesc ; BEGIN IF NOT In(to, s) THEN d := NewSetDesc() ; WITH d^ DO type := tokel ; string := s ; next := to ; END ; to := d ; Finished := FALSE END END AddSet ; (* OrSet - *) PROCEDURE OrSet (VAR to: SetDesc; from: SetDesc) ; BEGIN WHILE from#NIL DO WITH from^ DO CASE type OF tokel: AddSet(to, string) | litel: AddSet(to, GetSymKey(Aliases, string)) | idel : WarnError('not expecting ident in first symbol list') ; WasNoError := FALSE ELSE Halt('unknown element in enumeration type', __FILE__, __FUNCTION__, __LINE__) END END ; from := from^.next END END OrSet ; (* CalcFirstFactor - *) PROCEDURE CalcFirstFactor (f: FactorDesc; from: ProductionDesc; VAR to: SetDesc) ; BEGIN WHILE f#NIL DO WITH f^ DO CASE type OF id : IF ident^.definition=NIL THEN WarnError1("no rule found for an 'ident' called '%s'", ident^.name) ; HALT END ; OrSet(to, ident^.definition^.first) ; IF GetReachEnd(ident^.definition^.followinfo)=false THEN RETURN END | lit : IF GetSymKey(Aliases, string)=NulKey THEN WarnError1("unknown token for '%s'", string) ; WasNoError := FALSE ELSE AddSet(to, GetSymKey(Aliases, string)) END ; RETURN | sub , opt , mult: CalcFirstExpression(expr, from, to) | m2 : ELSE END END ; f := f^.next END END CalcFirstFactor ; (* CalcFirstTerm - *) PROCEDURE CalcFirstTerm (t: TermDesc; from: ProductionDesc; VAR to: SetDesc) ; BEGIN WHILE t#NIL DO CalcFirstFactor(t^.factor, from, to) ; t := t^.next END END CalcFirstTerm ; (* CalcFirstExpression - *) PROCEDURE CalcFirstExpression (e: ExpressionDesc; from: ProductionDesc; VAR to: SetDesc) ; BEGIN IF e#NIL THEN CalcFirstTerm(e^.term, from, to) END END CalcFirstExpression ; (* CalcFirstStatement - *) PROCEDURE CalcFirstStatement (s: StatementDesc; from: ProductionDesc; VAR to: SetDesc) ; BEGIN IF s#NIL THEN CalcFirstExpression(s^.expr, from, to) END END CalcFirstStatement ; (* CalcFirstProduction - calculates all of the first symbols for the grammer *) PROCEDURE CalcFirstProduction (p: ProductionDesc; from: ProductionDesc; VAR to: SetDesc) ; VAR s: SetDesc ; BEGIN IF p#NIL THEN IF p^.firstsolved THEN s := p^.first ; WHILE s#NIL DO CASE s^.type OF idel : CalcFirstProduction(s^.ident^.definition, from, to) | tokel, litel: AddSet(to, s^.string) ELSE END ; s := s^.next END ELSE CalcFirstStatement(p^.statement, from, to) END END END CalcFirstProduction ; (* WorkOutFollow - *) PROCEDURE WorkOutFollowFactor (f: FactorDesc; VAR followset: SetDesc; after: SetDesc) ; VAR foundepsilon, canreachend : TraverseResult ; BEGIN foundepsilon := true ; canreachend := true ; WHILE (f#NIL) AND (foundepsilon=true) DO WITH f^ DO CASE type OF id : IF ident^.definition=NIL THEN WarnError1("no rule found for an 'ident' called '%s'", ident^.name) ; HALT END ; OrSet(followset, ident^.definition^.first) | lit : AddSet(followset, GetSymKey(Aliases, string)) | sub : WorkOutFollowExpression(expr, followset, NIL) | opt : WorkOutFollowExpression(expr, followset, NIL) | mult: WorkOutFollowExpression(expr, followset, NIL) | m2 : ELSE END END ; IF GetEpsilon(f^.followinfo)=unknown THEN WarnError('internal error: epsilon unknown') ; PrettyCommentFactor(f, 3) ; WasNoError := FALSE END ; foundepsilon := GetEpsilon(f^.followinfo) ; canreachend := GetReachEnd(f^.followinfo) ; (* only goes from FALSE -> TRUE *) f := f^.next END ; IF canreachend=true THEN OrSet(followset, after) END END WorkOutFollowFactor ; (* WorkOutFollowTerm - *) PROCEDURE WorkOutFollowTerm (t: TermDesc; VAR followset: SetDesc; after: SetDesc) ; BEGIN IF t#NIL THEN WHILE t#NIL DO WITH t^ DO WorkOutFollowFactor(factor, followset, after) ; (* { '|' Term } *) END ; t := t^.next END END END WorkOutFollowTerm ; (* WorkOutFollowExpression - *) PROCEDURE WorkOutFollowExpression (e: ExpressionDesc; VAR followset: SetDesc; after: SetDesc) ; BEGIN IF e#NIL THEN WITH e^ DO WorkOutFollowTerm(term, followset, after) END END END WorkOutFollowExpression ; (* CollectFollow - collects the follow set from, f, into, to. *) PROCEDURE CollectFollow (VAR to: SetDesc; f: FollowDesc) ; BEGIN OrSet(to, f^.follow) END CollectFollow ; (* CalcFollowFactor - *) PROCEDURE CalcFollowFactor (f: FactorDesc; after: SetDesc) ; BEGIN WHILE f#NIL DO WITH f^ DO CASE type OF id : WorkOutFollowFactor(next, followinfo^.follow, after) | lit : WorkOutFollowFactor(next, followinfo^.follow, after) | opt , sub : CalcFirstFactor(next, NIL, followinfo^.follow) ; IF (next=NIL) OR (GetReachEnd(next^.followinfo)=true) THEN OrSet(followinfo^.follow, after) ; CalcFollowExpression(expr, followinfo^.follow) ELSE CalcFollowExpression(expr, followinfo^.follow) END | mult: CalcFirstFactor(f, NIL, followinfo^.follow) ; (* include first as we may repeat this sentance *) IF Debugging THEN WriteLn ; WriteString('found mult: and first is: ') ; EmitSet(followinfo^.follow, 0, 0) ; WriteLn END ; IF (next=NIL) OR (GetReachEnd(next^.followinfo)=true) THEN OrSet(followinfo^.follow, after) ; CalcFollowExpression(expr, followinfo^.follow) ELSE CalcFollowExpression(expr, followinfo^.follow) END ELSE END END ; f := f^.next END END CalcFollowFactor ; (* CalcFollowTerm - *) PROCEDURE CalcFollowTerm (t: TermDesc; after: SetDesc) ; BEGIN IF t#NIL THEN WHILE t#NIL DO WITH t^ DO CalcFollowFactor(factor, after) ; (* { '|' Term } *) END ; t := t^.next END END END CalcFollowTerm ; (* CalcFollowExpression - *) PROCEDURE CalcFollowExpression (e: ExpressionDesc; after: SetDesc) ; BEGIN IF e#NIL THEN WITH e^ DO CalcFollowTerm(term, after) END END END CalcFollowExpression ; (* CalcFollowStatement - given a bnf statement generate the follow set. *) PROCEDURE CalcFollowStatement (s: StatementDesc) ; BEGIN IF s#NIL THEN WITH s^ DO CalcFollowExpression(expr, NIL) END END END CalcFollowStatement ; (* CalcFollowProduction - *) PROCEDURE CalcFollowProduction (p: ProductionDesc) ; BEGIN IF p#NIL THEN WITH p^ DO CalcFollowStatement(statement) END END END CalcFollowProduction ; (* CalcEpsilonFactor - *) PROCEDURE CalcEpsilonFactor (f: FactorDesc) ; BEGIN WHILE f#NIL DO WITH f^ DO CASE type OF id : AssignEpsilon(GetEpsilon(ident^.definition^.followinfo)#unknown, followinfo, GetEpsilon(ident^.definition^.followinfo)) | lit : AssignEpsilon(TRUE, followinfo, false) | sub : CalcEpsilonExpression(expr) ; AssignEpsilon(GetEpsilon(expr^.followinfo)#unknown, followinfo, GetEpsilon(expr^.followinfo)) | m2 : AssignEpsilon(TRUE, followinfo, true) | opt , mult: CalcEpsilonExpression(expr) ; AssignEpsilon(TRUE, followinfo, true) ELSE END END ; f := f^.next END END CalcEpsilonFactor ; (* CalcEpsilonTerm - *) PROCEDURE CalcEpsilonTerm (t: TermDesc) ; BEGIN IF t#NIL THEN WHILE t#NIL DO WITH t^ DO IF factor#NIL THEN CASE GetReachEnd(factor^.followinfo) OF true : AssignEpsilon(TRUE, followinfo, true) | false: AssignEpsilon(TRUE, followinfo, false) | unknown: ELSE END END ; CalcEpsilonFactor(factor) (* { '|' Term } *) END ; t := t^.next END END END CalcEpsilonTerm ; (* CalcEpsilonExpression - *) PROCEDURE CalcEpsilonExpression (e: ExpressionDesc) ; VAR t : TermDesc ; result: TraverseResult ; BEGIN IF e#NIL THEN CalcEpsilonTerm(e^.term) ; IF GetEpsilon(e^.followinfo)=unknown THEN result := unknown ; WITH e^ DO t := term ; WHILE t#NIL DO IF GetEpsilon(t^.followinfo)#unknown THEN stop END ; CASE GetEpsilon(t^.followinfo) OF unknown: | true : result := true | false : IF result#true THEN result := false END ELSE END ; t := t^.next END END ; AssignEpsilon(result#unknown, e^.followinfo, result) END END END CalcEpsilonExpression ; (* CalcEpsilonStatement - given a bnf statement generate the follow set. *) PROCEDURE CalcEpsilonStatement (s: StatementDesc) ; BEGIN IF s#NIL THEN WITH s^ DO IF expr#NIL THEN AssignEpsilon(GetEpsilon(expr^.followinfo)#unknown, followinfo, GetEpsilon(expr^.followinfo)) END ; CalcEpsilonExpression(expr) END END END CalcEpsilonStatement ; (* CalcEpsilonProduction - *) PROCEDURE CalcEpsilonProduction (p: ProductionDesc) ; BEGIN IF p#NIL THEN (* IF p^.statement^.ident^.name=MakeKey('DefinitionModule') THEN stop END ; *) IF Debugging THEN WriteKey(p^.statement^.ident^.name) ; WriteString(' calculating epsilon') ; WriteLn END ; WITH p^ DO AssignEpsilon(GetEpsilon(statement^.followinfo)#unknown, followinfo, GetEpsilon(statement^.followinfo)) ; CalcEpsilonStatement(statement) END END END CalcEpsilonProduction ; (* CalcReachEndFactor - *) PROCEDURE CalcReachEndFactor (f: FactorDesc) : TraverseResult ; VAR canreachend, result : TraverseResult ; BEGIN IF f=NIL THEN RETURN( true ) (* we have reached the end of this factor list *) ELSE WITH f^ DO (* we need to traverse all factors even if we can short cut the answer to this list of factors *) result := CalcReachEndFactor(next) ; CASE type OF id : IF ident^.definition=NIL THEN WarnError1('definition for %s is absent (assuming epsilon is false for this production)', ident^.name) ; result := false ELSIF result#false THEN CASE GetReachEnd(ident^.definition^.followinfo) OF false : result := false | true : | unknown: result := unknown ELSE END END | lit : result := false | sub : CalcReachEndExpression(expr) ; IF (expr#NIL) AND (result=true) THEN result := GetReachEnd(expr^.followinfo) END | mult, opt : IF expr#NIL THEN (* not interested in the result as expression is optional *) CalcReachEndExpression(expr) END | m2 : ELSE END ; AssignReachEnd(result#unknown, followinfo, result) END ; RETURN( result ) END END CalcReachEndFactor ; (* CalcReachEndTerm - *) PROCEDURE CalcReachEndTerm (t: TermDesc) : TraverseResult ; VAR canreachend, result : TraverseResult ; BEGIN IF t#NIL THEN canreachend := false ; WHILE t#NIL DO WITH t^ DO result := CalcReachEndFactor(factor) ; AssignReachEnd(result#unknown, followinfo, result) ; CASE result OF true : canreachend := true | false : | unknown: IF canreachend=false THEN canreachend := unknown END ELSE END END ; t := t^.next (* { '|' Term } *) END ; RETURN( canreachend ) END END CalcReachEndTerm ; (* CalcReachEndExpression - *) PROCEDURE CalcReachEndExpression (e: ExpressionDesc) ; VAR result: TraverseResult ; BEGIN IF e=NIL THEN (* no expression, thus reached the end of this sentance *) ELSE WITH e^ DO result := CalcReachEndTerm(term) ; AssignReachEnd(result#unknown, followinfo, result) END END END CalcReachEndExpression ; (* CalcReachEndStatement - *) PROCEDURE CalcReachEndStatement (s: StatementDesc) ; BEGIN IF s#NIL THEN WITH s^ DO IF expr#NIL THEN CalcReachEndExpression(expr) ; AssignReachEnd(GetReachEnd(expr^.followinfo)#unknown, followinfo, GetReachEnd(expr^.followinfo)) END END END END CalcReachEndStatement ; PROCEDURE stop ; BEGIN END stop ; (* CalcReachEndProduction - *) PROCEDURE CalcReachEndProduction (p: ProductionDesc) ; BEGIN IF p#NIL THEN WITH p^ DO CalcReachEndStatement(statement) ; IF GetReachEnd(followinfo)#unknown THEN IF Debugging THEN WriteString('already calculated reach end for: ') ; WriteKey(p^.statement^.ident^.name) ; WriteString(' its value is ') ; IF GetReachEnd(followinfo)=true THEN WriteString('reachable') ELSE WriteString('non reachable') END ; WriteLn END END ; AssignReachEnd(GetReachEnd(statement^.followinfo)#unknown, followinfo, GetReachEnd(statement^.followinfo)) ; END END END CalcReachEndProduction ; (* EmptyFactor - *) PROCEDURE EmptyFactor (f: FactorDesc) : BOOLEAN ; BEGIN WHILE f#NIL DO WITH f^ DO CASE type OF id : IF NOT EmptyProduction(ident^.definition) THEN RETURN( FALSE ) END | lit : RETURN( FALSE ) | sub : IF NOT EmptyExpression(expr) THEN RETURN( FALSE ) END | opt , mult: RETURN( TRUE ) | m2 : ELSE END END ; f := f^.next END ; RETURN( TRUE ) END EmptyFactor ; (* EmptyTerm - returns TRUE if the term maybe empty. *) PROCEDURE EmptyTerm (t: TermDesc) : BOOLEAN ; BEGIN WHILE t#NIL DO IF EmptyFactor(t^.factor) THEN RETURN( TRUE ) ELSE t := t^.next END END ; RETURN( FALSE ) END EmptyTerm ; (* EmptyExpression - *) PROCEDURE EmptyExpression (e: ExpressionDesc) : BOOLEAN ; BEGIN IF e=NIL THEN RETURN( TRUE ) ELSE RETURN( EmptyTerm(e^.term) ) END END EmptyExpression ; (* EmptyStatement - returns TRUE if statement, s, is empty. *) PROCEDURE EmptyStatement (s: StatementDesc) : BOOLEAN ; BEGIN IF s=NIL THEN RETURN( TRUE ) ELSE RETURN( EmptyExpression(s^.expr) ) END END EmptyStatement ; (* EmptyProduction - returns if production, p, maybe empty. *) PROCEDURE EmptyProduction (p: ProductionDesc) : BOOLEAN ; BEGIN IF p=NIL THEN WarnError('unknown production') ; RETURN( TRUE ) ELSIF (p^.firstsolved) AND (p^.first#NIL) THEN (* predefined but first set to something - thus not empty *) RETURN( FALSE ) ELSE RETURN( EmptyStatement(p^.statement) ) END END EmptyProduction ; (* EmitFDLNotice - *) PROCEDURE EmitFDLNotice ; BEGIN Output.WriteString('@c Copyright (C) 2000-2023 Free Software Foundation, Inc.') ; Output.WriteLn ; Output.WriteLn ; Output.WriteString('@c This file is part of GCC.') ; Output.WriteLn ; Output.WriteString('@c Permission is granted to copy, distribute and/or modify this document') ; Output.WriteLn ; Output.WriteString('@c under the terms of the GNU Free Documentation License, Version 1.2 or') ; Output.WriteLn ; Output.WriteString('@c any later version published by the Free Software Foundation.') ; Output.WriteLn END EmitFDLNotice ; (* EmitRules - generates the BNF rules. *) PROCEDURE EmitRules ; BEGIN IF Texinfo AND FreeDocLicense THEN EmitFDLNotice END ; ForeachRuleDo(EmitRule) END EmitRules ; (* DescribeElement - *) PROCEDURE DescribeElement (name: WORD) ; VAR lit: Name ; BEGIN IF InitialElement THEN InitialElement := FALSE ELSE Output.WriteString(' |') END ; Output.WriteLn ; Indent := 3 ; IndentString('') ; Output.WriteKey(name) ; Output.WriteString(': ') ; lit := GetSymKey(ReverseAliases, name) ; IF MakeKey('"')=lit THEN Output.WriteString('str := ConCat(ConCatChar(ConCatChar(InitString("syntax error, found ') ; Output.Write("'") ; Output.WriteString('"), ') ; Output.Write("'") ; Output.Write('"') ; Output.Write("'") ; Output.WriteString("), ") ; Output.Write('"') ; Output.Write("'") ; Output.Write('"') ; Output.WriteString("), Mark(str))") ELSIF MakeKey("'")=lit THEN Output.WriteString("str := ConCat(ConCatChar(ConCatChar(InitString('syntax error, found ") ; Output.Write('"') ; Output.WriteString("'), ") ; Output.Write('"') ; Output.Write("'") ; Output.Write('"') ; Output.WriteString('), ') ; Output.Write("'") ; Output.Write('"') ; Output.Write("'") ; Output.WriteString('), Mark(str))') ELSE Output.WriteString("str := ConCat(InitString(") ; Output.Write('"') ; Output.WriteString("syntax error, found ") ; KeyWord(lit) ; Output.WriteString('"), Mark(str))') END END DescribeElement ; (* EmitInTestStop - construct a test for stop element, name. *) PROCEDURE EmitInTestStop (name: Name) ; VAR i, value: CARDINAL ; BEGIN IF LargestValue<=MaxElementsInSet THEN Output.WriteKey(name) ; Output.WriteString(' IN stopset') ; INCL (ParametersUsed, 0) ELSE value := GetSymKey(Values, name) ; i := value DIV MaxElementsInSet ; Output.WriteKey(name) ; Output.WriteString(' IN stopset') ; Output.WriteCard(i, 0) ; INCL (ParametersUsed, i) END END EmitInTestStop ; (* DescribeStopElement - *) PROCEDURE DescribeStopElement (name: WORD) ; VAR lit: Name ; BEGIN Indent := 3 ; IndentString('IF ') ; EmitInTestStop(name) ; Output.WriteLn ; IndentString('THEN') ; Output.WriteLn ; Indent := 6 ; lit := GetSymKey(ReverseAliases, name) ; IF (lit=NulName) OR (lit=MakeKey('')) THEN IndentString('(* ') ; Output.WriteKey(name) ; Output.WriteString(' has no token name (needed to generate error messages) *)') ELSIF MakeKey("'")=lit THEN IndentString('message := ConCatChar(ConCatChar(ConCatChar(ConCatChar(ConCatChar(message, ') ; Output.WriteString("' '), ") ; Output.Write("'") ; Output.Write('"') ; Output.WriteString("'), ") ; Output.Write('"') ; Output.Write("'") ; Output.WriteString('"), ') ; Output.Write("'") ; Output.Write('"') ; Output.WriteString("'), ',') ; INC(n) ; ") ELSIF MakeKey('"')=lit THEN IndentString("message := ConCatChar(ConCatChar(ConCatChar(ConCatChar(ConCatChar(message, ") ; Output.WriteString('" "), ') ; Output.Write('"') ; Output.Write("`") ; Output.WriteString('"), ') ; Output.Write("'") ; Output.Write('"') ; Output.WriteString("'), ") ; Output.Write('"') ; Output.Write("'") ; Output.WriteString('"), ",") ; INC(n) ; ') ELSE IndentString("message := ConCat(ConCatChar(message, ' ") ; Output.WriteString("'), ") ; Output.WriteString('Mark(InitString("') ; KeyWord(lit) ; Output.Write('"') ; Output.WriteString('))) ; INC(n)') END ; Output.WriteLn ; Indent := 3 ; IndentString('END ;') ; Output.WriteLn END DescribeStopElement ; (* EmitDescribeStop - *) PROCEDURE EmitDescribeStop ; VAR s: String ; BEGIN Output.WriteLn ; Indent := 0 ; IndentString('(*') ; Indent := 3 ; Output.WriteLn ; IndentString('DescribeStop - issues a message explaining what tokens were expected') ; Output.WriteLn ; Output.WriteString('*)') ; Output.WriteLn ; Output.WriteLn ; Indent := 0 ; IndentString('PROCEDURE DescribeStop (') ; ParametersUsed := {} ; Output.StartBuffer ; Output.WriteString(') : String ;') ; Output.WriteLn ; IndentString('VAR') ; Output.WriteLn ; Indent := 3 ; IndentString('n : CARDINAL ;') ; Output.WriteLn ; IndentString('str,') ; Output.WriteLn ; IndentString('message: String ;') ; Output.WriteLn ; Indent := 0 ; IndentString('BEGIN') ; Output.WriteLn ; Indent := 3 ; IndentString('n := 0 ;') ; Output.WriteLn ; IndentString("message := InitString('') ;") ; Output.WriteLn ; ForeachNodeDo(Aliases, DescribeStopElement) ; Output.WriteLn ; Indent := 3 ; IndentString('IF n=0') ; Output.WriteLn ; IndentString('THEN') ; Output.WriteLn ; Indent := 6 ; IndentString("str := InitString(' syntax error') ; ") ; Output.WriteLn ; IndentString('message := KillString(message) ; ') ; Output.WriteLn ; Indent := 3 ; IndentString('ELSIF n=1') ; Output.WriteLn ; IndentString('THEN') ; Output.WriteLn ; Indent := 6 ; IndentString("str := ConCat(message, Mark(InitString(' missing '))) ;") ; Output.WriteLn ; Indent := 3 ; IndentString('ELSE') ; Output.WriteLn ; Indent := 6 ; IndentString("str := ConCat(InitString(' expecting one of'), message) ;") ; Output.WriteLn ; IndentString("message := KillString(message) ;") ; Output.WriteLn ; Indent := 3 ; IndentString('END ;') ; Output.WriteLn ; IndentString('RETURN( str )') ; Output.WriteLn ; Indent := 0 ; IndentString('END DescribeStop ;') ; Output.WriteLn ; Output.WriteLn ; s := Output.EndBuffer () ; EmitStopParameters(TRUE) ; Output.KillWriteS (s) END EmitDescribeStop ; (* EmitDescribeError - *) PROCEDURE EmitDescribeError ; BEGIN Output.WriteLn ; Indent := 0 ; IndentString('(*') ; Output.WriteLn ; Indent := 3 ; IndentString('DescribeError - issues a message explaining what tokens were expected') ; Output.WriteLn ; Indent := 0 ; IndentString('*)') ; Output.WriteLn ; Output.WriteLn ; IndentString('PROCEDURE DescribeError ;') ; Output.WriteLn ; IndentString('VAR') ; Output.WriteLn ; Indent := 3 ; IndentString('str: String ;') ; Output.WriteLn ; Indent := 0 ; IndentString('BEGIN') ; Output.WriteLn ; Indent := 3 ; IndentString("str := InitString('') ;") ; Output.WriteLn ; (* was IndentString('str := DescribeStop(') ; EmitStopParameters(FALSE) ; Output.WriteString(') ;') ; Output.WriteLn ; *) IndentString('CASE ') ; WriteGetTokenType ; Output.WriteString(' OF') ; NewLine(3) ; InitialElement := TRUE ; ForeachNodeDo(Aliases, DescribeElement) ; Output.WriteLn ; Indent := 3 ; IndentString('ELSE') ; Output.WriteLn ; IndentString('END ;') ; Output.WriteLn ; IndentString('') ; Output.WriteKey(ErrorProcString) ; Output.WriteString('(str) ;') ; Output.WriteLn ; Indent := 0 ; IndentString('END DescribeError ;') ; Output.WriteLn END EmitDescribeError ; (* EmitSetTypes - write out the set types used during error recovery *) PROCEDURE EmitSetTypes ; VAR i, j, m, n: CARDINAL ; BEGIN Output.WriteString('(*') ; NewLine(3) ; Output.WriteString('expecting token set defined as an enumerated type') ; NewLine(3) ; Output.WriteString('(') ; i := 0 ; WHILE iMaxElementsInSet THEN i := 0 ; n := LargestValue DIV MaxElementsInSet ; WHILE i<=n DO j := (i*MaxElementsInSet) ; IF LargestValue<(i+1)*MaxElementsInSet-1 THEN m := LargestValue-1 ELSE m := (i+1)*MaxElementsInSet-1 END ; Output.WriteString('stop') ; Output.WriteCard(i, 0) ; Output.WriteString(' = [') ; Output.WriteKey(GetSymKey(ReverseValues, WORD(j))) ; Output.WriteString('..') ; Output.WriteKey(GetSymKey(ReverseValues, WORD(m))) ; Output.WriteString('] ;') ; NewLine(3) ; Output.WriteString('SetOfStop') ; Output.WriteCard(i, 0) ; Output.WriteString(' = SET OF stop') ; Output.WriteCard(i, 0) ; Output.WriteString(' ;') ; NewLine(3) ; INC(i) END ELSE Output.WriteString('SetOfStop') ; Output.WriteString(' = SET OF [') ; Output.WriteKey(GetSymKey(ReverseValues, WORD(0))) ; Output.WriteString('..') ; Output.WriteKey(GetSymKey(ReverseValues, WORD(LargestValue-1))) ; Output.WriteString('] ;') END ; NewLine(0) END EmitSetTypes ; (* EmitSupport - generates the support routines. *) PROCEDURE EmitSupport ; BEGIN IF ErrorRecovery THEN EmitSetTypes ; EmitDescribeStop ; EmitDescribeError END END EmitSupport ; (* DisposeSetDesc - dispose of the set list, s. *) PROCEDURE DisposeSetDesc (VAR s: SetDesc) ; VAR h, n: SetDesc ; BEGIN IF s#NIL THEN h := s ; n := s^.next ; REPEAT DISPOSE(h) ; h := n ; IF n#NIL THEN n := n^.next END UNTIL h=NIL ; s := NIL END END DisposeSetDesc ; (* OptionalFactor - *) PROCEDURE OptionalFactor (f: FactorDesc) : BOOLEAN ; BEGIN WHILE f#NIL DO WITH f^ DO CASE type OF id : | lit : | sub , opt , mult: IF OptionalExpression(expr) THEN RETURN( TRUE ) END | m2 : ELSE END END ; f := f^.next END ; RETURN( FALSE ) END OptionalFactor ; (* OptionalTerm - returns TRUE if the term maybe empty. *) PROCEDURE OptionalTerm (t: TermDesc) : BOOLEAN ; VAR u, v : TermDesc ; tov, tou: SetDesc ; BEGIN u := t ; WHILE u#NIL DO IF OptionalFactor(u^.factor) THEN RETURN( TRUE ) END ; v := t ; tou := NIL ; CalcFirstFactor(u^.factor, NIL, tou) ; WHILE v#NIL DO IF v#u THEN tov := NIL ; CalcFirstFactor(v^.factor, NIL, tov) ; IF IntersectionIsNil(tov, tou) THEN DisposeSetDesc(tov) ; ELSE WriteString('problem with two first sets. Set 1: ') ; EmitSet(tou, 0, 0) ; WriteLn ; WriteString(' Set 2: ') ; EmitSet(tov, 0, 0) ; WriteLn ; DisposeSetDesc(tou) ; DisposeSetDesc(tov) ; RETURN( TRUE ) END END ; v := v^.next END ; DisposeSetDesc(tou) ; u := u^.next END ; RETURN( FALSE ) END OptionalTerm ; (* OptionalExpression - *) PROCEDURE OptionalExpression (e: ExpressionDesc) : BOOLEAN ; BEGIN IF e=NIL THEN RETURN( FALSE ) ELSE RETURN( OptionalTerm(e^.term) ) END END OptionalExpression ; (* OptionalStatement - returns FALSE if statement, s, does not have a optional ambiguity. *) PROCEDURE OptionalStatement (s: StatementDesc) : BOOLEAN ; BEGIN IF s=NIL THEN RETURN( FALSE ) ELSE RETURN( OptionalExpression(s^.expr) ) END END OptionalStatement ; (* OptionalProduction - *) PROCEDURE OptionalProduction (p: ProductionDesc) : BOOLEAN ; BEGIN IF p=NIL THEN RETURN( FALSE ) ELSE RETURN( OptionalStatement(p^.statement) ) END END OptionalProduction ; (* CheckFirstFollow - *) PROCEDURE CheckFirstFollow (f: FactorDesc; after: FactorDesc) : BOOLEAN ; VAR first, follow: SetDesc ; BEGIN first := NIL ; CalcFirstFactor(f, NIL, first) ; follow := NIL ; follow := GetFollow(f^.followinfo) ; IF IntersectionIsNil(first, follow) THEN DisposeSetDesc(first) ; DisposeSetDesc(follow) ; RETURN( FALSE ) ELSE PrettyCommentFactor(f, 3) ; NewLine(3) ; WriteString('first: ') ; EmitSet(first, 0, 0) ; NewLine(3) ; WriteString('follow: ') ; EmitSet(follow, 0, 0) ; NewLine(3) ; DisposeSetDesc(first) ; DisposeSetDesc(follow) ; RETURN( TRUE ) END END CheckFirstFollow ; (* ConstrainedEmptyFactor - *) PROCEDURE ConstrainedEmptyFactor (f: FactorDesc) : BOOLEAN ; BEGIN WHILE f#NIL DO WITH f^ DO CASE type OF id : | lit : | sub , opt , mult: IF ConstrainedEmptyExpression(expr) THEN RETURN( TRUE ) END | m2 : ELSE END END ; IF (f^.type#m2) AND EmptyFactor(f) AND CheckFirstFollow(f, f^.next) THEN RETURN( TRUE ) END ; f := f^.next END ; RETURN( FALSE ) END ConstrainedEmptyFactor ; (* ConstrainedEmptyTerm - returns TRUE if the term maybe empty. *) PROCEDURE ConstrainedEmptyTerm (t: TermDesc) : BOOLEAN ; VAR first, follow: SetDesc ; BEGIN WHILE t#NIL DO IF ConstrainedEmptyFactor(t^.factor) THEN RETURN( TRUE ) ELSIF (t^.factor^.type#m2) AND EmptyFactor(t^.factor) AND CheckFirstFollow(t^.factor, t^.factor^.next) THEN RETURN( TRUE ) END ; t := t^.next END ; RETURN( FALSE ) END ConstrainedEmptyTerm ; (* ConstrainedEmptyExpression - *) PROCEDURE ConstrainedEmptyExpression (e: ExpressionDesc) : BOOLEAN ; BEGIN IF e=NIL THEN RETURN( FALSE ) ELSE RETURN( ConstrainedEmptyTerm(e^.term) ) END END ConstrainedEmptyExpression ; (* ConstrainedEmptyStatement - returns FALSE if statement, s, does not have a optional ambiguity. *) PROCEDURE ConstrainedEmptyStatement (s: StatementDesc) : BOOLEAN ; BEGIN IF s=NIL THEN RETURN( FALSE ) ELSE RETURN( ConstrainedEmptyExpression(s^.expr) ) END END ConstrainedEmptyStatement ; (* ConstrainedEmptyProduction - returns TRUE if a problem exists with, p. *) PROCEDURE ConstrainedEmptyProduction (p: ProductionDesc) : BOOLEAN ; BEGIN IF p=NIL THEN RETURN( FALSE ) ELSE RETURN( ConstrainedEmptyStatement(p^.statement) ) END END ConstrainedEmptyProduction ; (* TestForLALR1 - *) PROCEDURE TestForLALR1 (p: ProductionDesc) ; BEGIN IF OptionalProduction(p) THEN WarnError1('production %s has two optional sentances using | which both have the same start symbols', p^.statement^.ident^.name) ; WasNoError := FALSE ; PrettyCommentProduction(p) END ; (* IF ConstrainedEmptyProduction(p) THEN WarnError1('production %s has an empty sentance and the first and follow symbols intersect', p^.statement^.ident^.name) ; WasNoError := FALSE END *) END TestForLALR1 ; (* DoEpsilon - runs the epsilon interrelated rules *) PROCEDURE DoEpsilon (p: ProductionDesc) ; BEGIN CalcEpsilonProduction(p) ; CalcReachEndProduction(p) END DoEpsilon ; (* CheckComplete - checks that production, p, is complete. *) PROCEDURE CheckComplete (p: ProductionDesc) ; BEGIN IF GetReachEnd(p^.followinfo)=unknown THEN PrettyCommentProduction(p) ; WarnError1('cannot determine epsilon, probably a left recursive rule in %s and associated rules (hint rewrite using ebnf and eliminate left recursion)', p^.statement^.ident^.name) ; WasNoError := FALSE END END CheckComplete ; (* PostProcessRules - backpatch the ident to rule definitions and emit comments and code. *) PROCEDURE PostProcessRules ; BEGIN ForeachRuleDo(BackPatchIdentToDefinitions) ; IF NOT WasNoError THEN HALT END ; WhileNotCompleteDo(DoEpsilon) ; IF NOT WasNoError THEN HALT END ; ForeachRuleDo(CheckComplete) ; IF NOT WasNoError THEN HALT END ; WhileNotCompleteDo(CalculateFirstAndFollow) ; IF NOT WasNoError THEN HALT END ; ForeachRuleDo(TestForLALR1) ; IF NOT WasNoError THEN ForeachRuleDo(PrettyCommentProduction) END END PostProcessRules ; (* DisplayHelp - display a summary help and then exit (0). *) PROCEDURE DisplayHelp ; BEGIN WriteString('Usage: ppg [-l] [-c] [-d] [-e] [-k] [-t] [-k] [-p] [-x] [-f] [-o outputfile] filename') ; WriteLn ; WriteString(' -l suppress file and line source information') ; WriteLn ; WriteString(' -c do not generate any Modula-2 code within the parser rules') ; WriteLn ; WriteString(' -h or --help generate this help message') ; WriteLn ; WriteString(' -e do not generate a parser with error recovery') ; WriteLn ; WriteString(' -k generate keyword errors with GCC formatting directives') ; WriteLn ; WriteString(' -d generate internal debugging information') ; WriteLn ; WriteString(' -p only display the ebnf rules') ; WriteLn ; WriteString(' -t generate texinfo formating for pretty printing (-p)') ; WriteLn ; WriteString(' -x generate sphinx formating for pretty printing (-p)') ; WriteLn ; WriteString(' -f generate GNU Free Documentation header before pretty printing in texinfo') ; WriteLn ; WriteString(' -o write output to filename') ; WriteLn ; exit (0) END DisplayHelp ; (* ParseArgs - *) PROCEDURE ParseArgs ; VAR n, i: CARDINAL ; BEGIN ErrorRecovery := TRUE ; (* DefaultRecovery ; *) Debugging := FALSE ; PrettyPrint := FALSE ; KeywordFormatting := FALSE ; i := 1 ; n := Narg() ; WHILE i