Implement type checking
This commit is contained in:
parent
25852efcd6
commit
2bd4ccb40d
@ -1,6 +1,6 @@
|
||||
(output "This will print first.")
|
||||
|
||||
create_random_numbers = |count| => {
|
||||
create_random_numbers = |count <int>| {
|
||||
numbers = [];
|
||||
|
||||
while (length numbers) < count {
|
||||
|
@ -4,7 +4,7 @@ add_one = |numbers <list>| <list> {
|
||||
new_numbers = []
|
||||
|
||||
for number in numbers {
|
||||
new_list += number + 1
|
||||
new_numbers += number + 1
|
||||
}
|
||||
|
||||
new_numbers
|
||||
@ -13,4 +13,3 @@ add_one = |numbers <list>| <list> {
|
||||
foo = [1, 2, 3] -> (add_one)
|
||||
|
||||
(assert_equal [2 3 4] foo)
|
||||
|
||||
|
@ -63,24 +63,24 @@ impl AbstractTree for Assignment {
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
||||
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)
|
||||
}
|
||||
|
@ -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<Self> {
|
||||
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";
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -14,6 +14,8 @@ pub struct For {
|
||||
|
||||
impl AbstractTree for For {
|
||||
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
||||
Error::expect_syntax_node(source, "for", node)?;
|
||||
|
||||
let for_node = node.child(0).unwrap();
|
||||
let is_async = match for_node.kind() {
|
||||
"for" => false,
|
||||
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Expression>),
|
||||
Empty,
|
||||
Map(BTreeMap<String, Statement>),
|
||||
Table {
|
||||
column_names: Vec<Identifier>,
|
||||
rows: Box<Expression>,
|
||||
},
|
||||
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<Value> {
|
||||
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)
|
||||
|
18
src/error.rs
18
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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,16 +85,18 @@ impl<'c, 's> Evaluator<'c, 's> {
|
||||
}
|
||||
|
||||
pub fn run(self) -> Result<Value> {
|
||||
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 {
|
||||
|
@ -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;
|
||||
|
@ -56,8 +56,6 @@ impl AbstractTree for Function {
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
||||
println!("{self}");
|
||||
|
||||
let return_value = self.body.run(source, context)?;
|
||||
|
||||
if let Some(r#type) = &self.return_type {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -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<Expression>),
|
||||
Empty,
|
||||
Map(BTreeMap<String, Statement>),
|
||||
Table {
|
||||
column_names: Vec<Identifier>,
|
||||
rows: Box<Expression>,
|
||||
},
|
||||
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)
|
||||
}
|
||||
}
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user