Page 1 of 1

Infix.lsp as macro

PostPosted: Tue Jun 27, 2017 7:01 am
by hds1
Hi,
is it possible to transform the infix.lsp to a "true" macro i.e. doing the work at compile time ?
Or do i miss a critical point there ?
Calling "eval" during runtime is supposed to be expensive (at least in other lisps). Not sure how newlisp handles eval.
Reason for asking is that i redo some of the quaternion calculus as well as geographic math scripts.

i.e.
(INFIX:xlate "εk2 = aekb1 - aejbi + aeibj + aebk + akbe - ajbei + aibej + a1bek")
--> (setq εk2 (add (add (sub (add (add (add (sub aekb1 aejbi) aeibj) aebk) akbe) ajbei)
aibej) a1bek))
(INFIX:xlate "e3Xe4_x = sin(y3 - y4) * sin((x3 + x4) / 2) * cos((x3 - x4) / 2) - sin(y3 + y4) * cos((x3 + x4) / 2) * sin((x3 - x4) / 2)")
--> (setq e3Xe4_x (sub (mul (mul (sin (sub y3 y4)) (sin (div (add x3 x4) 2))) (cos (div (sub x3 x4) 2)))

I still can't wrap my mind around the lisp math syntax ... old school i fear ...

Re: Infix.lsp as macro

PostPosted: Tue Jun 27, 2017 12:45 pm
by ralph.ronnquist
You may do this kind of thing in two steps. Firstly, you define a macro, e.g.
Code: Select all
> (macro (mix) nil)
(lambda-macro () (expand 'nil))
Thereafter you redefine it to perform the "xlate" call the way you want it, e.g.
Code: Select all
> (constant 'mix (lambda-macro () (INFIX:xlate (join (map string (args)) " "))))
(lambda-macro () (INFIX:xlate (join (map string (args)) " ")))

Now you can use this in new definitions and expressions with "read-time" xlate-ion. E.g.,
Code: Select all
> (define (foo a b) (mix a +  b))
(lambda (a b) (add a b))
> '(mix 4 * 5 + sin(34) * 8 )
(add (mul 4 5) (mul (sin 34) 8))
> (mix 4 * 5 + sin(34) * 8 )
24.23266148896019

Of course, you might want to name it something else than 'mix.

Re: Infix.lsp as macro

PostPosted: Tue Jun 27, 2017 2:09 pm
by hds1
thanks a ton. I must admit that i would never ever thought of a redefiniton.
Can you explain why the macro becomes "executable" due to the redefiniton ?

Re: Infix.lsp as macro

PostPosted: Tue Jun 27, 2017 2:26 pm
by ralph.ronnquist
Well, being or not being a macro is, I believe, some flag attached to the symbol, which is set by the "(macro ..)" term. That term also wraps the given "body" into an "expand" term, as is typically useful for these kinds of macros. But not in this case, where you want the xlate-ion to be invoked at read time.

The "(constant ...)" term simply attaches a new definition to the symbol without messing with the flag, and thus redefines it. The manual has some discussion about this point.

Re: Infix.lsp as macro

PostPosted: Tue Jun 27, 2017 7:19 pm
by hds1
Exactly what is needed. Thx.
A minor setback is that infix.lsp does not handle "-" with one argument.
Math code is much clearer now.
Proof of expansion during read time attached.

Code: Select all
(load "infix.lsp")

(macro (mix) nil)
(constant 'mix (lambda-macro ()
       (INFIX:xlate (join (map string (args)) " "))))

(define (quatmul q1 q2)
  (let ((x1 (nth 0 q1))   (y1 (nth 1 q1))   (z1 (nth 2 q1)) (w1 (nth 3 q1))
   (x2 (nth 0 q2))   (y2 (nth 1 q2))   (z2 (nth 2 q2)) (w2 (nth 3 q2)))
    (mix w = 0 - x1 * x2 - y1 * y2 - z1 * z2 + w1 * w2)
    (mix x =     x1 * w2 + y1 * z2 - z1 * y2 + w1 * x2)
    (mix y = 0 - x1 * z2 + y1 * w2 + z1 * x2 + w1 * y2)
    (mix z =     x1 * y2 - y1 * x2 + z1 * w2 + w1 * z2)))

(quatmul '(1 2 3 4) '(-9 -4 4 4)) --> Should be 42
(string (source 'quatmul))

==>
(define (quatmul q1 q2)
(let ((x1 (nth 0 q1)) (y1 (nth 1 q1)) (z1 (nth 2 q1)) (w1 (nth 3 q1))
       (x2 (nth 0 q2)) (y2 (nth 1 q2)) (z2 (nth 2 q2)) (w2 (nth 3 q2)))
(setq w (add (sub (sub (sub 0 (mul x1 x2)) (mul y1 y2)) (mul z1 z2)) (mul w1 w2)))
(setq x (add (sub (add (mul x1 w2) (mul y1 z2)) (mul z1 y2)) (mul w1 x2)))
(setq y (add (add (add (mul (sub 0 x1) z2) (mul y1 w2)) (mul z1 x2)) (mul w1 y2)))
(setq z (add (add (sub (mul x1 y2) (mul y1 x2)) (mul z1 w2)) (mul w1 z2)))))