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.
- 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 +16 -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 +87 -26
- 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)))
|