31

I know that JavaScript doesn't support macros (Lisp-style ones) but I was wondering if anyone had a solution to maybe simulate macros? I Googled it, and one of the solutions suggested using eval(), but as he said, would be quite costly.

They don't really have to be very fancy. I just want to do simple stuff with them. And it shouldn't make debugging significantly harder :)

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
Anders Rune Jensen
  • 3,758
  • 2
  • 42
  • 53

8 Answers8

29

You could use parenscript. That'll give you macros for Javascript.

Luís Oliveira
  • 2,964
  • 1
  • 20
  • 26
27

A library by Mozilla (called SweetJS) is designed to simulate macros in JavaScript. For example, you can use SweetJS to replace the function keyword with def.

Anderson Green
  • 30,230
  • 67
  • 195
  • 328
  • 2
    SweetJS hasn't been updated since 2017. Do you know what's the context of SweetJS in 2022 ? Beside mozilla, I haven't heard/seen other people/company using macros in JS. Do you have personal experience with this ? Thanks – Alan Andrade Aug 01 '22 at 17:37
12

One can also now use ClojureScript to compile clojure to javascript and get macros that way. Note ClojureScript uses Google Closure.

Anders Rune Jensen
  • 3,758
  • 2
  • 42
  • 53
8

I've written a gameboy emulator in javascript and I simulate macros for cpu emulation this way:

macro code (the function returns a string with the macro code):

function CPU_CP_A(R,C) { // this function simulates the CP instruction, 
  return ''+             // sets CPU flags and stores in CCC the number
  'FZ=(RA=='+R+');'+     // of cpu cycles needed
  'FN=1;'+
  'FC=RA<'+R+';'+
  'FH=(RA&0x0F)<('+R+'&0x0F);'+
  'ICC='+C+';';
}

Using the "macro", so the code is generated "on the fly" and we don't need to make function calls to it or write lots of repeated code for each istruction...

OP[0xB8]=new Function(CPU_CP_A('RB',4)); // CP B
OP[0xB9]=new Function(CPU_CP_A('RC',4)); // CP C
OP[0xBA]=new Function(CPU_CP_A('RD',4)); // CP D
OP[0xBB]=new Function(CPU_CP_A('RE',4)); // CP E
OP[0xBC]=new Function('T1=HL>>8;'+CPU_CP_A('T1',4)); // CP H
OP[0xBD]=new Function('T1=HL&0xFF;'+CPU_CP_A('T1',4)); // CP L
OP[0xBE]=new Function('T1=MEM[HL];'+CPU_CP_A('T1',8)); // CP (HL)
OP[0xBF]=new Function(CPU_CP_A('RA',4)); // CP A

Now we can execute emulated code like this:

OP[MEM[PC]](); // MEM is an array of bytes and PC the program counter

Hope it helps...

  • 1
    `new Function(string)` is an `eval()` context, and as such, has the same performance characteristics of `eval()` – Havvy Jan 11 '14 at 13:30
  • 4
    I do not think that is true. It may be an eval, but the eval only occurs once. Once it is a function, it should execute any number of times at normal function speed. – Blake McBride Jun 03 '16 at 14:08
  • IIRC, it used to be the case that `Function` objects were not optimized by most JS engines, unlike "plain" JavaScript code. However, it was years ago when I read this, so I wonder how well the average JS engine supports them now. Especially with all the effort going into closure optimization. – Job Jan 28 '19 at 13:06
6
function unless(condition,body) {
    return 'if(! '+condition.toSource()+'() ) {' + body.toSource()+'(); }';
}


eval(unless( function() {
    return false;
  }, function() {
    alert("OK");
}));
  • Not a bad idea, but sadly the solution adds a eval and 2x function definitions too much. +1 for trying though. – Anders Rune Jensen Oct 26 '09 at 22:46
  • 1
    Macroses are expanded at COMPILE time, so we need to add compilation stage to JavaScript or forget about macroses. We can compile JavaScript by call to eval() function only, so we need eval() anyway. – Volodymyr M. Lisivka Nov 06 '09 at 15:10
  • macro are syntaxic sugar for more consise and expressive code. They do not increase theorically the type of thing you can do or not. They are expended one time at compile time, making the runtime cost to zero. Example here fail: eval will be called each time and the code is more verbose and less lisible than if one directly wrote the equivalent javascript. For the macros to be usefull you should be able to use it with a syntax like unless('false','alert("OK")'); – Nicolas Bousquet Jul 22 '11 at 09:08
  • @NicolasBousquet Macros give you the ability to manipulate code before it gets executed (and before it gets compiled, macro expansion time). You cannot do that without macros. – Luka Ramishvili May 15 '12 at 06:00
  • In the end, the code generated by macros is as powerful as the code written by hand, but any turing-complete language would give you that. What matters is that they increase what *you* can do. – Luka Ramishvili May 15 '12 at 06:01
3

LispyScript is the latest language that compiles to Javascript, that supports macros. It has a Lisp like tree syntax, but also maintains the same Javascript semantics. Disclaimer: I am the author of LispyScript.

Santosh
  • 1,928
  • 2
  • 15
  • 13
1

Check out the Linux/Unix/GNU M4 processor. It is a generic and powerful macro processor for any language. It is especially oriented towards Algol-style languages of which JavaScript is a member.

Blake McBride
  • 394
  • 3
  • 16
-2

Javascript is interpreted. Eval isn't any more costly that anything else in Javascript.

James Curran
  • 101,701
  • 37
  • 181
  • 258
  • 1
    Wrong! James, please read up on this question:http://stackoverflow.com/questions/86513/why-is-using-javascript-eval-function-a-bad-idea#87260 and validate your opinions before making misleading statements. The string passed to an eval must be parsed/interpreted every time the eval is called! – Ash Oct 11 '08 at 04:46
  • Yes, the string passed to eval must parsed every time eval is called -- but so must every other line of javascript. that's how interpreters works. As for the answer you linked to, he never mentions speed, just "much easier to read as well as less potentially buggy" – James Curran Oct 11 '08 at 05:16
  • 2
    I'll grant that this possibly may have been the case with Javascript in browsers say around 2000, but today there are serious optimisations being applied to plain Javascript code (ie non-eval'd) and this will only continue. Code in a string in any language cannot be optimised anywhere near as well. – Ash Oct 11 '08 at 05:36
  • 7
    Ash, the point James is trying to make, I believe, is that for a one time deal, eval works as quickly as literal JavaScript because *either way* the interpreter is doing the same thing - taking a string and executing it. What do you think literal JavaScript is? It is a string in an HTML file. – Jason Bunting Oct 11 '08 at 19:14