5

次のようなパーサーの例を考えてみましょう:

object TestParser extends RegexParsers {
    override protected val whiteSpace = """[ \t]*""".r  

    def eol = """(\r?\n)+""".r
    def item = "[a-zA-Z][a-zA-Z0-9-]*".r
    def list = "items:" ~> rep1sep(item,",") 
    def constraints = "exclude:" ~> item

    def itemsDefinition = (rep1sep(list, eol) ~ repsep(constraints,eol))
}

この入力を解析しようとすると (2 行が含まれていない場合、exclude は正常に動作します):

items: item1, item2, item3, item3, item4
items: item2, item3, item3, item5, item4    
items: item4, item5, item6, item10      
items: item1, item2, item3
exclude: item1
exclude: item2

次のエラーが表示されます。

[5.5] failure: `items:' expected but `e' found

       exclude: item1

       ^

問題は次の行で明らかです。

def itemsDefinition = (rep1sep(list, eol) ~ repsep(constraints,eol))

うまくいかない理由とは。バックトラックと何か関係がありますか?それを機能させるには、どのような代替手段が必要ですか?

4

1 に答える 1

6

リストと制約の間にeolが必要です

(rep1sep(list, eol) <~ eol) ~ repsep(constraint,eol)

答えを完成させる:

あなたの文法では、ターミネータではなく、リスト間のセパレータとして eol を指定しています。exclude最初の入力が最後の入力の直後に表示される入力を受け入れますitem3(空白はありますが、改行はありません)。

パーサーが不要な に到達するeolitems、 を探し、excludes代わりに検索します。表示されたエラーメッセージが表示されます。次に、パーサーは確かに前の新しい行にバックトラックします。リストの部分がそこで止まっている可能性を考慮して、除外を探します。ただし、代わりに eol が見つかった場合。したがって、別の考えられるエラー メッセージは"excludes expected, eol found"、この場合はより役に立ちます。

文法に選択肢があり、分岐が成功しない場合、パーサーは最も遠い位置でエラーを返します。これは通常正しい戦略です。文法で an"if"または aが許可"for"され、入力が であるとします"if !!!"。ブランチではif、エラーは次のようになります"(" expected, "!" found。ブランチではfor、メッセージは"for expected, if found". 明らかにif、2 番目のトークンに表示されるブランチからのメッセージは、最初のトークンに表示されるブランチからのメッセージよりもはるかに優れておりfor、まったく関連性がありません。

セパレーター/ターミネーターの問題については、次のことを検討してください。

  • 区切り文字 (;パスカル) :repsep(item, separator)
  • ターミネータ ( ;C) :rep(item <~ terminator)
  • フレキシブル :repsep(item, separator) <~ separator?

最後のものは、アイテムがまったくない場合に単一のセパレーターを許可します。これが望ましくない場合は、(rep1sep(item, separator) <~ separator?)?.

于 2012-02-22T16:25:47.777 に答える