1
0

Add indexes

This commit is contained in:
Jeff 2024-03-07 12:29:07 -05:00
parent 85d954181b
commit d99e3cb861
8 changed files with 126 additions and 5 deletions

View File

@ -4,11 +4,12 @@ use crate::{
Value, Value,
}; };
use super::{AbstractTree, Identifier, Logic, Math, Type, ValueNode}; use super::{AbstractTree, Identifier, Index, Logic, Math, Type, ValueNode};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Expression<'src> { pub enum Expression<'src> {
Identifier(Identifier), Identifier(Identifier),
Index(Box<Index<'src>>),
Logic(Box<Logic<'src>>), Logic(Box<Logic<'src>>),
Math(Box<Math<'src>>), Math(Box<Math<'src>>),
Value(ValueNode<'src>), Value(ValueNode<'src>),
@ -18,6 +19,7 @@ impl<'src> AbstractTree for Expression<'src> {
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> { fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
match self { match self {
Expression::Identifier(identifier) => identifier.expected_type(_context), Expression::Identifier(identifier) => identifier.expected_type(_context),
Expression::Index(index) => index.expected_type(_context),
Expression::Logic(logic) => logic.expected_type(_context), Expression::Logic(logic) => logic.expected_type(_context),
Expression::Math(math) => math.expected_type(_context), Expression::Math(math) => math.expected_type(_context),
Expression::Value(value_node) => value_node.expected_type(_context), Expression::Value(value_node) => value_node.expected_type(_context),
@ -27,6 +29,7 @@ impl<'src> AbstractTree for Expression<'src> {
fn validate(&self, _context: &Context) -> Result<(), ValidationError> { fn validate(&self, _context: &Context) -> Result<(), ValidationError> {
match self { match self {
Expression::Identifier(identifier) => identifier.validate(_context), Expression::Identifier(identifier) => identifier.validate(_context),
Expression::Index(index) => index.validate(_context),
Expression::Logic(logic) => logic.validate(_context), Expression::Logic(logic) => logic.validate(_context),
Expression::Math(math) => math.validate(_context), Expression::Math(math) => math.validate(_context),
Expression::Value(value_node) => value_node.validate(_context), Expression::Value(value_node) => value_node.validate(_context),
@ -36,6 +39,7 @@ impl<'src> AbstractTree for Expression<'src> {
fn run(self, _context: &Context) -> Result<Value, RuntimeError> { fn run(self, _context: &Context) -> Result<Value, RuntimeError> {
match self { match self {
Expression::Identifier(identifier) => identifier.run(_context), Expression::Identifier(identifier) => identifier.run(_context),
Expression::Index(index) => index.run(_context),
Expression::Logic(logic) => logic.run(_context), Expression::Logic(logic) => logic.run(_context),
Expression::Math(math) => math.run(_context), Expression::Math(math) => math.run(_context),
Expression::Value(value_node) => value_node.run(_context), Expression::Value(value_node) => value_node.run(_context),

View File

@ -0,0 +1,76 @@
use crate::{
context::Context,
error::{RuntimeError, ValidationError},
Value,
};
use super::{AbstractTree, Expression, Type, ValueNode};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct Index<'src> {
left: Expression<'src>,
right: Expression<'src>,
}
impl<'src> Index<'src> {
pub fn new(left: Expression<'src>, right: Expression<'src>) -> Self {
Self { left, right }
}
}
impl<'src> AbstractTree for Index<'src> {
fn expected_type(&self, context: &Context) -> Result<Type, ValidationError> {
let left_type = self.left.expected_type(context)?;
if let (
Expression::Value(ValueNode::List(expression_list)),
Expression::Value(ValueNode::Integer(index)),
) = (&self.left, &self.right)
{
let expression = if let Some(expression) = expression_list.get(*index as usize) {
expression
} else {
return Ok(Type::None);
};
expression.expected_type(context)
} else {
Err(ValidationError::CannotIndex(left_type))
}
}
fn validate(&self, context: &Context) -> Result<(), ValidationError> {
let left_type = self.left.expected_type(context)?;
match left_type {
Type::List => todo!(),
Type::ListOf(_) => todo!(),
Type::ListExact(_) => {
let right_type = self.right.expected_type(context)?;
if let Type::Integer = right_type {
Ok(())
} else {
Err(ValidationError::CannotIndexWith(left_type, right_type))
}
}
_ => Err(ValidationError::CannotIndex(left_type)),
}
}
fn run(self, _context: &Context) -> Result<Value, RuntimeError> {
let left_value = self.left.run(_context)?;
let right_value = self.right.run(_context)?;
if let (Some(list), Some(index)) = (left_value.as_list(), right_value.as_integer()) {
Ok(list
.get(index as usize)
.cloned()
.unwrap_or_else(Value::none))
} else {
Err(RuntimeError::ValidationFailure(
ValidationError::CannotIndexWith(left_value.r#type(), right_value.r#type()),
))
}
}
}

View File

@ -2,6 +2,7 @@ pub mod assignment;
pub mod block; pub mod block;
pub mod expression; pub mod expression;
pub mod identifier; pub mod identifier;
pub mod index;
pub mod logic; pub mod logic;
pub mod r#loop; pub mod r#loop;
pub mod math; pub mod math;
@ -11,7 +12,7 @@ pub mod value_node;
pub use self::{ pub use self::{
assignment::Assignment, block::Block, expression::Expression, identifier::Identifier, assignment::Assignment, block::Block, expression::Expression, identifier::Identifier,
logic::Logic, math::Math, r#loop::Loop, r#type::Type, statement::Statement, index::Index, logic::Logic, math::Math, r#loop::Loop, r#type::Type, statement::Statement,
value_node::ValueNode, value_node::ValueNode,
}; };

View File

@ -26,7 +26,15 @@ impl<'src> AbstractTree for ValueNode<'src> {
ValueNode::Boolean(_) => Type::Boolean, ValueNode::Boolean(_) => Type::Boolean,
ValueNode::Float(_) => Type::Float, ValueNode::Float(_) => Type::Float,
ValueNode::Integer(_) => Type::Integer, ValueNode::Integer(_) => Type::Integer,
ValueNode::List(_) => Type::List, ValueNode::List(items) => {
let mut item_types = Vec::with_capacity(items.len());
for expression in items {
item_types.push(expression.expected_type(_context)?);
}
Type::ListExact(item_types)
}
ValueNode::Map(_) => Type::Map, ValueNode::Map(_) => Type::Map,
ValueNode::Range(_) => Type::Range, ValueNode::Range(_) => Type::Range,
ValueNode::String(_) => Type::String, ValueNode::String(_) => Type::String,

View File

@ -85,6 +85,8 @@ impl Error {
"The variable {identifier} does not exist." "The variable {identifier} does not exist."
))); )));
} }
ValidationError::CannotIndex(_) => todo!(),
ValidationError::CannotIndexWith(_, _) => todo!(),
} }
report.finish() report.finish()
@ -137,6 +139,8 @@ impl From<ValidationError> for RuntimeError {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum ValidationError { pub enum ValidationError {
CannotIndex(Type),
CannotIndexWith(Type, Type),
ExpectedBoolean, ExpectedBoolean,
ExpectedIntegerOrFloat, ExpectedIntegerOrFloat,
RwLockPoison(RwLockPoisonError), RwLockPoison(RwLockPoisonError),

View File

@ -95,7 +95,6 @@ pub fn parser<'src>() -> DustParser<'src> {
use Operator::*; use Operator::*;
let logic_and_math = atom let logic_and_math = atom
.clone()
.pratt(( .pratt((
prefix(2, just(Token::Operator(Not)), |expression| { prefix(2, just(Token::Operator(Not)), |expression| {
Expression::Logic(Box::new(Logic::Not(expression))) Expression::Logic(Box::new(Logic::Not(expression)))
@ -143,6 +142,11 @@ pub fn parser<'src>() -> DustParser<'src> {
infix(left(1), just(Token::Operator(Modulo)), |left, right| { infix(left(1), just(Token::Operator(Modulo)), |left, right| {
Expression::Math(Box::new(Math::Modulo(left, right))) Expression::Math(Box::new(Math::Modulo(left, right)))
}), }),
infix(
left(3),
just(Token::Control(Control::Dot)),
|left, right| Expression::Index(Box::new(Index::new(left, right))),
),
)) ))
.boxed(); .boxed();

View File

@ -103,6 +103,22 @@ impl Value {
Err(ValidationError::ExpectedBoolean) Err(ValidationError::ExpectedBoolean)
} }
pub fn as_list(&self) -> Option<&Vec<Value>> {
if let ValueInner::List(list) = self.inner().as_ref() {
Some(list)
} else {
None
}
}
pub fn as_integer(&self) -> Option<i64> {
if let ValueInner::Integer(integer) = self.inner().as_ref() {
Some(*integer)
} else {
None
}
}
pub fn is_none(&self) -> bool { pub fn is_none(&self) -> bool {
self == get_none() self == get_none()
} }

View File

@ -12,5 +12,13 @@ fn logic() {
#[test] #[test]
fn math() { fn math() {
assert_eq!(interpret("1 + 1"), Ok(Value::integer(2))); assert_eq!(interpret("1 + 1"), Ok(Value::integer(2)));
assert_eq!(interpret("21 + 19 + 1 * 2"), Ok(Value::integer(42))); assert_eq!(
interpret("2 * (21 + 19 + 1 * 2) / 2"),
Ok(Value::integer(42))
);
}
#[test]
fn list_index() {
assert_eq!(interpret("foo = [1, 2, 3]; foo.2"), Ok(Value::integer(3)));
} }