Compare commits
21 Commits
refactor-t
...
rework-tem
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88b441b58c | ||
|
|
ad983a0d9c | ||
| 2ba9ad37b7 | |||
| fea7e58dd1 | |||
| dead339e8c | |||
| f46e087958 | |||
| e195ba8553 | |||
| 9a1757dda8 | |||
| 2641e9f4fb | |||
| 3d080b2bb0 | |||
| 3224e8cf6a | |||
| 0452f442b1 | |||
| 4389392123 | |||
|
|
555bd836a9 | ||
|
|
c4dd5786a8 | ||
|
|
24bcd93a36 | ||
|
|
80b69cc8ad | ||
|
|
794e97c473 | ||
|
|
61ddabf85f | ||
|
|
861b862a13 | ||
|
|
7007623e94 |
19
Makefile
19
Makefile
@@ -1,4 +1,19 @@
|
|||||||
CHICKEN_C = csc
|
CHICKEN_C = csc
|
||||||
|
|
||||||
sexc: sexc.scm
|
MODULES = sexc templates main
|
||||||
$(CHICKEN_C) $< -o $@
|
OBJ = $(MODULES:%=%.o)
|
||||||
|
|
||||||
|
# chicken flags
|
||||||
|
CFLAGS = -compile-syntax
|
||||||
|
|
||||||
|
sexc: $(OBJ)
|
||||||
|
$(CHICKEN_C) $^ -o $@
|
||||||
|
|
||||||
|
main.o: main.scm
|
||||||
|
$(CHICKEN_C) $< -c -o $@
|
||||||
|
|
||||||
|
%.o: %.scm
|
||||||
|
$(CHICKEN_C) $< -e -c -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OBJ) sexc
|
||||||
|
|||||||
61
Readme.org
61
Readme.org
@@ -1,9 +1,20 @@
|
|||||||
* The Sex language
|
* The Sex language
|
||||||
Sex is a S-expressions language, which transpiles to C.
|
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
|
* Compilation and usage
|
||||||
Sex is written in Chicken Scheme, so first you'll need to get yourself
|
First, get yourself a Chicken. Second, some Chicken deps.
|
||||||
a Chicken.
|
|
||||||
|
** 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
|
** Compilation
|
||||||
~make~
|
~make~
|
||||||
@@ -15,14 +26,56 @@ cat hello-world.sex | sexc > hello_world.c
|
|||||||
cc hello_world.c -o hello_world
|
cc hello_world.c -o hello_world
|
||||||
#+end_src
|
#+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
|
* Features
|
||||||
** Full C interoperability
|
** Full C interoperability
|
||||||
Just ~(include "Your/Favourite/Library.h")~ and use it as you would
|
Just ~(include "Your/Favourite/Library.h")~ and use it as you would
|
||||||
have is C. For hardcore fans of traditional Lisp naming convention,
|
have is C.
|
||||||
|
|
||||||
|
For hardcore fans of traditional Lisp naming convention,
|
||||||
Sex offers automatic unkebabification of all symbols, i.e. no more
|
Sex offers automatic unkebabification of all symbols, i.e. no more
|
||||||
ugly ~GL_ARRAY_BUFFER~'s in your code, they may be writted in their
|
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
|
||||||
|
Probably harmless idk.
|
||||||
|
|
||||||
** 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.
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
(include stdio.h)
|
(include stdio.h)
|
||||||
(include unistd.h)
|
|
||||||
|
|
||||||
(fn int main ((int argc) (char **argv))
|
(define (foo)
|
||||||
|
"Hello from Chicken code!\n")
|
||||||
|
|
||||||
|
(struct foo
|
||||||
|
((int a)
|
||||||
|
(float b)))
|
||||||
|
|
||||||
|
(pub fn int main ((int argc) (char **argv))
|
||||||
|
(var int a 10)
|
||||||
(puts "Hello from Sex!")
|
(puts "Hello from Sex!")
|
||||||
(var (array char 513) name)
|
(var (array char 512) name)
|
||||||
(puts "What is your name?")
|
(puts "What is your name?")
|
||||||
(scanf "%s" &name)
|
(scanf "%s" &name)
|
||||||
(printf "Hello, %s!\n" name)
|
(printf "Hello, %s!\n" name)
|
||||||
|
(printf ,(foo))
|
||||||
0)
|
0)
|
||||||
|
|||||||
36
lib/list.hsex
Normal file
36
lib/list.hsex
Normal 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 (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))))
|
||||||
28
lib/test-list.sex
Normal file
28
lib/test-list.sex
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
(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)
|
||||||
6
main.scm
Normal file
6
main.scm
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
;;; The purpose of this file is to compile it to the only
|
||||||
|
;;; .o that has main entry point.
|
||||||
|
|
||||||
|
(declare (uses sexc))
|
||||||
|
|
||||||
|
(main)
|
||||||
91
sex-mode.el
Normal file
91
sex-mode.el
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
(require 'scheme)
|
||||||
|
|
||||||
|
(defgroup sex-mode nil
|
||||||
|
"Major mode for Sex code."
|
||||||
|
:prefix 'sex-
|
||||||
|
:group 'languages)
|
||||||
|
|
||||||
|
(define-abbrev-table 'sex-mode-abbrev-table ()
|
||||||
|
"Abbrev table for Sex mode.
|
||||||
|
It has `scheme-mode-abbrev-table' as its parent."
|
||||||
|
:parents (list scheme-mode-abbrev-table))
|
||||||
|
|
||||||
|
(defvar sex-mode-syntax-table
|
||||||
|
(let ((table (make-syntax-table lisp-data-mode-syntax-table)))
|
||||||
|
table))
|
||||||
|
|
||||||
|
(defvar sex-mode-map
|
||||||
|
(let ((map (make-sparse-keymap)))
|
||||||
|
(set-keymap-parent map lisp-mode-shared-map)
|
||||||
|
map)
|
||||||
|
"Keymap for Sex mode.
|
||||||
|
All commands in `lisp-mode-shared-map' are inherited by this map.")
|
||||||
|
|
||||||
|
(defvar sex-mode-line-process "")
|
||||||
|
|
||||||
|
(defconst sex-font-lock-keywords
|
||||||
|
(eval-when-compile
|
||||||
|
(list
|
||||||
|
;; Declarations
|
||||||
|
(list (concat "("
|
||||||
|
(regexp-opt '("include"
|
||||||
|
"fn"
|
||||||
|
"pub"
|
||||||
|
"struct"
|
||||||
|
"template"
|
||||||
|
"var")
|
||||||
|
'word)
|
||||||
|
"\\>"
|
||||||
|
"[[:space:]]*"
|
||||||
|
"\\([[:word:]]*\\)")
|
||||||
|
'(1 font-lock-keyword-face)
|
||||||
|
'(2 font-lock-function-name-face))
|
||||||
|
;; Keywords
|
||||||
|
(list (concat "("
|
||||||
|
(regexp-opt '(
|
||||||
|
"case"
|
||||||
|
"do"
|
||||||
|
"if"
|
||||||
|
"for"
|
||||||
|
"goto"
|
||||||
|
"return"
|
||||||
|
"switch"
|
||||||
|
"var"
|
||||||
|
"while")
|
||||||
|
'word)
|
||||||
|
"\\>")
|
||||||
|
'(1 'font-lock-builtin-face)))))
|
||||||
|
|
||||||
|
(defun sex-mode-set-variables ()
|
||||||
|
(set-syntax-table sex-mode-syntax-table)
|
||||||
|
(setq local-abbrev-table sex-mode-abbrev-table)
|
||||||
|
(setq mode-line-process '("" sex-mode-line-process))
|
||||||
|
(setq font-lock-defaults
|
||||||
|
'((sex-font-lock-keywords)
|
||||||
|
nil nil
|
||||||
|
(("+-*/.<>=!?$%_&:" . "w"))
|
||||||
|
nil
|
||||||
|
(font-lock-mark-block-function . mark-defun)))
|
||||||
|
(setq-local prettify-symbols-alist lisp-prettify-symbols-alist))
|
||||||
|
|
||||||
|
(put 'fn 'lisp-indent-function 'defun)
|
||||||
|
(put 'template 'lisp-indent-function 'defun)
|
||||||
|
(put 'struct 'lisp-indent-function 'defun)
|
||||||
|
(put 'var 'lisp-indent-function 0)
|
||||||
|
|
||||||
|
;;;###autoload
|
||||||
|
(define-derived-mode sex-mode lisp-data-mode "Sex"
|
||||||
|
"Major mode for editing Sex code.
|
||||||
|
Editing commands are similar to those of `lisp-mode'.
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
Delete converts tabs to spaces as it moves back.
|
||||||
|
Blank lines separate paragraphs. Semicolons start comments.
|
||||||
|
\\{sex-mode-map}"
|
||||||
|
:group 'sex-mode
|
||||||
|
(sex-mode-set-variables))
|
||||||
|
|
||||||
|
;;;###autoload
|
||||||
|
(add-to-list 'auto-mode-alist '("\\.\\(sex\\|hsex\\)\\'" . sex-mode))
|
||||||
|
|
||||||
|
(provide 'sex-mode)
|
||||||
206
sexc.scm
206
sexc.scm
@@ -1,33 +1,183 @@
|
|||||||
(import brev-separate fmt fmt-c tree (chicken string))
|
(declare (unit sexc)
|
||||||
|
(uses templates))
|
||||||
|
|
||||||
|
(import brev-separate
|
||||||
|
(chicken pathname)
|
||||||
|
(chicken pretty-print)
|
||||||
|
(chicken process-context)
|
||||||
|
(chicken string)
|
||||||
|
fmt
|
||||||
|
fmt-c
|
||||||
|
getopt-long
|
||||||
|
srfi-1 ; list routines
|
||||||
|
tree)
|
||||||
|
|
||||||
|
(define +debug+ #f)
|
||||||
|
|
||||||
(define (unkebabify sym)
|
(define (unkebabify sym)
|
||||||
(string->symbol
|
(case sym
|
||||||
(string-translate (symbol->string sym) #\- #\_)))
|
((-) sym)
|
||||||
|
((--) sym)
|
||||||
|
((->) sym)
|
||||||
|
((-=) sym)
|
||||||
|
(else
|
||||||
|
(string->symbol
|
||||||
|
(string-translate (symbol->string sym) #\- #\_)))))
|
||||||
|
|
||||||
(define (loop)
|
(define (atom-to-fmt-c atom)
|
||||||
|
(case atom
|
||||||
|
((fn) '%fun)
|
||||||
|
((var) '%var)
|
||||||
|
((begin) '%begin)
|
||||||
|
((pointer) '%pointer)
|
||||||
|
((array) '%array)
|
||||||
|
((@) 'vector-ref)
|
||||||
|
((include) '%include)
|
||||||
|
((cast) '%cast)
|
||||||
|
(else
|
||||||
|
(if (symbol? atom)
|
||||||
|
(unkebabify atom)
|
||||||
|
atom))))
|
||||||
|
|
||||||
|
(define (tree-finder symbol)
|
||||||
|
(lambda (node)
|
||||||
|
(or (and (tree? node)
|
||||||
|
(eq? (car node) symbol))
|
||||||
|
#f)))
|
||||||
|
|
||||||
|
(define (walk-generic form acc)
|
||||||
|
(if (eq? (car form) 'unquote)
|
||||||
|
;; special case - replace top-level unquote with it's expansion
|
||||||
|
(append
|
||||||
|
(fold append (list)
|
||||||
|
(map (fn (walk-sex-tree x (list)))
|
||||||
|
(eval (cadr form))))
|
||||||
|
acc)
|
||||||
|
(let loop ((unquote-form (tree-find (tree-finder 'unquote) form #f)))
|
||||||
|
(if unquote-form
|
||||||
|
(begin
|
||||||
|
(let* ((inv (invert-tree form))
|
||||||
|
(pos (tree-local-position inv unquote-form)))
|
||||||
|
(for-each (fn
|
||||||
|
(set! form (tree-insert inv (tree-parent inv unquote-form) pos x))
|
||||||
|
(set! inv (invert-tree form)))
|
||||||
|
(reverse (eval (cadr unquote-form))))
|
||||||
|
(set! form (tree-prune inv unquote-form)))
|
||||||
|
(loop (tree-find (tree-finder 'unquote)
|
||||||
|
form
|
||||||
|
#f)))
|
||||||
|
(cons (tree-map atom-to-fmt-c form) acc)))))
|
||||||
|
|
||||||
|
(define (walk-function form static acc)
|
||||||
|
(if static
|
||||||
|
(walk-generic (list 'static form) acc)
|
||||||
|
(walk-generic (cdr form) acc)))
|
||||||
|
|
||||||
|
(define (walk-struct form acc)
|
||||||
|
(let ((name (unkebabify (cadr form))))
|
||||||
|
(walk-generic form (cons `(typedef struct ,name ,name) acc))))
|
||||||
|
|
||||||
|
(define (walk-sex-tree form acc)
|
||||||
|
(case (car form)
|
||||||
|
((fn) (walk-function form #t acc))
|
||||||
|
((pub) (walk-function form #f acc))
|
||||||
|
((struct) (walk-struct form acc))
|
||||||
|
(else (walk-generic form acc))))
|
||||||
|
|
||||||
|
(define (process-form form acc)
|
||||||
|
(case (car form)
|
||||||
|
((define) (eval form) acc)
|
||||||
|
((template) (eval form) acc)
|
||||||
|
((load) (eval form) acc)
|
||||||
|
((instance) (walk-sex-tree (eval form) acc))
|
||||||
|
(else
|
||||||
|
(walk-sex-tree form acc))))
|
||||||
|
|
||||||
|
(define (process-raw-forms raw-forms acc)
|
||||||
|
(if (null? raw-forms) (filter (fn (not (null? x)))
|
||||||
|
(reverse acc))
|
||||||
|
(process-raw-forms (cdr raw-forms)
|
||||||
|
(process-form (car raw-forms) acc))))
|
||||||
|
|
||||||
|
(define (read-forms acc)
|
||||||
(let ((r (read)))
|
(let ((r (read)))
|
||||||
(unless (eof-object? r)
|
(if (eof-object? r) (reverse acc)
|
||||||
(cond ((eqv? (car r) 'define) (eval r))
|
(read-forms (cons r acc)))))
|
||||||
(#t
|
|
||||||
(fmt #t
|
|
||||||
(c-expr
|
|
||||||
(tree-map
|
|
||||||
(fn
|
|
||||||
(case x
|
|
||||||
((fn) '%fun)
|
|
||||||
((var) '%var)
|
|
||||||
((begin) '%begin)
|
|
||||||
((pointer) '%pointer)
|
|
||||||
((array) '%array)
|
|
||||||
(([]) 'vector-ref)
|
|
||||||
((include) '%include)
|
|
||||||
((cast) '%cast)
|
|
||||||
(else
|
|
||||||
(if (symbol? x)
|
|
||||||
(unkebabify x)
|
|
||||||
x))))
|
|
||||||
r)))
|
|
||||||
(fmt #t "\n")))
|
|
||||||
(loop))))
|
|
||||||
|
|
||||||
(loop)
|
(define (emit-c forms)
|
||||||
|
(for-each (lambda (form)
|
||||||
|
(fmt #t (c-expr form))
|
||||||
|
(fmt #t "\n"))
|
||||||
|
forms))
|
||||||
|
|
||||||
|
;;; Main function facilities
|
||||||
|
|
||||||
|
(define opts-grammar
|
||||||
|
`((output "Write output to file"
|
||||||
|
(required #f)
|
||||||
|
(value #t)
|
||||||
|
(single-char #\o))
|
||||||
|
(help "Show this help"
|
||||||
|
(required #f)
|
||||||
|
(value #f)
|
||||||
|
(single-char #\h))
|
||||||
|
(macro-expand "Emit macro-expanded Sex code instead of C"
|
||||||
|
(required #f)
|
||||||
|
(value #f)
|
||||||
|
(single-char #\m))))
|
||||||
|
|
||||||
|
(define (print-help)
|
||||||
|
(fmt #t "Usage: sexc [OPTIONS] [FILE]\n")
|
||||||
|
(fmt #t "Options: -o, --output <file> Write output to file. If omitted, write to stdout\n")
|
||||||
|
(fmt #t " -h, --help Show this help\n")
|
||||||
|
(fmt #t " -m, --macro-expand Emit macro-expanded Sex code instead of C\n"))
|
||||||
|
|
||||||
|
(define (help-arg? args)
|
||||||
|
(assoc 'help args))
|
||||||
|
|
||||||
|
(define (get-arg args arg-name default)
|
||||||
|
(let ((arg (assoc arg-name args)))
|
||||||
|
(if arg (cdr arg)
|
||||||
|
default)))
|
||||||
|
|
||||||
|
(define (get-input-file args)
|
||||||
|
(let ((rest-args (assoc '@ args)))
|
||||||
|
(if (= 1 (length rest-args))
|
||||||
|
'stdin
|
||||||
|
(cadr rest-args))))
|
||||||
|
|
||||||
|
(define (read-from-file file)
|
||||||
|
(with-input-from-file (pathname-strip-directory file)
|
||||||
|
(fn (read-forms (list)))))
|
||||||
|
|
||||||
|
(define (set-working-directory file)
|
||||||
|
(change-directory
|
||||||
|
(normalize-pathname
|
||||||
|
(make-absolute-pathname
|
||||||
|
(current-directory)
|
||||||
|
(pathname-directory file)))))
|
||||||
|
|
||||||
|
(define (main)
|
||||||
|
(let* ((raw-args (command-line-arguments))
|
||||||
|
(current-dir (current-directory))
|
||||||
|
(args (getopt-long raw-args
|
||||||
|
opts-grammar))
|
||||||
|
(output (get-arg args 'output 'stdout))
|
||||||
|
(help (help-arg? args))
|
||||||
|
(input (get-input-file args)))
|
||||||
|
(if help (print-help)
|
||||||
|
(let* ((raw-forms
|
||||||
|
(if (eq? input 'stdin)
|
||||||
|
(read-forms (list))
|
||||||
|
(begin
|
||||||
|
(set-working-directory input)
|
||||||
|
(read-from-file input))))
|
||||||
|
(sex-forms (process-raw-forms raw-forms (list))))
|
||||||
|
(change-directory current-dir)
|
||||||
|
(if (get-arg args 'macro-expand #f)
|
||||||
|
(map pp sex-forms)
|
||||||
|
(if (not (eq? output 'stdout))
|
||||||
|
(with-output-to-file output
|
||||||
|
(lambda ()
|
||||||
|
(emit-c sex-forms)))
|
||||||
|
(emit-c sex-forms)))))))
|
||||||
|
|||||||
56
templates.scm
Normal file
56
templates.scm
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
(declare (unit templates))
|
||||||
|
|
||||||
|
(import brev-separate
|
||||||
|
fmt
|
||||||
|
regex
|
||||||
|
srfi-1 ; list routines
|
||||||
|
tree)
|
||||||
|
|
||||||
|
|
||||||
|
(define-syntax template
|
||||||
|
(syntax-rules ()
|
||||||
|
((_ (name subst-list args ...) body ...)
|
||||||
|
(define (name subst-list-arg args ...)
|
||||||
|
(let* ((subst-alist (map cons 'subst-list subst-list-arg))
|
||||||
|
(replaced-body (apply-substitution `(body ...) subst-alist)))
|
||||||
|
replaced-body)))))
|
||||||
|
|
||||||
|
(define (apply-symbol-substitution sym subst-alist)
|
||||||
|
;; All non-symbol substitutions will be filtered.
|
||||||
|
;; E.g. if the subst-alist is ((T + 1 2) (U . w) (W . e)),
|
||||||
|
;; only ((U . w) (W . e)) will be applied to symbols.
|
||||||
|
(let ((str (symbol->string sym))
|
||||||
|
(subst-map (map (fn
|
||||||
|
(if (symbol? (car x))
|
||||||
|
(cons
|
||||||
|
(fmt #f "([^\\-]?)" (symbol->string (car x)) "([\\-$]?)")
|
||||||
|
(fmt #f "\\1" (symbol->string (cdr x)) "\\2"))
|
||||||
|
x))
|
||||||
|
(filter (fn (not (list? (cdr x)))) subst-alist))))
|
||||||
|
(string->symbol
|
||||||
|
(string-substitute* str subst-map))))
|
||||||
|
|
||||||
|
(define (maybe-replace-symbol sym subst-alist)
|
||||||
|
(call/cc
|
||||||
|
(lambda (return)
|
||||||
|
(for-each (fn (when (eq? (car x) sym)
|
||||||
|
(return (cdr x))))
|
||||||
|
subst-alist)
|
||||||
|
(return sym))))
|
||||||
|
|
||||||
|
(define (apply-substitution target subst-alist)
|
||||||
|
;; Subsitute free symbols and -/$/^ separated parts
|
||||||
|
;; of symbols with provided forms.
|
||||||
|
;; E.g. with substitution (T int):
|
||||||
|
;; list-T -> list-int ; by apply-symbol-substitution
|
||||||
|
;; (var T data) -> (var int data) ; by maybe-replace-symbol
|
||||||
|
;; see respective functions for further details.
|
||||||
|
(tree-map
|
||||||
|
(fn
|
||||||
|
(if (symbol? x)
|
||||||
|
(let ((st (maybe-replace-symbol x subst-alist)))
|
||||||
|
(if (eq? st x)
|
||||||
|
(apply-symbol-substitution x subst-alist)
|
||||||
|
st))
|
||||||
|
x))
|
||||||
|
target))
|
||||||
Reference in New Issue
Block a user