2015-09-15 1 views
1

Я пишу интерпретатор игрушки для Лиспа языка, в котором у меня есть следующий код: CLСимвол манипуляции в сюсюкать макро

(defun mal-list (&rest args) 
    (make-mal :type 'list 
      :value args)) 
(register-fun '|list| #'mal-list) 

(defun mal-list? (arg) 
    (eq (mal-type arg) 'list)) 
(register-fun '|list?| #'mal-list?) 

Однако я предпочел бы просто написать что-то вроде этого:

(defmal list (&rest args) 
    (make-mal :type 'list 
      :value args)) 

(defmal list? (arg) 
    (eq (mal-type arg) 'list)) 

Я попытался написать макрос для этого, но у меня были проблемы с символами с барами (я довольно смущен относительно того, что это такое!). Это то, что я пробовал:

(defmacro defmal (name args &body body) 
    (let ((funsym (intern (format nil "~{~a~}" `(mal- ,name))))) 
    `(register-fun `|,name| (defun ,funsym ,args ,@body)))) 

, не получилось, потому что `|,name| literaly означает |,name|, а не |list|

Я предполагаю, что это проблема XY, но я не знаю, как подойти к этому иначе.

+0

У символов есть имена. По умолчанию, когда читатель встречается, например, 'foo', он получает строку' 'foo'', увеличивает ее, чтобы получить' 'FOO" ', и стажеров, чтобы получить символ' FOO'. Но символы не должны иметь расширенные имена. Если вы станете '' foo'' (в отличие от '' FOO "'), вы получите символ, который, вероятно, будет напечатан '\ f \ o \ o' или' | foo | '. –

+1

'(format nil" ~ {~ a ~} "' непредсказуем, потому что '* print-case *' может быть задан для множества вещей. '(Format nil" ~ a "'foo)' может возвращать' foo »,« FOO »или« Foo ». –

ответ

4

Синтаксис |...| только один из способов, которыми Лисп принтер может печатать символы, которые имеют символы в имени, которые должны быть экранированы (и что читатель может прочитать символов с этими видами символов в именах) :

(print (intern "foo")) 
;=> |foo| 

есть и другие способы тоже, в том числе вылетающих отдельных персонажей:

(print '|FOO|) 
;=> FOO 

(print '\f\o\o) 
;=> |foo| 

Что вы пытаетесь сделать, это просто создать символ, название которого включает в себя нижний регистр пусть Ослабляет. Это достаточно просто, как показано выше. Часть вашего вопроса, однако, является то, что вы получаете в качестве входных данных символа, имя которого полно заглавных букв, так что вам нужно downcase первого:

CL-USER> (symbol-name 'FOO) 
;=> "FOO" 

CL-USER> (intern (symbol-name 'FOO)) 
;=> FOO 

CL-USER> (string-downcase (symbol-name 'FOO)) 
;=> "foo" 

CL-USER> (intern (string-downcase (symbol-name 'FOO))) 
;=> |foo| 

В самом деле, потому что строки-downcase принимает строковые обозначения, а не только строки, вы можете передать символ непосредственно:

CL-USER> (intern (string-downcase 'BaR)) 
;=> |bar| 

Итак, после всего, что обработки строк, мы можем перейти к макро.


Похоже, что вы ищете что-то вроде этого:

(defmacro defmal (name lambda-list &body body) 
    (let ((mal-name (intern (concatenate 'string "MAL-" (symbol-name name)))) 
     (mal-norm (intern (string-downcase name)))) 
    `(progn 
     (defun ,mal-name ,lambda-list 
      ,@body) 
     (register-function ',mal-norm #',mal-name)))) 

CL-USER> (pprint (macroexpand-1 '(defmal list? (arg) 
            (eq (mal-type arg) 'list)))) 

(PROGN 
(DEFUN MAL-LIST? (ARG) (EQ (MAL-TYPE ARG) 'LIST)) 
(REGISTER-FUNCTION '|list?| #'MAL-LIST?)) 

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

(loop for case in '(:upcase :downcase :capitalize) 
    collect (let ((*print-case* case)) 
      (format nil "~a" 'foo))) 
;=> ("FOO" "foo" "Foo") 

Вместо этого, вы можете использовать СЦЕПИТЬ со строкой (или имя символа символа).Поскольку читатель также может иметь различные настройки чувствительности к регистру, иногда я даже делать (но не все это нравится):

(concatenate 'string (symbol-name '#:mal-) (symbol-name name)) 

Таким образом, если читатель делает ничего необычного (например, сохраняет случай, так что имя символа mal- равно "mal-), вы можете сохранить его и в своем собственном сгенерированном символе.

2

В дополнение к Joshua's detailed answer, рассмотреть возможность использования функции из библиотеки Alexandria:

  • format-symbol, как format, но внутри with-standard-io-syntax. Здесь t стенды для текущего пакета и имя является downcased:

    (format-symbol t "mal-~(~A~)" name) 
    => |mal-list| 
    
  • symbolicate сцепляет и стажеры в текущем пакете:

    (symbolicate '#:mal- name) 
    

    Вы можете отслуживших с любой |MAL-LIST| или |mal-list|, если ваш текущий считываемый файл сохраняет или нет. Для полноты обратите внимание, что readtable-case может быть установлен на following values: :upcase, :downcase, :preserve или :invert (этот я считаю довольно интересным).

+1

: инвертировать очень интересно, и значительно упрощает запись кода FFI/совместимости/CamelCased. Все строчные буквы сохраняют« обычное »поведение, например' (defun foo(). ..) => (DEFUN FOO() ...) ', но' (defun listProcesses() ...) => (DEFUN | listProcesses |() ...) '. –

 Смежные вопросы

  • Нет связанных вопросов^_^