diff --git a/dust-lang/src/abstract_tree.rs b/dust-lang/src/abstract_tree.rs index 6824af6..5748362 100644 --- a/dust-lang/src/abstract_tree.rs +++ b/dust-lang/src/abstract_tree.rs @@ -155,7 +155,7 @@ impl Statement { } }, Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(), - Statement::Constant(value) => Some(value.r#type(context)), + Statement::Constant(value) => Some(value.r#type()), Statement::FunctionCall { function, .. } => function.inner.expected_type(context), Statement::Identifier(identifier) => context.get_type(identifier), Statement::If { .. } => None, diff --git a/dust-lang/src/analyzer.rs b/dust-lang/src/analyzer.rs index f065385..1b1b607 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -1,4 +1,4 @@ -//! Tools for analyzing an abstract syntax tree and catch errors before running the virtual +//! Tools for analyzing an abstract syntax tree and catching errors before running the virtual //! machine. //! //! This module provides two anlysis options: diff --git a/dust-lang/src/context.rs b/dust-lang/src/context.rs index 3a5b41c..cf7655d 100644 --- a/dust-lang/src/context.rs +++ b/dust-lang/src/context.rs @@ -40,6 +40,7 @@ impl Context { pub fn get_type(&self, identifier: &Identifier) -> Option { match self.variables.read().unwrap().get(identifier) { Some((VariableData::Type(r#type), _)) => Some(r#type.clone()), + Some((VariableData::Value(value), _)) => Some(value.r#type()), _ => None, } } diff --git a/dust-lang/src/identifier.rs b/dust-lang/src/identifier.rs index 0da95c9..5bb8988 100644 --- a/dust-lang/src/identifier.rs +++ b/dust-lang/src/identifier.rs @@ -9,11 +9,12 @@ //! # use dust_lang::Identifier; //! let foo = Identifier::new("foo"); //! let also_foo = Identifier::new("foo"); +//! let another_foo = Identifier::new("foo"); //! -//! assert_eq!(foo.hard_count(), 2); +//! assert_eq!(foo.strong_count(), 4); // One for each of the above and one for the cache. //! ``` use std::{ - collections::HashSet, + collections::HashMap, fmt::{self, Display, Formatter}, hash::Hash, sync::{Arc, OnceLock, RwLock}, @@ -22,11 +23,11 @@ use std::{ use serde::{de::Visitor, Deserialize, Serialize}; /// In-use identifiers. -static IDENTIFIER_CACHE: OnceLock>> = OnceLock::new(); +static IDENTIFIER_CACHE: OnceLock>> = OnceLock::new(); /// Returns the identifier cache. -fn identifier_cache<'a>() -> &'a RwLock> { - IDENTIFIER_CACHE.get_or_init(|| RwLock::new(HashSet::new())) +fn identifier_cache<'a>() -> &'a RwLock> { + IDENTIFIER_CACHE.get_or_init(|| RwLock::new(HashMap::new())) } /// Key used to identify a value or type. @@ -38,25 +39,25 @@ pub struct Identifier(Arc); impl Identifier { /// Creates a new identifier or returns a clone of an existing one from a cache. pub fn new(text: T) -> Self { - let cache = identifier_cache().read().unwrap(); - let new = Identifier(Arc::new(text.to_string())); + let string = text.to_string(); + let mut cache = identifier_cache().write().unwrap(); - if cache.contains(&new) { - return new; + if let Some(old) = cache.get(&string) { + old.clone() + } else { + let new = Identifier(Arc::new(string.clone())); + + cache.insert(string, new.clone()); + + new } - - drop(cache); - - identifier_cache().write().unwrap().insert(new.clone()); - - new } pub fn as_str(&self) -> &str { self.0.as_str() } - pub fn hard_count(&self) -> usize { + pub fn strong_count(&self) -> usize { Arc::strong_count(&self.0) } } diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 4cfeffa..6a83b9a 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -25,6 +25,6 @@ pub use parser::{parse, ParseError, Parser}; pub use r#type::Type; pub use token::{Token, TokenKind, TokenOwned}; pub use value::{Value, ValueError}; -pub use vm::{run, Vm, VmError}; +pub use vm::{run, run_with_context, Vm, VmError}; pub type Span = (usize, usize); diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index e1296bd..585a1ba 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -44,10 +44,9 @@ use crate::{identifier::Identifier, AbstractSyntaxTree, Context, Type, Vm, VmErr /// ``` /// # use std::collections::HashMap; /// # use dust_lang::*; -/// let context = Context::new(); /// let value = Value::integer(42); /// -/// assert_eq!(value.r#type(&context), Type::Integer); +/// assert_eq!(value.r#type(), Type::Integer); /// ``` #[derive(Clone, Debug, PartialEq)] pub struct Value(Arc); @@ -89,8 +88,8 @@ impl Value { Value(Arc::new(ValueInner::String(to_string.to_string()))) } - pub fn r#type(&self, context: &Context) -> Type { - self.0.r#type(context) + pub fn r#type(&self) -> Type { + self.0.r#type() } pub fn get_property(&self, property: &Identifier) -> Option { @@ -665,18 +664,18 @@ pub enum ValueInner { } impl ValueInner { - fn r#type(&self, context: &Context) -> Type { + fn r#type(&self) -> Type { match self { ValueInner::Boolean(_) => Type::Boolean, ValueInner::Float(_) => Type::Float, ValueInner::Function(function) => Type::Function { type_parameters: function.type_parameters.clone(), value_parameters: function.value_parameters.clone(), - return_type: function.return_type(context).map(Box::new), + return_type: function.return_type.as_ref().cloned().map(Box::new), }, ValueInner::Integer(_) => Type::Integer, ValueInner::List(values) => { - let item_type = values.first().unwrap().r#type(context); + let item_type = values.first().unwrap().r#type(); Type::List { item_type: Box::new(item_type), @@ -686,7 +685,7 @@ impl ValueInner { let mut type_map = BTreeMap::new(); for (identifier, value) in value_map { - let r#type = value.r#type(context); + let r#type = value.r#type(); type_map.insert(identifier.clone(), r#type); } @@ -762,6 +761,7 @@ pub struct Function { pub name: Identifier, pub type_parameters: Option>, pub value_parameters: Option>, + pub return_type: Option, pub body: AbstractSyntaxTree, } @@ -786,16 +786,6 @@ impl Function { vm.run() } - - pub fn return_type(&self, variables: &Context) -> Option { - self.body - .nodes - .iter() - .last() - .unwrap() - .inner - .expected_type(variables) - } } impl Display for Function { diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 7919998..ebf490d 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -15,12 +15,36 @@ use crate::{ ValueError, }; +/// Run the source code and return the result. +/// +/// # Example +/// ``` +/// # use dust_lang::vm::run; +/// # use dust_lang::value::Value; +/// let result = run("40 + 2"); +/// +/// assert_eq!(result, Ok(Some(Value::integer(42)))); +/// ``` pub fn run(source: &str) -> Result, DustError> { let context = Context::new(); run_with_context(source, context) } +/// Run the source code with a context and return the result. +/// +/// # Example +/// ``` +/// # use dust_lang::{Context, Identifier, Value, run_with_context}; +/// let context = Context::new(); +/// +/// context.set_value(Identifier::new("foo"), Value::integer(40)); +/// context.update_last_position(&Identifier::new("foo"), (100, 100)); +/// +/// let result = run_with_context("foo + 2", context); +/// +/// assert_eq!(result, Ok(Some(Value::integer(42)))); +/// ``` pub fn run_with_context(source: &str, context: Context) -> Result, DustError> { let abstract_syntax_tree = parse(source)?; let mut analyzer = Analyzer::new(&abstract_syntax_tree, &context);