5 Commits

Author SHA1 Message Date
ca88d9b386 implement compound and designated literals support
All checks were successful
Sex CI / build-linux (pull_request) Successful in 4m46s
Sex CI / build-linux (push) Successful in 4m50s
2026-09-27 19:35:37 +03:00
a9b00f2135 use true bool, since we target c99 onwards 2026-09-27 18:06:59 +03:00
26e8e6c374 fix lambdas and add a test to prevent rotting
Lambdas rotted because there wasn't any test for them, and after
return type moved to the end, lambdas stayed assumning it in the
front. We fix this and add test program
2026-09-27 16:11:05 +03:00
62316e1e3d enable includes to have multiple targets
All checks were successful
Sex CI / build-linux (pull_request) Successful in 4m47s
Sex CI / build-linux (push) Successful in 4m47s
2026-09-26 16:54:51 +03:00
Pavel Kulyov
b1ab18b9af dev: require make deps manually for dev & CI
All checks were successful
Sex CI / build-linux (pull_request) Successful in 4m50s
Sex CI / build-linux (push) Successful in 4m52s
2026-09-24 01:10:59 +03:00
13 changed files with 297 additions and 56 deletions

View File

@@ -57,7 +57,9 @@ jobs:
hash -r hash -r
csc -version csc -version
# Eggs are pinned in eggs.lock and installed by the Makefile into .eggs/. - name: Install dependencies
run: make deps
- name: Build sexc - name: Build sexc
run: make && ./sexc --help run: make && ./sexc --help

View File

@@ -34,24 +34,27 @@ DEPSLOCK = eggs.lock
EGGS_DIR := $(abspath .eggs) EGGS_DIR := $(abspath .eggs)
# Compiler's own repository (types.db, chicken.*), ignoring ambient venv vars. # Compiler's own repository (types.db, chicken.*), ignoring ambient venv vars.
SYSTEM_CHICKEN_REPO := $(shell env -u CHICKEN_INSTALL_REPOSITORY -u CHICKEN_REPOSITORY_PATH -u CHICKEN_EGG_CACHE -u CHICKEN_INSTALL_PREFIX $(CHICKEN_INSTALL) -repository 2>/dev/null) SYSTEM_CHICKEN_REPO := $(shell env \
-u CHICKEN_INSTALL_REPOSITORY -u CHICKEN_REPOSITORY_PATH \
-u CHICKEN_EGG_CACHE -u CHICKEN_INSTALL_PREFIX \
$(CHICKEN_INSTALL) -repository 2>/dev/null)
CHICKEN_ABI := $(notdir $(SYSTEM_CHICKEN_REPO)) CHICKEN_ABI := $(notdir $(SYSTEM_CHICKEN_REPO))
EGGS_STAMP := $(EGGS_DIR)/.installed-$(CHICKEN_ABI) EGGS_STAMP := $(EGGS_DIR)/.installed-$(CHICKEN_ABI)
export CHICKEN_EGG_CACHE := $(EGGS_DIR)/cache # Try project-local chicken repository first. In case it doesn't exist (packaging for distros),
export CHICKEN_INSTALL_REPOSITORY := $(EGGS_DIR) # system-wide repository will be used.
export CHICKEN_INSTALL_PREFIX := $(EGGS_DIR) unexport CHICKEN_INSTALL_REPOSITORY
unexport CHICKEN_EGG_CACHE
unexport CHICKEN_INSTALL_PREFIX
export CHICKEN_REPOSITORY_PATH := $(EGGS_DIR):$(SYSTEM_CHICKEN_REPO) export CHICKEN_REPOSITORY_PATH := $(EGGS_DIR):$(SYSTEM_CHICKEN_REPO)
all: sexc all: sexc
sexc: $(EGGS_STAMP) $(OBJ) main.scm sexc: $(OBJ) main.scm
$(CHICKEN_C) $(CSC_FLAGS) main.scm -o sexc-tmp -link sexc $(CHICKEN_C) $(CSC_FLAGS) main.scm -o sexc-tmp -link sexc
# otherwise csc hangs, probably because it tries to compile to sexc.o first # otherwise csc hangs, probably because it tries to compile to sexc.o first
mv sexc-tmp sexc mv sexc-tmp sexc
$(OBJ): $(EGGS_STAMP)
#------------------------------------------------------------------ #------------------------------------------------------------------
utils.o: utils.module.scm utils.scm utils.o: utils.module.scm utils.scm
@@ -82,16 +85,16 @@ sexc.o: sexc.module.scm types.o sexc.scm fmt-c-writer.o sex-macros.o sex-modules
$(CHICKEN_C) $(CSC_FLAGS) $(MODULE_FLAGS) sexc.module.scm -o sexc.o -unit sexc -link fmt-c-writer,sex-macros,sex-modules,reader,semen,types,utils $(CHICKEN_C) $(CSC_FLAGS) $(MODULE_FLAGS) sexc.module.scm -o sexc.o -unit sexc -link fmt-c-writer,sex-macros,sex-modules,reader,semen,types,utils
# Unit testing # Unit testing
sex-tests: $(EGGS_STAMP) sex-tests:
$(MAKE) -C ./tests sex-tests $(MAKE) -C ./tests sex-tests
cp ./tests/sex-tests ./ cp ./tests/sex-tests ./
sextest: $(EGGS_STAMP) sextest:
$(MAKE) -C ./tools/sextest sextest $(MAKE) -C ./tools/sextest sextest
cp ./tools/sextest/sextest . cp ./tools/sextest/sextest .
SEX_TEST_PROGRAMS = hello-world lists comments unicode serialize features \ SEX_TEST_PROGRAMS = hello-world lists comments unicode serialize features \
feature-flags feature-flags lambdas compound-literals
# Multi-module linking is checked end to end; see tests/modules/Makefile. # Multi-module linking is checked end to end; see tests/modules/Makefile.
check-modules: sexc check-modules: sexc
@@ -116,9 +119,11 @@ installdirs:
uninstall: uninstall:
rm -f $(DESTDIR)$(bindir)/sexc rm -f $(DESTDIR)$(bindir)/sexc
# eggs.lock is the pin file (chicken-status -list). Install from it; $(EGGS_STAMP) deps-update: export CHICKEN_EGG_CACHE := $(EGGS_DIR)/cache
# do not float versions on a normal build. Regenerating the lock: $(EGGS_STAMP) deps-update: export CHICKEN_INSTALL_REPOSITORY := $(EGGS_DIR)
# make deps-update $(EGGS_STAMP) deps-update: export CHICKEN_INSTALL_PREFIX := $(EGGS_DIR)
$(EGGS_STAMP) deps-update: export CHICKEN_REPOSITORY_PATH := $(EGGS_DIR):$(SYSTEM_CHICKEN_REPO)
$(EGGS_STAMP): $(DEPSLOCK) $(EGGS_STAMP): $(DEPSLOCK)
mkdir -p $(CHICKEN_EGG_CACHE) mkdir -p $(CHICKEN_EGG_CACHE)
$(CHICKEN_INSTALL) -force -from-list $(DEPSLOCK) $(CHICKEN_INSTALL) -force -from-list $(DEPSLOCK)
@@ -126,10 +131,10 @@ $(EGGS_STAMP): $(DEPSLOCK)
deps: $(EGGS_STAMP) deps: $(EGGS_STAMP)
deps-update: $(DEPSFILE) deps-update: $(DEPSFILE) deps-clean
rm -rf $(EGGS_DIR)
mkdir -p $(CHICKEN_EGG_CACHE) mkdir -p $(CHICKEN_EGG_CACHE)
$(CHICKEN_INSTALL) -force $$(cat $(DEPSFILE)) $(CHICKEN_INSTALL) -force $$(cat $(DEPSFILE))
# Local repo only: a system path here would leak distro eggs into the lock.
CHICKEN_REPOSITORY_PATH=$(EGGS_DIR) $(CHICKEN_STATUS) -list | sort > $(DEPSLOCK).tmp CHICKEN_REPOSITORY_PATH=$(EGGS_DIR) $(CHICKEN_STATUS) -list | sort > $(DEPSLOCK).tmp
mv $(DEPSLOCK).tmp $(DEPSLOCK) mv $(DEPSLOCK).tmp $(DEPSLOCK)
touch $(EGGS_STAMP) touch $(EGGS_STAMP)

View File

@@ -12,22 +12,25 @@ Sex is statically typed, compiled general purpose language.
First, get yourself a Chicken, then, some Chicken deps. You also will First, get yourself a Chicken, then, some Chicken deps. You also will
need a C compiler. need a C compiler.
Eggs are installed into a project-local ~.eggs/~ repository; they do not ** Development
touch the Chicken system repository.
** Compilation
#+begin_src sh #+begin_src sh
make deps
make make
#+end_src #+end_src
That installs pinned eggs from ~eggs.lock~ into ~.eggs/~ if needed, then ~make deps~ installs pinned eggs from ~eggs.lock~ into a project-local
builds ~sexc~. ~dependencies.txt~ is the unpinned request list. To ~.eggs/~ repository. ~dependencies.txt~ is the unpinned request list.
refresh ~eggs.lock~ after changing it: To refresh ~eggs.lock~ after changing it:
#+begin_src sh #+begin_src sh
make deps-update make deps-update
#+end_src #+end_src
** Packaging for system package managers
- Depend ~sex~ package on Chicken-6 (with ~libchicken.a~) and all eggs from ~dependencies.txt~.
- Compile and install with ~make~ (usually no other arguments required).
- Move resulting ~sexc~ binary to the appropriate place.
** Static compilation ** Static compilation
The default ~CSC_FLAGS~ include ~-static~, so ~sexc~ does not need The default ~CSC_FLAGS~ include ~-static~, so ~sexc~ does not need
~.eggs~ at runtime and ~make install~ stays relocatable. Chicken must ~.eggs~ at runtime and ~make install~ stays relocatable. Chicken must
@@ -172,6 +175,38 @@ e.g. for checking output for other platform:
sexc example/sdl3-triangle.sex -C --no-platform-features --features=linux,unix,x86-64 sexc example/sdl3-triangle.sex -C --no-platform-features --features=linux,unix,x86-64
#+end_src #+end_src
** Aggregate initializers and compound literals
~#(...)~ is a brace initializer. On its own it has no type and takes one
from where it is written:
#+begin_src scheme
(var p (struct point) #(1 2))
#+end_src
A ~:~ inside one ends a type and makes the whole thing a compound
literal --- an unnamed object of that type, usable anywhere an
expression is:
#+begin_src scheme
(var q (struct point) #(struct point : 3 4))
(var a (* int) #([int 3] : 10 20 30))
(draw-line ui #(struct point : 0 0) end)
(var p (* struct point) (& #(struct point : 9 9))) ; an lvalue, so `&' works
#+end_src
The type is written as bare words, the way it is everywhere else in the
language; ~:~ is what ends it.
A leading ~.~ names a field, so initializers may be designated, given in
any order, and mixed with positional ones:
#+begin_src scheme
(var r (struct named) #(struct named : .n 7 .first-name "zoe"))
#+end_src
A compound literal written inside a block lives until the end of that
block and no longer, so returning its address is a dangling pointer.
** Syntactic macros ** Syntactic macros
Sex has support for syntactic macros. Macro definitions look like Sex has support for syntactic macros. Macro definitions look like
functions: they have a name, an argument list and a body. Macro should functions: they have a name, an argument list and a body. Macro should

View File

@@ -6,14 +6,14 @@
(pub fn main () int (pub fn main () int
(var a int 10) (var a int 10)
(var b int 20) (var b int 20)
(var (fn ((int) (int)) int) sum-fn sum) (var sum-fn (fn ((int) (int)) int) sum)
(var (fn ((int) (int)) int) sum-lambda (var sum-lambda (fn ((int) (int)) int)
(lambda ((a int) (b int)) int () (lambda ((a int) (b int)) int ()
(return (+ a b)))) (return (+ a b))))
(var (fn ((int)) int) sum-lambda-2 (var sum-lambda-2 (fn ((int)) int)
(lambda ((a int)) int () (lambda ((a int)) int ()
(return (+ a 20)))) (return (+ a 20))))
@@ -28,9 +28,9 @@
(return (+ a b 100))) (return (+ a b 100)))
a b)) a b))
(var (fn ((int)) int) l-1 (var l-1 (fn ((int)) int)
(lambda ((a int)) int () (lambda ((a int)) int ()
(var (fn ((int)) int) l-2 (var l-2 (fn ((int)) int)
(lambda ((a int)) int () (lambda ((a int)) int ()
(return (+ 60 a)))) (return (+ 60 a))))
(return (+ 600 (l-2 a))))) (return (+ 600 (l-2 a)))))

View File

@@ -120,10 +120,6 @@ forms, and what remains."
((|\||) 'bit-or) ((|\||) 'bit-or)
((|\|\||) '%or) ((|\|\||) '%or)
((|\|=|) 'bit-or=) ((|\|=|) 'bit-or=)
;; uh things we do for c89 compatibility
((bool) 'int)
((true) 1)
((false) 0)
(else (else
(if (symbol? atom) (if (symbol? atom)
(unkebabify atom) (unkebabify atom)
@@ -176,9 +172,7 @@ forms, and what remains."
(define (walk-expr form) (define (walk-expr form)
(match form (match form
((? vector?) ((? vector?) (walk-initializer (vector->list form)))
(list->vector
(walk-expr (vector->list form))))
((? atom?) ((? atom?)
(atom-to-fmt-c form)) (atom-to-fmt-c form))
;; (comment "text") -> /* text */. `%comment' is the fmt-c directive. ;; (comment "text") -> /* text */. `%comment' is the fmt-c directive.
@@ -238,6 +232,48 @@ forms, and what remains."
;; Drop comments so they will not generate additional comma ;; Drop comments so they will not generate additional comma
(else (map walk-expr (remove comment-form? form))))) (else (map walk-expr (remove comment-form? form)))))
;;; #(a b c) is a brace initializer. A `:' inside one ends a type and
;;; turns the whole thing into a C99 compound literal:
;;; #(struct point : 1 2) is (struct point){1, 2}, and the type is
;;; written as bare words, the way it is everywhere else in the
;;; language. `:' is the separator because it is the one thing that can
;;; be neither a type word nor an expression -- a named form would be a
;;; C identifier, and so shadowable (see issue #36).
(define (walk-initializer elements)
(let ((parts (list-split (remove comment-form? elements) ':)))
(if (null? (cdr parts))
(list->vector (walk-designators (car parts)))
(cons* '%compound
(walk-type (maybe-unwrap-type (car parts)))
(walk-designators (cadr parts))))))
;;; `.field value' is a designated initializer; anything else is
;;; positional. C lets the two be mixed, and nothing here stops it. A
;;; leading `.' cannot begin a C identifier, so a field name needs no
;;; keyword to introduce it and cannot collide with one.
(define (walk-designators elements)
(let loop ((es elements) (acc (list)))
(match es
(() (reverse acc))
(((? designator? d))
(sex-error elements "designated initializer without a value" d))
(((? designator? d) value . rest)
(loop rest
(cons (list '%designate
(atom-to-fmt-c (designator-field d))
(walk-expr value))
acc)))
((e . rest) (loop rest (cons (walk-expr e) acc))))))
(define (designator? x)
(and (symbol? x)
(let ((s (symbol->string x)))
(and (> (string-length s) 1)
(char=? #\. (string-ref s 0))))))
(define (designator-field d)
(string->symbol (substring (symbol->string d) 1)))
(define (walk-var form) (define (walk-var form)
;; (var a int) -> (%var int a) ;; (var a int) -> (%var int a)
;; (var a (const int) 32) -> (%var (const int) a 32) ;; (var a (const int) 32) -> (%var (const int) a 32)
@@ -380,8 +416,8 @@ forms, and what remains."
(map arg-type (remove comment-form? form))) (map arg-type (remove comment-form? form)))
(define (walk-function form) (define (walk-function form)
;; (fn ret-type name arglist body) -> normal function ;; (fn name arglist ret-type body) -> normal function
;; (fn ret-type name arglist) -> prototype ;; (fn name arglist ret-type) -> prototype
(if (>= (length form) 5) (if (>= (length form) 5)
(walk-fn-def form) (walk-fn-def form)
(cons '%prototype (cdr (walk-fn-def form))))) (cons '%prototype (cdr (walk-fn-def form)))))

View File

@@ -72,7 +72,7 @@
((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 . includes) (process-includes sex-form includes acc))
((or ('define name . _) ((or ('define name . _)
('pub 'define name . _)) ('pub 'define name . _))
(add-define name sex-form) (add-define name sex-form)
@@ -92,6 +92,17 @@
(else (sex-error sex-form "unknown top level form" sex-form)))) (else (sex-error sex-form "unknown top level form" sex-form))))
(define (process-includes sex-form includes acc)
;; consume (include ...) form and add to acc
;; (include <inc>) for each include
(let process ((includes includes)
(acc acc))
(if (null? includes)
acc
(process (cdr includes)
(cons (copy-form-source! sex-form `(include ,(car includes)))
acc)))))
(define (process-imports module-public-forms acc) (define (process-imports module-public-forms acc)
;; consume (import ...) form and process imports so ;; consume (import ...) form and process imports so
;; data types end up in types db ;; data types end up in types db
@@ -244,10 +255,10 @@
(define (make-aux-lambda-struct name form) (define (make-aux-lambda-struct name form)
(match form (match form
(('lambda ret-type arglist captures . body) (('lambda arglist ret-type captures . body)
;; Captures are ignored for now, but ;; Captures are ignored for now, but
;; we'll need them for TODO: closures support ;; we'll need them for TODO: closures support
(process-fn (copy-form-source! form `(fn ,ret-type ,name ,arglist ,@body)) (process-fn (copy-form-source! form `(fn ,name ,arglist ,ret-type ,@body))
(list))) (list)))
(else (sex-error form "malformed lambda" form)))) (else (sex-error form "malformed lambda" form))))

View File

@@ -14,6 +14,7 @@
c-in-expr c-in-stmt c-in-test c-in-expr c-in-stmt c-in-test
c-paren c-maybe-paren c-type c-literal? c-literal char->c-char c-paren c-maybe-paren c-type c-literal? c-literal char->c-char
c-struct c-union c-class c-enum c-typedef c-cast c-struct c-union c-class c-enum c-typedef c-cast
c-braced-list c-compound c-designate
c-expr c-expr/sexp c-apply c-op c-indent c-current-indent-string c-expr c-expr/sexp c-apply c-op c-indent c-current-indent-string
c-wrap-stmt c-open-brace c-close-brace c-wrap-stmt c-open-brace c-close-brace
c-block c-braced-block c-begin c-block c-braced-block c-begin
@@ -277,6 +278,8 @@
((%comment) ((apply c-comment (cdr x)) st)) ((%comment) ((apply c-comment (cdr x)) st))
((:) ((apply c-label (cdr x)) st)) ((:) ((apply c-label (cdr x)) st))
((%cast) ((apply c-cast (cdr x)) st)) ((%cast) ((apply c-cast (cdr x)) st))
((%compound) ((apply c-compound (cdr x)) st))
((%designate) ((apply c-designate (cdr x)) st))
((+ - & * / % ! ~ ^ && < > <= >= == != << >> ((+ - & * / % ! ~ ^ && < > <= >= == != << >>
= *= /= %= &= ^= >>= <<=) ; |\|| |\|\|| |\|=| = *= /= %= &= ^= >>= <<=) ; |\|| |\|\|| |\|=|
((apply c-op x) st)) ((apply c-op x) st))
@@ -306,17 +309,7 @@
((apply c-op "-=" (cdr x)) st)) ((apply c-op "-=" (cdr x)) st))
(else ((c-apply x) st)))))) (else ((c-apply x) st))))))
((vector? x) ((vector? x)
((c-wrap-stmt ((c-wrap-stmt (c-braced-list (vector->list x))) st))
(fmt-try-fit
(fmt-let 'no-wrap? #t
(cat "{" (fmt-join c-expr (vector->list x) ", ") "}"))
(lambda (st)
(let* ((col (fmt-col st))
(sep (string-append "," (make-nl-space col))))
((cat "{" (fmt-join c-expr (vector->list x) sep)
"}" nl)
st)))))
st))
(else (else
((c-literal x) st)))))) ((c-literal x) st))))))
@@ -832,6 +825,25 @@
(cat "(" (c-with-op 'paren (c-expr expr)) ")") (cat "(" (c-with-op 'paren (c-expr expr)) ")")
(c-expr expr)))) (c-expr expr))))
;; { a, b, c } -- on one line if it fits, one element per line if not.
(define (c-braced-list ls)
(fmt-try-fit
(fmt-let 'no-wrap? #t (cat "{" (fmt-join c-expr ls ", ") "}"))
(lambda (st)
(let* ((col (fmt-col st))
(sep (string-append "," (make-nl-space col))))
((cat "{" (fmt-join c-expr ls sep) "}" nl) st)))))
;; (T){ ... } -- a C99 compound literal, not a cast: the result is an
;; unnamed object and an lvalue, so `&' on it is legal. At block scope
;; it lives until the end of the enclosing block and no longer.
(define (c-compound type . init)
(cat "(" (c-type type) ")" (c-braced-list init)))
;; .field = value, inside a braced list
(define (c-designate field value)
(cat "." (c-expr field) " = " (c-expr value)))
(define (c-typedef type alias . o) (define (c-typedef type alias . o)
(c-wrap-stmt (c-wrap-stmt
(cat "typedef " (c-type type alias) (fmt-join/prefix c-expr o " ")))) (cat "typedef " (c-type type alias) (fmt-join/prefix c-expr o " "))))

View File

@@ -176,6 +176,7 @@ status, which is ours to pass on."
(define prelude (define prelude
'((include inttypes.h) '((include inttypes.h)
(include stdbool.h)
(typedef u8 uint8-t) (typedef u8 uint8-t)
(typedef i8 int8-t) (typedef i8 int8-t)

View File

@@ -31,10 +31,10 @@
(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))
;; c89 stuff ;; C99 onwards, true C spellings for bool
(test 'int (atom-to-fmt-c 'bool)) (test 'bool (atom-to-fmt-c 'bool))
(test 1 (atom-to-fmt-c 'true)) (test 'true (atom-to-fmt-c 'true))
(test 0 (atom-to-fmt-c 'false)) (test 'false (atom-to-fmt-c 'false))
;; dot-access -> %. member-access directive (kebab-converted operands) ;; dot-access -> %. member-access directive (kebab-converted operands)
(test '(%. a b) (walk-expr '(dot-access a b))) (test '(%. a b) (walk-expr '(dot-access a b)))

View File

@@ -101,6 +101,39 @@
'(%var (struct suc *) s (hoge piyo)) '(%var (struct suc *) s (hoge piyo))
(walk-var '(var s (* struct suc) (hoge piyo)))) (walk-var '(var s (* struct suc) (hoge piyo))))
;;; Initializers and compound literals
(test "a bare initializer is unchanged"
'#(1 2)
(walk-expr '#(1 2)))
(test "`:' ends the type and makes it a compound literal"
'(%compound (struct point) 3 4)
(walk-expr '#(struct point : 3 4)))
(test "the type is bare words, as everywhere else"
'(%compound (const char *) 65)
(walk-expr '#(* const char : 65)))
(test "and may be an array type"
'(%compound (%array int 3) 10 20 30)
(walk-expr '#(¤ int 3 : 10 20 30)))
(test "a grouped type is unwrapped the way a declaration's is"
'(%compound (%array int 3) 10)
(walk-expr '#((¤ int 3) : 10)))
(test "`.field value' is a designated initializer, kebab and all"
'(%compound (struct named) (%designate n 7) (%designate first_name "zoe"))
(walk-expr '#(struct named : .n 7 .first-name "zoe")))
(test "positional and designated may be mixed"
'(%compound (struct point) 1 (%designate y 5))
(walk-expr '#(struct point : 1 .y 5)))
(test "designators work in an untyped initializer too"
'#((%designate y 5))
(walk-expr '#(.y 5)))
;;; Fn defs ;;; Fn defs
(test (test
'(%fun void puk ((int) (%array float 8))) '(%fun void puk ((int) (%array float 8)))
@@ -150,7 +183,7 @@
(test (test
'(struct mega_kebab ((int a) '(struct mega_kebab ((int a)
((struct ((int year) (int month) (int day))) dob) ((struct ((int year) (int month) (int day))) dob)
((%fun int ((int) (%array int))) min))) ((%fun bool ((int) (%array int))) min)))
(walk-struct '(struct mega-kebab (walk-struct '(struct mega-kebab
((a int) ((a int)
(dob (struct ((year int) (dob (struct ((year int)

View File

@@ -115,4 +115,17 @@
(union t-doc-val ((i int) (f float)))) (union t-doc-val ((i int) (f float))))
(semen-process (semen-process
'((union t-doc-val "Either." ((i int) (f float)))))) '((union t-doc-val "Either." ((i int) (f float))))))
;; Includes
(test '((include stdio.h))
(semen-process
'((include stdio.h))))
(test '((include stdio.h)
(include stdlib.h))
(semen-process
'((include stdio.h
stdlib.h))))
(test '()
(semen-process
'((include))))
) )

View File

@@ -0,0 +1,49 @@
(input)
(output "plain 1 2"
"literal 3 4"
"designated zoe 7"
"through a pointer 9"
"array 10 20 30"
"argument 6"
"mixed 1 5")
(return 0)
;;; `:' inside #(...) ends a type and makes the rest a C99 compound
;;; literal. Without one, #(...) is the brace initializer it always was.
(include stdio.h)
(struct point ((x int) (y int)))
(struct named ((first-name (* const char)) (n int)))
(fn sum ((p (struct point))) int
(return (+ (. p x) (. p y))))
(pub fn main () int
;; unchanged: a bare initializer has no type of its own
(var p (struct point) #(1 2))
(printf "plain %d %d\n" (. p x) (. p y))
(var q (struct point) #(struct point : 3 4))
(printf "literal %d %d\n" (. q x) (. q y))
;; designated, out of declaration order, and kebab-cased
(var r (struct named) #(struct named : .n 7 .first-name "zoe"))
(printf "designated %s %d\n" (. r first-name) (. r n))
;; a compound literal is an lvalue, so its address can be taken --
;; until the end of the enclosing block, and no longer
(var pp (* struct point) (& #(struct point : 9 9)))
(printf "through a pointer %d\n" (-> pp x))
;; an array literal decays the way an array does
(var a (* int) #([int 3] : 10 20 30))
(printf "array %d %d %d\n" (¤ a 0) (¤ a 1) (¤ a 2))
(printf "argument %d\n" (sum #(struct point : 2 4)))
;; positional and designated may be mixed, as in C
(var m (struct point) #(struct point : 1 .y 5))
(printf "mixed %d %d\n" (. m x) (. m y))
(return 0))

View File

@@ -0,0 +1,44 @@
(input)
(output "Named fn through a pointer: 30"
"Lambda through a pointer: 30"
"Lambda called in place: 130"
"Nested lambdas: 666")
(return 0)
;;; Lambdas are lifted into toplevel functions by semen, so what this
;;; really checks is that the lifted `fn' comes out in the argument
;;; order the writer expects -- (fn name arglist ret-type . body).
(include stdio.h)
(fn sum ((a int) (b int)) int
(return (+ a b)))
(pub fn main () int
(var a int 10)
(var b int 20)
(var sum-fn (fn ((int) (int)) int) sum)
(printf "Named fn through a pointer: %d\n" (sum-fn a b))
(var sum-lambda (fn ((int) (int)) int)
(lambda ((a int) (b int)) int ()
(return (+ a b))))
(printf "Lambda through a pointer: %d\n" (sum-lambda a b))
(printf "Lambda called in place: %d\n"
((lambda ((a int) (b int)) int ()
(return (+ a b 100)))
a b))
;; A lambda inside a lambda: the inner one is lifted out of a
;; function that is itself being lifted
(var outer (fn ((int)) int)
(lambda ((x int)) int ()
(var inner (fn ((int)) int)
(lambda ((y int)) int ()
(return (+ 60 y))))
(return (+ 600 (inner x)))))
(printf "Nested lambdas: %d\n" (outer 6))
(return 0))