Add memory management
This commit is contained in:
parent
4be32d0a5d
commit
fdf286cb51
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -204,6 +204,7 @@ dependencies = [
|
||||
"clap",
|
||||
"colored",
|
||||
"env_logger",
|
||||
"log",
|
||||
"stanza",
|
||||
]
|
||||
|
||||
|
@ -18,4 +18,5 @@ chumsky = { version = "1.0.0-alpha.6", features = ["pratt", "label"] }
|
||||
clap = { version = "4.5.2", features = ["derive"] }
|
||||
colored = "2.1.0"
|
||||
env_logger = "0.11.3"
|
||||
log = "0.4.21"
|
||||
stanza = "0.5.1"
|
||||
|
@ -52,6 +52,7 @@ impl AbstractTree for Assignment {
|
||||
context.set_type(self.identifier.clone(), statement_type)?;
|
||||
}
|
||||
|
||||
self.identifier.validate(context)?;
|
||||
self.statement.validate(context)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -38,12 +38,12 @@ impl AbstractTree for FunctionCall {
|
||||
}
|
||||
|
||||
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
|
||||
let value = self.function.run(context)?.as_value()?;
|
||||
let value = self.function.run(context)?.as_return_value()?;
|
||||
let function = value.as_function()?;
|
||||
let mut arguments = Vec::with_capacity(self.arguments.len());
|
||||
|
||||
for expression in self.arguments {
|
||||
let value = expression.run(context)?.as_value()?;
|
||||
let value = expression.run(context)?.as_return_value()?;
|
||||
|
||||
arguments.push(value);
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ use std::{
|
||||
use crate::{
|
||||
context::Context,
|
||||
error::{RuntimeError, ValidationError},
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::{AbstractTree, Action, Type};
|
||||
@ -34,7 +33,7 @@ impl AbstractTree for Identifier {
|
||||
}
|
||||
|
||||
fn validate(&self, context: &Context) -> Result<(), ValidationError> {
|
||||
if let Some(_) = context.get_data(self)? {
|
||||
if context.add_allowance(self)? {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ValidationError::VariableNotFound(self.clone()))
|
||||
@ -42,12 +41,15 @@ impl AbstractTree for Identifier {
|
||||
}
|
||||
|
||||
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
|
||||
let value = context
|
||||
.get_value(&self)?
|
||||
.unwrap_or_else(Value::none)
|
||||
.clone();
|
||||
let return_action = context.get_value(&self)?.map(|value| Action::Return(value));
|
||||
|
||||
Ok(Action::Return(value))
|
||||
if let Some(action) = return_action {
|
||||
Ok(action)
|
||||
} else {
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::VariableNotFound(self.clone()),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::{
|
||||
context::Context,
|
||||
error::{RuntimeError, ValidationError},
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::{AbstractTree, Action, Expression, Type, ValueNode};
|
||||
@ -63,11 +62,13 @@ impl AbstractTree for Index {
|
||||
let right_value = self.right.run(_context)?.as_return_value()?;
|
||||
|
||||
if let (Some(list), Some(index)) = (left_value.as_list(), right_value.as_integer()) {
|
||||
Ok(Action::Return(
|
||||
list.get(index as usize)
|
||||
.cloned()
|
||||
.unwrap_or_else(Value::none),
|
||||
))
|
||||
let found_item = list.get(index as usize);
|
||||
|
||||
if let Some(item) = found_item {
|
||||
Ok(Action::Return(item.clone()))
|
||||
} else {
|
||||
Ok(Action::None)
|
||||
}
|
||||
} else {
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::CannotIndexWith(left_value.r#type(), right_value.r#type()),
|
||||
|
@ -44,19 +44,12 @@ pub trait AbstractTree {
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum Action {
|
||||
Break,
|
||||
Return(Value),
|
||||
Break,
|
||||
None,
|
||||
}
|
||||
|
||||
impl Action {
|
||||
pub fn as_value(self) -> Result<Value, ValidationError> {
|
||||
match self {
|
||||
Action::Return(value) => Ok(value),
|
||||
_ => Err(ValidationError::ExpectedValue),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_return_value(self) -> Result<Value, ValidationError> {
|
||||
if let Action::Return(value) = self {
|
||||
Ok(value)
|
||||
|
@ -17,7 +17,6 @@ pub enum ValueNode {
|
||||
Map(Vec<(Identifier, Option<Type>, Expression)>),
|
||||
Range(Range<i64>),
|
||||
String(String),
|
||||
Enum(Identifier, Identifier),
|
||||
Function {
|
||||
parameters: Vec<(Identifier, Type)>,
|
||||
return_type: Type,
|
||||
@ -43,7 +42,6 @@ impl AbstractTree for ValueNode {
|
||||
ValueNode::Map(_) => Type::Map,
|
||||
ValueNode::Range(_) => Type::Range,
|
||||
ValueNode::String(_) => Type::String,
|
||||
ValueNode::Enum(name, _) => Type::Custom(name.clone()),
|
||||
ValueNode::Function {
|
||||
parameters,
|
||||
return_type,
|
||||
@ -123,13 +121,6 @@ impl AbstractTree for ValueNode {
|
||||
}
|
||||
ValueNode::Range(range) => Value::range(range),
|
||||
ValueNode::String(string) => Value::string(string),
|
||||
ValueNode::Enum(name, variant) => {
|
||||
if name.as_str() == "Option" && variant.as_str() == "None" {
|
||||
Value::none()
|
||||
} else {
|
||||
Value::r#enum(name, variant)
|
||||
}
|
||||
}
|
||||
ValueNode::Function {
|
||||
parameters,
|
||||
return_type,
|
||||
@ -176,16 +167,6 @@ impl Ord for ValueNode {
|
||||
(Range(_), _) => Ordering::Greater,
|
||||
(String(left), String(right)) => left.cmp(right),
|
||||
(String(_), _) => Ordering::Greater,
|
||||
(Enum(left_name, left_variant), Enum(right_name, right_variant)) => {
|
||||
let name_cmp = left_name.cmp(right_name);
|
||||
|
||||
if name_cmp.is_eq() {
|
||||
left_variant.cmp(right_variant)
|
||||
} else {
|
||||
name_cmp
|
||||
}
|
||||
}
|
||||
(Enum(_, _), _) => Ordering::Greater,
|
||||
(
|
||||
Function {
|
||||
parameters: left_parameters,
|
||||
|
@ -27,15 +27,15 @@ impl AbstractTree for While {
|
||||
self.block.validate(_context)
|
||||
}
|
||||
|
||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
|
||||
while self
|
||||
.expression
|
||||
.clone()
|
||||
.run(_context)?
|
||||
.run(context)?
|
||||
.as_return_value()?
|
||||
.as_boolean()?
|
||||
{
|
||||
if let Action::Break = self.block.clone().run(_context)? {
|
||||
if let Action::Break = self.block.clone().run(context)? {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
103
src/context.rs
103
src/context.rs
@ -11,7 +11,25 @@ use crate::{
|
||||
};
|
||||
|
||||
pub struct Context {
|
||||
inner: Arc<RwLock<BTreeMap<Identifier, ValueData>>>,
|
||||
inner: Arc<RwLock<BTreeMap<Identifier, (ValueData, UsageData)>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UsageData(Arc<RwLock<UsageDataInner>>);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UsageDataInner {
|
||||
pub allowances: usize,
|
||||
pub uses: usize,
|
||||
}
|
||||
|
||||
impl Default for UsageData {
|
||||
fn default() -> Self {
|
||||
UsageData(Arc::new(RwLock::new(UsageDataInner {
|
||||
allowances: 0,
|
||||
uses: 0,
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -27,7 +45,7 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_data(data: BTreeMap<Identifier, ValueData>) -> Self {
|
||||
pub fn with_data(data: BTreeMap<Identifier, (ValueData, UsageData)>) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(data)),
|
||||
}
|
||||
@ -36,10 +54,10 @@ impl Context {
|
||||
pub fn inherit_types_from(other: &Context) -> Result<Self, RwLockPoisonError> {
|
||||
let mut new_data = BTreeMap::new();
|
||||
|
||||
for (identifier, value_data) in other.inner.read()?.iter() {
|
||||
for (identifier, (value_data, usage_data)) in other.inner.read()?.iter() {
|
||||
if let ValueData::Type(r#type) = value_data {
|
||||
if let Type::Function { .. } = r#type {
|
||||
new_data.insert(identifier.clone(), value_data.clone());
|
||||
new_data.insert(identifier.clone(), (value_data.clone(), usage_data.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -50,15 +68,15 @@ impl Context {
|
||||
pub fn inherit_data_from(other: &Context) -> Result<Self, RwLockPoisonError> {
|
||||
let mut new_data = BTreeMap::new();
|
||||
|
||||
for (identifier, value_data) in other.inner.read()?.iter() {
|
||||
for (identifier, (value_data, usage_data)) in other.inner.read()?.iter() {
|
||||
if let ValueData::Type(r#type) = value_data {
|
||||
if let Type::Function { .. } = r#type {
|
||||
new_data.insert(identifier.clone(), value_data.clone());
|
||||
new_data.insert(identifier.clone(), (value_data.clone(), usage_data.clone()));
|
||||
}
|
||||
}
|
||||
if let ValueData::Value(value) = value_data {
|
||||
if let ValueInner::Function { .. } = value.inner().as_ref() {
|
||||
new_data.insert(identifier.clone(), value_data.clone());
|
||||
new_data.insert(identifier.clone(), (value_data.clone(), usage_data.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,13 +84,42 @@ impl Context {
|
||||
Ok(Self::with_data(new_data))
|
||||
}
|
||||
|
||||
pub fn add_allowance(&self, identifier: &Identifier) -> Result<bool, RwLockPoisonError> {
|
||||
if let Some((_, usage_data)) = self.inner.read()?.get(identifier) {
|
||||
usage_data.0.write()?.allowances += 1;
|
||||
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_data(
|
||||
&self,
|
||||
identifier: &Identifier,
|
||||
) -> Result<Option<ValueData>, RwLockPoisonError> {
|
||||
if let Some(value_data) = self.inner.read()?.get(identifier) {
|
||||
let should_remove =
|
||||
if let Some((value_data, usage_data)) = self.inner.read()?.get(identifier) {
|
||||
let mut usage_data = usage_data.0.write()?;
|
||||
|
||||
log::trace!("Adding use for variable: {identifier}");
|
||||
|
||||
usage_data.uses += 1;
|
||||
|
||||
if usage_data.uses == usage_data.allowances {
|
||||
true
|
||||
} else {
|
||||
return Ok(Some(value_data.clone()));
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if should_remove {
|
||||
log::trace!("Removing varialble: {identifier}");
|
||||
|
||||
self.inner.write()?.remove(identifier);
|
||||
}
|
||||
|
||||
let value_data = match identifier.as_str() {
|
||||
"output" => ValueData::Value(BuiltInFunction::output()),
|
||||
@ -83,7 +130,11 @@ impl Context {
|
||||
}
|
||||
|
||||
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, RwLockPoisonError> {
|
||||
if let Some(ValueData::Type(r#type)) = self.inner.read()?.get(identifier) {
|
||||
if let Some((ValueData::Type(r#type), usage_data)) = self.inner.read()?.get(identifier) {
|
||||
log::trace!("Adding use for variable: {identifier}");
|
||||
|
||||
usage_data.0.write()?.uses += 1;
|
||||
|
||||
return Ok(Some(r#type.clone()));
|
||||
}
|
||||
|
||||
@ -96,9 +147,29 @@ impl Context {
|
||||
}
|
||||
|
||||
pub fn get_value(&self, identifier: &Identifier) -> Result<Option<Value>, RwLockPoisonError> {
|
||||
if let Some(ValueData::Value(value)) = self.inner.read()?.get(identifier) {
|
||||
let should_remove = if let Some((ValueData::Value(value), usage_data)) =
|
||||
self.inner.read()?.get(identifier)
|
||||
{
|
||||
let mut usage_data = usage_data.0.write()?;
|
||||
|
||||
log::trace!("Adding use for variable: {identifier}");
|
||||
|
||||
usage_data.uses += 1;
|
||||
|
||||
if usage_data.uses == usage_data.allowances {
|
||||
true
|
||||
} else {
|
||||
return Ok(Some(value.clone()));
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if should_remove {
|
||||
log::trace!("Removing varialble: {identifier}");
|
||||
|
||||
self.inner.write()?.remove(identifier);
|
||||
}
|
||||
|
||||
let value = match identifier.as_str() {
|
||||
"output" => BuiltInFunction::output(),
|
||||
@ -111,15 +182,19 @@ impl Context {
|
||||
pub fn set_type(&self, identifier: Identifier, r#type: Type) -> Result<(), RwLockPoisonError> {
|
||||
self.inner
|
||||
.write()?
|
||||
.insert(identifier, ValueData::Type(r#type));
|
||||
.insert(identifier, (ValueData::Type(r#type), UsageData::default()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_value(&self, identifier: Identifier, value: Value) -> Result<(), RwLockPoisonError> {
|
||||
self.inner
|
||||
.write()?
|
||||
.insert(identifier, ValueData::Value(value));
|
||||
let mut inner = self.inner.write()?;
|
||||
|
||||
if let Some((_value_data, usage_data)) = inner.remove(&identifier) {
|
||||
inner.insert(identifier, (ValueData::Value(value), usage_data));
|
||||
} else {
|
||||
inner.insert(identifier, (ValueData::Value(value), UsageData::default()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -169,13 +169,6 @@ pub fn parser<'src>() -> DustParser<'src> {
|
||||
)
|
||||
.map(|map_assigment_list| Expression::Value(ValueNode::Map(map_assigment_list)));
|
||||
|
||||
let r#enum = identifier
|
||||
.clone()
|
||||
.then_ignore(just(Token::Control(Control::DoubleColon)))
|
||||
.then(identifier.clone())
|
||||
.map(|(name, variant)| Expression::Value(ValueNode::Enum(name, variant)))
|
||||
.boxed();
|
||||
|
||||
let function = identifier
|
||||
.clone()
|
||||
.then(type_specification.clone())
|
||||
@ -219,7 +212,6 @@ pub fn parser<'src>() -> DustParser<'src> {
|
||||
identifier_expression.clone(),
|
||||
basic_value.clone(),
|
||||
list.clone(),
|
||||
r#enum.clone(),
|
||||
expression.clone().delimited_by(
|
||||
just(Token::Control(Control::ParenOpen)),
|
||||
just(Token::Control(Control::ParenClose)),
|
||||
@ -289,7 +281,6 @@ pub fn parser<'src>() -> DustParser<'src> {
|
||||
choice((
|
||||
function,
|
||||
range,
|
||||
r#enum,
|
||||
logic_math_and_index,
|
||||
identifier_expression,
|
||||
list,
|
||||
@ -661,22 +652,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assignment_with_custom_type() {
|
||||
assert_eq!(
|
||||
parse(&lex("foobar: Foo = Foo::Bar").unwrap()).unwrap()[0].0,
|
||||
Statement::Assignment(Assignment::new(
|
||||
Identifier::new("foobar"),
|
||||
Some(Type::Custom(Identifier::new("Foo"))),
|
||||
AssignmentOperator::Assign,
|
||||
Statement::Expression(Expression::Value(ValueNode::Enum(
|
||||
Identifier::new("Foo"),
|
||||
Identifier::new("Bar")
|
||||
)))
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assignment_with_list_types() {
|
||||
assert_eq!(
|
||||
@ -755,17 +730,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn r#enum() {
|
||||
assert_eq!(
|
||||
parse(&lex("Option::None").unwrap()).unwrap()[0].0,
|
||||
Statement::Expression(Expression::Value(ValueNode::Enum(
|
||||
Identifier::new("Option"),
|
||||
Identifier::new("None")
|
||||
)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list() {
|
||||
assert_eq!(
|
||||
|
37
src/value.rs
37
src/value.rs
@ -18,17 +18,6 @@ use crate::{
|
||||
error::{RuntimeError, ValidationError},
|
||||
};
|
||||
|
||||
pub static NONE: OnceLock<Value> = OnceLock::new();
|
||||
|
||||
fn get_none<'a>() -> &'a Value {
|
||||
NONE.get_or_init(|| {
|
||||
Value(Arc::new(ValueInner::Enum(
|
||||
Identifier::new("Option"),
|
||||
Identifier::new("None"),
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Value(Arc<ValueInner>);
|
||||
|
||||
@ -37,10 +26,6 @@ impl Value {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn none() -> Self {
|
||||
get_none().clone()
|
||||
}
|
||||
|
||||
pub fn boolean(boolean: bool) -> Self {
|
||||
Value(Arc::new(ValueInner::Boolean(boolean)))
|
||||
}
|
||||
@ -69,10 +54,6 @@ impl Value {
|
||||
Value(Arc::new(ValueInner::String(string)))
|
||||
}
|
||||
|
||||
pub fn r#enum(name: Identifier, variant: Identifier) -> Self {
|
||||
Value(Arc::new(ValueInner::Enum(name, variant)))
|
||||
}
|
||||
|
||||
pub fn function(parameters: Vec<(Identifier, Type)>, return_type: Type, body: Block) -> Self {
|
||||
Value(Arc::new(ValueInner::Function(Function::Parsed(
|
||||
ParsedFunction {
|
||||
@ -104,7 +85,6 @@ impl Value {
|
||||
ValueInner::Map(_) => Type::Map,
|
||||
ValueInner::Range(_) => Type::Range,
|
||||
ValueInner::String(_) => Type::String,
|
||||
ValueInner::Enum(name, _) => Type::Custom(name.clone()),
|
||||
ValueInner::Function(_) => todo!(),
|
||||
}
|
||||
}
|
||||
@ -149,10 +129,6 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_none(&self) -> bool {
|
||||
self == get_none()
|
||||
}
|
||||
|
||||
pub fn add(&self, other: &Self) -> Result<Value, ValidationError> {
|
||||
match (self.inner().as_ref(), other.inner().as_ref()) {
|
||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
||||
@ -236,8 +212,6 @@ impl Display for Value {
|
||||
}
|
||||
ValueInner::Range(_) => todo!(),
|
||||
ValueInner::String(string) => write!(f, "{string}"),
|
||||
|
||||
ValueInner::Enum(_, _) => todo!(),
|
||||
ValueInner::Function(Function::Parsed(ParsedFunction {
|
||||
parameters,
|
||||
return_type,
|
||||
@ -282,7 +256,6 @@ pub enum ValueInner {
|
||||
Map(BTreeMap<Identifier, Value>),
|
||||
Range(Range<i64>),
|
||||
String(String),
|
||||
Enum(Identifier, Identifier),
|
||||
}
|
||||
|
||||
impl Eq for ValueInner {}
|
||||
@ -320,16 +293,6 @@ impl Ord for ValueInner {
|
||||
(Range(_), _) => Ordering::Greater,
|
||||
(String(left), String(right)) => left.cmp(right),
|
||||
(String(_), _) => Ordering::Greater,
|
||||
(Enum(name_left, variant_left), Enum(name_right, variant_right)) => {
|
||||
let name_cmp = name_left.cmp(name_right);
|
||||
|
||||
if name_cmp.is_eq() {
|
||||
variant_left.cmp(variant_right)
|
||||
} else {
|
||||
name_cmp
|
||||
}
|
||||
}
|
||||
(Enum(..), _) => Ordering::Greater,
|
||||
(Function(left), Function(right)) => left.cmp(right),
|
||||
(Function(_), _) => Ordering::Greater,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user