1
0

Begin AST overhaul

This commit is contained in:
Jeff 2024-08-14 14:28:39 -04:00
parent e7b5390a55
commit 43b2393d8a
7 changed files with 1266 additions and 1899 deletions

View File

@ -1,580 +0,0 @@
//! In-memory representation of a Dust program.
use std::{
collections::{BTreeMap, VecDeque},
fmt::{self, Display, Formatter},
};
use serde::{Deserialize, Serialize};
use crate::{BuiltInFunction, Context, Identifier, Span, Type, Value};
/// In-memory representation of a Dust program.
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct AbstractSyntaxTree {
pub nodes: VecDeque<Node<Statement>>,
}
impl AbstractSyntaxTree {
pub fn new() -> Self {
Self {
nodes: VecDeque::new(),
}
}
}
impl Default for AbstractSyntaxTree {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Node<T> {
pub inner: T,
pub position: Span,
}
impl<T> Node<T> {
pub fn new(inner: T, position: Span) -> Self {
Self { inner, position }
}
}
impl<T: Display> Display for Node<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Statement {
// Assignment does not return a value, but has a side effect on the context
Assignment {
identifier: Node<Identifier>,
operator: Node<AssignmentOperator>,
value: Box<Node<Statement>>,
},
AssignmentMut {
identifier: Node<Identifier>,
value: Box<Node<Statement>>,
},
// Statement blocks, delimited by curly braces
AsyncBlock(Vec<Node<Statement>>),
Block(Vec<Node<Statement>>),
// Logic, math and comparison expressions with two operands
BinaryOperation {
left: Box<Node<Statement>>,
operator: Node<BinaryOperator>,
right: Box<Node<Statement>>,
},
// Logic and math expressions with one operand
UnaryOperation {
operator: Node<UnaryOperator>,
operand: Box<Node<Statement>>,
},
// Type definitions
StructDefinition(StructDefinition),
// Function calls and type instantiation
BuiltInFunctionCall {
function: BuiltInFunction,
type_arguments: Option<Vec<Node<Statement>>>,
value_arguments: Option<Vec<Node<Statement>>>,
},
Invokation {
invokee: Box<Node<Statement>>,
type_arguments: Option<Vec<Node<Statement>>>,
value_arguments: Option<Vec<Node<Statement>>>,
},
FieldsStructInstantiation {
name: Node<Identifier>,
fields: Vec<(Node<Identifier>, Node<Statement>)>,
},
// Loops
While {
condition: Box<Node<Statement>>,
body: Box<Node<Statement>>,
},
// Control flow
If {
condition: Box<Node<Statement>>,
body: Box<Node<Statement>>,
},
IfElse {
condition: Box<Node<Statement>>,
if_body: Box<Node<Statement>>,
else_body: Box<Node<Statement>>,
},
IfElseIf {
condition: Box<Node<Statement>>,
if_body: Box<Node<Statement>>,
else_ifs: Vec<(Node<Statement>, Node<Statement>)>,
},
IfElseIfElse {
condition: Box<Node<Statement>>,
if_body: Box<Node<Statement>>,
else_ifs: Vec<(Node<Statement>, Node<Statement>)>,
else_body: Box<Node<Statement>>,
},
// Identifier
//
// Identifier statements in the syntax tree (i.e. Node<Statement>) are evaluated as
// expressions or reconstructed into a Node<Identifier> by the parser
Identifier(Identifier),
// Value collection expressions
List(Vec<Node<Statement>>),
Map(Vec<(Node<Identifier>, Node<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.
Nil(Box<Node<Statement>>),
}
impl Statement {
pub fn expected_type(&self, context: &Context) -> Option<Type> {
match self {
Statement::AsyncBlock(_) => None,
Statement::Assignment { .. } => None,
Statement::AssignmentMut { .. } => None,
Statement::Block(statements) => statements.last().unwrap().inner.expected_type(context),
Statement::BinaryOperation {
left,
operator,
right,
} => match operator.inner {
BinaryOperator::Add
| BinaryOperator::Divide
| BinaryOperator::Modulo
| BinaryOperator::Multiply
| BinaryOperator::Subtract => Some(left.inner.expected_type(context)?),
BinaryOperator::Equal
| BinaryOperator::Greater
| BinaryOperator::GreaterOrEqual
| BinaryOperator::Less
| BinaryOperator::LessOrEqual
| BinaryOperator::And
| BinaryOperator::Or => Some(Type::Boolean),
BinaryOperator::FieldAccess => {
let left_type = left.inner.expected_type(context)?;
if let Type::Map(properties) = left_type {
let key = match &right.inner {
Statement::Identifier(identifier) => identifier,
_ => return None,
};
properties.get(key).cloned()
} else {
None
}
}
BinaryOperator::ListIndex => {
let left_type = left.inner.expected_type(context)?;
if let Type::List { item_type, .. } = left_type {
Some(*item_type)
} else {
None
}
}
},
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, ..
} => function.inner.expected_type(context),
Statement::Identifier(identifier) => context.get_type(identifier),
Statement::If { .. } => None,
Statement::IfElse { if_body, .. } => if_body.inner.expected_type(context),
Statement::IfElseIf { .. } => None,
Statement::IfElseIfElse { if_body, .. } => if_body.inner.expected_type(context),
Statement::List(nodes) => {
let item_type = nodes.first().unwrap().inner.expected_type(context)?;
Some(Type::List {
item_type: Box::new(item_type),
length: nodes.len(),
})
}
Statement::Map(nodes) => {
let mut types = BTreeMap::new();
for (identifier, item) in nodes {
types.insert(identifier.inner.clone(), item.inner.expected_type(context)?);
}
Some(Type::Map(types))
}
Statement::Nil(_) => None,
Statement::UnaryOperation { operator, operand } => match operator.inner {
UnaryOperator::Negate => Some(operand.inner.expected_type(context)?),
UnaryOperator::Not => Some(Type::Boolean),
},
Statement::StructDefinition(_) => None,
Statement::While { .. } => None,
}
}
pub fn block_statements_mut(&mut self) -> Option<&mut Vec<Node<Statement>>> {
match self {
Statement::Block(statements) => Some(statements),
_ => None,
}
}
pub fn map_properties_mut(&mut self) -> Option<&mut Vec<(Node<Identifier>, Node<Statement>)>> {
match self {
Statement::Map(properties) => Some(properties),
_ => None,
}
}
}
impl Display for Statement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Statement::Assignment {
identifier,
operator,
value,
} => {
write!(f, "{identifier} {operator} {value}")
}
Statement::AssignmentMut { identifier, value } => {
write!(f, "mut {identifier} = {value}")
}
Statement::AsyncBlock(statements) => {
write!(f, "async {{ ")?;
for (i, statement) in statements.iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
write!(f, "{statement}")?;
}
write!(f, " }}")
}
Statement::Block(statements) => {
write!(f, "{{ ")?;
for (i, statement) in statements.iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
write!(f, "{statement}")?;
}
write!(f, " }}")
}
Statement::BinaryOperation {
left,
operator,
right,
} => {
let operator = match operator.inner {
BinaryOperator::FieldAccess => return write!(f, "{left}.{right}"),
BinaryOperator::ListIndex => return write!(f, "{left}[{right}]"),
BinaryOperator::Add => "+",
BinaryOperator::Divide => "/",
BinaryOperator::Equal => "==",
BinaryOperator::Greater => ">",
BinaryOperator::GreaterOrEqual => ">=",
BinaryOperator::Less => "<",
BinaryOperator::LessOrEqual => "<=",
BinaryOperator::Modulo => "%",
BinaryOperator::Multiply => "*",
BinaryOperator::Subtract => "-",
BinaryOperator::And => "&&",
BinaryOperator::Or => "||",
};
write!(f, "{left} {operator} {right}")
}
Statement::BuiltInFunctionCall {
function,
type_arguments: type_parameters,
value_arguments: value_parameters,
} => {
write!(f, "{function}")?;
if let Some(type_parameters) = type_parameters {
write!(f, "<")?;
for (i, type_parameter) in type_parameters.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{type_parameter}")?;
}
write!(f, ">")?;
}
write!(f, "(")?;
if let Some(value_parameters) = value_parameters {
for (i, value_parameter) in value_parameters.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{value_parameter}")?;
}
}
write!(f, ")")
}
Statement::Constant(value) => write!(f, "{value}"),
Statement::ConstantMut(value) => write!(f, "{value}"),
Statement::FieldsStructInstantiation { name, fields } => {
write!(f, "{name} {{ ")?;
for (i, (identifier, value)) in fields.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{identifier}: {value}")?;
}
write!(f, " }}")
}
Statement::Invokation {
invokee: function,
type_arguments: type_parameters,
value_arguments: value_parameters,
} => {
write!(f, "{function}")?;
if let Some(type_parameters) = type_parameters {
write!(f, "<")?;
for (i, type_parameter) in type_parameters.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{type_parameter}")?;
}
write!(f, ">")?;
}
write!(f, "(")?;
if let Some(value_parameters) = value_parameters {
for (i, value_parameter) in value_parameters.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{value_parameter}")?;
}
}
write!(f, ")")
}
Statement::Identifier(identifier) => write!(f, "{identifier}"),
Statement::If { condition, body } => {
write!(f, "if {condition} {body}")
}
Statement::IfElse {
condition,
if_body,
else_body,
} => {
write!(f, "if {condition} {if_body} else {else_body}")
}
Statement::IfElseIf {
condition,
if_body,
else_ifs,
} => {
write!(f, "if {condition} {if_body}")?;
for (condition, body) in else_ifs {
write!(f, " else if {condition} {body}")?;
}
Ok(())
}
Statement::IfElseIfElse {
condition,
if_body,
else_ifs,
else_body,
} => {
write!(f, "if {condition} {if_body}")?;
for (condition, body) in else_ifs {
write!(f, " else if {condition} {body}")?;
}
write!(f, " else {else_body}")
}
Statement::List(nodes) => {
write!(f, "[")?;
for (i, node) in nodes.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{node}")?;
}
write!(f, "]")
}
Statement::Map(nodes) => {
write!(f, "{{")?;
for (i, (identifier, node)) in nodes.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{identifier} = {node}")?;
}
write!(f, "}}")
}
Statement::Nil(node) => write!(f, "{node};"),
Statement::UnaryOperation { operator, operand } => {
let operator = match operator.inner {
UnaryOperator::Negate => "-",
UnaryOperator::Not => "!",
};
write!(f, "{operator}{operand}")
}
Statement::StructDefinition(struct_definition) => {
write!(f, "{struct_definition}")
}
Statement::While { condition, body } => {
write!(f, "while {condition} {body}")
}
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum AssignmentOperator {
Assign,
AddAssign,
SubtractAssign,
}
impl Display for AssignmentOperator {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let operator = match self {
AssignmentOperator::Assign => "=",
AssignmentOperator::AddAssign => "+=",
AssignmentOperator::SubtractAssign => "-=",
};
write!(f, "{operator}")
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum BinaryOperator {
// Accessors
FieldAccess,
ListIndex,
// Math
Add,
Divide,
Modulo,
Multiply,
Subtract,
// Comparison
Equal,
Greater,
GreaterOrEqual,
Less,
LessOrEqual,
// Logic
And,
Or,
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum UnaryOperator {
Negate,
Not,
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum StructDefinition {
Unit {
name: Node<Identifier>,
},
Tuple {
name: Node<Identifier>,
items: Vec<Node<Type>>,
},
Fields {
name: Node<Identifier>,
fields: Vec<(Node<Identifier>, Node<Type>)>,
},
}
impl Display for StructDefinition {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
StructDefinition::Unit { name } => write!(f, "struct {name}"),
StructDefinition::Tuple {
name,
items: fields,
} => {
write!(f, "struct {name} {{")?;
for (i, field) in fields.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{field}")?;
}
write!(f, "}}")
}
StructDefinition::Fields { name, fields } => {
write!(f, "struct {name} {{")?;
for (i, (field_name, field_type)) in fields.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{field_name}: {field_type}")?;
}
write!(f, "}}")
}
}
}
}

View File

@ -0,0 +1,526 @@
use std::{
cmp::Ordering,
fmt::{self, Display, Formatter},
};
use serde::{Deserialize, Serialize};
use crate::{Identifier, Span, Value};
use super::{Node, Statement};
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Expression {
WithBlock(Node<Box<ExpressionWithBlock>>),
WithoutBlock(Node<Box<ExpressionWithoutBlock>>),
}
impl Expression {
pub fn call_expression(call_expression: CallExpression, position: Span) -> Self {
Expression::WithoutBlock(Node::new(
Box::new(ExpressionWithoutBlock::Call(call_expression)),
position,
))
}
pub fn field_access(field_access: FieldAccess, position: Span) -> Self {
Expression::WithoutBlock(Node::new(
Box::new(ExpressionWithoutBlock::FieldAccess(field_access)),
position,
))
}
pub fn operator_expression(operator_expression: OperatorExpression, position: Span) -> Self {
Expression::WithoutBlock(Node::new(
Box::new(ExpressionWithoutBlock::Operator(operator_expression)),
position,
))
}
pub fn r#loop(r#loop: Loop, position: Span) -> Self {
Expression::WithBlock(Node::new(
Box::new(ExpressionWithBlock::Loop(r#loop)),
position,
))
}
pub fn block(block: Block, position: Span) -> Self {
Expression::WithBlock(Node::new(
Box::new(ExpressionWithBlock::Block(block)),
position,
))
}
pub fn grouped(expression: Expression, position: Span) -> Self {
Expression::WithoutBlock(Node::new(
Box::new(ExpressionWithoutBlock::Grouped(expression)),
position,
))
}
pub fn struct_expression(struct_expression: StructExpression, position: Span) -> Self {
Expression::WithoutBlock(Node::new(
Box::new(ExpressionWithoutBlock::Struct(struct_expression)),
position,
))
}
pub fn identifier_expression(identifier: Identifier, position: Span) -> Self {
Expression::WithoutBlock(Node::new(
Box::new(ExpressionWithoutBlock::Identifier(identifier)),
position,
))
}
pub fn list(list_expression: ListExpression, position: Span) -> Self {
Expression::WithoutBlock(Node::new(
Box::new(ExpressionWithoutBlock::List(list_expression)),
position,
))
}
pub fn list_index(list_index: ListIndex, position: Span) -> Self {
Expression::WithoutBlock(Node::new(
Box::new(ExpressionWithoutBlock::ListIndex(list_index)),
position,
))
}
pub fn r#if(r#if: If, position: Span) -> Self {
Expression::WithBlock(Node::new(Box::new(ExpressionWithBlock::If(r#if)), position))
}
pub fn literal(literal: LiteralExpression, position: Span) -> Self {
Expression::WithoutBlock(Node::new(
Box::new(ExpressionWithoutBlock::Literal(literal)),
position,
))
}
pub fn identifier(identifier: Identifier, position: Span) -> Self {
Expression::WithoutBlock(Node::new(
Box::new(ExpressionWithoutBlock::Identifier(identifier)),
position,
))
}
pub fn position(&self) -> Span {
match self {
Expression::WithBlock(expression_node) => expression_node.position,
Expression::WithoutBlock(expression_node) => expression_node.position,
}
}
}
impl Display for Expression {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Expression::WithBlock(expression) => write!(f, "{}", expression),
Expression::WithoutBlock(expression) => write!(f, "{}", expression),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum ExpressionWithBlock {
Block(Block),
Loop(Loop),
If(If),
}
impl Display for ExpressionWithBlock {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
ExpressionWithBlock::Block(block) => write!(f, "{}", block),
ExpressionWithBlock::Loop(r#loop) => write!(f, "{}", r#loop),
ExpressionWithBlock::If(r#if) => write!(f, "{}", r#if),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum ExpressionWithoutBlock {
Call(CallExpression),
List(ListExpression),
Literal(LiteralExpression),
Identifier(Identifier),
Operator(OperatorExpression),
Struct(StructExpression),
Grouped(Expression),
FieldAccess(FieldAccess),
ListIndex(ListIndex),
}
impl Display for ExpressionWithoutBlock {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
ExpressionWithoutBlock::Call(call_expression) => write!(f, "{}", call_expression),
ExpressionWithoutBlock::List(list) => write!(f, "{}", list),
ExpressionWithoutBlock::Literal(literal) => write!(f, "{}", literal),
ExpressionWithoutBlock::Identifier(identifier) => write!(f, "{}", identifier),
ExpressionWithoutBlock::Operator(expression) => write!(f, "{}", expression),
ExpressionWithoutBlock::Struct(struct_expression) => write!(f, "{}", struct_expression),
ExpressionWithoutBlock::Grouped(expression) => write!(f, "({})", expression),
ExpressionWithoutBlock::FieldAccess(field_access) => write!(f, "{}", field_access),
ExpressionWithoutBlock::ListIndex(list_index) => write!(f, "{}", list_index),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct ListIndex {
pub list: Expression,
pub index: Expression,
}
impl Display for ListIndex {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}[{}]", self.list, self.index)
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct CallExpression {
pub function: Expression,
pub arguments: Vec<Expression>,
}
impl Display for CallExpression {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}(", self.function)?;
for (index, argument) in self.arguments.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{}", argument)?;
}
write!(f, ")")
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct FieldAccess {
pub container: Expression,
pub field: Node<Identifier>,
}
impl Display for FieldAccess {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}.{}", self.container, self.field)
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum ListExpression {
AutoFill {
repeat_operand: Expression,
length_operand: Expression,
},
Ordered(Vec<Expression>),
}
impl Display for ListExpression {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
ListExpression::AutoFill {
repeat_operand,
length_operand,
} => {
write!(f, "[{};{}]", repeat_operand, length_operand)
}
ListExpression::Ordered(expressions) => {
write!(f, "[")?;
for (index, expression) in expressions.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{}", expression)?;
}
write!(f, "]")
}
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum LiteralExpression {
Boolean(bool),
Float(f64),
Integer(i64),
Range(i64, i64),
String(String),
Value(Value),
}
impl Display for LiteralExpression {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
LiteralExpression::Boolean(boolean) => write!(f, "{}", boolean),
LiteralExpression::Float(float) => write!(f, "{}", float),
LiteralExpression::Integer(integer) => write!(f, "{}", integer),
LiteralExpression::Range(start, end) => write!(f, "{}..{}", start, end),
LiteralExpression::String(string) => write!(f, "{}", string),
LiteralExpression::Value(value) => write!(f, "{}", value),
}
}
}
impl Eq for LiteralExpression {}
impl Ord for LiteralExpression {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(LiteralExpression::Boolean(left), LiteralExpression::Boolean(right)) => {
left.cmp(right)
}
(LiteralExpression::Float(left), LiteralExpression::Float(right)) => {
left.to_bits().cmp(&right.to_bits())
}
(LiteralExpression::Integer(left), LiteralExpression::Integer(right)) => {
left.cmp(right)
}
(LiteralExpression::String(left), LiteralExpression::String(right)) => left.cmp(right),
(LiteralExpression::Value(left), LiteralExpression::Value(right)) => left.cmp(right),
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum OperatorExpression {
Assignment {
assignee: Expression,
value: Expression,
},
CompoundAssignment {
assignee: Expression,
operator: Node<MathOperator>,
value: Expression,
},
ErrorPropagation(Expression),
Negation(Expression),
Not(Expression),
Math {
left: Expression,
operator: Node<MathOperator>,
right: Expression,
},
Logic {
left: Expression,
operator: LogicOperator,
right: Expression,
},
}
impl Display for OperatorExpression {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
OperatorExpression::Assignment { assignee, value } => {
write!(f, "{} = {}", assignee, value)
}
OperatorExpression::CompoundAssignment {
assignee,
operator,
value,
} => write!(f, "{} {}= {}", assignee, operator, value),
OperatorExpression::ErrorPropagation(expression) => write!(f, "{}?", expression),
OperatorExpression::Negation(expression) => write!(f, "-{}", expression),
OperatorExpression::Not(expression) => write!(f, "!{}", expression),
OperatorExpression::Math {
left,
operator,
right,
} => {
write!(f, "{} {} {}", left, operator, right)
}
OperatorExpression::Logic {
left,
operator,
right,
} => {
write!(f, "{} {} {}", left, operator, right)
}
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum MathOperator {
Add,
Subtract,
Multiply,
Divide,
Modulo,
}
impl Display for MathOperator {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let operator = match self {
MathOperator::Add => "+",
MathOperator::Subtract => "-",
MathOperator::Multiply => "*",
MathOperator::Divide => "/",
MathOperator::Modulo => "%",
};
write!(f, "{}", operator)
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum LogicOperator {
And,
Or,
}
impl Display for LogicOperator {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let operator = match self {
LogicOperator::And => "&&",
LogicOperator::Or => "||",
};
write!(f, "{}", operator)
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct If {
pub condition: Expression,
pub if_block: Node<Block>,
pub else_block: Option<Node<Block>>,
}
impl Display for If {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "if {} {}", self.condition, self.if_block)?;
if let Some(else_block) = &self.else_block {
write!(f, " else {}", else_block)?;
}
Ok(())
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Block {
Async(Vec<Statement>),
Sync(Vec<Statement>),
}
impl Display for Block {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Block::Async(statements) => {
writeln!(f, "async {{ ")?;
for (i, statement) in statements.iter().enumerate() {
if i > 0 {
writeln!(f, " ")?;
}
writeln!(f, "{}", statement)?;
}
write!(f, " }}")
}
Block::Sync(statements) => {
writeln!(f, "{{ ")?;
for (i, statement) in statements.iter().enumerate() {
if i > 0 {
writeln!(f, " ")?;
}
writeln!(f, "{}", statement)?;
}
write!(f, " }}")
}
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Loop {
Infinite(Block),
While {
condition: Expression,
block: Node<Block>,
},
For {
identifier: Node<Identifier>,
iterator: Expression,
block: Block,
},
}
impl Display for Loop {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Loop::Infinite(block) => write!(f, "loop {}", block),
Loop::While { condition, block } => write!(f, "while {} {}", condition, block),
Loop::For {
identifier,
iterator,
block,
} => write!(f, "for {} in {} {}", identifier, iterator, block),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum StructExpression {
Unit {
name: Node<Identifier>,
},
Tuple {
name: Node<Identifier>,
items: Vec<Expression>,
},
Fields {
name: Node<Identifier>,
fields: Vec<(Node<Identifier>, Expression)>,
},
}
impl Display for StructExpression {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
StructExpression::Unit { name } => write!(f, "{}", name),
StructExpression::Tuple { name, items } => {
write!(f, "{}(", name)?;
for (index, item) in items.iter().enumerate() {
write!(f, "{}", item)?;
if index < items.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, ")")
}
StructExpression::Fields { name, fields } => {
write!(f, "{} {{", name)?;
for (index, (field, value)) in fields.iter().enumerate() {
write!(f, "{}: {}", field, value)?;
if index < fields.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "}}")
}
}
}
}

View File

@ -0,0 +1,145 @@
//! In-memory representation of a Dust program.
mod expression;
pub use expression::*;
use std::{
collections::VecDeque,
fmt::{self, Display, Formatter},
};
use serde::{Deserialize, Serialize};
use crate::{BuiltInFunction, Context, Identifier, Span, Type, Value};
/// In-memory representation of a Dust program.
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct AbstractSyntaxTree {
pub statements: VecDeque<Statement>,
}
impl AbstractSyntaxTree {
pub fn new() -> Self {
Self {
statements: VecDeque::new(),
}
}
}
impl Default for AbstractSyntaxTree {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Node<T> {
pub inner: T,
pub position: Span,
}
impl<T> Node<T> {
pub fn new(inner: T, position: Span) -> Self {
Self { inner, position }
}
}
impl<T: Display> Display for Node<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Statement {
Expression(Expression),
ExpressionNullified(Node<Expression>),
Let(Node<Let>),
StructDefinition(Node<StructDefinition>),
}
impl Statement {
pub fn position(&self) -> Span {
match self {
Statement::Expression(expression) => expression.position(),
Statement::ExpressionNullified(expression_node) => expression_node.position,
Statement::Let(r#let) => r#let.position,
Statement::StructDefinition(definition) => definition.position,
}
}
}
impl Display for Statement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Statement::Expression(expression) => write!(f, "{}", expression),
Statement::ExpressionNullified(expression) => write!(f, "{}", expression),
Statement::Let(r#let) => write!(f, "{}", r#let),
Statement::StructDefinition(definition) => write!(f, "{}", definition),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Let {
pub identifier: Node<Identifier>,
pub value: Node<Expression>,
}
impl Display for Let {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "let {} = {}", self.identifier, self.value)
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum StructDefinition {
Unit {
name: Node<Identifier>,
},
Tuple {
name: Node<Identifier>,
items: Vec<Node<Type>>,
},
Fields {
name: Node<Identifier>,
fields: Vec<(Node<Identifier>, Node<Type>)>,
},
}
impl Display for StructDefinition {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
StructDefinition::Unit { name } => write!(f, "struct {name}"),
StructDefinition::Tuple {
name,
items: fields,
} => {
write!(f, "struct {name} {{")?;
for (i, field) in fields.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{field}")?;
}
write!(f, "}}")
}
StructDefinition::Fields { name, fields } => {
write!(f, "struct {name} {{")?;
for (i, (field_name, field_type)) in fields.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{field_name}: {field_type}")?;
}
write!(f, "}}")
}
}
}
}

View File

@ -66,512 +66,14 @@ impl<'a> Analyzer<'a> {
}
pub fn analyze(&mut self) -> Result<(), AnalyzerError> {
for node in &self.abstract_tree.nodes {
for node in &self.abstract_tree.statements {
self.analyze_statement(node)?;
}
Ok(())
}
fn analyze_statement(&mut self, node: &Node<Statement>) -> Result<(), AnalyzerError> {
match &node.inner {
Statement::Assignment {
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::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)?;
}
}
Statement::BinaryOperation {
left,
operator,
right,
} => {
if let BinaryOperator::FieldAccess = operator.inner {
self.analyze_statement(left)?;
if let Statement::Identifier(_) = right.inner {
// Do not expect a value for property accessors
} else {
self.analyze_statement(right)?;
}
let left_type = left.inner.expected_type(self.context);
let right_type = right.inner.expected_type(self.context);
if let Some(Type::Map { .. }) = left_type {
if let Some(Type::String) = right_type {
// Allow indexing maps with strings
} else if let Statement::Identifier(_) = right.inner {
// Allow indexing maps with identifiers
} else {
return Err(AnalyzerError::ExpectedIdentifierOrString {
actual: right.as_ref().clone(),
});
}
} else {
return Err(AnalyzerError::ExpectedMap {
actual: left.as_ref().clone(),
});
}
// If the accessor is an identifier, check if it is a valid field
if let Statement::Identifier(identifier) = &right.inner {
if let Some(Type::Map(fields)) = &left_type {
if !fields.contains_key(identifier) {
return Err(AnalyzerError::UndefinedField {
identifier: right.as_ref().clone(),
statement: left.as_ref().clone(),
});
}
}
}
// If the accessor is a constant, check if it is a valid field
if let Statement::Constant(value) = &right.inner {
if let Some(field_name) = value.as_string() {
if let Some(Type::Map(fields)) = left_type {
if !fields.contains_key(&Identifier::new(field_name)) {
return Err(AnalyzerError::UndefinedField {
identifier: right.as_ref().clone(),
statement: left.as_ref().clone(),
});
}
}
}
}
return Ok(());
}
if let BinaryOperator::ListIndex = operator.inner {
self.analyze_statement(left)?;
self.analyze_statement(right)?;
if let Some(Type::List { length, .. }) = left.inner.expected_type(self.context)
{
let index_type = right.inner.expected_type(self.context);
if let Some(Type::Integer | Type::Range) = index_type {
// List and index are valid
} else {
return Err(AnalyzerError::ExpectedIntegerOrRange {
actual: right.as_ref().clone(),
});
}
// If the index is a constant, check if it is out of bounds
if let Statement::Constant(value) = &right.inner {
if let Some(index_value) = value.as_integer() {
let index_value = index_value as usize;
if index_value >= length {
return Err(AnalyzerError::IndexOutOfBounds {
list: left.as_ref().clone(),
index: right.as_ref().clone(),
index_value,
length,
});
}
}
}
} else {
return Err(AnalyzerError::ExpectedList {
actual: left.as_ref().clone(),
});
}
return Ok(());
}
self.analyze_statement(left)?;
self.analyze_statement(right)?;
let left_type = left.inner.expected_type(self.context);
let right_type = right.inner.expected_type(self.context);
if let BinaryOperator::Add
| BinaryOperator::Subtract
| BinaryOperator::Multiply
| BinaryOperator::Divide
| BinaryOperator::Greater
| BinaryOperator::GreaterOrEqual
| BinaryOperator::Less
| BinaryOperator::LessOrEqual = operator.inner
{
if let Some(expected_type) = left_type {
if let Some(actual_type) = right_type {
expected_type.check(&actual_type).map_err(|conflict| {
AnalyzerError::TypeConflict {
actual_statement: right.as_ref().clone(),
actual_type: conflict.actual,
expected: conflict.expected,
}
})?;
} else {
return Err(AnalyzerError::ExpectedValue {
actual: right.as_ref().clone(),
});
}
} else {
return Err(AnalyzerError::ExpectedValue {
actual: left.as_ref().clone(),
});
}
}
}
Statement::Block(statements) => {
for statement in statements {
self.analyze_statement(statement)?;
}
}
Statement::BuiltInFunctionCall {
function,
value_arguments,
..
} => {
let value_parameters = function.value_parameters();
if let Some(arguments) = value_arguments {
for argument in arguments {
self.analyze_statement(argument)?;
}
if arguments.len() != value_parameters.len() {
return Err(AnalyzerError::ExpectedValueArgumentCount {
expected: value_parameters.len(),
actual: arguments.len(),
position: node.position,
});
}
for ((_identifier, parameter_type), argument) in
value_parameters.iter().zip(arguments)
{
let argument_type_option = argument.inner.expected_type(self.context);
if let Some(argument_type) = argument_type_option {
parameter_type.check(&argument_type).map_err(|conflict| {
AnalyzerError::TypeConflict {
actual_statement: argument.clone(),
actual_type: conflict.actual,
expected: parameter_type.clone(),
}
})?;
} else {
return Err(AnalyzerError::ExpectedValue {
actual: argument.clone(),
});
}
}
if arguments.is_empty() && !value_parameters.is_empty() {
return Err(AnalyzerError::ExpectedValueArgumentCount {
expected: value_parameters.len(),
actual: 0,
position: node.position,
});
}
} else if !value_parameters.is_empty() {
return Err(AnalyzerError::ExpectedValueArgumentCount {
expected: value_parameters.len(),
actual: 0,
position: node.position,
});
}
}
Statement::Constant(_) => {}
Statement::ConstantMut(_) => {}
Statement::FieldsStructInstantiation {
name,
fields: field_arguments,
} => {
let expected_type = self.context.get_type(&name.inner);
if let Some(Type::Struct(StructType::Fields { fields, .. })) = expected_type {
for ((_, expected_type), (_, argument)) in
fields.iter().zip(field_arguments.iter())
{
let actual_type = argument.inner.expected_type(self.context);
if let Some(actual_type) = actual_type {
expected_type.check(&actual_type).map_err(|conflict| {
AnalyzerError::TypeConflict {
actual_statement: argument.clone(),
actual_type: conflict.actual,
expected: conflict.expected,
}
})?;
} else {
return Err(AnalyzerError::ExpectedValue {
actual: argument.clone(),
});
}
}
}
}
Statement::Invokation {
invokee,
value_arguments,
..
} => {
self.analyze_statement(invokee)?;
let invokee_type = invokee.inner.expected_type(self.context);
if let Some(arguments) = value_arguments {
for argument in arguments {
self.analyze_statement(argument)?;
}
if let Some(Type::Struct(struct_type)) = invokee_type {
match struct_type {
StructType::Unit { .. } => todo!(),
StructType::Tuple { fields, .. } => {
for (expected_type, argument) in fields.iter().zip(arguments.iter())
{
let actual_type = argument.inner.expected_type(self.context);
if let Some(actual_type) = actual_type {
expected_type.check(&actual_type).map_err(|conflict| {
AnalyzerError::TypeConflict {
actual_statement: argument.clone(),
actual_type: conflict.actual,
expected: expected_type.clone(),
}
})?;
} else {
return Err(AnalyzerError::ExpectedValue {
actual: argument.clone(),
});
}
}
}
StructType::Fields { .. } => todo!(),
}
}
}
}
Statement::Identifier(identifier) => {
let exists = self.context.update_last_position(identifier, node.position);
if !exists {
return Err(AnalyzerError::UndefinedVariable {
identifier: node.clone(),
});
}
}
Statement::If { condition, body } => {
self.analyze_statement(condition)?;
if let Some(Type::Boolean) = condition.inner.expected_type(self.context) {
// Condition is valid
} else {
return Err(AnalyzerError::ExpectedBoolean {
actual: condition.as_ref().clone(),
});
}
self.analyze_statement(body)?;
}
Statement::IfElse {
condition,
if_body,
else_body,
} => {
self.analyze_statement(condition)?;
if let Some(Type::Boolean) = condition.inner.expected_type(self.context) {
// Condition is valid
} else {
return Err(AnalyzerError::ExpectedBoolean {
actual: condition.as_ref().clone(),
});
}
self.analyze_statement(if_body)?;
self.analyze_statement(else_body)?;
}
Statement::IfElseIf {
condition,
if_body,
else_ifs,
} => {
self.analyze_statement(condition)?;
if let Some(Type::Boolean) = condition.inner.expected_type(self.context) {
// Condition is valid
} else {
return Err(AnalyzerError::ExpectedBoolean {
actual: condition.as_ref().clone(),
});
}
self.analyze_statement(if_body)?;
for (condition, body) in else_ifs {
self.analyze_statement(condition)?;
if let Some(Type::Boolean) = condition.inner.expected_type(self.context) {
// Condition is valid
} else {
return Err(AnalyzerError::ExpectedBoolean {
actual: condition.clone(),
});
}
self.analyze_statement(body)?;
}
}
Statement::IfElseIfElse {
condition,
if_body,
else_ifs,
else_body,
} => {
self.analyze_statement(condition)?;
if let Some(Type::Boolean) = condition.inner.expected_type(self.context) {
// Condition is valid
} else {
return Err(AnalyzerError::ExpectedBoolean {
actual: condition.as_ref().clone(),
});
}
self.analyze_statement(if_body)?;
for (condition, body) in else_ifs {
self.analyze_statement(condition)?;
if let Some(Type::Boolean) = condition.inner.expected_type(self.context) {
// Condition is valid
} else {
return Err(AnalyzerError::ExpectedBoolean {
actual: condition.clone(),
});
}
self.analyze_statement(body)?;
}
self.analyze_statement(else_body)?;
}
Statement::List(statements) => {
for statement in statements {
self.analyze_statement(statement)?;
}
}
Statement::Map(properties) => {
for (_key, value_node) in properties {
self.analyze_statement(value_node)?;
}
}
Statement::Nil(node) => {
self.analyze_statement(node)?;
}
Statement::StructDefinition(struct_definition) => {
let (name, r#type) = match struct_definition {
StructDefinition::Unit { name } => (
name.inner.clone(),
Type::Struct(StructType::Unit {
name: name.inner.clone(),
}),
),
StructDefinition::Tuple {
name,
items: fields,
} => (
name.inner.clone(),
Type::Struct(StructType::Tuple {
name: name.inner.clone(),
fields: fields
.iter()
.map(|type_node| type_node.inner.clone())
.collect(),
}),
),
StructDefinition::Fields { name, fields } => (
name.inner.clone(),
Type::Struct(StructType::Fields {
name: name.inner.clone(),
fields: fields
.iter()
.map(|(identifier, r#type)| {
(identifier.inner.clone(), r#type.inner.clone())
})
.collect(),
}),
),
};
self.context.set_type(name, r#type, node.position);
}
Statement::UnaryOperation { operator, operand } => {
self.analyze_statement(operand)?;
if let UnaryOperator::Negate = operator.inner {
if let Some(Type::Integer | Type::Float | Type::Number) =
operand.inner.expected_type(self.context)
{
// Operand is valid
} else {
return Err(AnalyzerError::ExpectedBoolean {
actual: operand.as_ref().clone(),
});
}
}
if let UnaryOperator::Not = operator.inner {
if let Some(Type::Boolean) = operand.inner.expected_type(self.context) {
// Operand is valid
} else {
return Err(AnalyzerError::ExpectedBoolean {
actual: operand.as_ref().clone(),
});
}
}
}
Statement::While { condition, body } => {
self.analyze_statement(condition)?;
self.analyze_statement(body)?;
if let Some(Type::Boolean) = condition.inner.expected_type(self.context) {
} else {
return Err(AnalyzerError::ExpectedBoolean {
actual: condition.as_ref().clone(),
});
}
}
}
fn analyze_statement(&mut self, _: &Node<Statement>) -> Result<(), AnalyzerError> {
Ok(())
}
}
@ -729,10 +231,70 @@ impl Display for AnalyzerError {
#[cfg(test)]
mod tests {
use crate::{Identifier, Value};
use crate::{AssignmentOperator, Identifier, Value};
use super::*;
#[test]
fn add_assign_wrong_type() {
let source = "
a = 1
a += 1.0
";
assert_eq!(
analyze(source),
Err(DustError::AnalyzerError {
analyzer_error: AnalyzerError::TypeConflict {
actual_statement: Node::new(
Statement::Assignment {
identifier: Node::new(Identifier::new("a"), (31, 32)),
operator: Node::new(AssignmentOperator::AddAssign, (33, 35)),
value: Box::new(Node::new(
Statement::Constant(Value::float(1.0)),
(38, 41)
))
},
(31, 32)
),
actual_type: Type::Integer,
expected: Type::Float
},
source
})
);
}
#[test]
fn subtract_assign_wrong_type() {
let source = "
a = 1
a -= 1.0
";
assert_eq!(
analyze(source),
Err(DustError::AnalyzerError {
analyzer_error: AnalyzerError::TypeConflict {
actual_statement: Node::new(
Statement::Assignment {
identifier: Node::new(Identifier::new("a"), (31, 32)),
operator: Node::new(AssignmentOperator::SubtractAssign, (33, 37)),
value: Box::new(Node::new(
Statement::Constant(Value::float(1.0)),
(40, 43)
))
},
(31, 32)
),
actual_type: Type::Integer,
expected: Type::Float
},
source
})
);
}
#[test]
fn tuple_struct_with_wrong_field_types() {
let source = "

File diff suppressed because it is too large Load Diff

View File

@ -389,6 +389,42 @@ impl Value {
Err(ValueError::CannotSubtract(self.clone(), other.clone()))
}
pub fn subtract_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_sub(*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_sub(*right);
return Ok(());
}
_ => {}
}
}
(Value::Immutable(_), _) => {
return Err(ValueError::CannotMutate(self.clone()));
}
}
Err(ValueError::CannotSubtract(self.clone(), other.clone()))
}
pub fn multiply(&self, other: &Value) -> Result<Value, ValueError> {
match (self, other) {
(Value::Immutable(left), Value::Immutable(right)) => {
@ -1424,7 +1460,7 @@ impl Display for Function {
write!(f, ") {{")?;
for statement in &self.body.nodes {
for statement in &self.body.statements {
write!(f, "{}", statement)?;
}

View File

@ -88,7 +88,7 @@ impl Vm {
let mut previous_position = (0, 0);
let mut previous_value = None;
while let Some(statement) = self.abstract_tree.nodes.pop_front() {
while let Some(statement) = self.abstract_tree.statements.pop_front() {
let new_position = statement.position;
previous_value = self.run_statement(statement)?;
@ -118,7 +118,18 @@ impl Vm {
return Err(VmError::ExpectedValue { position });
};
if let Some(existing_value) = self.context.get_value(&identifier.inner) {
if existing_value.is_mutable() {
existing_value.mutate(&value);
} else {
return Err(VmError::CannotMutate {
value: existing_value,
position: identifier.position,
});
}
} else {
self.context.set_value(identifier.inner, value);
}
Ok(None)
}
@ -146,14 +157,10 @@ impl Vm {
}
})?;
} 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);
return Err(VmError::CannotMutate {
value: left_value,
position: identifier.position,
});
}
Ok(None)
@ -173,14 +180,20 @@ impl Vm {
position: value_position,
});
};
let new_value = left_value.subtract(&right_value).map_err(|value_error| {
VmError::ValueError {
if left_value.is_mutable() {
left_value
.subtract_mut(&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);
} else {
return Err(VmError::CannotMutate {
value: left_value,
position: identifier.position,
});
}
Ok(None)
}
@ -748,6 +761,10 @@ pub enum VmError {
error: BuiltInFunctionError,
position: Span,
},
CannotMutate {
value: Value,
position: Span,
},
ExpectedBoolean {
position: Span,
},
@ -795,6 +812,7 @@ impl VmError {
match self {
Self::ParseError(parse_error) => parse_error.position(),
Self::ValueError { position, .. } => *position,
Self::CannotMutate { position, .. } => *position,
Self::BuiltInFunctionError { position, .. } => *position,
Self::ExpectedBoolean { position } => *position,
Self::ExpectedIdentifier { position } => *position,
@ -825,6 +843,9 @@ impl Display for VmError {
match self {
Self::ParseError(parse_error) => write!(f, "{}", parse_error),
Self::ValueError { error, .. } => write!(f, "{}", error),
Self::CannotMutate { value, .. } => {
write!(f, "Cannot mutate immutable value {}", value)
}
Self::BuiltInFunctionError { error, .. } => {
write!(f, "{}", error)
}
@ -834,7 +855,7 @@ impl Display for VmError {
Self::ExpectedFunction { actual, position } => {
write!(
f,
"Expected a function, but got: {} at position: {:?}",
"Expected a function, but got {} at position: {:?}",
actual, position
)
}
@ -908,7 +929,7 @@ mod tests {
#[test]
fn async_block() {
let input = "x = 1; async { x += 1; x -= 1; } x";
let input = "mut x = 1; async { x += 1; x -= 1; } x";
assert!(run(input).unwrap().unwrap().as_integer().is_some());
}
@ -1107,21 +1128,21 @@ mod tests {
#[test]
fn while_loop() {
let input = "x = 0; while x < 5 { x += 1; } x";
let input = "mut x = 0; while x < 5 { x += 1; } x";
assert_eq!(run(input), Ok(Some(Value::integer(5))));
}
#[test]
fn subtract_assign() {
let input = "x = 1; x -= 1; x";
let input = "mut x = 1; x -= 1; x";
assert_eq!(run(input), Ok(Some(Value::integer(0))));
}
#[test]
fn add_assign() {
let input = "x = 1; x += 1; x";
let input = "mut x = 1; x += 1; x";
assert_eq!(run(input), Ok(Some(Value::integer(2))));
}