use std::{collections::HashMap, f64::consts::PI};
use vexity::{
interpreter::{Interpreter, VarValue},
parser::parse,
};
#[test]
fn test_basic_var_defs() {
let script = r#"
let x = 5
let y = 10.53
let z = "Hello 🌎"
"#;
let statements = parse(script).expect("Parsing failed");
let mut interpreter = Interpreter::new();
interpreter.run(statements);
let x = interpreter
.vars
.get("x")
.and_then(|value| match value {
VarValue::Int(i) => Some(*i),
_ => None,
})
.expect("Variable `x` is not in interpreter state");
assert_eq!(x, 5);
let y = interpreter
.vars
.get("y")
.and_then(|value| match value {
VarValue::Float(f) => Some(*f),
_ => None,
})
.expect("Variable `y` is not in interpreter state");
assert_eq!(y, 10.53);
let z = interpreter
.vars
.get("z")
.and_then(|value| match value {
VarValue::String(s) => Some(s),
_ => None,
})
.expect("Variable `z` is not in interpreter state");
assert_eq!(z.as_str(), "Hello 🌎")
}
#[test]
fn test_basic_array_defs() {
let script = r#"
let x = [1, 2, 3, 4, 5]
let y = [1.5, 2.5, 3.5, 4.5, 5.5]
let z = ["Some", "array", "of", "strings", "!"]
"#;
let statements = parse(script).expect("Parsing failed");
let mut interpreter = Interpreter::new();
interpreter.run(statements);
let x = interpreter
.vars
.get("x")
.and_then(|value| match value {
VarValue::Array(a) => Some(
a.iter()
.map(|val| match val {
VarValue::Int(i) => *i,
_ => panic!("Variable `x` should be an array of integers"),
})
.collect::<Vec<i32>>(),
),
_ => None,
})
.expect("Variable `x` is not in interpreter state");
assert_eq!(x.len(), 5);
assert_eq!(x[0], 1);
assert_eq!(x[1], 2);
assert_eq!(x[2], 3);
assert_eq!(x[3], 4);
assert_eq!(x[4], 5);
let y = interpreter
.vars
.get("y")
.and_then(|value| match value {
VarValue::Array(a) => Some(
a.iter()
.map(|val| match val {
VarValue::Float(f) => *f,
_ => panic!("Variable `y` should be an array of floats"),
})
.collect::<Vec<f64>>(),
),
_ => None,
})
.expect("Variable `y` is not in interpreter state");
assert_eq!(y.len(), 5);
assert_eq!(y[0], 1.5);
assert_eq!(y[1], 2.5);
assert_eq!(y[2], 3.5);
assert_eq!(y[3], 4.5);
assert_eq!(y[4], 5.5);
let z = interpreter
.vars
.get("z")
.and_then(|value| match value {
VarValue::Array(a) => Some(
a.iter()
.map(|val| match val {
VarValue::String(s) => s.clone(),
_ => panic!("Variable `z` should be an array of strings"),
})
.collect::<Vec<String>>(),
),
_ => None,
})
.expect("Variable `z` is not in interpreter state");
assert_eq!(z.len(), 5);
assert_eq!(z[0].as_str(), "Some");
assert_eq!(z[1].as_str(), "array");
assert_eq!(z[2].as_str(), "of");
assert_eq!(z[3].as_str(), "strings");
assert_eq!(z[4].as_str(), "!");
}
#[test]
fn test_basic_hashmap_defs() {
let script = r#"
let x = { a: 1, b: 2, c: 3 }
let y = { pi: 3.141592653589793, e: 2.71 }
let z = { hello: "world", foo: "bar" }
"#;
let statements = parse(script).expect("Parsing failed");
let mut interpreter = Interpreter::new();
interpreter.run(statements);
let x = interpreter
.vars
.get("x")
.and_then(|value| match value {
VarValue::HashMap(m) => Some(
m.iter()
.map(|(k, v)| {
let i = match v {
VarValue::Int(i) => *i,
_ => panic!("x[{k}] should be an integer"),
};
(k.clone(), i)
})
.collect::<HashMap<String, i32>>(),
),
_ => None,
})
.expect("Variable `x` is not a hashmap");
assert_eq!(x.get("a"), Some(&1));
assert_eq!(x.get("b"), Some(&2));
assert_eq!(x.get("c"), Some(&3));
let y = interpreter
.vars
.get("y")
.and_then(|value| match value {
VarValue::HashMap(m) => Some(
m.iter()
.map(|(k, v)| {
let f = match v {
VarValue::Float(f) => *f,
_ => panic!("y[{k}] should be a float"),
};
(k.clone(), f)
})
.collect::<HashMap<String, f64>>(),
),
_ => None,
})
.expect("Variable `y` is not a hashmap");
assert!((y.get("pi").unwrap() - PI).abs() < 1e-6);
assert!((y.get("e").unwrap() - 2.71).abs() < 1e-6);
let z = interpreter
.vars
.get("z")
.and_then(|value| match value {
VarValue::HashMap(m) => Some(
m.iter()
.map(|(k, v)| {
let s = match v {
VarValue::String(s) => s.clone(),
_ => panic!("z[{k}] should be a string"),
};
(k.clone(), s)
})
.collect::<HashMap<String, String>>(),
),
_ => None,
})
.expect("Variable `z` is not a hashmap");
assert_eq!(z.get("hello").map(String::as_str), Some("world"));
assert_eq!(z.get("foo").map(String::as_str), Some("bar"));
}
#[test]
fn test_basic_var_bin_ops() {
let script = r#"
let a = 100
let b = 1
let a_minus_b = a - b
let j = 50
let k = 2
let j_divided_by_k = j / k
let j_multiplied_by_k = j * k
let x = 5
let y = 5
let x_plus_y = x + y
"#;
let statements = parse(script).expect("Parsing failed");
let mut interpreter = Interpreter::new();
interpreter.run(statements);
let a_minus_b = interpreter
.vars
.get("a_minus_b")
.and_then(|value| match value {
VarValue::Int(i) => Some(*i),
_ => None,
})
.expect("Variable `a_minues_b` not in interpreter state");
assert_eq!(a_minus_b, 99);
let x_plus_y = interpreter
.vars
.get("x_plus_y")
.and_then(|value| match value {
VarValue::Int(i) => Some(*i),
_ => None,
})
.expect("Variable `x_plus_y` not in interpreter state");
assert_eq!(x_plus_y, 10);
let j_over_k = interpreter
.vars
.get("j_divided_by_k")
.and_then(|value| match value {
VarValue::Int(i) => Some(*i),
_ => None,
})
.expect("Variable `j_divided_by_k` not in interpreter state");
assert_eq!(j_over_k, 25);
let jk = interpreter
.vars
.get("j_multiplied_by_k")
.and_then(|value| match value {
VarValue::Int(i) => Some(*i),
_ => None,
})
.expect("Variable `j_multiplied_by_k` not in interpreter state");
assert_eq!(jk, 100)
}
#[test]
fn test_basic_function_def() {
let script = r#"
fn add_one(x) {
x + 1
}
let x = 5
let x_plus_one = add_one(x)
"#;
let statements = parse(script).expect("Parsing failed");
let mut interpreter = Interpreter::new();
interpreter.run(statements);
let x_plus_one = interpreter
.vars
.get("x_plus_one")
.and_then(|value| match value {
VarValue::Int(i) => Some(*i),
_ => None,
})
.expect("Variable `x_plus_one` is not in interpreter state");
assert_eq!(x_plus_one, 6);
}
#[test]
fn test_basic_map_def() {
let script = r#"
let values = [1, 2, 3, 4, 5]
let values_squared = values.map(|x| x * x)
"#;
let statements = parse(script).expect("Parsing failed");
let mut interpreter = Interpreter::new();
interpreter.run(statements);
let values_squared = interpreter
.vars
.get("values_squared")
.and_then(|value| match value {
VarValue::Array(a) => Some(
a.iter()
.map(|value| match value {
VarValue::Int(i) => *i,
_ => panic!("Variable `x_squared` should be an array of integers"),
})
.collect::<Vec<i32>>(),
),
_ => None,
})
.expect("Variable `x_squared` is not in interpreter state");
assert_eq!(values_squared.len(), 5);
assert_eq!(values_squared[0], 1);
assert_eq!(values_squared[1], 4);
assert_eq!(values_squared[2], 9);
assert_eq!(values_squared[3], 16);
assert_eq!(values_squared[4], 25);
}
#[test]
fn test_array_length() {
let script = r#"
let a = [10, 20, 30]
let n = a.length
"#;
let statements = parse(script).expect("Parsing failed");
let mut interpreter = Interpreter::new();
interpreter.run(statements);
let len = interpreter
.vars
.get("n")
.and_then(|value| match value {
VarValue::Int(i) => Some(*i),
_ => None,
})
.expect("Variable `n` is not in interpreter state");
assert_eq!(len, 3);
}
#[test]
fn test_basic_range_def() {
let script = r#"
let squares = (0..5).map(|i| i * i)
"#;
let statements = parse(script).expect("Parsing failed");
let mut interpreter = Interpreter::new();
interpreter.run(statements);
let squares = interpreter
.vars
.get("squares")
.and_then(|value| match value {
VarValue::Array(arr) => Some(
arr.iter()
.map(|v| match v {
VarValue::Int(i) => *i,
_ => panic!("Expected array of ints in `squares`"),
})
.collect::<Vec<i32>>(),
),
_ => None,
})
.expect("Variable `squares` is not in interpreter state");
assert_eq!(squares, vec![0, 1, 4, 9, 16]);
}