Begin AST overhaul
This commit is contained in:
parent
e7b5390a55
commit
43b2393d8a
@ -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, "}}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
526
dust-lang/src/abstract_tree/expression.rs
Normal file
526
dust-lang/src/abstract_tree/expression.rs
Normal 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, "}}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
145
dust-lang/src/abstract_tree/mod.rs
Normal file
145
dust-lang/src/abstract_tree/mod.rs
Normal 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, "}}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
@ -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)?;
|
||||
}
|
||||
|
||||
|
@ -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))));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user