test(new-tests): new tests for values compute

main
flavien 2023-12-16 23:01:19 +01:00
parent 6aec24670d
commit 7ae18228a3
No known key found for this signature in database
10 changed files with 666 additions and 105 deletions

View File

@ -60,6 +60,7 @@ var: z => val: 8
- print statement OK
- string / char => indexing OK / escaping OK
- local scope for { statements } ?
- pow op ?
- array / map => iterators
- read / write
- types declaration

View File

@ -89,34 +89,56 @@ impl BinaryCompute for BinaryOp {
Ok(Value::String(Rc::new(copy)))
}
(Value::Char(l), Value::Char(r)) => Ok(Value::Char(l + r)),
(Value::Char(c), Value::Number(n)) | (Value::Number(n), Value::Char(c)) => {
match c.checked_add(n as u8) {
Some(c) => Ok(Value::Char(c)),
None => Err(format!("Char overflow: '{}' + {}", char::from(c), n)),
}
}
(Value::Char(l), Value::Number(r)) => match l.checked_add(r as u8) {
Some(n) => Ok(Value::Char(n)),
None => Err(format!("Char overflow: '{}' + {}", char::from(l), r)),
},
(Value::Number(l), Value::Char(r)) => match l.checked_add(r as i64) {
Some(n) => Ok(Value::Number(n)),
None => Err(format!("Integer overflow: {} + '{}'", l, char::from(r))),
},
(Value::String(l), Value::String(r)) => {
let new = l.iter().chain(r.iter()).copied().collect();
Ok(Value::String(Rc::new(new)))
}
(l, r) => Err(format!("Cannot add {} to {}", l, r)),
},
// TODO check overflow
BinaryOp::Subtraction => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Number(l - r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Char(l - r)),
(Value::Char(l), Value::Number(r)) => Ok(Value::Char(l - r as u8)),
(Value::Number(l), Value::Char(r)) => Ok(Value::Char(l as u8 - r)),
(l, r) => Err(format!("Cannot subtract {} from {}", l, r)),
(Value::Number(l), Value::Number(r)) => match l.checked_sub(r) {
Some(n) => Ok(Value::Number(n)),
None => Err(format!("Integer overflow: {} - {}", l, r)),
},
(Value::Char(l), Value::Char(r)) => match l.checked_sub(r) {
Some(n) => Ok(Value::Char(n)),
None => Err(format!(
"Char overflow: '{}' - '{}'",
char::from(l),
char::from(r)
)),
},
(Value::Char(l), Value::Number(r)) => match l.checked_sub(r as u8) {
Some(n) => Ok(Value::Char(n)),
None => Err(format!("Char overflow: '{}' - {}", char::from(l), r)),
},
(Value::Number(l), Value::Char(r)) => match l.checked_sub(r as i64) {
Some(n) => Ok(Value::Number(n)),
None => Err(format!("Integer overflow: {} - '{}'", l, char::from(r))),
},
(l, r) => Err(format!("Cannot subtract {} from {}", r, l)),
},
BinaryOp::Multiplication => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Number(l * r)),
(Value::Char(l), Value::Number(r)) => Ok(Value::String(Rc::new(
(0..r).map(|_| l).collect::<Vec<u8>>(),
))),
(Value::Number(l), Value::Char(r)) => Ok(Value::String(Rc::new(
(0..l).map(|_| r).collect::<Vec<u8>>(),
))),
(Value::Number(l), Value::Number(r)) => match l.checked_mul(r) {
Some(n) => Ok(Value::Number(n)),
None => Err(format!("Integer overflow: {} * {}", l, r)),
},
(Value::Char(c), Value::Number(n)) | (Value::Number(n), Value::Char(c)) => Ok(
Value::String(Rc::new((0..n).map(|_| c).collect::<Vec<u8>>())),
),
(Value::String(s), Value::Number(n)) | (Value::Number(n), Value::String(s)) => {
Ok(Value::String(Rc::new(
(0..n).flat_map(|_| s.iter().copied()).collect::<Vec<u8>>(),
)))
}
(l, r) => Err(format!("Cannot multiply {} by {}", l, r)),
},
BinaryOp::Division => match (left.evaluate(env)?, right.evaluate(env)?) {
@ -137,7 +159,10 @@ impl BinaryCompute for BinaryOp {
Ok(Value::Number(l % r))
}
}
(l, r) => Err(format!("Cannot modulo {} by {}", l, r)),
(l, r) => Err(format!(
"Cannot calculate the remainder with {} divisor of {}",
l, r
)),
},
BinaryOp::Equal => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l == r)),

View File

@ -1,16 +1,13 @@
use std::{
fmt::{Display, Formatter, Result},
rc::Rc,
};
use std::rc::Rc;
use crate::parser::syntax::Value;
use std::collections::HashMap;
pub trait Environment {
fn add(&mut self, name: &str, node: Value);
fn create(&mut self, name: &str, node: Value) -> bool;
fn assign(&mut self, name: &str, node: Value) -> Option<Value>;
fn get(&self, name: &str) -> Option<Value>;
fn share_global(&mut self) -> &mut HashMap<String, Value>;
}
#[derive(Default, Debug)]
@ -18,68 +15,84 @@ pub struct GlobalScope {
pub global: HashMap<String, Value>,
}
impl Display for GlobalScope {
fn fmt(&self, f: &mut Formatter) -> Result {
writeln!(f, "global: ")?;
let global_vars: Vec<String> = self
.global
.iter()
.map(|(key, val)| format!("var: {0} => val: {1}", key, val))
.collect();
writeln!(f, "{}", global_vars.join("\n"))?;
Ok(())
}
}
// TODO create a print function for scope that print recursively all the scopes
// impl Display for GlobalScope {
// fn fmt(&self, f: &mut Formatter) -> Result {
// writeln!(f, "global: ")?;
// let global_vars: Vec<String> = self
// .global
// .iter()
// .map(|(key, val)| format!("var: {0} => val: {1}", key, val))
// .collect();
// writeln!(f, "{}", global_vars.join("\n"))?;
// Ok(())
// }
// }
impl Environment for GlobalScope {
fn add(&mut self, name: &str, node: Value) {
self.global.insert(name.to_string(), node);
fn create(&mut self, name: &str, node: Value) -> bool {
match self.global.contains_key(name) {
true => false,
false => {
self.global.insert(name.to_string(), node);
true
}
}
}
fn assign(&mut self, name: &str, node: Value) -> Option<Value> {
match self.global.contains_key(name) {
true => self.global.insert(name.to_string(), node),
false => unreachable!("variable {} not found on assign, should never happen because it is checked before computing the value", name),
}
}
fn get(&self, name: &str) -> Option<Value> {
self.global.get(name).cloned()
}
fn share_global(&mut self) -> &mut HashMap<String, Value> {
&mut self.global
}
}
pub struct LocalScope<'a> {
pub global: &'a mut HashMap<String, Value>,
pub struct FunctionScope<'a> {
pub parent: &'a mut dyn Environment,
pub vars: HashMap<String, Value>,
}
impl LocalScope<'_> {
impl FunctionScope<'_> {
pub fn new<'a>(
env: &'a mut (dyn Environment + 'a),
arg_names: &Rc<Vec<String>>,
args: &[Value],
) -> LocalScope<'a> {
) -> FunctionScope<'a> {
let mut vars = HashMap::new();
for (arg_name, arg) in arg_names.iter().zip(args.iter()) {
vars.insert(arg_name.clone(), arg.clone());
}
LocalScope {
global: env.share_global(),
vars,
}
FunctionScope { parent: env, vars }
}
}
impl<'a> Environment for LocalScope<'a> {
fn add(&mut self, name: &str, node: Value) {
self.vars.insert(name.to_string(), node);
impl<'a> Environment for FunctionScope<'a> {
fn create(&mut self, name: &str, node: Value) -> bool {
match self.vars.contains_key(name) {
true => false,
false => {
self.vars.insert(name.to_string(), node);
true
}
}
}
fn assign(&mut self, name: &str, node: Value) -> Option<Value> {
match self.vars.contains_key(name) {
true => self.vars.insert(name.to_string(), node),
false => self.parent.assign(name, node),
}
}
fn get(&self, name: &str) -> Option<Value> {
match self.vars.get(name) {
Some(value) => Some(value.clone()),
None => self.global.get(name).cloned(),
None => self.parent.get(name),
}
}
fn share_global(&mut self) -> &mut HashMap<String, Value> {
self.global
}
}

View File

@ -1,6 +1,6 @@
use super::{
compute::{BinaryCompute, UnaryCompute, ValueCompute},
environment::{Environment, LocalScope},
environment::{Environment, FunctionScope},
machine::{ControlFlow, Execution},
};
use crate::parser::syntax::{Expr, Value};
@ -53,7 +53,7 @@ impl Evaluate for Expr {
args.len()
));
}
let mut fn_env = LocalScope::new(env, arg_names, &args);
let mut fn_env = FunctionScope::new(env, arg_names, &args);
if let ControlFlow::Return(value) = body.execute(&mut fn_env)? {
Ok(value)
} else {

View File

@ -21,6 +21,7 @@ pub trait Execution {
impl Execution for Vec<Stat> {
fn execute(&self, env: &mut dyn Environment) -> Result<ControlFlow, String> {
// TODO test create a new scope for each block / with tests
for stat in self {
match stat.execute(env)? {
ControlFlow::Next => (),
@ -67,36 +68,34 @@ impl Execution for Stat {
}
let mut copy = (*string).clone();
copy[i as usize] = expr.evaluate(env)?.char()?;
env.add(name, Value::String(Rc::new(copy)));
env.assign(name, Value::String(Rc::new(copy)));
} else {
if env.get(name).is_none() {
return Err(format!("Variable {} not found", name));
}
let value = expr.evaluate(env)?;
env.add(name, value);
env.assign(name, value);
}
Ok(ControlFlow::Next)
}
Stat::Break => Ok(ControlFlow::Break),
Stat::Continue => Ok(ControlFlow::Continue),
Stat::FunDefine(ref name, ref args, ref body) => {
if env.get(name).is_some() {
return Err(format!("Function {} already defined", name));
match env.create(name, Value::Function(args.clone(), body.clone())) {
true => Ok(ControlFlow::Next),
false => Err(format!("Function {} already defined", name)),
}
env.add(name, Value::Function(args.clone(), body.clone()));
Ok(ControlFlow::Next)
}
Stat::VarDefine(ref name, ref expr) => {
if env.get(name).is_some() {
return Err(format!("Variable {} already defined", name));
}
if let Some(expr) = expr {
let value = expr.evaluate(env)?;
env.add(name, value);
let value = if let Some(expr) = expr {
expr.evaluate(env)?
} else {
env.add(name, Value::None);
Value::None
};
match env.create(name, value) {
true => Ok(ControlFlow::Next),
false => Err(format!("Variable {} already defined", name)),
}
Ok(ControlFlow::Next)
}
Stat::Return(ref expr) => Ok(ControlFlow::Return(expr.evaluate(env)?)),
Stat::Print(ref expr) => {

View File

@ -128,6 +128,26 @@ mod tests {
assert_eq!(environment.unwrap_err(), "Char overflow: 'z' + 200")
}
#[test]
fn test_arithmetic_add_nb_char() {
let environment = run_string("var x = 3 + 'a';").unwrap();
let x = match environment.get("x") {
Some(Value::Number(value)) => value,
_ => panic!("x should be Value::Number"),
};
assert_eq!(x, 3 + b'a' as i64);
}
#[test]
fn test_arithmetic_add_nb_char_overflow() {
let environment = run_string("var x = 9223372036854775807 + 'z';");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Integer overflow: 9223372036854775807 + 'z'"
)
}
#[test]
fn test_arithmetic_add_char_char() {
let environment = run_string("var x = 'a' + 'b';").unwrap();
@ -206,33 +226,268 @@ mod tests {
assert_eq!(x, -1);
}
// TODO continue arithmetic sub tests
#[test]
fn test_arithmetic_sub_nb_nb_overflow() {
let environment = run_string("var x = -4611686018427387904 - 4611686018427387905;");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Integer overflow: -4611686018427387904 - 4611686018427387905"
)
}
// #[test]
// fn test_arithmetic_sub_nb_char() {
// let environment = run_string("var x = 108 - 'a';").unwrap();
// let x = match environment.get("x") {
// Some(Value::Char(value)) => value,
// _ => panic!("x should be Value::Number"),
// };
// assert_eq!(x, 108 - b'a');
// }
#[test]
fn test_arithmetic_sub_char_nb() {
let environment = run_string("var x = 'a' - 3;").unwrap();
let x = match environment.get("x") {
Some(Value::Char(value)) => value,
_ => panic!("x should be Value::Number"),
};
assert_eq!(x, b'a' - 3);
}
// #[test]
// fn test_arithmetic_sub_char_char() {
// let environment = run_string("var x = 'a' - 'b';").unwrap();
// let x = match environment.get("x") {
// Some(Value::Char(value)) => value,
// _ => panic!("x should be Value::Number"),
// };
// assert_eq!(x, b'a' - b'b');
// }
#[test]
fn test_arithmetic_sub_char_nb_overflow() {
let environment = run_string("var x = 'a' - 200;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Char overflow: 'a' - 200")
}
#[test]
fn test_arithmetic_sub_nb_char() {
let environment = run_string("var x = 3 - 'a';").unwrap();
let x = match environment.get("x") {
Some(Value::Number(value)) => value,
_ => panic!("x should be Value::Number"),
};
assert_eq!(x, 3 - b'a' as i64);
}
#[test]
fn test_arithmetic_sub_nb_char_overflow() {
let environment = run_string("var x = -9223372036854775807 - 'z';");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Integer overflow: -9223372036854775807 - 'z'"
)
}
#[test]
fn test_arithmetic_sub_char_char() {
let environment = run_string("var x = 'b' - 'a';").unwrap();
let x = match environment.get("x") {
Some(Value::Char(value)) => value,
_ => panic!("x should be Value::Number"),
};
assert_eq!(x, b'b' - b'a');
}
#[test]
fn test_arithmetic_sub_char_char_overflow() {
let environment = run_string("var x = 'a' - 'z';");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Char overflow: 'a' - 'z'")
}
#[test]
fn test_arithmetic_sub_str_str() {
let environment = run_string("var x = \"hello\" - \"world\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot subtract hello from world")
assert_eq!(environment.unwrap_err(), "Cannot subtract world from hello")
}
#[test]
fn test_arithmetic_sub_str_char() {
let environment = run_string("var x = \"hello\" - 'a';");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot subtract 'a' from hello")
}
#[test]
fn test_arithmetic_sub_nb_str() {
let environment = run_string("var x = 3 - \"hello\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot subtract hello from 3")
}
#[test]
fn test_arithmetic_sub_bool_str() {
let environment = run_string("var x = true - \"hello\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot subtract hello from true")
}
#[test]
fn test_arithmetic_sub_nb_bool() {
let environment = run_string("var x = 3 - true;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot subtract true from 3")
}
#[test]
fn test_arithmetic_sub_bool_bool() {
let environment = run_string("var x = true - false;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot subtract false from true")
}
#[test]
fn test_arithmetic_mul_nb_nb() {
let environment = run_string("var x = 3 * 4;").unwrap();
let x = match environment.get("x") {
Some(Value::Number(value)) => value,
_ => panic!("x should be Value::Number"),
};
assert_eq!(x, 12);
}
#[test]
fn test_arithmetic_mul_nb_nb_overflow() {
let environment = run_string("var x = 4611686018427387904 * 2;");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Integer overflow: 4611686018427387904 * 2"
)
}
#[test]
fn test_arithmetic_mul_char_nb() {
let environment = run_string("var x = 'a' * 3;").unwrap();
let x = match environment.get("x") {
Some(Value::String(value)) => value,
_ => panic!("x should be Value::String"),
};
assert_eq!(str::from_utf8(&x).unwrap(), "aaa");
}
#[test]
fn test_arithmetic_mul_nb_char() {
let environment = run_string("var x = 3 * 'a';").unwrap();
let x = match environment.get("x") {
Some(Value::String(value)) => value,
_ => panic!("x should be Value::String"),
};
assert_eq!(str::from_utf8(&x).unwrap(), "aaa");
}
#[test]
fn test_arithmetic_mul_char_char() {
let environment = run_string("var x = 'a' * 'b';");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot multiply 'a' by 'b'")
}
#[test]
fn test_arithmetic_mul_str_str() {
let environment = run_string("var x = \"hello\" * \"world\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot multiply hello by world")
}
#[test]
fn test_arithmetic_mul_str_char() {
let environment = run_string("var x = \"hello\" * 'a';");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot multiply hello by 'a'")
}
#[test]
fn test_arithmetic_mul_nb_str() {
let environment = run_string("var x = 3 * \"hello\";").unwrap();
let x = match environment.get("x") {
Some(Value::String(value)) => value,
_ => panic!("x should be Value::String"),
};
assert_eq!(str::from_utf8(&x).unwrap(), "hellohellohello");
}
#[test]
fn test_arithmetic_mul_bool_str() {
let environment = run_string("var x = true * \"hello\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot multiply true by hello")
}
#[test]
fn test_arithmetic_mul_nb_bool() {
let environment = run_string("var x = 3 * true;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot multiply 3 by true")
}
#[test]
fn test_arithmetic_mul_bool_bool() {
let environment = run_string("var x = true * false;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot multiply true by false")
}
#[test]
fn test_arithmetic_div_nb_nb() {
let environment = run_string("var x = 3 / 4;").unwrap();
let x = match environment.get("x") {
Some(Value::Number(value)) => value,
_ => panic!("x should be Value::Number"),
};
assert_eq!(x, 0);
}
#[test]
fn test_arithmetic_div_char_nb() {
let environment = run_string("var x = 'a' / 3;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot divide 'a' by 3")
}
#[test]
fn test_arithmetic_div_char_char() {
let environment = run_string("var x = 'a' / 'b';");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot divide 'a' by 'b'")
}
#[test]
fn test_arithmetic_div_str_str() {
let environment = run_string("var x = \"hello\" / \"world\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot divide hello by world")
}
#[test]
fn test_arithmetic_div_str_char() {
let environment = run_string("var x = \"hello\" / 'a';");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot divide hello by 'a'")
}
#[test]
fn test_arithmetic_div_nb_str() {
let environment = run_string("var x = 3 / \"hello\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot divide 3 by hello")
}
#[test]
fn test_arithmetic_div_bool_str() {
let environment = run_string("var x = true / \"hello\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot divide true by hello")
}
#[test]
fn test_arithmetic_div_nb_bool() {
let environment = run_string("var x = 3 / true;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot divide 3 by true")
}
#[test]
fn test_arithmetic_div_bool_bool() {
let environment = run_string("var x = true / false;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot divide true by false")
}
#[test]
@ -242,6 +497,96 @@ mod tests {
assert_eq!(environment.unwrap_err(), "attempt to divide by zero")
}
#[test]
fn test_arithmetic_mod_nb_nb() {
let environment = run_string("var x = 3 % 4;").unwrap();
let x = match environment.get("x") {
Some(Value::Number(value)) => value,
_ => panic!("x should be Value::Number"),
};
assert_eq!(x, 3);
}
#[test]
fn test_arithmetic_mod_char_nb() {
let environment = run_string("var x = 'a' % 3;");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Cannot calculate the remainder with 'a' divisor of 3"
)
}
#[test]
fn test_arithmetic_mod_char_char() {
let environment = run_string("var x = 'a' % 'b';");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Cannot calculate the remainder with 'a' divisor of 'b'"
)
}
#[test]
fn test_arithmetic_mod_str_str() {
let environment = run_string("var x = \"hello\" % \"world\";");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Cannot calculate the remainder with hello divisor of world"
)
}
#[test]
fn test_arithmetic_mod_str_char() {
let environment = run_string("var x = \"hello\" % 'a';");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Cannot calculate the remainder with hello divisor of 'a'"
)
}
#[test]
fn test_arithmetic_mod_nb_str() {
let environment = run_string("var x = 3 % \"hello\";");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Cannot calculate the remainder with 3 divisor of hello"
)
}
#[test]
fn test_arithmetic_mod_bool_str() {
let environment = run_string("var x = true % \"hello\";");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Cannot calculate the remainder with true divisor of hello"
)
}
#[test]
fn test_arithmetic_mod_nb_bool() {
let environment = run_string("var x = 3 % true;");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Cannot calculate the remainder with 3 divisor of true"
)
}
#[test]
fn test_arithmetic_mod_bool_bool() {
let environment = run_string("var x = true % false;");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Cannot calculate the remainder with true divisor of false"
)
}
#[test]
fn test_arithmetic_error_mod_zero() {
let environment = run_string("var x = 3 % 0;");
@ -307,6 +652,90 @@ mod tests {
assert_eq!(j, !false);
}
#[test]
fn test_boolean_expr_nb() {
let environment = run_string("var x = 3 || 4;").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(x);
let environment = run_string("var x = 0 || 4;").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(x);
let environment = run_string("var x = 3 && 4;").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(x);
let environment = run_string("var x = 0 && 4;").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(!x);
}
#[test]
fn test_boolean_expr_char() {
let environment = run_string("var x = 'a' || 'b';").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(x);
let environment = run_string("var x = '\\0' || 'b';").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(x);
let environment = run_string("var x = 'a' && 'b';").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(x);
let environment = run_string("var x = '\\0' && 'b';").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(!x);
}
#[test]
fn test_boolean_expr_str() {
let environment = run_string("var x = \"hello\" || \"world\";").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(x);
let environment = run_string("var x = \"\" || \"world\";").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(x);
let environment = run_string("var x = \"hello\" && \"world\";").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(x);
let environment = run_string("var x = \"\" && \"world\";").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(!x);
}
#[test]
fn test_compare_expr() {
let environment = run_file("test_programs/compare_expr.duck").unwrap();
@ -392,6 +821,86 @@ mod tests {
assert_eq!(p, (2 != 2));
}
#[test]
fn test_compare_expr_char() {
let environment = run_string("var x = 'a' < 'b';").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(x);
let environment = run_string("var x = 'b' < 'a';").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(!x);
let environment = run_string("var x = 'a' < 'a';").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(!x);
let environment = run_string("var x = 'a' > 'b';").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(!x);
let environment = run_string("var x = 'b' > 'a';").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(x);
let environment = run_string("var x = 'a' > 'a';").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(!x);
}
#[test]
fn test_compare_expr_str() {
let environment = run_string("var x = \"hello\" < \"world\";").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(x);
let environment = run_string("var x = \"world\" < \"hello\";").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(!x);
let environment = run_string("var x = \"hello\" < \"hello\";").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(!x);
let environment = run_string("var x = \"hello\" > \"world\";").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(!x);
let environment = run_string("var x = \"world\" > \"hello\";").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(x);
let environment = run_string("var x = \"hello\" > \"hello\";").unwrap();
let x = match environment.get("x") {
Some(Value::Boolean(value)) => value,
_ => panic!("x should be Value::Boolean"),
};
assert!(!x);
}
#[test]
fn test_conditional() {
let environment = run_file("test_programs/conditional.duck").unwrap();
@ -490,5 +999,20 @@ mod tests {
_ => panic!("b should be Value::Char"),
};
assert_eq!(b, b'l');
let c = match environment.get("c") {
Some(Value::String(value)) => value,
_ => panic!("a should be Value::String"),
};
assert_eq!(str::from_utf8(&c).unwrap(), "world world world ");
let d = match environment.get("d") {
Some(Value::String(value)) => value,
_ => panic!("d should be Value::String"),
};
assert_eq!(str::from_utf8(&d).unwrap(), "{\n\ttest: \"test\"\n}\n");
let e = match environment.get("e") {
Some(Value::Char(value)) => value,
_ => panic!("b should be Value::Char"),
};
assert_eq!(e, b'\'');
}
}

View File

@ -3,8 +3,9 @@
alpha = { 'a'..'z' | 'A'..'Z' }
digit = { '0'..'9' }
ascii_range = { '\x00'..'\x7F' }
ascii_char = ${ "\\'" | !("'") ~ ascii_range }
ascii_string = ${ ("\\\"" | (!("\"") ~ ascii_range))* }
escape_char = { "\\" ~ ("\"" | "'" | "\\" | "n" | "t" | "r" | "0" | "a" | "b" | "f" | "v" | "?") }
ascii_char = ${ escape_char | !("\'") ~ ascii_range }
ascii_string = ${ (escape_char | (!("\"") ~ ascii_range))* }
WHITESPACE = _{ " " | "\n" | "\t" }
COMMENT = _{ block_comment | line_comment }

View File

@ -76,6 +76,6 @@ pub fn renamed_parsing_error_rules(rule: &Rule) -> String {
Rule::WHITESPACE => "whitespace".to_owned(),
Rule::COMMENT => "comment".to_owned(),
Rule::EOI => "end of input".to_owned(),
_ => unreachable!(),
r => format!("{:?}", r),
}
}

View File

@ -1,5 +1,3 @@
var x = true;
var y;
y = 1;
print x;
print y;
y = 1;

View File

@ -2,9 +2,9 @@ var x = "hello ";
var y = "world";
var a = '!';
var z = x + y + a;
print z ;
a = a * 3;
x[1] = 'a';
var b = x[2];
print "{\n\ttest: \"test\"\n}\n";
print '\'';
var c = (y + ' ') * 3;
var d = "{\n\ttest: \"test\"\n}\n";
var e = '\'';