Add math
This commit is contained in:
parent
4d76023775
commit
c51b142130
@ -4,12 +4,13 @@ use crate::{
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::{AbstractTree, Identifier, Logic, Type, ValueNode};
|
||||
use super::{AbstractTree, Identifier, Logic, Math, Type, ValueNode};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum Expression<'src> {
|
||||
Identifier(Identifier),
|
||||
Logic(Box<Logic<'src>>),
|
||||
Math(Box<Math<'src>>),
|
||||
Value(ValueNode<'src>),
|
||||
}
|
||||
|
||||
@ -18,6 +19,7 @@ impl<'src> AbstractTree for Expression<'src> {
|
||||
match self {
|
||||
Expression::Identifier(identifier) => identifier.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),
|
||||
}
|
||||
}
|
||||
@ -26,15 +28,17 @@ impl<'src> AbstractTree for Expression<'src> {
|
||||
match self {
|
||||
Expression::Identifier(identifier) => identifier.validate(_context),
|
||||
Expression::Logic(logic) => logic.validate(_context),
|
||||
Expression::Math(math) => math.validate(_context),
|
||||
Expression::Value(value_node) => value_node.validate(_context),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(self, context: &Context) -> Result<Value, RuntimeError> {
|
||||
fn run(self, _context: &Context) -> Result<Value, RuntimeError> {
|
||||
match self {
|
||||
Expression::Identifier(identifier) => identifier.run(context),
|
||||
Expression::Logic(logic) => logic.run(context),
|
||||
Expression::Value(value_node) => value_node.run(context),
|
||||
Expression::Identifier(identifier) => identifier.run(_context),
|
||||
Expression::Logic(logic) => logic.run(_context),
|
||||
Expression::Math(math) => math.run(_context),
|
||||
Expression::Value(value_node) => value_node.run(_context),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
86
src/abstract_tree/math.rs
Normal file
86
src/abstract_tree/math.rs
Normal file
@ -0,0 +1,86 @@
|
||||
use crate::{
|
||||
context::Context,
|
||||
error::{RuntimeError, ValidationError},
|
||||
value::ValueInner,
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::{AbstractTree, Expression, Type};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum Math<'src> {
|
||||
Add(Expression<'src>, Expression<'src>),
|
||||
Subtract(Expression<'src>, Expression<'src>),
|
||||
Multiply(Expression<'src>, Expression<'src>),
|
||||
Divide(Expression<'src>, Expression<'src>),
|
||||
Modulo(Expression<'src>, Expression<'src>),
|
||||
}
|
||||
|
||||
impl<'src> AbstractTree for Math<'src> {
|
||||
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
||||
match self {
|
||||
Math::Add(left, _)
|
||||
| Math::Subtract(left, _)
|
||||
| Math::Multiply(left, _)
|
||||
| Math::Divide(left, _)
|
||||
| Math::Modulo(left, _) => left.expected_type(_context),
|
||||
}
|
||||
}
|
||||
|
||||
fn validate(&self, context: &Context) -> Result<(), ValidationError> {
|
||||
match self {
|
||||
Math::Add(left, right)
|
||||
| Math::Subtract(left, right)
|
||||
| Math::Multiply(left, right)
|
||||
| Math::Divide(left, right)
|
||||
| Math::Modulo(left, right) => {
|
||||
let left_type = left.expected_type(context)?;
|
||||
let right_type = right.expected_type(context)?;
|
||||
|
||||
match (left_type, right_type) {
|
||||
(Type::Integer, Type::Integer)
|
||||
| (Type::Float, Type::Float)
|
||||
| (Type::Integer, Type::Float)
|
||||
| (Type::Float, Type::Integer) => Ok(()),
|
||||
_ => Err(ValidationError::ExpectedIntegerOrFloat),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run(self, context: &Context) -> Result<Value, RuntimeError> {
|
||||
match self {
|
||||
Math::Add(left, right) => {
|
||||
let left_value = left.run(context)?;
|
||||
let right_value = right.run(context)?;
|
||||
|
||||
if let (ValueInner::Integer(left), ValueInner::Integer(right)) =
|
||||
(left_value.inner().as_ref(), right_value.inner().as_ref())
|
||||
{
|
||||
Ok(Value::integer(left + right))
|
||||
} else {
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::ExpectedIntegerOrFloat,
|
||||
))
|
||||
}
|
||||
}
|
||||
Math::Subtract(_, _) => todo!(),
|
||||
Math::Multiply(left, right) => {
|
||||
let left_value = left.run(context)?;
|
||||
let right_value = right.run(context)?;
|
||||
|
||||
if let (ValueInner::Integer(left), ValueInner::Integer(right)) =
|
||||
(left_value.inner().as_ref(), right_value.inner().as_ref())
|
||||
{
|
||||
Ok(Value::integer(left * right))
|
||||
} else {
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::ExpectedIntegerOrFloat,
|
||||
))
|
||||
}
|
||||
}
|
||||
Math::Divide(_, _) => todo!(),
|
||||
Math::Modulo(_, _) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
@ -4,13 +4,15 @@ pub mod expression;
|
||||
pub mod identifier;
|
||||
pub mod logic;
|
||||
pub mod r#loop;
|
||||
pub mod math;
|
||||
pub mod statement;
|
||||
pub mod r#type;
|
||||
pub mod value_node;
|
||||
|
||||
pub use self::{
|
||||
assignment::Assignment, block::Block, expression::Expression, identifier::Identifier,
|
||||
logic::Logic, r#loop::Loop, r#type::Type, statement::Statement, value_node::ValueNode,
|
||||
logic::Logic, math::Math, r#loop::Loop, r#type::Type, statement::Statement,
|
||||
value_node::ValueNode,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -67,6 +67,12 @@ impl Error {
|
||||
Label::new(span.start..span.end).with_message("Expected boolean."),
|
||||
);
|
||||
}
|
||||
ValidationError::ExpectedIntegerOrFloat => {
|
||||
report = report.with_label(
|
||||
Label::new(span.start..span.end)
|
||||
.with_message("Expected integer or float."),
|
||||
);
|
||||
}
|
||||
ValidationError::RwLockPoison(_) => todo!(),
|
||||
ValidationError::TypeCheck(TypeCheckError { actual, expected }) => {
|
||||
report = report.with_label(Label::new(span.start..span.end).with_message(
|
||||
@ -132,6 +138,7 @@ impl From<ValidationError> for RuntimeError {
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ValidationError {
|
||||
ExpectedBoolean,
|
||||
ExpectedIntegerOrFloat,
|
||||
RwLockPoison(RwLockPoisonError),
|
||||
TypeCheck(TypeCheckError),
|
||||
VariableNotFound(Identifier),
|
||||
|
20
src/lexer.rs
20
src/lexer.rs
@ -144,16 +144,16 @@ pub fn lexer<'src>() -> impl Parser<
|
||||
just("!").padded().to(Operator::Not),
|
||||
just("!=").padded().to(Operator::NotEqual),
|
||||
just("||").padded().to(Operator::Or),
|
||||
// assignment
|
||||
just("=").padded().to(Operator::Assign),
|
||||
just("+=").padded().to(Operator::AddAssign),
|
||||
just("-=").padded().to(Operator::SubAssign),
|
||||
// math
|
||||
just("+").padded().to(Operator::Add),
|
||||
just("-").padded().to(Operator::Subtract),
|
||||
just("*").padded().to(Operator::Multiply),
|
||||
just("/").padded().to(Operator::Divide),
|
||||
just("%").padded().to(Operator::Modulo),
|
||||
// assignment
|
||||
just("=").padded().to(Operator::Assign),
|
||||
just("+=").padded().to(Operator::AddAssign),
|
||||
just("-=").padded().to(Operator::SubAssign),
|
||||
))
|
||||
.map(Token::Operator);
|
||||
|
||||
@ -196,6 +196,18 @@ pub fn lexer<'src>() -> impl Parser<
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn math_operators() {
|
||||
assert_eq!(
|
||||
lex("1 + 1").unwrap(),
|
||||
vec![
|
||||
(Token::Integer(1), (0..1).into()),
|
||||
(Token::Operator(Operator::Add), (2..4).into()),
|
||||
(Token::Integer(1), (4..5).into())
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keywords() {
|
||||
assert_eq!(lex("int").unwrap()[0].0, Token::Keyword("int"))
|
||||
|
@ -90,7 +90,8 @@ pub fn parser<'src>() -> DustParser<'src> {
|
||||
|
||||
use Operator::*;
|
||||
|
||||
let logic = atom
|
||||
let logic_and_math = atom
|
||||
.clone()
|
||||
.pratt((
|
||||
prefix(2, just(Token::Operator(Not)), |expression| {
|
||||
Expression::Logic(Box::new(Logic::Not(expression)))
|
||||
@ -123,10 +124,31 @@ pub fn parser<'src>() -> DustParser<'src> {
|
||||
infix(left(1), just(Token::Operator(Or)), |left, right| {
|
||||
Expression::Logic(Box::new(Logic::Or(left, right)))
|
||||
}),
|
||||
infix(left(1), just(Token::Operator(Add)), |left, right| {
|
||||
Expression::Math(Box::new(Math::Add(left, right)))
|
||||
}),
|
||||
infix(left(1), just(Token::Operator(Subtract)), |left, right| {
|
||||
Expression::Math(Box::new(Math::Subtract(left, right)))
|
||||
}),
|
||||
infix(left(2), just(Token::Operator(Multiply)), |left, right| {
|
||||
Expression::Math(Box::new(Math::Multiply(left, right)))
|
||||
}),
|
||||
infix(left(2), just(Token::Operator(Divide)), |left, right| {
|
||||
Expression::Math(Box::new(Math::Divide(left, right)))
|
||||
}),
|
||||
infix(left(1), just(Token::Operator(Modulo)), |left, right| {
|
||||
Expression::Math(Box::new(Math::Modulo(left, right)))
|
||||
}),
|
||||
))
|
||||
.boxed();
|
||||
|
||||
choice((r#enum, logic, identifier_expression, list, basic_value))
|
||||
choice((
|
||||
r#enum,
|
||||
logic_and_math,
|
||||
identifier_expression,
|
||||
list,
|
||||
basic_value,
|
||||
))
|
||||
});
|
||||
|
||||
let statement = recursive(|statement| {
|
||||
@ -208,6 +230,17 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn math() {
|
||||
assert_eq!(
|
||||
parse(&lex("1 + 1").unwrap()).unwrap()[0].0,
|
||||
Statement::Expression(Expression::Math(Box::new(Math::Add(
|
||||
Expression::Value(ValueNode::Integer(1)),
|
||||
Expression::Value(ValueNode::Integer(1))
|
||||
))))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn r#loop() {
|
||||
assert_eq!(
|
||||
|
@ -95,6 +95,14 @@ impl Value {
|
||||
Err(ValidationError::ExpectedBoolean)
|
||||
}
|
||||
|
||||
pub fn as_number(&self) -> Result<bool, ValidationError> {
|
||||
if let ValueInner::Boolean(boolean) = self.0.as_ref() {
|
||||
return Ok(*boolean);
|
||||
}
|
||||
|
||||
Err(ValidationError::ExpectedBoolean)
|
||||
}
|
||||
|
||||
pub fn is_none(&self) -> bool {
|
||||
self == get_none()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user