The cheat-js Reference Manual

Table of Contents

Next: , Previous: , Up: (dir)   [Contents][Index]

The cheat-js Reference Manual

This is the cheat-js Reference Manual, generated automatically by Declt version 2.4 "Will Decker" on Wed Jun 20 10:54:58 2018 GMT+0.


Next: , Previous: , Up: Top   [Contents][Index]

1 Introduction

Cheat-JS - macros for JavaScript. Kinda.

This document assumes you already know Common Lisp and JavaScript.

About Cheat-JS

Lisp macros are powerful and easy to implement because Lisp programs are made of s-expressions.

Lisp-style macros are difficult to add to other languages because most languages have very non-uniform syntax compared to Lisp. Source transformations (and most importantly macros) would be easier in, say, JavaScript, if it were possible to convert the JavaScript code into s-expressions, transform it, and convert it back into JavaScript code.

Turns out that we can transform JavaScript code into an AST made of s-expressions using parse-js. Converting back into JavaScript code can be done with cl-uglify-js (ironically, cl-uglify-js:ast-gen-code is a capable pretty printer). All that remains to be done to have macros (well, defined in another language) is define transformations to be applied on the output of parse-js. This is what Cheat-JS does: get the parse-js AST, apply the transformations, convert back to JavaScript code.

The idea is rather obvious - the main reason Cheat-JS exists is that I could not find something similar on the net. There are probably many people who privately do similar things with tools like parse-js and the pretty-printer part of cl-uglify-js - that, or my Google skills failed me :)

Important Note

Cheat-JS includes a modified version of parse-js, written by Marijn Haverbeke. This is necessary because I (Miron Brezuleanu) needed to modify parse-js a little. The license of parse-js is in the LICENSE-parse-js.txt file. The modified files from parse-js included in Cheat-JS are parse.lisp, tokenize.lisp and util.lisp. The modifications were permitted by the parse-js license. This is not an official copy of parse-js and is not supported by Marijn Haverbeke. If the modified parsing code in Cheat-JS breaks, it's exclusively my fault - I messed up the code.

Cheat-JS also uses cl-uglify-js unmodified, via Quicklisp. These two libraries do most of the work, Cheat-JS is mostly 'glue code'.

BIG WARNING

I haven't used Cheat-JS on any large projects. I don't have enough imagination to compensate for this lack of experience, so it may have a lot of problems I haven't thought about. Right now it's just a proof of concept.

Some simple examples

Instead of writing:

var Person = function(name, shoeSize) {
    this.name = name;
    this.shoeSize = shoeSize;
};

Cheat-JS makes it possible to write:

var Person = @defclass(name, shoeSize);

(Some irregularities in JavaScript syntax make it much harder to expand something like @defclass(Person, name, shoeSize); into function Person(name, shoeSize) { this.name = name; this.shoeSize = shoeSize; };).

This assumes that we have defined a @defclass macro which does the above expansion - we'll define two such macros in this document.

One of the parse-js modifications necessary for this to work is allow @ as a character in identifiers (the Cheat-JS recommended convention for naming macros is @ followed by the macro name). Currently macro names can't be nested (i.e. some.namespace.@iife is not valid syntax).

Instead of writing:

var greeter = (function () {
    return {
        'hello': function(name) {
            console.log('hello, '+ name);
        };
    };
}());

Cheat-JS makes it possible to write:

var greeter = @iife(
    return {
        'hello': function(name) {
            console.log('hello, '+ name);
        }
    };
);

This assumes that we have defined a @iife macro that wraps its arguments with (function () { ... }()) (meaning of the IIFE acronym).

I also had to modify parse-js to convince it to parse macro invocations that look like function calls, but have a list of statements instead of a list of parameters (see the invocation of @iife above).

Instead of writing:

(function () {
    var testResult = someTest();
    if (testResult) {
        console.log("Yes!");
        console.log("We passed the test with result ", testResult, ".");
    }
})();

Cheat-JS makes it possible to write:

@whenLet(testResult, someTest();
    console.log("Yes!");
    console.log("We passed the test with result ", testResult, ".");
);

This macro is similar to alexandria:when-let. @whenLet has the most complicated interface possible for a Cheat-JS macro: its argument list has both expressions (testResult, someTest()) and statements (the console.log call). When invoking such macros, separate the expressions and the statements with a semicolon, as in the example above.

It is of course possible to define the anaphoric version of @whenLet, @awhen (from On Lisp, page 190).

The guide on how to write Cheat-JS macros (below in this document) is based on defining @defclass (and even a safer version of @defclass), @iife, @whenLet and @awhen.

Getting started

You can get Cheat-JS at http://github.com/mbrezu/cheat-js. It's probably best to git clone it inside the local-projects directory of your Quicklisp install, so you can load it with (ql:quickload :cheat-js) in your REPL.

Note: I've only tested it with SBCL, so I recommend you use SBCL too. It should work with other CL implementations, though (all the code required is standard CL).

Running the tests with:

(cheat-js:run-tests)

gives some confidence that things are not obviously broken (they are most likely broken, but in ways that are subtle enough to fool the tests).

The next section will provide you with some pointers on how to define your Cheat-JS macros. If you run into problems, look at the tests.lisp file for example code - the code there matches the text in the next section.

Your first Cheat-JS macros

Before running the macroexpansion function cheat-js:explode on your JavaScript source code, you need to install your macros.

First reset the list of installed macros:

> (cheat-js:clear-macros)

To define a macro, you need to know three things:

Let's define @defclass.

Defining @defclass

We need to see how the macro invocation looks like in JavaScript:

var Person = @defclass(name, shoeSize)

We can tell that the macro is an 'args only' macro (i.e. the invocation looks like a normal JavaScript function invocation, it does not contains statements). We can inform Cheat-JS about this:

> (cheat-js:register-args-macro "@defclass")

We also want to see the AST for the invocation (we use Cheat-JS's parsing function because the above snippet is not parsable by parse-js without tweaks; in particular, the parsing won't work as expected if we don't call register-args-macro as above, so don't skip that step):

> (cheat-js:parse-js "var Person = @defclass(name, shoeSize);")
(:TOPLEVEL
 ((:VAR
   (("Person" :MACRO-CALL (:NAME "@defclass")
     (:ARGS (:NAME "name") (:NAME "shoeSize")))))))

The part that starts with :MACRO-CALL is the interesting part; this is the AST representation of our macro invocation; this is what we need to transform into the expansion (don't worry about the apparently missing ( in front of :MACRO-CALL above, it's because the list following :VAR is made of conses, not lists).

What does the expansion look like? Let's see:

> (cheat-js:parse-js "var Person = function(name, shoeSize)
                      {
                          this.name = name;
                          this.shoeSize = shoeSize;
                      };")

(:TOPLEVEL
 ((:VAR
   (("Person" :FUNCTION NIL ("name" "shoeSize")
     ((:STAT (:ASSIGN T (:DOT (:NAME "this") "name") (:NAME "name")))
      (:STAT
       (:ASSIGN T (:DOT (:NAME "this") "shoeSize") (:NAME "shoeSize")))))))))

Comparing the two ASTs reveals that we need to transform the (:MACRO-CALL s-expression into the (:FUNCTION s-expression.

To make it clearer, we need to write a Common Lisp function to transform this:

(:MACRO-CALL (:NAME "@defclass")
 (:ARGS (:NAME "name") (:NAME "shoeSize")))

into this:

(:FUNCTION NIL ("name" "shoeSize")
 ((:STAT (:ASSIGN T (:DOT (:NAME "this") "name") (:NAME "name")))
  (:STAT (:ASSIGN T (:DOT (:NAME "this") "shoeSize") (:NAME "shoeSize")))))

We only need the macro arguments to perform the expansion. Since we told Cheat-JS this is an 'args only' macro, it knows we're only interested in the arguments (not the entire :MACRO-CALL tree), so that's what it will pass to our expander function:

> (defun defclass-expander (args)
    (let* ((names (mapcar #'second args)))
      `(:function nil ,names
                  ,(mapcar (lambda (name)
                             `(:stat
                               (:assign t
                                        (:dot (:name "this") ,name)
                                        (:name ,name))))
                           names))))

The parameter args contains the list of arguments extracted from (:ARGS... above (in our case ((:NAME "name") (:NAME "shoeSize"))). The value returned by the function should be the expansion shown above (s-expression starting with (:FUNCTION...).

Let's test it:

> (let ((args '((:NAME "name") (:NAME "shoeSize"))))
    (defclass-expander args))
(:FUNCTION NIL ("name" "shoeSize")
           ((:STAT (:ASSIGN T (:DOT (:NAME "this") "name") (:NAME "name")))
            (:STAT (:ASSIGN T
                            (:DOT (:NAME "this") "shoeSize")
                            (:NAME "shoeSize")))))

Looks OK. Let's tell Cheat-JS about our function:

> (cheat-js:register-macro-expander "@defclass" #'defclass-expander)

Now we can ask Cheat-JS to macroexpand our code:

> (cheat-js:explode "var Person = @defclass(name, shoeSize);")
"var Person = function(name, shoeSize)
{
    this.name = name;
    this.shoeSize = shoeSize;
};"

cl-uglify-js is a really good pretty printer, isn't it?

On to @iife.

Defining @iife

Again, we'll see the invocation (both JavaScript and parse-js AST), expansion and implementation for the macro.

Let's recall the JavaScript for the invocation from the examples above:

var greeter = @iife(
    return {
        'hello': function(name) {
            console.log('hello, '+ name);
        }
    };
);

This is a 'body' macro - its only argument is a list of JavaScript statements. Let's tell Cheat-JS:

> (cheat-js:register-body-macro "@iife")

We can now ask the parser for the invocation AST. We'll work with a simplified invocation, though - the example above will generate a large AST, and we can just as well manage with a smaller one. It's also better if our invocation has more than one statement, so let's try this:

> (cheat-js:parse-js "var a = @iife(alert(1); return 1;);")
(:TOPLEVEL
 ((:VAR
   (("a" :MACRO-CALL (:NAME "@iife")
     (:BODY (:STAT (:CALL (:NAME "alert") ((:NUM 1))))
            (:RETURN (:NUM 1))))))))

The expansion we desire for this invocation:

> (cheat-js:parse-js "var a = (function() { alert(1); return 1; })();")
(:TOPLEVEL
 ((:VAR
   (("a" :CALL
     (:FUNCTION NIL NIL
      ((:STAT (:CALL (:NAME "alert") ((:NUM 1)))) (:RETURN (:NUM 1))))
     NIL)))))

OK. We need to expand:

(:MACRO-CALL (:NAME "@iife")
  (:BODY (:STAT (:CALL (:NAME "alert") ((:NUM 1))))
         (:RETURN (:NUM 1))))

into:

(:CALL (:FUNCTION NIL NIL
         ((:STAT (:CALL (:NAME "alert") ((:NUM 1))))
          (:RETURN (:NUM 1))))
       NIL)

The function to do this is:

(defun iife-expander (body)
  `(:CALL (:FUNCTION NIL NIL ,body)
          NIL))

Since we told Cheat-JS this is a 'body only' macro, it extracts the body from the :MACRO-CALL AST and passes it to our expander.

A quick test:

> (let ((body '((:STAT (:CALL (:NAME "alert") ((:NUM 1))))
                (:RETURN (:NUM 1)))))
    (iife-expander body))
(:CALL
 (:FUNCTION NIL NIL
            ((:STAT (:CALL (:NAME "alert") ((:NUM 1))))
             (:RETURN (:NUM 1))))
 NIL)

Looks OK. Let's install it:

> (cheat-js:register-macro-expander "@iife" #'iife-expander)

Let's use it:

> (cheat-js:explode "var a = @iife(alert(1); return 1;);")
"var a = function() {
    alert(1);
    return 1;
}();"

Oops. Some parens got dropped. This is not really an issue, the resulting JavaScript is still valid. Let's try another example:

> (cheat-js:explode "@iife(alert(1); return 1;);")
"(function() {
    alert(1);
    return 1;
})();"

Good! The parens were required this time, and they are there.

Now that we have @iife we can write a safer @defclass (and also see an example of combining Cheat-JS macros).

Defining a safer @defclass

We used the following Javascript 'class definition' for the @defclass example:

var Person = function(name, shoeSize)
{
    this.name = name;
    this.shoeSize = shoeSize;
};

This code has a well-known problem. To create a Person object, one should use a call like new Person('John', 42);. If we forget the new keyword, we are in trouble, because this inside the function no longer refers to a newly created object, but to window, the global object.

Isn't there a way around this? Let's try to define Person like this:

var Person = (function () {
    function Person(name, shoeSize) {
        this.name = name;
        this.shoeSize = shoeSize;
    }
    return function (name, shoeSize) {
        return new Person(name, shoeSize);
    };
}());

Now it doesn't matter if we use new or we leave it out. A new Person object is always returned. The new definition is a bit verbose, though. Maybe we can use... a macro?

Let's examine the three pieces of data we need for a new macro. The invocation in JavaScript:

var Person = @safeDefclass(Person, name, shoeSize);

This is certainly more like it, conciseness-wise.

@safeDefclass, like @defclass, is an 'args only` macro:

> (cheat-js:register-args-macro "@safeDefclass")

The AST of the invocation:

> (cheat-js:parse-js "var Person = @safeDefclass(Person, name, shoeSize);")
(:TOPLEVEL
 ((:VAR
   (("Person" :MACRO-CALL (:NAME "@safeDefclass")
     (:ARGS (:NAME "Person") (:NAME "name") (:NAME "shoeSize")))))))

What about the expansion? Do we want the expansion shown above, or is it possible to expand to something shorter? This is a macro-combining opportunity, let's not waste it. The expansion is:

var Person = @iife(
    function Person(name, shoeSize) {
        this.name = name;
        this.shoeSize = shoeSize;
    };
    return function (name, shoeSize) {
        return new Person(name, shoeSize);
    };
);

If your REPL was restarted after defining @iife, please reinstall @iife in the new REPL by using the instructions in the last section (or the code in tests.lisp).

Now we can ask cheat-js about the AST of the expansion:

> (cheat-js:parse-js "var Person = @iife(
                          function Person(name, shoeSize) {
                              this.name = name;
                              this.shoeSize = shoeSize;
                          };
                          return function (name, shoeSize) {
                              return new Person(name, shoeSize);
                          };
                      );")
(:TOPLEVEL
 ((:VAR
   (("Person" :MACRO-CALL (:NAME "@iife")
     (:BODY
      (:DEFUN "Person" ("name" "shoeSize")
       ((:STAT (:ASSIGN T (:DOT (:NAME "this") "name") (:NAME "name")))
        (:STAT
         (:ASSIGN T (:DOT (:NAME "this") "shoeSize") (:NAME "shoeSize")))))
      (:BLOCK NIL)
      (:RETURN
       (:FUNCTION NIL ("name" "shoeSize")
        ((:RETURN
          (:NEW (:NAME "Person") ((:NAME "name") (:NAME "shoeSize")))))))))))))

So our :MACRO-CALL expands into a new :MACRO-CALL. Cheat-JS should be able to handle this, like a good little macroexpander.

To make writing the expansion function easier, let's isolate the source and target ASTs. The invocation:

(:MACRO-CALL (:NAME "@safeDefclass")
 (:ARGS (:NAME "Person") (:NAME "name") (:NAME "shoeSize")))

and our desired expansion:

(:MACRO-CALL (:NAME "@iife")
 (:BODY
  (:DEFUN "Person" ("name" "shoeSize")
   ((:STAT (:ASSIGN T (:DOT (:NAME "this") "name") (:NAME "name")))
    (:STAT (:ASSIGN T (:DOT (:NAME "this") "shoeSize") (:NAME "shoeSize")))))
  (:BLOCK NIL)
  (:RETURN
   (:FUNCTION NIL ("name" "shoeSize")
    ((:RETURN (:NEW (:NAME "Person") ((:NAME "name")
                                      (:NAME "shoeSize")))))))))

The expander function:

(defun safe-defclass-expander (args)
  (let* ((names (mapcar #'second args))
         (class-name (first names))
         (field-names (rest names)))
    `(:MACRO-CALL
      (:NAME "@iife")
      (:BODY
       (:DEFUN ,class-name ,field-names
         ,(mapcar (lambda (field)
                    `(:STAT (:ASSIGN T
                                     (:DOT (:NAME "this") ,field)
                                     (:NAME ,field))))
                  field-names))
       (:BLOCK NIL)
       (:RETURN
         (:FUNCTION NIL ,field-names
                    ((:RETURN (:NEW (:NAME ,class-name)
                                    ,(mapcar (lambda (field)
                                               (list :name field))
                                             field-names))))))))))

Test the expander function:

> (let* ((args '((:NAME "Person")
                 (:NAME "name")
                 (:NAME "shoeSize"))))
    (safe-defclass-expander args))
(:MACRO-CALL (:NAME "@iife")
 (:BODY
  (:DEFUN "Person" ("name" "shoeSize")
   ((:STAT (:ASSIGN T (:DOT (:NAME "this") "name")
                    (:NAME "name")))
    (:STAT (:ASSIGN T (:DOT (:NAME "this") "shoeSize")
                    (:NAME "shoeSize")))))
  (:BLOCK NIL)
  (:RETURN
   (:FUNCTION NIL ("name" "shoeSize")
    ((:RETURN (:NEW (:NAME "Person") ((:NAME "name")
                                      (:NAME "shoeSize")))))))))

It seems to do what we want. Let's install and test it:

> (cheat-js:register-macro-expander "@safeDefclass"
                                    #'safe-defclass-expander)
> (cheat-js:explode "var Person = @safeDefclass(Person, name, shoeSize);")
"var Person = function() {
    function Person(name, shoeSize) {
        this.name = name;
        this.shoeSize = shoeSize;
    }
    return function(name, shoeSize) {
        return new Person(name, shoeSize);
    };
}();"

So composing macros (@safeDefclass expands into a call to @iife) works.

Defining @whenLet

The @whenLet invocation in the examples isn't very suitable for defining @whenLet. We can do with a shorter body and more than one variable in the 'let' part. Let's use this invocation:

@whenLet(t1, 1, t2, 2, t3, 3; f(t1, t2, t3););

This is an 'args and body' macro (the test1, 1, test2, 2, test3, 3 is the 'args' part, f(t1, t2, t3); is the body). They are separated by a semicolon. Let's tell Cheat-JS about this macro:

> (cheat-js:register-args-and-body-macro "@whenLet")

The AST of the invocation:

> (cheat-js:parse-js "@whenLet(t1, 1, t2, 2, t3, 3; f(t1, t2, t3););")
(:TOPLEVEL
 ((:STAT
   (:MACRO-CALL (:NAME "@whenLet")
    ((:ARGS (:NAME "t1") (:NUM 1) (:NAME "t2") (:NUM 2) (:NAME "t3") (:NUM 3))
     (:BODY
      (:STAT (:CALL (:NAME "f") ((:NAME "t1") (:NAME "t2") (:NAME "t3"))))))))))

The :MACRO-CALL node has subnodes for both :ARGS and :BODY, they will be both passed to our expander function.

What about the expansion? As the when-let documentation says, @whenLet should run the statements in its body if all the bound variables are true. And it would be nice to have a new scope for our variables, so this is a suitable expansion:

(function(t1, t2, t3) {
    if (t1 && t2 && t3) {
        f(t1, t2, t3);
    }
})(1, 2, 3);

The AST of the expansion:

>(cheat-js:parse-js "(function(t1, t2, t3) {
                         if (t1 && t2 && t3) {
                             f(t1, t2, t3);
                         }
                     })(1, 2, 3);")
(:TOPLEVEL
 ((:STAT
   (:CALL
    (:FUNCTION NIL ("t1" "t2" "t3")
     ((:IF (:BINARY :&& (:BINARY :&& (:NAME "t1") (:NAME "t2")) (:NAME "t3"))
       (:BLOCK
        ((:STAT (:CALL (:NAME "f") ((:NAME "t1") (:NAME "t2") (:NAME "t3"))))))
       NIL)))
    ((:NUM 1) (:NUM 2) (:NUM 3))))))

So we need to transform the :MACRO-CALL node of the invocation into the topmost :CALL node of the expansion:

(defun when-let-expander (args body)
  (let* ((grouped-args (group args 2))
         (arg-vars (mapcar #'first grouped-args))
         (arg-values (mapcar #'second grouped-args))
         (arg-var-names (mapcar #'second arg-vars)))
    `(:CALL
      (:FUNCTION NIL ,arg-var-names
                 ((:IF ,(make-binary-and-ast arg-vars)
                       (:BLOCK
                        ,body)
                       NIL)))
      ,arg-values)))

Notice that when-let-expander has two arguments, args and body, which contain the contents of the :ARGS and :BODY nodes of the :MACRO-CALL tree. This function uses two helper functions shown below:

(defun group (list n)
  (if (< (length list) n)
      (if list
          (list list))
      (cons (subseq list 0 n)
            (group (subseq list n) n))))

(defun make-binary-and-ast (operands)
  (cond ((= 1 (length operands))
         (first operands))
        ((= 2 (length operands))
         (list* :binary :&& operands))
        ((> (length operands) 2)
         (let ((first-two (subseq operands 0 2))
               (rest (subseq operands 2)))
           (make-binary-and-ast (cons (make-binary-and-ast first-two)
                                      rest))))
        (t (error "Incorrect number of operands for @whenLet."))))

We need group to help with destructuring the variables and values, and make-binary-and-ast to build valid AST trees for 'and'-ing more than two operands.

Let's test our function:

> (let ((args '((:NAME "t1") (:NUM 1)
                (:NAME "t2") (:NUM 2)
                (:NAME "t3") (:NUM 3)))
        (body '((:STAT
                 (:CALL (:NAME "f")
                        ((:NAME "t1") (:NAME "t2") (:NAME "t3")))))))
    (when-let-expander args body))
(:CALL
 (:FUNCTION NIL ("t1" "t2" "t3")
  ((:IF (:BINARY :&& (:BINARY :&& (:NAME "t1") (:NAME "t2")) (:NAME "t3"))
    (:BLOCK
     ((:STAT (:CALL (:NAME "f") ((:NAME "t1") (:NAME "t2") (:NAME "t3"))))))
    NIL)))
 ((:NUM 1) (:NUM 2) (:NUM 3)))

The result of the function is identical to our desired expansion, so we can install our expander and test our new macro:

> (cheat-js:register-macro-expander "@whenLet" #'when-let-expander)
> (cheat-js:explode "@whenLet(t1, 1, t2, 2, t3, 3; f(t1, t2, t3););")
"(function(t1, t2, t3) {
    if (t1 && t2 && t3) {
        f(t1, t2, t3);
    }
})(1, 2, 3);"

Exercises:

  1. Can you define @let by simplifyind @whenLet a little?
  2. Can you define @iife so @iife(console.log(1);) expands into @let(;console.log(1);)? Why is the first semicolon (just after the opening paren) necessary in the last @let invocation?

Defining @awhen

... should be very easy. awhen, defined in On Lisp, page 190, is just when-let with one anaphoric variable, it. So the invocation:

@awhen(expr;f(it);)

should expand into:

@whenLet(it, expr;f(it);)

Let's declare @awhen to be an 'args and body' macro:

> (cheat-js:register-args-and-body-macro "@awhen")

If you have restarted your REPL after reading the last section, please make sure to redefine @whenLet in your new REPL.

The AST of the invocation:

> (cheat-js:parse-js "@awhen(expr;f(it);)")
(:TOPLEVEL
 ((:STAT
   (:MACRO-CALL (:NAME "@awhen")
    ((:ARGS (:NAME "expr"))
     (:BODY (:STAT (:CALL (:NAME "f") ((:NAME "it"))))))))))

The AST of the expansion:

> (cheat-js:parse-js "@whenLet(it, expr;f(it);)")
(:TOPLEVEL
 ((:STAT
   (:MACRO-CALL (:NAME "@whenLet")
    ((:ARGS (:NAME "it") (:NAME "expr"))
     (:BODY (:STAT (:CALL (:NAME "f") ((:NAME "it"))))))))))         

The expander:

(defun awhen-expander (args body)
  `(:MACRO-CALL (:NAME "@whenLet")
                ((:ARGS (:NAME "it") ,(first args))
                 (:BODY ,@body))))

Let's install the expander and test @awhen:

> (cheat-js:register-macro-expander "@awhen" #'awhen-expander)
> (cheat-js:explode "@awhen(expr;f(expr););")
"(function(it) {
    if (it) {
        f(expr);
    }
})(expr);"

It works!

More macros

See the reference for macros already packaged with Cheat-JS.

Conclusion

To define a Cheat-JS macro, you need to know the three things required for any macro: the invocation, the expansion, the transformation.

You also need to call one of the cheat-js:register-*-macro functions to declare the type of your macro and cheat-js:register-macro-expander to install the expander function. It's best to call cheat-js:clear-macros before your macro definitions to start clean.

Use cheat-js:explode to macroexpand JavaScript code after you defined the macros.

Troubleshooting

Right now Cheat-JS doesn't give very nice error messages when parsing a macro invocation fails; I'll try to improve error handling there.

If things break, right now the best course is to isolate the problem (it's a problem in the expander? is the expected AST for the expansion incorrect? etc.). The examples above should provide some information about how Cheat-JS works. Reading cheat-js.lisp (rather small right now, less than a hundred lines) and the tests.lisp files may provide more clues about what Cheat-JS expects from a macro definition.

Closing thoughts

Still reading? Wow!

One thing is obvious: Cheat-JS makes it possible to write macro-like transformations on JavaScript code, but it's not nearly as easy as writing Common Lisp macros. Maybe this isn't a bad thing - we should be writing macros only when there's no other way to avoid code duplication.

There are plenty of quirks. There's only so many transformations you can do (function calls are not as frequent in JavaScript as they are in Common Lisp, and macro invocations are 'hooked' to function calls). Maybe parse-js could be tweaked harder to make it possible to insert macros at other points. For now, the transformations possible with 'function call' macros are enough for me.

You need to be able to 'pattern match' ASTs and figure out how to transform macro invocations ASTs into macro expansions ASTs. This is a basic macro writing skill, but with an indirection (in Common Lisp the source code is the AST, not so with JavaScript).

You also need to know Common Lisp. In theory, a Cheat-JS based preprocessor could be distributed and used by people who are only 'consuming' macros produced by someone else (a 'macro producer'). Hmmm, people will certainly be amused if a 'macro producer' starts distributing a 40MB executable (this is about the minimum size for SBCL standalone executables) that explodes constructs in the source code into larger code :-)

With a JavaScript parser written in JavaScript it would be possible to do what Cheat-JS does without Common Lisp (though without backquotes the generation of macro expansions is probably a pain, and the AST would have to be uniform - nested arrays, no classes, maybe? - to make it easier to 'pattern match' and analyze).

Right now, the audience of Cheat-JS is probably the audience of ParenScript (mostly because you need to be a lisper to fully use Cheat-JS). I (Miron Brezuleanu) wrote a few thousands of lines of ParenScript code and found that there is some 'impedance mismatch' between ParenScript and JavaScript (especially around the '.' operator in JavaScript and modules in JavaScript). This was most likely my fault: instead of writing Lisp to be compiled to JavaScript, I was trying to write JavaScript with s-expressions. I found it harder to write code in ParenScript than in JavaScript, and the presence of macros didn't compensate for this extra effort. I tried to find a way to have macros while writing something closer to JavaScript. Cheat-JS is what I came up with.

Thanks for taking the time to read about Cheat-JS; I hope you'll find it useful (or at least amusing - or both)!

Please use the Github issues page to report any bugs or to post feature requests.


Next: , Previous: , Up: Top   [Contents][Index]

2 Systems

The main system appears first, followed by any subsystem dependency.


Previous: , Up: Systems   [Contents][Index]

2.1 cheat-js

Author

Miron Brezuleanu

License

Simplified BSD License

Description

Macros for JavaScript. Kinda.

Dependencies
Source

cheat-js.asd (file)

Components

Next: , Previous: , Up: Top   [Contents][Index]

3 Files

Files are sorted by type and then listed depth-first from the systems components trees.


Previous: , Up: Files   [Contents][Index]

3.1 Lisp


Next: , Previous: , Up: Lisp files   [Contents][Index]

3.1.1 cheat-js.asd

Location

cheat-js.asd

Systems

cheat-js (system)


Next: , Previous: , Up: Lisp files   [Contents][Index]

3.1.2 cheat-js/package.lisp

Parent

cheat-js (system)

Location

package.lisp

Packages

Next: , Previous: , Up: Lisp files   [Contents][Index]

3.1.3 cheat-js/util.lisp

Dependencies
Parent

cheat-js (system)

Location

util.lisp

Internal Definitions

Next: , Previous: , Up: Lisp files   [Contents][Index]

3.1.4 cheat-js/tokenize.lisp

Dependencies
Parent

cheat-js (system)

Location

tokenize.lisp

Exported Definitions
Internal Definitions

Next: , Previous: , Up: Lisp files   [Contents][Index]

3.1.5 cheat-js/parse.lisp

Dependencies
Parent

cheat-js (system)

Location

parse.lisp

Exported Definitions
Internal Definitions

Next: , Previous: , Up: Lisp files   [Contents][Index]

3.1.6 cheat-js/cheat-js.lisp

Dependencies
Parent

cheat-js (system)

Location

cheat-js.lisp

Exported Definitions
Internal Definitions

Next: , Previous: , Up: Lisp files   [Contents][Index]

3.1.7 cheat-js/macro-library.lisp

Dependencies
Parent

cheat-js (system)

Location

macro-library.lisp

Exported Definitions
Internal Definitions

Previous: , Up: Lisp files   [Contents][Index]

3.1.8 cheat-js/tests.lisp

Dependencies
Parent

cheat-js (system)

Location

tests.lisp

Internal Definitions

Next: , Previous: , Up: Top   [Contents][Index]

4 Packages

Packages are listed by definition order.


Next: , Previous: , Up: Packages   [Contents][Index]

4.1 cj-macro-library

Source

package.lisp (file)

Use List

common-lisp

Exported Definitions
Internal Definitions

Next: , Previous: , Up: Packages   [Contents][Index]

4.2 cheat-js-tests

Source

package.lisp (file)

Use List
Internal Definitions

Next: , Previous: , Up: Packages   [Contents][Index]

4.3 cheat-js

Source

package.lisp (file)

Use List

common-lisp

Exported Definitions
Internal Definitions

Previous: , Up: Packages   [Contents][Index]

4.4 parse-js

Source

package.lisp (file)

Use List

common-lisp

Used By List

cl-uglify-js

Exported Definitions
Internal Definitions

Next: , Previous: , Up: Top   [Contents][Index]

5 Definitions

Definitions are sorted by export status, category, package, and then by lexicographic order.


Next: , Previous: , Up: Definitions   [Contents][Index]

5.1 Exported definitions


Next: , Previous: , Up: Exported definitions   [Contents][Index]

5.1.1 Special variables

Special Variable: *allow-at-signs*
Package

parse-js

Source

tokenize.lisp (file)

Special Variable: *check-for-reserved-words*
Package

parse-js

Source

tokenize.lisp (file)

Special Variable: *ecma-version*
Package

parse-js

Source

tokenize.lisp (file)

Special Variable: *macro-hook*
Package

parse-js

Source

parse.lisp (file)


Next: , Previous: , Up: Exported definitions   [Contents][Index]

5.1.2 Functions

Function: clear-macros ()
Package

cheat-js

Source

cheat-js.lisp (file)

Function: explode JS-STRING
Package

cheat-js

Source

cheat-js.lisp (file)

Function: install-macros MACROS
Package

cj-macro-library

Source

macro-library.lisp (file)

Function: js-parse-error CONTROL &rest ARGS
Package

parse-js

Source

tokenize.lisp (file)

Function: lex-js STREAM &key INCLUDE-COMMENTS
Package

parse-js

Source

tokenize.lisp (file)

Function: list-macros ()
Package

cj-macro-library

Source

macro-library.lisp (file)

Function: parse-js JS-STRING
Package

cheat-js

Source

cheat-js.lisp (file)

Function: parse-js INPUT &key STRICT-SEMICOLONS ECMA-VERSION RESERVED-WORDS
Package

parse-js

Source

parse.lisp (file)

Function: parse-js-string &rest ARGS
Package

parse-js

Source

parse.lisp (file)

Function: read-js-number STREAM &key JUNK-ALLOWED
Package

parse-js

Source

tokenize.lisp (file)

Function: register-args-and-body-macro MACRO-NAME
Package

cheat-js

Source

cheat-js.lisp (file)

Function: register-args-macro MACRO-NAME
Package

cheat-js

Source

cheat-js.lisp (file)

Function: register-body-macro MACRO-NAME
Package

cheat-js

Source

cheat-js.lisp (file)

Function: register-macro MACRO-NAME MACRO-KIND MACRO-EXPANDER
Package

cheat-js

Source

cheat-js.lisp (file)

Function: register-macro-expander MACRO-NAME MACRO-EXPANDER
Package

cheat-js

Source

cheat-js.lisp (file)

Function: reset-gensym-counter ()
Package

cj-macro-library

Source

macro-library.lisp (file)

Function: run-tests ()
Package

cheat-js

Source

cheat-js.lisp (file)

Function: token-char INSTANCE
Function: (setf token-char) VALUE INSTANCE
Package

parse-js

Source

tokenize.lisp (file)

Function: token-comments-before INSTANCE
Function: (setf token-comments-before) VALUE INSTANCE
Package

parse-js

Source

tokenize.lisp (file)

Function: token-line INSTANCE
Function: (setf token-line) VALUE INSTANCE
Package

parse-js

Source

tokenize.lisp (file)

Function: token-newline-before INSTANCE
Function: (setf token-newline-before) VALUE INSTANCE
Package

parse-js

Source

tokenize.lisp (file)

Function: token-pos INSTANCE
Function: (setf token-pos) VALUE INSTANCE
Package

parse-js

Source

tokenize.lisp (file)

Function: token-type INSTANCE
Function: (setf token-type) VALUE INSTANCE
Package

parse-js

Source

tokenize.lisp (file)

Function: token-value INSTANCE
Function: (setf token-value) VALUE INSTANCE
Package

parse-js

Source

tokenize.lisp (file)


Next: , Previous: , Up: Exported definitions   [Contents][Index]

5.1.3 Generic functions

Generic Function: js-parse-error-char CONDITION
Package

parse-js

Methods
Method: js-parse-error-char (CONDITION js-parse-error)
Source

tokenize.lisp (file)

Generic Function: js-parse-error-line CONDITION
Package

parse-js

Methods
Method: js-parse-error-line (CONDITION js-parse-error)
Source

tokenize.lisp (file)


Previous: , Up: Exported definitions   [Contents][Index]

5.1.4 Conditions

Condition: js-parse-error ()
Package

parse-js

Source

tokenize.lisp (file)

Direct superclasses

simple-error (condition)

Direct methods
Direct slots
Slot: line
Initform

(quote parse-js::*line*)

Readers

js-parse-error-line (generic function)

Slot: char
Initform

(quote parse-js::*char*)

Readers

js-parse-error-char (generic function)


Previous: , Up: Definitions   [Contents][Index]

5.2 Internal definitions


Next: , Previous: , Up: Internal definitions   [Contents][Index]

5.2.1 Special variables

Special Variable: *assignment*
Package

parse-js

Source

parse.lisp (file)

Special Variable: *atom-keywords*
Package

parse-js

Source

tokenize.lisp (file)

Special Variable: *char*
Package

parse-js

Source

tokenize.lisp (file)

Special Variable: *cj-gensym-counter*
Package

cj-macro-library

Source

macro-library.lisp (file)

Special Variable: *dependencies*
Package

cj-macro-library

Source

macro-library.lisp (file)

Special Variable: *in-function*
Package

parse-js

Source

parse.lisp (file)

Special Variable: *keywords*
Package

parse-js

Source

tokenize.lisp (file)

Special Variable: *keywords-before-expression*
Package

parse-js

Source

tokenize.lisp (file)

Special Variable: *label-scope*
Package

parse-js

Source

parse.lisp (file)

Special Variable: *line*
Package

parse-js

Source

tokenize.lisp (file)

Special Variable: *line-terminators*
Package

parse-js

Source

tokenize.lisp (file)

Special Variable: *macros*
Package

cj-macro-library

Source

macro-library.lisp (file)

Special Variable: *macros*
Package

cheat-js

Source

cheat-js.lisp (file)

Special Variable: *operator-chars*
Package

parse-js

Source

tokenize.lisp (file)

Special Variable: *operators*
Package

parse-js

Source

tokenize.lisp (file)

Special Variable: *position*
Package

parse-js

Source

tokenize.lisp (file)

Special Variable: *precedence*
Package

parse-js

Source

parse.lisp (file)

Special Variable: *reserved-words-ecma-3*
Package

parse-js

Source

tokenize.lisp (file)

Special Variable: *reserved-words-ecma-5*
Package

parse-js

Source

tokenize.lisp (file)

Special Variable: *unary-postfix*
Package

parse-js

Source

parse.lisp (file)

Special Variable: *unary-prefix*
Package

parse-js

Source

parse.lisp (file)

Special Variable: *whitespace-chars*
Package

parse-js

Source

tokenize.lisp (file)


Next: , Previous: , Up: Internal definitions   [Contents][Index]

5.2.2 Macros

Macro: defun/defs NAME ARGS &body BODY
Package

parse-js

Source

util.lisp (file)

Macro: with-defs &body BODY
Package

parse-js

Source

util.lisp (file)

Macro: with-label-scope TYPE LABEL &body BODY
Package

parse-js

Source

parse.lisp (file)


Next: , Previous: , Up: Internal definitions   [Contents][Index]

5.2.3 Functions

Function: and-expander ARGS
Package

cj-macro-library

Source

macro-library.lisp (file)

Function: awhen-expander ARGS BODY
Package

cheat-js-tests

Source

tests.lisp (file)

Function: cj-gensym &optional PREFIX
Package

cj-macro-library

Source

macro-library.lisp (file)

Function: copy-macro-record INSTANCE
Package

cheat-js

Source

cheat-js.lisp (file)

Function: copy-token INSTANCE
Package

parse-js

Source

tokenize.lisp (file)

Function: dbind-expander ARGS
Package

cj-macro-library

Source

macro-library.lisp (file)

Function: defclass-expander ARGS BODY
Package

cj-macro-library

Source

macro-library.lisp (file)

Function: defclass-expander ARGS
Package

cheat-js-tests

Source

tests.lisp (file)

Function: dolist-expander ARGS BODY
Package

cj-macro-library

Source

macro-library.lisp (file)

Function: dotimes-expander ARGS BODY
Package

cj-macro-library

Source

macro-library.lisp (file)

Function: expand-macro MACRO-RECORD AST
Package

cheat-js

Source

cheat-js.lisp (file)

Function: fn-expander ARGS BODY
Package

cj-macro-library

Source

macro-library.lisp (file)

Function: fn0-expander BODY
Package

cj-macro-library

Source

macro-library.lisp (file)

Function: group LIST N
Package

cheat-js-tests

Source

tests.lisp (file)

Function: if-expander ARGS
Package

cj-macro-library

Source

macro-library.lisp (file)

Function: iife-expander BODY
Package

cj-macro-library

Source

macro-library.lisp (file)

Function: iife-expander BODY
Package

cheat-js-tests

Source

tests.lisp (file)

Function: init-library-tests ()
Package

cheat-js-tests

Source

tests.lisp (file)

Function: macro-hook NAME
Package

cheat-js

Source

cheat-js.lisp (file)

Function: macro-record-expander INSTANCE
Function: (setf macro-record-expander) VALUE INSTANCE
Package

cheat-js

Source

cheat-js.lisp (file)

Function: macro-record-kind INSTANCE
Function: (setf macro-record-kind) VALUE INSTANCE
Package

cheat-js

Source

cheat-js.lisp (file)

Function: macro-record-name INSTANCE
Function: (setf macro-record-name) VALUE INSTANCE
Package

cheat-js

Source

cheat-js.lisp (file)

Function: macro-record-p OBJECT
Package

cheat-js

Source

cheat-js.lisp (file)

Function: macroexpand-all AST
Package

cheat-js

Source

cheat-js.lisp (file)

Function: make-binary-and-ast OPERANDS
Package

cheat-js-tests

Source

tests.lisp (file)

Function: make-binary-ast OPERATOR OPERANDS
Package

cj-macro-library

Source

macro-library.lisp (file)

Function: make-macro-record &key (NAME NAME) (KIND KIND) (EXPANDER EXPANDER)
Package

cheat-js

Source

cheat-js.lisp (file)

Function: make-token &key (TYPE TYPE) (VALUE VALUE) (LINE LINE) (CHAR CHAR) (POS POS) (NEWLINE-BEFORE NEWLINE-BEFORE) (COMMENTS-BEFORE COMMENTS-BEFORE)
Package

parse-js

Source

tokenize.lisp (file)

Function: one-library-test INVOCATION EXPANSION
Package

cheat-js-tests

Source

tests.lisp (file)

Function: or-expander ARGS
Package

cj-macro-library

Source

macro-library.lisp (file)

Function: parse-js* STREAM &optional STRICT-SEMICOLONS
Package

parse-js

Source

parse.lisp (file)

Function: read-js-number-1 PEEK NEXT &key JUNK-ALLOWED
Package

parse-js

Source

tokenize.lisp (file)

Function: safe-defclass-expander ARGS
Package

cheat-js-tests

Source

tests.lisp (file)

Function: token-id TOKEN
Package

parse-js

Source

tokenize.lisp (file)

Function: token-p OBJECT
Package

parse-js

Source

tokenize.lisp (file)

Function: token-type-p TOKEN TYPE
Package

parse-js

Source

tokenize.lisp (file)

Function: tokenp TOKEN TYPE VALUE
Package

parse-js

Source

tokenize.lisp (file)

Function: when-let-expander ARGS BODY
Package

cheat-js-tests

Source

tests.lisp (file)


Previous: , Up: Internal definitions   [Contents][Index]

5.2.4 Structures

Structure: macro-record ()
Package

cheat-js

Source

cheat-js.lisp (file)

Direct superclasses

structure-object (structure)

Direct slots
Slot: name
Readers

macro-record-name (function)

Writers

(setf macro-record-name) (function)

Slot: kind
Readers

macro-record-kind (function)

Writers

(setf macro-record-kind) (function)

Slot: expander
Readers

macro-record-expander (function)

Writers

(setf macro-record-expander) (function)

Structure: token ()
Package

parse-js

Source

tokenize.lisp (file)

Direct superclasses

structure-object (structure)

Direct slots
Slot: type
Readers

token-type (function)

Writers

(setf token-type) (function)

Slot: value
Readers

token-value (function)

Writers

(setf token-value) (function)

Slot: line
Readers

token-line (function)

Writers

(setf token-line) (function)

Slot: char
Readers

token-char (function)

Writers

(setf token-char) (function)

Slot: pos
Readers

token-pos (function)

Writers

(setf token-pos) (function)

Slot: newline-before
Readers

token-newline-before (function)

Writers

(setf token-newline-before) (function)

Slot: comments-before
Readers

token-comments-before (function)

Writers

(setf token-comments-before) (function)


Previous: , Up: Top   [Contents][Index]

Appendix A Indexes


Next: , Previous: , Up: Indexes   [Contents][Index]

A.1 Concepts

Jump to:   C   F   L  
Index Entry  Section

C
cheat-js.asd: The cheat-js<dot>asd file
cheat-js/cheat-js.lisp: The cheat-js/cheat-js<dot>lisp file
cheat-js/macro-library.lisp: The cheat-js/macro-library<dot>lisp file
cheat-js/package.lisp: The cheat-js/package<dot>lisp file
cheat-js/parse.lisp: The cheat-js/parse<dot>lisp file
cheat-js/tests.lisp: The cheat-js/tests<dot>lisp file
cheat-js/tokenize.lisp: The cheat-js/tokenize<dot>lisp file
cheat-js/util.lisp: The cheat-js/util<dot>lisp file

F
File, Lisp, cheat-js.asd: The cheat-js<dot>asd file
File, Lisp, cheat-js/cheat-js.lisp: The cheat-js/cheat-js<dot>lisp file
File, Lisp, cheat-js/macro-library.lisp: The cheat-js/macro-library<dot>lisp file
File, Lisp, cheat-js/package.lisp: The cheat-js/package<dot>lisp file
File, Lisp, cheat-js/parse.lisp: The cheat-js/parse<dot>lisp file
File, Lisp, cheat-js/tests.lisp: The cheat-js/tests<dot>lisp file
File, Lisp, cheat-js/tokenize.lisp: The cheat-js/tokenize<dot>lisp file
File, Lisp, cheat-js/util.lisp: The cheat-js/util<dot>lisp file

L
Lisp File, cheat-js.asd: The cheat-js<dot>asd file
Lisp File, cheat-js/cheat-js.lisp: The cheat-js/cheat-js<dot>lisp file
Lisp File, cheat-js/macro-library.lisp: The cheat-js/macro-library<dot>lisp file
Lisp File, cheat-js/package.lisp: The cheat-js/package<dot>lisp file
Lisp File, cheat-js/parse.lisp: The cheat-js/parse<dot>lisp file
Lisp File, cheat-js/tests.lisp: The cheat-js/tests<dot>lisp file
Lisp File, cheat-js/tokenize.lisp: The cheat-js/tokenize<dot>lisp file
Lisp File, cheat-js/util.lisp: The cheat-js/util<dot>lisp file

Jump to:   C   F   L  

Next: , Previous: , Up: Indexes   [Contents][Index]

A.2 Functions

Jump to:   (  
A   C   D   E   F   G   I   J   L   M   O   P   R   S   T   W  
Index Entry  Section

(
(setf macro-record-expander): Internal functions
(setf macro-record-kind): Internal functions
(setf macro-record-name): Internal functions
(setf token-char): Exported functions
(setf token-comments-before): Exported functions
(setf token-line): Exported functions
(setf token-newline-before): Exported functions
(setf token-pos): Exported functions
(setf token-type): Exported functions
(setf token-value): Exported functions

A
and-expander: Internal functions
awhen-expander: Internal functions

C
cj-gensym: Internal functions
clear-macros: Exported functions
copy-macro-record: Internal functions
copy-token: Internal functions

D
dbind-expander: Internal functions
defclass-expander: Internal functions
defclass-expander: Internal functions
defun/defs: Internal macros
dolist-expander: Internal functions
dotimes-expander: Internal functions

E
expand-macro: Internal functions
explode: Exported functions

F
fn-expander: Internal functions
fn0-expander: Internal functions
Function, (setf macro-record-expander): Internal functions
Function, (setf macro-record-kind): Internal functions
Function, (setf macro-record-name): Internal functions
Function, (setf token-char): Exported functions
Function, (setf token-comments-before): Exported functions
Function, (setf token-line): Exported functions
Function, (setf token-newline-before): Exported functions
Function, (setf token-pos): Exported functions
Function, (setf token-type): Exported functions
Function, (setf token-value): Exported functions
Function, and-expander: Internal functions
Function, awhen-expander: Internal functions
Function, cj-gensym: Internal functions
Function, clear-macros: Exported functions
Function, copy-macro-record: Internal functions
Function, copy-token: Internal functions
Function, dbind-expander: Internal functions
Function, defclass-expander: Internal functions
Function, defclass-expander: Internal functions
Function, dolist-expander: Internal functions
Function, dotimes-expander: Internal functions
Function, expand-macro: Internal functions
Function, explode: Exported functions
Function, fn-expander: Internal functions
Function, fn0-expander: Internal functions
Function, group: Internal functions
Function, if-expander: Internal functions
Function, iife-expander: Internal functions
Function, iife-expander: Internal functions
Function, init-library-tests: Internal functions
Function, install-macros: Exported functions
Function, js-parse-error: Exported functions
Function, lex-js: Exported functions
Function, list-macros: Exported functions
Function, macro-hook: Internal functions
Function, macro-record-expander: Internal functions
Function, macro-record-kind: Internal functions
Function, macro-record-name: Internal functions
Function, macro-record-p: Internal functions
Function, macroexpand-all: Internal functions
Function, make-binary-and-ast: Internal functions
Function, make-binary-ast: Internal functions
Function, make-macro-record: Internal functions
Function, make-token: Internal functions
Function, one-library-test: Internal functions
Function, or-expander: Internal functions
Function, parse-js: Exported functions
Function, parse-js: Exported functions
Function, parse-js*: Internal functions
Function, parse-js-string: Exported functions
Function, read-js-number: Exported functions
Function, read-js-number-1: Internal functions
Function, register-args-and-body-macro: Exported functions
Function, register-args-macro: Exported functions
Function, register-body-macro: Exported functions
Function, register-macro: Exported functions
Function, register-macro-expander: Exported functions
Function, reset-gensym-counter: Exported functions
Function, run-tests: Exported functions
Function, safe-defclass-expander: Internal functions
Function, token-char: Exported functions
Function, token-comments-before: Exported functions
Function, token-id: Internal functions
Function, token-line: Exported functions
Function, token-newline-before: Exported functions
Function, token-p: Internal functions
Function, token-pos: Exported functions
Function, token-type: Exported functions
Function, token-type-p: Internal functions
Function, token-value: Exported functions
Function, tokenp: Internal functions
Function, when-let-expander: Internal functions

G
Generic Function, js-parse-error-char: Exported generic functions
Generic Function, js-parse-error-line: Exported generic functions
group: Internal functions

I
if-expander: Internal functions
iife-expander: Internal functions
iife-expander: Internal functions
init-library-tests: Internal functions
install-macros: Exported functions

J
js-parse-error: Exported functions
js-parse-error-char: Exported generic functions
js-parse-error-char: Exported generic functions
js-parse-error-line: Exported generic functions
js-parse-error-line: Exported generic functions

L
lex-js: Exported functions
list-macros: Exported functions

M
Macro, defun/defs: Internal macros
Macro, with-defs: Internal macros
Macro, with-label-scope: Internal macros
macro-hook: Internal functions
macro-record-expander: Internal functions
macro-record-kind: Internal functions
macro-record-name: Internal functions
macro-record-p: Internal functions
macroexpand-all: Internal functions
make-binary-and-ast: Internal functions
make-binary-ast: Internal functions
make-macro-record: Internal functions
make-token: Internal functions
Method, js-parse-error-char: Exported generic functions
Method, js-parse-error-line: Exported generic functions

O
one-library-test: Internal functions
or-expander: Internal functions

P
parse-js: Exported functions
parse-js: Exported functions
parse-js*: Internal functions
parse-js-string: Exported functions

R
read-js-number: Exported functions
read-js-number-1: Internal functions
register-args-and-body-macro: Exported functions
register-args-macro: Exported functions
register-body-macro: Exported functions
register-macro: Exported functions
register-macro-expander: Exported functions
reset-gensym-counter: Exported functions
run-tests: Exported functions

S
safe-defclass-expander: Internal functions

T
token-char: Exported functions
token-comments-before: Exported functions
token-id: Internal functions
token-line: Exported functions
token-newline-before: Exported functions
token-p: Internal functions
token-pos: Exported functions
token-type: Exported functions
token-type-p: Internal functions
token-value: Exported functions
tokenp: Internal functions

W
when-let-expander: Internal functions
with-defs: Internal macros
with-label-scope: Internal macros

Jump to:   (  
A   C   D   E   F   G   I   J   L   M   O   P   R   S   T   W  

Next: , Previous: , Up: Indexes   [Contents][Index]

A.3 Variables

Jump to:   *  
C   E   K   L   N   P   S   T   V  
Index Entry  Section

*
*allow-at-signs*: Exported special variables
*assignment*: Internal special variables
*atom-keywords*: Internal special variables
*char*: Internal special variables
*check-for-reserved-words*: Exported special variables
*cj-gensym-counter*: Internal special variables
*dependencies*: Internal special variables
*ecma-version*: Exported special variables
*in-function*: Internal special variables
*keywords*: Internal special variables
*keywords-before-expression*: Internal special variables
*label-scope*: Internal special variables
*line*: Internal special variables
*line-terminators*: Internal special variables
*macro-hook*: Exported special variables
*macros*: Internal special variables
*macros*: Internal special variables
*operator-chars*: Internal special variables
*operators*: Internal special variables
*position*: Internal special variables
*precedence*: Internal special variables
*reserved-words-ecma-3*: Internal special variables
*reserved-words-ecma-5*: Internal special variables
*unary-postfix*: Internal special variables
*unary-prefix*: Internal special variables
*whitespace-chars*: Internal special variables

C
char: Exported conditions
char: Internal structures
comments-before: Internal structures

E
expander: Internal structures

K
kind: Internal structures

L
line: Exported conditions
line: Internal structures

N
name: Internal structures
newline-before: Internal structures

P
pos: Internal structures

S
Slot, char: Exported conditions
Slot, char: Internal structures
Slot, comments-before: Internal structures
Slot, expander: Internal structures
Slot, kind: Internal structures
Slot, line: Exported conditions
Slot, line: Internal structures
Slot, name: Internal structures
Slot, newline-before: Internal structures
Slot, pos: Internal structures
Slot, type: Internal structures
Slot, value: Internal structures
Special Variable, *allow-at-signs*: Exported special variables
Special Variable, *assignment*: Internal special variables
Special Variable, *atom-keywords*: Internal special variables
Special Variable, *char*: Internal special variables
Special Variable, *check-for-reserved-words*: Exported special variables
Special Variable, *cj-gensym-counter*: Internal special variables
Special Variable, *dependencies*: Internal special variables
Special Variable, *ecma-version*: Exported special variables
Special Variable, *in-function*: Internal special variables
Special Variable, *keywords*: Internal special variables
Special Variable, *keywords-before-expression*: Internal special variables
Special Variable, *label-scope*: Internal special variables
Special Variable, *line*: Internal special variables
Special Variable, *line-terminators*: Internal special variables
Special Variable, *macro-hook*: Exported special variables
Special Variable, *macros*: Internal special variables
Special Variable, *macros*: Internal special variables
Special Variable, *operator-chars*: Internal special variables
Special Variable, *operators*: Internal special variables
Special Variable, *position*: Internal special variables
Special Variable, *precedence*: Internal special variables
Special Variable, *reserved-words-ecma-3*: Internal special variables
Special Variable, *reserved-words-ecma-5*: Internal special variables
Special Variable, *unary-postfix*: Internal special variables
Special Variable, *unary-prefix*: Internal special variables
Special Variable, *whitespace-chars*: Internal special variables

T
type: Internal structures

V
value: Internal structures

Jump to:   *  
C   E   K   L   N   P   S   T   V  

Previous: , Up: Indexes   [Contents][Index]

A.4 Data types

Jump to:   C   J   M   P   S   T  
Index Entry  Section

C
cheat-js: The cheat-js system
cheat-js: The cheat-js package
cheat-js-tests: The cheat-js-tests package
cj-macro-library: The cj-macro-library package
Condition, js-parse-error: Exported conditions

J
js-parse-error: Exported conditions

M
macro-record: Internal structures

P
Package, cheat-js: The cheat-js package
Package, cheat-js-tests: The cheat-js-tests package
Package, cj-macro-library: The cj-macro-library package
Package, parse-js: The parse-js package
parse-js: The parse-js package

S
Structure, macro-record: Internal structures
Structure, token: Internal structures
System, cheat-js: The cheat-js system

T
token: Internal structures

Jump to:   C   J   M   P   S   T