aboutsummaryrefslogtreecommitdiff
path: root/src/interpret
diff options
context:
space:
mode:
Diffstat (limited to 'src/interpret')
-rw-r--r--src/interpret/value.rs103
-rw-r--r--src/interpret/walker.rs97
2 files changed, 150 insertions, 50 deletions
diff --git a/src/interpret/value.rs b/src/interpret/value.rs
index 27f2668..86fe094 100644
--- a/src/interpret/value.rs
+++ b/src/interpret/value.rs
@@ -1,6 +1,6 @@
use crate::parse::ast::nodes::FnNode;
-use anyhow::{anyhow, Result};
use std::{cell::RefCell, fmt::Display, rc::Rc};
+use thiserror::Error;
type Ref<T> = RefCell<Rc<T>>;
@@ -15,79 +15,79 @@ pub enum Value {
}
impl Value {
- pub fn add(self, rhs: Value) -> Result<Value> {
+ pub fn add(self, rhs: Value) -> Result<Value, OperationError> {
match self {
- Value::Str(l) => match rhs {
- Value::Str(r) => Ok(Value::Str(l + &r)),
- Value::Float(r) => Ok(Value::Str(l + &r.to_string())),
- Value::Int(r) => Ok(Value::Str(l + &r.to_string())),
- _ => Err(anyhow!("Right operand can't be added.")),
+ Value::Str(ref l) => match rhs {
+ Value::Str(r) => Ok(Value::Str(format!("{}{}", l, r))),
+ Value::Float(r) => Ok(Value::Str(format!("{}{}", l, r))),
+ Value::Int(r) => Ok(Value::Str(format!("{}{}", l, r))),
+ r => Err(OperationError::AddTypes(self, r)),
},
Value::Float(l) => match rhs {
Value::Str(r) => Ok(Value::Str(l.to_string() + &r)),
Value::Float(r) => Ok(Value::Float(l + r)),
Value::Int(r) => Ok(Value::Float(l + r as f64)),
- _ => Err(anyhow!("Right operand can't be added.")),
+ r => Err(OperationError::AddTypes(self, r)),
},
Value::Int(l) => match rhs {
Value::Str(r) => Ok(Value::Str(l.to_string() + &r)),
Value::Float(r) => Ok(Value::Float(l as f64 + r)),
Value::Int(r) => Ok(Value::Int(l + r)),
- _ => Err(anyhow!("Right operand can't be added.")),
+ r => Err(OperationError::AddTypes(self, r)),
},
- _ => Err(anyhow!("Left operand can't be added.")),
+ _ => Err(OperationError::AddTypes(self, rhs)),
}
}
- pub fn sub(self, rhs: Value) -> Result<Value> {
+ pub fn sub(self, rhs: Value) -> Result<Value, OperationError> {
match self {
Value::Float(l) => match rhs {
Value::Float(r) => Ok(Value::Float(l - r)),
Value::Int(r) => Ok(Value::Float(l - r as f64)),
- _ => Err(anyhow!("Right operand can't be substracted.")),
+ r => Err(OperationError::SubTypes(self, r)),
},
Value::Int(l) => match rhs {
Value::Float(r) => Ok(Value::Float(l as f64 - r)),
Value::Int(r) => Ok(Value::Int(l - r)),
- _ => Err(anyhow!("Right operand can't be substracted.")),
+ r => Err(OperationError::SubTypes(self, r)),
},
- _ => Err(anyhow!("Left operand can't be substracted from.")),
+ _ => Err(OperationError::SubTypes(self, rhs)),
}
}
- pub fn mul(self, rhs: Value) -> Result<Value> {
+ pub fn mul(self, rhs: Value) -> Result<Value, OperationError> {
match self {
Value::Float(l) => match rhs {
Value::Float(r) => Ok(Value::Float(l * r)),
Value::Int(r) => Ok(Value::Float(l * r as f64)),
- _ => Err(anyhow!("Right operand can't be multiplied.")),
+ r => Err(OperationError::MulTypes(self, r)),
},
Value::Int(l) => match rhs {
Value::Float(r) => Ok(Value::Float(l as f64 * r)),
Value::Int(r) => Ok(Value::Int(l * r)),
- _ => Err(anyhow!("Right operand can't be multiplied.")),
+ r => Err(OperationError::MulTypes(self, r)),
},
- _ => Err(anyhow!("Left operand can't be multiplied.")),
+ _ => Err(OperationError::MulTypes(self, rhs)),
}
}
- pub fn div(self, rhs: Value) -> Result<Value> {
+ pub fn div(self, rhs: Value) -> Result<Value, OperationError> {
match self {
Value::Float(l) => match rhs {
Value::Float(r) => Ok(Value::Float(l / r)),
Value::Int(r) => Ok(Value::Float(l / r as f64)),
- _ => Err(anyhow!("Right operand can't be multiplied.")),
+ r => Err(OperationError::DivTypes(self, r)),
},
Value::Int(l) => match rhs {
Value::Float(r) => Ok(Value::Float(l as f64 / r)),
Value::Int(r) => Ok(Value::Float(l as f64 / r as f64)),
- _ => Err(anyhow!("Right operand can't be multiplied.")),
+ r => Err(OperationError::DivTypes(self, r)),
},
- _ => Err(anyhow!("Left operand can't be multiplied.")),
+ _ => Err(OperationError::DivTypes(self, rhs)),
}
}
- pub fn eq(self, rhs: Value) -> Result<Value> {
+ pub fn eq(self, rhs: Value) -> Result<Value, OperationError> {
match self {
Value::Str(l) => match rhs {
Value::Str(r) => Ok(Value::Bool(l == r)),
@@ -109,7 +109,7 @@ impl Value {
}
}
- pub fn neq(self, rhs: Value) -> Result<Value> {
+ pub fn neq(self, rhs: Value) -> Result<Value, OperationError> {
if let Ok(Value::Bool(value)) = self.eq(rhs) {
Ok(Value::Bool(!value))
} else {
@@ -117,50 +117,63 @@ impl Value {
}
}
- pub fn gt(self, rhs: Value) -> Result<Value> {
+ pub fn gt(self, rhs: Value) -> Result<Value, OperationError> {
match self {
Value::Float(r) => match rhs {
Value::Float(l) => Ok(Value::Bool(r > l)),
Value::Int(l) => Ok(Value::Bool(r > l as f64)),
- _ => Err(anyhow!("Right operand can't be compared.")),
+ r => Err(OperationError::CompareTypes(self, r)),
},
Value::Int(r) => match rhs {
Value::Float(l) => Ok(Value::Bool(r as f64 > l)),
Value::Int(l) => Ok(Value::Bool(r > l)),
- _ => Err(anyhow!("Right operand can't be compared.")),
+ r => Err(OperationError::CompareTypes(self, r)),
},
- _ => Err(anyhow!("Left operand can't be compared.")),
+ _ => Err(OperationError::CompareTypes(self, rhs)),
}
}
- pub fn gte(self, rhs: Value) -> Result<Value> {
+ pub fn gte(self, rhs: Value) -> Result<Value, OperationError> {
match self {
Value::Float(r) => match rhs {
Value::Float(l) => Ok(Value::Bool(r >= l)),
Value::Int(l) => Ok(Value::Bool(r >= l as f64)),
- _ => Err(anyhow!("Right operand can't be compared.")),
+ r => Err(OperationError::CompareTypes(self, r)),
},
Value::Int(r) => match rhs {
Value::Float(l) => Ok(Value::Bool(r as f64 >= l)),
Value::Int(l) => Ok(Value::Bool(r >= l)),
- _ => Err(anyhow!("Right operand can't be compared.")),
+ r => Err(OperationError::CompareTypes(self, r)),
},
- _ => Err(anyhow!("Left operand can't be compared.")),
+ _ => Err(OperationError::CompareTypes(self, rhs)),
}
}
- pub fn neg(self) -> Result<Value> {
+ pub fn neg(self) -> Result<Value, OperationError> {
match self {
Value::Float(float) => Ok(Value::Float(-float)),
Value::Int(int) => Ok(Value::Int(-int)),
- _ => Err(anyhow!("Can't negate value.")),
+ _ => Err(OperationError::NegType(self)),
}
}
- pub fn not(self) -> Result<Value> {
+ pub fn not(self) -> Result<Value, OperationError> {
match self {
Value::Bool(bool) => Ok(Value::Bool(bool)),
- _ => Err(anyhow!("Can't flip non-bool value.")),
+ _ => Err(OperationError::NotType(self)),
+ }
+ }
+}
+
+impl Value {
+ pub fn type_name(&self) -> &'static str {
+ match self {
+ Value::Str(_) => "Str",
+ Value::Float(_) => "Float",
+ Value::Int(_) => "Int",
+ Value::Bool(_) => "Bool",
+ Value::Fn(_) => "Fn",
+ Value::Void => "Void",
}
}
}
@@ -177,3 +190,21 @@ impl Display for Value {
}
}
}
+
+#[derive(Error, Debug)]
+pub enum OperationError {
+ #[error("Can't add value '{0}' of type '{}' to value '{1}' of type '{}'.", .0.type_name(), .1.type_name())]
+ AddTypes(Value, Value),
+ #[error("Can't subtract value '{1}' of type '{}' from value '{0}' of type '{}'.", .1.type_name(), .0.type_name())]
+ SubTypes(Value, Value),
+ #[error("Can't multiply value '{0}' of type '{}' with value '{1}' of type '{}'.", .0.type_name(), .1.type_name())]
+ MulTypes(Value, Value),
+ #[error("Can't divide value '{0}' of type '{}' by value '{1}' of type '{}'.", .0.type_name(), .1.type_name())]
+ DivTypes(Value, Value),
+ #[error("Can't compare value '{0}' of type '{}' with value '{1}' of type '{}'.", .0.type_name(), .1.type_name())]
+ CompareTypes(Value, Value),
+ #[error("Can't negate value '{0}' of type '{}'.", .0.type_name())]
+ NegType(Value),
+ #[error("Can't flip value '{0}' of type '{}'.", .0.type_name())]
+ NotType(Value),
+}
diff --git a/src/interpret/walker.rs b/src/interpret/walker.rs
index 7684747..db37a26 100644
--- a/src/interpret/walker.rs
+++ b/src/interpret/walker.rs
@@ -2,13 +2,16 @@ use std::{cell::RefCell, rc::Rc};
use crate::parse::ast::{
expression::Expression,
- nodes::{BinaryOperator as BinOp, BlockNode, Literal, UnaryOperator as UnOp},
+ nodes::{BinaryOperator as BinOp, BlockNode, Identifier, Literal, UnaryOperator as UnOp},
statement::Statement,
Program,
};
-use anyhow::{anyhow, Result};
+use thiserror::Error;
-use super::{scope::Scope, value::Value};
+use super::{
+ scope::Scope,
+ value::{OperationError, Value},
+};
pub struct Walker {
scope: Scope,
@@ -28,7 +31,7 @@ impl Walker {
}
}
- fn walk_statement(&mut self, statement: &Statement) -> Result<Option<Value>> {
+ fn walk_statement(&mut self, statement: &Statement) -> Result<Option<Value>, WalkerError> {
let result = match statement {
Statement::Expression(node) => {
self.walk_expression(node)?;
@@ -40,12 +43,23 @@ impl Walker {
None
}
Statement::Return(node) => Some(self.walk_expression(node)?),
+ Statement::Break(node) => {
+ let returned = if let Some(expression) = node {
+ Some(self.walk_expression(expression)?)
+ } else {
+ None
+ };
+ // If there's a loop above us it will catch this error.
+ return Err(WalkerError::LoopBreak(returned));
+ }
+ // Same here.
+ Statement::Continue => return Err(WalkerError::LoopContinue),
};
Ok(result)
}
- pub fn walk_expression(&mut self, node: &Expression) -> Result<Value> {
+ pub fn walk_expression(&mut self, node: &Expression) -> Result<Value, WalkerError> {
match node {
Expression::Binary { left, op, right } => {
// Assignment
@@ -79,7 +93,8 @@ impl Walker {
// No structure access yet.
BinOp::Dot => todo!(),
_ => unreachable!(),
- }?;
+ }
+ .map_err(WalkerError::OperationError)?;
Ok(new_value)
}
@@ -87,9 +102,10 @@ impl Walker {
let value = self.walk_expression(right)?;
let new_value = match op {
- UnOp::Minus => value.neg()?,
- UnOp::Not => value.not()?,
- };
+ UnOp::Minus => value.neg(),
+ UnOp::Not => value.not(),
+ }
+ .map_err(WalkerError::OperationError)?;
Ok(new_value)
}
@@ -116,9 +132,7 @@ impl Walker {
return self.walk_block(&conditional.block);
}
} else {
- return Err(anyhow!(
- "If and Elif expressions can only take boolean values as conditions."
- ));
+ return Err(WalkerError::WrongIfConditionType);
}
}
@@ -128,17 +142,53 @@ impl Walker {
Ok(Value::Void)
}
}
+ Expression::Loop(loop_node) => {
+ if let Some(condition) = &loop_node.condition {
+ loop {
+ if let Value::Bool(should_repeat) = self.walk_expression(condition)? {
+ if should_repeat {
+ match self.walk_block(&loop_node.body) {
+ Err(WalkerError::LoopBreak(loop_value)) => {
+ return Ok(loop_value.unwrap_or(Value::Void));
+ }
+ // Do nothing for continue and continue looping of course, you dummy.
+ Err(WalkerError::LoopContinue) => {}
+ // This is probably an actual error.
+ Err(x) => return Err(x),
+ _ => {}
+ }
+ } else {
+ return Ok(Value::Void);
+ }
+ } else {
+ return Err(WalkerError::WrongLoopConditionType);
+ }
+ }
+ } else {
+ // Same as above.
+ loop {
+ match self.walk_block(&loop_node.body) {
+ Err(WalkerError::LoopBreak(loop_value)) => {
+ break Ok(loop_value.unwrap_or(Value::Void));
+ }
+ Err(WalkerError::LoopContinue) => {}
+ Err(x) => return Err(x),
+ _ => {}
+ }
+ }
+ }
+ }
Expression::Identifier(ident) => {
if let Some(value) = self.scope.get_var(ident) {
Ok(value)
} else {
- Err(anyhow!("Unknown identifier: {}.", ident))
+ Err(WalkerError::UnknownIdentifier(ident.clone()))
}
}
}
}
- fn walk_block(&mut self, block: &BlockNode) -> Result<Value> {
+ fn walk_block(&mut self, block: &BlockNode) -> Result<Value, WalkerError> {
self.scope.nest();
for statement in block.statements.iter() {
@@ -156,3 +206,22 @@ impl Walker {
result
}
}
+
+// TODO: Add source maps to the errors.
+#[derive(Error, Debug)]
+pub enum WalkerError {
+ #[error("Unknown identifier '{0}'")]
+ UnknownIdentifier(Identifier),
+ #[error("Loop expressions can only take boolean values as conditions.")]
+ WrongLoopConditionType,
+ #[error("If and Elif expressions can only take boolean values as conditions.")]
+ WrongIfConditionType,
+ #[error(transparent)]
+ OperationError(OperationError),
+ // These are used for loop control flow and are only errors
+ // if continue and break statements are used outside of loops.
+ #[error("Continue statements are only valid inside loops.")]
+ LoopContinue,
+ #[error("Break statements are only valid inside loops.")]
+ LoopBreak(Option<Value>),
+}