Parsing Antlr 4:解析器中切换模式的方法

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是一个句柄,程序可以通过该句柄访问此处的文档内容

我正在尝试使用Antlr4构建一个MVS JCL识别器。一般的努力进展得相当顺利,但我在处理与*nix“heredocs”(内联文件)相当的MVS时遇到了麻烦。我不能使用lexer模式在JCL和heredoc内容之间切换,所以我正在寻找可能使用解析器级别的替代方法

IBM MVS允许使用“流内数据集”,类似于*nix here文档

例如:

这定义了一个三行内联文件,以字符“ZZ”结尾,可由使用标签“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);
}