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::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,

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

View File

@ -40,6 +40,7 @@ impl Context {
pub fn get_type(&self, identifier: &Identifier) -> Option<Type> {
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,
}
}

View File

@ -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<RwLock<HashSet<Identifier>>> = OnceLock::new();
static IDENTIFIER_CACHE: OnceLock<RwLock<HashMap<String, Identifier>>> = OnceLock::new();
/// Returns the identifier cache.
fn identifier_cache<'a>() -> &'a RwLock<HashSet<Identifier>> {
IDENTIFIER_CACHE.get_or_init(|| RwLock::new(HashSet::new()))
fn identifier_cache<'a>() -> &'a RwLock<HashMap<String, Identifier>> {
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<String>);
impl Identifier {
/// Creates a new identifier or returns a clone of an existing one from a cache.
pub fn new<T: ToString>(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)
}
}

View File

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

View File

@ -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<ValueInner>);
@ -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<Value> {
@ -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<Vec<Type>>,
pub value_parameters: Option<Vec<(Identifier, Type)>>,
pub return_type: Option<Type>,
pub body: AbstractSyntaxTree,
}
@ -786,16 +786,6 @@ impl Function {
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 {

View File

@ -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<Option<Value>, 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<Option<Value>, DustError> {
let abstract_syntax_tree = parse(source)?;
let mut analyzer = Analyzer::new(&abstract_syntax_tree, &context);