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_node = node.child_by_field_name("statement").unwrap();
let statement = Statement::from_syntax_node(source, statement_node, context)?; 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 { Ok(Assignment {
identifier, identifier,
type_definition, type_definition,
@ -77,6 +71,16 @@ impl AbstractTree for Assignment {
let new_value = match self.operator { let new_value = match self.operator {
AssignmentOperator::PlusEqual => { AssignmentOperator::PlusEqual => {
if let Some(mut previous_value) = context.variables()?.get(key).cloned() { 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 += value;
previous_value previous_value
} else { } else {
@ -91,15 +95,17 @@ impl AbstractTree for Assignment {
return Err(Error::VariableIdentifierNotFound(key.clone())); return Err(Error::VariableIdentifierNotFound(key.clone()));
} }
} }
AssignmentOperator::Equal => value, AssignmentOperator::Equal => {
};
if let Some(type_definition) = &self.type_definition { if let Some(type_definition) = &self.type_definition {
let new_value_type = new_value.r#type(context)?; let new_value_type = value.r#type(context)?;
type_definition.check(&new_value_type, context)?; type_definition.check(&new_value_type, context)?;
} }
value
}
};
context.variables_mut()?.insert(key.clone(), new_value); context.variables_mut()?.insert(key.clone(), new_value);
Ok(Value::Empty) Ok(Value::Empty)

View File

@ -59,7 +59,7 @@ impl AbstractTree for FunctionCall {
arguments.push(value); 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 } Self { r#type }
} }
pub fn r#type(&self) -> &Type { pub fn inner(&self) -> &Type {
&self.r#type &self.r#type
} }
pub fn take_inner(self) -> Type {
self.r#type
}
pub fn check(&self, other: &TypeDefinition, context: &Map) -> Result<()> { pub fn check(&self, other: &TypeDefinition, context: &Map) -> Result<()> {
match (&self.r#type, &other.r#type) { match (&self.r#type, &other.r#type) {
(Type::Any, _) (Type::Any, _)

View File

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

View File

@ -13,7 +13,7 @@ impl BuiltInFunction for Read {
"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_string = arguments.first().unwrap_or_default().as_string()?;
let path = PathBuf::from(path_string); let path = PathBuf::from(path_string);
@ -55,7 +55,7 @@ impl BuiltInFunction for Write {
"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 file_content = arguments.first().unwrap_or_default().as_string()?;
let path = arguments.get(1).unwrap_or(&Value::Empty).as_string()?; let path = arguments.get(1).unwrap_or(&Value::Empty).as_string()?;
@ -72,7 +72,7 @@ impl BuiltInFunction for Append {
"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 file_content = arguments.first().unwrap_or(&Value::Empty).as_string()?;
let path = arguments.get(1).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 fs;
mod output; mod output;
mod r#type;
pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 4] = pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 5] = [
[&output::Output, &fs::Read, &fs::Write, &fs::Append]; &fs::Read,
&fs::Write,
&fs::Append,
&output::Output,
&r#type::Type,
];
pub trait BuiltInFunction { pub trait BuiltInFunction {
fn name(&self) -> &'static str; 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; pub struct Output;
@ -7,7 +7,7 @@ impl BuiltInFunction for Output {
"output" "output"
} }
fn run(&self, arguments: &[Value]) -> Result<Value> { fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
for argument in arguments { for argument in arguments {
println!("{argument}"); 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, tool_name: &'static str,
expected: usize, expected: usize,
actual: usize, actual: usize,
@ -374,7 +374,7 @@ impl fmt::Display for Error {
), ),
RuntimeTypeCheck { expected, actual } => write!( RuntimeTypeCheck { expected, actual } => write!(
f, 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,15 +53,19 @@ impl Value {
if let Some(previous) = &previous_type { if let Some(previous) = &previous_type {
if &value_type != previous { if &value_type != previous {
break; return Ok(TypeDefinition::new(Type::List(Box::new(Type::Any))));
} }
} }
previous_type = Some(value_type); previous_type = Some(value_type);
} }
if let Some(previous) = previous_type {
Type::List(Box::new(previous.take_inner()))
} else {
Type::List(Box::new(Type::Any)) Type::List(Box::new(Type::Any))
} }
}
Value::Map(_) => Type::Map, Value::Map(_) => Type::Map,
Value::Table(_) => Type::Table, Value::Table(_) => Type::Table,
Value::Function(function) => { Value::Function(function) => {