2015-11-27 68 views
0

我有一個簡單的文件格式,我想用jison解析器生成器進行解析。該文件可以包含任意順序和數量的多個表達式。下面是解析器jison文件:Jison解析器在第一個規則後停止

/* lexical grammar */ 
%lex 

%% 

\s+     /* skip whitespace */ 
\"(\\.|[^"])*\"   return 'STRING' 

File\s*Version\s*\:  return 'FILEVERSION' 
[0-9]+("."[0-9]+)?\b  return 'NUMBER' 
<<EOF>>     return 'EOF' 
.      return 'INVALID' 

/lex 

%start expressions 

%% /* language grammar */ 

expressions 
    : EOF 
    | e expressions EOF 
    ; 

e 
    : STRING 
    | FILEID 
    ; 

FILEID 
    : FILEVERSION NUMBER { return $1 + $2; } 
    ; 

爲簡單起見,我已經縮短了文件,只有字符串和文件ID表達式。

我的問題是,如果第二個表達式只包含一個像字符串一樣的標記,則生成的解析器似乎只能識別一個或兩個完整表達式。例如:

文件版本:1.0

將被解析,或者

文件版本:1.0 「我的字符串」

此外還將進行解析,但對於

文件版本:1.0 「我的字符串」 「不解析字符串」

最後一個字符串不會被解析。

我試過jison debuggerjison page本身的代碼,但兩頁都顯示相同的結果。

我對這個問題的建議是:

  1. 一些詞法錯誤(正則表達式)
  2. 一些語法錯誤(左右遞歸)
  3. 一些行動分析器失蹤(一種{$$ = $ 1;})
  4. 其他一些野牛/ jison魔法我失蹤

我不是EBNF解析器導師所以請保持你的答案爲SIMPL儘可能。

回答

2

眼前的問題是,你returnFILEID生產。 return返回,所以解析結束於返回的值。通常,語義規則應該通過分配給變量$$來提供它們的結果。 (對於「單位規則」,右側只有一個符號是沒有必要的;在執行操作之前,解析器的確是$$ = $1,所以如果這就是你想要的,你可以像你一樣離開操作在你的兩個FILEID規則。)

此外,您expressions生產不符合$2做任何事情,所以即使你固定的第一個問題,你仍然只看到結果一個e

您的expressions生產也是不正確的,因爲它除了從基本情況下的EOF外還需要每e一個EOF標記。考慮如何生產工作:

expressions -> e expressions EOF 
      -> e e expressions EOF EOF 
      -> e e e expressions EOF EOF EOF 
      -> e e e EOF EOF EOF EOF 

就個人而言,我會建議使用左遞歸,而不是右遞歸。像jison這樣的自下而上的解析器更喜歡左遞歸,並且通常會導致更自然的語義規則,就像這種情況一樣。

最後,您需要在實際到達輸入末尾時返回最終值。在jison中,通常需要一個明確的開始規則,其語義動作爲return。因此,考慮到所有這些,我們試試這個:(我改變了一些非終端的名稱,並且降低了因爲對於非終端和上位用戶而言使用小寫的傳統方式FILEID,案例終端)關於你的正則表達式匹配字符串

%start prog 
%% 
prog : exprs EOF   { return $1; } 
     ; 
exprs :     { $$ = []; } 
     | exprs expr   { $$.push($2); } 
     ; 
expr : file_id 
     | STRING 
     ; 
file_id: FILEVERSION NUMBER { $$ = $1 + $2; } 
     ; 

一個注意:

\"(\\.|[^"])*\"   return 'STRING' 

雖然它顯然是在JavaScript的工作(主要是;見下文),我t將在flex中展示bug(或Posix兼容的正則表達式庫)。它主要在JavaScript中工作,因爲javascript正則表達式替換運算符|,訂購的是;如果第一個選擇匹配,第二個選擇就不會被嘗試,除非該模式的其餘部分不匹配(並且在這種情況下,該錯誤將被觸發)。

但是在(f)lex中,交替操作符注意到所有匹配的替代方案,並最終選擇最長可能的匹配。其結果是,匹配"\\"..."時,彈性將匹配令牌,直到第三報價,通過使用[^"]匹配前\然後\\.匹配\,這讓它繼續尋找收盤報價。

很容易寫的正則表達式,這樣它會與任何語義的工作,我強烈建議這樣做,如果你想遷移到不同的解析器生成器,通過簡單地確保\不匹配[^"]

\"(\\.|[^\\"])*\"  return 'STRING' 

這種變化也將修復細微的錯誤,甚至在JavaScript中,"\"被認爲是一個有效的字符串標記,如果它是在輸入最後一個字符串。在這種情況下,JavaScript的將首先使用\\.匹配\,但一旦它,它不會找到任何閉引號,然後它會原路返回,並嘗試與[^"]匹配,將匹配該\,允許那麼報價將被確認爲收盤報價。

+0

哇,非常感謝你這個清晰全面的答案! – tomvodi