Fix identifier caching; Add docs; Add minor fixes
This commit is contained in:
parent
f3bef42563
commit
40a71da3a5
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user