jashkenas/coffee-script — pull request #3171 by vanviegen (2013-09-18 17:03:12+00:00)
Title: Hygienic macro proposal

Here's the macro system we've been using internally. It's not very lispy, but that might be a good thing to most. :) I'd say it's pretty clean, well, for a macro system anyway...

Some examples. Optionally compiled debug info:
```coffeescript
macro debug (args...) -> new @Call(new @Literal("debugImpl"), args) if @debugging
macro -> @debugging = true
debug "test", 123 # console.log gets generated
macro -> @debugging = false
debug "test", somethingHeavy() # nothing gets into the output js
```

The mandatory swap example. Cheating a bit by using deconstruction, but this does show some of the more interesting features. The coffeescript code "[x,y] = [y,x]" get translated to nodes, and then an identifier substitution replaces x and y by whatever nodes are the arguments.
```coffeescript
macro swap (a,b) -> @substitute @csToNode("[x,y] = [y,x]"), {x:a,y:b}
swap c, d
```

If you want, you can go pretty crazy with this. Non-sense from the unit test:
```coffeescript
tst = (a,b) -> a*b
tst2 = -> 4
macro calc (c1,c2,c3,c4) -> @substitute @csToNode("x = (a) -> tst(a,c1) * c2\nx(c3) + x(c4)"), {c1,c2,c3,c4}
eq 144, calc 6, 3, 5, 3
eq 144, calc (macro -> @csToNode "tst2()+2"), 3, 5, 3
```

One thing we're using compile-time logic for, is resource versioning of web resources (so we can have clients cache them permanently, and all references to the will automatically change the (otherwise ignored) version number whenever the resource is updated.
```coffeescript
macro -> @getFileVer = (file) -> 13 # stub
macro resUrl (name) ->
name = @nodeToVal name
@valToNode "/data/#{name}?v=" + @getFileVer("static/#{name}")
someImg.src = resUrl 'test.png'
```

Also one of the more interesting applications (for us), is string translations.
```coffeescript
injectAndPluralize = (msg,arg) -> msg.replace("%1",arg).replace(/[\[\]]/g,'') # stub

macro -> @transDict = waterBottles: "%1 bottle[s] of water" # can be loaded from a file

macro trans (args...) ->
text = @transDict[@nodeToId args[0]]
args[0] = @valToNode text
new @Call(new @Literal("injectAndPluralize"), args)

someExample = trans waterBottles, 17

```

In this example, we're just doing the actual translation compile-time, but one could easily improve upon this by resolving the string insertions at compile time, and generating custom code for the pluralizations.
One could even accept the coffeescript string interpolation syntax. Eg:
```coffeescript
console.log trans "I'm to be translated within #{days} day[[s]]!"
```
would translate to:
```javascript
console.log "Vertaal mij binnen #{days} dag#{days==1?'':'en'}!"
```

I'm quite curious to here some thoughts about this approach...

Regards,
Farnk