Files
bds.mr.dpg/src.lib/grammar/dpglib.DpgParser.g
T
2026-01-03 18:33:48 +01:00

890 lines
26 KiB
Plaintext

unit dpglib.DpgParser;
uses
{
dpglib.types;
}
parser TDpgParser;
options
{
defaultErrorHandler = false;
importVocab = dpglib.DpgLexer;
exportVocab = 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; }
| "treeparser" { grType := 2; }
)
// ------------------------------------------------------------
// 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)?
{
fGrammarMaker.defineToken( tokenName, tokenString);
}
(tokenSpecOptions[tokenName])?
| tokenString:STRINGLIT
{
fGrammarMaker.defineToken( tokenName, tokenString);
}
(tokenSpecOptions[tokenString])?
)
SEMI
)*
RCURLY
;
// ----------------------------------------------------------------------------
// tokenSpecOptions
// ----------------------------------------------------------------------------
tokenSpecOptions[ t: IToken]
local
{
name : IToken;
value : IToken;
}
{
name := nil;
value := nil;
}
: OPEN
name=id ASSIGN value=optionValue
{
fGrammarMaker.refTokenSpecElemOption( t, name, value);
}
(
name=id ASSIGN value=optionValue
{
fGrammarMaker.refTokenSpecElemOption( t, name, value);
}
)*
CLOSE
;
// ----------------------------------------------------------------------------
// 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 (elementOptions)?
;
// ----------------------------------------------------------------------------
// element options
// ----------------------------------------------------------------------------
elementOptions
local
{
name : IToken;
value : IToken;
}
: OPEN
name=id ASSIGN value=optionValue
{
fGrammarMaker.refElemOption(name,value);
}
(
name=id ASSIGN value=optionValue
{
fGrammarMaker.refElemOption(name,value);
}
)*
CLOSE
;
// ----------------------------------------------------------------------------
// 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);}
|
tree
;
// ----------------------------------------------------------------------------
// tree
// ----------------------------------------------------------------------------
tree
: lp:TREE_BEGIN { fGrammarMaker.BeginTree(lp); }
rootNode { fGrammarMaker.BeginChildList; }
(element)+ { fGrammarMaker.EndChildList; }
RPAREN { fGrammarMaker.EndTree; }
;
// ----------------------------------------------------------------------------
// rootNode
// ----------------------------------------------------------------------------
rootNode
local
{
l : IToken;
}
: (
l=id COLON
{
CheckEndRule(l);
}
)?
terminal[l]
;
// ----------------------------------------------------------------------------
// 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;
}
:
( CARET { result := AUTOGEN_CARET; }
| 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;
}