Re: best way to write a source code generator?
- From: Pascal Costanza <pc@xxxxxxxxx>
- Date: Thu, 21 Sep 2006 23:30:03 +0200
Frank Buss wrote:
If I want to create Java files and files in other languages like this:
public class Test {
public int foo;
public String bar;
public int getFoo() {
return foo;
}
public void setFoo(int foo) {
this.foo = foo;
}
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
public String serialize() {
return "foo=" + foo + " bar=" + Utils.escapeString(bar);
}
public static Test deserialize(String data) {
Test result = new Test();
String elements[] = data.split(" ");
for (String element in elements) {
String pair = element.split("=");
String name = pair[0];
String value = pair[1];
if (name.equals("foo")) {
foo = Integer.parseInt(value);
} else if (name.equals("bar")) {
bar = value;
}
}
}
}
How do I write a generator for this in Lisp? I've started with this:
(define-class "Test" ()
(("foo" number)
("bar" string)))
With this definitions:
(defparameter *classes* (make-hash-table :test 'equal))
(defmacro define-class (name base-class fields)
`(setf (gethash ,name *classes*) (cons (car ',base-class) ',fields)))
But then it starts getting ugly:
(defun write-java-classes ()
(loop for class being the hash-keys in *classes* using (hash-value value)
do
(let* ((filename (concatenate 'string *java-directory* class
".java"))
(custom-section (read-custom-section filename)))
(format t "writing file ~a~%" filename)
(with-open-file (s (concatenate 'string filename)
:direction :output
:if-exists :supersede)
(destructuring-bind (base-class . fields) value
(format s "public class ~a ~a implements Foo {~%"
class
(if base-class (format nil "extends ~a" base-class)
""))
(write-custom-section s custom-section)
(loop for (name type) in fields do
(format s " public ~a ~a = ~a;~%"
(get-java-type-name type)
name
(get-default-value type)))
(format s " public String serialize() throws Exception {~%")
...
Lots of formats and difficult to read code, because of mixed target
language code within format strings and Lisp code, which is difficult to
maintain. How can I improve this?
Divide and conquer.
First, define a data structure for the source representation of classes. In your example, that could be something like this:
'(class "Test" (fields ("foo" number) ("bar" string)))
Then, define a similar data structure for the target representation, so for example this:
'(class "Test"
(implements "Foo")
(field public int "foo")
(field public string "bar")
(setter-method int "setFoo" "foo")
(getter-method int "getFoo" "foo")
(method serialize ...)
(method deserialize ...)
...)
It's a good idea to make this "compatible" with s-expressions, i.e., each structure should have a symbol as its car that states what the content is about. This makes it much easier to process them with functions built into Common Lisp.
Then you can write functions that convert the source representation into the target representation. The final step of converting the target representation to a textual format is then quite straightforward.
One idiom that I use for dealing with s-expressions is this:
(defun process (s-expression)
(apply #'process-generic s-expression))
(defgeneric process-generic (car &rest cdr))
Then you can write handy methods like this:
(defmethod process-generic ((car (eql 'class)) &rest cdr)
(destructuring-bind
(fields methods) cdr
...))
....or some such...
I hope this helps.
Pascal
--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
.
- References:
- best way to write a source code generator?
- From: Frank Buss
- best way to write a source code generator?
- Prev by Date: Re: Keywords and CL-WHO
- Next by Date: Re: (read-from-string "#.(values) 42")
- Previous by thread: best way to write a source code generator?
- Next by thread: Re: best way to write a source code generator?
- Index(es):
Relevant Pages
|