Implement option type

This commit is contained in:
Jeff 2023-12-26 20:05:19 -05:00
parent 9dfaf1420c
commit 34db948c6e
10 changed files with 4730 additions and 3707 deletions

View File

@ -1,6 +1,22 @@
create_user = (fn email <string>, name <option(string)>) <map> { create_user = (fn email <str>, name <option(str)>) <map> {
{ {
email = email email = email
username = (either_or username email) username = (either_or name email)
} }
} }
(assert_equal
{
email = "bob@example.com"
username = "bob"
},
(create_user "bob@example.com" some("bob"))
)
(assert_equal
{
email = "sue@example.com"
username = "sue@example.com"
},
(create_user "sue@example.com" none)
)

View File

@ -90,7 +90,6 @@ impl AbstractTree for Assignment {
AssignmentOperator::Equal => {} AssignmentOperator::Equal => {}
AssignmentOperator::PlusEqual => { 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))?;

View File

@ -152,8 +152,6 @@ impl AbstractTree for FunctionCall {
let identifier_type = identifier.expected_type(context)?; let identifier_type = identifier.expected_type(context)?;
println!("{identifier_type:?}");
if let Type::Function { if let Type::Function {
parameter_types: _, parameter_types: _,
return_type, return_type,

View File

@ -86,6 +86,10 @@ impl Type {
(Type::Option(left), Type::Option(right)) => { (Type::Option(left), Type::Option(right)) => {
if left == right { if left == right {
Ok(()) Ok(())
} else if let Type::Any = left.as_ref() {
Ok(())
} else if let Type::Any = right.as_ref() {
Ok(())
} else { } else {
Err(Error::TypeCheck { Err(Error::TypeCheck {
expected: self.clone(), expected: self.clone(),
@ -93,6 +97,7 @@ impl Type {
}) })
} }
} }
(Type::Option(_), Type::None) | (Type::None, Type::Option(_)) => Ok(()),
(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 {

View File

@ -7,6 +7,7 @@ mod commands;
mod data_formats; mod data_formats;
mod fs; mod fs;
mod network; mod network;
mod option;
mod output; mod output;
mod packages; mod packages;
mod random; mod random;
@ -16,7 +17,7 @@ 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; 18] = [ pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 21] = [
&assert::Assert, &assert::Assert,
&assert::AssertEqual, &assert::AssertEqual,
&collections::Length, &collections::Length,
@ -28,6 +29,9 @@ pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 18] = [
&fs::Write, &fs::Write,
&fs::Append, &fs::Append,
&network::Download, &network::Download,
&option::EitherOr,
&option::IsNone,
&option::IsSome,
&output::Output, &output::Output,
&packages::InstallPackages, &packages::InstallPackages,
&random::Random, &random::Random,

View File

@ -0,0 +1,69 @@
use crate::{BuiltInFunction, Map, Result, Type, Value};
pub struct EitherOr;
impl BuiltInFunction for EitherOr {
fn name(&self) -> &'static str {
"either_or"
}
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
let option = arguments.first().unwrap_or_default().as_option()?;
let value = if let Some(value) = option {
*value.clone()
} else {
arguments.get(1).unwrap_or_default().clone()
};
Ok(value)
}
fn r#type(&self) -> Type {
Type::Function {
parameter_types: vec![Type::Option(Box::new(Type::Any)), Type::Any],
return_type: Box::new(Type::Boolean),
}
}
}
pub struct IsNone;
impl BuiltInFunction for IsNone {
fn name(&self) -> &'static str {
"is_none"
}
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
let option = arguments.first().unwrap_or_default().as_option()?;
Ok(Value::Boolean(option.is_none()))
}
fn r#type(&self) -> Type {
Type::Function {
parameter_types: vec![Type::Option(Box::new(Type::Any))],
return_type: Box::new(Type::Boolean),
}
}
}
pub struct IsSome;
impl BuiltInFunction for IsSome {
fn name(&self) -> &'static str {
"is_some"
}
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
let option = arguments.first().unwrap_or_default().as_option()?;
Ok(Value::Boolean(option.is_some()))
}
fn r#type(&self) -> Type {
Type::Function {
parameter_types: vec![Type::Option(Box::new(Type::Any))],
return_type: Box::new(Type::Boolean),
}
}
}

View File

@ -414,8 +414,11 @@ module.exports = grammar({
'assert_equal', 'assert_equal',
'bash', 'bash',
'download', 'download',
'either_or',
'fish', 'fish',
'from_json', 'from_json',
'is_none',
'is_some',
'length', 'length',
'metadata', 'metadata',
'output', 'output',

View File

@ -1361,6 +1361,10 @@
"type": "STRING", "type": "STRING",
"value": "download" "value": "download"
}, },
{
"type": "STRING",
"value": "either_or"
},
{ {
"type": "STRING", "type": "STRING",
"value": "fish" "value": "fish"
@ -1369,6 +1373,14 @@
"type": "STRING", "type": "STRING",
"value": "from_json" "value": "from_json"
}, },
{
"type": "STRING",
"value": "is_none"
},
{
"type": "STRING",
"value": "is_some"
},
{ {
"type": "STRING", "type": "STRING",
"value": "length" "value": "length"

View File

@ -721,6 +721,10 @@
"type": "download", "type": "download",
"named": false "named": false
}, },
{
"type": "either_or",
"named": false
},
{ {
"type": "else", "type": "else",
"named": false "named": false
@ -773,6 +777,14 @@
"type": "integer", "type": "integer",
"named": true "named": true
}, },
{
"type": "is_none",
"named": false
},
{
"type": "is_some",
"named": false
},
{ {
"type": "length", "type": "length",
"named": false "named": false

File diff suppressed because it is too large Load Diff