Add more value mutability
This commit is contained in:
parent
8666f1cd9b
commit
e7b5390a55
@ -54,7 +54,7 @@ pub enum Statement {
|
|||||||
operator: Node<AssignmentOperator>,
|
operator: Node<AssignmentOperator>,
|
||||||
value: Box<Node<Statement>>,
|
value: Box<Node<Statement>>,
|
||||||
},
|
},
|
||||||
MutAssignment {
|
AssignmentMut {
|
||||||
identifier: Node<Identifier>,
|
identifier: Node<Identifier>,
|
||||||
value: Box<Node<Statement>>,
|
value: Box<Node<Statement>>,
|
||||||
},
|
},
|
||||||
@ -135,6 +135,7 @@ pub enum Statement {
|
|||||||
|
|
||||||
// Hard-coded value
|
// Hard-coded value
|
||||||
Constant(Value),
|
Constant(Value),
|
||||||
|
ConstantMut(Value),
|
||||||
|
|
||||||
// A statement that always returns None. Created with a semicolon, it causes the preceding
|
// 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.
|
// statement to return None. This is analagous to the semicolon in Rust.
|
||||||
@ -146,6 +147,7 @@ impl Statement {
|
|||||||
match self {
|
match self {
|
||||||
Statement::AsyncBlock(_) => None,
|
Statement::AsyncBlock(_) => None,
|
||||||
Statement::Assignment { .. } => None,
|
Statement::Assignment { .. } => None,
|
||||||
|
Statement::AssignmentMut { .. } => None,
|
||||||
Statement::Block(statements) => statements.last().unwrap().inner.expected_type(context),
|
Statement::Block(statements) => statements.last().unwrap().inner.expected_type(context),
|
||||||
Statement::BinaryOperation {
|
Statement::BinaryOperation {
|
||||||
left,
|
left,
|
||||||
@ -192,6 +194,7 @@ impl Statement {
|
|||||||
},
|
},
|
||||||
Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(),
|
Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(),
|
||||||
Statement::Constant(value) => Some(value.r#type()),
|
Statement::Constant(value) => Some(value.r#type()),
|
||||||
|
Statement::ConstantMut(value) => Some(value.r#type()),
|
||||||
Statement::FieldsStructInstantiation { name, .. } => context.get_type(&name.inner),
|
Statement::FieldsStructInstantiation { name, .. } => context.get_type(&name.inner),
|
||||||
Statement::Invokation {
|
Statement::Invokation {
|
||||||
invokee: function, ..
|
invokee: function, ..
|
||||||
@ -218,7 +221,6 @@ impl Statement {
|
|||||||
|
|
||||||
Some(Type::Map(types))
|
Some(Type::Map(types))
|
||||||
}
|
}
|
||||||
Statement::MutAssignment { .. } => None,
|
|
||||||
Statement::Nil(_) => None,
|
Statement::Nil(_) => None,
|
||||||
Statement::UnaryOperation { operator, operand } => match operator.inner {
|
Statement::UnaryOperation { operator, operand } => match operator.inner {
|
||||||
UnaryOperator::Negate => Some(operand.inner.expected_type(context)?),
|
UnaryOperator::Negate => Some(operand.inner.expected_type(context)?),
|
||||||
@ -254,6 +256,9 @@ impl Display for Statement {
|
|||||||
} => {
|
} => {
|
||||||
write!(f, "{identifier} {operator} {value}")
|
write!(f, "{identifier} {operator} {value}")
|
||||||
}
|
}
|
||||||
|
Statement::AssignmentMut { identifier, value } => {
|
||||||
|
write!(f, "mut {identifier} = {value}")
|
||||||
|
}
|
||||||
Statement::AsyncBlock(statements) => {
|
Statement::AsyncBlock(statements) => {
|
||||||
write!(f, "async {{ ")?;
|
write!(f, "async {{ ")?;
|
||||||
|
|
||||||
@ -340,6 +345,7 @@ impl Display for Statement {
|
|||||||
write!(f, ")")
|
write!(f, ")")
|
||||||
}
|
}
|
||||||
Statement::Constant(value) => write!(f, "{value}"),
|
Statement::Constant(value) => write!(f, "{value}"),
|
||||||
|
Statement::ConstantMut(value) => write!(f, "{value}"),
|
||||||
Statement::FieldsStructInstantiation { name, fields } => {
|
Statement::FieldsStructInstantiation { name, fields } => {
|
||||||
write!(f, "{name} {{ ")?;
|
write!(f, "{name} {{ ")?;
|
||||||
|
|
||||||
@ -452,9 +458,6 @@ impl Display for Statement {
|
|||||||
|
|
||||||
write!(f, "}}")
|
write!(f, "}}")
|
||||||
}
|
}
|
||||||
Statement::MutAssignment { identifier, value } => {
|
|
||||||
write!(f, "mut {identifier} = {value}")
|
|
||||||
}
|
|
||||||
Statement::Nil(node) => write!(f, "{node};"),
|
Statement::Nil(node) => write!(f, "{node};"),
|
||||||
Statement::UnaryOperation { operator, operand } => {
|
Statement::UnaryOperation { operator, operand } => {
|
||||||
let operator = match operator.inner {
|
let operator = match operator.inner {
|
||||||
|
@ -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) => {
|
Statement::AsyncBlock(statements) => {
|
||||||
for statement in statements {
|
for statement in statements {
|
||||||
self.analyze_statement(statement)?;
|
self.analyze_statement(statement)?;
|
||||||
@ -294,6 +308,7 @@ impl<'a> Analyzer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::Constant(_) => {}
|
Statement::Constant(_) => {}
|
||||||
|
Statement::ConstantMut(_) => {}
|
||||||
Statement::FieldsStructInstantiation {
|
Statement::FieldsStructInstantiation {
|
||||||
name,
|
name,
|
||||||
fields: field_arguments,
|
fields: field_arguments,
|
||||||
@ -479,20 +494,6 @@ impl<'a> Analyzer<'a> {
|
|||||||
self.analyze_statement(value_node)?;
|
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) => {
|
Statement::Nil(node) => {
|
||||||
self.analyze_statement(node)?;
|
self.analyze_statement(node)?;
|
||||||
}
|
}
|
||||||
|
@ -284,10 +284,17 @@ impl<'src> Parser<'src> {
|
|||||||
.parse()
|
.parse()
|
||||||
.map_err(|error| ParseError::BooleanError { error, position })?;
|
.map_err(|error| ParseError::BooleanError { error, position })?;
|
||||||
|
|
||||||
Ok(Node::new(
|
if let ParserMode::Mutable = self.mode {
|
||||||
Statement::Constant(Value::boolean(boolean)),
|
Ok(Node::new(
|
||||||
position,
|
Statement::ConstantMut(Value::boolean_mut(boolean)),
|
||||||
))
|
position,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(Node::new(
|
||||||
|
Statement::Constant(Value::boolean(boolean)),
|
||||||
|
position,
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(Token::Float(text), position) => {
|
(Token::Float(text), position) => {
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
@ -479,10 +486,17 @@ impl<'src> Parser<'src> {
|
|||||||
(Token::String(string), position) => {
|
(Token::String(string), position) => {
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
|
|
||||||
Ok(Node::new(
|
if let ParserMode::Mutable = self.mode {
|
||||||
Statement::Constant(Value::string(string)),
|
Ok(Node::new(
|
||||||
position,
|
Statement::ConstantMut(Value::string_mut(string)),
|
||||||
))
|
position,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(Node::new(
|
||||||
|
Statement::Constant(Value::string(string)),
|
||||||
|
position,
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(Token::LeftCurlyBrace, left_position) => {
|
(Token::LeftCurlyBrace, left_position) => {
|
||||||
self.next_token()?;
|
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;
|
let value_end = value.position.1;
|
||||||
|
|
||||||
Ok(Node::new(
|
Ok(Node::new(
|
||||||
Statement::MutAssignment { identifier, value },
|
Statement::AssignmentMut { identifier, value },
|
||||||
(left_position.0, value_end),
|
(left_position.0, value_end),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -1142,6 +1156,7 @@ impl<'src> Parser<'src> {
|
|||||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
enum ParserMode {
|
enum ParserMode {
|
||||||
Condition,
|
Condition,
|
||||||
|
Mutable,
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1258,20 +1273,20 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mutable_variable() {
|
fn mutable_variable() {
|
||||||
let input = "mut x = 42";
|
let input = "mut x = false";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(input),
|
parse(input),
|
||||||
Ok(AbstractSyntaxTree {
|
Ok(AbstractSyntaxTree {
|
||||||
nodes: [Node::new(
|
nodes: [Node::new(
|
||||||
Statement::MutAssignment {
|
Statement::AssignmentMut {
|
||||||
identifier: Node::new(Identifier::new("x"), (4, 5)),
|
identifier: Node::new(Identifier::new("x"), (4, 5)),
|
||||||
value: Box::new(Node::new(
|
value: Box::new(Node::new(
|
||||||
Statement::Constant(Value::integer(42)),
|
Statement::ConstantMut(Value::boolean_mut(false)),
|
||||||
(8, 10)
|
(8, 13)
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
(0, 10)
|
(0, 13)
|
||||||
)]
|
)]
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
|
@ -95,6 +95,16 @@ impl Value {
|
|||||||
Value::Mutable(Arc::new(RwLock::new(ValueData::Boolean(boolean))))
|
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 {
|
pub fn to_mut(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Value::Immutable(inner) => {
|
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 {
|
let other_data = match other {
|
||||||
Value::Immutable(inner) => inner.as_ref().clone(),
|
Value::Immutable(inner) => inner.as_ref().clone(),
|
||||||
Value::Mutable(inner_locked) => inner_locked.read().unwrap().clone(),
|
Value::Mutable(inner_locked) => inner_locked.read().unwrap().clone(),
|
||||||
@ -113,8 +123,6 @@ impl Value {
|
|||||||
match self {
|
match self {
|
||||||
Value::Mutable(locked) => {
|
Value::Mutable(locked) => {
|
||||||
*locked.write().unwrap() = other_data;
|
*locked.write().unwrap() = other_data;
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
Value::Immutable(_) => todo!(),
|
Value::Immutable(_) => todo!(),
|
||||||
}
|
}
|
||||||
@ -254,7 +262,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Value::Immutable(left), Value::Mutable(right)) => {
|
(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)) => {
|
(ValueData::Float(left), ValueData::Float(right)) => {
|
||||||
return Ok(Value::float(left + right));
|
return Ok(Value::float(left + right));
|
||||||
}
|
}
|
||||||
@ -286,6 +294,50 @@ impl Value {
|
|||||||
Err(ValueError::CannotAdd(self.clone(), other.clone()))
|
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> {
|
pub fn subtract(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Value::Immutable(left), Value::Immutable(right)) => {
|
(Value::Immutable(left), Value::Immutable(right)) => {
|
||||||
|
@ -137,14 +137,24 @@ impl Vm {
|
|||||||
position: value_position,
|
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)
|
Ok(None)
|
||||||
}
|
}
|
||||||
@ -649,7 +659,7 @@ impl Vm {
|
|||||||
|
|
||||||
Ok(Some(Value::map(values)))
|
Ok(Some(Value::map(values)))
|
||||||
}
|
}
|
||||||
Statement::MutAssignment { identifier, value } => {
|
Statement::AssignmentMut { identifier, value } => {
|
||||||
let position = value.position;
|
let position = value.position;
|
||||||
let value = if let Some(value) = self.run_statement(*value)? {
|
let value = if let Some(value) = self.run_statement(*value)? {
|
||||||
value.to_mut()
|
value.to_mut()
|
||||||
@ -661,6 +671,7 @@ impl Vm {
|
|||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
Statement::ConstantMut(value) => Ok(Some(value)),
|
||||||
Statement::Nil(node) => {
|
Statement::Nil(node) => {
|
||||||
let _return = self.run_statement(*node)?;
|
let _return = self.run_statement(*node)?;
|
||||||
|
|
||||||
@ -881,6 +892,20 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn async_block() {
|
fn async_block() {
|
||||||
let input = "x = 1; async { x += 1; x -= 1; } x";
|
let input = "x = 1; async { x += 1; x -= 1; } x";
|
||||||
|
Loading…
Reference in New Issue
Block a user