Skip to main content

nifty_check/
lib.rs

1//! Semantic checking for Nifty: resolves names, checks types and element arguments, and
2//! records which state every dynamic part of the UI reads.
3
4mod checker;
5pub mod elements;
6pub mod functions;
7pub mod hir;
8pub mod methods;
9
10use nifty_syntax::ast::File;
11use nifty_syntax::{Diagnostic, Parsed};
12
13/// The checked program. Run it only when `diagnostics` is empty: parts with errors are
14/// left out or marked with `Type::Error`.
15#[derive(Debug)]
16pub struct Checked {
17    pub program: hir::Program,
18    pub diagnostics: Vec<Diagnostic>,
19}
20
21/// Checks `files` as one program. They share one namespace, so a name declared in any of
22/// them can be used in all of them. The first is the entry file.
23#[must_use]
24pub fn check(files: &[&File]) -> Checked {
25    let (program, diagnostics) = checker::check(files);
26    Checked {
27        program,
28        diagnostics,
29    }
30}
31
32/// Checks a program's parsed files and returns the program; the first is the entry file.
33///
34/// # Errors
35///
36/// Returns the syntax errors if any file didn't parse, and otherwise the type errors.
37/// Type checking only runs on files that parse, so one syntax error doesn't cause a
38/// cascade of confusing type errors.
39pub fn compile(parsed: &[&Parsed]) -> Result<hir::Program, Vec<Diagnostic>> {
40    let syntax_errors: Vec<Diagnostic> = parsed
41        .iter()
42        .flat_map(|file| file.diagnostics.iter().cloned())
43        .collect();
44    if !syntax_errors.is_empty() {
45        return Err(syntax_errors);
46    }
47    let files: Vec<&File> = parsed.iter().map(|file| &file.file).collect();
48    let checked = check(&files);
49    if checked.diagnostics.is_empty() {
50        Ok(checked.program)
51    } else {
52        Err(checked.diagnostics)
53    }
54}
55
56#[cfg(test)]
57mod tests;