Могу ли я отличить аргумент строкового литерала от параметра, который оценивается как строка?

Я создаю предметно-ориентированный язык. Один из вызовов макроса выглядит примерно так:

(мой макрос foo (bar "baz" qux) yay)

С учетом того, как я написал свой макрос, вторым аргументом здесь должен быть список строк в паре круглых скобок. Таким образом, мой макрос работает до тех пор, пока bar и qux оцениваются как строки. Давайте просто скажем, что они эквивалентны "bar" и "qux", а my-macro обрабатывает второй аргумент как "bar baz qux".

Но теперь я понял, что я действительно хочу, чтобы «баз» сохранил свои кавычки; Я хочу, чтобы мой макрос обрабатывал второй аргумент как "bar \"baz\" qux". Это возможно? Могу ли я сказать внутри моего макроса, когда я могу просто использовать строку как есть, а когда мне нужно добавить двойные кавычки?

Заранее спасибо!


person nil    schedule 12.04.2011    source источник


Ответы (3)


Вашему макросу передается символ FOO, список (BAR "баз" QUX) и символ YAY в качестве аргументов. Если вы хотите увидеть, является ли какой-либо из элементов во втором аргументе строкой, используйте что-то вроде stringp в определении макроса для проверки аргумента.

Ваш макрос полностью контролирует, как работает вычисление в результирующем макрорасширении. Он видит все свои аргументы неоцененными. Если вы хотите узнать что-то о невычисленном коде, переданном в качестве аргументов, просто взгляните на него.

person Xach    schedule 12.04.2011
comment
Но я смотрю на это неправильно: (defmacro my-macro (arg) '(if (stringp, arg) (format nil \~a\, arg) (format nil ~a, arg))) (my-macro baz ) =› \baz\ (macroexpand '(my-macro evals-to-a-string)) =› \a string\ Я хочу, чтобы второй вычислялся как строка - person nil; 13.04.2011
comment
Не возвращайте код, выполняющий проверку stringp, выполняйте проверку stringp напрямую. (defmacro my-macro (arg) (if (stringp arg) arg ...)) - person Xach; 14.04.2011

Чтобы рассматривать второй аргумент как строку "bar \"baz\" qux", вы можете просто записать его в строку, например:

(defmacro my-macro (arg1 (&rest arg2) arg3)
  (list arg1 (write-to-string arg2 :case :downcase) arg3)) 

(macroexpand '(my-macro foo (bar "baz" qux) yay))

=> (FOO "(bar \"baz\" qux)" YAY)

person Terje Norderhaug    schedule 12.04.2011
comment
@terje-norderhaug Увы, я хочу, чтобы bar и qux оценивались, а baz оставалась строкой в ​​кавычках. Также строка в кавычках может появиться в любом месте внутри arg2... - person nil; 13.04.2011

Макрос может проверять тип своих аргументов во время развертывания. Например, этот макрос будет создавать различный код в зависимости от того, является ли аргумент строкой или нет:

(defmacro my-macro (arg) 
  (if (stringp arg)
     (format nil "\"~a\"" arg)
     `(format nil "~a" ,arg)))

Контрольная работа:

(my-macro "baz")

=> "\"baz\""

(let ((evals-to-a-string "a string"))
  (my-macro evals-to-a-string))

=> "a string"

Обратите внимание, что если аргумент не является строкой, макрос преобразуется в форму:

(macroexpand '(my-macro evals-to-a-string))

=> (format nil "~a" evals-to-a-string)

person Terje Norderhaug    schedule 13.04.2011