2017-04-20 9 views
0

私はハスケルで解析するのが本当に新しいですが、ほとんど意味があります。Parsec:オーバーラッピングパーサーの扱い

私は主に、より良い解析を学ぶためにテンプレートプログラムを構築しています。テンプレートは{{ value }}表記で値を補間できます。

{{ value }} 

This is normal Text 

{{ expression }} 

私はtemplateFromFile "./template.txt"を使用して、これを実行すると、私はエラーを取得する:

Left "./template.txt" (line 5, column 17): 
unexpected Directive " expression " 
ここ

は私の現在のパーサは、その後、私は、このようなファイルの何かにそれを実行し、

data Template a = Template [Either String a] 
data Directive = Directive String 

templateFromFile :: FilePath -> IO (Either ParseError (Template Directive)) 
templateFromFile = parseFromFile templateParser 

templateParser :: Parser (Template Directive) 
templateParser = do 
    tmp <- template 
    eof 
    return tmp 

template :: Parser (Template Directive) 
template = Template <$> many (dir <|> txt) 
    where 
     dir = Right <$> directive 
     txt = Left <$> many1 (anyChar <* notFollowedBy directive) 

directive :: Parser Directive 
directive = do 
    _ <- string "{{" 
    txt <- manyTill anyChar (string "}}") 
    return $ Directive txt 

です

どうしてこのようなことが起こり、どうすれば修正できますか?

私の基本的な理解は、many1 (anyChar <* notFollowedBy directive) は、次のディレクティブの開始まですべての文字を取得し、失敗してそのポイントまでの文字リストを返す必要があります。 これは前のmanyに戻り、dirの解析を再試行して成功する必要があります。明らかに何かが起こっている。私は 問題を解析する方法を考えているの間に他の場合 パーザーはほとんど重複しています。

私はこれをもっともっと慣用的に構造化する方法についていくつかのヒントをお伝えしたいと思います。私が愚かなやり方で何かをしているかどうか教えてください。乾杯!御時間ありがとうございます!

答えて

2

あなたにはいくつかの問題があります。まず、Parsecでは、パーサが入力を消費して失敗すると、それはエラーです。ですから、パーサとき:(文字ディレクティブが続くであるため)

anyChar <* notFollowedBy directive 

が失敗し、それがanyChar後は入力を消費している、そしてそれはすぐにエラーが発生し失敗しました。したがって、パーサ:

let p1 = many1 (anyChar <* notFollowedBy directive) 

は、命令に実行されると決して成功しません。たとえば:

let p2 = many1 (try (anyChar <* notFollowedBy directive)) 
parse p2 "" "okay {{}}" 

Right "okay"が得られ、第二の問題を明らかに:

parse p1 "" "okay" -- works 
parse p1 "" "oops {{}}" -- will fail after consuming "oops " 

あなたはtry句を挿入することによって、この問題を解決することができます。パーサーp2は、ディレクティブの直後にはスペースを含まないように、ディレクティブの後ろに続く文字だけを消費し、の文字を消費する手段を持たないため、スタックされません。 、現在の位置で、我々は文字をつかん前にディレクティブを見ていないされて最初にチェック

let p3 = many1 (notFollowedBy directive *> anyChar) 

は、あなたが実際に何かをしたいです。これが失敗すると入力を消費せずに失敗するため、try句は必要ありません。 (notFollowedByは、ドキュメントごとに、入力を消費することはありません。)

parse p3 "" "okay" -- returns Right "okay" 
parse p3 "" "okay {{}}" -- return Right "okay " 
parse p3 "" "{{fails}}" -- correctly fails w/o consuming input 

をので、とあなたの元の例を取る:

txt = Left <$> many1 (notFollowedBy directive *> anyChar) 

が正常に動作する必要があります。

0
many1 (anyChar <* notFollowedBy directive) 

これは、ディレクティブの後に続く文字のみを解析します。

{{ value }} 

This is normal Text 

{{ expression }} 

真ん中のテキストを解析すると、それが消費されていないディレクティブ(ディレクティブに続く、それはだから、まあ、文字)の前に改行を残し、最後tで停止するので、次の繰り返しになるが、あなたはディレクティブを解析しようとすると失敗します。次に、その改行でtxtを再試行します。パーサは、それがディレクティブの後に続くことを期待していませんが、それはエラーです。

+0

意味があります。私はそれを私が期待していることをするために最善に修正することができますか? –

+0

'txt = many1(notFollowedByディレクティブ*> anyChar)'は、ディレクティブごとに 'ディレクティブ'を2回実行しますが、最も簡単な修正です。 –