-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathAnnoState.hs
233 lines (182 loc) · 6.95 KB
/
AnnoState.hs
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
{- |
Module : $Header$
Description : parsing of interspersed annotations
Copyright : (c) Christian Maeder and Uni Bremen 2002-2006
License : GPLv2 or higher, see LICENSE.txt
Maintainer : Christian.Maeder@dfki.de
Stability : provisional
Portability : portable
Parsing of interspersed annotations
- a parser state to collect annotations
- parsing annoted keywords
- parsing an annoted item list
-}
module Common.AnnoState where
import Common.Parsec
import Common.Lexer
import Common.Token
import Common.Id
import Common.Keywords
import Common.AS_Annotation
import Common.AnnoParser
import Text.ParserCombinators.Parsec
import Data.List
import Control.Monad
-- | parsers that can collect annotations via side effects
type AParser st = GenParser Char (AnnoState st)
class AParsable a where
aparser :: AParser st a
-- used for CASL extensions. If there is no extension, just fail
instance AParsable () where
aparser = pzero
-- a parser for terms or formulas
class TermParser a where
termParser :: Bool -> AParser st a -- ^ True for terms, formulas otherwise
termParser _ = pzero
instance TermParser ()
aToTermParser :: AParser st a -> Bool -> AParser st a
aToTermParser p b = if b then pzero else p
-- | just the list of currently collected annotations
data AnnoState st = AnnoState { toAnnos :: [Annotation], _userState :: st }
-- | no annotations
emptyAnnos :: st -> AnnoState st
emptyAnnos = AnnoState []
-- | add further annotations to the input state
parseAnnos :: AnnoState a -> GenParser Char st (AnnoState a)
parseAnnos (AnnoState as b) =
do a <- skip >> annotations
return $ AnnoState (as ++ a) b
-- | add only annotations on consecutive lines to the input state
parseLineAnnos :: AnnoState a -> GenParser Char st (AnnoState a)
parseLineAnnos (AnnoState as b) =
do l <- mLineAnnos
return $ AnnoState (as ++ l) b
-- | add annotations to the internal state
addAnnos :: AParser st ()
addAnnos = getState >>= parseAnnos >>= setState
-- | add only annotations on consecutive lines to the internal state
addLineAnnos :: AParser st ()
addLineAnnos = getState >>= parseLineAnnos >>= setState
{- | extract all annotation from the internal state,
resets the internal state to 'emptyAnnos' -}
getAnnos :: AParser st [Annotation]
getAnnos = do
aSt <- getState
setState aSt { toAnnos = [] }
return $ toAnnos aSt
-- | annotations on consecutive lines
mLineAnnos :: GenParser Char st [Annotation]
mLineAnnos = optionL $ do
a <- annotationL
skipSmart
fmap (a :) $ optionL mLineAnnos
-- | explicitly parse annotations, reset internal state
annos :: AParser st [Annotation]
annos = addAnnos >> getAnnos
-- | explicitly parse annotations on consecutive lines. reset internal state
lineAnnos :: AParser st [Annotation]
lineAnnos = addLineAnnos >> getAnnos
-- | succeeds if the previous item is finished
tryItemEnd :: [String] -> AParser st ()
tryItemEnd l = try $ do
c <- lookAhead $ annos >>
(single (oneOf "\"([{") <|> placeS <|> scanAnySigns
<|> many (scanLPD <|> char '_' <?> "") <?> "")
unless (null c || elem c l) pzero
{- | keywords that indicate a new item for 'tryItemEnd'.
the quantifier exists does not start a new item. -}
startKeyword :: [String]
startKeyword = dotS : cDot : delete existsS casl_reserved_words
-- | parse preceding annotations and the following item
annoParser :: AParser st a -> AParser st (Annoted a)
annoParser = liftM2 addLeftAnno annos
allAnnoParser :: AParser st a -> AParser st (Annoted a)
allAnnoParser p = liftM2 appendAnno (annoParser p) lineAnnos
{- | parse preceding and consecutive trailing annotations of an item in
between. Unlike 'annosParser' do not treat all trailing annotations as
preceding annotations of the next item. -}
trailingAnnosParser :: AParser st a -> AParser st [Annoted a]
trailingAnnosParser p = do
l <- many1 $ allAnnoParser p
a <- annos -- append remaining annos to last item
return $ init l ++ [appendAnno (last l) a]
-- | parse an item list preceded and followed by annotations
annosParser :: AParser st a -> AParser st [Annoted a]
annosParser parser =
do a <- annos
l <- many1 $ pair parser annos
let ps = map fst l
as = map snd l
is = zipWith addLeftAnno (a : init as) ps
return (init is ++ [appendAnno (last is) (last as)])
{- | parse an item list preceded by a singular or plural keyword,
interspersed with semicolons and an optional semicolon at the end -}
itemList :: [String] -> String -> ([String] -> AParser st b)
-> ([Annoted b] -> Range -> a) -> AParser st a
itemList ks kw ip constr =
do p <- pluralKeyword kw
auxItemList (ks ++ startKeyword) [p] (ip ks) constr
{- | generalized version of 'itemList'
for an other keyword list for 'tryItemEnd' and without 'pluralKeyword' -}
auxItemList :: [String] -> [Token] -> AParser st b
-> ([Annoted b] -> Range -> a) -> AParser st a
auxItemList startKeywords ps parser constr = do
(vs, ts, ans) <- itemAux startKeywords (annoParser parser)
let r = zipWith appendAnno vs ans in
return (constr r (catRange (ps ++ ts)))
-- | parse an item list without a starting keyword
itemAux :: [String] -> AParser st a
-> AParser st ([a], [Token], [[Annotation]])
itemAux startKeywords itemParser =
do a <- itemParser
(m, an) <- optSemi
let r = return ([a], [], [an])
(tryItemEnd startKeywords >> r) <|>
do (ergs, ts, ans) <- itemAux startKeywords itemParser
return (a : ergs, m ++ ts, an : ans)
-- | collect preceding and trailing annotations
wrapAnnos :: AParser st a -> AParser st a
wrapAnnos p = try (addAnnos >> p) << addAnnos
-- | parse an annoted keyword
asKey :: String -> AParser st Token
asKey = wrapAnnos . pToken . toKey
-- * annoted keywords
anComma :: AParser st Token
anComma = wrapAnnos commaT
anSemi :: AParser st Token
anSemi = wrapAnnos semiT
semiOrComma :: CharParser st Token
semiOrComma = semiT <|> commaT
anSemiOrComma :: AParser st Token
anSemiOrComma = wrapAnnos semiOrComma << addLineAnnos
-- | check for a semicolon beyond annotations
trySemi :: AParser st Token
trySemi = try $ addAnnos >> semiT
-- | check for a semicolon or comma beyond annotations and trailing line annos
trySemiOrComma :: AParser st Token
trySemiOrComma = try (addAnnos >> semiOrComma) << addLineAnnos
-- | optional semicolon followed by annotations on consecutive lines
optSemi :: AParser st ([Token], [Annotation])
optSemi = do
s <- trySemi
a1 <- getAnnos
a2 <- lineAnnos
return ([s], a1 ++ a2)
<|> do
a <- lineAnnos
return ([], a)
equalT :: AParser st Token
equalT = wrapAnnos $ pToken $ reserved [exEqual]
(choice (map (keySign . string) [exEqual, equalS]) <?> show equalS)
colonT :: AParser st Token
colonT = asKey colonS
lessT :: AParser st Token
lessT = asKey lessS
dotT :: AParser st Token
dotT = asKey dotS <|> asKey cDot <?> show dotS
asT :: AParser st Token
asT = asKey asS
barT :: AParser st Token
barT = asKey barS
forallT :: AParser st Token
forallT = asKey forallS