a quasiquote implementation in newLisp

Started by jsmall, September 26, 2004, 12:10:26 PM

Previous topic - Next topic

jsmall

The following macro and function define

a quasiquote / unquote mechanism in newlisp.



Note the use of uq and uq@.






  ;; usage:  (qq (x y (uq (+ 1 2)) (uq@ (list 1 2 3))))
  ;;  ==>  (x y 3 1 2 3)

  (define-macro (qq s-expr)
    (qq-eval s-expr))


  ;; Since qq is a macro you can't use (args) within.
  ;; Use qq-eval instead which is not a macro and thus
  ;; (args) will not capture the qq's (args).

  ;; usage:  (qq-eval '(x y (uq (+ 1 2)) (uq@ (list 1 2 3))))
  ;;  ==>  (x y 3 1 2 3)


  (define (qq-eval s-expr , i)
    (if (list? s-expr)
      (begin
        (setq i 0)
        (while (< i (length s-expr))
          (let ((ss-expr (nth i s-expr)))
            (if (list? ss-expr)
              (cond
                ((= 'uq (first ss-expr))
                  (nth-set i s-expr (eval (qq-eval (last ss-expr))))
                  (inc 'i))
                ((= 'uq@ (first ss-expr))
                  (let ((ss-exprs (eval (qq-eval (last ss-expr)))))
                    (if (list? ss-exprs)
                      (begin
                        (pop s-expr i)
                        (dotimes (j (length ss-exprs))
                          (push (nth j ss-exprs) s-expr i)
                          (inc 'i)))
                      (begin
                        (nth-set i s-expr ss-exprs)
                        (inc 'i)))))
                 (true
                   (nth-set i s-expr (qq-eval ss-expr))
                   (inc 'i)))
              (begin
                (inc 'i)
                s-expr))))
           s-expr)
      s-expr))





The following demonstrates the use of qq and qq-eval.




  ;; Abbreviation for lambda or fn

  (define-macro ( )
    (eval (qq-eval '(lambda (uq (first (args))) (uq@ (rest (args)))))))


  ;; Abbreviation for define

  (define-macro (: _var-or-fn _value)
    (if (list? _var-or-fn)
      (eval (qq-eval '(define (uq _var-or-fn) (uq@ (rest (args))))))
      (eval (qq (set _var-or-fn (uq _value))))))




Notice that qq is not used whenever (args)

appears in the expression being quasiquoted.



I'm hoping this makes writing macros more

convenient in newLisp.



Warning: I've only briefly tested this.

Lutz

#1
>>>

;; Since qq is a macro you can't use (args) within.

>>>



its is the other way around macros *can* use (args) normal functions *can not*:



(define-macro (foo) (println (args)))



(foo 1 2 3) => (1 2 3)



(define (foo) (println (args)))



(foo 1 2 3) => ()



Lutz

Lutz

#2
Consider also using 'ref' for looking up 'uq' or 'uq@' expressions. This is how a qq-macro could look like for just handling the unquoting of uq:



(define-macro (qq s-exp)
  (while  (set 'idx (chop (ref 'uq s-exp)))
      (set 'qx (pop s-exp idx))
      (push (eval (first (rest qx))) s-exp idx))
  s-exp)

;; now use it
(set 'x 'hello)

(qq ( (uq (+ 1 2)) ((uq (+ 3 4)) (uq x)))) => (3 (7 hello))

;; something similar could be done for 'uq@'


'ref' returns the index positions of an expresssion found in a nested list in an index-list, which can be used directly in 'pop' or 'push' which can take index lists. 'pop' and 'push' are inverse of each other. The same index-list used to pop something can be used to push something else back into the same position.



Lutz

jsmall

#3
I've been working on the uq and uq@ using your code example:




(define-macro (qq s-exp)
  (while  (set 'idx (chop (ref 'uq s-exp)))
      (set 'qx (pop s-exp idx))
      (push (eval (first (rest qx))) s-exp idx))
  s-exp)

;; now use it
(set 'x 'hello)

(qq ( (uq (+ 1 2)) ((uq (+ 3 4)) (uq x)))) => (3 (7 hello))

;; something similar could be done for 'uq@'





1. It would be nice if "ref" had a version that took a predicate, e.g.




     (ref? (lambda (s-expr) (or (= 'uq s-expr) (= 'uq@ s-expr)))
            s-exp)



and returned the ref path along with the value returned by

the predicate.



2. Also if the continuation bookmark could be captured and

returned as well so that the depth first? search could continue

from where it left off that would also be nice.



Essentially my longer version was taking this approach (1 & 2) but

the size and speed advantage of your approach is obvious.