#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use crate::hints::Hint;
use crate::instructions::Instruction;
#[cfg(test)]
#[path = "inline_test.rs"]
mod test;
#[macro_export]
macro_rules! casm {
{$($tok:tt)*} => {
{
let mut ctx = $crate::inline::CasmContext::default();
$crate::casm_extend!(ctx, $($tok)*);
ctx
}
}
}
#[macro_export]
macro_rules! casm_extend {
($ctx:ident,) => {};
($ctx:ident, $dst:tt = $a:tt $(+ $b0:tt)? $(* $b1:tt)? $(,$ap:ident++)? ; $($tok:tt)*) => {
let body = $crate::instructions::InstructionBody::AssertEq(
$crate::instructions::AssertEqInstruction {
a: $crate::deref!($dst),
b: $crate::res!($a $(+ $b0)? $(* $b1)?),
}
);
$crate::append_instruction!($ctx, body $(,$ap++)?);
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, call rel $target:tt $(,$ap:ident++)? ; $($tok:tt)*) => {
let body = $crate::instructions::InstructionBody::Call(
$crate::instructions::CallInstruction {
target: $crate::deref_or_immediate!($target),
relative: true,
}
);
$crate::append_instruction!($ctx, body $(,$ap++)?);
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, call abs $target:tt $(,$ap:ident++)? ; $($tok:tt)*) => {
let body = $crate::instructions::InstructionBody::Call(
$crate::instructions::CallInstruction {
target: $crate::deref_or_immediate!($target),
relative: false,
}
);
$crate::append_instruction!($ctx, body $(,$ap++)?);
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, jmp rel $target:expr $(,$ap:ident++)? ; $($tok:tt)*) => {
let body = $crate::instructions::InstructionBody::Jump(
$crate::instructions::JumpInstruction {
target: $crate::deref_or_immediate!($target),
relative: true,
}
);
$crate::append_instruction!($ctx, body $(,$ap++)?);
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, jmp abs $target:tt $(,$ap:ident++)? ; $($tok:tt)*) => {
let body = $crate::instructions::InstructionBody::Jump(
$crate::instructions::JumpInstruction {
target: $crate::deref_or_immediate!($target),
relative: false,
}
);
$crate::append_instruction!($ctx, body $(,$ap++)?);
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, jmp rel $target:tt if $cond:tt != 0 $(,$ap:ident++)? ; $($tok:tt)*) => {
let body = $crate::instructions::InstructionBody::Jnz(
$crate::instructions::JnzInstruction {
jump_offset: $crate::deref_or_immediate!($target),
condition: $crate::deref!($cond),
}
);
$crate::append_instruction!($ctx, body $(,$ap++)?);
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, jmp $target:tt if $cond:tt != 0 $(,$ap:ident++)? ; $($tok:tt)*) => {
let body = $crate::instructions::InstructionBody::Jnz(
$crate::instructions::JnzInstruction {
jump_offset: $crate::deref_or_immediate!($target),
condition: $crate::deref!($cond),
}
);
$crate::append_instruction!($ctx, body $(,$ap++)?);
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, ap += $operand:tt $(,$ap:ident++)? ; $($tok:tt)*) => {
let body = $crate::instructions::InstructionBody::AddAp(
$crate::instructions::AddApInstruction { operand: $crate::res!($operand) }
);
$crate::append_instruction!($ctx, body $(,$ap++)?);
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, ret $(,$ap:ident++)? ; $($tok:tt)*) => {
let body = $crate::instructions::InstructionBody::Ret(
$crate::instructions::RetInstruction {}
);
$crate::append_instruction!($ctx, body $(,$ap++)?);
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, %{ memory $dst:tt = segments . add ( ) %} $($tok:tt)*) => {
$ctx.current_hints.push($crate::hints::CoreHint::AllocSegment{dst: $crate::deref!($dst)}.into());
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, %{ memory $dst:tt = memory $lhs:tt < memory $rhs:tt %} $($tok:tt)*) => {
$ctx.current_hints.push($crate::hints::CoreHint::TestLessThan{
lhs: $crate::res!($lhs),
rhs: $crate::res!($rhs),
dst: $crate::deref!($dst),
}.into());
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, %{ memory $dst:tt = memory $lhs:tt < $rhs:tt %} $($tok:tt)*) => {
$ctx.current_hints.push($crate::hints::CoreHint::TestLessThan{
lhs: $crate::res!($lhs),
rhs: $crate::res!($rhs),
dst: $crate::deref!($dst),
}.into());
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, %{ memory $dst:tt = $lhs:tt < memory $rhs:tt %} $($tok:tt)*) => {
$ctx.current_hints.push($crate::hints::CoreHint::TestLessThan{
lhs: $crate::res!($lhs),
rhs: $crate::res!($rhs),
dst: $crate::deref!($dst),
}.into());
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, %{ memory $dst:tt = memory $lhs:tt <= memory $rhs:tt %} $($tok:tt)*) => {
$ctx.current_hints.push($crate::hints::CoreHint::TestLessThanOrEqual{
lhs: $crate::res!($lhs),
rhs: $crate::res!($rhs),
dst: $crate::deref!($dst),
}.into());
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, %{ memory $dst:tt = memory $lhs:tt <= $rhs:tt %} $($tok:tt)*) => {
$ctx.current_hints.push($crate::hints::CoreHint::TestLessThanOrEqual{
lhs: $crate::res!($lhs),
rhs: $crate::res!($rhs),
dst: $crate::deref!($dst),
}.into());
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, %{ memory $dst:tt = $lhs:tt <= memory $rhs:tt %} $($tok:tt)*) => {
$ctx.current_hints.push($crate::hints::CoreHint::TestLessThanOrEqual{
lhs: $crate::res!($lhs),
rhs: $crate::res!($rhs),
dst: $crate::deref!($dst),
}.into());
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, %{ ( memory $quotient:tt, memory $remainder:tt )
= divmod ( memory $lhs:tt , memory $rhs:tt ) %} $($tok:tt)*) => {
$ctx.current_hints.push($crate::hints::CoreHint::DivMod{
lhs: $crate::res!($lhs),
rhs: $crate::res!($rhs),
quotient: $crate::deref!($quotient),
remainder: $crate::deref!($remainder),
}.into());
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, %{ ( memory $quotient:tt, memory $remainder:tt )
= divmod ( memory $lhs:tt , $rhs:tt ) %} $($tok:tt)*) => {
$ctx.current_hints.push($crate::hints::CoreHint::DivMod{
lhs: $crate::res!($lhs),
rhs: $crate::res!($rhs),
quotient: $crate::deref!($quotient),
remainder: $crate::deref!($remainder),
}.into());
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, %{ ( memory $quotient:tt, memory $remainder:tt )
= divmod ( $lhs:tt , memory $rhs:tt ) %} $($tok:tt)*) => {
$ctx.current_hints.push($crate::hints::CoreHint::DivMod{
lhs: $crate::res!($lhs),
rhs: $crate::res!($rhs),
quotient: $crate::deref!($quotient),
remainder: $crate::deref!($remainder),
}.into());
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, %{ ( memory $quotient:tt, memory $remainder:tt )
= divmod ( $lhs:tt , $rhs:tt ) %} $($tok:tt)*) => {
$ctx.current_hints.push($crate::hints::CoreHint::DivMod{
lhs: $crate::res!($lhs),
rhs: $crate::res!($rhs),
quotient: $crate::deref!($quotient),
remainder: $crate::deref!($remainder),
}.into());
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, %{ syscall_handler.syscall(syscall_ptr=memory $addr:tt + $offset:tt) %} $($tok:tt)*) => {
$ctx.current_hints.push($crate::hints::CoreHint::SystemCall {
system: $crate::operand::ResOperand::BinOp($crate::operand::BinOpOperand {
op: cairo_lang_casm::operand::Operation::Add,
a: $crate::deref!($addr),
b: $crate::deref_or_immediate!(num_bigint::BigInt::from($offset)),
})}.into());
$crate::casm_extend!($ctx, $($tok)*)
};
($ctx:ident, %{ syscall_handler.syscall(syscall_ptr=memory $addr:tt) %} $($tok:tt)*) => {
$ctx.current_hints.push($crate::hints::CoreHint::SystemCall {
system: $crate::operand::ResOperand::Deref($crate::deref!($addr))
}.into());
$crate::casm_extend!($ctx, $($tok)*)
};
}
#[macro_export]
macro_rules! append_instruction {
($ctx:ident, $body:ident $(,$ap:ident++)?) => {
let current_hints = core::mem::take(&mut $ctx.current_hints);
let instr = $crate::instructions::Instruction {
body: $body,
inc_ap: $crate::is_inc_ap!($($ap++)?),
hints: current_hints,
};
$ctx.current_code_offset += instr.body.op_size();
$ctx.instructions.push(instr);
};
}
#[macro_export]
macro_rules! is_inc_ap {
() => {
false
};
(ap + +) => {
true
};
}
#[derive(Default)]
pub struct CasmContext {
pub current_code_offset: usize,
pub current_hints: Vec<Hint>,
pub instructions: Vec<Instruction>,
}
#[macro_export]
macro_rules! deref {
([ap + $offset:expr]) => {
$crate::operand::CellRef { register: $crate::reg!(ap), offset: $offset }
};
([fp + $offset:expr]) => {
$crate::operand::CellRef { register: $crate::reg!(fp), offset: $offset }
};
([& $var:ident + $offset:expr]) => {
$crate::operand::CellRef { register: $var.register, offset: $var.offset + $offset }
};
([ap - $offset:expr]) => {
$crate::operand::CellRef { register: $crate::reg!(ap), offset: -$offset }
};
([fp - $offset:expr]) => {
$crate::operand::CellRef { register: $crate::reg!(fp), offset: -$offset }
};
([& $var:ident - $offset:expr]) => {
$crate::operand::CellRef { register: $var.register, offset: $var.offset - $offset }
};
([ap]) => {
$crate::operand::CellRef { register: $crate::reg!(ap), offset: 0 }
};
([fp]) => {
$crate::operand::CellRef { register: $crate::reg!(fp), offset: 0 }
};
([& $var:ident]) => {
$var
};
($a:expr) => {
$a
};
}
#[macro_export]
macro_rules! reg {
(ap) => {
$crate::operand::Register::AP
};
(fp) => {
$crate::operand::Register::FP
};
}
#[macro_export]
macro_rules! deref_or_immediate {
([$a:ident $($op:tt $offset:expr)?]) => {
$crate::operand::DerefOrImmediate::Deref($crate::deref!([$a $($op $offset)?]))
};
($a:expr) => {
$crate::operand::DerefOrImmediate::from($a)
};
}
#[macro_export]
macro_rules! res {
($a:tt + $b:tt) => {
$crate::operand::ResOperand::BinOp($crate::operand::BinOpOperand {
op: $crate::operand::Operation::Add,
a: $crate::deref!($a),
b: $crate::deref_or_immediate!($b),
})
};
($a:tt * $b:tt) => {
$crate::operand::ResOperand::BinOp($crate::operand::BinOpOperand {
op: $crate::operand::Operation::Mul,
a: $crate::deref!($a),
b: $crate::deref_or_immediate!($b),
})
};
([[ap $($op:tt $offset:expr)?]]) => {
$crate::operand::ResOperand::DoubleDeref($crate::deref!([ap $($op $offset)?]), 0)
};
([[ap $($op:tt $offset:expr)?] + $outer:expr]) => {
$crate::operand::ResOperand::DoubleDeref($crate::deref!([ap $($op $offset)?]), $outer)
};
([[ap $($op:tt $offset:expr)?] - $outer:expr]) => {
$crate::operand::ResOperand::DoubleDeref($crate::deref!([ap $($op $offset)?]), -$outer)
};
([[fp $($op:tt $offset:expr)?]]) => {
$crate::operand::ResOperand::DoubleDeref($crate::deref!([fp $($op $offset)?]), 0)
};
([[fp $($op:tt $offset:expr)?] + $outer:expr]) => {
$crate::operand::ResOperand::DoubleDeref($crate::deref!([fp $($op $offset)?]), $outer)
};
([[fp $($op:tt $offset:expr)?] - $outer:expr]) => {
$crate::operand::ResOperand::DoubleDeref($crate::deref!([fp $($op $offset)?]), -$outer)
};
([[&$a:expr]]) => {
$crate::operand::ResOperand::DoubleDeref($a, 0)
};
([[&$a:expr] + $b:expr]) => {
$crate::operand::ResOperand::DoubleDeref($a, $b)
};
($a:tt) => {
$crate::operand::ResOperand::from($crate::deref_or_immediate!($a))
};
}