1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
--- This module defines the basic data structures for
--- representing graphs and hypergraphs and the corresponding
--- graph parsers.

{-# OPTIONS_CYMAKE -Wno-incomplete-patterns -Wno-overlapping #-}

module Grappa where

--- All nodes are numbered.
type Node = Int

--- A type for the edge labels `t` can be passed as a type parameter.
--- In the simplest case this can be just strings representing their labels.
--- The tentacle numbers correspond to the position of a node in the
--- list of incident nodes.
type Edge t = (t, [Node])

--- A graph is declared as a list of edges each with its incident nodes
type Graph t = [Edge t]

--- The type of a graph parser. It is parameterized over the
--- type `res` of semantic values associated to graphs.
type Grappa t res = Graph t -> (res, Graph t)

--- Access the semantic representation of the result of a graph parser.
getSemRep :: (res, Graph t) -> res
getSemRep (r,_) = r

-- Some primitives for the construction of graph parsers.

--- Given an arbitrary value `v`, `pSucceed` always succeeds
--- returning `v` as a result without any consumption of the input graph `g`.
pSucceed :: res -> Grappa t res
pSucceed v g = (v, g)

--- `eoi` succeeds only if the graph is already completely consumed.
--- In this case, the result `()` is returned
eoi :: Grappa t ()
eoi [] = ((), [])

--- The parser `edge e` succeeds only if the given edge `e` is part of the
--- given input graph `g`.
edge :: Data t => Edge t -> Grappa t ()
edge e g | g =:= (g1 ++ e:g2) = ((), g1 ++ g2)
 where g1, g2 free

--- The choice between two parsers.
(<|>) :: Grappa t res -> Grappa t res -> Grappa t res
(p1 <|> _ ) g = p1 g
(_  <|> p2) g = p2 g

--- The successive application of two parsers where the result
--- is constructed by function application. 
(<*>) :: Grappa t (r1->r2) -> Grappa t r1 -> Grappa t r2
(p <*> q) g = case p g of
               (pv, g1) -> case q g1 of
                             (qv, g2) -> (pv qv, g2)

(<$>) :: (res1->res2) -> Grappa t res1 -> Grappa t res2
f <$> p = pSucceed f <*> p

(<$)  :: res1 -> Grappa t res2 -> Grappa t res1
f <$ p = const f <$> p

(<*)  :: Grappa t res1 -> Grappa t res2 -> Grappa t res1
p <* q = (\x _ -> x) <$> p <*> q

(*>)  :: Grappa t res1 -> Grappa t res2 -> Grappa t res2
p *> q = (\_ x -> x) <$> p <*> q


--- The combinator `chain p (n1,n2)` can be used to identify
--- a non-empty chain of graphs that can be parsed with `p`.
--- This chain has to be anchored between the nodes `n1` and `n2`.
chain :: ((Node,Node) -> Grappa t a) -> (Node,Node) -> Grappa t [a]
chain p (n1,n2) = (:[]) <$> p (n1,n2)
chain p (n1,n2) = (:)   <$> p (n1,n) <*> chain p (n,n2)
 where n free