Add math
This commit is contained in:
parent
4d76023775
commit
c51b142130
@ -4,12 +4,13 @@ use crate::{
|
|||||||
Value,
|
Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{AbstractTree, Identifier, Logic, Type, ValueNode};
|
use super::{AbstractTree, Identifier, 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),
|
||||||
Logic(Box<Logic<'src>>),
|
Logic(Box<Logic<'src>>),
|
||||||
|
Math(Box<Math<'src>>),
|
||||||
Value(ValueNode<'src>),
|
Value(ValueNode<'src>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ impl<'src> AbstractTree for Expression<'src> {
|
|||||||
match self {
|
match self {
|
||||||
Expression::Identifier(identifier) => identifier.expected_type(_context),
|
Expression::Identifier(identifier) => identifier.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::Value(value_node) => value_node.expected_type(_context),
|
Expression::Value(value_node) => value_node.expected_type(_context),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -26,15 +28,17 @@ impl<'src> AbstractTree for Expression<'src> {
|
|||||||
match self {
|
match self {
|
||||||
Expression::Identifier(identifier) => identifier.validate(_context),
|
Expression::Identifier(identifier) => identifier.validate(_context),
|
||||||
Expression::Logic(logic) => logic.validate(_context),
|
Expression::Logic(logic) => logic.validate(_context),
|
||||||
|
Expression::Math(math) => math.validate(_context),
|
||||||
Expression::Value(value_node) => value_node.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 {
|
match self {
|
||||||
Expression::Identifier(identifier) => identifier.run(context),
|
Expression::Identifier(identifier) => identifier.run(_context),
|
||||||
Expression::Logic(logic) => logic.run(context),
|
Expression::Logic(logic) => logic.run(_context),
|
||||||
Expression::Value(value_node) => value_node.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 identifier;
|
||||||
pub mod logic;
|
pub mod logic;
|
||||||
pub mod r#loop;
|
pub mod r#loop;
|
||||||
|
pub mod math;
|
||||||
pub mod statement;
|
pub mod statement;
|
||||||
pub mod r#type;
|
pub mod r#type;
|
||||||
pub mod value_node;
|
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, 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::{
|
use crate::{
|
||||||
|
@ -67,6 +67,12 @@ impl Error {
|
|||||||
Label::new(span.start..span.end).with_message("Expected boolean."),
|
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::RwLockPoison(_) => todo!(),
|
||||||
ValidationError::TypeCheck(TypeCheckError { actual, expected }) => {
|
ValidationError::TypeCheck(TypeCheckError { actual, expected }) => {
|
||||||
report = report.with_label(Label::new(span.start..span.end).with_message(
|
report = report.with_label(Label::new(span.start..span.end).with_message(
|
||||||
@ -132,6 +138,7 @@ impl From<ValidationError> for RuntimeError {
|
|||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum ValidationError {
|
pub enum ValidationError {
|
||||||
ExpectedBoolean,
|
ExpectedBoolean,
|
||||||
|
ExpectedIntegerOrFloat,
|
||||||
RwLockPoison(RwLockPoisonError),
|
RwLockPoison(RwLockPoisonError),
|
||||||
TypeCheck(TypeCheckError),
|
TypeCheck(TypeCheckError),
|
||||||
VariableNotFound(Identifier),
|
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::Not),
|
||||||
just("!=").padded().to(Operator::NotEqual),
|
just("!=").padded().to(Operator::NotEqual),
|
||||||
just("||").padded().to(Operator::Or),
|
just("||").padded().to(Operator::Or),
|
||||||
|
// assignment
|
||||||
|
just("=").padded().to(Operator::Assign),
|
||||||
|
just("+=").padded().to(Operator::AddAssign),
|
||||||
|
just("-=").padded().to(Operator::SubAssign),
|
||||||
// math
|
// math
|
||||||
just("+").padded().to(Operator::Add),
|
just("+").padded().to(Operator::Add),
|
||||||
just("-").padded().to(Operator::Subtract),
|
just("-").padded().to(Operator::Subtract),
|
||||||
just("*").padded().to(Operator::Multiply),
|
just("*").padded().to(Operator::Multiply),
|
||||||
just("/").padded().to(Operator::Divide),
|
just("/").padded().to(Operator::Divide),
|
||||||
just("%").padded().to(Operator::Modulo),
|
just("%").padded().to(Operator::Modulo),
|
||||||
// assignment
|
|
||||||
just("=").padded().to(Operator::Assign),
|
|
||||||
just("+=").padded().to(Operator::AddAssign),
|
|
||||||
just("-=").padded().to(Operator::SubAssign),
|
|
||||||
))
|
))
|
||||||
.map(Token::Operator);
|
.map(Token::Operator);
|
||||||
|
|
||||||
@ -196,6 +196,18 @@ pub fn lexer<'src>() -> impl Parser<
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn keywords() {
|
fn keywords() {
|
||||||
assert_eq!(lex("int").unwrap()[0].0, Token::Keyword("int"))
|
assert_eq!(lex("int").unwrap()[0].0, Token::Keyword("int"))
|
||||||
|
@ -90,7 +90,8 @@ pub fn parser<'src>() -> DustParser<'src> {
|
|||||||
|
|
||||||
use Operator::*;
|
use Operator::*;
|
||||||
|
|
||||||
let logic = 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)))
|
||||||
@ -123,10 +124,31 @@ pub fn parser<'src>() -> DustParser<'src> {
|
|||||||
infix(left(1), just(Token::Operator(Or)), |left, right| {
|
infix(left(1), just(Token::Operator(Or)), |left, right| {
|
||||||
Expression::Logic(Box::new(Logic::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();
|
.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| {
|
let statement = recursive(|statement| {
|
||||||
@ -208,6 +230,17 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn r#loop() {
|
fn r#loop() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -95,6 +95,14 @@ impl Value {
|
|||||||
Err(ValidationError::ExpectedBoolean)
|
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 {
|
pub fn is_none(&self) -> bool {
|
||||||
self == get_none()
|
self == get_none()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user