From 2bd4ccb40ddbaef45fc647832d166c0bead87175 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 27 Nov 2023 17:53:12 -0500 Subject: [PATCH] Implement type checking --- examples/async.ds | 2 +- examples/yield.ds | 3 +- src/abstract_tree/assignment.rs | 12 +- src/abstract_tree/block.rs | 4 +- src/abstract_tree/built_in_function.rs | 6 +- src/abstract_tree/for.rs | 2 + src/abstract_tree/type.rs | 18 +++ src/abstract_tree/value_node.rs | 85 +++++++------- src/error.rs | 18 ++- src/evaluator.rs | 14 ++- src/lib.rs | 2 +- src/value/function.rs | 2 - src/value/mod.rs | 18 ++- src/value/value_type.rs | 152 ------------------------- tree-sitter-dust/grammar.js | 2 +- 15 files changed, 113 insertions(+), 227 deletions(-) delete mode 100644 src/value/value_type.rs diff --git a/examples/async.ds b/examples/async.ds index ed313af..5e66524 100644 --- a/examples/async.ds +++ b/examples/async.ds @@ -1,6 +1,6 @@ (output "This will print first.") -create_random_numbers = |count| => { +create_random_numbers = |count | { numbers = []; while (length numbers) < count { diff --git a/examples/yield.ds b/examples/yield.ds index 5dd4406..8724be3 100644 --- a/examples/yield.ds +++ b/examples/yield.ds @@ -4,7 +4,7 @@ add_one = |numbers | { new_numbers = [] for number in numbers { - new_list += number + 1 + new_numbers += number + 1 } new_numbers @@ -13,4 +13,3 @@ add_one = |numbers | { foo = [1, 2, 3] -> (add_one) (assert_equal [2 3 4] foo) - diff --git a/src/abstract_tree/assignment.rs b/src/abstract_tree/assignment.rs index fa58e20..4bd2699 100644 --- a/src/abstract_tree/assignment.rs +++ b/src/abstract_tree/assignment.rs @@ -63,24 +63,24 @@ impl AbstractTree for Assignment { } fn run(&self, source: &str, context: &mut Map) -> Result { - let key = self.identifier.inner().clone(); + let key = self.identifier.inner(); let value = self.statement.run(source, context)?; let new_value = match self.operator { AssignmentOperator::PlusEqual => { - if let Some(mut previous_value) = context.variables()?.get(&key).cloned() { + if let Some(mut previous_value) = context.variables()?.get(key).cloned() { previous_value += value; previous_value } else { - Value::Empty + return Err(Error::VariableIdentifierNotFound(key.clone())); } } AssignmentOperator::MinusEqual => { - if let Some(mut previous_value) = context.variables()?.get(&key).cloned() { + if let Some(mut previous_value) = context.variables()?.get(key).cloned() { previous_value -= value; previous_value } else { - Value::Empty + return Err(Error::VariableIdentifierNotFound(key.clone())); } } AssignmentOperator::Equal => value, @@ -90,7 +90,7 @@ impl AbstractTree for Assignment { r#type.check(&new_value)?; } - context.variables_mut()?.insert(key, new_value); + context.variables_mut()?.insert(key.clone(), new_value); Ok(Value::Empty) } diff --git a/src/abstract_tree/block.rs b/src/abstract_tree/block.rs index 572076a..d050e05 100644 --- a/src/abstract_tree/block.rs +++ b/src/abstract_tree/block.rs @@ -4,7 +4,7 @@ use rayon::prelude::*; use serde::{Deserialize, Serialize}; use tree_sitter::Node; -use crate::{AbstractTree, Map, Result, Statement, Value}; +use crate::{AbstractTree, Error, Map, Result, Statement, Value}; #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] pub struct Block { @@ -14,7 +14,7 @@ pub struct Block { impl AbstractTree for Block { fn from_syntax_node(source: &str, node: Node) -> Result { - debug_assert_eq!("block", node.kind()); + Error::expect_syntax_node(source, "block", node)?; let first_child = node.child(0).unwrap(); let is_async = first_child.kind() == "async"; diff --git a/src/abstract_tree/built_in_function.rs b/src/abstract_tree/built_in_function.rs index 2d8ce99..6542f56 100644 --- a/src/abstract_tree/built_in_function.rs +++ b/src/abstract_tree/built_in_function.rs @@ -11,7 +11,7 @@ use reqwest::blocking::get; use serde::{Deserialize, Serialize}; use tree_sitter::Node; -use crate::{AbstractTree, Error, Expression, List, Map, Result, Table, Value, ValueType}; +use crate::{AbstractTree, Error, Expression, List, Map, Result, Table, Type, Value}; #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] pub enum BuiltInFunction { @@ -367,9 +367,9 @@ impl AbstractTree for BuiltInFunction { BuiltInFunction::Type(expression) => { let run_expression = expression.run(source, context); let value_type = if let Ok(value) = run_expression { - value.value_type() + value.r#type() } else if let Err(Error::VariableIdentifierNotFound(_)) = run_expression { - ValueType::Empty + Type::Any } else { return run_expression; }; diff --git a/src/abstract_tree/for.rs b/src/abstract_tree/for.rs index b48191b..7ca36f6 100644 --- a/src/abstract_tree/for.rs +++ b/src/abstract_tree/for.rs @@ -14,6 +14,8 @@ pub struct For { impl AbstractTree for For { fn from_syntax_node(source: &str, node: Node) -> Result { + Error::expect_syntax_node(source, "for", node)?; + let for_node = node.child(0).unwrap(); let is_async = match for_node.kind() { "for" => false, diff --git a/src/abstract_tree/type.rs b/src/abstract_tree/type.rs index a5509f2..bf38e4e 100644 --- a/src/abstract_tree/type.rs +++ b/src/abstract_tree/type.rs @@ -1,3 +1,5 @@ +use std::fmt::{self, Display, Formatter}; + use serde::{Deserialize, Serialize}; use tree_sitter::Node; @@ -89,3 +91,19 @@ impl AbstractTree for Type { Ok(Value::Empty) } } + +impl Display for Type { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Type::Any => write!(f, "any"), + Type::Boolean => write!(f, "bool"), + Type::Float => write!(f, "float"), + Type::Function => write!(f, "function"), + Type::Integer => write!(f, "integer"), + Type::List => write!(f, "list"), + Type::Map => write!(f, "map"), + Type::String => write!(f, "string"), + Type::Table => write!(f, "table"), + } + } +} diff --git a/src/abstract_tree/value_node.rs b/src/abstract_tree/value_node.rs index 40b2198..c001781 100644 --- a/src/abstract_tree/value_node.rs +++ b/src/abstract_tree/value_node.rs @@ -5,19 +5,23 @@ use tree_sitter::Node; use crate::{ AbstractTree, Error, Expression, Function, Identifier, List, Map, Result, Statement, Table, - Value, ValueType, + Value, }; #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] -pub struct ValueNode { - value_type: ValueType, - source: String, -} - -impl ValueNode { - pub fn new(value_type: ValueType, source: String) -> Self { - Self { value_type, source } - } +pub enum ValueNode { + Boolean(String), + Float(String), + Integer(String), + String(String), + List(Vec), + Empty, + Map(BTreeMap), + Table { + column_names: Vec, + rows: Box, + }, + Function(Function), } impl AbstractTree for ValueNode { @@ -25,12 +29,15 @@ impl AbstractTree for ValueNode { debug_assert_eq!("value", node.kind()); let child = node.child(0).unwrap(); - let value_type = match child.kind() { - "integer" => ValueType::Integer, - "float" => ValueType::Float, - "string" => ValueType::String, - "boolean" => ValueType::Boolean, - "empty" => ValueType::Empty, + let value_node = match child.kind() { + "boolean" => ValueNode::Boolean(source[child.byte_range()].to_string()), + "float" => ValueNode::Float(source[child.byte_range()].to_string()), + "integer" => ValueNode::Integer(source[child.byte_range()].to_string()), + "string" => { + let without_quotes = child.start_byte() + 1..child.end_byte() - 1; + + ValueNode::String(source[without_quotes].to_string()) + } "list" => { let mut expressions = Vec::new(); @@ -43,7 +50,7 @@ impl AbstractTree for ValueNode { } } - ValueType::List(expressions) + ValueNode::List(expressions) } "table" => { let identifier_list_node = child.child(1).unwrap(); @@ -63,7 +70,7 @@ impl AbstractTree for ValueNode { let expression_node = child.child(2).unwrap(); let expression = Expression::from_syntax_node(source, expression_node)?; - ValueType::Table { + ValueNode::Table { column_names, rows: Box::new(expression), } @@ -88,9 +95,9 @@ impl AbstractTree for ValueNode { } } - ValueType::Map(child_nodes) + ValueNode::Map(child_nodes) } - "function" => ValueType::Function(Function::from_syntax_node(source, child)?), + "function" => ValueNode::Function(Function::from_syntax_node(source, child)?), _ => { return Err(Error::UnexpectedSyntaxNode { expected: @@ -102,27 +109,19 @@ impl AbstractTree for ValueNode { } }; - Ok(ValueNode { - value_type, - source: source[child.byte_range()].to_string(), - }) + Ok(value_node) } fn run(&self, source: &str, context: &mut Map) -> Result { - let value = match &self.value_type { - ValueType::Any => todo!(), - ValueType::String => { - let without_quotes = &self.source[1..self.source.len() - 1]; + let value = match self { + ValueNode::Boolean(value_source) => Value::Boolean(value_source.parse().unwrap()), + ValueNode::Float(value_source) => Value::Float(value_source.parse().unwrap()), + ValueNode::Integer(value_source) => Value::Integer(value_source.parse().unwrap()), + ValueNode::String(value_source) => Value::String(value_source.parse().unwrap()), + ValueNode::List(expressions) => { + let mut values = Vec::with_capacity(expressions.len()); - Value::String(without_quotes.to_string()) - } - ValueType::Float => Value::Float(self.source.parse().unwrap()), - ValueType::Integer => Value::Integer(self.source.parse().unwrap()), - ValueType::Boolean => Value::Boolean(self.source.parse().unwrap()), - ValueType::List(nodes) => { - let mut values = Vec::with_capacity(nodes.len()); - - for node in nodes { + for node in expressions { let value = node.run(source, context)?; values.push(value); @@ -130,15 +129,15 @@ impl AbstractTree for ValueNode { Value::List(List::with_items(values)) } - ValueType::Empty => Value::Empty, - ValueType::Map(nodes) => { + ValueNode::Empty => Value::Empty, + ValueNode::Map(key_statement_pairs) => { let map = Map::new(); { let mut variables = map.variables_mut()?; - for (key, node) in nodes { - let value = node.run(source, context)?; + for (key, statement) in key_statement_pairs { + let value = statement.run(source, context)?; variables.insert(key.clone(), value); } @@ -146,7 +145,7 @@ impl AbstractTree for ValueNode { Value::Map(map) } - ValueType::Table { + ValueNode::Table { column_names, rows: row_expression, } => { @@ -172,7 +171,7 @@ impl AbstractTree for ValueNode { Value::Table(table) } - ValueType::Function(function) => Value::Function(function.clone()), + ValueNode::Function(function) => Value::Function(function.clone()), }; Ok(value) diff --git a/src/error.rs b/src/error.rs index 6e52772..536d415 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,7 +3,7 @@ //! To deal with errors from dependencies, either create a new error variant //! or use the ToolFailure variant if the error can only occur inside a tool. -use tree_sitter::Node; +use tree_sitter::{Node, Point}; use crate::{value::Value, Identifier}; @@ -16,7 +16,7 @@ pub enum Error { UnexpectedSyntaxNode { expected: &'static str, actual: &'static str, - location: tree_sitter::Point, + location: Point, relevant_source: String, }, @@ -127,12 +127,23 @@ pub enum Error { /// A custom error explained by its message. CustomMessage(String), + + /// Invalid user input. + Syntax { + source: String, + location: Point, + }, } impl Error { pub fn expect_syntax_node(source: &str, expected: &'static str, actual: Node) -> Result<()> { if expected == actual.kind() { Ok(()) + } else if actual.is_error() { + Err(Error::Syntax { + source: source[actual.byte_range()].to_string(), + location: actual.start_position(), + }) } else { Err(Error::UnexpectedSyntaxNode { expected, @@ -337,6 +348,9 @@ impl fmt::Display for Error { ), ToolFailure(message) => write!(f, "{message}"), CustomMessage(message) => write!(f, "{message}"), + Syntax { source, location } => { + write!(f, "Syntax error at {location}, this is not valid: {source}") + } } } } diff --git a/src/evaluator.rs b/src/evaluator.rs index 9335c56..41d8ba8 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -85,16 +85,18 @@ impl<'c, 's> Evaluator<'c, 's> { } pub fn run(self) -> Result { - let mut cursor = self.syntax_tree.walk(); - let root_node = cursor.node(); - let mut prev_result = Ok(Value::Empty); + let root_node = self.syntax_tree.root_node(); - for statement_node in root_node.children(&mut cursor) { + let mut prev_result = Value::Empty; + + for index in 0..root_node.child_count() { + let statement_node = root_node.child(index).unwrap(); let statement = Statement::from_syntax_node(self.source, statement_node)?; - prev_result = statement.run(self.source, self.context); + + prev_result = statement.run(self.source, self.context)?; } - prev_result + Ok(prev_result) } pub fn syntax_tree(&self) -> String { diff --git a/src/lib.rs b/src/lib.rs index 0411c27..e2b7fea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ pub use crate::{ abstract_tree::*, error::*, evaluator::*, - value::{function::Function, list::List, map::Map, table::Table, value_type::ValueType, Value}, + value::{function::Function, list::List, map::Map, table::Table, Value}, }; mod abstract_tree; diff --git a/src/value/function.rs b/src/value/function.rs index 4b6a604..cd4bb49 100644 --- a/src/value/function.rs +++ b/src/value/function.rs @@ -56,8 +56,6 @@ impl AbstractTree for Function { } fn run(&self, source: &str, context: &mut Map) -> Result { - println!("{self}"); - let return_value = self.body.run(source, context)?; if let Some(r#type) = &self.return_type { diff --git a/src/value/mod.rs b/src/value/mod.rs index 3c1e1e9..656b21e 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -1,7 +1,7 @@ //! Types that represent runtime values. use crate::{ error::{Error, Result}, - Function, List, Map, Table, Type, ValueType, + Function, List, Map, Table, Type, }; use serde::{ @@ -22,7 +22,6 @@ pub mod function; pub mod list; pub mod map; pub mod table; -pub mod value_type; /// Dust value representation. /// @@ -58,10 +57,6 @@ impl Value { } } - pub fn value_type(&self) -> ValueType { - ValueType::from(self) - } - pub fn is_table(&self) -> bool { matches!(self, Value::Table(_)) } @@ -348,6 +343,17 @@ impl SubAssign for Value { (Value::Integer(left), Value::Integer(right)) => *left -= right, (Value::Float(left), Value::Float(right)) => *left -= right, (Value::Float(left), Value::Integer(right)) => *left -= right as f64, + (Value::List(list), value) => { + let index_to_remove = list + .items() + .iter() + .enumerate() + .find_map(|(i, list_value)| if list_value == &value { Some(i) } else { None }); + + if let Some(index) = index_to_remove { + list.items_mut().remove(index); + } + } _ => {} } } diff --git a/src/value/value_type.rs b/src/value/value_type.rs deleted file mode 100644 index b245e85..0000000 --- a/src/value/value_type.rs +++ /dev/null @@ -1,152 +0,0 @@ -use std::{ - collections::BTreeMap, - fmt::{self, Debug, Display, Formatter}, -}; - -use serde::{Deserialize, Serialize}; - -use crate::{value_node::ValueNode, Expression, Function, Identifier, Statement, Value}; - -/// The type of a `Value`. -#[derive(Clone, Serialize, Deserialize, PartialOrd, Ord)] -pub enum ValueType { - Any, - String, - Float, - Integer, - Boolean, - List(Vec), - Empty, - Map(BTreeMap), - Table { - column_names: Vec, - rows: Box, - }, - Function(Function), -} - -impl Eq for ValueType {} - -impl PartialEq for ValueType { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (ValueType::Any, _) => true, - (_, ValueType::Any) => true, - (ValueType::String, ValueType::String) => true, - (ValueType::Float, ValueType::Float) => true, - (ValueType::Integer, ValueType::Integer) => true, - (ValueType::Boolean, ValueType::Boolean) => true, - (ValueType::List(left), ValueType::List(right)) => left == right, - (ValueType::Empty, ValueType::Empty) => true, - (ValueType::Map(left), ValueType::Map(right)) => left == right, - ( - ValueType::Table { - column_names: left_columns, - rows: left_rows, - }, - ValueType::Table { - column_names: right_columns, - rows: right_rows, - }, - ) => left_columns == right_columns && left_rows == right_rows, - (ValueType::Function(left), ValueType::Function(right)) => left == right, - _ => false, - } - } -} - -impl Display for ValueType { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match &self { - ValueType::Any => write!(f, "any"), - ValueType::String => write!(f, "string"), - ValueType::Float => write!(f, "float"), - ValueType::Integer => write!(f, "integer"), - ValueType::Boolean => write!(f, "boolean"), - ValueType::List(list) => { - write!(f, "(")?; - for (index, item) in list.iter().enumerate() { - if index > 0 { - write!(f, ", ")?; - } - - write!(f, "{item:?}")?; - } - - write!(f, ")") - } - ValueType::Empty => write!(f, "empty"), - ValueType::Map(_map) => write!(f, "map"), - ValueType::Table { - column_names: _, - rows: _, - } => { - write!(f, "table") - } - ValueType::Function(function) => write!(f, "{function}"), - } - } -} - -impl Debug for ValueType { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{self}") - } -} - -impl From<&Value> for ValueType { - fn from(value: &Value) -> Self { - match value { - Value::String(_) => ValueType::String, - Value::Float(_) => ValueType::Float, - Value::Integer(_) => ValueType::Integer, - Value::Boolean(_) => ValueType::Boolean, - Value::Empty => ValueType::Empty, - Value::List(list) => { - let value_nodes = list - .items() - .iter() - .map(|value| { - Expression::Value(ValueNode::new( - value.value_type(), - String::with_capacity(0), - )) - }) - .collect(); - - ValueType::List(value_nodes) - } - Value::Map(map) => { - let mut value_nodes = BTreeMap::new(); - - for (key, value) in map.variables().unwrap().iter() { - let value_type = value.value_type(); - let value_node = ValueNode::new(value_type, String::with_capacity(0)); - let statement = Statement::Expression(Expression::Value(value_node)); - - value_nodes.insert(key.to_string(), statement); - } - - ValueType::Map(value_nodes) - } - Value::Table(table) => ValueType::Table { - column_names: table - .headers() - .iter() - .map(|column_name| Identifier::new(column_name.clone())) - .collect(), - rows: Box::new(Expression::Value(ValueNode::new( - ValueType::List(Vec::with_capacity(0)), - String::with_capacity(0), - ))), - }, - Value::Function(function) => ValueType::Function(function.clone()), - } - } -} - -impl From<&mut Value> for ValueType { - fn from(value: &mut Value) -> Self { - From::<&Value>::from(value) - } -} diff --git a/tree-sitter-dust/grammar.js b/tree-sitter-dust/grammar.js index 5158492..5beaae2 100644 --- a/tree-sitter-dust/grammar.js +++ b/tree-sitter-dust/grammar.js @@ -276,7 +276,7 @@ module.exports = grammar({ function: $ => seq( '|', - repeat($._function_parameters), + field('parameters', repeat($._function_parameters)), '|', optional(field('return_type', $.type)), field('body', $.block),