3

解析について学習を始めたばかりで、JSON を読み取り、そのための単純なツリーを構築するために、(parsec を使用して) Haskell でこの単純なパーサーを作成しました。RFC 4627の文法を使用しています。

ただし、 string{"x":1 }を解析しようとすると、出力が得られます。

(行 1、列 8) の解析エラー:
予期しない "}"
空白文字または「,」が必要です

これは、右中括弧 (]) または口ひげ (}) の前にスペースがある場合にのみ発生しているようです。

私は何を間違えましたか?終了記号の前に空白を避けると、完全に機能します。

4

2 に答える 2

6

Parsec は巻き戻しとバックトラックを自動的に行いません。を記述するsepBy member valueSeparatorと、valueSeparatorは空白を消費するため、パーサーは値を次のように解析します。

{"x":1 }
[------- object
%        beginObject
 [-]     name
    %    nameSeparator
     %   jvalue
      [- valueSeparator
       X In valueSeparator: unexpected "}"

Legend:
[--]     full match
%        full char match
[--      incomplete match
X        incomplete char match

valueSeparator失敗した場合、1 つの文字が で既に一致しているため、Parsec は戻って別の解析の組み合わせを試行しませんvalueSeparator

問題を解決するには、次の 2 つのオプションがあります。

  1. JSON では空白は重要ではないため、常に重要なトークンの後に空白を消費してください。したがって、 atokは char の後の空白のみを消費する必要があるため、その定義はtok c = char c *> ws( (*>)from Control.Applicative); です。他のすべてのパーサーに同じルールを適用します。そのように「間違ったパーサー」に入った後に空白を消費することはないので、後戻りする必要はありません。
  2. try複数の文字を消費する可能性があり、失敗した場合に入力を巻き戻す必要があるパーサーの前に追加することにより、Parsec でバックトラッキングを使用します。

編集: ASCII グラフィックを更新して、より意味のあるものにしました。

于 2012-01-02T07:22:40.303 に答える
1

一般的な解決策は、すべてのパーサーに末尾の空白をスキップさせることです。これを行うためのきちんとした方法については、Parsecのドキュメントをlexeme(で)チェックしてください。または、簡単なバージョンを自分で作成してください。ParsecToken

 lexeme parser = do result <- parser
                    spaces
                    return result

次に、すべてのトークン(数値リテラルなど)でこの関数を使用します。このようにすると、式の最初の空白だけを気にする必要があります。

と友達の詳細についてParsecTokenは、Parsecドキュメントの「字句解析」セクションを参照してください。

トークンを手動でスキップできる最初の部分を除いて、トークンのの空白のみをスキップすることは理にかなっています。ParsecTokenモジュールを使用しなくなった場合でも、このアプローチを採用する必要があります。

両側に空白を消費することを除けばtok、私のように動作するものはすでにあるようです。トークンの後の空白のみを消費し、入力の最初の空白を手動で無視するように変更します。それは(理想的には:))問題を修正するはずです。lexeme

于 2012-01-02T07:15:54.147 に答える