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> {
|
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)?;
|
self.analyze_statement(node)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_statement(&mut self, node: &Node<Statement>) -> Result<(), AnalyzerError> {
|
fn analyze_statement(&mut self, _: &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(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -729,10 +231,70 @@ impl Display for AnalyzerError {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{Identifier, Value};
|
use crate::{AssignmentOperator, Identifier, Value};
|
||||||
|
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn tuple_struct_with_wrong_field_types() {
|
fn tuple_struct_with_wrong_field_types() {
|
||||||
let source = "
|
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()))
|
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> {
|
pub fn multiply(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Value::Immutable(left), Value::Immutable(right)) => {
|
(Value::Immutable(left), Value::Immutable(right)) => {
|
||||||
@ -1424,7 +1460,7 @@ impl Display for Function {
|
|||||||
|
|
||||||
write!(f, ") {{")?;
|
write!(f, ") {{")?;
|
||||||
|
|
||||||
for statement in &self.body.nodes {
|
for statement in &self.body.statements {
|
||||||
write!(f, "{}", statement)?;
|
write!(f, "{}", statement)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ impl Vm {
|
|||||||
let mut previous_position = (0, 0);
|
let mut previous_position = (0, 0);
|
||||||
let mut previous_value = None;
|
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;
|
let new_position = statement.position;
|
||||||
|
|
||||||
previous_value = self.run_statement(statement)?;
|
previous_value = self.run_statement(statement)?;
|
||||||
@ -118,7 +118,18 @@ impl Vm {
|
|||||||
return Err(VmError::ExpectedValue { position });
|
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);
|
self.context.set_value(identifier.inner, value);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
@ -146,14 +157,10 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
} else {
|
} else {
|
||||||
let new_value = left_value.add(&right_value).map_err(|value_error| {
|
return Err(VmError::CannotMutate {
|
||||||
VmError::ValueError {
|
value: left_value,
|
||||||
error: value_error,
|
position: identifier.position,
|
||||||
position: (identifier.position.0, value_position.1),
|
});
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
self.context.set_value(identifier.inner, new_value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
@ -173,14 +180,20 @@ impl Vm {
|
|||||||
position: value_position,
|
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,
|
error: value_error,
|
||||||
position: (identifier.position.0, value_position.1),
|
position: (identifier.position.0, value_position.1),
|
||||||
}
|
|
||||||
})?;
|
})?;
|
||||||
|
} else {
|
||||||
self.context.set_value(identifier.inner, new_value);
|
return Err(VmError::CannotMutate {
|
||||||
|
value: left_value,
|
||||||
|
position: identifier.position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
@ -748,6 +761,10 @@ pub enum VmError {
|
|||||||
error: BuiltInFunctionError,
|
error: BuiltInFunctionError,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
CannotMutate {
|
||||||
|
value: Value,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
ExpectedBoolean {
|
ExpectedBoolean {
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
@ -795,6 +812,7 @@ impl VmError {
|
|||||||
match self {
|
match self {
|
||||||
Self::ParseError(parse_error) => parse_error.position(),
|
Self::ParseError(parse_error) => parse_error.position(),
|
||||||
Self::ValueError { position, .. } => *position,
|
Self::ValueError { position, .. } => *position,
|
||||||
|
Self::CannotMutate { position, .. } => *position,
|
||||||
Self::BuiltInFunctionError { position, .. } => *position,
|
Self::BuiltInFunctionError { position, .. } => *position,
|
||||||
Self::ExpectedBoolean { position } => *position,
|
Self::ExpectedBoolean { position } => *position,
|
||||||
Self::ExpectedIdentifier { position } => *position,
|
Self::ExpectedIdentifier { position } => *position,
|
||||||
@ -825,6 +843,9 @@ impl Display for VmError {
|
|||||||
match self {
|
match self {
|
||||||
Self::ParseError(parse_error) => write!(f, "{}", parse_error),
|
Self::ParseError(parse_error) => write!(f, "{}", parse_error),
|
||||||
Self::ValueError { error, .. } => write!(f, "{}", error),
|
Self::ValueError { error, .. } => write!(f, "{}", error),
|
||||||
|
Self::CannotMutate { value, .. } => {
|
||||||
|
write!(f, "Cannot mutate immutable value {}", value)
|
||||||
|
}
|
||||||
Self::BuiltInFunctionError { error, .. } => {
|
Self::BuiltInFunctionError { error, .. } => {
|
||||||
write!(f, "{}", error)
|
write!(f, "{}", error)
|
||||||
}
|
}
|
||||||
@ -834,7 +855,7 @@ impl Display for VmError {
|
|||||||
Self::ExpectedFunction { actual, position } => {
|
Self::ExpectedFunction { actual, position } => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Expected a function, but got: {} at position: {:?}",
|
"Expected a function, but got {} at position: {:?}",
|
||||||
actual, position
|
actual, position
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -908,7 +929,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn async_block() {
|
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());
|
assert!(run(input).unwrap().unwrap().as_integer().is_some());
|
||||||
}
|
}
|
||||||
@ -1107,21 +1128,21 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn while_loop() {
|
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))));
|
assert_eq!(run(input), Ok(Some(Value::integer(5))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn subtract_assign() {
|
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))));
|
assert_eq!(run(input), Ok(Some(Value::integer(0))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_assign() {
|
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))));
|
assert_eq!(run(input), Ok(Some(Value::integer(2))));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user