Most files suffered major changes

- Less ambiguous syntax
- Better parser (Chumsky only does tokenization now)
- Tidy(|ier) error handling
- Facade for simplified embedding
- External code grouped in (fairly) self-contained Systems
- Dynamic action dispatch
- Many STL additions
This commit is contained in:
2023-08-17 20:47:08 +01:00
parent 751a02a1ec
commit 3fdabc29da
139 changed files with 4269 additions and 1783 deletions

View File

@@ -1,13 +1,14 @@
use std::fmt::Display;
use std::rc::Rc;
use super::location::Location;
use super::{ast, postmacro};
use crate::utils::Substack;
use crate::Sym;
use crate::error::{ErrorPosition, ProjectError};
use crate::utils::iter::box_once;
use crate::utils::{BoxedIter, Substack};
use crate::{Interner, Sym};
#[derive(Clone)]
pub enum Error {
#[derive(Debug, Clone)]
pub enum ErrorKind {
/// `()` as a clause is meaningless in lambda calculus
EmptyS,
/// Only `(...)` may be converted to typed lambdas. `[...]` and `{...}`
@@ -16,29 +17,44 @@ pub enum Error {
/// Placeholders shouldn't even occur in the code during macro
/// execution. Something is clearly terribly wrong
Placeholder,
/// Arguments can only be [ast::Clause::Name]
/// Arguments can only be a single [ast::Clause::Name]
InvalidArg,
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::EmptyS => {
write!(f, "`()` as a clause is meaningless in lambda calculus")
},
Error::BadGroup(_) => write!(
f,
"Only `(...)` may be converted to typed lambdas. `[...]` and \
`{{...}}` left in the code are signs of incomplete macro execution"
),
Error::Placeholder => write!(
f,
#[derive(Debug, Clone)]
pub struct Error {
pub location: Location,
pub kind: ErrorKind,
}
impl Error {
pub fn new(kind: ErrorKind, location: &Location) -> Self {
Self { location: location.clone(), kind }
}
}
impl ProjectError for Error {
fn description(&self) -> &str {
match self.kind {
ErrorKind::BadGroup(_) =>
"Only `(...)` may be converted to typed lambdas. `[...]` and `{{...}}` \
left in the code are signs of incomplete macro execution",
ErrorKind::EmptyS => "`()` as a clause is meaningless in lambda calculus",
ErrorKind::InvalidArg => "Argument names can only be Name nodes",
ErrorKind::Placeholder =>
"Placeholders shouldn't even appear in the code during macro \
execution, this is likely a compiler bug"
),
Error::InvalidArg => write!(f, "Arguments can only be Name nodes"),
execution,this is likely a compiler bug",
}
}
fn message(&self, _i: &Interner) -> String {
match self.kind {
ErrorKind::BadGroup(char) => format!("{} block found in the code", char),
_ => self.description().to_string(),
}
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.location.clone(), message: None })
}
}
/// Try to convert an expression from AST format to typed lambda
@@ -66,14 +82,16 @@ impl<'a> Context<'a> {
/// Process an expression sequence
fn exprv_rec<'a>(
location: &'a Location,
v: &'a [ast::Expr<Sym>],
ctx: Context<'a>,
) -> Result<postmacro::Expr, Error> {
let (last, rest) = v.split_last().ok_or(Error::EmptyS)?;
let (last, rest) =
(v.split_last()).ok_or_else(|| Error::new(ErrorKind::EmptyS, location))?;
if rest.is_empty() {
return expr_rec(&v[0], ctx);
}
let f = exprv_rec(rest, ctx)?;
let f = exprv_rec(location, rest, ctx)?;
let x = expr_rec(last, ctx)?;
let value = postmacro::Clause::Apply(Rc::new(f), Rc::new(x));
Ok(postmacro::Expr { value, location: Location::Unknown })
@@ -86,52 +104,44 @@ fn expr_rec<'a>(
) -> Result<postmacro::Expr, Error> {
if let ast::Clause::S(paren, body) = value {
if *paren != '(' {
return Err(Error::BadGroup(*paren));
return Err(Error::new(ErrorKind::BadGroup(*paren), location));
}
let expr = exprv_rec(body.as_ref(), ctx)?;
let expr = exprv_rec(location, body.as_ref(), ctx)?;
Ok(postmacro::Expr { value: expr.value, location: location.clone() })
} else {
let value = clause_rec(value, ctx)?;
let value = match value {
ast::Clause::P(p) => postmacro::Clause::P(p.clone()),
ast::Clause::Lambda(arg, b) => {
let name = match &arg[..] {
[ast::Expr { value: ast::Clause::Name(name), .. }] => name,
[ast::Expr { value: ast::Clause::Placeh { .. }, .. }] =>
return Err(Error::new(ErrorKind::Placeholder, location)),
_ => return Err(Error::new(ErrorKind::InvalidArg, location)),
};
let body_ctx = ctx.w_name(*name);
let body = exprv_rec(location, b.as_ref(), body_ctx)?;
postmacro::Clause::Lambda(Rc::new(body))
},
ast::Clause::Name(name) => {
let lvl_opt = (ctx.names.iter())
.enumerate()
.find(|(_, n)| *n == name)
.map(|(lvl, _)| lvl);
match lvl_opt {
Some(lvl) => postmacro::Clause::LambdaArg(lvl),
None => postmacro::Clause::Constant(*name),
}
},
ast::Clause::S(paren, entries) => {
if *paren != '(' {
return Err(Error::new(ErrorKind::BadGroup(*paren), location));
}
let expr = exprv_rec(location, entries.as_ref(), ctx)?;
expr.value
},
ast::Clause::Placeh { .. } =>
return Err(Error::new(ErrorKind::Placeholder, location)),
};
Ok(postmacro::Expr { value, location: location.clone() })
}
}
/// Process a clause
fn clause_rec<'a>(
cls: &'a ast::Clause<Sym>,
ctx: Context<'a>,
) -> Result<postmacro::Clause, Error> {
match cls {
ast::Clause::P(p) => Ok(postmacro::Clause::P(p.clone())),
ast::Clause::Lambda(expr, b) => {
let name = match expr.value {
ast::Clause::Name(name) => name,
ast::Clause::Placeh { .. } => return Err(Error::Placeholder),
_ => return Err(Error::InvalidArg),
};
let body_ctx = ctx.w_name(name);
let body = exprv_rec(b.as_ref(), body_ctx)?;
Ok(postmacro::Clause::Lambda(Rc::new(body)))
},
ast::Clause::Name(name) => {
let lvl_opt = ctx
.names
.iter()
.enumerate()
.find(|(_, n)| *n == name)
.map(|(lvl, _)| lvl);
Ok(match lvl_opt {
Some(lvl) => postmacro::Clause::LambdaArg(lvl),
None => postmacro::Clause::Constant(*name),
})
},
ast::Clause::S(paren, entries) => {
if *paren != '(' {
return Err(Error::BadGroup(*paren));
}
let expr = exprv_rec(entries.as_ref(), ctx)?;
Ok(expr.value)
},
ast::Clause::Placeh { .. } => Err(Error::Placeholder),
}
}