1use std::collections::HashMap;
2use std::collections::HashSet;
3use std::collections::hash_map::Entry;
4use std::error::Error;
5use std::fmt::Display;
6use std::fmt::Formatter;
7use std::fmt::Result as FmtResult;
8
9use itertools::Itertools;
10use nom::Finish;
11use nom::IResult;
12use nom::Parser;
13use nom::branch::alt;
14use nom::bytes::complete::is_not;
15use nom::bytes::complete::tag;
16use nom::bytes::complete::take_until;
17use nom::bytes::complete::take_while;
18use nom::bytes::complete::take_while1;
19use nom::character::char;
20use nom::character::complete::digit1;
21use nom::combinator::cut;
22use nom::combinator::eof;
23use nom::combinator::opt;
24use nom::combinator::value;
25use nom::error::context;
26use nom::multi::many0;
27use nom::multi::many1;
28use nom::multi::separated_list1;
29use nom::sequence::delimited;
30use nom::sequence::terminated;
31use nom_language::error::VerboseError;
32use nom_language::error::VerboseErrorKind;
33use twenty_first::bfe;
34use twenty_first::prelude::BFieldElement;
35
36use crate::instruction::ALL_INSTRUCTION_NAMES;
37use crate::instruction::AnInstruction;
38use crate::instruction::AssertionContext;
39use crate::instruction::Instruction;
40use crate::instruction::LabelledInstruction;
41use crate::instruction::TypeHint;
42use crate::op_stack::NumberOfWords;
43use crate::op_stack::OpStackElement;
44
45const KEYWORDS: [&str; 3] = [
46 "hint",
47 "error_id",
48 "error_message", ];
50
51#[derive(Debug, PartialEq)]
52pub struct ParseError<'a> {
53 pub input: &'a str,
54 pub errors: VerboseError<&'a str>,
55}
56
57#[derive(Debug, Clone, Eq, PartialEq, Hash)]
60pub enum InstructionToken<'a> {
61 Instruction(AnInstruction<String>, &'a str),
62 Label(String, &'a str),
63 Breakpoint(&'a str),
64 TypeHint(TypeHint, &'a str),
65 AssertionContext(AssertionContext, &'a str),
66}
67
68impl Display for ParseError<'_> {
69 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
70 let err_display = nom_language::error::convert_error(self.input, self.errors.clone());
71 write!(f, "{err_display}")
72 }
73}
74
75impl Error for ParseError<'_> {}
76
77impl<'a> InstructionToken<'a> {
78 pub fn token_str(&self) -> &'a str {
79 match self {
80 Self::Instruction(_, token_str) => token_str,
81 Self::Label(_, token_str) => token_str,
82 Self::Breakpoint(token_str) => token_str,
83 Self::TypeHint(_, token_str) => token_str,
84 Self::AssertionContext(_, token_str) => token_str,
85 }
86 }
87
88 pub fn to_labelled_instruction(&self) -> LabelledInstruction {
89 match self {
90 Self::Instruction(instr, _) => LabelledInstruction::Instruction(instr.to_owned()),
91 Self::Label(label, _) => LabelledInstruction::Label(label.to_owned()),
92 Self::Breakpoint(_) => LabelledInstruction::Breakpoint,
93 Self::TypeHint(type_hint, _) => LabelledInstruction::TypeHint(type_hint.to_owned()),
94 Self::AssertionContext(ctx, _) => LabelledInstruction::AssertionContext(ctx.to_owned()),
95 }
96 }
97}
98
99pub fn to_labelled_instructions(instructions: &[InstructionToken]) -> Vec<LabelledInstruction> {
100 instructions
101 .iter()
102 .map(|instruction| instruction.to_labelled_instruction())
103 .collect()
104}
105
106pub(crate) fn parse(input: &str) -> Result<Vec<InstructionToken<'_>>, ParseError<'_>> {
108 let (_, instructions) = tokenize(input)
109 .finish()
110 .map_err(|errors| ParseError { input, errors })?;
111
112 ensure_no_missing_or_duplicate_labels(input, &instructions)?;
113 ensure_assertion_context_is_matched_with_assertion(input, &instructions)?;
114
115 Ok(instructions)
116}
117
118fn ensure_no_missing_or_duplicate_labels<'a>(
119 input: &'a str,
120 instructions: &[InstructionToken<'a>],
121) -> Result<(), ParseError<'a>> {
122 let mut seen_labels = HashMap::<_, InstructionToken>::default();
124 let mut duplicate_labels = HashSet::default();
125 for instruction in instructions {
126 if let InstructionToken::Label(label, _) = instruction {
127 if let Some(first_occurrence) = seen_labels.get(label.as_str()) {
128 duplicate_labels.insert(first_occurrence.to_owned());
129 duplicate_labels.insert(instruction.to_owned());
130 } else {
131 seen_labels.insert(label.as_str(), instruction.to_owned());
132 }
133 }
134 }
135
136 let missing_labels = identify_missing_labels(instructions, seen_labels);
137
138 if duplicate_labels.is_empty() && missing_labels.is_empty() {
139 return Ok(());
140 }
141
142 let errors = errors_for_duplicate_and_missing_labels(duplicate_labels, missing_labels);
143 Err(ParseError { input, errors })
144}
145
146fn identify_missing_labels<'a>(
147 instructions: &[InstructionToken<'a>],
148 seen_labels: HashMap<&str, InstructionToken>,
149) -> HashSet<InstructionToken<'a>> {
150 let mut missing_labels = HashSet::default();
151 for instruction in instructions {
152 if let InstructionToken::Instruction(AnInstruction::Call(label), _) = instruction {
153 if !seen_labels.contains_key(label.as_str()) {
154 missing_labels.insert(instruction.to_owned());
155 }
156 }
157 }
158 missing_labels
159}
160
161fn errors_for_duplicate_and_missing_labels<'a>(
162 duplicate_labels: HashSet<InstructionToken<'a>>,
163 missing_labels: HashSet<InstructionToken<'a>>,
164) -> VerboseError<&'a str> {
165 let duplicate_label_error_context = VerboseErrorKind::Context("duplicate label");
166 let missing_label_error_context = VerboseErrorKind::Context("missing label");
167
168 let duplicate_label_errors =
169 errors_for_labels_with_context(duplicate_labels, duplicate_label_error_context);
170 let missing_label_errors =
171 errors_for_labels_with_context(missing_labels, missing_label_error_context);
172
173 let errors = [duplicate_label_errors, missing_label_errors].concat();
174 VerboseError { errors }
175}
176
177fn errors_for_labels_with_context(
178 labels: HashSet<InstructionToken<'_>>,
179 context: VerboseErrorKind,
180) -> Vec<(&str, VerboseErrorKind)> {
181 labels
182 .into_iter()
183 .map(|label| (label.token_str(), context.clone()))
184 .collect()
185}
186
187fn ensure_assertion_context_is_matched_with_assertion<'a>(
188 input: &'a str,
189 instructions: &[InstructionToken<'a>],
190) -> Result<(), ParseError<'a>> {
191 type IT<'a> = InstructionToken<'a>;
192
193 let mut accept_id = false;
194 let mut incorrectly_placed_contexts = HashSet::new();
195
196 for instruction in instructions {
197 match instruction {
198 IT::Instruction(AnInstruction::Assert | AnInstruction::AssertVector, _) => {
199 accept_id = true;
200 }
201 IT::AssertionContext(AssertionContext::ID(_), _) => {
202 if !accept_id {
203 incorrectly_placed_contexts.insert(instruction.clone());
204 }
205 accept_id = false;
206 }
207
208 _ => accept_id = false,
211 }
212 }
213
214 let parser_context = VerboseErrorKind::Context("incorrectly placed assertion context");
215 let errors = errors_for_labels_with_context(incorrectly_placed_contexts, parser_context);
216
217 if errors.is_empty() {
218 Ok(())
219 } else {
220 let errors = VerboseError { errors };
221 Err(ParseError { input, errors })
222 }
223}
224
225type ParseResult<'input, Out> = IResult<&'input str, Out, VerboseError<&'input str>>;
228
229pub fn tokenize(s: &str) -> ParseResult<'_, Vec<InstructionToken<'_>>> {
230 let (s, _) = comment_or_whitespace0(s)?;
231 let (s, instructions) = many0(alt((
232 label,
233 labelled_instruction,
234 breakpoint,
235 type_hint,
236 assertion_context,
237 )))
238 .parse(s)?;
239 let (s, _) = context("expected label, instruction, or end of file", eof).parse(s)?;
240
241 Ok((s, instructions))
242}
243
244fn label(label_s: &str) -> ParseResult<'_, InstructionToken<'_>> {
245 let (s, addr) = label_addr(label_s)?;
246 let (s, _) = whitespace0(s)?; let (s, _) = token0(":")(s)?; assert_label_is_legal(s, &addr)?;
253
254 Ok((s, InstructionToken::Label(addr, label_s)))
255}
256
257fn breakpoint(breakpoint_s: &str) -> ParseResult<'_, InstructionToken<'_>> {
258 let (s, _) = token1("break")(breakpoint_s)?;
259 Ok((s, InstructionToken::Breakpoint(breakpoint_s)))
260}
261
262fn labelled_instruction(s_instr: &str) -> ParseResult<'_, InstructionToken<'_>> {
263 let (s, instr) = an_instruction(s_instr)?;
264 Ok((s, InstructionToken::Instruction(instr, s_instr)))
265}
266
267fn an_instruction(s: &str) -> ParseResult<'_, AnInstruction<String>> {
268 let pop = pop_instruction();
270 let push = push_instruction();
271 let divine = divine_instruction();
272 let pick = pick_instruction();
273 let place = place_instruction();
274 let dup = dup_instruction();
275 let swap = swap_instruction();
276
277 let opstack_manipulation = alt((pop, push, divine, pick, place, dup, swap));
278
279 let halt = instruction("halt", AnInstruction::Halt);
281 let nop = instruction("nop", AnInstruction::Nop);
282 let skiz = instruction("skiz", AnInstruction::Skiz);
283 let call = call_instruction();
284 let return_ = instruction("return", AnInstruction::Return);
285 let recurse = instruction("recurse", AnInstruction::Recurse);
286 let recurse_or_return = instruction("recurse_or_return", AnInstruction::RecurseOrReturn);
287 let assert = instruction("assert", AnInstruction::Assert);
288
289 let control_flow = alt((nop, skiz, call, return_, halt));
290
291 let read_mem = read_mem_instruction();
293 let write_mem = write_mem_instruction();
294
295 let memory_access = alt((read_mem, write_mem));
296
297 let hash = instruction("hash", AnInstruction::Hash);
299 let assert_vector = instruction("assert_vector", AnInstruction::AssertVector);
300 let sponge_init = instruction("sponge_init", AnInstruction::SpongeInit);
301 let sponge_absorb = instruction("sponge_absorb", AnInstruction::SpongeAbsorb);
302 let sponge_absorb_mem = instruction("sponge_absorb_mem", AnInstruction::SpongeAbsorbMem);
303 let sponge_squeeze = instruction("sponge_squeeze", AnInstruction::SpongeSqueeze);
304
305 let hashing_related = alt((hash, sponge_init, sponge_squeeze));
306
307 let add = instruction("add", AnInstruction::Add);
309 let addi = addi_instruction();
310 let mul = instruction("mul", AnInstruction::Mul);
311 let invert = instruction("invert", AnInstruction::Invert);
312 let eq = instruction("eq", AnInstruction::Eq);
313 let split = instruction("split", AnInstruction::Split);
314 let lt = instruction("lt", AnInstruction::Lt);
315 let and = instruction("and", AnInstruction::And);
316 let xor = instruction("xor", AnInstruction::Xor);
317 let log_2_floor = instruction("log_2_floor", AnInstruction::Log2Floor);
318 let pow = instruction("pow", AnInstruction::Pow);
319 let div_mod = instruction("div_mod", AnInstruction::DivMod);
320 let pop_count = instruction("pop_count", AnInstruction::PopCount);
321 let xx_add = instruction("xx_add", AnInstruction::XxAdd);
322 let xx_mul = instruction("xx_mul", AnInstruction::XxMul);
323 let x_invert = instruction("x_invert", AnInstruction::XInvert);
324 let xb_mul = instruction("xb_mul", AnInstruction::XbMul);
325
326 let base_field_arithmetic_on_stack = alt((mul, invert, eq));
327 let bitwise_arithmetic_on_stack =
328 alt((split, lt, and, xor, log_2_floor, pow, div_mod, pop_count));
329 let extension_field_arithmetic_on_stack = alt((xx_add, xx_mul, x_invert, xb_mul));
330 let arithmetic_on_stack = alt((
331 base_field_arithmetic_on_stack,
332 bitwise_arithmetic_on_stack,
333 extension_field_arithmetic_on_stack,
334 ));
335
336 let read_io = read_io_instruction();
338 let write_io = write_io_instruction();
339
340 let read_write = alt((read_io, write_io));
341
342 let merkle_step = instruction("merkle_step", AnInstruction::MerkleStep);
344 let merkle_step_mem = instruction("merkle_step_mem", AnInstruction::MerkleStepMem);
345 let xx_dot_step = instruction("xx_dot_step", AnInstruction::XxDotStep);
346 let xb_dot_step = instruction("xb_dot_step", AnInstruction::XbDotStep);
347
348 let many_to_one = alt((xx_dot_step, xb_dot_step));
349
350 let syntax_ambiguous = alt((
355 recurse_or_return,
356 recurse,
357 assert_vector,
358 assert,
359 sponge_absorb_mem,
360 sponge_absorb,
361 addi,
362 add,
363 merkle_step_mem,
364 merkle_step,
365 ));
366
367 alt((
368 opstack_manipulation,
369 control_flow,
370 memory_access,
371 hashing_related,
372 arithmetic_on_stack,
373 read_write,
374 many_to_one,
375 syntax_ambiguous,
376 ))
377 .parse(s)
378}
379
380fn assert_label_is_legal<'s>(
381 s: &'s str,
382 label: &str,
383) -> Result<(), nom::Err<VerboseError<&'s str>>> {
384 if ALL_INSTRUCTION_NAMES.contains(&label) || KEYWORDS.contains(&label) {
385 let fail_context = "label must be neither instruction nor keyword";
386 let errors = vec![(s, VerboseErrorKind::Context(fail_context))];
387 return Err(nom::Err::Failure(VerboseError { errors }));
388 }
389
390 Ok(())
391}
392
393fn instruction<'a>(
394 name: &'a str,
395 instruction: AnInstruction<String>,
396) -> impl Fn(&'a str) -> ParseResult<'a, AnInstruction<String>> {
397 move |s: &'a str| {
398 let (s, _) = token1(name)(s)?;
399 Ok((s, instruction.clone()))
400 }
401}
402
403fn pop_instruction() -> impl Fn(&str) -> ParseResult<AnInstruction<String>> {
404 move |s: &str| {
405 let (s, _) = token1("pop")(s)?;
406 let (s, arg) = cut(number_of_words).parse(s)?;
407 Ok((s, AnInstruction::Pop(arg)))
408 }
409}
410
411fn push_instruction() -> impl Fn(&str) -> ParseResult<AnInstruction<String>> {
412 move |s: &str| {
413 let (s, _) = token1("push")(s)?;
414 let (s, elem) = cut(field_element).parse(s)?;
415 Ok((s, AnInstruction::Push(elem)))
416 }
417}
418
419fn addi_instruction() -> impl Fn(&str) -> ParseResult<AnInstruction<String>> {
420 move |s: &str| {
421 let (s, _) = token1("addi")(s)?;
422 let (s, elem) = cut(field_element).parse(s)?;
423 Ok((s, AnInstruction::AddI(elem)))
424 }
425}
426
427fn divine_instruction() -> impl Fn(&str) -> ParseResult<AnInstruction<String>> {
428 move |s: &str| {
429 let (s, _) = token1("divine")(s)?;
430 let (s, arg) = cut(number_of_words).parse(s)?;
431 Ok((s, AnInstruction::Divine(arg)))
432 }
433}
434
435fn pick_instruction() -> impl Fn(&str) -> ParseResult<AnInstruction<String>> {
436 move |s: &str| {
437 let (s, _) = token1("pick")(s)?;
438 let (s, arg) = cut(stack_register).parse(s)?;
439 Ok((s, AnInstruction::Pick(arg)))
440 }
441}
442
443fn place_instruction() -> impl Fn(&str) -> ParseResult<AnInstruction<String>> {
444 move |s: &str| {
445 let (s, _) = token1("place")(s)?;
446 let (s, arg) = cut(stack_register).parse(s)?;
447 Ok((s, AnInstruction::Place(arg)))
448 }
449}
450
451fn dup_instruction() -> impl Fn(&str) -> ParseResult<AnInstruction<String>> {
452 move |s: &str| {
453 let (s, _) = token1("dup")(s)?; let (s, stack_register) = cut(stack_register).parse(s)?;
455 Ok((s, AnInstruction::Dup(stack_register)))
456 }
457}
458
459fn swap_instruction() -> impl Fn(&str) -> ParseResult<AnInstruction<String>> {
460 move |s: &str| {
461 let (s, _) = token1("swap")(s)?;
462 let (s, stack_register) = cut(stack_register).parse(s)?;
463 Ok((s, AnInstruction::Swap(stack_register)))
464 }
465}
466
467fn call_instruction<'a>() -> impl Fn(&'a str) -> ParseResult<'a, AnInstruction<String>> {
468 move |s: &'a str| {
469 let (s, _) = token1("call")(s)?;
470 let (s, label) = cut(label_addr).parse(s)?;
471 let (s, _) = comment_or_whitespace1(s)?;
472
473 assert_label_is_legal(s, &label)?;
478
479 Ok((s, AnInstruction::Call(label)))
480 }
481}
482
483fn read_mem_instruction() -> impl Fn(&str) -> ParseResult<AnInstruction<String>> {
484 move |s: &str| {
485 let (s, _) = token1("read_mem")(s)?;
486 let (s, arg) = cut(number_of_words).parse(s)?;
487 Ok((s, AnInstruction::ReadMem(arg)))
488 }
489}
490
491fn write_mem_instruction() -> impl Fn(&str) -> ParseResult<AnInstruction<String>> {
492 move |s: &str| {
493 let (s, _) = token1("write_mem")(s)?;
494 let (s, arg) = cut(number_of_words).parse(s)?;
495 Ok((s, AnInstruction::WriteMem(arg)))
496 }
497}
498
499fn read_io_instruction() -> impl Fn(&str) -> ParseResult<AnInstruction<String>> {
500 move |s: &str| {
501 let (s, _) = token1("read_io")(s)?;
502 let (s, arg) = cut(number_of_words).parse(s)?;
503 Ok((s, AnInstruction::ReadIo(arg)))
504 }
505}
506
507fn write_io_instruction() -> impl Fn(&str) -> ParseResult<AnInstruction<String>> {
508 move |s: &str| {
509 let (s, _) = token1("write_io")(s)?;
510 let (s, arg) = cut(number_of_words).parse(s)?;
511 Ok((s, AnInstruction::WriteIo(arg)))
512 }
513}
514
515fn field_element(s_orig: &str) -> ParseResult<'_, BFieldElement> {
516 let (s, n) = context(
517 "expected a reasonably-sized integer",
518 nom::character::complete::i128,
519 )
520 .parse(s_orig)?;
521 let (s, _) = comment_or_whitespace1(s)?;
522
523 let p = i128::from(BFieldElement::P);
524 if n <= -p || p <= n {
525 let fail_context = "out-of-bounds constant";
526 let errors = vec![(s, VerboseErrorKind::Context(fail_context))];
527 return Err(nom::Err::Error(VerboseError { errors }));
528 }
529 let n = if n >= 0 { n } else { n + p };
530
531 Ok((s, BFieldElement::new(n as u64)))
532}
533
534fn stack_register(s: &str) -> ParseResult<'_, OpStackElement> {
535 let (s, n) = digit1(s)?;
536 let stack_register = match n {
537 "0" => OpStackElement::ST0,
538 "1" => OpStackElement::ST1,
539 "2" => OpStackElement::ST2,
540 "3" => OpStackElement::ST3,
541 "4" => OpStackElement::ST4,
542 "5" => OpStackElement::ST5,
543 "6" => OpStackElement::ST6,
544 "7" => OpStackElement::ST7,
545 "8" => OpStackElement::ST8,
546 "9" => OpStackElement::ST9,
547 "10" => OpStackElement::ST10,
548 "11" => OpStackElement::ST11,
549 "12" => OpStackElement::ST12,
550 "13" => OpStackElement::ST13,
551 "14" => OpStackElement::ST14,
552 "15" => OpStackElement::ST15,
553 _ => {
554 let fail_context = "using an out-of-bounds stack register (0-15 exist)";
555 let errors = vec![(s, VerboseErrorKind::Context(fail_context))];
556 return Err(nom::Err::Error(VerboseError { errors }));
557 }
558 };
559 let (s, _) = comment_or_whitespace1(s)?;
560
561 Ok((s, stack_register))
562}
563
564fn number_of_words(s: &str) -> ParseResult<'_, NumberOfWords> {
565 let (s, n) = digit1(s)?;
566 let arg = match n {
567 "1" => NumberOfWords::N1,
568 "2" => NumberOfWords::N2,
569 "3" => NumberOfWords::N3,
570 "4" => NumberOfWords::N4,
571 "5" => NumberOfWords::N5,
572 _ => {
573 let fail_context = "using an out-of-bounds argument (1-5 allowed)";
574 let errors = vec![(s, VerboseErrorKind::Context(fail_context))];
575 return Err(nom::Err::Error(VerboseError { errors }));
576 }
577 };
578 let (s, _) = comment_or_whitespace1(s)?; Ok((s, arg))
581}
582
583fn label_addr(s_orig: &str) -> ParseResult<'_, String> {
585 let (s, addr_part_0) = context(
586 "label must start with an alphabetic character or `_`",
587 take_while1(|c: char| c.is_alphabetic() || c == '_'),
588 )
589 .parse(s_orig)?;
590 let (s, addr_part_1) = context(
591 "label must contain only alphanumeric characters or `_` or `-`",
592 take_while(|c: char| c.is_alphanumeric() || c == '_' || c == '-'),
593 )
594 .parse(s)?;
595
596 Ok((s, format!("{addr_part_0}{addr_part_1}")))
597}
598
599fn comment_or_whitespace0(s: &str) -> ParseResult<'_, &str> {
603 let (s, _) = many0(alt((comment1, whitespace1))).parse(s)?;
604 Ok((s, ""))
605}
606
607fn comment_or_whitespace1<'a>(s: &'a str) -> ParseResult<'a, &'a str> {
611 let cws1 = |s: &'a str| -> ParseResult<&str> {
612 let (s, _) = many1(alt((comment1, whitespace1))).parse(s)?;
613 Ok((s, ""))
614 };
615 alt((eof, cws1)).parse(s)
616}
617
618fn comment1(s: &str) -> ParseResult<'_, ()> {
620 alt((eol_comment, inline_comment)).parse(s)
621}
622
623fn eol_comment(s: &str) -> ParseResult<'_, ()> {
625 let (s, _) = tag("//").parse(s)?;
626 let (s, _) = is_not("\n\r").parse(s)?;
627
628 Ok((s, ()))
629}
630
631fn inline_comment(s: &str) -> ParseResult<'_, ()> {
633 let (s, _) = tag("/*").parse(s)?;
634 let (s, _) = take_until("*/").parse(s)?;
635 let (s, _) = tag("*/").parse(s)?;
636
637 Ok((s, ()))
638}
639
640fn whitespace0(s: &str) -> ParseResult<'_, ()> {
642 let (s, _) = take_while(char::is_whitespace)(s)?;
643 Ok((s, ()))
644}
645
646fn whitespace1(s: &str) -> ParseResult<'_, ()> {
648 let (s, _) = take_while1(char::is_whitespace)(s)?;
649 Ok((s, ()))
650}
651
652fn token0<'a>(token: &'a str) -> impl Fn(&'a str) -> ParseResult<'a, ()> {
655 move |s: &'a str| {
656 let (s, _) = tag(token)(s)?;
657 let (s, _) = comment_or_whitespace0(s)?;
658 Ok((s, ()))
659 }
660}
661
662fn token1<'a>(token: &'a str) -> impl Fn(&'a str) -> ParseResult<'a, ()> {
665 move |s: &'a str| {
666 let (s, _) = tag(token)(s)?;
667 let (s, _) = comment_or_whitespace1(s)?;
668 Ok((s, ()))
669 }
670}
671
672fn type_hint(s_type_hint: &str) -> ParseResult<'_, InstructionToken<'_>> {
680 let (s, _) = token1("hint")(s_type_hint)?;
681 let (s, variable_name) = context(
682 "type hint requires variable's name",
683 cut(type_hint_variable_name),
684 )
685 .parse(s)?;
686 let (s, type_name) = context(
687 "type hint's type name is malformed",
688 cut(type_hint_type_name),
689 )
690 .parse(s)?;
691 let (s, _) = context("syntax error", cut(token0("="))).parse(s)?;
692 let (s, _) = context("syntax error", cut(tag("stack"))).parse(s)?;
693 let (s, _) = context("syntax error", cut(token0("["))).parse(s)?;
694 let (s, range_start) = context(
695 "type hint requires a stack position (or range) indicator",
696 cut(nom::character::complete::usize),
697 )
698 .parse(s)?;
699 let (s, _) = whitespace0(s)?;
700 let (s, maybe_range_end) = context(
701 "type hint's stack range indicator is malformed",
702 cut(type_hint_ending_index),
703 )
704 .parse(s)?;
705 let (s, _) = whitespace0(s)?;
706 let (s, _) = context(
707 "type hint requires closing bracket for stack position (or range) indicator",
708 cut(token0("]")),
709 )
710 .parse(s)?;
711
712 let length = match maybe_range_end {
713 Some(range_end) if range_end <= range_start => {
714 let fail_context = "type hint's stack range indicator must be non-empty";
715 let errors = vec![(s, VerboseErrorKind::Context(fail_context))];
716 return Err(nom::Err::Failure(VerboseError { errors }));
717 }
718 Some(range_end) => range_end - range_start,
719 None => 1,
720 };
721
722 let type_hint = TypeHint {
723 starting_index: range_start,
724 length,
725 type_name,
726 variable_name,
727 };
728
729 Ok((s, InstructionToken::TypeHint(type_hint, s_type_hint)))
730}
731
732fn type_hint_variable_name(s: &str) -> ParseResult<'_, String> {
733 let (s, variable_name_start) = context(
734 "type hint's variable name must start with a lowercase alphabetic character or `_`",
735 take_while1(|c: char| (c.is_alphabetic() && c.is_lowercase()) || c == '_'),
736 )
737 .parse(s)?;
738 let (s, variable_name_end) = context(
739 "type hint's variable name must contain only lowercase alphanumeric characters or `_`",
740 take_while(|c: char| (c.is_alphabetic() && c.is_lowercase()) || c == '_' || c.is_numeric()),
741 )
742 .parse(s)?;
743 let (s, _) = whitespace0(s)?;
744 let variable_name = format!("{variable_name_start}{variable_name_end}");
745 Ok((s, variable_name))
746}
747
748fn type_hint_type_name(s: &str) -> ParseResult<'_, Option<String>> {
749 #[derive(Debug, Clone)]
750 pub enum TypeHintType<'a> {
751 Simple(&'a str),
752 Generic(&'a str, Vec<TypeHintType<'a>>),
753 }
754
755 impl Display for TypeHintType<'_> {
756 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
757 match self {
758 TypeHintType::Simple(ty) => write!(f, "{ty}"),
759 TypeHintType::Generic(ty, ge) => write!(f, "{ty}<{ge}>", ge = ge.iter().join(", ")),
760 }
761 }
762 }
763
764 fn ty(input: &str) -> ParseResult<'_, TypeHintType<'_>> {
765 alt((generic_ty, simple_ty)).parse(input)
766 }
767
768 fn simple_ty(s: &str) -> ParseResult<'_, TypeHintType<'_>> {
769 let (s, ty) = ty_name(s)?;
770 Ok((s, TypeHintType::Simple(ty)))
771 }
772
773 fn generic_ty(s: &str) -> ParseResult<'_, TypeHintType<'_>> {
774 let (s, name) = ty_name(s)?;
775 let (s, _) = comment_or_whitespace0(s)?;
776
777 let list_elem = delimited(comment_or_whitespace0, ty, comment_or_whitespace0);
778 let separator = || delimited(comment_or_whitespace0, char(','), comment_or_whitespace0);
779 let ty_list = separated_list1(separator(), list_elem);
780 let ty_list = terminated(ty_list, opt(separator()));
781 let empty_list = value(Vec::new(), comment_or_whitespace1);
782 let (s, generics) = delimited(tag("<"), alt((ty_list, empty_list)), tag(">")).parse(s)?;
783
784 let ty = if generics.is_empty() {
785 TypeHintType::Simple(name)
786 } else {
787 TypeHintType::Generic(name, generics)
788 };
789
790 Ok((s, ty))
791 }
792
793 fn ty_name(input: &str) -> ParseResult<'_, &str> {
794 let (s, stars) = take_while(|c: char| c == '*').parse(input)?;
795 let (s, begin) = take_while1(|c: char| c.is_ascii_alphabetic() || c == '_').parse(s)?;
796 let (s, rest) = take_while(|c: char| c.is_ascii_alphanumeric() || c == '_').parse(s)?;
797
798 Ok((s, &input[..stars.len() + begin.len() + rest.len()]))
799 }
800
801 let Ok((s, _)) = token0(":")(s) else {
802 return Ok((s, None));
803 };
804
805 let (s, type_name) = cut(ty).parse(s)?;
806 let (s, _) = whitespace0(s)?;
807
808 Ok((s, Some(type_name.to_string())))
809}
810
811fn type_hint_ending_index(s: &str) -> ParseResult<'_, Option<usize>> {
812 let Ok((s, _)) = token0("..")(s) else {
813 return Ok((s, None));
814 };
815 let (s, range_end) = context(
816 "type hint with range indicator requires a range end",
817 cut(nom::character::complete::usize),
818 )
819 .parse(s)?;
820 let (s, _) = whitespace0(s)?;
821
822 Ok((s, Some(range_end)))
823}
824
825fn assertion_context(s_ctx: &str) -> ParseResult<'_, InstructionToken<'_>> {
826 let (s, assertion_context) = assertion_context_id(s_ctx)?;
827 let assertion_context = InstructionToken::AssertionContext(assertion_context, s_ctx);
828
829 Ok((s, assertion_context))
830}
831
832fn assertion_context_id(s_ctx: &str) -> ParseResult<'_, AssertionContext> {
833 let (s, _) = token1("error_id")(s_ctx)?;
834 let (s, id) = context(
835 "expected a valid error ID of type i128",
836 cut(nom::character::complete::i128),
837 )
838 .parse(s)?;
839 let (s, _) = comment_or_whitespace1(s)?;
840
841 let assertion_context = AssertionContext::ID(id);
842 Ok((s, assertion_context))
843}
844
845pub(crate) fn build_label_to_address_map(program: &[LabelledInstruction]) -> HashMap<String, u64> {
846 let mut label_map = HashMap::new();
847 let mut instruction_pointer = 0;
848
849 for labelled_instruction in program {
850 if let LabelledInstruction::Instruction(instruction) = labelled_instruction {
851 instruction_pointer += instruction.size() as u64;
852 continue;
853 }
854
855 let LabelledInstruction::Label(label) = labelled_instruction else {
856 continue;
857 };
858 let Entry::Vacant(new_label_map_entry) = label_map.entry(label.clone()) else {
859 panic!("Duplicate label: {label}");
860 };
861 new_label_map_entry.insert(instruction_pointer);
862 }
863
864 label_map
865}
866
867pub(crate) fn turn_labels_into_addresses(
868 labelled_instructions: &[LabelledInstruction],
869 label_to_address: &HashMap<String, u64>,
870) -> Vec<Instruction> {
871 fn turn_label_to_address_for_instruction(
872 labelled_instruction: &LabelledInstruction,
873 label_map: &HashMap<String, u64>,
874 ) -> Option<Instruction> {
875 let LabelledInstruction::Instruction(instruction) = labelled_instruction else {
876 return None;
877 };
878
879 let instruction_with_absolute_address =
880 instruction.map_call_address(|label| address_for_label(label, label_map));
881 Some(instruction_with_absolute_address)
882 }
883
884 fn address_for_label(label: &str, label_map: &HashMap<String, u64>) -> BFieldElement {
885 let maybe_address = label_map.get(label).map(|&a| bfe!(a));
886 maybe_address.unwrap_or_else(|| panic!("Label not found: {label}"))
887 }
888
889 labelled_instructions
890 .iter()
891 .filter_map(|inst| turn_label_to_address_for_instruction(inst, label_to_address))
892 .flat_map(|inst| vec![inst; inst.size()])
893 .collect()
894}
895
896#[cfg(test)]
897#[cfg_attr(coverage_nightly, coverage(off))]
898pub(crate) mod tests {
899 use assert2::assert;
900 use assert2::let_assert;
901 use itertools::Itertools;
902 use proptest::prelude::*;
903 use proptest_arbitrary_interop::arb;
904 use rand::Rng;
905 use strum::EnumCount;
906 use test_strategy::Arbitrary;
907 use test_strategy::proptest;
908 use twenty_first::bfe;
909 use twenty_first::prelude::Digest;
910
911 use crate::triton_asm;
912 use crate::triton_instr;
913 use crate::triton_program;
914
915 use super::*;
916
917 #[must_use]
918 struct TestCase<'a> {
919 input: &'a str,
920 expected: Vec<Instruction>,
921 message: &'static str,
922 }
923
924 #[must_use]
925 struct NegativeTestCase<'a> {
926 input: &'a str,
927 expected_error: &'static str,
928 expected_error_count: usize,
929 message: &'static str,
930 }
931
932 impl TestCase<'_> {
933 fn run(&self) {
934 let message = self.message;
935 let parse_result = parse(self.input).map_err(|err| format!("{message}:\n{err}"));
936 let_assert!(Ok(actual) = parse_result);
937
938 let labelled_instructions = to_labelled_instructions(&actual);
939 let label_to_address = build_label_to_address_map(&labelled_instructions);
940 let instructions =
941 turn_labels_into_addresses(&labelled_instructions, &label_to_address);
942 assert!(self.expected == instructions, "{message}");
943 }
944 }
945
946 impl NegativeTestCase<'_> {
947 fn run(self) {
948 let Self {
949 input,
950 expected_error,
951 expected_error_count,
952 message,
953 } = self;
954
955 let parse_result = parse(input);
956 if let Ok(instructions) = parse_result {
957 eprintln!("parser input: {input}");
958 eprintln!("parser output: {instructions:?}");
959 panic!("parser should fail, but didn't: {message}");
960 }
961
962 let actual_error = parse_result.map_err(|e| e.to_string()).unwrap_err();
963 let actual_error_count = actual_error.match_indices(expected_error).count();
964 if expected_error_count != actual_error_count {
965 eprintln!("Expected error message ({expected_error_count} times):");
966 eprintln!("{expected_error}");
967 eprintln!("Actual error message (found {actual_error_count} times):");
968 eprintln!("{actual_error}");
969 panic!("Additional context: {message}");
970 }
971 }
972 }
973
974 #[test]
975 fn parse_program_empty() {
976 TestCase {
977 input: "",
978 expected: vec![],
979 message: "empty string should parse as empty program",
980 }
981 .run();
982
983 TestCase {
984 input: " ",
985 expected: vec![],
986 message: "spaces should parse as empty program",
987 }
988 .run();
989
990 TestCase {
991 input: "\n",
992 expected: vec![],
993 message: "linebreaks should parse as empty program (1)",
994 }
995 .run();
996
997 TestCase {
998 input: " \n ",
999 expected: vec![],
1000 message: "linebreaks should parse as empty program (2)",
1001 }
1002 .run();
1003
1004 TestCase {
1005 input: " \n \n",
1006 expected: vec![],
1007 message: "linebreaks should parse as empty program (3)",
1008 }
1009 .run();
1010
1011 TestCase {
1012 input: "// empty program",
1013 expected: vec![],
1014 message: "single comment should parse as empty program",
1015 }
1016 .run();
1017
1018 TestCase {
1019 input: "// empty program\n",
1020 expected: vec![],
1021 message: "single comment with linebreak should parse as empty program",
1022 }
1023 .run();
1024
1025 TestCase {
1026 input: "// multi-line\n// comment",
1027 expected: vec![],
1028 message: "multiple comments should parse as empty program",
1029 }
1030 .run();
1031
1032 TestCase {
1033 input: "// multi-line\n// comment\n ",
1034 expected: vec![],
1035 message: "multiple comments with trailing whitespace should parse as empty program",
1036 }
1037 .run();
1038 }
1039
1040 #[test]
1041 fn parse_simple_programs() {
1042 TestCase {
1043 input: "halt",
1044 expected: vec![Instruction::Halt],
1045 message: "most simple program should work",
1046 }
1047 .run();
1048
1049 TestCase {
1050 input: "push -1 push 0 push 1",
1051 expected: [-1, -1, 0, 0, 1, 1]
1052 .map(|i| Instruction::Push(bfe!(i)))
1053 .to_vec(),
1054 message: "simple parameters for push should work",
1055 }
1056 .run();
1057 }
1058
1059 #[proptest]
1060 fn arbitrary_whitespace_and_comment_sequence_is_empty_program(whitespace: Vec<Whitespace>) {
1061 let whitespace = whitespace.into_iter().join("");
1062 TestCase {
1063 input: &whitespace,
1064 expected: vec![],
1065 message: "arbitrary whitespace should parse as empty program",
1066 }
1067 .run();
1068 }
1069
1070 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, EnumCount, Arbitrary)]
1071 enum Whitespace {
1072 Space,
1073 Tab,
1074 CarriageReturn,
1075 LineFeed,
1076 CarriageReturnLineFeed,
1077 Comment,
1078 }
1079
1080 impl Display for Whitespace {
1081 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
1082 match self {
1083 Self::Space => write!(f, " "),
1084 Self::Tab => write!(f, "\t"),
1085 Self::CarriageReturn => write!(f, "\r"),
1086 Self::LineFeed => writeln!(f),
1087 Self::CarriageReturnLineFeed => writeln!(f, "\r"),
1088 Self::Comment => writeln!(f, " // comment"),
1089 }
1090 }
1091 }
1092
1093 #[test]
1094 fn parse_program_whitespace() {
1095 NegativeTestCase {
1096 input: "poppop",
1097 expected_error: "n/a",
1098 expected_error_count: 0,
1099 message: "whitespace required between instructions (pop)",
1100 }
1101 .run();
1102
1103 NegativeTestCase {
1104 input: "dup 0dup 0",
1105 expected_error: "n/a",
1106 expected_error_count: 0,
1107 message: "whitespace required between instructions (dup 0)",
1108 }
1109 .run();
1110
1111 NegativeTestCase {
1112 input: "swap 10swap 10",
1113 expected_error: "n/a",
1114 expected_error_count: 0,
1115 message: "whitespace required between instructions (swap 10)",
1116 }
1117 .run();
1118
1119 NegativeTestCase {
1120 input: "push10",
1121 expected_error: "n/a",
1122 expected_error_count: 0,
1123 message: "push requires whitespace before its constant",
1124 }
1125 .run();
1126
1127 NegativeTestCase {
1128 input: "push 10pop",
1129 expected_error: "n/a",
1130 expected_error_count: 0,
1131 message: "push requires whitespace after its constant",
1132 }
1133 .run();
1134
1135 NegativeTestCase {
1136 input: "hello: callhello",
1137 expected_error: "n/a",
1138 expected_error_count: 0,
1139 message: "call requires whitespace before its label",
1140 }
1141 .run();
1142
1143 NegativeTestCase {
1144 input: "hello: popcall hello",
1145 expected_error: "n/a",
1146 expected_error_count: 0,
1147 message: "required space between pop and call",
1148 }
1149 .run();
1150 }
1151
1152 #[test]
1153 fn parse_program_label() {
1154 TestCase {
1155 input: "foo: call foo",
1156 expected: vec![Instruction::Call(bfe!(0)), Instruction::Call(bfe!(0))],
1157 message: "parse labels and calls to labels",
1158 }
1159 .run();
1160
1161 TestCase {
1162 input: "foo:call foo",
1163 expected: vec![Instruction::Call(bfe!(0)), Instruction::Call(bfe!(0))],
1164 message: "whitespace is not required after 'label:'",
1165 }
1166 .run();
1167
1168 NegativeTestCase {
1170 input: "foo: pop 1 foo: pop 1 call foo",
1171 expected_error: "duplicate label",
1172 expected_error_count: 2,
1173 message: "labels cannot occur twice",
1174 }
1175 .run();
1176
1177 NegativeTestCase {
1179 input: "foo: pop 1 call herp call derp",
1180 expected_error: "missing label",
1181 expected_error_count: 2,
1182 message: "non-existent labels cannot be called",
1183 }
1184 .run();
1185
1186 NegativeTestCase {
1188 input: "pop: call pop",
1189 expected_error: "label must be neither instruction nor keyword",
1190 expected_error_count: 1,
1191 message: "label names may not overlap with instruction names",
1192 }
1193 .run();
1194
1195 TestCase {
1196 input: "pops: call pops",
1197 expected: vec![Instruction::Call(bfe!(0)), Instruction::Call(bfe!(0))],
1198 message: "labels that share a common prefix with instruction are labels",
1199 }
1200 .run();
1201
1202 TestCase {
1203 input: "_call: call _call",
1204 expected: vec![Instruction::Call(bfe!(0)), Instruction::Call(bfe!(0))],
1205 message: "labels that share a common suffix with instruction are labels",
1206 }
1207 .run();
1208 }
1209
1210 #[test]
1211 fn parse_program_nonexistent_instructions() {
1212 NegativeTestCase {
1213 input: "pop 0",
1214 expected_error: "using an out-of-bounds argument (1-5 allowed)",
1215 expected_error_count: 1,
1216 message: "instruction `pop` cannot take argument `0`",
1217 }
1218 .run();
1219
1220 NegativeTestCase {
1221 input: "swap 16",
1222 expected_error: "using an out-of-bounds stack register (0-15 exist)",
1223 expected_error_count: 1,
1224 message: "there is no swap 16 instruction",
1225 }
1226 .run();
1227
1228 NegativeTestCase {
1229 input: "dup 16",
1230 expected_error: "using an out-of-bounds stack register (0-15 exist)",
1231 expected_error_count: 1,
1232 message: "there is no dup 16 instruction",
1233 }
1234 .run();
1235 }
1236
1237 #[test]
1238 fn parse_program_bracket_syntax() {
1239 NegativeTestCase {
1240 input: "foo: [foo]",
1241 expected_error: "expected label, instruction, or end of file",
1242 expected_error_count: 1,
1243 message: "brackets as call syntax sugar are unsupported",
1244 }
1245 .run();
1246
1247 NegativeTestCase {
1248 input: "foo: [bar]",
1249 expected_error: "expected label, instruction, or end of file",
1250 expected_error_count: 1,
1251 message: "brackets as call syntax sugar are unsupported",
1252 }
1253 .run();
1254 }
1255
1256 #[test]
1257 fn parse_program_label_must_start_with_alphabetic_character_or_underscore() {
1258 NegativeTestCase {
1259 input: "1foo: call 1foo",
1260 expected_error: "expected label, instruction, or end of file",
1261 expected_error_count: 1,
1262 message: "labels cannot start with a digit",
1263 }
1264 .run();
1265
1266 NegativeTestCase {
1267 input: "-foo: call -foo",
1268 expected_error: "expected label, instruction, or end of file",
1269 expected_error_count: 1,
1270 message: "labels cannot start with a dash",
1271 }
1272 .run();
1273
1274 NegativeTestCase {
1275 input: "call -foo",
1276 expected_error: "label must start with an alphabetic character or `_`",
1277 expected_error_count: 1,
1278 message: "labels cannot start with a dash",
1279 }
1280 .run();
1281
1282 TestCase {
1283 input: "_foo: call _foo",
1284 expected: vec![Instruction::Call(bfe!(0)), Instruction::Call(bfe!(0))],
1285 message: "labels can start with an underscore",
1286 }
1287 .run();
1288 }
1289
1290 #[test]
1291 fn parse_type_hint_with_zero_length_range() {
1292 NegativeTestCase {
1293 input: "hint foo: Type = stack[0..0]",
1294 expected_error: "type hint's stack range indicator must be non-empty",
1295 expected_error_count: 1,
1296 message: "parse type hint with zero-length range",
1297 }
1298 .run();
1299 }
1300
1301 #[test]
1302 fn parse_type_hint_with_range_and_offset_and_missing_closing_bracket() {
1303 NegativeTestCase {
1304 input: "hint foo: Type = stack[2..5",
1305 expected_error: "type hint requires closing bracket",
1306 expected_error_count: 1,
1307 message: "parse type hint with range and offset and missing closing bracket",
1308 }
1309 .run();
1310 }
1311
1312 #[test]
1313 fn parse_type_hint_with_range_and_offset_and_missing_opening_bracket() {
1314 NegativeTestCase {
1315 input: "hint foo: Type = stack2..5]",
1316 expected_error: "syntax error",
1317 expected_error_count: 1,
1318 message: "parse type hint with range and offset and missing opening bracket",
1319 }
1320 .run();
1321 }
1322
1323 #[test]
1324 fn parse_type_hint_with_range_and_offset_and_missing_equals_sign() {
1325 NegativeTestCase {
1326 input: "hint foo: Type stack[2..5];",
1327 expected_error: "syntax error",
1328 expected_error_count: 1,
1329 message: "parse type hint with range and offset and missing equals sign",
1330 }
1331 .run();
1332 }
1333
1334 #[test]
1335 fn parse_type_hint_with_range_and_offset_and_missing_type_name() {
1336 NegativeTestCase {
1337 input: "hint foo: = stack[2..5]",
1338 expected_error: "type hint's type name is malformed",
1339 expected_error_count: 1,
1340 message: "parse type hint with range and offset and missing type name",
1341 }
1342 .run();
1343 }
1344
1345 #[test]
1346 fn parse_type_hint_with_range_and_offset_and_missing_variable_name() {
1347 NegativeTestCase {
1348 input: "hint : Type = stack[2..5]",
1349 expected_error: "label must be neither instruction nor keyword",
1350 expected_error_count: 1,
1351 message: "parse type hint with range and offset and missing variable name",
1352 }
1353 .run();
1354 }
1355
1356 #[test]
1357 fn parse_type_hint_with_range_and_offset_and_missing_colon() {
1358 NegativeTestCase {
1359 input: "hint foo Type = stack[2..5]",
1360 expected_error: "syntax error",
1361 expected_error_count: 1,
1362 message: "parse type hint with range and offset and missing colon",
1363 }
1364 .run();
1365 }
1366
1367 #[test]
1368 fn parse_type_hint_with_range_and_offset_and_missing_hint() {
1369 NegativeTestCase {
1370 input: "foo: Type = stack[2..5];",
1371 expected_error: "expected label, instruction, or end of file",
1372 expected_error_count: 1,
1373 message: "parse type hint with range and offset and missing hint",
1374 }
1375 .run();
1376 }
1377
1378 #[test]
1379 fn parse_simple_assertion_contexts() {
1380 TestCase {
1381 input: "assert",
1382 expected: vec![Instruction::Assert],
1383 message: "naked assert",
1384 }
1385 .run();
1386 TestCase {
1387 input: "assert_vector",
1388 expected: vec![Instruction::AssertVector],
1389 message: "naked assert_vector",
1390 }
1391 .run();
1392
1393 TestCase {
1394 input: "assert error_id 42",
1395 expected: vec![Instruction::Assert],
1396 message: "assert, then id",
1397 }
1398 .run();
1399 TestCase {
1400 input: "assert_vector error_id 42",
1401 expected: vec![Instruction::AssertVector],
1402 message: "assert_vector, then id",
1403 }
1404 .run();
1405
1406 TestCase {
1407 input: "assert error_id -42",
1408 expected: vec![Instruction::Assert],
1409 message: "assert, then negative id",
1410 }
1411 .run();
1412 TestCase {
1413 input: "assert_vector error_id -42",
1414 expected: vec![Instruction::AssertVector],
1415 message: "assert_vector, then negative id",
1416 }
1417 .run();
1418
1419 TestCase {
1420 input: "assert error_id 42 nop",
1421 expected: vec![Instruction::Assert, Instruction::Nop],
1422 message: "assert, then id, then nop",
1423 }
1424 .run();
1425 TestCase {
1426 input: "assert_vector error_id 42 nop",
1427 expected: vec![Instruction::AssertVector, Instruction::Nop],
1428 message: "assert_vector, then id, then nop",
1429 }
1430 .run();
1431 }
1432
1433 #[test]
1434 fn assertion_context_error_id_can_handle_edge_case_ids() {
1435 let instructions = [
1436 ("assert", Instruction::Assert),
1437 ("assert_vector", Instruction::AssertVector),
1438 ];
1439 let ids = [i128::MIN, -1, 0, 1, i128::MAX];
1440
1441 for ((instruction_str, instruction), id) in instructions.into_iter().cartesian_product(ids)
1442 {
1443 TestCase {
1444 input: &format!("{instruction_str} error_id {id}"),
1445 expected: vec![instruction],
1446 message: "assert, then edge case id",
1447 }
1448 .run();
1449 }
1450 }
1451
1452 #[test]
1453 fn assertion_context_error_id_fails_on_too_large_error_ids() {
1454 NegativeTestCase {
1455 input: "assert error_id -170141183460469231731687303715884105729",
1456 expected_error: "expected a valid error ID of type i128",
1457 expected_error_count: 1,
1458 message: "error id smaller than i128::MIN",
1459 }
1460 .run();
1461 NegativeTestCase {
1462 input: "assert error_id 170141183460469231731687303715884105728",
1463 expected_error: "expected a valid error ID of type i128",
1464 expected_error_count: 1,
1465 message: "error id larger than i128::MAX",
1466 }
1467 .run();
1468 }
1469
1470 #[proptest]
1471 fn assertion_context_error_id_can_handle_any_valid_id(id: i128) {
1472 TestCase {
1473 input: &format!("assert error_id {id}"),
1474 expected: vec![Instruction::Assert],
1475 message: "assert, then random id",
1476 }
1477 .run();
1478 }
1479
1480 #[test]
1481 fn parse_erroneous_assertion_contexts_for_assert() {
1482 NegativeTestCase {
1483 input: "assert error_id",
1484 expected_error: "expected a valid error ID of type i128",
1485 expected_error_count: 1,
1486 message: "missing id after keyword `error_id`",
1487 }
1488 .run();
1489 NegativeTestCase {
1490 input: "assert error id 42",
1491 expected_error: "expected label, instruction, or end of file",
1492 expected_error_count: 1,
1493 message: "incorrect keyword `error id`",
1494 }
1495 .run();
1496 NegativeTestCase {
1497 input: "error_id 42",
1498 expected_error: "incorrectly placed assertion context",
1499 expected_error_count: 1,
1500 message: "standalone assertion context",
1501 }
1502 .run();
1503 NegativeTestCase {
1504 input: "error_id 42 error_id 42",
1505 expected_error: "incorrectly placed assertion context",
1506 expected_error_count: 2,
1507 message: "multiple standalone assertion contexts",
1508 }
1509 .run();
1510 NegativeTestCase {
1511 input: "nop error_id 42",
1512 expected_error: "incorrectly placed assertion context",
1513 expected_error_count: 1,
1514 message: "error id without assertion",
1515 }
1516 .run();
1517 NegativeTestCase {
1518 input: "assert error_id 42 error_id 1337",
1519 expected_error: "incorrectly placed assertion context",
1520 expected_error_count: 1,
1521 message: "multiple error ids for the same assertion",
1522 }
1523 .run();
1524 NegativeTestCase {
1525 input: "assert_vector error_id 42 error_id 1337",
1526 expected_error: "incorrectly placed assertion context",
1527 expected_error_count: 1,
1528 message: "multiple error ids for the same vector assertion",
1529 }
1530 .run();
1531 NegativeTestCase {
1532 input: "error_id 42 assert",
1533 expected_error: "incorrectly placed assertion context",
1534 expected_error_count: 1,
1535 message: "error id precedes assertion",
1536 }
1537 .run();
1538 NegativeTestCase {
1539 input: "error_id 42 assert error_id 1337",
1540 expected_error: "incorrectly placed assertion context",
1541 expected_error_count: 1,
1542 message: "first error id precedes assertion",
1543 }
1544 .run();
1545 }
1546
1547 #[proptest]
1548 fn assertion_context_error_id_fails_for_invalid_id(
1549 #[strategy(proptest::strategy::Union::new(["assert", "assert_vector"]))]
1550 instruction: String,
1551 #[filter(#id.parse::<i128>().is_err())] id: String,
1552 ) {
1553 if let Some(comment_idx) = id.find("//") {
1557 let (id, _) = id.split_at(comment_idx);
1558 prop_assume!(id.parse::<i128>().is_err());
1559 }
1560
1561 let input = format!("{instruction} error_id {id}");
1563 prop_assert!(parse(&input).is_err());
1564 }
1565
1566 #[proptest]
1567 fn type_hint_to_string_to_type_hint_is_identity(#[strategy(arb())] type_hint: TypeHint) {
1568 let type_hint_string = type_hint.to_string();
1569 let instruction_tokens =
1570 parse(&type_hint_string).map_err(|err| TestCaseError::fail(err.to_string()))?;
1571 let labelled_instructions = to_labelled_instructions(&instruction_tokens);
1572 prop_assert_eq!(1, labelled_instructions.len());
1573 let first_labelled_instruction = labelled_instructions[0].clone();
1574 let_assert!(LabelledInstruction::TypeHint(parsed_type_hint) = first_labelled_instruction);
1575 prop_assert_eq!(type_hint, parsed_type_hint);
1576 }
1577
1578 #[proptest]
1579 fn assertion_context_to_string_to_assertion_context_is_identity(
1580 #[strategy(arb())] context: AssertionContext,
1581 ) {
1582 let assert_with_context = format!("assert {context}");
1583 let instruction_tokens =
1584 parse(&assert_with_context).map_err(|err| TestCaseError::fail(err.to_string()))?;
1585 let labelled_instructions = to_labelled_instructions(&instruction_tokens);
1586 prop_assert_eq!(2, labelled_instructions.len());
1587 let_assert!(LabelledInstruction::AssertionContext(parsed) = &labelled_instructions[1]);
1588 prop_assert_eq!(&context, parsed);
1589 }
1590
1591 #[test]
1592 fn type_hint_type_can_contain_angle_brackets_and_leading_asterisks() {
1593 fn test(expected: Option<&'static str>, input: &'static str) {
1594 dbg!(input);
1595 let input = format!(": {input}");
1596 let parse_result = type_hint_type_name(&input);
1597 let Some(expected) = expected else {
1598 if let Ok((_, Some(ty))) = parse_result {
1599 panic!("expected error but parsed \"{ty}\"");
1600 }
1601 return assert!(parse_result.is_err());
1602 };
1603 let_assert!(Ok((rest, Some(ty))) = parse_result);
1604 dbg!(rest);
1605 assert!(expected == ty);
1606 }
1607
1608 test(Some("MyType"), "MyType");
1609 test(Some("MyType"), " MyType ");
1610
1611 test(Some("*Pointer"), "*Pointer");
1612 test(Some("*_Pointer"), "*_Pointer");
1613 test(Some("***Pointer"), "***Pointer");
1614 test(Some("*Po"), "*Po*nter");
1615 test(Some("*Po_"), "*Po_*nter");
1616
1617 test(Some("NoGenerics"), "NoGenerics<>");
1618 test(Some("NoGenerics"), "NoGenerics <>");
1619 test(Some("NoGenerics"), "NoGenerics< /* comment */ >");
1620 test(Some("NoGenerics"), "NoGenerics<");
1621 test(Some("NoGenerics"), "NoGenerics< /* comment */");
1622 test(Some("NoGenerics"), "NoGenerics< // comment");
1623
1624 test(Some("OneGeneric<A>"), "OneGeneric<A>");
1625 test(Some("OneGeneric<A>"), "OneGeneric<A >");
1626 test(Some("OneGeneric<A>"), "OneGeneric< A>");
1627 test(Some("OneGeneric<A>"), "OneGeneric< A >");
1628 test(Some("OneGeneric<A>"), "OneGeneric<A,>");
1629 test(Some("OneGeneric<A>"), "OneGeneric<A, /* … */ >");
1630 test(Some("OneGeneric"), "OneGeneric<A,,>");
1631 test(Some("OneGeneric"), "OneGeneric<,A,>");
1632 test(Some("OneGeneric<*A>"), "OneGeneric<*A>");
1633 test(Some("OneGeneric<*A>"), "OneGeneric<*A,>");
1634 test(Some("OneGeneric<*A>"), "OneGeneric<*A, >");
1635 test(Some("OneGeneric<*A>"), "OneGeneric< *A, >");
1636 test(Some("OneGeneric<*A>"), "OneGeneric< *A , >");
1637 test(Some("OneGeneric"), "OneGeneric< *A , , >");
1638
1639 test(Some("TwoGenerics<A, B>"), "TwoGenerics<A, B>");
1640 test(Some("TwoGenerics<A, B>"), "TwoGenerics<A, B >");
1641 test(Some("TwoGenerics<A, B>"), "TwoGenerics< A, B>");
1642 test(Some("TwoGenerics<A, B>"), "TwoGenerics< A, B >");
1643 test(Some("TwoGenerics<A, B>"), "TwoGenerics<A, B,>");
1644 test(Some("TwoGenerics<A, B>"), "TwoGenerics<A, B, /* … */ >");
1645 test(Some("TwoGenerics"), "TwoGenerics<A, B,,>");
1646 test(Some("TwoGenerics"), "TwoGenerics<,A, B,>");
1647 test(Some("TwoGenerics<*A, B>"), "TwoGenerics<*A, B>");
1648 test(Some("TwoGenerics<*A, B>"), "TwoGenerics<*A, B,>");
1649 test(Some("TwoGenerics<A, *B>"), "TwoGenerics<A, *B,>");
1650 test(Some("*TwoGenerics<A, B>"), "*TwoGenerics<A, B,>");
1651
1652 test(Some("Outer<Inner<A>>"), "Outer<Inner<A>>");
1653 test(Some("Outer<Inner<A>>"), "Outer < Inner < A > >");
1654 test(Some("Outer<*Inner<A>>"), "Outer < *Inner < A > >");
1655 test(Some("Outer<*Inner<*A>>"), "Outer < *Inner < *A > >");
1656 test(Some("*Outer<*Inner<*A>>"), "*Outer < *Inner < *A > >");
1657 test(Some("**Outer<**Inner<**A>>"), "**Outer < **Inner < **A > >");
1658
1659 test(None, "");
1660 test(None, "0");
1661 test(None, "*0");
1662 test(None, "*0MyType");
1663 }
1664
1665 #[test]
1666 fn triton_asm_macro() {
1667 let instructions = triton_asm!(write_io 3 push 17 call huh lt swap 3);
1668 assert_eq!(
1669 LabelledInstruction::Instruction(AnInstruction::WriteIo(NumberOfWords::N3)),
1670 instructions[0]
1671 );
1672 assert_eq!(
1673 LabelledInstruction::Instruction(AnInstruction::Push(bfe!(17))),
1674 instructions[1]
1675 );
1676 assert_eq!(
1677 LabelledInstruction::Instruction(AnInstruction::Call("huh".to_string())),
1678 instructions[2]
1679 );
1680 assert_eq!(
1681 LabelledInstruction::Instruction(AnInstruction::Lt),
1682 instructions[3]
1683 );
1684 assert_eq!(
1685 LabelledInstruction::Instruction(AnInstruction::Swap(OpStackElement::ST3)),
1686 instructions[4]
1687 );
1688 }
1689
1690 #[test]
1691 fn triton_asm_macro_with_a_single_return() {
1692 let instructions = triton_asm!(return);
1693 assert_eq!(
1694 LabelledInstruction::Instruction(AnInstruction::Return),
1695 instructions[0]
1696 );
1697 }
1698
1699 #[test]
1700 fn triton_asm_macro_with_a_single_assert() {
1701 let instructions = triton_asm!(assert);
1702 assert_eq!(
1703 LabelledInstruction::Instruction(AnInstruction::Assert),
1704 instructions[0]
1705 );
1706 }
1707
1708 #[test]
1709 fn triton_asm_macro_with_only_assert_and_return() {
1710 let instructions = triton_asm!(assert return);
1711 assert_eq!(
1712 LabelledInstruction::Instruction(AnInstruction::Assert),
1713 instructions[0]
1714 );
1715 assert_eq!(
1716 LabelledInstruction::Instruction(AnInstruction::Return),
1717 instructions[1]
1718 );
1719 }
1720
1721 #[test]
1722 fn triton_program_macro() {
1723 let program = triton_program!(
1724 call routine_1
1726 call routine_2 halt
1728
1729 routine_1:
1731 call routine_3
1735 return
1736
1737 routine_2:
1739 push 18 push 7 add
1740 push 25 eq assert
1741 return
1742
1743 routine_3:
1744 alternative_label:
1745 nop nop
1746 return
1747 );
1748 println!("{program}");
1749 }
1750
1751 #[test]
1752 fn triton_program_macro_interpolates_various_types() {
1753 let push_arg = rand::rng().random_range(0..BFieldElement::P);
1754 let instruction_push =
1755 LabelledInstruction::Instruction(AnInstruction::Push(push_arg.into()));
1756 let swap_argument = "1";
1757 triton_program!({instruction_push} push {push_arg} swap {swap_argument} eq assert halt);
1758 }
1759
1760 #[test]
1761 fn triton_instruction_macro_parses_simple_instructions() {
1762 assert_eq!(
1763 LabelledInstruction::Instruction(AnInstruction::Halt),
1764 triton_instr!(halt)
1765 );
1766 assert_eq!(
1767 LabelledInstruction::Instruction(AnInstruction::Add),
1768 triton_instr!(add)
1769 );
1770 assert_eq!(
1771 LabelledInstruction::Instruction(AnInstruction::Pop(NumberOfWords::N3)),
1772 triton_instr!(pop 3)
1773 );
1774 }
1775
1776 #[test]
1777 #[should_panic(expected = "not_an_instruction")]
1778 fn triton_instruction_macro_fails_when_encountering_unknown_instruction() {
1779 triton_instr!(not_an_instruction);
1780 }
1781
1782 #[test]
1783 fn triton_instruction_macro_parses_instructions_with_argument() {
1784 assert_eq!(
1785 LabelledInstruction::Instruction(AnInstruction::Push(bfe!(7))),
1786 triton_instr!(push 7)
1787 );
1788 assert_eq!(
1789 LabelledInstruction::Instruction(AnInstruction::Dup(OpStackElement::ST3)),
1790 triton_instr!(dup 3)
1791 );
1792 assert_eq!(
1793 LabelledInstruction::Instruction(AnInstruction::Swap(OpStackElement::ST5)),
1794 triton_instr!(swap 5)
1795 );
1796 assert_eq!(
1797 LabelledInstruction::Instruction(AnInstruction::Call("my_label".to_string())),
1798 triton_instr!(call my_label)
1799 );
1800 }
1801
1802 #[test]
1803 fn triton_asm_macro_can_repeat_instructions() {
1804 let instructions = triton_asm![push 42; 3];
1805 let expected_instructions =
1806 vec![LabelledInstruction::Instruction(AnInstruction::Push(bfe!(42))); 3];
1807 assert_eq!(expected_instructions, instructions);
1808
1809 let instructions = triton_asm![read_io 2; 15];
1810 let expected_instructions =
1811 vec![LabelledInstruction::Instruction(AnInstruction::ReadIo(NumberOfWords::N2)); 15];
1812 assert_eq!(expected_instructions, instructions);
1813
1814 let instructions = triton_asm![divine 3; Digest::LEN];
1815 let expected_instructions =
1816 vec![
1817 LabelledInstruction::Instruction(AnInstruction::Divine(NumberOfWords::N3));
1818 Digest::LEN
1819 ];
1820 assert_eq!(expected_instructions, instructions);
1821 }
1822
1823 #[test]
1824 fn break_gets_turned_into_labelled_instruction() {
1825 let instructions = triton_asm![break];
1826 let expected_instructions = vec![LabelledInstruction::Breakpoint];
1827 assert_eq!(expected_instructions, instructions);
1828 }
1829
1830 #[test]
1831 fn break_does_not_propagate_to_full_program() {
1832 let program = triton_program! { break halt break };
1833 assert_eq!(1, program.len_bwords());
1834 }
1835}