nydp 0.3.0 → 0.4.0

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