Skip to main content

frazaro_core/
kernel.rs

1//! The kernel's boundary, and the six seams through which everything else
2//! enters (`KERNEL.1`, 2026-10-06; the design is `web/CALLOSUM.md` ยง14).
3//!
4//! The kernel holds mechanism only: forms and their expansion, the emitters,
5//! the sheet model, recalculation over a declared subset once it lands, the
6//! relation set, and the ABI. It never holds English, message text, a
7//! default, chrome, a format beyond a trait, or a door. Each of those is
8//! data the kernel reads (the tables under `vla-lang/data/` and
9//! `core/data/`, a phrasebook, a library) or an implementation of one of the
10//! seams below. The rule that follows: the kernel grows a seam, never a
11//! feature.
12//!
13//! Since `PORT.12` the kernel is two crates in one dependency order: the
14//! language, `vla-lang`, and this crate, the bridges, which consumes it.
15//! `tools/check_kernel_boundary.ps1` pins the rule over both source trees.
16//! This module writes the seams down as data, so that a test can hold their
17//! counts and `frazaro describe` (`KERNEL.18`) can print them, and names the
18//! two traits: [`Engine`], whose first implementations `PORT.9` lands, and
19//! [`Projection`], the language's (`vla_lang::projection`, re-exported here
20//! with its [`Window`]), whose first implementation is the grid of
21//! `crate::view` (`KERNEL.4`) and whose second the values, the grid less its
22//! formula rows (`KERNEL.37`).
23
24use crate::messages::Refusal;
25
26pub use vla_lang::projection::{Projection, Window};
27
28/// One of the five entrances into the kernel.
29#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30pub struct Seam {
31    /// The seam's name, as `CONTRIBUTING.md` heads its row.
32    pub name: &'static str,
33    /// What a contribution through it is: a data file, or an implementation
34    /// of a trait in this crate or the language's.
35    pub entrance: &'static str,
36    /// The oracle a change through it must pass before it lands.
37    pub oracle: &'static str,
38    /// The implementations the kernel ships today, by name; empty where the
39    /// first is deferred to the roadmap item in `deferred_to`.
40    pub implementations: &'static [&'static str],
41    /// The roadmap item that lands the first implementation, or empty.
42    pub deferred_to: &'static str,
43}
44
45/// The six seams, in the order `CONTRIBUTING.md` lists them.
46pub const SEAMS: [Seam; 6] = [
47    Seam {
48        name: "sentences",
49        entrance: "a phrasebook file (.vla) with its test: proofs, loaded through api::vocab_gate",
50        oracle: "frazaro prove passes every proof; the translate and refusal goldens hold",
51        implementations: &["the phrasebooks a door loads, english.vla first"],
52        deferred_to: "",
53    },
54    Seam {
55        name: "paragraphs",
56        entrance: "a library of sentences with named slots, imported by a Use sentence",
57        oracle: "the build golden, and the stamp hashing every file used",
58        implementations: &[],
59        deferred_to: "G-USE",
60    },
61    Seam {
62        name: "engines",
63        entrance: "an implementation of kernel::Engine under a head-table symbol",
64        oracle: "a proof file of its kind, with clingo beside it where it applies",
65        implementations: &[],
66        deferred_to: "PORT.9",
67    },
68    Seam {
69        name: "formats and hosts",
70        entrance: "an implementation of reflect::Source to read, a writer beside it, and a kernel::HostProfile per door",
71        oracle: "the reflect, diff and audit goldens for a reader; the build golden for a writer",
72        implementations: &["xlsx (OOXML), read and written", "ods (OpenDocument), read"],
73        deferred_to: "",
74    },
75    Seam {
76        name: "projections",
77        entrance: "an implementation of vla_lang::projection::Projection",
78        oracle: "a view golden per projection",
79        implementations: &[
80            "grid, the view record of one window (view::Grid)",
81            "values, the view record less its formula rows (view::ValueGrid)",
82        ],
83        deferred_to: "",
84    },
85    Seam {
86        name: "functions",
87        entrance: "a function registered into vla_lang::calc::Library, with the fixture a host saved that shows it (excel::library)",
88        oracle: "the recalc golden: the fixture's cached values agree (check_recalc_golden.ps1)",
89        implementations: &[
90            "the language's fourteen day-one functions (vla_lang::calc::library)",
91            "Excel's library, the declared subset by measurement (excel::SUBSET)",
92        ],
93        deferred_to: "",
94    },
95];
96
97/// The seams, as data.
98pub fn seams() -> &'static [Seam] {
99    &SEAMS
100}
101
102/// A file format the kernel reads, writes, or both.
103#[derive(Clone, Copy, Debug, PartialEq, Eq)]
104pub struct Format {
105    pub name: &'static str,
106    pub extension: &'static str,
107    pub reads: bool,
108    pub writes: bool,
109}
110
111/// The formats seam's registry. `reflect::open` picks the reader by the
112/// bytes it is handed; the one writer is `sheet::ooxml::workbook_bytes`.
113pub const FORMATS: [Format; 2] = [
114    Format {
115        name: "OOXML",
116        extension: "xlsx",
117        reads: true,
118        writes: true,
119    },
120    Format {
121        name: "OpenDocument",
122        extension: "ods",
123        reads: true,
124        writes: false,
125    },
126];
127
128/// An engine: tables in, a table out, under a symbol of the head table,
129/// held by proof files of one kind. The relation type is the implementor's,
130/// since the port of `VLA_Relation` (`PORT.9`) defines it; the first
131/// implementations are SQL, DATALOG, PROLOG and OPTIMIZE, and a fifth proves
132/// the seam takes additions.
133pub trait Engine {
134    /// The relation an engine takes and gives.
135    type Table;
136    /// The head-table symbol the engine answers to, such as `datalog`.
137    fn head(&self) -> &str;
138    /// The proof-file kind that holds it, such as `test-datalog`.
139    fn proof_kind(&self) -> &str;
140    /// The tables in, the table out, or a refusal from the catalogue.
141    fn run(&self, tables: &[Self::Table]) -> Result<Self::Table, Refusal>;
142}
143
144/// A door's host profile: the statement heads it carries out with nothing
145/// running, and the catalogue id that refuses the rest by name.
146#[derive(Clone, Copy, Debug, PartialEq, Eq)]
147pub struct HostProfile {
148    pub door: &'static str,
149    /// The core forms, by head-table symbol, the door holds.
150    pub forms: &'static [&'static str],
151    /// The scaffold's own calls the door steps over without effect.
152    pub scaffold: &'static [&'static str],
153    /// The catalogue id of the refusal for a sentence outside the profile.
154    pub refuses_with: &'static str,
155}
156
157/// The file door: `frazaro build`, and the page's Workbook strip. What the
158/// walker in `build.rs` holds, read off its arms: a value or a formula into
159/// a range, `Work on sheet` and `Go to sheet`, a condition that folds, and
160/// a call of one of the program's own steps with no values, inlined. The
161/// rest is refused with the sentence quoted.
162pub const FILE_DOOR: HostProfile = HostProfile {
163    door: "file",
164    forms: &[
165        "at-line",
166        "begin",
167        "dim",
168        "on-error",
169        "label",
170        "exit-sub",
171        "exit-function",
172        "set!",
173        "if",
174        ".",
175    ],
176    scaffold: &["vla-report-error", "vlaensuresheet"],
177    refuses_with: "build-not-representable",
178};
179
180/// The host profiles the kernel knows. The Excel add-in needs none, since
181/// it holds every form; the page's viewport and the task pane add theirs
182/// with `KERNEL.19`.
183pub const HOST_PROFILES: [HostProfile; 1] = [FILE_DOOR];
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188    use crate::headtable;
189    use crate::reflect;
190
191    const FIXTURE_XLSX: &[u8] = include_bytes!("../../scripts/reflect/fixture.xlsx");
192    const FIXTURE_ODS: &[u8] = include_bytes!("../../scripts/reflect/opendocument.ods");
193
194    #[test]
195    fn six_seams_each_with_an_entrance_and_an_oracle() {
196        assert_eq!(seams().len(), 6);
197        let names: Vec<&str> = seams().iter().map(|s| s.name).collect();
198        assert_eq!(
199            names,
200            [
201                "sentences",
202                "paragraphs",
203                "engines",
204                "formats and hosts",
205                "projections",
206                "functions"
207            ]
208        );
209        for s in seams() {
210            assert!(!s.entrance.is_empty(), "{} names no entrance", s.name);
211            assert!(!s.oracle.is_empty(), "{} names no oracle", s.name);
212            assert!(
213                !s.implementations.is_empty() || !s.deferred_to.is_empty(),
214                "{} has neither an implementation nor the item that lands one",
215                s.name
216            );
217        }
218    }
219
220    #[test]
221    fn the_engines_seam_is_empty_today_and_the_projections_seam_has_its_grid_and_values() {
222        let engines = seams().iter().find(|s| s.name == "engines").unwrap();
223        assert!(engines.implementations.is_empty());
224        assert_eq!(engines.deferred_to, "PORT.9");
225        let projections = seams().iter().find(|s| s.name == "projections").unwrap();
226        assert_eq!(projections.implementations.len(), 2);
227        assert_eq!(projections.deferred_to, "");
228        assert_eq!(crate::view::Grid::default().name(), "grid");
229        assert!(projections.implementations[0].starts_with("grid"));
230        assert_eq!(crate::view::ValueGrid::default().name(), "values");
231        assert!(projections.implementations[1].starts_with("values"));
232        let paragraphs = seams().iter().find(|s| s.name == "paragraphs").unwrap();
233        assert_eq!(paragraphs.deferred_to, "G-USE");
234    }
235
236    #[test]
237    fn the_formats_registry_matches_the_readers_and_the_writer() {
238        assert_eq!(FORMATS.len(), 2);
239        assert!(reflect::open(FIXTURE_XLSX, "fixture.xlsx").is_ok());
240        assert!(reflect::open(FIXTURE_ODS, "opendocument.ods").is_ok());
241        let readers: Vec<&str> = FORMATS
242            .iter()
243            .filter(|f| f.reads)
244            .map(|f| f.extension)
245            .collect();
246        assert_eq!(readers, ["xlsx", "ods"]);
247        let writers: Vec<&str> = FORMATS
248            .iter()
249            .filter(|f| f.writes)
250            .map(|f| f.extension)
251            .collect();
252        assert_eq!(writers, ["xlsx"], "the one writer is the OOXML writer");
253        let formats = seams()
254            .iter()
255            .find(|s| s.name == "formats and hosts")
256            .unwrap();
257        assert_eq!(formats.implementations.len(), FORMATS.len());
258    }
259
260    #[test]
261    fn the_file_door_holds_only_core_forms_and_refuses_from_the_catalogue() {
262        assert_eq!(HOST_PROFILES.len(), 1);
263        assert_eq!(FILE_DOOR.door, "file");
264        for sym in FILE_DOOR.forms {
265            let canonical = headtable::resolve_head_alias(sym);
266            assert!(
267                headtable::row(&canonical).is_some(),
268                "{sym} is not a form of the head table"
269            );
270        }
271        for call in FILE_DOOR.scaffold {
272            assert!(
273                headtable::row(call).is_none(),
274                "{call} is a scaffold call, not a core form"
275            );
276        }
277        assert_eq!(FILE_DOOR.refuses_with, "build-not-representable");
278    }
279
280    #[test]
281    fn the_projection_trait_is_the_language_s() {
282        // A window made through the core's path is the language's type.
283        let w: vla_lang::projection::Window = Window {
284            sheet: "Model".to_string(),
285            range: crate::sheet::parse_a1_range("A1:F20").unwrap(),
286        };
287        assert_eq!(w.range.cells(), 120);
288    }
289}