use super::functions::trampoline;
use std::collections::{HashMap, HashSet};
#[allow(dead_code)]
pub enum MultiStep<State, Output> {
Even(State),
Odd(State),
Done(Output),
}
#[allow(dead_code)]
pub struct LoopDetector {
seen: std::collections::HashSet<u64>,
pub checked: u64,
}
impl LoopDetector {
#[allow(dead_code)]
pub fn new() -> Self {
Self {
seen: std::collections::HashSet::new(),
checked: 0,
}
}
#[allow(dead_code)]
pub fn check(&mut self, fingerprint: u64) -> bool {
self.checked += 1;
!self.seen.insert(fingerprint)
}
#[allow(dead_code)]
pub fn reset(&mut self) {
self.seen.clear();
self.checked = 0;
}
#[allow(dead_code)]
pub fn unique_states(&self) -> usize {
self.seen.len()
}
}
#[allow(dead_code)]
#[derive(Default)]
pub struct TrampolineMetricsRegistry {
pub functions: std::collections::HashMap<String, FunctionTcoMetrics>,
}
impl TrampolineMetricsRegistry {
#[allow(dead_code)]
pub fn new() -> Self {
Self::default()
}
#[allow(dead_code)]
pub fn record(&mut self, function: &str, depth: u64) {
self.functions
.entry(function.to_string())
.or_insert_with(|| FunctionTcoMetrics::new(function))
.record(depth);
}
#[allow(dead_code)]
pub fn top_by_steps(&self, n: usize) -> Vec<&FunctionTcoMetrics> {
let mut sorted: Vec<&FunctionTcoMetrics> = self.functions.values().collect();
sorted.sort_by_key(|b| std::cmp::Reverse(b.total_steps));
sorted.truncate(n);
sorted
}
#[allow(dead_code)]
pub fn total_calls(&self) -> u64 {
self.functions.values().map(|m| m.call_count).sum()
}
}
#[allow(dead_code)]
pub struct BoundedTrampoline {
pub step_limit: u64,
}
impl BoundedTrampoline {
#[allow(dead_code)]
pub fn new(step_limit: u64) -> Self {
Self { step_limit }
}
#[allow(dead_code)]
pub fn run<T>(&self, mut step: TailCall<T>) -> Result<T, String> {
let mut count = 0u64;
loop {
match step {
TailCall::Done(v) => return Ok(v),
TailCall::Call(f) => {
count += 1;
if count > self.step_limit {
return Err(format!(
"trampoline step limit {} exceeded at step {}",
self.step_limit, count
));
}
step = f();
}
}
}
}
}
#[allow(dead_code)]
pub struct TailCallScheduler<T> {
pending: Option<TailCall<T>>,
pub config: TailCallSchedulerConfig,
pub total_steps: u64,
}
impl<T> TailCallScheduler<T> {
#[allow(dead_code)]
pub fn new(step: TailCall<T>) -> Self {
Self {
pending: Some(step),
config: TailCallSchedulerConfig::default(),
total_steps: 0,
}
}
#[allow(dead_code)]
pub fn with_config(step: TailCall<T>, config: TailCallSchedulerConfig) -> Self {
Self {
pending: Some(step),
config,
total_steps: 0,
}
}
#[allow(dead_code)]
pub fn tick(&mut self) -> SchedulerTickResult<T> {
let batch = self.config.max_steps_per_batch;
for _ in 0..batch {
match self.pending.take() {
None => return SchedulerTickResult::Pending,
Some(TailCall::Done(v)) => return SchedulerTickResult::Finished(v),
Some(TailCall::Call(f)) => {
self.total_steps += 1;
if self.total_steps > self.config.step_limit {
return SchedulerTickResult::StepLimitExceeded;
}
self.pending = Some(f());
}
}
}
SchedulerTickResult::Pending
}
#[allow(dead_code)]
pub fn run_to_completion(mut self) -> Result<T, String> {
loop {
match self.tick() {
SchedulerTickResult::Finished(v) => return Ok(v),
SchedulerTickResult::StepLimitExceeded => {
return Err(format!("step limit {} exceeded", self.config.step_limit));
}
SchedulerTickResult::Pending => {}
}
}
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct UnrollConfig {
pub factor: usize,
pub full_unroll_limit: usize,
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct UnrollResult {
pub fully_unrolled: bool,
pub factor: usize,
pub iterations_saved: usize,
}
impl UnrollResult {
#[allow(dead_code)]
pub fn compute(n: usize, cfg: &UnrollConfig) -> Self {
if n <= cfg.full_unroll_limit {
UnrollResult {
fully_unrolled: true,
factor: n,
iterations_saved: n,
}
} else {
let factor = cfg.factor.min(n);
let remainder = n % factor;
let main_iters = n / factor;
let saved = n - main_iters - remainder;
UnrollResult {
fully_unrolled: false,
factor,
iterations_saved: saved,
}
}
}
}
#[allow(dead_code)]
#[derive(Clone, Debug, Default)]
pub struct EvaluationContext {
pub bindings: std::collections::HashMap<String, u64>,
pub depth: u32,
}
impl EvaluationContext {
#[allow(dead_code)]
pub fn new() -> Self {
Self::default()
}
#[allow(dead_code)]
pub fn bind(&mut self, name: &str, value: u64) {
self.bindings.insert(name.to_string(), value);
}
#[allow(dead_code)]
pub fn lookup(&self, name: &str) -> Option<u64> {
self.bindings.get(name).copied()
}
#[allow(dead_code)]
pub fn child(&self) -> Self {
Self {
bindings: self.bindings.clone(),
depth: self.depth + 1,
}
}
#[allow(dead_code)]
pub fn size(&self) -> usize {
self.bindings.len()
}
}
#[allow(dead_code)]
pub struct PeepholeOptimizer {
rules: Vec<PeepholeRule>,
pub rewrites: usize,
}
impl PeepholeOptimizer {
#[allow(dead_code)]
pub fn new() -> Self {
Self {
rules: Vec::new(),
rewrites: 0,
}
}
#[allow(dead_code)]
pub fn add_rule(&mut self, rule: PeepholeRule) {
self.rules.push(rule);
}
#[allow(dead_code)]
pub fn optimize<'a>(&mut self, opcodes: &[&'a str]) -> Vec<&'a str> {
let mut result: Vec<&'a str> = opcodes.to_vec();
let mut changed = true;
self.rewrites = 0;
while changed {
changed = false;
'outer: for rule in &self.rules {
let plen = rule.pattern.len();
if plen == 0 {
continue;
}
let mut i = 0;
while i + plen <= result.len() {
if result[i..i + plen] == rule.pattern[..] {
let mut new_result = result[..i].to_vec();
new_result.extend_from_slice(&rule.replacement);
new_result.extend_from_slice(&result[i + plen..]);
result = new_result;
self.rewrites += 1;
changed = true;
continue 'outer;
}
i += 1;
}
}
}
result
}
}
pub struct TailCallDetector {
pub tail_positions: Vec<usize>,
}
impl TailCallDetector {
pub fn new() -> Self {
TailCallDetector {
tail_positions: Vec::new(),
}
}
pub fn analyse(&mut self, opcode_names: &[&str]) {
self.tail_positions.clear();
for (i, name) in opcode_names.iter().enumerate() {
if *name == "Call" {
let next = opcode_names.get(i + 1).copied().unwrap_or("");
if next == "Return" || next == "Halt" {
self.tail_positions.push(i);
}
}
}
}
pub fn is_tail(&self, idx: usize) -> bool {
self.tail_positions.contains(&idx)
}
pub fn count(&self) -> usize {
self.tail_positions.len()
}
}
#[allow(dead_code)]
#[derive(Clone, Debug, Default)]
pub struct TcoStatistics {
pub total_runs: u64,
pub total_steps: u64,
pub global_max_depth: u64,
pub step_limit_hits: u64,
}
impl TcoStatistics {
#[allow(dead_code)]
pub fn new() -> Self {
Self::default()
}
#[allow(dead_code)]
pub fn record_run(&mut self, counter: &TailCallCounter, hit_limit: bool) {
self.total_runs += 1;
self.total_steps += counter.optimized;
if counter.max_depth > self.global_max_depth {
self.global_max_depth = counter.max_depth;
}
if hit_limit {
self.step_limit_hits += 1;
}
}
#[allow(dead_code)]
pub fn avg_steps(&self) -> f64 {
if self.total_runs == 0 {
0.0
} else {
self.total_steps as f64 / self.total_runs as f64
}
}
#[allow(dead_code)]
pub fn summary(&self) -> String {
format!(
"TcoStats: runs={}, total_steps={}, max_depth={}, limit_hits={}, avg_steps={:.2}",
self.total_runs,
self.total_steps,
self.global_max_depth,
self.step_limit_hits,
self.avg_steps()
)
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct StackFrame {
pub function: String,
pub locals: Vec<(String, u64)>,
pub return_address: usize,
}
impl StackFrame {
#[allow(dead_code)]
pub fn new(function: &str, return_address: usize) -> Self {
Self {
function: function.to_string(),
locals: Vec::new(),
return_address,
}
}
#[allow(dead_code)]
pub fn bind(&mut self, name: &str, value: u64) {
self.locals.push((name.to_string(), value));
}
#[allow(dead_code)]
pub fn lookup(&self, name: &str) -> Option<u64> {
self.locals
.iter()
.rev()
.find(|(n, _)| n == name)
.map(|(_, v)| *v)
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct InliningThreshold {
pub max_size: usize,
pub max_depth: usize,
pub min_call_count: u64,
}
pub struct RecursiveStep;
impl RecursiveStep {
pub fn run<I, A, F>(initial: I, acc: A, step: F) -> A
where
I: Clone + 'static,
A: Clone + 'static,
F: Fn(I, A) -> Option<(I, A)> + Clone + 'static,
{
fn go<
I: Clone + 'static,
A: Clone + 'static,
F: Fn(I, A) -> Option<(I, A)> + Clone + 'static,
>(
i: I,
a: A,
f: F,
) -> TailCall<A> {
match f(i, a.clone()) {
None => TailCall::Done(a),
Some((next_i, next_a)) => {
let f2 = f.clone();
TailCall::Call(Box::new(move || go(next_i, next_a, f2)))
}
}
}
trampoline(go(initial, acc, step))
}
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BinopKind {
Add,
Sub,
Mul,
Div,
}
impl BinopKind {
#[allow(dead_code)]
pub fn eval(self, lhs: u64, rhs: u64) -> Option<u64> {
match self {
BinopKind::Add => Some(lhs.wrapping_add(rhs)),
BinopKind::Sub => Some(lhs.wrapping_sub(rhs)),
BinopKind::Mul => Some(lhs.wrapping_mul(rhs)),
BinopKind::Div => lhs.checked_div(rhs),
}
}
}
pub enum TailCall<T> {
Done(T),
Call(Box<dyn FnOnce() -> TailCall<T>>),
}
pub enum StepResult<State, Output> {
Continue(State),
Finished(Output),
Error(String),
}
#[allow(dead_code)]
pub struct TailCallOptimizer {
detector: TailCallDetector,
pub stats: TailCallCounter,
}
impl TailCallOptimizer {
#[allow(dead_code)]
pub fn new() -> Self {
Self {
detector: TailCallDetector::new(),
stats: TailCallCounter::new(),
}
}
#[allow(dead_code)]
pub fn analyse_chunk(&mut self, opcodes: &[&str]) -> TailCallAnalysisReport {
self.detector.analyse(opcodes);
let report = TailCallAnalysisReport::build(&self.detector, opcodes);
self.stats.optimized += report.tail_positions.len() as u64;
report
}
#[allow(dead_code)]
pub fn is_tail_call(&self, idx: usize) -> bool {
self.detector.is_tail(idx)
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct TailCallAnalysisReport {
pub tail_positions: Vec<usize>,
pub total_calls: usize,
pub tail_ratio: f64,
pub summary: String,
}
impl TailCallAnalysisReport {
#[allow(dead_code)]
pub fn build(detector: &TailCallDetector, opcodes: &[&str]) -> Self {
let total_calls = opcodes.iter().filter(|&&op| op == "Call").count();
let tail_count = detector.count();
let tail_ratio = if total_calls == 0 {
0.0
} else {
tail_count as f64 / total_calls as f64
};
let summary = format!(
"{}/{} calls ({:.0}%) are in tail position",
tail_count,
total_calls,
tail_ratio * 100.0
);
Self {
tail_positions: detector.tail_positions.clone(),
total_calls,
tail_ratio,
summary,
}
}
}
#[allow(dead_code)]
pub struct ExtendedTailCallDetector {
pub current_function: String,
pub classified: Vec<(usize, TailPositionKind)>,
}
impl ExtendedTailCallDetector {
#[allow(dead_code)]
pub fn new(function_name: &str) -> Self {
Self {
current_function: function_name.to_string(),
classified: Vec::new(),
}
}
#[allow(dead_code)]
pub fn analyse_with_callees(&mut self, ops: &[(&str, Option<&str>)]) {
self.classified.clear();
for (i, (op, callee)) in ops.iter().enumerate() {
if *op != "Call" {
continue;
}
let next = ops.get(i + 1).map(|(o, _)| *o).unwrap_or("");
if next != "Return" && next != "Halt" {
self.classified.push((i, TailPositionKind::NonTail));
continue;
}
let kind = match *callee {
Some(name) if name == self.current_function => TailPositionKind::SelfTailCall,
Some(_) => TailPositionKind::MutualTailCall,
None => TailPositionKind::ExternalTailCall,
};
self.classified.push((i, kind));
}
}
#[allow(dead_code)]
pub fn count_kind(&self, kind: TailPositionKind) -> usize {
self.classified.iter().filter(|(_, k)| *k == kind).count()
}
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TailPositionKind {
SelfTailCall,
MutualTailCall,
ExternalTailCall,
NonTail,
}
#[allow(dead_code)]
pub enum Thunk<T> {
Deferred(Box<dyn FnOnce() -> T>),
Evaluated(T),
}
impl<T: Clone> Thunk<T> {
#[allow(dead_code)]
pub fn defer(f: impl FnOnce() -> T + 'static) -> Self {
Thunk::Deferred(Box::new(f))
}
#[allow(dead_code)]
pub fn force(&mut self) -> &T {
match self {
Thunk::Evaluated(ref v) => v,
Thunk::Deferred(_) => {
let Thunk::Deferred(f) =
std::mem::replace(self, Thunk::Evaluated(unsafe { std::mem::zeroed() }))
else {
unreachable!()
};
let val = f();
*self = Thunk::Evaluated(val);
match self {
Thunk::Evaluated(ref v) => v,
_ => unreachable!(),
}
}
}
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct RewriteRule {
pub lhs: String,
pub rhs: String,
pub unconditional: bool,
}
impl RewriteRule {
#[allow(dead_code)]
pub fn new(lhs: &str, rhs: &str) -> Self {
Self {
lhs: lhs.to_string(),
rhs: rhs.to_string(),
unconditional: true,
}
}
#[allow(dead_code)]
pub fn conditional(lhs: &str, rhs: &str) -> Self {
Self {
lhs: lhs.to_string(),
rhs: rhs.to_string(),
unconditional: false,
}
}
}
#[allow(dead_code)]
#[derive(Clone, Debug, Default)]
pub struct FunctionTcoMetrics {
pub name: String,
pub call_count: u64,
pub max_depth_eliminated: u64,
pub total_steps: u64,
}
impl FunctionTcoMetrics {
#[allow(dead_code)]
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
..Default::default()
}
}
#[allow(dead_code)]
pub fn record(&mut self, depth: u64) {
self.call_count += 1;
self.total_steps += depth;
if depth > self.max_depth_eliminated {
self.max_depth_eliminated = depth;
}
}
#[allow(dead_code)]
pub fn avg_depth(&self) -> f64 {
if self.call_count == 0 {
0.0
} else {
self.total_steps as f64 / self.call_count as f64
}
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct TailCallChain {
pub functions: Vec<String>,
pub can_fuse: bool,
}
impl TailCallChain {
#[allow(dead_code)]
pub fn new() -> Self {
Self {
functions: Vec::new(),
can_fuse: true,
}
}
#[allow(dead_code)]
pub fn push(&mut self, name: &str) {
self.functions.push(name.to_string());
}
#[allow(dead_code)]
pub fn mark_non_fusable(&mut self) {
self.can_fuse = false;
}
#[allow(dead_code)]
pub fn len(&self) -> usize {
self.functions.len()
}
#[allow(dead_code)]
pub fn is_empty(&self) -> bool {
self.functions.is_empty()
}
}
#[allow(dead_code)]
pub struct TailCallOptimizationPass {
pub optimizer: TailCallOptimizer,
pub processed: Vec<String>,
pub skipped: Vec<String>,
}
impl TailCallOptimizationPass {
#[allow(dead_code)]
pub fn new() -> Self {
Self {
optimizer: TailCallOptimizer::new(),
processed: Vec::new(),
skipped: Vec::new(),
}
}
#[allow(dead_code)]
pub fn process_function(
&mut self,
name: &str,
opcodes: &[&str],
skip: bool,
) -> Option<TailCallAnalysisReport> {
if skip {
self.skipped.push(name.to_string());
return None;
}
let report = self.optimizer.analyse_chunk(opcodes);
self.processed.push(name.to_string());
Some(report)
}
#[allow(dead_code)]
pub fn summary(&self) -> String {
format!(
"TCO pass: {} processed, {} skipped, {} tail calls found",
self.processed.len(),
self.skipped.len(),
self.optimizer.stats.optimized
)
}
}
#[derive(Clone, Debug, Default)]
pub struct TailCallCounter {
pub optimized: u64,
pub max_depth: u64,
}
impl TailCallCounter {
pub fn new() -> Self {
Self::default()
}
pub fn record(&mut self, depth: u64) {
self.optimized += 1;
if depth > self.max_depth {
self.max_depth = depth;
}
}
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum InliningDecision {
Inline,
DoNotInline,
ForceInline,
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct TailCallSchedulerConfig {
pub max_steps_per_batch: usize,
pub step_limit: u64,
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct TailCallBenchmarkResult {
pub name: String,
pub iterations: u64,
pub total_ns: u64,
pub value: u64,
}
impl TailCallBenchmarkResult {
#[allow(dead_code)]
pub fn throughput(&self) -> f64 {
if self.total_ns == 0 {
0.0
} else {
self.iterations as f64 / (self.total_ns as f64 / 1_000_000_000.0)
}
}
#[allow(dead_code)]
pub fn report(&self) -> String {
format!(
"Benchmark[{}]: {} iters, {} ns, value={}",
self.name, self.iterations, self.total_ns, self.value
)
}
}
#[allow(dead_code)]
pub struct PeepholeRule {
pub pattern: Vec<&'static str>,
pub replacement: Vec<&'static str>,
pub description: &'static str,
}
impl PeepholeRule {
#[allow(dead_code)]
pub fn new(
pattern: Vec<&'static str>,
replacement: Vec<&'static str>,
description: &'static str,
) -> Self {
Self {
pattern,
replacement,
description,
}
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct TailCallProof {
pub function_name: String,
pub decreasing_argument: String,
pub ordering: String,
pub justification: String,
}
impl TailCallProof {
#[allow(dead_code)]
pub fn new(
function_name: &str,
decreasing_argument: &str,
ordering: &str,
justification: &str,
) -> Self {
Self {
function_name: function_name.to_string(),
decreasing_argument: decreasing_argument.to_string(),
ordering: ordering.to_string(),
justification: justification.to_string(),
}
}
#[allow(dead_code)]
pub fn format(&self) -> String {
format!(
"TCO Proof for `{}`:\n Decreasing: {}\n Ordering: {}\n Justification: {}",
self.function_name, self.decreasing_argument, self.ordering, self.justification
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TailPosition {
Tail,
NonTail,
}
#[allow(dead_code)]
pub struct ExplicitCallStack {
frames: Vec<StackFrame>,
pub max_depth: usize,
}
impl ExplicitCallStack {
#[allow(dead_code)]
pub fn new() -> Self {
Self {
frames: Vec::new(),
max_depth: 0,
}
}
#[allow(dead_code)]
pub fn push(&mut self, frame: StackFrame) {
self.frames.push(frame);
if self.frames.len() > self.max_depth {
self.max_depth = self.frames.len();
}
}
#[allow(dead_code)]
pub fn pop(&mut self) -> Option<StackFrame> {
self.frames.pop()
}
#[allow(dead_code)]
pub fn depth(&self) -> usize {
self.frames.len()
}
#[allow(dead_code)]
pub fn top(&self) -> Option<&StackFrame> {
self.frames.last()
}
#[allow(dead_code)]
pub fn top_mut(&mut self) -> Option<&mut StackFrame> {
self.frames.last_mut()
}
#[allow(dead_code)]
pub fn backtrace(&self) -> Vec<&str> {
self.frames.iter().map(|f| f.function.as_str()).collect()
}
#[allow(dead_code)]
pub fn format_backtrace(&self) -> String {
let mut out = String::from("Backtrace (most recent last):\n");
for (i, frame) in self.frames.iter().enumerate() {
out.push_str(&format!(
" {:3}: {} @ {}\n",
i, frame.function, frame.return_address
));
}
out
}
}
#[allow(dead_code)]
pub enum SchedulerTickResult<T> {
Pending,
Finished(T),
StepLimitExceeded,
}
#[allow(dead_code)]
pub enum MutualTailCall<A, B, R> {
GoA(A, Box<dyn FnOnce(A) -> MutualTailCall<A, B, R>>),
GoB(B, Box<dyn FnOnce(B) -> MutualTailCall<A, B, R>>),
Done(R),
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub enum ContinuationFrame {
ApplyBinop { op: BinopKind, operand: u64 },
StoreResult { var: String },
PrintResult,
}
#[allow(dead_code)]
#[derive(Clone, Debug, PartialEq)]
pub enum PartialValue {
Known(u64),
Unknown(String),
Bottom,
}
impl PartialValue {
#[allow(dead_code)]
pub fn is_known(&self) -> bool {
matches!(self, PartialValue::Known(_))
}
#[allow(dead_code)]
pub fn add(&self, other: &PartialValue) -> PartialValue {
match (self, other) {
(PartialValue::Known(a), PartialValue::Known(b)) => {
PartialValue::Known(a.wrapping_add(*b))
}
(PartialValue::Bottom, _) | (_, PartialValue::Bottom) => PartialValue::Bottom,
_ => PartialValue::Unknown(format!("{:?} + {:?}", self, other)),
}
}
#[allow(dead_code)]
pub fn mul(&self, other: &PartialValue) -> PartialValue {
match (self, other) {
(PartialValue::Known(a), PartialValue::Known(b)) => {
PartialValue::Known(a.wrapping_mul(*b))
}
(PartialValue::Known(0), _) | (_, PartialValue::Known(0)) => PartialValue::Known(0),
(PartialValue::Bottom, _) | (_, PartialValue::Bottom) => PartialValue::Bottom,
_ => PartialValue::Unknown(format!("{:?} * {:?}", self, other)),
}
}
}
#[allow(dead_code)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct StateMachineState {
pub id: u32,
pub output: Vec<String>,
}
impl StateMachineState {
#[allow(dead_code)]
pub fn new(id: u32) -> Self {
Self {
id,
output: Vec::new(),
}
}
#[allow(dead_code)]
pub fn emit(mut self, s: &str) -> Self {
self.output.push(s.to_string());
self
}
}
#[allow(dead_code)]
pub struct ContinuationEvaluator {
pub values: Vec<u64>,
pub continuations: Vec<ContinuationFrame>,
pub env: std::collections::HashMap<String, u64>,
}
impl ContinuationEvaluator {
#[allow(dead_code)]
pub fn new() -> Self {
Self {
values: Vec::new(),
continuations: Vec::new(),
env: std::collections::HashMap::new(),
}
}
#[allow(dead_code)]
pub fn push_value(&mut self, v: u64) {
self.values.push(v);
}
#[allow(dead_code)]
pub fn pop_value(&mut self) -> Option<u64> {
self.values.pop()
}
#[allow(dead_code)]
pub fn push_cont(&mut self, frame: ContinuationFrame) {
self.continuations.push(frame);
}
#[allow(dead_code)]
pub fn step(&mut self) -> bool {
let frame = match self.continuations.pop() {
Some(f) => f,
None => return false,
};
match frame {
ContinuationFrame::ApplyBinop { op, operand } => {
if let Some(lhs) = self.values.pop() {
if let Some(result) = op.eval(lhs, operand) {
self.values.push(result);
}
}
}
ContinuationFrame::StoreResult { var } => {
if let Some(v) = self.values.last().copied() {
self.env.insert(var, v);
}
}
ContinuationFrame::PrintResult => {}
}
true
}
#[allow(dead_code)]
pub fn run(&mut self) {
while self.step() {}
}
}