Notes
――Please think of it as a kind of play. -It is not the story of Gauche's Are. ――The author is not very familiar with C language. Please point out any strange points.
Recently I've been playing with making Lisp in Python.
-Making minimal Lisp with Python (parsing) -Make a minimal Lisp with Python (evaluation + α) -Making minimal Lisp with Python (Jabashi)
This was fun with this, but I wasn't motivated because it wasn't practical, apart from using it and not using it, and I was wondering what to play next. At that time, I remembered reading an article that gauche seems to come with an S-expression → C language transpiler somewhere, and it seemed interesting, so I completely diverted the reader part of my own Lisp. I implemented it in Python. The original one is called CiSE (C in S-Expression), but the one implemented this time is ** a completely different thing **. By the way, it has a rather Common Lisp flavor.
Published under the name CLOP, so if you are interested, please. By the way, it's a name I don't really like, so I might change it if I think of it. CiSE can be cool.
The flow of generating C language from S-expressions is as follows.
A * predefined function * is, for example:
def aref(array, subscript):
return f"{array}[{subscript}]"
For example, (aref hoge fuga)
is converted to["aref", "hoge", "fuga"]
(1), and then" hoge [fuga] "
is generated (2). is.
At this time, if the first element of the list is not looked up, it is processed as a function of c. For example, (printf" hello ")
produces " printf (\ "hello \") "
(3).
Also, + hoge +
is converted to HOGE
, and hoge-hoge
is converted to hoge_hoge
.
I used the expression ** function ** as defined in advance, but the point is that it is like a macro. The following macros are defined in advance in CLOP.
* + - / 1+ 1- < > and aref ash break cast cond continue
decf declare defconstant defenum defstruct defsyntax
defun defunion defvar eq for if incf include let logand
logior logxor mod not or progn return setf when while
Most of the names come from Common Lisp, C, so you can almost imagine what they do.
If there is no oversight, you should be able to handle almost all C language functions.
Hello, world!
hello.clop
(include stdio)
(defun int:main (void)
(printf "Hello, world!\n")
(return 0))
I think I haven't seen it. The type is declared with the specifiers separated by colons, such as ʻunsigned: int: hoge`. You can convert it to C language with the following command.
$ clop translate hello.clop --out hello.c
hello.c
#include <stdio.h>
int main (void)
{
printf("Hello, world!\n");
return 0;
}
You can use macros for the time being. However, CLOP is nothing more than a ** list-> string converter ** after all, so there is no list operation function. Therefore, you cannot use powerful macros like Common Lisp, but you can write more flexibly than macro functions in C language. For example.
python
(defsyntax null (ptr)
(eq ,ptr +null+))
This is expanded as follows.
hoge == NULL // (null hoge)
It is unpleasant to have backticks even though there are no quotes, but if you add backticks, the value will be inserted from the argument given at the time of macro expansion. This is the same as the CPP macro, so here is a slightly more complicated example. CLOP allows you to use Common Lisp-like optional arguments, keyword arguments, and variadic arguments.
with-open-file.clop
(include stdio)
(defsyntax null (ptr)
(eq ,ptr +null+))
(defsyntax with-open-file ((stream filespec &optional (mode "w")) &body body)
(let ((FILE*:,stream (fopen ,filespec ,mode)))
(if (null ,stream)
(perror "Failed to open file")
(progn
,@body
(fclose ,stream)))))
If you do , @ hoge
, the contents of the argument will be expanded as it is. (One parenthesis can be taken)
You can write the macro definition in a single source code, but you can also pull it from another file with require
. At this time, the header file that is ʻincludeis also pulled at the same time if it is not already included. In addition, except for macro definition and include, it is skipped. Here is an example of
require the previous
with-open-file.clopwith
main.clop`.
main.clop
(include stdio)
(require with-open-file)
(defun int:main (void)
(with-open-file (fd "hello.txt")
(fprintf fd "Hello, world!\n"))
(return 0))
$ clop translate main.clop --out main.c
main.c
#include <stdio.h>
int main (void)
{
({
FILE* fd = fopen("hello.txt", "w");
fd == NULL? (perror("Failed to open file")) : (({
fprintf(fd, "Hello, world!\n");
fclose(fd);
}));
});
return 0;
}
It's a creepy code, but it works fine. CLOP makes heavy use of compound sentence expressions (such as ({hoge; fuge; ...})
), which can make readability terrible in some cases.
Let's think about the advantages.
--Improved readability (depending on the person) --The description will be shorter (maybe) --You can use macros with a little flexibility
Except for the third one, it's subtle.
I felt that the syntax of C language was complicated. I don't think when writing raw C, but when generating from an S-expression, I was quite worried about where to put parentheses and where to put a semicolon. Given that, I think the expressive power of S-expressions is amazing.
Implemented Oreore CiSE in Python. I think there is a universal desire to write non-S-expressions in S-expressions (for example, Hy). What about C language? I would like to touch the CiSE of the original gauche, but it is a pity that there is not much information. (Maybe you just can't find it. Please let me know if you have good documentation)
** Addendum ** There was such a thing. https://github.com/burtonsamograd/sxc
Recommended Posts