-
Notifications
You must be signed in to change notification settings - Fork 0
/
ybot_base.xtm
351 lines (286 loc) · 9.58 KB
/
ybot_base.xtm
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
;; lib-loading config
(if *impc:compiler:with-cache* (sys:load "libs/aot-cache/ybot_base.xtm" 'quiet))
(sys:load-preload-check 'ybot_base)
(define *xtmlib-ybot_base-loaded* #t)
(impc:aot:suppress-aot-do (sys:load "libs/core/adt.xtm"))
(impc:aot:insert-forms (sys:load "libs/core/adt.xtm" 'quiet))
(impc:aot:insert-header "xtmybot_base")
(impc:aot:import-ll "xtmybot_base")
;; Convenience utilities
(bind-macro (@ . args)
`(pref ,(car args) 0))
;;; left curry ;;;
;; curry: f(x1,x2,x3...), x2, x3, ... -> g(x1)
(bind-macro (curry . args)
`(lambda (x) (,(car args) x ,@(cdr args))))
(bind-func non-null:[bool,i8*]*
(lambda (p)
(not (null? p))))
(bind-func non-null:[bool,!a]*
(lambda (p)
(not (null? (cast p i8*)))))
(bind-func non-empty:[bool,List{!a}*]*
(lambda (lst)
(and (non-null lst) (< 0 (length lst)))))
(bind-func empty:[bool,List{!a}*]*
(lambda (lst)
(or (null? lst) (= 0 (length lst)))))
(bind-func non-empty:[bool,String*]*
(lambda (str)
(and (non-null str) (< 0 (length str)))))
(bind-func empty:[bool,String*]*
(lambda (str)
(or (null? str) (= 0 (length str)))))
;; This is a convenient way of checking something is not-null before doing stuff with it
(bind-macro (con . args)
`(cond
((non-null ,(car args))
(begin ,@(cdr args) void))
(else void)))
;; For literal binary numbers i.e. decimal 14 = hex e = (binary "1110")
(bind-func binary:[i8,i8*]*
(lambda (digits)
(let ((n:i64 (strlen digits))
(loop
(lambda (cc:i64 b:i8)
(cond
((< cc (min n 8))
(cond
((= (pref digits (- (- n 1) cc)) (i64toi8 49))
(loop (+ cc 1) (+ b (i64toi8 (<< 1 cc)))))
(else
(loop (+ cc 1) b))))
(else
b)))))
(loop 0 0))))
;; big endian combination of bytes
(bind-func be_literal:[i64,List{i8}*]*
(lambda (bytes)
(let ((loop
(lambda
(lst:List{i8}* j:i64 x:i64)
(cond
((and (non-empty lst) (< j 8))
(loop (cdr lst) (+ j 1) (bor x (<< (i8toui64 (car lst)) (* 8 j)))))
(else x)))))
(loop (reverse bytes) 0 0))))
;; little endian combination of bytes
(bind-func le_literal:[i64,List{i8}*]*
(lambda (bytes)
(let ((loop
(lambda
(lst:List{i8}* j:i64 x:i64)
(cond
((and (non-empty lst) (< j 8))
(loop (cdr lst) (+ j 1) (bor x (<< (i8toui64 (car lst)) (* 8 j)))))
(else x)))))
(loop (reverse bytes) 0 0))))
(bind-func bb:[i64,i8*,i64]*
(lambda (bstr:i8* n:i64)
(let ((lst:List{i8}* (map_t (lambda (m:i64) (binary "00000000")) (range n))))
(be_literal (cons (binary bstr) lst)))))
(bind-func print_byte:[void,i64]*
(lambda (d)
(let* ((bits:|8,bool|* (alloc)))
(doloop (n 8)
(aset! bits n (not (= 0 (modulo d 2))))
(set! d (/ d 2)))
(doloop (m 8)
(if (aref bits (- 7 m))
(printf "%d" 1)
(printf "%d" 0)))
(printf "\n")
void)))
(bind-func print_data:[void,i8*,i64]*
(lambda (data sz)
(doloop (i sz)
(cond
((= (pref data i) 0:i8)
(printf "_"))
(else
(printf "%c" (pref data i)))))
;(printf "\n")
void))
;; For colourful log printing
(bind-func colour_pencil:[void,i8*]*
(lambda (name)
(let ((n:i8
(cond
((= (i64toi32 0) (strcmp name "black")) (i64toi8 30))
((= (i64toi32 0) (strcmp name "red")) (i64toi8 31))
((= (i64toi32 0) (strcmp name "green")) (i64toi8 32))
((= (i64toi32 0) (strcmp name "yellow")) (i64toi8 33))
((= (i64toi32 0) (strcmp name "blue")) (i64toi8 34))
((= (i64toi32 0) (strcmp name "magenta")) (i64toi8 35))
((= (i64toi32 0) (strcmp name "cyan")) (i64toi8 36))
((= (i64toi32 0) (strcmp name "white")) (i64toi8 37))
(else (i64toi8 0)))))
(printf "%c[%dm" (i64toi8 27) n)
void)))
(bind-func highlighter:[void,i8*]*
(lambda (name)
(let ((n:i8
(cond
((= (i64toi32 0) (strcmp name "black")) (i64toi8 40))
((= (i64toi32 0) (strcmp name "red")) (i64toi8 41))
((= (i64toi32 0) (strcmp name "green")) (i64toi8 42))
((= (i64toi32 0) (strcmp name "yellow")) (i64toi8 43))
((= (i64toi32 0) (strcmp name "blue")) (i64toi8 44))
((= (i64toi32 0) (strcmp name "magenta")) (i64toi8 45))
((= (i64toi32 0) (strcmp name "cyan")) (i64toi8 46))
((= (i64toi32 0) (strcmp name "white")) (i64toi8 47))
(else (i64toi8 0)))))
(printf "%c[%dm" (i64toi8 27) n)
void)))
;; Naive sorting algorithm - no attempt at optimisation
;; Start with tmp list of already-sorted elements
;; for each element in list, see where it fits into the already-sorted list - insert it there and remove it from the to-be-sorted
(bind-func sort:[List{!a}*,List{!a}*,List{!a}*,[bool,!a,!a]*]*
(lambda (to_be_sorted already_sorted cmp)
(if (null? to_be_sorted)
already_sorted
(let* ((x (car to_be_sorted))
(y (cdr to_be_sorted))
(n:i64 (length already_sorted))
(loop:[List*,i64]*
(lambda (index:i64)
(cond
((= n 0)
(cons x already_sorted))
((>= index n)
(insert already_sorted n x))
((cmp x (nth already_sorted index))
(insert already_sorted index x))
(else (loop (+ index 1)))))))
(sort y (loop 0) cmp)))))
;; convenience wrapper around the (member) function that returns true/false
(bind-func contains:[bool,!a,List{!a}*]*
(lambda (element lst)
(non-empty (member element lst))))
;; Standard string join function (for some reason is missing from the extempore std library)
(bind-func join:[String*,String*,List{String*}*]*
(lambda (delim lst)
(cond
((null? lst) null)
(else
(let* ((stl:List{String*}* (reverse lst))
(loop (lambda (input:List{String*}* output:String*)
(cond
((null? input) output)
(else
(loop (cdr input) (cat (car input) (cat delim output))))))))
(loop (cdr stl) (car stl)))))))
;; Extra string functions
(bind-func cstring:[i8*,i8]*
(lambda (c)
(let ((p:i8* (zalloc 2)))
(pset! p 0 c)
(pset! p 1 0)
p)))
(bind-func to_lower:[i8,i8]*
(lambda (c)
(if (and (< (i64toi8 64) c) (< c (i64toi8 91)))
(+ c (i64toi8 32))
c)))
(bind-func to_upper:[i8,i8]*
(lambda (c)
(if (and (< (i64toi8 96) c) (< c (i64toi8 123)))
(- c (i64toi8 32))
c)))
(bind-func lower:[String*,i8*]*
(lambda (cstr)
(let* ((i:i64 0) (n:i64 (strlen cstr))
(buffer:i8* (zalloc (+ n 1))))
(dotimes (i n)
(pset! buffer i (to_lower (pref cstr i))))
(pset! buffer n 0)
(String buffer))))
(bind-func lower:[String*,String*]*
(lambda (str:String*)
(let ((cstr:i8* (cstring str)))
(lower:[String*,i8*]* cstr))))
(bind-func upper:[String*,i8*]*
(lambda (cstr)
(let* ((i:i64 0) (n:i64 (strlen cstr))
(buffer:i8* (zalloc (+ n 1))))
(dotimes (i n)
(pset! buffer i (to_upper (pref cstr i))))
(pset! buffer n 0)
(String buffer))))
(bind-func upper:[String*,String*]*
(lambda (str:String*)
(let ((cstr:i8* (cstring str)))
(upper:[String*,i8*]* cstr))))
(bind-func print_hex:[void,String*]*
(lambda (str)
(let ((i:i64 0)
(n:i64 (length str))
(data:i8* (tref str 1)))
(dotimes (i (- n 1))
(printf "%02X " (pref data i)))
(printf "%02X" (pref data (- n 1)))
void)))
(bind-func print_hex:[void,i8*,i64]*
(lambda (data n)
(let ((i:i64 0))
(dotimes (i (- n 1))
(cond
((= 3 (modulo i 4))
(printf "%02X " (pref data i)))
(else (printf "%02X." (pref data i)))))
(printf "%02X" (pref data (- n 1)))
void)))
(bind-func print_sanitised:[void,String*]*
(lambda (str:String*)
(cond
((non-null str)
(let ((i:i64 0) (n:i64 (length str))
(data:i8* (tref str 1)))
(dotimes (i n)
(let ((c:i8 (pref data i)))
(cond
((< 31:i8 c)
(printf "%c" c))
(else
(printf "[%02X]" c)))))
void))
(else void))))
;; override base.xtm string comparison, so that it doesn't crash on null
(bind-func equal
(lambda (s1:String* s2:String*)
(if (or (null? s1) (null? s2))
0:i1
(if (= (strcmp (tref s1 1) (tref s2 1)) 0)
1:i1 0:i1))))
(bind-func safe_cstr:[i8*,String*]*
(lambda (str:String*)
(cond
((non-null str)
(cstring str))
(else
(zalloc)))))
;; String parsing utilities
(bind-func first_word:[String*,String*]*
(lambda (str)
(let ((words (regex_split "[^a-zA-Z0-9]+" str)))
(if (non-empty words)
(car words)
null))))
(bind-func last_word:[String*,String*]*
(lambda (str)
(let ((words (regex_split "[^a-zA-Z0-9]+" str)))
(if (non-empty words)
(car (reverse words))
null))))
;; List style string functions
(bind-func top:[i8,String*]*
(lambda (str)
(cond
((non-empty str) (pref (tref str 1) 0))
(else 0:i8))))
(bind-func tail:[String*,String*]*
(lambda (str)
(cond
((non-empty str) (substring str 1 (length str)))
(else null))))
(impc:aot:insert-footer "xtmybot_base")