Pass all tests
This commit is contained in:
parent
199e1c9184
commit
fed119f38b
@ -80,7 +80,7 @@ impl AbstractTree for Assignment {
|
|||||||
context.set_value(self.identifier, value)?;
|
context.set_value(self.identifier, value)?;
|
||||||
}
|
}
|
||||||
AssignmentOperator::AddAssign => {
|
AssignmentOperator::AddAssign => {
|
||||||
if let Some(previous_value) = context.use_value(&self.identifier)? {
|
if let Some(previous_value) = context.get_value(&self.identifier)? {
|
||||||
let new_value = previous_value.add(&value)?;
|
let new_value = previous_value.add(&value)?;
|
||||||
|
|
||||||
context.set_value(self.identifier, new_value)?;
|
context.set_value(self.identifier, new_value)?;
|
||||||
@ -91,7 +91,7 @@ impl AbstractTree for Assignment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
AssignmentOperator::SubAssign => {
|
AssignmentOperator::SubAssign => {
|
||||||
if let Some(previous_value) = context.use_value(&self.identifier)? {
|
if let Some(previous_value) = context.get_value(&self.identifier)? {
|
||||||
let new_value = previous_value.subtract(&value)?;
|
let new_value = previous_value.subtract(&value)?;
|
||||||
|
|
||||||
context.set_value(self.identifier, new_value)?;
|
context.set_value(self.identifier, new_value)?;
|
||||||
@ -131,7 +131,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.use_value(&Identifier::new("foobar")),
|
context.get_value(&Identifier::new("foobar")),
|
||||||
Ok(Some(Value::integer(42)))
|
Ok(Some(Value::integer(42)))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -154,7 +154,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.use_value(&Identifier::new("foobar")),
|
context.get_value(&Identifier::new("foobar")),
|
||||||
Ok(Some(Value::integer(42)))
|
Ok(Some(Value::integer(42)))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -177,7 +177,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.use_value(&Identifier::new("foobar")),
|
context.get_value(&Identifier::new("foobar")),
|
||||||
Ok(Some(Value::integer(42)))
|
Ok(Some(Value::integer(42)))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -41,12 +41,7 @@ impl AbstractTree for Block {
|
|||||||
let mut previous = Action::None;
|
let mut previous = Action::None;
|
||||||
|
|
||||||
for statement in self.statements {
|
for statement in self.statements {
|
||||||
let action = statement.node.run(_context)?;
|
previous = statement.node.run(_context)?;
|
||||||
previous = match action {
|
|
||||||
Action::Return(value) => Action::Return(value),
|
|
||||||
Action::None => Action::None,
|
|
||||||
Action::Break => return Ok(action),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(previous)
|
Ok(previous)
|
||||||
|
@ -51,8 +51,9 @@ impl AbstractTree for FunctionCall {
|
|||||||
arguments.push(value);
|
arguments.push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
let function_context = Context::inherit_data_from(context)?;
|
let function_context = Context::new();
|
||||||
|
|
||||||
|
function_context.inherit_data_from(&context)?;
|
||||||
function.clone().call(arguments, function_context)
|
function.clone().call(arguments, function_context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ impl Identifier {
|
|||||||
|
|
||||||
impl AbstractTree for Identifier {
|
impl AbstractTree for Identifier {
|
||||||
fn expected_type(&self, context: &Context) -> Result<Type, ValidationError> {
|
fn expected_type(&self, context: &Context) -> Result<Type, ValidationError> {
|
||||||
if let Some(r#type) = context.use_type(self)? {
|
if let Some(r#type) = context.get_type(self)? {
|
||||||
Ok(r#type)
|
Ok(r#type)
|
||||||
} else {
|
} else {
|
||||||
Err(ValidationError::VariableNotFound(self.clone()))
|
Err(ValidationError::VariableNotFound(self.clone()))
|
||||||
@ -33,7 +33,7 @@ impl AbstractTree for Identifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn validate(&self, context: &Context) -> Result<(), ValidationError> {
|
fn validate(&self, context: &Context) -> Result<(), ValidationError> {
|
||||||
if context.add_allowance(self)? {
|
if context.contains(self)? {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(ValidationError::VariableNotFound(self.clone()))
|
Err(ValidationError::VariableNotFound(self.clone()))
|
||||||
@ -41,7 +41,7 @@ impl AbstractTree for Identifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
|
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
|
||||||
let return_action = context.use_value(&self)?.map(|value| Action::Return(value));
|
let return_action = context.get_value(&self)?.map(|value| Action::Return(value));
|
||||||
|
|
||||||
if let Some(action) = return_action {
|
if let Some(action) = return_action {
|
||||||
Ok(action)
|
Ok(action)
|
||||||
|
@ -32,8 +32,13 @@ impl AbstractTree for IfElse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn validate(&self, context: &Context) -> Result<(), ValidationError> {
|
fn validate(&self, context: &Context) -> Result<(), ValidationError> {
|
||||||
|
self.if_expression.node.validate(context)?;
|
||||||
|
self.if_block.validate(context)?;
|
||||||
|
|
||||||
if let Type::Boolean = self.if_expression.node.expected_type(context)? {
|
if let Type::Boolean = self.if_expression.node.expected_type(context)? {
|
||||||
if let Some(else_block) = &self.else_block {
|
if let Some(else_block) = &self.else_block {
|
||||||
|
else_block.validate(context)?;
|
||||||
|
|
||||||
let expected = self.if_block.expected_type(context)?;
|
let expected = self.if_block.expected_type(context)?;
|
||||||
let actual = else_block.expected_type(context)?;
|
let actual = else_block.expected_type(context)?;
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::Context,
|
context::Context,
|
||||||
error::{RuntimeError, ValidationError},
|
error::{RuntimeError, ValidationError},
|
||||||
@ -5,7 +7,7 @@ use crate::{
|
|||||||
|
|
||||||
use super::{AbstractTree, Action, Statement, Type, WithPosition};
|
use super::{AbstractTree, Action, Statement, Type, WithPosition};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Loop {
|
pub struct Loop {
|
||||||
statements: Vec<WithPosition<Statement>>,
|
statements: Vec<WithPosition<Statement>>,
|
||||||
}
|
}
|
||||||
@ -30,38 +32,92 @@ impl AbstractTree for Loop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||||
let mut index = 0;
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if index == self.statements.len() - 1 {
|
for statement in &self.statements {
|
||||||
index = 0;
|
let action = statement.node.clone().run(_context)?;
|
||||||
} else {
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let statement = self.statements[index].clone();
|
|
||||||
let action = statement.node.run(_context)?;
|
|
||||||
|
|
||||||
match action {
|
match action {
|
||||||
Action::Return(_) => {}
|
Action::Return(_) => {}
|
||||||
Action::None => {}
|
Action::None => {}
|
||||||
r#break => return Ok(r#break),
|
Action::Break => return Ok(Action::Break),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Loop {}
|
||||||
|
|
||||||
|
impl PartialEq for Loop {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.statements.eq(&other.statements)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Loop {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Loop {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.statements.cmp(&other.statements)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::{
|
||||||
|
abstract_tree::{
|
||||||
|
Assignment, AssignmentOperator, Block, Expression, Identifier, IfElse, Logic, ValueNode,
|
||||||
|
},
|
||||||
|
Value,
|
||||||
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_loop() {
|
fn basic_loop() {
|
||||||
let result = Loop {
|
let result = Loop::new(vec![Statement::Break.with_position((0, 0))]).run(&Context::new());
|
||||||
statements: vec![Statement::Break.with_position((0, 0))],
|
|
||||||
}
|
|
||||||
.run(&Context::new());
|
|
||||||
|
|
||||||
assert_eq!(result, Ok(Action::Break))
|
assert_eq!(result, Ok(Action::Break))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn complex_loop() {
|
||||||
|
let result = Block::new(vec![
|
||||||
|
Statement::Assignment(Assignment::new(
|
||||||
|
Identifier::new("i"),
|
||||||
|
None,
|
||||||
|
AssignmentOperator::Assign,
|
||||||
|
Statement::Expression(Expression::Value(ValueNode::Integer(1)))
|
||||||
|
.with_position((0, 0)),
|
||||||
|
))
|
||||||
|
.with_position((0, 0)),
|
||||||
|
Statement::Loop(Loop::new(vec![Statement::IfElse(IfElse::new(
|
||||||
|
Expression::Logic(Box::new(Logic::Greater(
|
||||||
|
Expression::Identifier(Identifier::new("i")).with_position((10, 11)),
|
||||||
|
Expression::Value(ValueNode::Integer(2)).with_position((14, 15)),
|
||||||
|
)))
|
||||||
|
.with_position((10, 15)),
|
||||||
|
Block::new(vec![Statement::Break.with_position((18, 24))]),
|
||||||
|
Some(Block::new(vec![Statement::Assignment(Assignment::new(
|
||||||
|
Identifier::new("i"),
|
||||||
|
None,
|
||||||
|
AssignmentOperator::AddAssign,
|
||||||
|
Statement::Expression(Expression::Value(ValueNode::Integer(1)))
|
||||||
|
.with_position((38, 39)),
|
||||||
|
))
|
||||||
|
.with_position((33, 39))])),
|
||||||
|
))
|
||||||
|
.with_position((0, 0))]))
|
||||||
|
.with_position((0, 0)),
|
||||||
|
Statement::Expression(Expression::Identifier(Identifier::new("i")))
|
||||||
|
.with_position((0, 0)),
|
||||||
|
])
|
||||||
|
.run(&Context::new());
|
||||||
|
|
||||||
|
assert_eq!(result, Ok(Action::Return(Value::integer(3))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,9 @@ impl AbstractTree for ValueNode {
|
|||||||
body,
|
body,
|
||||||
} = self
|
} = self
|
||||||
{
|
{
|
||||||
let function_context = Context::inherit_types_from(context)?;
|
let function_context = Context::new();
|
||||||
|
|
||||||
|
function_context.inherit_types_from(context)?;
|
||||||
|
|
||||||
for (identifier, r#type) in parameters {
|
for (identifier, r#type) in parameters {
|
||||||
function_context.set_type(identifier.clone(), r#type.node.clone())?;
|
function_context.set_type(identifier.clone(), r#type.node.clone())?;
|
||||||
|
158
src/context.rs
158
src/context.rs
@ -6,36 +6,13 @@ use std::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
abstract_tree::{Identifier, Type},
|
abstract_tree::{Identifier, Type},
|
||||||
error::RwLockPoisonError,
|
error::RwLockPoisonError,
|
||||||
value::{BuiltInFunction, ValueInner},
|
value::BuiltInFunction,
|
||||||
Value,
|
Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
inner: Arc<RwLock<BTreeMap<Identifier, (ValueData, UsageData)>>>,
|
inner: Arc<RwLock<BTreeMap<Identifier, ValueData>>>,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct UsageData(Arc<RwLock<UsageDataInner>>);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct UsageDataInner {
|
|
||||||
pub allowances: usize,
|
|
||||||
pub uses: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for UsageData {
|
|
||||||
fn default() -> Self {
|
|
||||||
UsageData(Arc::new(RwLock::new(UsageDataInner {
|
|
||||||
allowances: 0,
|
|
||||||
uses: 0,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum ValueData {
|
|
||||||
Type(Type),
|
|
||||||
Value(Value),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
@ -45,96 +22,47 @@ impl Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_data(data: BTreeMap<Identifier, (ValueData, UsageData)>) -> Self {
|
pub fn with_data(data: BTreeMap<Identifier, ValueData>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: Arc::new(RwLock::new(data)),
|
inner: Arc::new(RwLock::new(data)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inherit_types_from(other: &Context) -> Result<Self, RwLockPoisonError> {
|
pub fn inherit_types_from(&self, other: &Context) -> Result<(), RwLockPoisonError> {
|
||||||
let mut new_data = BTreeMap::new();
|
let mut self_data = self.inner.write()?;
|
||||||
|
|
||||||
for (identifier, (value_data, usage_data)) in other.inner.read()?.iter() {
|
for (identifier, value_data) in other.inner.read()?.iter() {
|
||||||
if let ValueData::Type(r#type) = value_data {
|
if let ValueData::Type(r#type) = value_data {
|
||||||
if let Type::Function { .. } = r#type {
|
if let Type::Function { .. } = r#type {
|
||||||
new_data.insert(identifier.clone(), (value_data.clone(), usage_data.clone()));
|
self_data.insert(identifier.clone(), value_data.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self::with_data(new_data))
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inherit_data_from(other: &Context) -> Result<Self, RwLockPoisonError> {
|
pub fn inherit_data_from(&self, other: &Context) -> Result<(), RwLockPoisonError> {
|
||||||
let mut new_data = BTreeMap::new();
|
let mut self_data = self.inner.write()?;
|
||||||
|
|
||||||
for (identifier, (value_data, usage_data)) in other.inner.read()?.iter() {
|
for (identifier, value_data) in other.inner.read()?.iter() {
|
||||||
if let ValueData::Type(r#type) = value_data {
|
self_data.insert(identifier.clone(), value_data.clone());
|
||||||
if let Type::Function { .. } = r#type {
|
|
||||||
new_data.insert(identifier.clone(), (value_data.clone(), usage_data.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let ValueData::Value(value) = value_data {
|
|
||||||
if let ValueInner::Function { .. } = value.inner().as_ref() {
|
|
||||||
new_data.insert(identifier.clone(), (value_data.clone(), usage_data.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self::with_data(new_data))
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_allowance(&self, identifier: &Identifier) -> Result<bool, RwLockPoisonError> {
|
pub fn contains(&self, identifier: &Identifier) -> Result<bool, RwLockPoisonError> {
|
||||||
if let Some((_, usage_data)) = self.inner.read()?.get(identifier) {
|
Ok(self.inner.read()?.contains_key(identifier))
|
||||||
usage_data.0.write()?.allowances += 1;
|
|
||||||
|
|
||||||
Ok(true)
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn use_data(
|
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, RwLockPoisonError> {
|
||||||
&self,
|
if let Some(value_data) = self.inner.read()?.get(identifier) {
|
||||||
identifier: &Identifier,
|
let r#type = match value_data {
|
||||||
) -> Result<Option<ValueData>, RwLockPoisonError> {
|
ValueData::Type(r#type) => r#type.clone(),
|
||||||
let should_remove =
|
ValueData::Value(value) => value.r#type(),
|
||||||
if let Some((value_data, usage_data)) = self.inner.read()?.get(identifier) {
|
|
||||||
let mut usage_data = usage_data.0.write()?;
|
|
||||||
|
|
||||||
log::trace!("Adding use for variable: {identifier}");
|
|
||||||
|
|
||||||
usage_data.uses += 1;
|
|
||||||
|
|
||||||
if usage_data.uses == usage_data.allowances {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
return Ok(Some(value_data.clone()));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if should_remove {
|
|
||||||
log::trace!("Removing varialble: {identifier}");
|
|
||||||
|
|
||||||
self.inner.write()?.remove(identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
let value_data = match identifier.as_str() {
|
|
||||||
"output" => ValueData::Value(BuiltInFunction::output()),
|
|
||||||
_ => return Ok(None),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(value_data))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn use_type(&self, identifier: &Identifier) -> Result<Option<Type>, RwLockPoisonError> {
|
|
||||||
if let Some((ValueData::Type(r#type), usage_data)) = self.inner.read()?.get(identifier) {
|
|
||||||
log::trace!("Adding use for variable: {identifier}");
|
|
||||||
|
|
||||||
usage_data.0.write()?.uses += 1;
|
|
||||||
|
|
||||||
return Ok(Some(r#type.clone()));
|
return Ok(Some(r#type.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,43 +74,23 @@ impl Context {
|
|||||||
Ok(Some(r#type))
|
Ok(Some(r#type))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn use_value(&self, identifier: &Identifier) -> Result<Option<Value>, RwLockPoisonError> {
|
pub fn get_value(&self, identifier: &Identifier) -> Result<Option<Value>, RwLockPoisonError> {
|
||||||
let should_remove = if let Some((ValueData::Value(value), usage_data)) =
|
if let Some(ValueData::Value(value)) = self.inner.read()?.get(identifier) {
|
||||||
self.inner.read()?.get(identifier)
|
Ok(Some(value.clone()))
|
||||||
{
|
|
||||||
let mut usage_data = usage_data.0.write()?;
|
|
||||||
|
|
||||||
log::trace!("Adding use for variable: {identifier}");
|
|
||||||
|
|
||||||
usage_data.uses += 1;
|
|
||||||
|
|
||||||
if usage_data.uses == usage_data.allowances {
|
|
||||||
true
|
|
||||||
} else {
|
} else {
|
||||||
return Ok(Some(value.clone()));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
if should_remove {
|
|
||||||
log::trace!("Removing varialble: {identifier}");
|
|
||||||
|
|
||||||
self.inner.write()?.remove(identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
let value = match identifier.as_str() {
|
let value = match identifier.as_str() {
|
||||||
"output" => BuiltInFunction::output(),
|
"output" => Value::built_in_function(BuiltInFunction::Output),
|
||||||
_ => return Ok(None),
|
_ => return Ok(None),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Some(value))
|
Ok(Some(value))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_type(&self, identifier: Identifier, r#type: Type) -> Result<(), RwLockPoisonError> {
|
pub fn set_type(&self, identifier: Identifier, r#type: Type) -> Result<(), RwLockPoisonError> {
|
||||||
self.inner
|
self.inner
|
||||||
.write()?
|
.write()?
|
||||||
.insert(identifier, (ValueData::Type(r#type), UsageData::default()));
|
.insert(identifier, ValueData::Type(r#type));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -190,12 +98,14 @@ impl Context {
|
|||||||
pub fn set_value(&self, identifier: Identifier, value: Value) -> Result<(), RwLockPoisonError> {
|
pub fn set_value(&self, identifier: Identifier, value: Value) -> Result<(), RwLockPoisonError> {
|
||||||
let mut inner = self.inner.write()?;
|
let mut inner = self.inner.write()?;
|
||||||
|
|
||||||
if let Some((_value_data, usage_data)) = inner.remove(&identifier) {
|
inner.insert(identifier, ValueData::Value(value));
|
||||||
inner.insert(identifier, (ValueData::Value(value), usage_data));
|
|
||||||
} else {
|
|
||||||
inner.insert(identifier, (ValueData::Value(value), UsageData::default()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum ValueData {
|
||||||
|
Type(Type),
|
||||||
|
Value(Value),
|
||||||
|
}
|
||||||
|
77
src/error.rs
77
src/error.rs
@ -1,6 +1,6 @@
|
|||||||
use std::sync::PoisonError;
|
use std::{ops::Range, sync::PoisonError};
|
||||||
|
|
||||||
use ariadne::{Color, Label, Report, ReportKind};
|
use ariadne::{Label, ReportBuilder};
|
||||||
use chumsky::{prelude::Rich, span::Span};
|
use chumsky::{prelude::Rich, span::Span};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -18,7 +18,10 @@ pub enum Error {
|
|||||||
expected: String,
|
expected: String,
|
||||||
span: (usize, usize),
|
span: (usize, usize),
|
||||||
},
|
},
|
||||||
Runtime(RuntimeError),
|
Runtime {
|
||||||
|
error: RuntimeError,
|
||||||
|
position: SourcePosition,
|
||||||
|
},
|
||||||
Validation {
|
Validation {
|
||||||
error: ValidationError,
|
error: ValidationError,
|
||||||
position: SourcePosition,
|
position: SourcePosition,
|
||||||
@ -26,7 +29,10 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
pub fn report(&self) -> Report {
|
pub fn build_report(
|
||||||
|
self,
|
||||||
|
mut builder: ReportBuilder<'_, Range<usize>>,
|
||||||
|
) -> ReportBuilder<'_, Range<usize>> {
|
||||||
match self {
|
match self {
|
||||||
Error::Parse { expected, span } => {
|
Error::Parse { expected, span } => {
|
||||||
let message = match expected.as_str() {
|
let message = match expected.as_str() {
|
||||||
@ -34,9 +40,7 @@ impl Error {
|
|||||||
expected => format!("Expected {expected}."),
|
expected => format!("Expected {expected}."),
|
||||||
};
|
};
|
||||||
|
|
||||||
Report::build(ReportKind::Custom("Lexing Error", Color::White), (), span.0)
|
builder.add_label(Label::new(span.0..span.1).with_message(message));
|
||||||
.with_label(Label::new(span.0..span.1).with_message(message))
|
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
Error::Lex { expected, span } => {
|
Error::Lex { expected, span } => {
|
||||||
let message = match expected.as_str() {
|
let message = match expected.as_str() {
|
||||||
@ -44,30 +48,27 @@ impl Error {
|
|||||||
expected => format!("Expected {expected}."),
|
expected => format!("Expected {expected}."),
|
||||||
};
|
};
|
||||||
|
|
||||||
Report::build(ReportKind::Custom("Lexing Error", Color::White), (), span.0)
|
builder.add_label(Label::new(span.0..span.1).with_message(message));
|
||||||
.with_label(Label::new(span.0..span.1).with_message(message))
|
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
Error::Runtime(_) => todo!(),
|
Error::Runtime { error, position } => match error {
|
||||||
Error::Validation { error, position } => {
|
RuntimeError::RwLockPoison(_) => todo!(),
|
||||||
let mut report = Report::build(
|
RuntimeError::ValidationFailure(validation_error) => {
|
||||||
ReportKind::Custom("Validation Error: The code was not run.", Color::White),
|
builder =
|
||||||
(),
|
Error::Validation {
|
||||||
0,
|
error: validation_error,
|
||||||
)
|
position,
|
||||||
.with_label(
|
}
|
||||||
Label::new(position.0..position.1).with_message("Error found in this item."),
|
.build_report(builder.with_note(
|
||||||
);
|
"The interpreter failed to catch this error during validation.",
|
||||||
|
));
|
||||||
match error {
|
}
|
||||||
|
},
|
||||||
|
Error::Validation { error, position } => match error {
|
||||||
ValidationError::ExpectedBoolean => {
|
ValidationError::ExpectedBoolean => {
|
||||||
report =
|
builder.add_label(Label::new(0..0).with_message("Expected boolean."));
|
||||||
report.with_label(Label::new(0..0).with_message("Expected boolean."));
|
|
||||||
}
|
}
|
||||||
ValidationError::ExpectedIntegerOrFloat => {
|
ValidationError::ExpectedIntegerOrFloat => {
|
||||||
report = report.with_label(
|
builder.add_label(Label::new(0..0).with_message("Expected integer or float."));
|
||||||
Label::new(0..0).with_message("Expected integer or float."),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
ValidationError::RwLockPoison(_) => todo!(),
|
ValidationError::RwLockPoison(_) => todo!(),
|
||||||
ValidationError::TypeCheck {
|
ValidationError::TypeCheck {
|
||||||
@ -77,7 +78,7 @@ impl Error {
|
|||||||
} => {
|
} => {
|
||||||
let TypeConflict { actual, expected } = conflict;
|
let TypeConflict { actual, expected } = conflict;
|
||||||
|
|
||||||
report = report.with_labels([
|
builder.add_labels([
|
||||||
Label::new(expected_postion.0..expected_postion.1)
|
Label::new(expected_postion.0..expected_postion.1)
|
||||||
.with_message(format!("Type {expected} established here.")),
|
.with_message(format!("Type {expected} established here.")),
|
||||||
Label::new(actual_position.0..actual_position.1)
|
Label::new(actual_position.0..actual_position.1)
|
||||||
@ -85,21 +86,21 @@ impl Error {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
ValidationError::VariableNotFound(identifier) => {
|
ValidationError::VariableNotFound(identifier) => {
|
||||||
report = report
|
builder.add_label(
|
||||||
.with_label(Label::new(0..0).with_message(format!(
|
Label::new(position.0..position.1)
|
||||||
"The variable {identifier} does not exist."
|
.with_message(format!("The variable {identifier} does not exist."))
|
||||||
)));
|
.with_priority(1),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ValidationError::CannotIndex(_) => todo!(),
|
ValidationError::CannotIndex(_) => todo!(),
|
||||||
ValidationError::CannotIndexWith(_, _) => todo!(),
|
ValidationError::CannotIndexWith(_, _) => todo!(),
|
||||||
ValidationError::InterpreterExpectedReturn => todo!(),
|
ValidationError::InterpreterExpectedReturn => todo!(),
|
||||||
ValidationError::ExpectedFunction => todo!(),
|
ValidationError::ExpectedFunction => todo!(),
|
||||||
ValidationError::ExpectedValue => todo!(),
|
ValidationError::ExpectedValue => todo!(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
report.finish()
|
builder
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,12 +122,6 @@ impl<'src> From<Rich<'_, Token<'src>>> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RuntimeError> for Error {
|
|
||||||
fn from(error: RuntimeError) -> Self {
|
|
||||||
Error::Runtime(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum RuntimeError {
|
pub enum RuntimeError {
|
||||||
RwLockPoison(RwLockPoisonError),
|
RwLockPoison(RwLockPoisonError),
|
||||||
|
@ -56,7 +56,12 @@ impl Interpreter {
|
|||||||
Action::Return(value) => Some(value),
|
Action::Return(value) => Some(value),
|
||||||
Action::None => continue,
|
Action::None => continue,
|
||||||
},
|
},
|
||||||
Err(runtime_error) => return Err(vec![Error::Runtime(runtime_error)]),
|
Err(runtime_error) => {
|
||||||
|
return Err(vec![Error::Runtime {
|
||||||
|
error: runtime_error,
|
||||||
|
position: statement.position,
|
||||||
|
}])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
src/main.rs
12
src/main.rs
@ -1,6 +1,6 @@
|
|||||||
//! Command line interface for the dust programming language.
|
//! Command line interface for the dust programming language.
|
||||||
|
|
||||||
use ariadne::Source;
|
use ariadne::{Color, Report, ReportKind, Source};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
|
||||||
@ -53,9 +53,17 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(errors) => {
|
Err(errors) => {
|
||||||
|
let mut report_builder =
|
||||||
|
Report::build(ReportKind::Custom("Dust Error", Color::White), (), 5);
|
||||||
|
|
||||||
for error in errors {
|
for error in errors {
|
||||||
error.report().eprint(Source::from(&source)).unwrap();
|
report_builder = error.build_report(report_builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
report_builder
|
||||||
|
.finish()
|
||||||
|
.eprint(Source::from(source))
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -656,6 +656,26 @@ mod tests {
|
|||||||
),)
|
),)
|
||||||
.with_position((7, 9))]),)
|
.with_position((7, 9))]),)
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse(&lex("loop { if i > 2 { break } else { i += 1 } }").unwrap()).unwrap()[0].node,
|
||||||
|
Statement::Loop(Loop::new(vec![Statement::IfElse(IfElse::new(
|
||||||
|
Expression::Logic(Box::new(Logic::Greater(
|
||||||
|
Expression::Identifier(Identifier::new("i")).with_position((10, 11)),
|
||||||
|
Expression::Value(ValueNode::Integer(2)).with_position((14, 15))
|
||||||
|
)))
|
||||||
|
.with_position((10, 15)),
|
||||||
|
Block::new(vec![Statement::Break.with_position((18, 24))]),
|
||||||
|
Some(Block::new(vec![Statement::Assignment(Assignment::new(
|
||||||
|
Identifier::new("i"),
|
||||||
|
None,
|
||||||
|
AssignmentOperator::AddAssign,
|
||||||
|
Statement::Expression(Expression::Value(ValueNode::Integer(1)))
|
||||||
|
.with_position((38, 39))
|
||||||
|
))
|
||||||
|
.with_position((33, 39))]))
|
||||||
|
),)
|
||||||
|
.with_position((7, 42))]))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -141,8 +141,8 @@ fn map_type_errors() {
|
|||||||
actual: Type::String,
|
actual: Type::String,
|
||||||
expected: Type::Boolean
|
expected: Type::Boolean
|
||||||
},
|
},
|
||||||
actual_position: (0, 0).into(),
|
actual_position: (15, 20).into(),
|
||||||
expected_position: (0, 0).into(),
|
expected_position: (8, 13).into(),
|
||||||
},
|
},
|
||||||
position: (0, 22).into()
|
position: (0, 22).into()
|
||||||
}])
|
}])
|
||||||
|
@ -30,8 +30,8 @@ fn set_variable_with_type_error() {
|
|||||||
actual: Type::Boolean,
|
actual: Type::Boolean,
|
||||||
expected: Type::String
|
expected: Type::String
|
||||||
},
|
},
|
||||||
actual_position: (0, 0).into(),
|
actual_position: (14, 18).into(),
|
||||||
expected_position: (0, 0).into()
|
expected_position: (8, 12).into()
|
||||||
},
|
},
|
||||||
position: (0, 18).into()
|
position: (0, 18).into()
|
||||||
}])
|
}])
|
||||||
@ -43,13 +43,13 @@ fn function_variable() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
interpret("foobar = (x: int): int { x }; foobar"),
|
interpret("foobar = (x: int): int { x }; foobar"),
|
||||||
Ok(Some(Value::function(
|
Ok(Some(Value::function(
|
||||||
vec![(Identifier::new("x"), Type::Integer.with_position((0, 0)))],
|
vec![(Identifier::new("x"), Type::Integer.with_position((13, 16)))],
|
||||||
Type::Integer.with_position((0, 0)),
|
Type::Integer.with_position((19, 23)),
|
||||||
Block::new(vec![Statement::Expression(Expression::Identifier(
|
Block::new(vec![Statement::Expression(Expression::Identifier(
|
||||||
Identifier::new("x")
|
Identifier::new("x")
|
||||||
))
|
))
|
||||||
.with_position((0, 0))])
|
.with_position((25, 26))])
|
||||||
.with_position((0, 0))
|
.with_position((9, 28))
|
||||||
)))
|
)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user