in midst of refactor

This commit is contained in:
2024-04-29 21:46:42 +02:00
parent ed0d64d52e
commit aa3f7e99ab
221 changed files with 5431 additions and 685 deletions

View File

@@ -0,0 +1,140 @@
//! Convert the preprocessed AST into IR
use std::collections::VecDeque;
use std::rc::Rc;
use substack::Substack;
use super::ir;
use crate::error::{ProjectError, ProjectResult};
use crate::location::{CodeOrigin, SourceRange};
use crate::name::Sym;
use crate::parse::parsed;
use crate::utils::unwrap_or::unwrap_or;
trait IRErrorKind: Clone + Send + Sync + 'static {
const DESCR: &'static str;
}
#[derive(Clone)]
struct IRError<T: IRErrorKind> {
at: SourceRange,
sym: SourceRange,
_kind: T,
}
impl<T: IRErrorKind> IRError<T> {
fn new(at: SourceRange, sym: SourceRange, _kind: T) -> Self { Self { at, sym, _kind } }
}
impl<T: IRErrorKind> ProjectError for IRError<T> {
const DESCRIPTION: &'static str = T::DESCR;
fn message(&self) -> String { format!("In {}, {}", self.sym, T::DESCR) }
fn one_position(&self) -> CodeOrigin { CodeOrigin::Source(self.at.clone()) }
}
#[derive(Clone)]
struct EmptyS;
impl IRErrorKind for EmptyS {
const DESCR: &'static str = "`()` as a clause is meaningless in lambda calculus";
}
#[derive(Clone)]
struct BadGroup;
impl IRErrorKind for BadGroup {
const DESCR: &'static str = "Only `(...)` may be used after macros. \
`[...]` and `{...}` left in the code are signs of incomplete macro execution";
}
#[derive(Clone)]
struct InvalidArg;
impl IRErrorKind for InvalidArg {
const DESCR: &'static str = "Argument names can only be Name nodes";
}
#[derive(Clone)]
struct PhLeak;
impl IRErrorKind for PhLeak {
const DESCR: &'static str = "Placeholders shouldn't even appear \
in the code during macro execution, this is likely a compiler bug";
}
/// Try to convert an expression from AST format to typed lambda
pub fn ast_to_ir(expr: parsed::Expr, symbol: SourceRange, module: Sym) -> ProjectResult<ir::Expr> {
expr_rec(expr, Context::new(symbol, module))
}
#[derive(Clone)]
struct Context<'a> {
names: Substack<'a, Sym>,
range: SourceRange,
module: Sym,
}
impl<'a> Context<'a> {
#[must_use]
fn w_name<'b>(&'b self, name: Sym) -> Context<'b>
where 'a: 'b {
Context { names: self.names.push(name), range: self.range.clone(), module: self.module.clone() }
}
}
impl Context<'static> {
#[must_use]
fn new(symbol: SourceRange, module: Sym) -> Self {
Self { names: Substack::Bottom, range: symbol, module }
}
}
/// Process an expression sequence
fn exprv_rec(
mut v: VecDeque<parsed::Expr>,
ctx: Context<'_>,
location: SourceRange,
) -> ProjectResult<ir::Expr> {
let last = unwrap_or! {v.pop_back(); {
return Err(IRError::new(location, ctx.range, EmptyS).pack());
}};
let v_end = match v.back() {
None => return expr_rec(last, ctx),
Some(penultimate) => penultimate.range.end(),
};
let f = exprv_rec(v, ctx.clone(), location.map_range(|r| r.start..v_end))?;
let x = expr_rec(last, ctx.clone())?;
let value = ir::Clause::Apply(Rc::new(f), Rc::new(x));
Ok(ir::Expr::new(value, location, ctx.module))
}
/// Process an expression
fn expr_rec(parsed::Expr { value, range }: parsed::Expr, ctx: Context) -> ProjectResult<ir::Expr> {
match value {
parsed::Clause::S(parsed::PType::Par, body) => {
return exprv_rec(body.to_vec().into(), ctx, range);
},
parsed::Clause::S(..) => return Err(IRError::new(range, ctx.range, BadGroup).pack()),
_ => (),
}
let value = match value {
parsed::Clause::Atom(a) => ir::Clause::Atom(a.clone()),
parsed::Clause::Lambda(arg, b) => {
let name = match &arg[..] {
[parsed::Expr { value: parsed::Clause::Name(name), .. }] => name,
[parsed::Expr { value: parsed::Clause::Placeh { .. }, .. }] =>
return Err(IRError::new(range.clone(), ctx.range, PhLeak).pack()),
_ => return Err(IRError::new(range.clone(), ctx.range, InvalidArg).pack()),
};
let body_ctx = ctx.w_name(name.clone());
let body = exprv_rec(b.to_vec().into(), body_ctx, range.clone())?;
ir::Clause::Lambda(Rc::new(body))
},
parsed::Clause::Name(name) => {
let lvl_opt = (ctx.names.iter()).enumerate().find(|(_, n)| **n == name).map(|(lvl, _)| lvl);
match lvl_opt {
Some(lvl) => ir::Clause::LambdaArg(lvl),
None => ir::Clause::Constant(name.clone()),
}
},
parsed::Clause::S(parsed::PType::Par, entries) =>
exprv_rec(entries.to_vec().into(), ctx.clone(), range.clone())?.value,
parsed::Clause::S(..) => return Err(IRError::new(range, ctx.range, BadGroup).pack()),
parsed::Clause::Placeh { .. } => return Err(IRError::new(range, ctx.range, PhLeak).pack()),
};
Ok(ir::Expr::new(value, range, ctx.module))
}

View File

@@ -0,0 +1,114 @@
//! IR is an abstract representation of Orchid expressions that's impractical
//! for all purposes except converting to and from other representations. Future
//! innovations in the processing and execution of code will likely operate on
//! this representation.
use std::fmt;
use std::rc::Rc;
use crate::foreign::atom::AtomGenerator;
use crate::location::{CodeLocation, SourceRange};
use crate::name::Sym;
use crate::utils::string_from_charset::string_from_charset;
/// Indicates whether either side needs to be wrapped. Syntax whose end is
/// ambiguous on that side must use parentheses, or forward the flag
#[derive(PartialEq, Eq, Clone, Copy)]
struct Wrap(bool, bool);
/// Code element with associated metadata
#[derive(Clone)]
pub struct Expr {
/// Code element
pub value: Clause,
/// Location metadata
pub location: CodeLocation,
}
impl Expr {
/// Create an IR expression
pub fn new(value: Clause, location: SourceRange, module: Sym) -> Self {
Self { value, location: CodeLocation::new_src(location, module) }
}
fn deep_fmt(&self, f: &mut fmt::Formatter<'_>, depth: usize, tr: Wrap) -> fmt::Result {
let Expr { value, .. } = self;
value.deep_fmt(f, depth, tr)?;
Ok(())
}
}
impl fmt::Debug for Expr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.deep_fmt(f, 0, Wrap(false, false))
}
}
/// Semantic code element
#[derive(Clone)]
pub enum Clause {
/// Function call expression
Apply(Rc<Expr>, Rc<Expr>),
/// Function expression
Lambda(Rc<Expr>),
/// Reference to an external constant
Constant(Sym),
/// Reference to a function argument
LambdaArg(usize),
/// An opaque non-callable value, eg. a file handle
Atom(AtomGenerator),
}
const ARGNAME_CHARSET: &str = "abcdefghijklmnopqrstuvwxyz";
fn parametric_fmt(
f: &mut fmt::Formatter<'_>,
depth: usize,
prefix: &str,
body: &Expr,
wrap_right: bool,
) -> fmt::Result {
if wrap_right {
write!(f, "(")?;
}
f.write_str(prefix)?;
f.write_str(&string_from_charset(depth as u64, ARGNAME_CHARSET))?;
f.write_str(".")?;
body.deep_fmt(f, depth + 1, Wrap(false, false))?;
if wrap_right {
write!(f, ")")?;
}
Ok(())
}
impl Clause {
fn deep_fmt(&self, f: &mut fmt::Formatter<'_>, depth: usize, Wrap(wl, wr): Wrap) -> fmt::Result {
match self {
Self::Atom(a) => write!(f, "{a:?}"),
Self::Lambda(body) => parametric_fmt(f, depth, "\\", body, wr),
Self::LambdaArg(skip) => {
let lambda_depth = (depth - skip - 1).try_into().unwrap();
f.write_str(&string_from_charset(lambda_depth, ARGNAME_CHARSET))
},
Self::Apply(func, x) => {
if wl {
write!(f, "(")?;
}
func.deep_fmt(f, depth, Wrap(false, true))?;
write!(f, " ")?;
x.deep_fmt(f, depth, Wrap(true, wr && !wl))?;
if wl {
write!(f, ")")?;
}
Ok(())
},
Self::Constant(token) => write!(f, "{token}"),
}
}
}
impl fmt::Debug for Clause {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.deep_fmt(f, 0, Wrap(false, false))
}
}

View File

@@ -0,0 +1,37 @@
//! Convert IR to the interpreter's NORT representation
use super::ir;
use crate::interpreter::nort;
use crate::interpreter::nort_builder::NortBuilder;
fn expr(expr: &ir::Expr, ctx: NortBuilder<(), usize>) -> nort::Expr {
clause(&expr.value, ctx).into_expr(expr.location.clone())
}
fn clause(cls: &ir::Clause, ctx: NortBuilder<(), usize>) -> nort::Clause {
match cls {
ir::Clause::Constant(name) => nort::Clause::Constant(name.clone()),
ir::Clause::Atom(a) => nort::Clause::Atom(a.run()),
ir::Clause::LambdaArg(n) => {
ctx.arg_logic(n);
nort::Clause::LambdaArg
},
ir::Clause::Apply(f, x) => ctx.apply_logic(|c| expr(f, c), |c| expr(x, c)),
ir::Clause::Lambda(body) => ctx.lambda_logic(&(), |c| expr(body, c)),
}
}
/// Convert an expression.
pub fn ir_to_nort(expr: &ir::Expr) -> nort::Expr {
let c = NortBuilder::new(&|count| {
let mut count: usize = *count;
Box::new(move |()| match count {
0 => true,
_ => {
count -= 1;
false
},
})
});
nort::ClauseInst::new(clause(&expr.value, c)).into_expr(expr.location.clone())
}

View File

@@ -0,0 +1,7 @@
//! Intermediate representation. Currently just an indirection between
//! [super::parse::parsed] and [super::interpreter::nort], in the future
//! hopefully a common point for alternate encodings, optimizations and targets.
pub mod ast_to_ir;
pub mod ir;
pub mod ir_to_nort;