feat(arrays): arrays implementation and tests
parent
76b364191a
commit
967891fb7b
|
@ -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
|
||||
|
|
|
@ -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()?,
|
||||
)),
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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]");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)* }
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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, "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue