use crate::error::{
OwnedRuleAttemptStepError, OwnedRunStepError, RuleAttemptStepError, RunError, RunFinishError,
RunStartError, RunStepError, TracedRunError,
};
use crate::input::RunSeed;
use crate::inspect::RuleView;
use crate::limits::{RuleAttemptCount, RuleAttemptLimit, StepCount};
use crate::program::{
ActiveRuleCursor, Program, ReturnOutput, RuleAttemptTargetSelection, RuleCursor,
RuleCursorAfterMiss, RunResult,
};
use crate::runtime::action::{AppliedRule, PreparedRuleApplication, prepare_matched_rule};
use crate::runtime::budget::{RuleAttemptBudgetState, RuntimeBudgetState};
use crate::runtime::matcher::{
RuleAttempt, RuleSearch, attempt_rule, find_next_match, runtime_rule_for_target,
};
use crate::runtime::once::OnceStateSet;
use crate::runtime::rewrite::RewriteScratch;
use crate::runtime::state::State;
use crate::trace::{BorrowedTraceEffect, BorrowedTraceEvent, RuntimeStateView};
use super::{OwnedRuleWitness, RuleAttemptStableReason, RuleMiss};
#[derive(Debug)]
pub(super) struct RunCore {
state: State,
scratch: RewriteScratch,
budget: RuntimeBudgetState,
once_states: OnceStateSet,
}
pub(super) struct Session<P> {
pub(super) program: P,
pub(super) core: RunCore,
}
pub(super) struct AttemptSession<P> {
pub(super) program: P,
pub(super) core: RunCore,
pub(super) cursor: RuleCursor,
pub(super) attempt_budget: RuleAttemptBudgetState,
}
struct MissCommit<'attempt, RuleWitness> {
cursor: &'attempt mut RuleCursor,
attempt_budget: &'attempt RuleAttemptBudgetState,
core: &'attempt RunCore,
attempt: RuleAttemptCount,
active_cursor: ActiveRuleCursor,
miss: RuleMiss<RuleWitness>,
}
struct RuleAttemptContext<'attempt, 'program> {
program: &'program Program,
core: &'attempt mut RunCore,
cursor: &'attempt mut RuleCursor,
attempt_budget: &'attempt mut RuleAttemptBudgetState,
}
struct WitnessedApplication<'program, 'once, 'budget, RuleWitness> {
prepared: PreparedRuleApplication<'program, 'once, 'budget>,
witness: RuleWitness,
}
pub(super) trait ProgramOwner {
fn program(&self) -> &Program;
}
#[derive(Debug, Clone, Copy)]
pub(super) struct BorrowedProgram<'program> {
pub(super) program: &'program Program,
}
#[derive(Debug)]
pub(super) struct OwnedProgram {
pub(super) program: Program,
}
pub(super) enum CoreStep<RuleWitness> {
Applied(CoreAppliedRule<RuleWitness>),
Stable(StepCount),
}
enum RuntimeStep<'program> {
Applied(AppliedRule<'program>),
Stable(StepCount),
}
pub(super) enum CoreAppliedRule<RuleWitness> {
Rewrite {
step: StepCount,
rule: RuleWitness,
},
Return {
step: StepCount,
rule: RuleWitness,
output: ReturnOutput,
},
}
pub(super) enum CoreRuleAttempt<RuleWitness> {
Missed {
attempt: RuleAttemptCount,
miss: RuleMiss<RuleWitness>,
},
Applied {
attempt: RuleAttemptCount,
applied: CoreAppliedRule<RuleWitness>,
},
Stable {
attempts: RuleAttemptCount,
steps: StepCount,
stable_reason: RuleAttemptStableReason<RuleWitness>,
},
}
impl<RuleWitness> CoreAppliedRule<RuleWitness> {
fn from_applied_rule(applied: AppliedRule<'_>, rule: RuleWitness) -> Self {
match applied {
AppliedRule::Rewrite(committed) => Self::Rewrite {
step: committed.step(),
rule,
},
AppliedRule::Return(committed) => Self::Return {
step: committed.step(),
rule,
output: committed.into_output(),
},
}
}
}
impl<'program, 'once, 'budget, RuleWitness>
WitnessedApplication<'program, 'once, 'budget, RuleWitness>
{
fn new<Error>(
prepared: PreparedRuleApplication<'program, 'once, 'budget>,
make_witness: impl FnOnce(RuleView<'program>) -> Result<RuleWitness, Error>,
) -> Result<Self, Error> {
let witness = make_witness(RuleView::new(prepared.rule()))?;
Ok(Self { prepared, witness })
}
fn commit(
self,
state: &mut State,
scratch: &mut RewriteScratch,
) -> CoreAppliedRule<RuleWitness> {
let applied = self.prepared.commit(state, scratch);
CoreAppliedRule::from_applied_rule(applied, self.witness)
}
}
impl ProgramOwner for BorrowedProgram<'_> {
fn program(&self) -> &Program {
self.program
}
}
impl ProgramOwner for OwnedProgram {
fn program(&self) -> &Program {
&self.program
}
}
impl RunCore {
fn new(program: &Program, seed: RunSeed) -> Result<Self, RunStartError> {
let (input, budget) = seed.into_runtime_parts();
let state = State::from_input(input);
let once_states = OnceStateSet::new(program.once_rule_count())?;
Ok(Self {
state,
scratch: RewriteScratch::new(),
budget,
once_states,
})
}
pub(super) const fn completed_steps(&self) -> StepCount {
self.budget.completed_steps()
}
pub(super) fn state(&self) -> RuntimeStateView<'_> {
self.state.view()
}
pub(super) fn into_stable_result(self, steps: StepCount) -> Result<RunResult, RunFinishError> {
let output = self
.state
.into_snapshot()
.map_err(RunFinishError::FinalOutput)?;
Ok(RunResult::stable(output, steps))
}
fn step_runtime<'program>(
&mut self,
program: &'program Program,
) -> Result<RuntimeStep<'program>, RunStepError> {
let matched =
match find_next_match(program.rule_scan(), &mut self.once_states, &self.state)? {
RuleSearch::Matched(matched) => matched,
RuleSearch::Stable => {
return Ok(RuntimeStep::Stable(self.budget.completed_steps()));
}
};
let state_len = self.state.byte_count();
let prepared =
prepare_matched_rule(&mut self.scratch, &mut self.budget, state_len, matched)?;
let applied = prepared.commit(&mut self.state, &mut self.scratch);
Ok(RuntimeStep::Applied(applied))
}
fn step(&mut self, program: &Program) -> Result<CoreStep<()>, RunStepError> {
let applied = match self.step_runtime(program)? {
RuntimeStep::Applied(applied) => applied,
RuntimeStep::Stable(steps) => return Ok(CoreStep::Stable(steps)),
};
Ok(CoreStep::Applied(CoreAppliedRule::from_applied_rule(
applied,
(),
)))
}
}
impl<P: ProgramOwner> Session<P> {
pub(super) fn new(program: P, seed: RunSeed) -> Result<Self, RunStartError> {
let core = RunCore::new(program.program(), seed)?;
Ok(Self { program, core })
}
pub(super) fn program(&self) -> &Program {
self.program.program()
}
pub(super) const fn completed_steps(&self) -> StepCount {
self.core.completed_steps()
}
pub(super) fn state(&self) -> RuntimeStateView<'_> {
self.core.state()
}
pub(super) fn step(&mut self) -> Result<CoreStep<()>, RunStepError> {
self.core.step(self.program.program())
}
pub(super) fn finish(mut self) -> Result<RunResult, RunFinishError> {
loop {
match self.step().map_err(RunFinishError::from)? {
CoreStep::Applied(CoreAppliedRule::Rewrite { .. }) => {}
CoreStep::Applied(CoreAppliedRule::Return { step, output, .. }) => {
return Ok(RunResult::from_return(output, step));
}
CoreStep::Stable(steps) => return self.core.into_stable_result(steps),
}
}
}
}
impl<P: ProgramOwner> AttemptSession<P> {
pub(super) fn new(
program: P,
seed: RunSeed,
limit: RuleAttemptLimit,
) -> Result<Self, RunStartError> {
let cursor = program.program().rule_attempt_cursor();
let core = RunCore::new(program.program(), seed)?;
Ok(Self {
program,
core,
cursor,
attempt_budget: RuleAttemptBudgetState::new(limit),
})
}
pub(super) fn program(&self) -> &Program {
self.program.program()
}
pub(super) const fn completed_steps(&self) -> StepCount {
self.core.completed_steps()
}
pub(super) const fn completed_attempts(&self) -> RuleAttemptCount {
self.attempt_budget.completed_attempts()
}
pub(super) fn state(&self) -> RuntimeStateView<'_> {
self.core.state()
}
}
impl<'program> Session<BorrowedProgram<'program>> {
pub(super) fn step_borrowed(&mut self) -> Result<CoreStep<RuleView<'program>>, RunStepError> {
step_with_witness(&mut self.core, self.program.program, Ok)
}
}
impl Session<OwnedProgram> {
pub(super) fn step_owned(&mut self) -> Result<CoreStep<OwnedRuleWitness>, OwnedRunStepError> {
step_with_witness(&mut self.core, &self.program.program, |rule| {
OwnedRuleWitness::from_rule_view(rule).map_err(OwnedRunStepError::RuleWitnessAllocation)
})
}
}
impl<'program> AttemptSession<BorrowedProgram<'program>> {
pub(super) fn step_borrowed(
&mut self,
) -> Result<CoreRuleAttempt<RuleView<'program>>, RuleAttemptStepError> {
let mut context = RuleAttemptContext {
program: self.program.program,
core: &mut self.core,
cursor: &mut self.cursor,
attempt_budget: &mut self.attempt_budget,
};
attempt_current_rule_with_witness(&mut context, Ok)
}
}
impl AttemptSession<OwnedProgram> {
pub(super) fn step_owned(
&mut self,
) -> Result<CoreRuleAttempt<OwnedRuleWitness>, OwnedRuleAttemptStepError> {
let mut context = RuleAttemptContext {
program: &self.program.program,
core: &mut self.core,
cursor: &mut self.cursor,
attempt_budget: &mut self.attempt_budget,
};
attempt_current_rule_with_witness(&mut context, |rule| {
OwnedRuleWitness::from_rule_view(rule)
.map_err(OwnedRuleAttemptStepError::RuleWitnessAllocation)
})
}
}
fn step_with_witness<'program, RuleWitness, Error>(
core: &mut RunCore,
program: &'program Program,
make_witness: impl FnOnce(RuleView<'program>) -> Result<RuleWitness, Error>,
) -> Result<CoreStep<RuleWitness>, Error>
where
Error: From<RunStepError>,
{
let matched = match find_next_match(program.rule_scan(), &mut core.once_states, &core.state)
.map_err(RunStepError::from)?
{
RuleSearch::Matched(matched) => matched,
RuleSearch::Stable => return Ok(CoreStep::Stable(core.budget.completed_steps())),
};
let state_len = core.state.byte_count();
let prepared = prepare_matched_rule(&mut core.scratch, &mut core.budget, state_len, matched)?;
let witnessed = WitnessedApplication::new(prepared, make_witness)?;
let applied = witnessed.commit(&mut core.state, &mut core.scratch);
Ok(CoreStep::Applied(applied))
}
fn attempt_current_rule_with_witness<'program, RuleWitness, Error>(
context: &mut RuleAttemptContext<'_, 'program>,
make_witness: impl FnOnce(RuleView<'program>) -> Result<RuleWitness, Error>,
) -> Result<CoreRuleAttempt<RuleWitness>, Error>
where
Error: From<RuleAttemptStepError> + From<RunStepError>,
{
let target = match context
.program
.select_attempt_target(context.cursor)
.map_err(RuleAttemptStepError::from)?
{
RuleAttemptTargetSelection::Target(target) => target,
RuleAttemptTargetSelection::NoExecutableRules => return Ok(no_executable_rules(context)),
};
let (active_cursor, target) = target.into_parts();
let runtime_rule = runtime_rule_for_target(&mut context.core.once_states, target)
.map_err(RunStepError::from)?;
let reservation = context
.attempt_budget
.reserve_next_attempt(context.core.state.byte_count())?;
let attempted = attempt_rule(runtime_rule, &context.core.state);
match attempted {
RuleAttempt::Missed(missed) => {
let witness = make_witness(RuleView::new(missed.rule()))?;
let attempt = reservation.commit();
Ok(commit_miss(MissCommit {
cursor: &mut *context.cursor,
attempt_budget: &*context.attempt_budget,
core: &*context.core,
attempt,
active_cursor,
miss: RuleMiss::new(witness, missed.reason()),
}))
}
RuleAttempt::Matched(matched) => {
let state_len = context.core.state.byte_count();
let prepared = prepare_matched_rule(
&mut context.core.scratch,
&mut context.core.budget,
state_len,
matched,
)?;
let witnessed = WitnessedApplication::new(prepared, make_witness)?;
let attempt = reservation.commit();
let applied = witnessed.commit(&mut context.core.state, &mut context.core.scratch);
if matches!(applied, CoreAppliedRule::Rewrite { .. }) {
*context.cursor = RuleCursor::Active(active_cursor.reset_to_first());
}
Ok(CoreRuleAttempt::Applied { attempt, applied })
}
}
}
fn no_executable_rules<RuleWitness>(
context: &RuleAttemptContext<'_, '_>,
) -> CoreRuleAttempt<RuleWitness> {
CoreRuleAttempt::Stable {
attempts: context.attempt_budget.completed_attempts(),
steps: context.core.completed_steps(),
stable_reason: RuleAttemptStableReason::NoExecutableRules,
}
}
fn commit_miss<RuleWitness>(context: MissCommit<'_, RuleWitness>) -> CoreRuleAttempt<RuleWitness> {
match context.active_cursor.advance_after_miss() {
RuleCursorAfterMiss::Stable => CoreRuleAttempt::Stable {
attempts: context.attempt_budget.completed_attempts(),
steps: context.core.completed_steps(),
stable_reason: RuleAttemptStableReason::FinalMiss(context.miss),
},
RuleCursorAfterMiss::Advanced(active_cursor) => {
*context.cursor = RuleCursor::Active(active_cursor);
CoreRuleAttempt::Missed {
attempt: context.attempt,
miss: context.miss,
}
}
}
}
impl<'program> Session<BorrowedProgram<'program>> {
pub(super) 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
.core
.step_runtime(self.program.program)
.map_err(RunFinishError::from)
.map_err(RunError::from)
.map_err(TracedRunError::Run)?
{
RuntimeStep::Applied(AppliedRule::Rewrite(committed)) => {
let step = committed.step();
let rule = RuleView::new(committed.rule());
Self::emit_step_trace(
&mut trace,
step,
rule,
BorrowedTraceEffect::Continue {
state: self.state(),
},
)?;
}
RuntimeStep::Applied(AppliedRule::Return(committed)) => {
let step = committed.step();
let rule = RuleView::new(committed.rule());
let output = committed.output_view();
Self::emit_step_trace(
&mut trace,
step,
rule,
BorrowedTraceEffect::Return { output },
)?;
return Ok(RunResult::from_return(committed.into_output(), step));
}
RuntimeStep::Stable(steps) => {
return self
.core
.into_stable_result(steps)
.map_err(RunError::from)
.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 Session<OwnedProgram> {
pub(super) fn into_program_core(self) -> (Program, RunCore) {
(self.program.program, self.core)
}
}
impl AttemptSession<OwnedProgram> {
pub(super) fn into_program_core(self) -> (Program, RunCore) {
(self.program.program, self.core)
}
}