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:
@@ -1,17 +1,14 @@
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::apply::apply;
|
||||
use super::context::{Context, Return};
|
||||
use super::error::RuntimeError;
|
||||
use crate::foreign::{Atom, Atomic, AtomicReturn, ExternError};
|
||||
use crate::foreign::AtomicReturn;
|
||||
use crate::representations::interpreted::{Clause, ExprInst};
|
||||
use crate::representations::Primitive;
|
||||
|
||||
/// Normalize an expression using beta reduction with memoization
|
||||
pub fn run(expr: ExprInst, mut ctx: Context) -> Result<Return, RuntimeError> {
|
||||
let (state, (gas, inert)) =
|
||||
expr.try_normalize(|cls| -> Result<(Clause, _), RuntimeError> {
|
||||
expr.try_normalize(|cls, loc| -> Result<(Clause, _), RuntimeError> {
|
||||
let mut i = cls.clone();
|
||||
while ctx.gas.map(|g| g > 0).unwrap_or(true) {
|
||||
match &i {
|
||||
@@ -33,7 +30,8 @@ pub fn run(expr: ExprInst, mut ctx: Context) -> Result<Return, RuntimeError> {
|
||||
i = clause.clone();
|
||||
},
|
||||
Clause::Constant(c) => {
|
||||
let symval = ctx.symbols.get(c).expect("missing symbol for value");
|
||||
let symval = (ctx.symbols.get(c))
|
||||
.ok_or_else(|| RuntimeError::MissingSymbol(*c, loc.clone()))?;
|
||||
ctx.gas = ctx.gas.map(|g| g - 1); // cost of lookup
|
||||
i = symval.expr().clause.clone();
|
||||
},
|
||||
@@ -46,111 +44,3 @@ pub fn run(expr: ExprInst, mut ctx: Context) -> Result<Return, RuntimeError> {
|
||||
})?;
|
||||
Ok(Return { state, gas, inert })
|
||||
}
|
||||
|
||||
/// Opaque inert data that may encode a command to a [Handler]
|
||||
pub type HandlerParm = Box<dyn Atomic>;
|
||||
|
||||
/// Reasons why a [Handler] could not interpret a command. Convertible from
|
||||
/// either variant
|
||||
pub enum HandlerErr {
|
||||
/// The command was addressed to us but its execution resulted in an error
|
||||
Extern(Rc<dyn ExternError>),
|
||||
/// This handler is not applicable, either because the [HandlerParm] is not a
|
||||
/// command or because it's meant for some other handler
|
||||
NA(HandlerParm),
|
||||
}
|
||||
impl From<Rc<dyn ExternError>> for HandlerErr {
|
||||
fn from(value: Rc<dyn ExternError>) -> Self {
|
||||
Self::Extern(value)
|
||||
}
|
||||
}
|
||||
impl<T> From<T> for HandlerErr
|
||||
where
|
||||
T: ExternError + 'static,
|
||||
{
|
||||
fn from(value: T) -> Self {
|
||||
Self::Extern(value.into_extern())
|
||||
}
|
||||
}
|
||||
impl From<HandlerParm> for HandlerErr {
|
||||
fn from(value: HandlerParm) -> Self {
|
||||
Self::NA(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Various possible outcomes of a [Handler] execution. Ok returns control to
|
||||
/// the interpreter. The meaning of Err is decided by the value in it.
|
||||
pub type HandlerRes = Result<ExprInst, HandlerErr>;
|
||||
|
||||
/// A trait for things that may be able to handle commands returned by Orchid
|
||||
/// code. This trait is implemented for `FnMut(HandlerParm) -> HandlerRes` and
|
||||
/// `(Handler, Handler)`, users are not supposed to implement it themselves.
|
||||
///
|
||||
/// A handler receives an arbitrary inert [Atomic] and uses [Atomic::as_any]
|
||||
/// then downcast_ref of [std::any::Any] to obtain a known type. If this fails,
|
||||
/// it returns the box in [HandlerErr::NA] which will be passed to the next
|
||||
/// handler.
|
||||
pub trait Handler {
|
||||
/// Attempt to resolve a command with this handler.
|
||||
fn resolve(&mut self, data: HandlerParm) -> HandlerRes;
|
||||
|
||||
/// If this handler isn't applicable, try the other one.
|
||||
fn or<T: Handler>(self, t: T) -> (Self, T)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
(self, t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Handler for F
|
||||
where
|
||||
F: FnMut(HandlerParm) -> HandlerRes,
|
||||
{
|
||||
fn resolve(&mut self, data: HandlerParm) -> HandlerRes {
|
||||
self(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Handler, U: Handler> Handler for (T, U) {
|
||||
fn resolve(&mut self, data: HandlerParm) -> HandlerRes {
|
||||
match self.0.resolve(data) {
|
||||
Err(HandlerErr::NA(data)) => self.1.resolve(data),
|
||||
x => x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [run] orchid code, executing any commands it returns using the specified
|
||||
/// [Handler]s.
|
||||
pub fn run_handler(
|
||||
mut expr: ExprInst,
|
||||
mut handler: impl Handler,
|
||||
mut ctx: Context,
|
||||
) -> Result<Return, RuntimeError> {
|
||||
loop {
|
||||
let ret = run(expr.clone(), ctx.clone())?;
|
||||
if ret.gas == Some(0) {
|
||||
return Ok(ret);
|
||||
}
|
||||
let state_ex = ret.state.expr();
|
||||
let a = if let Clause::P(Primitive::Atom(a)) = &state_ex.clause {
|
||||
a
|
||||
} else {
|
||||
mem::drop(state_ex);
|
||||
return Ok(ret);
|
||||
};
|
||||
let boxed = a.clone().0;
|
||||
expr = match handler.resolve(boxed) {
|
||||
Ok(expr) => expr,
|
||||
Err(HandlerErr::Extern(ext)) => Err(ext)?,
|
||||
Err(HandlerErr::NA(atomic)) =>
|
||||
return Ok(Return {
|
||||
gas: ret.gas,
|
||||
inert: ret.inert,
|
||||
state: Clause::P(Primitive::Atom(Atom(atomic))).wrap(),
|
||||
}),
|
||||
};
|
||||
ctx.gas = ret.gas;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user