| (* ppg.mod master source file of the ebnf parser generator. |
| |
| Copyright (C) 2003-2025 Free Software Foundation, Inc. |
| Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>. |
| |
| 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 |
| <http://www.gnu.org/licenses/>. *) |
| |
| 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 (i<HighSrc) AND (src[i]#nul) AND (j<HighDest) AND (src[i]#'%') DO |
| dest[j] := src[i] ; |
| INC(i) ; |
| INC(j) |
| END ; |
| IF (i+1<HighSrc) AND (src[i]='%') AND (j<HighDest) |
| THEN |
| IF src[i+1]='s' |
| THEN |
| dest[j] := nul ; |
| GetKey(n, str) ; |
| StrConCat(dest, str, dest) ; |
| j := StrLen(dest) ; |
| INC(i, 2) |
| ELSIF src[i+1]='d' |
| THEN |
| dest[j] := nul ; |
| CardToStr(n, 0, str) ; |
| StrConCat(dest, str, dest) ; |
| j := StrLen(dest) ; |
| INC(i, 2) |
| ELSE |
| dest[j] := src[i] ; |
| INC(i) ; |
| INC(j) |
| END |
| END ; |
| (* and finish off copying src into dest *) |
| WHILE (i<HighSrc) AND (src[i]#nul) AND (j<HighDest) DO |
| dest[j] := src[i] ; |
| INC(i) ; |
| INC(j) |
| END ; |
| IF j<HighDest |
| THEN |
| dest[j] := nul |
| END ; |
| END Format1 ; |
| |
| |
| (* |
| WarnError1 - |
| *) |
| |
| PROCEDURE WarnError1 (a: ARRAY OF CHAR; n: WORD) ; |
| VAR |
| line: ARRAY [0..MaxString] OF CHAR ; |
| BEGIN |
| Format1(a, n, line) ; |
| WarnError(line) |
| END WarnError1 ; |
| |
| |
| (* |
| PrettyFollow - |
| *) |
| |
| PROCEDURE PrettyFollow (start, end: ARRAY OF CHAR; f: FollowDesc) ; |
| BEGIN |
| IF Debugging |
| THEN |
| Output.WriteString(start) ; |
| IF f#NIL |
| THEN |
| WITH f^ DO |
| IF calcfollow |
| THEN |
| Output.WriteString('followset defined as:') ; |
| EmitSet(follow, 0, 0) |
| END ; |
| CASE reachend OF |
| |
| true : Output.WriteString(' [E]') | |
| false: Output.WriteString(' [C]') | |
| unknown: Output.WriteString(' [U]') |
| |
| ELSE |
| END ; |
| CASE epsilon OF |
| |
| true : Output.WriteString(' [e]') | |
| false : | |
| unknown: Output.WriteString(' [u]') |
| |
| ELSE |
| END |
| END |
| END ; |
| Output.WriteString(end) |
| END |
| END PrettyFollow ; |
| |
| |
| (* |
| NewFollow - creates a new follow descriptor and returns the data structure. |
| *) |
| |
| PROCEDURE NewFollow () : FollowDesc ; |
| VAR |
| f: FollowDesc ; |
| BEGIN |
| NEW(f) ; |
| WITH f^ DO |
| follow := NIL ; |
| reachend := unknown ; |
| epsilon := unknown ; |
| END ; |
| RETURN( f ) |
| END NewFollow ; |
| |
| |
| (* |
| AssignEpsilon - assigns the epsilon value and sets the epsilon to value, |
| providing condition is TRUE. |
| *) |
| |
| PROCEDURE AssignEpsilon (condition: BOOLEAN; f: FollowDesc; value: TraverseResult) ; |
| BEGIN |
| WITH f^ DO |
| IF condition AND (value#unknown) AND (epsilon=unknown) |
| THEN |
| epsilon := value ; |
| Finished := FALSE |
| END |
| END |
| END AssignEpsilon ; |
| |
| |
| (* |
| GetEpsilon - returns the value of epsilon |
| *) |
| |
| PROCEDURE GetEpsilon (f: FollowDesc) : TraverseResult ; |
| BEGIN |
| IF f=NIL |
| THEN |
| Halt('why is the follow info NIL?', |
| __FILE__, __FUNCTION__, __LINE__) |
| ELSE |
| RETURN( f^.epsilon ) |
| END |
| END GetEpsilon ; |
| |
| |
| (* |
| AssignReachEnd - assigns the reachend value providing that, condition, is TRUE. |
| *) |
| |
| PROCEDURE AssignReachEnd (condition: BOOLEAN; f: FollowDesc; value: TraverseResult) ; |
| BEGIN |
| IF condition |
| THEN |
| WITH f^ DO |
| IF (reachend=unknown) AND (value#unknown) |
| THEN |
| reachend := value ; |
| Finished := FALSE |
| END |
| END |
| END |
| END AssignReachEnd ; |
| |
| |
| (* |
| GetReachEnd - returns the value of reachend |
| *) |
| |
| PROCEDURE GetReachEnd (f: FollowDesc) : TraverseResult ; |
| BEGIN |
| IF f=NIL |
| THEN |
| Halt('why is the follow info NIL?', |
| __FILE__, __FUNCTION__, __LINE__) |
| ELSE |
| RETURN( f^.reachend ) |
| END |
| END GetReachEnd ; |
| |
| |
| (* |
| AssignFollow - assigns the follow set and sets the calcfollow to TRUE. |
| *) |
| |
| PROCEDURE AssignFollow (f: FollowDesc; s: SetDesc) ; |
| BEGIN |
| WITH f^ DO |
| IF calcfollow |
| THEN |
| Halt('why are we reassigning this follow set?', |
| __FILE__, __FUNCTION__, __LINE__) |
| END ; |
| follow := s ; |
| calcfollow := TRUE |
| END |
| END AssignFollow ; |
| |
| |
| (* |
| GetFollow - returns the follow set. |
| *) |
| |
| PROCEDURE GetFollow (f: FollowDesc) : SetDesc ; |
| BEGIN |
| IF f=NIL |
| THEN |
| Halt ('why is the follow info NIL?', |
| __FILE__, __FUNCTION__, __LINE__) |
| ELSE |
| WITH f^ DO |
| IF calcfollow |
| THEN |
| RETURN( follow ) |
| ELSE |
| Halt('not calculated the follow set yet..', |
| __FILE__, __FUNCTION__, __LINE__) |
| END |
| END |
| END |
| END GetFollow ; |
| |
| |
| (* |
| NewProduction - creates a new production and returns the data structure. |
| *) |
| |
| PROCEDURE NewProduction () : ProductionDesc ; |
| VAR |
| p: ProductionDesc ; |
| BEGIN |
| NEW(p) ; |
| IF TailProduction#NIL |
| THEN |
| TailProduction^.next := p |
| END ; |
| TailProduction := p ; |
| IF HeadProduction=NIL |
| THEN |
| HeadProduction := p |
| END ; |
| WITH p^ DO |
| next := NIL ; |
| statement := NIL ; |
| first := NIL ; |
| firstsolved := FALSE ; |
| followinfo := NewFollow() ; |
| line := GetCurrentLine() ; |
| description := NulName |
| END ; |
| RETURN( p ) |
| END NewProduction ; |
| |
| |
| (* |
| NewFactor - |
| *) |
| |
| PROCEDURE NewFactor () : FactorDesc ; |
| VAR |
| f: FactorDesc ; |
| BEGIN |
| NEW(f) ; |
| WITH f^ DO |
| next := NIL ; |
| followinfo := NewFollow() ; |
| line := GetCurrentLine() |
| END ; |
| RETURN( f ) |
| END NewFactor ; |
| |
| |
| (* |
| NewTerm - returns a new term. |
| *) |
| |
| PROCEDURE NewTerm () : TermDesc ; |
| VAR |
| t: TermDesc ; |
| BEGIN |
| NEW(t) ; |
| WITH t^ DO |
| factor := NIL ; |
| followinfo := NewFollow() ; |
| next := NIL ; |
| line := GetCurrentLine() |
| END ; |
| RETURN( t ) |
| END NewTerm ; |
| |
| |
| (* |
| NewExpression - returns a new expression. |
| *) |
| |
| PROCEDURE NewExpression () : ExpressionDesc ; |
| VAR |
| e: ExpressionDesc ; |
| BEGIN |
| NEW(e) ; |
| WITH e^ DO |
| term := NIL ; |
| followinfo := NewFollow() ; |
| line := GetCurrentLine() |
| END ; |
| RETURN( e ) |
| END NewExpression ; |
| |
| |
| (* |
| NewStatement - returns a new statement. |
| *) |
| |
| PROCEDURE NewStatement () : StatementDesc ; |
| VAR |
| s: StatementDesc ; |
| BEGIN |
| NEW(s) ; |
| WITH s^ DO |
| ident := NIL ; |
| expr := NIL ; |
| followinfo := NewFollow() ; |
| line := GetCurrentLine() |
| END ; |
| RETURN( s ) |
| END NewStatement ; |
| |
| |
| (* |
| NewSetDesc - creates a new set description and returns the data structure. |
| *) |
| |
| PROCEDURE NewSetDesc () : SetDesc ; |
| VAR |
| s: SetDesc ; |
| BEGIN |
| NEW(s) ; |
| WITH s^ DO |
| next := NIL |
| END ; |
| RETURN( s ) |
| END NewSetDesc ; |
| |
| |
| (* |
| NewCodeDesc - creates a new code descriptor and initializes all fields to zero. |
| *) |
| |
| PROCEDURE NewCodeDesc () : CodeDesc ; |
| VAR |
| c: CodeDesc ; |
| BEGIN |
| NEW(c) ; |
| WITH c^ DO |
| code := NIL ; |
| indent := 0 ; |
| line := GetCurrentLine() |
| END ; |
| RETURN( c ) |
| END NewCodeDesc ; |
| |
| |
| (* |
| CodeFragmentPrologue - consumes code text up to a "%" after a newline. |
| *) |
| |
| PROCEDURE CodeFragmentPrologue ; |
| BEGIN |
| LinePrologue := GetCurrentLine() ; |
| GetCodeFragment(CodePrologue) |
| END CodeFragmentPrologue ; |
| |
| |
| (* |
| CodeFragmentEpilogue - consumes code text up to a "%" after a newline. |
| *) |
| |
| PROCEDURE CodeFragmentEpilogue ; |
| BEGIN |
| LineEpilogue := GetCurrentLine() ; |
| GetCodeFragment(CodeEpilogue) |
| END CodeFragmentEpilogue ; |
| |
| |
| (* |
| CodeFragmentDeclaration - consumes code text up to a "%" after a newline. |
| *) |
| |
| PROCEDURE CodeFragmentDeclaration ; |
| BEGIN |
| LineDeclaration := GetCurrentLine() ; |
| GetCodeFragment(CodeDeclaration) |
| END CodeFragmentDeclaration ; |
| |
| |
| (* |
| GetCodeFragment - collects the code fragment up until ^ % |
| *) |
| |
| PROCEDURE GetCodeFragment (VAR h: CodeHunk) ; |
| VAR |
| i : CARDINAL ; |
| ch: CHAR ; |
| BEGIN |
| h := NIL ; |
| i := 0 ; |
| WHILE (PutChar(GetChar())#'%') AND (PutChar(GetChar())#nul) DO |
| REPEAT |
| WHILE (PutChar(GetChar())#nul) AND (PutChar(GetChar())#lf) DO |
| h := Add(h, GetChar(), i) |
| END ; |
| IF PutChar(GetChar())=lf |
| THEN |
| (* consume line feed *) |
| h := Add(h, GetChar(), i) ; |
| ch := PutChar(lf) |
| ELSIF PutChar(GetChar())=nul |
| THEN |
| ch := PutChar(nul) ; |
| ch := PutChar(lf) |
| ELSE |
| ch := PutChar(PutChar(GetChar())) |
| END |
| UNTIL GetChar()=lf |
| END ; |
| IF PutChar(GetChar())='%' |
| THEN |
| h := Add(h, nul, i) ; |
| ch := PutChar(' ') ; (* to give the following token % a delimiter infront of it *) |
| AdvanceToken |
| ELSE |
| WarnError('expecting % to terminate code fragment, found end of file') |
| END |
| END GetCodeFragment ; |
| |
| |
| (* |
| WriteCodeHunkList - writes the CodeHunk list in the correct order. |
| *) |
| |
| PROCEDURE WriteCodeHunkList (l: CodeHunk) ; |
| BEGIN |
| IF l#NIL |
| THEN |
| OnLineStart := FALSE ; |
| (* recursion *) |
| WITH l^ DO |
| WriteCodeHunkList(next) ; |
| Output.WriteString(codetext) |
| END |
| END |
| END WriteCodeHunkList ; |
| |
| |
| (* |
| WriteIndent - writes, n, spaces. |
| *) |
| |
| PROCEDURE WriteIndent (n: CARDINAL) ; |
| BEGIN |
| WHILE n>0 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<l DO |
| IF seentext |
| THEN |
| CheckWrite(a[i], curpos, left, seentext) |
| ELSE |
| IF a[i]=' ' |
| THEN |
| (* ignore space for now *) |
| INC(curpos) |
| ELSE |
| IF curpos>=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 Indent<Left DO |
| Output.Write(' ') ; |
| INC(Indent) |
| END |
| END NewLine ; |
| |
| |
| (* |
| CheckNewLine - |
| *) |
| |
| PROCEDURE CheckNewLine (Left: CARDINAL) ; |
| BEGIN |
| IF Indent=Left |
| THEN |
| Left := BaseNewLine |
| END ; |
| IF Indent>BaseRightMargin |
| 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<Indent DO |
| Output.Write(' ') ; |
| INC(i) |
| END ; |
| Output.WriteString(a) ; |
| LastLineNo := 0 |
| END IndentString ; |
| |
| |
| (* |
| KeyWord - writes out a keywork with optional formatting directives. |
| *) |
| |
| PROCEDURE KeyWord (n: Name) ; |
| BEGIN |
| IF KeywordFormatting |
| THEN |
| Output.WriteString('{%K') ; |
| IF (n = MakeKey('}')) OR (n = MakeKey('{')) OR (n = MakeKey('%')) |
| THEN |
| Output.Write('%') (* escape }, { or % *) |
| END ; |
| Output.WriteKey(n) ; |
| Output.Write('}') |
| ELSE |
| Output.WriteKey(n) |
| END |
| END KeyWord ; |
| |
| |
| (* |
| PrettyPara - |
| *) |
| |
| PROCEDURE PrettyPara (c1, c2: ARRAY OF CHAR; e: ExpressionDesc; Left: CARDINAL) ; |
| BEGIN |
| Output.WriteString(c1) ; |
| INC(Indent, StrLen(c1)) ; |
| Left := Indent ; |
| PrettyCommentExpression(e, Left) ; |
| Output.WriteString(c2) ; |
| INC(Indent, StrLen(c2)) |
| END PrettyPara ; |
| |
| |
| (* |
| WriteKeyTexinfo - |
| *) |
| |
| PROCEDURE WriteKeyTexinfo (s: Name) ; |
| VAR |
| ds : String ; |
| ch : CHAR ; |
| i, l: CARDINAL ; |
| BEGIN |
| IF Texinfo |
| THEN |
| ds := InitStringCharStar(KeyToCharStar(s)) ; |
| l := Length(ds) ; |
| i := 0 ; |
| WHILE i<l DO |
| ch := char(ds, i) ; |
| IF (ch='{') OR (ch='}') |
| THEN |
| Output.Write('@') |
| END ; |
| Output.Write(ch) ; |
| INC(i) |
| END |
| ELSE |
| Output.WriteKey(s) |
| END |
| END WriteKeyTexinfo ; |
| |
| |
| (* |
| PrettyCommentFactor - |
| *) |
| |
| PROCEDURE PrettyCommentFactor (f: FactorDesc; Left: CARDINAL) ; |
| VAR |
| curpos : CARDINAL ; |
| seentext: BOOLEAN ; |
| BEGIN |
| WHILE f#NIL DO |
| CheckNewLine(Left) ; |
| WITH f^ DO |
| CASE type OF |
| |
| id : Output.WriteKey(ident^.name) ; Output.WriteString(' ') ; |
| INC(Indent, LengthKey(ident^.name)+1) | |
| lit : IF MakeKey("'")=string |
| THEN |
| Output.Write('"') ; WriteKeyTexinfo(string) ; Output.WriteString('" ') |
| ELSE |
| Output.Write("'") ; WriteKeyTexinfo(string) ; Output.WriteString("' ") |
| END ; |
| INC(Indent, LengthKey(string)+3) | |
| sub: PrettyPara('( ', ' ) ', expr, Left) | |
| opt: PrettyPara('[ ', ' ] ', expr, Left) | |
| mult: IF Texinfo |
| THEN |
| PrettyPara('@{ ', ' @} ', expr, Left) |
| ELSE |
| PrettyPara('{ ', ' } ', expr, Left) |
| END | |
| m2 : IF EmitCode |
| THEN |
| NewLine(Left) ; Output.WriteString('% ') ; |
| seentext := FALSE ; |
| curpos := 0 ; |
| WriteCodeHunkListIndent(code^.code, code^.indent, curpos, Left+2, seentext) ; |
| Output.WriteString(' %') ; |
| NewLine(Left) |
| END |
| |
| ELSE |
| END ; |
| PrettyFollow('<f:', ':f>', 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:', ':t>', 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:', ':e>', e^.followinfo) |
| END |
| END PrettyCommentExpression ; |
| |
| |
| (* |
| PrettyCommentStatement - |
| *) |
| |
| PROCEDURE PrettyCommentStatement (s: StatementDesc; Left: CARDINAL) ; |
| BEGIN |
| IF s#NIL |
| THEN |
| PrettyCommentExpression(s^.expr, Left) ; |
| PrettyFollow('<s:', ':s>', 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:', ':p>', 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 |
| THEN |
| IF FormalParameters |
| THEN |
| Output.WriteString('; ') |
| ELSE |
| Output.WriteString(', ') |
| END |
| END |
| UNTIL 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 |
| THEN |
| Output.WriteString(', ') |
| END |
| UNTIL 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 ; |
| j := StrLen(str) ; |
| 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-2025 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 i<LargestValue DO |
| Output.WriteKey(GetSymKey(ReverseValues, WORD(i))) ; |
| INC(i) ; |
| IF i<LargestValue |
| THEN |
| Output.WriteString(', ') |
| END |
| END ; |
| Output.WriteString(') ;') ; NewLine(0) ; |
| Output.WriteString('*)') ; NewLine(0) ; |
| Output.WriteString('TYPE') ; NewLine(3) ; |
| IF LargestValue>MaxElementsInSet |
| 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<n DO |
| IF GetArg(ArgName, i) |
| THEN |
| IF StrEqual(ArgName, '-e') |
| THEN |
| ErrorRecovery := FALSE |
| ELSIF StrEqual(ArgName, '-d') |
| THEN |
| Debugging := TRUE ; |
| SetDebugging(TRUE) |
| ELSIF StrEqual(ArgName, '-c') |
| THEN |
| EmitCode := FALSE |
| ELSIF StrEqual(ArgName, '-k') |
| THEN |
| KeywordFormatting := TRUE |
| ELSIF StrEqual(ArgName, '-l') |
| THEN |
| SuppressFileLineTag := TRUE |
| ELSIF StrEqual(ArgName, '-h') OR StrEqual(ArgName, '--help') |
| THEN |
| DisplayHelp |
| ELSIF StrEqual(ArgName, '-p') |
| THEN |
| PrettyPrint := TRUE |
| ELSIF StrEqual(ArgName, '-t') |
| THEN |
| Texinfo := TRUE |
| ELSIF StrEqual(ArgName, '-x') |
| THEN |
| Sphinx := TRUE |
| ELSIF StrEqual(ArgName, '-f') |
| THEN |
| FreeDocLicense := TRUE |
| ELSIF StrEqual(ArgName, '-o') |
| THEN |
| INC (i) ; |
| IF GetArg(ArgName, i) |
| THEN |
| IF NOT Output.Open (ArgName) |
| THEN |
| WriteString('cannot open ') ; WriteString(ArgName) ; |
| WriteString(' for writing') ; WriteLn ; |
| exit (1) |
| END |
| END |
| ELSIF OpenSource(ArgName) |
| THEN |
| StrCopy (ArgName, FileName) ; |
| AdvanceToken |
| ELSE |
| WriteString('cannot open ') ; WriteString(ArgName) ; |
| WriteString(' for reading') ; WriteLn ; |
| exit (1) |
| END |
| END ; |
| INC (i) |
| END ; |
| IF n=1 |
| THEN |
| DisplayHelp |
| END |
| END ParseArgs ; |
| |
| |
| (* |
| Init - initialize the modules data structures |
| *) |
| |
| PROCEDURE Init ; |
| BEGIN |
| WasNoError := TRUE ; |
| Texinfo := FALSE ; |
| Sphinx := FALSE ; |
| FreeDocLicense := FALSE ; |
| EmitCode := TRUE ; |
| LargestValue := 0 ; |
| HeadProduction := NIL ; |
| CurrentProduction := NIL ; |
| InitTree(Aliases) ; |
| InitTree(ReverseAliases) ; |
| InitTree(Values) ; |
| InitTree(ReverseValues) ; |
| LastLineNo := 0 ; |
| CodePrologue := NIL ; |
| CodeEpilogue := NIL ; |
| CodeDeclaration := NIL ; |
| ErrorProcArray := MakeKey('Error') ; |
| ErrorProcString := MakeKey('ErrorS') ; |
| TokenTypeProc := MakeKey('GetCurrentTokenType()') ; |
| SymIsProc := MakeKey('SymIs') ; |
| OnLineStart := TRUE ; |
| ParseArgs ; |
| WasNoError := Main() ; (* this line will be manipulated by sed in buildpg *) |
| IF WasNoError |
| THEN |
| PostProcessRules ; |
| IF WasNoError |
| THEN |
| IF Debugging |
| THEN |
| EmitRules |
| ELSIF PrettyPrint |
| THEN |
| EmitRules |
| ELSE |
| Output.WriteString('(* it is advisable not to edit this file as it was automatically generated from the grammer file ') ; |
| Output.WriteString(FileName) ; Output.WriteString(' *)') ; Output.WriteLn ; |
| OnLineStart := FALSE ; |
| EmitFileLineTag(LinePrologue) ; |
| BeginningOfLine := TRUE ; |
| WriteCodeHunkList(CodePrologue) ; |
| EmitSupport ; |
| EmitFileLineTag(LineDeclaration) ; |
| WriteCodeHunkList(CodeDeclaration) ; |
| EmitRules ; |
| (* code rules *) |
| EmitFileLineTag(LineEpilogue) ; |
| WriteCodeHunkList(CodeEpilogue) |
| END |
| END |
| END ; |
| Output.Close |
| END Init ; |
| |
| |
| BEGIN |
| Init |
| END ppg. |
| (* |
| * Local variables: |
| * compile-command: "gm2 -I../gm2-libs:. -fbounds -freturn -c -g ppg.mod" |
| * End: |
| *) |