feat(arrays): arrays implementation and tests

main
flavien 2023-12-17 14:25:43 +01:00
parent 76b364191a
commit 967891fb7b
No known key found for this signature in database
10 changed files with 464 additions and 228 deletions

View File

@ -54,14 +54,15 @@ var: z => val: 8
- print statement OK
- string / char => indexing OK / escaping OK
- local scope for { statements } OK
- array / map => iterators
- array OK => iterators NOK
- read / write
- dict
- types declaration
- type checking before runtime
- types inference
- program arguments
- structs => methods (list container?)
- pow op ?
- pow / bitwise / bitshift operators
- range
- import from other files
- ast unit tests

View File

@ -1,41 +1,230 @@
use std::rc::Rc;
use super::{environment::Environment, evaluate::Evaluate};
use super::{
environment::Environment,
evaluate::{EvaluateExpr, EvaluateValue},
};
use crate::parser::syntax::{BinaryOp, Expr, UnaryOp, Value};
pub trait ValueCompute {
fn int(&self) -> Result<i64, String>;
fn truth(&self) -> Result<bool, String>;
fn str(&self) -> Result<Rc<Vec<u8>>, String>;
fn char(&self) -> Result<u8, String>;
fn add(&self, right: &Value) -> Result<Value, String>;
fn sub(&self, right: &Value) -> Result<Value, String>;
fn mult(&self, right: &Value) -> Result<Value, String>;
fn div(&self, right: &Value) -> Result<Value, String>;
fn modulo(&self, right: &Value) -> Result<Value, String>;
fn equal(&self, right: &Value) -> Result<Value, String>;
fn not_equal(&self, right: &Value) -> Result<Value, String>;
fn greater_equal(&self, right: &Value) -> Result<Value, String>;
fn greater_than(&self, right: &Value) -> Result<Value, String>;
fn lesser_equal(&self, right: &Value) -> Result<Value, String>;
fn lesser_than(&self, right: &Value) -> Result<Value, String>;
}
impl ValueCompute for Value {
fn int(&self) -> Result<i64, String> {
match self {
Value::Number(n) => Ok(*n),
_ => Err(format!("number intended here, not {}", self.get_type())),
fn add(&self, right: &Value) -> Result<Value, String> {
match (self, right) {
(Value::Number(l), Value::Number(r)) => match (*l).checked_add(*r) {
Some(n) => Ok(Value::Number(n)),
None => Err(format!("Integer overflow: {} + {}", l, r)),
},
(Value::String(l), Value::Char(r)) => {
let mut copy = Vec::with_capacity(l.len() + 1);
copy.extend_from_slice(l);
copy.push(*r);
Ok(Value::String(Rc::new(copy)))
}
(Value::Char(l), Value::String(r)) => {
let mut copy = Vec::with_capacity(r.len() + 1);
copy.push(*l);
copy.extend_from_slice(r);
Ok(Value::String(Rc::new(copy)))
}
(Value::Char(l), Value::Char(r)) => Ok(Value::Char(l + r)),
(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)))
}
(Value::Array(l), Value::Array(r)) => {
let new = l.iter().chain(r.iter()).cloned().collect();
Ok(Value::Array(Rc::new(new)))
}
(Value::Array(l), r) => {
let mut new = (**l).clone();
new.push(r.clone());
Ok(Value::Array(Rc::new(new)))
}
(l, r) => Err(format!("Cannot add {} to {}", l, r)),
}
}
fn truth(&self) -> Result<bool, String> {
match self {
Value::Boolean(b) => Ok(*b),
Value::Number(value) => Ok(*value != 0),
Value::String(string) => Ok(!string.is_empty()),
Value::Char(c) => Ok(*c != b'\0'),
_ => Err(format!("boolean intended here, not {}", self.get_type())),
fn sub(&self, right: &Value) -> Result<Value, String> {
match (self, right) {
(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)),
}
}
fn str(&self) -> Result<Rc<Vec<u8>>, String> {
match self {
Value::String(s) => Ok(s.clone()),
_ => Err(format!("string intended here, not {}", self.get_type())),
fn mult(&self, right: &Value) -> Result<Value, String> {
match (self, right) {
(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>>(),
)))
}
(Value::Array(arr), Value::Number(n)) | (Value::Number(n), Value::Array(arr)) => {
Ok(Value::Array(Rc::new(
(0..*n)
.flat_map(|_| arr.iter().cloned())
.collect::<Vec<Value>>(),
)))
}
(l, r) => Err(format!("Cannot multiply {} by {}", l, r)),
}
}
fn char(&self) -> Result<u8, String> {
match self {
Value::Char(c) => Ok(*c),
_ => Err(format!("char intended here, not {}", self.get_type())),
fn div(&self, right: &Value) -> Result<Value, String> {
match (self, right) {
(Value::Number(l), Value::Number(r)) => {
if *r == 0 {
Err("attempt to divide by zero".to_string())
} else {
Ok(Value::Number(l / r))
}
}
(l, r) => Err(format!("Cannot divide {} by {}", l, r)),
}
}
fn modulo(&self, right: &Value) -> Result<Value, String> {
match (self, right) {
(Value::Number(l), Value::Number(r)) => {
if *r == 0 {
Err("attempt to calculate the remainder with a divisor of zero".to_string())
} else {
Ok(Value::Number(l % r))
}
}
(l, r) => Err(format!(
"Cannot calculate the remainder with {} divisor of {}",
l, r
)),
}
}
fn equal(&self, right: &Value) -> Result<Value, String> {
match (self, right) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l == r)),
(Value::Boolean(l), Value::Boolean(r)) => Ok(Value::Boolean(l == r)),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l == r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l == r)),
(Value::Array(l), Value::Array(r)) => {
if l.len() != r.len() {
Ok(Value::Boolean(false))
} else {
for pair in l.iter().zip(r.iter()) {
if let Value::Boolean(false) = pair.0.equal(pair.1)? {
return Ok(Value::Boolean(false));
}
}
Ok(Value::Boolean(true))
}
}
(l, r) => Err(format!("Cannot evaluate equality between {} and {}", l, r)),
}
}
fn not_equal(&self, right: &Value) -> Result<Value, String> {
match (self, right) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l != r)),
(Value::Boolean(l), Value::Boolean(r)) => Ok(Value::Boolean(l != r)),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l != r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l != r)),
(Value::Array(l), Value::Array(r)) => {
if l.len() != r.len() {
Ok(Value::Boolean(true))
} else {
for pair in l.iter().zip(r.iter()) {
if let Value::Boolean(true) = pair.0.equal(pair.1)? {
return Ok(Value::Boolean(true));
}
}
Ok(Value::Boolean(false))
}
}
(l, r) => Err(format!(
"Cannot evaluate inequality between {} and {}",
l, r
)),
}
}
fn greater_equal(&self, right: &Value) -> Result<Value, String> {
match (self, right) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l >= r)),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l >= r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l >= r)),
(l, r) => Err(format!("Cannot compare {} and {}", l, r)),
}
}
fn greater_than(&self, right: &Value) -> Result<Value, String> {
match (self, right) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l > r)),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l > r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l > r)),
(l, r) => Err(format!("Cannot compare {} and {}", l, r)),
}
}
fn lesser_equal(&self, right: &Value) -> Result<Value, String> {
match (self, right) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l <= r)),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l <= r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l <= r)),
(l, r) => Err(format!("Cannot compare {} and {}", l, r)),
}
}
fn lesser_than(&self, right: &Value) -> Result<Value, String> {
match (self, right) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l < r)),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l < r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l < r)),
(l, r) => Err(format!("Cannot compare {} and {}", l, r)),
}
}
}
@ -71,140 +260,17 @@ impl BinaryCompute for BinaryOp {
right: &Expr,
) -> Result<Value, String> {
match self {
BinaryOp::Addition => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => match l.checked_add(r) {
Some(n) => Ok(Value::Number(n)),
None => Err(format!("Integer overflow: {} + {}", l, r)),
},
(Value::String(l), Value::Char(r)) => {
let mut copy = Vec::with_capacity(l.len() + 1);
copy.extend_from_slice(&l);
copy.push(r);
Ok(Value::String(Rc::new(copy)))
}
(Value::Char(l), Value::String(r)) => {
let mut copy = Vec::with_capacity(r.len() + 1);
copy.push(l);
copy.extend_from_slice(&r);
Ok(Value::String(Rc::new(copy)))
}
(Value::Char(l), Value::Char(r)) => Ok(Value::Char(l + r)),
(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)),
},
BinaryOp::Subtraction => match (left.evaluate(env)?, right.evaluate(env)?) {
(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)) => 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)?) {
(Value::Number(l), Value::Number(r)) => {
if r == 0 {
Err("attempt to divide by zero".to_string())
} else {
Ok(Value::Number(l / r))
}
}
(l, r) => Err(format!("Cannot divide {} by {}", l, r)),
},
BinaryOp::Modulo => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => {
if r == 0 {
Err("attempt to calculate the remainder with a divisor of zero".to_string())
} else {
Ok(Value::Number(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)),
(Value::Boolean(l), Value::Boolean(r)) => Ok(Value::Boolean(l == r)),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l == r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l == r)),
(l, r) => Err(format!("Cannot evaluate equality between {} and {}", l, r)),
},
BinaryOp::NotEqual => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l != r)),
(Value::Boolean(l), Value::Boolean(r)) => Ok(Value::Boolean(l != r)),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l != r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l != r)),
(l, r) => Err(format!(
"Cannot evaluate inequality between {} and {}",
l, r
)),
},
BinaryOp::GreaterEqual => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l >= r)),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l >= r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l >= r)),
(l, r) => Err(format!("Cannot compare {} and {}", l, r)),
},
BinaryOp::GreaterThan => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l > r)),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l > r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l > r)),
(l, r) => Err(format!("Cannot compare {} and {}", l, r)),
},
BinaryOp::LesserEqual => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l <= r)),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l <= r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l <= r)),
(l, r) => Err(format!("Cannot compare {} and {}", l, r)),
},
BinaryOp::LesserThan => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l < r)),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l < r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l < r)),
(l, r) => Err(format!("Cannot compare {} and {}", l, r)),
},
BinaryOp::Addition => left.evaluate(env)?.add(&right.evaluate(env)?),
BinaryOp::Subtraction => left.evaluate(env)?.sub(&right.evaluate(env)?),
BinaryOp::Multiplication => left.evaluate(env)?.mult(&right.evaluate(env)?),
BinaryOp::Division => left.evaluate(env)?.div(&right.evaluate(env)?),
BinaryOp::Modulo => left.evaluate(env)?.modulo(&right.evaluate(env)?),
BinaryOp::Equal => left.evaluate(env)?.equal(&right.evaluate(env)?),
BinaryOp::NotEqual => left.evaluate(env)?.not_equal(&right.evaluate(env)?),
BinaryOp::GreaterEqual => left.evaluate(env)?.greater_equal(&right.evaluate(env)?),
BinaryOp::GreaterThan => left.evaluate(env)?.greater_than(&right.evaluate(env)?),
BinaryOp::LesserEqual => left.evaluate(env)?.lesser_equal(&right.evaluate(env)?),
BinaryOp::LesserThan => left.evaluate(env)?.lesser_than(&right.evaluate(env)?),
BinaryOp::And => Ok(Value::Boolean(
left.evaluate(env)?.truth()? && right.evaluate(env)?.truth()?,
)),

View File

@ -1,68 +1,134 @@
use std::rc::Rc;
use super::{
compute::{BinaryCompute, UnaryCompute, ValueCompute},
compute::{BinaryCompute, UnaryCompute},
environment::{Environment, LocalScope},
machine::{ControlFlow, Execution},
};
use crate::parser::syntax::{Expr, Value};
pub trait Evaluate {
pub trait EvaluateValue {
fn int(&self) -> Result<i64, String>;
fn truth(&self) -> Result<bool, String>;
fn str(&self) -> Result<Rc<Vec<u8>>, String>;
fn char(&self) -> Result<u8, String>;
}
impl EvaluateValue for Value {
fn int(&self) -> Result<i64, String> {
match self {
Value::Number(n) => Ok(*n),
_ => Err(format!("number intended here, not {}", self.get_type())),
}
}
fn truth(&self) -> Result<bool, String> {
match self {
Value::Boolean(b) => Ok(*b),
Value::Number(value) => Ok(*value != 0),
Value::String(string) => Ok(!string.is_empty()),
Value::Char(c) => Ok(*c != b'\0'),
_ => Err(format!("boolean intended here, not {}", self.get_type())),
}
}
fn str(&self) -> Result<Rc<Vec<u8>>, String> {
match self {
Value::String(s) => Ok(s.clone()),
_ => Err(format!("string intended here, not {}", self.get_type())),
}
}
fn char(&self) -> Result<u8, String> {
match self {
Value::Char(c) => Ok(*c),
_ => Err(format!("char intended here, not {}", self.get_type())),
}
}
}
fn evaluate_variable(
env: &mut dyn Environment,
name: &str,
i_expr: &Option<Box<Expr>>,
) -> Result<Value, String> {
if let Some(i_expr) = i_expr {
let i = i_expr.evaluate(env)?.int()?;
match env.get(name) {
Some(Value::String(string)) => {
if i < 0 || i >= string.len() as i64 {
return Err(format!(
"Index {} out of bounds for string of length {}",
i,
string.len()
));
}
Ok(Value::Char(string[i as usize]))
}
Some(Value::Array(array)) => {
if i < 0 || i >= array.len() as i64 {
return Err(format!(
"Index {} out of bounds for array of length {}",
i,
array.len()
));
}
Ok(array[i as usize].clone())
}
Some(_) => Err(format!("{} is not indexable", name)),
_ => Err(format!("Variable {} not found", name)),
}
} else {
env.get(name)
.ok_or_else(|| format!("Variable {} not found", name))
}
}
fn evaluate_call(env: &mut dyn Environment, name: &str, args: &[Expr]) -> Result<Value, String> {
let function = env
.get(name)
.ok_or_else(|| format!("function {} not found", name))?;
match function {
Value::Function(ref arg_names, ref body) => {
let args = args
.iter()
.map(|arg| arg.evaluate(env))
.collect::<Result<Vec<Value>, String>>()?;
if arg_names.len() != args.len() {
return Err(format!(
"function {} takes {} arguments, found {}",
name,
arg_names.len(),
args.len()
));
}
let mut fn_env = LocalScope::new_function(env, name, arg_names, &args);
// println!("{}", fn_env.to_string());
if let ControlFlow::Return(value) = body.execute(&mut fn_env)? {
Ok(value)
} else {
Ok(Value::None)
}
}
_ => Err(format!("{} is not a function", name)),
}
}
pub trait EvaluateExpr {
fn evaluate(&self, env: &mut dyn Environment) -> Result<Value, String>;
}
impl Evaluate for Expr {
impl EvaluateExpr for Expr {
fn evaluate(&self, env: &mut dyn Environment) -> Result<Value, String> {
match *self {
Expr::Litteral(ref value) => Ok(value.clone()),
Expr::Variable(ref name, ref i_expr) => {
if let Some(i_expr) = i_expr {
let i = i_expr.evaluate(env)?.int()?;
let value = env
.get(name)
.ok_or_else(|| format!("Variable {} not found", name))?
.str()?;
if i < 0 || i >= value.len() as i64 {
return Err(format!(
"Index {} out of bounds for string of length {}",
i,
value.len()
));
}
Ok(Value::Char(value[i as usize]))
} else {
env.get(name)
.ok_or_else(|| format!("Variable {} not found", name))
}
}
Expr::Variable(ref name, ref i_expr) => evaluate_variable(env, name, i_expr),
Expr::UnaryOp(ref term, ref op) => op.compute(env, term),
Expr::BinaryOp(ref lhs, ref rhs, ref op) => op.compute(env, lhs, rhs),
Expr::Call(ref name, ref args) => {
let function = env
.get(name)
.ok_or_else(|| format!("function {} not found", name))?;
match function {
Value::Function(ref arg_names, ref body) => {
let args =
args.iter()
.map(|arg| arg.evaluate(env))
.collect::<Result<Vec<Value>, String>>()?;
if arg_names.len() != args.len() {
return Err(format!(
"function {} takes {} arguments, found {}",
name,
arg_names.len(),
args.len()
));
}
let mut fn_env = LocalScope::new_function(env, name, arg_names, &args);
// println!("{}", fn_env.to_string());
if let ControlFlow::Return(value) = body.execute(&mut fn_env)? {
Ok(value)
} else {
Ok(Value::None)
}
}
_ => Err(format!("{} is not a function", name)),
}
Expr::Call(ref name, ref args) => evaluate_call(env, name, args),
Expr::Array(ref exprs) => {
let values = exprs
.iter()
.map(|expr| expr.evaluate(env))
.collect::<Result<Vec<Value>, String>>()?;
Ok(Value::Array(Rc::new(values)))
}
}
}

View File

@ -1,9 +1,8 @@
use std::rc::Rc;
use super::{
compute::ValueCompute,
environment::{Environment, GlobalScope, LocalScope},
evaluate::Evaluate,
evaluate::{EvaluateExpr, EvaluateValue},
};
use crate::parser::syntax::{Stat, Value};
@ -54,21 +53,36 @@ impl Execution for Stat {
}
Stat::Assignment(ref name, ref i_expr, ref expr) => {
if let Some(i_expr) = i_expr {
let string = env
.get(name)
.ok_or_else(|| format!("Variable {} not found", name))?
.str()?;
let i = i_expr.evaluate(env)?.int()?;
if i < 0 || i >= string.len() as i64 {
return Err(format!(
"Index {} out of bounds for string of length {}",
i,
string.len()
));
match env.get(name) {
Some(Value::String(string)) => {
let i = i_expr.evaluate(env)?.int()?;
if i < 0 || i >= string.len() as i64 {
return Err(format!(
"Index {} out of bounds for string of length {}",
i,
string.len()
));
}
let mut copy = (*string).clone();
copy[i as usize] = expr.evaluate(env)?.char()?;
env.assign(name, Value::String(Rc::new(copy)));
}
Some(Value::Array(array)) => {
let i = i_expr.evaluate(env)?.int()?;
if i < 0 || i >= array.len() as i64 {
return Err(format!(
"Index {} out of bounds for array of length {}",
i,
array.len()
));
}
let mut copy = (*array).clone();
copy[i as usize] = expr.evaluate(env)?;
env.assign(name, Value::Array(Rc::new(copy)));
}
Some(_) => return Err(format!("Variable {} not indexable", name)),
_ => return Err(format!("Variable {} not found", name)),
}
let mut copy = (*string).clone();
copy[i as usize] = expr.evaluate(env)?.char()?;
env.assign(name, Value::String(Rc::new(copy)));
} else {
if env.get(name).is_none() {
return Err(format!("Variable {} not found", name));

View File

@ -1039,4 +1039,49 @@ mod tests {
None => true,
});
}
#[test]
fn test_arrays() {
let environment = run_file("test_programs/arrays.duck").unwrap();
let a = match environment.get("a") {
Some(value) => match value {
Value::Array(_) => value,
_ => panic!("a should be Value::Array"),
},
_ => panic!("a should be defined"),
};
assert_eq!(format!("{}", a), "[1, 2, 3, 4, 5, 6, 7, 8, 9]");
let b = match environment.get("b") {
Some(value) => match value {
Value::Array(_) => value,
_ => panic!("y should be Value::Array"),
},
_ => panic!("y should be defined"),
};
assert_eq!(format!("{}", b), "[3, 10]");
let c = match environment.get("c") {
Some(value) => match value {
Value::Array(_) => value,
_ => panic!("c should be Value::Array"),
},
_ => panic!("c should be defined"),
};
assert_eq!(format!("{}", c), "[1, 2, 3, 4, 5, 6, 7, 8, 9, 3, 10]");
let d = match environment.get("d") {
Some(value) => match value {
Value::Array(_) => value,
_ => panic!("d should be Value::Array"),
},
_ => panic!("d should be defined"),
};
assert_eq!(format!("{}", d), "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]");
let e = match environment.get("e") {
Some(value) => match value {
Value::Array(_) => value,
_ => panic!("e should be Value::Array"),
},
_ => panic!("e should be defined"),
};
assert_eq!(format!("{}", e), "[1, 4, 3]");
}
}

View File

@ -94,6 +94,13 @@ fn build_expr(pair: Pair<Rule>) -> Result<Expr, String> {
};
Ok(Expr::Litteral(Value::Char(value)))
}
Rule::array => {
let mut exprs = Vec::new();
for pair in primary.into_inner() {
exprs.push(build_expr(pair)?);
}
Ok(Expr::Array(exprs))
}
rule => Err(format!(
"atom expected number, boolean or variable, found {:?}",
rule

View File

@ -20,6 +20,7 @@ string = ${ "\"" ~ ascii_string ~ "\"" }
char = ${ "'" ~ ascii_char ~ "'" }
number = ${ (digit)+ }
boolean = ${ "true" | "false" }
array = { "[" ~ (expr ~ ("," ~ expr)*)? ~ "]" }
op_assign = { "=" }
op_add = { "+" }
@ -59,7 +60,7 @@ op_unary = _{
op_not
}
value = _{ call | boolean | number | variable | string | char | "(" ~ expr ~ ")" }
value = _{ call | boolean | number | variable | string | char | array | "(" ~ expr ~ ")" }
term = { op_unary* ~ value }
expr = { term ~ (op_binary ~ term)* }

View File

@ -82,4 +82,9 @@ mod tests {
fn test_local_block_env() {
parse_file("test_programs/local_block_env.duck");
}
#[test]
fn test_arrays() {
parse_file("test_programs/arrays.duck");
}
}

View File

@ -65,6 +65,7 @@ pub enum Value {
Function(Rc<Vec<String>>, Rc<Vec<Stat>>),
String(Rc<Vec<u8>>),
Char(u8),
Array(Rc<Vec<Value>>),
None,
}
@ -88,6 +89,7 @@ impl Value {
Value::Function(_, _) => "function",
Value::String(_) => "string",
Value::Char(_) => "char",
Value::Array(_) => "array",
Value::None => "none",
}
}
@ -101,6 +103,15 @@ impl Display for Value {
Value::Function(args, _) => write!(f, "fn({})", args.join(", ")),
Value::String(s) => unsafe { write!(f, "{}", str::from_utf8_unchecked(s)) },
Value::Char(c) => write!(f, "'{}'", char::from(*c)),
Value::Array(arr) => {
write!(f, "[")?;
let values = arr
.iter()
.map(|value| value.to_string())
.collect::<Vec<String>>();
write!(f, "{}", values.join(", "))?;
write!(f, "]")
}
Value::None => write!(f, "None"),
}
}
@ -114,6 +125,7 @@ pub enum Expr {
BinaryOp(Box<Expr>, Box<Expr>, BinaryOp),
UnaryOp(Box<Expr>, UnaryOp),
Call(String, Vec<Expr>),
Array(Vec<Expr>),
}
impl Display for Expr {
@ -138,6 +150,15 @@ impl Display for Expr {
write!(f, "{}", exprs.join(", "))?;
write!(f, ")")
}
Expr::Array(exprs) => {
write!(f, "[")?;
let exprs = exprs
.iter()
.map(|arg| arg.to_string())
.collect::<Vec<String>>();
write!(f, "{}", exprs.join(", "))?;
write!(f, "]")
}
}
}
}

10
test_programs/arrays.duck Normal file
View File

@ -0,0 +1,10 @@
fn double(n) {
return n * 2;
}
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var b = [a[0] + 2, double(a[4])]; // indexing, expr
var c = a + b; // array concat
var d = a + b[1]; // value push
var e = [1, 2, 3];
e[1] = 4; // indexing, assignment