Expand type checking
This commit is contained in:
parent
833a830b30
commit
0452243c08
@ -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)
|
||||
}
|
||||
|
@ -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())?;
|
||||
}
|
||||
|
@ -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)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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()?
|
||||
|
@ -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(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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());
|
||||
|
39
src/error.rs
39
src/error.rs
@ -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}\""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
src/main.rs
12
src/main.rs
@ -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();
|
||||
|
@ -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 => {}
|
||||
|
@ -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()?)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user