nydp 0.4.2 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +44 -0
  3. data/lib/lisp/core-010-precompile.nydp +13 -16
  4. data/lib/lisp/core-012-utils.nydp +3 -2
  5. data/lib/lisp/core-015-documentation.nydp +54 -23
  6. data/lib/lisp/core-017-builtin-dox.nydp +14 -12
  7. data/lib/lisp/core-020-utils.nydp +5 -5
  8. data/lib/lisp/core-030-syntax.nydp +166 -72
  9. data/lib/lisp/core-035-flow-control.nydp +38 -11
  10. data/lib/lisp/core-037-list-utils.nydp +12 -0
  11. data/lib/lisp/core-039-module.nydp +24 -0
  12. data/lib/lisp/core-040-utils.nydp +32 -12
  13. data/lib/lisp/core-041-string-utils.nydp +25 -1
  14. data/lib/lisp/core-042-date-utils.nydp +21 -1
  15. data/lib/lisp/core-043-list-utils.nydp +96 -64
  16. data/lib/lisp/core-070-prefix-list.nydp +1 -1
  17. data/lib/lisp/core-080-pretty-print.nydp +57 -17
  18. data/lib/lisp/core-090-hook.nydp +35 -1
  19. data/lib/lisp/core-100-utils.nydp +82 -2
  20. data/lib/lisp/core-110-hash-utils.nydp +56 -2
  21. data/lib/lisp/core-120-settings.nydp +16 -5
  22. data/lib/lisp/core-130-validations.nydp +51 -0
  23. data/lib/lisp/core-900-benchmarking.nydp +78 -20
  24. data/lib/lisp/tests/accum-examples.nydp +28 -1
  25. data/lib/lisp/tests/aif-examples.nydp +8 -3
  26. data/lib/lisp/tests/andify-examples.nydp +7 -0
  27. data/lib/lisp/tests/at-syntax-examples.nydp +17 -0
  28. data/lib/lisp/tests/best-examples.nydp +9 -0
  29. data/lib/lisp/tests/builtin-tests.nydp +19 -0
  30. data/lib/lisp/tests/case-examples.nydp +14 -0
  31. data/lib/lisp/tests/date-examples.nydp +54 -1
  32. data/lib/lisp/tests/destructuring-examples.nydp +46 -14
  33. data/lib/lisp/tests/detect-examples.nydp +12 -0
  34. data/lib/lisp/tests/dp-examples.nydp +24 -0
  35. data/lib/lisp/tests/each-tests.nydp +5 -0
  36. data/lib/lisp/tests/empty-examples.nydp +1 -1
  37. data/lib/lisp/tests/error-tests.nydp +4 -4
  38. data/lib/lisp/tests/explain-mac-examples.nydp +1 -1
  39. data/lib/lisp/tests/filter-forms-examples.nydp +15 -0
  40. data/lib/lisp/tests/hash-examples.nydp +25 -1
  41. data/lib/lisp/tests/list-grep-examples.nydp +40 -0
  42. data/lib/lisp/tests/list-tests.nydp +58 -1
  43. data/lib/lisp/tests/map-hash-examples.nydp +11 -0
  44. data/lib/lisp/tests/module-examples.nydp +10 -0
  45. data/lib/lisp/tests/multi-assign-examples.nydp +6 -0
  46. data/lib/lisp/tests/parser-tests.nydp +25 -0
  47. data/lib/lisp/tests/pretty-print-tests.nydp +17 -14
  48. data/lib/lisp/tests/set-difference-examples.nydp +8 -0
  49. data/lib/lisp/tests/settings-examples.nydp +17 -1
  50. data/lib/lisp/tests/string-tests.nydp +70 -1
  51. data/lib/lisp/tests/syntax-tests.nydp +5 -1
  52. data/lib/lisp/tests/to-integer-examples.nydp +16 -0
  53. data/lib/lisp/tests/validation-examples.nydp +15 -0
  54. data/lib/nydp.rb +10 -3
  55. data/lib/nydp/assignment.rb +10 -3
  56. data/lib/nydp/builtin.rb +1 -1
  57. data/lib/nydp/builtin/abs.rb +8 -0
  58. data/lib/nydp/builtin/date.rb +15 -1
  59. data/lib/nydp/builtin/error.rb +1 -1
  60. data/lib/nydp/builtin/hash.rb +24 -1
  61. data/lib/nydp/builtin/inspect.rb +1 -1
  62. data/lib/nydp/builtin/plus.rb +10 -2
  63. data/lib/nydp/builtin/random_string.rb +2 -2
  64. data/lib/nydp/builtin/{car.rb → regexp.rb} +2 -2
  65. data/lib/nydp/builtin/ruby_wrap.rb +72 -0
  66. data/lib/nydp/builtin/string_match.rb +2 -2
  67. data/lib/nydp/builtin/string_pad_left.rb +7 -0
  68. data/lib/nydp/builtin/string_pad_right.rb +7 -0
  69. data/lib/nydp/builtin/string_replace.rb +3 -3
  70. data/lib/nydp/builtin/string_split.rb +4 -3
  71. data/lib/nydp/builtin/to_integer.rb +23 -0
  72. data/lib/nydp/builtin/to_string.rb +2 -9
  73. data/lib/nydp/builtin/type_of.rb +9 -6
  74. data/lib/nydp/closure.rb +0 -3
  75. data/lib/nydp/cond.rb +23 -1
  76. data/lib/nydp/context_symbol.rb +14 -6
  77. data/lib/nydp/core.rb +36 -28
  78. data/lib/nydp/core_ext.rb +21 -5
  79. data/lib/nydp/date.rb +26 -18
  80. data/lib/nydp/function_invocation.rb +34 -26
  81. data/lib/nydp/helper.rb +35 -3
  82. data/lib/nydp/interpreted_function.rb +68 -40
  83. data/lib/nydp/literal.rb +1 -1
  84. data/lib/nydp/pair.rb +22 -5
  85. data/lib/nydp/parser.rb +11 -7
  86. data/lib/nydp/string_atom.rb +3 -4
  87. data/lib/nydp/symbol_lookup.rb +7 -7
  88. data/lib/nydp/tokeniser.rb +2 -2
  89. data/lib/nydp/truth.rb +10 -10
  90. data/lib/nydp/version.rb +1 -1
  91. data/lib/nydp/vm.rb +7 -0
  92. data/nydp.gemspec +2 -4
  93. data/spec/date_spec.rb +93 -0
  94. data/spec/embedded_spec.rb +12 -12
  95. data/spec/foreign_hash_spec.rb +14 -2
  96. data/spec/hash_non_hash_behaviour_spec.rb +7 -7
  97. data/spec/hash_spec.rb +24 -2
  98. data/spec/nydp_spec.rb +14 -2
  99. data/spec/pair_spec.rb +3 -1
  100. data/spec/parser_spec.rb +31 -20
  101. data/spec/rand_spec.rb +3 -3
  102. data/spec/spec_helper.rb +10 -1
  103. metadata +24 -37
  104. data/lib/nydp/builtin/cdr.rb +0 -7
  105. data/lib/nydp/builtin/cons.rb +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 93571312813f5383bd47d92b298cd95b0239f07e
4
- data.tar.gz: 242349e0186b701a602e94cbfdba9a1adfb5d5bb
2
+ SHA256:
3
+ metadata.gz: adb6cc0345ed64c1e0e7ac99c06d9aa1328482e7d51d771cd77b844e1c5a6e14
4
+ data.tar.gz: 997c1f318ed518f3d2100699c1532db9a140f7109d6e4ddef6e68055466eb23a
5
5
  SHA512:
6
- metadata.gz: 47e4c03909bcc94e0440d35f6a8e4f3cf36241832a89042456d18d556c6f8a34953d10382e2b5c17df6d28a7c530f9347c505cb6cf66931b9dcf18b34c2df88d
7
- data.tar.gz: c899014050d46f2a093dec88a62f6a44ff70fffdd6bc66392338c33699f49c92e4ef0239d49e337685190757b318409d614a9319463dedd0bba1883526e78dc6
6
+ metadata.gz: 4a80109cc7bf73c177c6f89697e386a0a12d3645512bd14db2ed036711ef2c0b11d80fef67cff8dbc6f7bfed23d103220b9bc909aa9b34590323375db699c5e3
7
+ data.tar.gz: 25864fdef654329baef7d494a54b926f6dd236399f131e5943505752784713b03dc9e79640407c1df6af17ac380c483d82e2fae5e35fa3f3636b1226384eaa58
data/README.md CHANGED
@@ -55,6 +55,22 @@ answer = Nydp.apply_function ns, :question, :life, ["The Universe", and_also(eve
55
55
 
56
56
  You can maintain multiple `ns` instances without mutual interference. In other words, assigning global variables while one `ns` is in scope will not affect the values of variables in any other `ns` (unless you've specifically arranged it to be so by duplicating namespaces or some such sorcery).
57
57
 
58
+
59
+ #### Facing the Truth
60
+
61
+ In conditional statements, nil is false, anything else is true
62
+
63
+ ```lisp
64
+ (if) ;; same as (if nil)
65
+ (if a) ;; same as a
66
+ (if a b) ;; same as (if a b nil)
67
+ (if a b c) ;; if a is nil, return c, otherwise return b
68
+ (if a b c d) ;; same as (if a b (if c d))
69
+ (if a b c d e) ;; same as (if a b (if c d e))
70
+
71
+ ;; and so on
72
+ ```
73
+
58
74
  ## Different from Arc :
59
75
 
60
76
  #### 1. Macro-expansion runs in lisp
@@ -151,8 +167,36 @@ nydp > (map &lastname german-composers) ; ampersand-syntax creates a function
151
167
 
152
168
  As with all other syntax, you can of course override the `ampersand-syntax` macro to handle your special needs.
153
169
 
170
+ You can combine special syntaxes ("%td" comes from nydp-html gem)
171
+
172
+ ```lisp
173
+
174
+ nydp > (map %td:&lastname german-composers)
175
+
176
+ "<td>Bach</td><td>Beethoven</td><td>Wagner</td><td>Mozart</td>"
177
+
178
+ ```
179
+
180
+ So, @%td@ expands to @(percent-syntax || td)@, @&lastname@ to @(ampersand-syntax || lastname)@, and the whole @%td:&lastname@
181
+ to @(colon-syntax (percent-syntax || td) (ampersand-syntax || lastname))@. Luckily for you, there's a fine @colon-syntax@ macro
182
+ that knows how to build a function out of these bits and pieces.
183
+
184
+
154
185
  Look for `SYMBOL_OPERATORS` in [parser.rb](lib/nydp/parser.rb) to see which syntax is recognised and in which order. The order of these definitions defines special-syntax-operator precedence.
155
186
 
187
+ Any character that is not special syntax will be recognised as part of a symbol. At time of writing, this includes the plus sign, hyphen, and slash.
188
+
189
+ ```lisp
190
+
191
+ ;; nonsense code illustrating the use of certain
192
+ ;; characters as function and variable names
193
+ (def //-+ (x y z)
194
+ (let -*- (x y)
195
+ (if z (//-+ x y -*-) -*-)))
196
+
197
+ ```
198
+
199
+
156
200
  #### 3. Special list syntax
157
201
 
158
202
  The parser detects alternative list delimiters
@@ -1,17 +1,15 @@
1
- (assign mac-expand (fn (names macfn expr)
2
- (cond macfn
3
- (pre-compile-with names
4
- (apply macfn (cdr expr)))
5
- expr)))
1
+ (assign mac-expand
2
+ (fn (names macfn expr)
3
+ (cond macfn
4
+ (handle-error
5
+ (fn (errors)
6
+ (error "expanding" (inspect expr) "with" (inspect macfn)))
7
+ (fn ()
8
+ (pre-compile-with names (apply macfn (cdr expr)))))
9
+ expr)))
6
10
 
7
11
  (assign macs (hash))
8
12
 
9
- (assign pre-compile-expr
10
- (fn (names expr)
11
- (mac-expand names
12
- (hash-get names (car expr))
13
- expr)))
14
-
15
13
  (assign pre-compile-each
16
14
  (fn (names args)
17
15
  (cond args
@@ -33,7 +31,10 @@
33
31
  (cond (eq? (car arg) 'quote)
34
32
  arg
35
33
  (pre-compile-each names
36
- (pre-compile-expr names arg)))
34
+ (mac-expand names
35
+ (hash-get names
36
+ (car arg))
37
+ arg)))
37
38
  arg)))
38
39
 
39
40
  (assign pre-compile-debug
@@ -136,7 +137,3 @@
136
137
 
137
138
  (hash-set macs 'quasiquote
138
139
  (fn (arg) (qq-quasiquote arg 0)))
139
-
140
- (hash-set macs 'do
141
- (fn args
142
- `((fn nil ,@args))))
@@ -8,7 +8,7 @@
8
8
  (car args))
9
9
  nil)))
10
10
 
11
- (def map-helper-0 (f things lc _)
11
+ (def map-helper-0 (f things lc)
12
12
  (if (pair? things)
13
13
  (map-helper-0 f (cdr things) (cdr-set lc (cons (f (car things)))))
14
14
  things
@@ -30,8 +30,9 @@
30
30
  (def map (f things)
31
31
  (map-helper-1 f things (cons)))
32
32
 
33
+ ;; push 'v onto the value for 'k in 'h
34
+ ;; the hash-values of h will all be lists, in reverse order of consing
33
35
  (def hash-cons (h k v)
34
- ; push 'v onto the value for 'k in 'h
35
36
  (hash-set h k (cons v (hash-get h k))))
36
37
 
37
38
  (def rev (things last-cdr)
@@ -19,6 +19,12 @@
19
19
  acc)))
20
20
  form))
21
21
 
22
+ (hash-set macs 'do
23
+ (fn forms
24
+ (if (no (cdr forms))
25
+ (car forms)
26
+ `((fn nil ,@forms)))))
27
+
22
28
  ((fn (this-chapter-name chapters chapter-new chapter-build chapter-add-to-chapter)
23
29
  (assign chapters (hash))
24
30
 
@@ -64,7 +70,7 @@
64
70
  (assign this-script nil)
65
71
  (assign this-plugin "Nydp Core")
66
72
 
67
- ((fn (dox examples chapters types dox-new dox-build)
73
+ ((fn (dox examples chapters types types-chapters dox-new dox-build)
68
74
  (def dox-build (hsh name what texts args src chapters)
69
75
  (hash-set hsh 'name name )
70
76
  (hash-set hsh 'what what )
@@ -79,16 +85,20 @@
79
85
  (def dox-new (item)
80
86
  (hash-cons dox (hash-get item 'name) item)
81
87
  (hash-cons types (hash-get item 'what) item)
82
- (dox-add-to-chapters item (hash-get item 'chapters)))
88
+ (dox-add-to-chapters item (hash-get item 'what) (hash-get item 'chapters) (hash)))
83
89
 
84
90
  (def dox-add-doc (name what texts args src chapters more)
85
91
  (cond (no (privately))
86
92
  (dox-new (dox-build (if more more (hash)) name what texts args src chapters))))
87
93
 
88
- (def dox-add-to-chapters (item chapters)
94
+ (def dox-add-to-chapters (item type chapters already)
89
95
  (cond chapters
90
- (do (chapter-add-item item (car chapters))
91
- (dox-add-to-chapters item (cdr chapters)))
96
+ (cond (no (hash-get already (car chapters)))
97
+ (do (hash-set already (car chapters) t)
98
+ (chapter-add-item item (car chapters))
99
+ (hash-cons types-chapters (inspect (cons type (car chapters))) item)
100
+ (dox-add-to-chapters item type (cdr chapters) already))
101
+ item)
92
102
  item))
93
103
 
94
104
  (def dox-add-examples (name example-exprs)
@@ -102,6 +112,11 @@
102
112
  (def dox-types () (hash-keys types))
103
113
  (def dox-items-by-type (type) (hash-get types type))
104
114
 
115
+ (def get-types-chapters () types-chapters)
116
+
117
+ (def dox-items-by-type-and-chapter (dox-type chapter)
118
+ (hash-get types-chapters (inspect (cons dox-type chapter))))
119
+
105
120
  (def dox-get-attr (name attr)
106
121
  (cond (dox? name)
107
122
  (hash-get (car (dox-lookup name)) attr)))
@@ -111,7 +126,7 @@
111
126
  (def dox-examples (name) (hash-get examples name ))
112
127
  (def dox-args (name) (dox-get-attr name 'args ))
113
128
  (def dox-example-names () (hash-keys examples )))
114
- (hash) (hash) (hash) (hash) nil)
129
+ (hash) (hash) (hash) (hash) (hash) nil)
115
130
 
116
131
  (def plugin-start (name) (assign this-plugin name) (chapter-end))
117
132
  (def plugin-end (name) (assign this-plugin nil ) (chapter-end))
@@ -128,9 +143,9 @@
128
143
  (cond (eq? event 'script-end)
129
144
  (script-end name))))))
130
145
 
146
+ ;; if the car of 'form is a key of 'hsh, add the cdr of 'form to the value of the key in 'hsh
147
+ ;; otherwise add the form to the list whose key is nil
131
148
  (def filter-form (hsh form)
132
- ; if the car of 'form is a key of 'hsh, add the cdr of 'form to the value of the key in 'hsh
133
- ; otherwise add the form to the list whose key is nil
134
149
  (cond (cond (pair? form)
135
150
  (hash-key? hsh (car form)))
136
151
  (hash-cons hsh (car form) (cdr form))
@@ -149,31 +164,43 @@
149
164
  (def rev-values (hsh)
150
165
  (rev-value-keys (hash-keys hsh) hsh (hash)))
151
166
 
167
+ ;; group forms by their first element, if the first element
168
+ ;; is already a key in hsh, collect all other elements under key nil
152
169
  (def filter-forms (hsh forms)
153
- ; group forms by their first element, if the first element
154
- ; is already a key in hsh, collect all other elements under key nil
155
170
  (cond forms
156
171
  (filter-forms (filter-form hsh (car forms)) (cdr forms))
157
172
  (rev-values hsh)))
158
173
 
159
- (def build-def-hash (hsh)
160
- (hash-set hsh 'comment nil)
161
- (hash-set hsh 'chapter nil)
162
- hsh)
174
+ (def filter-remove (key forms keyforms otherforms)
175
+ (cond forms
176
+ (cond (cond (pair? forms) (cond (pair? (car forms)) (eq? key (caar forms))))
177
+ (filter-remove key (cdr forms) (cons (car forms) keyforms) otherforms)
178
+ (filter-remove key (cdr forms) keyforms (cons (car forms) otherforms)))
179
+ (list (rev keyforms) (rev otherforms))))
163
180
 
181
+ (assign DEF-FORMS '(comment chapter))
182
+
183
+ (def build-def-hash ()
184
+ ((fn (h)
185
+ (map (fn (k) (hash-set h k nil)) DEF-FORMS)
186
+ h)
187
+ (hash)))
188
+
189
+ (def dox-build-def-name (name) name)
190
+
191
+ ;; used internally by 'mac
164
192
  (def define-mac-expr (name args body-forms)
165
- ; used internally by 'mac
166
193
  `(do (hash-set macs ',name (fun ,args ,@(hash-get body-forms nil)))
167
- (dox-add-doc ',name
194
+ (dox-add-doc ',(dox-build-def-name name)
168
195
  'mac
169
- ',(map car (hash-get body-forms 'comment))
196
+ ',(+ (fetch-and-clear-comments) (map car (hash-get body-forms 'comment)))
170
197
  ',args
171
198
  '(mac ,name ,args ,@(hash-get body-forms nil))
172
199
  ',(map car (hash-get body-forms 'chapter)))))
173
200
 
174
201
  (hash-set macs 'mac
175
202
  (fn (name args . body)
176
- (define-mac-expr name args (filter-forms (build-def-hash (hash)) body))))
203
+ (define-mac-expr name args (filter-forms (build-def-hash) body))))
177
204
 
178
205
  (dox-add-doc 'mac
179
206
  'mac
@@ -189,22 +216,26 @@
189
216
  '`((fn nil ,@args))
190
217
  '(nydp-core))
191
218
 
219
+ ;; override later to use '= instead of 'assign, giving us hash-assignment and other goodies for free
192
220
  (mac def-assign args `(assign ,@args))
193
221
 
222
+ ;; used internally by 'def
194
223
  (def define-def-expr (name args body-forms)
195
- ; used internally by 'def
196
- `(do (def-assign ,name (fun ,args ,@(filter-comments (hash-get body-forms nil))))
197
- (dox-add-doc ',name
224
+ `(do (def-assign ,name
225
+ ((fn (self-name)
226
+ (fun ,args ,@(filter-comments (hash-get body-forms nil))))
227
+ ',name))
228
+ (dox-add-doc ',(dox-build-def-name name)
198
229
  'def
199
230
  ',(+ (fetch-and-clear-comments) (map car (hash-get body-forms 'comment)))
200
231
  ',args
201
232
  '(def ,name ,args ,@(hash-get body-forms nil))
202
233
  ',(map car (hash-get body-forms 'chapter)))))
203
234
 
235
+ ;; define a new function in the global namespace
204
236
  (mac def (name args . body)
205
- ; define a new function in the global namespace
206
237
  (chapter nydp-core)
207
- (define-def-expr name args (filter-forms (build-def-hash (hash)) body)))
238
+ (define-def-expr name args (filter-forms (build-def-hash) body)))
208
239
 
209
240
  (mac comment (txt)
210
241
  (assign comments (cons txt comments))
@@ -1,6 +1,7 @@
1
1
  (dox-add-doc 'cons 'def '("with args a and b, returns a new cons cell, (a . b)") '(a b) nil '(list-manipulation))
2
2
  (dox-add-doc 'car 'def '("with args a, where a is a cons cell (x . y), return x." "Commonly used to get the first element of a list") '(a) nil '(list-manipulation))
3
3
  (dox-add-doc 'cdr 'def '("with args a, where a is a cons cell (x . y), return y." "Commonly used to get contents of a list, excluding the first element") '(a) nil '(list-manipulation))
4
+ (dox-add-doc 'log 'def '("write arg to Nydp.logger ; be sure to assign Nydp.logger first!") '(arg) nil '(nydp-core))
4
5
  (dox-add-doc '+ 'def '("with rest-args things, return the sum of the elements of things." "Will also increment dates and concatenate strings and lists") 'things nil '(math))
5
6
  (dox-add-doc '- 'def '("return the result of subtracting all other args from the first arg." "(- a b c d) is equivalent to (- a (+ b c d))") 'things nil '(math))
6
7
  (dox-add-doc '* 'def '("with rest-args things, return the product of the elements of things.") 'things nil '(math))
@@ -35,21 +36,22 @@
35
36
  (dox-add-doc 'pair? 'def '("t if arg is a cons cell or (equivalently) the start of a list") '(arg) nil '(nydp-core))
36
37
  (dox-add-doc 'hash? 'def '("t if arg is a hash") '(arg) nil '(nydp-core))
37
38
  (dox-add-doc 'sym? 'def '("t if arg is a symbol, nil otherwise") '(arg) nil '(nydp-core))
38
- (dox-add-doc 'ensuring 'def '("execute 'tricky-f, then 'ensure-f afterwards"
39
- "'ensure-f will always be executed, even if there is an error in 'tricky-f"
40
- "returns the return value of 'tricky-f") '(ensure-f tricky-f) nil '(flow-control))
39
+ (dox-add-doc 'ensuring 'def '("execute 'try-f, then 'ensure-f afterwards"
40
+ "'ensure-f will always be executed, even if there is an error in 'try-f"
41
+ "returns the return value of 'try-f. Similar to try/finally in java, or begin/ensure in ruby.") '(ensure-f try-f) nil '(flow-control))
41
42
  (dox-add-doc 'inspect 'def '("return a string representing 'arg, potentially (but not always) in a way that can be parsed back in to get the original object") '(arg) nil '(nydp-core))
42
43
  (dox-add-doc 'comment 'def '("does nothing at all." "Intercepted inside 'def and 'mac and used for documentation") '(arg) nil '(nydp-core))
43
44
  (dox-add-doc 'parse-in-string 'def '("parse the given string assuming a string-open delimiter has already been encountered") '(str) nil '(nydp-core))
44
45
  (dox-add-doc 'rand 'def '("return a random number ; with no args, between 0 and 1"
45
46
  "with 1 arg, an integer less than arg"
46
- "with two args, an integer >= arg 0 and < arg 1") 'args nil '(math))
47
- (dox-add-doc 'random-string 'def '("return a random string of length 'len (default 10)") '(len) nil '(string-manipulation))
48
- (dox-add-doc 'to-string 'def '("return a human-readable string representation of 'arg") '(arg) nil '(string-manipulation))
49
- (dox-add-doc 'string-length 'def '("return the length of 'arg") '(arg) nil '(string-manipulation))
50
- (dox-add-doc 'string-replace 'def '("replace 'pattern with 'insert in 'str") '(pattern insert str) nil '(string-manipulation))
51
- (dox-add-doc 'string-split 'def '("split 'str delimited by 'delim") '(str delim) nil '(string-manipulation))
52
- (dox-add-doc 'string-match 'def '("if 'str matches 'pattern, return hash with keys 'match and 'captures ; otherwise nil") '(str pattern) nil '(string-manipulation))
47
+ "with two args, an integer >= arg 0 and < arg 1") 'args nil '(math))
48
+ (dox-add-doc 'random-string 'def '("return a random string of length 'len (default 10)") '(len) nil '(string-manipulation))
49
+ (dox-add-doc 'regexp 'def '("return a regular expression compiled from the given string") '(str) nil '(string-manipulation))
50
+ (dox-add-doc 'to-string 'def '("return a human-readable string representation of 'arg") '(arg) nil '(string-manipulation))
51
+ (dox-add-doc 'string-length 'def '("return the length of 'arg") '(arg) nil '(string-manipulation))
52
+ (dox-add-doc 'string-replace 'def '("replace 'pattern with 'replacement in 'str") '(pattern replacement str) nil '(string-manipulation))
53
+ (dox-add-doc 'string-split 'def '("split 'str delimited by 'delim") '(str delim) nil '(string-manipulation))
54
+ (dox-add-doc 'string-match 'def '("if 'str matches 'pattern, return hash with keys 'match and 'captures ; otherwise nil") '(str pattern) nil '(string-manipulation))
53
55
  (dox-add-doc 'time 'def '("with no args, return the current time."
54
56
  "With one arg, if 'arg-0 is a number, return the current time plus 'arg-0 seconds."
55
57
  "With one arg, if 'arg-0 is a date, return the time at the beginning of the given date."
@@ -57,9 +59,9 @@
57
59
  "With two args, 'arg-0 must be a time."
58
60
  "If 'arg-1 is a number, return 'arg-0 plus 'arg-1 seconds as a time object."
59
61
  "If 'arg-1 is a time, return the number of seconds between the two (- 'arg-0 arg-1)."
60
- "Otherwise, expect 3, 4, 5, or 6 args, to construct a time from"
62
+ "Otherwise, expect 3 or more args, to construct a time from"
61
63
  "year, month, date, hours, minutes, seconds, milliseconds, reading arguments in that order,"
62
- "where hours, minutes, seconds, and milliseconds are optional") 'args nil '(date-time))
64
+ "where hours, minutes, seconds, and milliseconds are optional") 'args nil '(date-time))
63
65
  (dox-add-doc 'thread-locals 'def '("return a hash bound to the current thread") nil nil '(nydp-core))
64
66
  (dox-add-doc 'type-of 'def '("return a symbol for the type of 'arg") '(arg) nil '(nydp-core))
65
67
  (dox-add-doc 'eq? 'def '("return 't if 'arg-0 and 'arg-1 are equal, nil otherwise") '(arg-0 arg-1) nil '(nydp-core))
@@ -1,4 +1,4 @@
1
- (assign script-name "core-012-utils.nydp")
1
+ (assign script-name "core-020-utils.nydp")
2
2
 
3
3
  (dox-add-doc 'if
4
4
  'mac
@@ -43,12 +43,12 @@
43
43
  '(hash-set h k (cons v (hash-get h k)))
44
44
  '(hash-manipulation))
45
45
 
46
+ ;; equivalent to (join-str "~prefix~joint~(car things)" joint (cdr things)) - except
47
+ ;; 'string-pieces hasn't been defined yet, and if it were, it would be defined in terms of
48
+ ;; 'join-str, so it would be circular.
49
+ ;; see 'joinstr for a more powerful and easier-to-use implementation of the same idea
46
50
  (def join-str (prefix joint things)
47
51
  (chapter string-manipulation)
48
- ; equivalent to (join-str "~prefix~joint~(car things)" joint (cdr things)) - except
49
- ; 'string-pieces hasn't been defined yet, and if it were, it would be defined in terms of
50
- ; 'join-str, so it would be circular.
51
- ; see 'joinstr for a more powerful and easier-to-use implementation of the same idea
52
52
  (if things
53
53
  (join-str (+ (to-string prefix)
54
54
  joint
@@ -21,16 +21,26 @@ scoping, assignment, anonymous functions and more...")
21
21
  (apply orf
22
22
  (cdr args)))))
23
23
 
24
+ ; returns true if 'things is a list and the first item of the
25
+ ; list is the given object
24
26
  (def caris (obj things)
25
- ; returns true if 'things is a list and the first item of the
26
- ; list is the given object
27
27
  (and (pair? things)
28
- (eq? (car things) obj)))
28
+ (eq? (car things) obj)))
29
29
 
30
+ ; evaluate 'body if 'arg is nil
30
31
  (mac unless (arg . body)
31
- ; evaluate 'body if 'arg is nil
32
32
  `(if (no ,arg) (do ,@body)))
33
33
 
34
+ ; looks up a key in @
35
+ ; assumes local lexical context has defined a hash called '@
36
+ (mac prefix-at-syntax (name . names)
37
+ `(hash-get @ ',name))
38
+
39
+ (mac at-syntax names
40
+ (if (eq? (car names) '||)
41
+ `(prefix-at-syntax ,@(cdr names))
42
+ (error "unknown at-syntax: expected prefix-syntax (eg @name), got ~(join-str (car names) "@" (cdr names))")))
43
+
34
44
  (def expand-colon-syntax (names)
35
45
  (if (no (cdr names))
36
46
  `(apply ,(car names) args)
@@ -84,13 +94,17 @@ scoping, assignment, anonymous functions and more...")
84
94
  (cons (list (car things) (cadr things))
85
95
  (pairs (cddr things)))))
86
96
 
97
+ ;; like 'let, but creates and assigns multiple local variables.
98
+ ;; for example, "(with (a 1 b 2) (+ a b))" returns 3
87
99
  (mac with (parms . body)
88
- `((fun ,(map car (pairs parms))
89
- ,@body)
90
- ,@(map cadr (pairs parms))))
100
+ `((fun ,(map car (pairs parms))
101
+ ,@body)
102
+ ,@(map cadr (pairs parms))))
91
103
 
104
+ ;; create a lexical scope
105
+ ;; where val is assigned to var, execute 'body in that scope
92
106
  (mac let (var val . body)
93
- `(with (,var ,val) ,@body))
107
+ `((fun (,var) ,@body) ,val))
94
108
 
95
109
  (mac rfn (name parms . body)
96
110
  ; creates a named, locally-scoped function
@@ -105,17 +119,28 @@ scoping, assignment, anonymous functions and more...")
105
119
  ; same as 'rfn, but using the name 'self
106
120
  `(rfn self ,parms ,@body))
107
121
 
122
+ ;; a mix of rfn and with; creates a locally-scoped named function with
123
+ ;; the given parameter names, and invokes it with the given parameter
124
+ ;; values. It is possible to reference the function by its name from
125
+ ;; within the function (to pass as an argument or for recursive
126
+ ;; invocation)
108
127
  (mac rfnwith (name params . body)
109
- ; a mix of rfn and with; creates a locally-scoped named function with
110
- ; the given parameter names, and invokes it with the given parameter
111
- ; values. It is possible to reference the function by its name from
112
- ; within the function (to pass as an argument or for recursive
113
- ; invocation)
114
128
  (let ppairs (pairs params)
115
129
  `(let ,name nil
116
130
  (assign ,name (fun ,(map car ppairs) ,@body))
117
131
  (,name ,@(map cadr ppairs)))))
118
132
 
133
+ ;; (andify a b c) is equivalent to
134
+ ;; (fn args (and (apply a args) (apply b args) (apply c args)))
135
+ ;; or more simply
136
+ ;; (fn (x) (and (a x) (b x) (c x)))
137
+ ;; note: alias as 'andf ??
138
+ (def andify args
139
+ (fn args2 (rfnwith self (ands args)
140
+ (if ands (if (apply (car ands) args2)
141
+ (self (cdr ands)))
142
+ t))))
143
+
119
144
  (let uniq-counter 0
120
145
  (def uniq (prefix)
121
146
  (assign uniq-counter (+ uniq-counter 1))
@@ -123,9 +148,9 @@ scoping, assignment, anonymous functions and more...")
123
148
  (def reset-uniq-counter ()
124
149
  (assign uniq-counter 0)))
125
150
 
151
+ ;; creates a lexical scope with a unique symbol assigned to
152
+ ;; each variable in 'vars ; executes the 'body.
126
153
  (mac w/uniq (vars . body)
127
- ; creates a lexical scope with a unique symbol assigned to
128
- ; each variable in 'vars ; executes the 'body.
129
154
  (if (pair? vars)
130
155
  `(with ,(apply + (map (fn (n) `(,n (uniq ',n))) vars))
131
156
  ,@body)
@@ -194,37 +219,79 @@ scoping, assignment, anonymous functions and more...")
194
219
  (and (pair? name)
195
220
  (caris 'ampersand-syntax (car name))))
196
221
 
222
+ ;; (= (&key (expr)) (val))
223
+ ;; (= ((ampersand-syntax key) (expr)) (val))
224
+ ;; 'place is ((ampersand-syntax || key) (expr))
225
+ ;; we need (hash-set (expr) 'key (val))
226
+ ;; however,
227
+ ;; (= (&key.subkey (expr)) (val))
228
+ ;; 'place is ((ampersand-syntax || (dot-syntax key subkey)) (expr))
229
+ ;; we need (hash-set (hash-get (expr) 'key) 'subkey (val))
197
230
  (def ampersand-expression-assignment (place value)
198
- ; (= (&key (expr)) (val))
199
- ; (= ((ampersand-syntax key) (expr)) (val))
200
- ; 'place is ((ampersand-syntax || key) (expr))
201
- ; we need (hash-set (expr) 'key (val))
202
- ; however,
203
- ; (= (&key.subkey (expr)) (val))
204
- ; 'place is ((ampersand-syntax || (dot-syntax key subkey)) (expr))
205
- ; we need (hash-set (hash-get (expr) 'key) 'subkey (val))
206
- (with (k (cadr:cdar place)
207
- hsh (cadr place))
208
- (if (caris 'dot-syntax k)
209
- (dot-syntax-assignment (cons hsh (cdr k)) value)
210
- `(hash-set ,hsh ',k ,value))))
211
-
212
- (mac = (name value)
213
- ; generic assignment which unlike builtin 'assign, knows how to assign
214
- ; to hash keys
215
- ; (= (hash-get (expr) 'key) (val) => (hash-set (expr) 'key (val))
216
- ; (= h.k (val)) => (hash-set h 'k (val))
217
- ; (= h.j.k (val)) => (hash-set (hash-get h 'j) 'k (val))
218
- ; (= (&key (expr)) (val)) => (hash-set (expr) 'key (val))
219
- ; (= (&j.k (expr)) (val)) => (hash-set (hash-get (expr) 'j) 'k (val))
220
- (if (isa 'symbol name)
221
- `(assign ,name ,value)
222
- (caris 'dot-syntax name)
223
- (dot-syntax-assignment (cdr name) value)
224
- (caris 'hash-get name)
225
- (hash-get-assignment (cdr name) value)
226
- (ampersand-expression? name)
227
- (ampersand-expression-assignment name value)))
231
+ (let k (cadr:cdar place)
232
+ (let hsh (cadr place)
233
+ (if (caris 'dot-syntax k)
234
+ (dot-syntax-assignment (cons hsh (cdr k)) value)
235
+ `(hash-set ,hsh ',k ,value)))))
236
+
237
+ ;; used internally by 'destructuring-assign
238
+ (def destructuring-assigns (names values acc)
239
+ (if names
240
+ (if (pair? names)
241
+ (destructuring-assigns
242
+ (cdr names)
243
+ `(cdr ,values)
244
+ (cons `(= ,(car names) (car ,values)) acc))
245
+ (cons `(= ,names ,values) acc))
246
+ (rev acc)))
247
+
248
+ ;; used internally by 'assign-expr
249
+ (def destructuring-assign (name value)
250
+ (w/uniq destructuring-assign
251
+ `(let ,destructuring-assign ,value
252
+ ,@(destructuring-assigns name destructuring-assign))))
253
+
254
+ ;; used internally by '= macro
255
+ (def assign-expr (nv)
256
+ (let name (car nv)
257
+ (let value (cadr nv)
258
+ (if (isa 'symbol name)
259
+ `(assign ,name ,value)
260
+ (caris 'dot-syntax name)
261
+ (dot-syntax-assignment (cdr name) value)
262
+ (caris 'hash-get name)
263
+ (hash-get-assignment (cdr name) value)
264
+ (ampersand-expression? name)
265
+ (ampersand-expression-assignment name value)
266
+ (caris 'at-syntax name)
267
+ `(hash-set @ ',(caddr name) ,value)
268
+ (pair? name)
269
+ (destructuring-assign name value)
270
+ (error "unknown assignment to place: " (inspect name))))))
271
+
272
+ ;; generic assignment which unlike builtin 'assign, knows how to assign
273
+ ;; to hash keys
274
+ ;; (= (hash-get (expr) 'key) (val) => (hash-set (expr) 'key (val))
275
+ ;; (= h.k (val)) => (hash-set h 'k (val))
276
+ ;; (= h.j.k (val)) => (hash-set (hash-get h 'j) 'k (val))
277
+ ;; (= (&key (expr)) (val)) => (hash-set (expr) 'key (val))
278
+ ;; (= (&j.k (expr)) (val)) => (hash-set (hash-get (expr) 'j) 'k (val))
279
+ (mac = assignments
280
+ `(do ,@(map assign-expr (pairs assignments))))
281
+
282
+ ;; like 'let, but creates and assigns multiple local variables.
283
+ ;; for example, "(with (a 1 b 2) (+ a b))" returns 3
284
+ ;;
285
+ ;; later variables can references earlier ones:
286
+ ;; (with (a 1 b 2 c (+ a b)) (list a b c)) ;; returns (1 2 3)
287
+ (mac with (assignments . body)
288
+ `((fun ,(map car (pairs assignments))
289
+ (= ,@assignments)
290
+ ,@body) nil))
291
+
292
+ ;; quiet assignment ; like =, but expression returns nil
293
+ (mac #= (name value)
294
+ `(do (= ,name ,value) nil))
228
295
 
229
296
  ; increment the value at 'place by 'inc (default 1)
230
297
  (mac ++ (place inc) `(= ,place (+ ,place ,(or inc 1))))
@@ -283,16 +350,25 @@ scoping, assignment, anonymous functions and more...")
283
350
  ; (eg hash), the mutated value will be returned. See also 'returnlet
284
351
  (mac returning (val . body) (w/uniq retval `(returnlet ,retval ,val ,@body)))
285
352
 
286
- (mac aif (expr . body)
287
- ; like if, except the value of each condition is locally bound to the variable 'it
288
- ; eg (aif (find thing) (show it))
289
- ; source: arc.arc
290
- `(let it ,expr
291
- (if it
353
+ (mac ifv (var expr . body)
354
+ `(let ,var ,expr
355
+ (if ,var
292
356
  ,@(if (cddr body)
293
- `(,(car body) (aif ,@(cdr body)))
357
+ `(,(car body) (ifv ,var ,@(cdr body)))
294
358
  body))))
295
359
 
360
+ ; like if, except the value of each condition is locally bound to the variable 'it
361
+ ; eg (aif (find thing) (show it))
362
+ ; source: arc.arc
363
+ (mac aif (expr . body)
364
+ `(ifv it ,expr ,@body))
365
+
366
+ ;; returns the n-th item in the list 'things
367
+ (def nth (n things)
368
+ (if (eq? n 0)
369
+ (car things)
370
+ (nth (- n 1) (cdr things))))
371
+
296
372
  (def destructure/with (var args n)
297
373
  ; provides the argument expression to 'with when
298
374
  ; destructuring arguments are present in a 'fun definition
@@ -301,34 +377,52 @@ scoping, assignment, anonymous functions and more...")
301
377
  args
302
378
  `(,args (nthcdr ,n ,var))))
303
379
 
304
- (def destructure/build (given-args new-args body)
305
- ; used internally by 'fun
380
+ ;; issue a warning if any arg name is the name of a macro
381
+ (def fun/approve-arg-names (orig args body)
382
+ (if (pair? args)
383
+ (do (fun/approve-arg-names orig (car args) body)
384
+ (fun/approve-arg-names orig (cdr args) body))
385
+ args
386
+ (if (hash-get macs args)
387
+ (warnings/new 'arg-shadows-macro "arg " args " shadows macro " args " in arg list " orig " and body " body))))
388
+
389
+ ;; used internally by 'fun
390
+ (def destructure/build (given-args new-args body next)
306
391
  (if (pair? given-args)
307
392
  (if (sym? (car given-args))
308
393
  (destructure/build (cdr given-args)
309
394
  (cons (car given-args) new-args)
310
- body)
395
+ body
396
+ next)
311
397
  (w/uniq destructure
312
398
  (destructure/build (cdr given-args)
313
399
  (cons destructure new-args)
314
- `((with ,(destructure/with destructure (car given-args) 0) ,@body)))))
315
- `(fn ,(rev new-args given-args) ,@body)))
316
-
317
-
318
- (def fun/approve-arg-names (orig args)
319
- (if (pair? args)
320
- (do (fun/approve-arg-names orig (car args))
321
- (fun/approve-arg-names orig (cdr args)))
322
- args
323
- (if (hash-get macs args)
324
- (warnings/new 'arg-shadows-macro "arg " args " shadows macro " args " in arg list " orig))))
325
-
400
+ `((with ,(destructure/with destructure (car given-args) 0) ,@body))
401
+ next)))
402
+ (next (rev new-args given-args) body)))
403
+
404
+ (def fun/destructuring-args (args body next)
405
+ (fun/approve-arg-names args args body)
406
+ (destructure/build args nil body next))
407
+
408
+ (assign fun/expanders
409
+ (list
410
+ (cons 'destructuring-args fun/destructuring-args)
411
+ (cons 'core-builder (fn (args body next) `(fn ,args ,@body)))))
412
+
413
+ (def fun/expand (args body expanders)
414
+ (if expanders
415
+ ((cdar expanders)
416
+ args
417
+ body
418
+ (fn (a b)
419
+ (fun/expand a b (cdr expanders))))))
420
+
421
+ ;; build a 'fn form, changing 'args and 'body to
422
+ ;; properly handle any destructuring args if present
326
423
  (mac fun (args . body)
327
- ; build a 'fn form, changing 'args and 'body to
328
- ; properly handle any destructuring args if present
329
- (fun/approve-arg-names args args)
330
- (destructure/build args nil body))
424
+ (fun/expand args body fun/expanders))
331
425
 
332
- ; assign (f place) to place
426
+ ;; assign (f place) to place
333
427
  (mac zap (f place . args)
334
428
  `(= ,place (,f ,place ,@args)))