-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathShipSyntax.hs
396 lines (318 loc) · 10.8 KB
/
ShipSyntax.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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
{- |
Module : ./OWL2/ShipSyntax.hs
Copyright : (c) C. Maeder DFKI GmbH
License : GPLv2 or higher, see LICENSE.txt
Maintainer : Christian.Maeder@dfki.de
Stability : provisional
Portability : portable
different pretty printing for the SHIP tool
-}
module OWL2.ShipSyntax where
import OWL2.AS
import Common.Doc
import Common.DocUtils
import Common.Parsec
import Control.Monad
import Data.Char
import Text.ParserCombinators.Parsec
data Concept
= CName String
| NominalC String
| NotC Concept
| JoinedC (Maybe JunctionType) [Concept] -- Nothing denotes disjoint union
| Quant (Either QuantifierType (CardinalityType, Int)) Role Concept
deriving (Show, Eq, Ord)
topC :: Concept
topC = CName "T"
data NotOrInverse = NotR | InvR deriving (Show, Eq, Ord)
data Role
= RName String
| NominalR String String
| UnOp NotOrInverse Role
| JoinedR (Maybe JunctionType) [Role] -- Nothing denotes composition!
deriving (Show, Eq, Ord)
topR :: Role
topR = RName "TxT"
botR :: Role
botR = UnOp NotR topR
ppJunction :: Bool -> Maybe JunctionType -> Doc
ppJunction c mt = text $ case mt of
Just t -> case t of
UnionOf -> "+ "
IntersectionOf -> "& "
Nothing -> if c then "<+> " else ". "
pppNegConcept :: Concept -> Doc
pppNegConcept c = case c of
JoinedC {} -> parens
_ -> id
$ ppConcept c
pppConcept :: Bool -> Maybe JunctionType -> Concept -> Doc
pppConcept notLast mt c = case c of
Quant {} | notLast -> parens
JoinedC mti _
| mti /= mt && maybe True (const $ maybe False (/= UnionOf) mt) mti
-> parens
_ -> id
$ ppConcept c
ppConcept :: Concept -> Doc
ppConcept co = case co of
CName s -> (if co == topC then keyword else text) s
NominalC s -> braces $ text s
NotC c -> text "~" <> pppNegConcept c
JoinedC t l -> case reverse l of
[] -> ppConcept $ if t == Just IntersectionOf then topC else NotC topC
f : r -> fsep . prepPunctuate (ppJunction True t)
. reverse $ pppConcept False t f : map (pppConcept True t) r
Quant q r c -> fsep [(case q of
Left v -> keyword (showQuant v)
Right (n, i) -> text (showCard n) <+> text (show i)
) <+> ppRole r, dot <+> ppConcept c]
showQuant :: QuantifierType -> String
showQuant v = case v of
AllValuesFrom -> "all"
SomeValuesFrom -> "ex"
showCard :: CardinalityType -> String
showCard n = case n of
MinCardinality -> ">="
MaxCardinality -> "<="
ExactCardinality -> "="
showSame :: SameOrDifferent -> String
showSame s = case s of
Same -> "=="
Different -> "!="
pppRole :: Maybe JunctionType -> Role -> Doc
pppRole mt r = case r of
JoinedR (Just ti) _
| maybe True (\ to -> ti /= to && ti == UnionOf) mt
-> parens
_ -> id
$ ppRole r
ppRole :: Role -> Doc
ppRole ro = case ro of
RName s -> (if ro == topR then keyword else text) s
NominalR s t -> braces $ parens $ text s <> comma <+> text t
UnOp o r -> (case o of
NotR -> text "~"
InvR -> keyword "inv") <> (case r of
JoinedR {} -> parens
_ | o == InvR -> parens
_ -> id) (ppRole r)
JoinedR t l -> case l of
[] -> ppRole $ if t /= Just UnionOf then topR else botR
_ -> fsep . prepPunctuate (ppJunction False t) $ map (pppRole t) l
skip :: CharParser st ()
skip = forget $ many $ single (satisfy isSpace) <|> nestedComment "/*" "*/"
<|> tryString "//" <++> many (noneOf "\n")
myLetter :: CharParser st Char
myLetter = satisfy $ \ c -> isAlphaNum c || elem c "_"
nominal :: CharParser st String
nominal = reserved
["_", "all", "ex", "inv", "not", "if", "effect", "causes", "cond", "exec"]
(many1 myLetter) << skip
key :: String -> CharParser st ()
key s = forget $ try $ string s >> notFollowedBy myLetter
skipKey :: String -> CharParser st ()
skipKey s = key s << skip
quant :: CharParser st QuantifierType
quant = choice $ map (\ a -> key (showQuant a) >> return a)
[AllValuesFrom, SomeValuesFrom]
card :: CharParser st CardinalityType
card = choice $ map (\ a -> tryString (showCard a) >> return a)
[MinCardinality, MaxCardinality, ExactCardinality]
quantOrCard :: CharParser st (Either QuantifierType (CardinalityType, Int))
quantOrCard = fmap Left quant
<|> do
c <- card << skip
i <- many1 digit
return $ Right (c, read i)
skipChar :: Char -> CharParser st ()
skipChar c = char c >> skip
commaP, colonP, equalP, bulletP :: CharParser st ()
commaP = skipChar ','
colonP = skipChar ':'
equalP = skipChar '='
bulletP = skipChar '.'
primConcept :: CharParser st Concept
primConcept = do
q <- quantOrCard << skip
r <- primRole
bulletP
fmap (Quant q r) concept
<|> ((key "not" <|> skipChar '~') >> skip >> fmap NotC primConcept)
<|> braced (fmap NominalC nominal) -- allow more nominals
<|> parent concept
<|> fmap CName nominal
braced :: CharParser st a -> CharParser st a
braced p = skipChar '{' >> p << skipChar '}'
parent :: CharParser st a -> CharParser st a
parent p = skipChar '(' >> p << skipChar ')'
binPP :: ([a] -> a) -> CharParser st sep -> CharParser st a -> CharParser st a
binPP f sp p = do
a <- p
l <- many $ sp >> p
return $ if null l then a else f $ a : l
binP :: ([a] -> a) -> String -> CharParser st a -> CharParser st a
binP f s = binPP f $ tryString s >> skip
binC :: ([a] -> a) -> Char -> CharParser st a -> CharParser st a
binC f c = binPP f $ skipChar c
andConcept :: CharParser st Concept
andConcept = binC (JoinedC $ Just IntersectionOf) '&' primConcept
plus :: CharParser st ()
plus = try $ char '+' >> notFollowedBy (char '>') >> skip
orConcept :: CharParser st Concept
orConcept = binPP (JoinedC $ Just UnionOf) plus andConcept
concept :: CharParser st Concept
concept = binP (JoinedC Nothing) "<+>" orConcept
notOrInv :: CharParser st NotOrInverse
notOrInv = (char '~' >> return NotR)
<|> (key "inv" >> return InvR)
nomPair :: (String -> String -> a) -> CharParser st a
nomPair f = parent $ liftM2 f nominal $ commaP >> nominal
primRole :: CharParser st Role
primRole = do
o <- notOrInv << skip
fmap (UnOp o) primRole
<|> braced (nomPair NominalR)
<|> parent role
<|> fmap RName nominal
compRole :: CharParser st Role
compRole = binC (JoinedR Nothing) '.' primRole
andRole :: CharParser st Role
andRole = binC (JoinedR $ Just IntersectionOf) '&' compRole
role :: CharParser st Role
role = binPP (JoinedR $ Just UnionOf) plus andRole
data EqOrLess = Eq | Less deriving (Show, Eq, Ord)
data RoleType = RoleType Concept Concept deriving (Show, Eq, Ord)
-- a missing rhs may be modelled as "< T" or by no constructors
data ConceptRhs
= ADTCons [TBoxCons]
| ConceptRel EqOrLess Concept
deriving (Show, Eq, Ord)
data TBoxCons = TBoxCons Concept [(Role, Concept)]
deriving (Show, Eq, Ord)
data TBox
= ConceptDecl Concept ConceptRhs
| DisjointCs [Concept]
deriving (Show, Eq, Ord)
data RBox
= RoleDecl Role RoleType
| RoleRel Role EqOrLess Role
| RoleProp Character Role
deriving (Show, Eq, Ord)
-- | assertions
data ABox
= AConcept String Concept
| ARole String String Role
| AIndividual String SameOrDifferent String
deriving (Show, Eq, Ord)
data Box = Box [TBox] [RBox] [ABox]
deriving (Show, Eq, Ord)
ppEqOrLess :: EqOrLess -> Doc
ppEqOrLess s = case s of
Eq -> equals
Less -> less
ppRoleType :: RoleType -> Doc
ppRoleType (RoleType s t) =
fsep [colon <+> ppConcept s, cross <+> ppConcept t]
ppConceptRhs :: ConceptRhs -> Doc
ppConceptRhs rhs = case rhs of
ADTCons cs -> if null cs then empty else
text "::=" <+> vcat (prepPunctuate (text "| ") $ map ppTBoxCons cs)
ConceptRel o c -> ppEqOrLess o <+> ppConcept c
ppTBoxCons :: TBoxCons -> Doc
ppTBoxCons (TBoxCons c sels) =
ppConcept c <> if null sels then empty else
parens . sepByCommas $ map
(\ (r, a) -> ppRole r <+> colon <+> ppConcept a) sels
ppTBox :: TBox -> Doc
ppTBox b = case b of
ConceptDecl d m -> ppConcept d <+> ppConceptRhs m
DisjointCs cs -> keyword "Disjoint" <> parens (sepByCommas $ map ppConcept cs)
ppRBox :: RBox -> Doc
ppRBox b = case b of
RoleDecl r t -> fsep [ppRole r, ppRoleType t]
RoleRel r o t -> fsep [ppRole r, ppEqOrLess o <+> ppRole t]
RoleProp c s -> keyword (showCharacter c) <> parens (ppRole s)
ppABox :: ABox -> Doc
ppABox b = case b of
AConcept n c -> text n <> colon <> ppConcept c
ARole s t r -> parens (text s <> comma <+> text t)
<> colon <> ppRole r
AIndividual s c t -> text s <> text (showSame c) <> text t
ppBox :: Box -> Doc
ppBox (Box ts rs as) = let ppM c = keyword $ '%' : c : "BOX" in
vcat $ ppM 'T' : map ppTBox ts ++ ppM 'R' : map ppRBox rs
++ ppM 'A' : map ppABox as
showCharacter :: Character -> String
showCharacter c = case c of
Functional -> "Func"
InverseFunctional -> "FuncInv"
Reflexive -> "Ref"
Irreflexive -> "Irref"
Symmetric -> "Sym"
Asymmetric -> "Asym"
Antisymmetric -> "Dis"
Transitive -> "Trans"
character :: CharParser st Character
character = choice $ map (\ a -> key (showCharacter a) >> return a)
[minBound .. maxBound]
eqOrLess :: CharParser st EqOrLess
eqOrLess = (equalP >> return Eq) <|> (skipChar '<' >> return Less)
tbox :: CharParser st TBox
tbox = (key "Disjoint" >> fmap DisjointCs
(parent $ concept <:> many (commaP >> concept)))
<|> try (liftM2 ConceptDecl concept
(liftM2 ConceptRel eqOrLess concept
<|> fmap ADTCons
((tryString "::=" >> skip >> sepBy tboxCons (skipChar '|'))
<|> (char ':' >> pzero)
<|> (string "==" >> pzero)
<|> (string "!=" >> pzero)
<|> return [])))
tboxCons :: CharParser st TBoxCons
tboxCons = liftM2 TBoxCons concept
. optionL . parent . flip sepBy commaP
. pair role $ colonP >> concept
rbox :: CharParser st RBox
rbox = liftM2 RoleProp character (parent role)
<|> try (do
r <- role
liftM2 (RoleRel r) eqOrLess role
<|> fmap (RoleDecl r) (liftM2 RoleType
(colonP >> concept)
$ skipChar '*' >> concept)) -- added try to recognize abox
abox :: CharParser st ABox
abox = liftM2 ($) (nomPair ARole) (colonP >> role)
<|> do
n <- nominal
fmap (AConcept n) (colonP >> concept)
<|> liftM2 (AIndividual n) (pSame << skip) nominal
pSame :: CharParser st SameOrDifferent
pSame = choice $ map (\ a -> tryString (showSame a) >> return a)
[Same, Different]
box :: CharParser st Box
box = do
forget $ many imprts
optional $ skipKey "%TBOX"
ts <- many tbox
optional $ skipKey "%RBOX"
rs <- many rbox
optional $ skipKey "%ABOX"
as <- many abox
return $ Box ts rs as
imprts :: CharParser st ()
imprts = skipKey "import" << many1 (myLetter <|> char '.') << skip
<< optional rename
rename :: CharParser st ()
rename = do
skipKey "with"
optional $ skipKey "concepts" << nmap
optional $ skipKey "roles" << nmap
optional $ skipKey "individuals" << nmap
return ()
nmap :: GenParser Char st [()]
nmap = sepBy1 (nominal >> skipKey "as" << nominal) commaP
ppp :: (a -> Doc) -> CharParser () a -> String -> String
ppp pr pa s = case parse (skip >> pa << eof) "" s of
Right a -> show $ pr a
Left e -> show e