1
0

Continue implementing type checks

This commit is contained in:
Jeff 2023-12-02 02:34:23 -05:00
parent 9181c319b8
commit 7f1b53aabe
23 changed files with 7672 additions and 7816 deletions

View File

@ -2,8 +2,10 @@ cast = (download "https://api.sampleapis.com/futurama/cast")
characters = (download "https://api.sampleapis.com/futurama/characters")
episodes = (download "https://api.sampleapis.com/futurama/episodes")
cast_len = (length (from_json cast))
characters_len = (length (from_json characters))
episodes_len = (length (from_json episodes))
async {
cast_len = (length (from_json cast))
characters_len = (length (from_json characters))
episodes_len = (length (from_json episodes))
}
(output [cast_len, characters_len, episodes_len])

View File

@ -29,7 +29,7 @@ impl AbstractTree for Assignment {
let type_node = node.child(1);
let type_definition = if let Some(type_node) = type_node {
if type_node.kind() == "type_defintion" {
if type_node.kind() == "type_definition" {
Some(TypeDefinition::from_syntax_node(
source, type_node, context,
)?)
@ -63,35 +63,30 @@ impl AbstractTree for Assignment {
match operator {
AssignmentOperator::Equal => {
type_definition.abstract_check(
&statement_type,
context,
statement_node,
source,
)?;
type_definition.check(&statement_type, context, statement_node, source)?;
}
AssignmentOperator::PlusEqual => {
let identifier_type = identifier.expected_type(context)?;
type_definition.abstract_check(
&identifier_type,
context,
type_node.unwrap(),
source,
)?;
if let Type::List(item_type) = type_definition.inner() {
let item_type_definition = TypeDefinition::new(*item_type.clone());
let type_definition = if let Type::List(item_type) = type_definition.inner() {
TypeDefinition::new(item_type.as_ref().clone())
} else {
type_definition.clone()
};
type_definition.abstract_check(
item_type_definition.check(&identifier_type, context, node, source)?;
item_type_definition.check(
&statement_type,
context,
statement_node,
source,
)?;
} else {
type_definition.check(
&identifier_type,
context,
identifier_node,
source,
)?;
type_definition.check(&statement_type, context, statement_node, source)?;
}
}
AssignmentOperator::MinusEqual => todo!(),
}
@ -112,22 +107,6 @@ impl AbstractTree for Assignment {
let new_value = match self.operator {
AssignmentOperator::PlusEqual => {
if let Some(mut previous_value) = context.variables()?.get(key).cloned() {
if let Ok(list) = previous_value.as_list() {
let item_type = if let Some(type_defintion) = &self.type_definition {
if let Type::List(item_type) = type_defintion.inner() {
TypeDefinition::new(item_type.as_ref().clone())
} else {
TypeDefinition::new(Type::Empty)
}
} else if let Some(first) = list.items().first() {
first.r#type(context)?
} else {
TypeDefinition::new(Type::Any)
};
item_type.runtime_check(&value.r#type(context)?, context)?;
}
previous_value += value;
previous_value
} else {
@ -145,12 +124,6 @@ impl AbstractTree for Assignment {
AssignmentOperator::Equal => value,
};
if let Some(type_definition) = &self.type_definition {
let new_value_type = new_value.r#type(context)?;
type_definition.runtime_check(&new_value_type, context)?;
}
context.variables_mut()?.insert(key.clone(), new_value);
Ok(Value::Empty)
@ -183,7 +156,7 @@ mod tests {
fn list_add_assign() {
let test = evaluate(
"
x <list int> = []
x <[int]> = []
x += 1
x
",

View File

@ -2,21 +2,21 @@ use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{
AbstractTree, Error, Identifier, Map, Result, TypeDefinition, Value, BUILT_IN_FUNCTIONS,
AbstractTree, Error, Map, Result, TypeDefinition, Value, ValueNode, BUILT_IN_FUNCTIONS,
};
use super::expression::Expression;
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct FunctionCall {
function_name: Identifier,
function_expression: Expression,
arguments: Vec<Expression>,
}
impl FunctionCall {
pub fn new(function_name: Identifier, arguments: Vec<Expression>) -> Self {
pub fn new(function_expression: Expression, arguments: Vec<Expression>) -> Self {
Self {
function_name,
function_expression,
arguments,
}
}
@ -26,8 +26,8 @@ impl AbstractTree for FunctionCall {
fn from_syntax_node(source: &str, node: Node, context: &Map) -> Result<Self> {
debug_assert_eq!("function_call", node.kind());
let identifier_node = node.child(1).unwrap();
let function_name = Identifier::from_syntax_node(source, identifier_node, context)?;
let expression_node = node.child(1).unwrap();
let function_expression = Expression::from_syntax_node(source, expression_node, context)?;
let mut arguments = Vec::new();
@ -41,13 +41,13 @@ impl AbstractTree for FunctionCall {
}
}
let function_type = function_name.expected_type(context)?;
let function_type = function_expression.expected_type(context)?;
let function_call = FunctionCall {
function_name,
function_expression,
arguments,
};
function_type.abstract_check(
function_type.check(
&function_call.expected_type(context)?,
context,
node,
@ -58,7 +58,10 @@ impl AbstractTree for FunctionCall {
}
fn run(&self, source: &str, context: &Map) -> Result<Value> {
let key = self.function_name.inner();
let value = match &self.function_expression {
Expression::Value(value_node) => value_node.run(source, context)?,
Expression::Identifier(identifier) => {
let key = identifier.inner();
for built_in_function in BUILT_IN_FUNCTIONS {
if key == built_in_function.name() {
@ -75,26 +78,47 @@ impl AbstractTree for FunctionCall {
}
let variables = context.variables()?;
let function = if let Some(value) = variables.get(key) {
value.as_function()?
if let Some(value) = variables.get(key) {
value.clone()
} else {
return Err(Error::FunctionIdentifierNotFound(
self.function_name.clone(),
));
return Err(Error::FunctionIdentifierNotFound(identifier.clone()));
}
}
Expression::Index(index) => index.run(source, context)?,
Expression::Math(math) => math.run(source, context)?,
Expression::Logic(logic) => logic.run(source, context)?,
Expression::FunctionCall(function_call) => function_call.run(source, context)?,
Expression::Yield(r#yield) => r#yield.run(source, context)?,
};
function.call(&self.arguments, source, context)
value.as_function()?.call(&self.arguments, source, context)
}
fn expected_type(&self, context: &Map) -> Result<TypeDefinition> {
let function_name = self.function_name.inner();
match &self.function_expression {
Expression::Value(value_node) => {
if let ValueNode::Function(function) = value_node {
Ok(function.return_type().clone())
} else {
value_node.expected_type(context)
}
}
Expression::Identifier(identifier) => {
let function_name = identifier.inner();
if let Some(value) = context.variables()?.get(function_name) {
let return_type = value.as_function()?.return_type();
Ok(return_type.clone())
} else {
self.function_name.expected_type(context)
self.function_expression.expected_type(context)
}
}
Expression::Index(index) => index.expected_type(context),
Expression::Math(math) => math.expected_type(context),
Expression::Logic(logic) => logic.expected_type(context),
Expression::FunctionCall(function_call) => function_call.expected_type(context),
Expression::Yield(r#yield) => r#yield.expected_type(context),
}
}
}

View File

@ -47,7 +47,7 @@ impl AbstractTree for Identifier {
}
}
Ok(TypeDefinition::new(Type::Empty))
Ok(TypeDefinition::new(Type::Any))
}
}
}

View File

@ -23,79 +23,14 @@ impl TypeDefinition {
self.r#type
}
pub fn abstract_check(
pub fn check(
&self,
other: &TypeDefinition,
context: &Map,
node: Node,
source: &str,
) -> Result<()> {
self.runtime_check(other, context)
.map_err(|_| Error::TypeCheck {
expected: self.clone(),
actual: other.clone(),
location: node.start_position(),
source: source[node.byte_range()].to_string(),
})
}
pub fn runtime_check(&self, other: &TypeDefinition, context: &Map) -> Result<()> {
// match (&self.r#type, &other.r#type) {
// (Type::Any, _)
// | (_, Type::Any)
// | (Type::Boolean, Type::Boolean)
// | (Type::Empty, Type::Empty)
// | (Type::Float, Type::Float)
// | (Type::Integer, Type::Integer)
// | (Type::Map, Type::Map)
// | (Type::Number, Type::Number)
// | (Type::Number, Type::Integer)
// | (Type::Number, Type::Float)
// | (Type::Integer, Type::Number)
// | (Type::Float, Type::Number)
// | (Type::String, Type::String)
// | (Type::Table, Type::Table) => Ok(()),
// (Type::List(self_item_type), Type::List(other_item_type)) => {
// let self_defintion = TypeDefinition::new(self_item_type.as_ref().clone());
// let other_definition = &TypeDefinition::new(other_item_type.as_ref().clone());
// self_defintion.runtime_check(other_definition, context)
// }
// (
// Type::Function {
// parameter_types: self_parameter_types,
// return_type: self_return_type,
// },
// Type::Function {
// parameter_types: other_parameter_types,
// return_type: other_return_type,
// },
// ) => {
// let parameter_type_pairs = self_parameter_types
// .iter()
// .zip(other_parameter_types.iter());
// for (self_parameter_type, other_parameter_type) in parameter_type_pairs {
// TypeDefinition::new(self_parameter_type.clone()).runtime_check(
// &TypeDefinition::new(other_parameter_type.clone()),
// context,
// )?;
// }
// TypeDefinition::new(self_return_type.as_ref().clone()).runtime_check(
// &TypeDefinition::new(other_return_type.as_ref().clone()),
// context,
// )?;
// Ok(())
// }
// _ => Err(Error::RuntimeTypeCheck {
// expected: self.clone(),
// actual: other.clone(),
// }),
// }
Ok(())
self.r#type.check(&other.r#type, context, node, source)
}
}
@ -142,6 +77,58 @@ pub enum Type {
Table,
}
impl Type {
pub fn check(&self, other: &Type, context: &Map, node: Node, source: &str) -> Result<()> {
match (self, other) {
(Type::Any, _)
| (_, Type::Any)
| (Type::Boolean, Type::Boolean)
| (Type::Empty, Type::Empty)
| (Type::Float, Type::Float)
| (Type::Integer, Type::Integer)
| (Type::Map, Type::Map)
| (Type::Number, Type::Number)
| (Type::Number, Type::Integer)
| (Type::Number, Type::Float)
| (Type::Integer, Type::Number)
| (Type::Float, Type::Number)
| (Type::String, Type::String)
| (Type::Table, Type::Table) => Ok(()),
(Type::List(self_item_type), Type::List(other_item_type)) => {
self_item_type.check(&other_item_type, context, node, source)
}
(
Type::Function {
parameter_types: self_parameter_types,
return_type: self_return_type,
},
Type::Function {
parameter_types: other_parameter_types,
return_type: other_return_type,
},
) => {
let parameter_type_pairs = self_parameter_types
.iter()
.zip(other_parameter_types.iter());
for (self_parameter_type, other_parameter_type) in parameter_type_pairs {
self_parameter_type.check(&other_parameter_type, context, node, source)?;
}
self_return_type.check(other_return_type, context, node, source)?;
Ok(())
}
_ => Err(Error::TypeCheck {
expected: self.clone(),
actual: other.clone(),
location: node.start_position(),
source: source[node.byte_range()].to_string(),
}),
}
}
}
impl AbstractTree for Type {
fn from_syntax_node(source: &str, node: Node, context: &Map) -> Result<Self> {
Error::expect_syntax_node(source, "type", node)?;
@ -182,7 +169,6 @@ impl AbstractTree for Type {
"map" => Type::Map,
"num" => Type::Number,
"str" => Type::String,
"table" => Type::Table,
_ => {
return Err(Error::UnexpectedSyntaxNode {
expected: "any, bool, float, fn, int, list, map, num, str or table",
@ -225,7 +211,7 @@ impl Display for Type {
write!(f, "-> {return_type}")
}
Type::Integer => write!(f, "int"),
Type::List(item_type) => write!(f, "list {item_type}"),
Type::List(item_type) => write!(f, "[{item_type}]"),
Type::Map => write!(f, "map"),
Type::Number => write!(f, "num"),
Type::String => write!(f, "str"),

View File

@ -195,7 +195,7 @@ impl AbstractTree for ValueNode {
if let Some(previous) = previous_type {
if expression_type != previous {
return Ok(TypeDefinition::new(Type::Any));
return Ok(TypeDefinition::new(Type::List(Box::new(Type::Any))));
}
}

View File

@ -1,9 +1,7 @@
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{
AbstractTree, Expression, FunctionCall, Identifier, Map, Result, TypeDefinition, Value,
};
use crate::{AbstractTree, Expression, FunctionCall, Map, Result, TypeDefinition, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Yield {
@ -15,24 +13,24 @@ impl AbstractTree for Yield {
let input_node = node.child(0).unwrap();
let input = Expression::from_syntax_node(source, input_node, context)?;
let function_node = node.child(3).unwrap();
let function = Identifier::from_syntax_node(source, function_node, context)?;
let expression_node = node.child(3).unwrap();
let function_expression = Expression::from_syntax_node(source, expression_node, context)?;
let mut arguments = Vec::new();
arguments.push(input);
for index in 4..node.child_count() - 1 {
let node = node.child(index).unwrap();
let child = node.child(index).unwrap();
if node.is_named() {
let expression = Expression::from_syntax_node(source, node, context)?;
if child.is_named() {
let expression = Expression::from_syntax_node(source, child, context)?;
arguments.push(expression);
}
}
let call = FunctionCall::new(function, arguments);
let call = FunctionCall::new(function_expression, arguments);
Ok(Yield { call })
}

View File

@ -4,6 +4,7 @@ mod assert;
mod collections;
mod data_formats;
mod fs;
mod network;
mod output;
mod random;
mod r#type;
@ -22,7 +23,7 @@ pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 14] = [
&random::RandomBoolean,
&random::RandomFloat,
&random::RandomInteger,
&r#type::Type,
&r#type::TypeFunction,
];
pub trait BuiltInFunction {

View File

@ -0,0 +1,27 @@
use reqwest::blocking::get;
use crate::{BuiltInFunction, Error, Map, Result, Type, TypeDefinition, Value};
pub struct Download;
impl BuiltInFunction for Download {
fn name(&self) -> &'static str {
"download"
}
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
Error::expect_argument_amount(self, 1, arguments.len())?;
let url = arguments.first().unwrap().as_string()?;
let response = get(url)?;
Ok(Value::String(response.text()?))
}
fn type_definition(&self) -> TypeDefinition {
TypeDefinition::new(Type::Function {
parameter_types: vec![Type::String],
return_type: Box::new(Type::String),
})
}
}

View File

@ -1,8 +1,8 @@
use crate::{BuiltInFunction, Error, List, Map, Result, Value};
use crate::{BuiltInFunction, Error, List, Map, Result, Type, TypeDefinition, Value};
pub struct Type;
pub struct TypeFunction;
impl BuiltInFunction for Type {
impl BuiltInFunction for TypeFunction {
fn name(&self) -> &'static str {
"type"
}
@ -33,6 +33,9 @@ impl BuiltInFunction for Type {
}
fn type_definition(&self) -> crate::TypeDefinition {
todo!()
TypeDefinition::new(Type::Function {
parameter_types: vec![Type::String],
return_type: Box::new(Type::Any),
})
}
}

View File

@ -5,7 +5,7 @@
use tree_sitter::{Node, Point};
use crate::{value::Value, BuiltInFunction, Identifier, TypeDefinition};
use crate::{value::Value, BuiltInFunction, Identifier, Type};
use std::{fmt, io, num::ParseFloatError, string::FromUtf8Error, sync::PoisonError, time};
@ -21,15 +21,15 @@ pub enum Error {
},
TypeCheck {
expected: TypeDefinition,
actual: TypeDefinition,
expected: Type,
actual: Type,
location: Point,
source: String,
},
RuntimeTypeCheck {
expected: TypeDefinition,
actual: TypeDefinition,
expected: Type,
actual: Type,
},
/// The 'assert' macro did not resolve successfully.

View File

@ -62,15 +62,11 @@ impl Function {
println!("{key} {value}");
type_definition.runtime_check(&value.r#type(context)?, context)?;
function_context.variables_mut()?.insert(key.clone(), value);
}
let return_value = self.body.run(source, &function_context)?;
self.return_type
.runtime_check(&return_value.r#type(context)?, context)?;
Ok(return_value)
}
}
@ -90,11 +86,11 @@ impl AbstractTree for Function {
(parameter_types, return_type)
} else {
return Err(Error::TypeCheck {
expected: TypeDefinition::new(Type::Function {
expected: Type::Function {
parameter_types: Vec::with_capacity(0),
return_type: Box::new(Type::Empty),
}),
actual: type_definition,
},
actual: type_definition.take_inner(),
location: type_node.start_position(),
source: source[type_node.byte_range()].to_string(),
});

View File

@ -12,7 +12,8 @@ async { (output 'Whaddup') }
(statement
(expression
(function_call
(identifier)
(expression
(identifier))
(expression
(value
(string)))))))))

View File

@ -28,7 +28,8 @@ for i in [1, 2, 3] {
(statement
(expression
(function_call
(identifier)
(expression
(identifier))
(expression
(identifier)))))))))
@ -60,6 +61,7 @@ for list in list_of_lists {
(statement
(expression
(function_call
(identifier)
(expression
(identifier))
(expression
(identifier))))))))))))

View File

@ -58,7 +58,8 @@ Function Call
(statement
(expression
(function_call
(identifier)
(expression
(identifier))
(expression
(value
(string)))))))
@ -89,13 +90,15 @@ Complex Function
(statement
(expression
(function_call
(identifier)
(expression
(identifier))
(expression
(identifier)))))
(statement
(expression
(function_call
(identifier)
(expression
(identifier))
(expression
(identifier)))))))))))
@ -118,7 +121,8 @@ Complex Function Call
(statement
(expression
(function_call
(identifier)
(expression
(identifier))
(expression
(value
(string)))

View File

@ -109,7 +109,8 @@ x:(y):0
(identifier))
(expression
(function_call
(identifier)))))
(expression
(identifier))))))
(expression
(value
(integer)))))))

View File

@ -18,7 +18,8 @@ while true {
(statement
(expression
(function_call
(identifier)
(expression
(identifier))
(expression
(value
(string))))))))))

View File

@ -13,7 +13,8 @@ Simple Yield
(expression
(value
(integer)))
(identifier)))))
(expression
(identifier))))))
================================================================================
Yield Chain
@ -33,6 +34,9 @@ x -> (foo) -> (bar) -> (abc)
(yield
(expression
(identifier))
(identifier)))
(identifier)))
(identifier)))))
(expression
(identifier))))
(expression
(identifier))))
(expression
(identifier))))))

View File

@ -361,7 +361,6 @@ module.exports = grammar({
function: $ =>
seq(
$.type_definition,
seq(
'|',
repeat(
seq(
@ -372,13 +371,12 @@ module.exports = grammar({
'|',
$.block,
),
),
function_call: $ =>
prec.right(
seq(
'(',
$.identifier,
$.expression,
optional($._expression_list),
')',
),
@ -390,7 +388,7 @@ module.exports = grammar({
$.expression,
'->',
'(',
$.identifier,
$.expression,
optional($._expression_list),
')',
),

View File

@ -36,7 +36,6 @@
"if"
"else"
"for"
"transform"
"in"
"function"
] @keyword

View File

@ -1223,7 +1223,7 @@
},
{
"type": "SYMBOL",
"name": "identifier"
"name": "expression"
},
{
"type": "CHOICE",
@ -1264,7 +1264,7 @@
},
{
"type": "SYMBOL",
"name": "identifier"
"name": "expression"
},
{
"type": "CHOICE",

View File

@ -181,10 +181,6 @@
{
"type": "expression",
"named": true
},
{
"type": "identifier",
"named": true
}
]
}
@ -565,10 +561,6 @@
{
"type": "expression",
"named": true
},
{
"type": "identifier",
"named": true
}
]
}

File diff suppressed because it is too large Load Diff