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,
)?)
} else {
None
context
.variables()?
.get(identifier.inner())
.map(|(_, r#type)| TypeDefinition::new(r#type.clone()))
}
} else {
None
@ -83,10 +86,17 @@ impl AbstractTree for Assignment {
AssignmentOperator::MinusEqual => todo!(),
}
} else {
if let Type::List(item_type) = identifier_type {
item_type
.check(&statement_type)
.map_err(|error| error.at_node(statement_node, source))?;
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!(),
}
}
@ -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> {

View File

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

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

View File

@ -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
.check(&expression_type)
.map_err(|error| error.at_node(child, source))?;
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)));
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@ impl BuiltInFunction for Output {
println!("{argument}");
}
Ok(Value::Empty)
Ok(Value::Option(None))
}
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;
@ -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),
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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,
$.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,
')',
)
),
),

View File

@ -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"
}
]
}

View File

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