beck_core/
lib.rs

1//! Types, `Core`, placement and splitting — stages 4 through 8 of §4.1's pipeline.
2//!
3//! ```text
4//!  4 Resolve    modules, imports, name binding, hygiene scopes
5//!  5 Typecheck  HM → typed AST
6//!  6 Lower      desugar to CORE
7//!  7 PLACE      ◀── the product.  every Core node carries a tier
8//!  8 Split      partition Core per tier; SYNTHESISE boundaries
9//! ```
10//!
11//! Stages 4–6 are one pass ([`check`]) because §4.2 permits three IRs and no more: a separate
12//! resolved-but-untyped tree would be a fourth. Stages 7 and 8 are [`place`] and [`split`], which
13//! §4.1 calls out as "novel … where the engineering budget goes".
14
15pub mod backend;
16pub mod bundle;
17pub mod check;
18pub mod clock;
19pub mod command;
20pub mod compat;
21pub mod core;
22pub mod cost;
23pub mod delta;
24pub mod diff;
25pub mod docgen;
26pub mod edge;
27pub mod editor;
28pub mod engine;
29pub mod fields;
30pub mod frames;
31pub mod fuse;
32pub mod gen;
33pub mod graph;
34pub mod host;
35pub mod html;
36pub mod iface;
37pub mod incremental;
38pub mod liveness;
39pub mod net;
40pub mod pg;
41pub mod place;
42pub mod plan;
43pub mod pmap;
44pub mod prelude;
45pub mod project;
46pub mod query;
47pub mod read;
48pub mod relate;
49pub mod render;
50pub mod repr;
51pub mod row;
52pub mod secure;
53pub mod seq;
54pub mod signal;
55pub mod split;
56pub mod stdlib;
57pub mod style;
58pub mod testing;
59pub mod ty;
60
61pub use backend::{Backend, Callable, ExecError};
62/// The standard library's digests, encodings and identifiers.
63///
64/// Re-exported rather than defined here: they are the runtime library's, so that a compiled
65/// program and the tree-walker call one implementation of them (`docs/93` §93.12). The name is also a
66/// *function* in this crate — `beck_core::digest(&state)`, the state hash — and a module and a
67/// function may share one.
68pub use beck_prim::digest;
69pub use bundle::Bundle;
70pub use check::{check_module, check_module_importing, Def, Program, SignalDecl};
71pub use compat::{compare, is_breaking, Change};
72pub use core::{digest, Const, Core, CoreKind, Env, Prim, Value, VarId};
73pub use diff::{diff, Op as DiffOp, Path as DiffPath};
74pub use graph::{DepGraph, EdgeKind, GraphBuilder, GraphNode, NodeId, NodeKind};
75pub use html::Html;
76pub use iface::Interface;
77pub use place::{Key, Lock, Method, Solution};
78pub use pmap::PMap;
79pub use project::{compile_project, Sources};
80pub use row::{Ambient, Effect, Row};
81pub use secure::{sendable, storable, NotSendable};
82pub use signal::{Cut, Graph as SignalGraph, Op as SignalOp, SigId};
83pub use split::{Placed, Roles, StateRole};
84pub use testing::{Clause, Expectation, TestDef};
85pub use ty::{Tier, Ty, TyDecl};
86
87use beck_diag::{Diagnostics, FileId, SourceMap};
88
89/// The whole front end: parse, expand, check, place, split.
90///
91/// One function, so `beck check`, `beck run`, `beck build` and the test harnesses cannot drift
92/// apart — §4.6's "one binary serves `beck build`, `beck check`, `beck lsp` and `beck explain`;
93/// there is no separate language server implementation to drift."
94pub fn compile(file: FileId, name: &str, src: &str, diags: &mut Diagnostics) -> Option<Placed> {
95    compile_with(file, name, src, None, diags)
96}
97
98/// The same, against a previously solved placement — §3.4's stability guardrail.
99pub fn compile_with(
100    file: FileId,
101    name: &str,
102    src: &str,
103    lock: Option<&Lock>,
104    diags: &mut Diagnostics,
105) -> Option<Placed> {
106    let parsed = beck_syntax::parse_file(file, name, src, diags);
107    let expanded = beck_macro::expand_module(&parsed, diags);
108    let mut program = check_module(&expanded, diags);
109    // Stage 7: solve first, then verify. Verification runs over the *solved* tiers as well as the
110    // written ones, so an annotation and an inference are held to one standard.
111    let solution = place::solve(&program, lock);
112    place::apply(&mut program, &solution);
113    place::check_placement(&program, diags);
114    secure::check_security(&program, diags);
115    // After placement, because it reads the whole resolved program: a class written in one
116    // definition and used in another is one name, and §104.4's did-you-mean is over the table
117    // rather than over the file.
118    style::check_classes(&program, diags);
119    if diags.has_errors() {
120        return None;
121    }
122    // Three facts about the finished program, computed once for every backend rather than by one
123    // of them: which read of a local is its last (`liveness`), how many bindings each body makes,
124    // so a call can reserve them in one frame (`frames`), and where a record literal's fields go,
125    // so building one places them rather than sorting them (`fields`).
126    liveness::mark_program(&mut program);
127    frames::reserve_program(&mut program);
128    fields::order_program(&mut program);
129    let mut placed = split::split(program, diags)?;
130    placed.placement = solution;
131    Some(placed)
132}
133
134/// Parse, expand and check one source string, stopping before placement.
135///
136/// The shape a test that is interested in *inference* wants: a program whose rows are known even
137/// when its placement is the thing under test.
138pub fn check_str(name: &str, src: &str) -> (check::Program, Diagnostics, SourceMap) {
139    let mut map = SourceMap::new();
140    let file = map.add(name, src);
141    let mut diags = Diagnostics::new();
142    let parsed = beck_syntax::parse_file(file, name, src, &mut diags);
143    let expanded = beck_macro::expand_module(&parsed, &mut diags);
144    let program = check_module(&expanded, &mut diags);
145    (program, diags, map)
146}
147
148/// The same, admitting a **library**: a module with no merge point comes back as a
149/// [`split::Placed`] whose roles are placeholders rather than as `None`.
150///
151/// Separate from [`compile_str`] rather than replacing it, because "this compiled" and "this is an
152/// application" are different questions and every existing caller is asking the second. `beck test`
153/// asks the first (docs/27 §27.2); so does anything that only wants the module's definitions.
154pub fn compile_or_library_str(name: &str, src: &str) -> (Option<Placed>, Diagnostics, SourceMap) {
155    let mut map = SourceMap::new();
156    let file = map.add(name, src);
157    let mut diags = Diagnostics::new();
158    let parsed = beck_syntax::parse_file(file, name, src, &mut diags);
159    let expanded = beck_macro::expand_module(&parsed, &mut diags);
160    let mut program = check_module(&expanded, &mut diags);
161    let solution = place::solve(&program, None);
162    place::apply(&mut program, &solution);
163    place::check_placement(&program, &mut diags);
164    secure::check_security(&program, &mut diags);
165    if diags.has_errors() {
166        return (None, diags, map);
167    }
168    liveness::mark_program(&mut program);
169    frames::reserve_program(&mut program);
170    fields::order_program(&mut program);
171    let interface = iface::Interface::of(&program);
172    let placed = project::slice_or_library(
173        project::Project {
174            program,
175            solution,
176            interface,
177            // A single source string imports nothing, so there is nothing that could be missing.
178            unimplemented: Vec::new(),
179        },
180        &mut diags,
181    );
182    (placed, diags, map)
183}
184
185/// Compile one source string against a fresh source map. The shape every test uses.
186pub fn compile_str(name: &str, src: &str) -> (Option<Placed>, Diagnostics, SourceMap) {
187    let mut map = SourceMap::new();
188    let file = map.add(name, src);
189    let mut diags = Diagnostics::new();
190    let placed = compile(file, name, src, &mut diags);
191    (placed, diags, map)
192}