No more unquotes to instantiate templates. Also no need to pass quoted substitution lists, just use them as regular lisp macros.
225 lines
5.2 KiB
Org Mode
225 lines
5.2 KiB
Org Mode
* The Sex language
|
|
Sex is a S-expressions language, which transpiles to C.
|
|
|
|
Sex is also Chicken, since all source processing and compile-time
|
|
computations are written in Chicken.
|
|
|
|
And Chicken is [[https://call-cc.org][R5RS Scheme]].
|
|
|
|
* Compilation and usage
|
|
First, get yourself a Chicken. Second, some Chicken deps.
|
|
|
|
** Install Chicken Eggs
|
|
By the way, there's a way to make Chicken install eggs non-globally. Refer to
|
|
the documentation for more info:
|
|
https://wiki.call-cc.org/man/5/Extension%20tools#changing-the-repository-location
|
|
|
|
~chicken-install fmt getopt-long brev-separate~
|
|
|
|
** Compilation
|
|
~make~
|
|
|
|
** Usage
|
|
You'll also need a C compiler, so pick any.
|
|
#+begin_src
|
|
cat hello-world.sex | sexc > hello_world.c
|
|
cc hello_world.c -o hello_world
|
|
#+end_src
|
|
|
|
* Example
|
|
Here is an example, demonstrating what Sex source looks like, and what
|
|
it compiles too. An avid reader also shall notice how we call Chicken
|
|
procedures in Sex source.
|
|
|
|
The Sex source:
|
|
#+begin_src
|
|
(include stdio.h)
|
|
|
|
(define (foo)
|
|
"Hello from Chicken code!\n")
|
|
|
|
(pub fn int main ((int argc) (char **argv))
|
|
(puts "Hello from Sex!")
|
|
(var (array char 512) name)
|
|
(puts "What is your name?")
|
|
(scanf "%s" &name)
|
|
(printf "Hello, %s!\n" name)
|
|
(printf ,(foo))
|
|
0)
|
|
#+end_src
|
|
|
|
The resulting C source:
|
|
#+begin_src
|
|
#include <stdio.h>
|
|
|
|
int main (int argc, char **argv) {
|
|
puts("Hello from Sex!");
|
|
char name[512];
|
|
puts("What is your name?");
|
|
scanf("%s", &name);
|
|
printf("Hello, %s!\n", name);
|
|
printf("Hello from Chicken code!\n");
|
|
return 0;
|
|
}
|
|
#+end_src
|
|
|
|
* Features
|
|
** Full C interoperability
|
|
Just ~(include "Your/Favourite/Library.h")~ and use it as you would
|
|
have is C.
|
|
|
|
For hardcore fans of traditional Lisp naming convention,
|
|
Sex offers automatic unkebabification of all symbols, i.e. no more
|
|
ugly ~GL_ARRAY_BUFFER~ s in your code, they may be written in their
|
|
proper form: ~GL-ARRAY-BUFFER~.
|
|
|
|
** Auto typedef for structs
|
|
Probably harmless idk. Example:
|
|
#+begin_src
|
|
(struct foo
|
|
((float a)
|
|
(int b)))
|
|
#+end_src
|
|
expands to
|
|
#+begin_src
|
|
typedef struct foo foo;
|
|
|
|
struct foo {
|
|
float a;
|
|
int b;
|
|
};
|
|
#+end_src
|
|
|
|
** Syntactic templates
|
|
Sex has support for template substitutions. Any piece of code can be
|
|
templated. Template declarations look like functions: they have a
|
|
name, an argument list and a body. When declared template is
|
|
encountered during reading of Sex code, its body will udergo syntactic
|
|
rewriting using the provided values by the following rules:
|
|
1. If the value is a symbol, all arguments in a body are replaced with
|
|
the value, and also all /parts/ of any other symbol equal to the
|
|
value also get replaced.
|
|
2. If the value is a non-symbolic form, all arguments in a body are
|
|
replaced with it, but no symbolic substitution is performed.
|
|
|
|
Formally, template declaration has the following syntax:
|
|
#+begin_src
|
|
(template (name . substitute-args) . body)
|
|
#+end_src
|
|
|
|
*** Examples:
|
|
**** Structure with templated value type
|
|
#+begin_src
|
|
(template (foo ?T)
|
|
(struct foo-?T
|
|
((?T value))))
|
|
|
|
(foo float)
|
|
#+end_src
|
|
->
|
|
#+begin_src
|
|
typedef struct foo_float foo_float;
|
|
|
|
struct foo_float {
|
|
float value;
|
|
};
|
|
#+end_src
|
|
|
|
Note that ~?~ at the start of template argument is not syntax, just
|
|
convention.
|
|
|
|
**** Wrapper for checking return codes
|
|
#+begin_src
|
|
(template (check-sdl-return call message ret-code)
|
|
(if (< 0 call)
|
|
(begin
|
|
(puts message)
|
|
(return ret-code))))
|
|
|
|
(fn int init ()
|
|
(check-sdl-return
|
|
(SDL-Init SDL-INIT-VIDEO) "Failed to initialize SDL" 1)
|
|
...)
|
|
#+end_src
|
|
->
|
|
#+begin_src
|
|
static int init () {
|
|
if (0 < SDL_Init(SDL_INIT_VIDEO)) {
|
|
puts("Failed to initialize SDL");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
#+end_src
|
|
|
|
**** A bit of everything
|
|
#+begin_src
|
|
(template (list-T ?T)
|
|
(struct list-?T
|
|
((?T value)
|
|
((* list-?T) next))))
|
|
|
|
(template (list-for-each type list-var elt-var body)
|
|
(var type elt-var (-> list-var value))
|
|
(while (!= (-> list-var next) NULL)
|
|
body
|
|
(= list-var (-> list-var next))
|
|
(= elt-var (-> list-var value))))
|
|
|
|
; ... somewhere later
|
|
(list-T int)
|
|
|
|
(pub fn void print-list (((const list-int) *l))
|
|
(list-for-each int l v (printf "%d " v))
|
|
(printf "\n"))
|
|
#+end_src
|
|
Then will be expanded in the following code:
|
|
#+begin_src
|
|
(typedef struct list_int list_int)
|
|
(struct list_int ((int value) ((* list_int) next)))
|
|
(%fun void
|
|
print_list
|
|
(((const list_int) *l))
|
|
(%var int v (-> l value))
|
|
(while (!= (-> l next) NULL)
|
|
(printf "%d " v)
|
|
(= l (-> l next))
|
|
(= v (-> l value)))
|
|
(printf "\n"))
|
|
#+end_src
|
|
|
|
And then translated to:
|
|
#+begin_src
|
|
typedef struct list_int list_int;
|
|
|
|
struct list_int {
|
|
int value;
|
|
list_int *next;
|
|
};
|
|
|
|
void print_list (const list_int *l) {
|
|
int v = l->value;
|
|
while (l->next != NULL) {
|
|
printf("%d ", v);
|
|
l = l->next;
|
|
v = l->value;
|
|
}
|
|
printf("\n");
|
|
}
|
|
#+end_src
|
|
|
|
** Use an established environment for development
|
|
As Sex is S-expressions, you always have Emacs with paredit as your
|
|
best option.
|
|
|
|
*** sex-mode.el
|
|
To harness the power of sex-mode, add the following lines to your
|
|
~$HOME/.config/emacs/init.el~:
|
|
#+begin_src
|
|
(use-package sex-mode
|
|
:load-path "/path/to/sex"
|
|
:mode ("\\.sex\\'" "\\.seh\\'"))
|
|
#+end_src
|
|
|
|
** COMING SOON?: Polymorphism
|