Макрос LISP, который обрабатывает переменные и структуру данных внутри во время выполнения
У меня есть LISP, написанный на JavaScript ( https://jcubic.github.io/lips/ с онлайн-демонстрацией, где вы можете попробовать его), и у меня есть макрос, подобный этому:
(define-macro (globalize symbol)
(let ((obj (--> (. lips 'env) (get symbol))))
`(begin
,@(map (lambda (key)
(print (concat key " " (function? (. obj key))))
(if (function? (. obj key))
(let* ((fname (gensym))
(args (gensym))
(code `(define (,(string->symbol key) . ,args)
(apply (. ,obj ,key) ,args))))
(print code)
code)))
;; native Object.key function call on input object
(array->list (--> Object (keys obj)))))))
В этом коде я использую это:
(let ((obj (--> (. lips 'env) (get symbol))))
и я называю этот макрос, используя:
(globalize pfs)
создать функцию для каждого статического метода pfs (который представляет собой LightingFS из isomorphic-git, где каждая функция возвращает обещание, это как fs из узла).
Но это не будет работать для чего-то вроде этого:
(let ((x pfs))
(globalize x))
потому что lips.env - это глобальная среда.
Итак, мой вопрос заключается в том, как макрос должен работать? Должны ли они обрабатывать входные данные только как символы, чтобы они никогда не имели доступа к объекту до оценки кода lisp?
Как должен выглядеть макрос LISP, генерирующий набор функций на основе переменных. Например, в схеме, если у меня есть alist в переменной и я хочу сгенерировать функцию для каждого ключа, которая будет возвращать значение:
вход:
(define input `((foo . 10) (bar . 20)))
выход:
(begin
(define (foo) 10)
(define (bar) 20))
Могу ли я написать макрос, который даст такой вывод, если я использую (macro input)
? Или единственный вариант (macro ((foo . 10) (bar . 20)))
?
Я могу принять общий ответ Scheme или Common LISP, но, пожалуйста, не публикуйте синтаксис define и гигиенические макросы из схемы, у моего lisp их нет и никогда не будет.
Кажется, проблема в том, что я хочу получить доступ к значению во время расширения макроса, и оно должно иметь значение, которое во время выполнения. И второй вопрос. Является ли eval в этом случае единственным вариантом?
Это работает в biwascheme:
(define-macro (macro obj)
(let ((obj (eval obj)))
`(begin
,@(map (lambda (pair)
(let ((name (car pair))
(value (cdr pair)))
`(define (,name) ,value)))
obj))))
(define input `((foo . 10) (bar . 20)))
(macro input)
(foo)
;; ==> 10
(bar)
;; ==> 20
(в моем lisp eval не работает как в biwascheme, но это другая проблема).
но это не работает, потому что x не является глобальным:
(let ((x '((g . 10)))) (macro x))
Является ли макрос с eval чем-то, что вы обычно делаете, или их следует избегать? Есть ли другой способ генерировать набор функций на основе объекта времени выполнения.
1 ответ
В Common Lisp: создание и компиляция функций во время выполнения.
CL-USER 20 > (defparameter *input* '((foo . 10) (bar . 20)))
*INPUT*
CL-USER 21 > (defun make-my-functions (input)
(loop for (symbol . number) in input
do (compile symbol `(lambda () ,number))))
MAKE-MY-FUNCTIONS
CL-USER 22 > (make-my-functions *input*)
NIL
CL-USER 23 > (foo)
10
CL-USER 24 > (bar)
20
Из локальной переменной:
CL-USER 25 > (let ((input '((foo2 . 102) (bar3 . 303))))
(make-my-functions input))
NIL
CL-USER 26 > (bar3)
303
С макросом, более неуклюжим и ограниченным:
CL-USER 37 > (defparameter *input* '((foo1 . 101) (bar2 . 202)))
*INPUT*
CL-USER 38 > (defmacro def-my-functions (input &optional getter)
`(progn
,@(loop for (symbol . number) in (if getter
(funcall getter input)
input)
collect `(defun ,symbol () ,number))))
DEF-MY-FUNCTIONS
CL-USER 39 > (def-my-functions *input* symbol-value)
BAR2
CL-USER 40 > (foo1)
101