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_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)
|
||||||
}
|
}
|
||||||
|
@ -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())?;
|
||||||
}
|
}
|
||||||
|
@ -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)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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()?
|
||||||
|
@ -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(),
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
|
39
src/error.rs
39
src/error.rs
@ -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}."
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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 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();
|
||||||
|
@ -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 => {}
|
||||||
|
@ -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()?)
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user