1
0

Add memory management

This commit is contained in:
Jeff 2024-03-11 21:57:27 -04:00
parent 4be32d0a5d
commit fdf286cb51
12 changed files with 116 additions and 134 deletions

1
Cargo.lock generated
View File

@ -204,6 +204,7 @@ dependencies = [
"clap",
"colored",
"env_logger",
"log",
"stanza",
]

View File

@ -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"

View File

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

View File

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

View File

@ -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()),
))
}
}
}

View File

@ -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()),

View File

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

View File

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

View File

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

View File

@ -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(())
}

View File

@ -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!(

View File

@ -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,
}