2
votes

In my function I am reading input from the user which is expected to be a lisp form given as a string e.g.:

(sym1 sym2 (sym3 sym4))

My goal is to substitute some of the symbols with other symbols e.g.:

(sublis '((sym1 . sym1%)
          (sym2 . sym2%))
        str)

Because I am getting the input as a string, I am first converting it to a lisp form. Here is how the final function looks like:

(defun sublis-when-string (str)
  (sublis '((sym1 . sym1%)
            (sym2 . sym2%))
          (read-from-string str)))

When I compile the function and run it in the REPL with (sublis-when-string "(sym1 sym3 (sym2 sym4))") I correctly get:

(SYM1% SYM3 (SYM2% SYM4))

However when I run the whole program the substitutions do not work:

(SYM1 SYM3 (SYM2 SYM4))

This lead me to believe that the problems is with the package. When I changed the package in the REPL the substitutions were still not working.

My question is: How should I change my function so it works when called from other packages?

2

2 Answers

4
votes

You can define a package for your user:

(defpackage :my-user (:use) (:export #:sym1 #:sym2))

Of course, the exported symbols are the one you need to add in your substitution list. And then, you bind the *package* variable before reading:

(let ((*package* (find-package :my-user)))
  (read-from-string string))

Notice that all the symbols will be read from the :my-user package. Depending on how much you trust the source of your strings, you can also set *read-eval* to nil and define a minimalistic readtable too:

  • disable array notation that allocate very large arrays like #n() (bad user exhausting memory on purpose)
  • make : a terminating character to make qualified symbols throw an error (bad user interning symbols in other packages)
4
votes

If you want to use your function independently from the package in which the symbols are read, you can change your definition by adding an explicit test: when the values to be checked are symbols, the string= operator is used instead of default eql. For instance:

(defun sublis-when-string (str)
  (sublis '((sym1 . sym1%)
            (sym2 . sym2%))
          (read-from-string str)
          :test (lambda (x y)
                  (if (and (symbolp x) (symbolp y))
                     (string= x y)
                     (eql x y)))))

See the definition of sublis.