Skip to content

Commit

Permalink
Remove this.multi from macro env (arrays instead)
Browse files Browse the repository at this point in the history
Making macros return an instance of the specific passed-in
"multipleReturn" class was used to be necessary before the most recent
major changes to the macro return format, because it avoided confusion
with actual code.  (Arrays used to be used as the S-expression AST
format.)

Now such a requirement is unnecessarily strict.  Reducing reliance on
the `this`-environment passed to a macro reduces API surface area and
makes the macro functions easier to test in isolation.
  • Loading branch information
anko committed Jan 19, 2016
1 parent 3c74426 commit eec8e6f
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 64 deletions.
22 changes: 0 additions & 22 deletions doc/how-macros-work.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -258,28 +258,6 @@ When macros are called, the function associated with them is called with a
particular `this`-context, such that the `this` object contains properties with
various handy helper functions:

### `this.multi`

Allows you to return multiple expressions or statements from a macro. Just
call it with multiple arguments and return that.

<!-- !test in increment twice -->

(macro incrementTwice
(lambda (x) (return ((. this multi) `(++ ,x) `(++ ,x)))))

(incrementTwice hello)

compiles to

<!-- !test out increment twice -->

++hello;
++hello;

If your macro internally calls other macro functions, and want to know if those
returned multiple statements, just check if it's an instance of `this.multi`.

### `this.evaluate`

Lets you compile and run eslisp code at compile-time.
Expand Down
4 changes: 2 additions & 2 deletions readme.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -315,12 +315,12 @@ S-expression atom.

console.log(42);

You can return multiple statements from a macro with `this.multi`.
You can return multiple statements from a macro by returning an array.

<!-- !test in multiple-return macro -->

(macro log-and-delete (lambda (varName)
(return ((. this multi)
(return (array
`((. console log) ((. JSON stringify) ,varName))
`(delete ,varName)))))

Expand Down
25 changes: 12 additions & 13 deletions src/compile.ls
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
{ multiple-statements } = require \./import-macro

looks-like-positive-number = (atom-text) ->
atom-text.match /^\d+(\.\d+)?$/
looks-like-negative-number = (atom-text) ->
Expand Down Expand Up @@ -141,19 +139,20 @@ list-to-estree = (env, { values }:ast, options={}) ->
switch typeof! macro-return
| \Null => fallthrough
| \Undefined => null
| \Array =>

macro-return.for-each ->
switch typeof! it
| \Object => # that's OK
| otherwise =>
throw Error "Unexpected `#that` value received in multi-return"
macro-return.map ->
ast-to-estree env, it
..?loc ||= head.location

| \Object =>

if macro-return instanceof multiple-statements
macro-return.statements.for-each ->
switch typeof! it
| \Object => # that's OK
| otherwise =>
throw Error "Unexpected `#that` value received in multi-return"
macro-return.statements.map ->
ast-to-estree env, it
..?loc ||= head.location

else if macro-return.type in <[ atom list string ]>
if macro-return.type in <[ atom list string ]>
ast-to-estree env, macro-return
..?loc ||= head.location

Expand Down
4 changes: 0 additions & 4 deletions src/env.ls
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
es-generate = require \escodegen .generate _
compile = require \./compile

{ multiple-statements } = require \./import-macro

# Recursively search a macro table and its parents for a macro with a given
# name. Returns `null` if unsuccessful; a macro representing the function if
# successful.
Expand Down Expand Up @@ -99,8 +97,6 @@ class env
#
js |> eval

multi : multiple-statements

derive : ~>
# Create a derived environment with this one as its parent. This
# implements macro scope; macros defined in the new environment aren't
Expand Down
14 changes: 3 additions & 11 deletions src/import-macro.ls
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@
ast-errors = require \./esvalid-partial
{ is-expression } = require \esutils .ast

# This is only used to let macros return multiple statements, in a way
# detectable as different from other return types with an
# `instanceof`-check.
class multiple-statements
(...args) ~>
@statements = args

statementify = require \./es-statementify

# Only used directly by aliases
Expand All @@ -24,12 +17,11 @@ create-transform-macro = (env, func) ->

result = func.apply env, args

if result instanceof multiple-statements
return result.statements
if typeof! result is \Array
return result
else return [ result ]

module.exports = {
import-compilerspace-macro,
create-transform-macro,
multiple-statements
create-transform-macro
}
23 changes: 11 additions & 12 deletions test.ls
Original file line number Diff line number Diff line change
Expand Up @@ -849,8 +849,8 @@ test "macro constructor loading from IIFE can load nothing" ->
"""
..`@equals` ""

test "macro can return multiple statements with `multi`" ->
esl "(macro declareTwo (lambda () (return ((. this multi) '(var x 0) '(var y 1)))))
test "macro can return multiple statements by returning an array" ->
esl "(macro declareTwo (lambda () (return (array '(var x 0) '(var y 1)))))
(declareTwo)"
..`@equals` "var x = 0;\nvar y = 1;"

Expand Down Expand Up @@ -946,10 +946,10 @@ test "macros required from separate modules can access complation env" ->
fs.write-sync fd, """
module.exports = function() {
// Return two statements: a string and a generated symbol
return this.multi(
return [
this.atom("ok"),
this.atom("ok2")
);
];
};
"""

Expand Down Expand Up @@ -999,8 +999,8 @@ test "macro-generating macro" -> # yes srsly
test "macro generating macro and macro call" -> # yes srsly squared
esl '''
(macro define-and-call (lambda (x)
(return ((. this multi) `(macro what (lambda () (return `(hello))))
`(what)))))
(return (array `(macro what (lambda () (return `(hello))))
`(what)))))
(define-and-call)
'''
..`@equals` "hello();"
Expand Down Expand Up @@ -1038,7 +1038,7 @@ test "invalid AST returned by macro throws error" ->
test "macro multi-returning with bad values throws descriptive error" ->
try
esl '''
(macro breaking (lambda () (return ((. this multi) null))))
(macro breaking (lambda () (return (array null))))
(breaking)
'''
catch e
Expand Down Expand Up @@ -1069,7 +1069,7 @@ test "macro can return estree object" ->
test "macro can multi-return estree objects" ->
esl '''
(macro identifiers (lambda ()
(return ((. this multi)
(return (array
(object "type" "Identifier"
"name" "x")
(object "type" "Identifier"
Expand All @@ -1081,7 +1081,7 @@ test "macro can multi-return estree objects" ->
test "macro can multi-return a combination of estree and sexprs" ->
esl '''
(macro identifiers (lambda ()
(return ((. this multi)
(return (array
(object "type" "Identifier"
"name" "x")
'x))))
Expand Down Expand Up @@ -1132,13 +1132,12 @@ test "transform-macro can receive arguments" ->

test "transform-macro can multi-return" ->
wrapper = (...args) ->
this.multi (@atom \hi), (@atom "yo")
[ (@atom \hi), (@atom "yo") ]
esl "" transform-macros : [ wrapper ]
.. `@equals` "hi;\nyo;"

test "multiple transform-macros can be used" ->
wrapper-passthrough = (...args) ->
this.multi.apply null args
wrapper-passthrough = (...args) -> args
esl "(+ 1 2)" transform-macros : [ wrapper-passthrough, wrapper-passthrough ]
.. `@equals` "1 + 2;"

Expand Down

0 comments on commit eec8e6f

Please sign in to comment.