2014-12-05 69 views
3

我正嘗試使用Antlr4構建MVS JCL識別器。一般的努力進行得相當順利,但是我在處理與* nix「here docs」(內聯文件)相當的MVS時遇到了麻煩。我無法使用詞法分析器模式在JCL和here-doc內容之間進行觸發器翻譯,因此我正在尋找可能使用解析器級別的替代方法。Antlr 4:在解析器中切換模式的方法

IBM MVS允許使用「instream數據集」,類似於* nix here-docs。

實施例:

這定義使用標籤「ANYNAME」三線聯文件,用字符「ZZ」和訪問一個引用程序終止:

//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...線(我有位),然後直到我找到了(可能非默認)來讀取這裏-doc的內容在這裏-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 
            ; 

回答

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); 
} 
+0

感謝您抽出寶貴的時間爲我提供你的洞察力。我會審查它,並試圖讓我的頭腦圍繞你所建議的技術。我可能會回來更多的問題。 TIA。 – v0rl0n 2014-12-05 19:08:42