Add lots of type checking

This commit is contained in:
Jeff 2024-01-04 22:30:06 -05:00
parent 2a7988f9a6
commit 99ba7b78ab
17 changed files with 162 additions and 40 deletions

View File

@ -172,6 +172,10 @@ impl AbstractTree for BuiltInValue {
Ok(self.get().clone())
}
fn check_type(&self, _context: &Structure) -> Result<()> {
Ok(())
}
fn expected_type(&self, _context: &Structure) -> Result<Type> {
Ok(self.r#type())
}

View File

@ -11,6 +11,7 @@ pub struct For {
item_id: Identifier,
collection: Expression,
block: Block,
context: Structure,
}
impl AbstractTree for For {
@ -45,29 +46,42 @@ impl AbstractTree for For {
item_id: identifier,
collection: expression,
block: item,
context: Structure::clone_from(context)?,
})
}
fn check_type(&self, context: &Structure) -> Result<()> {
self.collection.check_type(context)?;
let key = self.item_id.inner();
let collection_type = self.collection.expected_type(context)?;
Type::list(Type::Any).check(&collection_type)?;
if let Type::List(item_type) = self.collection.expected_type(context)? {
self.context
.set(key.to_string(), Value::none(), Some(*item_type))?;
}
self.block.check_type(&self.context)
}
fn run(&self, source: &str, context: &Structure) -> Result<Value> {
let expression_run = self.collection.run(source, context)?;
let values = expression_run.as_list()?.items();
let key = self.item_id.inner();
let values = expression_run.as_list()?.items();
if self.is_async {
values.par_iter().try_for_each(|value| {
let iter_context = Structure::clone_from(context)?;
self.context.set(key.clone(), value.clone(), None)?;
iter_context.set(key.clone(), value.clone(), None)?;
self.block.run(source, &iter_context).map(|_value| ())
self.block.run(source, &self.context).map(|_value| ())
})?;
} else {
let loop_context = Structure::clone_from(context)?;
for value in values.iter() {
loop_context.set(key.clone(), value.clone(), None)?;
self.context.set(key.clone(), value.clone(), None)?;
self.block.run(source, &loop_context)?;
self.block.run(source, &self.context)?;
}
}

View File

@ -45,6 +45,8 @@ impl AbstractTree for FunctionCall {
}
fn check_type(&self, context: &Structure) -> Result<()> {
self.function_expression.check_type(context)?;
let variables = context.variables()?;
let function_type = match &self.function_expression {
FunctionExpression::Identifier(identifier) => {
@ -72,6 +74,8 @@ impl AbstractTree for FunctionCall {
};
for (index, expression) in self.arguments.iter().enumerate() {
expression.check_type(context)?;
if let Some(r#type) = parameter_types.get(index) {
r#type.check(&expression.expected_type(context)?)?;
}
@ -133,32 +137,14 @@ impl AbstractTree for FunctionCall {
}
fn expected_type(&self, context: &Structure) -> Result<Type> {
match &self.function_expression {
FunctionExpression::Identifier(identifier) => {
let identifier_type = identifier.expected_type(context)?;
let function_type = self.function_expression.expected_type(context)?;
if let Type::Function {
parameter_types: _,
return_type,
} = &identifier_type
{
Ok(*return_type.clone())
} else {
Ok(identifier_type)
}
}
FunctionExpression::FunctionCall(function_call) => function_call.expected_type(context),
FunctionExpression::Value(value_node) => {
let value_type = value_node.expected_type(context)?;
if let Type::Function { return_type, .. } = value_type {
if let Type::Function { return_type, .. } = function_type {
Ok(*return_type)
} else {
Ok(value_type)
}
}
FunctionExpression::Index(index) => index.expected_type(context),
FunctionExpression::Yield(r#yield) => r#yield.expected_type(context),
Err(Error::ExpectedFunctionExpression {
actual: self.function_expression.clone(),
})
}
}
}

View File

@ -54,6 +54,16 @@ impl AbstractTree for FunctionExpression {
Ok(function_expression)
}
fn check_type(&self, _context: &Structure) -> Result<()> {
match self {
FunctionExpression::Identifier(identifier) => identifier.check_type(_context),
FunctionExpression::FunctionCall(function_call) => function_call.check_type(_context),
FunctionExpression::Value(value_node) => value_node.check_type(_context),
FunctionExpression::Index(index) => index.check_type(_context),
FunctionExpression::Yield(r#yield) => r#yield.check_type(_context),
}
}
fn run(&self, source: &str, context: &Structure) -> Result<Value> {
match self {
FunctionExpression::Identifier(identifier) => identifier.run(source, context),

View File

@ -45,10 +45,12 @@ impl AbstractTree for Identifier {
Err(Error::VariableIdentifierNotFound(self.0.clone()))
}
fn expected_type(&self, context: &Structure) -> Result<Type> {
if let Some((_value, r#type)) = context.variables()?.get(&self.0) {
println!("{_value}");
fn check_type(&self, _context: &Structure) -> Result<()> {
Ok(())
}
fn expected_type(&self, context: &Structure) -> Result<Type> {
if let Some((_, r#type)) = context.variables()?.get(&self.0) {
Ok(r#type.clone())
} else {
Err(Error::VariableIdentifierNotFound(self.0.clone()))

View File

@ -55,6 +55,25 @@ impl AbstractTree for IfElse {
})
}
fn check_type(&self, _context: &Structure) -> Result<()> {
self.if_expression.check_type(_context)?;
self.if_block.check_type(_context)?;
for expression in &self.else_if_expressions {
expression.check_type(_context)?;
}
for block in &self.else_if_blocks {
block.check_type(_context)?;
}
if let Some(block) = &self.else_block {
block.check_type(_context)?;
}
Ok(())
}
fn run(&self, source: &str, context: &Structure) -> Result<Value> {
let if_boolean = self.if_expression.run(source, context)?.as_boolean()?;

View File

@ -93,6 +93,20 @@ impl AbstractTree for Index {
}
}
fn check_type(&self, context: &Structure) -> Result<()> {
let collection_type = self.collection.expected_type(context)?;
match collection_type {
Type::List(_) | Type::String | Type::Structure(_) | Type::StructureDefinition(_) => {
Ok(())
}
_ => Err(Error::TypeCheck {
expected: Type::Collection,
actual: collection_type,
}),
}
}
fn expected_type(&self, context: &Structure) -> Result<Type> {
match self.collection.expected_type(context)? {
Type::List(item_type) => Ok(*item_type.clone()),

View File

@ -51,6 +51,11 @@ impl AbstractTree for IndexAssignment {
})
}
fn check_type(&self, _context: &Structure) -> Result<()> {
self.index.check_type(_context)?;
self.statement.check_type(_context)
}
fn run(&self, source: &str, context: &Structure) -> Result<Value> {
let index_collection = self.index.collection.run(source, context)?;
let index_context = index_collection.as_structure().unwrap_or(context);

View File

@ -52,6 +52,15 @@ impl AbstractTree for IndexExpression {
Ok(abstract_node)
}
fn check_type(&self, _context: &Structure) -> Result<()> {
match self {
IndexExpression::Value(value_node) => value_node.check_type(_context),
IndexExpression::Identifier(identifier) => identifier.check_type(_context),
IndexExpression::Index(index) => index.check_type(_context),
IndexExpression::FunctionCall(function_call) => function_call.check_type(_context),
}
}
fn run(&self, source: &str, context: &Structure) -> Result<Value> {
match self {
IndexExpression::Value(value_node) => value_node.run(source, context),

View File

@ -59,6 +59,11 @@ impl AbstractTree for Logic {
})
}
fn check_type(&self, _context: &Structure) -> Result<()> {
self.left.check_type(_context)?;
self.right.check_type(_context)
}
fn run(&self, source: &str, context: &Structure) -> Result<Value> {
let left = self.left.run(source, context)?;
let right = self.right.run(source, context)?;

View File

@ -58,6 +58,21 @@ impl AbstractTree for Match {
})
}
fn check_type(&self, _context: &Structure) -> Result<()> {
self.matcher.check_type(_context)?;
for (expression, statement) in &self.options {
expression.check_type(_context)?;
statement.check_type(_context)?;
}
if let Some(statement) = &self.fallback {
statement.check_type(_context)?;
}
Ok(())
}
fn run(&self, source: &str, context: &Structure) -> Result<Value> {
let matcher_value = self.matcher.run(source, context)?;

View File

@ -48,6 +48,11 @@ impl AbstractTree for Math {
})
}
fn check_type(&self, _context: &Structure) -> Result<()> {
self.left.check_type(_context)?;
self.right.check_type(_context)
}
fn run(&self, source: &str, context: &Structure) -> Result<Value> {
let left = self.left.run(source, context)?;
let right = self.right.run(source, context)?;

View File

@ -108,9 +108,7 @@ pub trait AbstractTree: Sized {
fn from_syntax_node(source: &str, node: Node, context: &Structure) -> Result<Self>;
/// Verify the type integrity of the node.
fn check_type(&self, _context: &Structure) -> Result<()> {
Ok(())
}
fn check_type(&self, _context: &Structure) -> Result<()>;
/// Execute dust code by traversing the tree.
fn run(&self, source: &str, context: &Structure) -> Result<Value>;

View File

@ -79,6 +79,25 @@ impl AbstractTree for StructureInstantiator {
Ok(instantiator)
}
fn check_type(&self, context: &Structure) -> Result<()> {
for (_key, (statement_option, type_definition_option)) in &self.0 {
let statement_type = if let Some(statement) = statement_option {
statement.check_type(context)?;
Some(statement.expected_type(context)?)
} else {
None
};
if let (Some(statement_type), Some(type_definition)) =
(statement_type, type_definition_option)
{
type_definition.inner().check(&statement_type)?;
}
}
Ok(())
}
fn run(&self, source: &str, context: &Structure) -> Result<Value> {
let mut variables = BTreeMap::new();

View File

@ -34,6 +34,10 @@ impl AbstractTree for TypeDefinition {
Ok(TypeDefinition { r#type })
}
fn check_type(&self, _context: &Structure) -> Result<()> {
Ok(())
}
fn run(&self, source: &str, context: &Structure) -> Result<Value> {
self.r#type.run(source, context)
}
@ -251,6 +255,10 @@ impl AbstractTree for Type {
Ok(r#type)
}
fn check_type(&self, _context: &Structure) -> Result<()> {
Ok(())
}
fn run(&self, _source: &str, _context: &Structure) -> Result<Value> {
Ok(Value::none())
}

View File

@ -25,6 +25,11 @@ impl AbstractTree for While {
Ok(While { expression, block })
}
fn check_type(&self, _context: &Structure) -> Result<()> {
self.expression.check_type(_context)?;
self.block.check_type(_context)
}
fn run(&self, source: &str, context: &Structure) -> Result<Value> {
while self.expression.run(source, context)?.as_boolean()? {
self.block.run(source, context)?;

View File

@ -44,6 +44,10 @@ impl AbstractTree for Yield {
Ok(Yield { call })
}
fn check_type(&self, _context: &Structure) -> Result<()> {
self.call.check_type(_context)
}
fn run(&self, source: &str, context: &Structure) -> Result<Value> {
self.call.run(source, context)
}