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,
|
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,12 +86,19 @@ impl AbstractTree for Assignment {
|
|||||||
AssignmentOperator::MinusEqual => todo!(),
|
AssignmentOperator::MinusEqual => todo!(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
match operator {
|
||||||
|
AssignmentOperator::Equal => {}
|
||||||
|
AssignmentOperator::PlusEqual => {
|
||||||
if let Type::List(item_type) = identifier_type {
|
if let Type::List(item_type) = identifier_type {
|
||||||
|
println!("{item_type} {statement_type}");
|
||||||
item_type
|
item_type
|
||||||
.check(&statement_type)
|
.check(&statement_type)
|
||||||
.map_err(|error| error.at_node(statement_node, source))?;
|
.map_err(|error| error.at_node(statement_node, source))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
AssignmentOperator::MinusEqual => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let variable_key = identifier.inner().clone();
|
let variable_key = identifier.inner().clone();
|
||||||
let variable_type = if let Some(definition) = &type_definition {
|
let variable_type = if let Some(definition) = &type_definition {
|
||||||
@ -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> {
|
||||||
|
@ -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)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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> {
|
||||||
|
@ -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
|
|
||||||
} else {
|
|
||||||
expected_type
|
|
||||||
};
|
|
||||||
|
|
||||||
expected_type
|
|
||||||
.check(&expression_type)
|
.check(&expression_type)
|
||||||
.map_err(|error| error.at_node(child, source))?;
|
.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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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> {
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)?;
|
||||||
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(),
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
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;
|
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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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.
|
/// 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 } => {
|
||||||
|
@ -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;
|
||||||
|
@ -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}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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>
|
||||||
|
@ -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,
|
$.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',
|
')',
|
||||||
|
)
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
@ -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"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -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
Loading…
Reference in New Issue
Block a user