Add indexes
This commit is contained in:
parent
85d954181b
commit
d99e3cb861
@ -4,11 +4,12 @@ use crate::{
|
||||
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)]
|
||||
pub enum Expression<'src> {
|
||||
Identifier(Identifier),
|
||||
Index(Box<Index<'src>>),
|
||||
Logic(Box<Logic<'src>>),
|
||||
Math(Box<Math<'src>>),
|
||||
Value(ValueNode<'src>),
|
||||
@ -18,6 +19,7 @@ impl<'src> AbstractTree for Expression<'src> {
|
||||
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
||||
match self {
|
||||
Expression::Identifier(identifier) => identifier.expected_type(_context),
|
||||
Expression::Index(index) => index.expected_type(_context),
|
||||
Expression::Logic(logic) => logic.expected_type(_context),
|
||||
Expression::Math(math) => math.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> {
|
||||
match self {
|
||||
Expression::Identifier(identifier) => identifier.validate(_context),
|
||||
Expression::Index(index) => index.validate(_context),
|
||||
Expression::Logic(logic) => logic.validate(_context),
|
||||
Expression::Math(math) => math.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> {
|
||||
match self {
|
||||
Expression::Identifier(identifier) => identifier.run(_context),
|
||||
Expression::Index(index) => index.run(_context),
|
||||
Expression::Logic(logic) => logic.run(_context),
|
||||
Expression::Math(math) => math.run(_context),
|
||||
Expression::Value(value_node) => value_node.run(_context),
|
||||
|
76
src/abstract_tree/index.rs
Normal file
76
src/abstract_tree/index.rs
Normal 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()),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ pub mod assignment;
|
||||
pub mod block;
|
||||
pub mod expression;
|
||||
pub mod identifier;
|
||||
pub mod index;
|
||||
pub mod logic;
|
||||
pub mod r#loop;
|
||||
pub mod math;
|
||||
@ -11,7 +12,7 @@ pub mod value_node;
|
||||
|
||||
pub use self::{
|
||||
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,
|
||||
};
|
||||
|
||||
|
@ -26,7 +26,15 @@ impl<'src> AbstractTree for ValueNode<'src> {
|
||||
ValueNode::Boolean(_) => Type::Boolean,
|
||||
ValueNode::Float(_) => Type::Float,
|
||||
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::Range(_) => Type::Range,
|
||||
ValueNode::String(_) => Type::String,
|
||||
|
@ -85,6 +85,8 @@ impl Error {
|
||||
"The variable {identifier} does not exist."
|
||||
)));
|
||||
}
|
||||
ValidationError::CannotIndex(_) => todo!(),
|
||||
ValidationError::CannotIndexWith(_, _) => todo!(),
|
||||
}
|
||||
|
||||
report.finish()
|
||||
@ -137,6 +139,8 @@ impl From<ValidationError> for RuntimeError {
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ValidationError {
|
||||
CannotIndex(Type),
|
||||
CannotIndexWith(Type, Type),
|
||||
ExpectedBoolean,
|
||||
ExpectedIntegerOrFloat,
|
||||
RwLockPoison(RwLockPoisonError),
|
||||
|
@ -95,7 +95,6 @@ pub fn parser<'src>() -> DustParser<'src> {
|
||||
use Operator::*;
|
||||
|
||||
let logic_and_math = atom
|
||||
.clone()
|
||||
.pratt((
|
||||
prefix(2, just(Token::Operator(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| {
|
||||
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();
|
||||
|
||||
|
16
src/value.rs
16
src/value.rs
@ -103,6 +103,22 @@ impl Value {
|
||||
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 {
|
||||
self == get_none()
|
||||
}
|
||||
|
@ -12,5 +12,13 @@ fn logic() {
|
||||
#[test]
|
||||
fn math() {
|
||||
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)));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user