Need help with lisp list to macro arguments for compiler.

Discussion of Common Lisp
Post Reply
General Dissarray
Posts: 2
Joined: Mon Jan 05, 2015 1:25 am

Need help with lisp list to macro arguments for compiler.

Post by General Dissarray » Mon Jan 05, 2015 1:56 am

Hi!
First time posting here. Some background info: Been interested in common lisp a while, had a course with SICP as course book then after university i read OnLisp once, found it fascinating.

Now i am trying to make a DSL language which will read and then modify C# code and print the modified code out, but i got stuck.
I have managed to parse a C# func but then i have it as a lisp list in my program, which creates problems for my output macro.
I think it is easier if i show you, i will first show the working output macro then my macro (which doesn't work) which calls it with a list.

Calling expression:

Code: Select all

(fn-def-pattern ("public" "void" "setName") ("string" "newName") (list (list "int" "id")(list "bool" "employee"))  (list "var _a = 2;" (fn-body-add-setter-fn fn-new-para-name)))
Definition:

Code: Select all

(defmacro fn-def-pattern ((c-visibility c-ret-type c-name) (fn-new-para-type fn-new-para-name)  fnParams &body body)
  `(let ((c-name ,c-name)
	 (fn-new-para-name ,fn-new-para-name))
     (format nil "~a ( ~a ~a ){ ~% ~{~a~&~}  ~& } " 
	     (format nil "~a ~a ~a " ,c-visibility ,c-ret-type ,c-name )
	     (format nil "~{~{~a ~a~}~^, ~}" ,fnParams ) ;;  '((int id)(string name))
	     (format nil ",~a ~a" ,fn-new-para-type ,fn-new-para-name)
	     ;; ,(cadar body);;,@body;;,(cadar body)
	     ;; (list ,@body)
	     ,@body
	     )))

(defun fn-body-add-setter-fn ( var-name )
  (format nil "var _~a = ~a;" var-name var-name) )
Expansion:

Code: Select all

(LET ((C-NAME "setName") (FN-NEW-PARA-NAME "newName"))
  (FORMAT NIL
          "~a ( ~a ~a ){ ~% ~{~a~&~}  ~& } "
          (FORMAT NIL "~a ~a ~a " "public" "void" "setName")
          (FORMAT NIL
                  "~{~{~a ~a~}~^, ~}"
                  (LIST (LIST "int" "id") (LIST "bool" "employee")))
          (FORMAT NIL ",~a ~a" "string" "newName")
          (LIST "var _a = 2;" (FN-BODY-ADD-SETTER-FN FN-NEW-PARA-NAME)))
Result (which i want and find correct):

Code: Select all

"public void setName  ( int id, bool employee ,string newName ){ 
 var _a = 2;
var _newName = newName;
  
 } "
Calling expression:

Code: Select all

(let ((fn-list (list (list "public" "void" "setName") (list "string" "newName") (list (list "int" "id")(list "bool" "employee")) '"var _a = 2;" '(fn-body-add-setter-fn fn-new-para-name)))) (fn-def-pattern-list fn-list))
Definition:

Code: Select all

(defmacro fn-def-pattern-list (fn-list)
    `(fn-def-pattern  ((nth 0 (car ,fn-list)) (nth 1 (car ,fn-list)) (nth 2 (car ,fn-list)))
	 ((nth 0 (cadr ,fn-list)) (nth 1 (cadr ,fn-list)))
	 (nth 2 ,fn-list);;(("int" "id")("bool" "employee")) 
       (nthcdr 3 ,fn-list)));;`( ,(nth 3 fn-list))))));;(nth 3 ,fn-list)));;(list "var _a = 2;" (fn-body-add-setter-fn fn-new-para-name)))))
Expansion:

Code: Select all

(LET ((FN-LIST
       (LIST (LIST "public" "void" "setName")
             (LIST "string" "newName")
             (LIST (LIST "int" "id") (LIST "bool" "employee"))
             '"var _a = 2;"
             '(FN-BODY-ADD-SETTER-FN FN-NEW-PARA-NAME))))
  (LET ((C-NAME (NTH 2 (CAR FN-LIST)))
        (FN-NEW-PARA-NAME (NTH 1 (CADR FN-LIST))))
    (FORMAT NIL
            "~a ( ~a ~a ){ ~% ~{~a~&~}  ~& } "
            (FORMAT NIL
                    "~a ~a ~a "
                    (NTH 0 (CAR FN-LIST))
                    (NTH 1 (CAR FN-LIST))
                    (NTH 2 (CAR FN-LIST)))
            (FORMAT NIL "~{~{~a ~a~}~^, ~}" (NTH 2 FN-LIST))
            (FORMAT NIL ",~a ~a" (NTH 0 (CADR FN-LIST)) (NTH 1 (CADR FN-LIST)))
            (NTHCDR 3 FN-LIST))))
Result (which i find wrong and don't want):

Code: Select all

"public void setName  ( int id, bool employee ,string newName ){ 
 var _a = 2;
(FN-BODY-ADD-SETTER-FN FN-NEW-PARA-NAME)
  
 } "
I have tried many iterations of small futile changes, like you can see in the commented code, mainly trying with the body code list qouted or unqouted.
But i haven't managed to find a working solution and at this stage i am prepeared to accept anything that can give me correct end result.
I am happy for any help and suggestions! Thanks!

Kohath
Posts: 61
Joined: Mon Jul 07, 2008 8:06 pm
Location: Toowoomba, Queensland, Australia
Contact:

Re: Need help with lisp list to macro arguments for compiler

Post by Kohath » Sun Jan 18, 2015 5:44 am

Please take the following in your stride and keep going with Lisp, as it's well worth the effort. But this may hurt. ;)

General Dissarray, first I'd suggest using functions to make it easier to work with (IMHO). Rather than having bodies, you can pass a function, much like using mapcar instead of loop. Either way though, let's see... :? . I'll have a look when I've had more sleep.

What I'm going to have to do to make sense of it is to use parameter names in fn-def-pattern-list. I think the arguments are getting a bit mixed up. And you're passing a list as the body argument to fn-def-pattern - there are a few things to do.

Also, note that in your definition of fn-def-pattern, you prepare a binding for c-name, but you don't use it in the macro (fifth line of definition). To use your prepared binding, you must not evaluate c-name - i.e. get rid of the comma in front of c-name. The same goes for the other binding alongside c-name.

Kohath
Posts: 61
Joined: Mon Jul 07, 2008 8:06 pm
Location: Toowoomba, Queensland, Australia
Contact:

Re: Need help with lisp list to macro arguments for compiler

Post by Kohath » Sun Jan 18, 2015 3:27 pm

Okay, so the original problem was that you start with a macro that accepts code (body), but then you want to store that code in a list first. Lisp can do that, but you'd have to use eval I think. So to avoid that, please try something like this:

Code: Select all

(defun fn-def-pattern (c-visibility c-ret-type c-name fn-new-para-type fn-new-para-name fnParams body)
    (format nil "~a ( ~a~a ) {~%~{    ~a~^~&~}~%}"
        (format nil "~a ~a ~a" c-visibility c-ret-type c-name)
        (format nil "~{~{~a ~a~}~^, ~}" fnParams)
        (format nil ", ~a ~a" fn-new-para-type fn-new-para-name)
        (funcall body name para-name)
        ))

(defun fn-body-add-setter-fn ( var-name )
    (format nil "var _~a = ~a;" var-name var-name))

(defun fn-def-pattern-list (fn-list)
    (fn-def-pattern
        (nth 0 (car fn-list))
        (nth 1 (car fn-list))
        (nth 2 (car fn-list))
        (nth 0 (cadr fn-list))
        (nth 1 (cadr fn-list))
        (nth 2 fn-list)
        (nthcdr 3 fn-list)))
Which you would use like so:

Code: Select all

(let ((fn-list (list
                (list "public" "void" "setName")
                (list "string" "newName")
                (list (list "int" "id")(list "bool" "employee"))
                (lambda (name para-name) (declare (ignore name))
                    (list "var _a = 2;"
                          (fn-body-add-setter-fn para-name))))))
    (fn-def-pattern-list fn-list))
See how the body argument is a lambda - that in fn-def-pattern is what is funcalled. It's just a bit of boiler-plate that we can remove later with a macro.

Now that you've got some code that does what you want without macros and the evaluation issues, you can add a single convenience macro layer along the lines of:

Code: Select all

(defmacro fn-definition-pattern ((vis ret name) (para-type para-name) args &body body)
    (let ((fn-list (gensym "FN-LIST")))
        `(let ((,fn-list (list
                            (list ,vis ,ret ,name)
                            (list ,para-type ,para-name)
                            ',args
                            (lambda (name para-name) (declare (ignore name))
                                (list ,@body)))))
        (fn-def-pattern-list ,fn-list))))
The macro just generates code that looks like we wrote above. I quoted args because I didn't want to pull it apart just to wrap calls to list around it. But if you want to evaluate the elements of arg, you might want to do just that. But playing around with evaluation stuff is why I normally use functions in the first instance. Also notice the gensym to avoid variable capture (what if you use fn-list in your body?).

Some other notes:
  • I tweaked the whitespace in your format strings to get some nicer spacing. :roll: (Couldn't help myself)
  • If you do much more with function-definitions, you might want to put the function definition details into a struct/class so you don't have to keep passing around lots of arguments. Same could go for parameters (name+type in struct).
  • Note that using a lambda for fn-def-pattern decouples the argument names (now chosen in the macro) from your internal code names.
  • I like what you're trying to do. It could be a powerful tool. :D
  • Having that nice structure complicates handling in the functions - just like I flattened the argument list for fn-def-pattern, you could do the same for fn-list, which simplifies it a lot (see below).

Code: Select all

(defun fn-def-pattern-list (fn-list)
    (apply #'fn-def-pattern fn-list))

pjstirling
Posts: 166
Joined: Sun Nov 28, 2010 4:21 pm

Re: Need help with lisp list to macro arguments for compiler

Post by pjstirling » Mon Jan 19, 2015 11:30 am

You might also benefit from looking at parenscript http://common-lisp.net/project/parenscript/ which is a library that translates common-lisp forms to a javascript string.

General Dissarray
Posts: 2
Joined: Mon Jan 05, 2015 1:25 am

Re: Need help with lisp list to macro arguments for compiler

Post by General Dissarray » Mon Mar 30, 2015 1:59 am

Thanks Kohath, sorry for the long time for reply.
I went the way of using functions instead of macro for the output function.
Also the unbound c-name was intentional as an anaphoric macro.

New fn-def-pattern-fn.

Code: Select all

;; (defparameter test (parse-c-sharp-fn-basic (tokenize-c-sharp-fn *code-test*)))
;; (insert-after test 0 (list "string" "newName"))
;; (nconc(car(last test)) (list #'fn-body-add-setter-fn))
;; (fn-def-pattern-fn () test)

(defun insert-after (lst index newelt)
  (push newelt (cdr (nthcdr index lst))) 
  lst)

(defun fn-body-add-setter-fn ( var-name )
  (format nil "var _~a = ~a" var-name var-name) )

;; test looks like this now
;; (("public" "void" "setName") ("string" "newName") (("int" "id") ("bool" "employee")) (("var" "_a" "=" "2") ("var" "_employee" "=" "employee") #<Compiled-function FN-BODY-ADD-SETTER-FN #x21012CB5FF>))
(defun fn-def-pattern-fn ( env code-list)
  (destructuring-bind ((c-visibility c-ret-type c-name) (fn-new-para-type fn-new-para-name)  fnParams &body body) code-list
    (let ((env (cons fn-new-para-name env)))
      (format nil "~a(~a ~a){~%~{~{~T~{ ~a~};~&~}~&~}~&}" 
	      (format nil "~a ~a ~a" c-visibility c-ret-type c-name )
	      (format nil "~{~{~a ~a~}~^, ~}" fnParams ) ;;  '((int id)(string name))
	      (format nil ",~a ~a" fn-new-para-type fn-new-para-name)
	      ;; ,(cadar body);;,@body;;,(cadar body)
	      ;; (list ,@body)
	      (fn-def-pattern-body-fn env  (car body))
	      ))))

(defun fn-def-pattern-body-fn (env code-list)
  (list (mapcar #'(lambda (x) 
	      (if (and (consp x) (stringp(car x)))
		  x
		  (list (funcall x (car env) ))))
	  code-list)))

Code: Select all

;; test looks like this now
;; (("public" "void" "setName") ("string" "newName") (("int" "id") ("bool" "employee")) (("var" "_a" "=" "2") ("var" "_employee" "=" "employee") #<Compiled-function FN-BODY-ADD-SETTER-FN #x21012CB5FF>))

Code: Select all

CL-USER> (fn-def-pattern-fn () test)
"public void setName(int id, bool employee ,string newName){
  var _a = 2;
  var _employee = employee;
  var _newName = newName;
}"

Post Reply