Compare commits

...

3 Commits
main ... 0.4.4

Author SHA1 Message Date
2023e2c7e3 Roll back changes from 0.4.3 2024-02-22 23:49:07 -05:00
6b88fbf8b9 Add io:stdin to built-ins 2024-02-19 23:35:09 -05:00
cc76ca89cc Modify return/break syntax; Change Value::String 2024-02-19 22:32:06 -05:00
29 changed files with 27716 additions and 28960 deletions

View File

@ -9,7 +9,7 @@ use crate::{
}; };
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct FunctionNode { pub struct AnonymousFunction {
parameters: Vec<Identifier>, parameters: Vec<Identifier>,
body: Block, body: Block,
r#type: Type, r#type: Type,
@ -19,7 +19,7 @@ pub struct FunctionNode {
context: Context, context: Context,
} }
impl FunctionNode { impl AnonymousFunction {
pub fn parameters(&self) -> &Vec<Identifier> { pub fn parameters(&self) -> &Vec<Identifier> {
&self.parameters &self.parameters
} }
@ -51,9 +51,9 @@ impl FunctionNode {
} }
} }
impl AbstractTree for FunctionNode { impl AbstractTree for AnonymousFunction {
fn from_syntax(node: SyntaxNode, source: &str, context: &Context) -> Result<Self, SyntaxError> { fn from_syntax(node: SyntaxNode, source: &str, context: &Context) -> Result<Self, SyntaxError> {
SyntaxError::expect_syntax_node("function", node)?; SyntaxError::expect_syntax_node("anonymous_function", node)?;
let child_count = node.child_count(); let child_count = node.child_count();
let mut parameters = Vec::new(); let mut parameters = Vec::new();
@ -86,7 +86,7 @@ impl AbstractTree for FunctionNode {
let r#type = Type::function(parameter_types, return_type.take_inner()); let r#type = Type::function(parameter_types, return_type.take_inner());
let syntax_position = node.range().into(); let syntax_position = node.range().into();
Ok(FunctionNode { Ok(AnonymousFunction {
parameters, parameters,
body, body,
r#type, r#type,
@ -141,7 +141,7 @@ impl AbstractTree for FunctionNode {
} }
} }
impl Format for FunctionNode { impl Format for AnonymousFunction {
fn format(&self, output: &mut String, indent_level: u8) { fn format(&self, output: &mut String, indent_level: u8) {
let (parameter_types, return_type) = if let Type::Function { let (parameter_types, return_type) = if let Type::Function {
parameter_types, parameter_types,
@ -169,7 +169,7 @@ impl Format for FunctionNode {
} }
} }
impl Display for FunctionNode { impl Display for AnonymousFunction {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut string = String::new(); let mut string = String::new();

View File

@ -90,8 +90,9 @@ impl AbstractTree for As {
Value::List(list) => Value::List(list), Value::List(list) => Value::List(list),
Value::String(string) => { Value::String(string) => {
let chars = string let chars = string
.read()?
.chars() .chars()
.map(|char| Value::String(char.to_string())) .map(|char| Value::string(char))
.collect(); .collect();
Value::List(List::with_items(chars)) Value::List(List::with_items(chars))

View File

@ -65,7 +65,7 @@ impl AbstractTree for Command {
.wait_with_output()? .wait_with_output()?
.stdout; .stdout;
Ok(Value::String(String::from_utf8(output)?)) Ok(Value::string(String::from_utf8(output)?))
} }
} }

View File

@ -99,7 +99,7 @@ impl AbstractTree for Index {
} }
} else { } else {
let index_value = self.index.run(source, context)?; let index_value = self.index.run(source, context)?;
let identifier = Identifier::new(index_value.as_string()?); let identifier = Identifier::new(index_value.as_string()?.as_str());
if let Some(value) = map.get(&identifier) { if let Some(value) = map.get(&identifier) {
value value
@ -114,7 +114,7 @@ impl AbstractTree for Index {
} }
Value::String(string) => { Value::String(string) => {
let index = self.index.run(source, context)?.as_integer()? as usize; let index = self.index.run(source, context)?.as_integer()? as usize;
let item = string.chars().nth(index).unwrap_or_default(); let item = string.read()?.chars().nth(index).unwrap_or_default();
Ok(Value::string(item.to_string())) Ok(Value::string(item.to_string()))
} }

View File

@ -50,7 +50,7 @@ impl AbstractTree for IndexAssignment {
identifier identifier
} else { } else {
let index_run = self.index.index.run(source, context)?; let index_run = self.index.index.run(source, context)?;
let expected_identifier = Identifier::new(index_run.as_string()?); let expected_identifier = Identifier::new(index_run.as_string()?.as_str());
return Err(RuntimeError::ValidationFailure( return Err(RuntimeError::ValidationFailure(
ValidationError::VariableIdentifierNotFound(expected_identifier), ValidationError::VariableIdentifierNotFound(expected_identifier),

View File

@ -1,6 +1,7 @@
//! Abstract, executable representations of corresponding items found in Dust //! Abstract, executable representations of corresponding items found in Dust
//! source code. The types that implement [AbstractTree] are inteded to be //! source code. The types that implement [AbstractTree] are inteded to be
//! created by an [Interpreter]. //! created by an [Interpreter].
pub mod anonymous_function;
pub mod r#as; pub mod r#as;
pub mod assignment; pub mod assignment;
pub mod assignment_operator; pub mod assignment_operator;
@ -12,7 +13,6 @@ pub mod expression;
pub mod r#for; pub mod r#for;
pub mod function_call; pub mod function_call;
pub mod function_expression; pub mod function_expression;
pub mod function_node;
pub mod identifier; pub mod identifier;
pub mod if_else; pub mod if_else;
pub mod index; pub mod index;
@ -34,8 +34,8 @@ pub mod value_node;
pub mod r#while; pub mod r#while;
pub use { pub use {
assignment::*, assignment_operator::*, block::*, command::*, enum_defintion::*, anonymous_function::*, assignment::*, assignment_operator::*, block::*, command::*,
enum_pattern::*, expression::*, function_call::*, function_expression::*, function_node::*, enum_defintion::*, enum_pattern::*, expression::*, function_call::*, function_expression::*,
identifier::*, if_else::*, index::*, index_assignment::IndexAssignment, index_expression::*, identifier::*, if_else::*, index::*, index_assignment::IndexAssignment, index_expression::*,
logic::*, logic_operator::*, map_node::*, match_pattern::*, math::*, math_operator::*, r#as::*, logic::*, logic_operator::*, map_node::*, match_pattern::*, math::*, math_operator::*, r#as::*,
r#for::*, r#match::*, r#type::*, r#while::*, statement::*, struct_definition::*, r#for::*, r#match::*, r#type::*, r#while::*, statement::*, struct_definition::*,

View File

@ -5,7 +5,7 @@ use tree_sitter::Node as SyntaxNode;
use crate::{ use crate::{
error::{RuntimeError, SyntaxError, ValidationError}, error::{RuntimeError, SyntaxError, ValidationError},
AbstractTree, Context, Expression, Format, Function, FunctionNode, AbstractTree, Context, Expression, Format, Function, AnonymousFunction,
Identifier, List, Type, Value, TypeDefinition, MapNode, Identifier, List, Type, Value, TypeDefinition, MapNode,
}; };
@ -38,8 +38,8 @@ impl AbstractTree for ValueNode {
let value_node = match child.kind() { let value_node = match child.kind() {
"boolean" => ValueNode::Boolean(source[child.byte_range()].to_string()), "boolean" => ValueNode::Boolean(source[child.byte_range()].to_string()),
"float" => ValueNode::Float(source[child.byte_range()].to_string()), "float" => ValueNode::Float(source[child.byte_range()].to_string()),
"function" => { "anonymous_function" => {
let function_node = FunctionNode::from_syntax(child, source, context)?; let function_node = AnonymousFunction::from_syntax(child, source, context)?;
ValueNode::Function(Function::ContextDefined(function_node)) ValueNode::Function(Function::ContextDefined(function_node))
} }

View File

@ -7,7 +7,7 @@ use crate::{error::RuntimeError, Context, Type, Value};
use super::Callable; use super::Callable;
pub fn fs_functions() -> impl Iterator<Item = Fs> { pub fn all_fs_functions() -> impl Iterator<Item = Fs> {
all() all()
} }
@ -46,7 +46,7 @@ impl Callable for Fs {
RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let path = arguments.first().unwrap().as_string()?; let path = arguments.first().unwrap().as_string()?;
let mut file = File::open(path)?; let mut file = File::open(path.as_str())?;
let file_size = file.metadata()?.len() as usize; let file_size = file.metadata()?.len() as usize;
let mut file_content = String::with_capacity(file_size); let mut file_content = String::with_capacity(file_size);

View File

@ -0,0 +1,56 @@
use enum_iterator::{all, Sequence};
use serde::{Deserialize, Serialize};
use crate::{error::RuntimeError, Context, Type, Value};
use super::Callable;
pub fn all_io_functions() -> impl Iterator<Item = Io> {
all()
}
#[derive(Sequence, Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum Io {
Stdin,
}
impl Callable for Io {
fn name(&self) -> &'static str {
match self {
Io::Stdin => "stdin",
}
}
fn description(&self) -> &'static str {
match self {
Io::Stdin => "Read input from stdin.",
}
}
fn r#type(&self) -> crate::Type {
match self {
Io::Stdin => Type::Function {
parameter_types: vec![],
return_type: Box::new(Type::String),
},
}
}
fn call(
&self,
_arguments: &[Value],
_source: &str,
_context: &Context,
) -> Result<Value, RuntimeError> {
match self {
Io::Stdin => {
let mut input = String::new();
let stdin = std::io::stdin();
stdin.read_line(&mut input)?;
Ok(Value::string(input))
}
}
}
}

View File

@ -54,7 +54,7 @@ impl Callable for Json {
let value = arguments.first().unwrap(); let value = arguments.first().unwrap();
let json_string = serde_json::to_string(value)?; let json_string = serde_json::to_string(value)?;
Ok(Value::String(json_string)) Ok(Value::string(json_string))
} }
Json::CreatePretty => { Json::CreatePretty => {
RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
@ -62,13 +62,13 @@ impl Callable for Json {
let value = arguments.first().unwrap(); let value = arguments.first().unwrap();
let json_string = serde_json::to_string_pretty(value)?; let json_string = serde_json::to_string_pretty(value)?;
Ok(Value::String(json_string)) Ok(Value::string(json_string))
} }
Json::Parse => { Json::Parse => {
RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let json_string = arguments.first().unwrap().as_string()?; let json_string = arguments.first().unwrap().as_string()?;
let value = serde_json::from_str(json_string)?; let value = serde_json::from_str(json_string.as_str())?;
Ok(value) Ok(value)
} }

View File

@ -1,4 +1,5 @@
pub mod fs; pub mod fs;
pub mod io;
pub mod json; pub mod json;
pub mod str; pub mod str;
@ -12,7 +13,7 @@ use crate::{
Context, EnumInstance, Format, Identifier, Type, Value, Context, EnumInstance, Format, Identifier, Type, Value,
}; };
use self::{fs::Fs, json::Json, str::StrFunction}; use self::{fs::Fs, io::Io, json::Json, str::StrFunction};
pub trait Callable { pub trait Callable {
fn name(&self) -> &'static str; fn name(&self) -> &'static str;
@ -30,6 +31,7 @@ pub trait Callable {
pub enum BuiltInFunction { pub enum BuiltInFunction {
AssertEqual, AssertEqual,
Fs(Fs), Fs(Fs),
Io(Io),
Json(Json), Json(Json),
Length, Length,
Output, Output,
@ -45,6 +47,7 @@ impl Callable for BuiltInFunction {
match self { match self {
BuiltInFunction::AssertEqual => "assert_equal", BuiltInFunction::AssertEqual => "assert_equal",
BuiltInFunction::Fs(fs_function) => fs_function.name(), BuiltInFunction::Fs(fs_function) => fs_function.name(),
BuiltInFunction::Io(io_function) => io_function.name(),
BuiltInFunction::Json(json_function) => json_function.name(), BuiltInFunction::Json(json_function) => json_function.name(),
BuiltInFunction::Length => "length", BuiltInFunction::Length => "length",
BuiltInFunction::Output => "output", BuiltInFunction::Output => "output",
@ -60,6 +63,7 @@ impl Callable for BuiltInFunction {
match self { match self {
BuiltInFunction::AssertEqual => "assert_equal", BuiltInFunction::AssertEqual => "assert_equal",
BuiltInFunction::Fs(fs_function) => fs_function.description(), BuiltInFunction::Fs(fs_function) => fs_function.description(),
BuiltInFunction::Io(io_function) => io_function.description(),
BuiltInFunction::Json(json_function) => json_function.description(), BuiltInFunction::Json(json_function) => json_function.description(),
BuiltInFunction::Length => "length", BuiltInFunction::Length => "length",
BuiltInFunction::Output => "output", BuiltInFunction::Output => "output",
@ -75,6 +79,7 @@ impl Callable for BuiltInFunction {
match self { match self {
BuiltInFunction::AssertEqual => Type::function(vec![Type::Any, Type::Any], Type::None), BuiltInFunction::AssertEqual => Type::function(vec![Type::Any, Type::Any], Type::None),
BuiltInFunction::Fs(fs_function) => fs_function.r#type(), BuiltInFunction::Fs(fs_function) => fs_function.r#type(),
BuiltInFunction::Io(io_function) => io_function.r#type(),
BuiltInFunction::Json(json_function) => json_function.r#type(), BuiltInFunction::Json(json_function) => json_function.r#type(),
BuiltInFunction::Length => Type::function(vec![Type::Collection], Type::Integer), BuiltInFunction::Length => Type::function(vec![Type::Collection], Type::Integer),
BuiltInFunction::Output => Type::function(vec![Type::Any], Type::None), BuiltInFunction::Output => Type::function(vec![Type::Any], Type::None),
@ -113,6 +118,7 @@ impl Callable for BuiltInFunction {
} }
} }
BuiltInFunction::Fs(fs_function) => fs_function.call(arguments, _source, context), BuiltInFunction::Fs(fs_function) => fs_function.call(arguments, _source, context),
BuiltInFunction::Io(io_function) => io_function.call(arguments, _source, context),
BuiltInFunction::Json(json_function) => json_function.call(arguments, _source, context), BuiltInFunction::Json(json_function) => json_function.call(arguments, _source, context),
BuiltInFunction::Length => { BuiltInFunction::Length => {
RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;

View File

@ -221,19 +221,17 @@ impl Callable for StrFunction {
RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let pattern_string = arguments.get(1).unwrap().as_string()?; let pattern = arguments.get(1).unwrap().as_string()?;
let pattern = pattern_string.as_str();
Value::Boolean(string.ends_with(pattern)) Value::Boolean(string.ends_with(pattern.as_str()))
} }
StrFunction::Find => { StrFunction::Find => {
RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let pattern_string = arguments.get(1).unwrap().as_string()?; let pattern = arguments.get(1).unwrap().as_string()?;
let pattern = pattern_string.as_str();
let find = string let find = string
.find(pattern) .find(pattern.as_str())
.map(|index| Value::Integer(index as i64)); .map(|index| Value::Integer(index as i64));
if let Some(index) = find { if let Some(index) = find {
@ -271,9 +269,9 @@ impl Callable for StrFunction {
let index = arguments.get(1).unwrap().as_integer()? as usize; let index = arguments.get(1).unwrap().as_integer()? as usize;
let insertion = arguments.get(2).unwrap().as_string()?; let insertion = arguments.get(2).unwrap().as_string()?;
string.insert_str(index, insertion); string.insert_str(index, insertion.as_str());
Value::String(string) Value::none()
} }
StrFunction::Lines => { StrFunction::Lines => {
RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
@ -339,9 +337,9 @@ impl Callable for StrFunction {
let end = range[1].as_integer()? as usize; let end = range[1].as_integer()? as usize;
let pattern = arguments.get(2).unwrap().as_string()?; let pattern = arguments.get(2).unwrap().as_string()?;
string.replace_range(start..end, pattern); string.replace_range(start..end, pattern.as_str());
Value::String(string) Value::string(string)
} }
StrFunction::Retain => { StrFunction::Retain => {
RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
@ -576,13 +574,9 @@ impl Callable for StrFunction {
let input_string = arguments.first().unwrap().as_string()?; let input_string = arguments.first().unwrap().as_string()?;
let new_length = arguments.get(1).unwrap().as_integer()? as usize; let new_length = arguments.get(1).unwrap().as_integer()? as usize;
let new_string = input_string let new_string = input_string.chars().take(new_length).collect::<String>();
.chars()
.take(new_length)
.map(|char| char.to_string())
.collect();
Value::String(new_string) Value::string(new_string)
} }
}; };

View File

@ -4,12 +4,16 @@ use enum_iterator::{all, Sequence};
use serde::{Deserialize, Serialize}; 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::all_fs_functions, io::all_io_functions, json::json_functions, str::string_functions,
Callable,
},
BuiltInFunction, EnumInstance, Function, Identifier, List, Map, Value, BuiltInFunction, EnumInstance, Function, Identifier, List, Map, Value,
}; };
static ARGS: OnceLock<Value> = OnceLock::new(); static ARGS: OnceLock<Value> = OnceLock::new();
static FS: OnceLock<Value> = OnceLock::new(); static FS: OnceLock<Value> = OnceLock::new();
static IO: OnceLock<Value> = OnceLock::new();
static JSON: OnceLock<Value> = OnceLock::new(); static JSON: OnceLock<Value> = OnceLock::new();
static NONE: OnceLock<Value> = OnceLock::new(); static NONE: OnceLock<Value> = OnceLock::new();
static RANDOM: OnceLock<Value> = OnceLock::new(); static RANDOM: OnceLock<Value> = OnceLock::new();
@ -32,6 +36,9 @@ pub enum BuiltInValue {
/// File system tools. /// File system tools.
Fs, Fs,
/// Input and output tools.
Io,
/// JSON format tools. /// JSON format tools.
Json, Json,
@ -58,6 +65,7 @@ impl BuiltInValue {
BuiltInValue::Args => "args", BuiltInValue::Args => "args",
BuiltInValue::AssertEqual => "assert_equal", BuiltInValue::AssertEqual => "assert_equal",
BuiltInValue::Fs => "fs", BuiltInValue::Fs => "fs",
BuiltInValue::Io => "io",
BuiltInValue::Json => "json", BuiltInValue::Json => "json",
BuiltInValue::Length => BuiltInFunction::Length.name(), BuiltInValue::Length => BuiltInFunction::Length.name(),
BuiltInValue::None => "None", BuiltInValue::None => "None",
@ -75,6 +83,7 @@ impl BuiltInValue {
BuiltInValue::Args => "The command line arguments sent to this program.", BuiltInValue::Args => "The command line arguments sent to this program.",
BuiltInValue::AssertEqual => "Error if the two values are not equal.", BuiltInValue::AssertEqual => "Error if the two values are not equal.",
BuiltInValue::Fs => "File and directory tools.", BuiltInValue::Fs => "File and directory tools.",
BuiltInValue::Io => "Input/output 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::None => "The absence of a value.",
@ -98,11 +107,26 @@ impl BuiltInValue {
BuiltInValue::AssertEqual => { BuiltInValue::AssertEqual => {
Value::Function(Function::BuiltIn(BuiltInFunction::AssertEqual)) Value::Function(Function::BuiltIn(BuiltInFunction::AssertEqual))
} }
BuiltInValue::Io => IO
.get_or_init(|| {
let mut io_map = Map::new();
for io_function in all_io_functions() {
let key = io_function.name();
let value =
Value::Function(Function::BuiltIn(BuiltInFunction::Io(io_function)));
io_map.set(Identifier::new(key), value);
}
Value::Map(io_map)
})
.clone(),
BuiltInValue::Fs => FS BuiltInValue::Fs => FS
.get_or_init(|| { .get_or_init(|| {
let mut fs_map = Map::new(); let mut fs_map = Map::new();
for fs_function in fs_functions() { for fs_function in all_fs_functions() {
let key = fs_function.name(); let key = fs_function.name();
let value = let value =
Value::Function(Function::BuiltIn(BuiltInFunction::Fs(fs_function))); Value::Function(Function::BuiltIn(BuiltInFunction::Fs(fs_function)));

View File

@ -1,4 +1,7 @@
use std::fmt::{self, Display, Formatter}; use std::{
fmt::{self, Display, Formatter},
sync::PoisonError,
};
use colored::Colorize; use colored::Colorize;
use lyneate::Report; use lyneate::Report;
@ -261,6 +264,12 @@ impl ValidationError {
} }
} }
impl<T> From<PoisonError<T>> for ValidationError {
fn from(_: PoisonError<T>) -> Self {
ValidationError::RwLock(RwLockError)
}
}
impl From<RwLockError> for ValidationError { impl From<RwLockError> for ValidationError {
fn from(_error: RwLockError) -> Self { fn from(_error: RwLockError) -> Self {
ValidationError::RwLock(RwLockError) ValidationError::RwLock(RwLockError)

View File

@ -3,13 +3,13 @@ use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
built_in_functions::Callable, BuiltInFunction, Format, FunctionNode, Identifier, Type, built_in_functions::Callable, AnonymousFunction, BuiltInFunction, Format, Identifier, Type,
}; };
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Function { pub enum Function {
BuiltIn(BuiltInFunction), BuiltIn(BuiltInFunction),
ContextDefined(FunctionNode), ContextDefined(AnonymousFunction),
} }
impl Function { impl Function {

View File

@ -18,6 +18,7 @@ use std::{
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
marker::PhantomData, marker::PhantomData,
ops::RangeInclusive, ops::RangeInclusive,
sync::{Arc, RwLock, RwLockReadGuard},
}; };
pub use self::{ pub use self::{
@ -36,7 +37,7 @@ pub mod struct_instance;
/// Every dust variable has a key and a Value. Variables are represented by /// Every dust variable has a key and a Value. Variables are represented by
/// storing them in a VariableMap. This means the map of variables is itself a /// storing them in a VariableMap. This means the map of variables is itself a
/// value that can be treated as any other. /// value that can be treated as any other.
#[derive(Debug, Clone)] #[derive(Debug)]
pub enum Value { pub enum Value {
Boolean(bool), Boolean(bool),
Enum(EnumInstance), Enum(EnumInstance),
@ -46,7 +47,7 @@ pub enum Value {
List(List), List(List),
Map(Map), Map(Map),
Range(RangeInclusive<i64>), Range(RangeInclusive<i64>),
String(String), String(Arc<RwLock<String>>),
Struct(StructInstance), Struct(StructInstance),
} }
@ -64,7 +65,7 @@ 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(Arc::new(RwLock::new(string.into())))
} }
pub fn range(start: i64, end: i64) -> Self { pub fn range(start: i64, end: i64) -> Self {
@ -157,11 +158,11 @@ impl Value {
self == &Value::none() self == &Value::none()
} }
/// Borrows the value stored in `self` as `&String`, or returns `Err` if /// Borrows the value stored in `self` as `&str`, or returns `Err` if
/// `self` is not a `Value::String`. /// `self` is not a `Value::String`.
pub fn as_string(&self) -> Result<&String, ValidationError> { pub fn as_string(&self) -> Result<RwLockReadGuard<String>, ValidationError> {
match self { match self {
Value::String(string) => Ok(string), Value::String(string) => Ok(string.read()?),
value => Err(ValidationError::ExpectedString { value => Err(ValidationError::ExpectedString {
actual: value.clone(), actual: value.clone(),
}), }),
@ -274,7 +275,12 @@ impl Value {
Ok(Value::List(list)) Ok(Value::List(list))
} }
(Value::String(left), Value::String(right)) => Ok(Value::String(left + &right)), (Value::String(left), Value::String(right)) => {
let left = left.read()?.to_string();
let right = right.read()?;
Ok(Value::string(left + right.as_str()))
}
(left, right) => Err(ValidationError::CannotAdd { (left, right) => Err(ValidationError::CannotAdd {
left, left,
right, right,
@ -360,7 +366,13 @@ impl PartialEq for Value {
(Value::Integer(left), Value::Integer(right)) => left == right, (Value::Integer(left), Value::Integer(right)) => left == right,
(Value::Float(left), Value::Float(right)) => left == right, (Value::Float(left), Value::Float(right)) => left == right,
(Value::Boolean(left), Value::Boolean(right)) => left == right, (Value::Boolean(left), Value::Boolean(right)) => left == right,
(Value::String(left), Value::String(right)) => left == right, (Value::String(left), Value::String(right)) => {
if let (Ok(left), Ok(right)) = (left.read(), right.read()) {
left.as_str() == right.as_str()
} else {
false
}
}
(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,
@ -381,7 +393,13 @@ impl PartialOrd for Value {
impl Ord for Value { impl Ord for Value {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
match (self, other) { match (self, other) {
(Value::String(left), Value::String(right)) => left.cmp(right), (Value::String(left), Value::String(right)) => {
if let (Ok(left), Ok(right)) = (left.read(), right.read()) {
left.cmp(&right)
} else {
Ordering::Equal
}
}
(Value::String(_), _) => Ordering::Greater, (Value::String(_), _) => Ordering::Greater,
(Value::Float(left), Value::Float(right)) => left.total_cmp(right), (Value::Float(left), Value::Float(right)) => left.total_cmp(right),
(Value::Integer(left), Value::Integer(right)) => left.cmp(right), (Value::Integer(left), Value::Integer(right)) => left.cmp(right),
@ -424,7 +442,13 @@ impl Serialize for Value {
S: Serializer, S: Serializer,
{ {
match self { match self {
Value::String(inner) => serializer.serialize_str(inner), Value::String(inner) => {
let inner = inner
.read()
.map_err(|_| serde::ser::Error::custom("failed to get read lock on string"))?;
serializer.serialize_str(inner.as_str())
}
Value::Float(inner) => serializer.serialize_f64(*inner), Value::Float(inner) => serializer.serialize_f64(*inner),
Value::Integer(inner) => serializer.serialize_i64(*inner), Value::Integer(inner) => serializer.serialize_i64(*inner),
Value::Boolean(inner) => serializer.serialize_bool(*inner), Value::Boolean(inner) => serializer.serialize_bool(*inner),
@ -461,10 +485,33 @@ impl Serialize for Value {
} }
} }
impl Clone for Value {
fn clone(&self) -> Self {
log::trace!("Cloning value {self}");
match self {
Value::Boolean(boolean) => Value::Boolean(*boolean),
Value::Enum(r#enum) => Value::Enum(r#enum.clone()),
Value::Float(float) => Value::Float(*float),
Value::Function(function) => Value::Function(function.clone()),
Value::Integer(integer) => Value::Integer(*integer),
Value::List(list) => Value::List(list.clone()),
Value::Map(map) => Value::Map(map.clone()),
Value::Range(range) => Value::Range(range.clone()),
Value::String(string) => Value::String(string.clone()),
Value::Struct(r#struct) => Value::Struct(r#struct.clone()),
}
}
}
impl Display for Value { impl Display for Value {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
Value::String(string) => write!(f, "{string}"), Value::String(string) => {
let string = string.read().map_err(|_| fmt::Error)?;
write!(f, "{}", string.as_str())
}
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}"),
@ -531,7 +578,7 @@ impl TryFrom<Value> for String {
fn try_from(value: Value) -> std::result::Result<Self, Self::Error> { fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
if let Value::String(string) = value { if let Value::String(string) = value {
Ok(string) Ok(string.read()?.clone())
} else { } else {
Err(RuntimeError::ValidationFailure( Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedString { actual: value }, ValidationError::ExpectedString { actual: value },

View File

@ -8,12 +8,12 @@ fn string_as_string_list() {
assert_eq!( assert_eq!(
interpret("'foobar' as [str]"), interpret("'foobar' as [str]"),
Ok(Value::List(List::with_items(vec![ Ok(Value::List(List::with_items(vec![
Value::String("f".to_string()), Value::string("f"),
Value::String("o".to_string()), Value::string("o"),
Value::String("o".to_string()), Value::string("o"),
Value::String("b".to_string()), Value::string("b"),
Value::String("a".to_string()), Value::string("a"),
Value::String("r".to_string()), Value::string("r"),
]))) ])))
) )
} }

View File

@ -40,10 +40,10 @@ fn find() {
fn insert() { fn insert() {
assert_eq!( assert_eq!(
interpret("str:insert('ac', 1, 'b')"), interpret("str:insert('ac', 1, 'b')"),
Ok(Value::String("abc".to_string())) Ok(Value::string("abc"))
); );
assert_eq!( assert_eq!(
interpret("str:insert('foo', 3, 'bar')"), interpret("str:insert('foo', 3, 'bar')"),
Ok(Value::String("foobar".to_string())) Ok(Value::string("foobar"))
); );
} }

View File

@ -17,7 +17,7 @@ fn override_built_ins() {
Ok(Value::Enum(EnumInstance::new( Ok(Value::Enum(EnumInstance::new(
Identifier::new("Option"), Identifier::new("Option"),
Identifier::new("Some"), Identifier::new("Some"),
Some(Value::String("foo".to_string())), Some(Value::string("foo")),
))) )))
); );
} }
@ -65,7 +65,7 @@ fn result() {
Ok(Value::Enum(EnumInstance::new( Ok(Value::Enum(EnumInstance::new(
Identifier::new("Result"), Identifier::new("Result"),
Identifier::new("Error"), Identifier::new("Error"),
Some(Value::String("uh-oh!".to_string())), Some(Value::string("uh-oh!")),
))) )))
); );
} }

View File

@ -2,7 +2,7 @@ use dust_lang::{interpret, Value};
#[test] #[test]
fn simple_command() { fn simple_command() {
assert_eq!(interpret("^echo hi"), Ok(Value::String("hi\n".to_string()))) assert_eq!(interpret("^echo hi"), Ok(Value::string("hi\n")))
} }
#[test] #[test]

View File

@ -18,7 +18,7 @@ fn simple_struct() {
let mut map = Map::new(); let mut map = Map::new();
map.set(Identifier::new("bar"), Value::Integer(0)); map.set(Identifier::new("bar"), Value::Integer(0));
map.set(Identifier::new("baz"), Value::String("hiya".to_string())); map.set(Identifier::new("baz"), Value::string("hiya"));
let expected = Ok(Value::Struct(StructInstance::new( let expected = Ok(Value::Struct(StructInstance::new(
Identifier::new("Foo"), Identifier::new("Foo"),

View File

@ -43,6 +43,9 @@ Block with Return
(expression (expression
(value (value
(integer))))) (integer)))))
(statement
(statement_kind
(return)))
(statement (statement
(statement_kind (statement_kind
(expression (expression

View File

@ -22,7 +22,7 @@ fib = (i <int>) <int> {
(statement_kind (statement_kind
(expression (expression
(value (value
(function (anonymous_function
(identifier) (identifier)
(type_specification (type_specification
(type)) (type))

View File

@ -11,7 +11,7 @@ Anonymous Function
(statement_kind (statement_kind
(expression (expression
(value (value
(function (anonymous_function
(type_specification (type_specification
(type)) (type))
(block (block
@ -41,7 +41,7 @@ foobar = (x <int>, y <int>) <int> {
(statement_kind (statement_kind
(expression (expression
(value (value
(function (anonymous_function
(identifier) (identifier)
(type_specification (type_specification
(type)) (type))
@ -139,7 +139,7 @@ Anonymous Function Call
(function_call (function_call
(function_expression (function_expression
(value (value
(function (anonymous_function
(identifier) (identifier)
(type_specification (type_specification
(type)) (type))
@ -215,7 +215,7 @@ x(() <bool> { true })
(identifier)) (identifier))
(expression (expression
(value (value
(function (anonymous_function
(type_specification (type_specification
(type)) (type))
(block (block
@ -247,3 +247,35 @@ from_json(read('file.json'))
(expression (expression
(value (value
(string)))))))))) (string))))))))))
================================================================================
Function Definition
================================================================================
fn foo<T> (bar <T>) <T> {
bar
}
--------------------------------------------------------------------------------
(root
(statement
(statement_kind
(type_definition
(function_definition
(identifier)
(type_arguments
(type
(identifier)))
(identifier)
(type_specification
(type
(identifier)))
(type_specification
(type
(identifier)))
(block
(statement
(statement_kind
(expression
(identifier))))))))))

View File

@ -14,19 +14,33 @@ module.exports = grammar({
statement: $ => statement: $ =>
prec.left( prec.left(
seq( seq(
optional(
choice('return', 'break'),
),
$.statement_kind, $.statement_kind,
optional(';'), optional(';'),
), ),
), ),
break: $ =>
prec.left(
seq(
'break',
optional($.statement),
),
),
return: $ =>
prec.left(
seq(
'return',
optional($.statement),
),
),
statement_kind: $ => statement_kind: $ =>
prec.left( prec.left(
choice( choice(
$.assignment, $.assignment,
$.block, $.block,
$.break,
$.expression, $.expression,
$.for, $.for,
$.if_else, $.if_else,
@ -34,6 +48,7 @@ module.exports = grammar({
$.loop_node, $.loop_node,
$.match, $.match,
$.pipe, $.pipe,
$.return,
$.while, $.while,
$.type_definition, $.type_definition,
), ),
@ -123,7 +138,7 @@ module.exports = grammar({
value: $ => value: $ =>
choice( choice(
$.function, $.anonymous_function,
$.integer, $.integer,
$.float, $.float,
$.string, $.string,
@ -329,10 +344,7 @@ module.exports = grammar({
), ),
loop_node: $ => loop_node: $ =>
seq( seq('loop', $.block),
'loop',
$.block,
),
while: $ => while: $ =>
seq( seq(
@ -372,7 +384,7 @@ module.exports = grammar({
// Custom type with arguments // Custom type with arguments
seq( seq(
$.identifier, $.identifier,
$.type_arguments $.type_arguments,
), ),
// Map with exact fields // Map with exact fields
@ -417,7 +429,7 @@ module.exports = grammar({
), ),
), ),
function: $ => anonymous_function: $ =>
seq( seq(
'(', '(',
repeat( repeat(
@ -466,6 +478,7 @@ module.exports = grammar({
type_definition: $ => type_definition: $ =>
choice( choice(
$.enum_definition, $.enum_definition,
$.function_definition,
$.struct_definition, $.struct_definition,
), ),
@ -473,10 +486,7 @@ module.exports = grammar({
seq( seq(
'<', '<',
repeat1( repeat1(
seq( seq($.type, optional(',')),
$.type,
optional(','),
),
), ),
'>', '>',
), ),
@ -493,7 +503,9 @@ module.exports = grammar({
repeat1( repeat1(
seq( seq(
$.identifier, $.identifier,
optional($.type_arguments), optional(
$.type_arguments,
),
optional(','), optional(','),
), ),
), ),
@ -544,5 +556,23 @@ module.exports = grammar({
struct_instance: $ => struct_instance: $ =>
seq($.identifier, '::', $.map), seq($.identifier, '::', $.map),
function_definition: $ =>
seq(
'fn',
$.identifier,
optional($.type_arguments),
'(',
repeat(
seq(
$.identifier,
$.type_specification,
optional(','),
),
),
')',
$.type_specification,
$.block,
),
}, },
}); });

View File

@ -23,27 +23,6 @@
"content": { "content": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
{
"type": "CHOICE",
"members": [
{
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": "return"
},
{
"type": "STRING",
"value": "break"
}
]
},
{
"type": "BLANK"
}
]
},
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "statement_kind" "name": "statement_kind"
@ -63,6 +42,56 @@
] ]
} }
}, },
"break": {
"type": "PREC_LEFT",
"value": 0,
"content": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "break"
},
{
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "statement"
},
{
"type": "BLANK"
}
]
}
]
}
},
"return": {
"type": "PREC_LEFT",
"value": 0,
"content": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "return"
},
{
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "statement"
},
{
"type": "BLANK"
}
]
}
]
}
},
"statement_kind": { "statement_kind": {
"type": "PREC_LEFT", "type": "PREC_LEFT",
"value": 0, "value": 0,
@ -77,6 +106,10 @@
"type": "SYMBOL", "type": "SYMBOL",
"name": "block" "name": "block"
}, },
{
"type": "SYMBOL",
"name": "break"
},
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "expression" "name": "expression"
@ -105,6 +138,10 @@
"type": "SYMBOL", "type": "SYMBOL",
"name": "pipe" "name": "pipe"
}, },
{
"type": "SYMBOL",
"name": "return"
},
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "while" "name": "while"
@ -353,7 +390,7 @@
"members": [ "members": [
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "function" "name": "anonymous_function"
}, },
{ {
"type": "SYMBOL", "type": "SYMBOL",
@ -1274,7 +1311,7 @@
] ]
} }
}, },
"function": { "anonymous_function": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
{ {
@ -1414,6 +1451,10 @@
"type": "SYMBOL", "type": "SYMBOL",
"name": "enum_definition" "name": "enum_definition"
}, },
{
"type": "SYMBOL",
"name": "function_definition"
},
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "struct_definition" "name": "struct_definition"
@ -1680,6 +1721,75 @@
"name": "map" "name": "map"
} }
] ]
},
"function_definition": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "fn"
},
{
"type": "SYMBOL",
"name": "identifier"
},
{
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "type_arguments"
},
{
"type": "BLANK"
}
]
},
{
"type": "STRING",
"value": "("
},
{
"type": "REPEAT",
"content": {
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "identifier"
},
{
"type": "SYMBOL",
"name": "type_specification"
},
{
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": ","
},
{
"type": "BLANK"
}
]
}
]
}
},
{
"type": "STRING",
"value": ")"
},
{
"type": "SYMBOL",
"name": "type_specification"
},
{
"type": "SYMBOL",
"name": "block"
}
]
} }
}, },
"extras": [ "extras": [

View File

@ -1,4 +1,27 @@
[ [
{
"type": "anonymous_function",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "block",
"named": true
},
{
"type": "identifier",
"named": true
},
{
"type": "type_specification",
"named": true
}
]
}
},
{ {
"type": "as_node", "type": "as_node",
"named": true, "named": true,
@ -70,6 +93,21 @@
"named": true, "named": true,
"fields": {} "fields": {}
}, },
{
"type": "break",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": false,
"types": [
{
"type": "statement",
"named": true
}
]
}
},
{ {
"type": "command", "type": "command",
"named": true, "named": true,
@ -253,7 +291,26 @@
} }
}, },
{ {
"type": "function", "type": "function_call",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "expression",
"named": true
},
{
"type": "function_expression",
"named": true
}
]
}
},
{
"type": "function_definition",
"named": true, "named": true,
"fields": {}, "fields": {},
"children": { "children": {
@ -269,26 +326,11 @@
"named": true "named": true
}, },
{ {
"type": "type_specification", "type": "type_arguments",
"named": true
}
]
}
},
{
"type": "function_call",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "expression",
"named": true "named": true
}, },
{ {
"type": "function_expression", "type": "type_specification",
"named": true "named": true
} }
] ]
@ -598,6 +640,21 @@
] ]
} }
}, },
{
"type": "return",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": false,
"types": [
{
"type": "statement",
"named": true
}
]
}
},
{ {
"type": "root", "type": "root",
"named": true, "named": true,
@ -644,6 +701,10 @@
"type": "block", "type": "block",
"named": true "named": true
}, },
{
"type": "break",
"named": true
},
{ {
"type": "expression", "type": "expression",
"named": true "named": true
@ -672,6 +733,10 @@
"type": "pipe", "type": "pipe",
"named": true "named": true
}, },
{
"type": "return",
"named": true
},
{ {
"type": "type_definition", "type": "type_definition",
"named": true "named": true
@ -784,6 +849,10 @@
"type": "enum_definition", "type": "enum_definition",
"named": true "named": true
}, },
{
"type": "function_definition",
"named": true
},
{ {
"type": "struct_definition", "type": "struct_definition",
"named": true "named": true
@ -814,6 +883,10 @@
"multiple": false, "multiple": false,
"required": true, "required": true,
"types": [ "types": [
{
"type": "anonymous_function",
"named": true
},
{ {
"type": "boolean", "type": "boolean",
"named": true "named": true
@ -826,10 +899,6 @@
"type": "float", "type": "float",
"named": true "named": true
}, },
{
"type": "function",
"named": true
},
{ {
"type": "integer", "type": "integer",
"named": true "named": true
@ -1036,6 +1105,10 @@
"type": "float", "type": "float",
"named": false "named": false
}, },
{
"type": "fn",
"named": false
},
{ {
"type": "for", "type": "for",
"named": false "named": false

File diff suppressed because it is too large Load Diff