(** The milestone-2 prelude, written in Flan. Printing is deliberately *not* a primitive (plan.org, Milestone-2 primitives): [write-stdout] is the one output primitive and everything above it is Flan. That is what keeps a second backend cheap — a primitive is the only thing implemented twice. Its source is [lib/prelude.fln], in the indented syntax, embedded as a string at build time (the rule in lib/dune) and read with [Indent_reader]; every program gets it prepended by [Check.build_program] whether it asked for one or not. That is a real limit and it is stated as one: the prelude cannot be extended or replaced without rebuilding the compiler. There *is* a package loader now — [lib/load.ml], and [vendor/raylib] is a package that uses it — so the obstacle is no longer the mechanism. What is missing is the decision about what a [core:] package would mean for a program that imports nothing, and until that is made this docstring does not promise one. (It used to say the prelude became a [core:] package "at milestone 3". Milestone 3 came and went; the package did not.) The acceptance programs may call anything defined here. No printing function is here at all any more. [print] and [println] are the whole printing surface, and neither is a function: both are compiler-provided and structural, a walk over each argument's concrete type at the call site (check.ml, and the walk itself in render.ml). Both are variadic with Clojure's spacing -- arguments in order, one space between each pair, [println] ending the line. That is plan.org's Milestone 5 item, and it needed none of the rest of milestone 5 -- there is nothing to dispatch on at run time and no user-supplied printer to choose between, so no type variables are involved. The earlier note here said a single [println] had to wait for generics; it did not. The per-type family that used to live here -- [print-str], [print-i64], [print-f64], [print-bytes], [print-line], [newline] -- is gone, and [print] is strictly the better call for every one of them. [print] is the same walk as [println] without the trailing newline, so it covers the no-newline case that was the family's remaining excuse (see [show] in test/programs/slices.flan). And [(print-i64 x)] forced an explicit [(i64 x)] at every site, where [(print x)] takes the value as it is. That is not only shorter: the cast through the signed printer turned a [u64] above 2^63 into a negative number, where [print] routes it through [flan_u64_to_bytes] and prints what it actually holds. Implicit widening would have removed the cast at a [u8] or an [i32] site on its own, but not at that one -- a [u64] widens into nothing at all, and the printer it was being forced through was the wrong one. *) let source = Prelude_src.source let file = "" (* The bootstrap hook, and the whole of why a prelude function may now call a prelude macro. [Check.program] prepends this file to every program, and a macro module is built by running [Check.program] over the prelude — so a prelude function that calls a macro cannot be compiled *into the very module that would expand it*. That is a cycle, not an ordering mistake, and it is broken by making the prelude smaller for exactly the one build that cannot afford it: while a macro module is being built, [Macro] installs a reduction here that drops every [defn] depending, directly or transitively, on a macro. A *macro* that lands in that set is refused by name — see [Macro.reduce]. A ref rather than a parameter because the reader is [Check.program], which cannot be told, and because [Macro] sits above it and cannot be depended on from here. *) let bootstrap : (Form.t list -> Form.t list) ref = ref (fun fs -> fs) (* The prelude's forms as written, before [bootstrap]. The file keeps the name [] rather than [prelude.fln]: nothing can open it, and a diagnostic about a prelude form spells its code as the paren syntax does, which is what [Source.indented_at] gives for a name that is not [.fln]. Read once per process: the indented reader over the whole prelude is milliseconds, several readers want it per build, and a form is immutable. *) let parsed = lazy (Indent_reader.read_all ~file source) let read () = Lazy.force parsed let forms () = !bootstrap (read ())