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_node = node.child(0).unwrap();
|
||||||
let identifier = Identifier::from_syntax_node(source, identifier_node, context)?;
|
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_node = node.child(1).unwrap();
|
||||||
let type_definition = if type_node.kind() == "type_definition" {
|
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 = Statement::from_syntax_node(source, statement_node, context)?;
|
||||||
let statement_type = statement.expected_type(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_key = identifier.inner().clone();
|
||||||
let variable_type = if let Some(definition) = &type_definition {
|
let variable_type = if let Some(definition) = &type_definition {
|
||||||
definition.inner().clone()
|
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> {
|
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||||
let key = self.identifier.inner();
|
let key = self.identifier.inner();
|
||||||
let value = self.statement.run(source, context)?;
|
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> {
|
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||||
if self.is_async {
|
if self.is_async {
|
||||||
let statements = &self.statements;
|
let statements = &self.statements;
|
||||||
|
@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize};
|
|||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
built_in_functions::string_functions, AbstractTree, BuiltInFunction, Function, List, Map,
|
built_in_functions::string_functions, AbstractTree, BuiltInFunction, Function, Identifier,
|
||||||
Result, Type, Value,
|
List, Map, Result, Type, TypeDefinition, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
static ARGS: OnceLock<Value> = OnceLock::new();
|
static ARGS: OnceLock<Value> = OnceLock::new();
|
||||||
@ -31,12 +31,29 @@ impl BuiltInValue {
|
|||||||
match self {
|
match self {
|
||||||
BuiltInValue::Args => Type::list(Type::String),
|
BuiltInValue::Args => Type::list(Type::String),
|
||||||
BuiltInValue::AssertEqual => BuiltInFunction::AssertEqual.r#type(),
|
BuiltInValue::AssertEqual => BuiltInFunction::AssertEqual.r#type(),
|
||||||
BuiltInValue::Fs => Type::Map,
|
BuiltInValue::Fs => Type::Map(Vec::new()),
|
||||||
BuiltInValue::Json => Type::Map,
|
BuiltInValue::Json => Type::Map(Vec::new()),
|
||||||
BuiltInValue::Length => BuiltInFunction::Length.r#type(),
|
BuiltInValue::Length => BuiltInFunction::Length.r#type(),
|
||||||
BuiltInValue::Output => BuiltInFunction::Output.r#type(),
|
BuiltInValue::Output => BuiltInFunction::Output.r#type(),
|
||||||
BuiltInValue::Random => Type::Map,
|
BuiltInValue::Random => Type::Map(vec![
|
||||||
BuiltInValue::String => Type::Map,
|
(
|
||||||
|
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)
|
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> {
|
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||||
match self {
|
match self {
|
||||||
Expression::Value(value_node) => value_node.run(source, context),
|
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_node = node.child(0).unwrap();
|
||||||
let function_expression =
|
let function_expression =
|
||||||
FunctionExpression::from_syntax_node(source, function_node, context)?;
|
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();
|
let mut arguments = Vec::new();
|
||||||
|
|
||||||
for index in 2..node.child_count() - 1 {
|
for index in 2..node.child_count() - 1 {
|
||||||
@ -35,48 +33,47 @@ impl AbstractTree for FunctionCall {
|
|||||||
|
|
||||||
if child.is_named() {
|
if child.is_named() {
|
||||||
let expression = Expression::from_syntax_node(source, child, context)?;
|
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);
|
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 {
|
Ok(FunctionCall {
|
||||||
function_expression,
|
function_expression,
|
||||||
arguments,
|
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> {
|
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||||
let (name, value) = match &self.function_expression {
|
let (name, value) = match &self.function_expression {
|
||||||
FunctionExpression::Identifier(identifier) => {
|
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> {
|
fn expected_type(&self, context: &Map) -> Result<Type> {
|
||||||
match self.collection.expected_type(context)? {
|
match self.collection.expected_type(context)? {
|
||||||
Type::List(item_type) => Ok(*item_type.clone()),
|
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),
|
Type::None => Ok(Type::None),
|
||||||
r#type => Ok(r#type),
|
r#type => Ok(r#type),
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ pub mod expression;
|
|||||||
pub mod r#for;
|
pub mod r#for;
|
||||||
pub mod function_call;
|
pub mod function_call;
|
||||||
pub mod function_expression;
|
pub mod function_expression;
|
||||||
|
pub mod function_node;
|
||||||
pub mod identifier;
|
pub mod identifier;
|
||||||
pub mod if_else;
|
pub mod if_else;
|
||||||
pub mod index;
|
pub mod index;
|
||||||
@ -29,9 +30,9 @@ pub mod r#yield;
|
|||||||
|
|
||||||
pub use {
|
pub use {
|
||||||
assignment::*, block::*, built_in_value::*, expression::*, function_call::*,
|
assignment::*, block::*, built_in_value::*, expression::*, function_call::*,
|
||||||
function_expression::*, identifier::*, if_else::*, index::*, index_assignment::IndexAssignment,
|
function_expression::*, function_node::*, identifier::*, if_else::*, index::*,
|
||||||
index_expression::*, logic::*, math::*, r#for::*, r#match::*, r#while::*, r#yield::*,
|
index_assignment::IndexAssignment, index_expression::*, logic::*, math::*, r#for::*,
|
||||||
statement::*, type_definition::*, value_node::*,
|
r#match::*, r#while::*, r#yield::*, statement::*, type_definition::*, value_node::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
@ -59,6 +60,18 @@ impl AbstractTree for Root {
|
|||||||
Ok(Root { statements })
|
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> {
|
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||||
let mut value = Value::none();
|
let mut value = Value::none();
|
||||||
|
|
||||||
@ -92,6 +105,11 @@ pub trait AbstractTree: Sized {
|
|||||||
/// node's byte range.
|
/// node's byte range.
|
||||||
fn from_syntax_node(source: &str, node: Node, context: &Map) -> Result<Self>;
|
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.
|
/// Execute dust code by traversing the tree.
|
||||||
fn run(&self, source: &str, context: &Map) -> Result<Value>;
|
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> {
|
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||||
match self {
|
match self {
|
||||||
Statement::Assignment(assignment) => assignment.run(source, context),
|
Statement::Assignment(assignment) => assignment.run(source, context),
|
||||||
|
@ -3,7 +3,7 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tree_sitter::Node;
|
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)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct TypeDefinition {
|
pub struct TypeDefinition {
|
||||||
@ -61,7 +61,7 @@ pub enum Type {
|
|||||||
},
|
},
|
||||||
Integer,
|
Integer,
|
||||||
List(Box<Type>),
|
List(Box<Type>),
|
||||||
Map,
|
Map(Vec<(Identifier, TypeDefinition)>),
|
||||||
None,
|
None,
|
||||||
Number,
|
Number,
|
||||||
String,
|
String,
|
||||||
@ -92,13 +92,12 @@ impl Type {
|
|||||||
| (Type::Collection, Type::Collection)
|
| (Type::Collection, Type::Collection)
|
||||||
| (Type::Collection, Type::List(_))
|
| (Type::Collection, Type::List(_))
|
||||||
| (Type::List(_), Type::Collection)
|
| (Type::List(_), Type::Collection)
|
||||||
| (Type::Collection, Type::Map)
|
| (Type::Collection, Type::Map(_))
|
||||||
| (Type::Map, Type::Collection)
|
| (Type::Map(_), Type::Collection)
|
||||||
| (Type::Collection, Type::String)
|
| (Type::Collection, Type::String)
|
||||||
| (Type::String, Type::Collection)
|
| (Type::String, Type::Collection)
|
||||||
| (Type::Float, Type::Float)
|
| (Type::Float, Type::Float)
|
||||||
| (Type::Integer, Type::Integer)
|
| (Type::Integer, Type::Integer)
|
||||||
| (Type::Map, Type::Map)
|
|
||||||
| (Type::Number, Type::Number)
|
| (Type::Number, Type::Number)
|
||||||
| (Type::Number, Type::Integer)
|
| (Type::Number, Type::Integer)
|
||||||
| (Type::Number, Type::Float)
|
| (Type::Number, Type::Float)
|
||||||
@ -106,6 +105,16 @@ impl Type {
|
|||||||
| (Type::Float, Type::Number)
|
| (Type::Float, Type::Number)
|
||||||
| (Type::None, Type::None)
|
| (Type::None, Type::None)
|
||||||
| (Type::String, Type::String) => Ok(()),
|
| (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)) => {
|
(Type::Option(left), Type::Option(right)) => {
|
||||||
if left == right {
|
if left == right {
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -172,16 +181,15 @@ impl Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractTree for Type {
|
impl AbstractTree for Type {
|
||||||
fn from_syntax_node(source: &str, node: Node, _context: &Map) -> Result<Self> {
|
fn from_syntax_node(_source: &str, node: Node, _context: &Map) -> Result<Self> {
|
||||||
Error::expect_syntax_node(source, "type", node)?;
|
Error::expect_syntax_node(_source, "type", node)?;
|
||||||
|
|
||||||
let type_node = node.child(0).unwrap();
|
let type_node = node.child(0).unwrap();
|
||||||
|
|
||||||
let r#type =
|
let r#type = match type_node.kind() {
|
||||||
match type_node.kind() {
|
|
||||||
"[" => {
|
"[" => {
|
||||||
let item_type_node = node.child(1).unwrap();
|
let item_type_node = node.child(1).unwrap();
|
||||||
let item_type = Type::from_syntax_node(source, item_type_node, _context)?;
|
let item_type = Type::from_syntax_node(_source, item_type_node, _context)?;
|
||||||
|
|
||||||
Type::List(Box::new(item_type))
|
Type::List(Box::new(item_type))
|
||||||
}
|
}
|
||||||
@ -197,7 +205,7 @@ impl AbstractTree for Type {
|
|||||||
let child = node.child(index).unwrap();
|
let child = node.child(index).unwrap();
|
||||||
|
|
||||||
if child.is_named() {
|
if child.is_named() {
|
||||||
let parameter_type = Type::from_syntax_node(source, child, _context)?;
|
let parameter_type = Type::from_syntax_node(_source, child, _context)?;
|
||||||
|
|
||||||
parameter_types.push(parameter_type);
|
parameter_types.push(parameter_type);
|
||||||
}
|
}
|
||||||
@ -205,7 +213,7 @@ impl AbstractTree for Type {
|
|||||||
|
|
||||||
let final_node = node.child(child_count - 1).unwrap();
|
let final_node = node.child(child_count - 1).unwrap();
|
||||||
let return_type = if final_node.is_named() {
|
let return_type = if final_node.is_named() {
|
||||||
Type::from_syntax_node(source, final_node, _context)?
|
Type::from_syntax_node(_source, final_node, _context)?
|
||||||
} else {
|
} else {
|
||||||
Type::None
|
Type::None
|
||||||
};
|
};
|
||||||
@ -216,24 +224,51 @@ impl AbstractTree for Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"int" => Type::Integer,
|
"int" => Type::Integer,
|
||||||
"map" => Type::Map,
|
|
||||||
"num" => Type::Number,
|
"num" => Type::Number,
|
||||||
"none" => Type::None,
|
"none" => Type::None,
|
||||||
"str" => Type::String,
|
"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)?;
|
||||||
|
|
||||||
|
identifier_types.push((identifier.clone(), type_definition));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Type::Map(identifier_types)
|
||||||
|
}
|
||||||
"option" => {
|
"option" => {
|
||||||
let inner_type_node = node.child(2).unwrap();
|
let inner_type_node = node.child(2).unwrap();
|
||||||
let inner_type = Type::from_syntax_node(source, inner_type_node, _context)?;
|
let inner_type = Type::from_syntax_node(_source, inner_type_node, _context)?;
|
||||||
|
|
||||||
Type::Option(Box::new(inner_type))
|
Type::Option(Box::new(inner_type))
|
||||||
}
|
}
|
||||||
_ => return Err(Error::UnexpectedSyntaxNode {
|
_ => {
|
||||||
expected:
|
return Err(Error::UnexpectedSyntaxNode {
|
||||||
"any, bool, collection, float, function, int, list, map, num, str or option"
|
expected: "any, bool, float, int, num, str, option, (, [ or {".to_string(),
|
||||||
.to_string(),
|
|
||||||
actual: type_node.kind().to_string(),
|
actual: type_node.kind().to_string(),
|
||||||
location: type_node.start_position(),
|
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)
|
Ok(r#type)
|
||||||
@ -274,7 +309,15 @@ impl Display for Type {
|
|||||||
}
|
}
|
||||||
Type::Integer => write!(f, "int"),
|
Type::Integer => write!(f, "int"),
|
||||||
Type::List(item_type) => write!(f, "[{item_type}]"),
|
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::Number => write!(f, "num"),
|
||||||
Type::None => write!(f, "none"),
|
Type::None => write!(f, "none"),
|
||||||
Type::String => write!(f, "str"),
|
Type::String => write!(f, "str"),
|
||||||
|
@ -203,7 +203,18 @@ impl AbstractTree for ValueNode {
|
|||||||
Type::None
|
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)?,
|
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,
|
actual: usize,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ExpectedFunctionType {
|
||||||
|
actual: Type,
|
||||||
|
},
|
||||||
|
|
||||||
ExpectedString {
|
ExpectedString {
|
||||||
actual: Value,
|
actual: Value,
|
||||||
},
|
},
|
||||||
@ -217,16 +221,8 @@ impl Error {
|
|||||||
|
|
||||||
pub fn is_type_check_error(&self, other: &Error) -> bool {
|
pub fn is_type_check_error(&self, other: &Error) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Error::WithContext { error, .. } => {
|
Error::WithContext { error, .. } => error.as_ref() == other,
|
||||||
debug_assert_eq!(error.as_ref(), other);
|
_ => self == other,
|
||||||
|
|
||||||
error.as_ref() == other
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
debug_assert_eq!(self, other);
|
|
||||||
|
|
||||||
self == other
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -438,6 +434,7 @@ impl fmt::Display for Error {
|
|||||||
f,
|
f,
|
||||||
"Parsing was cancelled either manually or because it took too long."
|
"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 {
|
if let Some(abstract_tree) = &self.abstract_tree {
|
||||||
|
abstract_tree.check_type(&self.context)?;
|
||||||
abstract_tree.run(source, &self.context)
|
abstract_tree.run(source, &self.context)
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::none())
|
Ok(Value::none())
|
||||||
|
@ -8,12 +8,7 @@ pub use crate::{
|
|||||||
built_in_functions::BuiltInFunction,
|
built_in_functions::BuiltInFunction,
|
||||||
error::*,
|
error::*,
|
||||||
interpret::*,
|
interpret::*,
|
||||||
value::{
|
value::{function::Function, list::List, map::Map, Value},
|
||||||
function::{ContextDefinedFunction, Function},
|
|
||||||
list::List,
|
|
||||||
map::Map,
|
|
||||||
Value,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mod abstract_tree;
|
mod abstract_tree;
|
||||||
|
@ -3,15 +3,12 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use crate::{
|
use crate::{AbstractTree, BuiltInFunction, FunctionNode, Map, Result, Type, Value};
|
||||||
AbstractTree, Block, BuiltInFunction, Error, Identifier, Map, Result, Type, TypeDefinition,
|
|
||||||
Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum Function {
|
pub enum Function {
|
||||||
BuiltIn(BuiltInFunction),
|
BuiltIn(BuiltInFunction),
|
||||||
ContextDefined(ContextDefinedFunction),
|
ContextDefined(FunctionNode),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Function {
|
impl Display for Function {
|
||||||
@ -54,57 +51,19 @@ impl Function {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractTree for Function {
|
impl AbstractTree for Function {
|
||||||
fn from_syntax_node(source: &str, node: Node, context: &Map) -> Result<Self> {
|
fn from_syntax_node(_source: &str, _node: Node, _context: &Map) -> Result<Self> {
|
||||||
Error::expect_syntax_node(source, "function", node)?;
|
let inner_function = FunctionNode::from_syntax_node(_source, _node, _context)?;
|
||||||
|
|
||||||
let child_count = node.child_count();
|
Ok(Function::ContextDefined(inner_function))
|
||||||
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" {
|
fn check_type(&self, _context: &Map) -> Result<()> {
|
||||||
let type_definition = TypeDefinition::from_syntax_node(source, child, context)?;
|
match self {
|
||||||
|
Function::BuiltIn(_) => Ok(()),
|
||||||
parameter_types.push(type_definition.take_inner());
|
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> {
|
fn run(&self, _source: &str, _context: &Map) -> Result<Value> {
|
||||||
Ok(Value::Function(self.clone()))
|
Ok(Value::Function(self.clone()))
|
||||||
}
|
}
|
||||||
@ -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.
|
//! Types that represent runtime values.
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
Function, List, Map, Type,
|
Function, Identifier, List, Map, Type, TypeDefinition,
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::{
|
use serde::{
|
||||||
@ -74,7 +74,18 @@ impl Value {
|
|||||||
Type::List(Box::new(Type::Any))
|
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::Function(function) => function.r#type().clone(),
|
||||||
Value::String(_) => Type::String,
|
Value::String(_) => Type::String,
|
||||||
Value::Float(_) => Type::Float,
|
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]
|
#[test]
|
||||||
fn callback_type_check() {
|
fn callback_type_check() {
|
||||||
let result = interpret(
|
let result = interpret(
|
||||||
|
@ -352,6 +352,12 @@ module.exports = grammar({
|
|||||||
'num',
|
'num',
|
||||||
'str',
|
'str',
|
||||||
seq('[', $.type, ']'),
|
seq('[', $.type, ']'),
|
||||||
|
seq(
|
||||||
|
'{',
|
||||||
|
$.identifier,
|
||||||
|
$.type_definition,
|
||||||
|
'}',
|
||||||
|
),
|
||||||
seq(
|
seq(
|
||||||
'(',
|
'(',
|
||||||
repeat(
|
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",
|
"type": "SEQ",
|
||||||
"members": [
|
"members": [
|
||||||
|
@ -541,9 +541,17 @@
|
|||||||
"multiple": true,
|
"multiple": true,
|
||||||
"required": false,
|
"required": false,
|
||||||
"types": [
|
"types": [
|
||||||
|
{
|
||||||
|
"type": "identifier",
|
||||||
|
"named": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "type",
|
"type": "type",
|
||||||
"named": true
|
"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