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
@@ -16,7 +16,7 @@
16
16
  `(cond ,(car args) ,(cadr args)))
17
17
  (car args))
18
18
  nil)
19
- '(flow-control))
19
+ (dox/attrs (flow-control)))
20
20
 
21
21
  (dox-add-doc 'map
22
22
  'def
@@ -27,21 +27,23 @@
27
27
  (cons (f (car things)) (map f (cdr things)))
28
28
  things
29
29
  (f things))
30
- '(list-manipulation))
30
+ (dox/attrs (list-manipulation)))
31
31
 
32
32
  (dox-add-doc 'rev
33
33
  'def
34
- '("returns 'things in reverse order")
34
+ '("@things@ - the list to be reversed"
35
+ "@last-cdr@ - (normally nil) - an item (atom, list, nil, anything) "
36
+ "to be consed to the end of the reversed list.")
35
37
  '(things)
36
- '(if (pair? things) (rev (cdr things) (cons (car things) last-cdr)) last-cdr)
37
- '(list-manipulation))
38
+ '(def rev (things last-cdr) (loop (pair? things) ((fn nil (assign last-cdr (cons (car things) last-cdr)) (assign things (cdr things))))) last-cdr)
39
+ (dox/attrs (list-manipulation)))
38
40
 
39
41
  (dox-add-doc 'hash-cons
40
42
  'def
41
43
  '("push 'v onto the value for 'k in 'h")
42
44
  '(h k v)
43
45
  '(hash-set h k (cons v (hash-get h k)))
44
- '(hash-manipulation))
46
+ (dox/attrs (hash-manipulation)))
45
47
 
46
48
  ;; equivalent to (join-str "~prefix~joint~(car things)" joint (cdr things)) - except
47
49
  ;; 'string-pieces hasn't been defined yet, and if it were, it would be defined in terms of
@@ -56,3 +58,28 @@
56
58
  joint
57
59
  (cdr things))
58
60
  prefix))
61
+
62
+ ;; returns the 'thing if the 'thing is present? ; otherwise nil
63
+ ;; useful for compressing forms like
64
+ ;;
65
+ ;; (let thing (get-the-thing)
66
+ ;; (if (present? thing)
67
+ ;; (do-thing-stuff thing)))
68
+ ;;
69
+ ;; down to
70
+ ;;
71
+ ;; (aif (nb thing) (do-thing-stuff it))
72
+ ;;
73
+ ;;
74
+ ;; or, alternatively, compressing
75
+ ;;
76
+ ;; (let thing (get-the-thing)
77
+ ;; (if (present? thing)
78
+ ;; thing
79
+ ;; (get-the-other-thing)))
80
+ ;;
81
+ ;; down to
82
+ ;;
83
+ ;; (or (nb (get-the-thing)) (get-the-other-thing))
84
+ ;;
85
+ (def nb (thing) (if (present? thing) thing nil))
@@ -6,8 +6,8 @@
6
6
 
7
7
  (warnings/clear)
8
8
 
9
+ ;; apply f to each stored warning. For example, (warnings p) to print warnings to console
9
10
  (def warnings (f)
10
- ; apply f to each stored warning. For example, (warnings p) to print warnings to console
11
11
  (mapply f warnings))
12
12
 
13
13
  (def warnings/new (kind . info)
@@ -106,17 +106,17 @@ scoping, assignment, anonymous functions and more...")
106
106
  (mac let (var val . body)
107
107
  `((fun (,var) ,@body) ,val))
108
108
 
109
+ ;; creates a named, locally-scoped function
110
+ ;; with the given parameter names. It is possible
111
+ ;; to reference the function by its name from within
112
+ ;; the function (to pass as an argument or for
113
+ ;; recursive invocation)
109
114
  (mac rfn (name parms . body)
110
- ; creates a named, locally-scoped function
111
- ; with the given parameter names. It is possible
112
- ; to reference the function by its name from within
113
- ; the function (to pass as an argument or for
114
- ; recursive invocation)
115
115
  `(let ,name nil
116
116
  (assign ,name (fn ,parms ,@body))))
117
117
 
118
+ ;; same as @rfn@, but using the name @self@
118
119
  (mac afn (parms . body)
119
- ; same as 'rfn, but using the name 'self
120
120
  `(rfn self ,parms ,@body))
121
121
 
122
122
  ;; a mix of rfn and with; creates a locally-scoped named function with
@@ -130,23 +130,17 @@ scoping, assignment, anonymous functions and more...")
130
130
  (assign ,name (fun ,(map car ppairs) ,@body))
131
131
  (,name ,@(map cadr ppairs)))))
132
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
-
144
- (let uniq-counter 0
145
- (def uniq (prefix)
146
- (assign uniq-counter (+ uniq-counter 1))
147
- (sym (join-str prefix "-" (list uniq-counter))))
148
- (def reset-uniq-counter ()
149
- (assign uniq-counter 0)))
133
+ ;; increments a counter and appends it to prefix
134
+ ;; return value should be unique until @unique-counter@ is reset
135
+ ;; @unique-counter@ is reset before compiling a new expression, so under normal circumstances,
136
+ ;; the returned value is unique within an expression, but not across the entire system.
137
+ ;; Don't use these values for global variables!
138
+ (def uniq (prefix)
139
+ (assign uniq-counter (+ uniq-counter 1))
140
+ (sym (join-str prefix "-" (list uniq-counter))))
141
+
142
+ (def reset-uniq-counter ()
143
+ (assign uniq-counter 0))
150
144
 
151
145
  ;; creates a lexical scope with a unique symbol assigned to
152
146
  ;; each variable in 'vars ; executes the 'body.
@@ -156,9 +150,19 @@ scoping, assignment, anonymous functions and more...")
156
150
  ,@body)
157
151
  `(let ,vars (uniq ',vars) ,@body)))
158
152
 
153
+ ;; @(andify a b c)@ is equivalent to
154
+ ;; @(fn args (and (apply a args) (apply b args) (apply c args)))@
155
+ ;; or more simply
156
+ ;; @(fn (x) (and (a x) (b x) (c x)))@
157
+ ;; note: alias as 'andf ??
158
+ (mac andify args
159
+ (w/uniq a2
160
+ `(fn ,a2
161
+ (and ,@(map (fn (a) `(apply ,a ,a2)) args)))))
162
+
163
+ ;; lazy-evaluates each argument, returns the first
164
+ ;; non-nil result, or nil if all evaluate to nil.
159
165
  (mac or args
160
- ; lazy-evaluates each argument, returns the first
161
- ; non-nil result, or nil if all evaluate to nil.
162
166
  (if (cdr args)
163
167
  (let arg (car args)
164
168
  (if (isa 'symbol arg)
@@ -184,8 +188,8 @@ scoping, assignment, anonymous functions and more...")
184
188
  name)
185
189
  (list 'quote name)))
186
190
 
191
+ ;; (build-hash-getters '(a b c)) => (hash-get (hash-get a 'b) 'c)
187
192
  (def build-hash-getters (names acc)
188
- ;; (build-hash-getters '(a b c)) => (hash-get (hash-get a 'b) 'c)
189
193
  (if (no acc)
190
194
  (build-hash-getters (cdr names) (car names))
191
195
  names
@@ -198,12 +202,12 @@ scoping, assignment, anonymous functions and more...")
198
202
  (mac hash-lookup (names)
199
203
  (build-hash-getters names nil))
200
204
 
205
+ ;; parser expands a.b to (dot-syntax a b)
201
206
  (mac dot-syntax names
202
- ; parser expands a.b to (dot-syntax a b)
203
207
  `(hash-lookup ,names))
204
208
 
209
+ ;; parser expands a$b to (dollar-syntax a b)
205
210
  (mac dollar-syntax (_ name)
206
- ; parser expands a$b to (dollar-syntax a b)
207
211
  `(,name))
208
212
 
209
213
  (def dot-syntax-assignment (names value-expr)
@@ -293,13 +297,16 @@ scoping, assignment, anonymous functions and more...")
293
297
  (mac #= (name value)
294
298
  `(do (= ,name ,value) nil))
295
299
 
296
- ; increment the value at 'place by 'inc (default 1)
300
+ ;; increment the value at 'place by 'inc (default 1)
297
301
  (mac ++ (place inc) `(= ,place (+ ,place ,(or inc 1))))
298
302
 
299
- ; override previous definition to allow expressions like (def hsh.foo (arg arg2) ...)
303
+ ;; decrement the value at 'place by 'inc (default 1)
304
+ (mac -- (place inc) `(= ,place (- ,place ,(or inc 1))))
305
+
306
+ ;; override previous definition to allow expressions like (def hsh.foo (arg arg2) ...)
300
307
  (mac def-assign args `(= ,@args))
301
308
 
302
- ; evaluate ,val and assign result to ,place only if ,place is already nil
309
+ ;; evaluate ,val and assign result to ,place only if ,place is already nil
303
310
  (mac or= (place val) `(or ,place (= ,place ,val)))
304
311
 
305
312
  (def brace-list-hash-key (k)
@@ -307,33 +314,34 @@ scoping, assignment, anonymous functions and more...")
307
314
  (caris 'unquote k) (cadr k)
308
315
  k))
309
316
 
317
+ ;; TODO instead expand to: (hash 'k1 v1 'k2 v2 'k3 v3 ...)
318
+ ;; TODO builtin-hash function takes care of constructing the hash
310
319
  (def brace-list-build-hash (args)
311
- (w/uniq hash
312
- (let mappings (pairs args)
313
- `(let ,hash (hash)
314
- ,@(map (fn (m) `(hash-set ,hash ,(brace-list-hash-key (car m)) ,(cadr m))) mappings)
315
- ,hash))))
320
+ `(hash ,@(apply
321
+ +
322
+ (map (fn (kv) (list (brace-list-hash-key (car kv)) (cadr kv)))
323
+ (pairs args)))))
316
324
 
317
325
  (def build-ampersand-syntax (arg)
318
326
  (if (caris 'dot-syntax arg)
319
327
  `(fn (obj) ,(build-hash-lookup-from 'obj (cdr arg)))
320
328
  `(fn (obj) ,(build-hash-lookup-from 'obj (list arg)))))
321
329
 
330
+ ;; parser expands a&b to (ampersand-syntax a b)
322
331
  (mac ampersand-syntax (pfx . rest)
323
- ; parser expands a&b to (ampersand-syntax a b)
324
332
  (if (no (eq? pfx '||))
325
333
  (error "Irregular '& syntax: got prefix ~(inspect pfx) in ~(join-str pfx "&" rest)"))
326
334
  (if (cdr rest)
327
335
  (error "Irregular '& syntax: got suffix ~(inspect (cdr rest)) in ~(join-str pfx "&" rest)")
328
336
  (build-ampersand-syntax (car rest))))
329
337
 
330
- ; override 'brace-list-mono in order to provide a useful interpretation for "{ x }" syntax
338
+ ;; override 'brace-list-mono in order to provide a useful interpretation for "{ x }" syntax
331
339
  (mac brace-list-mono (arg) arg)
332
340
 
333
- ; interprets "{ }" as new hash
341
+ ;; interprets "{ }" as new hash
334
342
  (mac brace-list-empty () '(hash))
335
343
 
336
- ; parser expands { foo bar } to (brace-list foo bar)
344
+ ;; parser expands { foo bar } to (brace-list foo bar)
337
345
  (mac brace-list args
338
346
  (if (no args)
339
347
  `(brace-list-empty)
@@ -341,13 +349,13 @@ scoping, assignment, anonymous functions and more...")
341
349
  `(brace-list-mono ,(car args))
342
350
  (brace-list-build-hash args)))
343
351
 
344
- ; stores ,val in ,var, executes ,@body, returns ,var. Saves a line of code at the end of
345
- ; 'let. If 'body assigns to 'var, the assigned value of 'var will be returned. See also 'returning
352
+ ;; stores ,val in ,var, executes ,@body, returns ,var. Saves a line of code at the end of
353
+ ;; 'let. If 'body assigns to 'var, the assigned value of 'var will be returned. See also 'returning
346
354
  (mac returnlet (var val . body) `(let ,var ,val ,@body ,var))
347
355
 
348
- ; stores ,val, executes ,@body, and returns ,val. Assumes 'body is going to do something
349
- ; destructive with 'val, but you want 'val before it gets changed. Note that if 'val is mutated
350
- ; (eg hash), the mutated value will be returned. See also 'returnlet
356
+ ;; stores ,val, executes ,@body, and returns ,val. Assumes 'body is going to do something
357
+ ;; destructive with 'val, but you want 'val before it gets changed. Note that if 'val is mutated
358
+ ;; (eg hash), the mutated value will be returned. See also 'returnlet
351
359
  (mac returning (val . body) (w/uniq retval `(returnlet ,retval ,val ,@body)))
352
360
 
353
361
  (mac ifv (var expr . body)
@@ -363,11 +371,19 @@ scoping, assignment, anonymous functions and more...")
363
371
  (mac aif (expr . body)
364
372
  `(ifv it ,expr ,@body))
365
373
 
374
+ ;; returns the nth cdr of the list 'things
375
+ (def nthcdr (n things)
376
+ (loop (> n 0)
377
+ (= things (cdr things)
378
+ n (- n 1)))
379
+ things)
380
+
366
381
  ;; returns the n-th item in the list 'things
367
382
  (def nth (n things)
368
- (if (eq? n 0)
369
- (car things)
370
- (nth (- n 1) (cdr things))))
383
+ (loop (> n 0)
384
+ (= things (cdr things)
385
+ n (- n 1)))
386
+ (car things))
371
387
 
372
388
  (def destructure/with (var args n)
373
389
  ; provides the argument expression to 'with when
@@ -4,40 +4,33 @@
4
4
  ; executes 'body. If an error is raised, executes 'handler. Inside
5
5
  ; 'handler, the parameter 'errors is a list of error messages extracted from
6
6
  ; the sequence of errors that led here (Exception#cause in ruby or Throwable.getCause() in java)
7
- `(handle-error (fn (errors) ,handler)
8
- (fn () ,@body)))
7
+ `(handle-error (fn (errors traces) ,handler)
8
+ (fn () ,@body)))
9
9
 
10
+ ;; executes 'body. Afterwards, executes 'protection.
11
+ ;; 'protection is always executed even if there is an error.
10
12
  (mac ensure (protection . body)
11
- ; executes 'body. Afterwards, executes 'protection.
12
- ; 'protection is always executed even if there is an error.
13
13
  `(ensuring (fn () ,protection)
14
14
  (fn () ,@body)))
15
15
 
16
16
  ;; tests 'test, as long as 'test is non-nil,
17
17
  ;; repeatedly executes 'body
18
18
  (mac while (test . body)
19
- (w/uniq (rfname pred)
20
- `(rfnwith ,rfname (,pred ,test)
21
- (when ,pred
22
- ,@body
23
- (,rfname ,test)))))
19
+ `(loop ,test (do ,@body)))
24
20
 
25
- (mac loop (start test update . body)
26
- ; execute 'start, then for as long as 'test returns non-nil,
27
- ; execute 'body and 'update
28
- (w/uniq (gfn gparm)
29
- `(do ,start
30
- ((rfn ,gfn (,gparm)
31
- (if ,gparm
32
- (do ,@body ,update (,gfn ,test))))
33
- ,test))))
21
+ ;; execute 'start, then for as long as 'test returns non-nil,
22
+ ;; execute 'body and 'update
23
+ (mac looping (start test update . body)
24
+ `(do
25
+ ,start
26
+ (while ,test ,@body ,update)))
34
27
 
28
+ ;; assign 'init to 'v, then execute 'body 'max times,
29
+ ;; incrementing 'v at each iteration
35
30
  (mac for (v init max . body)
36
- ; assign 'init to 'v, then execute 'body 'max times,
37
- ; incrementing 'v at each iteration
38
31
  (w/uniq (gi gm)
39
32
  `(with (,v nil ,gi ,init ,gm (+ ,max 1))
40
- (loop (assign ,v ,gi) (< ,v ,gm) (assign ,v (+ ,v 1))
33
+ (looping (assign ,v ,gi) (< ,v ,gm) (assign ,v (+ ,v 1))
41
34
  ,@body))))
42
35
 
43
36
  ;; return a new function which is the original function with
@@ -56,14 +49,13 @@
56
49
  `(fn (arg)
57
50
  (,f ,@args arg)))
58
51
 
52
+ ;; if ,key is already in ,hsh - return the associated value.
53
+ ;; if ,key is not already in ,hsh - evaluate ,val, store the result
54
+ ;; under ,key in ,hsh, and return it
59
55
  (mac cache-get (hsh key val)
60
- ; if ,key is already in ,hsh - return the associated value.
61
- ; if ,key is not already in ,hsh - evaluate ,val, store the result
62
- ; under ,key in ,hsh, and return it
63
- (w/uniq (h k v)
64
- `(with (,h ,hsh ,k ,key)
65
- (let ,v (hash-get ,h ,k)
66
- (or ,v (returnlet ,v ,val (hash-set ,h ,k ,v)))))))
56
+ (w/uniq (h k)
57
+ `(with (,h ,hsh ,k ,key)
58
+ (or= (hash-get ,h ,k) ,val))))
67
59
 
68
60
  ;; same as 'def, but caches the result, keyed on args, so for a given set of args the result
69
61
  ;; is only ever calculated once
@@ -9,40 +9,103 @@
9
9
  ; invokes 'f for each element of 'things, first element processed first
10
10
  ; ( "l" in "eachl" = "leftmost first" )
11
11
  (def eachl (f things)
12
- (when things
13
- (f (car things))
14
- (eachl f (cdr things))))
12
+ (loop (pair? things)
13
+ (do
14
+ (f (car things))
15
+ (= things (cdr things)))))
15
16
 
16
17
  ;; if things is a pair,
17
18
  ;; if (cdr things) is nil, return (car things)
18
19
  ;; else recurse on (cdr things)
19
20
  ;; else return things
20
- (def list/last (things)
21
- (if (pair? things)
22
- (aif (cdr things)
23
- (list/last it)
24
- (car things))
25
- things))
21
+ ;;
22
+ ;; 'it is used internally
23
+ ;;
24
+ (def list/last (things it)
25
+ (loop (pair? things)
26
+ (= it (car things)
27
+ things (cdr things)))
28
+ (or things it))
29
+
30
+ ;; finds the index in 'things for which 'f returns non-nil,
31
+ ;; or nil if not found
32
+ (def list/find-index (f things)
33
+ (with (found nil
34
+ i -1)
35
+ (loop (and things
36
+ (no found))
37
+ (= found (f (car things))
38
+ things (cdr things)
39
+ i (+ i 1)))
40
+ (and found i)))
41
+
42
+ ;; finds the index of 'thing in a list 'things, such that for example,
43
+ ;; given a list 'my-list and an item 'thingy in the list,
44
+ ;; (nth (list/index-of thingy my-list) my-list) will return the value of thingy.
45
+ ;; returns nil if not found
46
+ (def list/index-of (thing things)
47
+ (list/find-index
48
+ (fn (it) (eq? it thing))
49
+ things))
50
+
51
+ ;; given a number 'n and a list 'things, return (a b) where a is
52
+ ;; the item at index n-1 or nil if not possible, and b is the item
53
+ ;; at index n+1 or nil if not possible
54
+ (def list/around (n things)
55
+ (if (and n
56
+ (< -1 n (len things)))
57
+ (list
58
+ (and (> n 0)
59
+ (nth (- n 1) things))
60
+ (nth (+ n 1) things))
61
+ (list nil nil)))
62
+
63
+ ;; finds the item before and the item after the given item in the given list.
64
+ ;; For example,
65
+ ;; (list/around λx(eq? x 'd) '(a b c d e f) ) will return '(c e)
66
+ (def list/around-f (f things)
67
+ (list/around
68
+ (list/find-index f things)
69
+ things))
70
+
71
+ ;; finds the item before and the item after the given item in the given list.
72
+ ;; For example,
73
+ ;; (list/around '(a b c d e f) 'd) will return '(c e)
74
+ (def list/around-thing (thing things)
75
+ (list/around
76
+ (list/index-of thing things)
77
+ things))
26
78
 
27
79
  ; invokes 'f for each element of 'things, last element processed first
28
80
  ; ( "r" in "eachr" = "rightmost first" )
29
81
  (def eachr (f things)
30
- (when things
31
- (eachr f (cdr things))
32
- (f (car things))))
82
+ (eachl f (rev things)))
33
83
 
34
84
  ; assign (cons x things) to things
35
- (mac push (x things) `(= ,things (cons ,x ,things)))
85
+ (mac push (x things)
86
+ `(= ,things (cons ,x ,things)))
87
+
88
+ ;; used internally by 'flatmap
89
+ (def flatmap-helper (f things res)
90
+ (loop (pair? things)
91
+ (let a (car things)
92
+ (= res
93
+ (if (pair? a)
94
+ (flatmap-helper f a res)
95
+ a
96
+ (cdr-set res (cons (f a)))
97
+ res)
98
+ things
99
+ (cdr things))))
100
+ (if things
101
+ (= res (set-cdr res (f things))))
102
+ res)
36
103
 
37
- ; flatten the given list, transforming each leaf-item, recursively
104
+ ;; flatten the given list, transforming each leaf-item, recursively
38
105
  (def flatmap (f things)
39
- (let acc nil
40
- (rfnwith flattenize (x things)
41
- (if (pair? x)
42
- (eachr flattenize x)
43
- x
44
- (push (f x) acc)))
45
- acc))
106
+ (let res (cons)
107
+ (flatmap-helper f things res)
108
+ (cdr res)))
46
109
 
47
110
  ; flatten the given list, recursively
48
111
  (def flatten (things) (flatmap x1 things))
@@ -59,16 +59,16 @@
59
59
  (mac in-private body
60
60
  `(w/privately t ,@body))
61
61
 
62
+ ;; a macro wrapper for 'map
63
+ ;; 'things is a list, 'x is the name of a variable, and 'expr
64
+ ;; is evaluated and collected for each 'x in 'things
65
+ ;; usage: (mapx items v (to-string v)) equivalent to (map to-string items)
62
66
  (mac mapx (things x expr)
63
- ; a macro wrapper for 'map
64
- ; 'things is a list, 'x is the name of a variable, and 'expr
65
- ; is evaluated and collected for each 'x in 'things
66
- ; usage: (mapx items v (to-string v)) equivalent to (map to-string items)
67
67
  (chapter list-manipulation)
68
68
  `(map (fun (,x) ,expr) ,things))
69
69
 
70
+ ;; 't if 'thing is not nil or a list or a hash
70
71
  (def atom? (thing)
71
- ; 't if 'thing is not a list or a hash
72
72
  (chapter nydp-core)
73
73
  (and thing
74
74
  (!pair? thing)
@@ -93,6 +93,8 @@
93
93
 
94
94
  ;; returns the first non-empty item in 'args
95
95
  ;; mac equivalent of (detect present? args)
96
+ ;; useful to obtain a non-blank value from a set of variables, for example
97
+ ;; (%span.name (dp {first} {last} {email} "unknown"))
96
98
  (mac dp args
97
99
  (if args
98
100
  (w/uniq nearg
@@ -108,6 +110,7 @@
108
110
  ;; (p (c)) ;;=> 1
109
111
  ;; (p (c))) ;;=> 2
110
112
  ;;
113
+ ;; see also 'seqf which does almost exactly the same thing
111
114
  (def counter ()
112
115
  (let i -1
113
116
  (fn () (++ i))))
@@ -4,26 +4,26 @@
4
4
  (def string-strip (txt)
5
5
  (string-replace "(\\A\\s+|\\s+\\z)" "" txt))
6
6
 
7
+ ;; flatten 'things into a single list (ie unnest lists)
8
+ ;; convert each item to a string
9
+ ;; return a single string which is the concatenation of each
10
+ ;; stringified item, with given 'txt inserted in between
11
+ ;; each item
7
12
  (def joinstr (txt . things)
8
- ; flatten 'things into a single list (ie unnest lists)
9
- ; convert each item to a string
10
- ; return a single string which is the concatenation of each
11
- ; stringified item, with given 'txt inserted in between
12
- ; each item
13
13
  (let joinables (flatten things)
14
14
  (apply +
15
15
  (to-string:car joinables)
16
16
  (flatten (map (fn (x) (list txt x))
17
17
  (cdr joinables))))))
18
18
 
19
- ; stringify join all the things and join them with no separator, like (joinstr "" things)
19
+ ;; stringify join all the things and join them with no separator, like (joinstr "" things)
20
20
  (def j things
21
- (apply + (flatmap to-string things)))
21
+ (apply + "" (flatmap to-string things)))
22
22
 
23
- ; string-interpolation syntax emits this form. Default implementation
24
- ; is to delegate to 'j , but containing forms may use macros that
25
- ; override this in order to provide specific interpolation behaviour
26
- ; (for example, formatting numbers or stripping HTML tags)
23
+ ;; string-interpolation syntax emits this form. Default implementation
24
+ ;; is to delegate to 'j , but containing forms may use macros that
25
+ ;; override this in order to provide specific interpolation behaviour
26
+ ;; (for example, formatting numbers or stripping HTML tags)
27
27
  (def string-pieces pieces
28
28
  (j pieces))
29
29
 
@@ -54,3 +54,9 @@
54
54
  "\nand args " (inspect args)))
55
55
  (apply (string-eval-fn str arg-names)
56
56
  args)))
57
+
58
+ ;; if txt is not blank/empty, return concatenation of before, txt, after
59
+ (def maybe-wrap-text (txt before after)
60
+ (if (nb txt)
61
+ (j before txt after)
62
+ txt))