@@ -22,10 +22,15 @@ import Data.ByteString.Char8 (ByteString, pack, unpack)
22
22
import Data.List (findIndex , partition )
23
23
import Data.Map (Map )
24
24
import Data.Map qualified as Map
25
+ import Data.Set qualified as Set
25
26
import Data.Text (Text )
26
27
import Prettyprinter (pretty , vsep )
27
28
import Text.Read (readMaybe )
28
29
30
+ import Booster.Definition.Attributes.Base (
31
+ KCollectionSymbolNames (.. ),
32
+ KListDefinition (.. ),
33
+ )
29
34
import Booster.Pattern.Base
30
35
import Booster.Pattern.Bool
31
36
import Booster.Pattern.Util
@@ -62,6 +67,9 @@ t `shouldHaveSort` s
62
67
, pretty t
63
68
]
64
69
70
+ isConstructorLike_ :: Term -> Bool
71
+ isConstructorLike_ = (. isConstructorLike) . getAttributes
72
+
65
73
------------------------------------------------------------
66
74
-- BOOL hooks
67
75
@@ -197,23 +205,78 @@ builtinsMAP :: Map ByteString BuiltinFunction
197
205
builtinsMAP =
198
206
Map. mapKeys (" MAP." <> ) $
199
207
Map. fromList
200
- [ " lookup" ~~> mapLookupHook
208
+ [ " update" ~~> mapUpdateHook
209
+ , " remove" ~~> mapRemoveHook
210
+ , -- removeAll: requires a Set argument
211
+ " size" ~~> mapSizeHook
212
+ , " lookup" ~~> mapLookupHook
201
213
, " lookupOrDefault" ~~> mapLookupOrDefaultHook
202
214
, " in_keys" ~~> mapInKeysHook
203
- , " update" ~~> mapUpdateHook
215
+ , -- keys: requires internal Set construction
216
+ " keys_list" ~~> mapKeysListHook
217
+ , " values" ~~> mapValuesHook
218
+ , " inclusion" ~~> mapInclusionHook
204
219
]
205
220
221
+ mapUpdateHook :: BuiltinFunction
222
+ mapUpdateHook args
223
+ | [KMap def pairs mbRest, key, newValue] <- args = do
224
+ case findIndex ((== key) . fst ) pairs of
225
+ Just idx ->
226
+ -- key was found (syntactically), update pairs list
227
+ let newPairs = take idx pairs <> ((key, newValue) : drop (idx + 1 ) pairs)
228
+ in pure $ Just $ KMap def newPairs mbRest
229
+ Nothing -- key could be in unevaluated or opaque part
230
+ | Just _ <- mbRest ->
231
+ pure Nothing -- have opaque part, no result
232
+ | any (not . isConstructorLike_ . fst ) pairs ->
233
+ pure Nothing -- have unevaluated keys, no result
234
+ | otherwise -> -- key certain to be absent, no rest: add pair
235
+ pure $ Just $ KMap def ((key, newValue) : pairs) Nothing
236
+ | [_other, _, _] <- args =
237
+ pure Nothing -- not an internalised map, maybe a function call
238
+ | otherwise =
239
+ throwE . renderText $ " MAP.update: wrong arity " <> pretty (length args)
240
+
241
+ mapRemoveHook :: BuiltinFunction
242
+ mapRemoveHook args
243
+ | [m@ (KMap def pairs mbRest), key] <- args = do
244
+ case findIndex ((== key) . fst ) pairs of
245
+ Just idx ->
246
+ -- key was found (syntactically), remove it
247
+ let newPairs = take idx pairs <> drop (idx + 1 ) pairs
248
+ in pure $ Just $ KMap def newPairs mbRest
249
+ Nothing -- key could be in unevaluated or opaque part
250
+ | Just _ <- mbRest ->
251
+ pure Nothing -- have opaque part, no result
252
+ | any (not . isConstructorLike_ . fst ) pairs ->
253
+ pure Nothing -- have unevaluated keys, no result
254
+ | otherwise -> -- key certain to be absent, no rest: map unchanged
255
+ pure $ Just m
256
+ | [_other, _] <- args =
257
+ pure Nothing -- not an internalised map, maybe a function call
258
+ | otherwise =
259
+ throwE . renderText $ " MAP.remove: wrong arity " <> pretty (length args)
260
+
261
+ mapSizeHook :: BuiltinFunction
262
+ mapSizeHook args
263
+ | [KMap _ pairs Nothing ] <- args =
264
+ -- no opaque part, size is association count
265
+ pure $ Just $ intTerm (fromIntegral $ length pairs)
266
+ | [_other, _] <- args =
267
+ pure Nothing -- not an internalised map, maybe a function call
268
+ | otherwise =
269
+ throwE . renderText $ " MAP.lookup: wrong arity " <> pretty (length args)
270
+
206
271
mapLookupHook :: BuiltinFunction
207
272
mapLookupHook args
208
273
| [KMap _ pairs _mbRest, key] <- args =
209
274
-- if the key is not found, return Nothing (no result),
210
275
-- regardless of whether the key _could_ still be there.
211
276
pure $ lookup key pairs
212
277
| [_other, _] <- args =
213
- -- other `shouldHaveSort` "SortMap"
214
278
pure Nothing -- not an internalised map, maybe a function call
215
279
| otherwise =
216
- -- FIXME write a helper function for arity check
217
280
throwE . renderText $ " MAP.lookup: wrong arity " <> pretty (length args)
218
281
219
282
mapLookupOrDefaultHook :: BuiltinFunction
@@ -226,12 +289,11 @@ mapLookupOrDefaultHook args
226
289
Nothing -- key could be in unevaluated or opaque part
227
290
| Just _ <- mbRest ->
228
291
pure Nothing -- have opaque part, no result
229
- | any (( \ ( Term a _) -> not a . isConstructorLike) . fst ) pairs ->
292
+ | any (not . isConstructorLike_ . fst ) pairs ->
230
293
pure Nothing -- have unevaluated keys, no result
231
294
| otherwise -> -- certain that the key is not in the map
232
295
pure $ Just defaultValue
233
296
| [_other, _, _] <- args =
234
- -- other `shouldHaveSort` "SortMap"
235
297
pure Nothing -- not an internalised map, maybe a function call
236
298
| otherwise =
237
299
throwE . renderText $ " MAP.lookupOrDefault: wrong arity " <> pretty (length args)
@@ -241,7 +303,7 @@ mapInKeysHook args
241
303
| [key, KMap _ pairs mbRest] <- args = do
242
304
-- only consider evaluated keys, return Nothing if any unevaluated ones are present
243
305
let (eval'edKeys, uneval'edKeys) =
244
- partition ( \ ( Term a _) -> a . isConstructorLike) (map fst pairs)
306
+ partition isConstructorLike_ (map fst pairs)
245
307
case (key `elem` eval'edKeys, key `elem` uneval'edKeys) of
246
308
(True , _) ->
247
309
-- constructor-like (evaluated) key is present
@@ -261,36 +323,106 @@ mapInKeysHook args
261
323
| otherwise =
262
324
throwE . renderText $ " MAP.in_keys: wrong arity " <> pretty (length args)
263
325
264
- mapUpdateHook :: BuiltinFunction
265
- mapUpdateHook args
266
- | [KMap def pairs mbRest, key, newValue] <- args = do
267
- case findIndex ((== key) . fst ) pairs of
268
- Just idx ->
269
- -- key was found (syntactically), update pairs list
270
- let newPairs = take idx pairs <> ((key, newValue) : drop (idx + 1 ) pairs)
271
- in pure $ Just $ KMap def newPairs mbRest
272
- Nothing -- key could be in unevaluated or opaque part
273
- | Just _ <- mbRest ->
274
- pure Nothing -- have opaque part, no result
275
- | any ((\ (Term a _) -> not a. isConstructorLike) . fst ) pairs ->
276
- pure Nothing -- have unevaluated keys, no result
277
- | otherwise -> -- key certain to be absent, no rest: add pair
278
- pure $ Just $ KMap def ((key, newValue) : pairs) Nothing
279
- | [_other, _, _] <- args =
280
- -- other `shouldHaveSort` "SortMap"
281
- pure Nothing -- not an internalised map, maybe a function call
282
- | otherwise =
283
- throwE . renderText $ " MAP.update: wrong arity " <> pretty (length args)
284
-
285
- ------------------------------------------------------------
326
+ mapKeysListHook :: BuiltinFunction
327
+ mapKeysListHook = \ case
328
+ [KMap _ pairs Nothing ] ->
329
+ -- known keys only, return as list
330
+ pure $ Just $ KList kItemListDef (map fst pairs) Nothing
331
+ [KMap _ _ (Just _)] ->
332
+ -- indeterminate part
333
+ pure Nothing
334
+ [_arg] ->
335
+ -- unevaluated
336
+ pure Nothing
337
+ args ->
338
+ throwE . renderText $ " MAP.keys_list: wrong arity " <> pretty (length args)
339
+
340
+ mapValuesHook :: BuiltinFunction
341
+ mapValuesHook = \ case
342
+ [KMap _ pairs Nothing ] ->
343
+ -- known values only, return as list
344
+ pure $ Just $ KList kItemListDef (map snd pairs) Nothing
345
+ [KMap _ _ (Just _)] ->
346
+ -- indeterminate part
347
+ pure Nothing
348
+ [_arg] ->
349
+ -- unevaluated
350
+ pure Nothing
351
+ args ->
352
+ throwE . renderText $ " MAP.values: wrong arity " <> pretty (length args)
353
+
354
+ mapInclusionHook :: BuiltinFunction
355
+ mapInclusionHook = \ case
356
+ [KMap d1 pairs1 mbRest1, KMap d2 pairs2 mbRest2]
357
+ | d1 /= d2 -> -- different kinds of map
358
+ pure Nothing
359
+ | pairs1 == pairs2 -- syntactically identical maps
360
+ , mbRest1 == mbRest2 ->
361
+ pure $ Just TrueBool
362
+ [KMap _ pairs1 Nothing , KMap _ pairs2 mbRest2]
363
+ -- fully concrete maps
364
+ | keySet pairs1 `Set.isSubsetOf` keySet pairs2 ->
365
+ pure $ Just TrueBool
366
+ | all isConstructorLike_ (keySet pairs1)
367
+ , all isConstructorLike_ (keySet pairs2)
368
+ , Nothing <- mbRest2 -> -- fully-known keys, certain to not be subset
369
+ pure $ Just FalseBool
370
+ | otherwise ->
371
+ pure Nothing -- unevaluated keys present, indeterminate
372
+ [KMap _ _ (Just _), KMap _ _ _] ->
373
+ pure Nothing -- indeterminate part cannot be checked
374
+ [_, _] ->
375
+ pure Nothing
376
+ args ->
377
+ throwE . renderText $ " MAP.inclusion: wrong arity " <> pretty (length args)
378
+ where
379
+ keySet = Set. fromList . map fst
380
+
381
+ -----------------------------------------------------------
286
382
-- LIST hooks
383
+
287
384
builtinsLIST :: Map ByteString BuiltinFunction
288
385
builtinsLIST =
289
386
Map. mapKeys (" LIST." <> ) $
290
387
Map. fromList
291
- [ " size" ~~> listSizeHook
388
+ [ " get" ~~> listGetHook
389
+ , " size" ~~> listSizeHook
292
390
]
293
391
392
+ listGetHook :: BuiltinFunction
393
+ listGetHook [KList _ heads mbRest, intArg] =
394
+ let headLen = length heads
395
+ in case fromIntegral <$> readIntTerm intArg of
396
+ Nothing ->
397
+ intArg `shouldHaveSort` " SortInt" >> pure Nothing
398
+ Just i
399
+ | 0 <= i ->
400
+ if i < headLen
401
+ then pure $ Just $ heads !! i -- positive index in range
402
+ else -- headLen <= i
403
+ case mbRest of
404
+ Nothing ->
405
+ -- index too large
406
+ pure Nothing -- actually #Bottom
407
+ Just _ ->
408
+ pure Nothing
409
+ | otherwise -> -- i < 0, negative index, consider rest
410
+ case mbRest of
411
+ Nothing
412
+ | 0 <= headLen - abs i ->
413
+ pure $ Just $ heads !! (headLen - abs i)
414
+ | otherwise ->
415
+ pure Nothing -- actually #Bottom
416
+ Just (_middle, tails)
417
+ | 0 <= length tails - abs i ->
418
+ pure $ Just $ tails !! (length tails - abs i)
419
+ | otherwise ->
420
+ pure Nothing -- indeterminate middle
421
+ listGetHook [_other, _] =
422
+ pure Nothing
423
+ listGetHook args =
424
+ throwE . renderText $ " LIST.get: wrong arity " <> pretty (length args)
425
+
294
426
listSizeHook :: BuiltinFunction
295
427
listSizeHook = \ case
296
428
[KList _ heads Nothing ] ->
@@ -302,6 +434,19 @@ listSizeHook = \case
302
434
moreArgs ->
303
435
throwE . renderText $ " LIST.size: wrong arity " <> pretty (length moreArgs)
304
436
437
+ kItemListDef :: KListDefinition
438
+ kItemListDef =
439
+ KListDefinition
440
+ { symbolNames =
441
+ KCollectionSymbolNames
442
+ { unitSymbolName = " Lbl'Stop'List"
443
+ , elementSymbolName = " LblListItem"
444
+ , concatSymbolName = " Lbl'Unds'List'Unds'"
445
+ }
446
+ , elementSortName = " SortKItem"
447
+ , listSortName = " SortList"
448
+ }
449
+
305
450
------------------------------------------------------------
306
451
-- KEQUAL hooks
307
452
builtinsKEQUAL :: Map ByteString BuiltinFunction
0 commit comments