10 Commits

Author SHA1 Message Date
18b9e20fee add support for nested lambdas 2025-10-01 12:37:59 +03:00
e888ed1281 add initial lambda support
No closures for now, but solid groundwork is laid.
2025-09-26 16:53:38 +03:00
21293c421f enable prefix form for keywords
I like writing :keyword more than #:keyword. That hash sign seems
redundant
2025-09-26 16:52:34 +03:00
d5bbe853aa don't force c89 after all 2025-09-26 14:51:44 +03:00
a7cd720057 update Readme.org 2025-09-26 14:51:44 +03:00
b314dfd58e re-implement macro-expansion using new semantic walker 2025-09-26 14:33:47 +03:00
d0005a6622 implement semantic code walking framework 2025-09-26 14:33:23 +03:00
13984ccc78 add binaries to .gitignore 2025-09-26 11:53:10 +03:00
d8d6f4f5a4 add .gitignore 2025-09-24 20:14:21 +03:00
42d58d3359 split semantic processing and fmt-c code generation
Introducing Sex SEMantic ENgine: the semen.
Also split reader to other file (it can be replaced in the future).
Macro expansion inside Sex code doesn't work yet, and it must be done
in semen, not during fmt-c generation as before.
2025-09-24 20:12:08 +03:00
22 changed files with 211 additions and 790 deletions

View File

@@ -1,55 +0,0 @@
name: Sex CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install chicken
run: |
wget -N https://code.call-cc.org/releases/5.4.0/chicken-5.4.0.tar.gz
tar zxf chicken-5.4.0.tar.gz
sudo apt install -y make
make -C chicken-5.4.0 PLATFORM=linux
sudo make -C chicken-5.4.0 PLATFORM=linux install
- name: Install dependencies
# FIXME: [project-local deps]: use venv or something
# run: make deps
run: sudo chicken-install $(cat dependencies.txt)
- name: Make sure that sexc builds
# FIXME: sexc being built with local .eggs/ libs needs the path at runtime.
# Without it there will be `Error: cannot load extension: fmt`.
run: make sexc && ./sexc --help
- name: Run tests
# FIXME: [project-local deps]: use local deps or build with -static
# run: make run-tests
run: make sex-tests && ./sex-tests
build-macos:
runs-on: macos-15
steps:
- uses: actions/checkout@v3
- name: Install chicken
run: brew install chicken make
- name: Install dependencies
# FIXME: [project-local deps]: use venv or something
# run: make deps
run: chicken-install $(cat dependencies.txt)
- name: Make sure that sexc builds
# FIXME: sexc being built with local .eggs/ libs needs the path at runtime.
# Without it there will be `Error: cannot load extension: fmt`.
run: make sexc && ./sexc --help
- name: Run tests
# FIXME: [project-local deps]: use local deps or build with -static
# run: make run-tests
run: make sex-tests && ./sex-tests

View File

@@ -1,5 +1,5 @@
CHICKEN_C = csc CHICKEN_C = csc
CSC_FLAGS += -K prefix CSC_FLAGS = -K prefix
MODULES = sexc fmt-c fmt-c-writer semen sex-macros sex-modules sex-reader utils MODULES = sexc fmt-c fmt-c-writer semen sex-macros sex-modules sex-reader utils
OBJ = $(MODULES:%=%.o) OBJ = $(MODULES:%=%.o)

View File

@@ -1,9 +1,4 @@
* The Sex language * The Sex language
#+NAME: the Sex logo
#+ATTR_HTML: :width 300px
[[sex.png][file:./sex.png]]
Sex is a S-expressions language. Sex is written in Chicken, which is a Sex is a S-expressions language. Sex is written in Chicken, which is a
[[https://call-cc.org][R5RS Scheme]]. [[https://call-cc.org][R5RS Scheme]].
Sex is statically typed, compiled general purpose language. Sex is statically typed, compiled general purpose language.
@@ -18,7 +13,7 @@ to export ~CHICKEN_INSTALL_REPOSITORY~ and ~CHICKEN_REPOSITORY_PATH~
environment variables. Refer to the documentation for more info: environment variables. Refer to the documentation for more info:
https://wiki.call-cc.org/man/5/Extension%20tools#changing-the-repository-location https://wiki.call-cc.org/man/5/Extension%20tools#changing-the-repository-location
~chicken-install `cat dependencies.txt`~ ~chicken-install fmt getopt-long brev-separate test tree srfi-1 srfi-13 srfi-69 matchable~
** Compilation ** Compilation
~make~ ~make~
@@ -54,13 +49,13 @@ An example of Sex source:
#+begin_src scheme #+begin_src scheme
(include stdio.h) (include stdio.h)
(pub fn main ((argc int) (argv [* const char])) int (pub fn int main ((int argc) (char **argv))
(puts "Hello from Sex!") (puts "Hello from Sex!")
(var name [char 512]) (var (array char 512) name)
(puts "What is your name?") (puts "What is your name?")
(scanf "%s" (cast (& name) (* char))) (scanf "%s" (cast char* &name))
(printf "Hello, %s!\n" name) (printf "Hello, %s!\n" name)
(return 0)) 0)
#+end_src #+end_src
Compile and run: Compile and run:
@@ -106,16 +101,16 @@ return Sex code.
(pub defmacro (list-T type) (pub defmacro (list-T type)
(let ((list-type (cat 'list- type))) (let ((list-type (cat 'list- type)))
`(struct ,list-type `(struct ,list-type
((value ,type) ((,type value)
(next (* ,list-type)))))) ((* ,list-type) next)))))
(list-T int) (list-T int)
#+end_src #+end_src
-> ->
#+begin_src scheme #+begin_src scheme
(struct list_int (struct list_int
((value int) ((int value)
(next (* list_int)))) ((* list_int) next)))
#+end_src #+end_src
**** Wrapper for checking return codes **** Wrapper for checking return codes
@@ -124,19 +119,20 @@ return Sex code.
`(if (< 0 ,call) `(if (< 0 ,call)
(begin (begin
(puts ,message) (puts ,message)
(return ,ret-code)))) (return ,ret-code)))))
(pub fn init () int (pub fn int init ()
(check-sdl-return (check-sdl-return
(SDL-Init SDL-INIT-VIDEO) "Failed to initialize SDL" 1) (SDL-Init SDL-INIT-VIDEO) "Failed to initialize SDL" 1)
...) ...)
#+end_src #+end_src
-> ->
#+begin_src scheme #+begin_src c
(pub fn init () int (%fun int init ()
(if (< 0 (SDL_Init SDL_INIT_VIDEO)) (if (< 0 (SDL_Init SDL_INIT_VIDEO))
(begin (puts "Failed to initialize SDL") (return 1))) (%begin (puts "Failed to initialize SDL") (return 1)))
...) ...)
}
#+end_src #+end_src
** Use an established environment for development ** Use an established environment for development

View File

@@ -1 +0,0 @@
fmt getopt-long brev-separate test tree srfi-1 srfi-13 srfi-69 matchable

View File

@@ -1,10 +1,10 @@
;;; Prototypes ;;; Prototypes
(fn puk () void) (fn void puk ())
(pub fn plak () void) (pub fn void plak ())
;;; Functions ;;; Functions
(fn foo () int (return 1)) (fn int foo () (return 1))
(pub fn bar ((a int) (b int)) void (pub fn void bar ((int a) (int b))
(printf "%d\n" (+ a b))) (printf "%d\n" (+ a b)))

View File

@@ -1,9 +1,9 @@
(include stdio.h) (include stdio.h)
(pub fn main ((argc int) (argv [* const char])) int (pub fn int main ((int argc) (char **argv))
(puts "Hello from Sex!") (puts "Hello from Sex!")
(var name [char 512]) (var (array char 512) name)
(puts "What is your name?") (puts "What is your name?")
(scanf "%s" (cast (& name) (* char))) (scanf "%s" (cast char* &name))
(printf "Hello, %s!\n" name) (printf "Hello, %s!\n" name)
(return 0)) (return 0))

View File

@@ -1,21 +1,21 @@
(include stdio.h) (include stdio.h)
(fn sum ((a int) (b int)) int (fn int sum ((int a) (int b))
(return (+ a b))) (return (+ a b)))
(pub fn main () int (pub fn int main ()
(var a int 10) (var int a 10)
(var b int 20) (var int b 20)
(var (fn ((int) (int)) int) sum-fn sum) (var (fn int ((int) (int))) sum-fn sum)
(var (fn ((int) (int)) int) sum-lambda (var (fn int ((int) (int))) sum-lambda
(lambda ((a int) (b int)) int () (lambda int ((int a) (int b)) ()
(return (+ a b)))) (return (+ a b))))
(var (fn ((int)) int) sum-lambda-2 (var (fn int ((int))) sum-lambda-2
(lambda ((a int)) int () (lambda int ((int a)) ()
(return (+ a 20)))) (return (+ a 20))))
(printf "Hello from main fn!\n") (printf "Hello from main fn!\n")
@@ -24,28 +24,15 @@
(printf "Calling fn ptr: %d\n" (sum-fn a b)) (printf "Calling fn ptr: %d\n" (sum-fn a b))
(printf "Calling lambda: %d\n" (sum-lambda a b)) (printf "Calling lambda: %d\n" (sum-lambda a b))
(printf "Calling other lambda: %d\n" (sum-lambda-2 a)) (printf "Calling other lambda: %d\n" (sum-lambda-2 a))
(printf "Calling lambda inplace: %d\n" ((lambda ((a int) (b int)) int () (printf "Calling lambda inplace: %d\n" ((lambda int ((int a) (int b)) ()
(return (+ a b 100))) (return (+ a b 100)))
a b)) a b))
(var (fn ((int)) int) l-1 (var (fn int ((int))) l-1
(lambda ((a int)) int () (lambda int ((int a)) ()
(var (fn ((int)) int) l-2 (var (fn int ((int))) l-2
(lambda ((a int)) int () (lambda int ((int a)) ()
(return (+ 60 a)))) (return (+ 60 a))))
(return (+ 600 (l-2 a))))) (return (+ 600 (l-2 a)))))
(printf "Calling nested lambdas: %d\n" (l-1 6)) (printf "Calling nested lambdas: %d\n" (l-1 6))
;; Not supported yet
;; Closure
;; (var (fn (fn ((int)) int) ((int))) make-adder
;; (lambda (fn int ((int a))) ()
;; (return (lambda int ((int b)) (a)
;; (return (+ a b))))))
;;
;; (var (fn int ((int))) add-10
;; (make-adder 10))
;; (var (fn int ((int))) add-20
;; (make-adder 20))
;; (printf "Calling closures: %d\n" (add-10 24))
(return 0)) (return 0))

View File

@@ -1,21 +1,21 @@
(pub defmacro (list-T type) (pub defmacro (list-T type)
(let ((list-type (cat 'list- type))) (let ((list-type (cat 'list- type)))
`(struct ,list-type `(struct ,list-type
((value ,type) ((,type value)
(next (* struct ,list-type)))))) ((* (struct ,list-type)) next)))))
(pub defmacro (make-list-T type is-public?) (pub defmacro (make-list-T type is-public?)
(let ((list-type (list 'struct (cat 'list- type))) (let ((list-type (list 'struct (cat 'list- type)))
(fn-name (cat 'make-list- type))) (fn-name (cat 'make-list- type)))
`(,@(if is-public? '(pub) '()) fn ,fn-name () (* ,list-type) `(,@(if is-public? '(pub) '()) fn (* ,list-type) ,fn-name ()
(var list (* ,list-type) (cast (malloc (sizeof ,list-type)) (* ,list-type))) (var (* ,list-type) list (cast (* ,list-type) (malloc (sizeof ,list-type))))
(= (-> list next) NULL) (= (-> list next) NULL)
(return list)))) (return list))))
(pub defmacro (add-value-list-T type is-public?) (pub defmacro (add-value-list-T type is-public?)
(let ((list-type (list 'struct (cat 'list- type))) (let ((list-type (list 'struct (cat 'list- type)))
(fn-name (cat 'add-value-list- type))) (fn-name (cat 'add-value-list- type)))
`(,@(if is-public? '(pub) '()) fn ,fn-name ((list * ,list-type) (value ,type)) void `(,@(if is-public? '(pub) '()) fn void ,fn-name ((,list-type *list) (,type value))
(while (!= (-> list next) NULL) (while (!= (-> list next) NULL)
(= list (-> list next))) (= list (-> list next)))
(= (-> list next) (,(cat 'make-list- type))) (= (-> list next) (,(cat 'make-list- type)))
@@ -24,24 +24,23 @@
(pub defmacro (length-list-T type is-public?) (pub defmacro (length-list-T type is-public?)
(let ((fn-name (cat 'length-list- type)) (let ((fn-name (cat 'length-list- type))
(list-type (list 'struct (cat 'list- type)))) (list-type (list 'struct (cat 'list- type))))
`(,@(if is-public? '(pub) '()) fn ,fn-name ((list ,(cons '* list-type))) size-t `(,@(if is-public? '(pub) '()) fn size-t ,fn-name ((,list-type *list))
(var n size-t 0) (var size-t n 0)
(while (!= (-> list next) NULL) (while (!= (-> list next) NULL)
(= list (-> list next)) (= list (-> list next))
(++ n)) (++ n))
(return n)))) (return n))))
(pub defmacro (is-empty-list-T type is-public?) (pub defmacro (is-empty-list-T type is-public?)
`(,@(if is-public? '(pub) '()) fn ,(cat 'is-empty-list- type) `(,@(if is-public? '(pub) '()) fn bool ,(cat 'is-empty-list- type)
((list ,(list '* 'struct (cat 'list- type)))) ((,(list 'struct (cat 'list- type)) *list))
bool
(return (== (-> list next) NULL)))) (return (== (-> list next) NULL))))
(pub defmacro (list-for-each list-type list-var elt-type elt-var what-do) (pub defmacro (list-for-each list-type list-var elt-type elt-var what-do)
(let ((list-var-2 (cat list-var '-2))) (let ((list-var-2 (cat list-var '-2)))
`(begin `(begin
(var ,list-var-2 (* ,list-type) ,list-var) (var (pointer ,list-type) ,list-var-2 ,list-var)
(var ,elt-var ,elt-type (-> ,list-var-2 value)) (var ,elt-type ,elt-var (-> ,list-var-2 value))
(while (!= (-> ,list-var-2 next) NULL) (while (!= (-> ,list-var-2 next) NULL)
,what-do ,what-do
(= ,list-var-2 (-> ,list-var-2 next)) (= ,list-var-2 (-> ,list-var-2 next))

View File

@@ -5,12 +5,12 @@
(import list) (import list)
(struct foo (struct foo
((a-field float) ((float a-field)
(b int) (int b)
(c (* const char)) ((const char *) c)
(not (fn ((val bool)) bool)))) ((fn bool ((bool val))) not)))
(var f (struct foo)) (var (struct foo) f)
(list-T int) (list-T int)
(make-list-T int #f) (make-list-T int #f)
@@ -18,16 +18,15 @@
(length-list-T int #f) (length-list-T int #f)
(is-empty-list-T int #f) (is-empty-list-T int #f)
(extern fn puk ((a int) (b float)) void) (extern fn void puk ((int a) (float b)))
(pub fn baz () bool (pub fn bool baz () (return true))
(return true))
(extern var i int) (extern var int i)
(var j int) (var int j)
(pub var k int) (pub var int k)
(pub fn main () int (pub fn int main ()
(var l (* struct list-int) (make-list-int)) (var (struct list-int) *l (make-list-int))
(printf "Size of the list: %lu\n" (length-list-int l)) (printf "Size of the list: %lu\n" (length-list-int l))
(add-value-list-int l 3) (add-value-list-int l 3)
(add-value-list-int l 4) (add-value-list-int l 4)
@@ -36,9 +35,9 @@
(printf "%d " v)) (printf "%d " v))
(printf "\n") (printf "\n")
(printf "Size of the list: %lu\n" (length-list-int l)) (printf "Size of the list: %lu\n" (length-list-int l))
(printf "%p\n" (cast l->next (* void))) (printf "%p\n" (cast void* l->next))
(return 0)) (return 0))
(pub fn print-list ((l (* const struct list-int))) void (pub fn void print-list (((const struct list-int) *l))
(list-for-each (const struct list-int) l int v (printf "%d " v)) (list-for-each (const struct list-int) l int v (printf "%d " v))
(printf "\n")) (printf "\n"))

View File

@@ -2,19 +2,15 @@
(declare (unit fmt-c-writer) (declare (unit fmt-c-writer)
(uses fmt-c (uses fmt-c
semen semen))
utils))
(import (chicken string) (import (chicken string)
(chicken syntax)
brev-separate brev-separate
fmt fmt
matchable
regex regex
srfi-1 ; lists srfi-1 ; lists
srfi-13 ; strings srfi-13 ; strings
srfi-39 ; parameters )
tree)
(define (unkebabify sym) (define (unkebabify sym)
(case sym (case sym
@@ -31,13 +27,15 @@
(case atom (case atom
((fn) '%fun) ((fn) '%fun)
((prototype) '%prototype) ((prototype) '%prototype)
((var) '%var)
((begin) '%block-begin) ((begin) '%block-begin)
((define) '%define) ((define) '%define)
((pointer) '%pointer) ((pointer) '%pointer)
((array) '%array) ((array) '%array)
((attribute) '%attribute) ((attribute) '%attribute)
((¤) 'vector-ref) ((@) 'vector-ref)
((include) '%include) ((include) '%include)
((cast) '%cast)
;; uh things we do for c89 compatibility ;; uh things we do for c89 compatibility
((bool) 'int) ((bool) 'int)
((true) 1) ((true) 1)
@@ -47,12 +45,6 @@
(unkebabify atom) (unkebabify atom)
atom)))) atom))))
(define (maybe-unwrap-type type)
(if (and (list? type)
(= 1 (length type)))
(car type)
type))
(define (make-field-access form) (define (make-field-access form)
(assert (assert
(= 2 (length form)) "Wrong field access format") (= 2 (length form)) "Wrong field access format")
@@ -60,224 +52,77 @@
(string->symbol (string->symbol
(fmt #f (cadr form) (car form))))) (fmt #f (cadr form) (car form)))))
(define (walk-generic-toplevel form) (define (walk-generic form acc)
(cond ((atom? form) (atom-to-fmt-c form)) (cond
((list? form) (map walk-generic-toplevel form)) ((null? form) (cons '() acc))
(else (error "Malformed form " form))))
(define (field-access-form? form) ;; vector, e.g. {}-initializer
(and (symbol? (car form)) ((vector? form)
(char=? #\. (string-ref (symbol->string (car form)) 0)))) (cons
(define (walk-expr form)
(match form
((? vector?)
(list->vector (list->vector
(walk-expr (vector->list form)))) (car (walk-generic (vector->list form) (list))))
((? atom?) acc))
(atom-to-fmt-c form))
((? field-access-form?)
(make-field-access form))
(('var . _) (walk-var form))
(('cast expr type) (list '%cast
(walk-type type)
(walk-expr expr)))
(('enum . _) (walk-enum form))
;; | is problematic... And c-or/bit-or/etc are actually
;; procedures, so we have to call the procedure itself
(('c-or . rest) (apply c-or (map walk-expr rest)))
(('c-bit-or . rest) (apply c-bit-or (map walk-expr rest)))
(('c-bit-or= . rest) (apply c-bit-or= (map walk-expr rest)))
(else (map walk-expr form))))
(define (walk-var form) ;; atom (hopefully)
;; (var a int) -> (%var int a) ((not (list? form)) (cons (atom-to-fmt-c form) acc))
;; (var a (const int) 32) -> (%var (const int) a 32)
;; (var b [const char 512]) -> (%var (%array (const char) 512) b)
;; note: [...] is actually (¤ ...) after reading
;; (var c (fn ((int) (float)) void)) -> (%var (%fun void ((int) (float))) c)
`(%var
,(walk-type (third form))
,(atom-to-fmt-c (second form))
.
,(if (null? (drop form 3))
(list)
(walk-expr (drop form 3))) ; optional init expression
))
(define (walk-type form) ;; another special case - field access
;; int -> int ((and (symbol? (car form))
;; (const int) -> const int (char=? #\. (string-ref (symbol->string (car form)) 0)))
;; [const char 512] ->(¤ (const char) 512) -> (%array (const char) 512) (cons (make-field-access form) acc))
;; [float 8] -> (%array float 8)
;; (* const char) -> (const char *)
;; (const * const * const char) -> (const char * const * const)
;; (fn ((int) (float)) void) -> (%fun void ((int) (float)))
(match form
(('¤ . array-type)
(if (integer? (last array-type))
;; sized array
(let* ((type-list (drop-right array-type 1))
(type (maybe-unwrap-type type-list))
(size (last array-type)))
`(%array ,(walk-type type)
,size))
;; sugar for pointer... Do we really need it? Guess why not,
;; it's a strong semantic cue
`(%array ,(walk-type (maybe-unwrap-type array-type)))))
(('fn arglist ret-type)
`(%fun ,(walk-type ret-type) ,(walk-arglist arglist)))
(('fn . _)
(assert #f "Malformed function type form"))
;; Special case: nested structs/unions ;; toplevel, or a start of a regular list form
((or ('struct . _)
('union . _)) (walk-struct form))
(('enum . _) (walk-enum form))
(else (else
(type-convert-to-c form)))) (let ((new-acc (list)))
(cons (fold-right
walk-generic
new-acc
form)
acc)))))
(define (type-convert-to-c type) (define (normalize-fn-form form)
;; Our pointers to C pointers
;; int -> int
;; * const char -> const char *
;; const * const char -> const char * const
(if (atom? type) (atom-to-fmt-c type)
(flatten
(tree-map atom-to-fmt-c
(flatten
(list-join (reverse (list-split type '*))
'(*)))))))
(define (walk-fn-def form)
(match form
(('fn name args ret-type . maybe-body)
`(%fun
,(walk-type ret-type)
,(atom-to-fmt-c name)
,(walk-arglist args)
.
,(walk-expr maybe-body)))))
;;; TODO: isn't there a better way?
(define (is-probably-type form)
(case (car form)
((¤ * const volatile struct union) #t)
(else #f)))
(define (walk-arglist form)
;; E.g.:
;; ((float) (int) (const char) (* const char) (¤ (* const struct res) 32))
;; ((f1 float) (f2 float) (f3 float) (res (¤ float 4)))
(map (fn
(match x
(('¤ . _) (walk-type x))
;; yeah shitty, but I don't know yet how to determine if the
;; first entry is part of the type and not an argument name
;; :(
((? is-probably-type) (walk-type x))
;; 1 element args are always type
((_) (walk-type x))
((var . type) (append (list (walk-type (maybe-unwrap-type type)))
(list (walk-type var))))))
form))
(define (walk-function form)
;; (fn ret-type name arglist body) -> normal function ;; (fn ret-type name arglist body) -> normal function
;; (fn ret-type name arglist) -> prototype ;; (fn ret-type name arglist) -> prototype
(if (>= (length form) 5) (if (>= (length form) 5)
(walk-fn-def form) form
(cons '%prototype (cdr (walk-fn-def form))))) (cons 'prototype (cdr form))))
(define (process-struct-fields fields) (define (walk-function form static)
(map (fn (if static
(let ((type (walk-type (last x)))) (walk-generic (list 'static (normalize-fn-form form))
(cons type (map atom-to-fmt-c (drop-right x 1))))) (list))
fields)) (walk-generic (normalize-fn-form (cdr form))
(list))))
(define (walk-struct form)
(match form
((type (fields ...) . attrs) ; anonymous struct
`(,type ,(process-struct-fields fields)
. ,(tree-map atom-to-fmt-c attrs)))
((type name) ; simple 'struct whatever', like in variable def
`(,type ,(atom-to-fmt-c name)))
((type name (fields ...) . attrs)
`(,type ,(atom-to-fmt-c name)
,(process-struct-fields fields)
. ,(tree-map atom-to-fmt-c attrs)))
(else (error "Malformed aggregate definition " form))))
(define (walk-enum form)
(match form
(('enum (values ...))
`(enum ,(map atom-to-fmt-c values)))
(('enum name (values ...))
`(enum ,(atom-to-fmt-c name) ,(map atom-to-fmt-c values)))))
(define (walk-extern form) (define (walk-extern form)
(match form (case (cadr form)
(('fn . _) ((fn)
;; extern function?.. What (list (cons 'extern (walk-function form #f))))
(list 'extern (walk-function form))) ((var)
(('var . _) (list (cons 'extern (walk-generic (cdr form) (list)))))
(list 'extern (walk-var form)))
(else (error "Extern what?")))) (else (error "Extern what?"))))
(define (walk-public form) (define (walk-public form)
(match form (case (cadr form)
(('fn . _) ((fn)
(walk-function form)) (walk-function form #f))
(('var . _) ((var)
(walk-var form)) (walk-generic (list 'static (cdr form)) (list)))
((or ('define . _) ((define defmacro import include struct typedef union var)
('defmacro . _)
('import . _)
('include . _)
('struct . _)
('union . _)
('typedef . _))
;; ignore here, used in generating public interface ;; ignore here, used in generating public interface
(process-toplevel-form form)) (process-toplevel-form (cdr form)))
(else (else
(error "Pub what?" (cadr form))))) (error "Pub what?" (cadr form)))))
(define (process-toplevel-form form) (define (process-toplevel-form form)
(match form ;; todo: rewrite to match
(('fn . _) (list 'static (walk-function form))) (case (car form)
(('var . _) (list 'static (walk-var form))) ((fn) (walk-function form #t))
(('extern . rest) (walk-extern rest)) ((extern) (walk-extern form))
(('pub . rest) (walk-public rest)) ((pub) (walk-public form))
((or ('struct . _) (else (walk-generic form (list)))))
('union . _)) (walk-struct form))
(('enum . _) (walk-enum form))
(('define name . rest) `(%define ,(atom-to-fmt-c name) ,@rest))
(else (walk-expr form))))
(define (get-line-num form)
(let ((num (get-line-number form)))
(if (string? num)
(last (string-split num ":"))
#f)))
(define sex-fmt-current-file (make-parameter "/dev/null"))
(define sex-fmt-line-num (make-parameter 0))
(define (line-directive-string)
(fmt #f #\# "line " (sex-fmt-line-num) " " #\" (sex-fmt-current-file) #\"))
(define (emit-c sex-forms) (define (emit-c sex-forms)
(for-each (lambda (form) (for-each (lambda (form)
(let ((start-line (get-line-num form))) (fmt #t (c-expr (car (process-toplevel-form form))) nl))
(when start-line
(sex-fmt-line-num start-line)
(fmt #t (line-directive-string) nl)))
(fmt #t (c-expr (process-toplevel-form form)) nl))
sex-forms)) sex-forms))

View File

@@ -9,7 +9,6 @@
(declare (unit fmt-c)) (declare (unit fmt-c))
(import fmt (import fmt
srfi-1
srfi-13) srfi-13)
(define (fmt-in-macro? st) (fmt-ref st 'in-macro?)) (define (fmt-in-macro? st) (fmt-ref st 'in-macro?))
@@ -85,8 +84,7 @@
(define (c-maybe-paren op x) (define (c-maybe-paren op x)
(lambda (st) (lambda (st)
((fmt-let 'op op ((fmt-let 'op op
(if (and (c-op<= (fmt-op st) op) (if (c-op<= (fmt-op st) op)
(not (vector? st)))
(c-paren x) (c-paren x)
x)) x))
st))) st)))
@@ -559,13 +557,10 @@
;; data structures ;; data structures
(define (c-struct/aux type x . o) (define (c-struct/aux type x . o)
;; can be just pointer to SUC, need to support such case:
;; struct whatever * - body is just '*'
(let* ((name (if (null? o) (if (or (symbol? x) (string? x)) x #f) x)) (let* ((name (if (null? o) (if (or (symbol? x) (string? x)) x #f) x))
(body (if name (if (not (null? o)) (car o) '()) x)) (body (if name (if (not (null? o)) (car o) '()) x))
(o (if (null? o) o (cdr o)))) (o (if (null? o) o (cdr o))))
(if (and (not (null? body)) (if (not (null? body))
(not (eq? '* body)))
(c-wrap-stmt (c-wrap-stmt
(cat (cat
(c-braced-block (c-braced-block
@@ -577,13 +572,7 @@
(c-wrap-stmt (c-expr body)))))) (c-wrap-stmt (c-expr body))))))
(if (pair? o) (cat " " (apply c-begin o)) (dsp "")))) (if (pair? o) (cat " " (apply c-begin o)) (dsp ""))))
(c-wrap-stmt (c-wrap-stmt
(cat type (cat type (if (and name (not (equal? name ""))) (cat " " name) ""))))))
(if (and name (not (equal? name "")))
(cat " " name)
"")
(if (not (null? body))
(cat body)
""))))))
(define (c-struct . args) (apply c-struct/aux "struct" args)) (define (c-struct . args) (apply c-struct/aux "struct" args))
(define (c-union . args) (apply c-struct/aux "union" args)) (define (c-union . args) (apply c-struct/aux "union" args))
@@ -608,11 +597,9 @@
(define (c-while check . body) (define (c-while check . body)
(c-reset-newline (c-reset-newline
(if (null? body)
(cat "while (" (c-in-test (c-expr check)) ");")
(cat (c-block (cat "while (" (c-in-test (c-expr check)) ")") (cat (c-block (cat "while (" (c-in-test (c-expr check)) ")")
(c-in-stmt (apply c-begin body))) (c-in-stmt (apply c-begin body)))
fl)))) fl)))
(define (c-for init check update . body) (define (c-for init check update . body)
(c-reset-newline (c-reset-newline
@@ -627,7 +614,7 @@
(define (c-param x) (define (c-param x)
(cond (cond
((procedure? x) x) ((procedure? x) x)
((pair? x) (c-param-type (car x) (cadr x))) ((pair? x) (c-type (car x) (cadr x)))
(else (error "missing type" x)))) (else (error "missing type" x))))
(define (c-field x) (define (c-field x)
@@ -701,33 +688,6 @@
(else (else
(cat (if (eq? '%pointer type) '* type) (if name (cat " " name) "")))))) (cat (if (eq? '%pointer type) '* type) (if name (cat " " name) ""))))))
(define (c-param-type type . o)
(let ((name (and (pair? o) (car o))))
(cond
((pair? type)
(case (car type)
((%fun)
(cat (c-type (cadr type) #f)
" (*" (or name "") ")("
(fmt-join (lambda (x) (c-type x #f)) (caddr type) ", ") ")"))
((%array)
(let ((name (cat name "[" (if (pair? (cddr type))
(c-expr (caddr type))
"")
"]")))
(c-type (cadr type) name)))
((%pointer *)
(let ((name (cat "*" (if name (c-expr name) ""))))
(c-type (cadr type)
(if (and (pair? (cadr type)) (eq? '%array (caadr type)))
(c-paren name)
name))))
(else (fmt-join/last c-expr (lambda (x) (c-type x name)) type " "))))
((not type)
(lambda (st) ((c-type (or (fmt-default-type st) 'int) name) st)))
(else
(cat (if (eq? '%pointer type) '* type) (if name (cat " " name) ""))))))
(define (c-var type name . init) (define (c-var type name . init)
(c-wrap-stmt (c-wrap-stmt
(if (pair? init) (if (pair? init)

View File

@@ -2,8 +2,7 @@
(declare (unit semen) (declare (unit semen)
(uses sex-macros (uses sex-macros
sex-modules sex-modules))
utils))
(import (import
(chicken string) (chicken string)
@@ -54,16 +53,13 @@
('pub 'fn . _) ('pub 'fn . _)
('extern 'fn . _)) (process-fn sex-form acc)) ('extern 'fn . _)) (process-fn sex-form acc))
((or ('struct . _) ((or ('struct . _)
('pub 'struct . _)) (process-struct sex-form acc)) ('pub struct . _)) (process-struct sex-form acc))
((or ('union . _) ((or ('union . _)
('pub 'union . _)) (process-struct sex-form acc)) ('pub 'union . _)) (process-struct sex-form acc))
((or ('enum . _)
('pub 'enum . _)) (process-struct sex-form acc))
((or ('var . _) ((or ('var . _)
('pub 'var . _) ('pub 'var . _)
('extern 'var . _)) (process-global-var sex-form acc)) ('extern 'var . _)) (process-global-var sex-form acc))
(('include _) (cons sex-form acc)) (('include _) (cons sex-form acc))
(('define . _) (cons sex-form acc))
(('import . modules) (('import . modules)
(semen-process-imports (get-public-forms modules) acc)) (semen-process-imports (get-public-forms modules) acc))
@@ -71,10 +67,6 @@
((or ('defmacro . rest) ((or ('defmacro . rest)
('pub 'defmacro . rest)) (defmacro rest) acc) ('pub 'defmacro . rest)) (defmacro rest) acc)
((or ('typedef new-type target)
('pub 'typedef new-type target))
(process-typedef new-type target acc))
(else (assert #f (fmt #f "Unknown top level form " sex-form))))) (else (assert #f (fmt #f "Unknown top level form " sex-form)))))
(define (semen-process-imports module-public-forms acc) (define (semen-process-imports module-public-forms acc)
@@ -91,46 +83,33 @@
acc)))))) acc))))))
(define (semen-macro-expand form) (define (semen-macro-expand form)
"Walk the form recursively and expand all macros, until none is left." "Walk the form recursively and expand all macros, unitl none is left."
(semen-walk-form (semen-walk-form
form form
(lambda (subform env) (lambda (subform env)
(if (sex-macro? subform) (if (sex-macro? subform)
(cons semen-walk-embed-result (semen-apply-macro subform (list))) (apply-macro subform)
subform)) subform))
#f)) #f))
;;; semen-walk-form and friends: form walker with various abilities. ;;; TODO: for greater inspiration, see SBCL's walk.lisp and their
;;; By default, replaces walked form with walk-fn result But may ;;; template system. Maybe it is worth it to implement something
;;; perform additional operations depending of what the walk function ;;; similar here
;;; has requested.
;;; For inspiration, see SBCL's walk.lisp and their template
;;; system.
(define semen-walk-embed-result (gensym)
;; For cases when result is a list which must be embedded in the
;; form, e.g. when it returned from a macro
)
(define (semen-walk-form form walk-fn env) (define (semen-walk-form form walk-fn env)
(if (atom? form) form (if (atom? form) form
(let ((new-form (walk-fn form env))) (let ((new-form (walk-fn form env)))
(cond ((not (eq? form new-form)) (cond ((not (eq? form new-form))
(semen-walk-form new-form walk-fn env)) (semen-walk-form new-form walk-fn env))
(else (else (recons
(let ((new-car (semen-walk-form (car new-form) walk-fn env)) new-form
(new-cdr (semen-walk-form (cdr new-form) walk-fn env))) (semen-walk-form (car new-form) walk-fn env)
(cond ((and (pair? new-car) (semen-walk-form (cdr new-form) walk-fn env)))))))
(eq? (car new-car) semen-walk-embed-result))
(append (cdr new-car) new-cdr))
(else
(recons new-form new-car new-cdr)))))))))
;;; Typdef (define (recons old-cons new-car new-cdr)
(if (and (eq? new-car (car old-cons))
(define (process-typedef new-type target acc) (eq? new-cdr (cdr old-cons)))
(cons `(typedef ,target ,new-type) acc)) old-cons
(cons new-car new-cdr)))
;;; Fn processing ;;; Fn processing
@@ -174,7 +153,7 @@
(process-fn `(fn ,ret-type ,name ,arglist ,@body) (list))) (process-fn `(fn ,ret-type ,name ,arglist ,@body) (list)))
(else (assert #f (fmt #f "Malformed lambda " form))))) (else (assert #f (fmt #f "Malformed lambda " form)))))
;;; Structs ;;; Struct
(define (process-struct sex-struct acc) (define (process-struct sex-struct acc)
(cons sex-struct acc)) (cons sex-struct acc))

View File

@@ -2,19 +2,12 @@
(include "utils.macros.scm") (include "utils.macros.scm")
(import (import (chicken pathname)
(chicken base)
(chicken io)
(chicken pathname)
(chicken port)
(chicken read-syntax)
(chicken string)
(chicken syntax)
brev-separate brev-separate
fmt) fmt)
(define (read-forms acc) (define (read-forms acc)
(let ((r (read-with-source-info (current-input-port)))) (let ((r (read)))
(if (eof-object? r) (reverse acc) (if (eof-object? r) (reverse acc)
(read-forms (cons r acc))))) (read-forms (cons r acc)))))
@@ -23,41 +16,7 @@
(with-input-from-file (pathname-strip-directory file) (with-input-from-file (pathname-strip-directory file)
(fn (read-forms (list)))))) (fn (read-forms (list))))))
(define (read-bracket port)
(let loop ((c (read-char port))
(str (string)))
(cond ((char=? c #\])
(cons '¤
(with-input-from-string str
(fn (port-map identity read)))))
((char=? c #\[)
(loop port (conc )))
(else
(loop (read-char port)
(conc str c))))))
(define open-bracket-counter (make-parameter 0))
(define (read-raw-forms input-source) (define (read-raw-forms input-source)
(let ((bracket-end (gensym)))
(set-read-syntax!
#\]
(lambda (port)
(when (= 0 (open-bracket-counter))
(error "Unmatched closing bracket"))
(open-bracket-counter (- (open-bracket-counter) 1))
bracket-end))
(set-read-syntax!
#\[
(lambda (port)
(open-bracket-counter (+ (open-bracket-counter) 1))
(let loop ((r (read port))
(acc (list)))
(if (eq? r bracket-end)
(cons '¤ (reverse acc))
(loop (read port)
(cons r acc)))))))
(if (eq? input-source 'stdin) (if (eq? input-source 'stdin)
(read-forms (list)) (read-forms (list))
(read-from-file input-source))) (read-from-file input-source)))

BIN
sex.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 600 KiB

View File

@@ -1,7 +1,6 @@
(declare (unit sexc) (declare (unit sexc)
(uses fmt-c-writer (uses fmt-c-writer
sex-reader sex-reader
utils
semen)) semen))
(include "utils.macros.scm") (include "utils.macros.scm")
@@ -118,18 +117,6 @@
(with-directory input-source (with-directory input-source
(semen-process raw-forms)))) (semen-process raw-forms))))
(define prelude
'((include inttypes.h)
(typedef u8 uint8-t)
(typedef i8 int8-t)
(typedef u16 uint16-t)
(typedef i16 int16-t)
(typedef u32 uint32-t)
(typedef i32 int32-t)
(typedef u64 uint64-t)
(typedef i64 int64-t)))
(define (main) (define (main)
(let* ((raw-args (command-line-arguments)) (let* ((raw-args (command-line-arguments))
(args (getopt-long raw-args (args (getopt-long raw-args
@@ -155,8 +142,7 @@
(return #f)) (return #f))
(load-persistent-module-paths) (load-persistent-module-paths)
(sex-fmt-current-file (to-absolute-pathname input)) (let* ((raw-forms (read-raw-forms input))
(let* ((raw-forms (append prelude (read-raw-forms input)))
(sex-forms (semantic-process-forms raw-forms input))) (sex-forms (semantic-process-forms raw-forms input)))
(if (or (get-arg args 'macro-expand #f) (if (or (get-arg args 'macro-expand #f)
(get-arg args 'emit-c #f)) (get-arg args 'emit-c #f))

View File

@@ -1,6 +1,6 @@
(test-group "basic" (test-begin "basic")
;; unkebabify ;;; unkebabify
(test '- (unkebabify '-)) (test '- (unkebabify '-))
(test '-- (unkebabify '--)) (test '-- (unkebabify '--))
(test '-> (unkebabify '->)) (test '-> (unkebabify '->))
@@ -11,21 +11,25 @@
(test '_this_->_member_ (unkebabify '-this-->-member-)) (test '_this_->_member_ (unkebabify '-this-->-member-))
(test '__->>> (unkebabify '--->>>)) (test '__->>> (unkebabify '--->>>))
;; atom-to-fmt-c ;;; atom-to-fmt-c
(test '%fun (atom-to-fmt-c 'fn)) (test '%fun (atom-to-fmt-c 'fn))
(test '%prototype (atom-to-fmt-c 'prototype)) (test '%prototype (atom-to-fmt-c 'prototype))
(test '%var (atom-to-fmt-c 'var))
(test '%block-begin (atom-to-fmt-c 'begin)) (test '%block-begin (atom-to-fmt-c 'begin))
(test '%define (atom-to-fmt-c 'define)) (test '%define (atom-to-fmt-c 'define))
(test '%pointer (atom-to-fmt-c 'pointer)) (test '%pointer (atom-to-fmt-c 'pointer))
(test '%array (atom-to-fmt-c 'array)) (test '%array (atom-to-fmt-c 'array))
(test 'vector-ref (atom-to-fmt-c '¤)) (test 'vector-ref (atom-to-fmt-c '@))
(test '%include (atom-to-fmt-c 'include)) (test '%include (atom-to-fmt-c 'include))
(test '%cast (atom-to-fmt-c 'cast))
;; c89 stuff ;;; c89 stuff
(test 'int (atom-to-fmt-c 'bool)) (test 'int (atom-to-fmt-c 'bool))
(test 1 (atom-to-fmt-c 'true)) (test 1 (atom-to-fmt-c 'true))
(test 0 (atom-to-fmt-c 'false)) (test 0 (atom-to-fmt-c 'false))
;; make-field-access ;;; make-field-access
(test 'a.b (make-field-access '(.b a))) (test 'a.b (make-field-access '(.b a)))
(test 'a.b.c (make-field-access '(.c a.b)))) (test 'a.b.c (make-field-access '(.c a.b)))
(test-end)

View File

@@ -1,159 +0,0 @@
;;; Types
(test-group "fmt-writer"
(test
'(const int)
(walk-type '(const int)))
(test
'(%array (const char) 512)
(walk-type '(¤ const char 512)))
(test
'(%array (float) 512)
(walk-type '(¤ (float) 512)))
(test
'(%array (const char) 512)
(walk-type '(¤ (const char) 512)))
(test
'(%array (const char))
(walk-type '(¤ const char)))
(test
'(%array (const char))
(walk-type '(¤ (const char))))
(test
'(%array float 8)
(walk-type '(¤ float 8)))
(test
"Pointer to const char"
'(const char *)
(walk-type '(* const char)))
(test
"Const pointer to const char"
'(const char * const)
(walk-type '(const * const char)))
(test
'(%fun void ((int) (float) (%array (struct what * const))))
(walk-type '(fn ((int) (float) (¤ (const * struct what))) void)))
(test
'(%fun void ((int) (%array float) (%array (struct what * const))))
(walk-type '(fn ((int) (¤ float) (¤ (const * struct what))) void)))
(test
'(%array (%fun void ((int) (%array float) (%array (struct what * const)))))
(walk-type '(¤ (fn ((int) (¤ float) (¤ (const * struct what))) void))))
;; Type convert to C
(test
'(int)
(type-convert-to-c '(int)))
(test
'(* int)
(type-convert-to-c '(int *)))
(test
'(* const int)
(type-convert-to-c '(const int *)))
(test
'(const * const char)
(type-convert-to-c '(const char * const)))
;;; Variable defs
(test
'(%var (%array float 8) a)
(walk-var '(var a (¤ float 8))))
(test
'(%var (int *) a (& n))
(walk-var '(var a (* int) (& n))))
(test
'(%var (const int *) a (& n))
(walk-var '(var a (* const int) (& n))))
(test
'(%var (struct suc) s)
(walk-var '(var s (struct suc))))
(test
'(%var (const struct suc *) s s1)
(walk-var '(var s (* const struct suc) s1)))
(test
'(%var (const struct suc *) s s1)
(walk-var '(var s (* (const struct suc)) s1)))
(test
'(%var (struct suc) s (hoge piyo))
(walk-var '(var s (struct suc) (hoge piyo))))
(test
'(%var (struct suc *) s (hoge piyo))
(walk-var '(var s (* struct suc) (hoge piyo))))
;;; Fn defs
(test
'(%fun void puk ((int) (%array float 8)))
(walk-fn-def '(fn puk ((int) (¤ float 8)) void)))
(test
'(%fun int main ((int argc) ((%array (const char)) argv))
(return 0))
(walk-fn-def
'(fn main ((argc int) (argv (¤ const char))) int
(return 0))))
(test
'(%fun int quxu (((struct piq *) bar))
(return 0))
(walk-fn-def
'(fn quxu ((bar (* struct piq))) int
(return 0))))
(test
'(%fun int quxu (((struct piq *) bar) ((%array (struct tt *)) ploop))
(return 0))
(walk-fn-def
'(fn quxu ((bar (* struct piq)) (ploop (¤ * struct tt))) int
(return 0))))
;; Structs
(test
'(struct no_kebab ((int a) (float f)))
(walk-struct '(struct no-kebab ((a int) (f float)))))
(test
'(struct settings ((u32 x y w h)
((%array (struct ((float r g b a))) 4) colors)))
(walk-struct
'(struct settings
((x y w h u32)
(colors [¤ struct ((r g b a float)) 4])))))
(test
'(struct settings ((u32 x y w h)
((%array (struct color ((float r g b a))) 4) colors)))
(walk-struct
'(struct settings
((x y w h u32)
(colors [¤ struct color ((r g b a float)) 4])))))
(test
'(struct mega_kebab ((int a)
((struct ((int year) (int month) (int day))) dob)
((%fun int ((int) (%array int))) min)))
(walk-struct '(struct mega-kebab
((a int)
(dob (struct ((year int)
(month int)
(day int))))
(min (fn ((int) (¤ int)) bool)))))))

View File

@@ -1,17 +0,0 @@
(import (chicken port))
(define-syntax reader-test
(syntax-rules ()
((reader-test result string)
(test result
(with-input-from-string string
(lambda () (read-raw-forms 'stdin)))))))
(test-group "reader"
;; []-syntax. For array types and array access expressions
(reader-test '((¤ * char)) "[* char]")
(reader-test '((¤ * * char const 512)) "[* * char const 512]")
(reader-test '((¤)) "[]")
(reader-test '((¤ (¤))) "[[]]")
(reader-test '((¤ (¤ const char))) "[[const char]]")
)

View File

@@ -9,9 +9,6 @@
(include "basic.scm") (include "basic.scm")
(include "semen.scm") (include "semen.scm")
(include "reader.scm")
(include "fmt-c-writer.scm")
(include "utils.scm")
;;; Should be the last in the test suite ;;; Should be the last in the test suite
(test-exit) (test-exit)

View File

@@ -1,5 +1,3 @@
(import srfi-69)
(define print-str-fn (define print-str-fn
'(fn void print-str ((string s)) '(fn void print-str ((string s))
(printf "%s" s))) (printf "%s" s)))
@@ -8,7 +6,7 @@
'(pub fn float sum ((int a) (int b)) '(pub fn float sum ((int a) (int b))
(return (cast float (+ a b))))) (return (cast float (+ a b)))))
(test-group "semen" (test-begin "semen")
(test-assert (sex-fn? print-str-fn)) (test-assert (sex-fn? print-str-fn))
(test #f (sex-fn-public? print-str-fn)) (test #f (sex-fn-public? print-str-fn))
(test 'void (sex-fn-return-type print-str-fn)) (test 'void (sex-fn-return-type print-str-fn))
@@ -35,11 +33,8 @@
;;; Macro expansion ;;; Macro expansion
(define (form-identity form env) (test 'a (semen-walk-form 'a identity))
form) (test '(a b c) (semen-walk-form '(a b c) identity))
(test 'a (semen-walk-form 'a form-identity (make-hash-table)))
(test '(a b c) (semen-walk-form '(a b c) form-identity (make-hash-table)))
(test 'a (semen-macro-expand 'a)) (test 'a (semen-macro-expand 'a))
(test '(a b c) (semen-macro-expand '(a b c))) (test '(a b c) (semen-macro-expand '(a b c)))
@@ -53,4 +48,6 @@
(test '((fn void foo ((int a) (int b)) (test '((fn void foo ((int a) (int b))
(return (+ a (* 10 b))))) (return (+ a (* 10 b)))))
(semen-process sex-code-macro)))) (semen-process sex-code-macro)))
(test-end)

View File

@@ -1,22 +0,0 @@
(test-group "utils"
(test
'((1) (2) (3))
(list-split '(1 * 2 * 3) '*))
(test
'((1 2 3))
(list-split '(1 2 3) '*))
(test
'(() (1) (2) (3) ())
(list-split '(* 1 * 2 * 3 *) '*))
(test
'((const) (const struct something))
(list-split '(const * const struct something) '*))
(test
'(1 * 2 * 3)
(list-join '(1 2 3) '*))
)

View File

@@ -4,8 +4,7 @@
(import (import
(chicken pathname) (chicken pathname)
(chicken process-context) (chicken process-context))
srfi-1)
(define (get-env-var name) (define (get-env-var name)
(get-environment-variable name)) (get-environment-variable name))
@@ -18,35 +17,3 @@
(make-absolute-pathname (make-absolute-pathname
(current-directory) (current-directory)
(pathname-directory file)))))) (pathname-directory file))))))
(define (to-absolute-pathname pathname)
(if (absolute-pathname? pathname)
pathname
(make-absolute-pathname
(current-directory)
pathname)))
(define (list-split src-list split-elt)
;; split '(1 2 / 3 4 / 5 6) by '/ -> '((1 2) (3 4) (5 6))
(fold (lambda (elt acc)
(if (eq? elt split-elt)
(append acc (list (list)))
(append (drop-right acc 1)
(list (append (last acc) (list elt))))))
(list (list))
src-list))
(define (list-join lists join-by)
(drop-right
(fold (lambda (elt acc)
(append acc (list elt) (list join-by)))
(list)
lists)
1))
;;; Reconstruct form
(define (recons old-cons new-car new-cdr)
(if (and (eq? new-car (car old-cons))
(eq? new-cdr (cdr old-cons)))
old-cons
(cons new-car new-cdr)))