nydp 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.md +77 -56
  4. data/lib/lisp/core-000.nydp +1 -1
  5. data/lib/lisp/core-010-precompile.nydp +49 -29
  6. data/lib/lisp/core-012-utils.nydp +12 -8
  7. data/lib/lisp/core-015-documentation.nydp +41 -15
  8. data/lib/lisp/core-017-builtin-dox.nydp +621 -100
  9. data/lib/lisp/core-020-utils.nydp +33 -6
  10. data/lib/lisp/core-025-warnings.nydp +1 -1
  11. data/lib/lisp/core-030-syntax.nydp +64 -48
  12. data/lib/lisp/core-035-flow-control.nydp +20 -28
  13. data/lib/lisp/core-037-list-utils.nydp +84 -21
  14. data/lib/lisp/core-040-utils.nydp +8 -5
  15. data/lib/lisp/core-041-string-utils.nydp +17 -11
  16. data/lib/lisp/core-043-list-utils.nydp +140 -77
  17. data/lib/lisp/core-045-dox-utils.nydp +1 -0
  18. data/lib/lisp/core-050-test-runner.nydp +8 -12
  19. data/lib/lisp/core-070-prefix-list.nydp +19 -15
  20. data/lib/lisp/core-080-pretty-print.nydp +13 -5
  21. data/lib/lisp/core-090-hook.nydp +11 -11
  22. data/lib/lisp/core-100-utils.nydp +51 -66
  23. data/lib/lisp/core-110-hash-utils.nydp +34 -7
  24. data/lib/lisp/core-120-settings.nydp +14 -9
  25. data/lib/lisp/core-130-validations.nydp +28 -13
  26. data/lib/lisp/core-900-benchmarking.nydp +420 -47
  27. data/lib/lisp/tests/000-empty-args-examples.nydp +5 -0
  28. data/lib/lisp/tests/andify-examples.nydp +1 -1
  29. data/lib/lisp/tests/auto-hash-examples.nydp +6 -1
  30. data/lib/lisp/tests/best-examples.nydp +1 -1
  31. data/lib/lisp/tests/boot-tests.nydp +1 -1
  32. data/lib/lisp/tests/date-examples.nydp +129 -102
  33. data/lib/lisp/tests/destructuring-examples.nydp +1 -1
  34. data/lib/lisp/tests/dox-tests.nydp +2 -2
  35. data/lib/lisp/tests/hash-examples.nydp +58 -33
  36. data/lib/lisp/tests/list-tests.nydp +137 -1
  37. data/lib/lisp/tests/pretty-print-tests.nydp +12 -0
  38. data/lib/lisp/tests/rotate-2d-array-examples.nydp +26 -0
  39. data/lib/lisp/tests/sort-examples.nydp +5 -5
  40. data/lib/lisp/tests/string-tests.nydp +16 -5
  41. data/lib/lisp/tests/syntax-tests.nydp +10 -2
  42. data/lib/lisp/tests/time-examples.nydp +8 -1
  43. data/lib/lisp/tests/unparse-tests.nydp +13 -7
  44. data/lib/nydp/assignment.rb +15 -28
  45. data/lib/nydp/builtin/abs.rb +4 -3
  46. data/lib/nydp/builtin/apply.rb +8 -10
  47. data/lib/nydp/builtin/cdr_set.rb +1 -1
  48. data/lib/nydp/builtin/comment.rb +1 -3
  49. data/lib/nydp/builtin/date.rb +11 -28
  50. data/lib/nydp/builtin/divide.rb +3 -10
  51. data/lib/nydp/builtin/ensuring.rb +6 -21
  52. data/lib/nydp/builtin/error.rb +2 -4
  53. data/lib/nydp/builtin/eval.rb +9 -4
  54. data/lib/nydp/builtin/greater_than.rb +7 -8
  55. data/lib/nydp/builtin/handle_error.rb +10 -34
  56. data/lib/nydp/builtin/hash.rb +24 -45
  57. data/lib/nydp/builtin/inspect.rb +1 -3
  58. data/lib/nydp/builtin/is_equal.rb +4 -7
  59. data/lib/nydp/builtin/less_than.rb +6 -7
  60. data/lib/nydp/builtin/log.rb +7 -0
  61. data/lib/nydp/builtin/math_ceiling.rb +1 -3
  62. data/lib/nydp/builtin/math_floor.rb +1 -3
  63. data/lib/nydp/builtin/math_power.rb +1 -3
  64. data/lib/nydp/builtin/math_round.rb +2 -2
  65. data/lib/nydp/builtin/minus.rb +7 -14
  66. data/lib/nydp/builtin/parse.rb +5 -5
  67. data/lib/nydp/builtin/parse_in_string.rb +5 -7
  68. data/lib/nydp/builtin/plus.rb +14 -31
  69. data/lib/nydp/builtin/pre_compile.rb +1 -3
  70. data/lib/nydp/builtin/puts.rb +4 -8
  71. data/lib/nydp/builtin/quit.rb +1 -1
  72. data/lib/nydp/builtin/rand.rb +6 -11
  73. data/lib/nydp/builtin/random_string.rb +2 -4
  74. data/lib/nydp/builtin/rng.rb +25 -0
  75. data/lib/nydp/builtin/ruby_wrap.rb +27 -14
  76. data/lib/nydp/builtin/script_run.rb +1 -3
  77. data/lib/nydp/builtin/set_intersection.rb +3 -4
  78. data/lib/nydp/builtin/set_union.rb +3 -4
  79. data/lib/nydp/builtin/sort.rb +2 -7
  80. data/lib/nydp/builtin/string_match.rb +5 -13
  81. data/lib/nydp/builtin/string_replace.rb +2 -7
  82. data/lib/nydp/builtin/string_split.rb +3 -8
  83. data/lib/nydp/builtin/sym.rb +2 -9
  84. data/lib/nydp/builtin/thread_locals.rb +2 -2
  85. data/lib/nydp/builtin/time.rb +38 -44
  86. data/lib/nydp/builtin/times.rb +6 -15
  87. data/lib/nydp/builtin/to_integer.rb +8 -14
  88. data/lib/nydp/builtin/to_string.rb +2 -13
  89. data/lib/nydp/builtin/type_of.rb +10 -16
  90. data/lib/nydp/builtin/vm_info.rb +2 -10
  91. data/lib/nydp/builtin.rb +15 -37
  92. data/lib/nydp/compiler.rb +29 -19
  93. data/lib/nydp/cond.rb +95 -88
  94. data/lib/nydp/context_symbol.rb +11 -9
  95. data/lib/nydp/core.rb +74 -73
  96. data/lib/nydp/core_ext.rb +87 -26
  97. data/lib/nydp/date.rb +22 -19
  98. data/lib/nydp/error.rb +2 -3
  99. data/lib/nydp/function_invocation.rb +76 -289
  100. data/lib/nydp/helper.rb +18 -9
  101. data/lib/nydp/interpreted_function.rb +159 -25
  102. data/lib/nydp/lexical_context.rb +9 -8
  103. data/lib/nydp/lexical_context_builder.rb +1 -1
  104. data/lib/nydp/literal.rb +3 -7
  105. data/lib/nydp/loop.rb +72 -0
  106. data/lib/nydp/namespace.rb +52 -0
  107. data/lib/nydp/pair.rb +146 -50
  108. data/lib/nydp/parser.rb +9 -11
  109. data/lib/nydp/plugin.rb +88 -19
  110. data/lib/nydp/runner.rb +141 -23
  111. data/lib/nydp/symbol.rb +16 -26
  112. data/lib/nydp/symbol_lookup.rb +3 -2
  113. data/lib/nydp/tokeniser.rb +1 -1
  114. data/lib/nydp/truth.rb +2 -37
  115. data/lib/nydp/version.rb +1 -1
  116. data/lib/nydp.rb +33 -44
  117. data/nydp.gemspec +2 -1
  118. data/spec/date_spec.rb +26 -32
  119. data/spec/embedded_spec.rb +22 -22
  120. data/spec/error_spec.rb +12 -16
  121. data/spec/foreign_hash_spec.rb +21 -36
  122. data/spec/hash_non_hash_behaviour_spec.rb +12 -29
  123. data/spec/hash_spec.rb +36 -49
  124. data/spec/literal_spec.rb +6 -6
  125. data/spec/nydp_spec.rb +14 -14
  126. data/spec/pair_spec.rb +8 -8
  127. data/spec/parser_spec.rb +41 -37
  128. data/spec/rand_spec.rb +1 -4
  129. data/spec/spec_helper.rb +3 -3
  130. data/spec/string_atom_spec.rb +15 -16
  131. data/spec/symbol_spec.rb +27 -52
  132. data/spec/thread_local_spec.rb +23 -8
  133. data/spec/time_spec.rb +4 -10
  134. data/spec/tokeniser_spec.rb +10 -10
  135. metadata +25 -13
  136. data/lib/nydp/builtin/modulo.rb +0 -11
  137. data/lib/nydp/builtin/regexp.rb +0 -7
  138. data/lib/nydp/builtin/sqrt.rb +0 -7
  139. data/lib/nydp/builtin/string_pad_left.rb +0 -7
  140. data/lib/nydp/builtin/string_pad_right.rb +0 -7
  141. data/lib/nydp/hash.rb +0 -9
  142. data/lib/nydp/image_store.rb +0 -21
  143. data/lib/nydp/vm.rb +0 -129
@@ -1,13 +1,13 @@
1
1
  (chapter-start 'list-manipulation)
2
2
 
3
+ ;; alias for 'detect
4
+ ;; true if thing is in things, nil otherwise
3
5
  (def include? (thing things)
4
- ; alias for 'detect
5
- ; true if thing is in things, nil otherwise
6
6
  (detect thing things))
7
7
 
8
+ ;; sort 'things according to the value
9
+ ;; returned by 'f for each thing in 'things
8
10
  (def sort-by (f things)
9
- ; sort 'things according to the value
10
- ; returned by 'f for each thing in 'things
11
11
  (let tmp (hash)
12
12
  (each thing things
13
13
  (hash-cons tmp
@@ -17,7 +17,7 @@
17
17
  (map λx(hash-get tmp x)
18
18
  (sort:hash-keys tmp)))))
19
19
 
20
- ; like 'sort-by, except when 'f returns nil, use 'instead as the sort key instead
20
+ ;; like 'sort-by, except when 'f returns nil, use 'instead as the sort key instead
21
21
  (def safe-sort-by (f instead things)
22
22
  (sort-by λi(or (f i) instead) things))
23
23
 
@@ -27,38 +27,37 @@
27
27
  ;; takes a function f, returns a new function that takes a list and sorts the list by 'f
28
28
  (def safe-sort-by-f (f instead) (curry1 safe-sort-by f instead))
29
29
 
30
+ ;; basically (reduce freduce (map fmap things))
30
31
  (def mapreduce (fmap freduce things)
31
- ; same as (reduce freduce (map fmap things))
32
- ; returns the resulting list
33
- (if (pair? things)
34
- (freduce (fmap (car things))
35
- (mapreduce fmap freduce (cdr things)))
36
- things
37
- (freduce (map fmap things))
38
- (freduce)))
39
-
40
- ; map 'f over 'things and sum the resulting list
41
- (def mapsum (f things) (mapreduce f + things))
42
-
32
+ (reduce freduce (map fmap things)))
33
+
34
+ ;; map 'f over 'things and sum the resulting list, excluding nils
35
+ (def mapsum (f things)
36
+ (apply + 0 (compact:map f things)))
37
+
38
+ ;; returns a new function f which takes a parameter x
39
+ ;; for each call to f with any value Z for x
40
+ ;; f returns true if this f has previously seen Z
41
+ ;; f returns nil otherwise.
42
+ ;; Note that each call to 'seen? returns a new function with
43
+ ;; its own history independent of previous calls to 'seen?
43
44
  (def seen? ()
44
- ; returns a new function f which takes a parameter x
45
- ; for each call to f with any value Z for x
46
- ; f returns true if this f has previously seen Z
47
- ; f returns nil otherwise.
48
- ; Note that each call to 'seen? returns a new function with
49
- ; its own history independent of previous calls to 'seen?
50
45
  (let seen (hash)
51
46
  λx(returning seen.,x (= seen.,x t))))
52
47
 
53
48
  ; return a list containing all the elements of 'things, but with no duplicates
54
49
  (def uniqify (things) (reject (seen?) things))
55
50
 
51
+ ;; like 'group-by, but uses and returns the supplied 'h parameter which should be a hash instance
52
+ (def group-by-h (f h things)
53
+ (returning h
54
+ (each thing things
55
+ (hash-cons h (f thing) thing))))
56
+
56
57
  ;; return a hash of 'things keyed by (f thing) for
57
58
  ;; each thing in 'things
58
59
  (def group-by (f things)
59
- (returnlet hsh {}
60
- (each thing things
61
- (hash-cons hsh (f thing) thing))))
60
+ (group-by-h f {} things))
62
61
 
63
62
  (with (m2i λd(+ (* 12 d.year) (- d.month 1))
64
63
  i2m λi(date (/ i 12) (+ 1 (mod i 12)) 1))
@@ -70,22 +69,6 @@
70
69
  (let mi (m2i anchor)
71
70
  (map λm(i2m (+ mi m)) mm))))
72
71
 
73
- ;; each call to the name 'accfn-name with an arg will append the arg to the end of a list.
74
- ;; This form returns the resulting list.
75
- ;; Example (collect first names from a list of people)
76
- ;;
77
- ;; (accum a (each person people (a person.firstname)))
78
- ;;
79
- ;; will return (Alice Bob Carol Declan Eliza Fionn)
80
- ;;
81
- (mac accum (accfn-name . body)
82
- (w/uniq (things last-cons)
83
- `(with (,last-cons (cons) ,things nil)
84
- (= ,things ,last-cons)
85
- (let ,accfn-name (fn (a) (= ,last-cons (cdr-set ,last-cons (cons a))) a)
86
- ,@body
87
- (cdr ,things)))))
88
-
89
72
  ;; like 'accum, except 'accfn-name expects 2 args, a key and a value
90
73
  ;; value is hash-consed onto key in an internally-maintained hash
91
74
  ;; the form returns the resulting hash
@@ -105,8 +88,10 @@
105
88
  (if (< n stop)
106
89
  (r (+ (acc n) 1))))))
107
90
 
108
- ; return a function that returns 'start on first invocation,
109
- ; and 'start + n * 'incr for each nth invocation
91
+ ;; return a function that returns 'start on first invocation,
92
+ ;; and 'start + n * 'incr for each nth invocation
93
+ ;;
94
+ ;; see also 'counter which does almost exactly the same thing
110
95
  (def seqf (start incr)
111
96
  (let i (or incr 1)
112
97
  (fn () (returning start (++ start i)))))
@@ -116,30 +101,30 @@
116
101
  ; for each item in 'args
117
102
  (def mapply (f args) (map λa(apply f a) args))
118
103
 
104
+ ;; create a function called 'name ; each invocation of the function will
105
+ ;; return the next value in 'things, cycling around to the start if no things are left
119
106
  (mac def/cycler (name things)
120
- ; create a function called 'name ; each invocation of the function will
121
- ; return the next value in 'things, cycling around to the start if no things are left
122
107
  `(with (i -1 xs ',things list-len ,(len things))
123
108
  (def ,name (j)
124
109
  (comment ,(just "each call to ~name returns the next value from ~(inspect things)"))
125
110
  (nth (= i (mod (+ 1 (or j i)) list-len))
126
111
  xs))))
127
112
 
113
+ ;; returns a list (list a b c) where
114
+ ;; 'a is a subset of 'items
115
+ ;; 'b is the sum of sizes of items in 'a : (apply + (map size-f a))
116
+ ;; 'c is the subset of 'items not in 'a
117
+ ;; invariants:
118
+ ;; b < maximum-size
119
+ ;; 'a + 'c is equal to 'items
120
+ ;; arguments:
121
+ ;; 'items is the list of things of which you have too many
122
+ ;; 'bucket is either nil, or a list if you have an existing partially-filled bucket
123
+ ;; 'size-f is a function that can tell the size of each item in 'items
124
+ ;; 'bucket-size is the size of the existing bucket, or 0 if empty
125
+ ;; 'maximum-size is the maximum allowed size for the bucket
126
+ ;; implementation note: this function exploits the behaviour of '> returning its last argument when true
128
127
  (def bucket/fill (items bucket size-f bucket-size maximum-size)
129
- ; returns a list (list a b c) where
130
- ; 'a is a subset of 'items
131
- ; 'b is the sum of sizes of items in 'a : (apply + (map size-f a))
132
- ; 'c is the subset of 'items not in 'a
133
- ; invariants:
134
- ; b < maximum-size
135
- ; 'a + 'c is equal to 'items
136
- ; arguments:
137
- ; 'items is the list of things of which you have too many
138
- ; 'bucket is either nil, or a list if you have an existing partially-filled bucket
139
- ; 'size-f is a function that can tell the size of each item in 'items
140
- ; 'bucket-size is the size of the existing bucket, or 0 if empty
141
- ; 'maximum-size is the maximum allowed size for the bucket
142
- ; implementation note: this function exploits the behaviour of '> returning its last argument when true
143
128
  (aif (and items
144
129
  (> maximum-size (+ (size-f (car items)) bucket-size)))
145
130
  (bucket/fill (cdr items)
@@ -155,15 +140,15 @@
155
140
  maximum-size)
156
141
  (list (rev bucket) bucket-size items)))
157
142
 
143
+ ;; used by 'fill-buckets
158
144
  (def bucket/new (buckets)
159
- ; used by 'fill-buckets
160
145
  (cons { bucket-size 0 } buckets))
161
146
 
147
+ ;; useful for pagination where each item may have a different size
148
+ ;; returns a list of hash with keys 'bucket-size and key
149
+ ;; if buckets is non-nil, assumes it is a list of previously-established buckets
150
+ ;; will add new items to first bucket if its 'bucket-size permits
162
151
  (def fill-buckets (items max buckets size-f key)
163
- ; useful for pagination where each item may have a different size
164
- ; returns a list of hash with keys 'bucket-size and key
165
- ; if buckets is non-nil, assumes it is a list of previously-established buckets
166
- ; will add new items to first bucket if its 'bucket-size permits
167
152
  (if items
168
153
  (if buckets
169
154
  (let initial (car buckets)
@@ -177,7 +162,7 @@
177
162
  (fill-buckets items max (bucket/new buckets) size-f key))
178
163
  buckets))
179
164
 
180
- ; return the list except for the last element
165
+ ;; return the list except for the last element
181
166
  (def all-but-last (things)
182
167
  (accum acc
183
168
  ((afn (xs)
@@ -9,6 +9,11 @@
9
9
  (mac auto-hash names
10
10
  `(brace-list ,@(flatten:map λn(list n n) names)))
11
11
 
12
+ ;; allows #(a b c) as shortcut for (auto-hash a b c)
13
+ ;; which is itself a shortcut for { a a b b c c }
14
+ (define-prefix-list-macro "#" no-vars keys
15
+ `(auto-hash ,@keys))
16
+
12
17
  ;; like 'map, but for a hash instead of a list ; provided function 'f takes three arguments,
13
18
  ;; a key, the corresponding value from the given hash, and the index of the item in the list
14
19
  (def map-hash (f h pre)
@@ -22,17 +27,26 @@
22
27
  ;; v, the value
23
28
  ;; i, the index
24
29
  ;;
25
- (def hash-transform-values (f h pre)
30
+ (def hash-transform-values (f h)
26
31
  (returnlet newh {}
27
- (map-hash λkvi(hash-set newh k (f k v i)))))
28
-
32
+ (map-hash λkvi(hash-set newh k (f k v i)) h)))
29
33
 
30
- ;; Return a new hash where keys are (map f things) and values are the corresponding things.
31
- ;; No attempt is made to avoid clobbering items. Use 'group-by if there are duplicate keys.
32
- (def hashify (f things)
34
+ ;; Return a new hash where keys are (map f things) and corresponding values are (map g things).
35
+ ;; No attempt is made to avoid clobbering items. Use 'group-by instead, if there are duplicate keys.
36
+ ;;
37
+ ;; example: (hashify &firstname x1 people) returns { "johann" <bach record> "ludwig" <beethoven record> }
38
+ ;;
39
+ ;; reverse: (hashify x1 &firstname people) returns { <bach record> "johann" <beethoven record> "ludwig" }
40
+ ;;
41
+ ;; example: (hashify &firstname &lastname people) returns { "johann" "bach" "ludwig" "van beethoven" }
42
+ ;;
43
+ ;; example: (hashify &born &lastname people) returns { 1685 "bach" 1770 "van beethoven" }
44
+ ;;
45
+ ;; reverse example: (hashify &lastname &born people) returns { "bach" 1685 "van beethoven" 1770 }
46
+ (def hashify (f g things)
33
47
  (returnlet hsh {}
34
48
  (each thing things
35
- (hash-set hsh (f thing) thing))))
49
+ (hash-set hsh (f thing) (g thing)))))
36
50
 
37
51
  ;; like 'group-by, except 'f returns multiple items, each of which
38
52
  ;; is used to key the thing in question
@@ -59,3 +73,16 @@
59
73
  (each ,kvar (hash-keys ,xs)
60
74
  (let ,vvar (hash-get ,xs ,kvar)
61
75
  ,@body)))))
76
+
77
+ ;; merge two hashes of the format k => (v0 v1...)
78
+ ;;
79
+ ;; example: h0 is { a (1 2) b (3 4) }, h1 is { a (2 5) c (6 7) }
80
+ ;;
81
+ ;; (hash/merge-lists h0 h1) will be: { a (1 2 5) b (3 4) c (6 7) }
82
+ ;;
83
+ ;; Uses set-union when merging lists, so merged lists
84
+ ;; contain no duplicates.
85
+ (def hash/merge-lists (h0 h1)
86
+ (returnlet h (hash-merge { } h0)
87
+ (each k (hash-keys h1)
88
+ (= h.,k (⋃ h.,k h1.,k)))))
@@ -3,7 +3,7 @@
3
3
  (assign settings {})
4
4
  (assign initial-settings {})
5
5
 
6
- ; convert expr to a function that returns the expr, unless expr is a symbol in which case we assume it already refers to a fn
6
+ ;; convert expr to a function that returns the expr, unless expr is a symbol in which case we assume it already refers to a fn
7
7
  (def settings/fn (expr)
8
8
  (if (sym? expr) expr
9
9
  (or (atom? expr) (no expr)) `(k ,expr)
@@ -12,33 +12,38 @@
12
12
  (caris 'brace-list expr) `(k ,expr)
13
13
  `(fn (_) ,expr)))
14
14
 
15
- ; update value of setting 'name
15
+ ;; update value of setting 'name
16
16
  (mac set-setting (name value)
17
17
  `(do (hash-cons (dox-item-by-type 'setting ',(sym name))
18
18
  'values
19
19
  { plugin this-plugin script this-script value ',value })
20
20
  (hash-set settings ',(sym name) ,(settings/fn value))))
21
21
 
22
- ; update value of setting 'name
22
+ ;; update value of setting 'name
23
23
  (mac reset-setting (name)
24
24
  `(set-setting ,name ,(hash-get initial-settings (sym name))))
25
25
 
26
+ ;; define a setting in the given 'context with a 'name and an 'initial value, with a 'doc to explain it
27
+ ;; if value is a function, it is invoked with 'context and 'name to retrieve its value
28
+ ;; if value is a constant, it is wrapped in a function to return the constant
26
29
  (mac def-setting (name initial)
27
- ; define a setting in the given 'context with a 'name and an 'initial value, with a 'doc to explain it
28
- ; if value is a function, it is invoked with 'context and 'name to retrieve its value
29
- ; if value is a constant, it is wrapped in a function to return the constant
30
30
  (let context (car:string-split name ".")
31
31
  `(do (dox-add-doc ',(sym name)
32
32
  'setting
33
33
  ',(fetch-and-clear-comments)
34
34
  nil
35
35
  '(def-setting ,name ,initial)
36
- '(,(sym "settings/~context"))
37
- { setting { default ',initial context ',context name ',name } })
36
+ (hash-merge
37
+ { setting { default ',initial context ',context name ',name } }
38
+ (dox/attrs (,(sym "settings/~context")))))
38
39
  (hash-set initial-settings ',(sym name) ',initial)
39
40
  (set-setting ,(sym name) ,initial))))
40
41
 
41
- ; get the value of the given setting. Raises an error if the setting is unknown
42
+ ;; get the value of the given setting. Raises an error if the setting is unknown.
43
+ ;; (note for testing: when using set-settings in ruby, make sure to quote string values, eg
44
+ ;; set-settings "key.for.setting" => "this is the value".inspect
45
+ ;; or alternatively,
46
+ ;; set-settings "key.for.setting" => '"this is the value"'
42
47
  (def setting (name)
43
48
  (aif (hash-get settings (sym name))
44
49
  (on-err (error "can't get value of setting ~name : stored object is ~(inspect it)")
@@ -8,11 +8,13 @@
8
8
  (def validate/fn+ (type context f)
9
9
  (hash-cons validations (list type context) f)))
10
10
 
11
- ;;
12
11
  ;; returns a hash of error-name to list of error messages
13
12
  ;;
14
13
  ;; An empty return value signifies an error-free 'thing
15
14
  ;;
15
+ ;; @thing@ the thing to validate
16
+ ;; @context@ an identifier to select the subset of validations to apply
17
+ ;;
16
18
  (def validate (thing context)
17
19
  (returnlet msgs {}
18
20
  (let msgf λem(hash-cons msgs e m)
@@ -21,31 +23,44 @@
21
23
 
22
24
  ;; declare a validation routine for type 'type in context 'context
23
25
  ;;
24
- ;; 'type must be a symbol
25
- ;; 'context must be a symbol
26
- ;; 'body is one or more nydp expressions.
26
+ ;; @type@ must be a symbol
27
+ ;; @context@ must be a symbol
28
+ ;; @body@ is one or more nydp expressions.
27
29
  ;;
28
- ;; 'body will be embedded in a function with access to the following variables :
30
+ ;; @body@ will be embedded in a function with access to the following variables :
29
31
  ;;
30
32
  ;; * the value of the 'type argument
31
33
  ;; * ctx
32
34
  ;; * mf
33
35
  ;;
34
- ;; 'mf ("message function") is a function that takes two arguments and is used to store
36
+ ;; @mf@ ("message function") is a function that takes two arguments and is used to store
35
37
  ;; the validation error message
36
38
  ;; example: (mf "Last name" "Last name must not be empty")
37
39
  ;;
38
40
  ;; example usage:
39
41
  ;;
40
- ;; (validate/def invoice issue
41
- ;; (if (no invoice.account)
42
- ;; (mf "Account" "Account must be a client account"))
43
- ;; (if (!> invoice.total 0)
44
- ;; (mf "Amount" "Amount must be greater than zero"))
45
- ;; (if (any? !&group invoice.invoice-items)
46
- ;; (mf "Group" "Each line must be assigned to a group")))
42
+ ;; <pre><code>
43
+ ;; (validate/def invoice issue
44
+ ;; (if (no invoice.account)
45
+ ;; (mf "Account" "Account must be a client account"))
46
+ ;; (if (!> invoice.total 0)
47
+ ;; (mf "Amount" "Amount must be greater than zero"))
48
+ ;; (if (any? !&group invoice.invoice-items)
49
+ ;; (mf "Group" "Each line must be assigned to a group")))
50
+ ;; </code></pre>
51
+ ;;
52
+ ;; <br>
47
53
  ;;
48
54
  ;; if your routine makes no call to 'mf then 'validate will return an empty hash, which should be
49
55
  ;; interpreted as signifying that the object in question is error free in the given context.
56
+ ;;
57
+ ;; run your validations thus:
58
+ ;;
59
+ ;; <pre><code>
60
+ ;; (let validations (validate invoice 'issue)
61
+ ;; (if (empty? validations)
62
+ ;; (invoice.issue)))
63
+ ;; </code></pre>
64
+ ;;
50
65
  (mac validate/def (type context . body)
51
66
  `(validate/fn+ ',type ',context (fn (,type ctx mf) ,@body)))