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

View File

@ -59,7 +59,7 @@ impl AbstractTree for For {
iter_context
.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| ())
})?;
@ -69,7 +69,7 @@ impl AbstractTree for For {
for value in values.iter() {
loop_context
.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())?;
}

View File

@ -52,9 +52,9 @@ impl AbstractTree for FunctionCall {
let argument_type = argument.expected_type(context)?;
if let Type::Function { return_type, .. } = argument_type {
r#type.check(&return_type, context, node, source)?;
r#type.check(&return_type)?;
} 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> {
if let Some((value, _)) = context.variables()?.get(&self.0) {
value.r#type(context)
Ok(value.r#type())
} else {
for built_in_function in BUILT_IN_FUNCTIONS {
if self.0 == built_in_function.name() {

View File

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

View File

@ -67,7 +67,7 @@ pub enum 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) {
(Type::Any, _)
| (_, Type::Any)
@ -83,7 +83,7 @@ impl Type {
| (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, context, node, source)
self_item_type.check(&other_item_type)
}
(
Type::Function {
@ -100,18 +100,16 @@ impl Type {
.zip(other_parameter_types.iter());
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(())
}
_ => Err(Error::TypeCheck {
expected: self.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 {
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));
}

View File

@ -7,11 +7,11 @@ impl BuiltInFunction for TypeFunction {
"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())?;
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 text_without_brackets = &type_text[1..type_text.len() - 1];
@ -20,7 +20,7 @@ impl BuiltInFunction for TypeFunction {
let mut answers = Vec::new();
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 text_without_brackets = &type_text[1..type_text.len() - 1];
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)]
pub enum Error {
WithContext {
error: Box<Error>,
location: Point,
source: String,
},
UnexpectedSyntaxNode {
expected: &'static str,
actual: &'static str,
@ -23,13 +29,6 @@ pub enum Error {
TypeCheck {
expected: Type,
actual: Type,
location: Point,
source: String,
},
RuntimeTypeCheck {
expected: Type,
actual: Type,
},
/// The 'assert' macro did not resolve successfully.
@ -148,6 +147,14 @@ pub enum 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<()> {
if expected == actual.kind() {
Ok(())
@ -368,19 +375,15 @@ impl fmt::Display for Error {
Syntax { source, location } => {
write!(f, "Syntax error at {location}, this is not valid: {source}")
}
TypeCheck {
expected,
actual,
TypeCheck { expected, actual } => write!(
f,
"Type check error. Expected type {expected} but got type {actual}."
),
WithContext {
error,
location,
source,
} => write!(
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}."
),
} => write!(f, "{error} Occured at {location}: \"{source}\""),
}
}
}

View File

@ -12,7 +12,7 @@ use tree_sitter::Parser as TSParser;
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.
#[derive(Parser, Debug)]
@ -63,16 +63,16 @@ fn main() {
context
.variables_mut()
.unwrap()
.insert("input".to_string(), Value::String(input));
.insert("input".to_string(), (Value::String(input), Type::String));
}
if let Some(path) = args.input_path {
let file_contents = read_to_string(path).unwrap();
context
.variables_mut()
.unwrap()
.insert("input".to_string(), Value::String(file_contents));
context.variables_mut().unwrap().insert(
"input".to_string(),
(Value::String(file_contents), Type::String),
);
}
let mut parser = TSParser::new();

View File

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

View File

@ -41,6 +41,20 @@ impl Map {
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)>>> {
Ok(self.variables.write()?)
}

View File

@ -42,17 +42,17 @@ pub enum Value {
}
impl Value {
pub fn r#type(&self, context: &Map) -> Result<Type> {
pub fn r#type(&self) -> Type {
let r#type = match self {
Value::List(list) => {
let mut previous_type = None;
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 &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,
};
Ok(r#type)
r#type
}
pub fn is_string(&self) -> bool {