use crate::error::{RunError, TracedRunError};
use crate::inspect::RuleView;
use crate::program::{Program, ReturnOutputView, RunLimits, RunResult, StepCount};
use crate::rule::Rule;
use crate::runtime::RuntimeInput;
use crate::runtime::action::{
AppliedRule, AppliedRuleEffect, apply_matched_rule, materialize_return_output,
};
use crate::runtime::budget::RuntimeBudgetState;
use crate::runtime::input::InitialStateBytes;
use crate::runtime::matcher::{RuleSearch, find_next_match};
use crate::runtime::once::OnceStateSet;
use crate::runtime::rewrite::RewriteScratch;
use crate::runtime::state::State;
use crate::trace::{BorrowedTraceEffect, BorrowedTraceEvent, RuntimeStateView};
pub struct RunSession<'program> {
core: RunCore<'program>,
}
#[derive(Debug, PartialEq, Eq)]
struct RunCore<'program> {
state: State,
scratch: RewriteScratch,
budget: RuntimeBudgetState,
rules: &'program [Rule],
once_states: OnceStateSet,
}
pub enum StepTransition<'program> {
Applied(AppliedStep<'program>),
Stable(StableRun<'program>),
Returned(ReturnedRun<'program>),
Failed(FailedRun<'program>),
}
pub struct AppliedStep<'program> {
step: StepCount,
rule: RuleView<'program>,
session: RunSession<'program>,
}
pub struct StableRun<'program> {
steps: StepCount,
core: RunCore<'program>,
}
#[derive(Clone, Copy)]
pub struct ReturnedRun<'program> {
step: StepCount,
rule: RuleView<'program>,
output: ReturnOutputView<'program>,
}
pub struct FailedRun<'program> {
error: RunError,
session: RunSession<'program>,
}
impl core::fmt::Debug for RunSession<'_> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("RunSession")
.field("completed_steps", &self.completed_steps())
.field("state", &self.state())
.finish()
}
}
impl core::fmt::Debug for StepTransition<'_> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Applied(applied) => formatter.debug_tuple("Applied").field(applied).finish(),
Self::Stable(stable) => formatter.debug_tuple("Stable").field(stable).finish(),
Self::Returned(returned) => formatter.debug_tuple("Returned").field(returned).finish(),
Self::Failed(failed) => formatter.debug_tuple("Failed").field(failed).finish(),
}
}
}
impl core::fmt::Debug for AppliedStep<'_> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("AppliedStep")
.field("step", &self.step())
.field("rule", &self.rule())
.field("state", &self.state())
.finish()
}
}
impl core::fmt::Debug for StableRun<'_> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("StableRun")
.field("steps", &self.steps())
.field("state", &self.state())
.finish()
}
}
impl core::fmt::Debug for ReturnedRun<'_> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("ReturnedRun")
.field("step", &self.step())
.field("rule", &self.rule())
.field("output", &self.output())
.finish()
}
}
impl core::fmt::Debug for FailedRun<'_> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("FailedRun")
.field("error", &self.error())
.field("completed_steps", &self.completed_steps())
.field("state", &self.state())
.finish()
}
}
impl<'program> RunCore<'program> {
fn new(
program: &'program Program,
input: RuntimeInput,
limits: RunLimits,
) -> Result<Self, RunError> {
let budget = RuntimeBudgetState::new(limits);
let input = InitialStateBytes::from_runtime_input(input, budget)?;
let state = State::from_input(input);
let once_states = OnceStateSet::new(program.once_slot_count())?;
Ok(Self {
state,
scratch: RewriteScratch::new(),
budget,
rules: program.rule_slice(),
once_states,
})
}
const fn completed_steps(&self) -> StepCount {
self.budget.completed_steps()
}
fn state(&self) -> RuntimeStateView<'_> {
self.state.view()
}
fn into_stable_result(self, steps: StepCount) -> Result<RunResult, RunError> {
Ok(RunResult::stable(self.state.into_snapshot()?, steps))
}
}
impl<'program> RunSession<'program> {
pub(crate) fn new(
program: &'program Program,
input: RuntimeInput,
limits: RunLimits,
) -> Result<Self, RunError> {
Ok(Self {
core: RunCore::new(program, input, limits)?,
})
}
#[must_use]
pub const fn completed_steps(&self) -> StepCount {
self.core.completed_steps()
}
#[must_use]
pub fn state(&self) -> RuntimeStateView<'_> {
self.core.state()
}
#[must_use]
pub fn step(mut self) -> StepTransition<'program> {
let applied = {
let RunCore {
state,
scratch,
budget,
rules,
once_states,
} = &mut self.core;
let matched = match find_next_match(rules, once_states, state) {
RuleSearch::Matched(matched) => matched,
RuleSearch::Stable => {
let steps = budget.completed_steps();
return StepTransition::Stable(StableRun {
steps,
core: self.core,
});
}
};
apply_matched_rule(state, scratch, budget, matched)
};
let applied = match applied {
Ok(applied) => applied,
Err(error) => return StepTransition::Failed(FailedRun::new(error, self)),
};
applied.into_transition(self)
}
pub fn finish(mut self) -> Result<RunResult, RunError> {
loop {
match self.step() {
StepTransition::Applied(applied) => {
self = applied.into_session();
}
StepTransition::Stable(stable) => {
return stable.into_result();
}
StepTransition::Returned(returned) => {
return returned.into_result();
}
StepTransition::Failed(failed) => return Err(failed.into_error()),
}
}
}
pub(crate) fn run_with_borrowed_trace<F, E>(
mut self,
mut trace: F,
) -> Result<RunResult, TracedRunError<E>>
where
F: for<'run> FnMut(BorrowedTraceEvent<'program, 'run>) -> Result<(), E>,
{
trace(BorrowedTraceEvent::Initial {
state: self.state(),
})
.map_err(TracedRunError::Trace)?;
loop {
match self.step() {
StepTransition::Applied(applied) => {
Self::emit_step_trace(
&mut trace,
applied.step(),
applied.rule(),
BorrowedTraceEffect::Continue {
state: applied.state(),
},
)?;
self = applied.into_session();
}
StepTransition::Stable(stable) => {
return stable.into_result().map_err(TracedRunError::Run);
}
StepTransition::Returned(returned) => {
Self::emit_step_trace(
&mut trace,
returned.step(),
returned.rule(),
BorrowedTraceEffect::Return {
output: returned.output(),
},
)?;
return returned.into_result().map_err(TracedRunError::Run);
}
StepTransition::Failed(failed) => {
return Err(TracedRunError::Run(failed.into_error()));
}
}
}
}
fn emit_step_trace<F, E>(
trace: &mut F,
step: StepCount,
rule: RuleView<'program>,
effect: BorrowedTraceEffect<'program, '_>,
) -> Result<(), TracedRunError<E>>
where
F: for<'run> FnMut(BorrowedTraceEvent<'program, 'run>) -> Result<(), E>,
{
trace(BorrowedTraceEvent::Step { step, rule, effect }).map_err(TracedRunError::Trace)
}
}
impl<'program> AppliedRule<'program> {
fn into_transition(self, session: RunSession<'program>) -> StepTransition<'program> {
match self.effect {
AppliedRuleEffect::Continue => StepTransition::Applied(AppliedStep {
step: self.step,
rule: self.rule,
session,
}),
AppliedRuleEffect::Return(output) => StepTransition::Returned(ReturnedRun {
step: self.step,
rule: self.rule,
output,
}),
}
}
}
impl<'program> AppliedStep<'program> {
#[must_use]
pub const fn step(&self) -> StepCount {
self.step
}
#[must_use]
pub const fn rule(&self) -> RuleView<'program> {
self.rule
}
#[must_use]
pub fn state(&self) -> RuntimeStateView<'_> {
self.session.state()
}
#[must_use]
pub fn into_session(self) -> RunSession<'program> {
self.session
}
}
impl StableRun<'_> {
#[must_use]
pub const fn steps(&self) -> StepCount {
self.steps
}
#[must_use]
pub fn state(&self) -> RuntimeStateView<'_> {
self.core.state()
}
pub fn into_result(self) -> Result<RunResult, RunError> {
self.core.into_stable_result(self.steps)
}
}
impl<'program> ReturnedRun<'program> {
#[must_use]
pub const fn step(&self) -> StepCount {
self.step
}
#[must_use]
pub const fn rule(&self) -> RuleView<'program> {
self.rule
}
#[must_use]
pub const fn output(&self) -> ReturnOutputView<'program> {
self.output
}
pub fn into_result(self) -> Result<RunResult, RunError> {
Ok(RunResult::from_return(
materialize_return_output(self.output)?,
self.step,
))
}
}
impl<'program> FailedRun<'program> {
fn new(error: RunError, session: RunSession<'program>) -> Self {
Self { error, session }
}
#[must_use]
pub const fn error(&self) -> &RunError {
&self.error
}
#[must_use]
pub const fn completed_steps(&self) -> StepCount {
self.session.completed_steps()
}
#[must_use]
pub fn state(&self) -> RuntimeStateView<'_> {
self.session.state()
}
#[must_use]
pub fn into_error(self) -> RunError {
self.error
}
}
impl core::fmt::Display for FailedRun<'_> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.error.fmt(formatter)
}
}
impl core::error::Error for FailedRun<'_> {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
Some(&self.error)
}
}