diff --git a/etk-asm/src/ops/expression.rs b/etk-asm/src/ops/expression.rs index b5508606..9b569451 100644 --- a/etk-asm/src/ops/expression.rs +++ b/etk-asm/src/ops/expression.rs @@ -1,5 +1,5 @@ use super::macros::{ExpressionMacroInvocation, MacroDefinition}; -use num_bigint::BigInt; +use num_bigint::{BigInt, Sign}; use snafu::OptionExt; use snafu::{Backtrace, Snafu}; use std::collections::HashMap; @@ -113,6 +113,9 @@ pub enum Expression { /// A division operation. Divide(Box, Box), + + /// A signed to unsigned integer conversion operation. + Uint(Box, usize), } impl Debug for Expression { @@ -127,6 +130,9 @@ impl Debug for Expression { Expression::Divide(lhs, rhs) => { write!(f, r#"Expression::Divide({:?}, {:?})"#, lhs, rhs) } + Expression::Uint(s, bits) => { + write!(f, r#"Expression::Uint{}({:?})"#, bits, s) + } } } } @@ -141,6 +147,7 @@ impl fmt::Display for Expression { Expression::Minus(lhs, rhs) => write!(f, r#"{}-{}"#, lhs, rhs), Expression::Times(lhs, rhs) => write!(f, r#"{}*{}"#, lhs, rhs), Expression::Divide(lhs, rhs) => write!(f, r#"{}/{}"#, lhs, rhs), + Expression::Uint(s, bits) => write!(f, r#"uint{}({})"#, bits, s), } } } @@ -219,6 +226,17 @@ impl Expression { Expression::Minus(lhs, rhs) => eval(lhs, ctx)? - eval(rhs, ctx)?, Expression::Times(lhs, rhs) => eval(lhs, ctx)? * eval(rhs, ctx)?, Expression::Divide(lhs, rhs) => eval(lhs, ctx)? / eval(rhs, ctx)?, + Expression::Uint(expr, bits) => { + let eval = eval(expr, ctx)?; + match eval.sign() { + Sign::Minus => { + let mut bytes = eval.to_signed_bytes_be(); + bytes.resize(bits / 8, 0xff); + BigInt::from_bytes_le(Sign::Plus, &bytes) + } + _ => eval, + } + } }; Ok(ret) @@ -232,7 +250,7 @@ impl Expression { pub fn labels(&self, macros: &MacrosMap) -> Result, Error> { fn dfs(x: &Expression, m: &MacrosMap) -> Result, Error> { match x { - Expression::Expression(e) => dfs(e, m), + Expression::Expression(e) | Expression::Uint(e, _) => dfs(e, m), Expression::Macro(macro_invocation) => m .get(¯o_invocation.name) .context(UnknownMacro { @@ -261,7 +279,7 @@ impl Expression { pub fn replace_label(&mut self, old: &str, new: &str) { fn dfs(x: &mut Expression, old: &str, new: &str) { match x { - Expression::Expression(e) => dfs(e, new, old), + Expression::Expression(e) | Expression::Uint(e, _) => dfs(e, new, old), Expression::Terminal(Terminal::Label(ref mut label)) => { if *label == old { *label = new.to_string(); @@ -290,7 +308,7 @@ impl Expression { *x = expr.clone(); } } - Expression::Expression(e) => dfs(e, var, expr), + Expression::Expression(e) | Expression::Uint(e, _) => dfs(e, var, expr), Expression::Plus(lhs, rhs) | Expression::Minus(lhs, rhs) | Expression::Times(lhs, rhs) diff --git a/etk-asm/src/parse/asm.pest b/etk-asm/src/parse/asm.pest index a8982aa7..10fb4fbe 100644 --- a/etk-asm/src/parse/asm.pest +++ b/etk-asm/src/parse/asm.pest @@ -60,6 +60,7 @@ string_char = _{ "\\\\" | "\\\"" | (!"\\" ~ !"\"" ~ ANY) } expression_macro_definition = !{ "%def" ~ function_declaration ~ NEWLINE ~ expression ~ NEWLINE ~ "%end" } expression_macro = { function_invocation } +uint = ${ "uint" ~ ASCII_NONZERO_DIGIT+ ~ expression } selector = ${ "selector(\"" ~ selector_function_declaration ~ "\")" } topic = ${ "topic(\"" ~ selector_function_declaration ~ "\")" } selector_function_declaration = @{ function_name ~ "(" ~ function_parameter* ~ ("," ~ function_parameter)* ~ ")" } @@ -85,7 +86,7 @@ label_definition = { label ~ ":" } // infix math // //////////////// expression = !{ term ~ (operation ~ term)* } -term = _{ instruction_macro_variable | selector | topic | expression_macro | label | number | negative_decimal | "(" ~ expression ~ ")" } +term = _{ instruction_macro_variable | uint | selector | topic | expression_macro | label | number | negative_decimal | "(" ~ expression ~ ")" } negative_decimal = @{ "-" ~ ASCII_DIGIT+ } operation = _{ plus | minus | times | divide } plus = { "+" } diff --git a/etk-asm/src/parse/expression.rs b/etk-asm/src/parse/expression.rs index 10adc6a2..dd8c2941 100644 --- a/etk-asm/src/parse/expression.rs +++ b/etk-asm/src/parse/expression.rs @@ -40,6 +40,13 @@ pub(crate) fn parse(pair: Pair) -> Result { .into() } Rule::label => Terminal::Label(txt.to_string()).into(), + Rule::uint => { + let num_bits: usize = txt[4..txt.find("(").unwrap()].parse().unwrap(); + Expression::Uint( + Box::new(climber.climb(pair.into_inner(), primary, infix)), + num_bits, + ) + } Rule::selector => parse_selector(pair, 4), Rule::topic => parse_selector(pair, 32), Rule::expression_macro => macros::parse_expression_macro(pair).unwrap(), diff --git a/etk-asm/src/parse/mod.rs b/etk-asm/src/parse/mod.rs index af6e9ef5..515c73d6 100644 --- a/etk-asm/src/parse/mod.rs +++ b/etk-asm/src/parse/mod.rs @@ -594,4 +594,60 @@ mod tests { ]; assert_eq!(parse_asm(&asm).unwrap(), expected); } + + #[test] + fn parse_uint() { + let asm = r#" + push1 uint8(-1) + push1 uint8(-2) + push32 uint8(-1) + push32 uint256(-1) + "#; + let expected = nodes![ + Op::from(Push1(Imm::with_expression(Expression::Uint( + Box::new(Expression::Terminal(Terminal::Number(BigInt::from(-1)))), + 8 + )))), + Op::from(Push1(Imm::with_expression(Expression::Uint( + Box::new(Expression::Terminal(Terminal::Number(BigInt::from(-2)))), + 8 + )))), + Op::from(Push32(Imm::with_expression(Expression::Uint( + Box::new(Expression::Terminal(Terminal::Number(BigInt::from(-1)))), + 8 + )))), + Op::from(Push32(Imm::with_expression(Expression::Uint( + Box::new(Expression::Terminal(Terminal::Number(BigInt::from(-1)))), + 256 + )))), + ]; + assert_matches!(parse_asm(asm), Ok(e) if e == expected); + } + + #[test] + fn parse_uint_nested() { + let asm = r#" + %def foobar() + -1 + %end + push1 uint8(foobar()) + "#; + let expected = nodes![ + ExpressionMacroDefinition { + name: "foobar".into(), + parameters: vec![], + content: Imm::with_expression(Expression::Terminal(Terminal::Number( + BigInt::from(-1) + ))), + }, + Op::from(Push1(Imm::with_expression(Expression::Uint( + Box::new(Expression::Macro(ExpressionMacroInvocation { + name: "foobar".into(), + parameters: vec![] + })), + 8 + )))), + ]; + assert_matches!(parse_asm(asm), Ok(e) if e == expected); + } } diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index 1dac9e68..f3e62d72 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -108,6 +108,20 @@ fn instruction_macro_with_two_instructions_per_line() { assert_matches!(err, Error::Parse { .. }); } +#[test] +fn expression_macro_uint() -> Result<(), Error> { + let mut output = Vec::new(); + let mut ingester = Ingest::new(&mut output); + ingester.ingest_file(source(&["expression-macro", "uint.etk"]))?; + + assert_eq!( + output, + hex!("60ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + ); + + Ok(()) +} + #[test] fn every_op() -> Result<(), Error> { let mut output = Vec::new(); diff --git a/etk-asm/tests/asm/expression-macro/uint.etk b/etk-asm/tests/asm/expression-macro/uint.etk new file mode 100644 index 00000000..9f69ce71 --- /dev/null +++ b/etk-asm/tests/asm/expression-macro/uint.etk @@ -0,0 +1,5 @@ +%def foobar() + 0 +%end +push1 uint8(-1) +push32 uint256(foobar()-1)