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?
--
Frank Buss, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Pascal Costanza
Subject: Re: best way to write a source code generator?
Date:
Message-ID: <4ngeipFa8eorU1@individual.net>
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/
Frank Buss <··@frank-buss.de> wrote:
+---------------
| If I want to create Java files and files in other languages like this:
| ..
| How do I write a generator for this in Lisp? I've started with this:
| ..
| But then it starts getting ugly: ...
...
| 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?
+---------------
Take a look at Aubrey Jaffer's "Schlep":
http://www-swiss.ai.mit.edu/~jaffer/Docupage/schlep.html
Yes, I know it's from Scheme to C, not CL to Java, and the weird
Hungarian typing is a turnoff [IMHO], though I once made the beginnings
of adding CL-style typing decorations, but he spent a lot of effort
trying to get the resulting generated C code be readable, properly
indented, etc. [And IMHO he did fairly well.]
You might find a useful idea or two in there [or not, YMMV]...
-Rob
-----
Rob Warnock <····@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
In article <······························@40tude.net>,
Frank Buss <··@frank-buss.de> 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)))
Why start with a macro? Start with the data. Define the data
and write procedures to process the data.
A macro can be added later. In this early design stage it
makes debugging hard. Think functions.
Start with one java class. Process the components of the class.
You really need to think about bottom up design in Lisp.
>
> 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:
>
Again, output adds just another complexity in early design.
Output to files can be added later. Start with writing to
a stream, for example just standard output. Iteration over
a data structure is also to be added later.
write-java-class (class stream)
write-java-class-component ...
write-java-class-component ...
write-java-class-component ...
write-java-class-component ...
write-java-classes (classes stream)
write-java-classes-to-file (classes pathname)
Build a language of operators that work for what you want
to do. Build these operators so that they can be easily
combined (FUNCTIONS) or extended (GENERIC FUNCTIONS).
> (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?
CL-HTTP has several examples for languages embedded in Lisp.
For example to write some HTML versions. It uses an approach
to define functions and macros representing code generators
for the target language. In the Lisp user code you would then
use the macros and functions to generate the target code. There
you won't see any FORMAT statements anymore. Macros would be
used to create scope (WITH-JAVA-CLASS , ...) and functions
would be used to create output (WRITE-JAVA-PARAMETER-LIST, ...).
The code is optimized to cons as little as possible.
From: Thomas A. Russ
Subject: Re: best way to write a source code generator?
Date:
Message-ID: <ymiirjb4xhr.fsf@sevak.isi.edu>
Frank Buss <··@frank-buss.de> wrote:
> If I want to create Java files and files in other languages like this:
[snip]
> How do I write a generator for this in Lisp? I've started with this:
>
> (define-class "Test" ()
> (("foo" number)
> ("bar" string)))
Well, you could take a look at our Stella project:
http://www.isi.edu/isd/LOOM/Stella
This involves doing the programming in our own intermediate language and
then processing the resulting code for translation into CL, Java or C++.
For example, we would write
(defclass Test ()
:public-slots
((foo :type integer)
(bar :type string)))
which would be translated into:
(CL:DEFCLASS TEST (CLSYS-ROOT-OBJECT)
((FOO :TYPE CL:FIXNUM :INITFORM NULL-INTEGER
:ALLOCATION :INSTANCE
:ACCESSOR %FOO)
(BAR :TYPE CL:SIMPLE-STRING :INITFORM STELLA::NULL-STRING
:ALLOCATION :INSTANCE :ACCESSOR %BAR)))
(CL:DEFUN NEW-TEST ()
(CL:LET* ((SELF NULL))
(CL:SETQ SELF (CL:MAKE-INSTANCE (CL:QUOTE TEST)))
(CL:SETF (%BAR SELF) STELLA::NULL-STRING)
(CL:SETF (%FOO SELF) NULL-INTEGER)
(CL:RETURN-FROM NEW-TEST SELF)))
or for Java:
public class Test {
public int foo;
public String bar;
public static Test newTest() {
{ Test self = null;
self = new Test();
self.bar = null;
self.foo = Stella.NULL_INTEGER;
return (self);
}
}
}
and for C++, using the Boehm GC library:
class Test : public gc {
public:
int foo;
char* bar;
};
Test* newTest() {
{ Test* self = NULL;
self = new Test();
self->bar = NULL;
self->foo = NULL_INTEGER;
return (self);
}
}
--
Thomas A. Russ, USC/Information Sciences Institute
hi,
maybe 'linj' project have a connection with what you are trying to
achive
http://www.evaluator.pt/linj.html
the tutorial page may give some hints
http://www.evaluator.pt/downloads/tutorial.html
regards,
cmo-0
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?
>
> --
> Frank Buss, ··@frank-buss.de
> http://www.frank-buss.de, http://www.it4-systems.de
Frank Buss <··@frank-buss.de> writes:
> How do I write a generator for this in Lisp? I've started with this:
For brainstorming purposes you might have a look at the pretty printer
section of this paper:
Some Useful Lisp Algorithms: Part 2
http://www.cl-user.net/asp/web-sites/tr1993-017
Paolo
--
Why Lisp? http://wiki.alu.org/RtL%20Highlight%20Film
The Common Lisp Directory: http://www.cl-user.net
From: Ivan Shvedunov
Subject: Re: best way to write a source code generator?
Date:
Message-ID: <4nhn6hFabun2U1@individual.net>
Maybe this will help a little bit:
http://depni.sinp.msu.ru/~ivan_iv/lisp/sharpclass.html
http://depni.sinp.msu.ru/~ivan_iv/lisp/sharpclass.lisp
(I wrote this little sample some time ago to show it in
a .NET-related forum)
From: Ivan Shvedunov
Subject: Re: best way to write a source code generator?
Date:
Message-ID: <4nhnc4Fagq9uU1@individual.net>
You may also google for monkeylib or ask Peter Seibel to provide you a
link to the latest version. It contains a nice Javascript generator
among other useful things.