nydp 0.5.1 → 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 +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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2163d395501072ea321375834496bc7228aa3b19f8991c0ccc9ac69f3e951426
|
4
|
+
data.tar.gz: 3a3b17042f42b1c2e0653a27e4aebf5000f248d59fc56a1e39f065df3a2fc97b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c1ff800db3fed15b1a541c4926ffe53a8d2962dc54969265d472c4fefb1eb453db4097aaf10750c39ccc86174967665689f38f63c557f42a9fe2a575a268fcf
|
7
|
+
data.tar.gz: 4c87fb842105b2614643f31d5f1c965129dec628310007b3cc6054756917d54c6a794a06234aa5bf6b658285fc6a0c7feaacef87d8c7dd778640fdabb9b38e76
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,16 +1,14 @@
|
|
1
1
|
# `'nydp`
|
2
2
|
|
3
|
-
`'nydp` is a new LISP dialect, much inspired by [Arc](http://arclanguage.org/), and hence indirectly by all of `'arc`'s ancestors,
|
4
|
-
and implemented in Ruby.
|
3
|
+
`'nydp` is a new LISP dialect, much inspired by [Arc](http://arclanguage.org/), and hence indirectly by all of `'arc`'s ancestors, and implemented in Ruby.
|
5
4
|
|
6
|
-
`'nydp` is "Not Your Daddy's Parentheses", a reference to [Xkcd 297](http://xkcd.com/297/) (itself a reference
|
7
|
-
to Star Wars), as well as to the meme [Not Your Daddy's Q](http://tvtropes.org/pmwiki/pmwiki.php/Main/NotYourDaddysX), where Q is a
|
8
|
-
modern, improved Q quite unlike the Q your daddy used. `'nydp` also shamelessly piggypacks on the
|
9
|
-
catchiness and popularity of the [NYPD](https://en.wikipedia.org/wiki/NYPD_Blue) abbreviation ("New York Police Department",
|
10
|
-
for those who have no interest in popular US politics or TV).
|
5
|
+
`'nydp` is "Not Your Daddy's Parentheses", a reference to [Xkcd 297](http://xkcd.com/297/) (itself a reference to Star Wars), as well as to the meme [Not Your Daddy's Q](http://tvtropes.org/pmwiki/pmwiki.php/Main/NotYourDaddysX), where Q is a modern, improved Q quite unlike the Q your daddy used. `'nydp` also shamelessly piggypacks on the catchiness and popularity of the [NYPD](https://en.wikipedia.org/wiki/NYPD_Blue) abbreviation ("New York Police Department").
|
11
6
|
|
12
|
-
We do not wish to suggest by "Not Your Daddy's Parentheses" that Common Lisp, Scheme, Racket, Arc, Clojure or your favourite
|
13
|
-
|
7
|
+
We do not wish to suggest by "Not Your Daddy's Parentheses" that Common Lisp, Scheme, Racket, Arc, Clojure or your favourite other lisp are somehow old-fashioned, inferior, or in need of improvement in any way.
|
8
|
+
|
9
|
+
Further, we deplore and lament the gender bias and the age bias implicit in the use of the word "Daddy". We hereby declare that `'nydp` is also not your mommy's parentheses, and, in fact, is not the parentheses of any other family member, including gender-neutral and nonbinary members, deceased ancestors and as-yet unconceived descendants. `'nydp` is equivalently and unequivocally not their parentheses either, and neither more or less so than the parentheses of any male or masculine or father-role family member. It is possible that in due course this project may be renamed "Not Your Parents' Parentheses" even if `'nypp` doesn't have quite the same ring to it and the 6-tuple "Parent" appears twice in the name.
|
10
|
+
|
11
|
+
Yet more deplorable and lamentable is the fact that "Daddy" appears to suggest that the age-gender axis is the only relevant axis under consideration. We hereby assert and declare that `'nydp` is also not any parentheses you may obtain (in any manner legal or otherwise) from any race, religion, nationality, sexual orientation, or choice of text editor.
|
14
12
|
|
15
13
|
The goal of `'nydp` is to allow untrusted users run sandboxed server-side scripts. By default, `'nydp` provides no system access :
|
16
14
|
|
@@ -23,8 +21,7 @@ The goal of `'nydp` is to allow untrusted users run sandboxed server-side script
|
|
23
21
|
|
24
22
|
[Peruse `'nydp`'s features here](lib/lisp/tests) in the `tests` directory.
|
25
23
|
|
26
|
-
Pronunciation guide: there is no fixed canonical pronunciation of `'nydp`. Just keep in mind that if you find yourself
|
27
|
-
wading _knee-deep_ through raw sewage, it's still better than working on a java project in a bank.
|
24
|
+
Pronunciation guide: there is no fixed canonical pronunciation of `'nydp`. Just keep in mind that if you find yourself wading _knee-deep_ through raw sewage, it's still better than working on a java or C# project in a bank. Or insurance company, if that's your thing.
|
28
25
|
|
29
26
|
## Running
|
30
27
|
|
@@ -37,7 +34,7 @@ welcome to nydp
|
|
37
34
|
nydp >
|
38
35
|
```
|
39
36
|
|
40
|
-
The REPL uses the readline library so you can use up- and down-arrows to navigate history.
|
37
|
+
The REPL uses the readline library so you can use up- and down-arrows to navigate history. IRB also uses readline and command history may get a little bit mixed-up if you start nydp inside an irb session.
|
41
38
|
|
42
39
|
#### Invoking from Ruby
|
43
40
|
|
@@ -46,15 +43,14 @@ Suppose you want to invoke the function named `question` with some arguments. Do
|
|
46
43
|
```ruby
|
47
44
|
ns = Nydp.build_nydp # keep this for later re-use, it's expensive to set up
|
48
45
|
|
49
|
-
answer =
|
46
|
+
answer = ns.apply :question, :life, ["The Universe", and_also(everything)]
|
50
47
|
|
51
48
|
==> 42
|
52
49
|
```
|
53
50
|
|
54
|
-
`ns` is
|
55
|
-
|
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).
|
51
|
+
`ns` is a Nydp::Namespace object, mapping ruby symbols to nydp symbols for quick lookup at nydp compile-time. The nydp symbols maintain the values of global variables, including all builtin functions and any other functions defined using `def`.
|
57
52
|
|
53
|
+
Nydp is designed to operate in a multi-tenant architectural context, so you can instantiate multiple Namespace instances, where each tenant applies their own custom scripts ; such scripts will not interfere with one another (unless you've specifically arranged it to be so by duplicating namespaces or some such sorcery).
|
58
54
|
|
59
55
|
#### Facing the Truth
|
60
56
|
|
@@ -77,7 +73,7 @@ In conditional statements, nil is false, anything else is true
|
|
77
73
|
|
78
74
|
After parsing its input, `'nydp` passes the result as an argument to the `pre-compile` function. This is where things get a little bit circular: initially, `pre-compile` is a builtin function that just returns its argument. `pre-compile` bootstraps itself into existence in [core-010-precompile.nydp](lib/lisp/core-010-precompile.nydp).
|
79
75
|
|
80
|
-
You can override `pre-compile` to transform the expression in any way you wish. By default, the `core-010-precompile.nydp` implementation of `pre-compile` performs macro-expansion.
|
76
|
+
You can override `pre-compile` to transform the expression in any way you wish. By default, the `core-010-precompile.nydp` implementation of `pre-compile` performs macro-expansion by searching for keywords in the `macs` global variable and applying the corresponding function to the given expression if a function is found.
|
81
77
|
|
82
78
|
|
83
79
|
```lisp
|
@@ -177,14 +173,16 @@ nydp > (map %td:&lastname german-composers)
|
|
177
173
|
|
178
174
|
```
|
179
175
|
|
180
|
-
So,
|
181
|
-
to
|
182
|
-
that knows how to build a function out of these bits
|
176
|
+
So, `%td` expands to `(percent-syntax || td)`, `&lastname` to `(ampersand-syntax || lastname)`,
|
177
|
+
and the whole `%td:&lastname` to `(colon-syntax (percent-syntax || td) (ampersand-syntax || lastname))`.
|
178
|
+
Luckily for you, there's a fine `colon-syntax` macro that knows how to build a function out of these bits
|
179
|
+
and pieces.
|
183
180
|
|
181
|
+
Look for `SYMBOL_OPERATORS` in [parser.rb](lib/nydp/parser.rb) to see which syntax is recognised and in
|
182
|
+
which order. The order of these definitions defines special-syntax-operator precedence.
|
184
183
|
|
185
|
-
|
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.
|
184
|
+
Any character that is not special syntax will be recognised as part of a symbol. At time of writing, this
|
185
|
+
includes the plus sign, hyphen, and slash.
|
188
186
|
|
189
187
|
```lisp
|
190
188
|
|
@@ -208,7 +206,7 @@ nydp > (parse "{ a 1 b 2 }")
|
|
208
206
|
|
209
207
|
```
|
210
208
|
|
211
|
-
`brace-list` is a macro that expands to create a hash literal. It assumes every item 0 is a literal symbol
|
209
|
+
`brace-list` is a macro that expands to create a hash literal. It assumes every item 0 is a literal key (symbol, string or number), item 1 is the corresponding value which is evaluated at run time, and so on for each following item-pair.
|
212
210
|
|
213
211
|
```lisp
|
214
212
|
|
@@ -233,9 +231,9 @@ nydp > (let bar "Mister Nice Guy" "hello, ~bar")
|
|
233
231
|
|
234
232
|
; this is a more tricky example because we need to make a string with an interpolation token in it
|
235
233
|
|
236
|
-
nydp > (let s
|
234
|
+
nydp > (let s "\"hello, \~world\"" (parse s))
|
237
235
|
|
238
|
-
==> (string-pieces "hello, " world
|
236
|
+
==> (string-pieces "hello, " world) ; "hello, ", followed by the interpolation `world`
|
239
237
|
|
240
238
|
; It is possible to nest interpolations. Note that as with many popular language features, just because you can do something, does not mean you should:
|
241
239
|
|
@@ -250,22 +248,37 @@ nydp > (with (a 1 b 2)
|
|
250
248
|
==> AND ALSO, Consider 3, the third (and final) thing
|
251
249
|
```
|
252
250
|
|
253
|
-
By default, `string-pieces` is a function that just concatenates the string value of its arguments. You can redefine it as a macro to
|
254
|
-
perform more fun stuff, or you can detect it within another macro to do extra-special stuff with it. The 'nydp-html gem detects
|
255
|
-
'string-pieces and gives it special treatment in order to render haml and textile efficiently, and also to capture and report errors
|
256
|
-
inside interpolations and report them correctly.
|
257
|
-
|
251
|
+
By default, `string-pieces` is a function that just concatenates the string value of its arguments. You can redefine it as a macro to perform more fun stuff, or you can detect it within another macro to do extra-special stuff with it. The 'nydp-html gem detects 'string-pieces and gives it special powers in order to render haml and textile efficiently, and also to capture and report errors inside interpolations and report them correctly.
|
258
252
|
|
259
253
|
#### 5. No continuations.
|
260
254
|
|
261
255
|
Sorry. While technically possible ... why bother?
|
262
256
|
|
257
|
+
#### 6. No tail-call optimisation / tail-call elimination
|
258
|
+
|
259
|
+
This isn't completely true. For performance reasons, the nydp stack maps directly to the underlying ruby stack, and it is possible to enable TCO in your ruby VM. Nydp doesn't enable it for you though, you'll need to take care of this yourself. So basically nydp doesn't guarantee you TCO, but if you're in control of your deployment environment, you can still have it.
|
260
|
+
|
261
|
+
As a result, nydp has an extra keyword, 'loop, so that recursive solutions can be rewritten as iterative ones. For example
|
262
|
+
|
263
|
+
```lisp
|
264
|
+
(def elegant-recursive-solution (n f things)
|
265
|
+
(if (> n 0)
|
266
|
+
(elegant-recursive-solution (- n 1) (cdr things))
|
267
|
+
(f things)))
|
268
|
+
|
269
|
+
;; can be rewritten as
|
270
|
+
(def stupid-iterative-solution (n f things)
|
271
|
+
(loop (> n 0)
|
272
|
+
(= things (cdr things)
|
273
|
+
n (- n 1)))
|
274
|
+
(f things))
|
275
|
+
```
|
263
276
|
|
264
277
|
## Besides that, what can Nydp do?
|
265
278
|
|
266
279
|
#### 1. Functions and variables exist in the same namespace.
|
267
280
|
#### 2. Macros are maintained in a hash called 'macs in the main namespace.
|
268
|
-
#### 3.
|
281
|
+
#### 3. Loop construct (see above under 'no tail-call optimisation')
|
269
282
|
#### 4. 'if like Arc:
|
270
283
|
|
271
284
|
```lisp
|
@@ -355,8 +368,7 @@ nydp > (parse "%w(a b c)")
|
|
355
368
|
==> (prefix-list "%w" (a b c))
|
356
369
|
```
|
357
370
|
|
358
|
-
This allows for preprocessing lists in a way not possible for macros. nydp uses this feature to implement shortcut
|
359
|
-
functions, as in
|
371
|
+
This allows for preprocessing lists in a way not possible for macros. nydp uses this feature to implement shortcut monoexpression functions with single-letter arguments, as in
|
360
372
|
|
361
373
|
```lisp
|
362
374
|
nydp > (parse "λx(len x)")
|
@@ -380,6 +392,13 @@ nydp > (pre-compile '(prefix-list "λxy" (* x y)))
|
|
380
392
|
==> (fn (x y) (* x y))
|
381
393
|
```
|
382
394
|
|
395
|
+
Example:
|
396
|
+
|
397
|
+
```lisp
|
398
|
+
nydp > (map λx(* x x) '(1 2 3 4))
|
399
|
+
|
400
|
+
==> (1 4 9 16)
|
401
|
+
|
383
402
|
Use 'define-prefix-list-macro to define a new handler for a prefix-list. Here's the code for the 'λ shortcut:
|
384
403
|
|
385
404
|
```lisp
|
@@ -395,11 +414,11 @@ In this case, the regex matches an initial 'λ ; there is no constraint however
|
|
395
414
|
|
396
415
|
Once the 'dox system is bootstrapped, any further use of 'mac or 'def will create documentation.
|
397
416
|
|
398
|
-
|
417
|
+
Comments immediately prior to the start of the form body will be used to generate help text. For example:
|
399
418
|
|
400
419
|
```lisp
|
401
|
-
nydp >
|
402
|
-
|
420
|
+
nydp > ; return the foo of x and y
|
421
|
+
(def foo (x y)
|
403
422
|
(* x y))
|
404
423
|
|
405
424
|
nydp > (dox foo)
|
@@ -421,14 +440,11 @@ nydp > (dox-lookup 'foo)
|
|
421
440
|
((foo def ("return the foo of x and y") (x y) (def foo (x y) (* x y))))
|
422
441
|
```
|
423
442
|
|
424
|
-
Not as friendly, but more amenable to programmatic manipulation. Each subsequent definition of 'foo (if you override it, define
|
425
|
-
it as a macro, or define it again in some other context) will generate a new documentation structure, which will simply be preprended to
|
426
|
-
the existing list.
|
443
|
+
Not as friendly, but more amenable to programmatic manipulation. Each subsequent definition of 'foo (if you override it, define it as a macro, or define it again in some other context) will generate a new documentation structure, which will simply be preprended to the existing list.
|
427
444
|
|
428
445
|
#### 11. Pretty-Printing
|
429
446
|
|
430
|
-
'dox above uses the pretty printer to display code source. The pretty-printer is hard-coded to handle some special cases,
|
431
|
-
so it will unparse special syntax, prefix-lists, quote, quasiquote, unquote, and unquote-splicing.
|
447
|
+
'dox above uses the pretty printer to display code source. The pretty-printer is hard-coded to handle some special cases, so it will unparse special syntax, prefix-lists, quote, quasiquote, unquote, and unquote-splicing.
|
432
448
|
|
433
449
|
You can examine its behaviour at the repl:
|
434
450
|
|
@@ -448,13 +464,11 @@ nydp > (p:pp:dox-src 'pp/find-breaks)
|
|
448
464
|
(list form))))
|
449
465
|
```
|
450
466
|
|
451
|
-
The pretty-printer is still rather primitive in that it only indents according to some hard-coded rules, and according to argument-count
|
452
|
-
for documented macros. It has no means of wrapping forms that get too long, or that extend beyond a certain predefined margin or column number.
|
467
|
+
The pretty-printer is still rather primitive in that it only indents according to some hard-coded rules, and according to argument-count for documented macros. It has no means of wrapping forms that get too long, or that extend beyond a certain predefined margin or column number.
|
453
468
|
|
454
469
|
#### 12. DSLs
|
455
470
|
|
456
|
-
The `pre-compile` system (described earlier in "Macro-expansion runs in lisp") is available for implementing local, mini-languages. To do this, use `pre-compile-with`
|
457
|
-
in a macro. `pre-compile-with` expects a hash with expansion rules, and an expression to expand using these rules. For example, to build a "describe" dsl :
|
471
|
+
The `pre-compile` system (described earlier in "Macro-expansion runs in lisp") is available for implementing local, mini-languages. To do this, use `pre-compile-with` in a macro. `pre-compile-with` expects a hash with expansion rules, and an expression to expand using these rules. For example, to build a "describe" dsl :
|
458
472
|
|
459
473
|
```lisp
|
460
474
|
(= describe-dsl (hash))
|
@@ -467,20 +481,27 @@ in a macro. `pre-compile-with` expects a hash with expansion rules, and an expre
|
|
467
481
|
',outcome))
|
468
482
|
```
|
469
483
|
|
470
|
-
In this example, `describe-dsl` establishes rules for expanding `def` and for `p` which will shadow their usual meanings, but only in the context
|
471
|
-
of a `describe` form.
|
484
|
+
In this example, `describe-dsl` establishes rules for expanding `def` and for `p` which will shadow their usual meanings, but only in the context of a `describe` form.
|
472
485
|
|
473
|
-
This technique is useful to avoid cluttering the global macro-namespace with rules that are used only in a specific context. A word of warning:
|
474
|
-
if you shadow a popular name, for example `let`, `map` or `do`, you are likely to run into trouble when some non-shadowed macro inside your
|
475
|
-
dsl context expands into something you have shadowed; in this case your shadowy definitions will be used to further expand the result.
|
486
|
+
This technique is useful to avoid cluttering the global macro-namespace with rules that are used only in a specific context. A word of warning: if you shadow a popular name, for example `let`, `map` or `do`, you are likely to run into trouble when some non-shadowed macro inside your dsl context expands into something you have shadowed; in this case your shadowy definitions will be used to further expand the result.
|
476
487
|
|
477
|
-
For example, if you shadow `with`, but not `let`, and then you use a `let` form within a sample of the dsl you are specifying ; the `let`
|
478
|
-
form expands into a `with` form, thinking the `with` will expand in the usual way, but in fact no, your private dsl is going to expand
|
479
|
-
this `with` in your private special way, possibly in a way that's incompatible with the form produced by `let`.
|
488
|
+
For example, if you shadow `with`, but not `let`, and then you use a `let` form within a sample of the dsl you are specifying ; the `let` form expands into a `with` form, thinking the `with` will expand in the usual way, but in fact no, your private dsl is going to expand this `with` in your private special way, possibly in a way that's incompatible with the form produced by `let`.
|
480
489
|
|
481
|
-
If, notwithstanding the aforementioned, the outcome works the way you expected it to, then you have the wrong kind of genius and your
|
482
|
-
license to program should be revoked.
|
490
|
+
If, notwithstanding the aforementioned, you do this anyway and the outcome works the way you expected it to, then you have the wrong kind of genius and your license to program should be revoked.
|
483
491
|
|
492
|
+
## Extending NYDP
|
493
|
+
|
494
|
+
You can add any object implementing #call (for example, a ruby Proc) to your `ns` instance:
|
495
|
+
|
496
|
+
```ruby
|
497
|
+
ns.assign(:"send-mail", -> {|props| Net::SMTP.deliver_the_letter(props).the_sooner_the_better }) # imaginary example
|
498
|
+
```
|
499
|
+
|
500
|
+
In your lisp,
|
501
|
+
|
502
|
+
```lisp
|
503
|
+
(send-mail { from "myself@example.com" to "yourself@example.com" subject "SPECIAL OFFER" body (mail-body blah-blah) })
|
504
|
+
```
|
484
505
|
|
485
506
|
## Installation
|
486
507
|
|
data/lib/lisp/core-000.nydp
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
;;
|
3
3
|
;; Acknowledgements to Paul Graham, Robert Morris, and their ancestor programmers.
|
4
4
|
;; nydp's main inspiration is arc, and many nydp features (including, but not limited
|
5
|
-
;; to, 'do, 'rfn, '
|
5
|
+
;; to, 'do, 'rfn, 'for) were directly inspired by (aka stolen from) arc.arc. See
|
6
6
|
;; README.md however for some significant differences
|
7
7
|
|
8
8
|
(assign noop (fn))
|
@@ -1,9 +1,11 @@
|
|
1
|
+
(assign uniq-counter 0)
|
2
|
+
|
1
3
|
(assign mac-expand
|
2
4
|
(fn (names macfn expr)
|
3
5
|
(cond macfn
|
4
6
|
(handle-error
|
5
|
-
(fn (errors)
|
6
|
-
(error "expanding" (inspect expr) "with"
|
7
|
+
(fn (errors traces)
|
8
|
+
(error "expanding" (inspect expr) "with" macfn))
|
7
9
|
(fn ()
|
8
10
|
(pre-compile-with names (apply macfn (cdr expr)))))
|
9
11
|
expr)))
|
@@ -11,37 +13,41 @@
|
|
11
13
|
(assign macs (hash))
|
12
14
|
|
13
15
|
(assign pre-compile-each
|
14
|
-
(fn (names
|
15
|
-
(cond
|
16
|
-
(cond (pair?
|
16
|
+
(fn (names exprs)
|
17
|
+
(cond exprs
|
18
|
+
(cond (pair? exprs)
|
17
19
|
(cons (pre-compile-with names
|
18
|
-
(car
|
20
|
+
(car exprs))
|
19
21
|
(pre-compile-each names
|
20
|
-
(cdr
|
21
|
-
|
22
|
+
(cdr exprs)))
|
23
|
+
exprs))))
|
22
24
|
|
23
25
|
(assign pre-compile-msg
|
24
26
|
(fn (src compiled)
|
25
|
-
(p "pre-compile" src "\n -> " compiled)
|
26
27
|
compiled))
|
27
28
|
|
28
29
|
(assign pre-compile-raw
|
29
|
-
(fn (names
|
30
|
-
(cond (pair?
|
31
|
-
(cond (eq? (car
|
32
|
-
|
33
|
-
(
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
30
|
+
(fn (names expr)
|
31
|
+
(cond (pair? expr)
|
32
|
+
(cond (eq? (car expr) 'quote)
|
33
|
+
expr
|
34
|
+
(cond (isa 'symbol (car expr))
|
35
|
+
(pre-compile-each
|
36
|
+
names
|
37
|
+
(mac-expand names
|
38
|
+
(hash-get names
|
39
|
+
(car expr))
|
40
|
+
expr))
|
41
|
+
(pre-compile-each
|
42
|
+
names
|
43
|
+
expr)))
|
44
|
+
expr)))
|
39
45
|
|
40
46
|
(assign pre-compile-debug
|
41
|
-
(fn (names
|
42
|
-
(pre-compile-msg
|
47
|
+
(fn (names expr)
|
48
|
+
(pre-compile-msg expr
|
43
49
|
(pre-compile-raw names
|
44
|
-
|
50
|
+
expr))))
|
45
51
|
|
46
52
|
(assign debug-pre-compile
|
47
53
|
(fn (arg)
|
@@ -54,8 +60,17 @@
|
|
54
60
|
|
55
61
|
; builtin pre-compile does nothing; override here to provide macro-expansion
|
56
62
|
(assign pre-compile
|
57
|
-
(fn (
|
58
|
-
(pre-compile-with macs
|
63
|
+
(fn (expr)
|
64
|
+
(pre-compile-with macs expr)))
|
65
|
+
|
66
|
+
;; this is the entry point for the interpreter ; we need to reset uniq-counter for each new expression
|
67
|
+
;; (for performance purposes only - to ensure identical nydp code produces identical ruby code) ; but
|
68
|
+
;; we can't reset uniq-counter in the middle of pre-compiling an expression, otherwise @uniq@ won't be
|
69
|
+
;; even slightly unique any more
|
70
|
+
(assign pre-compile-new-expression
|
71
|
+
(fn (expr)
|
72
|
+
(assign uniq-counter 0)
|
73
|
+
(pre-compile-with macs expr)))
|
59
74
|
|
60
75
|
; we override this later to provide argument deconstruction
|
61
76
|
(hash-set macs 'fun
|
@@ -81,28 +96,33 @@
|
|
81
96
|
(qq-quasiquote rest level))
|
82
97
|
arg))
|
83
98
|
|
99
|
+
(def qq-build-cons (a b)
|
100
|
+
(cond b
|
101
|
+
(list 'cons a b)
|
102
|
+
(list 'cons a)))
|
103
|
+
|
84
104
|
(def qq-skip-unquote-splicing (arg rest level)
|
85
|
-
(
|
105
|
+
(qq-build-cons
|
86
106
|
(list 'list ''unquote-splicing (qq-quasiquote arg (- level 1)))
|
87
107
|
(qq-quasiquote rest level)))
|
88
108
|
|
89
109
|
(def qq-handle-quasiquote (arg rest level)
|
90
|
-
(
|
110
|
+
(qq-build-cons
|
91
111
|
(list 'list ''quasiquote (qq-quasiquote arg (+ level 1)))
|
92
112
|
(qq-quasiquote rest level)))
|
93
113
|
|
94
114
|
(def qq-handle-unquote (arg rest level)
|
95
|
-
(
|
115
|
+
(qq-build-cons
|
96
116
|
(qq-maybe-unquote arg level)
|
97
117
|
(qq-quasiquote rest level)))
|
98
118
|
|
99
119
|
(def qq-unquote-recurse (arg rest level)
|
100
|
-
(
|
120
|
+
(qq-build-cons
|
101
121
|
(qq-quasiquote arg level)
|
102
122
|
(qq-quasiquote rest level)))
|
103
123
|
|
104
124
|
(def qq-handle-plain (arg rest level)
|
105
|
-
(
|
125
|
+
(qq-build-cons
|
106
126
|
(list 'quote arg)
|
107
127
|
(qq-quasiquote rest level)))
|
108
128
|
|
@@ -9,9 +9,11 @@
|
|
9
9
|
nil)))
|
10
10
|
|
11
11
|
(def map-helper-0 (f things lc)
|
12
|
-
(
|
13
|
-
|
14
|
-
|
12
|
+
(loop (pair? things)
|
13
|
+
((fn nil
|
14
|
+
(assign lc (cdr-set lc (cons (f (car things)))))
|
15
|
+
(assign things (cdr things)))))
|
16
|
+
(if things
|
15
17
|
(cdr-set lc (f things))))
|
16
18
|
|
17
19
|
(def map-helper-1 (f things acc)
|
@@ -35,9 +37,11 @@
|
|
35
37
|
(def hash-cons (h k v)
|
36
38
|
(hash-set h k (cons v (hash-get h k))))
|
37
39
|
|
40
|
+
;; 'things - the list to be reversed
|
41
|
+
;; 'last-cdr - (normally nil) - an item (atom, list, nil, anything) to be consed to the end of the reversed list.
|
38
42
|
(def rev (things last-cdr)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
43
|
+
(loop (pair? things)
|
44
|
+
((fn nil
|
45
|
+
(assign last-cdr (cons (car things) last-cdr))
|
46
|
+
(assign things (cdr things)))))
|
47
|
+
last-cdr)
|
@@ -77,9 +77,6 @@
|
|
77
77
|
(hash-set hsh 'texts texts )
|
78
78
|
(hash-set hsh 'args args )
|
79
79
|
(hash-set hsh 'src src )
|
80
|
-
(hash-set hsh 'chapters (cons (chapter-current) chapters))
|
81
|
-
(hash-set hsh 'file this-script )
|
82
|
-
(hash-set hsh 'plugin this-plugin )
|
83
80
|
hsh)
|
84
81
|
|
85
82
|
(def dox-new (item)
|
@@ -87,11 +84,19 @@
|
|
87
84
|
(hash-cons types (hash-get item 'what) item)
|
88
85
|
(dox-add-to-chapters item (hash-get item 'what) (hash-get item 'chapters) (hash)))
|
89
86
|
|
90
|
-
(def dox-add-doc (name what texts args src
|
91
|
-
(
|
92
|
-
|
87
|
+
(def dox-add-doc (name what texts args src etc)
|
88
|
+
(handle-error
|
89
|
+
(fn (errors traces) (error "failed to add dox for " name " - " what))
|
90
|
+
(fn ()
|
91
|
+
(cond (no etc)
|
92
|
+
(error (inspect name) " : please provide arguments (name what texts args src etc) where etc is a hash with at least keys (chapters plugin file)"))
|
93
|
+
(cond (no (privately))
|
94
|
+
(dox-new
|
95
|
+
(dox-build etc name what texts args src))))))
|
93
96
|
|
94
97
|
(def dox-add-to-chapters (item type chapters already)
|
98
|
+
(cond (pair? (car chapters))
|
99
|
+
(error "dox-add-to-chapters : expected symbol, got " (inspect (car chapters)) " for " (inspect item)))
|
95
100
|
(cond chapters
|
96
101
|
(cond (no (hash-get already (car chapters)))
|
97
102
|
(do (hash-set already (car chapters) t)
|
@@ -131,7 +136,7 @@
|
|
131
136
|
(def plugin-start (name) (assign this-plugin name) (chapter-end))
|
132
137
|
(def plugin-end (name) (assign this-plugin nil ) (chapter-end))
|
133
138
|
(def script-start (name) (assign this-script name) (chapter-end))
|
134
|
-
(def script-end (name) (assign this-script nil ) (chapter-end))
|
139
|
+
(def script-end (name) (assign this-script nil ) (fetch-and-clear-comments) (chapter-end))
|
135
140
|
|
136
141
|
(def script-run (event name)
|
137
142
|
(cond (eq? event 'plugin-start)
|
@@ -180,14 +185,27 @@
|
|
180
185
|
|
181
186
|
(assign DEF-FORMS '(comment chapter))
|
182
187
|
|
188
|
+
(def hash-init (h keys v)
|
189
|
+
(if keys
|
190
|
+
(do (hash-set h (car keys) v)
|
191
|
+
(hash-init h (cdr keys) v)))
|
192
|
+
h)
|
193
|
+
|
183
194
|
(def build-def-hash ()
|
184
|
-
(
|
185
|
-
(map (fn (k) (hash-set h k nil)) DEF-FORMS)
|
186
|
-
h)
|
187
|
-
(hash)))
|
195
|
+
(hash-init (hash) DEF-FORMS nil))
|
188
196
|
|
189
197
|
(def dox-build-def-name (name) name)
|
190
198
|
|
199
|
+
(def dox-chapters-expr-helper (chaps)
|
200
|
+
(cond chaps
|
201
|
+
`(quote (,@chaps))))
|
202
|
+
|
203
|
+
(def dox-chapters-expr (chaps)
|
204
|
+
(cond (chapter-current)
|
205
|
+
(dox-chapters-expr-helper (cons (chapter-current) (apply + nil chaps)))
|
206
|
+
(cond chaps
|
207
|
+
(dox-chapters-expr-helper (apply + chaps)))))
|
208
|
+
|
191
209
|
;; used internally by 'mac
|
192
210
|
(def define-mac-expr (name args body-forms)
|
193
211
|
`(do (hash-set macs ',name (fun ,args ,@(hash-get body-forms nil)))
|
@@ -196,7 +214,9 @@
|
|
196
214
|
',(+ (fetch-and-clear-comments) (map car (hash-get body-forms 'comment)))
|
197
215
|
',args
|
198
216
|
'(mac ,name ,args ,@(hash-get body-forms nil))
|
199
|
-
',(
|
217
|
+
(hash 'chapters ,(dox-chapters-expr (hash-get body-forms 'chapter))
|
218
|
+
'plugin ',this-plugin
|
219
|
+
'file ',this-script))))
|
200
220
|
|
201
221
|
(hash-set macs 'mac
|
202
222
|
(fn (name args . body)
|
@@ -207,14 +227,18 @@
|
|
207
227
|
'("define a new global macro")
|
208
228
|
'(name args . body)
|
209
229
|
'`(hash-set macs ',name (fn ,args ,@body))
|
210
|
-
'(nydp-core)
|
230
|
+
(hash 'chapters '(nydp-core)
|
231
|
+
'plugin this-plugin
|
232
|
+
'file this-script))
|
211
233
|
|
212
234
|
(dox-add-doc 'do
|
213
235
|
'mac
|
214
236
|
'("perform a series of operations")
|
215
237
|
'args
|
216
238
|
'`((fn nil ,@args))
|
217
|
-
'(nydp-core)
|
239
|
+
(hash 'chapters '(nydp-core)
|
240
|
+
'plugin this-plugin
|
241
|
+
'file this-script))
|
218
242
|
|
219
243
|
;; override later to use '= instead of 'assign, giving us hash-assignment and other goodies for free
|
220
244
|
(mac def-assign args `(assign ,@args))
|
@@ -230,7 +254,9 @@
|
|
230
254
|
',(+ (fetch-and-clear-comments) (map car (hash-get body-forms 'comment)))
|
231
255
|
',args
|
232
256
|
'(def ,name ,args ,@(hash-get body-forms nil))
|
233
|
-
',(
|
257
|
+
(hash 'chapters ,(dox-chapters-expr (hash-get body-forms 'chapter))
|
258
|
+
'plugin ',this-plugin
|
259
|
+
'file ',this-script))))
|
234
260
|
|
235
261
|
;; define a new function in the global namespace
|
236
262
|
(mac def (name args . body)
|