use crate::error::{RunError, TracedRunError};
use crate::input::RunSeed;
use crate::inspect::{RulePosition, RuleView};
use crate::limits::StepCount;
use crate::program::{Program, ReturnOutput, RunResult};
use crate::runtime::action::{AppliedRule, CommittedReturnRule, apply_matched_rule};
use crate::runtime::budget::RuntimeBudgetState;
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> {
session: Session<BorrowedProgram<'program>>,
}
pub struct OwnedRunSession {
session: Session<OwnedProgram>,
}
#[derive(Debug)]
struct RunCore {
state: State,
scratch: RewriteScratch,
budget: RuntimeBudgetState,
once_states: OnceStateSet,
}
struct Session<P> {
program: P,
core: RunCore,
}
trait ProgramOwner {
fn program(&self) -> &Program;
}
#[derive(Debug, Clone, Copy)]
struct BorrowedProgram<'program> {
program: &'program Program,
}
#[derive(Debug)]
struct OwnedProgram {
program: Program,
}
pub enum StepTransition<'program> {
Applied(AppliedStep<'program>),
Stable(StableRun<'program>),
Returned(ReturnedRun<'program>),
Failed(FailedRun<'program>),
}
pub struct AppliedStep<'program> {
step: StepCount,
rule_position: RulePosition,
session: RunSession<'program>,
}
pub struct StableRun<'program> {
steps: StepCount,
program: &'program Program,
core: RunCore,
}
pub struct ReturnedRun<'program> {
step: StepCount,
rule_position: RulePosition,
program: &'program Program,
output: ReturnOutput,
}
pub struct FailedRun<'program> {
error: RunError,
session: RunSession<'program>,
}
pub enum OwnedStepTransition {
Applied(OwnedAppliedStep),
Stable(OwnedStableRun),
Returned(OwnedReturnedRun),
Failed(OwnedFailedRun),
}
pub struct OwnedAppliedStep {
step: StepCount,
rule_position: RulePosition,
session: OwnedRunSession,
}
pub struct OwnedStableRun {
steps: StepCount,
program: Program,
core: RunCore,
}
pub struct OwnedReturnedRun {
step: StepCount,
rule_position: RulePosition,
program: Program,
output: ReturnOutput,
}
pub struct OwnedFailedRun {
error: RunError,
session: OwnedRunSession,
}
enum CoreStep<'program> {
Applied(AppliedRule<'program>),
Stable(StepCount),
}
impl ProgramOwner for BorrowedProgram<'_> {
fn program(&self) -> &Program {
self.program
}
}
impl ProgramOwner for OwnedProgram {
fn program(&self) -> &Program {
&self.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 OwnedRunSession {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("OwnedRunSession")
.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 OwnedStepTransition {
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_position", &self.rule_position())
.field("state", &self.state())
.finish()
}
}
impl core::fmt::Debug for OwnedAppliedStep {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("OwnedAppliedStep")
.field("step", &self.step())
.field("rule_position", &self.rule_position())
.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 OwnedStableRun {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("OwnedStableRun")
.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_position", &self.rule_position())
.field("output", &self.output())
.finish()
}
}
impl core::fmt::Debug for OwnedReturnedRun {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("OwnedReturnedRun")
.field("step", &self.step())
.field("rule_position", &self.rule_position())
.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 core::fmt::Debug for OwnedFailedRun {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("OwnedFailedRun")
.field("error", &self.error())
.field("completed_steps", &self.completed_steps())
.field("state", &self.state())
.finish()
}
}
impl RunCore {
fn new(program: &Program, seed: RunSeed) -> Result<Self, RunError> {
let (input, budget) = seed.into_runtime_parts();
let state = State::from_input(input);
let once_states = OnceStateSet::new(program.rule_slice())?;
Ok(Self {
state,
scratch: RewriteScratch::new(),
budget,
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))
}
fn step<'program>(
&mut self,
program: &'program Program,
) -> Result<CoreStep<'program>, RunError> {
let matched =
match find_next_match(program.rule_slice(), &mut self.once_states, &self.state)? {
RuleSearch::Matched(matched) => matched,
RuleSearch::Stable => return Ok(CoreStep::Stable(self.budget.completed_steps())),
};
Ok(CoreStep::Applied(apply_matched_rule(
&mut self.state,
&mut self.scratch,
&mut self.budget,
matched,
)?))
}
}
impl<P: ProgramOwner> Session<P> {
fn new(program: P, seed: RunSeed) -> Result<Self, RunError> {
let core = RunCore::new(program.program(), seed)?;
Ok(Self { program, core })
}
fn program(&self) -> &Program {
self.program.program()
}
const fn completed_steps(&self) -> StepCount {
self.core.completed_steps()
}
fn state(&self) -> RuntimeStateView<'_> {
self.core.state()
}
fn step(&mut self) -> Result<CoreStep<'_>, RunError> {
self.core.step(self.program.program())
}
}
impl<'program> Session<BorrowedProgram<'program>> {
fn step_borrowed(&mut self) -> Result<CoreStep<'program>, RunError> {
self.core.step(self.program.program)
}
fn finish_borrowed(mut self) -> Result<RunResult, RunError> {
loop {
match self.step_borrowed()? {
CoreStep::Applied(AppliedRule::Rewrite(_)) => {}
CoreStep::Applied(AppliedRule::Return(committed)) => {
return Ok(committed.into_result());
}
CoreStep::Stable(steps) => return self.core.into_stable_result(steps),
}
}
}
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_borrowed().map_err(TracedRunError::Run)? {
CoreStep::Applied(AppliedRule::Rewrite(committed)) => {
Self::emit_step_trace(
&mut trace,
committed.step(),
committed.rule(),
BorrowedTraceEffect::Continue {
state: self.state(),
},
)?;
}
CoreStep::Applied(AppliedRule::Return(committed)) => {
let step = committed.step();
let rule = committed.rule();
let output = committed.output_view();
Self::emit_step_trace(
&mut trace,
step,
rule,
BorrowedTraceEffect::Return { output },
)?;
return Ok(committed.into_result());
}
CoreStep::Stable(steps) => {
return self
.core
.into_stable_result(steps)
.map_err(TracedRunError::Run);
}
}
}
}
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 CommittedReturnRule<'_> {
fn into_result(self) -> RunResult {
let step = self.step();
RunResult::from_return(self.into_output(), step)
}
}
impl Session<OwnedProgram> {
fn into_program_core(self) -> (Program, RunCore) {
(self.program.program, self.core)
}
}
pub(crate) fn finish_borrowed_run(program: &Program, seed: RunSeed) -> Result<RunResult, RunError> {
Session::new(BorrowedProgram { program }, seed)?.finish_borrowed()
}
pub(crate) fn run_with_borrowed_trace<'program, F, E>(
program: &'program Program,
seed: RunSeed,
trace: F,
) -> Result<RunResult, TracedRunError<E>>
where
F: for<'run> FnMut(BorrowedTraceEvent<'program, 'run>) -> Result<(), E>,
{
Session::new(BorrowedProgram { program }, seed)
.map_err(TracedRunError::Run)?
.run_with_borrowed_trace(trace)
}
impl<'program> RunSession<'program> {
pub(crate) fn new(program: &'program Program, seed: RunSeed) -> Result<Self, RunError> {
Ok(Self {
session: Session::new(BorrowedProgram { program }, seed)?,
})
}
#[must_use]
pub const fn completed_steps(&self) -> StepCount {
self.session.completed_steps()
}
#[must_use]
pub fn program(&self) -> &'program Program {
self.session.program.program
}
#[must_use]
pub fn state(&self) -> RuntimeStateView<'_> {
self.session.state()
}
#[must_use]
pub fn step(mut self) -> StepTransition<'program> {
match self.session.step_borrowed() {
Ok(CoreStep::Applied(AppliedRule::Rewrite(committed))) => {
let step = committed.step();
let rule = committed.rule().position();
StepTransition::Applied(AppliedStep {
step,
rule_position: rule,
session: self,
})
}
Ok(CoreStep::Applied(AppliedRule::Return(committed))) => {
let step = committed.step();
let rule = committed.rule().position();
let output = committed.into_output();
let Session { program, core: _ } = self.session;
StepTransition::Returned(ReturnedRun {
step,
rule_position: rule,
program: program.program,
output,
})
}
Ok(CoreStep::Stable(steps)) => {
let Session { program, core } = self.session;
StepTransition::Stable(StableRun {
steps,
program: program.program,
core,
})
}
Err(error) => StepTransition::Failed(FailedRun::new(error, 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 Ok(returned.into_result());
}
StepTransition::Failed(failed) => return Err(failed.into_error()),
}
}
}
}
impl OwnedRunSession {
pub(crate) fn new(program: Program, seed: RunSeed) -> Result<Self, RunError> {
Ok(Self {
session: Session::new(OwnedProgram { program }, seed)?,
})
}
#[must_use]
pub const fn completed_steps(&self) -> StepCount {
self.session.completed_steps()
}
#[must_use]
pub fn program(&self) -> &Program {
self.session.program()
}
#[must_use]
pub fn into_program(self) -> Program {
let (program, _core) = self.session.into_program_core();
program
}
#[must_use]
pub fn state(&self) -> RuntimeStateView<'_> {
self.session.state()
}
#[must_use]
pub fn step(mut self) -> OwnedStepTransition {
match self.session.step() {
Ok(CoreStep::Applied(AppliedRule::Rewrite(committed))) => {
let step = committed.step();
let rule = committed.rule().position();
OwnedStepTransition::Applied(OwnedAppliedStep {
step,
rule_position: rule,
session: self,
})
}
Ok(CoreStep::Applied(AppliedRule::Return(committed))) => {
let step = committed.step();
let rule = committed.rule().position();
let output = committed.into_output();
let (program, _core) = self.session.into_program_core();
OwnedStepTransition::Returned(OwnedReturnedRun {
step,
rule_position: rule,
program,
output,
})
}
Ok(CoreStep::Stable(steps)) => {
let (program, core) = self.session.into_program_core();
OwnedStepTransition::Stable(OwnedStableRun {
steps,
program,
core,
})
}
Err(error) => OwnedStepTransition::Failed(OwnedFailedRun::new(error, self)),
}
}
pub fn finish(mut self) -> Result<RunResult, RunError> {
loop {
match self.step() {
OwnedStepTransition::Applied(applied) => {
self = applied.into_session();
}
OwnedStepTransition::Stable(stable) => {
return stable.into_result();
}
OwnedStepTransition::Returned(returned) => {
return Ok(returned.into_result());
}
OwnedStepTransition::Failed(failed) => return Err(failed.into_error()),
}
}
}
}
impl<'program> AppliedStep<'program> {
#[must_use]
pub const fn step(&self) -> StepCount {
self.step
}
#[must_use]
pub const fn rule_position(&self) -> RulePosition {
self.rule_position
}
#[must_use]
pub fn state(&self) -> RuntimeStateView<'_> {
self.session.state()
}
#[must_use]
pub fn into_session(self) -> RunSession<'program> {
self.session
}
}
impl OwnedAppliedStep {
#[must_use]
pub const fn step(&self) -> StepCount {
self.step
}
#[must_use]
pub const fn rule_position(&self) -> RulePosition {
self.rule_position
}
#[must_use]
pub fn state(&self) -> RuntimeStateView<'_> {
self.session.state()
}
#[must_use]
pub fn into_session(self) -> OwnedRunSession {
self.session
}
}
impl<'program> StableRun<'program> {
#[must_use]
pub const fn steps(&self) -> StepCount {
self.steps
}
#[must_use]
pub const fn program(&self) -> &'program Program {
self.program
}
#[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 OwnedStableRun {
#[must_use]
pub const fn steps(&self) -> StepCount {
self.steps
}
#[must_use]
pub const fn program(&self) -> &Program {
&self.program
}
#[must_use]
pub fn into_program(self) -> Program {
self.program
}
#[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 program(&self) -> &'program Program {
self.program
}
#[must_use]
pub const fn rule_position(&self) -> RulePosition {
self.rule_position
}
#[must_use]
pub const fn output(&self) -> &ReturnOutput {
&self.output
}
#[must_use]
pub fn into_result(self) -> RunResult {
RunResult::from_return(self.output, self.step)
}
}
impl OwnedReturnedRun {
#[must_use]
pub const fn step(&self) -> StepCount {
self.step
}
#[must_use]
pub const fn program(&self) -> &Program {
&self.program
}
#[must_use]
pub fn into_program(self) -> Program {
self.program
}
#[must_use]
pub const fn rule_position(&self) -> RulePosition {
self.rule_position
}
#[must_use]
pub const fn output(&self) -> &ReturnOutput {
&self.output
}
#[must_use]
pub fn into_result(self) -> RunResult {
RunResult::from_return(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 program(&self) -> &'program Program {
self.session.program()
}
#[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)
}
}
impl OwnedFailedRun {
fn new(error: RunError, session: OwnedRunSession) -> 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 program(&self) -> &Program {
self.session.program()
}
#[must_use]
pub fn state(&self) -> RuntimeStateView<'_> {
self.session.state()
}
#[must_use]
pub fn into_error(self) -> RunError {
self.error
}
#[must_use]
pub fn into_session(self) -> OwnedRunSession {
self.session
}
#[must_use]
pub fn into_parts(self) -> (RunError, OwnedRunSession) {
(self.error, self.session)
}
}
impl core::fmt::Display for OwnedFailedRun {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.error.fmt(formatter)
}
}
impl core::error::Error for OwnedFailedRun {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
Some(&self.error)
}
}