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 ───────────────────────────────────────── *)