tl; dr、バックトラックを制限できるパーサーを実装するにはどうすればよいですか?パーサーはモナド変換子スタックです。
このアプローチの論文、ブログ、または実装例は見つかりませんでした。バックトラッキングを制限するための一般的なアプローチは、コンストラクターが追加されたデータ型、またはバックトラッキングがデフォルトでオフになっているParsecアプローチのようです。
私の現在の実装(commit
コンビネータを使用、以下を参照)は間違っています。型、型クラスに属しているかどうかはわかりません。私のインスタンスは、本来あるべきと思われるほど一般的ではありません。
誰かがこれをきれいに行う方法を説明したり、私にリソースを教えてもらえますか?
現在のコードを以下に追加しました。投稿が長すぎてごめんなさい!
スタック:
StateT
MaybeT/ListT
Either e
意図は、バックトラッキングが中間層で動作することです-Nothing
または空のリストは必ずしもエラーを生成しません、それは単に別のブランチを試す必要があることを意味します-一方、最下層はエラーのためのものです(いくつかの文脈で情報)解析をすぐに中止します。
{-# LANGUAGE NoMonomorphismRestriction, FunctionalDependencies,
FlexibleInstances, UndecidableInstances #-}
import Control.Monad.Trans.State (StateT(..))
import Control.Monad.State.Class (MonadState(..))
import Control.Monad.Trans.Maybe (MaybeT(..))
import Control.Monad.Trans.List (ListT(..))
import Control.Monad (MonadPlus(..), guard)
type Parser e t mm a = StateT [t] (mm (Either e)) a
newtype DParser e t a =
DParser {getDParser :: Parser e t MaybeT a}
instance Monad (DParser e t) where
return = DParser . return
(DParser d) >>= f = DParser (d >>= (getDParser . f))
instance MonadPlus (DParser e t) where
mzero = DParser (StateT (const (MaybeT (Right Nothing))))
mplus = undefined -- will worry about later
instance MonadState [t] (DParser e t) where
get = DParser get
put = DParser . put
いくつかの構文解析クラス:
class (Monad m) => MonadParser t m n | m -> t, m -> n where
item :: m t
parse :: m a -> [t] -> n (a, [t])
class (Monad m, MonadParser t m n) => CommitParser t m n where
commit :: m a -> m a
それらのインスタンス:
instance MonadParser t (DParser e t) (MaybeT (Either e)) where
item =
get >>= \xs -> case xs of
(y:ys) -> put ys >> return y;
[] -> mzero;
parse = runStateT . getDParser
instance CommitParser t (DParser [t] t) (MaybeT (Either [t])) where
commit p =
DParser (
StateT (\ts -> MaybeT $ case runMaybeT (parse p ts) of
Left e -> Left e;
Right Nothing -> Left ts;
Right (Just x) -> Right (Just x);))
そして、さらにいくつかのコンビネータ:
satisfy f =
item >>= \x ->
guard (f x) >>
return x
literal x = satisfy (== x)
次に、これらのパーサー:
ab = literal 'a' >> literal 'b'
ab' = literal 'a' >> commit (literal 'b')
これらの結果を与える:
> myParse ab "abcd"
Right (Just ('b',"cd")) -- succeeds
> myParse ab' "abcd"
Right (Just ('b',"cd")) -- 'commit' doesn't affect success
> myParse ab "acd"
Right Nothing -- <== failure but not an error
> myParse ab' "acd"
Left "cd" -- <== error b/c of 'commit'