1
0

Implement runtime type checking

This commit is contained in:
Jeff 2023-11-30 02:09:55 -05:00
parent 8826d08392
commit 3dc78a7066
10 changed files with 84 additions and 30 deletions

View File

@ -56,12 +56,6 @@ impl AbstractTree for Assignment {
let statement_node = node.child_by_field_name("statement").unwrap();
let statement = Statement::from_syntax_node(source, statement_node, context)?;
if let Some(type_defintion) = &type_definition {
let statement_type = statement.expected_type(context)?;
type_defintion.check(&statement_type, context)?;
}
Ok(Assignment {
identifier,
type_definition,
@ -77,6 +71,16 @@ impl AbstractTree for Assignment {
let new_value = match self.operator {
AssignmentOperator::PlusEqual => {
if let Some(mut previous_value) = context.variables()?.get(key).cloned() {
if let Ok(list) = previous_value.as_list() {
let first_item_type = if let Some(first) = list.items().first() {
first.r#type(context)?
} else {
TypeDefinition::new(Type::Any)
};
first_item_type.check(&value.r#type(context)?, context)?;
}
previous_value += value;
previous_value
} else {
@ -91,15 +95,17 @@ impl AbstractTree for Assignment {
return Err(Error::VariableIdentifierNotFound(key.clone()));
}
}
AssignmentOperator::Equal => value,
AssignmentOperator::Equal => {
if let Some(type_definition) = &self.type_definition {
let new_value_type = value.r#type(context)?;
type_definition.check(&new_value_type, context)?;
}
value
}
};
if let Some(type_definition) = &self.type_definition {
let new_value_type = new_value.r#type(context)?;
type_definition.check(&new_value_type, context)?;
}
context.variables_mut()?.insert(key.clone(), new_value);
Ok(Value::Empty)

View File

@ -59,7 +59,7 @@ impl AbstractTree for FunctionCall {
arguments.push(value);
}
return built_in_function.run(&arguments);
return built_in_function.run(&arguments, context);
}
}

View File

@ -15,10 +15,14 @@ impl TypeDefinition {
Self { r#type }
}
pub fn r#type(&self) -> &Type {
pub fn inner(&self) -> &Type {
&self.r#type
}
pub fn take_inner(self) -> Type {
self.r#type
}
pub fn check(&self, other: &TypeDefinition, context: &Map) -> Result<()> {
match (&self.r#type, &other.r#type) {
(Type::Any, _)

View File

@ -203,9 +203,9 @@ impl AbstractTree for ValueNode {
}
if let Some(previous) = previous_type {
previous
TypeDefinition::new(Type::List(Box::new(previous.take_inner())))
} else {
TypeDefinition::new(Type::Any)
TypeDefinition::new(Type::List(Box::new(Type::Any)))
}
}
ValueNode::Empty => TypeDefinition::new(Type::Any),

View File

@ -13,7 +13,7 @@ impl BuiltInFunction for Read {
"read"
}
fn run(&self, arguments: &[Value]) -> Result<Value> {
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
let path_string = arguments.first().unwrap_or_default().as_string()?;
let path = PathBuf::from(path_string);
@ -55,7 +55,7 @@ impl BuiltInFunction for Write {
"write"
}
fn run(&self, arguments: &[Value]) -> Result<Value> {
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
let file_content = arguments.first().unwrap_or_default().as_string()?;
let path = arguments.get(1).unwrap_or(&Value::Empty).as_string()?;
@ -72,7 +72,7 @@ impl BuiltInFunction for Append {
"append"
}
fn run(&self, arguments: &[Value]) -> Result<Value> {
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
let file_content = arguments.first().unwrap_or(&Value::Empty).as_string()?;
let path = arguments.get(1).unwrap_or(&Value::Empty).as_string()?;

View File

@ -1,12 +1,18 @@
use crate::{Result, Value};
use crate::{Map, Result, Value};
mod fs;
mod output;
mod r#type;
pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 4] =
[&output::Output, &fs::Read, &fs::Write, &fs::Append];
pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 5] = [
&fs::Read,
&fs::Write,
&fs::Append,
&output::Output,
&r#type::Type,
];
pub trait BuiltInFunction {
fn name(&self) -> &'static str;
fn run(&self, arguments: &[Value]) -> Result<Value>;
fn run(&self, arguments: &[Value], context: &Map) -> Result<Value>;
}

View File

@ -1,4 +1,4 @@
use crate::{BuiltInFunction, Result, Value};
use crate::{BuiltInFunction, Map, Result, Value};
pub struct Output;
@ -7,7 +7,7 @@ impl BuiltInFunction for Output {
"output"
}
fn run(&self, arguments: &[Value]) -> Result<Value> {
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
for argument in arguments {
println!("{argument}");
}

View File

@ -0,0 +1,34 @@
use crate::{BuiltInFunction, Error, List, Map, Result, Value};
pub struct Type;
impl BuiltInFunction for Type {
fn name(&self) -> &'static str {
"type"
}
fn run(&self, arguments: &[Value], context: &Map) -> Result<Value> {
Error::expect_function_argument_amount("type", 1, arguments.len())?;
if arguments.len() == 1 {
let type_definition = arguments.first().unwrap().r#type(context)?;
let type_text = type_definition.to_string();
let text_without_brackets = &type_text[1..type_text.len() - 1];
Ok(Value::String(text_without_brackets.to_string()))
} else {
let mut answers = Vec::new();
for value in arguments {
let type_definition = value.r#type(context)?;
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());
answers.push(text_as_value);
}
Ok(Value::List(List::with_items(answers)))
}
}
}

View File

@ -166,7 +166,7 @@ impl Error {
}
}
pub fn expect_tool_argument_amount(
pub fn expect_function_argument_amount(
tool_name: &'static str,
expected: usize,
actual: usize,
@ -374,7 +374,7 @@ impl fmt::Display for Error {
),
RuntimeTypeCheck { expected, actual } => write!(
f,
"Type check error. Expected type {expected} but got value {actual}."
"Type check error. Expected type {expected} but got value with type {actual}."
),
}
}

View File

@ -53,14 +53,18 @@ impl Value {
if let Some(previous) = &previous_type {
if &value_type != previous {
break;
return Ok(TypeDefinition::new(Type::List(Box::new(Type::Any))));
}
}
previous_type = Some(value_type);
}
Type::List(Box::new(Type::Any))
if let Some(previous) = previous_type {
Type::List(Box::new(previous.take_inner()))
} else {
Type::List(Box::new(Type::Any))
}
}
Value::Map(_) => Type::Map,
Value::Table(_) => Type::Table,