Skip to content

Commit 0d14130

Browse files
committed
[Parsers] End Parsley chapter
1 parent 92d60c6 commit 0d14130

File tree

1 file changed

+125
-1
lines changed

1 file changed

+125
-1
lines changed

en/lessons/parsers/parsley.md

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,128 @@ title: Parsley
55

66
{% include toc.html %}
77

8-
https://hackage.haskell.org/package/parsley
8+
From the documentation:
9+
10+
> [parsley](https://hackage.haskell.org/package/parsley) is a staged selective
11+
> parser combinator library, which means it does not support monadic operations,
12+
> and relies on Typed Template Haskell to generate very fast code.
13+
14+
# Definition
15+
16+
Let's seen how it's defined:
17+
18+
```haskell
19+
newtype Parser a = Parser {unParser :: Fix (Combinator :+: ScopeRegister) a}
20+
21+
-- Core datatype
22+
data Combinator (k :: Type -> Type) (a :: Type) where
23+
Pure :: Defunc a -> Combinator k a
24+
Satisfy :: Defunc (Char -> Bool) -> Combinator k Char
25+
(:<*>:) :: k (a -> b) -> k a -> Combinator k b
26+
(:*>:) :: k a -> k b -> Combinator k b
27+
(:<*:) :: k a -> k b -> Combinator k a
28+
(:<|>:) :: k a -> k a -> Combinator k a
29+
Empty :: Combinator k a
30+
Try :: k a -> Combinator k a
31+
LookAhead :: k a -> Combinator k a
32+
Let :: Bool -> MVar a -> Combinator k a
33+
NotFollowedBy :: k a -> Combinator k ()
34+
Branch :: k (Either a b) -> k (a -> c) -> k (b -> c) -> Combinator k c
35+
Match :: k a -> [Defunc (a -> Bool)] -> [k b] -> k b -> Combinator k b
36+
Loop :: k () -> k a -> Combinator k a
37+
MakeRegister :: ΣVar a -> k a -> k b -> Combinator k b
38+
GetRegister :: ΣVar a -> Combinator k a
39+
PutRegister :: ΣVar a -> k a -> Combinator k ()
40+
Position :: PosSelector -> Combinator k Int
41+
Debug :: String -> k a -> Combinator k a
42+
MetaCombinator :: MetaCombinator -> k a -> Combinator k a
43+
```
44+
45+
Clearly, that's the complete opposite approach of everything we have seen so
46+
far, each operation has a constructor.
47+
48+
We can also have a look at their associated types:
49+
50+
```haskell
51+
52+
data ScopeRegister (k :: Type -> Type) (a :: Type) where
53+
ScopeRegister :: k a -> (forall r. Reg r a -> k b) -> ScopeRegister k b
54+
55+
data PosSelector where
56+
Line :: PosSelector
57+
Col :: PosSelector
58+
59+
{-|
60+
This is an opaque representation of a parsing register.
61+
It is the abstracted representation of a runtime storage location.
62+
-}
63+
newtype Reg (r :: Type) a = Reg (ΣVar a)
64+
65+
data MetaCombinator where
66+
-- | After this combinator exits, a cut has happened
67+
Cut :: MetaCombinator
68+
-- | This combinator requires a cut from below to respect parsec semantics
69+
RequiresCut :: MetaCombinator
70+
-- | This combinator denotes that within its scope, cut semantics are not enforced
71+
CutImmune :: MetaCombinator
72+
73+
{-|
74+
An identifier representing concrete registers and mutable state.
75+
-}
76+
newtype ΣVar (a :: Type) = ΣVar IΣVar
77+
78+
{-|
79+
Underlying untyped identifier, which is numeric but otherwise opaque.
80+
-}
81+
newtype IΣVar = IΣVar Word64 deriving newtype (Ord, Eq, Num, Enum, Show, Ix)
82+
83+
{-|
84+
This datatype is useful for providing an /inspectable/ representation of common Haskell functions.
85+
-}
86+
data Defunc a -- complex implementation
87+
88+
```
89+
90+
Not particularly interesting, but it is very detailed.
91+
92+
# Running the parser
93+
94+
We can have a look at how everything is used, from the example:
95+
96+
```haskell
97+
parseOut :: ByteString -> Maybe [Out]
98+
parseOut = $$(Parsley.parse myParser)
99+
```
100+
101+
Then we expect the `Template` work to be here:
102+
103+
```haskell
104+
parse :: (Trace, Input input) => Parser a -> Code (input -> Maybe a)
105+
parse p = [||\input -> $$(eval [||input||] (compile (try p) codeGen))||]
106+
```
107+
108+
The implementation is quite complex, but it'll hopefully help to get a better picture:
109+
110+
```haskell
111+
{-|
112+
Translates a parser represented with combinators into its machine representation.
113+
-}
114+
{-# INLINEABLE codeGen #-}
115+
codeGen :: Trace
116+
=> Maybe (MVar x) -- ^ The name of the parser, if it exists.
117+
-> Fix Combinator x -- ^ The definition of the parser.
118+
-> Set SomeΣVar -- ^ The free registers it requires to run.
119+
-> IMVar -- ^ The binding identifier to start name generation from.
120+
-> LetBinding o a x
121+
122+
eval :: forall o a. (Trace, Ops o) => Code (InputDependant o) -> LetBinding o a a -> DMap MVar (LetBinding o a) -> Code (Maybe a)
123+
124+
compile :: forall compiled a. Trace => Parser a -> (forall x. Maybe (MVar x) -> Fix Combinator x -> Set IΣVar -> IMVar -> IΣVar -> compiled x) -> (compiled a, DMap MVar compiled)
125+
```
126+
127+
The idea is to compile `Combinator` to `Template`'s `Code` through a complete machinery.
128+
129+
# Conclusion
130+
131+
The special feature of `parsley` is to be mostly working at compile-time,
132+
making the implementation far more complex.

0 commit comments

Comments
 (0)