|
2 | 2 | Copyright : (c) Runtime Verification, 2023
|
3 | 3 | License : BSD-3-Clause
|
4 | 4 |
|
5 |
| -Builtin functions. Only a select few functions are implemented. |
| 5 | +Builtin functions as described in [docs/hooks.md](https://github.com/runtimeverification/haskell-backend/blob/master/docs/hooks.md). |
| 6 | +Only selected functions are implemented. |
6 | 7 |
|
7 | 8 | Built-in functions are looked up by their symbol attribute 'hook' from
|
8 | 9 | the definition's symbol map.
|
| 10 | +
|
| 11 | +The built-in function fails outright when its function is called with |
| 12 | +a wrong argument count. When required arguments are unevaluated, the |
| 13 | +hook returns 'Nothing'. |
9 | 14 | -}
|
10 | 15 | module Booster.Builtin (
|
11 | 16 | hooks,
|
12 | 17 | ) where
|
13 | 18 |
|
14 |
| -import Control.Monad |
15 |
| -import Control.Monad.Trans.Except |
16 |
| -import Data.ByteString.Char8 (ByteString) |
17 |
| -import Data.List (partition) |
| 19 | +import Data.ByteString (ByteString) |
18 | 20 | import Data.Map (Map)
|
19 | 21 | import Data.Map qualified as Map
|
20 |
| -import Data.Text (Text) |
21 |
| -import Prettyprinter (pretty, vsep) |
22 |
| - |
23 |
| -import Booster.Pattern.Base |
24 |
| -import Booster.Pattern.Bool |
25 |
| -import Booster.Pattern.Util |
26 |
| -import Booster.Prettyprinter |
27 | 22 |
|
28 |
| --- built-in functions may fail on arity or sort errors, and may be |
29 |
| --- partial (returning a Maybe type) |
30 |
| -type BuiltinFunction = [Term] -> Except Text (Maybe Term) |
| 23 | +import Booster.Builtin.BOOL |
| 24 | +import Booster.Builtin.Base |
| 25 | +import Booster.Builtin.INT |
| 26 | +import Booster.Builtin.KEQUAL |
| 27 | +import Booster.Builtin.LIST |
| 28 | +import Booster.Builtin.MAP |
31 | 29 |
|
32 | 30 | hooks :: Map ByteString BuiltinFunction
|
33 | 31 | hooks =
|
34 | 32 | Map.unions
|
35 |
| - [ builtinsKEQUAL |
| 33 | + [ builtinsBOOL |
| 34 | + , builtinsINT |
36 | 35 | , builtinsMAP
|
| 36 | + , builtinsLIST |
| 37 | + , builtinsKEQUAL |
37 | 38 | ]
|
38 |
| - |
39 |
| ------------------------------------------------------------- |
40 |
| -(~~>) :: ByteString -> BuiltinFunction -> (ByteString, BuiltinFunction) |
41 |
| -(~~>) = (,) |
42 |
| - |
43 |
| ------------------------------------------------------------- |
44 |
| --- MAP hooks |
45 |
| --- only lookups and in_keys are implemented |
46 |
| -builtinsMAP :: Map ByteString BuiltinFunction |
47 |
| -builtinsMAP = |
48 |
| - Map.mapKeys ("MAP." <>) $ |
49 |
| - Map.fromList |
50 |
| - [ "lookup" ~~> mapLookupHook |
51 |
| - , "lookupOrDefault" ~~> mapLookupOrDefaultHook |
52 |
| - , "in_keys" ~~> mapInKeysHook |
53 |
| - ] |
54 |
| - |
55 |
| -mapLookupHook :: BuiltinFunction |
56 |
| -mapLookupHook args |
57 |
| - | [KMap _ pairs _mbRest, key] <- args = |
58 |
| - -- if the key is not found, return Nothing (no result), |
59 |
| - -- regardless of whether the key _could_ still be there. |
60 |
| - pure $ lookup key pairs |
61 |
| - | [_other, _] <- args = |
62 |
| - -- other `shouldHaveSort` "SortMap" |
63 |
| - pure Nothing -- not an internalised map, maybe a function call |
64 |
| - | otherwise = |
65 |
| - -- FIXME write a helper function for arity check |
66 |
| - throwE . renderText $ "MAP.lookup: wrong arity " <> pretty (length args) |
67 |
| - |
68 |
| -mapLookupOrDefaultHook :: BuiltinFunction |
69 |
| -mapLookupOrDefaultHook args |
70 |
| - | [KMap _ pairs mbRest, key, defaultValue] <- args = do |
71 |
| - case lookup key pairs of |
72 |
| - Just value -> |
73 |
| - -- key was found, simply return |
74 |
| - pure $ Just value |
75 |
| - Nothing -- key could be in unevaluated or opaque part |
76 |
| - | Just _ <- mbRest -> |
77 |
| - pure Nothing -- have opaque part, no result |
78 |
| - | any ((\(Term a _) -> not a.isConstructorLike) . fst) pairs -> |
79 |
| - pure Nothing -- have unevaluated keys, no result |
80 |
| - | otherwise -> -- certain that the key is not in the map |
81 |
| - pure $ Just defaultValue |
82 |
| - | [_other, _, _] <- args = |
83 |
| - -- other `shouldHaveSort` "SortMap" |
84 |
| - pure Nothing -- not an internalised map, maybe a function call |
85 |
| - | otherwise = |
86 |
| - throwE . renderText $ "MAP.lookupOrDefault: wrong arity " <> pretty (length args) |
87 |
| - |
88 |
| -mapInKeysHook :: BuiltinFunction |
89 |
| -mapInKeysHook args |
90 |
| - | [key, KMap _ pairs mbRest] <- args = do |
91 |
| - -- only consider evaluated keys, return Nothing if any unevaluated ones are present |
92 |
| - let (eval'edKeys, uneval'edKeys) = |
93 |
| - partition (\(Term a _) -> a.isConstructorLike) (map fst pairs) |
94 |
| - case (key `elem` eval'edKeys, key `elem` uneval'edKeys) of |
95 |
| - (True, _) -> |
96 |
| - -- constructor-like (evaluated) key is present |
97 |
| - pure $ Just TrueBool |
98 |
| - (False, True) -> |
99 |
| - -- syntactically-equal unevaluated key is present |
100 |
| - pure $ Just TrueBool |
101 |
| - (False, False) |
102 |
| - | Nothing <- mbRest -- no opaque rest |
103 |
| - , null uneval'edKeys -> -- no keys unevaluated |
104 |
| - pure $ Just FalseBool |
105 |
| - | otherwise -> -- key could be present once evaluated |
106 |
| - pure Nothing |
107 |
| - | [_, _other] <- args = do |
108 |
| - -- other `shouldHaveSort` "SortMap" |
109 |
| - pure Nothing -- not an internalised map, maybe a function call |
110 |
| - | otherwise = |
111 |
| - throwE . renderText $ "MAP.in_keys: wrong arity " <> pretty (length args) |
112 |
| - |
113 |
| ------------------------------------------------------------- |
114 |
| --- KEQUAL hooks |
115 |
| -builtinsKEQUAL :: Map ByteString BuiltinFunction |
116 |
| -builtinsKEQUAL = |
117 |
| - Map.fromList |
118 |
| - [ "KEQUAL.ite" ~~> iteHook |
119 |
| - , "KEQUAL.eq" ~~> equalsKHook |
120 |
| - , "KEQUAL.ne" ~~> nequalsKHook |
121 |
| - ] |
122 |
| - |
123 |
| -iteHook :: BuiltinFunction |
124 |
| -iteHook args |
125 |
| - | [cond, thenVal, elseVal] <- args = do |
126 |
| - cond `shouldHaveSort` "SortBool" |
127 |
| - unless (sortOfTerm thenVal == sortOfTerm elseVal) $ |
128 |
| - throwE . renderText . vsep $ |
129 |
| - [ "Different sorts in alternatives:" |
130 |
| - , pretty thenVal |
131 |
| - , pretty elseVal |
132 |
| - ] |
133 |
| - case cond of |
134 |
| - TrueBool -> pure $ Just thenVal |
135 |
| - FalseBool -> pure $ Just elseVal |
136 |
| - _other -> pure Nothing |
137 |
| - | otherwise = |
138 |
| - throwE . renderText $ "KEQUAL.ite: wrong arity " <> pretty (length args) |
139 |
| - |
140 |
| -equalsKHook :: BuiltinFunction |
141 |
| -equalsKHook args |
142 |
| - | [KSeq _ l, KSeq _ r] <- args = pure $ evalEqualsK l r |
143 |
| - | otherwise = |
144 |
| - throwE . renderText $ "KEQUAL.eq: wrong arity " <> pretty (length args) |
145 |
| - |
146 |
| -nequalsKHook :: BuiltinFunction |
147 |
| -nequalsKHook args |
148 |
| - | [KSeq _ l, KSeq _ r] <- args = pure $ negateBool <$> evalEqualsK l r |
149 |
| - | otherwise = |
150 |
| - throwE . renderText $ "KEQUAL.ne: wrong arity " <> pretty (length args) |
151 |
| - |
152 |
| -evalEqualsK :: Term -> Term -> Maybe Term |
153 |
| -evalEqualsK (SymbolApplication sL _ argsL) (SymbolApplication sR _ argsR) |
154 |
| - | isConstructorSymbol sL && isConstructorSymbol sR = |
155 |
| - if sL == sR |
156 |
| - then foldAndBool <$> zipWithM evalEqualsK argsL argsR |
157 |
| - else pure FalseBool |
158 |
| -evalEqualsK (SymbolApplication symbol _ _) DomainValue{} |
159 |
| - | isConstructorSymbol symbol = pure FalseBool |
160 |
| -evalEqualsK (SymbolApplication symbol _ _) Injection{} |
161 |
| - | isConstructorSymbol symbol = pure FalseBool |
162 |
| -evalEqualsK (SymbolApplication symbol _ _) KMap{} |
163 |
| - | isConstructorSymbol symbol = pure FalseBool |
164 |
| -evalEqualsK (SymbolApplication symbol _ _) KList{} |
165 |
| - | isConstructorSymbol symbol = pure FalseBool |
166 |
| -evalEqualsK (SymbolApplication symbol _ _) KSet{} |
167 |
| - | isConstructorSymbol symbol = pure FalseBool |
168 |
| -evalEqualsK DomainValue{} (SymbolApplication symbol _ _) |
169 |
| - | isConstructorSymbol symbol = pure FalseBool |
170 |
| -evalEqualsK Injection{} (SymbolApplication symbol _ _) |
171 |
| - | isConstructorSymbol symbol = pure FalseBool |
172 |
| -evalEqualsK KMap{} (SymbolApplication symbol _ _) |
173 |
| - | isConstructorSymbol symbol = pure FalseBool |
174 |
| -evalEqualsK KList{} (SymbolApplication symbol _ _) |
175 |
| - | isConstructorSymbol symbol = pure FalseBool |
176 |
| -evalEqualsK KSet{} (SymbolApplication symbol _ _) |
177 |
| - | isConstructorSymbol symbol = pure FalseBool |
178 |
| -evalEqualsK (Injection s1L s2L l) (Injection s1R s2R r) |
179 |
| - | s1L == s1R && s2L == s2R = evalEqualsK l r |
180 |
| -evalEqualsK l@DomainValue{} r@DomainValue{} = |
181 |
| - pure $ if l == r then TrueBool else FalseBool |
182 |
| -evalEqualsK l r = |
183 |
| - if l == r |
184 |
| - then pure TrueBool |
185 |
| - else fail "cannot evaluate" -- i.e., result is Nothing |
186 |
| - |
187 |
| --- check for simple (parameter-less) sorts |
188 |
| -shouldHaveSort :: Term -> SortName -> Except Text () |
189 |
| -t `shouldHaveSort` s |
190 |
| - | sortOfTerm t == SortApp s [] = |
191 |
| - pure () |
192 |
| - | otherwise = |
193 |
| - throwE . renderText . vsep $ |
194 |
| - [ pretty $ "Argument term has unexpected sort (expected " <> show s <> "):" |
195 |
| - , pretty t |
196 |
| - ] |
0 commit comments