-
Notifications
You must be signed in to change notification settings - Fork 6
/
Helpers.hs
232 lines (192 loc) · 6.24 KB
/
Helpers.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
module Helpers where
import Data.List
import Data.List.Extra
import qualified Text.Casing as Casing -- cabal install casing
import DocHandler
import Head
import Snippets
-- (MOD) helpers
moduleHeader name (Module m doc) shared imp =
"defmodule " ++
name ++
" do\n" ++
ident
(moduleDoc doc ++
sharedVariablesDeclaration shared ++
oracleDelaration ++
if imp
then "import " ++ m ++ "\n\n"
else "")
sharedVariablesDeclaration :: [String] -> String
sharedVariablesDeclaration [] = ""
sharedVariablesDeclaration shared =
unlines ([ "def shared_variables do", " [" ] ++ map (\v -> " :" ++ snake v ++ ",") shared ++ [" ]", "end"])
moduleContext (Module m _) = [(m, "module")]
-- (CALL) helpers
call g i [] = snake i
call g i ps = let divider = if (i, "local") `elem` g then "." else ""
in snake i ++ divider ++ "(" ++ intercalate ", " ps ++ ")"
-- (IF) helpers
ifExpr c t e = "(if " ++ c ++ ", do: " ++ t ++ ", else: " ++ e ++ ")"
-- (REC-LIT) helpers
isLiteral ((Key _), _) = True
isLiteral _ = False
-- (INFO-*) helpers
actionName (ActionCall i ps) = i ++ "(" ++ intercalate ", " (map interpolate ps) ++ ")"
actionName a = escape (show a)
escape cs = foldr (++) "" (map escape' cs)
escape' :: Char -> String
escape' c
| c `elem` regexChars = '\\' : [c]
| otherwise = [c]
where
regexChars = "\\+()^$.{}]|\""
-- Others
enclose s = "(" ++ s ++ ")"
cFold :: [ElixirCode] -> ElixirCode
cFold [] = "true"
cFold [c] = c
cFold cs = let (preassignments, conditions) = partition preassignment cs
in intercalate "" (map (++ "\n") preassignments) ++ "Enum.all?([" ++ intercalate ", " conditions ++ "])"
aFold :: [ElixirCode] -> ElixirCode
aFold [] = "%{}"
aFold as =
let (preassignments, as') = partition preassignment as
(postAssignments, actions) = partition postassignment as'
kvs = intercalate ",\n" (map keyValue actions)
initialVariables =
case actions of
[] -> []
_ -> ["%{\n" ++ ident kvs ++ "\n}"]
in intercalate "" (map (++ "\n") preassignments) ++ mapMerge (initialVariables ++ postAssignments)
orFold :: [ElixirCode] -> ElixirCode
orFold [] = "true"
orFold [c] = c
orFold cs = "Enum.any?([" ++ intercalate ", " cs ++ "])"
isUnchanged (Unchanged _) = True
isUnchanged _ = False
allUnchanged xs = dropWhile isUnchanged xs == []
keyValue a = drop 3 (dropEnd 2 a)
mapMerge [m] = m
mapMerge (m:ms) = "Map.merge(\n " ++ m ++ ",\n" ++ ident (mapMerge ms) ++ ")\n"
preassignment as = any (isPrefixOf " = ") (tails as)
postassignment as =
(head as) == '(' ||
take 2 as == "if" || dropWhile (/= ':') as == [] || take 4 as == "Enum" || take 3 as == "Map" || take 4 as == "List"
interpolate (Lit (Str i)) = i
interpolate (Ref i) = "#{inspect " ++ i ++ "}"
interpolate i = show i
declaration i ps = "def " ++ snake i ++ "(" ++ intercalate ", " ("variables" : ps) ++ ") do\n"
identAndSeparate sep ls = (intercalate (sep ++ "\n") (map ((++) " ") ls))
unzipAndFold :: [([a], [b])] -> ([a], [b])
unzipAndFold = foldr (\x (a, b) -> (fst x ++ a, snd x ++ b)) ([], [])
snake i = Casing.toQuietSnake (Casing.fromAny i)
pascal i = Casing.toPascal (Casing.fromAny i)
ident block = intercalate "\n" (map tabIfline (lines block))
mapAndJoin f ls = intercalate "\n" (map f ls)
tabIfline [] = []
tabIfline xs = " " ++ xs
isNamed i (ActionDefinition id _ _ _) = i == id
isNamed i (ValueDefinition id _ _) = i == id
isNamed _ _ = False
name (ActionDefinition i _ _ _) = i
name (ValueDefinition i _ _) = i
name _ = ""
moduleName (Module i _) = i
partitionCondition (Condition c) = [c]
partitionCondition (ActionCall c ps) = [ConditionCall c ps]
partitionCondition (ActionOr cs) =
let ics = foldr (++) [] (map partitionCondition cs)
in if ics /= []
then [Or ics]
else []
partitionCondition (ActionAnd cs) =
let ics = foldr (++) [] (map partitionCondition cs)
in if ics /= []
then [And ics]
else []
partitionCondition (Exists i v (ActionOr cs)) =
let ics = foldr (++) [] (map partitionCondition cs)
in if ics /= []
then [Or ics]
else []
partitionCondition _ = []
specialDef :: String -> String -> Definition -> Bool
specialDef _ _ (Constants _) = True
specialDef _ _ (Variables _) = True
specialDef i n d = (isNamed i d) || (isNamed n d)
findConstants ds =
concat
(map
(\d ->
case d of
Constants cs -> cs
_ -> [])
ds)
findVariables ds =
concat
(map
(\d ->
case d of
Variables cs -> cs
_ -> [])
ds)
findIdentifier i ds =
case find (isNamed i) ds of
Just a -> a
Nothing -> error ("Definition not found: " ++ show i ++ " in " ++ show ds)
starterTask mName name init =
unlines
[ "defmodule Mix.Tasks." ++ pascal (mName ++ "_" ++ name ++ "Starter") ++ " do"
, " @moduledoc \"Printed when the user requests `mix help echo`\""
, " @shortdoc \"Echoes arguments\""
, " use Mix.Task"
, " import " ++ mName
, " import " ++ mName ++ "_" ++ name
, ""
, " @impl Mix.Task"
, " def run(args) do"
, " variables = %{}"
, " initial_state = " ++ init
, " oracle_host = String.to_atom(Enum.at(args, 0))"
, " Node.connect(oracle_host)"
, " oracle_pid = find_oracle()"
, " IO.puts(inspect(oracle_pid))"
, " main(oracle_pid, initial_state, 0)"
, " end"
, ""
, " def find_oracle() do"
, " o = :global.whereis_name(\"oracle\")"
, " if o == :undefined do"
, " find_oracle()"
, " else"
, " o"
, " end"
, " end"
, "end"
]
tracerStarterTask name trace =
unlines
[ "defmodule Mix.Tasks.Startmodel do"
, " @moduledoc \"Printed when the user requests `mix help echo`\""
, " @shortdoc \"Echoes arguments\""
, " use Mix.Task"
, ""
, " @impl Mix.Task"
, " def run(_) do"
, " trace = ["
] ++
trace ++
unlines
[ " ]"
, ""
, " oracle = spawn(TraceCheckerOracle, :start, [trace])"
, " " ++ name ++ ".main(oracle, Enum.at(trace, 0), 0)"
, " end"
, "end"
]
toValue :: Action -> Value
toValue (ActionAnd as) = And (map toValue as)
toValue (Condition v) = v
toValue (ActionCall i ps) = ConditionCall i ps
toValue a = error("Not a value: " ++ show a)