nydp 0.1.2 → 0.1.3

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +25 -13
  3. data/lib/lisp/core-015-documentation.nydp +11 -2
  4. data/lib/lisp/core-030-syntax.nydp +52 -8
  5. data/lib/lisp/core-040-utils.nydp +35 -47
  6. data/lib/lisp/core-045-dox-utils.nydp +85 -0
  7. data/lib/lisp/core-050-test-runner.nydp +20 -2
  8. data/lib/lisp/core-080-pretty-print.nydp +5 -1
  9. data/lib/lisp/tests/boot-tests.nydp +125 -255
  10. data/lib/lisp/tests/car-examples.nydp +16 -0
  11. data/lib/lisp/tests/collect-tests.nydp +28 -0
  12. data/lib/lisp/tests/cons-examples.nydp +8 -0
  13. data/lib/lisp/tests/curry-tests.nydp +17 -18
  14. data/lib/lisp/tests/detect-examples.nydp +24 -0
  15. data/lib/lisp/tests/dot-syntax-examples.nydp +40 -0
  16. data/lib/lisp/tests/dox-tests.nydp +116 -100
  17. data/lib/lisp/tests/dynamic-scope-test.nydp +10 -10
  18. data/lib/lisp/tests/each-tests.nydp +4 -5
  19. data/lib/lisp/tests/error-tests.nydp +17 -16
  20. data/lib/lisp/tests/explain-mac-examples.nydp +24 -0
  21. data/lib/lisp/tests/foundation-test.nydp +57 -223
  22. data/lib/lisp/tests/hash-examples.nydp +41 -0
  23. data/lib/lisp/tests/isa-examples.nydp +7 -0
  24. data/lib/lisp/tests/len-examples.nydp +11 -0
  25. data/lib/lisp/tests/list-tests.nydp +110 -75
  26. data/lib/lisp/tests/parser-tests.nydp +67 -109
  27. data/lib/lisp/tests/pretty-print-tests.nydp +19 -20
  28. data/lib/lisp/tests/quasiquote-examples.nydp +10 -0
  29. data/lib/lisp/tests/rfnwith-tests.nydp +7 -0
  30. data/lib/lisp/tests/string-tests.nydp +60 -31
  31. data/lib/lisp/tests/syntax-tests.nydp +22 -24
  32. data/lib/lisp/tests/type-of-examples.nydp +48 -0
  33. data/lib/lisp/tests/unparse-tests.nydp +2 -3
  34. data/lib/nydp.rb +2 -1
  35. data/lib/nydp/version.rb +1 -1
  36. metadata +14 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 63983bd000bf898d3987e7878f25a16e0be94924
4
- data.tar.gz: ec47cf4bf570c09a63d17a53008fe92671460cbc
3
+ metadata.gz: f4074a517649b09d3911d3b593e19ff0f0bc6d66
4
+ data.tar.gz: cf2d6625885b45055f2e09601dc5914da308d5e2
5
5
  SHA512:
6
- metadata.gz: 02e72f25772989690a4ba44cee9e5f054528d0d2202779674fd232fab1806bf24d2840bd0e6e1ce289875b6aac739cfdd94e7e4da86513d1020cecce1cc7ac62
7
- data.tar.gz: 680159117248665c00a5166bf6d9e1a58247170d28d532215529f35f7a562add5464db10ee1bcbc617a38ee7eb3402777c484f27b388834c9da0aef68189947c
6
+ metadata.gz: 722eb57b1b40efaed06e432f3d70e00e18cda57ccef51b404f755b4b35dab3456a68a3542bb2185e68a22c5c1b2463c0320b5a223fa6901c1240ad542c07a0f3
7
+ data.tar.gz: 4d5a6a702eb006751548d594515022f1c2e2f8b2697d45b95973db83abb2ad2d53d29d6495fbde9281f21c9ae98bf02e87b5b6c19e6ae9c053ad9152804f41d8
data/README.md CHANGED
@@ -1,16 +1,18 @@
1
- # NYDP
1
+ # `'nydp`
2
2
 
3
- NYDP is a new LISP dialect, much inspired by [Arc](http://arclanguage.org/), 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,
4
+ and implemented in Ruby.
4
5
 
5
- NYDP is "Not Your Daddy's Parentheses", a reference to [Xkcd 297](http://xkcd.com/297/) (itself a reference
6
- 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,
7
- improved Q unlike the Q your daddy used. "NYDP" also shamelessly piggypacks on the
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
8
9
  catchiness and popularity of the [NYPD](https://en.wikipedia.org/wiki/NYPD_Blue) abbreviation ("New York Police Department",
9
10
  for those who have no interest in popular US politics or TV).
10
11
 
11
- 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.
12
+ We do not wish to suggest by "Not Your Daddy's Parentheses" that Common Lisp, Scheme, Racket, Arc, Clojure or your favourite
13
+ other lisp are somehow old-fashioned, inferior, or in need of improvement in any way.
12
14
 
13
- The goal of NYDP is to allow untrusted users run sandboxed server-side scripts. By default, NYDP provides no system access :
15
+ The goal of `'nydp` is to allow untrusted users run sandboxed server-side scripts. By default, `'nydp` provides no system access :
14
16
 
15
17
  * no file functions
16
18
  * no network functions
@@ -19,7 +21,10 @@ The goal of NYDP is to allow untrusted users run sandboxed server-side scripts.
19
21
  * no threading functions
20
22
  * no ruby calls
21
23
 
22
- [Peruse NYDP's features here](lib/lisp/tests) in the `tests` directory.
24
+ [Peruse `'nydp`'s features here](lib/lisp/tests) in the `tests` directory.
25
+
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.
23
28
 
24
29
  ## Running
25
30
 
@@ -41,7 +46,7 @@ Suppose you want to invoke the function named `question` with some arguments. Do
41
46
  ```ruby
42
47
  ns = Nydp.build_nydp # keep this for later re-use, it's expensive to set up
43
48
 
44
- answer = Nydp.apply_function ns, :question, :life, ("The Universe" and everything())
49
+ answer = Nydp.apply_function ns, :question, :life, ["The Universe" and(everything)]
45
50
 
46
51
  ==> 42
47
52
  ```
@@ -55,7 +60,7 @@ You can maintain multiple `ns` instances without mutual interference. In other w
55
60
 
56
61
  #### 1. Macro-expansion runs in lisp
57
62
 
58
- 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 [boot.nydp](lib/lisp/boot.nydp).
63
+ 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 [boot.nydp](lib/lisp/boot.nydp).
59
64
 
60
65
  You can override `pre-compile` to transform the expression in any way you wish. By default, the `boot.nydp` implementation of `pre-compile` performs macro-expansion.
61
66
 
@@ -185,7 +190,7 @@ nydp > (parse "\"foo\"")
185
190
 
186
191
  nydp > (let bar "Mister Nice Guy" "hello, ~bar")
187
192
 
188
- ==> hello, Mister Nice Guy
193
+ ==> "hello, Mister Nice Guy"
189
194
 
190
195
  ; this is a more tricky example because we need to make a string with an interpolation token in it
191
196
 
@@ -193,6 +198,8 @@ nydp > (let s (joinstr "" "\"hello, " '~ "world\"") (parse s))
193
198
 
194
199
  ==> (string-pieces "hello, " world "") ; "hello, ", followed by the interpolation 'world, followed by the empty string after 'world
195
200
 
201
+ ; 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:
202
+
196
203
  nydp > (def also (str) "\nAND ALSO, ~str")
197
204
  nydp > (with (a 1 b 2)
198
205
  (p "Consider ~a : the first thing,
@@ -204,13 +211,17 @@ nydp > (with (a 1 b 2)
204
211
  ==> AND ALSO, Consider 3, the third (and final) thing
205
212
  ```
206
213
 
207
- 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 treatment in order to render haml and textile efficiently, and also to capture and report errors inside interpolations and report them correctly.
214
+ By default, `string-pieces` is a function that just concatenates the string value of its arguments. You can redefine it as a macro to
215
+ perform more fun stuff, or you can detect it within another macro to do extra-special stuff with it. The 'nydp-html gem detects
216
+ 'string-pieces and gives it special treatment in order to render haml and textile efficiently, and also to capture and report errors
217
+ inside interpolations and report them correctly.
208
218
 
209
219
 
210
220
  #### 5. No continuations.
211
221
 
212
222
  Sorry. While technically possible ... why bother?
213
223
 
224
+
214
225
  #### 6. No argument destructuring
215
226
 
216
227
  However, this doesn't need to be built-in, it can be done with macros alone. On the other hand, "rest" arguments are implicitly available using the same syntax as Arc uses:
@@ -277,7 +288,8 @@ nydp > (parse "; blah blah")
277
288
  ==> (comment "blah blah")
278
289
  ```
279
290
 
280
- Except in 'mac and 'def forms, by default, `comment` is a macro that expands to nil. If you have a better idea, go for it.
291
+ Except in 'mac and 'def forms, by default, `comment` is a macro that expands to nil. If you have a better idea, go for it. Any comments present at the
292
+ beginning of the `body` argument to `mac` or `def` are considered documentation. (See "self-documenting" below).
281
293
 
282
294
  #### 8 Prefix lists
283
295
 
@@ -1,10 +1,16 @@
1
- ((fn (dox)
1
+ ((fn (dox examples)
2
2
  (def dox-add-doc (name what texts args src)
3
3
  (hash-set dox
4
4
  name
5
5
  (cons (list name what texts args src)
6
6
  (hash-get dox sym))))
7
7
 
8
+ (def dox-add-examples (name example-exprs)
9
+ (hash-set examples
10
+ name
11
+ (cons example-exprs
12
+ (hash-get examples sym))))
13
+
8
14
  (def dox-lookup (sym) (hash-get dox sym))
9
15
 
10
16
  (def dox? (sym) (hash-key? dox sym))
@@ -19,10 +25,13 @@
19
25
  (cond (dox? name)
20
26
  (car (cddr (cddr (car (dox-lookup name)))))))
21
27
 
28
+ (def dox-examples (name)
29
+ (hash-get examples name))
30
+
22
31
  (def dox-arg-names (name)
23
32
  (cond (dox? name)
24
33
  (cadddr (car (dox-lookup name))))))
25
- (hash))
34
+ (hash) (hash))
26
35
 
27
36
  (def isa-comment? (thing)
28
37
  (cond (pair? thing)
@@ -1,4 +1,7 @@
1
1
  (def orf args
2
+ ; evaluates each arg in 'args, returns the
3
+ ; first non-nil value, or nil if they are
4
+ ; all nil
2
5
  (cond args
3
6
  (cond (car args)
4
7
  (car args)
@@ -6,6 +9,7 @@
6
9
  (cdr args)))))
7
10
 
8
11
  (mac unless (arg . body)
12
+ ; evaluate 'body if 'arg is nil
9
13
  `(if (no ,arg) (do ,@body)))
10
14
 
11
15
  (def expand-colon-syntax (names)
@@ -27,11 +31,23 @@
27
31
  (error "Irregular ': syntax: got ~(inspect names) : not prefix-syntax : in ~(joinstr ":" names)"))
28
32
 
29
33
  (mac colon-syntax names
34
+ ; handle syntax of the form a:b, which the parser expands to
35
+ ; (colon-syntax a b). By default, this complains if colon is used
36
+ ; as a prefix (ie it disallows ":foo"), otherwise creates a new
37
+ ; function which is the composition of the functions named in its
38
+ ; arguments. For example,
39
+ ; (count:parts spaceship) is the same as (count (parts spaceship))
30
40
  ((orf (hash-get colon-syntax-overrides (car names))
31
41
  default-colon-syntax)
32
42
  names))
33
43
 
34
44
  (mac bang-syntax (pfx . rest)
45
+ ; handle syntax of the form !x, which the parser expands to
46
+ ; (bang-syntax || x). By default, this complains if there is
47
+ ; a non-empty prefix (ie it disallows x!y), otherwise it creates
48
+ ; a new function which is the negation of the given named function.
49
+ ; For example,
50
+ ; (!eq? a 10) is the same as (no:eq? a 10), which is the same as (no (eq? a 10))
35
51
  (if (no (eq? pfx '||))
36
52
  (error "Irregular '! syntax: got prefix ~(inspect pfx) in ~(joinstr "!" (cons pfx rest))"))
37
53
  (if (cdr rest)
@@ -64,6 +80,30 @@
64
80
  (mac let (var val . body)
65
81
  `(with (,var ,val) ,@body))
66
82
 
83
+ (mac rfn (name parms . body)
84
+ ; creates a named, locally-scoped function
85
+ ; with the given parameter names. It is possible
86
+ ; to reference the function by its name from within
87
+ ; the function (to pass as an argument or for
88
+ ; recursive invocation)
89
+ `(let ,name nil
90
+ (assign ,name (fn ,parms ,@body))))
91
+
92
+ (mac afn (parms . body)
93
+ ; same as 'rfn, but using the name 'self
94
+ `(rfn self ,parms ,@body))
95
+
96
+ (mac rfnwith (name params . body)
97
+ ; a mix of rfn and with; creates a locally-scoped named function with
98
+ ; the given parameter names, and invokes it with the given parameter
99
+ ; values. It is possible to reference the function by its name from
100
+ ; within the function (to pass as an argument or for recursive
101
+ ; invocation)
102
+ (let ppairs (pairs params)
103
+ `(let ,name nil
104
+ (assign ,name (fn ,(map car ppairs) ,@body))
105
+ (,name ,@(map cadr ppairs)))))
106
+
67
107
  (let uniq-counter 0
68
108
  (def uniq (prefix)
69
109
  (sym (joinstr "-"
@@ -74,16 +114,20 @@
74
114
  (assign uniq-counter 0)))
75
115
 
76
116
  (mac w/uniq (vars . body)
77
- (if (pair? vars)
78
- `(with ,(apply + (map (fn (n) (list n '(uniq ',n))) vars))
79
- ,@body)
80
- `(let ,vars (uniq ',vars) ,@body)))
117
+ ; creates a lexical scope with a unique symbol assigned to
118
+ ; each variable in 'vars ; executes the 'body.
119
+ (if (pair? vars)
120
+ `(with ,(apply + (map (fn (n) (list n '(uniq ',n))) vars))
121
+ ,@body)
122
+ `(let ,vars (uniq ',vars) ,@body)))
81
123
 
82
124
  (mac or args
83
- (cond args
84
- (w/uniq ora
85
- `(let ,ora ,(car args)
86
- (cond ,ora ,ora (or ,@(cdr args)))))))
125
+ ; lazy-evaluates each argument, returns the first
126
+ ; non-nil result, or nil if all evaluate to nil.
127
+ (cond args
128
+ (w/uniq ora
129
+ `(let ,ora ,(car args)
130
+ (cond ,ora ,ora (or ,@(cdr args)))))))
87
131
 
88
132
  (mac pop (xs)
89
133
  (w/uniq gp
@@ -14,12 +14,10 @@
14
14
 
15
15
  (def flatten (things)
16
16
  (let acc nil
17
- (let flattenize nil
18
- (assign flattenize (fn (x)
19
- (if (pair? x)
20
- (eachr flattenize x)
21
- (push x acc))))
22
- (flattenize things))
17
+ (rfnwith flattenize (x things)
18
+ (if (pair? x)
19
+ (eachr flattenize x)
20
+ (push x acc)))
23
21
  acc))
24
22
 
25
23
  (def string-strip (txt)
@@ -40,6 +38,9 @@
40
38
  (joinstr "" pieces))
41
39
 
42
40
  (def detect (f things)
41
+ ; if 'things is a list, return the first item in the list for which 'f returns non-nil
42
+ ; otherwise, return 'things if (f things) is non-nil
43
+ ; otherwise, nil
43
44
  (if (pair? things)
44
45
  (let it (car things)
45
46
  (or
@@ -48,10 +49,26 @@
48
49
  (f things)
49
50
  things))
50
51
 
52
+ (def collect (f things)
53
+ ; if 'things is a list, return all the items in the list for which 'f returns non-nil
54
+ ; otherwise, return 'things if (f things) is non-nil
55
+ ; otherwise, nil
56
+ (rfnwith collector (items things)
57
+ (if (no items)
58
+ nil
59
+ (pair? items)
60
+ (if (f (car items))
61
+ (cons (car items)
62
+ (collector (cdr items)))
63
+ (collector (cdr items)))
64
+ (f items)
65
+ items)))
66
+
51
67
  (def nth (n things)
52
- (if (eq? n 0)
53
- (car things)
54
- (nth (- n 1) (cdr things))))
68
+ ; returns the n-th item in the list 'things
69
+ (if (eq? n 0)
70
+ (car things)
71
+ (nth (- n 1) (cdr things))))
55
72
 
56
73
  (def iso (x y)
57
74
  (or (eq? x y)
@@ -68,8 +85,10 @@
68
85
  (def quotify (arg) `(quote ,arg))
69
86
 
70
87
  (def caris (obj things)
71
- (and (isa 'pair things)
72
- (eq? (car things) obj)))
88
+ ; returns true if 'things is a list and the first item of the
89
+ ; list is the given object
90
+ (and (isa 'pair things)
91
+ (eq? (car things) obj)))
73
92
 
74
93
  (def len (xs)
75
94
  (if (pair? xs) (+ 1 (len (cdr xs)))
@@ -79,6 +98,7 @@
79
98
  (assign dynamics (hash))
80
99
 
81
100
  (mac dynamic (name)
101
+ ; creates a dynamic variable.
82
102
  (hash-set dynamics name t)
83
103
  (let with-mac-name (sym "w/~name")
84
104
  (w/uniq prev
@@ -93,20 +113,17 @@
93
113
  (def ,name () (hash-get (thread-locals) ',name))))))
94
114
 
95
115
  (mac on-err (handler . body)
116
+ ; executes 'body. If an error is raised, executes 'handler. Inside
117
+ ; 'handler, the parameter 'err refers to the error that was raised.
96
118
  `(handle-error (fn (err) ,handler)
97
119
  (fn () ,@body)))
98
120
 
99
121
  (mac ensure (protection . body)
122
+ ; executes 'body. Afterwards, executes 'protection.
123
+ ; 'protection is always executed even if there is an error.
100
124
  `(ensuring (fn () ,protection)
101
125
  (fn () ,@body)))
102
126
 
103
- (mac rfn (name parms . body)
104
- `(let ,name nil
105
- (assign ,name (fn ,parms ,@body))))
106
-
107
- (mac afn (parms . body)
108
- `(rfn self ,parms ,@body))
109
-
110
127
  (mac loop (start test update . body)
111
128
  (w/uniq (gfn gparm)
112
129
  `(do ,start
@@ -143,35 +160,6 @@
143
160
  acc))
144
161
  (car things) (cdr things)))
145
162
 
146
- (def dox-show-src (src)
147
- ; show 'src as source code.
148
- ; expect to override this later when pretty-printing is available
149
- (inspect src))
150
-
151
- (def dox-show-info (name what texts args src)
152
- ; show the given dox info
153
- ; 'info arg is a dox object from the dox system
154
- "~(if (eq? what 'mac) "Macro"
155
- (eq? what 'def) "Function"
156
- what) : ~name
157
-
158
- args : ~(inspect args)
159
-
160
- ~(joinstr "\n" texts)
161
-
162
- source
163
- ======
164
- ~(dox-show-src src)
165
- ")
166
-
167
- (mac dox (name)
168
- ; show dox for the given name
169
- `(let infos (dox-lookup ',name)
170
- (if (no infos)
171
- (p "No documentation for" ',name)
172
- (each info infos
173
- (p:apply dox-show-info info)))))
174
-
175
163
  (def proper? (list)
176
164
  ; t if this is a proper list (last cdr is nil)
177
165
  ; nil otherwise (last cdr is neither cons nor nil)
@@ -0,0 +1,85 @@
1
+ (def dox-show-src (src)
2
+ ; show 'src as source code.
3
+ ; expect to override this later when pretty-printing is available
4
+ (inspect src))
5
+
6
+ (def dox-show-info (name what texts args src)
7
+ ; show the given dox info
8
+ ; 'info arg is a dox object from the dox system
9
+ "~(if (eq? what 'mac) "Macro"
10
+ (eq? what 'def) "Function"
11
+ what) : ~name
12
+
13
+ args : ~(inspect args)
14
+
15
+ ~(joinstr "\n" texts)
16
+
17
+ source
18
+ ======
19
+ ~(dox-show-src src)
20
+ ")
21
+
22
+ (def dox-show-one-example (name example)
23
+ "~|name| ~(car example)
24
+
25
+ running :
26
+ ~(dox-show-src:cadr example)
27
+
28
+ produces : ~(dox-show-src:caddr example)
29
+
30
+ --------------------------------
31
+ ")
32
+
33
+ (def dox-show-examples (name examples)
34
+ (if examples
35
+ "
36
+ Examples for ~name
37
+ ==================
38
+
39
+ ~(joinstr "\n\n" (map (curry dox-show-one-example name) examples))
40
+
41
+ "))
42
+
43
+ (def dox-all-items ()
44
+ ; return all documentation items in a single list
45
+ (apply joinlists (map dox-lookup (dox-names))))
46
+
47
+ (def dox-with-documentation (dox-item)
48
+ ; a documentation filter that returns non-nil for items with text documentation
49
+ ; use with 'dox-all-items to gather dox items that are explicitly documented
50
+ ; for example (cleverly-show (collect dox-with-documentation (dox-all-items)))
51
+ (nth 2 dox-item))
52
+
53
+ (mac dox (name)
54
+ ; show dox for the given name, or all available dox if name is not given
55
+ (if name
56
+ `(let infos (dox-lookup ',name)
57
+ (if (no infos)
58
+ (p "No documentation for" ',name)
59
+ (each info infos
60
+ (p:apply dox-show-info info)))
61
+ (let examples (dox-examples ',name)
62
+ (if (no examples)
63
+ (p "No examples for" ',name)
64
+ (p (joinstr "\n" (map (curry dox-show-examples ',name) examples)))))
65
+ nil)
66
+ `(let infos (collect dox-with-documentation (dox-all-items))
67
+ (p "documentation available for the following:")
68
+ (each info infos
69
+ (p:inspect:firstn 2 info)))))
70
+
71
+ (def-colon-syntax dox names
72
+ (let target (cadr names)
73
+ `(dox ,(if (eq? target '||)
74
+ nil
75
+ target))))
76
+
77
+ (def explain-mac (n expr)
78
+ (if (eq? n 0)
79
+ expr
80
+ (let macfn (hash-get macs (car expr))
81
+ (if macfn
82
+ (explain-mac (- n 1)
83
+ (apply macfn
84
+ (cdr expr)))
85
+ expr))))