Implement option value
This commit is contained in:
parent
20a6e707c5
commit
9dfaf1420c
6
examples/option.ds
Normal file
6
examples/option.ds
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
create_user = (fn email <string>, name <option(string)>) <map> {
|
||||||
|
{
|
||||||
|
email = email
|
||||||
|
username = (either_or username email)
|
||||||
|
}
|
||||||
|
}
|
@ -28,6 +28,7 @@ impl AbstractTree for FunctionCall {
|
|||||||
let function_expression = Expression::from_syntax_node(source, expression_node, context)?;
|
let function_expression = Expression::from_syntax_node(source, expression_node, context)?;
|
||||||
let function_type = function_expression.expected_type(context)?;
|
let function_type = function_expression.expected_type(context)?;
|
||||||
|
|
||||||
|
let mut minimum_parameter_count = 0;
|
||||||
let mut arguments = Vec::new();
|
let mut arguments = Vec::new();
|
||||||
|
|
||||||
for index in 2..node.child_count() - 1 {
|
for index in 2..node.child_count() - 1 {
|
||||||
@ -43,6 +44,11 @@ impl AbstractTree for FunctionCall {
|
|||||||
} = &function_type
|
} = &function_type
|
||||||
{
|
{
|
||||||
if let Some(r#type) = parameter_types.get(argument_index) {
|
if let Some(r#type) = parameter_types.get(argument_index) {
|
||||||
|
if let Type::Option(_) = r#type {
|
||||||
|
} else {
|
||||||
|
minimum_parameter_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
r#type
|
r#type
|
||||||
.check(&expression_type)
|
.check(&expression_type)
|
||||||
.map_err(|error| error.at_node(child, source))?;
|
.map_err(|error| error.at_node(child, source))?;
|
||||||
@ -54,13 +60,13 @@ impl AbstractTree for FunctionCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Type::Function {
|
if let Type::Function {
|
||||||
parameter_types, ..
|
parameter_types: _, ..
|
||||||
} = &function_type
|
} = &function_type
|
||||||
{
|
{
|
||||||
if arguments.len() != parameter_types.len() {
|
if arguments.len() < minimum_parameter_count {
|
||||||
return Err(Error::ExpectedFunctionArgumentAmount {
|
return Err(Error::ExpectedFunctionArgumentMinimum {
|
||||||
source: source[expression_node.byte_range()].to_string(),
|
source: source[expression_node.byte_range()].to_string(),
|
||||||
expected: parameter_types.len(),
|
minumum_expected: minimum_parameter_count,
|
||||||
actual: arguments.len(),
|
actual: arguments.len(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -41,14 +41,18 @@ impl BuiltInFunction for Sh {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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())?;
|
|
||||||
|
|
||||||
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");
|
||||||
|
|
||||||
command.arg("-c");
|
command.arg("-c");
|
||||||
command.arg(command_text);
|
command.arg(command_text);
|
||||||
|
|
||||||
|
let extra_command_text = arguments.get(1).unwrap_or_default().as_option()?;
|
||||||
|
|
||||||
|
if let Some(text) = extra_command_text {
|
||||||
|
command.args(["--", text.as_string()?]);
|
||||||
|
}
|
||||||
|
|
||||||
let output = command.spawn()?.wait_with_output()?.stdout;
|
let output = command.spawn()?.wait_with_output()?.stdout;
|
||||||
|
|
||||||
Ok(Value::String(String::from_utf8(output)?))
|
Ok(Value::String(String::from_utf8(output)?))
|
||||||
@ -56,7 +60,7 @@ impl BuiltInFunction for Sh {
|
|||||||
|
|
||||||
fn r#type(&self) -> crate::Type {
|
fn r#type(&self) -> crate::Type {
|
||||||
Type::Function {
|
Type::Function {
|
||||||
parameter_types: vec![Type::String],
|
parameter_types: vec![Type::String, Type::Option(Box::new(Type::String))],
|
||||||
return_type: Box::new(Type::String),
|
return_type: Box::new(Type::String),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
41
src/error.rs
41
src/error.rs
@ -74,9 +74,9 @@ pub enum Error {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/// A function was called with the wrong amount of arguments.
|
/// A function was called with the wrong amount of arguments.
|
||||||
ExpectedArgumentMinimum {
|
ExpectedFunctionArgumentMinimum {
|
||||||
function_name: &'static str,
|
source: String,
|
||||||
minimum: usize,
|
minumum_expected: usize,
|
||||||
actual: usize,
|
actual: usize,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -136,6 +136,10 @@ pub enum Error {
|
|||||||
actual: Value,
|
actual: Value,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ExpectedOption {
|
||||||
|
actual: Value,
|
||||||
|
},
|
||||||
|
|
||||||
/// A string, list, map or table value was expected.
|
/// A string, list, map or table value was expected.
|
||||||
ExpectedCollection {
|
ExpectedCollection {
|
||||||
actual: Value,
|
actual: Value,
|
||||||
@ -205,22 +209,6 @@ impl Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expect_argument_minimum<F: BuiltInFunction>(
|
|
||||||
function: &F,
|
|
||||||
minimum: usize,
|
|
||||||
actual: usize,
|
|
||||||
) -> Result<()> {
|
|
||||||
if actual < minimum {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Error::ExpectedArgumentMinimum {
|
|
||||||
function_name: function.name(),
|
|
||||||
minimum,
|
|
||||||
actual,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_type_check_error(&self, other: &Error) -> bool {
|
pub fn is_type_check_error(&self, other: &Error) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Error::WithContext { error, .. } => {
|
Error::WithContext { error, .. } => {
|
||||||
@ -335,14 +323,16 @@ impl fmt::Display for Error {
|
|||||||
f,
|
f,
|
||||||
"{source} expected {expected} arguments, but got {actual}.",
|
"{source} expected {expected} arguments, but got {actual}.",
|
||||||
),
|
),
|
||||||
ExpectedArgumentMinimum {
|
ExpectedFunctionArgumentMinimum {
|
||||||
function_name,
|
source,
|
||||||
minimum,
|
minumum_expected,
|
||||||
actual,
|
actual,
|
||||||
} => write!(
|
} => {
|
||||||
|
write!(
|
||||||
f,
|
f,
|
||||||
"{function_name} expected a minimum of {minimum} arguments, but got {actual}.",
|
"{source} expected at least {minumum_expected} arguments, but got {actual}."
|
||||||
),
|
)
|
||||||
|
}
|
||||||
ExpectedString { actual } => {
|
ExpectedString { actual } => {
|
||||||
write!(f, "Expected a string but got {actual}.")
|
write!(f, "Expected a string but got {actual}.")
|
||||||
}
|
}
|
||||||
@ -379,6 +369,7 @@ impl fmt::Display for Error {
|
|||||||
ExpectedFunction { actual } => {
|
ExpectedFunction { actual } => {
|
||||||
write!(f, "Expected function, but got {actual}.")
|
write!(f, "Expected function, but got {actual}.")
|
||||||
}
|
}
|
||||||
|
ExpectedOption { actual } => write!(f, "Expected option, but got {actual}."),
|
||||||
ExpectedCollection { actual } => {
|
ExpectedCollection { actual } => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{AbstractTree, Block, Error, Identifier, Map, Result, Type, Value};
|
use crate::{AbstractTree, Block, Identifier, Map, Result, Type, Value};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
@ -48,14 +48,6 @@ impl Function {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(&self, arguments: &[Value], source: &str, outer_context: &Map) -> Result<Value> {
|
pub fn call(&self, arguments: &[Value], source: &str, outer_context: &Map) -> Result<Value> {
|
||||||
if self.parameters.len() != arguments.len() {
|
|
||||||
return Err(Error::ExpectedFunctionArgumentAmount {
|
|
||||||
source: "unknown".to_string(),
|
|
||||||
expected: self.parameters.len(),
|
|
||||||
actual: arguments.len(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let context = Map::clone_from(outer_context)?;
|
let context = Map::clone_from(outer_context)?;
|
||||||
let parameter_argument_pairs = self.parameters.iter().zip(arguments.iter());
|
let parameter_argument_pairs = self.parameters.iter().zip(arguments.iter());
|
||||||
|
|
||||||
|
@ -220,6 +220,16 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `Option`, or returns `Err` if `self` is not a `Value::Option`.
|
||||||
|
pub fn as_option(&self) -> Result<&Option<Box<Value>>> {
|
||||||
|
match self {
|
||||||
|
Value::Option(option) => Ok(option),
|
||||||
|
value => Err(Error::ExpectedOption {
|
||||||
|
actual: value.clone(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `()`, or returns `Err` if `self` is not a `Value::Option(None)`.
|
/// Returns `()`, or returns `Err` if `self` is not a `Value::Option(None)`.
|
||||||
pub fn as_none(&self) -> Result<()> {
|
pub fn as_none(&self) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
|
Loading…
Reference in New Issue
Block a user