21 Commits

Author SHA1 Message Date
37a252f2b1 Make Sex templates more pleasant syntactically
No more unquotes to instantiate templates.
Also no need to pass quoted substitution lists, just use them as
regular lisp macros.
2025-07-11 18:27:50 +03:00
162d4f5322 add info about Emacs sex-mode to Readme.org 2025-06-19 12:31:43 +03:00
7115f61538 update Readme.org
a bit of spellchecking, add some examples for templates
2025-06-16 15:18:21 +03:00
alex-eg
a1e0fbc4f9 support unkebabification in #()-forms
which are vectors in Chicken, {}-initializers in C
2025-06-14 19:02:45 +03:00
alex-eg
e1c386ebcd support cpp define 2025-06-14 18:49:01 +03:00
alex-eg
27822cee2a prefix define and define-syntax with chicken-
as we have define in cpp
2025-06-14 18:09:50 +03:00
alex-eg
af6b7a37ee don't unkebabify -> in symbols (it's -> from C) 2025-06-14 17:48:41 +03:00
alex-eg
1229f89153 fix pub function prototypes 2025-06-14 17:21:40 +03:00
alex-eg
bb3d7c1d3e fix non-symbolic template replacements causing error on expansion
symbol->string doesn't work for anything besides synmols. And not
any (not (list? ...)) is a symbol. String are not, for example.
2025-06-14 17:08:18 +03:00
alex-eg
df9c62ffa4 add define and define-syntax syntax coloring in sex-mode.el 2025-06-14 14:10:19 +03:00
alex-eg
3da85c3e2e change load to chicken-load in sex sources
to explicitly denote that it is chiken
2025-06-14 14:09:39 +03:00
alex-eg
8b1ade4f24 add chicken-import support to sex
also fix unquoting to single atom processing wrongly in walk-sex-tree
2025-06-14 14:07:41 +03:00
alex-eg
750d8f8256 rename dir lib -> example
because it is not quite lib yet, but an example dump definitely
2025-06-14 12:40:15 +03:00
alex-eg
db8ff39171 hanlde pub for global vars correctly 2025-06-14 12:39:13 +03:00
alex-eg
0ebdef2792 add union support to sex-mode.el 2025-06-13 18:21:06 +03:00
alex-eg
2295b5ad17 add extern support 2025-06-13 18:20:51 +03:00
alex-eg
4efa730b21 sex headers are now seh, not hsex 2025-06-13 14:47:55 +03:00
alex-eg
7f2463823a update Readme.org 2025-06-13 14:31:10 +03:00
alex-eg
e9c0323e6e add field access support (also unfuck tree walker a bit) 2025-06-12 23:40:35 +03:00
alex-eg
497603c111 remove useless empty line 2025-06-12 23:40:15 +03:00
alex-eg
35c7539d1e fix indent 2025-06-12 23:40:08 +03:00
8 changed files with 399 additions and 115 deletions

View File

@@ -74,10 +74,151 @@ ugly ~GL_ARRAY_BUFFER~ s in your code, they may be written in their
proper form: ~GL-ARRAY-BUFFER~. proper form: ~GL-ARRAY-BUFFER~.
** Auto typedef for structs ** Auto typedef for structs
Probably harmless idk. 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 ** Use an established environment for development
As Sex is S-expressions, you always have Emacs with paredit as your As Sex is S-expressions, you always have Emacs with paredit as your
best option. best option.
** COMING SOON: Polymorhpism *** 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

36
example/list.seh Normal file
View File

@@ -0,0 +1,36 @@
(template (list-T ?T)
(struct list-?T
((?T value)
((* list-?T) next))))
(template (make-list-T ?T is-public?)
(,@(if 'is-public? '(pub) '()) fn (* list-?T) make-list-?T ()
(var (* list-?T) list (cast (* list-?T) (malloc (sizeof list-?T))))
(= (-> list next) NULL)
list))
(template (add-value-list-?T ?T is-public?)
(,@(if 'is-public? '(pub) '()) fn void add-value-list-?T ((list-?T *list) (T value))
(while (!= (-> list next) NULL)
(= list (-> list next)))
(= (-> list next) (make-list-?T))
(= (-> list value) value)))
(template (length-list-?T ?T is-public?)
(,@(if 'is-public? '(pub) '()) fn size-t length-list-?T ((list-?T *list))
(var size-t n 0)
(while (!= (-> list next) NULL)
(= list (-> list next))
(++ n))
n))
(template (is-empty-list-?T ?T is-public?)
(,@(if 'is-public? '(pub) '()) fn bool is-empty-list-?T ((list-?T *list))
(== (-> list next) NULL)))
(template (list-for-each list-var elt-var what-do)
(var int elt-var (-> list-var value))
(while (!= (-> list-var next) NULL)
what-do
(= list-var (-> list-var next))
(= elt-var (-> list-var value))))

69
example/test-list.sex Normal file
View File

@@ -0,0 +1,69 @@
(include stdlib.h)
(include stddef.h)
(include stdbool.h)
(include stdio.h)
(chicken-import srfi-1 brev-separate) ; list routines, e.g. fold
(chicken-load "list.seh")
(chicken-define (imports-test a b c)
(fold + 0 (list 1 2 3 a b c)))
(struct foo
((float a-field)
(int b)
((const char *) c)
((fn bool ((bool val))) not)))
(var foo f #((= .a-field 1.2)))
(list-T int)
(make-list-T int)
(add-value-list-T int)
(length-list-T int)
(is-empty-list-T int)
(extern fn void puk ((int a) (float b)))
(fn int bar () ,(imports-test 10 20 30))
(pub fn void baz () true)
(extern var int i)
(var int j)
(pub var int k)
(pub fn int main ()
(var (* list-int) l (make-list-int))
(printf "Size of the list: %zu\n" (length-list-int l))
(add-value-list-int l 3)
(add-value-list-int l 4)
(printf "Size of the list: %zu\n" (length-list-int l))
(list-for-each l v
(printf "%d " v))
(printf "\n")
(printf "%zu\n" l->next)
0)
(pub fn segs-renderer* create-renderer ())
(pub fn void clear-command-buffer ((segs-renderer *r)))
(pub fn void add-render-command ((segs-renderer *r) ((fn void ()) command)))
(pub fn void commit-command-buffer ((segs-renderer *r)))
(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"))

View File

@@ -1,36 +0,0 @@
(template (list-T (T))
(struct list-T
((T value)
((* list-T) next))))
(template (make-list-T (T) is-public?)
(,@(if is-public? '(pub) '()) fn (* list-T) make-list-T ()
(var (* list-T) list (cast (* list-T) (malloc (sizeof list-T))))
(= (-> list next) NULL)
list))
(template (add-value-list-T (T) is-public?)
(,@(if is-public? '(pub) '()) fn void add-value-list-T ((list-T *list) (T value))
(while (!= (-> list next) NULL)
(= list (-> list next)))
(= (-> list next) (make-list-T))
(= (-> list value) value)))
(template (length-list-T (T) is-public?)
(,@(if is-public? '(pub) '()) fn size-t length-list-T ((list-T *list))
(var size-t n 0)
(while (!= (-> list next) NULL)
(= list (-> list next))
(++ n))
n))
(template (is-empty-list-T (T) is-public?)
(,@(if is-public? '(pub) '()) fn bool is-empty-list-T ((list-T *list))
(== (-> list next) NULL)))
(template (list-for-each (what-do list-var elt-var))
(var int elt-var (-> list-var value))
(while (!= (-> list-var next) NULL)
what-do
(= list-var (-> list-var next))
(= elt-var (-> list-var value))))

View File

@@ -1,28 +0,0 @@
(include stdlib.h)
(include stddef.h)
(include stdbool.h)
(include stdio.h)
(load "list.hsex")
(struct foo
((float a)
(int b)))
,(list-T '(int))
,(make-list-T '(int) #f)
,(add-value-list-T '(int) #f)
,(length-list-T '(int) #f)
,(is-empty-list-T '(int) #f)
(fn bool bar () false)
(pub fn int main ()
(var (* list-int) l (make-list-int))
(printf "Size of the list: %zu\n" (length-list-int l))
(add-value-list-int l 3)
(add-value-list-int l 4)
(printf "Size of the list: %zu\n" (length-list-int l))
,(list-for-each '((printf "%d " v) l v))
(printf "\n")
0)

View File

@@ -28,12 +28,19 @@ All commands in `lisp-mode-shared-map' are inherited by this map.")
(list (list
;; Declarations ;; Declarations
(list (concat "(" (list (concat "("
(regexp-opt '("include" (regexp-opt '("chicken-define"
"chicken-define-syntax"
"chicken-import"
"chicken-load"
"define"
"extern"
"include"
"fn" "fn"
"pub" "pub"
"struct" "struct"
"template" "template"
"var") "var"
"union")
'word) 'word)
"\\>" "\\>"
"[[:space:]]*" "[[:space:]]*"

155
sexc.scm
View File

@@ -3,12 +3,14 @@
(import brev-separate (import brev-separate
(chicken pathname) (chicken pathname)
(chicken plist)
(chicken pretty-print) (chicken pretty-print)
(chicken process-context) (chicken process-context)
(chicken string) (chicken string)
fmt fmt
fmt-c fmt-c
getopt-long getopt-long
regex
srfi-1 ; list routines srfi-1 ; list routines
tree) tree)
@@ -22,13 +24,16 @@
((-=) sym) ((-=) sym)
(else (else
(string->symbol (string->symbol
(string-translate (symbol->string sym) #\- #\_))))) (string-substitute "-(?!>)" "_"
(symbol->string sym) #t)))))
(define (atom-to-fmt-c atom) (define (atom-to-fmt-c atom)
(case atom (case atom
((fn) '%fun) ((fn) '%fun)
((prototype) '%prototype)
((var) '%var) ((var) '%var)
((begin) '%begin) ((begin) '%begin)
((define) '%define)
((pointer) '%pointer) ((pointer) '%pointer)
((array) '%array) ((array) '%array)
((@) 'vector-ref) ((@) 'vector-ref)
@@ -45,57 +50,137 @@
(eq? (car node) symbol)) (eq? (car node) symbol))
#f))) #f)))
(define (make-field-access form)
(assert (= 2 (length form)) "Wrong field access format")
(unkebabify
(string->symbol
(fmt #f (cadr form) (car form)))))
(define (walk-generic form acc) (define (walk-generic form acc)
(if (eq? (car form) 'unquote) (cond
;; special case - replace top-level unquote with it's expansion ((null? form) (cons '() acc))
(append
(fold append (list) ;; vector, e.g. {}-initializer
(map (fn (walk-sex-tree x (list))) ((vector? form)
(eval (cadr form)))) (cons
acc) (list->vector
(let loop ((unquote-form (tree-find (tree-finder 'unquote) form #f))) (car (walk-sex-tree (vector->list form) (list))))
(if unquote-form acc))
(begin
(let* ((inv (invert-tree form)) ;; atom (hopefully)
(pos (tree-local-position inv unquote-form))) ((not (list? form)) (cons (atom-to-fmt-c form) acc))
(for-each (fn
(set! form (tree-insert inv (tree-parent inv unquote-form) pos x)) ;; special case - replace unquote with its expansion
(set! inv (invert-tree form))) ((eq? (car form) 'unquote)
(reverse (eval (cadr unquote-form)))) (fold
(set! form (tree-prune inv unquote-form))) cons
(loop (tree-find (tree-finder 'unquote) acc
form (car ; bc walk-sex-tree always
#f))) ; wraps its result
(cons (tree-map atom-to-fmt-c form) acc))))) (walk-sex-tree (eval (cadr form)) (list)))))
;; another special case - field access
((and (symbol? (car form))
(char=? #\. (string-ref (symbol->string (car form)) 0)))
(cons (make-field-access form) acc))
;; another special case - template
((template? form)
(append (fold
walk-generic
(list)
(eval form))
acc))
;; toplevel, or a start of a regular list form
(else
(let ((new-acc (list)))
(cons (reverse
(fold
walk-generic
new-acc
form))
acc)))))
(define (normalize-fn-form form)
;; (fn ret-type name arglist body) -> normal function
;; (fn ret-type name arglist) -> prototype
(if (>= (length form) 5)
form
(cons 'prototype (cdr form))))
(define (walk-function form static acc) (define (walk-function form static acc)
(if static (if static
(walk-generic (list 'static form) acc) (append (walk-generic (list 'static (normalize-fn-form form))
(walk-generic (cdr form) acc))) (list))
acc)
(append (walk-generic (normalize-fn-form (cdr form))
(list))
acc)))
(define (walk-struct form acc) (define (walk-struct form acc)
(let ((name (unkebabify (cadr form)))) (let ((name (unkebabify (cadr form))))
(walk-generic form (cons `(typedef struct ,name ,name) acc)))) (append (walk-generic form (list))
(cons `(typedef struct ,name ,name) acc))))
(define (walk-extern form acc)
(case (cadr form)
((fn)
(append
(list (cons 'extern (walk-function form #f (list))))
acc))
((var)
(append
(list (cons 'extern (walk-generic (cdr form) (list))))
acc))
(else (error "Extern what?"))))
(define (walk-public form acc)
(case (cadr form)
((fn)
(walk-function form #f acc))
((var)
(append (walk-generic (list 'static (cdr form)) (list)) acc))
(else (error "Pub what?"))))
(define (template? form)
(and (list? form)
(symbol? (car form))
(get (car form) 'sex-template)))
(define (walk-sex-tree form acc) (define (walk-sex-tree form acc)
(case (car form) (if (list? form)
((fn) (walk-function form #t acc)) (if (template? form)
((pub) (walk-function form #f acc)) (fold (fn (walk-sex-tree x y))
((struct) (walk-struct form acc)) acc
(else (walk-generic form acc)))) (eval form))
(case (car form)
((fn) (walk-function form #t acc))
((extern) (walk-extern form acc))
((pub) (walk-public form acc))
((struct union) (walk-struct form acc))
((unquote) (fold (fn (walk-sex-tree x y))
acc
(eval (cadr form))))
(else (append (walk-generic form (list)) acc))))
;; only for unquote support
(list (list (atom-to-fmt-c form)))))
(define (process-form form acc) (define (process-form form acc)
(case (car form) (case (car form)
((define) (eval form) acc) ((chicken-define) (eval (cons 'define (cdr form))) acc)
((template) (eval form) acc) ((template) (eval form) acc)
((load) (eval form) acc) ((chicken-load)
((instance) (walk-sex-tree (eval form) acc)) (load (cadr form)) acc)
((chicken-import)
(eval (cons 'import (cdr form))) acc)
(else (else
(walk-sex-tree form acc)))) (walk-sex-tree form acc))))
(define (process-raw-forms raw-forms acc) (define (process-raw-forms raw-forms acc)
(if (null? raw-forms) (filter (fn (not (null? x))) (if (null? raw-forms)
(reverse acc)) (reverse acc)
(process-raw-forms (cdr raw-forms) (process-raw-forms (cdr raw-forms)
(process-form (car raw-forms) acc)))) (process-form (car raw-forms) acc))))

View File

@@ -1,19 +1,27 @@
(declare (unit templates)) (declare (unit templates))
(import brev-separate (import
fmt (chicken plist)
regex brev-separate
srfi-1 ; list routines fmt
tree) regex
srfi-1 ; list routines
tree)
(define (register-template name)
(put! name 'sex-template #t))
(define-syntax template (define-syntax template
(syntax-rules () (syntax-rules ()
((_ (name subst-list args ...) body ...) ((template (name . args) . body)
(define (name subst-list-arg args ...) (begin
(let* ((subst-alist (map cons 'subst-list subst-list-arg)) (register-template 'name)
(replaced-body (apply-substitution `(body ...) subst-alist))) (define-syntax name
replaced-body))))) (syntax-rules ()
((name . applied-args)
(let* ((subst-alist (map cons 'args 'applied-args))
(replaced-body (apply-substitution `body subst-alist)))
replaced-body))))))))
(define (apply-symbol-substitution sym subst-alist) (define (apply-symbol-substitution sym subst-alist)
;; All non-symbol substitutions will be filtered. ;; All non-symbol substitutions will be filtered.
@@ -23,10 +31,12 @@
(subst-map (map (fn (subst-map (map (fn
(if (symbol? (car x)) (if (symbol? (car x))
(cons (cons
(fmt #f "([^\\-]?)" (symbol->string (car x)) "([\\-$]?)") (fmt #f "([^\\-]?)"
(regexp-escape (symbol->string (car x)))
"([\\-$]?)")
(fmt #f "\\1" (symbol->string (cdr x)) "\\2")) (fmt #f "\\1" (symbol->string (cdr x)) "\\2"))
x)) x))
(filter (fn (not (list? (cdr x)))) subst-alist)))) (filter (fn (symbol? (cdr x))) subst-alist))))
(string->symbol (string->symbol
(string-substitute* str subst-map)))) (string-substitute* str subst-map))))