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_type = function_expression.expected_type(context)?;
|
||||
|
||||
let mut minimum_parameter_count = 0;
|
||||
let mut arguments = Vec::new();
|
||||
|
||||
for index in 2..node.child_count() - 1 {
|
||||
@ -43,6 +44,11 @@ impl AbstractTree for FunctionCall {
|
||||
} = &function_type
|
||||
{
|
||||
if let Some(r#type) = parameter_types.get(argument_index) {
|
||||
if let Type::Option(_) = r#type {
|
||||
} else {
|
||||
minimum_parameter_count += 1;
|
||||
}
|
||||
|
||||
r#type
|
||||
.check(&expression_type)
|
||||
.map_err(|error| error.at_node(child, source))?;
|
||||
@ -54,13 +60,13 @@ impl AbstractTree for FunctionCall {
|
||||
}
|
||||
|
||||
if let Type::Function {
|
||||
parameter_types, ..
|
||||
parameter_types: _, ..
|
||||
} = &function_type
|
||||
{
|
||||
if arguments.len() != parameter_types.len() {
|
||||
return Err(Error::ExpectedFunctionArgumentAmount {
|
||||
if arguments.len() < minimum_parameter_count {
|
||||
return Err(Error::ExpectedFunctionArgumentMinimum {
|
||||
source: source[expression_node.byte_range()].to_string(),
|
||||
expected: parameter_types.len(),
|
||||
minumum_expected: minimum_parameter_count,
|
||||
actual: arguments.len(),
|
||||
});
|
||||
}
|
||||
|
@ -41,14 +41,18 @@ impl BuiltInFunction for Sh {
|
||||
}
|
||||
|
||||
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 mut command = Command::new("sh");
|
||||
|
||||
command.arg("-c");
|
||||
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;
|
||||
|
||||
Ok(Value::String(String::from_utf8(output)?))
|
||||
@ -56,7 +60,7 @@ impl BuiltInFunction for Sh {
|
||||
|
||||
fn r#type(&self) -> crate::Type {
|
||||
Type::Function {
|
||||
parameter_types: vec![Type::String],
|
||||
parameter_types: vec![Type::String, Type::Option(Box::new(Type::String))],
|
||||
return_type: Box::new(Type::String),
|
||||
}
|
||||
}
|
||||
|
43
src/error.rs
43
src/error.rs
@ -74,9 +74,9 @@ pub enum Error {
|
||||
},
|
||||
|
||||
/// A function was called with the wrong amount of arguments.
|
||||
ExpectedArgumentMinimum {
|
||||
function_name: &'static str,
|
||||
minimum: usize,
|
||||
ExpectedFunctionArgumentMinimum {
|
||||
source: String,
|
||||
minumum_expected: usize,
|
||||
actual: usize,
|
||||
},
|
||||
|
||||
@ -136,6 +136,10 @@ pub enum Error {
|
||||
actual: Value,
|
||||
},
|
||||
|
||||
ExpectedOption {
|
||||
actual: Value,
|
||||
},
|
||||
|
||||
/// A string, list, map or table value was expected.
|
||||
ExpectedCollection {
|
||||
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 {
|
||||
match self {
|
||||
Error::WithContext { error, .. } => {
|
||||
@ -335,14 +323,16 @@ impl fmt::Display for Error {
|
||||
f,
|
||||
"{source} expected {expected} arguments, but got {actual}.",
|
||||
),
|
||||
ExpectedArgumentMinimum {
|
||||
function_name,
|
||||
minimum,
|
||||
ExpectedFunctionArgumentMinimum {
|
||||
source,
|
||||
minumum_expected,
|
||||
actual,
|
||||
} => write!(
|
||||
f,
|
||||
"{function_name} expected a minimum of {minimum} arguments, but got {actual}.",
|
||||
),
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"{source} expected at least {minumum_expected} arguments, but got {actual}."
|
||||
)
|
||||
}
|
||||
ExpectedString { actual } => {
|
||||
write!(f, "Expected a string but got {actual}.")
|
||||
}
|
||||
@ -379,6 +369,7 @@ impl fmt::Display for Error {
|
||||
ExpectedFunction { actual } => {
|
||||
write!(f, "Expected function, but got {actual}.")
|
||||
}
|
||||
ExpectedOption { actual } => write!(f, "Expected option, but got {actual}."),
|
||||
ExpectedCollection { actual } => {
|
||||
write!(
|
||||
f,
|
||||
|
@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
|
||||
|
||||
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)]
|
||||
pub struct Function {
|
||||
@ -48,14 +48,6 @@ impl Function {
|
||||
}
|
||||
|
||||
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 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)`.
|
||||
pub fn as_none(&self) -> Result<()> {
|
||||
match self {
|
||||
|
Loading…
Reference in New Issue
Block a user