Almost Alpha

Massive improvements across the board.

One day I'll adopt incremental commits.
This commit is contained in:
2024-02-23 23:59:24 +00:00
parent c279301583
commit ed0d64d52e
147 changed files with 4121 additions and 4203 deletions

94
src/facade/unbound_ref.rs Normal file
View File

@@ -0,0 +1,94 @@
//! Referencing a constant that doesn't exist is a runtime error in Orchid, even
//! though this common error condition is usually caused by faulty macro
//! execution. This module constains functions to detect and raise these errors
//! eagerly.
use std::fmt;
use hashbrown::HashSet;
use trait_set::trait_set;
use crate::error::{ProjectError, Reporter};
use crate::interpreter::nort::{Clause, Expr};
use crate::location::{CodeGenInfo, CodeLocation};
use crate::name::Sym;
use crate::sym;
/// Start with a symbol
pub fn unbound_refs_sym<E: SubError>(
symbol: Sym,
location: CodeLocation,
visited: &mut HashSet<Sym>,
load: &mut impl FnMut(Sym, CodeLocation) -> Result<Expr, E>,
reporter: &Reporter,
) {
if visited.insert(symbol.clone()) {
match load(symbol.clone(), location.clone()) {
Err(error) => reporter.report(MissingSymbol { symbol, location, error }.pack()),
Ok(expr) => unbound_refs_expr(expr, visited, load, reporter),
}
}
}
/// Find all unbound constant names in a snippet. This is mostly useful to
/// detect macro errors.
pub fn unbound_refs_expr<E: SubError>(
expr: Expr,
visited: &mut HashSet<Sym>,
load: &mut impl FnMut(Sym, CodeLocation) -> Result<Expr, E>,
reporter: &Reporter,
) {
expr.search_all(&mut |s: &Expr| {
if let Clause::Constant(symbol) = &*s.cls_mut() {
unbound_refs_sym(symbol.clone(), s.location(), visited, load, reporter)
}
None::<()>
});
}
/// Assert that the code contains no invalid references that reference missing
/// symbols. [Clause::Constant]s can be created procedurally, so this isn't a
/// total guarantee, more of a convenience.
pub fn validate_refs<E: SubError>(
all_syms: HashSet<Sym>,
reporter: &Reporter,
load: &mut impl FnMut(Sym, CodeLocation) -> Result<Expr, E>,
) -> HashSet<Sym> {
let mut visited = HashSet::new();
for sym in all_syms {
let location = CodeLocation::new_gen(CodeGenInfo::no_details(sym!(orchidlang::validate_refs)));
unbound_refs_sym(sym, location, &mut visited, load, reporter);
}
visited
}
trait_set! {
/// Any error the reference walker can package into a [MissingSymbol]
pub trait SubError = fmt::Display + Clone + Send + Sync + 'static;
}
/// Information about a reproject failure
#[derive(Clone)]
pub struct MissingSymbol<E: SubError> {
/// The error returned by the loader function. This is usually a ismple "not
/// found", but with unusual setups it might provide some useful info.
pub error: E,
/// Location of the first reference to the missing symbol.
pub location: CodeLocation,
/// The symbol in question
pub symbol: Sym,
}
impl<E: SubError> ProjectError for MissingSymbol<E> {
const DESCRIPTION: &'static str = "A name not referring to a known symbol was found in the source after \
macro execution. This can either mean that a symbol name was mistyped, or \
that macro execution didn't correctly halt.";
fn message(&self) -> String { format!("{}: {}", self.symbol, self.error) }
fn one_position(&self) -> crate::location::CodeOrigin { self.location.origin() }
}
// struct MissingSymbols {
// errors: Vec<ErrorPosition>,
// }
// impl ProjectError for MissingSymbols {
// fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> {
// self.errors.iter().cloned() } }