Skip to content

Commit

Permalink
Add some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
danhper committed Jul 12, 2024
1 parent 0a8d566 commit 1d55fe5
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 4 deletions.
39 changes: 35 additions & 4 deletions src/interpreter/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,30 +274,46 @@ pub fn evaluate_expression(env: &mut Env, expr: Box<Expression>) -> BoxFuture<'_
Expression::PreIncrement(_, expr) => {
let current_value = evaluate_expression(env, expr.clone()).await?;
let lhs = Lhs::try_from_expr(expr.as_ref().clone(), env).await?;
let new_value = (current_value + 1.into())?;
let new_value = (current_value + 1u64.into())?;
lhs.execute_assign(new_value.clone(), env)?;
Ok(new_value)
}
Expression::PreDecrement(_, expr) => {
let current_value = evaluate_expression(env, expr.clone()).await?;
let lhs = Lhs::try_from_expr(expr.as_ref().clone(), env).await?;
let new_value = (current_value - 1.into())?;
let new_value = (current_value - 1u64.into())?;
lhs.execute_assign(new_value.clone(), env)?;
Ok(new_value)
}
Expression::PostIncrement(_, expr) => {
let current_value = evaluate_expression(env, expr.clone()).await?;
let lhs = Lhs::try_from_expr(expr.as_ref().clone(), env).await?;
lhs.execute_assign((current_value.clone() + 1.into())?, env)?;
lhs.execute_assign((current_value.clone() + 1u64.into())?, env)?;
Ok(current_value)
}
Expression::PostDecrement(_, expr) => {
let current_value = evaluate_expression(env, expr.clone()).await?;
let lhs = Lhs::try_from_expr(expr.as_ref().clone(), env).await?;
lhs.execute_assign((current_value.clone() - 1.into())?, env)?;
lhs.execute_assign((current_value.clone() - 1u64.into())?, env)?;
Ok(current_value)
}

Expression::AssignAdd(_, left, right) => {
_eval_binop_assign(env, left, right, |a, b| a + b).await
}
Expression::AssignSubtract(_, left, right) => {
_eval_binop_assign(env, left, right, |a, b| a - b).await
}
Expression::AssignMultiply(_, left, right) => {
_eval_binop_assign(env, left, right, |a, b| a * b).await
}
Expression::AssignDivide(_, left, right) => {
_eval_binop_assign(env, left, right, |a, b| a / b).await
}
Expression::AssignModulo(_, left, right) => {
_eval_binop_assign(env, left, right, |a, b| a % b).await
}

Expression::HexNumberLiteral(_, n, _) => {
let result = if n.len() == 42 {
Value::Addr(Address::from_hex(n)?)
Expand Down Expand Up @@ -610,3 +626,18 @@ where
let rhs = evaluate_expression(env, rexpr).await?;
f(lhs, rhs)
}

async fn _eval_binop_assign<F>(
env: &mut Env,
lexpr: Box<Expression>,
rexpr: Box<Expression>,
f: F,
) -> Result<Value>
where
F: FnOnce(Value, Value) -> Result<Value>,
{
let lhs = Lhs::try_from_expr(lexpr.as_ref().clone(), env).await?;
let new_value = _eval_binop(env, lexpr, rexpr, f).await?;
lhs.execute_assign(new_value.clone(), env)?;
Ok(new_value)
}
7 changes: 7 additions & 0 deletions src/interpreter/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,12 @@ impl From<u128> for Value {
}
}

impl From<&str> for Value {
fn from(s: &str) -> Self {
Value::Str(s.to_string())
}
}

impl<const N: usize> From<alloy::primitives::FixedBytes<N>> for Value {
fn from(bytes: alloy::primitives::FixedBytes<N>) -> Self {
Value::FixBytes(B256::from_slice(&bytes[..]), N)
Expand Down Expand Up @@ -268,6 +274,7 @@ impl Value {
}
}

#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> Result<usize> {
let len = match self {
Value::Array(items) => items.len(),
Expand Down
121 changes: 121 additions & 0 deletions tests/interpreter_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use eclair::interpreter::{self, Config, Env, Type, Value};

#[tokio::test]
async fn test_binops() {
let mut env = _create_env();

_check_result(&mut env, "1 + 8", Value::from(9u64)).await;
_check_result(&mut env, "int256(1) - 8", Value::from(-7)).await;
_check_result(&mut env, "3 + 8 * 4", Value::from(35u64)).await;
_check_result(&mut env, "(10 + 4) % 3", Value::from(2u64)).await;
}

#[tokio::test]
async fn test_string() {
let mut env = _create_env();

_check_result(&mut env, "\"foo\"", Value::from("foo")).await;
_check_result(&mut env, "\"foo\".length", Value::from(3u64)).await;
_check_result(&mut env, "\"foo\".concat(\"bar\")", Value::from("foobar")).await;
}

#[tokio::test]
async fn test_builtin_type() {
let mut env = _create_env();

_check_result(&mut env, "type(1)", Value::TypeObject(Type::Uint(256))).await;
_check_result(
&mut env,
"type(uint256)",
Value::TypeObject(Type::Type(Box::new(Type::Uint(256)))),
)
.await;
}

#[tokio::test]
async fn test_builtin_format() {
let mut env = _create_env();

_check_result(&mut env, "2e18.format()", Value::from("2.00")).await;
_check_result(&mut env, "3.5678e7.format(6)", Value::from("35.68")).await;
_check_result(&mut env, "\"foo\".format()", Value::from("foo")).await;
}

#[tokio::test]
async fn test_defined_functions() {
let mut env = _create_env();

_execute(&mut env, "function add(a, b) { return a + b; }").await;
_check_result(&mut env, "add(1, 2)", Value::from(3u64)).await;

_execute(
&mut env,
"function cond(pred, yes, no) { if (pred) return yes; else return no; }",
)
.await;
_check_result(&mut env, "cond(true, 1, 2)", Value::from(1u64)).await;
_check_result(&mut env, "cond(false, 1, 2)", Value::from(2u64)).await;
}

#[tokio::test]
async fn test_for_loop() {
let mut env = _create_env();

let res = _execute(
&mut env,
r#"
a = 1;
for (i = 1; i <= 5; i++) {
a *= i;
}
a;
"#,
)
.await;
assert_eq!(res, Some(Value::from(120u64)));

let res = _execute(
&mut env,
r#"
a = 1;
for (i = 1; i <= 5; i++) {
if (a > 10) break;
a *= i;
}
a
"#,
)
.await;
assert_eq!(res, Some(Value::from(24u64)));

let res = _execute(
&mut env,
r#"
a = 1;
for (i = 1; i <= 5; i++) {
if (i % 2 == 0) continue;
a *= i;
}
a
"#,
)
.await;
assert_eq!(res, Some(Value::from(15u64)));
}

async fn _execute(env: &mut Env, code: &str) -> Option<Value> {
interpreter::evaluate_code(env, code).await.unwrap()
}

async fn _check_result(env: &mut Env, code: &str, expected: Value) {
let res = _execute(env, code).await;
assert_eq!(res, Some(expected));
}

fn _create_env() -> Env {
let foundry_conf = foundry_config::load_config();
let config = Config::new(None, false, foundry_conf);
let mut env = Env::new(config);
interpreter::load_builtins(&mut env);
env
}

0 comments on commit 1d55fe5

Please sign in to comment.