Skip to content

Commit

Permalink
Core overhaul (now safe!)
Browse files Browse the repository at this point in the history
  • Loading branch information
noughtmare committed Apr 25, 2023
1 parent 32550a2 commit 4053ab9
Show file tree
Hide file tree
Showing 2 changed files with 193 additions and 82 deletions.
2 changes: 1 addition & 1 deletion gigaparsec.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ source-repository head
location: https://github.com/noughtmare/gigaparsec

common common
build-depends: base >= 4.14 && <5, containers
build-depends: base >= 4.14 && <5, containers, free
ghc-options: -Wall
default-language: GHC2021

Expand Down
273 changes: 192 additions & 81 deletions src/Gigaparsec/Core.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,147 +6,258 @@
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}

module Gigaparsec.Core where

import qualified Data.Set as Set
import Data.Set (Set)
import qualified Data.Map as Map
import Data.Map (Map)
import Unsafe.Coerce ( unsafeCoerce )
import GHC.Exts (Any)
import Data.Maybe (fromMaybe)
import Data.Kind
import Control.Applicative
import Data.Functor.Compose
import Control.Applicative ( asum, Alternative((<|>)) )
import Data.Functor.Compose ( Compose(Compose) )
import Control.Applicative.Free ( hoistAp, Ap(..) )
import Data.Proxy ( Proxy(..) )
import Data.Char (intToDigit)

data Action m a where
Match :: Char -> Action m ()
Descend :: m a -> Action m a
data (f + g) a = L (f a) | R (g a) deriving Show

type family Equal a b where
Equal a a = True
Equal a b = False

class f < g where
inj :: f a -> g a

instance In (Equal f g) f g h => f < (g + h) where
inj = injIn (Proxy :: Proxy (Equal f g))

class In b f g h where
injIn :: Proxy b -> f a -> (g + h) a

instance (f ~ g) => In True f g h where
injIn Proxy = L
instance f < h => In False f g h where
injIn Proxy = R . inj

instance (OrdF f, OrdF g) => OrdF (f + g) where
compareF (L x) (L y) = compareF x y
compareF (L _) (R _) = LT
compareF (R _) (L _) = GT
compareF (R x) (R y) = compareF x y

data Match a where
Match :: Char -> Match ()
deriving instance Show (Match a)

instance OrdF Match where
compareF (Match x) (Match y) = compare x y

send :: f < g => f a -> Alt g a
send x = Alt [Ap (inj x) (Pure id)]

match :: Match < f => Char -> Alt f ()
match c = send (Match c)

-- Free alternative, see https://hackage.haskell.org/package/free-5.2/docs/Control-Alternative-Free.html
-- But this one is higher order.
-- But this one also satisfies right distributivity: f <*> (x <|> y) = (f <*> x) <|> (f <*> y)

newtype Alt f a = Alt { alternatives :: [Ap f a] }
deriving (Functor, Applicative, Alternative) via Compose [] (Ap f)

hoistAlt :: (forall x. f x -> g x) -> Alt f a -> Alt g a
hoistAlt f (Alt xs) = Alt (fmap (hoistAp f) xs)

type Alt :: (Type -> Type) -> ((Type -> Type) -> Type -> Type) -> Type -> Type
newtype Alt m f a = Alt [AltF m f a]
deriving (Functor, Applicative, Alternative) via Compose [] (AltF m f)
type AltF :: (Type -> Type) -> ((Type -> Type) -> Type -> Type) -> Type -> Type
data AltF m f a = Pure a | forall b. Ap (f m b) (AltF m f (b -> a))
deriving instance Functor (AltF m f)
instance Applicative (AltF m f) where
pure = Pure
Pure f <*> q = fmap f q
Ap x p <*> q = Ap x (flip <$> p <*> q)
data Pa f a b where
Erup :: Pa f a a
Pa :: Pa f a b -> f c -> Pa f (c -> a) b
deriving instance (forall c. Show (f c)) => Show (Pa f a b)

type Name :: Type -> Type -> Type
newtype Name n a = Name n deriving Show
comparePa :: OrdF f => Pa f a b -> Pa f c d -> Ordering
comparePa Erup Erup = EQ
comparePa Erup _ = LT
comparePa _ Erup = GT
comparePa (Pa xs x) (Pa ys y) = compareF x y <> comparePa xs ys

newtype G n = G (Map n [AltF (Name n) Action Any])
showAp :: (forall x. Show (f x)) => Ap f a -> String
showAp Pure{} = "Pure"
showAp (Ap x xs) = "(Ap (" ++ show x ++ ") (" ++ showAp xs ++ "))"

lookupG :: forall n a. Ord n => G n -> Name n a -> [AltF (Name n) Action a]
lookupG (G m) (Name n) = unsafeCoerce (m Map.! n)
compareAp :: OrdF f => Ap f a -> Ap f b -> Ordering
compareAp Pure{} Pure{} = EQ
compareAp Pure{} _ = LT
compareAp _ Pure{} = GT
compareAp (Ap x xs) (Ap y ys) = compareF x y <> compareAp xs ys

data Deps n a b where
Self :: Deps n a a
Dep :: !(Name n b) -> !Int -> !Int -> !(Deps n a c) -> Deps n a (b -> c)
deriving instance Show n => Show (Deps n a b)
newtype G f g = G { lookupG :: forall a. f a -> Alt (Match + g) a }

data Slot n a = forall b.
data Slot f a = forall b.
Slot
!(Name n a) -- ^ The name of the current nonterminal
!Int -- ^ The number of the current alternative
!Int -- ^ The number of symbols that have already been processed
!(Deps n a b) -- ^ The dependencies, i.e. the (external) nonterminals that have been processed
(AltF (Name n) Action b) -- ^ The actions that still need to be done
!(f a) -- ^ The name of the current nonterminal
(Pa (Match + f) b a) -- ^ The processed dependencies
(Ap (Match + f) b) -- ^ The actions that still need to be done

data Descriptor n a =
instance (forall x. (Show (f x))) => Show (Slot f a) where
show (Slot x y z) = "(Slot (" ++ show x ++ ") (" ++ show y ++ ") (" ++ showAp z ++ "))"

data Descriptor f a =
Descriptor
!(Slot n a)
!(Slot f a)
!Int -- ^ The left extent
!Int -- ^ The pivot
String -- ^ The remainder of the input (to the right of the pivot)
deriving instance (forall x. (Show (f x))) => Show (Descriptor f a)

data SomeDescriptor n = forall a. SomeDescriptor (Descriptor n a)
instance Ord n => Eq (SomeDescriptor n) where
data SomeDescriptor f = forall a. SomeDescriptor (Descriptor f a)
deriving instance (forall x. (Show (f x))) => Show (SomeDescriptor f)
instance (OrdF f) => Eq (SomeDescriptor f) where
SomeDescriptor x == SomeDescriptor y = compareDescriptor x y == EQ
instance Ord n => Ord (SomeDescriptor n) where
instance (OrdF f) => Ord (SomeDescriptor f) where
compare (SomeDescriptor x) (SomeDescriptor y) = compareDescriptor x y

compareName :: Ord n => Name n a -> Name n b -> Ordering
compareName (Name x) (Name y) = compare x y
class OrdF f where
compareF :: f a -> f b -> Ordering

data SomeF f where
SomeF :: f a -> SomeF f
instance OrdF f => Eq (SomeF f) where
SomeF x == SomeF y = compareF x y == EQ
instance OrdF f => Ord (SomeF f) where
compare (SomeF x) (SomeF y) = compareF x y

compareSlot :: Ord n => Slot n a -> Slot n b -> Ordering
compareSlot (Slot x1 x2 x3 _ _) (Slot y1 y2 y3 _ _) = compareName x1 y1 <> compare x2 y2 <> compare x3 y3
compareSlot :: OrdF f => Slot f a -> Slot f b -> Ordering
compareSlot (Slot x1 x2 x3) (Slot y1 y2 y3) = compareF x1 y1 <> comparePa x2 y2 <> compareAp x3 y3

compareDescriptor :: Ord n => Descriptor n a -> Descriptor n b -> Ordering
compareDescriptor :: OrdF f => Descriptor f a -> Descriptor f b -> Ordering
compareDescriptor (Descriptor x1 x2 x3 _) (Descriptor y1 y2 y3 _) = compareSlot x1 y1 <> compare x2 y2 <> compare x3 y3

initialDescriptor :: Name n a -> String -> Int -> AltF (Name n) Action a -> SomeDescriptor n
initialDescriptor n xs i act = SomeDescriptor (Descriptor (Slot n i 0 Self act) 0 0 xs)
initialDescriptor :: f a -> String -> Ap (Match + f) a -> SomeDescriptor f
initialDescriptor nt xs act = SomeDescriptor (Descriptor (Slot nt Erup act) 0 0 xs)

newtype WaitForAscend n = WA (Map (n, Int) [Int -> String -> SomeDescriptor n])
newtype WaitForAscend f = WA (Map (SomeF f, Int) [Int -> String -> SomeDescriptor f])

emptyWA :: WaitForAscend n
emptyWA = WA Map.empty

lookupWA :: forall a n. Ord n => WaitForAscend n -> Name n a -> Int -> [Int -> String -> SomeDescriptor n]
lookupWA (WA m) (Name n) k = fromMaybe [] (m Map.!? (n, k))
lookupWA :: forall a f. OrdF f => WaitForAscend f -> f a -> Int -> [Int -> String -> SomeDescriptor f]
lookupWA (WA m) nt k = fromMaybe [] (m Map.!? (SomeF nt, k))

insertWA :: Ord n => Name n a -> Int -> (Int -> String -> SomeDescriptor n) -> WaitForAscend n -> WaitForAscend n
insertWA (Name n) k f (WA m) = WA (Map.insertWith (++) (n, k) [f] m)
insertWA :: OrdF f => f a -> Int -> (Int -> String -> SomeDescriptor f) -> WaitForAscend f -> WaitForAscend f
insertWA nt k f (WA m) = WA (Map.insertWith (++) (SomeF nt, k) [f] m)

newtype WaitForDescend n = WD (Map (n, Int) [(Int, String)])
newtype WaitForDescend f = WD (Map (SomeF f, Int) [(Int, String)])

emptyWD :: WaitForDescend n
emptyWD = WD Map.empty

lookupWD :: forall a n. Ord n => WaitForDescend n -> Name n a -> Int -> [(Int, String)]
lookupWD (WD m) (Name n) k = fromMaybe [] (m Map.!? (n, k))
lookupWD :: forall a f. OrdF f => WaitForDescend f -> f a -> Int -> [(Int, String)]
lookupWD (WD m) nt k = fromMaybe [] (m Map.!? (SomeF nt, k))

insertWD :: Ord n => Name n a -> Int -> (Int, String) -> WaitForDescend n -> WaitForDescend n
insertWD (Name n) k x (WD m) = WD (Map.insertWith (++) (n, k) [x] m)
insertWD :: OrdF f => f a -> Int -> (Int, String) -> WaitForDescend f -> WaitForDescend f
insertWD nt k x (WD m) = WD (Map.insertWith (++) (SomeF nt, k) [x] m)

parse :: forall n a. Ord n => (G n, Name n a) -> String -> Set (SomeDescriptor n)
parse (g, z) xs0 = go Set.empty emptyWA emptyWD (zipWith (initialDescriptor z xs0) [0..] (lookupG g z)) where
go :: Set (SomeDescriptor n) -> WaitForAscend n -> WaitForDescend n -> [SomeDescriptor n] -> Set (SomeDescriptor n)
parse :: forall a f g. (OrdF f, g < f) => Gram f -> g a -> String -> Set (SomeDescriptor f)
parse g z xs0 = go Set.empty emptyWA emptyWD (map (initialDescriptor (inj z) xs0) (alternatives (lookupG g (inj z)))) where
go :: Set (SomeDescriptor f) -> WaitForAscend f -> WaitForDescend f -> [SomeDescriptor f] -> Set (SomeDescriptor f)

-- If we've already processed this descriptor then we can skip it
go u wa wd (d : rs) | d `Set.member` u = go u wa wd rs

-- If we're currently 'Match'ing a character and that character appears in the input text then we can continue
go u wa wd (d@(SomeDescriptor (Descriptor (Slot x a i ds (Ap (Match c) r)) l k xs)) : rs)
| c' : xs' <- xs, c == c' = go (Set.insert d u) wa wd (SomeDescriptor (Descriptor (Slot x a (i + 1) ds (($ ()) <$> r)) l (k + 1) xs') : rs)
go u wa wd (d@(SomeDescriptor (Descriptor (Slot x ds (Ap (L (Match c)) r)) l k xs)) : rs)
| c' : xs' <- xs, c == c' = go (Set.insert d u) wa wd (SomeDescriptor (Descriptor (Slot x (Pa ds (L (Match c))) r) l (k + 1) xs') : rs)
-- otherwise we skip this descriptor
| otherwise = go u wa wd rs

-- If we're descending into a nonterminal then we check if something was already waiting for us to descend.
go u wa wd (d@(SomeDescriptor (Descriptor (Slot x a i ds (Ap (Descend n) next)) l k xs)) : rs)
= go (Set.insert d u) (insertWA n k (\r xs' -> SomeDescriptor (Descriptor (Slot x a (i + 1) (Dep n k r ds) next) l r xs')) wa) wd $
case lookupWD wd n k of
go u wa wd (d@(SomeDescriptor (Descriptor (Slot x ds (Ap (R nt) next)) l k xs)) : rs)
= go (Set.insert d u) (insertWA nt k (\r xs' -> SomeDescriptor (Descriptor (Slot x (Pa ds (R nt)) next) l r xs')) wa) wd $
case lookupWD wd nt k of
-- If nothing was waiting for this then we start descending by adding initial descriptors the nonterminal we are descending into.
[] -> [ SomeDescriptor (Descriptor (Slot n a' 0 Self acts) k k xs) | (a', acts) <- zip [0..] (lookupG g n) ] ++ rs
[] -> [ SomeDescriptor (Descriptor (Slot nt Erup acts) k k xs) | acts <- alternatives (lookupG g nt) ] ++ rs
-- If something was waiting for us then we can take over where they left off.
_ -> [ SomeDescriptor (Descriptor (Slot x a (i + 1) (Dep n k r ds) next) l r xs') | (r, xs') <- lookupWD wd n k ] ++ rs
_ -> [ SomeDescriptor (Descriptor (Slot x (Pa ds (R nt)) next) l r xs') | (r, xs') <- lookupWD wd nt k ] ++ rs

-- If we have reached the end of a descriptor then we ascend.
go u wa wd (d@(SomeDescriptor (Descriptor (Slot x _ _ _ (Pure _)) k r xs)) : rs)
go u wa wd (d@(SomeDescriptor (Descriptor (Slot x _ (Pure _)) k r xs)) : rs)
= go (Set.insert d u) wa (insertWD x k (r, xs) wd)
([ f r xs | f <- lookupWA wa x k ] ++ rs)

-- If we have no more work then parsing is done!
go u _ _ [] = u

decode :: forall n a. (Show n, Ord n) => Set (SomeDescriptor n) -> Name n a -> Int -> Int -> [a]
decode ds0 = lookupM where
m :: Map (n, Int, Int) [Any]
m = Map.fromListWith (++)
[ ((x, l, r), map unsafeCoerce (go ds [a]))
| SomeDescriptor (Descriptor (Slot (Name x) _ _ ds (Pure a)) l r _) <- Set.toList ds0
]

lookupM :: forall c. Name n c -> Int -> Int -> [c]
lookupM (Name n) l r = maybe [] (map unsafeCoerce) (m Map.!? (n, l, r))

go :: forall b c. Deps n b c -> [c] -> [b]
go Self x = x
go (Dep n l r xs) fs = go xs $ fs <*> lookupM n l r
-- decode :: forall a f. Set (SomeDescriptor f) -> f a -> Int -> Int -> [a]
-- decode ds0 = lookupM where
-- m :: Map (n, Int, Int) [Any]
-- m = Map.fromListWith (++)
-- [ ((x, l, r), map unsafeCoerce (go ds [a]))
-- | SomeDescriptor (Descriptor (Slot nt _ _ ds (Pure a)) l r _) <- Set.toList ds0
-- ]
--
-- lookupM :: forall c. f c -> Int -> Int -> [c]
-- lookupM nt l r = maybe [] (map unsafeCoerce) (m Map.!? (n, l, r))
--
-- go :: forall b c. Deps f b c -> [c] -> [b]
-- go Self x = x
-- go (Dep n l r xs) fs = go xs $ fs <*> lookupM n l r

data End a deriving (Show)
instance OrdF End where
compareF x _ = case x of

data Number a where
Number :: Number Int
deriving instance Show (Number a)
instance OrdF Number where
compareF Number Number = EQ

number :: Number < f => Alt f Int
number = send Number

data Digit a where
Digit :: Digit Int
deriving instance Show (Digit a)
instance OrdF Digit where
compareF Digit Digit = EQ

digit :: Digit < f => Alt f Int
digit = send Digit

data Expr a where
Expr :: Expr Int
deriving instance Show (Expr a)

instance OrdF Expr where
compareF Expr Expr = EQ

expr :: Expr < f => Alt f Int
expr = send Expr

onR :: (g a -> h a) -> (f + g) a -> (f + h) a
onR f (R x) = R (f x)
onR _ (L x) = L x

infixr +
infixr <||>

(<||>) :: G f h -> G g h -> G (f + g) h
G f <||> G g = G $ \case
L y -> f y
R y -> g y

type Gram f = G f f

gram :: Gram (Expr + Number + Digit + End)
gram = G (\Expr -> (+) <$> expr <* match '+' <*> expr <|> number)
<||> G (\Number -> (\x y -> 10 * x + y) <$> number <*> digit <|> digit)
<||> G (\Digit -> asum [x <$ match (intToDigit x) | x <- [0..9]])
<||> G (\case)

ex1 :: Set (SomeDescriptor (Expr + Number + Digit + End))
ex1 = parse gram Expr "123+456"

-- >>> ex1
-- fromList [SomeDescriptor (Descriptor (Slot (L Expr) (Erup) ((Ap (R (L Expr)) ((Ap (L (Match '+')) ((Ap (R (L Expr)) (Pure)))))))) 0 0 "123+456"),SomeDescriptor (Descriptor (Slot (L Expr) (Erup) ((Ap (R (L Expr)) ((Ap (L (Match '+')) ((Ap (R (L Expr)) (Pure)))))))) 4 4 "456"),SomeDescriptor (Descriptor (Slot (L Expr) (Erup) ((Ap (R (R (L Number))) (Pure)))) 0 0 "123+456"),SomeDescriptor (Descriptor (Slot (L Expr) (Erup) ((Ap (R (R (L Number))) (Pure)))) 4 4 "456"),SomeDescriptor (Descriptor (Slot (L Expr) (Pa (Pa Erup (R (L Expr))) (L (Match '+'))) ((Ap (R (L Expr)) (Pure)))) 0 4 "456"),SomeDescriptor (Descriptor (Slot (L Expr) (Pa Erup (R (L Expr))) ((Ap (L (Match '+')) ((Ap (R (L Expr)) (Pure)))))) 0 3 "+456"),SomeDescriptor (Descriptor (Slot (L Expr) (Pa (Pa (Pa Erup (R (L Expr))) (L (Match '+'))) (R (L Expr))) (Pure)) 0 5 "56"),SomeDescriptor (Descriptor (Slot (L Expr) (Pa (Pa (Pa Erup (R (L Expr))) (L (Match '+'))) (R (L Expr))) (Pure)) 0 6 "6"),SomeDescriptor (Descriptor (Slot (L Expr) (Pa (Pa (Pa Erup (R (L Expr))) (L (Match '+'))) (R (L Expr))) (Pure)) 0 7 ""),SomeDescriptor (Descriptor (Slot (L Expr) (Pa Erup (R (R (L Number)))) (Pure)) 0 1 "23+456"),SomeDescriptor (Descriptor (Slot (L Expr) (Pa Erup (R (R (L Number)))) (Pure)) 0 2 "3+456"),SomeDescriptor (Descriptor (Slot (L Expr) (Pa Erup (R (R (L Number)))) (Pure)) 0 3 "+456"),SomeDescriptor (Descriptor (Slot (L Expr) (Pa Erup (R (R (L Number)))) (Pure)) 4 5 "56"),SomeDescriptor (Descriptor (Slot (L Expr) (Pa Erup (R (R (L Number)))) (Pure)) 4 6 "6"),SomeDescriptor (Descriptor (Slot (L Expr) (Pa Erup (R (R (L Number)))) (Pure)) 4 7 ""),SomeDescriptor (Descriptor (Slot (R (L Number)) (Erup) ((Ap (R (R (L Number))) ((Ap (R (R (R (L Digit)))) (Pure)))))) 0 0 "123+456"),SomeDescriptor (Descriptor (Slot (R (L Number)) (Erup) ((Ap (R (R (L Number))) ((Ap (R (R (R (L Digit)))) (Pure)))))) 4 4 "456"),SomeDescriptor (Descriptor (Slot (R (L Number)) (Erup) ((Ap (R (R (R (L Digit)))) (Pure)))) 0 0 "123+456"),SomeDescriptor (Descriptor (Slot (R (L Number)) (Erup) ((Ap (R (R (R (L Digit)))) (Pure)))) 4 4 "456"),SomeDescriptor (Descriptor (Slot (R (L Number)) (Pa Erup (R (R (L Number)))) ((Ap (R (R (R (L Digit)))) (Pure)))) 0 1 "23+456"),SomeDescriptor (Descriptor (Slot (R (L Number)) (Pa Erup (R (R (L Number)))) ((Ap (R (R (R (L Digit)))) (Pure)))) 0 2 "3+456"),SomeDescriptor (Descriptor (Slot (R (L Number)) (Pa Erup (R (R (L Number)))) ((Ap (R (R (R (L Digit)))) (Pure)))) 0 3 "+456"),SomeDescriptor (Descriptor (Slot (R (L Number)) (Pa Erup (R (R (L Number)))) ((Ap (R (R (R (L Digit)))) (Pure)))) 4 5 "56"),SomeDescriptor (Descriptor (Slot (R (L Number)) (Pa Erup (R (R (L Number)))) ((Ap (R (R (R (L Digit)))) (Pure)))) 4 6 "6"),SomeDescriptor (Descriptor (Slot (R (L Number)) (Pa Erup (R (R (L Number)))) ((Ap (R (R (R (L Digit)))) (Pure)))) 4 7 ""),SomeDescriptor (Descriptor (Slot (R (L Number)) (Pa Erup (R (R (R (L Digit))))) (Pure)) 0 1 "23+456"),SomeDescriptor (Descriptor (Slot (R (L Number)) (Pa Erup (R (R (R (L Digit))))) (Pure)) 4 5 "56"),SomeDescriptor (Descriptor (Slot (R (L Number)) (Pa (Pa Erup (R (R (L Number)))) (R (R (R (L Digit))))) (Pure)) 0 2 "3+456"),SomeDescriptor (Descriptor (Slot (R (L Number)) (Pa (Pa Erup (R (R (L Number)))) (R (R (R (L Digit))))) (Pure)) 0 3 "+456"),SomeDescriptor (Descriptor (Slot (R (L Number)) (Pa (Pa Erup (R (R (L Number)))) (R (R (R (L Digit))))) (Pure)) 4 6 "6"),SomeDescriptor (Descriptor (Slot (R (L Number)) (Pa (Pa Erup (R (R (L Number)))) (R (R (R (L Digit))))) (Pure)) 4 7 ""),SomeDescriptor (Descriptor (Slot (R (R (L Digit))) (Erup) ((Ap (L (Match '1')) (Pure)))) 0 0 "123+456"),SomeDescriptor (Descriptor (Slot (R (R (L Digit))) (Erup) ((Ap (L (Match '2')) (Pure)))) 1 1 "23+456"),SomeDescriptor (Descriptor (Slot (R (R (L Digit))) (Erup) ((Ap (L (Match '3')) (Pure)))) 2 2 "3+456"),SomeDescriptor (Descriptor (Slot (R (R (L Digit))) (Erup) ((Ap (L (Match '4')) (Pure)))) 4 4 "456"),SomeDescriptor (Descriptor (Slot (R (R (L Digit))) (Erup) ((Ap (L (Match '5')) (Pure)))) 5 5 "56"),SomeDescriptor (Descriptor (Slot (R (R (L Digit))) (Erup) ((Ap (L (Match '6')) (Pure)))) 6 6 "6"),SomeDescriptor (Descriptor (Slot (R (R (L Digit))) (Pa Erup (L (Match '1'))) (Pure)) 0 1 "23+456"),SomeDescriptor (Descriptor (Slot (R (R (L Digit))) (Pa Erup (L (Match '2'))) (Pure)) 1 2 "3+456"),SomeDescriptor (Descriptor (Slot (R (R (L Digit))) (Pa Erup (L (Match '3'))) (Pure)) 2 3 "+456"),SomeDescriptor (Descriptor (Slot (R (R (L Digit))) (Pa Erup (L (Match '4'))) (Pure)) 4 5 "56"),SomeDescriptor (Descriptor (Slot (R (R (L Digit))) (Pa Erup (L (Match '5'))) (Pure)) 5 6 "6"),SomeDescriptor (Descriptor (Slot (R (R (L Digit))) (Pa Erup (L (Match '6'))) (Pure)) 6 7 "")]

0 comments on commit 4053ab9

Please sign in to comment.