0
votes

For some code I was working I've needed to handle 'x inside macro. What is standard way of handling those values?

I have code like this:

(define (quoted-symbol? x)
   (and (pair? x) (eq? (car x) 'quote) (symbol? (cadr x)) (null? (cddr x))))

(define-macro (test x)
   (if (quoted-symbol? x)
      `(begin
          (display ',(cadr x))
          (newline))))

(test 'hello) ;; 'hello will be expanded into list (quote hello)

Is this how this should be handled, or is just in macro you don't use quoted symbols?

NOTE: I'm not asking about hygienic macros (I'm asking about real lisp macros), so please no answers with hygienic macros.

EDIT:

My macro works correctly in Guile and BiwaScheme and in my own scheme like lisp in JavaScript. Here is better example:

(define-macro (test x)
   (if (quoted-symbol? x)
       `',(cadr x)))

(define (example arg)
  (list arg (test 'world)))

(example 'hello)

the question was not about display, but about (cadr x).

EDIT2: You've asked so here you go, my macro:

(define-macro (--> expr . code)
  "Helper macro that simplify calling methods on objects. It work with chaining

   usage: (--> ($ \"body\")
               (css \"color\" \"red\")
               (on \"click\" (lambda () (print \"click\"))))

          (--> document (querySelectorAll \"div\"))

          (--> (fetch \"https://jcubic.pl\") (text) (match /<title>([^<]+)<\/title>/) 1)

          (--> document (querySelectorAll \".cmd-prompt\") 0 \"innerText\")"
  (let ((obj (gensym)))
    `(let* ((,obj ,(if (and (symbol? expr) (not (null? (match /\./ (symbol->string expr)))))
                       `(.. ,expr)
                       `,expr)))
       ,@(map (lambda (code)
                (let ((name (gensym))
                      (value (gensym)))
                  `(let* ((,name ,(cond ((quoted-symbol? code) (symbol->string (cadr code)))
                                        ((pair? code) (symbol->string (car code)))
                                        (true code)))
                          (,value (. ,obj ,name)))
                     ,(if (and (pair? code) (not (quoted-symbol? code)))
                         `(set! ,obj (,value ,@(cdr code)))
                         `(set! ,obj ,value)))))
              code)
       ,obj)))

;; ---------------------------------------------------------------------------------------
(define (quoted-symbol? x)
   "(quoted-symbol? code)

   Helper function that test if value is quoted symbol. To be used in macros
   that pass literal code that is transformed by parser.

   usage:

      (define-macro (test x)
         (if (quoted-symbol? x)
             `',(cadr x)))

      (list 'hello (test 'world))"
   (and (pair? x) (eq? (car x) 'quote) (symbol? (cadr x)) (null? (cddr x))))

the macro is used in my scheme like lisp in JavaScript, like the doc string suggest:

(--> document (querySelectorAll ".class") 0 "innerText")

I want to support:

(--> document (querySelectorAll ".class") 0 'innerText)

The code can be tested online at: https://jcubic.github.io/lips/ (You need to copy/paste the code since current version allow only method calls).

To get expansion you can use

(pprint (macroexpand (-->  document (querySelector "x"))))

if it don't work (don't expand) it mean that macro is broken somehow.

dot is build in function that get property of an object and .. macro:

(define-macro (.. expr)
  "(.. foo.bar.baz)

   Macro that gets value from nested object where argument is comma separated symbol"
  (if (not (symbol? expr))
      expr
      (let ((parts (split "." (symbol->string expr))))
        (if (single parts)
            expr
            `(. ,(string->symbol (car parts)) ,@(cdr parts))))))

that can be use to get nested property like (.. document.body.innerHTML)

1
I think you should make clear what you're trying to achieve: what purpose do you have in treating quoted symbols specially? What should the expansion be if the argument to the macro is not a quoted symbol? This all smells strongly like an XY question.user5920214
Your last edition of the macro returns undefined when it is not a quoted symbol. Eg (test var) will fail and (test (quote var)) expands to (quote var) which then gets evaluated to the symbol var by scheme. What is the purpose of the identity macro? Could you come with a real problem you have?Sylwester
@Sylwester I've edited my question with my code as you've requested.jcubic
I don't know enough JS to understand what the semantics of what you are trying to do is, but almost certainly the answer is not to do this in the macro: have the macro rewrite to a syntactically-less-convenient function call (or set of function calls) and then let the functions handle the differences between strings and symbols.user5920214

1 Answers

0
votes

Scheme doesn't have "real lisp macros". Some implementations has something similar, but the forms have different names and uses. They are not portable at all.

The standard way of handling 'x is to handle it like an expression that gets evaluated in the expansion. Eg.

(define var 'x)

(test 'x)
(test var)

The two test forms should amount to the same even though the macro test gets (quote x) in the first and the symbol var in the second. At the time of the expansion var does not exist since the implementation can expand all the macros before starting.

You implementation of test will not work. Eg. the display might be run one or twice and then each time you call a procedure that uses it it will gfail since the expansion is the undefined value and it might not be fit for evaluation. eg.

(define (example arg)
  (list arg (test 'w)))

When this is defined you get 'w or (quote w) printed with a newline and then the procedure it tries to store is:

(define (example arg)
  (list arg #<undefined>))

Note that what constitutes the undefined value is chosen by the implementaion, but I know for sure that in many implementaions you cannot evaluate #<undefined>.