Parsing Antlr 4:解析器中切换模式的方法
我正在尝试使用Antlr4构建一个MVS JCL识别器。一般的努力进展得相当顺利,但我在处理与*nix“heredocs”(内联文件)相当的MVS时遇到了麻烦。我不能使用lexer模式在JCL和heredoc内容之间切换,所以我正在寻找可能使用解析器级别的替代方法 IBM MVS允许使用“流内数据集”,类似于*nix here文档 例如: 这定义了一个三行内联文件,以字符“ZZ”结尾,可由使用标签“ANYNAME”的引用程序访问:Parsing Antlr 4:解析器中切换模式的方法,parsing,antlr,antlr4,Parsing,Antlr,Antlr4,我正在尝试使用Antlr4构建一个MVS JCL识别器。一般的努力进展得相当顺利,但我在处理与*nix“heredocs”(内联文件)相当的MVS时遇到了麻烦。我不能使用lexer模式在JCL和heredoc内容之间切换,所以我正在寻找可能使用解析器级别的替代方法 IBM MVS允许使用“流内数据集”,类似于*nix here文档 例如: 这定义了一个三行内联文件,以字符“ZZ”结尾,可由使用标签“ANYNAME”的引用程序访问: ANYNAME是一个句柄,程序可以通过该句柄访问此处的文档内容
ANYNAME
是一个句柄,程序可以通过该句柄访问此处的文档内容
DD*
为必填项,并通知MVS此处有一份单据
SYMBOLS=(JCLONLY,FILEREF)
是关于如何处理here文档的可选详细信息
DLM=ZZ
也是可选的,它定义了here doc终止符(默认终止符=/*
)
我需要能够在解析器级别处理//ANYNAME…
行(我有该位),然后读取here文档内容,直到找到(可能是非默认的)here文档终止符。从某种意义上说,这看起来像是一个lexer模式的机会——但在这一点上,我在解析器中工作,没有固定的终止符
我需要关于如何切换模式以处理我的here文档的指导,然后再次切换以继续处理我的JCL
下面是我的语法的一个非常简略的版本(到目前为止,实际的语法大约有2200行,而且是不完整的)
谢谢你的见解。我感谢你的帮助、意见和建议
/* the ddstmt parser rule should be considered the main entry point. It handles (at least):
//ANYNAME DD *,SYMBOLS=(JCLONLY,FILEREF),DLM=ZZ
and // DD *,DLM=ZZ
and //ANYNAME DD *,SYMBOLS=EXECSYS
and //ANYNAME DD *
I need to be able process the above line as JCL then read the here-doc content...
"HEREDOC TEXT 1"
"HEREDOC TEXT 2"
"HEREDOC TEXT 3"
as either a single token or a series of tokens, then, after reading the here-doc
delimiter...
"ZZ"
, go back to processing regular JCL again.
*/
/* lexer rules: */
LINECOMMENT3 : SLASH SLASH STAR ;
DSLASH : SLASH SLASH ;
INSTREAMTERMINATE : SLASH STAR ;
SLASH : '/' ;
STAR : '*' ;
OPAREN : '(' ;
CPAREN : ')' ;
COMMA : ',' ;
KWDD : 'DD' ;
KWDLM : 'DLM' ;
KWSYMBOLS : 'SYMBOLS' ;
KWDATA : 'DATA' ;
SYMBOLSTARGET : 'JCLONLY'|'EXECSYS'|'CNVTSYS' ;
EQ : '=' ;
APOST : '\'' ;
fragment
SPC : ' ' ;
SPCS : SPC+ ;
NL : ('\r'? '\n') ;
UNQUOTEDTEXT : (APOST APOST|~[=\'\"\r\n\t,/() ])+ ;
/* parser rules: */
label : unquotedtext
;
separator : SPCS
;
/* handle crazy JCL comment rules - start */
partcomment : SPCS partcommenttext NL
;
partcommenttext : ((~NL+?)?)
;
linecomment : LINECOMMENT3 linecommenttext NL
;
linecommenttext : ((~NL+?)?)
;
postcommaeol : ( (partcomment|NL) linecomment* DSLASH SPCS )?
;
poststmteol : ( (partcomment|NL) linecomment* )?
;
/* handle crazy JCL comment rules - end */
ddstmt : DSLASH (label|) separator KWDD separator dddecl
;
dddecl : ...
| ddinstreamdecl
| ...
;
ddinstreamdecl : (STAR|KWDATA) poststmteol ddinstreamopts
;
ddinstreamopts : ( COMMA postcommaeol ddinstreamopt poststmteol )*
;
ddinstreamopt : ( ddinstreamdelim
| symbolsdecl
)
;
ddinstreamdelim : KWDLM EQ unquotedtext
;
symbolsdecl : KWSYMBOLS EQ symbolsdef
;
symbolsdef : OPAREN symbolstarget ( COMMA symbolsloggingdd )? CPAREN
| symbolstarget
;
symbolstarget : SYMBOLSTARGET
;
symbolsloggingdd : unquotedtext
;
unquotedtext : UNQUOTEDTEXT
;
在开始解析操作之前,lexer需要能够标记整个文档。从解析器内部控制lexer的任何尝试都会导致无尽的噩梦。下面的片段展示了谓词如何与lexer模式结合使用,以检测带有用户定义分隔符的字符串的结尾。关键部分是记录开始分隔符,然后对照它检查从行首开始的标记
PHP_NOWDOC_START
: '<<<\'' PHP_IDENTIFIER '\'' {_input.La(1) == '\r' || _input.La(1) == '\n'}?
-> pushMode(PhpNowDoc)
;
mode PhpNowDoc;
PhpNowDoc_NEWLINE : NEWLINE -> type(NEWLINE);
PHP_NOWDOC_END
: {_input.La(-1) == '\n'}?
PHP_IDENTIFIER ';'?
{CheckHeredocEnd(_input.La(1), Text);}?
-> popMode
;
PHP_NOWDOC_TEXT
: ~[\r\n]+
;
感谢您抽出时间为我提供您的见解。我会复习一下,试着了解一下你建议的技巧。我可能会回来问更多的问题。蒂亚。
PHP_NOWDOC_START
: '<<<\'' PHP_IDENTIFIER '\'' {_input.La(1) == '\r' || _input.La(1) == '\n'}?
-> pushMode(PhpNowDoc)
;
mode PhpNowDoc;
PhpNowDoc_NEWLINE : NEWLINE -> type(NEWLINE);
PHP_NOWDOC_END
: {_input.La(-1) == '\n'}?
PHP_IDENTIFIER ';'?
{CheckHeredocEnd(_input.La(1), Text);}?
-> popMode
;
PHP_NOWDOC_TEXT
: ~[\r\n]+
;
public override IToken NextToken()
{
IToken token = base.NextToken();
switch (token.Type)
{
case PHP_NOWDOC_START:
// <<<'identifier'
_heredocIdentifier = token.Text.Substring(3).Trim('\'');
break;
case PHP_NOWDOC_END:
_heredocIdentifier = null;
break;
default:
break;
}
return token;
}
private bool CheckHeredocEnd(int la1, string text)
{
// identifier
// - or -
// identifier;
bool semi = text[text.Length - 1] == ';';
string identifier = semi ? text.Substring(0, text.Length - 1) : text;
return string.Equals(identifier, HeredocIdentifier, StringComparison.Ordinal);
}