use alloc::{string::String, sync::Arc};
use midenc_hir::{SourceSpan, Type};
use super::{OpEmitter, masm};
impl OpEmitter<'_> {
pub fn mtree_get(&mut self, span: SourceSpan) {
self.felt_stack_transform(masm::Instruction::MTreeGet, 6, 8, "Merkle", span);
}
pub fn mtree_set(&mut self, span: SourceSpan) {
self.felt_stack_transform(masm::Instruction::MTreeSet, 10, 8, "Merkle", span);
}
pub fn mtree_merge(&mut self, span: SourceSpan) {
self.felt_stack_transform(masm::Instruction::MTreeMerge, 8, 4, "Merkle", span);
}
pub fn mtree_verify(&mut self, message: Option<&str>, span: SourceSpan) {
for index in 0..10 {
let operand = self.stack.get(index).expect("operand stack is empty");
assert_eq!(operand.ty(), Type::Felt, "expected Merkle operand to be felt");
}
let instruction = match message.filter(|message| !message.is_empty()) {
Some(message) => Self::mtree_verify_with_message_inst(message.to_owned(), span),
None => masm::Instruction::MTreeVerify,
};
self.emit(instruction, span);
}
fn mtree_verify_with_message_inst(message: String, span: SourceSpan) -> masm::Instruction {
masm::Instruction::MTreeVerifyWithError(masm::Immediate::Value(masm::Span::new(
span,
Arc::<str>::from(message),
)))
}
}
#[cfg(test)]
mod tests {
use alloc::{collections::BTreeSet, rc::Rc};
use midenc_hir::Context;
use super::*;
use crate::{OperandStack, masm::Op};
fn emitter_with_felts(count: usize) -> (Vec<masm::Op>, OperandStack, BTreeSet<masm::Invoke>) {
let block = Vec::default();
let context = Rc::new(Context::default());
let mut stack = OperandStack::new(context);
for _ in 0..count {
stack.push(Type::Felt);
}
(block, stack, BTreeSet::default())
}
#[test]
fn merkle_stack_transforms_emit_vm_instructions() {
let span = SourceSpan::default();
for (inputs, outputs, instruction) in [
(6, 8, masm::Instruction::MTreeGet),
(10, 8, masm::Instruction::MTreeSet),
(8, 4, masm::Instruction::MTreeMerge),
] {
let (mut block, mut stack, mut invoked) = emitter_with_felts(inputs);
let mut emitter = OpEmitter::new(&mut invoked, &mut block, &mut stack);
match instruction {
masm::Instruction::MTreeGet => emitter.mtree_get(span),
masm::Instruction::MTreeSet => emitter.mtree_set(span),
masm::Instruction::MTreeMerge => emitter.mtree_merge(span),
_ => unreachable!(),
}
assert_eq!(emitter.stack_len(), outputs);
assert!(emitter.stack().iter().all(|ty| *ty == Type::Felt));
assert_eq!(&block[0], &Op::Inst(masm::Span::new(span, instruction)));
}
}
#[test]
fn mtree_verify_emits_vm_instruction_without_changing_stack() {
let (mut block, mut stack, mut invoked) = emitter_with_felts(10);
let mut emitter = OpEmitter::new(&mut invoked, &mut block, &mut stack);
let span = SourceSpan::default();
emitter.mtree_verify(None, span);
assert_eq!(emitter.stack_len(), 10);
assert_eq!(&block[0], &Op::Inst(masm::Span::new(span, masm::Instruction::MTreeVerify)));
}
#[test]
fn mtree_verify_with_message_preserves_diagnostic() {
let (mut block, mut stack, mut invoked) = emitter_with_felts(10);
let mut emitter = OpEmitter::new(&mut invoked, &mut block, &mut stack);
let span = SourceSpan::default();
emitter.mtree_verify(Some("bad path"), span);
let Op::Inst(inst) = &block[0] else {
panic!("expected instruction")
};
let masm::Instruction::MTreeVerifyWithError(masm::Immediate::Value(message)) = inst.inner()
else {
panic!("expected mtree_verify.err instruction")
};
assert_eq!(message.inner().as_ref(), "bad path");
}
}