1
0

Implement new type checking

This commit is contained in:
Jeff 2024-01-03 19:57:06 -05:00
parent 4e861620ce
commit ff6cc707d2
21 changed files with 10734 additions and 10460 deletions

View File

@ -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)?;

View File

@ -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;

View File

@ -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()),
}
}

View File

@ -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),

View File

@ -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) => {

View 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())
}
}

View File

@ -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),
}

View File

@ -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>;

View File

@ -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),

View File

@ -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"),

View File

@ -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)?,
};

View File

@ -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}."),
}
}
}

View File

@ -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())

View File

@ -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;

View File

@ -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)
}
}

View File

@ -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,

View File

@ -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(

View File

@ -352,6 +352,12 @@ module.exports = grammar({
'num',
'str',
seq('[', $.type, ']'),
seq(
'{',
$.identifier,
$.type_definition,
'}',
),
seq(
'(',
repeat(

View File

@ -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": [

View File

@ -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