Implement option value

This commit is contained in:
Jeff 2023-12-26 19:33:19 -05:00
parent 20a6e707c5
commit 9dfaf1420c
6 changed files with 51 additions and 42 deletions

6
examples/option.ds Normal file
View File

@ -0,0 +1,6 @@
create_user = (fn email <string>, name <option(string)>) <map> {
{
email = email
username = (either_or username email)
}
}

View File

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

View File

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

View File

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

View File

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

View File

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