Implement custom and built-in types
This commit is contained in:
parent
e1c3e8bc0d
commit
e7f5d66297
@ -14,7 +14,6 @@ pub struct Assignment {
|
|||||||
type_specification: Option<TypeSpecification>,
|
type_specification: Option<TypeSpecification>,
|
||||||
operator: AssignmentOperator,
|
operator: AssignmentOperator,
|
||||||
statement: Statement,
|
statement: Statement,
|
||||||
|
|
||||||
syntax_position: SourcePosition,
|
syntax_position: SourcePosition,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
EnumInstance::new(self.identifier.inner().clone(), variant, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,9 +89,11 @@ impl AbstractTree for Match {
|
|||||||
{
|
{
|
||||||
let statement_context = Context::with_variables_from(context)?;
|
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
|
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);
|
return statement.run(source, &statement_context);
|
||||||
|
@ -3,6 +3,7 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
built_in_types::BuiltInType,
|
||||||
error::{RuntimeError, SyntaxError, ValidationError},
|
error::{RuntimeError, SyntaxError, ValidationError},
|
||||||
AbstractTree, Context, Format, Identifier, SyntaxNode, Value,
|
AbstractTree, Context, Format, Identifier, SyntaxNode, Value,
|
||||||
};
|
};
|
||||||
@ -13,6 +14,10 @@ pub enum Type {
|
|||||||
Boolean,
|
Boolean,
|
||||||
Collection,
|
Collection,
|
||||||
Custom(Identifier),
|
Custom(Identifier),
|
||||||
|
CustomWithArgument {
|
||||||
|
name: Identifier,
|
||||||
|
argument: Box<Type>,
|
||||||
|
},
|
||||||
Float,
|
Float,
|
||||||
Function {
|
Function {
|
||||||
parameter_types: Vec<Type>,
|
parameter_types: Vec<Type>,
|
||||||
@ -21,14 +26,17 @@ pub enum Type {
|
|||||||
Integer,
|
Integer,
|
||||||
List(Box<Type>),
|
List(Box<Type>),
|
||||||
Map,
|
Map,
|
||||||
None,
|
|
||||||
Number,
|
Number,
|
||||||
String,
|
String,
|
||||||
Range,
|
Range,
|
||||||
Option(Box<Type>),
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Type {
|
impl Type {
|
||||||
|
pub fn option(inner_type: Option<Type>) -> Self {
|
||||||
|
BuiltInType::Option.get(inner_type).clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn list(item_type: Type) -> Self {
|
pub fn list(item_type: Type) -> Self {
|
||||||
Type::List(Box::new(item_type))
|
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.
|
/// 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
|
/// 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::Number, Type::Float)
|
||||||
| (Type::Integer, Type::Number)
|
| (Type::Integer, Type::Number)
|
||||||
| (Type::Float, Type::Number)
|
| (Type::Float, Type::Number)
|
||||||
| (Type::None, Type::None)
|
|
||||||
| (Type::String, Type::String) => true,
|
| (Type::String, Type::String) => true,
|
||||||
(Type::Custom(left), Type::Custom(right)) => left == right,
|
(Type::Custom(left), Type::Custom(right)) => left == right,
|
||||||
(Type::Option(_), Type::None) => true,
|
(
|
||||||
(Type::Option(left), Type::Option(right)) => {
|
Type::CustomWithArgument {
|
||||||
if let Type::Any = left.as_ref() {
|
name: left_name,
|
||||||
true
|
argument: left_argument,
|
||||||
} else if left == right {
|
},
|
||||||
true
|
Type::CustomWithArgument {
|
||||||
} else {
|
name: right_name,
|
||||||
|
argument: right_argument,
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
if left_name != right_name {
|
||||||
false
|
false
|
||||||
|
} else {
|
||||||
|
left_argument == right_argument
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Type::List(self_item_type), Type::List(other_item_type)) => {
|
(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 type_node = node.child(0).unwrap();
|
||||||
|
|
||||||
let r#type = match type_node.kind() {
|
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_node = node.child(1).unwrap();
|
||||||
let item_type = Type::from_syntax(item_type_node, _source, _context)?;
|
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() {
|
let return_type = if final_node.is_named() {
|
||||||
Type::from_syntax(final_node, _source, _context)?
|
Type::from_syntax(final_node, _source, _context)?
|
||||||
} else {
|
} else {
|
||||||
Type::None
|
Type::option(None)
|
||||||
};
|
};
|
||||||
|
|
||||||
Type::Function {
|
Type::Function {
|
||||||
@ -178,16 +201,9 @@ impl AbstractTree for Type {
|
|||||||
"num" => Type::Number,
|
"num" => Type::Number,
|
||||||
"none" => Type::None,
|
"none" => Type::None,
|
||||||
"str" => Type::String,
|
"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 {
|
return Err(SyntaxError::UnexpectedSyntaxNode {
|
||||||
expected: "any, bool, float, int, num, str, option, custom type, (, [ or {"
|
expected: "any, bool, float, int, num, str, custom type, (, [ or {".to_string(),
|
||||||
.to_string(),
|
|
||||||
actual: type_node.kind().to_string(),
|
actual: type_node.kind().to_string(),
|
||||||
location: type_node.start_position(),
|
location: type_node.start_position(),
|
||||||
relevant_source: _source[type_node.byte_range()].to_string(),
|
relevant_source: _source[type_node.byte_range()].to_string(),
|
||||||
@ -217,8 +233,11 @@ impl Format for Type {
|
|||||||
Type::Any => output.push_str("any"),
|
Type::Any => output.push_str("any"),
|
||||||
Type::Boolean => output.push_str("bool"),
|
Type::Boolean => output.push_str("bool"),
|
||||||
Type::Collection => output.push_str("collection"),
|
Type::Collection => output.push_str("collection"),
|
||||||
|
|
||||||
Type::Custom(_) => todo!(),
|
Type::Custom(_) => todo!(),
|
||||||
|
Type::CustomWithArgument {
|
||||||
|
name: _,
|
||||||
|
argument: _,
|
||||||
|
} => todo!(),
|
||||||
Type::Float => output.push_str("float"),
|
Type::Float => output.push_str("float"),
|
||||||
Type::Function {
|
Type::Function {
|
||||||
parameter_types,
|
parameter_types,
|
||||||
@ -246,14 +265,9 @@ impl Format for Type {
|
|||||||
Type::Map => {
|
Type::Map => {
|
||||||
output.push_str("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::Number => output.push_str("num"),
|
||||||
Type::String => output.push_str("str"),
|
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!(),
|
Type::Range => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -266,6 +280,9 @@ impl Display for Type {
|
|||||||
Type::Boolean => write!(f, "bool"),
|
Type::Boolean => write!(f, "bool"),
|
||||||
Type::Collection => write!(f, "collection"),
|
Type::Collection => write!(f, "collection"),
|
||||||
Type::Custom(identifier) => write!(f, "{identifier}"),
|
Type::Custom(identifier) => write!(f, "{identifier}"),
|
||||||
|
Type::CustomWithArgument { name, argument } => {
|
||||||
|
write!(f, "{name}<{argument}>")
|
||||||
|
}
|
||||||
Type::Float => write!(f, "float"),
|
Type::Float => write!(f, "float"),
|
||||||
Type::Function {
|
Type::Function {
|
||||||
parameter_types,
|
parameter_types,
|
||||||
@ -290,9 +307,6 @@ impl Display for Type {
|
|||||||
Type::Number => write!(f, "num"),
|
Type::Number => write!(f, "num"),
|
||||||
Type::None => write!(f, "none"),
|
Type::None => write!(f, "none"),
|
||||||
Type::String => write!(f, "str"),
|
Type::String => write!(f, "str"),
|
||||||
Type::Option(inner_type) => {
|
|
||||||
write!(f, "option({})", inner_type)
|
|
||||||
}
|
|
||||||
Type::Range => todo!(),
|
Type::Range => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ pub enum ValueNode {
|
|||||||
Integer(String),
|
Integer(String),
|
||||||
String(String),
|
String(String),
|
||||||
List(Vec<Expression>),
|
List(Vec<Expression>),
|
||||||
Option(Option<Box<Expression>>),
|
|
||||||
Map(MapNode),
|
Map(MapNode),
|
||||||
Range(RangeInclusive<i64>),
|
Range(RangeInclusive<i64>),
|
||||||
Struct {
|
Struct {
|
||||||
@ -68,18 +67,6 @@ impl AbstractTree for ValueNode {
|
|||||||
"map" => {
|
"map" => {
|
||||||
ValueNode::Map(MapNode::from_syntax(child, source, context)?)
|
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" => {
|
"range" => {
|
||||||
let mut split = source[child.byte_range()].split("..");
|
let mut split = source[child.byte_range()].split("..");
|
||||||
let start = split.next().unwrap().parse().unwrap();
|
let start = split.next().unwrap().parse().unwrap();
|
||||||
@ -158,19 +145,21 @@ impl AbstractTree for ValueNode {
|
|||||||
Type::List(Box::new(Type::Any))
|
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::Map(_) => Type::Map,
|
||||||
ValueNode::Struct { name, .. } => {
|
ValueNode::Struct { name, .. } => {
|
||||||
Type::Custom(name.clone())
|
Type::Custom(name.clone())
|
||||||
}
|
}
|
||||||
ValueNode::Range(_) => Type::Range,
|
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)
|
Ok(r#type)
|
||||||
@ -212,15 +201,6 @@ impl AbstractTree for ValueNode {
|
|||||||
|
|
||||||
Value::List(List::with_items(values))
|
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::Map(map_node) => map_node.run(source, context)?,
|
||||||
ValueNode::Range(range) => Value::Range(range.clone()),
|
ValueNode::Range(range) => Value::Range(range.clone()),
|
||||||
ValueNode::Struct { name, properties } => {
|
ValueNode::Struct { name, properties } => {
|
||||||
@ -247,7 +227,7 @@ impl AbstractTree for ValueNode {
|
|||||||
};
|
};
|
||||||
let instance = if let Some(definition) = context.get_definition(name.inner())? {
|
let instance = if let Some(definition) = context.get_definition(name.inner())? {
|
||||||
if let TypeDefinition::Enum(enum_defintion) = definition {
|
if let TypeDefinition::Enum(enum_defintion) = definition {
|
||||||
enum_defintion.instantiate(variant.inner().clone(), value)
|
enum_defintion.instantiate(variant.inner().clone(), Some(value))
|
||||||
} else {
|
} else {
|
||||||
return Err(RuntimeError::ValidationFailure(
|
return Err(RuntimeError::ValidationFailure(
|
||||||
ValidationError::ExpectedEnumDefintion {
|
ValidationError::ExpectedEnumDefintion {
|
||||||
@ -290,15 +270,6 @@ impl Format for ValueNode {
|
|||||||
|
|
||||||
output.push(']');
|
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::Map(map_node) => map_node.format(output, indent_level),
|
||||||
ValueNode::Struct { name, properties } => {
|
ValueNode::Struct { name, properties } => {
|
||||||
name.format(output, indent_level);
|
name.format(output, indent_level);
|
||||||
@ -325,8 +296,6 @@ impl Ord for ValueNode {
|
|||||||
(ValueNode::String(_), _) => Ordering::Greater,
|
(ValueNode::String(_), _) => Ordering::Greater,
|
||||||
(ValueNode::List(left), ValueNode::List(right)) => left.cmp(right),
|
(ValueNode::List(left), ValueNode::List(right)) => left.cmp(right),
|
||||||
(ValueNode::List(_), _) => Ordering::Greater,
|
(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(left), ValueNode::Map(right)) => left.cmp(right),
|
||||||
(ValueNode::Map(_), _) => Ordering::Greater,
|
(ValueNode::Map(_), _) => Ordering::Greater,
|
||||||
(ValueNode::Struct{ name: left_name, properties: left_properties }, ValueNode::Struct {name: right_name, properties: right_properties} ) => {
|
(ValueNode::Struct{ name: left_name, properties: left_properties }, ValueNode::Struct {name: right_name, properties: right_properties} ) => {
|
||||||
|
@ -103,13 +103,13 @@ impl Callable for BuiltInFunction {
|
|||||||
Value::Enum(EnumInstance::new(
|
Value::Enum(EnumInstance::new(
|
||||||
"Result".to_string(),
|
"Result".to_string(),
|
||||||
"Ok".to_string(),
|
"Ok".to_string(),
|
||||||
Value::none(),
|
Some(Value::none()),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Value::Enum(EnumInstance::new(
|
Value::Enum(EnumInstance::new(
|
||||||
"Result".to_string(),
|
"Result".to_string(),
|
||||||
"Error".to_string(),
|
"Error".to_string(),
|
||||||
Value::none(),
|
Some(Value::none()),
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use enum_iterator::Sequence;
|
use enum_iterator::Sequence;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{error::RuntimeError, Context, List, Type, Value};
|
use crate::{error::RuntimeError, Context, EnumInstance, List, Type, Value};
|
||||||
|
|
||||||
use super::Callable;
|
use super::Callable;
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ impl Callable for StrFunction {
|
|||||||
}
|
}
|
||||||
StrFunction::Find => Type::function(
|
StrFunction::Find => Type::function(
|
||||||
vec![Type::String, Type::String],
|
vec![Type::String, Type::String],
|
||||||
Type::option(Type::Integer),
|
Type::option(Some(Type::Integer)),
|
||||||
),
|
),
|
||||||
StrFunction::Insert => Type::function(
|
StrFunction::Insert => Type::function(
|
||||||
vec![Type::String, Type::Integer, Type::String],
|
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::Parse => Type::function(vec![Type::String], Type::Any),
|
||||||
StrFunction::Remove => Type::function(
|
StrFunction::Remove => Type::function(
|
||||||
vec![Type::String, Type::Integer],
|
vec![Type::String, Type::Integer],
|
||||||
Type::option(Type::String),
|
Type::option(Some(Type::String)),
|
||||||
),
|
),
|
||||||
StrFunction::ReplaceRange => Type::function(
|
StrFunction::ReplaceRange => Type::function(
|
||||||
vec![Type::String, Type::list(Type::Integer), Type::String],
|
vec![Type::String, Type::list(Type::Integer), Type::String],
|
||||||
@ -175,9 +175,10 @@ impl Callable for StrFunction {
|
|||||||
StrFunction::StartsWith => {
|
StrFunction::StartsWith => {
|
||||||
Type::function(vec![Type::String, Type::String], Type::Boolean)
|
Type::function(vec![Type::String, Type::String], Type::Boolean)
|
||||||
}
|
}
|
||||||
StrFunction::StripPrefix => {
|
StrFunction::StripPrefix => Type::function(
|
||||||
Type::function(vec![Type::String, Type::String], Type::option(Type::String))
|
vec![Type::String, Type::String],
|
||||||
}
|
Type::option(Some(Type::String)),
|
||||||
|
),
|
||||||
StrFunction::ToLowercase => Type::function(vec![Type::String], Type::String),
|
StrFunction::ToLowercase => Type::function(vec![Type::String], Type::String),
|
||||||
StrFunction::ToUppercase => Type::function(vec![Type::String], Type::String),
|
StrFunction::ToUppercase => Type::function(vec![Type::String], Type::String),
|
||||||
StrFunction::Truncate => {
|
StrFunction::Truncate => {
|
||||||
@ -233,9 +234,21 @@ impl Callable for StrFunction {
|
|||||||
let pattern = pattern_string.as_str();
|
let pattern = pattern_string.as_str();
|
||||||
let find = string
|
let find = string
|
||||||
.find(pattern)
|
.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 => {
|
StrFunction::IsAscii => {
|
||||||
RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
|
RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
|
||||||
@ -292,9 +305,9 @@ impl Callable for StrFunction {
|
|||||||
let string = arguments.first().unwrap().as_string()?;
|
let string = arguments.first().unwrap().as_string()?;
|
||||||
|
|
||||||
if let Ok(integer) = string.parse::<i64>() {
|
if let Ok(integer) = string.parse::<i64>() {
|
||||||
Value::option(Some(Value::Integer(integer)))
|
Value::Integer(integer)
|
||||||
} else if let Ok(float) = string.parse::<f64>() {
|
} else if let Ok(float) = string.parse::<f64>() {
|
||||||
Value::option(Some(Value::Float(float)))
|
Value::Float(float)
|
||||||
} else {
|
} else {
|
||||||
Value::none()
|
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 => {
|
StrFunction::SplitTerminator => {
|
||||||
RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
|
RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
|
||||||
@ -458,7 +475,15 @@ impl Callable for StrFunction {
|
|||||||
.strip_prefix(prefix)
|
.strip_prefix(prefix)
|
||||||
.map(|remainder| Value::string(remainder.to_string()));
|
.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 => {
|
StrFunction::ToLowercase => {
|
||||||
RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
|
RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
|
||||||
|
36
src/built_in_types.rs
Normal file
36
src/built_in_types.rs
Normal 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"))
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,8 @@ use serde::{Deserialize, Serialize};
|
|||||||
use crate::{
|
use crate::{
|
||||||
built_in_functions::{fs::fs_functions, json::json_functions, str::string_functions, Callable},
|
built_in_functions::{fs::fs_functions, json::json_functions, str::string_functions, Callable},
|
||||||
error::{RuntimeError, SyntaxError, ValidationError},
|
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();
|
static ARGS: OnceLock<Value> = OnceLock::new();
|
||||||
@ -14,6 +15,7 @@ static FS: OnceLock<Value> = OnceLock::new();
|
|||||||
static JSON: OnceLock<Value> = OnceLock::new();
|
static JSON: OnceLock<Value> = OnceLock::new();
|
||||||
static RANDOM: OnceLock<Value> = OnceLock::new();
|
static RANDOM: OnceLock<Value> = OnceLock::new();
|
||||||
static STRING: OnceLock<Value> = OnceLock::new();
|
static STRING: OnceLock<Value> = OnceLock::new();
|
||||||
|
static NONE: OnceLock<Value> = OnceLock::new();
|
||||||
|
|
||||||
/// Returns the entire built-in value API.
|
/// Returns the entire built-in value API.
|
||||||
pub fn all_built_in_values() -> impl Iterator<Item = BuiltInValue> {
|
pub fn all_built_in_values() -> impl Iterator<Item = BuiltInValue> {
|
||||||
@ -38,6 +40,9 @@ pub enum BuiltInValue {
|
|||||||
/// Get the length of a collection.
|
/// Get the length of a collection.
|
||||||
Length,
|
Length,
|
||||||
|
|
||||||
|
/// The absence of a value.
|
||||||
|
None,
|
||||||
|
|
||||||
/// Print a value to stdout.
|
/// Print a value to stdout.
|
||||||
Output,
|
Output,
|
||||||
|
|
||||||
@ -57,6 +62,7 @@ impl BuiltInValue {
|
|||||||
BuiltInValue::Fs => "fs",
|
BuiltInValue::Fs => "fs",
|
||||||
BuiltInValue::Json => "json",
|
BuiltInValue::Json => "json",
|
||||||
BuiltInValue::Length => BuiltInFunction::Length.name(),
|
BuiltInValue::Length => BuiltInFunction::Length.name(),
|
||||||
|
BuiltInValue::None => "None",
|
||||||
BuiltInValue::Output => "output",
|
BuiltInValue::Output => "output",
|
||||||
BuiltInValue::Random => "random",
|
BuiltInValue::Random => "random",
|
||||||
BuiltInValue::Str => "str",
|
BuiltInValue::Str => "str",
|
||||||
@ -73,6 +79,7 @@ impl BuiltInValue {
|
|||||||
BuiltInValue::Fs => "File and directory tools.",
|
BuiltInValue::Fs => "File and directory tools.",
|
||||||
BuiltInValue::Json => "JSON formatting tools.",
|
BuiltInValue::Json => "JSON formatting tools.",
|
||||||
BuiltInValue::Length => BuiltInFunction::Length.description(),
|
BuiltInValue::Length => BuiltInFunction::Length.description(),
|
||||||
|
BuiltInValue::None => "The absence of a value.",
|
||||||
BuiltInValue::Output => "output",
|
BuiltInValue::Output => "output",
|
||||||
BuiltInValue::Random => "random",
|
BuiltInValue::Random => "random",
|
||||||
BuiltInValue::Str => "string",
|
BuiltInValue::Str => "string",
|
||||||
@ -89,6 +96,7 @@ impl BuiltInValue {
|
|||||||
BuiltInValue::Fs => Type::Map,
|
BuiltInValue::Fs => Type::Map,
|
||||||
BuiltInValue::Json => Type::Map,
|
BuiltInValue::Json => Type::Map,
|
||||||
BuiltInValue::Length => BuiltInFunction::Length.r#type(),
|
BuiltInValue::Length => BuiltInFunction::Length.r#type(),
|
||||||
|
BuiltInValue::None => Type::Custom(Identifier::new("Option")),
|
||||||
BuiltInValue::Output => BuiltInFunction::Output.r#type(),
|
BuiltInValue::Output => BuiltInFunction::Output.r#type(),
|
||||||
BuiltInValue::Random => Type::Map,
|
BuiltInValue::Random => Type::Map,
|
||||||
BuiltInValue::Str => Type::Map,
|
BuiltInValue::Str => Type::Map,
|
||||||
@ -134,6 +142,13 @@ impl BuiltInValue {
|
|||||||
Value::Map(Map::with_values(json_context))
|
Value::Map(Map::with_values(json_context))
|
||||||
}),
|
}),
|
||||||
BuiltInValue::Length => &Value::Function(Function::BuiltIn(BuiltInFunction::Length)),
|
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::Output => &Value::Function(Function::BuiltIn(BuiltInFunction::Output)),
|
||||||
BuiltInValue::Random => RANDOM.get_or_init(|| {
|
BuiltInValue::Random => RANDOM.get_or_init(|| {
|
||||||
let mut random_context = BTreeMap::new();
|
let mut random_context = BTreeMap::new();
|
||||||
|
@ -18,6 +18,7 @@ pub use tree_sitter::Node as SyntaxNode;
|
|||||||
pub mod abstract_tree;
|
pub mod abstract_tree;
|
||||||
pub mod built_in_functions;
|
pub mod built_in_functions;
|
||||||
pub mod built_in_type_definitions;
|
pub mod built_in_type_definitions;
|
||||||
|
pub mod built_in_types;
|
||||||
pub mod built_in_values;
|
pub mod built_in_values;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::Value;
|
use crate::Value;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct EnumInstance {
|
pub struct EnumInstance {
|
||||||
name: String,
|
name: String,
|
||||||
variant_name: String,
|
variant_name: String,
|
||||||
value: Box<Value>,
|
value: Option<Box<Value>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnumInstance {
|
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 {
|
Self {
|
||||||
name,
|
name,
|
||||||
variant_name,
|
variant_name,
|
||||||
value: Box::new(value),
|
value: value.map(|value| Box::new(value)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,13 +28,13 @@ impl EnumInstance {
|
|||||||
&self.variant_name
|
&self.variant_name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value(&self) -> &Value {
|
pub fn value(&self) -> &Option<Box<Value>> {
|
||||||
&self.value
|
&self.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for EnumInstance {
|
impl Display for EnumInstance {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
103
src/value/mod.rs
103
src/value/mod.rs
@ -1,5 +1,7 @@
|
|||||||
//! Types that represent runtime values.
|
//! 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::{
|
use serde::{
|
||||||
de::{MapAccess, SeqAccess, Visitor},
|
de::{MapAccess, SeqAccess, Visitor},
|
||||||
@ -42,18 +44,23 @@ pub enum Value {
|
|||||||
Integer(i64),
|
Integer(i64),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Range(RangeInclusive<i64>),
|
Range(RangeInclusive<i64>),
|
||||||
Option(Option<Box<Value>>),
|
|
||||||
Struct(StructInstance),
|
Struct(StructInstance),
|
||||||
Enum(EnumInstance),
|
Enum(EnumInstance),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Value {
|
|
||||||
fn default() -> Self {
|
|
||||||
Value::none()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Value {
|
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 {
|
pub fn string<T: Into<String>>(string: T) -> Self {
|
||||||
Value::String(string.into())
|
Value::String(string.into())
|
||||||
}
|
}
|
||||||
@ -102,31 +109,12 @@ impl Value {
|
|||||||
Value::Float(_) => Type::Float,
|
Value::Float(_) => Type::Float,
|
||||||
Value::Integer(_) => Type::Integer,
|
Value::Integer(_) => Type::Integer,
|
||||||
Value::Boolean(_) => Type::Boolean,
|
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::Range(_) => todo!(),
|
||||||
Value::Struct(_) => todo!(),
|
Value::Struct(_) => todo!(),
|
||||||
Value::Enum(_) => 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 {
|
pub fn is_string(&self) -> bool {
|
||||||
matches!(self, Value::String(_))
|
matches!(self, Value::String(_))
|
||||||
}
|
}
|
||||||
@ -151,14 +139,6 @@ impl Value {
|
|||||||
matches!(self, Value::List(_))
|
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 {
|
pub fn is_map(&self) -> bool {
|
||||||
matches!(self, Value::Map(_))
|
matches!(self, Value::Map(_))
|
||||||
}
|
}
|
||||||
@ -167,6 +147,10 @@ impl Value {
|
|||||||
matches!(self, Value::Function(_))
|
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`.
|
/// 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> {
|
pub fn as_string(&self) -> Result<&String, RuntimeError> {
|
||||||
match self {
|
match self {
|
||||||
@ -259,39 +243,17 @@ impl Value {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `Option`, or returns `Err` if `self` is not a `Value::Option`.
|
impl Default for Value {
|
||||||
pub fn as_option(&self) -> Result<&Option<Box<Value>>, RuntimeError> {
|
fn default() -> Self {
|
||||||
match self {
|
Value::none()
|
||||||
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 {
|
impl Default for &Value {
|
||||||
fn default() -> Self {
|
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::List(left), Value::List(right)) => left == right,
|
||||||
(Value::Map(left), Value::Map(right)) => left == right,
|
(Value::Map(left), Value::Map(right)) => left == right,
|
||||||
(Value::Function(left), Value::Function(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::Range(left), Value::Range(right)) => left == right,
|
||||||
(Value::Struct(left), Value::Struct(right)) => left == right,
|
(Value::Struct(left), Value::Struct(right)) => left == right,
|
||||||
(Value::Enum(left), Value::Enum(right)) => left == right,
|
(Value::Enum(left), Value::Enum(right)) => left == right,
|
||||||
@ -495,9 +456,7 @@ impl Ord for Value {
|
|||||||
|
|
||||||
left_len.cmp(&right_len)
|
left_len.cmp(&right_len)
|
||||||
}
|
}
|
||||||
(Value::Range(_), _) => Ordering::Greater,
|
(Value::Range(_), _) => Ordering::Less,
|
||||||
(Value::Option(left), Value::Option(right)) => left.cmp(right),
|
|
||||||
(Value::Option(_), _) => Ordering::Less,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -522,7 +481,6 @@ impl Serialize for Value {
|
|||||||
|
|
||||||
list.end()
|
list.end()
|
||||||
}
|
}
|
||||||
Value::Option(inner) => inner.serialize(serializer),
|
|
||||||
Value::Map(map) => {
|
Value::Map(map) => {
|
||||||
let entries = map.inner();
|
let entries = map.inner();
|
||||||
let mut map = serializer.serialize_map(Some(entries.len()))?;
|
let mut map = serializer.serialize_map(Some(entries.len()))?;
|
||||||
@ -548,13 +506,6 @@ impl Display for Value {
|
|||||||
Value::Float(float) => write!(f, "{float}"),
|
Value::Float(float) => write!(f, "{float}"),
|
||||||
Value::Integer(int) => write!(f, "{int}"),
|
Value::Integer(int) => write!(f, "{int}"),
|
||||||
Value::Boolean(boolean) => write!(f, "{boolean}"),
|
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::List(list) => write!(f, "{list}"),
|
||||||
Value::Map(map) => write!(f, "{map}"),
|
Value::Map(map) => write!(f, "{map}"),
|
||||||
Value::Function(function) => write!(f, "{function}"),
|
Value::Function(function) => write!(f, "{function}"),
|
||||||
@ -839,9 +790,7 @@ impl<'de> Visitor<'de> for ValueVisitor {
|
|||||||
where
|
where
|
||||||
D: serde::Deserializer<'de>,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
Ok(Value::Option(Some(Box::new(Value::deserialize(
|
Ok(Value::Enum(EnumInstance::deserialize(deserializer)?))
|
||||||
deserializer,
|
|
||||||
)?))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_unit<E>(self) -> std::result::Result<Self::Value, E>
|
fn visit_unit<E>(self) -> std::result::Result<Self::Value, E>
|
||||||
|
@ -29,11 +29,11 @@ fn ends_with() {
|
|||||||
fn find() {
|
fn find() {
|
||||||
let result = interpret("str:find('abc', 'a')");
|
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')");
|
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]
|
#[test]
|
||||||
|
@ -7,7 +7,7 @@ fn option() {
|
|||||||
Ok(Value::Enum(EnumInstance::new(
|
Ok(Value::Enum(EnumInstance::new(
|
||||||
"Option".to_string(),
|
"Option".to_string(),
|
||||||
"None".to_string(),
|
"None".to_string(),
|
||||||
Value::none()
|
Some(Value::none()),
|
||||||
)))
|
)))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -21,7 +21,7 @@ fn option() {
|
|||||||
Ok(Value::Enum(EnumInstance::new(
|
Ok(Value::Enum(EnumInstance::new(
|
||||||
"Option".to_string(),
|
"Option".to_string(),
|
||||||
"Some".to_string(),
|
"Some".to_string(),
|
||||||
Value::Integer(1),
|
Some(Value::Integer(1)),
|
||||||
)))
|
)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -33,7 +33,7 @@ fn result() {
|
|||||||
Ok(Value::Enum(EnumInstance::new(
|
Ok(Value::Enum(EnumInstance::new(
|
||||||
"Result".to_string(),
|
"Result".to_string(),
|
||||||
"Ok".to_string(),
|
"Ok".to_string(),
|
||||||
Value::Integer(1)
|
Some(Value::Integer(1)),
|
||||||
)))
|
)))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -46,7 +46,7 @@ fn result() {
|
|||||||
Ok(Value::Enum(EnumInstance::new(
|
Ok(Value::Enum(EnumInstance::new(
|
||||||
"Result".to_string(),
|
"Result".to_string(),
|
||||||
"Error".to_string(),
|
"Error".to_string(),
|
||||||
Value::String("uh-oh!".to_string())
|
Some(Value::String("uh-oh!".to_string())),
|
||||||
)))
|
)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ fn assert_equal() {
|
|||||||
Ok(Value::Enum(EnumInstance::new(
|
Ok(Value::Enum(EnumInstance::new(
|
||||||
"Result".to_string(),
|
"Result".to_string(),
|
||||||
"Ok".to_string(),
|
"Ok".to_string(),
|
||||||
Value::none()
|
Some(Value::none()),
|
||||||
)))
|
)))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -26,7 +26,7 @@ fn assert_equal() {
|
|||||||
Ok(Value::Enum(EnumInstance::new(
|
Ok(Value::Enum(EnumInstance::new(
|
||||||
"Result".to_string(),
|
"Result".to_string(),
|
||||||
"Error".to_string(),
|
"Error".to_string(),
|
||||||
Value::none()
|
Some(Value::none()),
|
||||||
)))
|
)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ fn simple_enum() {
|
|||||||
Ok(Value::Enum(EnumInstance::new(
|
Ok(Value::Enum(EnumInstance::new(
|
||||||
"Foobar".to_string(),
|
"Foobar".to_string(),
|
||||||
"Foo".to_string(),
|
"Foo".to_string(),
|
||||||
Value::none()
|
Some(Value::none())
|
||||||
)))
|
)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -45,11 +45,11 @@ fn nested_enum() {
|
|||||||
Ok(Value::Enum(EnumInstance::new(
|
Ok(Value::Enum(EnumInstance::new(
|
||||||
"Foobar".to_string(),
|
"Foobar".to_string(),
|
||||||
"Bar".to_string(),
|
"Bar".to_string(),
|
||||||
Value::Enum(EnumInstance::new(
|
Some(Value::Enum(EnumInstance::new(
|
||||||
"Fizzbuzz".to_string(),
|
"Fizzbuzz".to_string(),
|
||||||
"Fizz".to_string(),
|
"Fizz".to_string(),
|
||||||
Value::none()
|
Some(Value::none())
|
||||||
))
|
)))
|
||||||
)))
|
)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ fn callback() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn built_in_function_call() {
|
fn built_in_function_call() {
|
||||||
assert_eq!(interpret("output('Hiya')"), Ok(Value::Option(None)));
|
assert_eq!(interpret("output('Hiya')"), Ok(Value::none()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2,8 +2,8 @@ use dust_lang::*;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty() {
|
fn empty() {
|
||||||
assert_eq!(interpret("x = 9"), Ok(Value::Option(None)));
|
assert_eq!(interpret("x = 9"), Ok(Value::none()));
|
||||||
assert_eq!(interpret("x = 1 + 1"), Ok(Value::Option(None)));
|
assert_eq!(interpret("x = 1 + 1"), Ok(Value::none()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2,7 +2,7 @@ use dust_lang::*;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn while_loop() {
|
fn while_loop() {
|
||||||
assert_eq!(interpret("while false { 'foo' }"), Ok(Value::Option(None)))
|
assert_eq!(interpret("while false { 'foo' }"), Ok(Value::none()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -380,12 +380,6 @@ module.exports = grammar({
|
|||||||
')',
|
')',
|
||||||
optional(seq('->', $.type)),
|
optional(seq('->', $.type)),
|
||||||
),
|
),
|
||||||
seq(
|
|
||||||
'option',
|
|
||||||
'(',
|
|
||||||
$.type,
|
|
||||||
')',
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user