nydp 0.4.3 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/lib/lisp/core-010-precompile.nydp +5 -6
  3. data/lib/lisp/core-012-utils.nydp +2 -1
  4. data/lib/lisp/core-015-documentation.nydp +17 -11
  5. data/lib/lisp/core-020-utils.nydp +5 -5
  6. data/lib/lisp/core-030-syntax.nydp +29 -9
  7. data/lib/lisp/core-035-flow-control.nydp +15 -6
  8. data/lib/lisp/core-037-list-utils.nydp +22 -0
  9. data/lib/lisp/core-039-module.nydp +24 -0
  10. data/lib/lisp/core-040-utils.nydp +11 -12
  11. data/lib/lisp/core-041-string-utils.nydp +24 -0
  12. data/lib/lisp/core-042-date-utils.nydp +16 -0
  13. data/lib/lisp/core-043-list-utils.nydp +72 -50
  14. data/lib/lisp/core-080-pretty-print.nydp +50 -17
  15. data/lib/lisp/core-090-hook.nydp +13 -1
  16. data/lib/lisp/core-100-utils.nydp +82 -2
  17. data/lib/lisp/core-110-hash-utils.nydp +38 -0
  18. data/lib/lisp/core-120-settings.nydp +11 -2
  19. data/lib/lisp/core-900-benchmarking.nydp +17 -17
  20. data/lib/lisp/tests/accum-examples.nydp +28 -1
  21. data/lib/lisp/tests/at-syntax-examples.nydp +17 -0
  22. data/lib/lisp/tests/best-examples.nydp +9 -0
  23. data/lib/lisp/tests/builtin-tests.nydp +10 -0
  24. data/lib/lisp/tests/case-examples.nydp +14 -0
  25. data/lib/lisp/tests/date-examples.nydp +54 -1
  26. data/lib/lisp/tests/detect-examples.nydp +12 -0
  27. data/lib/lisp/tests/dp-examples.nydp +24 -0
  28. data/lib/lisp/tests/empty-examples.nydp +1 -1
  29. data/lib/lisp/tests/error-tests.nydp +4 -4
  30. data/lib/lisp/tests/hash-examples.nydp +17 -0
  31. data/lib/lisp/tests/list-grep-examples.nydp +40 -0
  32. data/lib/lisp/tests/list-tests.nydp +39 -0
  33. data/lib/lisp/tests/module-examples.nydp +10 -0
  34. data/lib/lisp/tests/parser-tests.nydp +16 -0
  35. data/lib/lisp/tests/pretty-print-tests.nydp +8 -2
  36. data/lib/lisp/tests/settings-examples.nydp +1 -1
  37. data/lib/lisp/tests/string-tests.nydp +48 -0
  38. data/lib/lisp/tests/syntax-tests.nydp +5 -1
  39. data/lib/nydp.rb +6 -3
  40. data/lib/nydp/assignment.rb +10 -3
  41. data/lib/nydp/builtin.rb +1 -1
  42. data/lib/nydp/builtin/abs.rb +8 -0
  43. data/lib/nydp/builtin/date.rb +9 -0
  44. data/lib/nydp/builtin/error.rb +1 -1
  45. data/lib/nydp/builtin/hash.rb +11 -1
  46. data/lib/nydp/builtin/ruby_wrap.rb +69 -0
  47. data/lib/nydp/builtin/string_pad_left.rb +7 -0
  48. data/lib/nydp/builtin/string_pad_right.rb +7 -0
  49. data/lib/nydp/builtin/type_of.rb +9 -6
  50. data/lib/nydp/closure.rb +0 -3
  51. data/lib/nydp/cond.rb +23 -1
  52. data/lib/nydp/context_symbol.rb +14 -6
  53. data/lib/nydp/core.rb +33 -29
  54. data/lib/nydp/core_ext.rb +5 -4
  55. data/lib/nydp/date.rb +17 -17
  56. data/lib/nydp/function_invocation.rb +33 -25
  57. data/lib/nydp/helper.rb +12 -2
  58. data/lib/nydp/interpreted_function.rb +68 -40
  59. data/lib/nydp/literal.rb +1 -1
  60. data/lib/nydp/pair.rb +13 -2
  61. data/lib/nydp/parser.rb +3 -0
  62. data/lib/nydp/symbol_lookup.rb +7 -7
  63. data/lib/nydp/version.rb +1 -1
  64. data/nydp.gemspec +2 -4
  65. data/spec/date_spec.rb +79 -0
  66. data/spec/parser_spec.rb +11 -0
  67. metadata +15 -36
  68. data/lib/nydp/builtin/car.rb +0 -7
  69. data/lib/nydp/builtin/cdr.rb +0 -7
  70. data/lib/nydp/builtin/cons.rb +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1a6a8e0215b81d92423402f9933413c58ebcdad9
4
- data.tar.gz: eeb8d8bb621ed6349d2e9d4e7823c3be5e875d04
3
+ metadata.gz: 1360fe08561240c0e05c70a69629b42b38078275
4
+ data.tar.gz: 70c0dfe6df1a0b2bf24ef39aca70646d7f8c3cac
5
5
  SHA512:
6
- metadata.gz: 1f32b55812f569e1329b657eed211054be4e275fcd778671ec7e251fe2b98fc7390531a9742ca819c9eb076aeb9bb34ffd2390c0fd5ebe43ddfc35d75e0eae95
7
- data.tar.gz: 2f5cad91e0551afb32c410751c56b11e1b55158218a8bdab8eb5db3d2b38719d3cfb63cc04ca961b7e1436075dbaf10ad3d5d82bf75a61f1fc1d91fd25c03a3f
6
+ metadata.gz: 5db0e86f33a7ebe4afc33478d1fe6a2da5a6a8d07b85d66532cd155adb9b8b567d6edbe90f3ab0b437f59a03ec2047ca51778c2d0923355131b8e9b718260c83
7
+ data.tar.gz: 0d4491009c98301ab947668fd396bf7f3544b229de8d1820065073f0bfb01e7db509cba3f749f07f6575b791222d1aa7870c3d0e86d96dce4e95a1ddac8cba00
@@ -1,8 +1,11 @@
1
1
  (assign mac-expand
2
2
  (fn (names macfn expr)
3
3
  (cond macfn
4
- (pre-compile-with names
5
- (apply macfn (cdr expr)))
4
+ (handle-error
5
+ (fn (errors)
6
+ (error "expanding" (inspect expr) "with" (inspect macfn)))
7
+ (fn ()
8
+ (pre-compile-with names (apply macfn (cdr expr)))))
6
9
  expr)))
7
10
 
8
11
  (assign macs (hash))
@@ -134,7 +137,3 @@
134
137
 
135
138
  (hash-set macs 'quasiquote
136
139
  (fn (arg) (qq-quasiquote arg 0)))
137
-
138
- (hash-set macs 'do
139
- (fn args
140
- `((fn nil ,@args))))
@@ -30,8 +30,9 @@
30
30
  (def map (f things)
31
31
  (map-helper-1 f things (cons)))
32
32
 
33
+ ;; push 'v onto the value for 'k in 'h
34
+ ;; the hash-values of h will all be lists, in reverse order of consing
33
35
  (def hash-cons (h k v)
34
- ; push 'v onto the value for 'k in 'h
35
36
  (hash-set h k (cons v (hash-get h k))))
36
37
 
37
38
  (def rev (things last-cdr)
@@ -19,6 +19,10 @@
19
19
  acc)))
20
20
  form))
21
21
 
22
+ (hash-set macs 'do
23
+ (fn forms
24
+ `((fn nil ,@forms))))
25
+
22
26
  ((fn (this-chapter-name chapters chapter-new chapter-build chapter-add-to-chapter)
23
27
  (assign chapters (hash))
24
28
 
@@ -88,7 +92,7 @@
88
92
  (def dox-add-to-chapters (item type chapters)
89
93
  (cond chapters
90
94
  (do (chapter-add-item item (car chapters))
91
- (hash-cons types-chapters (cons type (car chapters)) item)
95
+ (hash-cons types-chapters (inspect (cons type (car chapters))) item)
92
96
  (dox-add-to-chapters item type (cdr chapters)))
93
97
  item))
94
98
 
@@ -103,8 +107,10 @@
103
107
  (def dox-types () (hash-keys types))
104
108
  (def dox-items-by-type (type) (hash-get types type))
105
109
 
106
- (def dox-items-by-type-and-chapter (type chapter)
107
- (hash-get types-chapters (cons type chapter)))
110
+ (def get-types-chapters () types-chapters)
111
+
112
+ (def dox-items-by-type-and-chapter (dox-type chapter)
113
+ (hash-get types-chapters (inspect (cons dox-type chapter))))
108
114
 
109
115
  (def dox-get-attr (name attr)
110
116
  (cond (dox? name)
@@ -132,9 +138,9 @@
132
138
  (cond (eq? event 'script-end)
133
139
  (script-end name))))))
134
140
 
141
+ ;; if the car of 'form is a key of 'hsh, add the cdr of 'form to the value of the key in 'hsh
142
+ ;; otherwise add the form to the list whose key is nil
135
143
  (def filter-form (hsh form)
136
- ; if the car of 'form is a key of 'hsh, add the cdr of 'form to the value of the key in 'hsh
137
- ; otherwise add the form to the list whose key is nil
138
144
  (cond (cond (pair? form)
139
145
  (hash-key? hsh (car form)))
140
146
  (hash-cons hsh (car form) (cdr form))
@@ -153,9 +159,9 @@
153
159
  (def rev-values (hsh)
154
160
  (rev-value-keys (hash-keys hsh) hsh (hash)))
155
161
 
162
+ ;; group forms by their first element, if the first element
163
+ ;; is already a key in hsh, collect all other elements under key nil
156
164
  (def filter-forms (hsh forms)
157
- ; group forms by their first element, if the first element
158
- ; is already a key in hsh, collect all other elements under key nil
159
165
  (cond forms
160
166
  (filter-forms (filter-form hsh (car forms)) (cdr forms))
161
167
  (rev-values hsh)))
@@ -165,12 +171,12 @@
165
171
  (hash-set hsh 'chapter nil)
166
172
  hsh)
167
173
 
174
+ ;; used internally by 'mac
168
175
  (def define-mac-expr (name args body-forms)
169
- ; used internally by 'mac
170
176
  `(do (hash-set macs ',name (fun ,args ,@(hash-get body-forms nil)))
171
177
  (dox-add-doc ',name
172
178
  'mac
173
- ',(map car (hash-get body-forms 'comment))
179
+ ',(+ (fetch-and-clear-comments) (map car (hash-get body-forms 'comment)))
174
180
  ',args
175
181
  '(mac ,name ,args ,@(hash-get body-forms nil))
176
182
  ',(map car (hash-get body-forms 'chapter)))))
@@ -195,8 +201,8 @@
195
201
 
196
202
  (mac def-assign args `(assign ,@args))
197
203
 
204
+ ;; used internally by 'def
198
205
  (def define-def-expr (name args body-forms)
199
- ; used internally by 'def
200
206
  `(do (def-assign ,name (fun ,args ,@(filter-comments (hash-get body-forms nil))))
201
207
  (dox-add-doc ',name
202
208
  'def
@@ -205,8 +211,8 @@
205
211
  '(def ,name ,args ,@(hash-get body-forms nil))
206
212
  ',(map car (hash-get body-forms 'chapter)))))
207
213
 
214
+ ;; define a new function in the global namespace
208
215
  (mac def (name args . body)
209
- ; define a new function in the global namespace
210
216
  (chapter nydp-core)
211
217
  (define-def-expr name args (filter-forms (build-def-hash (hash)) body)))
212
218
 
@@ -1,4 +1,4 @@
1
- (assign script-name "core-012-utils.nydp")
1
+ (assign script-name "core-020-utils.nydp")
2
2
 
3
3
  (dox-add-doc 'if
4
4
  'mac
@@ -43,12 +43,12 @@
43
43
  '(hash-set h k (cons v (hash-get h k)))
44
44
  '(hash-manipulation))
45
45
 
46
+ ;; equivalent to (join-str "~prefix~joint~(car things)" joint (cdr things)) - except
47
+ ;; 'string-pieces hasn't been defined yet, and if it were, it would be defined in terms of
48
+ ;; 'join-str, so it would be circular.
49
+ ;; see 'joinstr for a more powerful and easier-to-use implementation of the same idea
46
50
  (def join-str (prefix joint things)
47
51
  (chapter string-manipulation)
48
- ; equivalent to (join-str "~prefix~joint~(car things)" joint (cdr things)) - except
49
- ; 'string-pieces hasn't been defined yet, and if it were, it would be defined in terms of
50
- ; 'join-str, so it would be circular.
51
- ; see 'joinstr for a more powerful and easier-to-use implementation of the same idea
52
52
  (if things
53
53
  (join-str (+ (to-string prefix)
54
54
  joint
@@ -21,16 +21,26 @@ scoping, assignment, anonymous functions and more...")
21
21
  (apply orf
22
22
  (cdr args)))))
23
23
 
24
+ ; returns true if 'things is a list and the first item of the
25
+ ; list is the given object
24
26
  (def caris (obj things)
25
- ; returns true if 'things is a list and the first item of the
26
- ; list is the given object
27
27
  (and (pair? things)
28
- (eq? (car things) obj)))
28
+ (eq? (car things) obj)))
29
29
 
30
+ ; evaluate 'body if 'arg is nil
30
31
  (mac unless (arg . body)
31
- ; evaluate 'body if 'arg is nil
32
32
  `(if (no ,arg) (do ,@body)))
33
33
 
34
+ ; looks up a key in @
35
+ ; assumes local lexical context has defined a hash called '@
36
+ (mac prefix-at-syntax (name . names)
37
+ `(hash-get @ ',name))
38
+
39
+ (mac at-syntax names
40
+ (if (eq? (car names) '||)
41
+ `(prefix-at-syntax ,@(cdr names))
42
+ (error "unknown at-syntax: expected prefix-syntax (eg @name), got ~(join-str (car names) "@" (cdr names))")))
43
+
34
44
  (def expand-colon-syntax (names)
35
45
  (if (no (cdr names))
36
46
  `(apply ,(car names) args)
@@ -89,8 +99,10 @@ scoping, assignment, anonymous functions and more...")
89
99
  ,@body)
90
100
  ,@(map cadr (pairs parms))))
91
101
 
102
+ ;; same as ( (fn (var) body) val ) -> ie create a lexical scope
103
+ ;; where val is assigned to var, execute 'body in that scope
92
104
  (mac let (var val . body)
93
- `(with (,var ,val) ,@body))
105
+ `(with (,var ,val) ,@body))
94
106
 
95
107
  (mac rfn (name parms . body)
96
108
  ; creates a named, locally-scoped function
@@ -120,6 +132,7 @@ scoping, assignment, anonymous functions and more...")
120
132
  ;; (fn args (and (apply a args) (apply b args) (apply c args)))
121
133
  ;; or more simply
122
134
  ;; (fn (x) (and (a x) (b x) (c x)))
135
+ ;; note: alias as 'andf ??
123
136
  (def andify args
124
137
  (fn args2 (rfnwith self (ands args)
125
138
  (if ands (if (apply (car ands) args2)
@@ -234,7 +247,14 @@ scoping, assignment, anonymous functions and more...")
234
247
  (caris 'hash-get name)
235
248
  (hash-get-assignment (cdr name) value)
236
249
  (ampersand-expression? name)
237
- (ampersand-expression-assignment name value)))
250
+ (ampersand-expression-assignment name value)
251
+ (caris 'at-syntax name)
252
+ `(hash-set @ ',(caddr name) ,value)
253
+ (error "unknown assignment to place: ~(inspect name)")))
254
+
255
+ ;; quiet assignment ; like =, but expression returns nil
256
+ (mac #= (name value)
257
+ `(do (= ,name ,value) nil))
238
258
 
239
259
  ; increment the value at 'place by 'inc (default 1)
240
260
  (mac ++ (place inc) `(= ,place (+ ,place ,(or inc 1))))
@@ -336,12 +356,12 @@ scoping, assignment, anonymous functions and more...")
336
356
  (if (hash-get macs args)
337
357
  (warnings/new 'arg-shadows-macro "arg " args " shadows macro " args " in arg list " orig " and body " body))))
338
358
 
359
+ ;; build a 'fn form, changing 'args and 'body to
360
+ ;; properly handle any destructuring args if present
339
361
  (mac fun (args . body)
340
- ; build a 'fn form, changing 'args and 'body to
341
- ; properly handle any destructuring args if present
342
362
  (fun/approve-arg-names args args body)
343
363
  (destructure/build args nil body))
344
364
 
345
- ; assign (f place) to place
365
+ ;; assign (f place) to place
346
366
  (mac zap (f place . args)
347
367
  `(= ,place (,f ,place ,@args)))
@@ -40,12 +40,21 @@
40
40
  (loop (assign ,v ,gi) (< ,v ,gm) (assign ,v (+ ,v 1))
41
41
  ,@body))))
42
42
 
43
- (mac curry (func . args1)
44
- ; return a new function which is the original function with
45
- ; the given args1 already applied
46
- ; arguments to the new function are whatever arguments remain
47
- ; for the old function
48
- `(fn args (apply ,func ,@args1 args)))
43
+ ;; return a new function which is the original function with
44
+ ;; the given args1 already applied
45
+ ;; arguments to the new function are whatever arguments remain
46
+ ;; for the old function
47
+ ;; Could be (mac curry things `(fn args (apply ,@things args))) but less readable
48
+ (mac curry (f . args0)
49
+ `(fn args
50
+ (apply ,f ,@args0 args)))
51
+
52
+ ;; like curry, but the returned function takes only a single arg (assumes all
53
+ ;; args but one are provided here)
54
+ ;; Could be (mac curry1 things `(fn (arg) (,@things arg))) but less readable
55
+ (mac curry1 (f . args)
56
+ `(fn (arg)
57
+ (,f ,@args arg)))
49
58
 
50
59
  (mac cache-get (hsh key val)
51
60
  ; if ,key is already in ,hsh - return the associated value.
@@ -13,6 +13,17 @@
13
13
  (f (car things))
14
14
  (eachl f (cdr things))))
15
15
 
16
+ ;; if things is a pair,
17
+ ;; if (cdr things) is nil, return (car things)
18
+ ;; else recurse on (cdr things)
19
+ ;; else return things
20
+ (def list/last (things)
21
+ (if (pair? things)
22
+ (aif (cdr things)
23
+ (list/last it)
24
+ (car things))
25
+ things))
26
+
16
27
  ; invokes 'f for each element of 'things, last element processed first
17
28
  ; ( "r" in "eachr" = "rightmost first" )
18
29
  (def eachr (f things)
@@ -49,3 +60,14 @@
49
60
  ; a 'key, returns vx from 'al where kx is equal to 'key
50
61
  ; #attribution: lifted almost directly from arc.arc
51
62
  (def alref (key al) (cadr (assoc key al)))
63
+
64
+ ;; returns the first non-empty item in 'args
65
+ ;; mac equivalent of (detect present? args)
66
+ (mac dp args
67
+ (if args
68
+ (w/uniq nearg
69
+ `(let ,nearg ,(car args)
70
+ (if (empty? ,nearg)
71
+ (dp ,@(cdr args))
72
+ ,nearg)))
73
+ nil))
@@ -0,0 +1,24 @@
1
+ ;; creates a private namespace for support functions for one or more explicitly exported/public functions
2
+ ;;
3
+ ;; (module foo
4
+ ;; (def h ...)
5
+ ;; (def u ...)
6
+ ;; (export bar (x y) (h u x y)))
7
+ ;;
8
+ ;; results in 'foo/bar being universally available, but 'h and 'u are visible only within the module and override
9
+ ;; any other 'h or 'u defined elsewhere, in the scope of the module.
10
+ (mac module (module-name . forms)
11
+ (let private-names nil
12
+ (let module-forms
13
+ { def (fn (name args . body)
14
+ (push nil private-names)
15
+ (push name private-names)
16
+ `(assign ,name (fn ,args ,@body)))
17
+ export-def macs.def
18
+ export (fn (name args . body)
19
+ `(export-def
20
+ ,(sym (+ (to-string module-name) "/" (to-string name)))
21
+ ,args
22
+ ,@body)) }
23
+ (let module-body (pre-compile-each module-forms forms)
24
+ `(with (,@private-names) ,@module-body)))))
@@ -11,21 +11,20 @@
11
11
  (def num? (arg) (comment "true if arg is a number") (isa 'number arg))
12
12
  (def string? (arg) (comment "true if arg is a string") (isa 'string arg))
13
13
 
14
- (mac just (expr)
15
- ; this is useful sometimes when 'expr can't stand on its own due to lexical ambiguity, most often in string interpolations
16
- ; for example, in "hello ~person, how are you", the parser will try to interpolate the symbol "person," rather than the
17
- ; expected "person". In this case, use "hello ~(just person), how are you"
18
- expr)
14
+ ;; this is useful sometimes when 'expr can't stand on its own due to lexical ambiguity, most often in string interpolations
15
+ ;; for example, in "hello ~person, how are you", the parser will try to interpolate the symbol "person," rather than the
16
+ ;; expected "person". In this case, use "hello ~(just person), how are you"
17
+ (mac just (expr) expr)
19
18
 
20
19
  (def quotify (arg) `(quote ,arg))
21
20
 
22
- ; return a function that always returns 'arg, similar to K in SKI calculus
21
+ ;; return a function that always returns 'arg, similar to K in SKI calculus
23
22
  (defmemo k (arg) (fn nil arg))
24
23
 
24
+ ;; return the length of 'things where 'things may be nil, a string, list or hash
25
+ ;; length of nil is zero, length of hash is number of keys, length of string
26
+ ;; is number of characters, length of list is number of direct items - no recursive counting
25
27
  (def len (things)
26
- ; return the length of 'things where 'things may be nil, a string, list or hash
27
- ; length of nil is zero, length of hash is number of keys, length of string
28
- ; is number of characters, length of list is number of direct items - no recursive counting
29
28
  (chapter list-manipulation)
30
29
  (chapter string-manipulation)
31
30
  (chapter hash-manipulation)
@@ -37,8 +36,8 @@
37
36
 
38
37
  (assign dynamics (hash))
39
38
 
39
+ ;; creates a dynamic variable.
40
40
  (mac dynamic (name initial)
41
- ; creates a dynamic variable.
42
41
  (let with-mac-name (sym:+ "w/" name)
43
42
  (w/uniq prev
44
43
  `(do
@@ -53,10 +52,10 @@
53
52
  (def ,name () (hash-get (thread-locals) ',name))))))
54
53
 
55
54
 
56
- ; overrides 'privately defined earlier in documentation manager
55
+ ;; overrides 'privately defined earlier in documentation manager
57
56
  (dynamic privately)
58
57
 
59
- ; suppress documentation of anything defined in 'body
58
+ ;; suppress documentation of anything defined in 'body
60
59
  (mac in-private body
61
60
  `(w/privately t ,@body))
62
61
 
@@ -30,3 +30,27 @@
30
30
  ; return the first 'length chars of string 'str
31
31
  (def string-truncate (str length)
32
32
  (string-replace "(.{~|length|}).*" "\\1" str))
33
+
34
+ ;; returns a function with args 'args whose body is 'str. 'str should be a string,
35
+ ;; 'args should correspond to interpolations within 'str
36
+ ;;
37
+ ;; example: (string-eval-fn "hello \~u.firstname" 'u)
38
+ ;; returns (fn (u) (string-pieces "hello " u.firstname))
39
+ (defmemo string-eval-fn (str args)
40
+ (eval `(fn ,args
41
+ ,(parse-in-string str))))
42
+
43
+ ;; assigns 'args respectively to 'arg-names and evals 'str in that context.
44
+ ;; Assumes 'str contains interpolations which reference 'arg-names.
45
+ ;; Useful for evaluating user-supplied strings ; dangerous for the same reason.
46
+ ;;
47
+ ;; example: (string/eval-with-args "\~x + \~y is \~(+ x y)" '(x y) 2 3)
48
+ ;; returns "2 + 3 is 5"
49
+ ;;
50
+ (def string/eval-with-args (str arg-names . args)
51
+ (on-err
52
+ (error (j "error evaluating " (inspect str)
53
+ "\nwith arg names " (inspect arg-names)
54
+ "\nand args " (inspect args)))
55
+ (apply (string-eval-fn str arg-names)
56
+ args)))
@@ -7,3 +7,19 @@
7
7
  ;; return a Time object representing the time 's seconds ago
8
8
  (def seconds-ago (s)
9
9
  (- (time) s))
10
+
11
+ (def anniversary/previous (anchor anniv)
12
+ (let d (date anchor.year
13
+ anniv.month
14
+ anniv.day)
15
+ (if (< d anchor)
16
+ d
17
+ d.last-year)))
18
+
19
+ (def anniversary/next (anchor anniv)
20
+ (let d (date anchor.year
21
+ anniv.month
22
+ anniv.day)
23
+ (if (> d anchor)
24
+ d
25
+ d.next-year)))
@@ -23,75 +23,71 @@
23
23
  (intersperse inbetween (cdr things)))
24
24
  things))
25
25
 
26
+ ;; expects 'things a list of lists, joins the lists
27
+ ;; returns (a b X c d X e f)
28
+ ;; placing 'inbetween in between each list.
29
+ ;; For example (intersperse-splicing 'X '((a b) (c d) (e f)))
26
30
  (def intersperse-splicing (inbetween things)
27
- ; expects 'things a list of lists, joins the lists
28
- ; placing 'inbetween in between each list.
29
- ; For example (intersperse-splicing 'X '((a b) (c d) (e f)))
30
- ; returns (a b X c d X e f)
31
31
  (apply joinlists (intersperse (list inbetween) things)))
32
32
 
33
- (def collect (f things)
34
- ; if 'things is a list, return all the items in the list for which 'f returns non-nil
35
- ; otherwise, return 'things if (f things) is non-nil
36
- ; otherwise, nil
37
- ; note that this preserves improper lists and may return only the lastcdr if all else fails...
38
- (rfnwith collector (items things)
39
- (if (no items)
40
- nil
41
- (pair? items)
42
- (if (f (car items))
43
- (cons (car items)
44
- (collector (cdr items)))
45
- (collector (cdr items)))
46
- (f items)
47
- items)))
33
+ ;; if 'things is a list, return all the items in the list for which 'f returns non-nil
34
+ ;; otherwise, return 'things if (f things) is non-nil
35
+ ;; otherwise, nil
36
+ ;; note that this preserves improper lists and may return only the lastcdr if all else fails...
37
+ (def collect (f items)
38
+ (if (pair? items)
39
+ (if (f (car items))
40
+ (cons (car items)
41
+ (collect f (cdr items)))
42
+ (collect f (cdr items)))
43
+ items
44
+ (if (f items)
45
+ items)))
48
46
 
49
47
  (assign select collect)
50
48
 
51
- ; return a new list containing only 'present? items from the given list
49
+ ;; return a new list containing only 'present? items from the given list
52
50
  (def compact (things) (collect present? things))
53
51
 
52
+ ;; return the sum of all non-nil values (consider nil as zero)
54
53
  (def +nz args
55
- ; return the sum of all non-nil values (consider nil as zero)
56
54
  (apply + (compact args)))
57
55
 
56
+ ;; return all the items in 'things for which 'f returns nil
58
57
  (def reject (f things)
59
- ; return all the items in 'things for which 'f returns nil
60
58
  (collect !f things))
61
59
 
60
+ ;; returns the n-th item in the list 'things
62
61
  (def nth (n things)
63
- ; returns the n-th item in the list 'things
64
62
  (if (eq? n 0)
65
63
  (car things)
66
64
  (nth (- n 1) (cdr things))))
67
65
 
66
+ ;; repeatedly assigns an element of 'things to 'var,
67
+ ;; and executes 'body each time
68
68
  (mac each (var things . body)
69
- ; repeatedly assigns an element of 'things to 'var,
70
- ; and executes 'body each time
71
69
  (w/uniq (xs c)
72
- `((rfn ,c (,xs)
73
- (if (pair? ,xs)
74
- (do (let ,var (car ,xs) ,@body)
75
- (,c (cdr ,xs)))))
76
- ,things)))
70
+ `(rfnwith ,c (,xs ,things)
71
+ (if (pair? ,xs)
72
+ (do (let ,var (car ,xs) ,@body)
73
+ (,c (cdr ,xs)))))))
77
74
 
78
75
  (def reduce (f things)
79
- ((rfn rd (acc list)
80
- (if (pair? list)
81
- (rd (f acc (car list))
82
- (cdr list))
83
- acc))
84
- (car things) (cdr things)))
85
-
76
+ (rfnwith rd (acc (car things) list (cdr things))
77
+ (if (pair? list)
78
+ (rd (f acc (car list))
79
+ (cdr list))
80
+ acc)))
81
+
82
+ ;; t if this is a proper list (last cdr is nil)
83
+ ;; nil otherwise (last cdr is neither cons nor nil)
86
84
  (def proper? (list)
87
- ; t if this is a proper list (last cdr is nil)
88
- ; nil otherwise (last cdr is neither cons nor nil)
89
85
  (or (no list)
90
86
  (and (pair? list)
91
87
  (proper? (cdr list)))))
92
88
 
89
+ ;; returns the first 'n items in the list 'things
93
90
  (def firstn (n things)
94
- ; returns the first 'n items in the list 'things
95
91
  (if (eq? n 0) nil
96
92
  things (cons (car things)
97
93
  (firstn (- n 1)
@@ -139,25 +135,39 @@
139
135
  (detect (curry eq? f)
140
136
  things)))
141
137
 
138
+ ;; split things into a list of lists each n long
142
139
  (def tuples (n things)
143
- ;; split things into a list of lists each n long
144
140
  (rfnwith _ (list things)
145
141
  (if (no list)
146
142
  nil
147
143
  (cons (firstn n list) (_ (nthcdr n list))))))
148
144
 
149
- (def best (f things)
150
- (if (no things)
151
- nil
152
- (let winner (car things)
153
- (each thing (cdr things)
154
- (if (f thing winner)
155
- (= winner thing)))
156
- winner)))
145
+ ;; iterates through 'things pairwise (a,b then b,c then c,d etc), returns whichever is preferred by 'better-p
146
+ ;; example (best > '(3 1 4 1 5 9 2)) returns 9
147
+ ;; "better-p" -> takes two args a and b, return t if a is "better" than b, nil otherwise
148
+ (def best (better-p things)
149
+ (reduce (fn (a b)
150
+ (if (better-p a b) a b))
151
+ things))
157
152
 
158
153
  (def min things (best < things))
159
154
  (def max things (best > things))
160
155
 
156
+ ;; returns a function taking two args a and b, that compares attributes of two objects
157
+ ;; 'map-f takes one arg a returning a1
158
+ ;; 'compare-p takes two args a1 and b1, returns t if a1 is "better" than b1
159
+ ;;
160
+ ;; useful in conjunction with 'best : (best (map-compare-f > &size) (list { size 1 } { size 7 } { size 3 })) returns { size 7 }
161
+ (def map-compare-f (compare-p map-f)
162
+ (fn (a b) (compare-p (map-f a) (map-f b))))
163
+
164
+ ;; iterate over 'things, calling 'on-atom for each non-list element, and calling
165
+ ;; 'on-list for each list element.
166
+ ;; 'on-atom takes one parameter, the element in question;
167
+ ;; 'on-list takes two parameters: a function to call for recursing, and the list in question.
168
+ ;; 'on-list should be something like (fn (rec xs) (foo xs) (map rec xs)) to construct a new list,
169
+ ;; or (fn (rec xs) (foo xs) (eachl rec xs)) to iterate without constructing a new list
170
+ ;; see 'list-gsub for an example of constructing a new list using this.
161
171
  (def map-recurse (on-atom on-list things)
162
172
  ((afn (xs)
163
173
  (if (pair? xs)
@@ -166,8 +176,20 @@
166
176
  (on-atom xs)))
167
177
  things))
168
178
 
179
+ ;; like map-recurse, but doesn't depend on caller to initiate recursion
180
+ ;; 'on-atom and 'on-list are functions each taking one parameter
181
+ ;; return value is last returned item from on-atom or on-list
182
+ (def list/traverse (on-atom on-list things)
183
+ (map-recurse
184
+ (fn (s)
185
+ (on-atom s))
186
+ (fn (rec xs)
187
+ (on-list xs)
188
+ (eachl rec xs))
189
+ things))
190
+
191
+ ;; recursively replaces 'old with 'new inside 'list
169
192
  (def list-gsub (list old new)
170
- ; recursively replaces 'old with 'new inside 'list
171
193
  (map-recurse (fn (s) (if (eq? s old) new s))
172
194
  (fn (m things)
173
195
  (if (eq? things old)