Fix identifier caching; Add docs; Add minor fixes

This commit is contained in:
Jeff 2024-08-13 12:23:25 -04:00
parent f3bef42563
commit 40a71da3a5
7 changed files with 53 additions and 37 deletions

View File

@ -155,7 +155,7 @@ impl Statement {
} }
}, },
Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(), 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::FunctionCall { function, .. } => function.inner.expected_type(context),
Statement::Identifier(identifier) => context.get_type(identifier), Statement::Identifier(identifier) => context.get_type(identifier),
Statement::If { .. } => None, Statement::If { .. } => None,

View File

@ -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. //! machine.
//! //!
//! This module provides two anlysis options: //! This module provides two anlysis options:

View File

@ -40,6 +40,7 @@ impl Context {
pub fn get_type(&self, identifier: &Identifier) -> Option<Type> { pub fn get_type(&self, identifier: &Identifier) -> Option<Type> {
match self.variables.read().unwrap().get(identifier) { match self.variables.read().unwrap().get(identifier) {
Some((VariableData::Type(r#type), _)) => Some(r#type.clone()), Some((VariableData::Type(r#type), _)) => Some(r#type.clone()),
Some((VariableData::Value(value), _)) => Some(value.r#type()),
_ => None, _ => None,
} }
} }

View File

@ -9,11 +9,12 @@
//! # use dust_lang::Identifier; //! # use dust_lang::Identifier;
//! let foo = Identifier::new("foo"); //! let foo = Identifier::new("foo");
//! let also_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::{ use std::{
collections::HashSet, collections::HashMap,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
hash::Hash, hash::Hash,
sync::{Arc, OnceLock, RwLock}, sync::{Arc, OnceLock, RwLock},
@ -22,11 +23,11 @@ use std::{
use serde::{de::Visitor, Deserialize, Serialize}; use serde::{de::Visitor, Deserialize, Serialize};
/// In-use identifiers. /// In-use identifiers.
static IDENTIFIER_CACHE: OnceLock<RwLock<HashSet<Identifier>>> = OnceLock::new(); static IDENTIFIER_CACHE: OnceLock<RwLock<HashMap<String, Identifier>>> = OnceLock::new();
/// Returns the identifier cache. /// Returns the identifier cache.
fn identifier_cache<'a>() -> &'a RwLock<HashSet<Identifier>> { fn identifier_cache<'a>() -> &'a RwLock<HashMap<String, Identifier>> {
IDENTIFIER_CACHE.get_or_init(|| RwLock::new(HashSet::new())) IDENTIFIER_CACHE.get_or_init(|| RwLock::new(HashMap::new()))
} }
/// Key used to identify a value or type. /// Key used to identify a value or type.
@ -38,25 +39,25 @@ pub struct Identifier(Arc<String>);
impl Identifier { impl Identifier {
/// Creates a new identifier or returns a clone of an existing one from a cache. /// Creates a new identifier or returns a clone of an existing one from a cache.
pub fn new<T: ToString>(text: T) -> Self { pub fn new<T: ToString>(text: T) -> Self {
let cache = identifier_cache().read().unwrap(); let string = text.to_string();
let new = Identifier(Arc::new(text.to_string())); let mut cache = identifier_cache().write().unwrap();
if cache.contains(&new) { if let Some(old) = cache.get(&string) {
return new; 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 { pub fn as_str(&self) -> &str {
self.0.as_str() self.0.as_str()
} }
pub fn hard_count(&self) -> usize { pub fn strong_count(&self) -> usize {
Arc::strong_count(&self.0) Arc::strong_count(&self.0)
} }
} }

View File

@ -25,6 +25,6 @@ pub use parser::{parse, ParseError, Parser};
pub use r#type::Type; pub use r#type::Type;
pub use token::{Token, TokenKind, TokenOwned}; pub use token::{Token, TokenKind, TokenOwned};
pub use value::{Value, ValueError}; 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); pub type Span = (usize, usize);

View File

@ -44,10 +44,9 @@ use crate::{identifier::Identifier, AbstractSyntaxTree, Context, Type, Vm, VmErr
/// ``` /// ```
/// # use std::collections::HashMap; /// # use std::collections::HashMap;
/// # use dust_lang::*; /// # use dust_lang::*;
/// let context = Context::new();
/// let value = Value::integer(42); /// let value = Value::integer(42);
/// ///
/// assert_eq!(value.r#type(&context), Type::Integer); /// assert_eq!(value.r#type(), Type::Integer);
/// ``` /// ```
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Value(Arc<ValueInner>); pub struct Value(Arc<ValueInner>);
@ -89,8 +88,8 @@ impl Value {
Value(Arc::new(ValueInner::String(to_string.to_string()))) Value(Arc::new(ValueInner::String(to_string.to_string())))
} }
pub fn r#type(&self, context: &Context) -> Type { pub fn r#type(&self) -> Type {
self.0.r#type(context) self.0.r#type()
} }
pub fn get_property(&self, property: &Identifier) -> Option<Value> { pub fn get_property(&self, property: &Identifier) -> Option<Value> {
@ -665,18 +664,18 @@ pub enum ValueInner {
} }
impl ValueInner { impl ValueInner {
fn r#type(&self, context: &Context) -> Type { fn r#type(&self) -> Type {
match self { match self {
ValueInner::Boolean(_) => Type::Boolean, ValueInner::Boolean(_) => Type::Boolean,
ValueInner::Float(_) => Type::Float, ValueInner::Float(_) => Type::Float,
ValueInner::Function(function) => Type::Function { ValueInner::Function(function) => Type::Function {
type_parameters: function.type_parameters.clone(), type_parameters: function.type_parameters.clone(),
value_parameters: function.value_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::Integer(_) => Type::Integer,
ValueInner::List(values) => { ValueInner::List(values) => {
let item_type = values.first().unwrap().r#type(context); let item_type = values.first().unwrap().r#type();
Type::List { Type::List {
item_type: Box::new(item_type), item_type: Box::new(item_type),
@ -686,7 +685,7 @@ impl ValueInner {
let mut type_map = BTreeMap::new(); let mut type_map = BTreeMap::new();
for (identifier, value) in value_map { 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); type_map.insert(identifier.clone(), r#type);
} }
@ -762,6 +761,7 @@ pub struct Function {
pub name: Identifier, pub name: Identifier,
pub type_parameters: Option<Vec<Type>>, pub type_parameters: Option<Vec<Type>>,
pub value_parameters: Option<Vec<(Identifier, Type)>>, pub value_parameters: Option<Vec<(Identifier, Type)>>,
pub return_type: Option<Type>,
pub body: AbstractSyntaxTree, pub body: AbstractSyntaxTree,
} }
@ -786,16 +786,6 @@ impl Function {
vm.run() vm.run()
} }
pub fn return_type(&self, variables: &Context) -> Option<Type> {
self.body
.nodes
.iter()
.last()
.unwrap()
.inner
.expected_type(variables)
}
} }
impl Display for Function { impl Display for Function {

View File

@ -15,12 +15,36 @@ use crate::{
ValueError, 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<Option<Value>, DustError> { pub fn run(source: &str) -> Result<Option<Value>, DustError> {
let context = Context::new(); let context = Context::new();
run_with_context(source, context) 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<Option<Value>, DustError> { pub fn run_with_context(source: &str, context: Context) -> Result<Option<Value>, DustError> {
let abstract_syntax_tree = parse(source)?; let abstract_syntax_tree = parse(source)?;
let mut analyzer = Analyzer::new(&abstract_syntax_tree, &context); let mut analyzer = Analyzer::new(&abstract_syntax_tree, &context);