Skip to main content

decl_lang/
pipeline.rs

1//! The single-module pipeline — a port of the reference implementation's
2//! pipeline.ts: bind and evaluate every output of one module's
3//! declarations (the judgment the conformance runner applies), and the
4//! source-level report that front-ends and embedders consume.
5use crate::ast::{Decl, DeclBody};
6use crate::checker::check_module;
7use crate::engine::{Engine, RootSrc};
8use crate::parse::parse_source;
9
10use crate::semantics::{sort_diags, Diag, Env, Scope};
11use std::rc::Rc;
12
13pub struct Pipeline {
14    pub env: Rc<Env>,
15    pub eng: Rc<Engine>,
16    pub diags: Vec<Diag>,
17}
18
19pub fn run_pipeline(decls: &[Decl]) -> Pipeline {
20    let env = Env::new();
21    env.load(decls);
22    let eng = Engine::new(env.clone());
23    let outs = env.outputs.borrow().clone();
24    for (name, ty_ast, expr) in outs {
25        let sc = Scope::new(&name, None);
26        match env.resolve(&ty_ast, None) {
27            Ok(rt) => eng.bind_root(&name, RootSrc::Expr(&expr), &rt, &sc),
28            Err(e) => env.report(Diag::error(e, name.clone(), None)),
29        }
30    }
31    eng.drive(&env);
32    let diags = sort_diags(env.diagnostics_vec()); // §6.7
33    env.diag_set(diags.clone());
34    Pipeline { env, eng, diags }
35}
36
37/// the phase that decided a source-level report
38#[derive(Clone, Copy, PartialEq, Debug)]
39pub enum Phase {
40    Parse,
41    Check,
42    Evaluate,
43}
44pub struct Report {
45    pub phase: Phase,
46    pub ok: bool,
47    pub parse_errors: Vec<(usize, usize)>,
48    /// static-checker diagnostics
49    pub checks: Vec<Diag>,
50    /// binding / evaluation / assertion diagnostics
51    pub diagnostics: Vec<Diag>,
52    /// every `output`, serialized as canonical JSON text
53    pub outputs: Vec<(String, String)>,
54    /// input roots declared by the module (not bound here)
55    pub inputs: Vec<String>,
56}
57
58/// parse, check, and evaluate one module given as source text
59pub fn evaluate_source(source: &str) -> Report {
60    let parsed = parse_source(source);
61    let inputs: Vec<String> = parsed
62        .decls
63        .iter()
64        .filter_map(|d| {
65            if let DeclBody::Input { name, .. } = &d.body {
66                Some(name.clone())
67            } else {
68                None
69            }
70        })
71        .collect();
72    if !parsed.errors.is_empty() {
73        return Report {
74            phase: Phase::Parse,
75            ok: false,
76            parse_errors: parsed.errors,
77            checks: vec![],
78            diagnostics: vec![],
79            outputs: vec![],
80            inputs,
81        };
82    }
83    let checks = check_module(&parsed.decls, None, None);
84    if checks.iter().any(|d| d.severity == "error") {
85        return Report {
86            phase: Phase::Check,
87            ok: false,
88            parse_errors: vec![],
89            checks,
90            diagnostics: vec![],
91            outputs: vec![],
92            inputs,
93        };
94    }
95    let Pipeline { env, eng, diags } = run_pipeline(&parsed.decls);
96    let ok = !diags.iter().any(|d| d.severity == "error");
97    let outputs = if ok {
98        env.outputs
99            .borrow()
100            .iter()
101            .filter_map(|(n, _, _)| {
102                env.root(n)
103                    .map(|v| (n.clone(), eng.serialize(&v, n, false)))
104            })
105            .collect()
106    } else {
107        vec![]
108    };
109    Report {
110        phase: Phase::Evaluate,
111        ok,
112        parse_errors: vec![],
113        checks,
114        diagnostics: diags,
115        outputs,
116        inputs,
117    }
118}