nydp 0.3.0 → 0.4.0

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/.zeiger.yml +28 -0
  3. data/lib/lisp/core-000.nydp +1 -1
  4. data/lib/lisp/core-015-documentation.nydp +6 -9
  5. data/lib/lisp/core-017-builtin-dox.nydp +33 -0
  6. data/lib/lisp/core-025-warnings.nydp +15 -0
  7. data/lib/lisp/core-030-syntax.nydp +38 -2
  8. data/lib/lisp/core-035-flow-control.nydp +2 -2
  9. data/lib/lisp/core-037-list-utils.nydp +7 -5
  10. data/lib/lisp/core-040-utils.nydp +15 -4
  11. data/lib/lisp/core-043-list-utils.nydp +1 -0
  12. data/lib/lisp/core-045-dox-utils.nydp +6 -0
  13. data/lib/lisp/core-050-test-runner.nydp +9 -9
  14. data/lib/lisp/core-070-prefix-list.nydp +2 -2
  15. data/lib/lisp/core-090-hook.nydp +24 -0
  16. data/lib/lisp/core-100-utils.nydp +38 -10
  17. data/lib/lisp/tests/ampersand-syntax-examples.nydp +26 -0
  18. data/lib/lisp/tests/boot-tests.nydp +1 -1
  19. data/lib/lisp/tests/collect-tests.nydp +4 -0
  20. data/lib/lisp/tests/destructuring-examples.nydp +18 -1
  21. data/lib/lisp/tests/fill-bucket-examples.nydp +46 -2
  22. data/lib/lisp/tests/floor-examples.nydp +58 -0
  23. data/lib/lisp/tests/k-examples.nydp +5 -0
  24. data/lib/lisp/tests/power-examples.nydp +16 -0
  25. data/lib/lisp/tests/string-tests.nydp +8 -0
  26. data/lib/lisp/tests/syntax-tests.nydp +6 -0
  27. data/lib/lisp/tests/zip-examples.nydp +16 -0
  28. data/lib/nydp.rb +6 -2
  29. data/lib/nydp/assignment.rb +1 -2
  30. data/lib/nydp/builtin/ensuring.rb +1 -2
  31. data/lib/nydp/builtin/greater_than.rb +2 -2
  32. data/lib/nydp/builtin/handle_error.rb +1 -2
  33. data/lib/nydp/builtin/less_than.rb +2 -2
  34. data/lib/nydp/builtin/math_ceiling.rb +7 -0
  35. data/lib/nydp/builtin/math_floor.rb +7 -0
  36. data/lib/nydp/builtin/math_power.rb +7 -0
  37. data/lib/nydp/builtin/math_round.rb +7 -0
  38. data/lib/nydp/builtin/parse.rb +2 -2
  39. data/lib/nydp/builtin/parse_in_string.rb +3 -3
  40. data/lib/nydp/builtin/pre_compile.rb +0 -1
  41. data/lib/nydp/compiler.rb +1 -1
  42. data/lib/nydp/cond.rb +3 -6
  43. data/lib/nydp/context_symbol.rb +40 -32
  44. data/lib/nydp/core.rb +8 -2
  45. data/lib/nydp/function_invocation.rb +3 -5
  46. data/lib/nydp/image_store.rb +21 -0
  47. data/lib/nydp/interpreted_function.rb +8 -12
  48. data/lib/nydp/lexical_context_builder.rb +19 -35
  49. data/lib/nydp/pair.rb +2 -1
  50. data/lib/nydp/parser.rb +4 -0
  51. data/lib/nydp/plugin.rb +15 -8
  52. data/lib/nydp/runner.rb +3 -3
  53. data/lib/nydp/symbol.rb +3 -1
  54. data/lib/nydp/symbol_lookup.rb +2 -2
  55. data/lib/nydp/truth.rb +2 -2
  56. data/lib/nydp/version.rb +1 -1
  57. data/lib/nydp/vm.rb +47 -27
  58. data/spec/date_spec.rb +2 -2
  59. data/spec/embedded_spec.rb +16 -16
  60. data/spec/error_spec.rb +1 -1
  61. data/spec/nydp_spec.rb +13 -4
  62. data/spec/parser_spec.rb +63 -16
  63. data/spec/spec_helper.rb +1 -2
  64. data/spec/string_atom_spec.rb +2 -2
  65. data/spec/symbol_spec.rb +2 -2
  66. data/spec/tokeniser_spec.rb +101 -0
  67. metadata +16 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8ca4e399a839f5396035c2e8fe863c35e6c5acc4
4
- data.tar.gz: 0e1ae8034c02c3d2401b97082d949bb29345ea5b
3
+ metadata.gz: 8d9a87eb0c014a51f391ddd1ba6686165a01ab48
4
+ data.tar.gz: 8c3f3146c0ae8dc18ac6e86324274423d75be651
5
5
  SHA512:
6
- metadata.gz: 9bf68cd0b9627cf49ea2ddeb0f8ef53344b04576ecd5204dd04b943683b9b2610ba422c83d1ece2a9dc7b89968e2bfa251accad3e2bb9a450539160d660979c2
7
- data.tar.gz: 2b40296df57b45e4d9e8be8f73aeecf38c2af80ac67d6da64407d57e69850c99820f582f14be8aa7f459ddc84e21cac6fbfa85759bed549b18474a6699f1524a
6
+ metadata.gz: f946a10304dd7422fb73243b9cd3ef40fab33aa9ff4f0b53568278761b64905d00714ed0cf7401db29b8c3613d8a683460ed48feef3f174ab0c724dd3e5f922e
7
+ data.tar.gz: 1925438dd0eca5a718e6f41ff39ef9f35e1a9ed18279e58f2ebfa88c284cb46526f4864932db706dc346662a59cb153fbec70fca1047aeddc742740de971dab5
@@ -0,0 +1,28 @@
1
+ search:
2
+ - "bin/**/*"
3
+ - "lib/**/*"
4
+ - "spec/**/*"
5
+ - "*.gemspec"
6
+ - ".*.yml"
7
+ - ".git?*"
8
+ - "Gemfile*"
9
+ - README
10
+ ignore:
11
+ - .doc$
12
+ - .ods$
13
+ - .gz$
14
+ - .png$
15
+ - .gif$
16
+ - .zip$
17
+ - .jpg$
18
+ - .xcf$
19
+ - .pdf$
20
+ - /select2/.*.js$
21
+ stats:
22
+ nydp:
23
+ - ".*\\.nydp"
24
+ code:
25
+ - "lib/.*\\.(rb|rake)"
26
+ test:
27
+ - "spec/.*"
28
+ - "test/.*"
@@ -5,8 +5,8 @@
5
5
  ;; to, 'do, 'rfn, 'loop, 'for) were directly inspired by (aka stolen from) arc.arc. See
6
6
  ;; README.md however for some significant differences
7
7
 
8
+ (assign noop (fn))
8
9
  (assign list (fn args args))
9
- (assign noop (fn args nil))
10
10
  (assign x1 (fn (arg) arg))
11
11
  (assign caar (fn (arg) (car (car arg))))
12
12
  (assign cadr (fn (arg) (car (cdr arg))))
@@ -14,9 +14,6 @@
14
14
  (hash-set hsh 'name name)
15
15
  hsh)
16
16
 
17
- (def chapter-names () (hash-keys chapters))
18
- (def chapter-current () this-chapter-name)
19
-
20
17
  (def chapter-build (name chapter)
21
18
  (cond chapter
22
19
  chapter
@@ -25,8 +22,10 @@
25
22
  name
26
23
  (chapter-new (hash) name)))))
27
24
 
28
- (def chapter-find (name)
29
- (chapter-build name (hash-get chapters name)))
25
+ (def chapter-names () (hash-keys chapters))
26
+ (def chapter-current () this-chapter-name)
27
+ (def chapter-delete (name) (hash-set chapters name nil))
28
+ (def chapter-find (name) (chapter-build name (hash-get chapters name)))
30
29
 
31
30
  (def chapter-add-to-chapter (chapter attribute thing)
32
31
  (cond chapter
@@ -62,9 +61,9 @@
62
61
  (hash-cons dox (hash-get item 'name) item)
63
62
  (dox-add-to-chapters item (hash-get item 'chapters)))
64
63
 
65
- (def dox-add-doc (name what texts args src chapters)
64
+ (def dox-add-doc (name what texts args src chapters more)
66
65
  (cond (no (privately))
67
- (dox-new (dox-build (hash) name what texts args src chapters))))
66
+ (dox-new (dox-build (if more more (hash)) name what texts args src chapters))))
68
67
 
69
68
  (def dox-add-to-chapters (item chapters)
70
69
  (cond chapters
@@ -85,8 +84,6 @@
85
84
  (cond (dox? name)
86
85
  (hash-get (car (dox-lookup name)) attr)))
87
86
 
88
- (def dox-chapter-names () (hash-keys chapters ))
89
- (def dox-chapter (name) (hash-get chapters name ))
90
87
  (def dox-what-is? (name) (dox-get-attr name 'what ))
91
88
  (def dox-src (name) (dox-get-attr name 'src ))
92
89
  (def dox-examples (name) (hash-get examples name ))
@@ -7,6 +7,10 @@
7
7
  (dox-add-doc '/ 'def '("return the result of dividing all other args into the first arg." "(/ a b c d) is equivalent to (/ a (* b c d))") 'things nil '(math))
8
8
  (dox-add-doc '> 'def '("true if each arg is greater than the next arg") 'things nil '(math))
9
9
  (dox-add-doc '< 'def '("true if each arg is less than the next arg") 'things nil '(math))
10
+ (dox-add-doc '** 'def '("returns a to the power of b") '(a b) nil '(math))
11
+ (dox-add-doc '⌊ 'def '("returns the floor of a (round towards -∞)") '(a) nil '(math))
12
+ (dox-add-doc '⌈ 'def '("returns the ceiling of a (round towards +∞)") '(a) nil '(math))
13
+ (dox-add-doc 'round 'def '("returns a rounded half-away from zero") '(a) nil '(math))
10
14
  (dox-add-doc 'mod 'def '("return the remainder after diving a by b") '(a b) nil '(math))
11
15
  (dox-add-doc 'eval 'def '("evaluate the given lisp expression") '(expr) nil '(nydp-core))
12
16
  (dox-add-doc 'hash 'def '("create a new Hash instance") nil nil '(hash-manipulation))
@@ -18,6 +22,19 @@
18
22
  (dox-add-doc 'sort 'def '("return 'things, sorted according to their natural sort order") 'things nil '(list-manipulation))
19
23
  (dox-add-doc 'sqrt 'def '("return the square root of 'arg") '(arg) nil '(math))
20
24
  (dox-add-doc 'sym 'def '("return the symbol for the given string 'arg") '(arg) nil '(nydp-core))
25
+ (dox-add-doc 'list 'def '("returns args as a list") 'args nil '(nydp-core))
26
+ (dox-add-doc 'noop 'def '("does nothing; returns nil") nil nil '(nydp-core))
27
+ (dox-add-doc 'x1 'def '("just returns arg; the identity function") '(arg) nil '(nydp-core))
28
+ (dox-add-doc 'caar 'def '("car of car; same as (car (car arg))") '(arg) nil '(nydp-core))
29
+ (dox-add-doc 'cadr 'def '("car of cdr; same as (car (cdr arg))") '(arg) nil '(nydp-core))
30
+ (dox-add-doc 'cdar 'def '("cdr of car; same as (cdr (car arg))") '(arg) nil '(nydp-core))
31
+ (dox-add-doc 'cddr 'def '("cdr of cdr; same as (cdr (cdr arg))") '(arg) nil '(nydp-core))
32
+ (dox-add-doc 'no 'def '("t if arg is nil, nil otherwise") '(arg) nil '(nydp-core))
33
+ (dox-add-doc 'just 'def '("just returns arg; the identity function") '(arg) nil '(nydp-core))
34
+ (dox-add-doc 'isa 'def '("t if (type-of obj) is equal to type") '(type obj) nil '(nydp-core))
35
+ (dox-add-doc 'pair? 'def '("t if arg is a cons cell or (equivalently) the start of a list") '(arg) nil '(nydp-core))
36
+ (dox-add-doc 'hash? 'def '("t if arg is a hash") '(arg) nil '(nydp-core))
37
+ (dox-add-doc 'sym? 'def '("t if arg is a symbol, nil otherwise") '(arg) nil '(nydp-core))
21
38
  (dox-add-doc 'ensuring 'def '("execute 'tricky-f, then 'ensure-f afterwards"
22
39
  "'ensure-f will always be executed, even if there is an error in 'tricky-f"
23
40
  "returns the return value of 'tricky-f") '(ensure-f tricky-f) nil '(flow-control))
@@ -29,6 +46,7 @@
29
46
  (dox-add-doc 'string-length 'def '("return the length of 'arg") '(arg) nil '(string-manipulation))
30
47
  (dox-add-doc 'string-replace 'def '("replace 'pattern with 'insert in 'str") '(pattern insert str) nil '(string-manipulation))
31
48
  (dox-add-doc 'string-split 'def '("split 'str delimited by 'delim") '(str delim) nil '(string-manipulation))
49
+ (dox-add-doc 'string-match 'def '("if 'str matches 'pattern, return hash with keys 'match and 'captures ; otherwise nil") '(str pattern) nil '(string-manipulation))
32
50
  (dox-add-doc 'time 'def '("with no args, return the current time."
33
51
  "With one arg, if 'arg-0 is a number, return the current time plus 'arg-0 seconds."
34
52
  "With one arg, if 'arg-0 is a date, return the time at the beginning of the given date."
@@ -53,3 +71,18 @@
53
71
  (dox-add-doc 'script-run 'def '("announces the start of a plugin load or a script load."
54
72
  "'event may be one of '(script-start script-end plugin-start plugin-end)"
55
73
  "'name is the name of the script or plugin concerned") '(event name) nil '(nydp-core))
74
+
75
+ (dox-add-doc 'chapter-end 'def '("Announce the end of a chapter. Called by 'plugin-start, 'plugin-end, 'script-start, 'script-end") nil nil '(nydp/documentation))
76
+ (dox-add-doc 'chapter-start 'def '("Announce the start of a chapter. Creates a new chapter if the named chapter does not already exist") '(chapter-name description) nil '(nydp/documentation))
77
+ (dox-add-doc 'chapter-names 'def '("Get the names of all the chapters nydp knows about") nil nil '(nydp/documentation))
78
+ (dox-add-doc 'chapter-current 'def '("Get the name of the chapter in progress right now - this is normally the last value sent to 'chapter-start") nil nil '(nydp/documentation))
79
+ (dox-add-doc 'chapter-delete 'def '("Remove the named chapter") '(name) nil '(nydp/documentation))
80
+ (dox-add-doc 'chapter-find 'def '("Get the named chapter") '(name) nil '(nydp/documentation))
81
+ (dox-add-doc 'dox-add-doc 'def '("Store the provided documentation item."
82
+ "'name is the name of the item"
83
+ "'what is the type of the item ('def or 'mac or 'thingy ... this is user-definable, not related to 'type-of)"
84
+ "'texts is a list of strings to store for this item"
85
+ "'args is the args if the item has the notion of args"
86
+ "'src the source code of the item if any"
87
+ "'chapters the chapters to which the item should be added, if any") '(name what texts args src chapters) nil '(nydp/documentation))
88
+ (dox-add-doc 'dox-add-examples 'def '("Add the given examples to the dox for the named item") '(name example-exprs) nil '(nydp/documentation))
@@ -0,0 +1,15 @@
1
+ (chapter-start 'nydp/warnings "Store, display, and manage warnings")
2
+
3
+ (def warnings/clear nil
4
+ ; clear all stored warnings
5
+ (assign warnings/list nil))
6
+
7
+ (warnings/clear)
8
+
9
+ (def warnings (f)
10
+ ; apply f to each stored warning. For example, (warnings p) to print warnings to console
11
+ (mapply f warnings))
12
+
13
+ (def warnings/new (kind . info)
14
+ ; store a warning for future retrieval or display
15
+ (assign warnings/list (cons (cons kind info) warnings/list)))
@@ -190,15 +190,41 @@ scoping, assignment, anonymous functions and more...")
190
190
  (def hash-get-assignment (lookup value)
191
191
  `(hash-set ,@lookup ,value))
192
192
 
193
+ (def ampersand-expression? (name)
194
+ (and (pair? name)
195
+ (caris 'ampersand-syntax (car name))))
196
+
197
+ (def ampersand-expression-assignment (place value)
198
+ ; (= (&key (expr)) (val))
199
+ ; (= ((ampersand-syntax key) (expr)) (val))
200
+ ; 'place is ((ampersand-syntax || key) (expr))
201
+ ; we need (hash-set (expr) 'key (val))
202
+ ; however,
203
+ ; (= (&key.subkey (expr)) (val))
204
+ ; 'place is ((ampersand-syntax || (dot-syntax key subkey)) (expr))
205
+ ; we need (hash-set (hash-get (expr) 'key) 'subkey (val))
206
+ (with (k (cadr:cdar place)
207
+ hsh (cadr place))
208
+ (if (caris 'dot-syntax k)
209
+ (dot-syntax-assignment (cons hsh (cdr k)) value)
210
+ `(hash-set ,hsh ',k ,value))))
211
+
193
212
  (mac = (name value)
194
213
  ; generic assignment which unlike builtin 'assign, knows how to assign
195
214
  ; to hash keys
215
+ ; (= (hash-get (expr) 'key) (val) => (hash-set (expr) 'key (val))
216
+ ; (= h.k (val)) => (hash-set h 'k (val))
217
+ ; (= h.j.k (val)) => (hash-set (hash-get h 'j) 'k (val))
218
+ ; (= (&key (expr)) (val)) => (hash-set (expr) 'key (val))
219
+ ; (= (&j.k (expr)) (val)) => (hash-set (hash-get (expr) 'j) 'k (val))
196
220
  (if (isa 'symbol name)
197
221
  `(assign ,name ,value)
198
222
  (caris 'dot-syntax name)
199
223
  (dot-syntax-assignment (cdr name) value)
200
224
  (caris 'hash-get name)
201
- (hash-get-assignment (cdr name) value)))
225
+ (hash-get-assignment (cdr name) value)
226
+ (ampersand-expression? name)
227
+ (ampersand-expression-assignment name value)))
202
228
 
203
229
  (mac def-assign args
204
230
  ; override previous definition to allow expressions like (def hsh.foo (arg arg2) ...)
@@ -276,7 +302,7 @@ scoping, assignment, anonymous functions and more...")
276
302
  (if (pair? args)
277
303
  `(,(car args) (nth ,n ,var) ,@(destructure/with var (cdr args) (+ n 1)))
278
304
  args
279
- `(,args (lastcdr ,var))))
305
+ `(,args (nthcdr ,n ,var))))
280
306
 
281
307
  (def destructure/build (given-args new-args body)
282
308
  ; used internally by 'fun
@@ -291,7 +317,17 @@ scoping, assignment, anonymous functions and more...")
291
317
  `((with ,(destructure/with destructure (car given-args) 0) ,@body)))))
292
318
  `(fn ,(rev new-args given-args) ,@body)))
293
319
 
320
+
321
+ (def fun/approve-arg-names (orig args)
322
+ (if (pair? args)
323
+ (do (fun/approve-arg-names orig (car args))
324
+ (fun/approve-arg-names orig (cdr args)))
325
+ args
326
+ (if (hash-get macs args)
327
+ (warnings/new 'arg-shadows-macro "arg " args " shadows macro " args " in arg list " orig))))
328
+
294
329
  (mac fun (args . body)
295
330
  ; build a 'fn form, changing 'args and 'body to
296
331
  ; properly handle any destructuring args if present
332
+ (fun/approve-arg-names args args)
297
333
  (destructure/build args nil body))
@@ -63,6 +63,6 @@
63
63
  (w/uniq h
64
64
  `(let ,h (hash)
65
65
  (def ,name ,args
66
- ,@(if forms.comment (map (fn (c) list 'comment c)) forms.comment)
67
- ,(if forms.chapter `(chapter ,forms.chapter))
66
+ ,@(map (fn (c) (cons 'comment c)) forms.comment)
67
+ ,@(map (fn (c) (cons 'chapter c)) forms.chapter)
68
68
  (cache-get ,h (list ,@args) (do ,@(hash-get forms nil))))))))
@@ -1,10 +1,10 @@
1
1
  (chapter-start 'list-manipulation "utilities for manipulating and iterating over lists, including filters and transforms")
2
2
 
3
- (def zip (a b)
4
- ; takes two lists, (p q r) and (1 2 3), returns ((p 1) (q 2) (r 3))
5
- (if a
6
- (cons (list (car a) (car b))
7
- (zip (cdr a) (cdr b)))))
3
+ (def zip args
4
+ ; takes a list of lists, ((p q r) (1 2 3) (a b c) ...), returns ((p 1 a ...) (q 2 b ...) (r 3 c ...))
5
+ (if (car args)
6
+ (cons (map car args)
7
+ (apply zip (map cdr args)))))
8
8
 
9
9
  (def eachr (f things)
10
10
  (when things
@@ -12,9 +12,11 @@
12
12
  (f (car things))))
13
13
 
14
14
  (mac push (x things)
15
+ ; assign (cons x things) to things
15
16
  `(= ,things (cons ,x ,things)))
16
17
 
17
18
  (def flatten (things)
19
+ ; flatten the given list, recursively
18
20
  (let acc nil
19
21
  (rfnwith flattenize (x things)
20
22
  (if (pair? x)
@@ -4,13 +4,24 @@
4
4
  (or (eq? x y)
5
5
  (and (pair? x)
6
6
  (pair? y)
7
+ (eq? (len x) (len y))
7
8
  (iso (car x) (car y))
8
9
  (iso (cdr x) (cdr y)))))
9
10
 
10
- (def num? (arg) (isa 'number arg))
11
- (def string? (arg) (isa 'string arg))
12
- (mac just (arg) arg)
13
- (def quotify (arg) `(quote ,arg))
11
+ (def num? (arg) (comment "true if arg is a number") (isa 'number arg))
12
+ (def string? (arg) (comment "true if arg is a string") (isa 'string arg))
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)
19
+
20
+ (def quotify (arg) `(quote ,arg))
21
+
22
+ (defmemo k (arg)
23
+ ; return a function that always returns 'arg, similar to K in SKI calculus
24
+ (fn nil arg))
14
25
 
15
26
  (def len (things)
16
27
  ; return the length of 'things where 'things may be nil, a string, list or hash
@@ -34,6 +34,7 @@
34
34
  ; if 'things is a list, return all the items in the list for which 'f returns non-nil
35
35
  ; otherwise, return 'things if (f things) is non-nil
36
36
  ; otherwise, nil
37
+ ; note that this preserves improper lists and may return only the lastcdr if all else fails...
37
38
  (rfnwith collector (items things)
38
39
  (if (no items)
39
40
  nil
@@ -84,3 +84,9 @@ Examples for ~name
84
84
  (apply macfn
85
85
  (cdr expr)))
86
86
  expr))))
87
+
88
+ (def chapter-remove-item (chapter-name item-name)
89
+ ; remove the named item from the named chapter
90
+ (chapter nydp/documentation)
91
+ (let ch (chapter-find chapter-name)
92
+ (= ch.contents (collect (fn (item) (!eq? item.name item-name)) ch.contents))))
@@ -26,15 +26,15 @@
26
26
 
27
27
  (def execute-single-test (desc test passf failf verbose)
28
28
  (if verbose (p desc " - " (car test)))
29
- (with (expected (nth 2 test) result (eval (nth 1 test)))
30
- (if (iso result expected)
31
- (passf)
32
- (do (p desc " - " (car test) " - FAILED:
33
- running " (pp (nth 1 test)) ",
34
- expected " (inspect expected) ",
35
- got " (inspect result) "
36
- ")
37
- (failf)))))
29
+ (on-err (error (j "FAILED " desc " - " (car test)) (pp:nth 1 test))
30
+ (with (expected (nth 2 test) result (eval (nth 1 test)))
31
+ (if (iso result expected)
32
+ (passf)
33
+ (do (p desc " - " (car test) " - FAILED:
34
+ running " (pp (nth 1 test)) ",
35
+ expected " (inspect expected) ",
36
+ got " (inspect result) "\n")
37
+ (failf))))))
38
38
 
39
39
  (def execute-tests (desc tests passf failf verbose)
40
40
  (execute-test desc
@@ -26,8 +26,8 @@
26
26
  `(push (cons ,regex (fn (,prefix-var ,list-var) ,@body))
27
27
  prefix-list-prefixes))
28
28
 
29
- (define-prefix-list-macro "^λ.+" vars expr
29
+ (define-prefix-list-macro "^λ.*" vars expr
30
30
  ;; allows (map λa(upcase a.name) people)
31
31
  ;; as shortcut for (map (fn (a) (upcase a.name)) people)
32
- (let var-list (map sym (cdr:string-split vars))
32
+ (let var-list (map sym (collect !empty? (cdr:string-split vars)))
33
33
  `(fn ,var-list ,expr)))
@@ -1,21 +1,45 @@
1
+ (chapter-start 'nydp/hooks "event management - execute a piece of code when something happens")
2
+
1
3
  (let hooks {}
2
4
  (def hook-names ()
5
+ ; return the list of hook-names
3
6
  (hash-keys hooks))
4
7
 
5
8
  (def hooks-for (hook-name)
9
+ ; return the list of hooks for 'hook-name
6
10
  hooks.,hook-name)
7
11
 
8
12
  (def add-hook (hook-name f)
13
+ ; add a function 'f to execute when 'hook-name is fired
9
14
  (hash-cons hooks hook-name f))
10
15
 
11
16
  (def clear-hooks (hook-name)
17
+ ; remove all hooks for 'hook-name
12
18
  (= hooks.,hook-name nil))
13
19
 
20
+ (def without-hooks (hook-name f)
21
+ ; temporarily remove all hooks for 'hook-name, restoring them after running 'f
22
+ (let previous-hooks (hooks-for hook-name)
23
+ (ensure (= hooks.,hook-name previous-hooks)
24
+ (clear-hooks hook-name)
25
+ (f))))
26
+
14
27
  (def remove-hook (hook-name f)
28
+ ; only works if you have a reference to the original function
15
29
  (= hooks.,hook-name
16
30
  (collect (curry !eq? f)
17
31
  hooks.,hook-name)))
18
32
 
19
33
  (def run-hooks (hook-name . args)
34
+ ; apply all functions attached to 'hook-name to given 'args
20
35
  (each hook (hooks-for hook-name)
21
36
  (apply hook args))))
37
+
38
+ (add-hook 'warnings/new λw(apply p w))
39
+
40
+ (let super warnings/new
41
+ (def warnings/new (kind . info)
42
+ ; enhance original warnings/new to run the 'warnings/new hook
43
+ (chapter nydp/warnings)
44
+ (apply super kind info)
45
+ (run-hooks 'warnings/new (cons kind info))))
@@ -87,7 +87,7 @@
87
87
  (nth (= i (mod (+ 1 (or j i)) list-len))
88
88
  xs))))
89
89
 
90
- (def fill-bucket (items bucket size-f bucket-size maximum-size)
90
+ (def bucket/fill (items bucket size-f bucket-size maximum-size)
91
91
  ; returns a list (list a b c) where
92
92
  ; 'a is a subset of 'items
93
93
  ; 'b is the sum of sizes of items in 'a : (apply + (map size-f a))
@@ -101,12 +101,40 @@
101
101
  ; 'size-f is a function that can tell the size of each item in 'items
102
102
  ; 'bucket-size is the size of the existing bucket, or 0 if empty
103
103
  ; 'maximum-size is the maximum allowed size for the bucket
104
- (let next-item (car items)
105
- (let next-size (+ (size-f next-item) bucket-size)
106
- (if (< next-size maximum-size)
107
- (fill-bucket (cdr items)
108
- (cons next-item bucket)
109
- size-f
110
- next-size
111
- maximum-size)
112
- (list (rev bucket) bucket-size items)))))
104
+ ; implementation note: this function exploits the behaviour of '> returning its last argument when true
105
+ (aif (and items
106
+ (> maximum-size (+ (size-f (car items)) bucket-size)))
107
+ (bucket/fill (cdr items)
108
+ (cons (car items) bucket)
109
+ size-f
110
+ it
111
+ maximum-size)
112
+ (and items (eq? bucket-size 0))
113
+ (bucket/fill (cdr items)
114
+ (cons (car items) bucket)
115
+ size-f
116
+ (size-f (car items))
117
+ maximum-size)
118
+ (list (rev bucket) bucket-size items)))
119
+
120
+ (def bucket/new (buckets)
121
+ ; used by 'fill-buckets
122
+ (cons { bucket-size 0 } buckets))
123
+
124
+ (def fill-buckets (items max buckets size-f key)
125
+ ; useful for pagination where each item may have a different size
126
+ ; returns a list of hash with keys 'bucket-size and key
127
+ ; if buckets is non-nil, assumes it is a list of previously-established buckets
128
+ ; will add new items to first bucket if its 'bucket-size permits
129
+ (if items
130
+ (if buckets
131
+ (let initial (car buckets)
132
+ (let (these size others)
133
+ (bucket/fill items nil size-f initial.bucket-size max)
134
+ (hash-set initial key these)
135
+ (= initial.bucket-size size)
136
+ (if others
137
+ (fill-buckets others max (bucket/new buckets) size-f key)
138
+ (fill-buckets others max buckets size-f key))))
139
+ (fill-buckets items max (bucket/new buckets) size-f key))
140
+ buckets))