triton_isa/
lib.rs

1// See the corresponding attribute in triton_vm/lib.rs
2#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
3
4pub use twenty_first;
5
6pub mod error;
7pub mod instruction;
8pub mod op_stack;
9pub mod parser;
10pub mod program;
11
12/// Compile an entire program written in [Triton assembly][tasm].
13/// Triton VM can run the resulting [`Program`](program::Program); see there for
14/// details.
15///
16/// It is possible to use string-like interpolation to insert instructions,
17/// arguments, labels, or other substrings into the program.
18///
19/// # Examples
20///
21/// ```
22/// # use triton_isa::triton_program;
23/// # use twenty_first::prelude::*;
24/// let program = triton_program!(
25///     read_io 1 push 5 mul
26///     call check_eq_15
27///     push 17 write_io 1
28///     halt
29///     // assert that the top of the stack is 15
30///     check_eq_15:
31///         push 15 eq assert
32///         return
33/// );
34/// ```
35///
36/// Any type with an appropriate [`Display`](std::fmt::Display) implementation
37/// can be interpolated. This includes, for example, primitive types like `u64`
38/// and `&str`, but also [`Instruction`](instruction::Instruction)s,
39/// [`BFieldElement`](twenty_first::prelude::BFieldElement)s, and
40/// [`Label`](instruction::LabelledInstruction)s, among others.
41///
42/// ```
43/// # use twenty_first::prelude::*;
44/// # use triton_isa::triton_program;
45/// # use triton_isa::instruction::Instruction;
46/// let element_0 = BFieldElement::new(0);
47/// let label = "my_label";
48/// let instruction_push = Instruction::Push(bfe!(42));
49/// let dup_arg = 1;
50/// let program = triton_program!(
51///     push {element_0}
52///     call {label} halt
53///     {label}:
54///        {instruction_push}
55///        dup {dup_arg}
56///        skiz recurse return
57/// );
58/// ```
59///
60/// # Panics
61///
62/// **Panics** if the program cannot be parsed.
63/// Examples for parsing errors are:
64/// - unknown (_e.g._ misspelled) instructions
65/// - invalid instruction arguments, _e.g._, `push 1.5` or `swap 42`
66/// - missing or duplicate labels
67/// - invalid labels, _e.g._, using a reserved keyword or starting a label with
68///   a digit
69///
70/// For a version that returns a `Result`, see
71/// [`Program::from_code()`][from_code].
72///
73/// [tasm]: https://triton-vm.org/spec/instructions.html
74/// [from_code]: program::Program::from_code
75#[macro_export]
76macro_rules! triton_program {
77    {$($source_code:tt)*} => {{
78        let labelled_instructions = $crate::triton_asm!($($source_code)*);
79        $crate::program::Program::new(&labelled_instructions)
80    }};
81}
82
83/// Compile [Triton assembly][tasm] into a list of labelled
84/// [`Instruction`](instruction::LabelledInstruction)s.
85/// Similar to [`triton_program!`](triton_program), it is possible to use
86/// string-like interpolation to insert instructions, arguments, labels, or
87/// other expressions.
88///
89/// Similar to [`vec!`], a single instruction can be repeated a specified number
90/// of times.
91///
92/// Furthermore, a list of
93/// [`LabelledInstruction`](instruction::LabelledInstruction)s can be inserted
94/// like so: `{&list}`.
95///
96/// The labels for instruction `call`, if any, are also parsed. Instruction
97/// `call` can refer to a label defined later in the program, _i.e.,_ labels are
98/// not checked for existence or uniqueness by this parser.
99///
100/// # Examples
101///
102/// ```
103/// # use triton_isa::triton_asm;
104/// let push_argument = 42;
105/// let instructions = triton_asm!(
106///     push 1 call some_label
107///     push {push_argument}
108///     some_other_label: skiz halt return
109/// );
110/// assert_eq!(7, instructions.len());
111/// ```
112///
113/// One instruction repeated several times:
114///
115/// ```
116/// # use triton_isa::triton_asm;
117/// # use triton_isa::instruction::LabelledInstruction;
118/// # use triton_isa::instruction::AnInstruction::SpongeAbsorb;
119/// let instructions = triton_asm![sponge_absorb; 3];
120/// assert_eq!(3, instructions.len());
121/// assert_eq!(LabelledInstruction::Instruction(SpongeAbsorb), instructions[0]);
122/// assert_eq!(LabelledInstruction::Instruction(SpongeAbsorb), instructions[1]);
123/// assert_eq!(LabelledInstruction::Instruction(SpongeAbsorb), instructions[2]);
124/// ```
125///
126/// Inserting substring of labelled instructions:
127///
128/// ```
129/// # use triton_isa::instruction::AnInstruction::Push;
130/// # use triton_isa::instruction::AnInstruction::Pop;
131/// # use triton_isa::op_stack::NumberOfWords::N1;
132/// # use triton_isa::instruction::LabelledInstruction;
133/// # use triton_isa::triton_asm;
134/// # use twenty_first::prelude::*;
135/// let insert_me = triton_asm!(
136///     pop 1
137///     nop
138///     pop 1
139/// );
140/// let surrounding_code = triton_asm!(
141///     push 0
142///     {&insert_me}
143///     push 1
144/// );
145/// # let zero = bfe!(0);
146/// # assert_eq!(LabelledInstruction::Instruction(Push(zero)), surrounding_code[0]);
147/// assert_eq!(LabelledInstruction::Instruction(Pop(N1)), surrounding_code[1]);
148/// assert_eq!(LabelledInstruction::Instruction(Pop(N1)), surrounding_code[3]);
149/// # let one = bfe!(1);
150/// # assert_eq!(LabelledInstruction::Instruction(Push(one)), surrounding_code[4]);
151/// ```
152///
153/// # Panics
154///
155/// **Panics** if the instructions cannot be parsed.
156/// For examples, see [`triton_program!`](triton_program), with the exception
157/// that labels are not checked for existence or uniqueness.
158///
159/// [tasm]: https://triton-vm.org/spec/instructions.html
160#[macro_export]
161macro_rules! triton_asm {
162    (@fmt $fmt:expr, $($args:expr,)*; ) => {
163        format_args!($fmt $(,$args)*).to_string()
164    };
165    (@fmt $fmt:expr, $($args:expr,)*;
166        hint $var:ident: $ty:ident = stack[$start:literal..$end:literal] $($tail:tt)*) => {
167        $crate::triton_asm!(@fmt
168            concat!($fmt, " hint {}: {} = stack[{}..{}] "),
169            $($args,)* stringify!($var), stringify!($ty), $start, $end,;
170            $($tail)*
171        )
172    };
173    (@fmt $fmt:expr, $($args:expr,)*;
174        hint $var:ident = stack[$start:literal..$end:literal] $($tail:tt)*) => {
175        $crate::triton_asm!(@fmt
176            concat!($fmt, " hint {} = stack[{}..{}] "),
177            $($args,)* stringify!($var), $start, $end,;
178            $($tail)*
179        )
180    };
181    (@fmt $fmt:expr, $($args:expr,)*;
182        hint $var:ident: $ty:ident = stack[$index:literal] $($tail:tt)*) => {
183        $crate::triton_asm!(@fmt
184            concat!($fmt, " hint {}: {} = stack[{}] "),
185            $($args,)* stringify!($var), stringify!($ty), $index,;
186            $($tail)*
187        )
188    };
189    (@fmt $fmt:expr, $($args:expr,)*;
190        hint $var:ident = stack[$index:literal] $($tail:tt)*) => {
191        $crate::triton_asm!(@fmt
192            concat!($fmt, " hint {} = stack[{}] "),
193            $($args,)* stringify!($var), $index,;
194            $($tail)*
195        )
196    };
197    (@fmt $fmt:expr, $($args:expr,)*; $label_declaration:ident: $($tail:tt)*) => {
198        $crate::triton_asm!(@fmt
199            concat!($fmt, " ", stringify!($label_declaration), ": "), $($args,)*; $($tail)*
200        )
201    };
202    (@fmt $fmt:expr, $($args:expr,)*; $instruction:ident $($tail:tt)*) => {
203        $crate::triton_asm!(@fmt
204            concat!($fmt, " ", stringify!($instruction), " "), $($args,)*; $($tail)*
205        )
206    };
207    (@fmt $fmt:expr, $($args:expr,)*; $instruction_argument:literal $($tail:tt)*) => {
208        $crate::triton_asm!(@fmt
209            concat!($fmt, " ", stringify!($instruction_argument), " "), $($args,)*; $($tail)*
210        )
211    };
212    (@fmt $fmt:expr, $($args:expr,)*; {$label_declaration:expr}: $($tail:tt)*) => {
213        $crate::triton_asm!(@fmt concat!($fmt, "{}: "), $($args,)* $label_declaration,; $($tail)*)
214    };
215    (@fmt $fmt:expr, $($args:expr,)*; {&$instruction_list:expr} $($tail:tt)*) => {
216        $crate::triton_asm!(@fmt
217            concat!($fmt, "{} "), $($args,)*
218            $instruction_list.iter().map(|instr| instr.to_string()).collect::<Vec<_>>().join(" "),;
219            $($tail)*
220        )
221    };
222    (@fmt $fmt:expr, $($args:expr,)*; {$expression:expr} $($tail:tt)*) => {
223        $crate::triton_asm!(@fmt concat!($fmt, "{} "), $($args,)* $expression,; $($tail)*)
224    };
225
226    // repeated instructions
227    [pop $arg:literal; $num:expr] => { vec![ $crate::triton_instr!(pop $arg); $num ] };
228    [push $arg:literal; $num:expr] => { vec![ $crate::triton_instr!(push $arg); $num ] };
229    [divine $arg:literal; $num:expr] => { vec![ $crate::triton_instr!(divine $arg); $num ] };
230    [pick $arg:literal; $num:expr] => { vec![ $crate::triton_instr!(pick $arg); $num ] };
231    [place $arg:literal; $num:expr] => { vec![ $crate::triton_instr!(place $arg); $num ] };
232    [dup $arg:literal; $num:expr] => { vec![ $crate::triton_instr!(dup $arg); $num ] };
233    [swap $arg:literal; $num:expr] => { vec![ $crate::triton_instr!(swap $arg); $num ] };
234    [call $arg:ident; $num:expr] => { vec![ $crate::triton_instr!(call $arg); $num ] };
235    [read_mem $arg:literal; $num:expr] => { vec![ $crate::triton_instr!(read_mem $arg); $num ] };
236    [write_mem $arg:literal; $num:expr] => { vec![ $crate::triton_instr!(write_mem $arg); $num ] };
237    [read_io $arg:literal; $num:expr] => { vec![ $crate::triton_instr!(read_io $arg); $num ] };
238    [write_io $arg:literal; $num:expr] => { vec![ $crate::triton_instr!(write_io $arg); $num ] };
239    [$instr:ident; $num:expr] => { vec![ $crate::triton_instr!($instr); $num ] };
240
241    // entry point
242    {$($source_code:tt)*} => {{
243        let source_code = $crate::triton_asm!(@fmt "",; $($source_code)*);
244        let (_, instructions) = $crate::parser::tokenize(&source_code).unwrap();
245        $crate::parser::to_labelled_instructions(&instructions)
246    }};
247}
248
249/// Compile a single [Triton assembly][tasm] instruction into a
250/// [`LabelledInstruction`](instruction::LabelledInstruction).
251///
252/// # Examples
253///
254/// ```
255/// # use triton_isa::triton_instr;
256/// # use triton_isa::instruction::LabelledInstruction;
257/// # use triton_isa::instruction::AnInstruction::Call;
258/// let instruction = triton_instr!(call my_label);
259/// assert_eq!(LabelledInstruction::Instruction(Call("my_label".to_string())), instruction);
260/// ```
261///
262/// [tasm]: https://triton-vm.org/spec/instructions.html
263#[macro_export]
264macro_rules! triton_instr {
265    (pop $arg:literal) => {{
266        let argument = $crate::op_stack::NumberOfWords::try_from($arg).unwrap();
267        let instruction = $crate::instruction::AnInstruction::<String>::Pop(argument);
268        $crate::instruction::LabelledInstruction::Instruction(instruction)
269    }};
270    (push $arg:expr) => {{
271        let argument = $crate::twenty_first::prelude::BFieldElement::from($arg);
272        let instruction = $crate::instruction::AnInstruction::<String>::Push(argument);
273        $crate::instruction::LabelledInstruction::Instruction(instruction)
274    }};
275    (divine $arg:literal) => {{
276        let argument = $crate::op_stack::NumberOfWords::try_from($arg).unwrap();
277        let instruction = $crate::instruction::AnInstruction::<String>::Divine(argument);
278        $crate::instruction::LabelledInstruction::Instruction(instruction)
279    }};
280    (pick $arg:literal) => {{
281        let argument = $crate::op_stack::OpStackElement::try_from($arg).unwrap();
282        let instruction = $crate::instruction::AnInstruction::<String>::Pick(argument);
283        $crate::instruction::LabelledInstruction::Instruction(instruction)
284    }};
285    (place $arg:literal) => {{
286        let argument = $crate::op_stack::OpStackElement::try_from($arg).unwrap();
287        let instruction = $crate::instruction::AnInstruction::<String>::Place(argument);
288        $crate::instruction::LabelledInstruction::Instruction(instruction)
289    }};
290    (dup $arg:literal) => {{
291        let argument = $crate::op_stack::OpStackElement::try_from($arg).unwrap();
292        let instruction = $crate::instruction::AnInstruction::<String>::Dup(argument);
293        $crate::instruction::LabelledInstruction::Instruction(instruction)
294    }};
295    (swap $arg:literal) => {{
296        let argument = $crate::op_stack::OpStackElement::try_from($arg).unwrap();
297        let instruction = $crate::instruction::AnInstruction::<String>::Swap(argument);
298        $crate::instruction::LabelledInstruction::Instruction(instruction)
299    }};
300    (call $arg:ident) => {{
301        let argument = stringify!($arg).to_string();
302        let instruction = $crate::instruction::AnInstruction::<String>::Call(argument);
303        $crate::instruction::LabelledInstruction::Instruction(instruction)
304    }};
305    (read_mem $arg:literal) => {{
306        let argument = $crate::op_stack::NumberOfWords::try_from($arg).unwrap();
307        let instruction = $crate::instruction::AnInstruction::<String>::ReadMem(argument);
308        $crate::instruction::LabelledInstruction::Instruction(instruction)
309    }};
310    (write_mem $arg:literal) => {{
311        let argument = $crate::op_stack::NumberOfWords::try_from($arg).unwrap();
312        let instruction = $crate::instruction::AnInstruction::<String>::WriteMem(argument);
313        $crate::instruction::LabelledInstruction::Instruction(instruction)
314    }};
315    (addi $arg:expr) => {{
316        let argument = $crate::twenty_first::prelude::BFieldElement::from($arg);
317        let instruction = $crate::instruction::AnInstruction::<String>::AddI(argument);
318        $crate::instruction::LabelledInstruction::Instruction(instruction)
319    }};
320    (read_io $arg:literal) => {{
321        let argument = $crate::op_stack::NumberOfWords::try_from($arg).unwrap();
322        let instruction = $crate::instruction::AnInstruction::<String>::ReadIo(argument);
323        $crate::instruction::LabelledInstruction::Instruction(instruction)
324    }};
325    (write_io $arg:literal) => {{
326        let argument = $crate::op_stack::NumberOfWords::try_from($arg).unwrap();
327        let instruction = $crate::instruction::AnInstruction::<String>::WriteIo(argument);
328        $crate::instruction::LabelledInstruction::Instruction(instruction)
329    }};
330    ($instr:ident) => {{
331        let (_, instructions) = $crate::parser::tokenize(stringify!($instr)).unwrap();
332        instructions[0].to_labelled_instruction()
333    }};
334}
335
336#[cfg(test)]
337#[cfg_attr(coverage_nightly, coverage(off))]
338mod tests {
339    use super::*;
340
341    #[test]
342    fn public_types_implement_usual_auto_traits() {
343        fn implements_auto_traits<T: Sized + Send + Sync + Unpin>() {}
344
345        implements_auto_traits::<error::AssertionError>();
346        implements_auto_traits::<error::InstructionError>();
347        implements_auto_traits::<error::NumberOfWordsError>();
348        implements_auto_traits::<error::OpStackElementError>();
349        implements_auto_traits::<error::OpStackError>();
350        implements_auto_traits::<error::ParseError>();
351        implements_auto_traits::<error::ProgramDecodingError>();
352
353        implements_auto_traits::<instruction::Instruction>();
354        implements_auto_traits::<instruction::AnInstruction<usize>>();
355        implements_auto_traits::<instruction::InstructionBit>();
356        implements_auto_traits::<instruction::TypeHint>();
357
358        implements_auto_traits::<op_stack::NumberOfWords>();
359        implements_auto_traits::<op_stack::OpStack>();
360        implements_auto_traits::<op_stack::OpStackElement>();
361        implements_auto_traits::<op_stack::UnderflowIO>();
362
363        implements_auto_traits::<parser::InstructionToken>();
364
365        implements_auto_traits::<program::InstructionIter>();
366        implements_auto_traits::<program::Program>();
367    }
368}