1
0

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>, type_specification: Option<TypeSpecification>,
operator: AssignmentOperator, operator: AssignmentOperator,
statement: Statement, statement: Statement,
syntax_position: SourcePosition, 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) 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)?; 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);

View File

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

View File

@ -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} ) => {

View File

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

View File

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

View File

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

View File

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

View File

@ -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 { impl Value {
fn default() -> Self { pub fn none() -> Self {
Value::none() BuiltInValue::None.get().clone()
} }
pub fn some(value: Value) -> Value {
Value::Enum(EnumInstance::new(
"Option".to_string(),
"Some".to_string(),
Some(value),
))
} }
impl 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`.
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()`. impl Default for Value {
pub fn as_none(&self) -> Result<(), RuntimeError> { fn default() -> Self {
match self { Value::none()
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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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