Expand type checking

This commit is contained in:
Jeff 2023-12-09 17:55:47 -05:00
parent 833a830b30
commit 0452243c08
13 changed files with 78 additions and 69 deletions

View File

@ -58,17 +58,24 @@ 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)?;
if let Some((_previous_value, previous_type)) = context.variables()?.get(identifier.inner())
{
let type_check = previous_type.check(&statement.expected_type(context)?);
if let Err(error) = type_check {
return Err(error.with_context(
statement_node.start_position(),
source[statement_node.byte_range()].to_string(),
));
}
}
if let Some(type_definition) = &type_definition { if let Some(type_definition) = &type_definition {
let statement_type = statement.expected_type(context)?; let statement_type = statement.expected_type(context)?;
match operator { match operator {
AssignmentOperator::Equal => { AssignmentOperator::Equal => {
type_definition.inner().check( type_definition.inner().check(&statement_type)?;
&statement_type,
context,
statement_node,
source,
)?;
} }
AssignmentOperator::PlusEqual => { AssignmentOperator::PlusEqual => {
let identifier_type = identifier.expected_type(context)?; let identifier_type = identifier.expected_type(context)?;
@ -76,21 +83,11 @@ impl AbstractTree for Assignment {
if let Type::List(item_type) = type_definition.inner() { if let Type::List(item_type) = type_definition.inner() {
println!("{item_type}"); println!("{item_type}");
item_type.check(&identifier_type, context, identifier_node, source)?; item_type.check(&identifier_type)?;
item_type.check(&statement_type, context, statement_node, source)?; item_type.check(&statement_type)?;
} else { } else {
type_definition.inner().check( type_definition.inner().check(&identifier_type)?;
&identifier_type, type_definition.inner().check(&statement_type)?;
context,
identifier_node,
source,
)?;
type_definition.inner().check(
&statement_type,
context,
statement_node,
source,
)?;
} }
} }
AssignmentOperator::MinusEqual => todo!(), AssignmentOperator::MinusEqual => todo!(),
@ -128,11 +125,8 @@ impl AbstractTree for Assignment {
} }
AssignmentOperator::Equal => value, AssignmentOperator::Equal => value,
}; };
let new_value_type = new_value.r#type(context)?;
context context.set(key.clone(), new_value)?;
.variables_mut()?
.insert(key.clone(), (new_value, new_value_type));
Ok(Value::Empty) Ok(Value::Empty)
} }

View File

@ -59,7 +59,7 @@ impl AbstractTree for For {
iter_context iter_context
.variables_mut()? .variables_mut()?
.insert(key.clone(), (value.clone(), value.r#type(context)?)); .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| ())
})?; })?;
@ -69,7 +69,7 @@ impl AbstractTree for For {
for value in values.iter() { for value in values.iter() {
loop_context loop_context
.variables_mut()? .variables_mut()?
.insert(key.clone(), (value.clone(), value.r#type(context)?)); .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

@ -52,9 +52,9 @@ impl AbstractTree for FunctionCall {
let argument_type = argument.expected_type(context)?; let argument_type = argument.expected_type(context)?;
if let Type::Function { return_type, .. } = argument_type { if let Type::Function { return_type, .. } = argument_type {
r#type.check(&return_type, context, node, source)?; r#type.check(&return_type)?;
} else { } else {
r#type.check(&argument_type, context, node, source)?; r#type.check(&argument_type)?;
} }
} }
} }

View File

@ -43,7 +43,7 @@ impl AbstractTree for Identifier {
fn expected_type(&self, context: &Map) -> Result<Type> { fn expected_type(&self, context: &Map) -> Result<Type> {
if let Some((value, _)) = context.variables()?.get(&self.0) { if let Some((value, _)) = context.variables()?.get(&self.0) {
value.r#type(context) Ok(value.r#type())
} 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() {

View File

@ -85,7 +85,7 @@ impl AbstractTree for IndexAssignment {
} }
AssignmentOperator::Equal => value, AssignmentOperator::Equal => value,
}; };
let new_value_type = new_value.r#type(context)?; let new_value_type = new_value.r#type();
index_context index_context
.variables_mut()? .variables_mut()?

View File

@ -67,7 +67,7 @@ pub enum Type {
} }
impl Type { impl Type {
pub fn check(&self, other: &Type, context: &Map, node: Node, source: &str) -> Result<()> { pub fn check(&self, other: &Type) -> Result<()> {
match (self, other) { match (self, other) {
(Type::Any, _) (Type::Any, _)
| (_, Type::Any) | (_, Type::Any)
@ -83,7 +83,7 @@ impl Type {
| (Type::Float, Type::Number) | (Type::Float, Type::Number)
| (Type::String, Type::String) => Ok(()), | (Type::String, Type::String) => Ok(()),
(Type::List(self_item_type), Type::List(other_item_type)) => { (Type::List(self_item_type), Type::List(other_item_type)) => {
self_item_type.check(&other_item_type, context, node, source) self_item_type.check(&other_item_type)
} }
( (
Type::Function { Type::Function {
@ -100,18 +100,16 @@ 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, context, node, source)?; self_parameter_type.check(&other_parameter_type)?;
} }
self_return_type.check(other_return_type, context, node, source)?; self_return_type.check(other_return_type)?;
Ok(()) Ok(())
} }
_ => Err(Error::TypeCheck { _ => Err(Error::TypeCheck {
expected: self.clone(), expected: self.clone(),
actual: other.clone(), actual: other.clone(),
location: node.start_position(),
source: source[node.byte_range()].to_string(),
}), }),
} }
} }

View File

@ -132,7 +132,7 @@ impl AbstractTree for ValueNode {
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(context)?; let value_type = value.r#type();
variables.insert(key.clone(), (value, value_type)); variables.insert(key.clone(), (value, value_type));
} }

View File

@ -7,11 +7,11 @@ impl BuiltInFunction for TypeFunction {
"type" "type"
} }
fn run(&self, arguments: &[Value], context: &Map) -> Result<Value> { fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
Error::expect_argument_amount(self, 1, arguments.len())?; Error::expect_argument_amount(self, 1, arguments.len())?;
if arguments.len() == 1 { if arguments.len() == 1 {
let type_definition = arguments.first().unwrap().r#type(context)?; let type_definition = arguments.first().unwrap().r#type();
let type_text = type_definition.to_string(); let type_text = type_definition.to_string();
let text_without_brackets = &type_text[1..type_text.len() - 1]; let text_without_brackets = &type_text[1..type_text.len() - 1];
@ -20,7 +20,7 @@ impl BuiltInFunction for TypeFunction {
let mut answers = Vec::new(); let mut answers = Vec::new();
for value in arguments { for value in arguments {
let type_definition = value.r#type(context)?; let type_definition = value.r#type();
let type_text = type_definition.to_string(); let type_text = type_definition.to_string();
let text_without_brackets = &type_text[1..type_text.len() - 1]; let text_without_brackets = &type_text[1..type_text.len() - 1];
let text_as_value = Value::String(text_without_brackets.to_string()); let text_as_value = Value::String(text_without_brackets.to_string());

View File

@ -13,6 +13,12 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub enum Error { pub enum Error {
WithContext {
error: Box<Error>,
location: Point,
source: String,
},
UnexpectedSyntaxNode { UnexpectedSyntaxNode {
expected: &'static str, expected: &'static str,
actual: &'static str, actual: &'static str,
@ -23,13 +29,6 @@ pub enum Error {
TypeCheck { TypeCheck {
expected: Type, expected: Type,
actual: Type, actual: Type,
location: Point,
source: String,
},
RuntimeTypeCheck {
expected: Type,
actual: Type,
}, },
/// The 'assert' macro did not resolve successfully. /// The 'assert' macro did not resolve successfully.
@ -148,6 +147,14 @@ pub enum Error {
} }
impl Error { impl Error {
pub fn with_context(self, location: Point, source: String) -> Self {
Error::WithContext {
error: Box::new(self),
location,
source,
}
}
pub fn expect_syntax_node(source: &str, expected: &'static str, actual: Node) -> Result<()> { pub fn expect_syntax_node(source: &str, expected: &'static str, actual: Node) -> Result<()> {
if expected == actual.kind() { if expected == actual.kind() {
Ok(()) Ok(())
@ -368,19 +375,15 @@ impl fmt::Display for Error {
Syntax { source, location } => { Syntax { source, location } => {
write!(f, "Syntax error at {location}, this is not valid: {source}") write!(f, "Syntax error at {location}, this is not valid: {source}")
} }
TypeCheck { TypeCheck { expected, actual } => write!(
expected, f,
actual, "Type check error. Expected type {expected} but got type {actual}."
),
WithContext {
error,
location, location,
source, source,
} => write!( } => write!(f, "{error} Occured at {location}: \"{source}\""),
f,
"Type check error at {location}. Expected type {expected} but got type {actual}: {source}."
),
RuntimeTypeCheck { expected, actual } => write!(
f,
"Type check error. Expected type {expected} but got value with type {actual}."
),
} }
} }
} }

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, Value}; use dust_lang::{evaluate_with_context, language, Interpreter, Map, Type, Value};
/// Command-line arguments to be parsed. /// Command-line arguments to be parsed.
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -63,16 +63,16 @@ fn main() {
context context
.variables_mut() .variables_mut()
.unwrap() .unwrap()
.insert("input".to_string(), Value::String(input)); .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 context.variables_mut().unwrap().insert(
.variables_mut() "input".to_string(),
.unwrap() (Value::String(file_contents), Type::String),
.insert("input".to_string(), Value::String(file_contents)); );
} }
let mut parser = TSParser::new(); let mut parser = TSParser::new();

View File

@ -76,7 +76,7 @@ impl Function {
for ((identifier, argument_type), expression) in parameter_argument_pairs { for ((identifier, argument_type), expression) in parameter_argument_pairs {
let value = expression.run(source, context)?; let value = expression.run(source, context)?;
let value_type = value.r#type(context)?; let value_type = value.r#type();
match argument_type { match argument_type {
Type::Any => {} Type::Any => {}

View File

@ -41,6 +41,20 @@ impl Map {
Ok(self.variables.read()?) Ok(self.variables.read()?)
} }
pub fn set(&self, key: String, value: Value) -> Result<Option<(Value, Type)>> {
let value_type = value.r#type();
let previous = self
.variables
.write()?
.insert(key, (value, value_type.clone()));
if let Some((_previous_value, previous_type)) = previous.clone() {
previous_type.check(&value_type)?;
}
Ok(previous)
}
pub fn variables_mut(&self) -> Result<RwLockWriteGuard<BTreeMap<String, (Value, Type)>>> { pub fn variables_mut(&self) -> Result<RwLockWriteGuard<BTreeMap<String, (Value, Type)>>> {
Ok(self.variables.write()?) Ok(self.variables.write()?)
} }

View File

@ -42,17 +42,17 @@ pub enum Value {
} }
impl Value { impl Value {
pub fn r#type(&self, context: &Map) -> Result<Type> { pub fn r#type(&self) -> Type {
let r#type = match self { let r#type = match self {
Value::List(list) => { Value::List(list) => {
let mut previous_type = None; let mut previous_type = None;
for value in list.items().iter() { for value in list.items().iter() {
let value_type = value.r#type(context)?; let value_type = value.r#type();
if let Some(previous) = &previous_type { if let Some(previous) = &previous_type {
if &value_type != previous { if &value_type != previous {
return Ok(Type::List(Box::new(Type::Any))); return Type::List(Box::new(Type::Any));
} }
} }
@ -74,7 +74,7 @@ impl Value {
Value::Empty => Type::Empty, Value::Empty => Type::Empty,
}; };
Ok(r#type) r#type
} }
pub fn is_string(&self) -> bool { pub fn is_string(&self) -> bool {