-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmacros.slisp
287 lines (227 loc) · 7.53 KB
/
macros.slisp
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
;; Built in macros for SugarLisp.
;; Note: Many of these are the same (or slightly modifed) from LispyScript 1
;;;;;;;;;;;;;;;;;;;; Conditionals ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(export undefined? (macro (obj)
(=== (typeof ~obj) "undefined")))
(export defined? (macro (obj)
(!== (typeof ~obj) "undefined")))
(export null? (macro (obj)
(=== ~obj null)))
(export nil? (macro (obj)
(|| (undefined? ~obj) (null? ~obj))))
(export true? (macro (obj)
(=== true ~obj)))
(export false? (macro (obj)
(=== false ~obj)))
(export boolean? (macro (obj)
(=== (typeof ~obj) "boolean")))
(export zero? (macro (obj)
(=== 0 ~obj)))
(export number? (macro (obj)
(=== (Object.prototype.toString.call ~obj) "[object Number]")))
(export even? (macro (num)
(=== (% ~num 2) 0)))
(export odd? (macro (num)
(!== (% ~num 2) 0)))
(export string? (macro (obj)
(=== (Object.prototype.toString.call ~obj) "[object String]")))
(export array? (macro (obj)
(=== (Object.prototype.toString.call ~obj) "[object Array]")))
(export object? (macro (obj)
(=== (Object.prototype.toString.call ~obj) "[object Object]")))
(export function? (macro (obj)
(=== (Object.prototype.toString.call ~obj) "[object Function]")))
;;;;;;;;;;;;;;;;;;;;;;; Expressions ;;;;;;;;;;;;;;;;;;;;
(export when (macro (cond ...rest)
(if? ~cond (do ~rest))))
(export unless (macro (cond ...rest)
(when (! ~cond) (do ~rest))))
(export cond (macro (...rest)
(if? (#args-shift rest) (#args-shift rest) (#args-if rest (cond ~rest)))))
(export case (macro (matchto ...rest)
(if? (=== ~matchto (#args-shift rest)) (#args-shift rest) (#args-if rest (case ~matchto ~rest)))))
(export arrayInit (macro (len obj)
((function (l o)
(var ret [])
(js "for(var i=0;i<l;i++) ret.push(o);")
ret) ~len ~obj)))
(export arrayInit2d (macro (i j obj)
((function (i j o)
(var ret [])
(js "for(var n=0;n<i;n++){var inn=[];for(var m=0;m<j;m++) inn.push(o); ret.push(inn);}")
ret) ~i ~j ~obj)))
;; method chaining macrofn
(export -> (macro (func form ...rest)
(#args-if rest
(-> (((#args-shift form) ~func) ~@form) ~rest)
(((#args-shift form) ~func) ~@form))))
;; alias one symbol name to another
;; e.g. select one of several "implementation" (to) macros used behind "logical" (from) name
;; see the sugarlisp-async macros for an example of use
(export alias (macro (from to)
(macro ~from (...rest) (~to ~(js "sl.list('~','rest')")))))
;;;;;;;;;;;;;;;;;;;;;; Iteration and Looping ;;;;;;;;;;;;;;;;;;;;
(export each (macro (arr ...rest)
(.forEach ~arr ~rest)))
(export eachPair (macro (arr fn)
((function (___a ___f)
(js "for(var ___n=0;___n<___a.length-1;___n+=2){ ___f(___a[___n], ___a[___n+1]); }"))
~arr ~fn)))
(export reduce (macro (arr ...rest)
(.reduce ~arr ~rest)))
(export eachKey (macro (obj fn ...rest)
((function (o f s)
(var _k (Object.keys o))
(each _k
(function (elem)
(f.call s (get elem o) elem o)))) ~obj ~fn ~rest)))
(export each2d (macro (arr fn)
(each ~arr
(function (___elem ___i ___oa)
(each ___elem
(function (___val ___j ___ia)
(~fn ___val ___j ___i ___ia ___oa)))))))
(export map (macro (...rest)
(Array.prototype.map.call ~rest)))
(export filter (macro (...rest)
(Array.prototype.filter.call ~rest)))
(export some (macro (...rest)
(Array.prototype.some.call ~rest)))
(export every (macro (...rest)
(Array.prototype.every.call ~rest)))
(export loop (macro (args vals ...rest)
((function ()
(var recur null
___result (! undefined)
___nextArgs null
___f (function ~args ~rest))
(set recur
(function ()
(set ___nextArgs arguments)
(if? (=== ___result undefined)
undefined
(do
(set ___result undefined)
(js "while(___result===undefined) ___result=___f.apply(this,___nextArgs);")
___result))))
(recur ~@vals)))))
// list comprehenson
(export list-of (macro (...rest)
(doMonad arrayMonad ~rest)))
(export first (macro (arr)
(get 0 ~arr)))
(export rest (macro (arr)
(~arr.slice 1 ~arr.length)))
(export empty? (macro (arr)
(=== 0 ~arr.length)))
;;;;;;;;;;;;;;;;;;;; Templates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Note we now promote the use of our "html dialect" in lieu of these.
;; However template-repeat is still used by the testRunner macro below.
(export template (macro (name args ...rest)
(var ~name
(function ~args
(str ~rest)))))
(export template-repeat (macro (arg ...rest)
(reduce ~arg
(function (___memo elem index)
(+ ___memo (str ~rest))) "")))
(export template-repeat-key (macro (obj ...rest)
(do
(var ___ret "")
(eachKey ~obj
(function (value key)
(set ___ret (+ ___ret (str ~rest)))))
___ret)))
;;;;;;;;;;;;;;;;;;; Unit Testing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(export assert (macro (cond message)
(if? (true? ~cond)
(+ "Passed - " ~message)
(+ "Failed - " ~message))))
(export testGroup (macro (name ...rest)
(var ~name
(function ()
(array ~rest)))))
(export testRunner (macro (groupname desc)
((function (groupname desc)
(var start (new Date)
tests (groupname)
passed 0
failed 0)
(each tests
(function (elem)
(if? (elem.match #/^Passed/)
++passed
++failed)))
(str
(str "\n" desc "\n" start "\n\n")
(template-repeat tests elem "\n")
"\nTotal tests " tests.length
"\nPassed " passed
"\nFailed " failed
"\nDuration " (- (new Date) start) "ms\n")) ~groupname ~desc)))
;;;;;;;;;;;;;;;; Monads ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(export identityMonad (macro ()
(object
mBind (function (mv mf) (mf mv))
mResult (function (v) v))))
(export maybeMonad (macro ()
(object
mBind (function (mv mf) (if? (null? mv) null (mf mv)))
mResult (function (v) v)
mZero null)))
(export arrayMonad (macro ()
(object
mBind (function (mv mf)
(reduce
(map mv mf)
(function (accum val) (accum.concat val))
[]))
mResult (function (v) [v])
mZero []
mPlus (function ()
(reduce
(Array.prototype.slice.call arguments)
(function (accum val) (accum.concat val))
[])))))
(export stateMonad (macro ()
(object
mBind (function (mv f)
(function (s)
(var l (mv s)
v (get 0 l)
ss (get 1 l))
((f v) ss)))
mResult (function (v) (function (s) [v, s])))))
(export continuationMonad (macro ()
(object
mBind (function (mv mf)
(function (c)
(mv
(function (v)
((mf v) c)))))
mResult (function (v)
(function (c)
(c v))))))
(export m-bind (macro (bindings expr)
(mBind (#args-second bindings)
(function ((#args-shift bindings))
(#args-if bindings (m-bind ~bindings ~expr) ((function () ~expr)))))))
(export withMonad (macro (monad ...rest)
((function (___monad)
(var mBind ___monad.mBind
mResult ___monad.mResult
mZero ___monad.mZero
mPlus ___monad.mPlus)
~rest) (~monad))))
(export doMonad (macro (monad bindings expr)
(withMonad ~monad
(var ____mResult
(function (___arg)
(if? (&& (undefined? ___arg) !(undefined? mZero))
mZero
(mResult ___arg))))
(m-bind ~bindings (____mResult ~expr)))))
(export monad (macro (name obj)
(var ~name
(function ()
~obj))))