nydp 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 56ae5257ab6792eeaef112c3e81991dae7c43ce1
4
- data.tar.gz: 327b5770bc8f1c442202d340f5392288e58efdbe
3
+ metadata.gz: 1cb1c4582106367e04bc3ac5163db7401782331c
4
+ data.tar.gz: 3c8be4535a704cdbe02674796f4068d6f079880c
5
5
  SHA512:
6
- metadata.gz: 65b2df58e4d78c39e8ea8aacb1e3e0298269b5f2b03f6d9fb11e3ce44bf45012cd35b7b397a74de46d6e10f36a1b64be179c9efae28d5ca709959c50c9574e2b
7
- data.tar.gz: 9e787875c9011054dbaa9b416b84ef162f798bea1f5c33fde9a834322fc54e8435d537a37ce7a7e62e1972db95103aca67a3336b2e727bf7da07b94953af34f8
6
+ metadata.gz: 7523046a3da0fb43634736645674566d77c10a3178b911765d849503c9b66cdb32e9b17810365795bddc7f2bf5fe655d24c1e2f10b6253054987cdfdbe5a6a16
7
+ data.tar.gz: 82f1f13fb1e3badf6f80c8a9ca6077019b01c0f8e571c61b1e1ade4ba1c640c4c8945f9e3d77442d69e77382e5ecee60587d51b9010ff03ec10ee4bb422664c8
@@ -0,0 +1 @@
1
+ *.nydp linguist-language=NYDP
data/README.md CHANGED
@@ -1,35 +1,252 @@
1
- # Nydp
1
+ # NYDP
2
+
3
+ NYDP is a new LISP dialect, much inspired by [Arc](http://arclanguage.org/), and implemented in Ruby.
2
4
 
3
5
  NYDP is "Not Your Daddy's Parentheses", a reference to [Xkcd 297](http://xkcd.com/297/) (itself a reference
4
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,
5
7
  improved Q unlike the Q your daddy used. "NYDP" also shamelessly piggypacks on the
6
8
  catchiness and popularity of the [NYPD](https://en.wikipedia.org/wiki/NYPD_Blue) abbreviation ("New York Police Department",
7
- for those who have no interest in popular US TV or authoritarian politics).
9
+ for those who have no interest in popular US politics or TV).
10
+
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
+
13
+ The goal of NYDP is to allow untrusted users run sandboxed server-side scripts. By default, NYDP provides no system access :
14
+
15
+ * no file functions
16
+ * no network functions
17
+ * no IO other than $stdin and $stdout
18
+ * no process functions
19
+ * no threading functions
20
+ * no ruby calls
21
+
22
+ [Peruse NYDP's features here](lib/lisp/tests) in the `tests` directory.
23
+
24
+ ## Running
25
+
26
+ #### Get a REPL :
27
+
28
+ ```Shell
29
+ $ bundle exec bin/nydp
30
+ welcome to nydp
31
+ ^D to exit
32
+ nydp >
33
+ ```
34
+
35
+ The REPL uses the readline library so you can use up- and down-arrows to navigate history.
36
+
37
+ #### Invoking from Ruby
38
+
39
+ Suppose you want to invoke the function named `question` with some arguments. Do this:
40
+
41
+ ```ruby
42
+ ns = Nydp.build_nydp # keep this for later re-use, it's expensive to set up
8
43
 
9
- Macro-expansion is not built-in to the interpreter; however, the compiler will invoke 'pre-compile before compiling, passing
10
- the expression to compile as an argument. You can override 'pre-compile to transform the expression in any way you wish. By default,
11
- nydp provides an implementation of 'pre-compile that performs macro-expansion.
44
+ answer = Nydp.apply_function ns, :question, :life, ("The Universe" and everything())
12
45
 
46
+ ==> 42
13
47
  ```
48
+
49
+ `ns` is just a plain old ruby hash, 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`.
50
+
51
+ 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).
52
+
53
+
54
+ ## Different from Arc :
55
+
56
+ #### 1. Macro-expansion runs in lisp
57
+
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).
59
+
60
+ 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
+
62
+
63
+
64
+ ```lisp
14
65
  (def pre-compile (expr)
15
66
  (map pre-compile
16
67
  (if (mac-names (car expr))
17
68
  (pre-compile (mac-expand (car expr) (cdr expr)))
18
69
  expr)))
70
+
71
+ (mac yoyo (thing) `(do-yoyo ,thing))
72
+
73
+ nydp > (pre-compile '(yoyo 42))
74
+
75
+ ==> (do-yoyo 42)
76
+ ```
77
+
78
+
79
+ #### 2. Special symbol syntax
80
+
81
+ The parser detects syntax embedded in smybol names and emits a form whose first element names the syntax used. Here's an example:
82
+
83
+ ```lisp
84
+
85
+ nydp > (parse "x.y")
86
+
87
+ ==> (dot-syntax x y)
88
+
89
+ nydp > (parse "$x x$ x$x $x$ $$")
90
+
91
+ ==> (dollar-syntax || x) ; '|| is the empty symbol.
92
+ ==> (dollar-syntax x ||)
93
+ ==> (dollar-syntax x x)
94
+ ==> (dollar-syntax || x ||)
95
+ ==> (dollar-syntax || || ||)
96
+
97
+ nydp > (parse "!foo")
98
+
99
+ ==> (bang-syntax || foo)
100
+
101
+ nydp > (parse "!x.$y")
102
+
103
+ ==> (bang-syntax || (dot-syntax x (dollar-syntax || y)))
104
+
105
+ ```
106
+
107
+ Nydp provides macros for some but not all possible special syntax
108
+
109
+ ```lisp
110
+ nydp > (pre-compile 'x.y)
111
+
112
+ ==> (hash-get x 'y) ; 'dot-syntax is a macro that expands to perform hash lookups
113
+
114
+ nydp > (pre-compile 'x.y.z)
115
+
116
+ ==> (hash-get (hash-get x 'y) 'z)
117
+
118
+
119
+ nydp > (pre-compile '!eq?)
120
+
121
+ ==> (fn args (no (apply eq? args)))
122
+
123
+ nydp > (pre-compile '(!eq? a b))
124
+
125
+ ==> ((fn args (no (apply eq? args))) a b) ; equivalent to (no (eq? a b))
19
126
  ```
20
127
 
21
- ; blah blah
128
+ 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.
129
+
130
+ #### 3. Special list syntax
131
+
132
+ The parser detects alternative list delimiters
133
+
134
+ ```lisp
135
+ nydp > (parse "{ a 1 b 2 }")
136
+
137
+ ==> (brace-list a 1 b 2)
22
138
 
23
139
  ```
24
- ==> (comment "blah blah")
25
140
 
26
- ==> (mac comment (txt) nil)
141
+ `brace-list` is a macro that expands to create a hash literal. It assumes every (2n+1)th items are literal symbol keys, and every (2(n+1))th item is the corresponding value which is evaluated at run time.
142
+
143
+ ```lisp
144
+
145
+ nydp > { a 1 b (author-name) }
146
+
147
+ ==> {a=>1, b=>"conanite"}
148
+
149
+ ```
150
+
151
+
152
+
153
+ #### 4. Sensible, nestable string interpolation
154
+
155
+ The parser detects lisp code inside strings. When this happens, instead of emitting a string literal, the parser emits a form whose car is the symbol `string-pieces`.
156
+
157
+ ```lisp
158
+ nydp > (parse "\"foo\"")
159
+
160
+ ==> "foo"
161
+
162
+ nydp > (let bar "Mister Nice Guy" "hello, ~bar")
163
+
164
+ ==> hello, Mister Nice Guy
165
+
166
+ ; this is a more tricky example because we need to make a string with an interpolation token in it
167
+
168
+ nydp > (let s (joinstr "" "\"hello, " '~ "world\"") (parse s))
169
+
170
+ ==> (string-pieces "hello, " world "") ; "hello, ", followed by the interpolation 'world, followed by the empty string after 'world
171
+
172
+ nydp > (def also (str) "\nAND ALSO, ~str")
173
+ nydp > (with (a 1 b 2)
174
+ (p "Consider ~a : the first thing,
175
+ ~(also "Consider ~b : the second thing,
176
+ ~(also "Consider ~(+ a b), the third (and final) thing")")"))
177
+
178
+ ==> Consider 1 : the first thing,
179
+ ==> AND ALSO, Consider 2 : the second thing,
180
+ ==> AND ALSO, Consider 3, the third (and final) thing
27
181
  ```
28
182
 
29
- We do not wish to suggest by "Not Your Daddy's Parentheses" that Common Lisp,
30
- Scheme, Racket, Arc, Clojure or your favourite other lisp are somehow
31
- old-fashioned, inferior, or in need of improvement in any way.
183
+ 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.
184
+
185
+
186
+ #### 5. No continuations.
187
+
188
+ Sorry. While technically possible ... why bother?
189
+
190
+ #### 6. No argument destructuring
32
191
 
192
+ However, this doesn't need to be built-in, it can be done with macros alone.
193
+
194
+
195
+ ## Besides that, what can Nydp do?
196
+
197
+ #### 1. Functions and variables exist in the same namespace.
198
+ #### 2. Macros are maintained in a hash called 'macs in the main namespace.
199
+ #### 3. General [tail call elimination](https://en.wikipedia.org/wiki/Tail_call) allowing recursion without stack overflow in some cases.
200
+ #### 4. 'if like Arc:
201
+
202
+ ```lisp
203
+ (if a b c d e) ; equivalent to ruby :
204
+ ```
205
+
206
+ ```ruby
207
+ if a
208
+ b
209
+ elsif c
210
+ d
211
+ else e
212
+ ```
213
+
214
+ #### 5. Lexically scoped, but with macros to define dynamic variables backed by ruby threadlocals.
215
+
216
+ ```lisp
217
+ nydp> (dynamic foo)
218
+
219
+ nydp> (def do-something () (+ (foo) 1))
220
+
221
+ nydp> (w/foo 99 (do-something))
222
+
223
+ ==> 100
224
+
225
+ nydp> (foo)
226
+
227
+ ==> nil
228
+ ```
229
+
230
+ #### 6. Basic error handling
231
+
232
+ ```lisp
233
+ nydp> (on-err (p "error")
234
+ (ensure (p "make sure this happens")
235
+ (/ 1 0)))
236
+
237
+ make sure this happens
238
+ error
239
+ ```
240
+
241
+ #### 7 Intercept comments
242
+
243
+ ```lisp
244
+ nydp > (parse "; blah blah")
245
+
246
+ ==> (comment "blah blah")
247
+
248
+ By default, `comment` is a macro that expands to nil. If you have a better idea, go for it. (doc-comments for example)
249
+ ```
33
250
 
34
251
  ## Installation
35
252
 
@@ -45,9 +262,6 @@ Or install it yourself as:
45
262
 
46
263
  $ gem install nydp
47
264
 
48
- ## Usage
49
-
50
- TODO: Write usage instructions here
51
265
 
52
266
  ## Contributing
53
267
 
@@ -0,0 +1,18 @@
1
+ (def bm-pythag ()
2
+ (for i 1 100
3
+ (for j 1 100
4
+ (sqrt (+ (* i i) (* j j))))))
5
+
6
+ (def bmf (f n)
7
+ (for b 1 n (f)))
8
+
9
+ (def bm (f n)
10
+ (let time (millisecs)
11
+ (bmf f n)
12
+ (let elapsed (- (millisecs) time)
13
+ (p "took: ~elapsed ms")
14
+ (p "~n iterations, ~(/ elapsed n) ms per iteration")))
15
+ nil)
16
+
17
+ (def rbs ()
18
+ (bm bm-pythag 20))
@@ -1,12 +1,17 @@
1
1
  ; -*- lisp -*-
2
2
 
3
+ ;;
4
+ ;; Acknowledgements to Paul Graham. Some nydp features defined in this file (including,
5
+ ;; but not limited to, 'do, 'rfn, 'loop, 'for) are stolen directly from arc.arc
6
+ ;;
7
+
3
8
  (assign last-cons (fn (xs)
4
9
  (cond (pair? (cdr xs))
5
10
  (last-cons (cdr xs))
6
11
  xs)))
7
12
 
8
13
 
9
- (assign append-list! (fn (list-1 list-2)
14
+ (assign append-list (fn (list-1 list-2)
10
15
  (cdr-set (last-cons list-1) list-2)
11
16
  list-1))
12
17
 
@@ -15,7 +20,7 @@
15
20
  (assign caar (fn (arg) (car (car arg))))
16
21
  (assign cadr (fn (arg) (car (cdr arg))))
17
22
  (assign cddr (fn (arg) (cdr (cdr arg))))
18
- (assign no (fn (arg) (eq? arg nil)))
23
+ (assign no (fn (arg) (cond arg nil t)))
19
24
  (assign just (fn (arg) arg))
20
25
  (assign pargs (fn args (apply p args) (last args)))
21
26
 
@@ -145,6 +150,14 @@
145
150
  `(cond ,(car args) ,(cadr args)))
146
151
  (car args))))
147
152
 
153
+ (mac bang-syntax (pfx . rest)
154
+ (if (no (eq? pfx '||))
155
+ (error "Irregular ! syntax: got prefix ~(inspect pfx) in ~(joinstr "!" (cons pfx rest))"))
156
+ (if (cdr rest)
157
+ (error "Irregular ! syntax: got suffix ~(inspect (cdr rest)) in ~(joinstr "!" (cons pfx rest))"))
158
+ `(fn args
159
+ (no (apply ,(car rest) args))))
160
+
148
161
  (mac and args
149
162
  (if args
150
163
  (if (cdr args)
@@ -215,13 +228,12 @@
215
228
  (flattenize things))
216
229
  acc))
217
230
 
218
- (def joinstr (txt things)
219
- (if (no (pair? things))
220
- (error "joinstr : 'things is a %%(type-of things) expected a list : %%(inspect things)"))
221
- (apply +
222
- (to-string (car things))
223
- (flatten (zip (map (fn (_) txt) (cdr things))
224
- (map to-string (cdr things))))))
231
+ (def joinstr (txt . things)
232
+ (let joinables (flatten things)
233
+ (apply +
234
+ (to-string (car joinables))
235
+ (flatten (zip (map (fn (_) txt) (cdr joinables))
236
+ (map to-string (cdr joinables)))))))
225
237
 
226
238
  (let uniq-counter 0
227
239
  (def uniq (prefix)
@@ -263,6 +275,7 @@
263
275
  (iso (cdr x) (cdr y)))))
264
276
 
265
277
  (def isa (type obj) (eq? (type-of obj) type))
278
+ (def sym? (arg) (isa 'symbol arg))
266
279
  (mac just (arg) arg)
267
280
  (def quotify (arg) `(quote ,arg))
268
281
 
@@ -317,5 +330,51 @@
317
330
  (mac = (name value)
318
331
  (if (isa 'symbol name)
319
332
  `(assign ,name ,value)
320
- (caris 'dot-syntax name)
333
+ (caris 'dot-syntax name)
321
334
  (dot-syntax-assignment (cdr name) value)))
335
+
336
+ (def brace-list-hash-key (k)
337
+ (if (isa 'symbol k) `(quote ,k)
338
+ (caris 'unquote k) (cadr k)
339
+ k))
340
+
341
+ (def brace-list-build-hash (args)
342
+ (w/uniq hash
343
+ (let mappings (pairs args)
344
+ `(let ,hash (hash)
345
+ ,@(map (fn (m) `(hash-set ,hash ,(brace-list-hash-key (car m)) ,(cadr m))) mappings)
346
+ ,hash))))
347
+
348
+ (mac brace-list args
349
+ (if (no (cdr args))
350
+ (car args)
351
+ (brace-list-build-hash args)))
352
+
353
+ (mac on-err (handler . body)
354
+ `(handle-error (fn (err) ,handler)
355
+ (fn () ,@body)))
356
+
357
+ (mac ensure (protection . body)
358
+ `(ensuring (fn () ,protection)
359
+ (fn () ,@body)))
360
+
361
+ (mac rfn (name parms . body)
362
+ `(let ,name nil
363
+ (assign ,name (fn ,parms ,@body))))
364
+
365
+ (mac afn (parms . body)
366
+ `(rfn self ,parms ,@body))
367
+
368
+ (mac loop (start test update . body)
369
+ (w/uniq (gfn gparm)
370
+ `(do ,start
371
+ ((rfn ,gfn (,gparm)
372
+ (if ,gparm
373
+ (do ,@body ,update (,gfn ,test))))
374
+ ,test))))
375
+
376
+ (mac for (v init max . body)
377
+ (w/uniq (gi gm)
378
+ `(with (,v nil ,gi ,init ,gm (+ ,max 1))
379
+ (loop (assign ,v ,gi) (< ,v ,gm) (assign ,v (+ ,v 1))
380
+ ,@body))))
@@ -20,8 +20,39 @@
20
20
  (make-plus +seven 7)
21
21
  (make-plus +eleven 11)
22
22
 
23
+ ;
24
+ ; another contrived example to check deeply nested lexical scoping
25
+ ;
26
+ (let test-a0 "a0"
27
+ (def test-foo (f0 f1)
28
+ (with (w0 "w0" w1 "w1" w2 "w2" w3 "w3")
29
+ (let f (fn (x0) (joinstr " " test-a0 x0 f0 x0 f1))
30
+ (map f (list w0 w1 w2 w3))))))
31
+
23
32
  (register-test
24
33
  '(suite "Boot Tests"
34
+ (suite "hashtables"
35
+ ("build a hash table from brace-list syntax"
36
+ (let hsh { foo 1 bar 2 }
37
+ (list 'foo hsh.foo 'bar hsh.bar))
38
+ (foo 1 bar 2))
39
+
40
+ ("single-item brace list is just the thing itself"
41
+ (let zi 10 "finds the ~{zi}th item")
42
+ "finds the 10th item")
43
+
44
+ ("unquotes hash keys"
45
+ (with (zi 'foo chi 'bar yi 'grr)
46
+ (let hsh { ,zi 10 ,chi 11 ,yi 12 }
47
+ (list zi hsh.foo chi hsh.bar yi hsh.grr)))
48
+ (foo 10 bar 11 grr 12))
49
+
50
+ ("allows literal and invocation hash keys"
51
+ (with (zi "hello" chi "world")
52
+ (let hsh { (joinstr " " zi chi) 10 "yesterday" 11 }
53
+ (list "hello world" (hash-get hsh "hello world") "yesterday" (hash-get hsh "yesterday"))))
54
+ ("hello world" 10 "yesterday" 11)))
55
+
25
56
  (suite "list management"
26
57
  ("'pair breaks a list into pairs"
27
58
  (pairs '(1 a 2 b 3 c))
@@ -43,6 +74,18 @@
43
74
  (joinstr "" '("foo" "bar" "bax"))
44
75
  "foobarbax")
45
76
 
77
+ ("joins separate elements into a string"
78
+ (joinstr "/" "foo" "bar" "bax")
79
+ "foo/bar/bax")
80
+
81
+ ("joins a single thing"
82
+ (joinstr "/" "foo")
83
+ "foo")
84
+
85
+ ("joins nested and separate elements into a string"
86
+ (joinstr "/" "foo" "bar" '(twiddle diddle) "bax")
87
+ "foo/bar/twiddle/diddle/bax")
88
+
46
89
  ("joins elements into a string"
47
90
  (joinstr " - " '(1 2 3))
48
91
  "1 - 2 - 3")
@@ -54,9 +97,30 @@
54
97
  (suite "map"
55
98
  ("maps a function over a list of numbers"
56
99
  (map (fn (x) (* x x)) '(1 2 3))
57
- (1 4 9)))
100
+ (1 4 9))
101
+
102
+ ("maps a string join function over a list of strings"
103
+ (test-foo "x" "y")
104
+ ("a0 w0 x w0 y" "a0 w1 x w1 y" "a0 w2 x w2 y" "a0 w3 x w3 y"))
58
105
 
59
106
  (suite "pre-compile"
107
+ (suite "bang-syntax"
108
+ ("expansion"
109
+ (pre-compile '(!eq? a b))
110
+ ((fn args (no (apply eq? args))) a b))
111
+
112
+ ("bang-syntax for 'eq?"
113
+ (!eq? 1 2)
114
+ t)
115
+
116
+ ("bang-syntax for 'caris"
117
+ (!caris 'foo '(foo bar))
118
+ nil)
119
+
120
+ ("bang-syntax for 'caris"
121
+ (!caris 'foo '(zozo foo bar))
122
+ t))
123
+
60
124
  ("expands 'let"
61
125
  (do
62
126
  (def x+3*z (x y)