nydp 0.5.1 → 0.6.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 (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)))