1
0

Add more value mutability

This commit is contained in:
Jeff 2024-08-14 04:59:27 -04:00
parent 8666f1cd9b
commit e7b5390a55
5 changed files with 142 additions and 46 deletions

View File

@ -54,7 +54,7 @@ pub enum Statement {
operator: Node<AssignmentOperator>,
value: Box<Node<Statement>>,
},
MutAssignment {
AssignmentMut {
identifier: Node<Identifier>,
value: Box<Node<Statement>>,
},
@ -135,6 +135,7 @@ pub enum Statement {
// Hard-coded value
Constant(Value),
ConstantMut(Value),
// A statement that always returns None. Created with a semicolon, it causes the preceding
// statement to return None. This is analagous to the semicolon in Rust.
@ -146,6 +147,7 @@ impl Statement {
match self {
Statement::AsyncBlock(_) => None,
Statement::Assignment { .. } => None,
Statement::AssignmentMut { .. } => None,
Statement::Block(statements) => statements.last().unwrap().inner.expected_type(context),
Statement::BinaryOperation {
left,
@ -192,6 +194,7 @@ impl Statement {
},
Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(),
Statement::Constant(value) => Some(value.r#type()),
Statement::ConstantMut(value) => Some(value.r#type()),
Statement::FieldsStructInstantiation { name, .. } => context.get_type(&name.inner),
Statement::Invokation {
invokee: function, ..
@ -218,7 +221,6 @@ impl Statement {
Some(Type::Map(types))
}
Statement::MutAssignment { .. } => None,
Statement::Nil(_) => None,
Statement::UnaryOperation { operator, operand } => match operator.inner {
UnaryOperator::Negate => Some(operand.inner.expected_type(context)?),
@ -254,6 +256,9 @@ impl Display for Statement {
} => {
write!(f, "{identifier} {operator} {value}")
}
Statement::AssignmentMut { identifier, value } => {
write!(f, "mut {identifier} = {value}")
}
Statement::AsyncBlock(statements) => {
write!(f, "async {{ ")?;
@ -340,6 +345,7 @@ impl Display for Statement {
write!(f, ")")
}
Statement::Constant(value) => write!(f, "{value}"),
Statement::ConstantMut(value) => write!(f, "{value}"),
Statement::FieldsStructInstantiation { name, fields } => {
write!(f, "{name} {{ ")?;
@ -452,9 +458,6 @@ impl Display for Statement {
write!(f, "}}")
}
Statement::MutAssignment { identifier, value } => {
write!(f, "mut {identifier} = {value}")
}
Statement::Nil(node) => write!(f, "{node};"),
Statement::UnaryOperation { operator, operand } => {
let operator = match operator.inner {

View File

@ -91,6 +91,20 @@ impl<'a> Analyzer<'a> {
});
}
}
Statement::AssignmentMut { identifier, value } => {
self.analyze_statement(value)?;
let value_type = value.inner.expected_type(self.context);
if let Some(r#type) = value_type {
self.context
.set_type(identifier.inner.clone(), r#type, identifier.position);
} else {
return Err(AnalyzerError::ExpectedValue {
actual: value.as_ref().clone(),
});
}
}
Statement::AsyncBlock(statements) => {
for statement in statements {
self.analyze_statement(statement)?;
@ -294,6 +308,7 @@ impl<'a> Analyzer<'a> {
}
}
Statement::Constant(_) => {}
Statement::ConstantMut(_) => {}
Statement::FieldsStructInstantiation {
name,
fields: field_arguments,
@ -479,20 +494,6 @@ impl<'a> Analyzer<'a> {
self.analyze_statement(value_node)?;
}
}
Statement::MutAssignment { identifier, value } => {
self.analyze_statement(value)?;
let value_type = value.inner.expected_type(self.context);
if let Some(r#type) = value_type {
self.context
.set_type(identifier.inner.clone(), r#type, identifier.position);
} else {
return Err(AnalyzerError::ExpectedValue {
actual: value.as_ref().clone(),
});
}
}
Statement::Nil(node) => {
self.analyze_statement(node)?;
}

View File

@ -284,10 +284,17 @@ impl<'src> Parser<'src> {
.parse()
.map_err(|error| ParseError::BooleanError { error, position })?;
Ok(Node::new(
Statement::Constant(Value::boolean(boolean)),
position,
))
if let ParserMode::Mutable = self.mode {
Ok(Node::new(
Statement::ConstantMut(Value::boolean_mut(boolean)),
position,
))
} else {
Ok(Node::new(
Statement::Constant(Value::boolean(boolean)),
position,
))
}
}
(Token::Float(text), position) => {
self.next_token()?;
@ -479,10 +486,17 @@ impl<'src> Parser<'src> {
(Token::String(string), position) => {
self.next_token()?;
Ok(Node::new(
Statement::Constant(Value::string(string)),
position,
))
if let ParserMode::Mutable = self.mode {
Ok(Node::new(
Statement::ConstantMut(Value::string_mut(string)),
position,
))
} else {
Ok(Node::new(
Statement::Constant(Value::string(string)),
position,
))
}
}
(Token::LeftCurlyBrace, left_position) => {
self.next_token()?;
@ -708,11 +722,11 @@ impl<'src> Parser<'src> {
});
}
let value = Box::new(self.parse_statement(0)?);
let value = Box::new(self.parse_statement_in_mode(ParserMode::Mutable, 0)?);
let value_end = value.position.1;
Ok(Node::new(
Statement::MutAssignment { identifier, value },
Statement::AssignmentMut { identifier, value },
(left_position.0, value_end),
))
}
@ -1142,6 +1156,7 @@ impl<'src> Parser<'src> {
#[derive(Debug, PartialEq, Clone, Copy)]
enum ParserMode {
Condition,
Mutable,
None,
}
@ -1258,20 +1273,20 @@ mod tests {
#[test]
fn mutable_variable() {
let input = "mut x = 42";
let input = "mut x = false";
assert_eq!(
parse(input),
Ok(AbstractSyntaxTree {
nodes: [Node::new(
Statement::MutAssignment {
Statement::AssignmentMut {
identifier: Node::new(Identifier::new("x"), (4, 5)),
value: Box::new(Node::new(
Statement::Constant(Value::integer(42)),
(8, 10)
Statement::ConstantMut(Value::boolean_mut(false)),
(8, 13)
)),
},
(0, 10)
(0, 13)
)]
.into()
})

View File

@ -95,6 +95,16 @@ impl Value {
Value::Mutable(Arc::new(RwLock::new(ValueData::Boolean(boolean))))
}
pub fn string_mut<T: ToString>(to_string: T) -> Self {
Value::Mutable(Arc::new(RwLock::new(ValueData::String(
to_string.to_string(),
))))
}
pub fn is_mutable(&self) -> bool {
matches!(self, Value::Mutable(_))
}
pub fn to_mut(self) -> Self {
match self {
Value::Immutable(inner) => {
@ -104,7 +114,7 @@ impl Value {
}
}
pub fn mutate(&self, other: &Value) -> Result<(), VmError> {
pub fn mutate(&self, other: &Value) {
let other_data = match other {
Value::Immutable(inner) => inner.as_ref().clone(),
Value::Mutable(inner_locked) => inner_locked.read().unwrap().clone(),
@ -113,8 +123,6 @@ impl Value {
match self {
Value::Mutable(locked) => {
*locked.write().unwrap() = other_data;
Ok(())
}
Value::Immutable(_) => todo!(),
}
@ -254,7 +262,7 @@ impl Value {
}
}
(Value::Immutable(left), Value::Mutable(right)) => {
match (&*right.read().unwrap(), left.as_ref()) {
match (left.as_ref(), &*right.read().unwrap()) {
(ValueData::Float(left), ValueData::Float(right)) => {
return Ok(Value::float(left + right));
}
@ -286,6 +294,50 @@ impl Value {
Err(ValueError::CannotAdd(self.clone(), other.clone()))
}
pub fn add_mut(&self, other: &Value) -> Result<(), ValueError> {
match (self, other) {
(Value::Mutable(left), Value::Mutable(right)) => {
match (&mut *left.write().unwrap(), &*right.read().unwrap()) {
(ValueData::Float(left), ValueData::Float(right)) => {
*left += right;
return Ok(());
}
(ValueData::Integer(left), ValueData::Integer(right)) => {
*left = left.saturating_add(*right);
return Ok(());
}
(ValueData::String(left), ValueData::String(right)) => {
left.push_str(right);
return Ok(());
}
_ => {}
}
}
(Value::Mutable(left), Value::Immutable(right)) => {
match (&mut *left.write().unwrap(), right.as_ref()) {
(ValueData::Float(left), ValueData::Float(right)) => {
*left += right;
return Ok(());
}
(ValueData::Integer(left), ValueData::Integer(right)) => {
*left = left.saturating_add(*right);
return Ok(());
}
(ValueData::String(left), ValueData::String(right)) => {
left.push_str(right);
return Ok(());
}
_ => {}
}
}
(Value::Immutable(_), _) => {
return Err(ValueError::CannotMutate(self.clone()));
}
}
Err(ValueError::CannotAdd(self.clone(), other.clone()))
}
pub fn subtract(&self, other: &Value) -> Result<Value, ValueError> {
match (self, other) {
(Value::Immutable(left), Value::Immutable(right)) => {

View File

@ -137,14 +137,24 @@ impl Vm {
position: value_position,
});
};
let new_value = left_value.add(&right_value).map_err(|value_error| {
VmError::ValueError {
error: value_error,
position: (identifier.position.0, value_position.1),
}
})?;
self.context.set_value(identifier.inner, new_value);
if left_value.is_mutable() {
left_value.add_mut(&right_value).map_err(|value_error| {
VmError::ValueError {
error: value_error,
position: (identifier.position.0, value_position.1),
}
})?;
} else {
let new_value = left_value.add(&right_value).map_err(|value_error| {
VmError::ValueError {
error: value_error,
position: (identifier.position.0, value_position.1),
}
})?;
self.context.set_value(identifier.inner, new_value);
}
Ok(None)
}
@ -649,7 +659,7 @@ impl Vm {
Ok(Some(Value::map(values)))
}
Statement::MutAssignment { identifier, value } => {
Statement::AssignmentMut { identifier, value } => {
let position = value.position;
let value = if let Some(value) = self.run_statement(*value)? {
value.to_mut()
@ -661,6 +671,7 @@ impl Vm {
Ok(None)
}
Statement::ConstantMut(value) => Ok(Some(value)),
Statement::Nil(node) => {
let _return = self.run_statement(*node)?;
@ -881,6 +892,20 @@ mod tests {
use super::*;
#[test]
fn mutate_variable() {
let input = "
mut x = ''
x += 'foo'
x += 'bar'
x
";
assert_eq!(run(input), Ok(Some(Value::string_mut("foobar"))));
}
#[test]
fn async_block() {
let input = "x = 1; async { x += 1; x -= 1; } x";