Implement custom and built-in types

This commit is contained in:
Jeff 2024-02-15 10:33:25 -05:00
parent e1c3e8bc0d
commit e7f5d66297
20 changed files with 206 additions and 200 deletions

View File

@ -14,7 +14,6 @@ pub struct Assignment {
type_specification: Option<TypeSpecification>,
operator: AssignmentOperator,
statement: Statement,
syntax_position: SourcePosition,
}

View File

@ -27,7 +27,7 @@ impl EnumDefinition {
}
}
pub fn instantiate(&self, variant: String, content: Value) -> EnumInstance {
pub fn instantiate(&self, variant: String, content: Option<Value>) -> EnumInstance {
EnumInstance::new(self.identifier.inner().clone(), variant, content)
}

View File

@ -89,9 +89,11 @@ impl AbstractTree for Match {
{
let statement_context = Context::with_variables_from(context)?;
if let Some(identifier) = enum_pattern.inner_identifier() {
if let (Some(identifier), Some(value)) =
(enum_pattern.inner_identifier(), enum_instance.value())
{
statement_context
.set_value(identifier.inner().clone(), enum_instance.value().clone())?;
.set_value(identifier.inner().clone(), value.as_ref().clone())?;
}
return statement.run(source, &statement_context);

View File

@ -3,6 +3,7 @@ use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize};
use crate::{
built_in_types::BuiltInType,
error::{RuntimeError, SyntaxError, ValidationError},
AbstractTree, Context, Format, Identifier, SyntaxNode, Value,
};
@ -13,6 +14,10 @@ pub enum Type {
Boolean,
Collection,
Custom(Identifier),
CustomWithArgument {
name: Identifier,
argument: Box<Type>,
},
Float,
Function {
parameter_types: Vec<Type>,
@ -21,14 +26,17 @@ pub enum Type {
Integer,
List(Box<Type>),
Map,
None,
Number,
String,
Range,
Option(Box<Type>),
None,
}
impl Type {
pub fn option(inner_type: Option<Type>) -> Self {
BuiltInType::Option.get(inner_type).clone()
}
pub fn list(item_type: Type) -> Self {
Type::List(Box::new(item_type))
}
@ -40,10 +48,6 @@ impl Type {
}
}
pub fn option(optional_type: Type) -> Self {
Type::Option(Box::new(optional_type))
}
/// Returns a boolean indicating whether is type is accepting of the other.
///
/// The types do not need to match exactly. For example, the Any variant matches all of the
@ -70,17 +74,22 @@ impl Type {
| (Type::Number, Type::Float)
| (Type::Integer, Type::Number)
| (Type::Float, Type::Number)
| (Type::None, Type::None)
| (Type::String, Type::String) => true,
(Type::Custom(left), Type::Custom(right)) => left == right,
(Type::Option(_), Type::None) => true,
(Type::Option(left), Type::Option(right)) => {
if let Type::Any = left.as_ref() {
true
} else if left == right {
true
} else {
(
Type::CustomWithArgument {
name: left_name,
argument: left_argument,
},
Type::CustomWithArgument {
name: right_name,
argument: right_argument,
},
) => {
if left_name != right_name {
false
} else {
left_argument == right_argument
}
}
(Type::List(self_item_type), Type::List(other_item_type)) => {
@ -136,7 +145,21 @@ impl AbstractTree for Type {
let type_node = node.child(0).unwrap();
let r#type = match type_node.kind() {
"identifier" => Type::Custom(Identifier::from_syntax(type_node, _source, _context)?),
"identifier" => {
if node.child_count() == 1 {
Type::Custom(Identifier::from_syntax(type_node, _source, _context)?)
} else {
let name = Identifier::from_syntax(type_node, _source, _context)?;
let argument_node = node.child(2).unwrap();
let argument = Type::from_syntax(argument_node, _source, _context)?;
Type::CustomWithArgument {
name,
argument: Box::new(argument),
}
}
}
"[" => {
let item_type_node = node.child(1).unwrap();
let item_type = Type::from_syntax(item_type_node, _source, _context)?;
@ -165,7 +188,7 @@ impl AbstractTree for Type {
let return_type = if final_node.is_named() {
Type::from_syntax(final_node, _source, _context)?
} else {
Type::None
Type::option(None)
};
Type::Function {
@ -178,16 +201,9 @@ impl AbstractTree for Type {
"num" => Type::Number,
"none" => Type::None,
"str" => Type::String,
"option" => {
let inner_type_node = node.child(2).unwrap();
let inner_type = Type::from_syntax(inner_type_node, _source, _context)?;
Type::Option(Box::new(inner_type))
}
_ => {
return Err(SyntaxError::UnexpectedSyntaxNode {
expected: "any, bool, float, int, num, str, option, custom type, (, [ or {"
.to_string(),
expected: "any, bool, float, int, num, str, custom type, (, [ or {".to_string(),
actual: type_node.kind().to_string(),
location: type_node.start_position(),
relevant_source: _source[type_node.byte_range()].to_string(),
@ -217,8 +233,11 @@ impl Format for Type {
Type::Any => output.push_str("any"),
Type::Boolean => output.push_str("bool"),
Type::Collection => output.push_str("collection"),
Type::Custom(_) => todo!(),
Type::CustomWithArgument {
name: _,
argument: _,
} => todo!(),
Type::Float => output.push_str("float"),
Type::Function {
parameter_types,
@ -246,14 +265,9 @@ impl Format for Type {
Type::Map => {
output.push_str("map");
}
Type::None => output.push_str("none"),
Type::None => output.push_str("Option::None"),
Type::Number => output.push_str("num"),
Type::String => output.push_str("str"),
Type::Option(optional_type) => {
output.push_str("option(");
optional_type.format(output, indent_level);
output.push(')');
}
Type::Range => todo!(),
}
}
@ -266,6 +280,9 @@ impl Display for Type {
Type::Boolean => write!(f, "bool"),
Type::Collection => write!(f, "collection"),
Type::Custom(identifier) => write!(f, "{identifier}"),
Type::CustomWithArgument { name, argument } => {
write!(f, "{name}<{argument}>")
}
Type::Float => write!(f, "float"),
Type::Function {
parameter_types,
@ -290,9 +307,6 @@ impl Display for Type {
Type::Number => write!(f, "num"),
Type::None => write!(f, "none"),
Type::String => write!(f, "str"),
Type::Option(inner_type) => {
write!(f, "option({})", inner_type)
}
Type::Range => todo!(),
}
}

View File

@ -17,7 +17,6 @@ pub enum ValueNode {
Integer(String),
String(String),
List(Vec<Expression>),
Option(Option<Box<Expression>>),
Map(MapNode),
Range(RangeInclusive<i64>),
Struct {
@ -68,18 +67,6 @@ impl AbstractTree for ValueNode {
"map" => {
ValueNode::Map(MapNode::from_syntax(child, source, context)?)
}
"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(expression_node, source, context)?;
ValueNode::Option(Some(Box::new(expression)))
}
}
"range" => {
let mut split = source[child.byte_range()].split("..");
let start = split.next().unwrap().parse().unwrap();
@ -158,19 +145,21 @@ impl AbstractTree for ValueNode {
Type::List(Box::new(Type::Any))
}
}
ValueNode::Option(option) => {
if let Some(expression) = option {
Type::Option(Box::new(expression.expected_type(context)?))
} else {
Type::None
}
}
ValueNode::Map(_) => Type::Map,
ValueNode::Struct { name, .. } => {
Type::Custom(name.clone())
}
ValueNode::Range(_) => Type::Range,
ValueNode::Enum { name, .. } => Type::Custom(name.clone()),
ValueNode::Enum { name, variant: _, expression } => {
if let Some(expression) = expression {
Type::CustomWithArgument {
name: name.clone(),
argument: Box::new(expression.expected_type(context)?)
}
} else {
Type::Custom(name.clone())
}
},
};
Ok(r#type)
@ -212,15 +201,6 @@ impl AbstractTree for ValueNode {
Value::List(List::with_items(values))
}
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(map_node) => map_node.run(source, context)?,
ValueNode::Range(range) => Value::Range(range.clone()),
ValueNode::Struct { name, properties } => {
@ -247,7 +227,7 @@ impl AbstractTree for ValueNode {
};
let instance = if let Some(definition) = context.get_definition(name.inner())? {
if let TypeDefinition::Enum(enum_defintion) = definition {
enum_defintion.instantiate(variant.inner().clone(), value)
enum_defintion.instantiate(variant.inner().clone(), Some(value))
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedEnumDefintion {
@ -290,15 +270,6 @@ impl Format for ValueNode {
output.push(']');
}
ValueNode::Option(option) => {
if let Some(expression) = option {
output.push_str("some(");
expression.format(output, indent_level);
output.push(')');
} else {
output.push_str("none");
}
}
ValueNode::Map(map_node) => map_node.format(output, indent_level),
ValueNode::Struct { name, properties } => {
name.format(output, indent_level);
@ -325,8 +296,6 @@ impl Ord for ValueNode {
(ValueNode::String(_), _) => Ordering::Greater,
(ValueNode::List(left), ValueNode::List(right)) => left.cmp(right),
(ValueNode::List(_), _) => Ordering::Greater,
(ValueNode::Option(left), ValueNode::Option(right)) => left.cmp(right),
(ValueNode::Option(_), _) => Ordering::Greater,
(ValueNode::Map(left), ValueNode::Map(right)) => left.cmp(right),
(ValueNode::Map(_), _) => Ordering::Greater,
(ValueNode::Struct{ name: left_name, properties: left_properties }, ValueNode::Struct {name: right_name, properties: right_properties} ) => {

View File

@ -103,13 +103,13 @@ impl Callable for BuiltInFunction {
Value::Enum(EnumInstance::new(
"Result".to_string(),
"Ok".to_string(),
Value::none(),
Some(Value::none()),
))
} else {
Value::Enum(EnumInstance::new(
"Result".to_string(),
"Error".to_string(),
Value::none(),
Some(Value::none()),
))
};

View File

@ -1,7 +1,7 @@
use enum_iterator::Sequence;
use serde::{Deserialize, Serialize};
use crate::{error::RuntimeError, Context, List, Type, Value};
use crate::{error::RuntimeError, Context, EnumInstance, List, Type, Value};
use super::Callable;
@ -122,7 +122,7 @@ impl Callable for StrFunction {
}
StrFunction::Find => Type::function(
vec![Type::String, Type::String],
Type::option(Type::Integer),
Type::option(Some(Type::Integer)),
),
StrFunction::Insert => Type::function(
vec![Type::String, Type::Integer, Type::String],
@ -137,7 +137,7 @@ impl Callable for StrFunction {
StrFunction::Parse => Type::function(vec![Type::String], Type::Any),
StrFunction::Remove => Type::function(
vec![Type::String, Type::Integer],
Type::option(Type::String),
Type::option(Some(Type::String)),
),
StrFunction::ReplaceRange => Type::function(
vec![Type::String, Type::list(Type::Integer), Type::String],
@ -175,9 +175,10 @@ impl Callable for StrFunction {
StrFunction::StartsWith => {
Type::function(vec![Type::String, Type::String], Type::Boolean)
}
StrFunction::StripPrefix => {
Type::function(vec![Type::String, Type::String], Type::option(Type::String))
}
StrFunction::StripPrefix => Type::function(
vec![Type::String, Type::String],
Type::option(Some(Type::String)),
),
StrFunction::ToLowercase => Type::function(vec![Type::String], Type::String),
StrFunction::ToUppercase => Type::function(vec![Type::String], Type::String),
StrFunction::Truncate => {
@ -233,9 +234,21 @@ impl Callable for StrFunction {
let pattern = pattern_string.as_str();
let find = string
.find(pattern)
.map(|index| Box::new(Value::Integer(index as i64)));
.map(|index| Value::Integer(index as i64));
Value::Option(find)
if let Some(index) = find {
Value::Enum(EnumInstance::new(
"Option".to_string(),
"Some".to_string(),
Some(index),
))
} else {
Value::Enum(EnumInstance::new(
"Option".to_string(),
"None".to_string(),
Some(Value::none()),
))
}
}
StrFunction::IsAscii => {
RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
@ -292,9 +305,9 @@ impl Callable for StrFunction {
let string = arguments.first().unwrap().as_string()?;
if let Ok(integer) = string.parse::<i64>() {
Value::option(Some(Value::Integer(integer)))
Value::Integer(integer)
} else if let Ok(float) = string.parse::<f64>() {
Value::option(Some(Value::Float(float)))
Value::Float(float)
} else {
Value::none()
}
@ -413,7 +426,11 @@ impl Callable for StrFunction {
]))
});
Value::option(sections)
if let Some(sections) = sections {
Value::some(sections)
} else {
Value::none()
}
}
StrFunction::SplitTerminator => {
RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
@ -458,7 +475,15 @@ impl Callable for StrFunction {
.strip_prefix(prefix)
.map(|remainder| Value::string(remainder.to_string()));
Value::option(stripped)
if let Some(value) = stripped {
Value::Enum(EnumInstance::new(
"Option".to_string(),
"Some".to_string(),
Some(value),
))
} else {
Value::none()
}
}
StrFunction::ToLowercase => {
RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;

36
src/built_in_types.rs Normal file
View File

@ -0,0 +1,36 @@
use std::sync::OnceLock;
use enum_iterator::Sequence;
use serde::{Deserialize, Serialize};
use crate::{Identifier, Type};
static OPTION: OnceLock<Type> = OnceLock::new();
#[derive(Sequence, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum BuiltInType {
Option,
}
impl BuiltInType {
pub fn name(&self) -> &'static str {
match self {
BuiltInType::Option => todo!(),
}
}
pub fn get(&self, inner_type: Option<Type>) -> &Type {
match self {
BuiltInType::Option => OPTION.get_or_init(|| {
if let Some(inner_type) = inner_type {
Type::CustomWithArgument {
name: Identifier::new("Option"),
argument: Box::new(inner_type),
}
} else {
Type::Custom(Identifier::new("Option"))
}
}),
}
}
}

View File

@ -6,7 +6,8 @@ use serde::{Deserialize, Serialize};
use crate::{
built_in_functions::{fs::fs_functions, json::json_functions, str::string_functions, Callable},
error::{RuntimeError, SyntaxError, ValidationError},
AbstractTree, BuiltInFunction, Context, Format, Function, List, Map, SyntaxNode, Type, Value,
AbstractTree, BuiltInFunction, Context, EnumInstance, Format, Function, Identifier, List, Map,
SyntaxNode, Type, Value,
};
static ARGS: OnceLock<Value> = OnceLock::new();
@ -14,6 +15,7 @@ static FS: OnceLock<Value> = OnceLock::new();
static JSON: OnceLock<Value> = OnceLock::new();
static RANDOM: OnceLock<Value> = OnceLock::new();
static STRING: OnceLock<Value> = OnceLock::new();
static NONE: OnceLock<Value> = OnceLock::new();
/// Returns the entire built-in value API.
pub fn all_built_in_values() -> impl Iterator<Item = BuiltInValue> {
@ -38,6 +40,9 @@ pub enum BuiltInValue {
/// Get the length of a collection.
Length,
/// The absence of a value.
None,
/// Print a value to stdout.
Output,
@ -57,6 +62,7 @@ impl BuiltInValue {
BuiltInValue::Fs => "fs",
BuiltInValue::Json => "json",
BuiltInValue::Length => BuiltInFunction::Length.name(),
BuiltInValue::None => "None",
BuiltInValue::Output => "output",
BuiltInValue::Random => "random",
BuiltInValue::Str => "str",
@ -73,6 +79,7 @@ impl BuiltInValue {
BuiltInValue::Fs => "File and directory tools.",
BuiltInValue::Json => "JSON formatting tools.",
BuiltInValue::Length => BuiltInFunction::Length.description(),
BuiltInValue::None => "The absence of a value.",
BuiltInValue::Output => "output",
BuiltInValue::Random => "random",
BuiltInValue::Str => "string",
@ -89,6 +96,7 @@ impl BuiltInValue {
BuiltInValue::Fs => Type::Map,
BuiltInValue::Json => Type::Map,
BuiltInValue::Length => BuiltInFunction::Length.r#type(),
BuiltInValue::None => Type::Custom(Identifier::new("Option")),
BuiltInValue::Output => BuiltInFunction::Output.r#type(),
BuiltInValue::Random => Type::Map,
BuiltInValue::Str => Type::Map,
@ -134,6 +142,13 @@ impl BuiltInValue {
Value::Map(Map::with_values(json_context))
}),
BuiltInValue::Length => &Value::Function(Function::BuiltIn(BuiltInFunction::Length)),
BuiltInValue::None => NONE.get_or_init(|| {
Value::Enum(EnumInstance::new(
"Option".to_string(),
"None".to_string(),
None,
))
}),
BuiltInValue::Output => &Value::Function(Function::BuiltIn(BuiltInFunction::Output)),
BuiltInValue::Random => RANDOM.get_or_init(|| {
let mut random_context = BTreeMap::new();

View File

@ -18,6 +18,7 @@ pub use tree_sitter::Node as SyntaxNode;
pub mod abstract_tree;
pub mod built_in_functions;
pub mod built_in_type_definitions;
pub mod built_in_types;
pub mod built_in_values;
pub mod context;
pub mod error;

View File

@ -1,20 +1,22 @@
use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize};
use crate::Value;
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct EnumInstance {
name: String,
variant_name: String,
value: Box<Value>,
value: Option<Box<Value>>,
}
impl EnumInstance {
pub fn new(name: String, variant_name: String, value: Value) -> Self {
pub fn new(name: String, variant_name: String, value: Option<Value>) -> Self {
Self {
name,
variant_name,
value: Box::new(value),
value: value.map(|value| Box::new(value)),
}
}
@ -26,13 +28,13 @@ impl EnumInstance {
&self.variant_name
}
pub fn value(&self) -> &Value {
pub fn value(&self) -> &Option<Box<Value>> {
&self.value
}
}
impl Display for EnumInstance {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}::{}({})", self.name, self.variant_name, self.value)
write!(f, "{}::{}({:?})", self.name, self.variant_name, self.value)
}
}

View File

@ -1,5 +1,7 @@
//! Types that represent runtime values.
use crate::{error::RuntimeError, Identifier, Type, TypeSpecification};
use crate::{
built_in_values::BuiltInValue, error::RuntimeError, Identifier, Type, TypeSpecification,
};
use serde::{
de::{MapAccess, SeqAccess, Visitor},
@ -42,18 +44,23 @@ pub enum Value {
Integer(i64),
Boolean(bool),
Range(RangeInclusive<i64>),
Option(Option<Box<Value>>),
Struct(StructInstance),
Enum(EnumInstance),
}
impl Default for Value {
fn default() -> Self {
Value::none()
}
}
impl Value {
pub fn none() -> Self {
BuiltInValue::None.get().clone()
}
pub fn some(value: Value) -> Value {
Value::Enum(EnumInstance::new(
"Option".to_string(),
"Some".to_string(),
Some(value),
))
}
pub fn string<T: Into<String>>(string: T) -> Self {
Value::String(string.into())
}
@ -102,31 +109,12 @@ impl Value {
Value::Float(_) => Type::Float,
Value::Integer(_) => Type::Integer,
Value::Boolean(_) => Type::Boolean,
Value::Option(option) => {
if let Some(value) = option {
Type::Option(Box::new(value.r#type()))
} else {
Type::None
}
}
Value::Range(_) => todo!(),
Value::Struct(_) => todo!(),
Value::Enum(_) => todo!(),
}
}
pub fn none() -> Self {
Value::Option(None)
}
pub fn some(value: Value) -> Self {
Value::Option(Some(Box::new(value)))
}
pub fn option(option: Option<Value>) -> Self {
Value::Option(option.map(Box::new))
}
pub fn is_string(&self) -> bool {
matches!(self, Value::String(_))
}
@ -151,14 +139,6 @@ impl Value {
matches!(self, Value::List(_))
}
pub fn is_option(&self) -> bool {
matches!(self, Value::Option(_))
}
pub fn is_none(&self) -> bool {
matches!(self, Value::Option(None))
}
pub fn is_map(&self) -> bool {
matches!(self, Value::Map(_))
}
@ -167,6 +147,10 @@ impl Value {
matches!(self, Value::Function(_))
}
pub fn is_none(&self) -> bool {
self == &Value::none()
}
/// Borrows the value stored in `self` as `&String`, or returns `Err` if `self` is not a `Value::String`.
pub fn as_string(&self) -> Result<&String, RuntimeError> {
match self {
@ -259,39 +243,17 @@ impl Value {
}),
}
}
}
/// Returns `Option`, or returns `Err` if `self` is not a `Value::Option`.
pub fn as_option(&self) -> Result<&Option<Box<Value>>, RuntimeError> {
match self {
Value::Option(option) => Ok(option),
value => Err(RuntimeError::ExpectedOption {
actual: value.clone(),
}),
}
}
/// Returns `()`, or returns `Err` if `self` is not a `Value::none()`.
pub fn as_none(&self) -> Result<(), RuntimeError> {
match self {
Value::Option(option) => {
if option.is_none() {
Ok(())
} else {
Err(RuntimeError::ExpectedNone {
actual: self.clone(),
})
}
}
value => Err(RuntimeError::ExpectedNone {
actual: value.clone(),
}),
}
impl Default for Value {
fn default() -> Self {
Value::none()
}
}
impl Default for &Value {
fn default() -> Self {
&Value::Option(None)
BuiltInValue::None.get()
}
}
@ -445,7 +407,6 @@ impl PartialEq for Value {
(Value::List(left), Value::List(right)) => left == right,
(Value::Map(left), Value::Map(right)) => left == right,
(Value::Function(left), Value::Function(right)) => left == right,
(Value::Option(left), Value::Option(right)) => left == right,
(Value::Range(left), Value::Range(right)) => left == right,
(Value::Struct(left), Value::Struct(right)) => left == right,
(Value::Enum(left), Value::Enum(right)) => left == right,
@ -495,9 +456,7 @@ impl Ord for Value {
left_len.cmp(&right_len)
}
(Value::Range(_), _) => Ordering::Greater,
(Value::Option(left), Value::Option(right)) => left.cmp(right),
(Value::Option(_), _) => Ordering::Less,
(Value::Range(_), _) => Ordering::Less,
}
}
}
@ -522,7 +481,6 @@ impl Serialize for Value {
list.end()
}
Value::Option(inner) => inner.serialize(serializer),
Value::Map(map) => {
let entries = map.inner();
let mut map = serializer.serialize_map(Some(entries.len()))?;
@ -548,13 +506,6 @@ impl Display for Value {
Value::Float(float) => write!(f, "{float}"),
Value::Integer(int) => write!(f, "{int}"),
Value::Boolean(boolean) => write!(f, "{boolean}"),
Value::Option(option) => {
if let Some(value) = option {
write!(f, "some({})", value)
} else {
write!(f, "none")
}
}
Value::List(list) => write!(f, "{list}"),
Value::Map(map) => write!(f, "{map}"),
Value::Function(function) => write!(f, "{function}"),
@ -839,9 +790,7 @@ impl<'de> Visitor<'de> for ValueVisitor {
where
D: serde::Deserializer<'de>,
{
Ok(Value::Option(Some(Box::new(Value::deserialize(
deserializer,
)?))))
Ok(Value::Enum(EnumInstance::deserialize(deserializer)?))
}
fn visit_unit<E>(self) -> std::result::Result<Self::Value, E>

View File

@ -29,11 +29,11 @@ fn ends_with() {
fn find() {
let result = interpret("str:find('abc', 'a')");
assert_eq!(result, Ok(Value::Option(Some(Box::new(Value::Integer(0))))));
assert_eq!(result, Ok(Value::some(Value::Integer(0))));
let result = interpret("str:find('foobar', 'b')");
assert_eq!(result, Ok(Value::Option(Some(Box::new(Value::Integer(3))))));
assert_eq!(result, Ok(Value::some(Value::Integer(3))));
}
#[test]

View File

@ -7,7 +7,7 @@ fn option() {
Ok(Value::Enum(EnumInstance::new(
"Option".to_string(),
"None".to_string(),
Value::none()
Some(Value::none()),
)))
);
assert_eq!(
@ -21,7 +21,7 @@ fn option() {
Ok(Value::Enum(EnumInstance::new(
"Option".to_string(),
"Some".to_string(),
Value::Integer(1),
Some(Value::Integer(1)),
)))
);
}
@ -33,7 +33,7 @@ fn result() {
Ok(Value::Enum(EnumInstance::new(
"Result".to_string(),
"Ok".to_string(),
Value::Integer(1)
Some(Value::Integer(1)),
)))
);
assert_eq!(
@ -46,7 +46,7 @@ fn result() {
Ok(Value::Enum(EnumInstance::new(
"Result".to_string(),
"Error".to_string(),
Value::String("uh-oh!".to_string())
Some(Value::String("uh-oh!".to_string())),
)))
);
}

View File

@ -18,7 +18,7 @@ fn assert_equal() {
Ok(Value::Enum(EnumInstance::new(
"Result".to_string(),
"Ok".to_string(),
Value::none()
Some(Value::none()),
)))
);
assert_eq!(
@ -26,7 +26,7 @@ fn assert_equal() {
Ok(Value::Enum(EnumInstance::new(
"Result".to_string(),
"Error".to_string(),
Value::none()
Some(Value::none()),
)))
);
}

View File

@ -18,7 +18,7 @@ fn simple_enum() {
Ok(Value::Enum(EnumInstance::new(
"Foobar".to_string(),
"Foo".to_string(),
Value::none()
Some(Value::none())
)))
);
}
@ -45,11 +45,11 @@ fn nested_enum() {
Ok(Value::Enum(EnumInstance::new(
"Foobar".to_string(),
"Bar".to_string(),
Value::Enum(EnumInstance::new(
Some(Value::Enum(EnumInstance::new(
"Fizzbuzz".to_string(),
"Fizz".to_string(),
Value::none()
))
Some(Value::none())
)))
)))
);
}

View File

@ -43,7 +43,7 @@ fn callback() {
#[test]
fn built_in_function_call() {
assert_eq!(interpret("output('Hiya')"), Ok(Value::Option(None)));
assert_eq!(interpret("output('Hiya')"), Ok(Value::none()));
}
#[test]

View File

@ -2,8 +2,8 @@ use dust_lang::*;
#[test]
fn empty() {
assert_eq!(interpret("x = 9"), Ok(Value::Option(None)));
assert_eq!(interpret("x = 1 + 1"), Ok(Value::Option(None)));
assert_eq!(interpret("x = 9"), Ok(Value::none()));
assert_eq!(interpret("x = 1 + 1"), Ok(Value::none()));
}
#[test]

View File

@ -2,7 +2,7 @@ use dust_lang::*;
#[test]
fn while_loop() {
assert_eq!(interpret("while false { 'foo' }"), Ok(Value::Option(None)))
assert_eq!(interpret("while false { 'foo' }"), Ok(Value::none()))
}
#[test]

View File

@ -380,12 +380,6 @@ module.exports = grammar({
')',
optional(seq('->', $.type)),
),
seq(
'option',
'(',
$.type,
')',
),
),
),