test: sync
[haskell/symantic-parser.git] / src / Symantic / Parser / Machine / Generate.hs
index c4a22ee2ce56102df5e38d6f445a2a2e547d40f4..d2fb688c09751534f697a41d143c4e0eab0c1264 100644 (file)
@@ -9,19 +9,22 @@ import Control.Monad (Monad(..))
 import Data.Bool (Bool)
 import Data.Char (Char)
 import Data.Either (Either(..))
-import Data.Function (($))
--- import Data.Functor ((<$>))
+import Data.Function (($), (.))
+import Data.Functor ((<$>))
 import Data.Int (Int)
+import Data.List (minimum)
+import Data.Map (Map)
 import Data.Maybe (Maybe(..))
-import Data.Ord (Ord, Ordering(..))
+import Data.Ord (Ord(..), Ordering(..))
 import Data.Semigroup (Semigroup(..))
 import Data.Set (Set)
 import Language.Haskell.TH (CodeQ, Code(..))
-import Prelude (($!))
+import Prelude (($!), (+), (-))
 import Text.Show (Show(..))
-import qualified Data.Eq as Eq
+import qualified Data.Map.Strict as Map
 import qualified Data.Set as Set
 import qualified Language.Haskell.TH.Syntax as TH
+-- import qualified Control.Monad.Trans.Writer as Writer
 
 import Symantic.Univariant.Trans
 import Symantic.Parser.Grammar.Combinators (ErrorItem(..))
@@ -29,17 +32,30 @@ import Symantic.Parser.Machine.Input
 import Symantic.Parser.Machine.Instructions
 import qualified Symantic.Parser.Haskell as H
 
+genCode :: TermInstr a -> CodeQ a
+genCode = trans
+
 -- * Type 'Gen'
 -- | Generate the 'CodeQ' parsing the input.
-newtype Gen inp vs es a = Gen { unGen ::
-  GenCtx inp vs es a ->
-  CodeQ (Either (ParsingError inp) a)
-}
+data Gen inp vs es a = Gen
+  { minHorizon :: Map TH.Name Horizon -> Horizon
+    -- ^ Minimal input length required by the parser to not fail.
+    -- This requires to be given an 'horizonByName'
+    -- containing the 'Horizon's of all the 'TH.Name's
+    -- this parser 'call's, 'jump's or 'refJoin's to.
+  , unGen ::
+      GenCtx inp vs es a ->
+      CodeQ (Either (ParsingError inp) a)
+  }
 
 -- ** Type 'ParsingError'
 data ParsingError inp
   =  ParsingErrorStandard
   {  parsingErrorOffset :: Offset
+    -- | Note that if an 'ErrorItemHorizon' greater than 1
+    -- is amongst the 'parsingErrorExpecting'
+    -- then this is only the 'InputToken'
+    -- at the begining of the expected 'Horizon'.
   ,  parsingErrorUnexpected :: Maybe (InputToken inp)
   ,  parsingErrorExpecting :: Set (ErrorItem (InputToken inp))
   }
@@ -48,6 +64,14 @@ deriving instance Show (InputToken inp) => Show (ParsingError inp)
 -- ** Type 'Offset'
 type Offset = Int
 
+-- ** Type 'Horizon'
+-- | Synthetized minimal input length
+-- required for a successful parsing.
+-- Used with 'horizon' to factorize input length checks,
+-- instead of checking the input length
+-- one 'InputToken' by one 'InputToken' at each 'read'.
+type Horizon = Offset
+
 -- ** Type 'Cont'
 type Cont inp v a =
   {-farthestInput-}Cursor inp ->
@@ -78,9 +102,9 @@ data FarthestError inp = FarthestError
   }
 -}
 
--- | @('generate' input mach)@ generates @TemplateHaskell@ code
--- parsing given 'input' according to given 'mach'ine.
-generate ::
+-- | @('generateCode' input mach)@ generates @TemplateHaskell@ code
+-- parsing the given 'input' according to the given 'Machine'.
+generateCode ::
   forall inp ret.
   Ord (InputToken inp) =>
   Show (InputToken inp) =>
@@ -91,7 +115,7 @@ generate ::
   Show (Cursor inp) =>
   Gen inp '[] ('Succ 'Zero) ret ->
   CodeQ (Either (ParsingError inp) ret)
-generate input (Gen k) = [||
+generateCode input k = [||
   -- Pattern bindings containing unlifted types
   -- should use an outermost bang pattern.
   let !(# init, readMore, readNext #) = $$(cursorOf input) in
@@ -105,7 +129,7 @@ generate input (Gen k) = [||
             else Nothing
         , parsingErrorExpecting = Set.fromList farExp
         } in
-  $$(k GenCtx
+  $$(unGen k GenCtx
     { valueStack = ValueStackEmpty
     , failStack = FailStackCons [||finalFail||] FailStackEmpty
     , retCode = [||finalRet||]
@@ -115,6 +139,8 @@ generate input (Gen k) = [||
     -- , farthestError = [||Nothing||]
     , farthestInput = [||init||]
     , farthestExpecting = [|| [] ||]
+    , horizon = 0
+    , horizonByName = Map.empty
     })
   ||]
 
@@ -127,290 +153,304 @@ data GenCtx inp vs (es::Peano) a =
   -- , InputToken inp ~ Char
   ) => GenCtx
   { valueStack :: ValueStack vs
-  , failStack :: FailStack inp es a
+  , failStack :: FailStack inp a es
+  --, failStacks :: FailStack inp es a
   , retCode :: CodeQ (Cont inp a a)
   , input :: CodeQ (Cursor inp)
   , moreInput :: CodeQ (Cursor inp -> Bool)
   , nextInput :: CodeQ (Cursor inp -> (# InputToken inp, Cursor inp #))
   , farthestInput :: CodeQ (Cursor inp)
   , farthestExpecting :: CodeQ [ErrorItem (InputToken inp)]
+    -- | Remaining horizon
+  , horizon :: Offset
+    -- | Horizon for each 'call' or 'jump'.
+  , horizonByName :: Map TH.Name Offset
   }
 
 -- ** Type 'ValueStack'
 data ValueStack vs where
   ValueStackEmpty :: ValueStack '[]
   ValueStackCons ::
-    -- TODO: maybe use H.Haskell instead of CodeQ ?
-    -- as in https://github.com/j-mie6/ParsleyHaskell/popFail/3ec0986a5017866919a6404c14fe78678b7afb46
-    { valueStackHead :: CodeQ v
+    { valueStackHead :: TermInstr v
     , valueStackTail :: ValueStack vs
     } -> ValueStack (v ': vs)
 
 -- ** Type 'FailStack'
-data FailStack inp es a where
-  FailStackEmpty :: FailStack inp 'Zero a
+data FailStack inp a es where
+  FailStackEmpty :: FailStack inp a 'Zero
   FailStackCons ::
     { failStackHead :: CodeQ (FailHandler inp a)
-    , failStackTail :: FailStack inp es a
+    , failStackTail :: FailStack inp a es
     } ->
-    FailStack inp ('Succ es) a
+    FailStack inp a ('Succ es)
 
 instance Stackable Gen where
-  push x k = Gen $ \ctx -> unGen k ctx
-    { valueStack = ValueStackCons (liftCode x) (valueStack ctx) }
-  pop k = Gen $ \ctx -> unGen k ctx
-    { valueStack = valueStackTail (valueStack ctx) }
-  liftI2 f k = Gen $ \ctx -> unGen k ctx
-    { valueStack =
-      let ValueStackCons y (ValueStackCons x xs) = valueStack ctx in
-      ValueStackCons (liftCode2 f x y) xs
+  push x k = k
+    { unGen = \ctx -> unGen k ctx
+      { valueStack = ValueStackCons x (valueStack ctx) }
+    }
+  pop k = k
+    { unGen = \ctx -> unGen k ctx
+      { valueStack = valueStackTail (valueStack ctx) }
     }
-  swap k = Gen $ \ctx -> unGen k ctx
-    { valueStack =
+  liftI2 f k = k
+    { unGen = \ctx -> unGen k ctx
+      { valueStack =
         let ValueStackCons y (ValueStackCons x xs) = valueStack ctx in
-        ValueStackCons x (ValueStackCons y xs)
+        ValueStackCons (f H.:@ x H.:@ y) xs
+      }
+    }
+  swap k = k
+    { unGen = \ctx -> unGen k ctx
+      { valueStack =
+          let ValueStackCons y (ValueStackCons x xs) = valueStack ctx in
+          ValueStackCons x (ValueStackCons y xs)
+      }
     }
 instance Branchable Gen where
-  case_ kx ky = Gen $ \ctx ->
-    let ValueStackCons v vs = valueStack ctx in
-    [||
-      case $$v of
-        Left  x -> $$(unGen kx ctx{ valueStack = ValueStackCons [||x||] vs })
-        Right y -> $$(unGen ky ctx{ valueStack = ValueStackCons [||y||] vs })
-    ||]
-  choices fs ks kd = Gen $ \ctx ->
-    let ValueStackCons v vs = valueStack ctx in
-    go ctx{valueStack = vs} v fs ks
+  caseI kx ky = Gen
+    { minHorizon = \ls ->
+      minHorizon kx ls `min` minHorizon ky ls
+    , unGen = \ctx ->
+      let ValueStackCons v vs = valueStack ctx in
+      [||
+        case $$(genCode v) of
+          Left  x -> $$(unGen kx ctx{ valueStack = ValueStackCons (H.Term [||x||]) vs })
+          Right y -> $$(unGen ky ctx{ valueStack = ValueStackCons (H.Term [||y||]) vs })
+      ||]
+    }
+  choices fs ks kd = Gen
+    { minHorizon = \ls -> minimum $
+        minHorizon kd ls :
+        (($ ls) . minHorizon <$> ks)
+    , unGen = \ctx ->
+      let ValueStackCons v vs = valueStack ctx in
+      go ctx{valueStack = vs} v fs ks
+    }
     where
-    go ctx x (f:fs') (Gen k:ks') = [||
-      if $$(liftCode1 f x) then $$(k ctx)
+    go ctx x (f:fs') (k:ks') = [||
+      if $$(genCode (f H.:@ x))
+      then $$(unGen k ctx)
       else $$(go ctx x fs' ks')
       ||]
     go ctx _ _ _ = unGen kd ctx
 instance Failable Gen where
-  fail failExp = Gen $ \ctx@GenCtx{} -> [||
-    let (# farInp, farExp #) =
-          case $$compareOffset $$(farthestInput ctx) $$(input ctx) of
-            LT -> (# $$(input ctx), failExp #)
-            EQ -> (# $$(farthestInput ctx), ($$(farthestExpecting ctx) <> failExp) #)
-            GT -> (# $$(farthestInput ctx), $$(farthestExpecting ctx) #) in
-    {-
-    trace ("fail: "
-      <>" failExp="<>show @[ErrorItem Char] failExp
-      <>" farthestExpecting="<>show @[ErrorItem Char] ($$(farthestExpecting ctx))
-      <>" farExp="<>show @[ErrorItem Char] farExp) $
-    -}
-    $$(failStackHead (failStack ctx))
-      $$(input ctx) farInp farExp
-    ||]
-  popFail k = Gen $ \ctx ->
-    let FailStackCons _e es = failStack ctx in
-    unGen k ctx{failStack = es}
-  catchFail ok ko = Gen $ \ctx@GenCtx{} -> [||
-    let _ = "catchFail" in $$(unGen ok ctx
-      { failStack = FailStackCons [|| \(!failInp) (!farInp) (!farExp) ->
-          -- trace ("catchFail: " <> "farExp="<>show farExp) $
-          $$(unGen ko ctx
-            -- Push the input as it was when entering the catchFail.
-            { valueStack = ValueStackCons (input ctx) (valueStack ctx)
-            -- Move the input to the failing position.
-            , input = [||failInp||]
-            -- Set the farthestInput to the farthest computed by 'fail'
-            , farthestInput = [||farInp||]
-            , farthestExpecting = [||farExp||]
-            })
-        ||] (failStack ctx)
-      })
-    ||]
+  fail failExp = Gen
+    { minHorizon = \_hs -> 0
+    , unGen = \ctx@GenCtx{} -> [||
+      let (# farInp, farExp #) =
+            case $$compareOffset $$(farthestInput ctx) $$(input ctx) of
+              LT -> (# $$(input ctx), failExp #)
+              EQ -> (# $$(farthestInput ctx), ($$(farthestExpecting ctx) <> failExp) #)
+              GT -> (# $$(farthestInput ctx), $$(farthestExpecting ctx) #) in
+      $$(failStackHead (failStack ctx))
+        $$(input ctx) farInp farExp
+      ||]
+    }
+  popFail k = k
+    { unGen = \ctx ->
+      unGen k ctx{failStack = failStackTail (failStack ctx)}
+    }
+  catchFail ok ko = Gen
+    { minHorizon = \ls -> minHorizon ok ls `min` minHorizon ko ls
+    , unGen = \ctx@GenCtx{} -> unGen ok ctx
+        { failStack = FailStackCons [|| \(!failInp) (!farInp) (!farExp) ->
+            -- trace ("catchFail: " <> "farExp="<>show farExp) $
+            $$(unGen ko ctx
+              -- Push the input as it was when entering the catchFail.
+              { valueStack = ValueStackCons (H.Term (input ctx)) (valueStack ctx)
+              -- Move the input to the failing position.
+              , input = [||failInp||]
+              -- Set the farthestInput to the farthest computed by 'fail'
+              , farthestInput = [||farInp||]
+              , farthestExpecting = [||farExp||]
+              })
+          ||] (failStack ctx)
+        }
+    }
 instance Inputable Gen where
-  loadInput k = Gen $ \ctx ->
-    let ValueStackCons input vs = valueStack ctx in
-    unGen k ctx{valueStack = vs, input}
-  pushInput k = Gen $ \ctx ->
-    unGen k ctx{valueStack = ValueStackCons (input ctx) (valueStack ctx)}
+  loadInput k = k
+    { unGen = \ctx ->
+      let ValueStackCons input vs = valueStack ctx in
+      unGen k ctx
+        { valueStack = vs
+        , input = genCode input
+        , horizon = 0
+        }
+    }
+  pushInput k = k
+    { unGen = \ctx ->
+      unGen k ctx{valueStack = ValueStackCons (H.Term (input ctx)) (valueStack ctx)}
+    }
 instance Routinable Gen where
-  call (LetName n) k = Gen $ \ctx -> [||
-    let _ = "call" in
-    $$(Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
-      $$(suspend k ctx)
-      $$(input ctx)
-      $! $$(failStackHead (failStack ctx))
-    ||]
-  jump (LetName n) = Gen $ \ctx -> [||
-    let _ = "jump" in
-    $$(Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
-      $$(retCode ctx)
-      $$(input ctx)
-      $! $$(failStackHead (failStack ctx))
-    ||]
-  ret = Gen $ \ctx -> unGen (resume (retCode ctx)) ctx
-  subroutine (LetName n) sub k = Gen $ \ctx -> Code $ TH.unsafeTExpCoerce $ do
-    body <- TH.unTypeQ $ TH.examineCode $ [|| -- buildRec in Parsley
-      -- SubRoutine
-      -- Why using $! at call site and not ! here on ko?
-      \ !ok !inp ko ->
-        $$(unGen sub ctx
-          { valueStack = ValueStackEmpty
-          , failStack = FailStackCons [||ko||] FailStackEmpty
-          , input = [||inp||]
-          , retCode = [||ok||]
-          -- , farthestInput = [|inp|]
-          -- , farthestExpecting = [|| [] ||]
-          })
+  call (LetName n) k = k
+    { minHorizon = (Map.! n)
+    , unGen = \ctx -> [||
+      let _ = "call" in
+      $$(Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
+        {-ok-}$$(generateSuspend k ctx)
+        $$(input ctx)
+        $! $$(failStackHead (failStack ctx))
       ||]
-    let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
-    expr <- TH.unTypeQ (TH.examineCode (unGen k ctx))
-    return (TH.LetE [decl] expr)
+    }
+  jump (LetName n) = Gen
+    { minHorizon = (Map.! n)
+    , unGen = \ctx -> [||
+      let _ = "jump" in
+      $$(Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
+        {-ok-}$$(retCode ctx)
+        $$(input ctx)
+        $! $$(failStackHead (failStack ctx))
+      ||]
+    }
+  ret = Gen
+    { minHorizon = \_hs -> 0
+    , unGen = \ctx -> unGen (generateResume (retCode ctx)) ctx
+    }
+  subroutine (LetName n) sub k = Gen
+    { minHorizon = \hs ->
+        minHorizon k $
+          Map.insert n (minHorizon sub (Map.insert n 0 hs)) hs
+    , unGen = \ctx -> Code $ TH.unsafeTExpCoerce $ do
+      body <- TH.unTypeQ $ TH.examineCode $ [|| -- buildRec in Parsley
+        -- SubRoutine
+        -- Why using $! at call site and not ! here on ko?
+        \ !ok !inp ko ->
+          $$(unGen sub ctx
+            { valueStack = ValueStackEmpty
+            , failStack = FailStackCons [||ko||] FailStackEmpty
+            , input = [||inp||]
+            , retCode = [||ok||]
+            -- , farthestInput = [|inp|]
+            -- , farthestExpecting = [|| [] ||]
+            , horizon = 0
+            , horizonByName = Map.insert n 0 (horizonByName ctx)
+            })
+        ||]
+      let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
+      expr <- TH.unTypeQ (TH.examineCode (unGen k ctx
+        { horizonByName =
+            Map.insert n
+              (minHorizon sub
+                (Map.insert n 0 (horizonByName ctx)))
+              (horizonByName ctx)
+        }))
+      return (TH.LetE [decl] expr)
+    }
 
-suspend ::
+-- | Generate a continuation to be called with 'generateResume',
+-- used when 'call' 'ret'urns.
+generateSuspend ::
   {-k-}Gen inp (v ': vs) es a ->
   GenCtx inp vs es a ->
   CodeQ (Cont inp v a)
-suspend k ctx = [||
+generateSuspend k ctx = [||
   let _ = "suspend" in
   \farInp farExp v !inp ->
     $$(unGen k ctx
-      { valueStack = ValueStackCons [||v||] (valueStack ctx)
+      { valueStack = ValueStackCons (H.Term [||v||]) (valueStack ctx)
       , input = [||inp||]
       , farthestInput = [||farInp||]
       , farthestExpecting = [||farExp||]
+      , horizon = 0
       }
     )
   ||]
 
-resume :: CodeQ (Cont inp v a) -> Gen inp (v ': vs) es a
-resume k = Gen $ \ctx -> [||
-  let _ = "resume" in
-  $$k
-    $$(farthestInput ctx)
-    $$(farthestExpecting ctx)
-    $$(valueStackHead (valueStack ctx))
-    $$(input ctx)
-  ||]
+-- | Generate a call to the 'generateSuspend' continuation,
+-- used when 'call' 'ret'urns.
+generateResume ::
+  CodeQ (Cont inp v a) ->
+  Gen inp (v ': vs) es a
+generateResume k = Gen
+  { minHorizon = \_hs -> 0
+  , unGen = \ctx -> [||
+    let _ = "resume" in
+    $$k
+      $$(farthestInput ctx)
+      $$(farthestExpecting ctx)
+      (let _ = "resume.genCode" in $$(genCode (valueStackHead (valueStack ctx))))
+      $$(input ctx)
+    ||]
+  }
 
 instance Joinable Gen where
-  defJoin (LetName n) sub k = Gen $ \ctx -> Code $ TH.unsafeTExpCoerce $ do
-    body <- TH.unTypeQ $ TH.examineCode $ [||
-      \farInp farExp v !inp ->
-        $$(unGen sub ctx
-          { valueStack = ValueStackCons [||v||] (valueStack ctx)
-          , input = [||inp||]
-          , farthestInput = [||farInp||]
-          , farthestExpecting = [||farExp||]
-          })
-      ||]
-    let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
-    expr <- TH.unTypeQ (TH.examineCode (unGen k ctx))
-    return (TH.LetE [decl] expr)
-  refJoin (LetName n) =
-    resume (Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
-instance Readable Gen Char where
-  read farExp p k =
-    -- TODO: piggy bank
-    maybeEmitCheck (Just 1) k
-    where
-    maybeEmitCheck Nothing ok = sat (liftCode p) ok (fail farExp)
-    maybeEmitCheck (Just n) ok = Gen $ \ctx ->
-      let FailStackCons e es = failStack ctx in
-      [||
-      let readFail = $$(e) in -- Factorize failure code
-      $$((`unGen` ctx{failStack = FailStackCons [||readFail||] es}) $ emitLengthCheck n
-        {-ok-}(sat (liftCode p) ok
-          {-ko-}(fail farExp))
-        {-ko-}(fail farExp))
-      ||]
+  defJoin (LetName n) sub k = k
+    { minHorizon = \hs ->
+        minHorizon k $
+          Map.insert n (minHorizon sub (Map.insert n 0 hs)) hs
+    , unGen = \ctx -> Code $ TH.unsafeTExpCoerce $ do
+      body <- TH.unTypeQ $ TH.examineCode $ [||
+        \farInp farExp v !inp ->
+          $$(unGen sub ctx
+            { valueStack = ValueStackCons (H.Term [||v||]) (valueStack ctx)
+            , input = [||inp||]
+            , farthestInput = [||farInp||]
+            , farthestExpecting = [||farExp||]
+            , horizon = 0
+            , horizonByName = Map.insert n 0 (horizonByName ctx)
+            })
+        ||]
+      let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
+      expr <- TH.unTypeQ (TH.examineCode (unGen k ctx
+        { horizonByName =
+            Map.insert n
+              (minHorizon sub
+                (Map.insert n 0 (horizonByName ctx)))
+              (horizonByName ctx)
+        }))
+      return (TH.LetE [decl] expr)
+    }
+  refJoin (LetName n) = (generateResume (Code (TH.unsafeTExpCoerce (return (TH.VarE n)))))
+    { minHorizon = (Map.! n)
+    }
+instance Readable Char Gen where
+  read farExp p = checkHorizon . checkToken farExp p
 
-sat ::
+checkHorizon ::
+  TH.Lift (InputToken inp) =>
+  {-ok-}Gen inp vs ('Succ es) a ->
+  Gen inp vs ('Succ es) a
+checkHorizon ok = ok
+  { minHorizon = \hs -> 1 + minHorizon ok hs
+  , unGen = \ctx0@GenCtx{failStack = FailStackCons e es} -> [||
+      -- Factorize failure code
+      let readFail = $$(e) in
+      $$(
+        let ctx = ctx0{ failStack = FailStackCons [||readFail||] es } in
+        if horizon ctx >= 1
+        then unGen ok ctx0{horizon = horizon ctx - 1}
+        else let minHoz = minHorizon ok (horizonByName ctx) in
+          [||
+          if $$(moreInput ctx)
+               $$(if minHoz > 0
+                 then [||$$shiftRight minHoz $$(input ctx)||]
+                 else input ctx)
+          then $$(unGen ok ctx{horizon = minHoz})
+          else let _ = "checkHorizon.else" in
+            $$(unGen (fail [ErrorItemHorizon (minHoz + 1)]) ctx)
+          ||]
+      )
+    ||]
+  }
+
+checkToken ::
   forall inp vs es a.
-  -- Cursorable (Cursor inp) =>
-  -- InputToken inp ~ Char =>
   Ord (InputToken inp) =>
   TH.Lift (InputToken inp) =>
-  {-predicate-}CodeQ (InputToken inp -> Bool) ->
+  [ErrorItem (InputToken inp)] ->
+  {-predicate-}TermInstr (InputToken inp -> Bool) ->
   {-ok-}Gen inp (InputToken inp ': vs) ('Succ es) a ->
-  {-ko-}Gen inp vs ('Succ es) a ->
   Gen inp vs ('Succ es) a
-sat p ok ko = Gen $ \ctx -> [||
-  let !(# c, cs #) = $$(nextInput ctx) $$(input ctx) in
-  if $$p c
-  then $$(unGen ok ctx
-    { valueStack = ValueStackCons [||c||] (valueStack ctx)
-    , input = [||cs||]
-    })
-  else let _ = "sat.else" in $$(unGen ko ctx)
-  ||]
-
-{-
-evalSat ::
-  -- Cursorable inp =>
-  -- HandlerOps inp =>
-  InstrPure (Char -> Bool) ->
-  Gen inp (Char ': vs) ('Succ es) a ->
-  Gen inp vs ('Succ es) a
-evalSat p k = do
-  bankrupt <- asks isBankrupt
-  hasChange <- asks hasCoin
-  if | bankrupt -> maybeEmitCheck (Just 1) <$> k
-     | hasChange -> maybeEmitCheck Nothing <$> local spendCoin k
-     | otherwise -> local breakPiggy (maybeEmitCheck . Just <$> asks coins <*> local spendCoin k)
-  where
-  maybeEmitCheck Nothing mk ctx = sat (genDefunc p) mk (raise ctx) ctx
-  maybeEmitCheck (Just n) mk ctx =
-    [|| let bad = $$(raise ctx) in $$(emitLengthCheck n (sat (genDefunc p) mk [||bad||]) [||bad||] ctx)||]
--}
-
-emitLengthCheck ::
-  TH.Lift (InputToken inp) =>
-  Int -> Gen inp vs es a -> Gen inp vs es a -> Gen inp vs es a
-emitLengthCheck 0 ok _ko = ok
-emitLengthCheck 1 ok ko = Gen $ \ctx -> [||
-  if $$(moreInput ctx) $$(input ctx)
-  then $$(unGen ok ctx)
-  else let _ = "sat.length-check.else" in $$(unGen ko ctx)
-  ||]
-{-
-emitLengthCheck n ok ko ctx = Gen $ \ctx -> [||
-  if $$moreInput ($$shiftRight $$(input ctx) (n - 1))
-  then $$(unGen ok ctx)
-  else $$(unGen ko ctx {farthestExpecting = [||farExp||]})
-  ||]
--}
-
-
-liftCode :: InstrPure a -> CodeQ a
-liftCode = trans
-{-# INLINE liftCode #-}
-
-liftCode1 :: InstrPure (a -> b) -> CodeQ a -> CodeQ b
-liftCode1 p a = case p of
-  InstrPureSameOffset -> [|| $$sameOffset $$a ||]
-  InstrPureHaskell h -> go a h
-  where
-  go :: CodeQ a -> H.Haskell (a -> b) -> CodeQ b
-  go qa = \case
-    (H.:$) -> [|| \x -> $$qa x ||]
-    (H.:.) -> [|| \g x -> $$qa (g x) ||]
-    H.Flip -> [|| \x y -> $$qa y x ||]
-    (H.:.) H.:@ f H.:@ g -> [|| $$(go (go qa g) f) ||]
-    H.Const -> [|| \_ -> $$qa ||]
-    H.Flip H.:@ H.Const -> H.id
-    h@(H.Flip H.:@ _f) -> [|| \x -> $$(liftCode2 (InstrPureHaskell h) qa [||x||]) ||]
-    H.Eq x -> [|| $$(trans x) Eq.== $$qa ||]
-    H.Id -> qa
-    h -> [|| $$(trans h) $$qa ||]
+checkToken farExp p ok = ok
+  { unGen = \ctx -> [||
+    let !(# c, cs #) = $$(nextInput ctx) $$(input ctx) in
+    if $$(genCode p) c
+    then $$(unGen ok ctx
+      { valueStack = ValueStackCons (H.Term [||c||]) (valueStack ctx)
+      , input = [||cs||]
+      })
+    else let _ = "checkToken.else" in $$(unGen (fail farExp) ctx)
+    ||]
+  }
 
-liftCode2 :: InstrPure (a -> b -> c) -> CodeQ a -> CodeQ b -> CodeQ c
-liftCode2 p a b = case p of
-  InstrPureSameOffset -> [|| $$sameOffset $$a $$b ||]
-  InstrPureHaskell h -> go a b h
-  where
-  go :: CodeQ a -> CodeQ b -> H.Haskell (a -> b -> c) -> CodeQ c
-  go qa qb = \case
-    (H.:$) -> [|| $$qa $$qb ||]
-    (H.:.) -> [|| \x -> $$qa ($$qb x) ||]
-    H.Flip -> [|| \x -> $$qa x $$qb ||]
-    H.Flip H.:@ H.Const -> [|| $$qb ||]
-    H.Flip H.:@ f -> go qb qa f
-    H.Const -> [|| $$qa ||]
-    H.Cons -> [|| $$qa : $$qb ||]
-    h -> [|| $$(trans h) $$qa $$qb ||]