use std::collections::BTreeMap;
use nebu::Goldilocks;
use super::layout::*;
pub(crate) type Row = [Goldilocks; W1];
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MachineError {
Native(&'static str),
Unsupported(&'static str),
Budget,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Entry {
Atom(u64),
Pair(u64, u64),
Frame(u64, [u64; 4]),
State(u64, u64, u64),
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct MemOp {
pub row: usize,
pub slot: usize,
pub tuple: [u64; 6],
pub write: bool,
}
#[derive(Default)]
pub(crate) struct Builder {
pub rows: Vec<Row>,
pub ops: Vec<MemOp>,
pub store: Vec<Option<Entry>>,
pub alloc: u64,
pub need_dig: Vec<u64>,
pub need_hop: Vec<u64>,
}
pub(crate) fn g(v: u64) -> Goldilocks {
Goldilocks::new(v)
}
impl Builder {
pub fn row(&mut self, kind: usize) -> usize {
let mut r = [Goldilocks::ZERO; W1];
r[kind] = Goldilocks::ONE;
self.rows.push(r);
self.rows.len() - 1
}
pub fn set(&mut self, r: usize, col: usize, v: u64) {
self.rows[r][col] = g(v);
}
pub fn setf(&mut self, r: usize, col: usize, v: Goldilocks) {
self.rows[r][col] = v;
}
fn access(&mut self, r: usize, s: usize, tag: u64, key: u64, p: [u64; 4], write: bool) {
self.set(r, slot(s, KEY), key);
for (i, &v) in p.iter().enumerate() {
self.set(r, slot(s, P0 + i), v);
}
self.ops.push(MemOp {
row: r,
slot: s,
tuple: [tag, key, p[0], p[1], p[2], p[3]],
write,
});
}
pub fn read(&mut self, r: usize, s: usize, tag: u64, key: u64, p: [u64; 4]) {
self.access(r, s, tag, key, p, false);
}
pub fn write(&mut self, r: usize, s: usize, tag: u64, key: u64, p: [u64; 4]) {
self.access(r, s, tag, key, p, true);
}
pub fn entry(&self, id: u64) -> Option<Entry> {
self.store.get(id as usize).copied().flatten()
}
pub fn put(&mut self, id: u64, e: Entry) {
let i = id as usize;
if self.store.len() <= i {
self.store.resize(i + 1, None);
}
self.store[i] = Some(e);
}
pub fn fresh(&mut self) -> u64 {
let id = self.alloc;
self.alloc += 1;
id
}
pub fn atom(&self, id: u64) -> Result<u64, MachineError> {
match self.entry(id) {
Some(Entry::Atom(v)) => Ok(v),
_ => Err(MachineError::Native("type error: atom expected")),
}
}
pub fn pair(&self, id: u64) -> Result<(u64, u64), MachineError> {
match self.entry(id) {
Some(Entry::Pair(l, r)) => Ok((l, r)),
_ => Err(MachineError::Native("pair expected")),
}
}
pub fn alloc_noun(&mut self, r: usize, s: usize, id: u64, e: Entry) {
match e {
Entry::Atom(v) => self.write(r, s, TAG_ATOM, id, [v, 0, 0, 0]),
Entry::Pair(a, b) => self.write(r, s, TAG_PAIR, id, [a, b, 0, 0]),
Entry::State(ns, k, v) => self.write(r, s, TAG_STATE, id, [ns, k, v, 0]),
Entry::Frame(..) => unreachable!("frames go through alloc_frame"),
}
self.put(id, e);
}
pub fn alloc_frame(&mut self, r: usize, s: usize, id: u64, tag: u64, p: [u64; 4]) {
self.write(r, s, tag, id, p);
self.put(id, Entry::Frame(tag, p));
}
pub fn read_noun(&mut self, r: usize, s: usize, id: u64) -> Result<Entry, MachineError> {
let e = self
.entry(id)
.ok_or(MachineError::Native("dangling noun"))?;
match e {
Entry::Atom(v) => self.read(r, s, TAG_ATOM, id, [v, 0, 0, 0]),
Entry::Pair(a, b) => self.read(r, s, TAG_PAIR, id, [a, b, 0, 0]),
Entry::Frame(..) | Entry::State(..) => {
return Err(MachineError::Native("frame read as noun"));
}
}
Ok(e)
}
}
pub(crate) fn intern(
noun: &crate::execution::ExecutionNoun,
ids: &mut BTreeMap<(u64, u64, u64), u64>,
order: &mut Vec<Entry>,
) -> u64 {
use crate::execution::ExecutionNoun as N;
let key = match noun {
N::Atom(v) => (0, *v, 0),
N::Pair(a, b) => {
let l = intern(a, ids, order);
let r = intern(b, ids, order);
(1, l, r)
}
};
if let Some(&id) = ids.get(&key) {
return id;
}
order.push(if key.0 == 0 {
Entry::Atom(key.1)
} else {
Entry::Pair(key.1, key.2)
});
let id = order.len() as u64;
ids.insert(key, id);
id
}
pub(crate) fn frame_flag(tag: u64) -> Option<usize> {
if let Some(&(f, _)) = FRAME_TAGS.iter().find(|&&(_, t)| t == tag) {
return Some(f);
}
if (TAG_B1..TAG_B2).contains(&tag) {
return matches!(tag - TAG_B1, 5..=7 | 9..=12 | 14 | 17).then_some(F_B1);
}
match tag.checked_sub(TAG_B2)? {
5..=7 => Some(F_B2AR),
10..=14 => Some(F_B2W),
_ => None,
}
}
pub(crate) fn op_of(tag: u64) -> Option<(usize, u64)> {
if WORD_TAGS.contains(&tag) {
return Some((OP_WORD, WORD_COST));
}
OPS.iter()
.find(|&&(_, t, _)| t == tag)
.map(|&(o, _, c)| (FLAG0 + o, c))
}
pub type WitnessFn<'a> =
&'a dyn Fn(u64, &crate::execution::ExecutionNoun) -> Option<crate::execution::ExecutionNoun>;
#[derive(Clone, Copy, Default)]
pub struct Hints<'a> {
pub witness: Option<WitnessFn<'a>>,
pub state: Option<(
&'a crate::execution::state_evidence::StateEvidence,
[u64; 4],
)>,
}