Implement new type checking
This commit is contained in:
parent
4e861620ce
commit
ff6cc707d2
@ -26,7 +26,6 @@ impl AbstractTree for Assignment {
|
||||
|
||||
let identifier_node = node.child(0).unwrap();
|
||||
let identifier = Identifier::from_syntax_node(source, identifier_node, context)?;
|
||||
let identifier_type = identifier.expected_type(context)?;
|
||||
|
||||
let type_node = node.child(1).unwrap();
|
||||
let type_definition = if type_node.kind() == "type_definition" {
|
||||
@ -56,42 +55,6 @@ impl AbstractTree for Assignment {
|
||||
let statement = Statement::from_syntax_node(source, statement_node, context)?;
|
||||
let statement_type = statement.expected_type(context)?;
|
||||
|
||||
if let Some(type_definition) = &type_definition {
|
||||
match operator {
|
||||
AssignmentOperator::Equal => {
|
||||
type_definition
|
||||
.inner()
|
||||
.check(&statement_type)
|
||||
.map_err(|error| error.at_node(statement_node, source))?;
|
||||
}
|
||||
AssignmentOperator::PlusEqual => {
|
||||
if let Type::List(item_type) = type_definition.inner() {
|
||||
item_type
|
||||
.check(&statement_type)
|
||||
.map_err(|error| error.at_node(statement_node, source))?;
|
||||
} else {
|
||||
type_definition
|
||||
.inner()
|
||||
.check(&identifier_type)
|
||||
.map_err(|error| error.at_node(identifier_node, source))?;
|
||||
}
|
||||
}
|
||||
AssignmentOperator::MinusEqual => todo!(),
|
||||
}
|
||||
} else {
|
||||
match operator {
|
||||
AssignmentOperator::Equal => {}
|
||||
AssignmentOperator::PlusEqual => {
|
||||
if let Type::List(item_type) = identifier_type {
|
||||
item_type
|
||||
.check(&statement_type)
|
||||
.map_err(|error| error.at_node(statement_node, source))?;
|
||||
}
|
||||
}
|
||||
AssignmentOperator::MinusEqual => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
let variable_key = identifier.inner().clone();
|
||||
let variable_type = if let Some(definition) = &type_definition {
|
||||
definition.inner().clone()
|
||||
@ -111,6 +74,42 @@ impl AbstractTree for Assignment {
|
||||
})
|
||||
}
|
||||
|
||||
fn check_type(&self, context: &Map) -> Result<()> {
|
||||
let statement_type = self.statement.expected_type(context)?;
|
||||
|
||||
if let Some(type_definition) = &self.type_definition {
|
||||
match self.operator {
|
||||
AssignmentOperator::Equal => {
|
||||
type_definition.inner().check(&statement_type)?;
|
||||
}
|
||||
AssignmentOperator::PlusEqual => {
|
||||
if let Type::List(item_type) = type_definition.inner() {
|
||||
item_type.check(&statement_type)?;
|
||||
} else {
|
||||
type_definition
|
||||
.inner()
|
||||
.check(&self.identifier.expected_type(context)?)?;
|
||||
}
|
||||
}
|
||||
AssignmentOperator::MinusEqual => todo!(),
|
||||
}
|
||||
} else {
|
||||
match self.operator {
|
||||
AssignmentOperator::Equal => {}
|
||||
AssignmentOperator::PlusEqual => {
|
||||
if let Type::List(item_type) = self.identifier.expected_type(context)? {
|
||||
item_type.check(&statement_type)?;
|
||||
}
|
||||
}
|
||||
AssignmentOperator::MinusEqual => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
self.statement.check_type(context)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||
let key = self.identifier.inner();
|
||||
let value = self.statement.run(source, context)?;
|
||||
|
@ -50,6 +50,18 @@ impl AbstractTree for Block {
|
||||
})
|
||||
}
|
||||
|
||||
fn check_type(&self, _context: &Map) -> Result<()> {
|
||||
for statement in &self.statements {
|
||||
if let Statement::Return(inner_statement) = statement {
|
||||
return inner_statement.check_type(_context);
|
||||
} else {
|
||||
statement.check_type(_context)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||
if self.is_async {
|
||||
let statements = &self.statements;
|
||||
|
@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize};
|
||||
use tree_sitter::Node;
|
||||
|
||||
use crate::{
|
||||
built_in_functions::string_functions, AbstractTree, BuiltInFunction, Function, List, Map,
|
||||
Result, Type, Value,
|
||||
built_in_functions::string_functions, AbstractTree, BuiltInFunction, Function, Identifier,
|
||||
List, Map, Result, Type, TypeDefinition, Value,
|
||||
};
|
||||
|
||||
static ARGS: OnceLock<Value> = OnceLock::new();
|
||||
@ -31,12 +31,29 @@ impl BuiltInValue {
|
||||
match self {
|
||||
BuiltInValue::Args => Type::list(Type::String),
|
||||
BuiltInValue::AssertEqual => BuiltInFunction::AssertEqual.r#type(),
|
||||
BuiltInValue::Fs => Type::Map,
|
||||
BuiltInValue::Json => Type::Map,
|
||||
BuiltInValue::Fs => Type::Map(Vec::new()),
|
||||
BuiltInValue::Json => Type::Map(Vec::new()),
|
||||
BuiltInValue::Length => BuiltInFunction::Length.r#type(),
|
||||
BuiltInValue::Output => BuiltInFunction::Output.r#type(),
|
||||
BuiltInValue::Random => Type::Map,
|
||||
BuiltInValue::String => Type::Map,
|
||||
BuiltInValue::Random => Type::Map(vec![
|
||||
(
|
||||
Identifier::new("boolean".to_string()),
|
||||
TypeDefinition::new(BuiltInFunction::RandomBoolean.r#type()),
|
||||
),
|
||||
(
|
||||
Identifier::new("float".to_string()),
|
||||
TypeDefinition::new(BuiltInFunction::RandomFloat.r#type()),
|
||||
),
|
||||
(
|
||||
Identifier::new("from".to_string()),
|
||||
TypeDefinition::new(BuiltInFunction::RandomFrom.r#type()),
|
||||
),
|
||||
(
|
||||
Identifier::new("integer".to_string()),
|
||||
TypeDefinition::new(BuiltInFunction::RandomInteger.r#type()),
|
||||
),
|
||||
]),
|
||||
BuiltInValue::String => Type::Map(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,18 @@ impl AbstractTree for Expression {
|
||||
Ok(expression)
|
||||
}
|
||||
|
||||
fn check_type(&self, _context: &Map) -> Result<()> {
|
||||
match self {
|
||||
Expression::Value(value_node) => value_node.check_type(_context),
|
||||
Expression::Identifier(identifier) => identifier.check_type(_context),
|
||||
Expression::Math(math) => math.check_type(_context),
|
||||
Expression::Logic(logic) => logic.check_type(_context),
|
||||
Expression::FunctionCall(function_call) => function_call.check_type(_context),
|
||||
Expression::Index(index) => index.check_type(_context),
|
||||
Expression::Yield(r#yield) => r#yield.check_type(_context),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||
match self {
|
||||
Expression::Value(value_node) => value_node.run(source, context),
|
||||
|
@ -25,9 +25,7 @@ impl AbstractTree for FunctionCall {
|
||||
let function_node = node.child(0).unwrap();
|
||||
let function_expression =
|
||||
FunctionExpression::from_syntax_node(source, function_node, context)?;
|
||||
let function_type = function_expression.expected_type(context)?;
|
||||
|
||||
let mut minimum_parameter_count = 0;
|
||||
let mut arguments = Vec::new();
|
||||
|
||||
for index in 2..node.child_count() - 1 {
|
||||
@ -35,48 +33,47 @@ impl AbstractTree for FunctionCall {
|
||||
|
||||
if child.is_named() {
|
||||
let expression = Expression::from_syntax_node(source, child, context)?;
|
||||
let expression_type = expression.expected_type(context)?;
|
||||
let argument_index = arguments.len();
|
||||
|
||||
if let Type::Function {
|
||||
parameter_types, ..
|
||||
} = &function_type
|
||||
{
|
||||
if let Some(r#type) = parameter_types.get(argument_index) {
|
||||
if let Type::Option(_) = r#type {
|
||||
} else {
|
||||
minimum_parameter_count += 1;
|
||||
}
|
||||
|
||||
r#type
|
||||
.check(&expression_type)
|
||||
.map_err(|error| error.at_node(child, source))?;
|
||||
}
|
||||
}
|
||||
|
||||
arguments.push(expression);
|
||||
}
|
||||
}
|
||||
|
||||
if let Type::Function {
|
||||
parameter_types: _, ..
|
||||
} = &function_type
|
||||
{
|
||||
if arguments.len() < minimum_parameter_count {
|
||||
return Err(Error::ExpectedFunctionArgumentMinimum {
|
||||
source: source[function_node.byte_range()].to_string(),
|
||||
minumum_expected: minimum_parameter_count,
|
||||
actual: arguments.len(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(FunctionCall {
|
||||
function_expression,
|
||||
arguments,
|
||||
})
|
||||
}
|
||||
|
||||
fn check_type(&self, context: &Map) -> Result<()> {
|
||||
let function_expression_type = self.function_expression.expected_type(context)?;
|
||||
let parameter_types = if let Type::Function {
|
||||
parameter_types, ..
|
||||
} = &function_expression_type
|
||||
{
|
||||
parameter_types
|
||||
} else {
|
||||
return Err(Error::ExpectedFunctionType {
|
||||
actual: function_expression_type,
|
||||
});
|
||||
};
|
||||
|
||||
for (index, expression) in self.arguments.iter().enumerate() {
|
||||
if let Some(r#type) = parameter_types.get(index) {
|
||||
r#type.check(&expression.expected_type(context)?)?;
|
||||
}
|
||||
}
|
||||
|
||||
if self.arguments.len() != parameter_types.len() {
|
||||
return Err(Error::ExpectedFunctionArgumentAmount {
|
||||
source: "TODO".to_string(),
|
||||
expected: parameter_types.len(),
|
||||
actual: self.arguments.len(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||
let (name, value) = match &self.function_expression {
|
||||
FunctionExpression::Identifier(identifier) => {
|
||||
|
135
src/abstract_tree/function_node.rs
Normal file
135
src/abstract_tree/function_node.rs
Normal file
@ -0,0 +1,135 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tree_sitter::Node;
|
||||
|
||||
use crate::{
|
||||
AbstractTree, Block, Error, Function, Identifier, Map, Result, Type, TypeDefinition, Value,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct FunctionNode {
|
||||
parameters: Vec<Identifier>,
|
||||
body: Block,
|
||||
r#type: Type,
|
||||
}
|
||||
|
||||
impl FunctionNode {
|
||||
pub fn new(parameters: Vec<Identifier>, body: Block, r#type: Type) -> Self {
|
||||
Self {
|
||||
parameters,
|
||||
body,
|
||||
r#type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parameters(&self) -> &Vec<Identifier> {
|
||||
&self.parameters
|
||||
}
|
||||
|
||||
pub fn body(&self) -> &Block {
|
||||
&self.body
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> &Type {
|
||||
&self.r#type
|
||||
}
|
||||
|
||||
pub fn return_type(&self) -> &Type {
|
||||
match &self.r#type {
|
||||
Type::Function {
|
||||
parameter_types: _,
|
||||
return_type,
|
||||
} => return_type.as_ref(),
|
||||
_ => &Type::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call(
|
||||
&self,
|
||||
name: Option<String>,
|
||||
arguments: &[Value],
|
||||
source: &str,
|
||||
outer_context: &Map,
|
||||
) -> Result<Value> {
|
||||
let parameter_argument_pairs = self.parameters.iter().zip(arguments.iter());
|
||||
let function_context = Map::clone_from(outer_context)?;
|
||||
|
||||
for (identifier, value) in parameter_argument_pairs {
|
||||
let key = identifier.inner().clone();
|
||||
|
||||
function_context.set(key, value.clone(), None)?;
|
||||
}
|
||||
|
||||
if let Some(name) = name {
|
||||
function_context.set(
|
||||
name,
|
||||
Value::Function(Function::ContextDefined(self.clone())),
|
||||
None,
|
||||
)?;
|
||||
}
|
||||
|
||||
let return_value = self.body.run(source, &function_context)?;
|
||||
|
||||
Ok(return_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl AbstractTree for FunctionNode {
|
||||
fn from_syntax_node(source: &str, node: Node, context: &Map) -> Result<Self> {
|
||||
Error::expect_syntax_node(source, "function", node)?;
|
||||
|
||||
let child_count = node.child_count();
|
||||
let mut parameters = Vec::new();
|
||||
let mut parameter_types = Vec::new();
|
||||
|
||||
for index in 1..child_count - 3 {
|
||||
let child = node.child(index).unwrap();
|
||||
|
||||
if child.kind() == "identifier" {
|
||||
let identifier = Identifier::from_syntax_node(source, child, context)?;
|
||||
|
||||
parameters.push(identifier);
|
||||
}
|
||||
|
||||
if child.kind() == "type_definition" {
|
||||
let type_definition = TypeDefinition::from_syntax_node(source, child, context)?;
|
||||
|
||||
parameter_types.push(type_definition.take_inner());
|
||||
}
|
||||
}
|
||||
|
||||
let function_context = Map::clone_from(context)?;
|
||||
|
||||
for (parameter_name, parameter_type) in parameters.iter().zip(parameter_types.iter()) {
|
||||
function_context.set(
|
||||
parameter_name.inner().clone(),
|
||||
Value::none(),
|
||||
Some(parameter_type.clone()),
|
||||
)?;
|
||||
}
|
||||
|
||||
let return_type_node = node.child(child_count - 2).unwrap();
|
||||
let return_type = TypeDefinition::from_syntax_node(source, return_type_node, context)?;
|
||||
|
||||
let body_node = node.child(child_count - 1).unwrap();
|
||||
let body = Block::from_syntax_node(source, body_node, &function_context)?;
|
||||
|
||||
let r#type = Type::function(parameter_types, return_type.take_inner());
|
||||
|
||||
Ok(FunctionNode::new(parameters, body, r#type))
|
||||
}
|
||||
|
||||
fn check_type(&self, context: &Map) -> Result<()> {
|
||||
self.return_type()
|
||||
.check(&self.body.expected_type(context)?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, _source: &str, _context: &Map) -> Result<Value> {
|
||||
Ok(Value::Function(Function::ContextDefined(self.clone())))
|
||||
}
|
||||
|
||||
fn expected_type(&self, _context: &Map) -> Result<Type> {
|
||||
Ok(self.r#type().clone())
|
||||
}
|
||||
}
|
@ -92,7 +92,17 @@ impl AbstractTree for Index {
|
||||
fn expected_type(&self, context: &Map) -> Result<Type> {
|
||||
match self.collection.expected_type(context)? {
|
||||
Type::List(item_type) => Ok(*item_type.clone()),
|
||||
Type::Map => Ok(Type::Any),
|
||||
Type::Map(identifier_types) => {
|
||||
if let IndexExpression::Identifier(index_identifier) = &self.index {
|
||||
for (identifier, r#type) in identifier_types {
|
||||
if &identifier == index_identifier {
|
||||
return Ok(r#type.take_inner());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Type::None)
|
||||
}
|
||||
Type::None => Ok(Type::None),
|
||||
r#type => Ok(r#type),
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ pub mod expression;
|
||||
pub mod r#for;
|
||||
pub mod function_call;
|
||||
pub mod function_expression;
|
||||
pub mod function_node;
|
||||
pub mod identifier;
|
||||
pub mod if_else;
|
||||
pub mod index;
|
||||
@ -29,9 +30,9 @@ pub mod r#yield;
|
||||
|
||||
pub use {
|
||||
assignment::*, block::*, built_in_value::*, expression::*, function_call::*,
|
||||
function_expression::*, identifier::*, if_else::*, index::*, index_assignment::IndexAssignment,
|
||||
index_expression::*, logic::*, math::*, r#for::*, r#match::*, r#while::*, r#yield::*,
|
||||
statement::*, type_definition::*, value_node::*,
|
||||
function_expression::*, function_node::*, identifier::*, if_else::*, index::*,
|
||||
index_assignment::IndexAssignment, index_expression::*, logic::*, math::*, r#for::*,
|
||||
r#match::*, r#while::*, r#yield::*, statement::*, type_definition::*, value_node::*,
|
||||
};
|
||||
|
||||
use tree_sitter::Node;
|
||||
@ -59,6 +60,18 @@ impl AbstractTree for Root {
|
||||
Ok(Root { statements })
|
||||
}
|
||||
|
||||
fn check_type(&self, _context: &Map) -> Result<()> {
|
||||
for statement in &self.statements {
|
||||
if let Statement::Return(inner_statement) = statement {
|
||||
return inner_statement.check_type(_context);
|
||||
} else {
|
||||
statement.check_type(_context)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||
let mut value = Value::none();
|
||||
|
||||
@ -92,6 +105,11 @@ pub trait AbstractTree: Sized {
|
||||
/// node's byte range.
|
||||
fn from_syntax_node(source: &str, node: Node, context: &Map) -> Result<Self>;
|
||||
|
||||
/// Verify the type integrity of the node.
|
||||
fn check_type(&self, _context: &Map) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Execute dust code by traversing the tree.
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value>;
|
||||
|
||||
|
@ -66,6 +66,20 @@ impl AbstractTree for Statement {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_type(&self, _context: &Map) -> Result<()> {
|
||||
match self {
|
||||
Statement::Assignment(assignment) => assignment.check_type(_context),
|
||||
Statement::Expression(expression) => expression.check_type(_context),
|
||||
Statement::IfElse(if_else) => if_else.check_type(_context),
|
||||
Statement::Match(r#match) => r#match.check_type(_context),
|
||||
Statement::While(r#while) => r#while.check_type(_context),
|
||||
Statement::Block(block) => block.check_type(_context),
|
||||
Statement::For(r#for) => r#for.check_type(_context),
|
||||
Statement::IndexAssignment(index_assignment) => index_assignment.check_type(_context),
|
||||
Statement::Return(statement) => statement.check_type(_context),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||
match self {
|
||||
Statement::Assignment(assignment) => assignment.run(source, context),
|
||||
|
@ -3,7 +3,7 @@ use std::fmt::{self, Display, Formatter};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tree_sitter::Node;
|
||||
|
||||
use crate::{AbstractTree, Error, Map, Result, Value};
|
||||
use crate::{AbstractTree, Error, Identifier, Map, Result, Value};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct TypeDefinition {
|
||||
@ -61,7 +61,7 @@ pub enum Type {
|
||||
},
|
||||
Integer,
|
||||
List(Box<Type>),
|
||||
Map,
|
||||
Map(Vec<(Identifier, TypeDefinition)>),
|
||||
None,
|
||||
Number,
|
||||
String,
|
||||
@ -92,13 +92,12 @@ impl Type {
|
||||
| (Type::Collection, Type::Collection)
|
||||
| (Type::Collection, Type::List(_))
|
||||
| (Type::List(_), Type::Collection)
|
||||
| (Type::Collection, Type::Map)
|
||||
| (Type::Map, Type::Collection)
|
||||
| (Type::Collection, Type::Map(_))
|
||||
| (Type::Map(_), Type::Collection)
|
||||
| (Type::Collection, Type::String)
|
||||
| (Type::String, Type::Collection)
|
||||
| (Type::Float, Type::Float)
|
||||
| (Type::Integer, Type::Integer)
|
||||
| (Type::Map, Type::Map)
|
||||
| (Type::Number, Type::Number)
|
||||
| (Type::Number, Type::Integer)
|
||||
| (Type::Number, Type::Float)
|
||||
@ -106,6 +105,16 @@ impl Type {
|
||||
| (Type::Float, Type::Number)
|
||||
| (Type::None, Type::None)
|
||||
| (Type::String, Type::String) => Ok(()),
|
||||
(Type::Map(left), Type::Map(right)) => {
|
||||
if left == right {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::TypeCheck {
|
||||
expected: self.clone(),
|
||||
actual: other.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
(Type::Option(left), Type::Option(right)) => {
|
||||
if left == right {
|
||||
Ok(())
|
||||
@ -172,69 +181,95 @@ impl Type {
|
||||
}
|
||||
|
||||
impl AbstractTree for Type {
|
||||
fn from_syntax_node(source: &str, node: Node, _context: &Map) -> Result<Self> {
|
||||
Error::expect_syntax_node(source, "type", node)?;
|
||||
fn from_syntax_node(_source: &str, node: Node, _context: &Map) -> Result<Self> {
|
||||
Error::expect_syntax_node(_source, "type", node)?;
|
||||
|
||||
let type_node = node.child(0).unwrap();
|
||||
|
||||
let r#type =
|
||||
match type_node.kind() {
|
||||
"[" => {
|
||||
let item_type_node = node.child(1).unwrap();
|
||||
let item_type = Type::from_syntax_node(source, item_type_node, _context)?;
|
||||
let r#type = match type_node.kind() {
|
||||
"[" => {
|
||||
let item_type_node = node.child(1).unwrap();
|
||||
let item_type = Type::from_syntax_node(_source, item_type_node, _context)?;
|
||||
|
||||
Type::List(Box::new(item_type))
|
||||
Type::List(Box::new(item_type))
|
||||
}
|
||||
"any" => Type::Any,
|
||||
"bool" => Type::Boolean,
|
||||
"collection" => Type::Collection,
|
||||
"float" => Type::Float,
|
||||
"(" => {
|
||||
let child_count = node.child_count();
|
||||
let mut parameter_types = Vec::new();
|
||||
|
||||
for index in 1..child_count - 2 {
|
||||
let child = node.child(index).unwrap();
|
||||
|
||||
if child.is_named() {
|
||||
let parameter_type = Type::from_syntax_node(_source, child, _context)?;
|
||||
|
||||
parameter_types.push(parameter_type);
|
||||
}
|
||||
}
|
||||
"any" => Type::Any,
|
||||
"bool" => Type::Boolean,
|
||||
"collection" => Type::Collection,
|
||||
"float" => Type::Float,
|
||||
"(" => {
|
||||
let child_count = node.child_count();
|
||||
let mut parameter_types = Vec::new();
|
||||
|
||||
for index in 1..child_count - 2 {
|
||||
let child = node.child(index).unwrap();
|
||||
let final_node = node.child(child_count - 1).unwrap();
|
||||
let return_type = if final_node.is_named() {
|
||||
Type::from_syntax_node(_source, final_node, _context)?
|
||||
} else {
|
||||
Type::None
|
||||
};
|
||||
|
||||
if child.is_named() {
|
||||
let parameter_type = Type::from_syntax_node(source, child, _context)?;
|
||||
Type::Function {
|
||||
parameter_types,
|
||||
return_type: Box::new(return_type),
|
||||
}
|
||||
}
|
||||
"int" => Type::Integer,
|
||||
|
||||
parameter_types.push(parameter_type);
|
||||
"num" => Type::Number,
|
||||
"none" => Type::None,
|
||||
"str" => Type::String,
|
||||
"{" => {
|
||||
let child_count = node.child_count();
|
||||
let mut identifier_types = Vec::new();
|
||||
let mut identifier = None;
|
||||
|
||||
for index in 1..child_count - 1 {
|
||||
let child = node.child(index).unwrap();
|
||||
|
||||
match child.kind() {
|
||||
"identifier" => {
|
||||
identifier =
|
||||
Some(Identifier::from_syntax_node(_source, child, _context)?);
|
||||
}
|
||||
}
|
||||
"type_definition" => {
|
||||
if let Some(identifier) = &identifier {
|
||||
let type_definition =
|
||||
TypeDefinition::from_syntax_node(_source, child, _context)?;
|
||||
|
||||
let final_node = node.child(child_count - 1).unwrap();
|
||||
let return_type = if final_node.is_named() {
|
||||
Type::from_syntax_node(source, final_node, _context)?
|
||||
} else {
|
||||
Type::None
|
||||
};
|
||||
|
||||
Type::Function {
|
||||
parameter_types,
|
||||
return_type: Box::new(return_type),
|
||||
identifier_types.push((identifier.clone(), type_definition));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
"int" => Type::Integer,
|
||||
"map" => Type::Map,
|
||||
"num" => Type::Number,
|
||||
"none" => Type::None,
|
||||
"str" => Type::String,
|
||||
"option" => {
|
||||
let inner_type_node = node.child(2).unwrap();
|
||||
let inner_type = Type::from_syntax_node(source, inner_type_node, _context)?;
|
||||
|
||||
Type::Option(Box::new(inner_type))
|
||||
}
|
||||
_ => return Err(Error::UnexpectedSyntaxNode {
|
||||
expected:
|
||||
"any, bool, collection, float, function, int, list, map, num, str or option"
|
||||
.to_string(),
|
||||
Type::Map(identifier_types)
|
||||
}
|
||||
"option" => {
|
||||
let inner_type_node = node.child(2).unwrap();
|
||||
let inner_type = Type::from_syntax_node(_source, inner_type_node, _context)?;
|
||||
|
||||
Type::Option(Box::new(inner_type))
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::UnexpectedSyntaxNode {
|
||||
expected: "any, bool, float, int, num, str, option, (, [ or {".to_string(),
|
||||
actual: type_node.kind().to_string(),
|
||||
location: type_node.start_position(),
|
||||
relevant_source: source[type_node.byte_range()].to_string(),
|
||||
}),
|
||||
};
|
||||
relevant_source: _source[type_node.byte_range()].to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
Ok(r#type)
|
||||
}
|
||||
@ -274,7 +309,15 @@ impl Display for Type {
|
||||
}
|
||||
Type::Integer => write!(f, "int"),
|
||||
Type::List(item_type) => write!(f, "[{item_type}]"),
|
||||
Type::Map => write!(f, "map"),
|
||||
Type::Map(identifier_types) => {
|
||||
write!(f, "{{")?;
|
||||
|
||||
for (identifier, r#type) in identifier_types {
|
||||
write!(f, "{} {}", identifier.inner(), r#type)?;
|
||||
}
|
||||
|
||||
write!(f, "}}")
|
||||
}
|
||||
Type::Number => write!(f, "num"),
|
||||
Type::None => write!(f, "none"),
|
||||
Type::String => write!(f, "str"),
|
||||
|
@ -203,7 +203,18 @@ impl AbstractTree for ValueNode {
|
||||
Type::None
|
||||
}
|
||||
}
|
||||
ValueNode::Map(_) => Type::Map,
|
||||
ValueNode::Map(statements) => {
|
||||
let mut identifier_types = Vec::new();
|
||||
|
||||
for (key, (statement, _)) in statements {
|
||||
identifier_types.push((
|
||||
Identifier::new(key.clone()),
|
||||
TypeDefinition::new(statement.expected_type(context)?),
|
||||
));
|
||||
}
|
||||
|
||||
Type::Map(identifier_types)
|
||||
}
|
||||
ValueNode::BuiltInValue(built_in_value) => built_in_value.expected_type(context)?,
|
||||
};
|
||||
|
||||
|
17
src/error.rs
17
src/error.rs
@ -83,6 +83,10 @@ pub enum Error {
|
||||
actual: usize,
|
||||
},
|
||||
|
||||
ExpectedFunctionType {
|
||||
actual: Type,
|
||||
},
|
||||
|
||||
ExpectedString {
|
||||
actual: Value,
|
||||
},
|
||||
@ -217,16 +221,8 @@ impl Error {
|
||||
|
||||
pub fn is_type_check_error(&self, other: &Error) -> bool {
|
||||
match self {
|
||||
Error::WithContext { error, .. } => {
|
||||
debug_assert_eq!(error.as_ref(), other);
|
||||
|
||||
error.as_ref() == other
|
||||
}
|
||||
_ => {
|
||||
debug_assert_eq!(self, other);
|
||||
|
||||
self == other
|
||||
}
|
||||
Error::WithContext { error, .. } => error.as_ref() == other,
|
||||
_ => self == other,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -438,6 +434,7 @@ impl fmt::Display for Error {
|
||||
f,
|
||||
"Parsing was cancelled either manually or because it took too long."
|
||||
),
|
||||
ExpectedFunctionType { actual } => write!(f, "Expected a function but got {actual}."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -115,6 +115,7 @@ impl Interpreter {
|
||||
};
|
||||
|
||||
if let Some(abstract_tree) = &self.abstract_tree {
|
||||
abstract_tree.check_type(&self.context)?;
|
||||
abstract_tree.run(source, &self.context)
|
||||
} else {
|
||||
Ok(Value::none())
|
||||
|
@ -8,12 +8,7 @@ pub use crate::{
|
||||
built_in_functions::BuiltInFunction,
|
||||
error::*,
|
||||
interpret::*,
|
||||
value::{
|
||||
function::{ContextDefinedFunction, Function},
|
||||
list::List,
|
||||
map::Map,
|
||||
Value,
|
||||
},
|
||||
value::{function::Function, list::List, map::Map, Value},
|
||||
};
|
||||
|
||||
mod abstract_tree;
|
||||
|
@ -3,15 +3,12 @@ use std::fmt::{self, Display, Formatter};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tree_sitter::Node;
|
||||
|
||||
use crate::{
|
||||
AbstractTree, Block, BuiltInFunction, Error, Identifier, Map, Result, Type, TypeDefinition,
|
||||
Value,
|
||||
};
|
||||
use crate::{AbstractTree, BuiltInFunction, FunctionNode, Map, Result, Type, Value};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Function {
|
||||
BuiltIn(BuiltInFunction),
|
||||
ContextDefined(ContextDefinedFunction),
|
||||
ContextDefined(FunctionNode),
|
||||
}
|
||||
|
||||
impl Display for Function {
|
||||
@ -54,55 +51,17 @@ impl Function {
|
||||
}
|
||||
|
||||
impl AbstractTree for Function {
|
||||
fn from_syntax_node(source: &str, node: Node, context: &Map) -> Result<Self> {
|
||||
Error::expect_syntax_node(source, "function", node)?;
|
||||
fn from_syntax_node(_source: &str, _node: Node, _context: &Map) -> Result<Self> {
|
||||
let inner_function = FunctionNode::from_syntax_node(_source, _node, _context)?;
|
||||
|
||||
let child_count = node.child_count();
|
||||
let mut parameters = Vec::new();
|
||||
let mut parameter_types = Vec::new();
|
||||
Ok(Function::ContextDefined(inner_function))
|
||||
}
|
||||
|
||||
for index in 1..child_count - 3 {
|
||||
let child = node.child(index).unwrap();
|
||||
|
||||
if child.kind() == "identifier" {
|
||||
let identifier = Identifier::from_syntax_node(source, child, context)?;
|
||||
|
||||
parameters.push(identifier);
|
||||
}
|
||||
|
||||
if child.kind() == "type_definition" {
|
||||
let type_definition = TypeDefinition::from_syntax_node(source, child, context)?;
|
||||
|
||||
parameter_types.push(type_definition.take_inner());
|
||||
}
|
||||
fn check_type(&self, _context: &Map) -> Result<()> {
|
||||
match self {
|
||||
Function::BuiltIn(_) => Ok(()),
|
||||
Function::ContextDefined(defined_function) => defined_function.check_type(_context),
|
||||
}
|
||||
|
||||
let function_context = Map::clone_from(context)?;
|
||||
|
||||
for (parameter_name, parameter_type) in parameters.iter().zip(parameter_types.iter()) {
|
||||
function_context.set(
|
||||
parameter_name.inner().clone(),
|
||||
Value::none(),
|
||||
Some(parameter_type.clone()),
|
||||
)?;
|
||||
}
|
||||
|
||||
let return_type_node = node.child(child_count - 2).unwrap();
|
||||
let return_type = TypeDefinition::from_syntax_node(source, return_type_node, context)?;
|
||||
|
||||
let body_node = node.child(child_count - 1).unwrap();
|
||||
let body = Block::from_syntax_node(source, body_node, &function_context)?;
|
||||
|
||||
return_type
|
||||
.inner()
|
||||
.check(&body.expected_type(&function_context)?)
|
||||
.map_err(|error| error.at_node(body_node, source))?;
|
||||
|
||||
let r#type = Type::function(parameter_types, return_type.take_inner());
|
||||
|
||||
Ok(Self::ContextDefined(ContextDefinedFunction::new(
|
||||
parameters, body, r#type,
|
||||
)))
|
||||
}
|
||||
|
||||
fn run(&self, _source: &str, _context: &Map) -> Result<Value> {
|
||||
@ -116,71 +75,3 @@ impl AbstractTree for Function {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ContextDefinedFunction {
|
||||
parameters: Vec<Identifier>,
|
||||
body: Block,
|
||||
r#type: Type,
|
||||
}
|
||||
|
||||
impl ContextDefinedFunction {
|
||||
pub fn new(parameters: Vec<Identifier>, body: Block, r#type: Type) -> Self {
|
||||
Self {
|
||||
parameters,
|
||||
body,
|
||||
r#type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parameters(&self) -> &Vec<Identifier> {
|
||||
&self.parameters
|
||||
}
|
||||
|
||||
pub fn body(&self) -> &Block {
|
||||
&self.body
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> &Type {
|
||||
&self.r#type
|
||||
}
|
||||
|
||||
pub fn return_type(&self) -> &Type {
|
||||
match &self.r#type {
|
||||
Type::Function {
|
||||
parameter_types: _,
|
||||
return_type,
|
||||
} => return_type.as_ref(),
|
||||
_ => &Type::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call(
|
||||
&self,
|
||||
name: Option<String>,
|
||||
arguments: &[Value],
|
||||
source: &str,
|
||||
outer_context: &Map,
|
||||
) -> Result<Value> {
|
||||
let parameter_argument_pairs = self.parameters.iter().zip(arguments.iter());
|
||||
let function_context = Map::clone_from(outer_context)?;
|
||||
|
||||
for (identifier, value) in parameter_argument_pairs {
|
||||
let key = identifier.inner().clone();
|
||||
|
||||
function_context.set(key, value.clone(), None)?;
|
||||
}
|
||||
|
||||
if let Some(name) = name {
|
||||
function_context.set(
|
||||
name,
|
||||
Value::Function(Function::ContextDefined(self.clone())),
|
||||
None,
|
||||
)?;
|
||||
}
|
||||
|
||||
let return_value = self.body.run(source, &function_context)?;
|
||||
|
||||
Ok(return_value)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Types that represent runtime values.
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
Function, List, Map, Type,
|
||||
Function, Identifier, List, Map, Type, TypeDefinition,
|
||||
};
|
||||
|
||||
use serde::{
|
||||
@ -74,7 +74,18 @@ impl Value {
|
||||
Type::List(Box::new(Type::Any))
|
||||
}
|
||||
}
|
||||
Value::Map(_) => Type::Map,
|
||||
Value::Map(map) => {
|
||||
let mut identifier_types = Vec::new();
|
||||
|
||||
for (key, (value, _)) in map.variables().unwrap().iter() {
|
||||
identifier_types.push((
|
||||
Identifier::new(key.clone()),
|
||||
TypeDefinition::new(value.r#type()),
|
||||
));
|
||||
}
|
||||
|
||||
Type::Map(identifier_types)
|
||||
}
|
||||
Value::Function(function) => function.r#type().clone(),
|
||||
Value::String(_) => Type::String,
|
||||
Value::Float(_) => Type::Float,
|
||||
|
@ -441,6 +441,27 @@ mod type_definition {
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn argument_count_check() {
|
||||
let result = interpret(
|
||||
"
|
||||
foo = (x <int>) <bool> {
|
||||
x
|
||||
}
|
||||
foo()
|
||||
",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Err(Error::ExpectedFunctionArgumentAmount {
|
||||
source: "foo".to_string(),
|
||||
expected: 1,
|
||||
actual: 0
|
||||
}),
|
||||
result
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callback_type_check() {
|
||||
let result = interpret(
|
||||
|
@ -352,6 +352,12 @@ module.exports = grammar({
|
||||
'num',
|
||||
'str',
|
||||
seq('[', $.type, ']'),
|
||||
seq(
|
||||
'{',
|
||||
$.identifier,
|
||||
$.type_definition,
|
||||
'}',
|
||||
),
|
||||
seq(
|
||||
'(',
|
||||
repeat(
|
||||
|
@ -1132,6 +1132,27 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "{"
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "identifier"
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "type_definition"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
|
@ -541,9 +541,17 @@
|
||||
"multiple": true,
|
||||
"required": false,
|
||||
"types": [
|
||||
{
|
||||
"type": "identifier",
|
||||
"named": true
|
||||
},
|
||||
{
|
||||
"type": "type",
|
||||
"named": true
|
||||
},
|
||||
{
|
||||
"type": "type_definition",
|
||||
"named": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user