doc: fix reference to Symantic.Typed
[haskell/symantic-parser.git] / src / Symantic / Parser / Machine / Generate.hs
index a268294f0fcf456a651443339162c78e18087365..bfe106d6650dba2a6a0de0a2380dfe620e44e8d4 100644 (file)
@@ -1,4 +1,6 @@
 {-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE DeriveAnyClass #-} -- For NFData instances
+{-# LANGUAGE DeriveGeneric #-} -- For NFData instances
 {-# LANGUAGE StandaloneDeriving #-} -- For Show (ParsingError inp)
 {-# LANGUAGE ConstraintKinds #-} -- For Dict
 {-# LANGUAGE TemplateHaskell #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 module Symantic.Parser.Machine.Generate where
 
+import Control.DeepSeq (NFData(..))
 import Control.Monad (Monad(..))
 import Data.Bool (Bool)
 import Data.Char (Char)
 import Data.Either (Either(..), either)
+import Data.Foldable (foldMap', toList, null)
 import Data.Function (($), (.), id, const, on)
 import Data.Functor (Functor, (<$>), (<$))
-import Data.Foldable (foldMap', toList, null)
 import Data.Int (Int)
 import Data.List.NonEmpty (NonEmpty(..))
 import Data.Map (Map)
 import Data.Maybe (Maybe(..))
-import Data.Eq (Eq(..))
 import Data.Ord (Ord(..), Ordering(..))
 import Data.Proxy (Proxy(..))
 import Data.Semigroup (Semigroup(..))
@@ -27,32 +29,36 @@ import Data.Set (Set)
 import Data.String (String)
 import Data.Traversable (Traversable(..))
 import Data.Typeable (Typeable)
+import Data.Word (Word8)
+import GHC.Generics (Generic)
+import GHC.Show (showCommaSpace)
 import Language.Haskell.TH (CodeQ)
 import Prelude ((+), (-), error)
-import Text.Show (Show(..))
--- import qualified Control.Monad.Trans.State.Strict as MT
+import Text.Show (Show(..), showParen, showString)
 import qualified Data.HashMap.Strict as HM
 import qualified Data.List as List
 import qualified Data.List.NonEmpty as NE
 import qualified Data.Map.Internal as Map_
-import qualified Data.Set.Internal as Set_
 import qualified Data.Map.Strict as Map
 import qualified Data.Set as Set
+import qualified Data.Set.Internal as Set_
 import qualified Language.Haskell.TH as TH
 import qualified Language.Haskell.TH.Syntax as TH
 
-import Symantic.Univariant.Letable
-import Symantic.Univariant.Trans
+import Symantic.Typed.Derive
+import Symantic.Typed.ObserveSharing
 import Symantic.Parser.Grammar.Combinators (Exception(..), Failure(..), SomeFailure(..), inputTokenProxy)
 import Symantic.Parser.Machine.Input
 import Symantic.Parser.Machine.Instructions
 import qualified Language.Haskell.TH.HideName as TH
-import qualified Symantic.Parser.Haskell as H
+import qualified Symantic.Typed.Lang as Prod
+import qualified Symantic.Typed.Optimize as Prod
 
 --import Debug.Trace
 
-genCode :: TermInstr a -> CodeQ a
-genCode = trans
+-- | Convenient utility to generate some final 'TH.CodeQ'.
+genCode :: Splice a -> CodeQ a
+genCode = derive . Prod.normalOrderReduction
 
 -- * Type 'Gen'
 -- | Generate the 'CodeQ' parsing the input.
@@ -69,12 +75,15 @@ data Gen inp vs a = Gen
 -- | @('generateCode' input mach)@ generates @TemplateHaskell@ code
 -- parsing the given 'input' according to the given 'Machine'.
 generateCode ::
-  Ord (InputToken inp) =>
+  {-
+  Eq (InputToken inp) =>
+  NFData (InputToken inp) =>
   Show (InputToken inp) =>
   Typeable (InputToken inp) =>
   TH.Lift (InputToken inp) =>
+  -}
   -- InputToken inp ~ Char =>
-  Input inp =>
+  Inputable inp =>
   Show (Cursor inp) =>
   Gen inp '[] a ->
   CodeQ (inp -> Either (ParsingError inp) a)
@@ -85,7 +94,7 @@ generateCode k = [|| \(input :: inp) ->
         finalRet = \_farInp _farExp v _inp -> Right v
         finalRaise :: forall b. (Catcher inp b)
           = \ !exn _failInp !farInp !farExp ->
-          Left ParsingErrorStandard
+          Left ParsingError
           { parsingErrorOffset = offset farInp
           , parsingErrorException = exn
           , parsingErrorUnexpected =
@@ -122,21 +131,46 @@ generateCode k = [|| \(input :: inp) ->
         }
       )
     ||]
-  where
 
 -- ** Type 'ParsingError'
 data ParsingError inp
-  =  ParsingErrorStandard
+  =  ParsingError
   {  parsingErrorOffset :: Offset
   ,  parsingErrorException :: Exception
-     -- | Note that if an 'FailureHorizon' greater than 1
+     -- | Note: if a 'FailureHorizon' greater than 1
      -- is amongst the 'parsingErrorExpecting'
-     -- then this is only the 'InputToken'
+     -- then 'parsingErrorUnexpected' is only the 'InputToken'
      -- at the begining of the expected 'Horizon'.
   ,  parsingErrorUnexpected :: Maybe (InputToken inp)
   ,  parsingErrorExpecting :: Set SomeFailure
-  }
-deriving instance Show (InputToken inp) => Show (ParsingError inp)
+  } deriving (Generic)
+deriving instance NFData (InputToken inp) => NFData (ParsingError inp)
+--deriving instance Show (InputToken inp) => Show (ParsingError inp)
+instance Show (InputToken inp) => Show (ParsingError inp) where
+  showsPrec p ParsingError{..} =
+    showParen (p >= 11) $
+      showString "ParsingErrorStandard {" .
+      showString "parsingErrorOffset = " .
+      showsPrec 0 parsingErrorOffset .
+      showCommaSpace .
+      showString "parsingErrorException = " .
+      showsPrec 0 parsingErrorException .
+      showCommaSpace .
+      showString "parsingErrorUnexpected = " .
+      showsPrec 0 parsingErrorUnexpected .
+      showCommaSpace .
+      showString "parsingErrorExpecting = fromList " .
+      showsPrec 0 (
+        -- Sort on the string representation
+        -- because the 'Ord' of the 'SomeFailure'
+        -- is based upon hashes ('typeRepFingerprint')
+        -- depending on packages' ABI and whether
+        -- cabal-install's setup is --inplace or not,
+        -- and that would be too unstable for golden tests.
+        List.sortBy (compare `on` show) $
+        Set.toList parsingErrorExpecting
+      ) .
+      showString "}"
 
 -- ** Type 'ErrorLabel'
 type ErrorLabel = String
@@ -217,9 +251,14 @@ data FarthestError inp = FarthestError
 -- | This is an inherited (top-down) context
 -- only present at compile-time, to build TemplateHaskell splices.
 data GenCtx inp vs a =
-  ( TH.Lift (InputToken inp)
-  , Cursorable (Cursor inp)
+  ( Cursorable (Cursor inp)
+  {-
+  , TH.Lift (InputToken inp)
   , Show (InputToken inp)
+  , Eq (InputToken inp)
+  , Typeable (InputToken inp)
+  , NFData (InputToken inp)
+  -}
   ) => GenCtx
   { valueStack :: ValueStack vs
   , catchStackByLabel :: Map Exception (NonEmpty (CodeQ (Catcher inp a)))
@@ -252,7 +291,7 @@ data GenCtx inp vs a =
 data ValueStack vs where
   ValueStackEmpty :: ValueStack '[]
   ValueStackCons ::
-    { valueStackHead :: TermInstr v
+    { valueStackHead :: Splice v
     , valueStackTail :: ValueStack vs
     } -> ValueStack (v ': vs)
 
@@ -269,7 +308,7 @@ instance InstrValuable Gen where
     { unGen = \ctx -> {-trace "unGen.lift2Value" $-} unGen k ctx
       { valueStack =
         let ValueStackCons y (ValueStackCons x vs) = valueStack ctx in
-        ValueStackCons (f H.:@ x H.:@ y) vs
+        ValueStackCons (f Prod..@ x Prod..@ y) vs
       }
     }
   swapValue k = k
@@ -287,8 +326,8 @@ instance InstrBranchable Gen where
       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 })
+          Left  x -> $$(unGen kx ctx{ valueStack = ValueStackCons (splice [||x||]) vs })
+          Right y -> $$(unGen ky ctx{ valueStack = ValueStackCons (splice [||y||]) vs })
       ||]
     }
   choicesBranch fs ks kd = Gen
@@ -300,7 +339,7 @@ instance InstrBranchable Gen where
     }
     where
     go ctx x (f:fs') (k:ks') = [||
-      if $$(genCode (H.optimizeTerm (f H.:@ x)))
+      if $$(genCode (f Prod..@ x))
       then
         let _ = "choicesBranch.then" in
         $$({-trace "unGen.choicesBranch.k" $-} unGen k ctx)
@@ -332,7 +371,7 @@ instance InstrExceptionable Gen where
         }
     , unGen = \ctx@GenCtx{} -> {-trace ("unGen.fail: "<>show exn) $-}
       if null fs
-      then [||
+      then [|| -- Raise without updating the farthest error.
           $$(raiseException ctx ExceptionFailure)
             ExceptionFailure
             {-failInp-}$$(input ctx)
@@ -367,8 +406,8 @@ instance InstrExceptionable Gen where
                 -- as they were when entering 'catch',
                 -- they will be available to 'loadInput', if any.
                 { valueStack =
-                    ValueStackCons (H.Term (input ctx)) $
-                    --ValueStackCons (H.Term [||exn||]) $
+                    ValueStackCons (splice (input ctx)) $
+                    --ValueStackCons (Prod.var [||exn||]) $
                     valueStack ctx
                 , horizonStack =
                     checkedHorizon ctx : horizonStack ctx
@@ -392,20 +431,12 @@ instance InstrExceptionable Gen where
         }
       ) ||]
     }
-
--- ** Type 'Catcher'
-type Catcher inp a =
-  Exception ->
-  {-failInp-}Cursor inp ->
-  {-farInp-}Cursor inp ->
-  {-farExp-}(Set SomeFailure) ->
-  Either (ParsingError inp) a
 instance InstrInputable Gen where
   pushInput k = k
     { unGen = \ctx ->
         {-trace "unGen.pushInput" $-}
         unGen k ctx
-          { valueStack = H.Term (input ctx) `ValueStackCons` valueStack ctx
+          { valueStack = splice (input ctx) `ValueStackCons` valueStack ctx
           , horizonStack = checkedHorizon ctx : horizonStack ctx
           }
     }
@@ -575,7 +606,7 @@ generateSuspend k ctx = [||
   let _ = $$(liftTypedString $ "suspend") in
   \farInp farExp v !inp ->
     $$({-trace "unGen.generateSuspend" $-} unGen k ctx
-      { valueStack = ValueStackCons ({-trace "unGen.generateSuspend.value" $-} H.Term [||v||]) (valueStack ctx)
+      { valueStack = ValueStackCons ({-trace "unGen.generateSuspend.value" $-} splice [||v||]) (valueStack ctx)
       , input = [||inp||]
       , farthestInput = [||farInp||]
       , farthestExpecting = [||farExp||]
@@ -600,12 +631,20 @@ generateResume k = Gen
     $$k
       $$(farthestInput ctx)
       $$(farthestExpecting ctx)
-      (let _ = "resume.genCode" in $$({-trace "unGen.generateResume.genCode" $-} genCode $ H.optimizeTerm $
-        valueStackHead $ valueStack ctx))
+      (let _ = "resume.genCode" in $$({-trace "unGen.generateResume.genCode" $-}
+        genCode $ valueStackHead $ valueStack ctx))
       $$(input ctx)
     ||]
   }
 
+-- ** Type 'Catcher'
+type Catcher inp a =
+  Exception ->
+  {-failInp-}Cursor inp ->
+  {-farInp-}Cursor inp ->
+  {-farExp-}(Set SomeFailure) ->
+  Either (ParsingError inp) a
+
 instance InstrJoinable Gen where
   defJoin (LetName n) sub k = k
     { unGen =
@@ -616,7 +655,7 @@ instance InstrJoinable Gen where
             -- Called by 'generateResume'.
             \farInp farExp v !inp ->
               $$({-trace ("unGen.defJoin.next: "<>show n) $-} unGen sub ctx
-                { valueStack = ValueStackCons (H.Term [||v||]) (valueStack ctx)
+                { valueStack = ValueStackCons (splice [||v||]) (valueStack ctx)
                 , input = [||inp||]
                 , farthestInput = [||farInp||]
                 , farthestExpecting = [||farExp||]
@@ -654,13 +693,18 @@ instance InstrJoinable Gen where
     }
 instance InstrReadable Char Gen where
   read fs p = checkHorizon . checkToken fs p
+instance InstrReadable Word8 Gen where
+  read fs p = checkHorizon . checkToken fs p
 
 checkHorizon ::
   forall inp vs a.
-  Eq (InputToken inp) =>
+  -- Those constraints are not used anyway
+  -- because (TH.Lift SomeFailure) uses 'inputTokenProxy'.
   Ord (InputToken inp) =>
-  Typeable (InputToken inp) =>
+  Show (InputToken inp) =>
   TH.Lift (InputToken inp) =>
+  NFData (InputToken inp) =>
+  Typeable (InputToken inp) =>
   {-ok-}Gen inp vs a ->
   Gen inp vs a
 checkHorizon ok = ok
@@ -682,7 +726,7 @@ checkHorizon ok = ok
         if checkedHorizon ctx >= 1
         then unGen ok ctx0{checkedHorizon = checkedHorizon ctx - 1}
         else let minHoriz =
-                    either (\err -> 0) id $
+                    either (\_err -> 0) id $
                     minReads $ finalGenAnalysis ctx ok in
           [||
           if $$(moreInput ctx)
@@ -736,21 +780,23 @@ finalGenAnalysis ctx k =
   finalGenAnalysisByLet ctx
 
 checkToken ::
-  Ord (InputToken inp) =>
-  TH.Lift (InputToken inp) =>
   Set SomeFailure ->
-  {-predicate-}TermInstr (InputToken inp -> Bool) ->
+  {-predicate-}Splice (InputToken inp -> Bool) ->
   {-ok-}Gen inp (InputToken inp ': vs) a ->
   Gen inp vs a
 checkToken fs p ok = ok
   { unGen = \ctx -> {-trace "unGen.read" $-} [||
     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 fs) ctx)
-    ||]
+    $$(genCode $
+      Prod.ifThenElse
+        (p Prod..@ splice [||c||])
+        (splice $ unGen ok ctx
+          { valueStack = ValueStackCons (splice [||c||]) (valueStack ctx)
+          , input = [||cs||]
+          })
+        (splice [||
+          let _ = "checkToken.else" in
+          $$(unGen (fail fs) ctx)
+        ||])
+    )||]
   }