-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcommon.lisp
556 lines (470 loc) · 17.8 KB
/
common.lisp
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
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
(in-package :cl-wfx)
(defgeneric parameter (parameter))
(defparameter *log-shit-p* nil)
(defun log-shit (shit)
(when *log-shit-p*
(with-open-file (out (merge-pathnames "shit.log" (user-homedir-pathname))
:direction :output
:if-exists :append
:if-does-not-exist :create)
(pprint (frmt "***********************************************************~%~A" shit) out)
(close out)))
nil)
;;GLOBAL VARS and Shortcut functions ################################
(defvar *sys-license-code* "000000")
(defvar *context* nil
"Not to be set. Exposes the current context.")
(defvar *module* nil
"Not to be set. Exposes the current module of the current context.")
(defvar *session* nil
"Not to be set. Used internally Exposes the current session.")
(defvar *system* nil
"Global variable designating the current system.
Notes:
Dont set manually use with-system macro.")
;;Dynamic code evaluation and reading ##########################################3
(defparameter *lambda-functions*
(list 'cl-wfx:frmt
'cl-wfx::wfx-query-context-data-document
'cl-wfx::wfx-query-context-data
'cl-wfx:with-html
'cl-wfx:with-html-string
'cl-wfx::render-report
'cl-wfx::parameter
'cl-who:htm
'cl-who:str
'cl-who:esc
'cl-getx:getx))
(defun lambda-eval-safe (lambdax)
#|
(let* ((sandbox-impl::*allowed-extra-symbols*
*lambda-functions*)
(lambda-result (make-array '(0) :element-type 'base-char
:fill-pointer 0 :adjustable t)))
(with-output-to-string (s lambda-result)
(let ((kunru-sandbox::*msg-value-prefix* "")
(kunru-sandbox::*msg-error-prefix* "")
(kunru-sandbox::*msg-value-formatter* "~{~S~^<br/> ~}")
(kunru-sandbox::*msg-no-value-message* "Nil"))
(kunru-sandbox::read-eval-print lambdax s)
lambda-result)))
|#)
(defun read-s-expressions (expressions-string)
(let ((stream (make-string-input-stream expressions-string))
(expressions))
(when stream
(loop with expr = nil
do
(setf expr (read stream nil))
(when expr
(setf expressions (push expr expressions)))
while expr)
(close stream))
(reverse expressions)))
(defun read-no-eval (value)
:documentation "Function to convert string to s-expressions. Any value that is not a string is returned as is. When the value to read is a string it could consist of multiple s-expresssions, the second parameter returned indicates if this is the case or not. If multiple a list of s-expressions is returned else a single s-expression."
(let ((*read-eval* nil)
(expressions))
(if value
(if (stringp value)
(progn
(setf expressions
(read-s-expressions value))
(if (> (length expressions) 1)
(values expressions t)
(values (first expressions))))
(values value nil)))))
(defun evaluable-p (s-expression)
(fboundp (first s-expression)))
(defun clear-eval-log ()
(when *context*
(setf (gethash :debug-error (cache *context*))
nil)
(setf (gethash :debug-results (cache *context*))
nil)
(setf (gethash :debug-backtrace (cache *context*))
nil)))
(defun log-eval (error results backtrace)
(when *context*
(setf (gethash :debug-error (cache *context*))
error)
(setf (gethash :debug-results (cache *context*))
results)
(setf (gethash :debug-backtrace (cache *context*))
backtrace)))
(defun list-backtrace (&optional condition)
(with-output-to-string (out)
(uiop:print-backtrace :stream out :condition condition)))
(defun read-eval (expressions-string)
:documentation "Function converts a string to s-expressions and returns the results of evaluating each s-expression in turn. If the s-expression is not deemed to be evaluatable the expression is returned as is."
(let ((results)
(last-expression))
(clear-eval-log)
(handler-case
(let ((stream (make-string-input-stream expressions-string)))
(when stream
(loop with expr = nil
do
(setf expr (read stream nil))
(when expr
(setf last-expression expr)
(if (evaluable-p expr)
(push (eval expr) results)
(push expr results)))
while expr)
(close stream))
;; (setf results (reverse results))
(values (car results)
(cdr results)
(list
:error nil
:backtrace nil)))
(error (c)
(break "?~A" c)
(let ((backtrace (list-backtrace)))
(log-eval c results backtrace)
(values c
results
(list
:error c
:backtrace (append (list (list last-expression))
(list backtrace)))))))))
(defun eval-blob% (blob)
(read-eval (blob-string-value blob)))
(defun eval% (object &key package-name)
(cond ((and (document-p object) (document-of-type-p object "lambda"))
(let ((*package* (or (and package-name (or (find-package package-name)
(make-package package-name)))
*package*)))
(eval-blob% (getx object :code))))
((and (document-p object) (document-of-type-p object "package"))
(let ((*package* (or
(and package-name (or (find-package package-name)
(make-package package-name)))
(and (getx object :package)
(find-package (frmt "~A" (getx object :package))))
(and (getx object :package)
(make-package (frmt "~A" (getx object :package))))
*package*)))
(eval-blob% (getx object :code))))
((stringp object)
(let ((*package* (or (and package-name (or (find-package package-name)
(make-package package-name)))
*package*)))
(read-eval object)))
(t
(clear-eval-log)
(let ((*package* (or (and package-name (or (find-package package-name)
(make-package package-name)))
*package*)))
(handler-case
(eval object)
(error (c)
(log-eval c nil (list-backtrace c))
(error c)))))))
(defun load-blob% (blob)
(load (make-string-input-stream (blob-string-value blob))))
(defun load% (object &key package)
(cond ((and (document-p object) (document-of-type-p object "lambda"))
(let ((*package* (or package *package*)))
(load-blob% (getx object :code))))
((and (document-p object) (document-of-type-p object "package"))
(let ((*package* (or
package
(and (getx object :package)
(find-package (frmt "~A" (getx object :package))))
(and (getx object :package)
(make-package (frmt "~A" (getx object :package))))
*package*)))
(load-blob% (getx object :code))))
((blob-p object)
(load-blob% object))
((stringp object)
(load object))
(t
(load object))))
(defun apply% (function arg &rest arguments)
(handler-case
(apply function arg arguments)
(error (c)
(log-eval c nil (list-backtrace c))
(error c))))
(defun funcall% (function &rest arguments)
(handler-case
(apply function (car arguments) (cdr arguments))
(error (c)
(log-eval c nil (list-backtrace c))
(error c))))
;;#############################STRINGS
;;FORMAT
(defun format-money-for-export-no-cents (value &key (include-comma nil))
(typecase value
(null "")
((or integer single-float ratio float)
(if include-comma
(format nil "~:d" (truncate value))
(format nil "~d" (truncate value))))
(t
(princ-to-string value))))
(defun format-money-for-export (value &key (include-comma nil))
(typecase value
(null "")
((or integer single-float ratio float)
(if include-comma
(format nil "~:d" value)
(format nil "~d" value)))
(t
(princ-to-string value))))
(defun frmt-money (value &key (include-comma t))
(typecase value
(null "")
(number
(multiple-value-bind (quot rem) (truncate (round value 1/100) 100)
(format nil "~@?.~2,'0d"
(if include-comma "~:d" "~d")
quot (abs rem))))
(t
(princ-to-string value))))
#|
;;STRING MANIPULATION
(defun trim-whitespace (string)
(string-trim
'(#\Space #\Newline #\Tab #\Return) string))
|#
(defun assert-ends-with-/ (string)
(assert (char= (alexandria:last-elt string) #\/)))
(defun id-string (id)
(ppcre:regex-replace-all "[^A-Za-z0-9-_:.]"
(substitute #\- #\Space (string-downcase id)) ""))
(defun replace-all (string part replacement &key (test #'char=))
"Returns a new string in which all the occurences of the part
is replaced with replacement."
(when string
(with-output-to-string (out)
(loop with part-length = (length part)
for old-pos = 0 then (+ pos part-length)
for pos = (search part string
:start2 old-pos
:test test)
do (write-string string out
:start old-pos
:end (or pos (length string)))
when pos do (write-string replacement out)
while pos))))
(defun replace-all-list (string value-pairs)
(let ((new-val string))
(dolist (value-pair value-pairs)
(setf new-val (replace-all new-val
(first value-pair)
(second value-pair))))
new-val))
(defun sanitize-file-name (name)
(when name
(string-downcase
(replace-all-list name
'(("_" "-")
("(" "-")
(")" "-")
("'" "-")
("\"" "-")
("," "-")
(" " "-")
("“" "")
("\"" "")
("." "-")
("?" "")
("--" "-")
(":" "-"))))))
#|
(defun plural-name (name)
(setf name (frmt "~A" name))
(let ((last-char (char name (1- (length name)))))
(cond ((char-equal last-char #\s)
name)
((char-equal last-char #\y)
(alexandria:symbolicate (subseq name 0
(1- (length name)))
'ies))
(t
(alexandria:symbolicate name 's)))))
|#
;;#####################VALUE CHECKS
#|
(defun empty-p (value)
"Checks if value is null or an empty string."
(or
(not value)
(null value)
(equal value "")
(if (stringp value)
(string-equal value "NIL"))
(equal (trim-whitespace (princ-to-string value)) "")))
|#
(declaim (inline ensure-num))
(defun ensure-num (value)
:documentation "If there is junk in the value then 0 is returned."
(let ((final-val 0))
(if (empty-p value)
final-val
(if (stringp value)
(setf final-val (read-from-string value))
(setf final-val value)))
(cond ((numberp final-val)
final-val)
(t
0))))
;;#####################DATES
(defvar *time-zone* 0)
(defun decode-iso-date (date)
(ppcre:register-groups-bind ((#'parse-integer year)
(#'parse-integer month)
(#'parse-integer day))
("(\\d{4})-(\\d{1,2})-(\\d{1,2})" date)
(values year month day)))
(defvar *short-months*
#("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"))
(defvar *long-months*
#("January" "February" "March" "April" "May" "June" "July" "August" "September"
"October" "November" "December"))
(defvar *short-months-afrikaans*
#("Jan" "Feb" "Mrt" "Apr" "Mei" "Jun" "Jul" "Aug" "Sep" "Okt" "Nov" "Des"))
(defvar *long-months-afrikaans*
#("Januarie" "Februarie" "Maart" "April" "Mei" "Junie"
"Julie" "Augustus" "September" "Oktober" "November" "Desember"))
(defun month-number (month)
(let ((position (or (position (frmt "~A" month) *short-months*
:test #'string-equal)
(position (frmt "~A" month) *long-months*
:test #'string-equal)
(position (frmt "~A" month) *short-months-afrikaans*
:test #'string-equal)
(position (frmt "~A" month) *long-months-afrikaans*
:test #'string-equal))))
(when position
(1+ position))))
(defun ensure-parse-integer (value &key (start 0) end)
(typecase value
(string
(multiple-value-bind (integer position)
(parse-integer value :junk-allowed t
:start start :end end)
(when (= (length value) position)
integer)))
(integer value)))
(defun decode-date-string (date)
(multiple-value-bind (year month day) (decode-iso-date date)
(if year
(values year month day)
(let ((date-split (ppcre:split "[.\\/ -]+" (naive-impl::trim-whitespace date))))
(and date-split
(let* ((month-raw (second date-split))
(month (or (month-number month-raw)
(ensure-parse-integer month-raw)))
(year (ensure-parse-integer (third date-split)))
(day (ensure-parse-integer (first date-split))))
(values year month day)))))))
(defun decode-date (date &key time-zone)
(etypecase date
(string
(decode-date-string date))
(integer
(multiple-value-bind (a b c day month year)
(decode-universal-time date (or time-zone *time-zone*))
(declare (ignore a b c))
(values year month day)))))
(defvar *month-days* '(31 28 31 30 31 30 31 31 30 31 30 31))
(defun leap-year-p (year)
(cond
((zerop (rem year 400)) t)
((zerop (rem year 100)) nil)
((zerop (rem year 4)) t)))
(defun month-days (month-number year)
(if (and (leap-year-p year)
(= month-number 2))
29
(nth (1- month-number)
*month-days*)))
(defun check-date (date month year)
;; Technically, there can be a 31 dec 1899 date, if the time-zone is
;; west of GMT, but it's not particularly important.
(when (and (typep year '(integer 1900))
(typep month '(integer 1 12))
(plusp date))
(let ((days ;;(svref *month-days* (1- month))
(month-days month year)))
(cond ((and (= month 2)
(leap-year-p year))
(<= date 29))
(t
(<= date days))))))
(defun parse-date (date &key time-zone)
(etypecase date
(integer date)
(string
(multiple-value-bind (year month date) (decode-date-string date)
(when (check-date date month year)
(encode-universal-time 0 0 0 date month year
(or time-zone *time-zone*)))))
(null nil)))
(defun short-month-name (n)
(when (array-in-bounds-p *short-months* (1- n))
(aref *short-months* (1- n))))
(defun long-month-name (n)
(when (array-in-bounds-p *long-months* (1- n))
(aref *long-months* (1- n))))
(defun build-date (year month day)
(format nil "~d ~a ~d" day (short-month-name month) year))
(defun build-date-time (year month day hour min sec
&optional timezone)
(declare (ignore timezone))
(format nil "~d ~a ~d ~@{~2,'0d~^:~}"
day (short-month-name month) year hour min sec))
(defun format-universal-date (universal-date)
(when universal-date
(if (stringp universal-date)
universal-date
(multiple-value-bind (a b c day month year)
(decode-universal-time universal-date
*time-zone*)
(declare (ignore a b c))
(build-date year month day)))))
(defun format-universal-date-web (universal-date)
(when universal-date
(if (stringp universal-date)
universal-date
(multiple-value-bind (a b c day month year)
(decode-universal-time universal-date
*time-zone*)
(declare (ignore a b c))
(frmt "~A-~2,'0d-~2,'0d" year month day)))))
(defun format-date (date)
(if (typep date 'unsigned-byte)
(format-universal-date date)
date))
(defun build-system-date (year month day)
(format nil "~d-~2,'0d-~2,'0d" year month day))
(defun format-system-universal-date (universal-date)
(when universal-date
(if (stringp universal-date)
universal-date
(multiple-value-bind (a b c day month year)
(decode-universal-time universal-date
*time-zone*)
(declare (ignore a b c))
(build-system-date year month day)))))
(defun format-system-date (date)
(if (typep date 'unsigned-byte)
(format-system-universal-date date)
date))
(defconstant +24h-secs+ (* 60 60 24))
(defconstant +hour-secs+ (* 60 60))
(defun date-diff (start-date end-date &key return-type)
(cond ((equal return-type :days)
(/ (- end-date start-date) +24h-secs+))
((equal return-type :hours)
(/ (- end-date start-date) +hour-secs+))
((equal return-type :minutes)
(/ (- end-date start-date) 60))
(t
(- end-date start-date))))
;;########################################list manipulation