kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit c09a8d50742caad102d77ca255eb63ae2b68a6ce
parent 0460147a6eb6d881eee3041393e2f1c120c56f4d
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon,  1 Jun 2026 12:06:07 -0700

frontend: C registers its extensions; drop the fallback-to-C

cfree_language_for_path defaulted any unrecognized extension to
CFREE_LANG_C, making C a privileged fallback rather than a peer of the
other frontends. Have the C frontend claim c/h like every other language
claims its extensions, and return a new CFREE_LANG_UNKNOWN sentinel when
no registered frontend matches. The compile-session guards already reject
out-of-range languages, so an unrecognized input now surfaces an error
instead of being misparsed as C.

Diffstat:
Mdoc/FRONTENDS.md | 9+++++----
Mdriver/cmd/dbg.c | 3+++
Minclude/cfree/compile.h | 9+++++++++
Mlang/c/c.c | 18+++++++++++++-----
Msrc/api/compile.c | 10++++++----
5 files changed, 36 insertions(+), 13 deletions(-)

diff --git a/doc/FRONTENDS.md b/doc/FRONTENDS.md @@ -76,10 +76,11 @@ registry just provides the default wiring. `cfree_language_for_path` (`src/api/compile.c`) maps a file path to a language by extracting the trailing extension and walking every registered frontend's `extensions` list (case-insensitively, so `.S` and `.s` both hit asm's `"s"`). -The C frontend claims *no* extensions on purpose: an unrecognized extension -falls through to `CFREE_LANG_C` as the default, so listing `c`/`h` would only -duplicate the fallback. asm claims `s`, toy claims `toy`, wasm claims `wat` -and `wasm`. +No frontend is privileged. C is just another language: it claims `c` and `h`, +asm claims `s`, toy claims `toy`, and wasm claims `wat` and `wasm`. A path +whose extension no frontend claims — or that has no extension — resolves to +`CFREE_LANG_UNKNOWN` rather than silently defaulting to C, so an unrecognized +input is rejected instead of misparsed. ``` path --cfree_language_for_path--> CfreeLanguage diff --git a/driver/cmd/dbg.c b/driver/cmd/dbg.c @@ -1782,6 +1782,7 @@ static const char* dbg_jit_language_name(CfreeLanguage lang) { case CFREE_LANG_WASM: return "wasm"; case CFREE_LANG_C: + case CFREE_LANG_UNKNOWN: case CFREE_LANG_COUNT: break; } @@ -1797,6 +1798,7 @@ static const char* dbg_jit_language_suffix(CfreeLanguage lang) { case CFREE_LANG_WASM: return ".wat"; case CFREE_LANG_C: + case CFREE_LANG_UNKNOWN: case CFREE_LANG_COUNT: break; } @@ -1812,6 +1814,7 @@ static const char* dbg_jit_default_name(CfreeLanguage lang) { case CFREE_LANG_WASM: return "<dbg-jit.wat>"; case CFREE_LANG_C: + case CFREE_LANG_UNKNOWN: case CFREE_LANG_COUNT: break; } diff --git a/include/cfree/compile.h b/include/cfree/compile.h @@ -15,6 +15,10 @@ */ typedef enum CfreeLanguage { + /* No registered frontend claims the input. cfree_language_for_path + * returns this when an extension matches nothing; it is never a valid + * frontend index (the compile paths reject it). */ + CFREE_LANG_UNKNOWN = -1, CFREE_LANG_C = 0, CFREE_LANG_ASM = 1, CFREE_LANG_TOY = 2, @@ -103,6 +107,11 @@ typedef struct CfreeFrontendVTable { CfreeFrontendAbortFn abort; } CfreeFrontendVTable; +/* Map a path to a language purely by its registered extension. Walks every + * registered frontend's `extensions` list (case-insensitively) and returns + * the owning CfreeLanguage. No frontend is privileged: a path whose extension + * is unclaimed (or that has no extension) returns CFREE_LANG_UNKNOWN rather + * than defaulting to any particular language. */ CFREE_API CfreeLanguage cfree_language_for_path(CfreeCompiler*, const char* path); CFREE_API CfreeStatus cfree_register_frontend(CfreeCompiler*, CfreeLanguage, diff --git a/lang/c/c.c b/lang/c/c.c @@ -136,11 +136,19 @@ static void c_frontend_free(CfreeFrontendState* frontend) { h->free(h, fe, sizeof(*fe)); } -/* C claims no extensions — the language-for-path lookup falls through - * to CFREE_LANG_C as the default when nothing else matches, so listing - * `c`/`h` here would only duplicate that fallback. */ +/* C is just another frontend: it claims its source/header extensions so the + * language-for-path lookup resolves them by the same registry walk as every + * other language, with no special fallback. */ +static const CfreeSlice c_extensions[] = {CFREE_SLICE_LIT("c"), + CFREE_SLICE_LIT("h")}; + const CfreeFrontendVTable cfree_c_frontend_vtable = { - c_frontend_new, c_frontend_compile, c_frontend_free, NULL, 0, + c_frontend_new, + c_frontend_compile, + c_frontend_free, + c_extensions, + (uint32_t)(sizeof c_extensions / sizeof c_extensions[0]), /* commit/abort: C has no durable cross-compile state yet */ - NULL, NULL, + NULL, + NULL, }; diff --git a/src/api/compile.c b/src/api/compile.c @@ -87,11 +87,11 @@ CfreeLanguage cfree_language_for_path(CfreeCompiler* c, const char* path) { int have_ext = 0; unsigned lang; - if (!c || !path) return CFREE_LANG_C; + if (!c || !path) return CFREE_LANG_UNKNOWN; for (len = 0; path[len]; ++len) { } /* Strip back to the last `.` after the final `/`. No dot → no - * extension → fall through to the C default. */ + * extension → no language claims it. */ i = len; while (i > 0) { --i; @@ -103,7 +103,7 @@ CfreeLanguage cfree_language_for_path(CfreeCompiler* c, const char* path) { break; } } - if (!have_ext) return CFREE_LANG_C; + if (!have_ext) return CFREE_LANG_UNKNOWN; for (lang = 0; lang < CFREE_LANG_COUNT; ++lang) { const CfreeFrontendVTable* v = c->frontends[lang]; @@ -113,7 +113,9 @@ CfreeLanguage cfree_language_for_path(CfreeCompiler* c, const char* path) { if (ext_eq_ci(ext, v->extensions[e])) return (CfreeLanguage)lang; } } - return CFREE_LANG_C; + /* No registered frontend claims this extension. C is not special — it does + * not absorb unrecognized inputs. */ + return CFREE_LANG_UNKNOWN; } CfreeStatus cfree_register_frontend(CfreeCompiler* c, CfreeLanguage lang,