unit z.dpglib.DpgParser; uses { z.dpglib.types; } parser TDpgParser; options { defaultErrorHandler = false; importVocab = z.dpglib.DpgLexer; exportVocab = z.dpglib.DpgParser; k = 2; } memberdecl { protected fGrammarMaker : IGrammarBehavior; fTool : ITool; fNesting : integer; fExchangeDir : AnsiString; fGrammarFile : AnsiString; fGrammarUnit : AnsiString; private function lastInRule : boolean; procedure checkEndRule( pToken: IToken); public constructor Create( pParserState : IParserState; pGrammarMaker : IGrammarBehavior; pTool : ITool; pExchangeDir : AnsiString); overload; constructor Create( pTokenBuffer : ITokenBuffer; pGrammarMaker : IGrammarBehavior; pTool : ITool; pExchangeDir : AnsiString); overload; constructor Create( pTokenStream : ITokenStream; pGrammarMaker : IGrammarBehavior; pTool : ITool; pExchangeDir : AnsiString); overload; destructor Destroy; override; } // ---------------------------------------------------------------------------- // grammar // ---------------------------------------------------------------------------- grammar local { unitName: IToken; } : "unit" "[" unitName=qualifiedId {fGrammarUnit := unitName.TokenText;} SEMI (usesDecl)? (constDecl)? (typeDecl)? classDecl {fGrammarMaker.endGrammar;} ; // ---------------------------------------------------------------------------- // usesDecl // ---------------------------------------------------------------------------- usesDecl : USES ( // tr:TOKENREF SEMI {fGrammarMaker.defineUses( tr);} // | rr:RULEREF SEMI {fGrammarMaker.defineUses( rr);} qualifiedUsesName SEMI )* RCURLY ; qualifiedUsesName local { id: AnsiString; } : ( r:TOKENREF | r:RULEREF ) { id := r.TokenText; } ( WILDCARD ( r:TOKENREF | r:RULEREF) { id := id +'.'+ r.TokenText; } )* { fGrammarMaker.defineUses(id); } ; // ---------------------------------------------------------------------------- // constDecl // ---------------------------------------------------------------------------- constDecl : "const" a:ACTION {fGrammarMaker.RefConstAction( a);} ; // ---------------------------------------------------------------------------- // typeDecl // ---------------------------------------------------------------------------- typeDecl : "type" a:ACTION {fGrammarMaker.RefTypeAction( a);} ; // ---------------------------------------------------------------------------- // classDecl // ---------------------------------------------------------------------------- classDecl local { grType : integer; grObject : IToken; grSuper : IToken; } { grObject := nil; grSuper := nil; } : // ------------------------------------------------------------ // Determine parser type // ------------------------------------------------------------ ( "lexer" { grType := 0; } | "parser" { grType := 1; } ) // ------------------------------------------------------------ // get class name // ------------------------------------------------------------ grObject = id // ------------------------------------------------------------ // get superclass name // ------------------------------------------------------------ // ( // LPAREN // grSuper=id // RPAREN // )? SEMI // ------------------------------------------------------------ // Start the grammar // ------------------------------------------------------------ { // --------------------------------------------------------- // Now we have enough information to start the grammar. // --------------------------------------------------------- case grType of 0: fGrammarMaker.StartLexer( InputState.FileName, grObject, grSuper); 1: fGrammarMaker.StartParser( InputState.FileName, grObject, grSuper); 2: fGrammarMaker.StartTreeWalker( InputState.FileName, grObject, grSuper); end; fGrammarMaker.defineGrammarUnit( fGrammarUnit); } // ------------------------------------------------------------ // Process optional class "options {...}" clause // ------------------------------------------------------------ (classOptions)? // ------------------------------------------------------------ // Process optional class "tokens {...}" clause // But only for lexers. // ------------------------------------------------------------ ( {grType=0}? classTokens)? // ------------------------------------------------------------ // Process optional class "memberDecl {...}" clause // ------------------------------------------------------------ (classMemberDecl)? // ------------------------------------------------------------ // Well, the rules // ------------------------------------------------------------ rules // ------------------------------------------------------------ // Process optional class "memberDecl {...}" clause // ------------------------------------------------------------ (classMemberDef)? ; // ---------------------------------------------------------------------------- // classOptions // ---------------------------------------------------------------------------- classOptions local { optName : IToken; optValue : IToken; } : OPTIONS ( optName = id ASSIGN optValue = optionValue SEMI {fGrammarMaker.setGrammarOption( optName, optValue);} )* RCURLY ; // ---------------------------------------------------------------------------- // classTokens // ---------------------------------------------------------------------------- classTokens : TOKENS ( { tokenName := nil; tokenString := nil; } ( tokenName: TOKENREF (ASSIGN tokenString:STRINGLIT)? | tokenString: STRINGLIT ) SEMI {fGrammarMaker.defineToken( tokenName, tokenString);} )* RCURLY ; // ---------------------------------------------------------------------------- // classMemberDecl // ---------------------------------------------------------------------------- classMemberDecl : "memberDecl" memberDecl:ACTION {fGrammarMaker.refMemberDecl(memberDecl);} ; // ---------------------------------------------------------------------------- // classMemberDef // ---------------------------------------------------------------------------- classMemberDef : "memberDef" memberDef:ACTION {fGrammarMaker.refMemberDef(memberDef);} ; // ---------------------------------------------------------------------------- // rules // ---------------------------------------------------------------------------- rules : (rule)* ; // ---------------------------------------------------------------------------- // ruleExceptionBlock // ---------------------------------------------------------------------------- ruleExceptionBlock : t:"except" a:ACTION { fGrammarMaker.RefRuleExHandler( t, a); } | t:"finally" a:ACTION { fGrammarMaker.RefRuleExHandler( t, a); } ; // ---------------------------------------------------------------------------- // ruleExceptionBlock // ---------------------------------------------------------------------------- altExceptionBlock : t:"except" a:ACTION { fGrammarMaker.RefAltExHandler( t, a); } | t:"finally" a:ACTION { fGrammarMaker.RefAltExHandler( t, a); } ; // ---------------------------------------------------------------------------- // rule // ---------------------------------------------------------------------------- rule local { access : AnsiString; ag : integer; returns : IToken; name : IToken; } { access := 'public'; args := nil; name := nil; ag := AUTOGEN_NONE; } : // ------------------------------------------------------------ // Parse rule scope // ------------------------------------------------------------ ( "public" { access := 'public'; } | "protected" { access := 'protected'; } | "private" { access := 'private'; } )? // ------------------------------------------------------------ // Parse rule name // ------------------------------------------------------------ name=id // ------------------------------------------------------------ // Parse optional BANG operator // ------------------------------------------------------------ // ( BANG { ag := AUTOGEN_BANG;} )? // ------------------------------------------------------------ // Optional arguments // ------------------------------------------------------------ ( args:ARGACTION)? // ------------------------------------------------------------ // Optional return type // ------------------------------------------------------------ ( "returns" ret:ARGACTION)? // ------------------------------------------------------------ // Now start the rule definition // ------------------------------------------------------------ { fGrammarMaker.defineRuleName( name, access, true, ''); if args <> nil then fGrammarMaker.refArgAction( args); if ret <> nil then fGrammarMaker.refReturnAction( ret); } // ------------------------------------------------------------ // Optional rule options // ------------------------------------------------------------ (ruleOptions)? // ------------------------------------------------------------ // Optional rule local variable declarations // ------------------------------------------------------------ ( "local" locals:ACTION {fGrammarMaker.refRuleLocals( locals);} )? // ------------------------------------------------------------ // Optional rule init action // ------------------------------------------------------------ ( initAction:ACTION {fGrammarMaker.refInitAction( initAction);} )? // ------------------------------------------------------------ // Rule block // ------------------------------------------------------------ COLON block SEMI // ------------------------------------------------------------ // Optional exception handler // ------------------------------------------------------------ ( ruleExceptionBlock)? // ------------------------------------------------------------ // Finish the rule // ------------------------------------------------------------ { fGrammarMaker.endRule('');} ; // ---------------------------------------------------------------------------- // block // ---------------------------------------------------------------------------- block { INC( fNesting); } : alternative (OR alternative)* {DEC(fNesting);} ; // ---------------------------------------------------------------------------- // alternative // ---------------------------------------------------------------------------- alternative local { autoGen : boolean; } { autoGen := true; } : // (BANG {autoGen := false;})? {fGrammarMaker.beginAlt( autoGen);} (elem)* (altExceptionBlock)? {fGrammarMaker.endAlt;} ; // ---------------------------------------------------------------------------- // elem // ---------------------------------------------------------------------------- elem : element ; // ---------------------------------------------------------------------------- // element // ---------------------------------------------------------------------------- element local { assignId : IToken; assignLabel : IToken; autoGen : integer; } { assignId := nil; assignLabel := nil; autoGen := AUTOGEN_NONE; } : ( assignId=id ASSIGN (assignLabel=id COLON {checkEndRule(assignLabel);})? ( ruleRef:RULEREF (args:ARGACTION)? (ag:BANG {autoGen := AUTOGEN_BANG;})? {fGrammarMaker.refRule( assignId, ruleRef, assignLabel, args, autoGen);} | tokenRef:TOKENREF (args:ARGACTION)? {fGrammarMaker.refToken( assignId, tokenRef, assignLabel, args, false, autoGen, lastInRule);} ) ) | (assignLabel=id COLON {checkEndRule(assignLabel);})? ( ruleRef:RULEREF (args:ARGACTION)? (ag:BANG {autoGen := AUTOGEN_BANG;})? {fGrammarMaker.refRule( assignId, ruleRef, assignLabel, args, autoGen);} | range[assignLabel] | terminal[assignLabel] | NOT (notTerminal[assignLabel] | ebnf[ assignLabel, true]) | ebnf[ assignLabel, false] ) | action:ACTION {fGrammarMaker.refAction( action);} | semPred:SEMPRED {fGrammarMaker.refSemPred( semPred);} ; // ---------------------------------------------------------------------------- // range // ---------------------------------------------------------------------------- range [pTokenLabel: IToken] local { autoGen: integer; } { autoGen := AUTOGEN_NONE; } : crLeft:CHARLIT RANGE crRight:CHARLIT (BANG {autoGen := AUTOGEN_BANG;} )? {fGrammarMaker.refCharRange( crLeft, crRight, pTokenLabel, autoGen, lastInRule);} | (trLeft:TOKENREF | trLeft:STRINGLIT) RANGE (trRight:TOKENREF | trRight:STRINGLIT) autoGen=astTypeSpec {fGrammarMaker.refTokenRange( trLeft, trRight, pTokenLabel, autoGen, lastInRule);} ; // ---------------------------------------------------------------------------- // terminal // ---------------------------------------------------------------------------- terminal [pTokenLabel: IToken] local { autoGen : integer; } { autoGen := AUTOGEN_NONE; aa := nil; } : cl:CHARLIT (BANG {autoGen := AUTOGEN_BANG;} )? {fGrammarMaker.refCharLiteral( cl, pTokenLabel, false, autoGen, lastInRule);} | tr:TOKENREF autoGen=astTypeSpec (aa:ARGACTION)? {fGrammarMaker.refToken( nil, tr, pTokenLabel, aa, false, autoGen, lastInRule);} | sl:STRINGLIT autoGen=astTypeSpec {fGrammarMaker.refStringLiteral( sl, pTokenLabel, autoGen, lastInRule);} | wc:WILDCARD autogen=astTypeSpec {fGrammarMaker.refWildCard( wc, pTokenLabel, autoGen);} ; // ---------------------------------------------------------------------------- // notTerminal // ---------------------------------------------------------------------------- notTerminal [pTokenLabel: IToken] local { autoGen : integer; } { autoGen := AUTOGEN_NONE; } : cl:CHARLIT (BANG {autoGen := AUTOGEN_BANG;} )? {fGrammarMaker.refCharLiteral( cl, pTokenLabel, true, autoGen, lastInRule);} | tr:TOKENREF autoGen=astTypeSpec {fGrammarMaker.refToken( nil, tr, pTokenLabel, nil, true, autoGen, lastInRule);} ; // ---------------------------------------------------------------------------- // ebnf // ---------------------------------------------------------------------------- ebnf [pTokenLabel: IToken; pTokenNot: boolean] : lp:LPAREN {fGrammarMaker.beginSubrule( pTokenLabel, lp, pTokenNot);} ( // --------------------------------------------------------- // 2nd alt and optional branch ambig due to linear approx // LL(2) issue. COLON ACTION matched correctly in 2nd alt. // --------------------------------------------------------- options { warnWhenFollowAmbig = false; } : subRuleOptions (aa:ACTION {fGrammarMaker.refInitAction(aa);} )? COLON | aa:ACTION {fGrammarMaker.refInitAction(aa);} COLON )? block RPAREN ( ( QUEST { fGrammarMaker.optionalSubrule; } | STAR { fGrammarMaker.zeroOrMoreSubrule; } | PLUS { fGrammarMaker.oneOrMoreSubrule; } | AT { fGrammarMaker.nmSubrule; } LPAREN ( m:INTEGER { fGrammarMaker.refRangeLow( StrToInt(m.TokenText)); } ( COMMA { fGrammarMaker.refRangeHigh( maxint); } ( n:INTEGER { fGrammarMaker.refRangeHigh(StrToInt(n.TokenText)); } )? )? | COMMA n:INTEGER { fGrammarMaker.refRangeHigh(StrToInt(n.TokenText)); } ) RPAREN | IMPLIES {fGrammarMaker.synPred; } )? // ( BANG {fGrammarMaker.noASTSubrule;} )? ) {fGrammarMaker.endSubRule;} ; // ---------------------------------------------------------------------------- // optionValue // ---------------------------------------------------------------------------- optionValue returns [IToken] : result=qualifiedId | result:STRINGLIT | result:CHARLIT | result:INTEGER ; // ---------------------------------------------------------------------------- // subruleOptions // ---------------------------------------------------------------------------- subruleOptions local { optName : IToken; optValue : IToken; } : OPTIONS ( optName = id ASSIGN optValue = optionValue SEMI {fGrammarMaker.setSubruleOption( optName, optValue);} )* RCURLY ; // ---------------------------------------------------------------------------- // ruleOptions // ---------------------------------------------------------------------------- ruleOptions local { optName : IToken; optValue : IToken; } : OPTIONS ( optName = id ASSIGN optValue = optionValue SEMI {fGrammarMaker.setRuleOption( optName, optValue);} )* RCURLY ; // ---------------------------------------------------------------------------- // astTypeSpec // ---------------------------------------------------------------------------- astTypeSpec returns [integer] { result := AUTOGEN_NONE; } : (BANG {result := AUTOGEN_BANG;})? ; // ---------------------------------------------------------------------------- // qualifiedId // ---------------------------------------------------------------------------- qualifiedId returns [IToken] local { buf : AnsiString; a : IToken; } : a=id { buf := a.TokenText; } ( WILDCARD a=id { buf := buf + '.' + a.TokenText; } )* { // ----------------------------------------------------------- // Can either TOKENREF or RULEREF. Should really create QID or // something else instead. // ----------------------------------------------------------- result := TToken.Create( TT_TOKENREF, buf); result.TokenLine := a.TokenLine; result.TokenColumn := a.TokenColumn; } ; // ---------------------------------------------------------------------------- // id // ---------------------------------------------------------------------------- id returns [IToken] : result:TOKENREF | result:RULEREF ; memberdef { // ============================================================================ // Constructor // ============================================================================ constructor TDpgParser.Create( pParserState : IParserState; pGrammarMaker : IGrammarBehavior; pTool : ITool; pExchangeDir : AnsiString); begin inherited Create( pParserState, 2); fGrammarMaker := pGrammarMaker; fExchangeDir := pExchangeDir; fTool := pTool; fNesting := 0; end; // ============================================================================ // Constructor // ============================================================================ constructor TDpgParser.Create( pTokenBuffer : ITokenBuffer; pGrammarMaker : IGrammarBehavior; pTool : ITool; pExchangeDir : AnsiString); begin inherited Create( pTokenBuffer, 2); fGrammarMaker := pGrammarMaker; fExchangeDir := pExchangeDir; fTool := pTool; fNesting := 0; end; // ============================================================================ // Constructor // ============================================================================ constructor TDpgParser.Create( pTokenStream : ITokenStream; pGrammarMaker : IGrammarBehavior; pTool : ITool; pExchangeDir : AnsiString); begin inherited Create( pTokenStream, 2); fGrammarMaker := pGrammarMaker; fExchangeDir := pExchangeDir; fTool := pTool; fNesting := 0; end; // ============================================================================ // Destructor // ============================================================================ destructor TDpgParser.Destroy; begin fGrammarMaker := nil; fTool := nil; inherited; end; // ============================================================================ // lastInRule // ============================================================================ function TDpgParser.lastInRule: boolean; begin if (fNesting = 0) and (LA(1) in [TT_SEMI, TT_OR]) then result := true else result := false; end; // ============================================================================ // checkEndRule // ============================================================================ procedure TDpgParser.checkEndRule( pToken: IToken); begin if pToken <> nil then if pToken.TokenColumn = 1 then fTool.Warning('Did you forget to close the previous rule?', InputState.FileName, pToken.TokenLine, pToken.TokenColumn); end; }