forked from Orchid/orchid
in midst of refactor
This commit is contained in:
140
orchidlang/src/intermediate/ast_to_ir.rs
Normal file
140
orchidlang/src/intermediate/ast_to_ir.rs
Normal 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))
|
||||
}
|
||||
114
orchidlang/src/intermediate/ir.rs
Normal file
114
orchidlang/src/intermediate/ir.rs
Normal 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))
|
||||
}
|
||||
}
|
||||
37
orchidlang/src/intermediate/ir_to_nort.rs
Normal file
37
orchidlang/src/intermediate/ir_to_nort.rs
Normal 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())
|
||||
}
|
||||
7
orchidlang/src/intermediate/mod.rs
Normal file
7
orchidlang/src/intermediate/mod.rs
Normal 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;
|
||||
Reference in New Issue
Block a user