1
0

Implement type checking for functions and indexes

This commit is contained in:
Jeff 2023-12-13 15:47:41 -05:00
parent b91e23fef3
commit ce4d366bab
21 changed files with 94 additions and 333 deletions

View File

@ -1,4 +1,4 @@
create_random_numbers <(int)> = fn |count| { create_random_numbers = (fn count <int>) <any> {
numbers = [] numbers = []
while (length numbers) < count { while (length numbers) < count {

View File

@ -4,26 +4,26 @@ all_cards = {
weapons = ['Rope' 'Lead_Pipe' 'Knife'] weapons = ['Rope' 'Lead_Pipe' 'Knife']
} }
is_ready_to_solve <(map) -> bool> = fn |cards| { is_ready_to_solve = (fn cards <map>) <bool> {
((length cards:suspects) == 1) ((length cards:suspects) == 1)
&& ((length cards:rooms) == 1) && ((length cards:rooms) == 1)
&& ((length cards:weapons) == 1) && ((length cards:weapons) == 1)
} }
take_turn <(map str str) -> map> = fn |cards opponent_card current_room| { take_turn = (fn cards <map>, opponent_card <str>, current_room <str>) <map> {
cards = (remove_card opponent_card cards) cards = (remove_card opponent_card cards)
cards = (make_guess current_room cards) cards = (make_guess current_room cards)
cards cards
} }
remove_card <(map str) -> map> = fn |cards opponent_card| { remove_card = (fn cards <map>, opponent_card <str>) <map> {
cards:rooms -= opponent_card cards:rooms -= opponent_card
cards:suspects -= opponent_card cards:suspects -= opponent_card
cards:weapons -= opponent_card cards:weapons -= opponent_card
cards cards
} }
make_guess <(map str)> = fn |cards current_room| { make_guess = (fn cards <map> current_room <str>) <map> {
if (is_ready_to_solve cards) { if (is_ready_to_solve cards) {
(output 'It was ' (output 'It was '
+ cards:suspects:0 + cards:suspects:0

View File

@ -1,6 +1,6 @@
1 -> (output) 1 -> (output)
add_one <([int]) -> [int]> = fn |numbers| { add_one = (fn numbers <[int]>) <[int]> {
new_numbers = [] new_numbers = []
for number in numbers { for number in numbers {

View File

@ -57,9 +57,14 @@ impl AbstractTree for Assignment {
let statement_node = node.child(child_count - 1).unwrap(); let statement_node = node.child(child_count - 1).unwrap();
let statement = Statement::from_syntax_node(source, statement_node, context)?; let statement = Statement::from_syntax_node(source, statement_node, context)?;
let statement_type = statement.expected_type(context)?;
if let Some(type_definition) = &type_definition { if let Some(type_definition) = &type_definition {
let statement_type = statement.expected_type(context)?; context.set(
identifier.inner().clone(),
Value::Empty,
Some(type_definition.inner().clone()),
)?;
match operator { match operator {
AssignmentOperator::Equal => { AssignmentOperator::Equal => {
@ -78,22 +83,6 @@ impl AbstractTree for Assignment {
} }
AssignmentOperator::MinusEqual => todo!(), AssignmentOperator::MinusEqual => todo!(),
} }
} else if let Some((_previous_value, previous_type)) =
context.variables()?.get(identifier.inner())
{
let statement_type = statement.expected_type(context)?;
let type_check = if let Type::List(item_type) = previous_type {
item_type.check(&statement_type)
} else {
previous_type.check(&statement_type)
};
if let Err(error) = type_check {
return Err(error.with_context(
statement_node.start_position(),
source[statement_node.byte_range()].to_string(),
));
}
} }
Ok(Assignment { Ok(Assignment {
@ -129,6 +118,8 @@ impl AbstractTree for Assignment {
}; };
if let Some(type_defintion) = &self.type_definition { if let Some(type_defintion) = &self.type_definition {
type_defintion.inner().check(&new_value.r#type())?;
context.set(key.clone(), new_value, Some(type_defintion.inner().clone()))?; context.set(key.clone(), new_value, Some(type_defintion.inner().clone()))?;
} else { } else {
context.set(key.clone(), new_value, None)?; context.set(key.clone(), new_value, None)?;

View File

@ -57,9 +57,7 @@ impl AbstractTree for For {
values.par_iter().try_for_each(|value| { values.par_iter().try_for_each(|value| {
let mut iter_context = Map::clone_from(context)?; let mut iter_context = Map::clone_from(context)?;
iter_context iter_context.set(key.clone(), value.clone(), None)?;
.variables_mut()?
.insert(key.clone(), (value.clone(), value.r#type()));
self.block.run(source, &mut iter_context).map(|_value| ()) self.block.run(source, &mut iter_context).map(|_value| ())
})?; })?;
@ -67,9 +65,7 @@ impl AbstractTree for For {
let loop_context = Map::clone_from(context)?; let loop_context = Map::clone_from(context)?;
for value in values.iter() { for value in values.iter() {
loop_context loop_context.set(key.clone(), value.clone(), None)?;
.variables_mut()?
.insert(key.clone(), (value.clone(), value.r#type()));
self.block.run(source, &mut loop_context.clone())?; self.block.run(source, &mut loop_context.clone())?;
} }

View File

@ -26,6 +26,7 @@ impl AbstractTree for FunctionCall {
let expression_node = node.child(1).unwrap(); let expression_node = node.child(1).unwrap();
let function_expression = Expression::from_syntax_node(source, expression_node, context)?; let function_expression = Expression::from_syntax_node(source, expression_node, context)?;
let function_type = function_expression.expected_type(context)?;
let mut arguments = Vec::new(); let mut arguments = Vec::new();
@ -34,24 +35,28 @@ impl AbstractTree for FunctionCall {
if child.is_named() { if child.is_named() {
let expression = Expression::from_syntax_node(source, child, context)?; let expression = Expression::from_syntax_node(source, child, context)?;
let expression_type = expression.expected_type(context)?;
arguments.push(expression); let argument_index = arguments.len();
}
}
let function_type = function_expression.expected_type(context)?;
if let Type::Function { if let Type::Function {
parameter_types, parameter_types,
return_type: _, return_type: _,
} = function_type } = &function_type
{ {
let argument_type_pairs = arguments.iter().zip(parameter_types.iter()); let expected_type = parameter_types.get(argument_index).unwrap();
for (argument, r#type) in argument_type_pairs { println!("{expected_type} {expression_type}");
let argument_type = argument.expected_type(context)?;
r#type.check(&argument_type)?; expected_type
.check(&expression_type)
.map_err(|error| Error::WithContext {
error: Box::new(error),
location: child.start_position(),
source: source[child.byte_range()].to_string(),
})?;
}
arguments.push(expression);
} }
} }

View File

@ -47,7 +47,9 @@ impl AbstractTree for Identifier {
} else { } else {
for built_in_function in BUILT_IN_FUNCTIONS { for built_in_function in BUILT_IN_FUNCTIONS {
if self.0 == built_in_function.name() { if self.0 == built_in_function.name() {
return Ok(built_in_function.r#type()); if let Type::Function { return_type, .. } = built_in_function.r#type() {
return Ok(*return_type);
}
} }
} }

View File

@ -88,7 +88,12 @@ impl AbstractTree for Index {
} }
fn expected_type(&self, context: &Map) -> Result<Type> { fn expected_type(&self, context: &Map) -> Result<Type> {
self.collection.expected_type(context) match self.collection.expected_type(context)? {
Type::List(item_type) => Ok(*item_type.clone()),
Type::Map => Ok(Type::Any),
Type::Empty => Ok(Type::Empty),
_ => todo!(),
}
} }
} }

View File

@ -85,11 +85,8 @@ impl AbstractTree for IndexAssignment {
} }
AssignmentOperator::Equal => value, AssignmentOperator::Equal => value,
}; };
let new_value_type = new_value.r#type();
index_context index_context.set(index_key.clone(), new_value, None)?;
.variables_mut()?
.insert(index_key.clone(), (new_value, new_value_type));
Ok(Value::Empty) Ok(Value::Empty)
} }

View File

@ -76,8 +76,10 @@ impl AbstractTree for Match {
} }
} }
fn expected_type(&self, _context: &Map) -> Result<Type> { fn expected_type(&self, context: &Map) -> Result<Type> {
todo!() let (_, first_statement) = self.options.first().unwrap();
first_statement.expected_type(context)
} }
} }

View File

@ -19,7 +19,7 @@ pub mod logic;
pub mod r#match; pub mod r#match;
pub mod math; pub mod math;
pub mod statement; pub mod statement;
pub mod type_defintion; pub mod type_definition;
pub mod r#use; pub mod r#use;
pub mod value_node; pub mod value_node;
pub mod r#while; pub mod r#while;
@ -28,7 +28,7 @@ pub mod r#yield;
pub use { pub use {
assignment::*, block::*, expression::*, function_call::*, identifier::*, if_else::*, index::*, assignment::*, block::*, expression::*, function_call::*, identifier::*, if_else::*, index::*,
index_assignment::IndexAssignment, logic::*, math::*, r#for::*, r#match::*, r#use::*, index_assignment::IndexAssignment, logic::*, math::*, r#for::*, r#match::*, r#use::*,
r#while::*, r#yield::*, statement::*, type_defintion::*, value_node::*, r#while::*, r#yield::*, statement::*, type_definition::*, value_node::*,
}; };
use tree_sitter::Node; use tree_sitter::Node;

View File

@ -100,10 +100,24 @@ impl Type {
.zip(other_parameter_types.iter()); .zip(other_parameter_types.iter());
for (self_parameter_type, other_parameter_type) in parameter_type_pairs { for (self_parameter_type, other_parameter_type) in parameter_type_pairs {
self_parameter_type.check(&other_parameter_type)?; let check = self_parameter_type.check(&other_parameter_type);
if let Err(Error::TypeCheck { .. }) = check {
return Err(Error::TypeCheck {
expected: self.clone(),
actual: other.clone(),
});
}
} }
self_return_type.check(other_return_type)?; let check = self_return_type.check(other_return_type);
if let Err(Error::TypeCheck { .. }) = check {
return Err(Error::TypeCheck {
expected: self.clone(),
actual: other.clone(),
});
}
Ok(()) Ok(())
} }

View File

@ -1,217 +0,0 @@
use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Error, Map, Result, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct TypeDefinition {
r#type: Type,
}
impl TypeDefinition {
pub fn new(r#type: Type) -> Self {
Self { r#type }
}
pub fn inner(&self) -> &Type {
&self.r#type
}
pub fn take_inner(self) -> Type {
self.r#type
}
}
impl AbstractTree for TypeDefinition {
fn from_syntax_node(source: &str, node: Node, context: &Map) -> Result<Self> {
Error::expect_syntax_node(source, "type_definition", node)?;
let type_node = node.child(1).unwrap();
let r#type = Type::from_syntax_node(source, type_node, context)?;
Ok(TypeDefinition { r#type })
}
fn run(&self, source: &str, context: &Map) -> Result<Value> {
self.r#type.run(source, context)
}
fn expected_type(&self, context: &Map) -> Result<Type> {
self.r#type.expected_type(context)
}
}
impl Display for TypeDefinition {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "<{}>", self.r#type)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub enum Type {
Any,
Boolean,
Empty,
Float,
Function {
parameter_types: Vec<Type>,
return_type: Box<Type>,
},
Integer,
List(Box<Type>),
Map,
Number,
String,
}
impl Type {
pub fn check(&self, other: &Type) -> Result<()> {
match (self, other) {
(Type::Any, _)
| (_, Type::Any)
| (Type::Boolean, Type::Boolean)
| (Type::Empty, Type::Empty)
| (Type::Float, Type::Float)
| (Type::Integer, Type::Integer)
| (Type::Map, Type::Map)
| (Type::Number, Type::Number)
| (Type::Number, Type::Integer)
| (Type::Number, Type::Float)
| (Type::Integer, Type::Number)
| (Type::Float, Type::Number)
| (Type::String, Type::String) => Ok(()),
(Type::List(self_item_type), Type::List(other_item_type)) => {
self_item_type.check(&other_item_type)
}
(
Type::Function {
parameter_types: self_parameter_types,
return_type: self_return_type,
},
Type::Function {
parameter_types: other_parameter_types,
return_type: other_return_type,
},
) => {
let parameter_type_pairs = self_parameter_types
.iter()
.zip(other_parameter_types.iter());
for (self_parameter_type, other_parameter_type) in parameter_type_pairs {
self_parameter_type.check(&other_parameter_type)?;
}
self_return_type.check(other_return_type)?;
Ok(())
}
_ => Err(Error::TypeCheck {
expected: self.clone(),
actual: other.clone(),
}),
}
}
}
impl AbstractTree for Type {
fn from_syntax_node(source: &str, node: Node, context: &Map) -> Result<Self> {
Error::expect_syntax_node(source, "type", node)?;
let type_node = node.child(0).unwrap();
let r#type = match type_node.kind() {
"[" => {
let item_type_node = node.child(1).unwrap();
let item_type = Type::from_syntax_node(source, item_type_node, context)?;
Type::List(Box::new(item_type))
}
"any" => Type::Any,
"bool" => Type::Boolean,
"float" => Type::Float,
"(" => {
let child_count = node.child_count();
let mut parameter_types = Vec::new();
for index in 1..child_count - 2 {
let child = node.child(index).unwrap();
if child.is_named() {
let parameter_type = Type::from_syntax_node(source, child, context)?;
parameter_types.push(parameter_type);
}
}
let final_node = node.child(child_count - 1).unwrap();
let return_type = if final_node.is_named() {
Type::from_syntax_node(source, final_node, context)?
} else {
Type::Empty
};
Type::Function {
parameter_types,
return_type: Box::new(return_type),
}
}
"int" => Type::Integer,
"map" => Type::Map,
"num" => Type::Number,
"str" => Type::String,
_ => {
return Err(Error::UnexpectedSyntaxNode {
expected: "any, bool, float, fn, int, list, map, num or str",
actual: type_node.kind(),
location: type_node.start_position(),
relevant_source: source[type_node.byte_range()].to_string(),
})
}
};
Ok(r#type)
}
fn run(&self, _source: &str, _context: &Map) -> Result<Value> {
Ok(Value::Empty)
}
fn expected_type(&self, _context: &Map) -> Result<Type> {
Ok(Type::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::Empty => write!(f, "empty"),
Type::Float => write!(f, "float"),
Type::Function {
parameter_types,
return_type,
} => {
write!(f, "(")?;
for parameter_type in parameter_types {
write!(f, "{parameter_type}")?;
if parameter_type != parameter_types.last().unwrap() {
write!(f, " ")?;
}
}
write!(f, ")")?;
write!(f, " -> {return_type}")
}
Type::Integer => write!(f, "int"),
Type::List(item_type) => write!(f, "[{item_type}]"),
Type::Map => write!(f, "map"),
Type::Number => write!(f, "num"),
Type::String => write!(f, "str"),
}
}
}

View File

@ -31,8 +31,8 @@ impl AbstractTree for Use {
evaluate_with_context(&file_contents, &mut file_context)?; evaluate_with_context(&file_contents, &mut file_context)?;
for (key, value) in file_context.variables()?.iter() { for (key, (value, r#type)) in file_context.variables()?.iter() {
context.variables_mut()?.insert(key.clone(), value.clone()); context.set(key.clone(), value.clone(), Some(r#type.clone()))?;
} }
Ok(Value::Map(file_context)) Ok(Value::Map(file_context))

View File

@ -145,13 +145,10 @@ impl AbstractTree for ValueNode {
let map = Map::new(); let map = Map::new();
{ {
let mut variables = map.variables_mut()?;
for (key, statement) in key_statement_pairs { for (key, statement) in key_statement_pairs {
let value = statement.run(source, context)?; let value = statement.run(source, context)?;
let value_type = value.r#type();
variables.insert(key.clone(), (value, value_type)); map.set(key.clone(), value, None)?;
} }
} }
@ -252,15 +249,9 @@ mod tests {
fn evaluate_map() { fn evaluate_map() {
let map = Map::new(); let map = Map::new();
{ map.set("x".to_string(), Value::Integer(1), None).unwrap();
let mut variables = map.variables_mut().unwrap(); map.set("foo".to_string(), Value::String("bar".to_string()), None)
.unwrap();
variables.insert("x".to_string(), (Value::Integer(1), Type::Integer));
variables.insert(
"foo".to_string(),
(Value::String("bar".to_string()), Type::String),
);
}
assert_eq!(evaluate("{ x = 1, foo = 'bar' }"), Ok(Value::Map(map))); assert_eq!(evaluate("{ x = 1, foo = 'bar' }"), Ok(Value::Map(map)));
} }

View File

@ -24,24 +24,14 @@ impl BuiltInFunction for Read {
let entry = entry?; let entry = entry?;
let file_data = Map::new(); let file_data = Map::new();
{
let mut file_data_variables = file_data.variables_mut()?;
let name = entry.file_name().to_string_lossy().to_string(); let name = entry.file_name().to_string_lossy().to_string();
let metadata = entry.metadata()?; let metadata = entry.metadata()?;
let created = metadata.created()?.elapsed()?.as_secs() as i64; let created = metadata.created()?.elapsed()?.as_secs() as i64;
let modified = metadata.modified()?.elapsed()?.as_secs() as i64; let modified = metadata.modified()?.elapsed()?.as_secs() as i64;
file_data_variables file_data.set("name".to_string(), Value::String(name), None)?;
.insert("name".to_string(), (Value::String(name), Type::String)); file_data.set("created".to_string(), Value::Integer(created), None)?;
file_data_variables.insert( file_data.set("modified".to_string(), Value::Integer(modified), None)?;
"created".to_string(),
(Value::Integer(created), Type::Integer),
);
file_data_variables.insert(
"modified".to_string(),
(Value::Integer(modified), Type::Integer),
);
}
files.items_mut().push(Value::Map(file_data)); files.items_mut().push(Value::Map(file_data));
} }

View File

@ -31,13 +31,9 @@ pub fn evaluate(source: &str) -> Result<Value> {
/// # use dust_lang::*; /// # use dust_lang::*;
/// let mut context = Map::new(); /// let mut context = Map::new();
/// ///
/// { /// context.set("one".into(), 1.into(), None);
/// let mut variables = context.variables_mut().unwrap(); /// context.set("two".into(), 2.into(), None);
/// /// context.set("three".into(), 3.into(), None);
/// variables.insert("one".into(), (1.into(), Type::Integer));
/// variables.insert("two".into(), (2.into(), Type::Integer));
/// variables.insert("three".into(), (3.into(), Type::Integer));
/// }
/// ///
/// let dust_code = "four = 4 one + two + three + four"; /// let dust_code = "four = 4 one + two + three + four";
/// ///

View File

@ -12,7 +12,7 @@ use tree_sitter::Parser as TSParser;
use std::{borrow::Cow, fs::read_to_string}; use std::{borrow::Cow, fs::read_to_string};
use dust_lang::{evaluate_with_context, language, Interpreter, Map, Type, Value}; use dust_lang::{evaluate_with_context, language, Interpreter, Map, Value};
/// Command-line arguments to be parsed. /// Command-line arguments to be parsed.
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -61,18 +61,16 @@ fn main() {
if let Some(input) = args.input { if let Some(input) = args.input {
context context
.variables_mut() .set("input".to_string(), Value::String(input), None)
.unwrap() .unwrap();
.insert("input".to_string(), (Value::String(input), Type::String));
} }
if let Some(path) = args.input_path { if let Some(path) = args.input_path {
let file_contents = read_to_string(path).unwrap(); let file_contents = read_to_string(path).unwrap();
context.variables_mut().unwrap().insert( context
"input".to_string(), .set("input".to_string(), Value::String(file_contents), None)
(Value::String(file_contents), Type::String), .unwrap();
);
} }
let mut parser = TSParser::new(); let mut parser = TSParser::new();

View File

@ -82,9 +82,7 @@ impl Function {
let key = identifier.inner().clone(); let key = identifier.inner().clone();
function_context function_context.set(key, value, Some(value_type))?;
.variables_mut()?
.insert(key, (value, value_type));
} }
let return_value = self.body.run(source, &function_context)?; let return_value = self.body.run(source, &function_context)?;

View File

@ -3,7 +3,7 @@ use std::{
cmp::Ordering, cmp::Ordering,
collections::BTreeMap, collections::BTreeMap,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, sync::{Arc, RwLock, RwLockReadGuard},
}; };
use crate::{value::Value, Result, Type}; use crate::{value::Value, Result, Type};
@ -59,10 +59,6 @@ impl Map {
Ok(previous) Ok(previous)
} }
pub fn variables_mut(&self) -> Result<RwLockWriteGuard<BTreeMap<String, (Value, Type)>>> {
Ok(self.variables.write()?)
}
} }
impl Default for Map { impl Default for Map {

View File

@ -781,11 +781,8 @@ impl<'de> Visitor<'de> for ValueVisitor {
let map = Map::new(); let map = Map::new();
{ {
let mut variables = map.variables_mut().unwrap();
while let Some((key, value)) = access.next_entry::<String, Value>()? { while let Some((key, value)) = access.next_entry::<String, Value>()? {
let r#type = value.r#type(); map.set(key, value, None).unwrap();
variables.insert(key, (value, r#type));
} }
} }