jcubic
jcubic

Reputation: 66490

LISP macro that process variables and data structure inside at runtime

I have LISP written in JavaScript (https://jcubic.github.io/lips/ with online demo where you can try it) and I have macro like this:

(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)))))))

In this code I use this:

(let ((obj (--> (. lips 'env) (get symbol))))

and I call this macro using:

(globalize pfs)

to create function for each static method of pfs (which is LightingFS from isomorphic-git where each function return a promise, it's like fs from node).

But it will not work for something like this:

(let ((x pfs))
  (globalize x))

because lips.env is global enviroment.

So my question is this how macro should work? Should they only process input data as symbols so they never have access to object before evaluation of lisp code?

How the LISP macro that generate bunch of functions based on variable should look like. For instance in scheme if I have alist in variable and want to generate function for each key that will return a value:

input:

(define input `((foo . 10) (bar . 20)))

output:

(begin
  (define (foo) 10)
  (define (bar) 20))

Can I write macro that will give such output if I use (macro input)? Or the only option is (macro ((foo . 10) (bar . 20)))?

I can accept generic Scheme or Common LISP answer but please don't post define-syntax and hygienic macros from scheme, My lisp don't have them and will never have.

The problem seems to be that I want to access value at macro expansion time and it need to have the value that in runtime. And second question Is eval in this case the only option?

This works in 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

(in my lisp eval don't work like in biwascheme but that's other issue).

but this don't work, because x is not global:

(let ((x '((g . 10)))) (macro x))

Is macro with eval something you would normally do, or should them be avoided? Is there other way to generate bunch of functions based on runtime object.

Upvotes: 0

Views: 328

Answers (1)

Rainer Joswig
Rainer Joswig

Reputation: 139261

In Common Lisp: creating and compiling functions at runtime.

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

From a local variable:

CL-USER 25 > (let ((input '((foo2 . 102) (bar3 . 303))))
               (make-my-functions input))
NIL

CL-USER 26 > (bar3)
303

With a macro, more clumsy and limited:

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

Upvotes: 3

Related Questions