Skip to content

Commit 5f7f584

Browse files
author
impinball
committed
Betterify property access, implement ES5 getters and ES6 syntax for objects
This intentionally reserves generators, because those haven't been implemented in functions yet. It will be easy to add when they are implemented elsewhere, though.
1 parent bdc02b7 commit 5f7f584

File tree

7 files changed

+453
-206
lines changed

7 files changed

+453
-206
lines changed

doc/basics-reference.markdown

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,6 @@ generate arbitrary JavaScript are built in to eslisp.
156156
| `regex` | regular expression literal |
157157
| `var` | variable declaration |
158158
| `.` | member expression |
159-
| `get` | *computed* member expression |
160159
| `switch` | switch statement |
161160
| `if` | conditional statement |
162161
| `?:` | ternary expression |
@@ -291,13 +290,13 @@ elements.
291290
];
292291

293292
Object literals are created with the `object` macro which expects its
294-
parameters to be alternating keys and values.
293+
parameters to be simple pairs keys and values.
295294

296295
<!-- !test in object macro -->
297296

298297
(object)
299-
(object a 1)
300-
(object "a" 1 "b" 2)
298+
(object (:a 1))
299+
(object ("a" 1) ("b" 2))
301300

302301
<!-- !test out object macro -->
303302

@@ -308,13 +307,63 @@ parameters to be alternating keys and values.
308307
'b': 2
309308
});
310309

310+
ES5 getters and setters can be used.
311+
312+
<!-- !test in object getter setter -->
313+
314+
(var data 0)
315+
(object
316+
(get :data () (return data))
317+
(set :data (value) (= data value)))
318+
319+
<!-- !test out object getter setter -->
320+
321+
var data = 0;
322+
({
323+
get data() {
324+
return data;
325+
},
326+
set data(value) {
327+
data = value;
328+
}
329+
});
330+
331+
ES6 methods, property shorthand, computed properties, etc. can be used. Computed
332+
properties can even be used with getters. Generators have not yet been
333+
implemented, though, so generator methods are not available.
334+
335+
<!-- !test in object es6 -->
336+
337+
(var prop 2)
338+
(var data (Symbol "data"))
339+
(object
340+
(:prop)
341+
((. Symbol :toStringTag) "foo")
342+
(:method (arg) (return (+ arg 1)))
343+
(get data () (return 1)))
344+
345+
<!-- !test out object es6 -->
346+
347+
var prop = 2;
348+
var data = Symbol('data');
349+
({
350+
prop,
351+
[Symbol.toStringTag]: 'foo',
352+
method(arg) {
353+
return arg + 1;
354+
},
355+
get [data]() {
356+
return 1;
357+
}
358+
});
359+
311360
Property access uses the `.` macro.
312361

313362
<!-- !test in property access macro -->
314363

315364
(. a 1)
316-
(. a b (. c d))
317-
(. a 1 "b" c)
365+
(. a :b (. c :d))
366+
(. a 1 "b" :c)
318367

319368
<!-- !test out property access macro -->
320369

@@ -325,13 +374,13 @@ Property access uses the `.` macro.
325374
If you wish you could just write those as `a.b.c` in eslisp code, use the
326375
[*eslisp-propertify*][10] user-macro.
327376

328-
For *computed* property access, use the `get` macro.
377+
For *computed* property access, omit the leading colon.
329378

330379
<!-- !test in computed property access macro -->
331380

332-
(get a b)
333-
(get a b c 1)
334-
(= (get a b) 5)
381+
(. a b)
382+
(. a b c 1)
383+
(= (. a b) 5)
335384

336385
<!-- !test out computed property access macro -->
337386

@@ -398,9 +447,9 @@ the `default`-case clause.
398447
<!-- !test in switch statement -->
399448

400449
(switch x
401-
(1 ((. console log) "it is 1")
450+
(1 ((. console :log) "it is 1")
402451
(break))
403-
(default ((. console log) "it is not 1")))
452+
(default ((. console :log) "it is not 1")))
404453

405454
<!-- !test out switch statement -->
406455

@@ -499,7 +548,7 @@ header, the second to be the right, and the rest to be body statements.
499548
<!-- !test in for-in loop -->
500549

501550
(forin (var x) xs
502-
((. console log) (get xs x)))
551+
((. console :log) (. xs x)))
503552

504553
<!-- !test out for-in loop -->
505554

@@ -566,7 +615,7 @@ or `finally`, in which case they are treated as the catch- or finally-clause.
566615
(catch err
567616
(logError err)
568617
(f a b))
569-
(finally ((. console log) "done")))
618+
(finally ((. console :log) "done")))
570619

571620
<!-- !test out try-catch -->
572621

doc/how-macros-work.markdown

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,12 @@ Yey!
5555

5656
We could of course have written the macro function in eslisp instead:
5757

58-
(= (. module exports)
58+
(= (. module :exports)
5959
(lambda (name)
60-
(return ((. this list)
61-
((. this atom) "=")
60+
(return ((. this :list)
61+
((. this :atom) "=")
6262
name
63-
((. this string) "hello")))))
63+
((. this :string) "hello")))))
6464

6565
That compiles to the same JS before. In fact, you can write macros in any
6666
language you want, as long as you can compile it to JS before `require`-ing it
@@ -74,12 +74,12 @@ syntax for *quoting*, which makes macro return values much easier to read:
7474
To make macros clearer to read, eslisp has special syntax for returning stuff
7575
that represents code. Let's rewrite the previous hello-assigning macro:
7676

77-
(= (. module exports) (lambda (name) (return `(var ,name "hello"))))
77+
(= (. module :exports) (lambda (name) (return `(var ,name "hello"))))
7878

7979
That does exactly the same thing, but it contains less of the
8080
`atom`/`list`/`string` constructor fluff, so it's clearer to read. The `(.
8181
this list)` constructor is replaced with a `` ` `` (backtick). The `var` atom
82-
no longer needs to be written explicitly as `((. this atom) var)` and there's
82+
no longer needs to be written explicitly as `((. this :atom) var)` and there's
8383
now a `,` (comma) before `name`.
8484

8585
In various other Lisp family languages that eslisp is inspired by, the backtick
@@ -97,10 +97,10 @@ like
9797
module.exports = function (name) {
9898
return {
9999
type : "list",
100-
values : Array.prototype.concat(
100+
values : [].concat(
101101
[ { type : "atom", value : "var" } ],
102102
[ name ],
103-
[ { type : "string" value : "hello" ]
103+
[ { type : "string", value : "hello" } ]
104104
)
105105
};
106106
};
@@ -117,13 +117,13 @@ expression necessary to calculate the mean of some variables, you could do
117117
(lambda ()
118118

119119
; Convert arguments object to an array
120-
(var argumentsAsArray ((. Array prototype slice call) arguments 0))
120+
(var argumentsAsArray ((. Array :prototype :slice :call) arguments 0))
121121

122122
; Make an eslisp list object from the arguments
123-
(var args ((. this list apply) null argumentsAsArray))
123+
(var args ((. this :list :apply) null argumentsAsArray))
124124

125125
; Make an eslisp atom representing the number of arguments
126-
(var total ((. this atom) (. arguments length)))
126+
(var total ((. this :atom) (. arguments :length)))
127127

128128
; Return a division of the sum of the arguments by the total
129129
(return `(/ (+ ,@args) ,total))))
@@ -179,9 +179,9 @@ list.
179179
; Redefine the macro in an inner scope
180180
(macro one (lambda () (return '1.1))) ; "very large value of 1"
181181

182-
((. console log) (one)))
182+
((. console :log) (one)))
183183

184-
((. console log) (one))
184+
((. console :log) (one))
185185

186186
<!-- !test out function-expression scope macro -->
187187

@@ -266,7 +266,7 @@ call it with multiple arguments and return that.
266266
<!-- !test in increment twice -->
267267

268268
(macro incrementTwice
269-
(lambda (x) (return ((. this multi) `(++ ,x) `(++ ,x)))))
269+
(lambda (x) (return ((. this :multi) `(++ ,x) `(++ ,x)))))
270270

271271
(incrementTwice hello)
272272

@@ -290,9 +290,9 @@ compile-time:
290290
<!-- !test in precompute -->
291291

292292
(macro precompute
293-
(lambda (list) (return ((. this atom) ((. this evaluate) list)))))
293+
(lambda (list) (return ((. this :atom) ((. this :evaluate) list)))))
294294

295-
(precompute (+ 1 2 (* 5 (. Math PI))))
295+
(precompute (+ 1 2 (* 5 (. Math :PI))))
296296

297297
compiles to
298298

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,14 @@
3939
"source-map": "^0.5.3",
4040
"tape": "^4.0.0",
4141
"tests-ex-markdown": "^2.0.0",
42-
"tmp": "0.0.28"
42+
"tmp": "0.0.28",
43+
"uuid": "^2.0.1"
4344
},
4445
"dependencies": {
4546
"chalk": "^1.1.1",
4647
"concat-stream": "^1.4.7",
4748
"convert-source-map": "^1.1.2",
48-
"escodegen": "^1.4.1",
49+
"escodegen": "^1.7.1",
4950
"esutils": "^2.0.2",
5051
"esvalid": "1.1.0",
5152
"nopt": "^3.0.3",

readme.markdown

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ your own language features, [like this][9].
1818
; Only include given statement if `$DEBUG` environment variable is set
1919
(macro debug
2020
(lambda (statement)
21-
(return (?: (. process env DEBUG)
21+
(return (?: (. process :env :DEBUG)
2222
statement
2323
null))))
2424

2525
(var fib ; Fibonacci number sequence
2626
(lambda (x)
27-
(debug ((. console log) (+ "resolving number " x)))
27+
(debug ((. console :log) (+ "resolving number " x)))
2828
(switch x
2929
(0 (return 0))
3030
(1 (return 1))
@@ -132,8 +132,9 @@ arguments as the rest:
132132
<!-- !test in simple macros -->
133133

134134
; The "." macro compiles to property access.
135-
(. a b)
136-
(. a b 5 c "yo")
135+
(. a :b)
136+
(. a :b c)
137+
(. a :b 5 :c "yo")
137138

138139
; The "+" macro compiles to addition.
139140
(+ 1 2)
@@ -143,10 +144,11 @@ arguments as the rest:
143144
<!-- !test out simple macros -->
144145

145146
a.b;
147+
a.b[c];
146148
a.b[5].c['yo'];
147149
1 + 2;
148150

149-
If the `(. a b)` syntax feels tedious, you might like the [eslisp-propertify][34] transform macro, which lets you write `a.b` instead.
151+
If the `(. a :b)` syntax feels tedious, you might like the [eslisp-propertify][34] transform macro, which lets you write `a.b` instead.
150152

151153
If the first element of a list isn't the name of a macro which is in scope, it
152154
compiles to a function call:
@@ -171,7 +173,7 @@ These can of course be nested:
171173
(var x (+ 1 (* 2 3)))
172174

173175
; Calling the result of a property access expression
174-
((. console log) "hi")
176+
((. console :log) "hi")
175177

176178
<!-- !test out nested macros -->
177179

@@ -293,7 +295,7 @@ Macros can use [`quasiquote`][36] (`` ` ``), `unquote` (`,`) and
293295
<!-- !test in macro and call -->
294296

295297
(macro m (lambda (x) (return `(+ ,x 2))))
296-
((. console log) (m 40))
298+
((. console :log) (m 40))
297299

298300
<!-- !test out macro and call -->
299301

@@ -307,9 +309,9 @@ S-expression atom.
307309
<!-- !test in evaluate in macro -->
308310

309311
(macro add2 (lambda (x)
310-
(var xPlusTwo (+ ((. this evaluate) x) 2))
311-
(return ((. this atom) xPlusTwo))))
312-
((. console log) (add2 40))
312+
(var xPlusTwo (+ ((. this :evaluate) x) 2))
313+
(return ((. this :atom) xPlusTwo))))
314+
((. console :log) (add2 40))
313315

314316
<!-- !test out evaluate in macro -->
315317

@@ -320,8 +322,8 @@ You can return multiple statements from a macro with `this.multi`.
320322
<!-- !test in multiple-return macro -->
321323

322324
(macro log-and-delete (lambda (varName)
323-
(return ((. this multi)
324-
`((. console log) ((. JSON stringify) ,varName))
325+
(return ((. this :multi)
326+
`((. console :log) ((. JSON :stringify) ,varName))
325327
`(delete ,varName)))))
326328

327329
(log-and-delete someVariable)
@@ -338,9 +340,9 @@ compilation side-effects and conditional compilation.
338340

339341
; Only include statement if `$DEBUG` environment variable is set
340342
(macro debug (lambda (statement)
341-
(return (?: (. process env DEBUG) statement null))))
343+
(return (?: (. process :env :DEBUG) statement null))))
342344

343-
(debug ((. console log) "debug output"))
345+
(debug ((. console :log) "debug output"))
344346
(yep)
345347

346348
<!-- !test out nothing-returning macro -->
@@ -358,9 +360,9 @@ and the variables in the IIFE are shared between them.
358360
(macro ((lambda ()
359361
(var x 0) ; visible to all of the macro functions
360362
(return
361-
(object increment (lambda () (return ((. this atom) (++ x))))
362-
decrement (lambda () (return ((. this atom) (-- x))))
363-
get (lambda () (return ((. this atom) x))))))))
363+
(object (:increment (lambda () (return ((. this :atom) (++ x)))))
364+
(:decrement (lambda () (return ((. this :atom) (-- x)))))
365+
(:get (lambda () (return ((. this :atom) x)))))))))
364366

365367
(increment)
366368
(increment)
@@ -415,7 +417,7 @@ The compiler runs as a [REPL][42] if given no arguments, though it doesn't
415417

416418
You can also just pipe data to it to compile it if you want.
417419

418-
echo '((. console log) "Yo!")' | eslc
420+
echo '((. console :log) "Yo!")' | eslc
419421

420422
Or pass a filename, like `eslc myprogram.esl`.
421423

0 commit comments

Comments
 (0)