lispcommon-lispclispgnu-common-lisp

building a hash table with gensym and macrolet


I'm trying to build a hash table (among other actions) while reading. I don't want the hash table to have global scope (yet), so I'm doing this with a macro and gensym. Inside the macro x, I'm defining a macro s which is similar to setf, but defines an entry in a hash table instead of defining a symbol somewhere. It blows up. I think I understand the error message, but how do I make it work?

The code:

#!/usr/bin/clisp -repl

(defmacro x (&rest statements)
  (let ((config-variables (gensym)))
    `(macrolet ((s (place value)
                  (setf (gethash 'place ,config-variables) value)))
       (let ((,config-variables (make-hash-table :test #'eq)))
         (progn ,@statements)
         ,config-variables))))

(defun load-config ()
  (let ((config-file-tree (read *standard-input*)))
    (eval config-file-tree)))

(defun load-test-config ()
  (with-input-from-string (*standard-input* "(x (s fred 3) (s barney 5))")
    (load-config)))

(load-test-config)

The output:

*** - LET*: variable #:G12655 has no value
The following restarts are available:
USE-VALUE      :R1      Input a value to be used instead of #:G12655.
STORE-VALUE    :R2      Input a new value for #:G12655.
SKIP           :R3      skip (LOAD-TEST-CONFIG)
STOP           :R4      stop loading file /u/asterisk/semicolon/build.l/stackoverflow-semi

Solution

  • In a macrolet you are as well defining a macro, so the usual rules apply, i.e. you have to backquote expressions, that are to be evaluated at run-time. Like this:

    (defmacro x (&rest statements)
      (let ((config-variables (gensym)))
        `(macrolet ((s (place value)
                     `(setf (gethash ',place ,',config-variables) ,value)))
          (let ((,config-variables (make-hash-table :test #'eq)))
            (progn ,@statements)
            ,config-variables))))