nydp 0.4.3 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
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)