1mod args;
2mod expression;
3mod macros;
4
5pub(crate) mod error;
6mod parser {
7 #![allow(clippy::upper_case_acronyms)]
8
9 use pest_derive::Parser;
10
11 #[derive(Parser)]
12 #[grammar = "parse/asm.pest"]
13 pub(super) struct AsmParser;
14}
15
16use std::convert::TryInto;
17
18use self::{
19 error::ParseError,
20 parser::{AsmParser, Rule},
21};
22use crate::ast::Node;
23use crate::ops::AbstractOp;
24use etk_ops::shanghai::Op;
25use num_bigint::BigInt;
26use pest::{iterators::Pair, Parser};
27
28pub(crate) fn parse_asm(asm: &str) -> Result<Vec<Node>, ParseError> {
29 let mut program: Vec<Node> = Vec::new();
30
31 let pairs = AsmParser::parse(Rule::program, asm)?;
32 for pair in pairs {
33 let node = match pair.as_rule() {
34 Rule::builtin => macros::parse_builtin(pair)?,
35 Rule::EOI => continue,
36 _ => parse_abstract_op(pair)?.into(),
37 };
38 program.push(node);
39 }
40
41 Ok(program)
42}
43
44fn parse_abstract_op(pair: Pair<Rule>) -> Result<AbstractOp, ParseError> {
45 let ret = match pair.as_rule() {
46 Rule::local_macro => macros::parse(pair)?,
47 Rule::label_definition => {
48 AbstractOp::Label(pair.into_inner().next().unwrap().as_str().to_string())
49 }
50 Rule::push => parse_push(pair)?,
51 Rule::op => {
52 let spec: Op<()> = pair.as_str().parse().unwrap();
53 let op = Op::new(spec).unwrap();
54 AbstractOp::Op(op)
55 }
56 _ => unreachable!(),
57 };
58
59 Ok(ret)
60}
61
62fn parse_push(pair: Pair<Rule>) -> Result<AbstractOp, ParseError> {
63 let mut pair = pair.into_inner();
64 let size = pair.next().unwrap();
65 let size: usize = size.as_str().parse().unwrap();
66 let operand = pair.next().unwrap();
67
68 let spec = Op::<()>::push(size).unwrap();
69 let expr = expression::parse(operand)?;
70
71 if let Ok(val) = expr.eval() {
72 let max = BigInt::pow(&BigInt::from(2u32), (8 * size).try_into().unwrap());
73 if val >= max {
74 return error::ImmediateTooLarge.fail();
75 }
76 }
77
78 Ok(AbstractOp::Op(spec.with(expr).unwrap()))
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use crate::ops::{
85 Expression, ExpressionMacroDefinition, ExpressionMacroInvocation, Imm,
86 InstructionMacroDefinition, InstructionMacroInvocation, Terminal,
87 };
88 use assert_matches::assert_matches;
89 use etk_ops::shanghai::*;
90 use hex_literal::hex;
91 use num_bigint::Sign;
92 use std::path::PathBuf;
93
94 macro_rules! nodes {
95 ($($x:expr),+ $(,)?) => (
96 vec![$(Node::from($x)),+]
97 );
98 }
99
100 #[test]
101 fn parse_ops() {
102 let asm = r#"
103 stop
104 pc
105 gas
106 xor
107 push0
108 "#;
109 let expected = nodes![
110 Op::from(Stop),
111 Op::from(GetPc),
112 Op::from(Gas),
113 Op::from(Xor),
114 Op::from(Push0)
115 ];
116 assert_matches!(parse_asm(asm), Ok(e) if e == expected);
117 }
118
119 #[test]
120 fn parse_single_line() {
121 let asm = r#"
122 push1 0b0; push1 0b1
123 "#;
124 let expected = nodes![
125 Op::from(Push1(Imm::from([0]))),
126 Op::from(Push1(Imm::from([1])))
127 ];
128 assert_matches!(parse_asm(asm), Ok(e) if e == expected);
129 }
130
131 #[test]
132 fn parse_mixed_lines() {
133 let asm = r#"
134 push1 0b0; push1 0b1
135 push1 0b1
136 "#;
137 let expected = nodes![
138 Op::from(Push1(Imm::from([0]))),
139 Op::from(Push1(Imm::from([1]))),
140 Op::from(Push1(Imm::from([1])))
141 ];
142 assert_matches!(parse_asm(asm), Ok(e) if e == expected);
143 }
144
145 #[test]
146 fn parse_push_binary() {
147 let asm = r#"
148 # simple cases
149 push1 0b0
150 push1 0b1
151 "#;
152 let expected = nodes![
153 Op::from(Push1(Imm::from([0]))),
154 Op::from(Push1(Imm::from([1])))
155 ];
156 assert_matches!(parse_asm(asm), Ok(e) if e == expected);
157 }
158
159 #[test]
160 fn parse_push_octal() {
161 let asm = r#"
162 # simple cases
163 push1 0o0
164 push1 0o7
165 push2 0o400
166 "#;
167 let expected = nodes![
168 Op::from(Push1(Imm::from([0]))),
169 Op::from(Push1(Imm::from([7]))),
170 Op::from(Push2(Imm::from([1, 0]))),
171 ];
172 println!("{:?}\n\n{:?}", parse_asm(asm), expected);
173 assert_matches!(parse_asm(asm), Ok(e) if e == expected);
174 }
175
176 #[test]
177 fn parse_push_decimal() {
178 let asm = r#"
179 # simple cases
180 push1 0
181 push1 1
182
183 # left-pad values too small
184 push2 42
185
186 # barely enough for 2 bytes
187 push2 256
188
189 # just enough for 4 bytes
190 push4 4294967295
191 "#;
192 let expected = nodes![
193 Op::from(Push1(0u8.into())),
194 Op::from(Push1(Imm::from([1]))),
195 Op::from(Push2(Imm::from([0, 42]))),
196 Op::from(Push2(Imm::from(hex!("0100")))),
197 Op::from(Push4(Imm::from(hex!("ffffffff")))),
198 ];
199 assert_matches!(parse_asm(asm), Ok(e) if e == expected);
200
201 let asm = "push1 256";
202 assert_matches!(parse_asm(asm), Err(ParseError::ImmediateTooLarge { .. }));
203 }
204
205 #[test]
206 fn parse_push_hex() {
207 let asm = r#"
208 push1 0x01 # comment
209 push1 0x42
210 push2 0x0102
211 push4 0x01020304
212 push8 0x0102030405060708
213 push16 0x0102030405060708090a0b0c0d0e0f10
214 push24 0x0102030405060708090a0b0c0d0e0f101112131415161718
215 push32 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20
216 "#;
217 let expected = nodes![
218 Op::from(Push1(Imm::from(hex!("01")))),
219 Op::from(Push1(Imm::from(hex!("42")))),
220 Op::from(Push2(Imm::from(hex!("0102")))),
221 Op::from(Push4(Imm::from(hex!("01020304")))),
222 Op::from(Push8(Imm::from(hex!("0102030405060708")))),
223 Op::from(Push16(Imm::from(hex!("0102030405060708090a0b0c0d0e0f10")))),
224 Op::from(Push24(Imm::from(hex!(
225 "0102030405060708090a0b0c0d0e0f101112131415161718"
226 )))),
227 Op::from(Push32(Imm::from(hex!(
228 "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"
229 )))),
230 ];
231 assert_matches!(parse_asm(asm), Ok(e) if e == expected);
232
233 let asm = "push2 0x010203";
234 assert_matches!(parse_asm(asm), Err(ParseError::ImmediateTooLarge { .. }));
235 }
236
237 #[test]
238 fn parse_variable_ops() {
239 let asm = r#"
240 swap1
241 swap4
242 swap16
243 dup1
244 dup4
245 dup16
246 log0
247 log4
248 "#;
249 let expected = nodes![
250 Op::from(Swap1),
251 Op::from(Swap4),
252 Op::from(Swap16),
253 Op::from(Dup1),
254 Op::from(Dup4),
255 Op::from(Dup16),
256 Op::from(Log0),
257 Op::from(Log4),
258 ];
259 assert_matches!(parse_asm(asm), Ok(e) if e == expected);
260 }
261
262 #[test]
263 fn parse_jumpdest_no_label() {
264 let asm = "jumpdest";
265 let expected = nodes![Op::from(JumpDest)];
266 assert_matches!(parse_asm(asm), Ok(e) if e == expected);
267 }
268
269 #[test]
270 fn parse_jumpdest_label() {
271 let asm = "start:\njumpdest";
272 let expected = nodes![AbstractOp::Label("start".into()), Op::from(JumpDest),];
273 assert_matches!(parse_asm(asm), Ok(e) if e == expected);
274 }
275
276 #[test]
277 fn parse_push_label() {
278 let asm = r#"
279 push2 snake_case
280 jumpi
281 "#;
282 let expected = nodes![
283 Op::from(Push2(Imm::with_label("snake_case"))),
284 Op::from(JumpI)
285 ];
286 assert_matches!(parse_asm(asm), Ok(e) if e == expected);
287 }
288
289 #[test]
290 fn parse_push_op_as_label() {
291 let asm = r#"
292 push1:
293 push1 push1
294 jumpi
295 "#;
296 let expected = nodes![
297 AbstractOp::Label("push1".into()),
298 Op::from(Push1(Imm::with_label("push1"))),
299 Op::from(JumpI),
300 ];
301 assert_matches!(parse_asm(asm), Ok(e) if e == expected);
302 }
303
304 #[test]
305 fn parse_selector() {
306 let asm = r#"
307 push4 selector("name()")
308 push4 selector("balanceOf(address)")
309 push4 selector("transfer(address,uint256)")
310 push4 selector("approve(address,uint256)")
311 push32 topic("transfer(address,uint256)")
312 "#;
313 let expected = nodes![
314 Op::from(Push4(Imm::from(hex!("06fdde03")))),
315 Op::from(Push4(Imm::from(hex!("70a08231")))),
316 Op::from(Push4(Imm::from(hex!("a9059cbb")))),
317 Op::from(Push4(Imm::from(hex!("095ea7b3")))),
318 Op::from(Push32(Imm::from(hex!(
319 "a9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b"
320 )))),
321 ];
322 println!("{:?}\n\n{:?}", parse_asm(asm), expected);
323 assert_matches!(parse_asm(asm), Ok(e) if e == expected);
324 }
325
326 #[test]
327 fn parse_selector_with_spaces() {
328 let asm = r#"
329 push4 selector("name( )")
330 "#;
331 assert_matches!(parse_asm(asm), Err(ParseError::Lexer { .. }));
332 }
333
334 #[test]
335 fn parse_include() {
336 let asm = format!(
337 r#"
338 push1 1
339 %include("foo.asm")
340 push1 2
341 "#,
342 );
343 let expected = nodes![
344 Op::from(Push1(Imm::from(1u8))),
345 Node::Include(PathBuf::from("foo.asm")),
346 Op::from(Push1(Imm::from(2u8))),
347 ];
348 assert_matches!(parse_asm(&asm), Ok(e) if e == expected)
349 }
350
351 #[test]
352 fn parse_include_hex() {
353 let asm = format!(
354 r#"
355 push1 1
356 %include_hex("foo.hex")
357 push1 2
358 "#,
359 );
360 let expected = nodes![
361 Op::from(Push1(Imm::from(1u8))),
362 Node::IncludeHex(PathBuf::from("foo.hex")),
363 Op::from(Push1(Imm::from(2u8))),
364 ];
365 assert_matches!(parse_asm(&asm), Ok(e) if e == expected)
366 }
367
368 #[test]
369 fn parse_import() {
370 let asm = format!(
371 r#"
372 push1 1
373 %import("foo.asm")
374 push1 2
375 "#,
376 );
377 let expected = nodes![
378 Op::from(Push1(Imm::from(1u8))),
379 Node::Import(PathBuf::from("foo.asm")),
380 Op::from(Push1(Imm::from(2u8))),
381 ];
382 assert_matches!(parse_asm(&asm), Ok(e) if e == expected)
383 }
384
385 #[test]
386 fn parse_import_extra_argument() {
387 let asm = format!(
388 r#"
389 %import("foo.asm", "bar.asm")
390 "#,
391 );
392 assert!(matches!(
393 parse_asm(&asm),
394 Err(ParseError::ExtraArgument {
395 expected: 1,
396 backtrace: _
397 })
398 ))
399 }
400
401 #[test]
402 fn parse_import_missing_argument() {
403 let asm = format!(
404 r#"
405 %import()
406 "#,
407 );
408 assert!(matches!(
409 parse_asm(&asm),
410 Err(ParseError::MissingArgument {
411 got: 0,
412 expected: 1,
413 backtrace: _,
414 })
415 ))
416 }
417
418 #[test]
419 fn parse_import_argument_type() {
420 let asm = format!(
421 r#"
422 %import(0x44)
423 "#,
424 );
425 assert_matches!(parse_asm(&asm), Err(ParseError::ArgumentType { .. }))
426 }
427
428 #[test]
429 fn parse_import_spaces() {
430 let asm = format!(
431 r#"
432 push1 1
433 %import( "hello.asm" )
434 push1 2
435 "#,
436 );
437 let expected = nodes![
438 Op::from(Push1(Imm::from(1u8))),
439 Node::Import(PathBuf::from("hello.asm")),
440 Op::from(Push1(Imm::from(2u8))),
441 ];
442 assert_matches!(parse_asm(&asm), Ok(e) if e == expected)
443 }
444
445 #[test]
446 fn parse_push_macro_with_label() {
447 let asm = format!(
448 r#"
449 push1 1
450 %push( hello )
451 push1 2
452 "#,
453 );
454 let expected = nodes![
455 Op::from(Push1(Imm::from(1u8))),
456 AbstractOp::Push(Imm::with_label("hello")),
457 Op::from(Push1(Imm::from(2u8))),
458 ];
459 assert_matches!(parse_asm(&asm), Ok(e) if e == expected)
460 }
461
462 #[test]
463 fn parse_instruction_macro() {
464 let asm = format!(
465 r#"
466 %macro my_macro(foo, bar)
467 gasprice
468 pop
469 push1 $foo + $bar
470 %push(0x42)
471 %another_macro()
472 %end
473 %my_macro(0x42, 10)
474 "#,
475 );
476 let expected = nodes![
477 AbstractOp::MacroDefinition(
478 InstructionMacroDefinition {
479 name: "my_macro".into(),
480 parameters: vec!["foo".into(), "bar".into()],
481 contents: vec![
482 AbstractOp::new(GasPrice),
483 AbstractOp::new(Pop),
484 AbstractOp::new(Push1(
485 Expression::Plus(
486 Terminal::Variable("foo".to_string()).into(),
487 Terminal::Variable("bar".to_string()).into()
488 )
489 .into()
490 )),
491 AbstractOp::Push(0x42u8.into()),
492 AbstractOp::Macro(InstructionMacroInvocation {
493 name: "another_macro".into(),
494 parameters: vec![]
495 })
496 ]
497 }
498 .into()
499 ),
500 AbstractOp::Macro(InstructionMacroInvocation {
501 name: "my_macro".into(),
502 parameters: vec![
503 BigInt::from_bytes_be(Sign::Plus, &vec![0x42]).into(),
504 BigInt::from_bytes_be(
505 Sign::Plus,
506 &vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10]
507 )
508 .into()
509 ]
510 })
511 ];
512
513 assert_eq!(parse_asm(&asm).unwrap(), expected)
514 }
515
516 #[test]
517 fn parse_expression() {
518 let asm = format!(
519 r#"
520 push1 1+-1
521 push1 2*foo
522 push1 (1+(2*foo))-(bar/42)
523 push1 0x20+0o1+0b10
524 "#,
525 );
526 let expected = nodes![
527 Op::from(Push1(Imm::with_expression(Expression::Plus(
528 1.into(),
529 BigInt::from(-1).into(),
530 )))),
531 Op::from(Push1(Imm::with_expression(Expression::Times(
532 2.into(),
533 Terminal::Label("foo".into()).into()
534 )))),
535 Op::from(Push1(Imm::with_expression(Expression::Minus(
536 Box::new(Expression::Plus(
537 1.into(),
538 Box::new(Expression::Times(
539 2.into(),
540 Terminal::Label("foo".into()).into()
541 ))
542 )),
543 Box::new(Expression::Divide(
544 Terminal::Label("bar".into()).into(),
545 42.into()
546 ))
547 )))),
548 Op::from(Push1(Imm::with_expression(Expression::Plus(
549 Box::new(Expression::Plus(
550 Terminal::Number(0x20.into()).into(),
551 1.into()
552 )),
553 2.into()
554 ))))
555 ];
556 assert_eq!(parse_asm(&asm).unwrap(), expected)
557 }
558
559 #[test]
560 fn parse_push_macro_with_expression() {
561 let asm = format!(
562 r#"
563 push1 1
564 %push( 1 + 1 )
565 push1 2
566 "#,
567 );
568 let expected = nodes![
569 Op::from(Push1(Imm::from(1u8))),
570 AbstractOp::Push(Imm::with_expression(Expression::Plus(1.into(), 1.into()))),
571 Op::from(Push1(Imm::from(2u8))),
572 ];
573 assert_matches!(parse_asm(&asm), Ok(e) if e == expected)
574 }
575
576 #[test]
577 fn parse_expression_macro() {
578 let asm = format!(
579 r#"
580 %def foobar()
581 1+2
582 %end
583 push1 foobar()
584 "#,
585 );
586 let expected = nodes![
587 ExpressionMacroDefinition {
588 name: "foobar".into(),
589 parameters: vec![],
590 content: Imm::with_expression(Expression::Plus(1.into(), 2.into())),
591 },
592 Op::from(Push1(Imm::with_macro(ExpressionMacroInvocation {
593 name: "foobar".into(),
594 parameters: vec![]
595 }))),
596 ];
597 assert_eq!(parse_asm(&asm).unwrap(), expected);
598 }
599}