Fix async/function bug; Remove Table; Add packages
This commit is contained in:
parent
afa937a697
commit
364ce9cb33
@ -35,7 +35,10 @@ impl AbstractTree for Assignment {
|
||||
source, type_node, context,
|
||||
)?)
|
||||
} else {
|
||||
None
|
||||
context
|
||||
.variables()?
|
||||
.get(identifier.inner())
|
||||
.map(|(_, r#type)| TypeDefinition::new(r#type.clone()))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@ -83,12 +86,19 @@ impl AbstractTree for Assignment {
|
||||
AssignmentOperator::MinusEqual => todo!(),
|
||||
}
|
||||
} else {
|
||||
match operator {
|
||||
AssignmentOperator::Equal => {}
|
||||
AssignmentOperator::PlusEqual => {
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
|
||||
let variable_key = identifier.inner().clone();
|
||||
let variable_type = if let Some(definition) = &type_definition {
|
||||
@ -97,7 +107,7 @@ impl AbstractTree for Assignment {
|
||||
statement_type
|
||||
};
|
||||
|
||||
context.set(variable_key, Value::Empty, Some(variable_type))?;
|
||||
context.set(variable_key, Value::Option(None), Some(variable_type))?;
|
||||
|
||||
Ok(Assignment {
|
||||
identifier,
|
||||
@ -137,7 +147,7 @@ impl AbstractTree for Assignment {
|
||||
context.set(key.clone(), new_value, None)?;
|
||||
}
|
||||
|
||||
Ok(Value::Empty)
|
||||
Ok(Value::Option(None))
|
||||
}
|
||||
|
||||
fn expected_type(&self, _context: &Map) -> Result<Type> {
|
||||
|
@ -52,7 +52,7 @@ impl AbstractTree for Block {
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||
if self.is_async {
|
||||
let statements = &self.statements;
|
||||
let final_result = RwLock::new(Ok(Value::Empty));
|
||||
let final_result = RwLock::new(Ok(Value::Option(None)));
|
||||
|
||||
statements
|
||||
.into_par_iter()
|
||||
@ -86,7 +86,7 @@ impl AbstractTree for Block {
|
||||
prev_result = Some(statement.run(source, context));
|
||||
}
|
||||
|
||||
prev_result.unwrap_or(Ok(Value::Empty))
|
||||
prev_result.unwrap_or(Ok(Value::Option(None)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ impl AbstractTree for For {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Empty)
|
||||
Ok(Value::Option(None))
|
||||
}
|
||||
|
||||
fn expected_type(&self, _context: &Map) -> Result<Type> {
|
||||
|
@ -39,26 +39,33 @@ impl AbstractTree for FunctionCall {
|
||||
let argument_index = arguments.len();
|
||||
|
||||
if let Type::Function {
|
||||
parameter_types,
|
||||
return_type: _,
|
||||
parameter_types, ..
|
||||
} = &function_type
|
||||
{
|
||||
let expected_type = parameter_types.get(argument_index).unwrap();
|
||||
let expected_type = if let Type::List(item_type) = expected_type {
|
||||
item_type
|
||||
} else {
|
||||
expected_type
|
||||
};
|
||||
|
||||
expected_type
|
||||
if let Some(r#type) = parameter_types.get(argument_index) {
|
||||
r#type
|
||||
.check(&expression_type)
|
||||
.map_err(|error| error.at_node(child, source))?;
|
||||
}
|
||||
}
|
||||
|
||||
arguments.push(expression);
|
||||
}
|
||||
}
|
||||
|
||||
if let Type::Function {
|
||||
parameter_types, ..
|
||||
} = &function_type
|
||||
{
|
||||
if arguments.len() != parameter_types.len() {
|
||||
return Err(Error::ExpectedFunctionArgumentAmount {
|
||||
source: source[expression_node.byte_range()].to_string(),
|
||||
expected: parameter_types.len(),
|
||||
actual: arguments.len(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(FunctionCall {
|
||||
function_expression,
|
||||
arguments,
|
||||
@ -110,7 +117,7 @@ impl AbstractTree for FunctionCall {
|
||||
arguments.push(value);
|
||||
}
|
||||
|
||||
value.as_function()?.call(&arguments, source, context)
|
||||
value.as_function()?.call(&arguments, source)
|
||||
}
|
||||
|
||||
fn expected_type(&self, context: &Map) -> Result<Type> {
|
||||
@ -183,6 +190,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn evaluate_built_in_function_call() {
|
||||
assert_eq!(evaluate("(output 'Hiya')"), Ok(Value::Empty));
|
||||
assert_eq!(evaluate("(output 'Hiya')"), Ok(Value::Option(None)));
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ impl AbstractTree for IfElse {
|
||||
if let Some(block) = &self.else_block {
|
||||
block.run(source, context)
|
||||
} else {
|
||||
Ok(Value::Empty)
|
||||
Ok(Value::Option(None))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ impl AbstractTree for IndexAssignment {
|
||||
previous_value += value;
|
||||
previous_value
|
||||
} else {
|
||||
Value::Empty
|
||||
Value::Option(None)
|
||||
}
|
||||
}
|
||||
AssignmentOperator::MinusEqual => {
|
||||
@ -80,7 +80,7 @@ impl AbstractTree for IndexAssignment {
|
||||
previous_value -= value;
|
||||
previous_value
|
||||
} else {
|
||||
Value::Empty
|
||||
Value::Option(None)
|
||||
}
|
||||
}
|
||||
AssignmentOperator::Equal => value,
|
||||
@ -88,7 +88,7 @@ impl AbstractTree for IndexAssignment {
|
||||
|
||||
index_context.set(index_key.clone(), new_value, None)?;
|
||||
|
||||
Ok(Value::Empty)
|
||||
Ok(Value::Option(None))
|
||||
}
|
||||
|
||||
fn expected_type(&self, _context: &Map) -> Result<Type> {
|
||||
|
@ -72,7 +72,7 @@ impl AbstractTree for Match {
|
||||
if let Some(fallback) = &self.fallback {
|
||||
fallback.run(source, context)
|
||||
} else {
|
||||
Ok(Value::Empty)
|
||||
Ok(Value::Option(None))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ impl AbstractTree for Root {
|
||||
}
|
||||
|
||||
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 {
|
||||
value = statement.run(source, context)?;
|
||||
|
@ -64,6 +64,7 @@ pub enum Type {
|
||||
Map,
|
||||
Number,
|
||||
String,
|
||||
Option(Option<Box<Type>>),
|
||||
}
|
||||
|
||||
impl Type {
|
||||
@ -82,6 +83,16 @@ impl Type {
|
||||
| (Type::Integer, Type::Number)
|
||||
| (Type::Float, Type::Number)
|
||||
| (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)) => {
|
||||
if self_item_type.check(&other_item_type).is_err() {
|
||||
Err(Error::TypeCheck {
|
||||
@ -178,9 +189,15 @@ impl AbstractTree for Type {
|
||||
"map" => Type::Map,
|
||||
"num" => Type::Number,
|
||||
"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 {
|
||||
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(),
|
||||
location: type_node.start_position(),
|
||||
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> {
|
||||
Ok(Value::Empty)
|
||||
Ok(Value::Option(None))
|
||||
}
|
||||
|
||||
fn expected_type(&self, _context: &Map) -> Result<Type> {
|
||||
@ -229,6 +246,13 @@ impl Display for Type {
|
||||
Type::Map => write!(f, "map"),
|
||||
Type::Number => write!(f, "num"),
|
||||
Type::String => write!(f, "str"),
|
||||
Type::Option(option) => {
|
||||
if let Some(r#type) = option {
|
||||
write!(f, "some({})", r#type)
|
||||
} else {
|
||||
write!(f, "none")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ pub enum ValueNode {
|
||||
Integer(String),
|
||||
String(String),
|
||||
List(Vec<Expression>),
|
||||
Empty,
|
||||
Option(Option<Box<Expression>>),
|
||||
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
|
||||
parameters.iter().zip(parameter_types.iter())
|
||||
{
|
||||
function_context.set(
|
||||
function_context_types.set(
|
||||
parameter_name.inner().clone(),
|
||||
Value::Empty,
|
||||
Value::Option(None),
|
||||
Some(parameter_type.clone()),
|
||||
)?;
|
||||
}
|
||||
@ -67,19 +67,14 @@ impl AbstractTree for ValueNode {
|
||||
TypeDefinition::from_syntax_node(source, return_type_node, context)?;
|
||||
|
||||
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 {
|
||||
parameter_types,
|
||||
return_type: Box::new(return_type.take_inner()),
|
||||
};
|
||||
|
||||
ValueNode::Function(Function::new(
|
||||
parameters,
|
||||
body,
|
||||
Some(r#type),
|
||||
function_context,
|
||||
))
|
||||
ValueNode::Function(Function::new(parameters, body, Some(r#type)))
|
||||
}
|
||||
"integer" => ValueNode::Integer(source[child.byte_range()].to_string()),
|
||||
"string" => {
|
||||
@ -138,9 +133,22 @@ impl AbstractTree for ValueNode {
|
||||
|
||||
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 {
|
||||
expected: "string, integer, float, boolean, list, map, or empty",
|
||||
expected: "string, integer, float, boolean, list, map, or option",
|
||||
actual: child.kind(),
|
||||
location: child.start_position(),
|
||||
relevant_source: source[child.byte_range()].to_string(),
|
||||
@ -169,7 +177,15 @@ impl AbstractTree for ValueNode {
|
||||
|
||||
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) => {
|
||||
let map = Map::new();
|
||||
|
||||
@ -216,7 +232,13 @@ impl AbstractTree for ValueNode {
|
||||
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,
|
||||
};
|
||||
|
||||
@ -231,8 +253,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn evaluate_empty() {
|
||||
assert_eq!(evaluate("x = 9"), Ok(Value::Empty));
|
||||
assert_eq!(evaluate("x = 1 + 1"), Ok(Value::Empty));
|
||||
assert_eq!(evaluate("x = 9"), Ok(Value::Option(None)));
|
||||
assert_eq!(evaluate("x = 1 + 1"), Ok(Value::Option(None)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -333,4 +355,11 @@ mod tests {
|
||||
);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ impl AbstractTree for While {
|
||||
self.block.run(source, context)?;
|
||||
}
|
||||
|
||||
Ok(Value::Empty)
|
||||
Ok(Value::Option(None))
|
||||
}
|
||||
|
||||
fn expected_type(&self, context: &Map) -> Result<Type> {
|
||||
@ -40,10 +40,10 @@ impl AbstractTree for While {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::evaluate;
|
||||
use crate::{evaluate, Value};
|
||||
|
||||
#[test]
|
||||
fn evalualate_while_loop() {
|
||||
assert_eq!(evaluate("while false { 'foo' }"), Ok(crate::Value::Empty))
|
||||
assert_eq!(evaluate("while false { 'foo' }"), Ok(Value::Option(None)))
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ impl BuiltInFunction for Assert {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Empty)
|
||||
Ok(Value::Option(None))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
@ -39,7 +39,7 @@ impl BuiltInFunction for AssertEqual {
|
||||
let right = arguments.get(1).unwrap();
|
||||
|
||||
if left == right {
|
||||
Ok(Value::Empty)
|
||||
Ok(Value::Option(None))
|
||||
} else {
|
||||
Err(Error::AssertEqualFailed {
|
||||
expected: left.clone(),
|
||||
|
@ -2,6 +2,37 @@ use std::process::Command;
|
||||
|
||||
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;
|
||||
|
||||
impl BuiltInFunction for Sh {
|
||||
@ -15,9 +46,8 @@ impl BuiltInFunction for Sh {
|
||||
let command_text = arguments.first().unwrap().as_string()?;
|
||||
let mut command = Command::new("sh");
|
||||
|
||||
for word in command_text.split(' ') {
|
||||
command.arg(word);
|
||||
}
|
||||
command.arg("-c");
|
||||
command.arg(command_text);
|
||||
|
||||
let output = command.spawn()?.wait_with_output()?.stdout;
|
||||
|
||||
|
@ -61,11 +61,11 @@ impl BuiltInFunction for Write {
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
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)?;
|
||||
|
||||
Ok(Value::Empty)
|
||||
Ok(Value::Option(None))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
@ -84,8 +84,14 @@ impl BuiltInFunction for Append {
|
||||
}
|
||||
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
let file_content = arguments.first().unwrap_or(&Value::Empty).as_string()?;
|
||||
let path = arguments.get(1).unwrap_or(&Value::Empty).as_string()?;
|
||||
let file_content = arguments
|
||||
.first()
|
||||
.unwrap_or(&Value::Option(None))
|
||||
.as_string()?;
|
||||
let path = arguments
|
||||
.get(1)
|
||||
.unwrap_or(&Value::Option(None))
|
||||
.as_string()?;
|
||||
|
||||
File::options()
|
||||
.append(true)
|
||||
@ -93,7 +99,7 @@ impl BuiltInFunction for Append {
|
||||
.open(path)?
|
||||
.write_all(file_content.as_bytes())?;
|
||||
|
||||
Ok(Value::Empty)
|
||||
Ok(Value::Option(None))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
|
@ -8,6 +8,7 @@ mod data_formats;
|
||||
mod fs;
|
||||
mod network;
|
||||
mod output;
|
||||
mod packages;
|
||||
mod random;
|
||||
mod r#type;
|
||||
|
||||
@ -15,10 +16,11 @@ mod r#type;
|
||||
///
|
||||
/// This is the public interface to access built-in functions by iterating over
|
||||
/// the references it holds.
|
||||
pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 16] = [
|
||||
pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 18] = [
|
||||
&assert::Assert,
|
||||
&assert::AssertEqual,
|
||||
&collections::Length,
|
||||
&commands::Raw,
|
||||
&commands::Sh,
|
||||
&data_formats::FromJson,
|
||||
&data_formats::ToJson,
|
||||
@ -27,6 +29,7 @@ pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 16] = [
|
||||
&fs::Append,
|
||||
&network::Download,
|
||||
&output::Output,
|
||||
&packages::InstallPackages,
|
||||
&random::Random,
|
||||
&random::RandomBoolean,
|
||||
&random::RandomFloat,
|
||||
|
@ -12,7 +12,7 @@ impl BuiltInFunction for Output {
|
||||
println!("{argument}");
|
||||
}
|
||||
|
||||
Ok(Value::Empty)
|
||||
Ok(Value::Option(None))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
|
35
src/built_in_functions/packages.rs
Normal file
35
src/built_in_functions/packages.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
use crate::{BuiltInFunction, Error, List, Map, Result, Type, Value};
|
||||
use crate::{BuiltInFunction, Error, Map, Result, Type, Value};
|
||||
|
||||
pub struct TypeFunction;
|
||||
|
||||
@ -10,32 +10,15 @@ impl BuiltInFunction for TypeFunction {
|
||||
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
|
||||
Error::expect_argument_amount(self, 1, arguments.len())?;
|
||||
|
||||
if arguments.len() == 1 {
|
||||
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];
|
||||
let type_text = arguments.first().unwrap().r#type().to_string();
|
||||
|
||||
Ok(Value::String(text_without_brackets.to_string()))
|
||||
} 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)))
|
||||
}
|
||||
Ok(Value::String(type_text))
|
||||
}
|
||||
|
||||
fn r#type(&self) -> Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::String],
|
||||
return_type: Box::new(Type::Any),
|
||||
parameter_types: vec![Type::Any],
|
||||
return_type: Box::new(Type::String),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
25
src/error.rs
25
src/error.rs
@ -60,12 +60,19 @@ pub enum Error {
|
||||
},
|
||||
|
||||
/// A function was called with the wrong amount of arguments.
|
||||
ExpectedArgumentAmount {
|
||||
ExpectedBuiltInFunctionArgumentAmount {
|
||||
function_name: &'static str,
|
||||
expected: 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.
|
||||
ExpectedArgumentMinimum {
|
||||
function_name: &'static str,
|
||||
@ -113,7 +120,7 @@ pub enum Error {
|
||||
actual: Value,
|
||||
},
|
||||
|
||||
ExpectedEmpty {
|
||||
ExpectedNone {
|
||||
actual: Value,
|
||||
},
|
||||
|
||||
@ -190,7 +197,7 @@ impl Error {
|
||||
if expected == actual {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::ExpectedArgumentAmount {
|
||||
Err(Error::ExpectedBuiltInFunctionArgumentAmount {
|
||||
function_name: function.name(),
|
||||
expected,
|
||||
actual,
|
||||
@ -312,7 +319,7 @@ impl fmt::Display for Error {
|
||||
"An operator expected {} arguments, but got {}.",
|
||||
expected, actual
|
||||
),
|
||||
ExpectedArgumentAmount {
|
||||
ExpectedBuiltInFunctionArgumentAmount {
|
||||
function_name: tool_name,
|
||||
expected,
|
||||
actual,
|
||||
@ -320,6 +327,14 @@ impl fmt::Display for Error {
|
||||
f,
|
||||
"{tool_name} expected {expected} arguments, but got {actual}.",
|
||||
),
|
||||
ExpectedFunctionArgumentAmount {
|
||||
source,
|
||||
expected,
|
||||
actual,
|
||||
} => write!(
|
||||
f,
|
||||
"{source} expected {expected} arguments, but got {actual}.",
|
||||
),
|
||||
ExpectedArgumentMinimum {
|
||||
function_name,
|
||||
minimum,
|
||||
@ -358,7 +373,7 @@ impl fmt::Display for Error {
|
||||
"Expected a list of len {}, but got {:?}.",
|
||||
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}."),
|
||||
ExpectedTable { actual } => write!(f, "Expected a table, but got {actual}."),
|
||||
ExpectedFunction { actual } => {
|
||||
|
@ -9,7 +9,7 @@ pub use crate::{
|
||||
built_in_functions::{BuiltInFunction, BUILT_IN_FUNCTIONS},
|
||||
error::*,
|
||||
evaluate::*,
|
||||
value::{function::Function, list::List, map::Map, table::Table, Value},
|
||||
value::{function::Function, list::List, map::Map, Value},
|
||||
};
|
||||
|
||||
mod abstract_tree;
|
||||
|
@ -94,7 +94,7 @@ fn main() {
|
||||
|
||||
match eval_result {
|
||||
Ok(value) => {
|
||||
if !value.is_empty() {
|
||||
if !value.is_none() {
|
||||
println!("{value}")
|
||||
}
|
||||
}
|
||||
|
@ -9,16 +9,10 @@ pub struct Function {
|
||||
parameters: Vec<Identifier>,
|
||||
body: Block,
|
||||
r#type: Type,
|
||||
context: Map,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn new(
|
||||
parameters: Vec<Identifier>,
|
||||
body: Block,
|
||||
r#type: Option<Type>,
|
||||
context: Map,
|
||||
) -> Self {
|
||||
pub fn new(parameters: Vec<Identifier>, body: Block, r#type: Option<Type>) -> Self {
|
||||
let r#type = r#type.unwrap_or(Type::Function {
|
||||
parameter_types: vec![Type::Any; parameters.len()],
|
||||
return_type: Box::new(Type::Any),
|
||||
@ -28,7 +22,6 @@ impl Function {
|
||||
parameters,
|
||||
body,
|
||||
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() {
|
||||
return Err(Error::ExpectedArgumentAmount {
|
||||
function_name: "",
|
||||
return Err(Error::ExpectedFunctionArgumentAmount {
|
||||
source: "unknown".to_string(),
|
||||
expected: self.parameters.len(),
|
||||
actual: arguments.len(),
|
||||
});
|
||||
}
|
||||
|
||||
for (key, (value, r#type)) in context.variables()?.iter() {
|
||||
if self.context.variables()?.contains_key(key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.context
|
||||
.set(key.clone(), value.clone(), Some(r#type.clone()))?;
|
||||
}
|
||||
|
||||
let context = Map::new();
|
||||
let parameter_argument_pairs = self.parameters.iter().zip(arguments.iter());
|
||||
|
||||
for (identifier, value) in parameter_argument_pairs {
|
||||
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)
|
||||
}
|
||||
|
@ -55,6 +55,12 @@ impl Map {
|
||||
|
||||
Ok(previous)
|
||||
}
|
||||
|
||||
pub fn clear(&self) -> Result<()> {
|
||||
self.variables.write()?.clear();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Map {
|
||||
|
@ -21,14 +21,13 @@ use std::{
|
||||
pub mod function;
|
||||
pub mod list;
|
||||
pub mod map;
|
||||
pub mod table;
|
||||
|
||||
/// Dust value representation.
|
||||
///
|
||||
/// 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
|
||||
/// value that can be treated as any other.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Value {
|
||||
List(List),
|
||||
Map(Map),
|
||||
@ -37,8 +36,13 @@ pub enum Value {
|
||||
Float(f64),
|
||||
Integer(i64),
|
||||
Boolean(bool),
|
||||
#[default]
|
||||
Empty,
|
||||
Option(Option<Box<Value>>),
|
||||
}
|
||||
|
||||
impl Default for Value {
|
||||
fn default() -> Self {
|
||||
Value::Option(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
@ -71,7 +75,13 @@ impl Value {
|
||||
Value::Float(_) => Type::Float,
|
||||
Value::Integer(_) => Type::Integer,
|
||||
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
|
||||
@ -101,8 +111,12 @@ impl Value {
|
||||
matches!(self, Value::List(_))
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
matches!(self, Value::Empty)
|
||||
pub fn is_option(&self) -> bool {
|
||||
matches!(self, Value::Option(_))
|
||||
}
|
||||
|
||||
pub fn is_none(&self) -> bool {
|
||||
matches!(self, Value::Option(None))
|
||||
}
|
||||
|
||||
pub fn is_map(&self) -> bool {
|
||||
@ -206,11 +220,19 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `()`, or returns`Err` if `self` is not a `Value::Empty`.
|
||||
pub fn as_empty(&self) -> Result<()> {
|
||||
/// Returns `()`, or returns `Err` if `self` is not a `Value::Option(None)`.
|
||||
pub fn as_none(&self) -> Result<()> {
|
||||
match self {
|
||||
Value::Empty => Ok(()),
|
||||
value => Err(Error::ExpectedEmpty {
|
||||
Value::Option(option) => {
|
||||
if option.is_none() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::ExpectedNone {
|
||||
actual: self.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
value => Err(Error::ExpectedNone {
|
||||
actual: value.clone(),
|
||||
}),
|
||||
}
|
||||
@ -219,7 +241,7 @@ impl Value {
|
||||
|
||||
impl Default for &Value {
|
||||
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::Map(left), Value::Map(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,
|
||||
}
|
||||
}
|
||||
@ -408,8 +430,8 @@ impl Ord for Value {
|
||||
(Value::Map(_), _) => Ordering::Greater,
|
||||
(Value::Function(left), Value::Function(right)) => left.cmp(right),
|
||||
(Value::Function(_), _) => Ordering::Greater,
|
||||
(Value::Empty, Value::Empty) => Ordering::Equal,
|
||||
(Value::Empty, _) => Ordering::Less,
|
||||
(Value::Option(left), Value::Option(right)) => left.cmp(right),
|
||||
(Value::Option(_), _) => Ordering::Less,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -434,7 +456,7 @@ impl Serialize for Value {
|
||||
|
||||
list.end()
|
||||
}
|
||||
Value::Empty => todo!(),
|
||||
Value::Option(inner) => inner.serialize(serializer),
|
||||
Value::Map(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::Integer(int) => write!(f, "{int}"),
|
||||
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) => {
|
||||
write!(f, "[")?;
|
||||
for value in list.items().iter() {
|
||||
@ -506,7 +534,7 @@ impl From<Value> for Result<Value> {
|
||||
|
||||
impl From<()> for Value {
|
||||
fn from(_: ()) -> Self {
|
||||
Value::Empty
|
||||
Value::Option(None)
|
||||
}
|
||||
}
|
||||
|
||||
@ -729,7 +757,7 @@ impl<'de> Visitor<'de> for ValueVisitor {
|
||||
where
|
||||
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>
|
||||
@ -747,7 +775,7 @@ impl<'de> Visitor<'de> for ValueVisitor {
|
||||
where
|
||||
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>
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -78,6 +78,7 @@ module.exports = grammar({
|
||||
$.boolean,
|
||||
$.list,
|
||||
$.map,
|
||||
$.option
|
||||
),
|
||||
|
||||
integer: $ =>
|
||||
@ -176,6 +177,18 @@ module.exports = grammar({
|
||||
'}',
|
||||
),
|
||||
|
||||
|
||||
option: $ =>
|
||||
choice(
|
||||
'none',
|
||||
seq(
|
||||
'some',
|
||||
'(',
|
||||
$.expression,
|
||||
')',
|
||||
),
|
||||
),
|
||||
|
||||
index: $ =>
|
||||
prec.left(
|
||||
1,
|
||||
@ -331,6 +344,11 @@ module.exports = grammar({
|
||||
'any',
|
||||
'bool',
|
||||
'float',
|
||||
'int',
|
||||
'map',
|
||||
'num',
|
||||
'str',
|
||||
seq('[', $.type, ']'),
|
||||
seq(
|
||||
'(',
|
||||
repeat(
|
||||
@ -342,11 +360,12 @@ module.exports = grammar({
|
||||
')',
|
||||
optional(seq('->', $.type)),
|
||||
),
|
||||
'int',
|
||||
seq('[', $.type, ']'),
|
||||
'map',
|
||||
'num',
|
||||
'str',
|
||||
seq(
|
||||
'option',
|
||||
'(',
|
||||
$.type,
|
||||
')',
|
||||
)
|
||||
),
|
||||
),
|
||||
|
||||
|
@ -223,6 +223,10 @@
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"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": {
|
||||
"type": "PREC_LEFT",
|
||||
"value": 1,
|
||||
@ -1062,6 +1096,39 @@
|
||||
"type": "STRING",
|
||||
"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",
|
||||
"members": [
|
||||
@ -1120,16 +1187,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "int"
|
||||
},
|
||||
{
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "["
|
||||
"value": "option"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "("
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
@ -1137,21 +1204,9 @@
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "]"
|
||||
"value": ")"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "map"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "num"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "str"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -390,6 +390,21 @@
|
||||
"named": true,
|
||||
"fields": {}
|
||||
},
|
||||
{
|
||||
"type": "option",
|
||||
"named": true,
|
||||
"fields": {},
|
||||
"children": {
|
||||
"multiple": false,
|
||||
"required": false,
|
||||
"types": [
|
||||
{
|
||||
"type": "expression",
|
||||
"named": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "return",
|
||||
"named": true,
|
||||
@ -529,6 +544,10 @@
|
||||
"type": "map",
|
||||
"named": true
|
||||
},
|
||||
{
|
||||
"type": "option",
|
||||
"named": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"named": true
|
||||
@ -720,11 +739,11 @@
|
||||
},
|
||||
{
|
||||
"type": "float",
|
||||
"named": true
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "float",
|
||||
"named": false
|
||||
"named": true
|
||||
},
|
||||
{
|
||||
"type": "fn",
|
||||
@ -770,10 +789,18 @@
|
||||
"type": "metadata",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "none",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "num",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "option",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "output",
|
||||
"named": false
|
||||
@ -806,6 +833,10 @@
|
||||
"type": "return",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "some",
|
||||
"named": false
|
||||
},
|
||||
{
|
||||
"type": "str",
|
||||
"named": false
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user