nydp 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +77 -56
- data/lib/lisp/core-000.nydp +1 -1
- data/lib/lisp/core-010-precompile.nydp +49 -29
- data/lib/lisp/core-012-utils.nydp +12 -8
- data/lib/lisp/core-015-documentation.nydp +41 -15
- data/lib/lisp/core-017-builtin-dox.nydp +621 -100
- data/lib/lisp/core-020-utils.nydp +33 -6
- data/lib/lisp/core-025-warnings.nydp +1 -1
- data/lib/lisp/core-030-syntax.nydp +64 -48
- data/lib/lisp/core-035-flow-control.nydp +20 -28
- data/lib/lisp/core-037-list-utils.nydp +84 -21
- data/lib/lisp/core-040-utils.nydp +8 -5
- data/lib/lisp/core-041-string-utils.nydp +17 -11
- data/lib/lisp/core-043-list-utils.nydp +140 -77
- data/lib/lisp/core-045-dox-utils.nydp +1 -0
- data/lib/lisp/core-050-test-runner.nydp +8 -12
- data/lib/lisp/core-070-prefix-list.nydp +19 -15
- data/lib/lisp/core-080-pretty-print.nydp +13 -5
- data/lib/lisp/core-090-hook.nydp +11 -11
- data/lib/lisp/core-100-utils.nydp +51 -66
- data/lib/lisp/core-110-hash-utils.nydp +34 -7
- data/lib/lisp/core-120-settings.nydp +14 -9
- data/lib/lisp/core-130-validations.nydp +28 -13
- data/lib/lisp/core-900-benchmarking.nydp +420 -47
- data/lib/lisp/tests/000-empty-args-examples.nydp +5 -0
- data/lib/lisp/tests/andify-examples.nydp +1 -1
- data/lib/lisp/tests/auto-hash-examples.nydp +6 -1
- data/lib/lisp/tests/best-examples.nydp +1 -1
- data/lib/lisp/tests/boot-tests.nydp +1 -1
- data/lib/lisp/tests/date-examples.nydp +129 -102
- data/lib/lisp/tests/destructuring-examples.nydp +1 -1
- data/lib/lisp/tests/dox-tests.nydp +2 -2
- data/lib/lisp/tests/hash-examples.nydp +58 -33
- data/lib/lisp/tests/list-tests.nydp +137 -1
- data/lib/lisp/tests/pretty-print-tests.nydp +12 -0
- data/lib/lisp/tests/rotate-2d-array-examples.nydp +26 -0
- data/lib/lisp/tests/sort-examples.nydp +5 -5
- data/lib/lisp/tests/string-tests.nydp +30 -5
- data/lib/lisp/tests/syntax-tests.nydp +10 -2
- data/lib/lisp/tests/time-examples.nydp +8 -1
- data/lib/lisp/tests/unparse-tests.nydp +13 -7
- data/lib/nydp/assignment.rb +15 -28
- data/lib/nydp/builtin/abs.rb +4 -3
- data/lib/nydp/builtin/apply.rb +8 -10
- data/lib/nydp/builtin/cdr_set.rb +1 -1
- data/lib/nydp/builtin/comment.rb +1 -3
- data/lib/nydp/builtin/date.rb +11 -28
- data/lib/nydp/builtin/divide.rb +3 -10
- data/lib/nydp/builtin/ensuring.rb +6 -21
- data/lib/nydp/builtin/error.rb +2 -4
- data/lib/nydp/builtin/eval.rb +9 -4
- data/lib/nydp/builtin/greater_than.rb +7 -8
- data/lib/nydp/builtin/handle_error.rb +10 -34
- data/lib/nydp/builtin/hash.rb +24 -45
- data/lib/nydp/builtin/inspect.rb +1 -3
- data/lib/nydp/builtin/is_equal.rb +4 -7
- data/lib/nydp/builtin/less_than.rb +6 -7
- data/lib/nydp/builtin/log.rb +7 -0
- data/lib/nydp/builtin/math_ceiling.rb +1 -3
- data/lib/nydp/builtin/math_floor.rb +1 -3
- data/lib/nydp/builtin/math_power.rb +1 -3
- data/lib/nydp/builtin/math_round.rb +2 -2
- data/lib/nydp/builtin/minus.rb +7 -14
- data/lib/nydp/builtin/parse.rb +5 -5
- data/lib/nydp/builtin/parse_in_string.rb +5 -7
- data/lib/nydp/builtin/plus.rb +14 -31
- data/lib/nydp/builtin/pre_compile.rb +1 -3
- data/lib/nydp/builtin/puts.rb +4 -8
- data/lib/nydp/builtin/quit.rb +1 -1
- data/lib/nydp/builtin/rand.rb +6 -11
- data/lib/nydp/builtin/random_string.rb +2 -4
- data/lib/nydp/builtin/rng.rb +25 -0
- data/lib/nydp/builtin/ruby_wrap.rb +27 -14
- data/lib/nydp/builtin/script_run.rb +1 -3
- data/lib/nydp/builtin/set_intersection.rb +3 -4
- data/lib/nydp/builtin/set_union.rb +3 -4
- data/lib/nydp/builtin/sort.rb +2 -7
- data/lib/nydp/builtin/string_match.rb +5 -13
- data/lib/nydp/builtin/string_replace.rb +2 -7
- data/lib/nydp/builtin/string_split.rb +3 -8
- data/lib/nydp/builtin/sym.rb +2 -9
- data/lib/nydp/builtin/thread_locals.rb +2 -2
- data/lib/nydp/builtin/time.rb +38 -44
- data/lib/nydp/builtin/times.rb +6 -15
- data/lib/nydp/builtin/to_integer.rb +8 -14
- data/lib/nydp/builtin/to_string.rb +2 -13
- data/lib/nydp/builtin/type_of.rb +10 -16
- data/lib/nydp/builtin/vm_info.rb +2 -10
- data/lib/nydp/builtin.rb +15 -37
- data/lib/nydp/compiler.rb +29 -19
- data/lib/nydp/cond.rb +95 -88
- data/lib/nydp/context_symbol.rb +11 -9
- data/lib/nydp/core.rb +74 -73
- data/lib/nydp/core_ext.rb +88 -24
- data/lib/nydp/date.rb +22 -19
- data/lib/nydp/error.rb +2 -3
- data/lib/nydp/function_invocation.rb +76 -289
- data/lib/nydp/helper.rb +18 -9
- data/lib/nydp/interpreted_function.rb +159 -25
- data/lib/nydp/lexical_context.rb +9 -8
- data/lib/nydp/lexical_context_builder.rb +1 -1
- data/lib/nydp/literal.rb +3 -7
- data/lib/nydp/loop.rb +72 -0
- data/lib/nydp/namespace.rb +52 -0
- data/lib/nydp/pair.rb +146 -50
- data/lib/nydp/parser.rb +9 -11
- data/lib/nydp/plugin.rb +88 -19
- data/lib/nydp/runner.rb +141 -23
- data/lib/nydp/symbol.rb +16 -26
- data/lib/nydp/symbol_lookup.rb +3 -2
- data/lib/nydp/tokeniser.rb +1 -1
- data/lib/nydp/truth.rb +2 -37
- data/lib/nydp/version.rb +1 -1
- data/lib/nydp.rb +33 -44
- data/nydp.gemspec +2 -1
- data/spec/date_spec.rb +26 -32
- data/spec/embedded_spec.rb +22 -22
- data/spec/error_spec.rb +12 -16
- data/spec/foreign_hash_spec.rb +21 -36
- data/spec/hash_non_hash_behaviour_spec.rb +12 -29
- data/spec/hash_spec.rb +36 -49
- data/spec/literal_spec.rb +6 -6
- data/spec/nydp_spec.rb +14 -14
- data/spec/pair_spec.rb +8 -8
- data/spec/parser_spec.rb +41 -37
- data/spec/rand_spec.rb +1 -4
- data/spec/spec_helper.rb +3 -3
- data/spec/string_atom_spec.rb +15 -16
- data/spec/symbol_spec.rb +27 -52
- data/spec/thread_local_spec.rb +23 -8
- data/spec/time_spec.rb +4 -10
- data/spec/tokeniser_spec.rb +10 -10
- metadata +25 -13
- data/lib/nydp/builtin/modulo.rb +0 -11
- data/lib/nydp/builtin/regexp.rb +0 -7
- data/lib/nydp/builtin/sqrt.rb +0 -7
- data/lib/nydp/builtin/string_pad_left.rb +0 -7
- data/lib/nydp/builtin/string_pad_right.rb +0 -7
- data/lib/nydp/hash.rb +0 -9
- data/lib/nydp/image_store.rb +0 -21
- 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
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
(
|
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
|
-
|
109
|
-
|
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
|
-
|
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
|
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
|
31
|
-
;; No attempt is made to avoid clobbering items. Use 'group-by if there are duplicate keys.
|
32
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
37
|
-
|
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
|
-
|
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
|
-
;;
|
25
|
-
;;
|
26
|
-
;;
|
26
|
+
;; @type@ must be a symbol
|
27
|
+
;; @context@ must be a symbol
|
28
|
+
;; @body@ is one or more nydp expressions.
|
27
29
|
;;
|
28
|
-
;;
|
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
|
-
;;
|
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
|
-
;;
|
41
|
-
;; (
|
42
|
-
;;
|
43
|
-
;;
|
44
|
-
;;
|
45
|
-
;;
|
46
|
-
;;
|
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)))
|