しかし、私はすでに非常に堅牢なパーサーを持っており、まさにそれを実行してくれます。
実際にはそれほど堅牢ではありません。パーサーには余分な括弧の問題があり、解析できません
((1) (2))
たとえば、一部の不正な入力に対して例外がスローされます。
singleP = Single . read <$> many digit
使用することがありますread "" :: Int
。
邪魔にならないように、優先順位引数は、括弧が必要かどうかを判断するために使用されます。
infixr 6 :+:
data a :+: b = a :+: b
data C = C Int
data D = D C
application の優先順位は の優先順位よりも高いため、C 12
の引数としてa を括弧で囲む必要はありませんが、 の引数として括弧で囲む必要があります。(:+:)
(:+:)
C 12
D
したがって、通常は次のようなものがあります
readsPrec p = needsParens (p >= precedenceLevel) someParser
wheresomeParser
は括弧で囲まずに入力からの値をneedsParens True thing
解析し、thing
括弧の間をneedsParens False thing
解析しますが、thing
オプションで括弧で囲まれた a を解析します [常に必要以上の括弧を受け入れる必要があり、((((((1))))))
としてうまく解析する必要がありInt
ます]。
パーサーは、リストやタプルなどを読み取るときに入力の一部を値の一部として解析するためreadsPrec p
に使用されるため、解析された値だけでなく、入力の残りの部分も返さなければなりません。
parsec
これにより、パーサーをパーサーに変換する簡単な方法は次のようにreadsPrec
なります。
withRemaining :: Parser a -> Parser (a, String)
withRemaining p = (,) <$> p <*> getInput
parsecToReadsPrec :: Parser a -> Int -> ReadS a
parsecToReadsPrec parsecParser prec input
= case parse (withremaining $ needsParens (prec >= threshold) parsecParser) "" input of
Left _ -> []
Right result -> [result]
ただし、GHC を使用している場合は、パーサーの代わりにReadPrec / ReadP
( を使用して構築された) パーサーを使用し、 の代わりに定義する方が望ましい場合があります。Text.ParserCombinators.ReadP[rec]
parsec
readPrec
readsPrec