1use crate::{
4 U256, encode_pair, encode_single,
5 eyre::{self, Result},
6};
7use revm_bytecode::opcode::{self as op, OpCode};
8use revm_primitives::map::HashMap;
9use std::cmp::Ordering;
10
11pub fn parse_asm(s: &str) -> Result<Vec<u8>> {
35 let tokens = preprocess(s)?;
36 let items = parse_items(&tokens)?;
37 layout_and_emit(&items)
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
46pub(super) struct Token<'a> {
47 pub src: &'a str,
49 pub kind: TokenKind,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
55pub(super) enum TokenKind {
56 Ident,
58 Label,
60 LabelRef,
62 Number(U256),
64 Comma,
66 LParen,
68 RParen,
70 ParamRef,
72 Whitespace,
74 Comment,
76 Unknown,
78}
79
80pub(super) struct Tokenizer<'a> {
84 src: &'a str,
85 pos: usize,
86}
87
88impl<'a> Tokenizer<'a> {
89 pub(super) fn new(src: &'a str) -> Self {
90 Self { src, pos: 0 }
91 }
92
93 fn remaining(&self) -> &'a str {
94 &self.src[self.pos..]
95 }
96
97 fn peek_char(&self) -> Option<char> {
98 self.remaining().chars().next()
99 }
100
101 fn advance(&mut self, n: usize) {
102 self.pos += n;
103 }
104
105 fn read_word(&mut self) -> &'a str {
107 let rest = self.remaining();
108 let end = rest.find(|c: char| !c.is_ascii_alphanumeric() && c != '_').unwrap_or(rest.len());
109 let word = &rest[..end];
110 self.advance(end);
111 word
112 }
113
114 fn read_number(&mut self) -> Token<'a> {
116 let rest = self.remaining();
117 let end = if rest.starts_with("0x") || rest.starts_with("0X") {
118 2 + rest[2..].find(|c: char| !c.is_ascii_hexdigit()).unwrap_or(rest.len() - 2)
119 } else {
120 rest.find(|c: char| !c.is_ascii_digit()).unwrap_or(rest.len())
121 };
122 let src = &rest[..end];
123 self.advance(end);
124 match src.parse::<U256>() {
125 Ok(n) => Token { src, kind: TokenKind::Number(n) },
126 Err(_) => Token { src, kind: TokenKind::Unknown },
127 }
128 }
129}
130
131impl<'a> Iterator for Tokenizer<'a> {
132 type Item = Token<'a>;
133
134 fn next(&mut self) -> Option<Token<'a>> {
135 let rest = self.remaining();
137 let trimmed = rest.trim_start_matches(|c: char| c.is_ascii_whitespace());
138 let ws_len = rest.len() - trimmed.len();
139 if ws_len > 0 {
140 let src = &self.src[self.pos..self.pos + ws_len];
141 self.advance(ws_len);
142 return Some(Token { src, kind: TokenKind::Whitespace });
143 }
144
145 let c = self.peek_char()?;
146
147 match c {
148 ';' => {
149 let start = self.pos;
150 if let Some(nl) = self.remaining().find('\n') {
151 self.advance(nl);
152 } else {
153 self.pos = self.src.len();
154 }
155 Some(Token { src: &self.src[start..self.pos], kind: TokenKind::Comment })
156 }
157
158 '%' => {
159 let start = self.pos;
160 self.advance(1);
161 let name = self.read_word();
162 Some(if name.is_empty() {
163 Token { src: &self.src[start..self.pos], kind: TokenKind::Unknown }
164 } else {
165 Token { src: name, kind: TokenKind::LabelRef }
166 })
167 }
168
169 '$' => {
170 let start = self.pos;
171 self.advance(1);
172 let name = self.read_word();
173 Some(if name.is_empty() {
174 Token { src: &self.src[start..self.pos], kind: TokenKind::Unknown }
175 } else {
176 Token { src: name, kind: TokenKind::ParamRef }
177 })
178 }
179
180 ',' => {
181 let src = &self.src[self.pos..self.pos + 1];
182 self.advance(1);
183 Some(Token { src, kind: TokenKind::Comma })
184 }
185 '(' => {
186 let src = &self.src[self.pos..self.pos + 1];
187 self.advance(1);
188 Some(Token { src, kind: TokenKind::LParen })
189 }
190 ')' => {
191 let src = &self.src[self.pos..self.pos + 1];
192 self.advance(1);
193 Some(Token { src, kind: TokenKind::RParen })
194 }
195
196 '0'..='9' => Some(self.read_number()),
197
198 _ if c.is_ascii_alphabetic() || c == '_' => {
199 let word = self.read_word();
200 if self.peek_char() == Some(':') {
201 self.advance(1);
202 Some(Token { src: word, kind: TokenKind::Label })
203 } else {
204 Some(Token { src: word, kind: TokenKind::Ident })
205 }
206 }
207
208 ':' => {
209 self.advance(1);
210 Some(Token { src: "", kind: TokenKind::Label })
211 }
212
213 _ => {
214 let start = self.pos;
215 while let Some(c) = self.peek_char() {
217 if c.is_ascii_whitespace()
218 || c.is_ascii_alphanumeric()
219 || matches!(c, '_' | ';' | '%' | '$' | ',' | '(' | ')' | ':')
220 {
221 break;
222 }
223 self.advance(c.len_utf8());
224 }
225 Some(Token { src: &self.src[start..self.pos], kind: TokenKind::Unknown })
226 }
227 }
228 }
229}
230
231struct MacroDef<'a> {
237 is_fn: bool,
239 params: Vec<&'a str>,
240 body: Vec<Token<'a>>,
241}
242
243fn builtin_macros() -> HashMap<&'static str, MacroDef<'static>> {
245 let mut m = HashMap::default();
246 m.insert(
247 "RET_WORD",
248 MacroDef {
249 is_fn: false,
250 params: vec![],
251 body: vec![
252 Token { src: "PUSH0", kind: TokenKind::Ident },
253 Token { src: "MSTORE", kind: TokenKind::Ident },
254 Token { src: "PUSH1", kind: TokenKind::Ident },
255 Token { src: "0x20", kind: TokenKind::Number(U256::from(0x20)) },
256 Token { src: "PUSH0", kind: TokenKind::Ident },
257 Token { src: "RETURN", kind: TokenKind::Ident },
258 ],
259 },
260 );
261 m
262}
263
264fn preprocess(s: &str) -> Result<Vec<Token<'_>>> {
267 let mut macros = builtin_macros();
268
269 let mut rest_start = Vec::new();
272 for line in s.lines() {
273 let trimmed = line.trim();
274 if let Some(after) = trimmed.strip_prefix("#define")
275 && (after.is_empty() || after.starts_with(|c: char| c.is_ascii_whitespace()))
276 {
277 parse_define(after, &mut macros)?;
278 } else {
279 let offset = trimmed.as_ptr() as usize - s.as_ptr() as usize;
281 rest_start.push((offset, offset + trimmed.len()));
282 }
283 }
284
285 let mut raw = Vec::new();
287 for &(start, end) in &rest_start {
288 let line = &s[start..end];
289 raw.extend(
290 Tokenizer::new(line)
291 .filter(|t| !matches!(t.kind, TokenKind::Whitespace | TokenKind::Comment)),
292 );
293 }
294
295 if macros.is_empty() {
296 return Ok(raw);
297 }
298
299 expand_macros(raw, ¯os)
300}
301
302fn parse_define<'a>(after: &'a str, macros: &mut HashMap<&'a str, MacroDef<'a>>) -> Result<()> {
304 let mut tok = Tokenizer::new(after)
305 .filter(|t| !matches!(t.kind, TokenKind::Whitespace | TokenKind::Comment))
306 .peekable();
307
308 let name = match tok.next() {
309 Some(Token { src, kind: TokenKind::Ident }) => src,
310 Some(other) => return Err(eyre::eyre!("expected macro name after #define, got {other:?}")),
311 None => return Err(eyre::eyre!("expected macro name after #define")),
312 };
313
314 let is_fn = after.as_bytes().get(name.as_ptr() as usize - after.as_ptr() as usize + name.len())
317 == Some(&b'(');
318
319 let all_tokens: Vec<Token<'a>> = tok.collect();
320 let mut i = 0;
321
322 let mut params = Vec::new();
323 if is_fn && matches!(all_tokens.get(i), Some(Token { kind: TokenKind::LParen, .. })) {
324 i += 1; if !matches!(all_tokens.get(i), Some(Token { kind: TokenKind::RParen, .. })) {
326 loop {
327 match all_tokens.get(i) {
328 Some(Token { src: p, kind: TokenKind::Ident }) => {
329 params.push(*p);
330 i += 1;
331 }
332 other => {
333 return Err(eyre::eyre!(
334 "expected parameter name in #define {name}, got {other:?}"
335 ));
336 }
337 }
338 match all_tokens.get(i) {
339 Some(Token { kind: TokenKind::RParen, .. }) => {
340 i += 1;
341 break;
342 }
343 Some(Token { kind: TokenKind::Comma, .. }) => i += 1,
344 other => {
345 return Err(eyre::eyre!(
346 "expected ',' or ')' in #define {name} parameter list, got {other:?}"
347 ));
348 }
349 }
350 }
351 } else {
352 i += 1; }
354 }
355
356 let body = all_tokens[i..].to_vec();
357 macros.insert(name, MacroDef { is_fn, params, body });
358 Ok(())
359}
360
361fn expand_macros<'a>(
363 tokens: Vec<Token<'a>>,
364 macros: &HashMap<&str, MacroDef<'a>>,
365) -> Result<Vec<Token<'a>>> {
366 let mut out = Vec::with_capacity(tokens.len());
367 let mut iter = tokens.into_iter().peekable();
368
369 while let Some(tok) = iter.next() {
370 let TokenKind::Ident = &tok.kind else {
371 out.push(tok);
372 continue;
373 };
374 let Some(mac) = macros.get(tok.src) else {
375 out.push(tok);
376 continue;
377 };
378 let name = tok.src;
379
380 if !mac.is_fn {
381 out.extend(mac.body.iter().cloned());
383 } else {
384 eyre::ensure!(
386 matches!(iter.next(), Some(Token { kind: TokenKind::LParen, .. })),
387 "macro {name:?} expects arguments",
388 );
389
390 let mut args: Vec<Vec<Token<'a>>> = vec![vec![]];
392 let mut depth = 1u32;
393 loop {
394 let t = iter
395 .next()
396 .ok_or_else(|| eyre::eyre!("unclosed '(' in macro invocation {name:?}"))?;
397 match &t.kind {
398 TokenKind::LParen => {
399 depth += 1;
400 args.last_mut().unwrap().push(t);
401 }
402 TokenKind::RParen => {
403 depth -= 1;
404 if depth == 0 {
405 break;
406 }
407 args.last_mut().unwrap().push(t);
408 }
409 TokenKind::Comma if depth == 1 => args.push(vec![]),
410 _ => args.last_mut().unwrap().push(t),
411 }
412 }
413
414 if mac.params.is_empty() {
416 eyre::ensure!(
417 args.len() == 1 && args[0].is_empty(),
418 "macro {name:?} takes no arguments"
419 );
420 } else {
421 eyre::ensure!(
422 args.len() == mac.params.len(),
423 "macro {name:?} expects {} argument(s), got {}",
424 mac.params.len(),
425 args.len()
426 );
427 }
428
429 for body_tok in &mac.body {
431 if let TokenKind::ParamRef = body_tok.kind
432 && let Some(idx) = mac.params.iter().position(|p| *p == body_tok.src)
433 {
434 out.extend(args[idx].iter().cloned());
435 } else {
436 out.push(body_tok.clone());
437 }
438 }
439 }
440 }
441 Ok(out)
442}
443
444enum Item<'a> {
450 Label(&'a str),
452 Inst(Inst<'a>),
454}
455
456struct Inst<'a> {
458 opcode: u8,
459 imm: Option<Imm<'a>>,
460 push_kind: PushKind,
461}
462
463enum Imm<'a> {
465 Number(U256),
467 Label(&'a str),
469}
470
471enum PushKind {
473 None,
475 Fixed(u8),
477 Auto,
479}
480
481fn parse_items<'a>(tokens: &[Token<'a>]) -> Result<Vec<Item<'a>>> {
483 let mut items = Vec::new();
484 let mut i = 0;
485 while i < tokens.len() {
486 match &tokens[i].kind {
487 TokenKind::Label => {
488 let name = tokens[i].src;
489 eyre::ensure!(!name.is_empty(), "empty label name");
490 items.push(Item::Label(name));
491 i += 1;
492 }
493 TokenKind::Ident => {
494 let word = tokens[i].src;
495 if word == "PUSH" {
496 i += 1;
497 let imm = expect_imm(tokens, &mut i, "PUSH")?;
498 items.push(Item::Inst(Inst {
499 opcode: 0,
500 imm: Some(imm),
501 push_kind: PushKind::Auto,
502 }));
503 } else if word == "DUP" || word == "SWAP" {
504 let is_swap = word == "SWAP";
505 i += 1;
506 let n = expect_number_u8(tokens, &mut i, word)?;
507 eyre::ensure!(n >= 1, "{word} index must be >= 1, got {n}");
508 if n <= 16 {
509 let base = if is_swap { op::SWAP1 } else { op::DUP1 };
510 items.push(Item::Inst(Inst {
511 opcode: base + n - 1,
512 imm: None,
513 push_kind: PushKind::None,
514 }));
515 } else {
516 let eof_op = if is_swap { op::SWAPN } else { op::DUPN };
517 let raw = encode_single(n).ok_or_else(|| {
518 eyre::eyre!("{word} index {n} out of valid range [1, 235]")
519 })?;
520 items.push(Item::Inst(Inst {
521 opcode: eof_op,
522 imm: Some(Imm::Number(U256::from(raw))),
523 push_kind: PushKind::Fixed(1),
524 }));
525 }
526 } else {
527 let opc = OpCode::parse(word)
528 .ok_or_else(|| eyre::eyre!("invalid opcode: {word:?}"))?;
529 let opcode = opc.get();
530 i += 1;
531
532 if opcode == op::DUPN || opcode == op::SWAPN {
533 let n = expect_number_u8(tokens, &mut i, opc)?;
534 let raw = encode_single(n).ok_or_else(|| {
535 eyre::eyre!("{opc} index {n} out of valid range [17, 235]")
536 })?;
537 items.push(Item::Inst(Inst {
538 opcode,
539 imm: Some(Imm::Number(U256::from(raw))),
540 push_kind: PushKind::Fixed(1),
541 }));
542 } else if opcode == op::EXCHANGE {
543 let n = expect_number_u8(tokens, &mut i, opc)?;
544 let m = expect_number_u8(tokens, &mut i, opc)?;
545 let raw = encode_pair(n, m).ok_or_else(|| {
546 eyre::eyre!("EXCHANGE pair ({n}, {m}) cannot be encoded")
547 })?;
548 items.push(Item::Inst(Inst {
549 opcode,
550 imm: Some(Imm::Number(U256::from(raw))),
551 push_kind: PushKind::Fixed(1),
552 }));
553 } else {
554 let imm_len = opc.info().immediate_size();
555 if imm_len > 0 {
556 let imm = expect_imm(tokens, &mut i, opc)?;
557 items.push(Item::Inst(Inst {
558 opcode,
559 imm: Some(imm),
560 push_kind: PushKind::Fixed(imm_len),
561 }));
562 } else {
563 if matches!(
564 tokens.get(i),
565 Some(Token { kind: TokenKind::Number(_), .. })
566 ) {
567 eyre::bail!("unexpected immediate for opcode {opc}");
568 }
569 items.push(Item::Inst(Inst {
570 opcode,
571 imm: None,
572 push_kind: PushKind::None,
573 }));
574 }
575 }
576 }
577 }
578 TokenKind::Unknown => return Err(eyre::eyre!("unexpected token: {:?}", tokens[i].src)),
579 _ => return Err(eyre::eyre!("unexpected token: {:?}", tokens[i])),
580 }
581 }
582 Ok(items)
583}
584
585fn expect_number_u8(
587 tokens: &[Token<'_>],
588 i: &mut usize,
589 ctx: impl std::fmt::Display,
590) -> Result<u8> {
591 let tok = tokens.get(*i).ok_or_else(|| eyre::eyre!("missing immediate for opcode {ctx}"))?;
592 *i += 1;
593 match &tok.kind {
594 TokenKind::Number(n) => {
595 let v: u64 =
596 n.try_into().map_err(|_| eyre::eyre!("invalid {ctx} immediate: too large"))?;
597 u8::try_from(v).map_err(|_| eyre::eyre!("invalid {ctx} immediate: too large"))
598 }
599 _ => Err(eyre::eyre!("expected numeric immediate for {ctx}, got {tok:?}")),
600 }
601}
602
603fn expect_imm<'a>(
605 tokens: &[Token<'a>],
606 i: &mut usize,
607 ctx: impl std::fmt::Display,
608) -> Result<Imm<'a>> {
609 let tok = tokens.get(*i).ok_or_else(|| eyre::eyre!("missing immediate for opcode {ctx}"))?;
610 *i += 1;
611 match &tok.kind {
612 TokenKind::Number(n) => Ok(Imm::Number(*n)),
613 TokenKind::LabelRef => Ok(Imm::Label(tok.src)),
614 _ => Err(eyre::eyre!("expected immediate for {ctx}, got {tok:?}")),
615 }
616}
617
618fn encode_imm(num: U256, size: Option<u8>) -> Result<Vec<u8>> {
624 let mut bytes = num.to_be_bytes_trimmed_vec();
625 if let Some(size) = size {
626 debug_assert!(size <= 32);
627 match bytes.len().cmp(&(size as usize)) {
628 Ordering::Less => {
629 let extend = size as usize - bytes.len();
630 bytes.splice(0..0, std::iter::repeat_n(0, extend));
631 }
632 Ordering::Equal => {}
633 Ordering::Greater => {
634 eyre::bail!("expected at most {size} immediate bytes, got {}", bytes.len());
635 }
636 }
637 }
638 debug_assert!(bytes.len() <= 32);
639 Ok(bytes)
640}
641
642fn min_push_width(val: usize) -> u8 {
644 if val == 0 {
645 0
646 } else {
647 let bits = usize::BITS - val.leading_zeros();
648 bits.div_ceil(8) as u8
649 }
650}
651
652fn layout_and_emit(items: &[Item<'_>]) -> Result<Vec<u8>> {
654 let mut auto_label_indices = Vec::new();
655 let mut has_any_label = false;
656
657 for (i, item) in items.iter().enumerate() {
658 match item {
659 Item::Label(_) => has_any_label = true,
660 Item::Inst(inst) => {
661 if matches!(inst.imm, Some(Imm::Label(_))) {
662 has_any_label = true;
663 if matches!(inst.push_kind, PushKind::Auto) {
664 auto_label_indices.push(i);
665 }
666 }
667 }
668 }
669 }
670
671 if !has_any_label {
673 let mut code = Vec::with_capacity(32);
674 for item in items {
675 if let Item::Inst(inst) = item {
676 emit_inst_no_labels(inst, &mut code)?;
677 }
678 }
679 return Ok(code);
680 }
681
682 let mut auto_widths = vec![0u8; items.len()];
684 let mut label_pcs = HashMap::<&str, usize>::default();
685
686 loop {
687 label_pcs.clear();
688 let mut pc = 0usize;
689 for (i, item) in items.iter().enumerate() {
690 match item {
691 Item::Label(name) => {
692 label_pcs.insert(name, pc);
693 }
694 Item::Inst(inst) => {
695 pc += 1;
696 match &inst.push_kind {
697 PushKind::None => {}
698 PushKind::Fixed(n) => pc += *n as usize,
699 PushKind::Auto => {
700 let width = match &inst.imm {
701 Some(Imm::Label(_)) => auto_widths[i],
702 Some(Imm::Number(n)) => {
703 let bytes = n.to_be_bytes_trimmed_vec();
704 bytes.len() as u8
705 }
706 None => 0,
707 };
708 pc += width as usize;
709 }
710 }
711 }
712 }
713 }
714
715 let mut changed = false;
716 for &i in &auto_label_indices {
717 if let Item::Inst(inst) = &items[i]
718 && let Some(Imm::Label(name)) = &inst.imm
719 {
720 let target_pc =
721 *label_pcs.get(name).ok_or_else(|| eyre::eyre!("undefined label: {name:?}"))?;
722 let needed = min_push_width(target_pc);
723 if needed > auto_widths[i] {
724 auto_widths[i] = needed;
725 changed = true;
726 }
727 }
728 }
729
730 if !changed {
731 break;
732 }
733 }
734
735 let mut code = Vec::with_capacity(64);
737 for (i, item) in items.iter().enumerate() {
738 if let Item::Inst(inst) = item {
739 match &inst.push_kind {
740 PushKind::None => {
741 code.push(inst.opcode);
742 }
743 PushKind::Fixed(size) => {
744 code.push(inst.opcode);
745 let val = resolve_imm(inst.imm.as_ref().unwrap(), &label_pcs)?;
746 let bytes = encode_imm(val, Some(*size))?;
747 code.extend_from_slice(&bytes);
748 }
749 PushKind::Auto => {
750 let val = resolve_imm(inst.imm.as_ref().unwrap(), &label_pcs)?;
751 let width = match &inst.imm {
752 Some(Imm::Label(_)) => auto_widths[i],
753 _ => {
754 let bytes = val.to_be_bytes_trimmed_vec();
755 bytes.len() as u8
756 }
757 };
758 let push0 = OpCode::PUSH0.get();
759 code.push(push0 + width);
760 if width > 0 {
761 let bytes = encode_imm(val, Some(width))?;
762 code.extend_from_slice(&bytes);
763 }
764 }
765 }
766 }
767 }
768
769 Ok(code)
770}
771
772fn resolve_imm(imm: &Imm<'_>, label_pcs: &HashMap<&str, usize>) -> Result<U256> {
774 match imm {
775 Imm::Number(n) => Ok(*n),
776 Imm::Label(name) => {
777 let pc = label_pcs.get(name).ok_or_else(|| eyre::eyre!("undefined label: {name:?}"))?;
778 Ok(U256::from(*pc))
779 }
780 }
781}
782
783fn emit_inst_no_labels(inst: &Inst<'_>, code: &mut Vec<u8>) -> Result<()> {
785 match &inst.push_kind {
786 PushKind::None => {
787 code.push(inst.opcode);
788 }
789 PushKind::Fixed(size) => {
790 code.push(inst.opcode);
791 let Imm::Number(n) = inst.imm.as_ref().unwrap() else {
792 unreachable!();
793 };
794 let bytes = encode_imm(*n, Some(*size))?;
795 code.extend_from_slice(&bytes);
796 }
797 PushKind::Auto => {
798 let Imm::Number(n) = inst.imm.as_ref().unwrap() else {
799 unreachable!();
800 };
801 let bytes = encode_imm(*n, None)?;
802 let push0 = OpCode::PUSH0.get();
803 code.push(push0 + bytes.len() as u8);
804 code.extend_from_slice(&bytes);
805 }
806 }
807 Ok(())
808}
809
810#[cfg(test)]
811mod tests {
812 use super::*;
813 use revm_bytecode::opcode as op;
814
815 #[test]
816 fn basic_opcodes() {
817 let cases: &[(&str, Vec<u8>)] = &[
818 ("ADD ; ADD\n ADD", vec![op::ADD, op::ADD]),
819 ("PUSH1 0", vec![op::PUSH1, 0]),
820 ("PUSH3 0x000069", vec![op::PUSH3, 0, 0, 0x69]),
821 ("PUSH3 0x69 ; padded", vec![op::PUSH3, 0, 0, 0x69]),
822 ("PUSH 0", vec![op::PUSH0]),
823 ("PUSH 1", vec![op::PUSH1, 1]),
824 ("PUSH 2", vec![op::PUSH1, 2]),
825 ("PUSH 69", vec![op::PUSH1, 69]),
826 ("PUSH 0x2222", vec![op::PUSH2, 0x22, 0x22]),
827 ];
828 for (s, expected) in cases.iter() {
829 let code = match parse_asm(s) {
830 Ok(code) => code,
831 Err(e) => panic!("code: {s:?}\n\n err: {e}"),
832 };
833 assert_eq!(code, *expected, "{s:?}");
834 }
835 }
836
837 #[test]
838 fn label_forward_ref() {
839 let code = parse_asm(
840 "
841 PUSH %target
842 JUMP
843 target:
844 JUMPDEST
845 STOP
846 ",
847 )
848 .unwrap();
849 assert_eq!(code, vec![op::PUSH1, 3, op::JUMP, op::JUMPDEST, op::STOP]);
850 }
851
852 #[test]
853 fn label_backward_ref() {
854 let code = parse_asm(
855 "
856 target:
857 JUMPDEST
858 PUSH %target
859 JUMP
860 ",
861 )
862 .unwrap();
863 assert_eq!(code, vec![op::JUMPDEST, op::PUSH0, op::JUMP]);
864 }
865
866 #[test]
867 fn label_fixed_width() {
868 let code = parse_asm(
869 "
870 PUSH1 %target
871 JUMP
872 target:
873 JUMPDEST
874 STOP
875 ",
876 )
877 .unwrap();
878 assert_eq!(code, vec![op::PUSH1, 3, op::JUMP, op::JUMPDEST, op::STOP]);
879 }
880
881 #[test]
882 fn multiple_labels_same_pc() {
883 let code = parse_asm(
884 "
885 a:
886 b:
887 JUMPDEST
888 PUSH %a
889 PUSH %b
890 STOP
891 ",
892 )
893 .unwrap();
894 assert_eq!(code, vec![op::JUMPDEST, op::PUSH0, op::PUSH0, op::STOP]);
895 }
896
897 #[test]
898 fn dup_auto() {
899 assert_eq!(parse_asm("DUP 1").unwrap(), vec![op::DUP1]);
901 assert_eq!(parse_asm("DUP 16").unwrap(), vec![op::DUP16]);
902 assert_eq!(parse_asm("DUP 17").unwrap(), vec![op::DUPN, 0x80]);
904 assert_eq!(parse_asm("DUP 108").unwrap(), vec![op::DUPN, 0xDB]);
905 assert!(parse_asm("DUP 0").is_err());
907 assert!(parse_asm("DUP 236").is_err());
909 }
910
911 #[test]
912 fn swap_auto() {
913 assert_eq!(parse_asm("SWAP 1").unwrap(), vec![op::SWAP1]);
915 assert_eq!(parse_asm("SWAP 16").unwrap(), vec![op::SWAP16]);
916 assert_eq!(parse_asm("SWAP 17").unwrap(), vec![op::SWAPN, 0x80]);
918 assert_eq!(parse_asm("SWAP 108").unwrap(), vec![op::SWAPN, 0xDB]);
919 assert!(parse_asm("SWAP 0").is_err());
921 }
922
923 #[test]
924 fn dupn() {
925 assert_eq!(parse_asm("DUPN 17").unwrap(), vec![op::DUPN, 0x80]);
927 assert_eq!(parse_asm("DUPN 108").unwrap(), vec![op::DUPN, 0xDB]);
928 assert!(parse_asm("DUPN 16").is_err());
929 assert!(parse_asm("DUPN 0").is_err());
930 assert!(parse_asm("DUPN 236").is_err());
931 assert!(parse_asm("DUPN abc").is_err());
932 }
933
934 #[test]
935 fn swapn() {
936 assert_eq!(parse_asm("SWAPN 17").unwrap(), vec![op::SWAPN, 0x80]);
937 assert_eq!(parse_asm("SWAPN 108").unwrap(), vec![op::SWAPN, 0xDB]);
938 assert!(parse_asm("SWAPN 16").is_err());
939 assert!(parse_asm("SWAPN 0").is_err());
940 }
941
942 #[test]
943 fn exchange() {
944 assert_eq!(parse_asm("EXCHANGE 1 2").unwrap(), vec![op::EXCHANGE, 0x8E]);
946 assert!(parse_asm("EXCHANGE 1 14").is_ok());
947 assert!(parse_asm("EXCHANGE 2 1").is_err());
949 assert!(parse_asm("EXCHANGE 0 1").is_err());
951 assert!(parse_asm("EXCHANGE 1").is_err());
953 assert!(parse_asm("EXCHANGE a b").is_err());
955 }
956
957 #[test]
958 fn slotnum() {
959 assert_eq!(parse_asm("SLOTNUM").unwrap(), vec![op::SLOTNUM]);
960 }
961
962 #[test]
963 fn undefined_label() {
964 assert!(parse_asm("PUSH %missing JUMP").is_err());
965 }
966
967 #[test]
968 fn empty_label() {
969 assert!(parse_asm(": STOP").is_err());
970 }
971
972 #[test]
973 fn empty_label_ref() {
974 assert!(parse_asm("PUSH % JUMP").is_err());
975 }
976
977 #[test]
978 fn define_macro() {
979 let code = parse_asm(
980 "
981 #define TWO PUSH 2
982 TWO
983 TWO
984 ADD
985 ",
986 )
987 .unwrap();
988 assert_eq!(code, vec![op::PUSH1, 2, op::PUSH1, 2, op::ADD]);
989 }
990
991 #[test]
992 fn builtin_ret_word() {
993 let code = parse_asm("CALLVALUE RET_WORD").unwrap();
994 assert_eq!(
995 code,
996 vec![op::CALLVALUE, op::PUSH0, op::MSTORE, op::PUSH1, 0x20, op::PUSH0, op::RETURN]
997 );
998 }
999
1000 #[test]
1001 fn define_override_builtin() {
1002 let code = parse_asm(
1003 "
1004 #define RET_WORD STOP
1005 RET_WORD
1006 ",
1007 )
1008 .unwrap();
1009 assert_eq!(code, vec![op::STOP]);
1010 }
1011
1012 #[test]
1013 fn define_with_args() {
1014 let code = parse_asm(
1015 "
1016 #define PUSH_TWO(a, b) PUSH $a PUSH $b
1017 PUSH_TWO(1, 2)
1018 ADD
1019 ",
1020 )
1021 .unwrap();
1022 assert_eq!(code, vec![op::PUSH1, 1, op::PUSH1, 2, op::ADD]);
1023 }
1024
1025 #[test]
1026 fn define_single_arg() {
1027 let code = parse_asm(
1028 "
1029 #define PUSH_AND_STORE(val) PUSH $val PUSH0 MSTORE
1030 PUSH_AND_STORE(0x42)
1031 ",
1032 )
1033 .unwrap();
1034 assert_eq!(code, vec![op::PUSH1, 0x42, op::PUSH0, op::MSTORE]);
1035 }
1036
1037 #[test]
1038 fn define_missing_args() {
1039 assert!(
1040 parse_asm(
1041 "
1042 #define FOO(a) PUSH $a
1043 FOO
1044 "
1045 )
1046 .is_err()
1047 );
1048 }
1049
1050 #[test]
1051 fn define_wrong_arg_count() {
1052 assert!(
1053 parse_asm(
1054 "
1055 #define FOO(a, b) PUSH $a PUSH $b
1056 FOO(1)
1057 "
1058 )
1059 .is_err()
1060 );
1061 }
1062
1063 #[test]
1064 fn define_empty_name() {
1065 assert!(parse_asm("#define").is_err());
1066 assert!(parse_asm("#define ").is_err());
1067 }
1068
1069 #[test]
1070 fn define_invalid_name() {
1071 assert!(parse_asm("#define - STOP").is_err());
1073 assert!(parse_asm("#define (a) STOP").is_err());
1075 }
1076
1077 #[test]
1078 fn define_no_space_after_keyword() {
1079 assert!(parse_asm("#defineFOO STOP").is_err());
1081 }
1082
1083 #[test]
1084 fn define_zero_arg_fn() {
1085 let code = parse_asm(
1086 "
1087 #define NOP() ADD
1088 NOP()
1089 ",
1090 )
1091 .unwrap();
1092 assert_eq!(code, vec![op::ADD]);
1093 }
1094
1095 #[test]
1096 fn define_zero_arg_fn_bare_is_error() {
1097 assert!(
1099 parse_asm(
1100 "
1101 #define NOP() ADD
1102 NOP
1103 "
1104 )
1105 .is_err()
1106 );
1107 }
1108
1109 #[test]
1110 fn define_zero_arg_fn_with_args_is_error() {
1111 assert!(
1112 parse_asm(
1113 "
1114 #define NOP() ADD
1115 NOP(1)
1116 "
1117 )
1118 .is_err()
1119 );
1120 }
1121
1122 #[test]
1123 fn define_malformed_param_list() {
1124 assert!(parse_asm("#define FOO(a STOP").is_err());
1126 assert!(parse_asm("#define FOO(a b) STOP").is_err());
1128 assert!(parse_asm("#define FOO(1) STOP").is_err());
1130 assert!(parse_asm("#define FOO(a,) STOP").is_err());
1132 }
1133
1134 #[test]
1135 fn define_space_before_paren_is_object_like() {
1136 assert!(
1139 parse_asm(
1140 "
1141 #define FOO (a) STOP
1142 FOO
1143 "
1144 )
1145 .is_err()
1146 );
1147 }
1148}