我正在尝试使用Antlr4构建一个MVS JCL识别器。总体工作进行得相当顺利,但我在处理MVS等效的*nix "here docs“(内联文件)时遇到了问题。我不能使用lexer模式在JCL和here-doc内容之间翻转触发器,所以我正在寻找可能使用解析器级别的替代方案。
IBM允许使用"instream数据集“,类似于这里的*nix -docs。
示例:
这定义了一个以字符"ZZ“结尾的三行内联文件,并使用标签”ANYNAME“对引用程序进行访问:
//ANYNAME DD *,SYMBOLS=(JCLONLY,FILEREF),DLM=ZZ
HEREDOC TEXT 1
HEREDOC TEXT 2
HEREDOC TEXT 3
ZZ
//NEXTFILE DD ...stuff...ANYNAME是一个句柄,程序可以通过这个句柄访问here-doc内容。
DD *是强制性的,并通知MVS这里有一个-doc。
SYMBOLS=(JCLONLY,FILEREF)是一个可选的细节,它涉及到这里-文档的处理方式。
DLM=ZZ也是可选的,并定义了here-doc终止符(默认终止符= /*)。
我需要能够在解析器级别处理//ANYNAME...行(我有这个位),然后读取here-doc内容,直到我找到这里-doc终止符(可能不是默认的)为止。从某种意义上说,这看起来像是一个lexer模式的机会--但此时我正在解析器中工作,而且我没有固定的终止符可供使用。
我需要指导如何切换模式,以处理我的这里-文档,然后切换回来继续处理我的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
;发布于 2014-12-05 17:07:32
您的lexer需要能够在解析操作开始之前对整个文档进行标记。任何试图从解析器内部控制lexer的尝试都会给我们带来无尽的恶梦。下面的PHP 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]+
;标识符实际上记录在NextToken()的自定义覆盖中(这里显示的是C#目标):
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);
}https://stackoverflow.com/questions/27320692
复制相似问题