首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Antlr 4:解析器中切换模式的方法

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

Stack Overflow用户
提问于 2014-12-05 16:43:06
回答 1查看 2.2K关注 0票数 3

我正在尝试使用Antlr4构建一个MVS JCL识别器。总体工作进行得相当顺利,但我在处理MVS等效的*nix "here docs“(内联文件)时遇到了问题。我不能使用lexer模式在JCL和here-doc内容之间翻转触发器,所以我正在寻找可能使用解析器级别的替代方案。

IBM允许使用"instream数据集“,类似于这里的*nix -docs。

示例:

这定义了一个以字符"ZZ“结尾的三行内联文件,并使用标签”ANYNAME“对引用程序进行访问:

代码语言:javascript
复制
//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行,而且不完整)。

谢谢你的见解。我感谢你的帮助,意见和建议。

代码语言:javascript
复制
/* 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
                                    ;
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-12-05 17:07:32

您的lexer需要能够在解析操作开始之前对整个文档进行标记。任何试图从解析器内部控制lexer的尝试都会给我们带来无尽的恶梦。下面的PHP Lexer片段展示了如何将谓词与lexer模式结合使用,以检测带有用户定义分隔符的字符串的结尾。关键部分是记录开始分隔符,然后检查从行的开头开始的标记。

代码语言:javascript
复制
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#目标):

代码语言:javascript
复制
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);
}
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27320692

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档