Add type db, some nice macro features and a brand new SDL3 example #27
Reference in New Issue
Block a user
Delete Branch "sdl-example"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
c709c69176to85bf1c163cBuilt and ran this on CHICKEN 6.0.0 (worktree at
85bf1c1). This is the real thing: a type database the macros can actually ask questions of, plus an SDL3 program that only exists because the earlier codegen holes got closed.What I ran
make run-teststests/modules)example/serialize.sexcompile and runpoint { x=0 y=0 }/point { x=640 y=-480 }/person { name=Alex age=34 height=1.82 }serialize-structon an imported structserialize-struct: no such struct: point(pub enum color …)same file or via importpub must be followed by a definition(* (* char))file:line:bad.sex:2: unknown top level form: (wat 1 2)sexc -Cofexample/sdl3-triangle.sexswitch (event.type),(void *)(2 * sizeof(GLfloat)),SDL_GLContextccdies on-framework;sexcstill exits 0cc -o out /tmp/temp….c(extension is.c, so dropping-x cis correct)What is good
The type database is the feature.
map-fields+type-matchturn a struct name into a printer;example/serialize.sexis the proof, andtests/sex-programs/serialize.sexpins it. Macros getcat, a no-opcomment, and read access to the DB, which is the right surface.Module linking was actually wrong before: an imported
pub fnlost itspuband becamestatic, and an importedpub varbecame a private copy with an initializer. Keepingpubon prototypes and rewriting vars toexternis the correct C model, andtests/moduleswould have caught the old behaviour (it links and checks thatgreet-countis shared).sex-errorcitingfile:lineis what the location map was for. Rejecting nested pointer chains instead of silently dropping a*is the same kind of fix. Writing a temp.crather than stdin is simpler and lets#line/-gpoint at a real file; the.csuffix is why-x ccan go.The triangle is why this compiler is worth it. The generated C is what you would have written by hand: core-profile 3.3, uniforms for centre/spin/radius, mouse follow, LMB to spin, Esc to quit.
switch (. event type)surviving asswitch (event.type)is the previous PR paying off.Remaining, not blocking
process-importsregisters macros, thenconses everything else.register-aggregate!never runs for apub structthat arrived throughimport, so the serialize example does not work across modules. The TODO onprocess-public-interface-formalready says this;match-sex-formon those pasted forms is the obvious next step.(pub enum …)is rejected both as a public interface and bywalk-public(struct/union/typedef are handled; enum is not). A non-pubenum compiles. The error from the writer also has nofile:line:, because it is handed the cdr of thepubform.get-fields/map-fieldsdo not chase typedefs.(typedef point-t point)plus(serialize-struct point-t)fails;get-underlying-typereturnspointand the macro would have to take that extra step. Fine if documented; easy to trip over.sexcstill exits 0 whenccfails.compile-to-filenow returnsprocess-wait's values, butmaindrops them.sextesttreats a 0 as "compiled"..cis deleted afterccreturns, but leaks ifemit-cthrows (nested-pointer error left/tmp/temp….cbehind).OpenGL/gl3.hand-framework OpenGL. On Linux that include is missing and-frameworkis not a gcc flag. Worth a one-line "this one is macOS" in the header, orSDL3/SDL_opengl.hplus a GL loader if you want it to build where CI runs.tests/types.module.scmexports*, so the test build warns thatsemen.scmredefines importedcomment-form?. Productiontypes.module.scmis explicit and does not.Looks good to merge. The module/typedef holes are the natural follow-up, not a reason to sit on this.
@@ -0,0 +13,4 @@(define GL-SILENCE-DEPRECATION 1) ; OpenGL is deprecated on macOS(include SDL3/SDL.h)This, plus
-framework OpenGLin the build line, is Apple's GL 3.3. On LinuxOpenGL/gl3.his missing and gcc does not know-framework; swapping inSDL3/SDL_opengl.his not enough either (noglCreateShader). Fine as a macOS example — worth saying so in the header, since CI is Ubuntu.(pub enum …)dies here: struct/union/typedef are forwarded, enum is not. Same-file(pub enum color (red green blue))ispub must be followed by a definitionwith nofile:line:(this is the cdr of thepubform, which has no location of its own). Non-pubenums emit fine.@@ -212,2 +218,4 @@(register-aggregate! sex-struct)(cons sex-struct acc))(define (register-aggregate! form)This is what makes
serialize-structwork — but only for aggregates that go throughmatch-sex-form.process-importsstill justconses public structs, so apub structliving in another module is visible as C and invisible tomap-fields. I compiled that:serialize-struct: no such struct: point. The TODO on the module reader is this.@@ -76,0 +75,4 @@;; A variable becomes an `extern' declaration((var)(cons (copy-form-source! form (cons 'extern (take (cdr form) 3))) acc))((define defmacro import include struct typedef union)Same hole on import:
struct typedef unionare pasted,enumis not, so--public-interfaceon a module that exports an enum ispub must be followed by a definitionand cites the location correctly. Addingenumnext tounionis the whitelist half;walk-publicis the other.@@ -140,0 +134,4 @@(list))(list c-file)cc-args))))(call-with-values (lambda () (process-wait proc))These wait values never become the process exit code:
mainignores whatcompile-to-filereturns, sosexc file.sex -- -bogus-flagprints the cc error and exits 0.sextestthen treats that as a successful compile. Alsodelete-file*only runs after wait; ifemit-cthrows, the temp.cstays in/tmp.Thanks — every one of these reproduced, and all seven are fixed, each with the test that would have caught it.
d0d0ea8(pub enum …)rejected, nofile:line:847bb14get-fields/map-fieldsdon't chase typedefs97ef62bccfails294a275.cleaks when emission throws294a275bf83baa,a221e0ftests/types.module.scmexports*9763f5f1 — imported types. You were right that
match-sex-formon the pasted forms is the next step; it turned out to be the whole step.process-importsis now(fold match-sex-form acc module-public-forms), and the hand-rolleddefmacrocase is gone, sincematch-sex-formalready had one. That also fixed a latent one: a transitivepub importused to be pasted as an(import …)form and reached the writer as if it were a function call. Following imports for real made an include guard necessary — a diamond would otherwise paste the shared module twice into one translation unit, and a cycle would not terminate — so a module is now pasted at most once per unit, keyed by absolute path.2 —
pub enum.walk-publicand the module whitelist both have it now. Fixing that exposed a layer underneath: an enum can be written as a type, not only as a definition, and neither side could render it.(var m (enum color) red)crashed inc-enum, and(var e (enum (up down)) up)emittedenum e { up, down }—c-typewas passing the declarator's name in as the enum's tag, so the variable vanished. Both now do whatstructalready did:c-enumrenders a bare tag the wayc-struct/auxrendersstruct point p, andc-typelays the type out before the name being declared. Marked MODIFIED FROM UPSTREAM, next to the cast fix. Thepubcdr carries the form's location now too — though with enum handled, that branch is only reachable if semen and the writer disagree again.3 — typedefs. Resolved rather than documented:
get-fieldsandmap-fieldsfollow the alias, through(typedef point-t point)and(typedef point-t (struct point))alike, and through chains.get-type-infostill returns what was written, for a caller that wants to see the typedef. One consequence worth naming: an enumerator's type now comes back named as the enum was declared —enum mood, neverenum mood_t, which is not a C type.get-underlying-typealso stops on a typedef that leads back to itself; nothing prevents one from being written, and it used to spin.4 and 5.
compile-to-filereturnscc's status andmainexits with it. The temp file neededhandle-exceptions, notdynamic-wind— an unhandled error ends the process without unwinding, which is exactly why the file survived. Both are failure-path behaviour that no unit test can reach, so they live intests/exit-code/, next to the module-linking test, andmake run-testsruns them.6 — the triangle. This one grew a feature. Rather than document the header swap, Sex now reads
#+and#-feature expressions the way Common Lisp does (bf83baa), and the example picks its GL header with them:Read time, not compile time, which is the whole difference from cpp's
#ifdef: what does not apply never reaches macro expansion, the type database or the writer, so a guarded form may name things that do not exist on the other platform at all. Features are the host's —software-version,software-type,machine-typeas CHICKEN reports them — plus--features. Both build lines are in the file; pkg-config carries the flags on either platform, apart from Apple's GL framework, which has no pkg-config file to carry.Since this machine cannot compile the Linux branch,
--no-platform-featuresreads a file as another platform would, which is how that branch was checked:SDL3/SDL_opengl.hstayed out of it: on Apple it resolves to the 2.1 header, which has noglGenVertexArraysand so cannot bind the VAO this program needs.That work turned up one more thing. sextest's
(compilation …)form never worked — it looked forcompileamong the settings while the reader collectscompilation, so the flags were always#fand appending them built an improper argv. Fixed in6e4cb80, along with a subtler problem it exposed: sextest reads the program and prints it back to sexc, so#+/#-were resolved on its side with its own feature list. It reads the file again with whatever--featuresthe compilation flags ask for, and the reader now defaults to the host's features, so anything reading Sex sees what the compiler would.7. Same export list as production, so the suite tests what ships. No warning in the test build any more.
Verification, on top of
make run-tests(unit + six programs +tests/modules+ the new exit-code check):tests/modulesnow also expands a macro that reads an imported struct's fields — directly and through the imported typedef — so the cross-module hole cannot come back silently, andtests/sex-programs/features.sexpins the feature expressions end to end, including a wholemaindropped by#-.LGTM
@@ -62,0 +69,4 @@# The failure paths are checked end to end; see tests/exit-code/Makefile.check-exit-code: sexc@$(MAKE) --no-print-directory -C ./tests/exit-code check SEXC=../../sexcStrange place to for that definition btw. But I see, the tests/ layout is kinda per-subproject, so probably that makes sense, but lookin' alien to me.
@@ -176,0 +198,4 @@((symbol? test) (and (memq test (current-features)) #t))((pair? test)(case (car test)((and) (every feature-true? (cdr test)))🤯 So straightforward