From a34b63af5d1d0bb45c9245e8c409d418c600f232 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sat, 12 Sep 2026 20:41:32 +0700 Subject: [PATCH] A prelude type is a name, not a list head The last commit put the prelude's types into the set the parser uses to tell a return type from the first form of a body, and put them in plainly. That set is read by two arms: a bare symbol, and a list head. The list-head arm is why (defn f [] (Some 1) (bar)) does not lose its body, and the comment above it has warned about this since it was written -- so adding Rune plainly made (defn f [] (Rune {.code 65}) (bar)) a function returning a Rune with a one-form body, silently, in every file in the language. Confirmed before fixing: it failed with "a map type is {K V}", which is the misparse arriving a step later wearing someone else's error. The enums already solve this one comment up, under their own key, for the same reason. The prelude's types go in the same way. No prelude type takes arguments, so a bare symbol is the only type position any of them can occupy. Both halves are pinned in test_flan.ml's return-type section: Form is a return type, and a prelude struct literal opening a body is not. --- lib/parse.ml | 24 ++++++++++++++++++++++-- test/test_flan.ml | 15 +++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/lib/parse.ml b/lib/parse.ml index 7208e34..1fc6dfd 100644 --- a/lib/parse.ml +++ b/lib/parse.ml @@ -821,7 +821,8 @@ and qualified_type types s = and is_type_form types (f : Form.t) = match f.v with | Sym s -> - Names.mem s types || Names.mem ("enum " ^ s) types || qualified_type types s + Names.mem s types || Names.mem ("enum " ^ s) types + || Names.mem ("prelude " ^ s) types || qualified_type types s | Vec _ -> true (* [T] and [n T] are only types *) | Map _ -> true (* {K V} in this position *) | List ({ v = Sym n; _ } :: _) -> @@ -875,9 +876,28 @@ let types_in (base : Names.t) (forms : Form.t list) : Names.t = because a Vec in that position is a type whatever is in it, which is exactly the kind of half-working that hides this. + They go in under their own key, for the reason the enums do one comment up. + Added plainly, [is_type_form]'s list-head arm would read [(Rune {.code 65})] + as a type application and eat it as a return type — silently, in every file + in the language, which is the exact failure the comment above + [is_type_form] warns about. No prelude type takes arguments, so a bare + symbol is the only type position any of them can occupy, and the bare-symbol + arm is the only one that asks. + Read once: the prelude is a constant string and this set is a constant of it. *) -let prelude_types = lazy (types_in builtin_types (Prelude.forms ())) +let prelude_types = + lazy + (List.fold_left + (fun acc (f : Form.t) -> + match f.v with + | Form.List [ { v = Form.Sym ("defstruct" | "defunion" | "defalias"); _ }; + { v = Form.Sym n; _ }; _ ] -> + Names.add ("prelude " ^ n) acc + | Form.List [ { v = Form.Sym "defenum"; _ }; { v = Form.Sym n; _ }; _ ] -> + Names.add ("enum " ^ n) acc + | _ -> acc) + builtin_types (Prelude.forms ())) let declared_types (forms : Form.t list) : Names.t = types_in (Lazy.force prelude_types) forms diff --git a/test/test_flan.ml b/test/test_flan.ml index 7767c51..991a142 100644 --- a/test/test_flan.ml +++ b/test/test_flan.ml @@ -484,6 +484,21 @@ let () = check "unknown capitalised head is a body form" (ret_and_body "unknown" "(defn f [] (Nope 1) (bar))" = (false, 2)); + (* The prelude's types are every file's types -- Check.program prepends the + prelude to every program -- and until macros needed it, nothing told the + parser so. A macro is (defn m [args [Form]] Form ...) and bare Form in + return position was read as the first form of the body. *) + check "a prelude type is a return type" + (ret_and_body "prelude" "(defn f [] Form (g))" = (true, 1)); + + (* And the other half, which is the whole reason those names go in under + their own key: a prelude type is a bare symbol in type position and never + a list head, so a struct literal of one opening a body stays a body form. + Added plainly this reads as a type application and eats the body, in every + file in the language, and nothing would have said so. *) + check "a prelude struct literal is NOT a return type" + (ret_and_body "preludelit" "(defn f [] (Rune {.code 65}) (bar))" = (false, 2)); + () (* ── Checker: AST → typed IR ───────────────────────────────────────── *)