1
0

Fix async/function bug; Remove Table; Add packages

This commit is contained in:
Jeff 2023-12-22 15:02:22 -05:00
parent afa937a697
commit 364ce9cb33
29 changed files with 14514 additions and 13072 deletions

View File

@ -35,7 +35,10 @@ impl AbstractTree for Assignment {
source, type_node, context, source, type_node, context,
)?) )?)
} else { } else {
None context
.variables()?
.get(identifier.inner())
.map(|(_, r#type)| TypeDefinition::new(r#type.clone()))
} }
} else { } else {
None None
@ -83,10 +86,17 @@ impl AbstractTree for Assignment {
AssignmentOperator::MinusEqual => todo!(), AssignmentOperator::MinusEqual => todo!(),
} }
} else { } else {
if let Type::List(item_type) = identifier_type { match operator {
item_type AssignmentOperator::Equal => {}
.check(&statement_type) AssignmentOperator::PlusEqual => {
.map_err(|error| error.at_node(statement_node, source))?; if let Type::List(item_type) = identifier_type {
println!("{item_type} {statement_type}");
item_type
.check(&statement_type)
.map_err(|error| error.at_node(statement_node, source))?;
}
}
AssignmentOperator::MinusEqual => todo!(),
} }
} }
@ -97,7 +107,7 @@ impl AbstractTree for Assignment {
statement_type statement_type
}; };
context.set(variable_key, Value::Empty, Some(variable_type))?; context.set(variable_key, Value::Option(None), Some(variable_type))?;
Ok(Assignment { Ok(Assignment {
identifier, identifier,
@ -137,7 +147,7 @@ impl AbstractTree for Assignment {
context.set(key.clone(), new_value, None)?; context.set(key.clone(), new_value, None)?;
} }
Ok(Value::Empty) Ok(Value::Option(None))
} }
fn expected_type(&self, _context: &Map) -> Result<Type> { fn expected_type(&self, _context: &Map) -> Result<Type> {

View File

@ -52,7 +52,7 @@ impl AbstractTree for Block {
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;
let final_result = RwLock::new(Ok(Value::Empty)); let final_result = RwLock::new(Ok(Value::Option(None)));
statements statements
.into_par_iter() .into_par_iter()
@ -86,7 +86,7 @@ impl AbstractTree for Block {
prev_result = Some(statement.run(source, context)); prev_result = Some(statement.run(source, context));
} }
prev_result.unwrap_or(Ok(Value::Empty)) prev_result.unwrap_or(Ok(Value::Option(None)))
} }
} }

View File

@ -71,7 +71,7 @@ impl AbstractTree for For {
} }
} }
Ok(Value::Empty) Ok(Value::Option(None))
} }
fn expected_type(&self, _context: &Map) -> Result<Type> { fn expected_type(&self, _context: &Map) -> Result<Type> {

View File

@ -39,26 +39,33 @@ impl AbstractTree for FunctionCall {
let argument_index = arguments.len(); let argument_index = arguments.len();
if let Type::Function { if let Type::Function {
parameter_types, parameter_types, ..
return_type: _,
} = &function_type } = &function_type
{ {
let expected_type = parameter_types.get(argument_index).unwrap(); if let Some(r#type) = parameter_types.get(argument_index) {
let expected_type = if let Type::List(item_type) = expected_type { r#type
item_type .check(&expression_type)
} else { .map_err(|error| error.at_node(child, source))?;
expected_type }
};
expected_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() != parameter_types.len() {
return Err(Error::ExpectedFunctionArgumentAmount {
source: source[expression_node.byte_range()].to_string(),
expected: parameter_types.len(),
actual: arguments.len(),
});
}
}
Ok(FunctionCall { Ok(FunctionCall {
function_expression, function_expression,
arguments, arguments,
@ -110,7 +117,7 @@ impl AbstractTree for FunctionCall {
arguments.push(value); arguments.push(value);
} }
value.as_function()?.call(&arguments, source, context) value.as_function()?.call(&arguments, source)
} }
fn expected_type(&self, context: &Map) -> Result<Type> { fn expected_type(&self, context: &Map) -> Result<Type> {
@ -183,6 +190,6 @@ mod tests {
#[test] #[test]
fn evaluate_built_in_function_call() { fn evaluate_built_in_function_call() {
assert_eq!(evaluate("(output 'Hiya')"), Ok(Value::Empty)); assert_eq!(evaluate("(output 'Hiya')"), Ok(Value::Option(None)));
} }
} }

View File

@ -76,7 +76,7 @@ impl AbstractTree for IfElse {
if let Some(block) = &self.else_block { if let Some(block) = &self.else_block {
block.run(source, context) block.run(source, context)
} else { } else {
Ok(Value::Empty) Ok(Value::Option(None))
} }
} }
} }

View File

@ -70,7 +70,7 @@ impl AbstractTree for IndexAssignment {
previous_value += value; previous_value += value;
previous_value previous_value
} else { } else {
Value::Empty Value::Option(None)
} }
} }
AssignmentOperator::MinusEqual => { AssignmentOperator::MinusEqual => {
@ -80,7 +80,7 @@ impl AbstractTree for IndexAssignment {
previous_value -= value; previous_value -= value;
previous_value previous_value
} else { } else {
Value::Empty Value::Option(None)
} }
} }
AssignmentOperator::Equal => value, AssignmentOperator::Equal => value,
@ -88,7 +88,7 @@ impl AbstractTree for IndexAssignment {
index_context.set(index_key.clone(), new_value, None)?; index_context.set(index_key.clone(), new_value, None)?;
Ok(Value::Empty) Ok(Value::Option(None))
} }
fn expected_type(&self, _context: &Map) -> Result<Type> { fn expected_type(&self, _context: &Map) -> Result<Type> {

View File

@ -72,7 +72,7 @@ impl AbstractTree for Match {
if let Some(fallback) = &self.fallback { if let Some(fallback) = &self.fallback {
fallback.run(source, context) fallback.run(source, context)
} else { } else {
Ok(Value::Empty) Ok(Value::Option(None))
} }
} }

View File

@ -57,7 +57,7 @@ impl AbstractTree for Root {
} }
fn run(&self, source: &str, context: &Map) -> Result<Value> { fn run(&self, source: &str, context: &Map) -> Result<Value> {
let mut value = Value::Empty; let mut value = Value::Option(None);
for statement in &self.statements { for statement in &self.statements {
value = statement.run(source, context)?; value = statement.run(source, context)?;

View File

@ -64,6 +64,7 @@ pub enum Type {
Map, Map,
Number, Number,
String, String,
Option(Option<Box<Type>>),
} }
impl Type { impl Type {
@ -82,6 +83,16 @@ impl Type {
| (Type::Integer, Type::Number) | (Type::Integer, Type::Number)
| (Type::Float, Type::Number) | (Type::Float, Type::Number)
| (Type::String, Type::String) => Ok(()), | (Type::String, Type::String) => Ok(()),
(Type::Option(left), Type::Option(right)) => {
if left == right {
Ok(())
} else {
Err(Error::TypeCheck {
expected: self.clone(),
actual: other.clone(),
})
}
}
(Type::List(self_item_type), Type::List(other_item_type)) => { (Type::List(self_item_type), Type::List(other_item_type)) => {
if self_item_type.check(&other_item_type).is_err() { if self_item_type.check(&other_item_type).is_err() {
Err(Error::TypeCheck { Err(Error::TypeCheck {
@ -178,9 +189,15 @@ impl AbstractTree for Type {
"map" => Type::Map, "map" => Type::Map,
"num" => Type::Number, "num" => Type::Number,
"str" => Type::String, "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(Some(Box::new(inner_type)))
}
_ => { _ => {
return Err(Error::UnexpectedSyntaxNode { return Err(Error::UnexpectedSyntaxNode {
expected: "any, bool, float, fn, int, list, map, num or str", expected: "any, bool, float, function, int, list, map, num, str or option",
actual: type_node.kind(), actual: type_node.kind(),
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(),
@ -192,7 +209,7 @@ impl AbstractTree for Type {
} }
fn run(&self, _source: &str, _context: &Map) -> Result<Value> { fn run(&self, _source: &str, _context: &Map) -> Result<Value> {
Ok(Value::Empty) Ok(Value::Option(None))
} }
fn expected_type(&self, _context: &Map) -> Result<Type> { fn expected_type(&self, _context: &Map) -> Result<Type> {
@ -229,6 +246,13 @@ impl Display for Type {
Type::Map => write!(f, "map"), Type::Map => write!(f, "map"),
Type::Number => write!(f, "num"), Type::Number => write!(f, "num"),
Type::String => write!(f, "str"), Type::String => write!(f, "str"),
Type::Option(option) => {
if let Some(r#type) = option {
write!(f, "some({})", r#type)
} else {
write!(f, "none")
}
}
} }
} }
} }

View File

@ -16,7 +16,7 @@ pub enum ValueNode {
Integer(String), Integer(String),
String(String), String(String),
List(Vec<Expression>), List(Vec<Expression>),
Empty, Option(Option<Box<Expression>>),
Map(BTreeMap<String, (Statement, Option<Type>)>), Map(BTreeMap<String, (Statement, Option<Type>)>),
} }
@ -50,14 +50,14 @@ impl AbstractTree for ValueNode {
} }
} }
let function_context = Map::clone_from(context)?; let function_context_types = Map::clone_from(context)?;
for (parameter_name, parameter_type) in for (parameter_name, parameter_type) in
parameters.iter().zip(parameter_types.iter()) parameters.iter().zip(parameter_types.iter())
{ {
function_context.set( function_context_types.set(
parameter_name.inner().clone(), parameter_name.inner().clone(),
Value::Empty, Value::Option(None),
Some(parameter_type.clone()), Some(parameter_type.clone()),
)?; )?;
} }
@ -67,19 +67,14 @@ impl AbstractTree for ValueNode {
TypeDefinition::from_syntax_node(source, return_type_node, context)?; TypeDefinition::from_syntax_node(source, return_type_node, context)?;
let body_node = child.child(child_count - 1).unwrap(); let body_node = child.child(child_count - 1).unwrap();
let body = Block::from_syntax_node(source, body_node, &function_context)?; let body = Block::from_syntax_node(source, body_node, &function_context_types)?;
let r#type = Type::Function { let r#type = Type::Function {
parameter_types, parameter_types,
return_type: Box::new(return_type.take_inner()), return_type: Box::new(return_type.take_inner()),
}; };
ValueNode::Function(Function::new( ValueNode::Function(Function::new(parameters, body, Some(r#type)))
parameters,
body,
Some(r#type),
function_context,
))
} }
"integer" => ValueNode::Integer(source[child.byte_range()].to_string()), "integer" => ValueNode::Integer(source[child.byte_range()].to_string()),
"string" => { "string" => {
@ -138,9 +133,22 @@ impl AbstractTree for ValueNode {
ValueNode::Map(child_nodes) ValueNode::Map(child_nodes)
} }
"option" => {
let first_grandchild = child.child(0).unwrap();
if first_grandchild.kind() == "none" {
ValueNode::Option(None)
} else {
let expression_node = child.child(2).unwrap();
let expression =
Expression::from_syntax_node(source, expression_node, context)?;
ValueNode::Option(Some(Box::new(expression)))
}
}
_ => { _ => {
return Err(Error::UnexpectedSyntaxNode { return Err(Error::UnexpectedSyntaxNode {
expected: "string, integer, float, boolean, list, map, or empty", expected: "string, integer, float, boolean, list, map, or option",
actual: child.kind(), actual: child.kind(),
location: child.start_position(), location: child.start_position(),
relevant_source: source[child.byte_range()].to_string(), relevant_source: source[child.byte_range()].to_string(),
@ -169,7 +177,15 @@ impl AbstractTree for ValueNode {
Value::List(List::with_items(values)) Value::List(List::with_items(values))
} }
ValueNode::Empty => Value::Empty, ValueNode::Option(option) => {
let option_value = if let Some(expression) = option {
Some(Box::new(expression.run(source, context)?))
} else {
None
};
Value::Option(option_value)
}
ValueNode::Map(key_statement_pairs) => { ValueNode::Map(key_statement_pairs) => {
let map = Map::new(); let map = Map::new();
@ -216,7 +232,13 @@ impl AbstractTree for ValueNode {
Type::List(Box::new(Type::Any)) Type::List(Box::new(Type::Any))
} }
} }
ValueNode::Empty => Type::Any, ValueNode::Option(option) => {
if let Some(expression) = option {
Type::Option(Some(Box::new(expression.expected_type(context)?)))
} else {
Type::Option(None)
}
}
ValueNode::Map(_) => Type::Map, ValueNode::Map(_) => Type::Map,
}; };
@ -231,8 +253,8 @@ mod tests {
#[test] #[test]
fn evaluate_empty() { fn evaluate_empty() {
assert_eq!(evaluate("x = 9"), Ok(Value::Empty)); assert_eq!(evaluate("x = 9"), Ok(Value::Option(None)));
assert_eq!(evaluate("x = 1 + 1"), Ok(Value::Empty)); assert_eq!(evaluate("x = 1 + 1"), Ok(Value::Option(None)));
} }
#[test] #[test]
@ -333,4 +355,11 @@ mod tests {
); );
assert_eq!(Ok(&Type::Boolean), function.return_type()); assert_eq!(Ok(&Type::Boolean), function.return_type());
} }
#[test]
fn evaluate_option() {
let result = evaluate("x <option(int)> = some(1); x").unwrap();
assert_eq!(Value::Option(Some(Box::new(Value::Integer(1)))), result);
}
} }

View File

@ -30,7 +30,7 @@ impl AbstractTree for While {
self.block.run(source, context)?; self.block.run(source, context)?;
} }
Ok(Value::Empty) Ok(Value::Option(None))
} }
fn expected_type(&self, context: &Map) -> Result<Type> { fn expected_type(&self, context: &Map) -> Result<Type> {
@ -40,10 +40,10 @@ impl AbstractTree for While {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::evaluate; use crate::{evaluate, Value};
#[test] #[test]
fn evalualate_while_loop() { fn evalualate_while_loop() {
assert_eq!(evaluate("while false { 'foo' }"), Ok(crate::Value::Empty)) assert_eq!(evaluate("while false { 'foo' }"), Ok(Value::Option(None)))
} }
} }

View File

@ -14,7 +14,7 @@ impl BuiltInFunction for Assert {
} }
} }
Ok(Value::Empty) Ok(Value::Option(None))
} }
fn r#type(&self) -> Type { fn r#type(&self) -> Type {
@ -39,7 +39,7 @@ impl BuiltInFunction for AssertEqual {
let right = arguments.get(1).unwrap(); let right = arguments.get(1).unwrap();
if left == right { if left == right {
Ok(Value::Empty) Ok(Value::Option(None))
} else { } else {
Err(Error::AssertEqualFailed { Err(Error::AssertEqualFailed {
expected: left.clone(), expected: left.clone(),

View File

@ -2,6 +2,37 @@ use std::process::Command;
use crate::{BuiltInFunction, Error, Map, Result, Type, Value}; use crate::{BuiltInFunction, Error, Map, Result, Type, Value};
pub struct Raw;
impl BuiltInFunction for Raw {
fn name(&self) -> &'static str {
"raw"
}
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
Error::expect_argument_amount(self, 2, arguments.len())?;
let program = arguments.first().unwrap().as_string()?;
let command_arguments = arguments.get(1).unwrap().as_list()?;
let mut command = Command::new(program);
for argument in command_arguments.items().iter() {
command.arg(argument.as_string()?);
}
let output = command.spawn()?.wait_with_output()?.stdout;
Ok(Value::String(String::from_utf8(output)?))
}
fn r#type(&self) -> crate::Type {
Type::Function {
parameter_types: vec![Type::String, Type::List(Box::new(Type::String))],
return_type: Box::new(Type::String),
}
}
}
pub struct Sh; pub struct Sh;
impl BuiltInFunction for Sh { impl BuiltInFunction for Sh {
@ -15,9 +46,8 @@ impl BuiltInFunction for Sh {
let command_text = arguments.first().unwrap().as_string()?; let command_text = arguments.first().unwrap().as_string()?;
let mut command = Command::new("sh"); let mut command = Command::new("sh");
for word in command_text.split(' ') { command.arg("-c");
command.arg(word); command.arg(command_text);
}
let output = command.spawn()?.wait_with_output()?.stdout; let output = command.spawn()?.wait_with_output()?.stdout;

View File

@ -61,11 +61,11 @@ impl BuiltInFunction for Write {
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> { fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
let file_content = arguments.first().unwrap_or_default().as_string()?; let file_content = arguments.first().unwrap_or_default().as_string()?;
let path = arguments.get(1).unwrap_or(&Value::Empty).as_string()?; let path = arguments.get(1).unwrap_or_default().as_string()?;
write(path, file_content)?; write(path, file_content)?;
Ok(Value::Empty) Ok(Value::Option(None))
} }
fn r#type(&self) -> Type { fn r#type(&self) -> Type {
@ -84,8 +84,14 @@ impl BuiltInFunction for Append {
} }
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> { fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
let file_content = arguments.first().unwrap_or(&Value::Empty).as_string()?; let file_content = arguments
let path = arguments.get(1).unwrap_or(&Value::Empty).as_string()?; .first()
.unwrap_or(&Value::Option(None))
.as_string()?;
let path = arguments
.get(1)
.unwrap_or(&Value::Option(None))
.as_string()?;
File::options() File::options()
.append(true) .append(true)
@ -93,7 +99,7 @@ impl BuiltInFunction for Append {
.open(path)? .open(path)?
.write_all(file_content.as_bytes())?; .write_all(file_content.as_bytes())?;
Ok(Value::Empty) Ok(Value::Option(None))
} }
fn r#type(&self) -> Type { fn r#type(&self) -> Type {

View File

@ -8,6 +8,7 @@ mod data_formats;
mod fs; mod fs;
mod network; mod network;
mod output; mod output;
mod packages;
mod random; mod random;
mod r#type; mod r#type;
@ -15,10 +16,11 @@ mod r#type;
/// ///
/// This is the public interface to access built-in functions by iterating over /// This is the public interface to access built-in functions by iterating over
/// the references it holds. /// the references it holds.
pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 16] = [ pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 18] = [
&assert::Assert, &assert::Assert,
&assert::AssertEqual, &assert::AssertEqual,
&collections::Length, &collections::Length,
&commands::Raw,
&commands::Sh, &commands::Sh,
&data_formats::FromJson, &data_formats::FromJson,
&data_formats::ToJson, &data_formats::ToJson,
@ -27,6 +29,7 @@ pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 16] = [
&fs::Append, &fs::Append,
&network::Download, &network::Download,
&output::Output, &output::Output,
&packages::InstallPackages,
&random::Random, &random::Random,
&random::RandomBoolean, &random::RandomBoolean,
&random::RandomFloat, &random::RandomFloat,

View File

@ -12,7 +12,7 @@ impl BuiltInFunction for Output {
println!("{argument}"); println!("{argument}");
} }
Ok(Value::Empty) Ok(Value::Option(None))
} }
fn r#type(&self) -> Type { fn r#type(&self) -> Type {

View File

@ -0,0 +1,35 @@
use std::process::Command;
use crate::{BuiltInFunction, Error, Map, Result, Type, Value};
pub struct InstallPackages;
impl BuiltInFunction for InstallPackages {
fn name(&self) -> &'static str {
"install_packages"
}
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
Error::expect_argument_amount(self, 1, arguments.len())?;
let mut command = Command::new("sudo");
let argument_list = arguments.first().unwrap().as_list()?;
command.args(&["dnf", "-y", "install"]);
for argument in argument_list.items().iter() {
command.arg(argument.as_string()?);
}
command.spawn()?.wait()?;
Ok(Value::Option(None))
}
fn r#type(&self) -> Type {
Type::Function {
parameter_types: vec![Type::List(Box::new(Type::String))],
return_type: Box::new(Type::Empty),
}
}
}

View File

@ -1,4 +1,4 @@
use crate::{BuiltInFunction, Error, List, Map, Result, Type, Value}; use crate::{BuiltInFunction, Error, Map, Result, Type, Value};
pub struct TypeFunction; pub struct TypeFunction;
@ -10,32 +10,15 @@ impl BuiltInFunction for TypeFunction {
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> { fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
Error::expect_argument_amount(self, 1, arguments.len())?; Error::expect_argument_amount(self, 1, arguments.len())?;
if arguments.len() == 1 { let type_text = arguments.first().unwrap().r#type().to_string();
let type_definition = arguments.first().unwrap().r#type();
let type_text = type_definition.to_string();
let text_without_brackets = &type_text[1..type_text.len() - 1];
Ok(Value::String(text_without_brackets.to_string())) Ok(Value::String(type_text))
} else {
let mut answers = Vec::new();
for value in arguments {
let type_definition = value.r#type();
let type_text = type_definition.to_string();
let text_without_brackets = &type_text[1..type_text.len() - 1];
let text_as_value = Value::String(text_without_brackets.to_string());
answers.push(text_as_value);
}
Ok(Value::List(List::with_items(answers)))
}
} }
fn r#type(&self) -> Type { fn r#type(&self) -> Type {
Type::Function { Type::Function {
parameter_types: vec![Type::String], parameter_types: vec![Type::Any],
return_type: Box::new(Type::Any), return_type: Box::new(Type::String),
} }
} }
} }

View File

@ -60,12 +60,19 @@ pub enum Error {
}, },
/// A function was called with the wrong amount of arguments. /// A function was called with the wrong amount of arguments.
ExpectedArgumentAmount { ExpectedBuiltInFunctionArgumentAmount {
function_name: &'static str, function_name: &'static str,
expected: usize, expected: usize,
actual: usize, actual: usize,
}, },
/// A function was called with the wrong amount of arguments.
ExpectedFunctionArgumentAmount {
source: String,
expected: usize,
actual: usize,
},
/// A function was called with the wrong amount of arguments. /// A function was called with the wrong amount of arguments.
ExpectedArgumentMinimum { ExpectedArgumentMinimum {
function_name: &'static str, function_name: &'static str,
@ -113,7 +120,7 @@ pub enum Error {
actual: Value, actual: Value,
}, },
ExpectedEmpty { ExpectedNone {
actual: Value, actual: Value,
}, },
@ -190,7 +197,7 @@ impl Error {
if expected == actual { if expected == actual {
Ok(()) Ok(())
} else { } else {
Err(Error::ExpectedArgumentAmount { Err(Error::ExpectedBuiltInFunctionArgumentAmount {
function_name: function.name(), function_name: function.name(),
expected, expected,
actual, actual,
@ -312,7 +319,7 @@ impl fmt::Display for Error {
"An operator expected {} arguments, but got {}.", "An operator expected {} arguments, but got {}.",
expected, actual expected, actual
), ),
ExpectedArgumentAmount { ExpectedBuiltInFunctionArgumentAmount {
function_name: tool_name, function_name: tool_name,
expected, expected,
actual, actual,
@ -320,6 +327,14 @@ impl fmt::Display for Error {
f, f,
"{tool_name} expected {expected} arguments, but got {actual}.", "{tool_name} expected {expected} arguments, but got {actual}.",
), ),
ExpectedFunctionArgumentAmount {
source,
expected,
actual,
} => write!(
f,
"{source} expected {expected} arguments, but got {actual}.",
),
ExpectedArgumentMinimum { ExpectedArgumentMinimum {
function_name, function_name,
minimum, minimum,
@ -358,7 +373,7 @@ impl fmt::Display for Error {
"Expected a list of len {}, but got {:?}.", "Expected a list of len {}, but got {:?}.",
expected_len, actual expected_len, actual
), ),
ExpectedEmpty { actual } => write!(f, "Expected an empty value, but got {actual}."), ExpectedNone { actual } => write!(f, "Expected an empty value, but got {actual}."),
ExpectedMap { actual } => write!(f, "Expected a map, but got {actual}."), ExpectedMap { actual } => write!(f, "Expected a map, but got {actual}."),
ExpectedTable { actual } => write!(f, "Expected a table, but got {actual}."), ExpectedTable { actual } => write!(f, "Expected a table, but got {actual}."),
ExpectedFunction { actual } => { ExpectedFunction { actual } => {

View File

@ -9,7 +9,7 @@ pub use crate::{
built_in_functions::{BuiltInFunction, BUILT_IN_FUNCTIONS}, built_in_functions::{BuiltInFunction, BUILT_IN_FUNCTIONS},
error::*, error::*,
evaluate::*, evaluate::*,
value::{function::Function, list::List, map::Map, table::Table, Value}, value::{function::Function, list::List, map::Map, Value},
}; };
mod abstract_tree; mod abstract_tree;

View File

@ -94,7 +94,7 @@ fn main() {
match eval_result { match eval_result {
Ok(value) => { Ok(value) => {
if !value.is_empty() { if !value.is_none() {
println!("{value}") println!("{value}")
} }
} }

View File

@ -9,16 +9,10 @@ pub struct Function {
parameters: Vec<Identifier>, parameters: Vec<Identifier>,
body: Block, body: Block,
r#type: Type, r#type: Type,
context: Map,
} }
impl Function { impl Function {
pub fn new( pub fn new(parameters: Vec<Identifier>, body: Block, r#type: Option<Type>) -> Self {
parameters: Vec<Identifier>,
body: Block,
r#type: Option<Type>,
context: Map,
) -> Self {
let r#type = r#type.unwrap_or(Type::Function { let r#type = r#type.unwrap_or(Type::Function {
parameter_types: vec![Type::Any; parameters.len()], parameter_types: vec![Type::Any; parameters.len()],
return_type: Box::new(Type::Any), return_type: Box::new(Type::Any),
@ -28,7 +22,6 @@ impl Function {
parameters, parameters,
body, body,
r#type, r#type,
context,
} }
} }
@ -54,33 +47,25 @@ impl Function {
} }
} }
pub fn call(&self, arguments: &[Value], source: &str, context: &Map) -> Result<Value> { pub fn call(&self, arguments: &[Value], source: &str) -> Result<Value> {
if self.parameters.len() != arguments.len() { if self.parameters.len() != arguments.len() {
return Err(Error::ExpectedArgumentAmount { return Err(Error::ExpectedFunctionArgumentAmount {
function_name: "", source: "unknown".to_string(),
expected: self.parameters.len(), expected: self.parameters.len(),
actual: arguments.len(), actual: arguments.len(),
}); });
} }
for (key, (value, r#type)) in context.variables()?.iter() { let context = Map::new();
if self.context.variables()?.contains_key(key) {
continue;
}
self.context
.set(key.clone(), value.clone(), Some(r#type.clone()))?;
}
let parameter_argument_pairs = self.parameters.iter().zip(arguments.iter()); let parameter_argument_pairs = self.parameters.iter().zip(arguments.iter());
for (identifier, value) in parameter_argument_pairs { for (identifier, value) in parameter_argument_pairs {
let key = identifier.inner().clone(); let key = identifier.inner().clone();
self.context.set(key, value.clone(), None)?; context.set(key, value.clone(), None)?;
} }
let return_value = self.body.run(source, &self.context)?; let return_value = self.body.run(source, &context)?;
Ok(return_value) Ok(return_value)
} }

View File

@ -55,6 +55,12 @@ impl Map {
Ok(previous) Ok(previous)
} }
pub fn clear(&self) -> Result<()> {
self.variables.write()?.clear();
Ok(())
}
} }
impl Default for Map { impl Default for Map {

View File

@ -21,14 +21,13 @@ use std::{
pub mod function; pub mod function;
pub mod list; pub mod list;
pub mod map; pub mod map;
pub mod table;
/// Dust value representation. /// Dust value representation.
/// ///
/// Every dust variable has a key and a Value. Variables are represented by /// Every dust variable has a key and a Value. Variables are represented by
/// storing them in a VariableMap. This means the map of variables is itself a /// storing them in a VariableMap. This means the map of variables is itself a
/// value that can be treated as any other. /// value that can be treated as any other.
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone)]
pub enum Value { pub enum Value {
List(List), List(List),
Map(Map), Map(Map),
@ -37,8 +36,13 @@ pub enum Value {
Float(f64), Float(f64),
Integer(i64), Integer(i64),
Boolean(bool), Boolean(bool),
#[default] Option(Option<Box<Value>>),
Empty, }
impl Default for Value {
fn default() -> Self {
Value::Option(None)
}
} }
impl Value { impl Value {
@ -71,7 +75,13 @@ impl Value {
Value::Float(_) => Type::Float, Value::Float(_) => Type::Float,
Value::Integer(_) => Type::Integer, Value::Integer(_) => Type::Integer,
Value::Boolean(_) => Type::Boolean, Value::Boolean(_) => Type::Boolean,
Value::Empty => Type::Empty, Value::Option(option) => {
if let Some(value) = option {
Type::Option(Some(Box::new(value.r#type())))
} else {
Type::Option(None)
}
}
}; };
r#type r#type
@ -101,8 +111,12 @@ impl Value {
matches!(self, Value::List(_)) matches!(self, Value::List(_))
} }
pub fn is_empty(&self) -> bool { pub fn is_option(&self) -> bool {
matches!(self, Value::Empty) matches!(self, Value::Option(_))
}
pub fn is_none(&self) -> bool {
matches!(self, Value::Option(None))
} }
pub fn is_map(&self) -> bool { pub fn is_map(&self) -> bool {
@ -206,11 +220,19 @@ impl Value {
} }
} }
/// Returns `()`, or returns`Err` if `self` is not a `Value::Empty`. /// Returns `()`, or returns `Err` if `self` is not a `Value::Option(None)`.
pub fn as_empty(&self) -> Result<()> { pub fn as_none(&self) -> Result<()> {
match self { match self {
Value::Empty => Ok(()), Value::Option(option) => {
value => Err(Error::ExpectedEmpty { if option.is_none() {
Ok(())
} else {
Err(Error::ExpectedNone {
actual: self.clone(),
})
}
}
value => Err(Error::ExpectedNone {
actual: value.clone(), actual: value.clone(),
}), }),
} }
@ -219,7 +241,7 @@ impl Value {
impl Default for &Value { impl Default for &Value {
fn default() -> Self { fn default() -> Self {
&Value::Empty &Value::Option(None)
} }
} }
@ -371,7 +393,7 @@ impl PartialEq for Value {
(Value::List(left), Value::List(right)) => left == right, (Value::List(left), Value::List(right)) => left == right,
(Value::Map(left), Value::Map(right)) => left == right, (Value::Map(left), Value::Map(right)) => left == right,
(Value::Function(left), Value::Function(right)) => left == right, (Value::Function(left), Value::Function(right)) => left == right,
(Value::Empty, Value::Empty) => true, (Value::Option(left), Value::Option(right)) => left == right,
_ => false, _ => false,
} }
} }
@ -408,8 +430,8 @@ impl Ord for Value {
(Value::Map(_), _) => Ordering::Greater, (Value::Map(_), _) => Ordering::Greater,
(Value::Function(left), Value::Function(right)) => left.cmp(right), (Value::Function(left), Value::Function(right)) => left.cmp(right),
(Value::Function(_), _) => Ordering::Greater, (Value::Function(_), _) => Ordering::Greater,
(Value::Empty, Value::Empty) => Ordering::Equal, (Value::Option(left), Value::Option(right)) => left.cmp(right),
(Value::Empty, _) => Ordering::Less, (Value::Option(_), _) => Ordering::Less,
} }
} }
} }
@ -434,7 +456,7 @@ impl Serialize for Value {
list.end() list.end()
} }
Value::Empty => todo!(), Value::Option(inner) => inner.serialize(serializer),
Value::Map(inner) => inner.serialize(serializer), Value::Map(inner) => inner.serialize(serializer),
Value::Function(inner) => inner.serialize(serializer), Value::Function(inner) => inner.serialize(serializer),
} }
@ -448,7 +470,13 @@ impl Display for Value {
Value::Float(float) => write!(f, "{float}"), Value::Float(float) => write!(f, "{float}"),
Value::Integer(int) => write!(f, "{int}"), Value::Integer(int) => write!(f, "{int}"),
Value::Boolean(boolean) => write!(f, "{boolean}"), Value::Boolean(boolean) => write!(f, "{boolean}"),
Value::Empty => write!(f, "empty"), Value::Option(option) => {
if let Some(value) = option {
write!(f, "some({})", value)
} else {
write!(f, "none")
}
}
Value::List(list) => { Value::List(list) => {
write!(f, "[")?; write!(f, "[")?;
for value in list.items().iter() { for value in list.items().iter() {
@ -506,7 +534,7 @@ impl From<Value> for Result<Value> {
impl From<()> for Value { impl From<()> for Value {
fn from(_: ()) -> Self { fn from(_: ()) -> Self {
Value::Empty Value::Option(None)
} }
} }
@ -729,7 +757,7 @@ impl<'de> Visitor<'de> for ValueVisitor {
where where
E: serde::de::Error, E: serde::de::Error,
{ {
Ok(Value::Empty) Ok(Value::Option(None))
} }
fn visit_some<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error> fn visit_some<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error>
@ -747,7 +775,7 @@ impl<'de> Visitor<'de> for ValueVisitor {
where where
E: serde::de::Error, E: serde::de::Error,
{ {
Ok(Value::Empty) Ok(Value::Option(None))
} }
fn visit_newtype_struct<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error> fn visit_newtype_struct<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error>

View File

@ -1,344 +0,0 @@
use crate::{Error, List, Map, Result, Value};
use comfy_table::{Cell, Color, ContentArrangement, Table as ComfyTable};
use serde::{Deserialize, Serialize};
use std::{
cmp::Ordering,
fmt::{self, Display, Formatter},
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Table {
headers: Vec<String>,
rows: Vec<Vec<Value>>,
primary_key_index: usize,
}
impl Table {
pub fn new(headers: Vec<String>) -> Self {
Table {
headers,
rows: Vec::new(),
primary_key_index: 0,
}
}
pub fn with_capacity(capacity: usize, headers: Vec<String>) -> Self {
Table {
headers,
rows: Vec::with_capacity(capacity),
primary_key_index: 0,
}
}
pub fn from_raw_parts(headers: Vec<String>, rows: Vec<Vec<Value>>) -> Self {
Table {
headers,
rows,
primary_key_index: 0,
}
}
pub fn reserve(&mut self, additional: usize) {
self.rows.reserve(additional);
}
pub fn headers(&self) -> &Vec<String> {
&self.headers
}
pub fn rows(&self) -> &Vec<Vec<Value>> {
&self.rows
}
pub fn len(&self) -> usize {
self.rows.len()
}
pub fn is_empty(&self) -> bool {
self.rows.is_empty()
}
pub fn sort(&mut self) {
self.rows.sort();
}
pub fn insert(&mut self, row: Vec<Value>) -> Result<()> {
if row.len() != self.headers.len() {
return Err(Error::WrongColumnAmount {
expected: self.headers.len(),
actual: row.len(),
});
}
self.rows.push(row);
Ok(())
}
pub fn remove(&mut self, index: usize) -> Result<()> {
self.rows.remove(index);
Ok(())
}
pub fn get_row(&self, index: usize) -> Option<&Vec<Value>> {
self.rows.get(index)
}
pub fn get(&self, value: &Value) -> Option<&Vec<Value>> {
let primary_key = self.headers().get(self.primary_key_index)?;
self.get_where(primary_key, value)
}
pub fn get_where(&self, column_name: &str, expected: &Value) -> Option<&Vec<Value>> {
let column_index = self.get_column_index(column_name)?;
for row in &self.rows {
if let Some(actual) = row.get(column_index) {
if actual == expected {
return Some(row);
}
}
}
None
}
pub fn filter(&self, column_name: &str, expected: &Value) -> Option<Table> {
let mut filtered = Table::new(self.headers.clone());
let column_index = self.get_column_index(column_name)?;
for row in &self.rows {
let actual = row.get(column_index).unwrap();
if actual == expected {
let _ = filtered.insert(row.clone());
}
}
Some(filtered)
}
pub fn get_column_index(&self, column_name: &str) -> Option<usize> {
let column_names = &self.headers;
for (i, column) in column_names.iter().enumerate() {
if column == column_name {
return Some(i);
}
}
None
}
}
impl Display for Table {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut table = ComfyTable::new();
table
.load_preset("││──├─┼┤│ ┬┴╭╮╰╯")
.set_content_arrangement(ContentArrangement::Dynamic)
.set_header(&self.headers);
for row in &self.rows {
let row = row.iter().map(|value| {
let text = match value {
Value::List(list) => {
let mut string = "(".to_string();
for (index, value) in list.items().iter().enumerate() {
if index > 0 {
string.push_str(", ");
}
string.push_str(&value.to_string());
}
string.push(')');
string
}
Value::Map(map) => format!("Map ({} items)", map.variables().unwrap().len()),
Value::Function(_) => "Function".to_string(),
Value::Empty => "Empty".to_string(),
value => value.to_string(),
};
let mut cell = Cell::new(text).bg(Color::Rgb {
r: 40,
g: 40,
b: 40,
});
if value.is_string() {
cell = cell.fg(Color::Green);
}
if value.is_integer() {
cell = cell.fg(Color::Blue);
}
if value.is_boolean() {
cell = cell.fg(Color::Red);
}
if value.is_function() {
cell = cell.fg(Color::Cyan);
}
cell
});
table.add_row(row);
}
if self.headers.is_empty() {
table.set_header(["empty"]);
}
write!(f, "{table}")
}
}
impl From<&Value> for Table {
fn from(value: &Value) -> Self {
match value {
Value::String(string) => {
let mut table = Table::new(vec!["string".to_string()]);
table
.insert(vec![Value::String(string.to_string())])
.unwrap();
table
}
Value::Float(float) => {
let mut table = Table::new(vec!["float".to_string()]);
table.insert(vec![Value::Float(*float)]).unwrap();
table
}
Value::Integer(integer) => {
let mut table = Table::new(vec!["integer".to_string()]);
table.insert(vec![Value::Integer(*integer)]).unwrap();
table
}
Value::Boolean(boolean) => {
let mut table = Table::new(vec!["boolean".to_string()]);
table.insert(vec![Value::Boolean(*boolean)]).unwrap();
table
}
Value::List(list) => Self::from(list),
Value::Empty => Table::new(Vec::with_capacity(0)),
Value::Map(map) => Result::<Table>::from(map).unwrap(),
Value::Function(function) => {
let mut table = Table::new(vec!["function".to_string()]);
table
.insert(vec![Value::Function(function.clone())])
.unwrap();
table
}
}
}
}
impl From<&List> for Table {
fn from(list: &List) -> Self {
let mut table = Table::new(vec!["index".to_string(), "item".to_string()]);
for (i, value) in list.items().iter().enumerate() {
table
.insert(vec![Value::Integer(i as i64), value.clone()])
.unwrap();
}
table
}
}
impl From<&mut List> for Table {
fn from(list: &mut List) -> Self {
let mut table = Table::new(vec!["index".to_string(), "item".to_string()]);
for (i, value) in list.items().iter().enumerate() {
if let Ok(list) = value.as_list() {
table.insert(list.items().clone()).unwrap();
} else {
table
.insert(vec![Value::Integer(i as i64), value.clone()])
.unwrap();
}
}
table
}
}
impl From<Map> for Result<Table> {
fn from(map: Map) -> Self {
let variables = map.variables()?;
let keys = variables.keys().cloned().collect();
let values = variables.values().map(|(value, _)| value.clone()).collect();
let mut table = Table::new(keys);
table.insert(values)?;
Ok(table)
}
}
impl From<&Map> for Result<Table> {
fn from(map: &Map) -> Self {
let variables = map.variables()?;
let keys = variables.keys().cloned().collect();
let values = variables.values().map(|(value, _)| value.clone()).collect();
let mut table = Table::new(keys);
table.insert(values)?;
Ok(table)
}
}
impl From<&mut Map> for Result<Table> {
fn from(map: &mut Map) -> Self {
let variables = map.variables()?;
let keys = variables.keys().cloned().collect();
let values = variables.values().map(|(value, _)| value.clone()).collect();
let mut table = Table::new(keys);
table
.insert(values)
.expect("Failed to create Table from Map. This is a no-op.");
Ok(table)
}
}
impl Eq for Table {}
impl PartialEq for Table {
fn eq(&self, other: &Self) -> bool {
if self.headers != other.headers {
return false;
}
self.rows == other.rows
}
}
impl PartialOrd for Table {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.headers.partial_cmp(&other.headers)
}
}
impl Ord for Table {
fn cmp(&self, other: &Self) -> Ordering {
self.headers.cmp(&other.headers)
}
}

View File

@ -78,6 +78,7 @@ module.exports = grammar({
$.boolean, $.boolean,
$.list, $.list,
$.map, $.map,
$.option
), ),
integer: $ => integer: $ =>
@ -176,6 +177,18 @@ module.exports = grammar({
'}', '}',
), ),
option: $ =>
choice(
'none',
seq(
'some',
'(',
$.expression,
')',
),
),
index: $ => index: $ =>
prec.left( prec.left(
1, 1,
@ -331,6 +344,11 @@ module.exports = grammar({
'any', 'any',
'bool', 'bool',
'float', 'float',
'int',
'map',
'num',
'str',
seq('[', $.type, ']'),
seq( seq(
'(', '(',
repeat( repeat(
@ -342,11 +360,12 @@ module.exports = grammar({
')', ')',
optional(seq('->', $.type)), optional(seq('->', $.type)),
), ),
'int', seq(
seq('[', $.type, ']'), 'option',
'map', '(',
'num', $.type,
'str', ')',
)
), ),
), ),

View File

@ -223,6 +223,10 @@
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "map" "name": "map"
},
{
"type": "SYMBOL",
"name": "option"
} }
] ]
}, },
@ -537,6 +541,36 @@
} }
] ]
}, },
"option": {
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": "none"
},
{
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "some"
},
{
"type": "STRING",
"value": "("
},
{
"type": "SYMBOL",
"name": "expression"
},
{
"type": "STRING",
"value": ")"
}
]
}
]
},
"index": { "index": {
"type": "PREC_LEFT", "type": "PREC_LEFT",
"value": 1, "value": 1,
@ -1062,6 +1096,39 @@
"type": "STRING", "type": "STRING",
"value": "float" "value": "float"
}, },
{
"type": "STRING",
"value": "int"
},
{
"type": "STRING",
"value": "map"
},
{
"type": "STRING",
"value": "num"
},
{
"type": "STRING",
"value": "str"
},
{
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "["
},
{
"type": "SYMBOL",
"name": "type"
},
{
"type": "STRING",
"value": "]"
}
]
},
{ {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
@ -1120,16 +1187,16 @@
} }
] ]
}, },
{
"type": "STRING",
"value": "int"
},
{ {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
{ {
"type": "STRING", "type": "STRING",
"value": "[" "value": "option"
},
{
"type": "STRING",
"value": "("
}, },
{ {
"type": "SYMBOL", "type": "SYMBOL",
@ -1137,21 +1204,9 @@
}, },
{ {
"type": "STRING", "type": "STRING",
"value": "]" "value": ")"
} }
] ]
},
{
"type": "STRING",
"value": "map"
},
{
"type": "STRING",
"value": "num"
},
{
"type": "STRING",
"value": "str"
} }
] ]
} }

View File

@ -390,6 +390,21 @@
"named": true, "named": true,
"fields": {} "fields": {}
}, },
{
"type": "option",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": false,
"types": [
{
"type": "expression",
"named": true
}
]
}
},
{ {
"type": "return", "type": "return",
"named": true, "named": true,
@ -529,6 +544,10 @@
"type": "map", "type": "map",
"named": true "named": true
}, },
{
"type": "option",
"named": true
},
{ {
"type": "string", "type": "string",
"named": true "named": true
@ -720,11 +739,11 @@
}, },
{ {
"type": "float", "type": "float",
"named": true "named": false
}, },
{ {
"type": "float", "type": "float",
"named": false "named": true
}, },
{ {
"type": "fn", "type": "fn",
@ -770,10 +789,18 @@
"type": "metadata", "type": "metadata",
"named": false "named": false
}, },
{
"type": "none",
"named": false
},
{ {
"type": "num", "type": "num",
"named": false "named": false
}, },
{
"type": "option",
"named": false
},
{ {
"type": "output", "type": "output",
"named": false "named": false
@ -806,6 +833,10 @@
"type": "return", "type": "return",
"named": false "named": false
}, },
{
"type": "some",
"named": false
},
{ {
"type": "str", "type": "str",
"named": false "named": false

File diff suppressed because it is too large Load Diff