Skip to content

Commit

Permalink
feat: negative to unsigned int conversion (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
bsh98 committed Feb 12, 2023
1 parent 79d3ad9 commit 35a592c
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 5 deletions.
26 changes: 22 additions & 4 deletions etk-asm/src/ops/expression.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -113,6 +113,9 @@ pub enum Expression {

/// A division operation.
Divide(Box<Self>, Box<Self>),

/// A signed to unsigned integer conversion operation.
Uint(Box<Self>, usize),
}

impl Debug for Expression {
Expand All @@ -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)
}
}
}
}
Expand All @@ -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),
}
}
}
Expand Down Expand Up @@ -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)
Expand All @@ -232,7 +250,7 @@ impl Expression {
pub fn labels(&self, macros: &MacrosMap) -> Result<Vec<String>, Error> {
fn dfs(x: &Expression, m: &MacrosMap) -> Result<Vec<String>, Error> {
match x {
Expression::Expression(e) => dfs(e, m),
Expression::Expression(e) | Expression::Uint(e, _) => dfs(e, m),
Expression::Macro(macro_invocation) => m
.get(&macro_invocation.name)
.context(UnknownMacro {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion etk-asm/src/parse/asm.pest
Original file line number Diff line number Diff line change
Expand Up @@ -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)* ~ ")" }
Expand All @@ -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 = { "+" }
Expand Down
7 changes: 7 additions & 0 deletions etk-asm/src/parse/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ pub(crate) fn parse(pair: Pair<Rule>) -> Result<Expression, ParseError> {
.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(),
Expand Down
56 changes: 56 additions & 0 deletions etk-asm/src/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
14 changes: 14 additions & 0 deletions etk-asm/tests/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
5 changes: 5 additions & 0 deletions etk-asm/tests/asm/expression-macro/uint.etk
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
%def foobar()
0
%end
push1 uint8(-1)
push32 uint256(foobar()-1)

0 comments on commit 35a592c

Please sign in to comment.