I am away from a lispy computer, but here are the problem areas in js.lisp:

(defun js-expand-form (expr) "Expand a javascript form." (cond ((atom expr) (multiple-value-bind (js-macro macro-env) (lookup-macro expr) (if js-macro (js-expand-form (let ((*js-macro-env* macro-env)) (funcall js-macro))) expr)))

;;; macros (define-js-compiler-macro macrolet (macros &rest body) (let* ((macro-env (make-hash-table :test 'equal)) (*js-macro-env* (cons macro-env *js-macro-env*))) (dolist (macro macros) (destructuring-bind (name arglist &rest body) macro (setf (gethash (symbol-name name) macro-env) (compile nil `(lambda ,arglist ,@body))))) (js-compile `(progn ,@body))))

(defjsmacro symbol-macrolet (macros &rest body) `(macrolet ,(mapcar #'(lambda (macro) `(,(first macro) () ,@(rest macro))) macros) ,@body))

The problem is that symbol-macrolets use the same machinery as regular macrolets. A solution is to maintain parallel symbol and regular macro environments...

;; new variables
(eval-when (:compile-toplevel :load-toplevel :execute)
(defvar *js-symbol-macro-toplevel* (make-hash-table :test 'equal) "Toplevel of symbol macro expansion, holds all the toplevel javascript macros.") (defvar *js-symbol-macro-env* (list js-symbol-macro-toplevel*) "Current symbol macro environment."))

;; amend (lookup-macro ..) to take a :type argument.
(defun lookup-macro (name &keyword (type :lambda))
"Lookup the macro NAME in the current macro expansion environment. Returns the macro and the parent macro environment of this macro." (unless (symbolp name) (return-from lookup-macro nil)) (do ((env (case type
(:lambda *js-macro-env*)
(:symbol *js-symbol-macro-env*)
(t (error "Invalid macro type ~A" type)))
(cdr env))) ((null env) nil) (let ((val (gethash (symbol-name name) (car env)))) (when val (return-from lookup-macro (values val (or (cdr env) (list
(case type
(:lambda *js-macro-toplevel*)
(:symbol *js-symbol-macro-toplevel*)
(t (error "Invalid macro type ~A" type))))))))))

;; get rid of (defjsmacro symbol-macrolet ...) and replace it with
;; a compiler macro:
(define-js-compiler-macro symbol-macrolet (macros &rest body) (let* ((macro-env (make-hash-table :test 'equal)) (*js-symbol-macro-env* (cons macro-env *js-symbol-macro-env*))) (dolist (macro macros) (setf (gethash (symbol-name (first macro)) macro-env) (compile nil `(lambda () ,@(rest macro))))) (js-compile `(progn ,@body))))

;; change expand-form
(defun js-expand-form (expr) "Expand a javascript form." (cond ((atom expr) (multiple-value-bind (js-macro macro-env) (lookup-macro expr :type :symbol) (if js-macro (js-expand-form (let ((*js-symbol-macro-env* macro-env)) (funcall js-macro))) expr))) ((js-compiler-macro-form-p expr) expr) ((equal (first expr) 'quote) expr) (t (let ((js-macro (lookup-macro (car expr)))) (if js-macro (js-expand-form (apply js-macro (cdr expr))) expr)))))


This should at least give an idea of the relevant areas of code.

-Red