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

时间:2014-12-05 16:43:06

标签: parsing antlr antlr4

我正在尝试使用Antlr4构建MVS JCL识别器。一般的努力进展顺利,但我在处理相当于* nix“here docs”(内联文件)的MVS时遇到了麻烦。我不能使用词法分析器模式在JCL和here-doc内容之间进行翻转,所以我正在寻找可能使用解析器级别的替代方案。

IBM MVS允许使用“instream数据集”,类似于* nix here-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以下是here-doc。

SYMBOLS=(JCLONLY,FILEREF)是有关如何处理here-doc的可选详细信息。

DLM=ZZ也是可选的,定义了here-doc终结符(默认终结符= /*)。

我需要能够在解析器级别处理//ANYNAME...行(我有那个位),然后读取here-doc内容,直到找到(可能是非默认的) here-doc终结者。从某种意义上说,这看起来像是词法模式的机会 - 但此时我在解析器中工作,我没有固定的终结器可以使用。

我需要有关如何切换模式以处理我的here-doc的指导,然后再切换回来继续处理我的JCL。

我的语法的一个非常简略的版本(实际语法,到目前为止,大约2,200行并且不完整)。

感谢您的任何见解。感谢您的帮助,意见和建议。

/* 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
                                    ;

1 个答案:

答案 0 :(得分:3)

您的词法分析器需要能够在解析操作开始之前对整个文档进行标记。任何试图从解析器中控制词法分析器的尝试都是无休止的噩梦。 PHP 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);
}