Remimplement features
This commit is contained in:
parent
0fd19a623d
commit
e3d821a1c3
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -165,6 +165,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"dust-lang",
|
||||
"env_logger",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -16,8 +16,7 @@ use crate::{
|
||||
MapExpression, Node, OperatorExpression, RangeExpression, Span, Statement,
|
||||
StructDefinition, StructExpression, TupleAccessExpression,
|
||||
},
|
||||
parse, Context, DustError, Expression, FieldsStructType, Identifier, StructType, TupleType,
|
||||
Type,
|
||||
parse, Context, DustError, Expression, Identifier, StructType, Type,
|
||||
};
|
||||
|
||||
/// Analyzes the abstract syntax tree for errors.
|
||||
@ -85,62 +84,44 @@ impl<'a> Analyzer<'a> {
|
||||
self.analyze_expression(&expression_node.inner)?;
|
||||
}
|
||||
Statement::Let(let_statement) => match &let_statement.inner {
|
||||
LetStatement::Let { identifier, value } => {
|
||||
let type_option = value.return_type(self.context);
|
||||
LetStatement::Let { identifier, value }
|
||||
| LetStatement::LetMut { identifier, value } => {
|
||||
self.analyze_expression(value)?;
|
||||
|
||||
if let Some(r#type) = type_option {
|
||||
self.context.set_type(
|
||||
let r#type = value.return_type(self.context);
|
||||
|
||||
if let Some(r#type) = r#type {
|
||||
self.context.set_variable_type(
|
||||
identifier.inner.clone(),
|
||||
r#type,
|
||||
identifier.position,
|
||||
);
|
||||
} else {
|
||||
return Err(AnalysisError::ExpectedValue {
|
||||
actual: statement.clone(),
|
||||
return Err(AnalysisError::ExpectedValueFromExpression {
|
||||
actual: value.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
self.analyze_expression(value)?;
|
||||
}
|
||||
LetStatement::LetMut { identifier, value } => {
|
||||
let type_option = value.return_type(self.context);
|
||||
|
||||
if let Some(r#type) = type_option {
|
||||
self.context.set_type(
|
||||
identifier.inner.clone(),
|
||||
r#type,
|
||||
identifier.position,
|
||||
);
|
||||
} else {
|
||||
return Err(AnalysisError::ExpectedValue {
|
||||
actual: statement.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
self.analyze_expression(value)?;
|
||||
}
|
||||
LetStatement::LetType {
|
||||
identifier,
|
||||
r#type,
|
||||
value,
|
||||
} => todo!(),
|
||||
LetStatement::LetMutType {
|
||||
identifier,
|
||||
r#type,
|
||||
value,
|
||||
} => todo!(),
|
||||
LetStatement::LetType { .. } => todo!(),
|
||||
LetStatement::LetMutType { .. } => todo!(),
|
||||
},
|
||||
Statement::StructDefinition(struct_definition) => {
|
||||
let (name, struct_type) = match &struct_definition.inner {
|
||||
StructDefinition::Unit { name } => {
|
||||
(name.inner.clone(), Type::Struct(StructType::Unit))
|
||||
}
|
||||
StructDefinition::Unit { name } => (
|
||||
name,
|
||||
Type::Struct(StructType::Unit {
|
||||
name: name.inner.clone(),
|
||||
}),
|
||||
),
|
||||
StructDefinition::Tuple { name, items } => {
|
||||
let fields = items.iter().map(|item| item.inner.clone()).collect();
|
||||
|
||||
(
|
||||
name.inner.clone(),
|
||||
Type::Struct(StructType::Tuple(TupleType { fields })),
|
||||
name,
|
||||
Type::Struct(StructType::Tuple {
|
||||
name: name.inner.clone(),
|
||||
fields,
|
||||
}),
|
||||
)
|
||||
}
|
||||
StructDefinition::Fields { name, fields } => {
|
||||
@ -152,14 +133,16 @@ impl<'a> Analyzer<'a> {
|
||||
.collect();
|
||||
|
||||
(
|
||||
name.inner.clone(),
|
||||
Type::Struct(StructType::Fields(FieldsStructType { fields })),
|
||||
name,
|
||||
Type::Struct(StructType::Fields {
|
||||
name: name.inner.clone(),
|
||||
fields,
|
||||
}),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
self.context
|
||||
.set_type(name, struct_type, struct_definition.position);
|
||||
todo!("Set constructor")
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,24 +162,19 @@ impl<'a> Analyzer<'a> {
|
||||
}
|
||||
}
|
||||
Expression::FieldAccess(field_access_expression) => {
|
||||
let FieldAccessExpression { container, .. } =
|
||||
let FieldAccessExpression { container, field } =
|
||||
field_access_expression.inner.as_ref();
|
||||
|
||||
self.context
|
||||
.update_last_position(&field.inner, field.position);
|
||||
self.analyze_expression(container)?;
|
||||
}
|
||||
Expression::Grouped(expression) => {
|
||||
self.analyze_expression(expression.inner.as_ref())?;
|
||||
}
|
||||
Expression::Identifier(identifier) => {
|
||||
let found = self
|
||||
.context
|
||||
self.context
|
||||
.update_last_position(&identifier.inner, identifier.position);
|
||||
|
||||
if !found {
|
||||
return Err(AnalysisError::UndefinedVariable {
|
||||
identifier: identifier.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
Expression::If(if_expression) => self.analyze_if(&if_expression.inner)?,
|
||||
Expression::List(list_expression) => match list_expression.inner.as_ref() {
|
||||
@ -256,6 +234,31 @@ impl<'a> Analyzer<'a> {
|
||||
} => {
|
||||
self.analyze_expression(assignee)?;
|
||||
self.analyze_expression(modifier)?;
|
||||
|
||||
let expected_type = assignee.return_type(self.context);
|
||||
let actual_type = modifier.return_type(self.context);
|
||||
|
||||
if expected_type.is_none() {
|
||||
return Err(AnalysisError::ExpectedValueFromExpression {
|
||||
actual: assignee.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
if actual_type.is_none() {
|
||||
return Err(AnalysisError::ExpectedValueFromExpression {
|
||||
actual: modifier.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
if let (Some(expected_type), Some(actual_type)) = (expected_type, actual_type) {
|
||||
expected_type.check(&actual_type).map_err(|type_conflct| {
|
||||
AnalysisError::TypeConflict {
|
||||
actual_expression: modifier.clone(),
|
||||
actual_type,
|
||||
expected: expected_type,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
}
|
||||
OperatorExpression::ErrorPropagation(_) => todo!(),
|
||||
OperatorExpression::Negation(expression) => {
|
||||
@ -285,30 +288,14 @@ impl<'a> Analyzer<'a> {
|
||||
},
|
||||
Expression::Struct(struct_expression) => match struct_expression.inner.as_ref() {
|
||||
StructExpression::Unit { name } => {
|
||||
let found = self
|
||||
.context
|
||||
.update_last_position(&name.inner, name.position);
|
||||
|
||||
if !found {
|
||||
return Err(AnalysisError::UndefinedType {
|
||||
identifier: name.clone(),
|
||||
});
|
||||
}
|
||||
todo!("Update constructor position");
|
||||
}
|
||||
StructExpression::Fields { name, fields } => {
|
||||
let found = self
|
||||
.context
|
||||
.update_last_position(&name.inner, name.position);
|
||||
todo!("Update constructor position");
|
||||
|
||||
if !found {
|
||||
return Err(AnalysisError::UndefinedType {
|
||||
identifier: name.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
for (_, expression) in fields {
|
||||
self.analyze_expression(expression)?;
|
||||
}
|
||||
// for (_, expression) in fields {
|
||||
// self.analyze_expression(expression)?;
|
||||
// }
|
||||
}
|
||||
},
|
||||
Expression::TupleAccess(tuple_access) => {
|
||||
@ -390,9 +377,12 @@ pub enum AnalysisError {
|
||||
ExpectedMap {
|
||||
actual: Statement,
|
||||
},
|
||||
ExpectedValue {
|
||||
ExpectedValueFromStatement {
|
||||
actual: Statement,
|
||||
},
|
||||
ExpectedValueFromExpression {
|
||||
actual: Expression,
|
||||
},
|
||||
ExpectedValueArgumentCount {
|
||||
expected: usize,
|
||||
actual: usize,
|
||||
@ -405,7 +395,7 @@ pub enum AnalysisError {
|
||||
length: usize,
|
||||
},
|
||||
TypeConflict {
|
||||
actual_statement: Statement,
|
||||
actual_expression: Expression,
|
||||
actual_type: Type,
|
||||
expected: Type,
|
||||
},
|
||||
@ -436,12 +426,13 @@ impl AnalysisError {
|
||||
AnalysisError::ExpectedIntegerOrRange { actual } => actual.position(),
|
||||
AnalysisError::ExpectedList { actual } => actual.position(),
|
||||
AnalysisError::ExpectedMap { actual } => actual.position(),
|
||||
AnalysisError::ExpectedValue { actual } => actual.position(),
|
||||
AnalysisError::ExpectedValueFromExpression { actual } => actual.position(),
|
||||
AnalysisError::ExpectedValueFromStatement { actual } => actual.position(),
|
||||
AnalysisError::ExpectedValueArgumentCount { position, .. } => *position,
|
||||
AnalysisError::IndexOutOfBounds { index, .. } => index.position(),
|
||||
AnalysisError::TypeConflict {
|
||||
actual_statement, ..
|
||||
} => actual_statement.position(),
|
||||
actual_expression, ..
|
||||
} => actual_expression.position(),
|
||||
AnalysisError::UndefinedField { identifier, .. } => identifier.position(),
|
||||
AnalysisError::UndefinedType { identifier } => identifier.position,
|
||||
AnalysisError::UndefinedVariable { identifier } => identifier.position,
|
||||
@ -470,8 +461,15 @@ impl Display for AnalysisError {
|
||||
}
|
||||
AnalysisError::ExpectedList { actual } => write!(f, "Expected list, found {}", actual),
|
||||
AnalysisError::ExpectedMap { actual } => write!(f, "Expected map, found {}", actual),
|
||||
AnalysisError::ExpectedValue { actual, .. } => {
|
||||
write!(f, "Expected value, found {}", actual)
|
||||
AnalysisError::ExpectedValueFromExpression { actual, .. } => {
|
||||
write!(
|
||||
f,
|
||||
"Expected expression to produce a value, found {}",
|
||||
actual
|
||||
)
|
||||
}
|
||||
AnalysisError::ExpectedValueFromStatement { actual, .. } => {
|
||||
write!(f, "Expected statement to produce a value, found {}", actual)
|
||||
}
|
||||
AnalysisError::ExpectedValueArgumentCount {
|
||||
expected, actual, ..
|
||||
@ -487,7 +485,7 @@ impl Display for AnalysisError {
|
||||
index_value, list, length
|
||||
),
|
||||
AnalysisError::TypeConflict {
|
||||
actual_statement,
|
||||
actual_expression: actual_statement,
|
||||
actual_type,
|
||||
expected,
|
||||
} => {
|
||||
@ -521,18 +519,27 @@ impl Display for AnalysisError {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{Identifier, Value};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn add_assign_wrong_type() {
|
||||
let source = "
|
||||
a = 1
|
||||
let mut a = 1;
|
||||
a += 1.0
|
||||
";
|
||||
|
||||
assert_eq!(analyze(source), todo!());
|
||||
assert_eq!(
|
||||
analyze(source),
|
||||
Err(DustError::AnalysisError {
|
||||
analysis_error: AnalysisError::TypeConflict {
|
||||
actual_expression: Expression::literal(1.0, (45, 48)),
|
||||
actual_type: Type::Float,
|
||||
expected: Type::Integer,
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -542,7 +549,17 @@ mod tests {
|
||||
a -= 1.0
|
||||
";
|
||||
|
||||
assert_eq!(analyze(source), todo!());
|
||||
assert_eq!(
|
||||
analyze(source),
|
||||
Err(DustError::AnalysisError {
|
||||
analysis_error: AnalysisError::TypeConflict {
|
||||
actual_expression: Expression::literal(1.0, (45, 48)),
|
||||
actual_type: Type::Float,
|
||||
expected: Type::Integer,
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -6,9 +6,7 @@ use std::{
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
Context, FieldsStructType, FunctionType, Identifier, RangeableType, StructType, TupleType, Type,
|
||||
};
|
||||
use crate::{Context, FunctionType, Identifier, RangeableType, StructType, Type};
|
||||
|
||||
use super::{Node, Span, Statement};
|
||||
|
||||
@ -268,17 +266,17 @@ impl Expression {
|
||||
pub fn return_type(&self, context: &Context) -> Option<Type> {
|
||||
match self {
|
||||
Expression::Block(block_expression) => {
|
||||
block_expression.inner.as_ref().return_type(context)
|
||||
Some(block_expression.inner.return_type(context)?)
|
||||
}
|
||||
Expression::Call(call_expression) => {
|
||||
let CallExpression { invoker, .. } = call_expression.inner.as_ref();
|
||||
|
||||
let invoker_type = invoker.return_type(context)?;
|
||||
let invoker_type = invoker.return_type(context);
|
||||
|
||||
if let Type::Function(FunctionType { return_type, .. }) = invoker_type {
|
||||
if let Some(Type::Function(FunctionType { return_type, .. })) = invoker_type {
|
||||
return_type.map(|r#type| *r#type)
|
||||
} else if let Type::Struct(_) = invoker_type {
|
||||
Some(invoker_type)
|
||||
} else if let Some(Type::Struct(_)) = invoker_type {
|
||||
invoker_type
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -287,11 +285,9 @@ impl Expression {
|
||||
let FieldAccessExpression { container, field } =
|
||||
field_access_expression.inner.as_ref();
|
||||
|
||||
let container_type = container.return_type(context)?;
|
||||
let container_type = container.return_type(context);
|
||||
|
||||
if let Type::Struct(StructType::Fields(FieldsStructType { fields, .. })) =
|
||||
container_type
|
||||
{
|
||||
if let Some(Type::Struct(StructType::Fields { fields, .. })) = container_type {
|
||||
fields
|
||||
.into_iter()
|
||||
.find(|(name, _)| name == &field.inner)
|
||||
@ -301,12 +297,13 @@ impl Expression {
|
||||
}
|
||||
}
|
||||
Expression::Grouped(expression) => expression.inner.return_type(context),
|
||||
Expression::Identifier(identifier) => context.get_type(&identifier.inner),
|
||||
Expression::If(if_expression) => match if_expression.inner.as_ref() {
|
||||
Expression::Identifier(identifier) => context.get_variable_type(&identifier.inner),
|
||||
Expression::If(if_expression) => {
|
||||
return match if_expression.inner.as_ref() {
|
||||
IfExpression::If { .. } => None,
|
||||
IfExpression::IfElse { if_block, .. } => if_block.inner.return_type(context),
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
Expression::List(list_expression) => match list_expression.inner.as_ref() {
|
||||
ListExpression::AutoFill { repeat_operand, .. } => {
|
||||
let item_type = repeat_operand.return_type(context)?;
|
||||
@ -396,7 +393,7 @@ impl Expression {
|
||||
})
|
||||
}
|
||||
Expression::Struct(struct_expression) => match struct_expression.inner.as_ref() {
|
||||
StructExpression::Fields { fields, .. } => {
|
||||
StructExpression::Fields { name, fields } => {
|
||||
let mut types = HashMap::with_capacity(fields.len());
|
||||
|
||||
for (field, expression) in fields {
|
||||
@ -405,17 +402,20 @@ impl Expression {
|
||||
types.insert(field.inner.clone(), r#type);
|
||||
}
|
||||
|
||||
Some(Type::Struct(StructType::Fields(FieldsStructType {
|
||||
Some(Type::Struct(StructType::Fields {
|
||||
name: name.inner.clone(),
|
||||
fields: types,
|
||||
})))
|
||||
}))
|
||||
}
|
||||
StructExpression::Unit { .. } => Some(Type::Struct(StructType::Unit)),
|
||||
StructExpression::Unit { name } => Some(Type::Struct(StructType::Unit {
|
||||
name: name.inner.clone(),
|
||||
})),
|
||||
},
|
||||
Expression::TupleAccess(tuple_access_expression) => {
|
||||
let TupleAccessExpression { tuple, index } = tuple_access_expression.inner.as_ref();
|
||||
let tuple_value = tuple.return_type(context)?;
|
||||
|
||||
if let Type::Tuple(TupleType { fields }) = tuple_value {
|
||||
if let Type::Tuple(fields) = tuple_value {
|
||||
fields.get(index.inner).cloned()
|
||||
} else {
|
||||
None
|
||||
@ -944,12 +944,13 @@ pub enum BlockExpression {
|
||||
impl BlockExpression {
|
||||
fn return_type(&self, context: &Context) -> Option<Type> {
|
||||
match self {
|
||||
BlockExpression::Async(statements) => statements
|
||||
.last()
|
||||
.and_then(|statement| statement.return_type(context)),
|
||||
BlockExpression::Sync(statements) => statements
|
||||
.last()
|
||||
.and_then(|statement| statement.return_type(context)),
|
||||
BlockExpression::Async(statements) | BlockExpression::Sync(statements) => {
|
||||
if let Some(statement) = statements.last() {
|
||||
statement.return_type(context)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,17 +19,6 @@ impl Statement {
|
||||
Statement::StructDefinition(Node::new(struct_definition, position))
|
||||
}
|
||||
|
||||
pub fn return_type(&self, context: &Context) -> Option<Type> {
|
||||
match self {
|
||||
Statement::Expression(expression) => expression.return_type(context),
|
||||
Statement::ExpressionNullified(expression_node) => {
|
||||
expression_node.inner.return_type(context)
|
||||
}
|
||||
Statement::Let(_) => None,
|
||||
Statement::StructDefinition(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn position(&self) -> Span {
|
||||
match self {
|
||||
Statement::Expression(expression) => expression.position(),
|
||||
@ -38,6 +27,15 @@ impl Statement {
|
||||
Statement::StructDefinition(definition) => definition.position,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn return_type(&self, context: &Context) -> Option<Type> {
|
||||
match self {
|
||||
Statement::Expression(expression) => expression.return_type(context),
|
||||
Statement::ExpressionNullified(expression) => None,
|
||||
Statement::Let(_) => None,
|
||||
Statement::StructDefinition(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Statement {
|
||||
|
@ -22,7 +22,7 @@ impl Context {
|
||||
}
|
||||
|
||||
/// Creates a deep copy of another context.
|
||||
pub fn with_variables_from(other: &Self) -> Self {
|
||||
pub fn with_data_from(other: &Self) -> Self {
|
||||
Self {
|
||||
variables: Arc::new(RwLock::new(other.variables.read().unwrap().clone())),
|
||||
}
|
||||
@ -44,7 +44,7 @@ impl Context {
|
||||
}
|
||||
|
||||
/// Returns the type of the variable with the given identifier.
|
||||
pub fn get_type(&self, identifier: &Identifier) -> Option<Type> {
|
||||
pub fn get_variable_type(&self, identifier: &Identifier) -> Option<Type> {
|
||||
match self.variables.read().unwrap().get(identifier) {
|
||||
Some((VariableData::Type(r#type), _)) => Some(r#type.clone()),
|
||||
Some((VariableData::Value(value), _)) => Some(value.r#type()),
|
||||
@ -61,7 +61,7 @@ impl Context {
|
||||
}
|
||||
|
||||
/// Returns the value of the variable with the given identifier.
|
||||
pub fn get_value(&self, identifier: &Identifier) -> Option<Value> {
|
||||
pub fn get_variable_value(&self, identifier: &Identifier) -> Option<Value> {
|
||||
match self.variables.read().unwrap().get(identifier) {
|
||||
Some((VariableData::Value(value), _)) => Some(value.clone()),
|
||||
_ => None,
|
||||
@ -69,7 +69,7 @@ impl Context {
|
||||
}
|
||||
|
||||
/// Sets a variable to a type, with a position given for garbage collection.
|
||||
pub fn set_type(&self, identifier: Identifier, r#type: Type, position: Span) {
|
||||
pub fn set_variable_type(&self, identifier: Identifier, r#type: Type, position: Span) {
|
||||
log::trace!("Setting {identifier} to type {type} at {position:?}");
|
||||
|
||||
self.variables
|
||||
@ -79,7 +79,7 @@ impl Context {
|
||||
}
|
||||
|
||||
/// Sets a variable to a value.
|
||||
pub fn set_value(&self, identifier: Identifier, value: Value) {
|
||||
pub fn set_variable_value(&self, identifier: Identifier, value: Value) {
|
||||
log::trace!("Setting {identifier} to value {value}");
|
||||
|
||||
let mut variables = self.variables.write().unwrap();
|
||||
|
@ -314,21 +314,12 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
let expression = self.parse_expression(0)?;
|
||||
let end = self.current_position.1;
|
||||
let is_nullified = if let Token::Semicolon = self.current_token {
|
||||
|
||||
if let Token::Semicolon = self.current_token {
|
||||
let position = (start_position.0, self.current_position.1);
|
||||
|
||||
self.next_token()?;
|
||||
|
||||
true
|
||||
} else {
|
||||
matches!(
|
||||
expression,
|
||||
Expression::Block(_) | Expression::Loop(_) | Expression::If(_)
|
||||
)
|
||||
};
|
||||
|
||||
if is_nullified {
|
||||
let position = (start_position.0, end);
|
||||
|
||||
Ok(Statement::ExpressionNullified(Node::new(
|
||||
expression, position,
|
||||
)))
|
||||
@ -446,10 +437,9 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
if let Token::LeftCurlyBrace = self.current_token {
|
||||
let name = Node::new(identifier, start_position);
|
||||
|
||||
self.next_token()?;
|
||||
|
||||
let name = Node::new(identifier, start_position);
|
||||
let mut fields = Vec::new();
|
||||
|
||||
loop {
|
||||
@ -1274,16 +1264,12 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(AbstractSyntaxTree {
|
||||
statements: [Statement::ExpressionNullified(Node::new(
|
||||
Expression::block(
|
||||
statements: [Statement::Expression(Expression::block(
|
||||
BlockExpression::Async(vec![
|
||||
Statement::ExpressionNullified(Node::new(
|
||||
Expression::operator(
|
||||
OperatorExpression::Assignment {
|
||||
assignee: Expression::identifier(
|
||||
Identifier::new("x"),
|
||||
(8, 9)
|
||||
),
|
||||
assignee: Expression::identifier(Identifier::new("x"), (8, 9)),
|
||||
value: Expression::literal(42, (12, 14)),
|
||||
},
|
||||
(8, 14)
|
||||
@ -1292,18 +1278,13 @@ mod tests {
|
||||
)),
|
||||
Statement::Expression(Expression::operator(
|
||||
OperatorExpression::Assignment {
|
||||
assignee: Expression::identifier(
|
||||
Identifier::new("y"),
|
||||
(16, 17)
|
||||
),
|
||||
assignee: Expression::identifier(Identifier::new("y"), (16, 17)),
|
||||
value: Expression::literal(4.0, (20, 23)),
|
||||
},
|
||||
(16, 23)
|
||||
))
|
||||
]),
|
||||
(0, 25)
|
||||
),
|
||||
(0, 25)
|
||||
))]
|
||||
.into()
|
||||
})
|
||||
@ -1597,8 +1578,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(AbstractSyntaxTree::with_statements([
|
||||
Statement::ExpressionNullified(Node::new(
|
||||
Expression::r#if(
|
||||
Statement::Expression(Expression::r#if(
|
||||
IfExpression::If {
|
||||
condition: Expression::identifier(Identifier::new("x"), (3, 4)),
|
||||
if_block: Node::new(
|
||||
@ -1609,8 +1589,6 @@ mod tests {
|
||||
)
|
||||
},
|
||||
(0, 10)
|
||||
),
|
||||
(0, 10)
|
||||
))
|
||||
]))
|
||||
);
|
||||
@ -1623,8 +1601,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(AbstractSyntaxTree::with_statements([
|
||||
Statement::ExpressionNullified(Node::new(
|
||||
Expression::r#if(
|
||||
Statement::Expression(Expression::r#if(
|
||||
IfExpression::IfElse {
|
||||
condition: Expression::identifier(Identifier::new("x"), (3, 4)),
|
||||
if_block: Node::new(
|
||||
@ -1641,8 +1618,6 @@ mod tests {
|
||||
))
|
||||
},
|
||||
(0, 21)
|
||||
),
|
||||
(0, 21)
|
||||
))
|
||||
]))
|
||||
);
|
||||
@ -1655,8 +1630,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(AbstractSyntaxTree::with_statements([
|
||||
Statement::ExpressionNullified(Node::new(
|
||||
Expression::r#if(
|
||||
Statement::Expression(Expression::r#if(
|
||||
IfExpression::IfElse {
|
||||
condition: Expression::identifier(Identifier::new("x"), (3, 4)),
|
||||
if_block: Node::new(
|
||||
@ -1667,10 +1641,7 @@ mod tests {
|
||||
),
|
||||
r#else: ElseExpression::If(Node::new(
|
||||
Box::new(IfExpression::IfElse {
|
||||
condition: Expression::identifier(
|
||||
Identifier::new("z"),
|
||||
(19, 20)
|
||||
),
|
||||
condition: Expression::identifier(Identifier::new("z"), (19, 20)),
|
||||
if_block: Node::new(
|
||||
BlockExpression::Sync(vec![Statement::Expression(
|
||||
Expression::identifier(Identifier::new("a"), (23, 24))
|
||||
@ -1688,8 +1659,6 @@ mod tests {
|
||||
)),
|
||||
},
|
||||
(0, 37)
|
||||
),
|
||||
(0, 37)
|
||||
))
|
||||
]))
|
||||
)
|
||||
@ -1702,8 +1671,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(AbstractSyntaxTree::with_statements([
|
||||
Statement::ExpressionNullified(Node::new(
|
||||
Expression::while_loop(
|
||||
Statement::Expression(Expression::while_loop(
|
||||
Expression::operator(
|
||||
OperatorExpression::Comparison {
|
||||
left: Expression::identifier(Identifier::new("x"), (6, 7)),
|
||||
@ -1713,24 +1681,17 @@ mod tests {
|
||||
(6, 12)
|
||||
),
|
||||
Node::new(
|
||||
BlockExpression::Sync(vec![Statement::Expression(
|
||||
Expression::operator(
|
||||
BlockExpression::Sync(vec![Statement::Expression(Expression::operator(
|
||||
OperatorExpression::CompoundAssignment {
|
||||
assignee: Expression::identifier(
|
||||
Identifier::new("x"),
|
||||
(15, 16)
|
||||
),
|
||||
assignee: Expression::identifier(Identifier::new("x"), (15, 16)),
|
||||
operator: Node::new(MathOperator::Add, (17, 19)),
|
||||
modifier: Expression::literal(1, (20, 21)),
|
||||
},
|
||||
(15, 21)
|
||||
)
|
||||
)]),
|
||||
))]),
|
||||
(13, 23)
|
||||
),
|
||||
(0, 23)
|
||||
),
|
||||
(0, 23)
|
||||
))
|
||||
]))
|
||||
)
|
||||
@ -1781,8 +1742,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(AbstractSyntaxTree::with_statements([
|
||||
Statement::ExpressionNullified(Node::new(
|
||||
Expression::block(
|
||||
Statement::Expression(Expression::block(
|
||||
BlockExpression::Sync(vec![Statement::Expression(Expression::operator(
|
||||
OperatorExpression::Math {
|
||||
left: Expression::literal(40, (2, 4)),
|
||||
@ -1792,8 +1752,6 @@ mod tests {
|
||||
(2, 8)
|
||||
))]),
|
||||
(0, 10)
|
||||
),
|
||||
(0, 10)
|
||||
))
|
||||
]))
|
||||
)
|
||||
@ -1806,8 +1764,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(AbstractSyntaxTree::with_statements([
|
||||
Statement::ExpressionNullified(Node::new(
|
||||
Expression::block(
|
||||
Statement::Expression(Expression::block(
|
||||
BlockExpression::Sync(vec![
|
||||
Statement::ExpressionNullified(Node::new(
|
||||
Expression::assignment(
|
||||
@ -1832,8 +1789,6 @@ mod tests {
|
||||
))
|
||||
]),
|
||||
(0, 34)
|
||||
),
|
||||
(0, 34)
|
||||
))
|
||||
]))
|
||||
)
|
||||
|
@ -17,7 +17,7 @@ use std::{
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Identifier;
|
||||
use crate::{Enum, Identifier, Struct, Value};
|
||||
|
||||
/// Description of a kind of value.
|
||||
///
|
||||
@ -53,7 +53,7 @@ pub enum Type {
|
||||
},
|
||||
String,
|
||||
Struct(StructType),
|
||||
Tuple(TupleType),
|
||||
Tuple(Vec<Type>),
|
||||
}
|
||||
|
||||
impl Type {
|
||||
@ -266,36 +266,8 @@ impl Display for Type {
|
||||
Type::Number => write!(f, "num"),
|
||||
Type::Range { r#type } => write!(f, "{type} range"),
|
||||
Type::String => write!(f, "str"),
|
||||
Type::Struct(struct_type) => match struct_type {
|
||||
StructType::Unit => write!(f, "()"),
|
||||
StructType::Tuple(TupleType { fields }) => {
|
||||
write!(f, "(")?;
|
||||
|
||||
for (index, r#type) in fields.iter().enumerate() {
|
||||
write!(f, "{type}")?;
|
||||
|
||||
if index != fields.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
StructType::Fields(FieldsStructType { fields }) => {
|
||||
write!(f, "{{")?;
|
||||
|
||||
for (index, (name, r#type)) in fields.iter().enumerate() {
|
||||
write!(f, "{name}: {type}")?;
|
||||
|
||||
if index != fields.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, "}}")
|
||||
}
|
||||
},
|
||||
Type::Tuple(TupleType { fields }) => {
|
||||
Type::Struct(struct_type) => write!(f, "{struct_type}"),
|
||||
Type::Tuple(fields) => {
|
||||
write!(f, "(")?;
|
||||
|
||||
for (index, r#type) in fields.iter().enumerate() {
|
||||
@ -439,88 +411,257 @@ impl Display for FunctionType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum StructType {
|
||||
Unit,
|
||||
Tuple(TupleType),
|
||||
Fields(FieldsStructType),
|
||||
Unit {
|
||||
name: Identifier,
|
||||
},
|
||||
Tuple {
|
||||
name: Identifier,
|
||||
fields: Vec<Type>,
|
||||
},
|
||||
Fields {
|
||||
name: Identifier,
|
||||
fields: HashMap<Identifier, Type>,
|
||||
},
|
||||
}
|
||||
|
||||
impl StructType {
|
||||
pub fn name(&self) -> &Identifier {
|
||||
match self {
|
||||
StructType::Unit { name } => name,
|
||||
StructType::Tuple { name, .. } => name,
|
||||
StructType::Fields { name, .. } => name,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn constructor(&self) -> Option<Constructor> {
|
||||
let constructor = match self {
|
||||
StructType::Unit { name } => Constructor::Unit { name: name.clone() },
|
||||
StructType::Tuple { name, fields } => Constructor::Tuple(TupleConstructor {
|
||||
name: name.clone(),
|
||||
tuple_arguments: Vec::with_capacity(fields.len()),
|
||||
}),
|
||||
StructType::Fields { name, fields } => Constructor::Fields {
|
||||
name: name.clone(),
|
||||
field_arguments: HashMap::with_capacity(fields.len()),
|
||||
},
|
||||
};
|
||||
|
||||
Some(constructor)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for StructType {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
StructType::Unit => write!(f, "()"),
|
||||
StructType::Tuple(tuple) => write!(f, "{tuple}"),
|
||||
StructType::Fields(fields) => write!(f, "{fields}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
StructType::Unit { name } => write!(f, "{name}"),
|
||||
StructType::Tuple { name, fields } => {
|
||||
write!(f, "{name}(")?;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct TupleType {
|
||||
pub fields: Vec<Type>,
|
||||
}
|
||||
|
||||
impl Display for TupleType {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "(")?;
|
||||
|
||||
for (index, field) in self.fields.iter().enumerate() {
|
||||
for (index, field) in fields.iter().enumerate() {
|
||||
write!(f, "{field}")?;
|
||||
|
||||
if index != self.fields.len() - 1 {
|
||||
if index != fields.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
StructType::Fields { name, fields } => {
|
||||
write!(f, "{name} {{")?;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct FieldsStructType {
|
||||
pub fields: HashMap<Identifier, Type>,
|
||||
}
|
||||
|
||||
impl Display for FieldsStructType {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{{ ")?;
|
||||
|
||||
for (index, (identifier, r#type)) in self.fields.iter().enumerate() {
|
||||
for (index, (identifier, r#type)) in fields.iter().enumerate() {
|
||||
write!(f, "{identifier}: {type}")?;
|
||||
|
||||
if index != self.fields.len() - 1 {
|
||||
if index != fields.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, " }}")
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for FieldsStructType {
|
||||
impl PartialOrd for StructType {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for FieldsStructType {
|
||||
impl Ord for StructType {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.fields.iter().cmp(other.fields.iter())
|
||||
match (self, other) {
|
||||
(StructType::Unit { name: left_name }, StructType::Unit { name: right_name }) => {
|
||||
left_name.cmp(right_name)
|
||||
}
|
||||
(StructType::Unit { .. }, _) => Ordering::Greater,
|
||||
(
|
||||
StructType::Tuple {
|
||||
name: left_name,
|
||||
fields: left_fields,
|
||||
},
|
||||
StructType::Tuple {
|
||||
name: right_name,
|
||||
fields: right_fields,
|
||||
},
|
||||
) => {
|
||||
let name_cmp = left_name.cmp(right_name);
|
||||
|
||||
if name_cmp == Ordering::Equal {
|
||||
left_fields.cmp(right_fields)
|
||||
} else {
|
||||
name_cmp
|
||||
}
|
||||
}
|
||||
(StructType::Tuple { .. }, _) => Ordering::Greater,
|
||||
(
|
||||
StructType::Fields {
|
||||
name: left_name,
|
||||
fields: left_fields,
|
||||
},
|
||||
StructType::Fields {
|
||||
name: right_name,
|
||||
fields: right_fields,
|
||||
},
|
||||
) => {
|
||||
let name_cmp = left_name.cmp(right_name);
|
||||
|
||||
if name_cmp == Ordering::Equal {
|
||||
let len_cmp = left_fields.len().cmp(&right_fields.len());
|
||||
|
||||
if len_cmp == Ordering::Equal {
|
||||
left_fields.iter().cmp(right_fields.iter())
|
||||
} else {
|
||||
len_cmp
|
||||
}
|
||||
} else {
|
||||
name_cmp
|
||||
}
|
||||
}
|
||||
(StructType::Fields { .. }, _) => Ordering::Greater,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Constructor {
|
||||
Unit {
|
||||
name: Identifier,
|
||||
},
|
||||
Tuple(TupleConstructor),
|
||||
Fields {
|
||||
name: Identifier,
|
||||
field_arguments: HashMap<Identifier, Value>,
|
||||
},
|
||||
Enum {
|
||||
name: Identifier,
|
||||
r#type: EnumType,
|
||||
variant_constructor: Box<Constructor>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Constructor {
|
||||
pub fn construct(self) -> Value {
|
||||
match self {
|
||||
Constructor::Unit { name } => Value::Struct(Struct::Unit { name }),
|
||||
Constructor::Tuple(tuple_constructor) => tuple_constructor.construct(),
|
||||
Constructor::Fields {
|
||||
name,
|
||||
field_arguments,
|
||||
} => Value::Struct(Struct::Fields {
|
||||
name,
|
||||
fields: field_arguments,
|
||||
}),
|
||||
Constructor::Enum {
|
||||
name,
|
||||
r#type,
|
||||
variant_constructor,
|
||||
} => Value::Enum(Enum {
|
||||
name,
|
||||
r#type,
|
||||
variant_data: Self::make_struct(*variant_constructor),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_struct(this: Self) -> Struct {
|
||||
match this {
|
||||
Constructor::Unit { name } => Struct::Unit { name },
|
||||
Constructor::Tuple(TupleConstructor {
|
||||
name,
|
||||
tuple_arguments,
|
||||
}) => Struct::Tuple {
|
||||
name,
|
||||
fields: tuple_arguments,
|
||||
},
|
||||
Constructor::Fields {
|
||||
name,
|
||||
field_arguments,
|
||||
} => Struct::Fields {
|
||||
name,
|
||||
fields: field_arguments,
|
||||
},
|
||||
Constructor::Enum {
|
||||
variant_constructor,
|
||||
..
|
||||
} => Self::make_struct(*variant_constructor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct TupleConstructor {
|
||||
pub name: Identifier,
|
||||
pub tuple_arguments: Vec<Value>,
|
||||
}
|
||||
|
||||
impl TupleConstructor {
|
||||
pub fn push_argument(&mut self, value: Value) {
|
||||
self.tuple_arguments.push(value);
|
||||
}
|
||||
|
||||
pub fn construct(self) -> Value {
|
||||
Value::Struct(Struct::Tuple {
|
||||
name: self.name,
|
||||
fields: self.tuple_arguments,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct EnumType {
|
||||
name: Identifier,
|
||||
variants: Vec<StructType>,
|
||||
pub name: Identifier,
|
||||
pub variants: Vec<StructType>,
|
||||
}
|
||||
|
||||
impl EnumType {
|
||||
pub fn constructor(&self) -> Option<Constructor> {
|
||||
let get_variant_constructor = self
|
||||
.variants
|
||||
.iter()
|
||||
.find_map(|struct_type| struct_type.constructor());
|
||||
|
||||
if let Some(variant_constructor) = get_variant_constructor {
|
||||
Some(Constructor::Enum {
|
||||
name: self.name.clone(),
|
||||
r#type: self.clone(),
|
||||
variant_constructor: Box::new(variant_constructor),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for EnumType {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let EnumType { name, variants } = self;
|
||||
|
||||
write!(f, "enum {name} {{")?;
|
||||
write!(f, "enum {name} {{ ")?;
|
||||
|
||||
for (index, variant) in variants.iter().enumerate() {
|
||||
write!(f, "{variant}")?;
|
||||
@ -530,7 +671,7 @@ impl Display for EnumType {
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, "}}")
|
||||
write!(f, " }}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,8 @@ use serde::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
AbstractSyntaxTree, Context, EnumType, FieldsStructType, FunctionType, Identifier,
|
||||
RangeableType, RuntimeError, StructType, TupleType, Type, Vm,
|
||||
AbstractSyntaxTree, Context, EnumType, FunctionType, Identifier, RangeableType, RuntimeError,
|
||||
StructType, Type, Vm,
|
||||
};
|
||||
|
||||
/// Dust value representation
|
||||
@ -57,7 +57,7 @@ pub enum Value {
|
||||
Boolean(bool),
|
||||
Byte(u8),
|
||||
Character(char),
|
||||
Enum { name: Identifier, r#type: EnumType },
|
||||
Enum(Enum),
|
||||
Float(f64),
|
||||
Function(Function),
|
||||
Integer(i64),
|
||||
@ -80,6 +80,10 @@ impl Value {
|
||||
Value::Mutable(Arc::new(RwLock::new(value)))
|
||||
}
|
||||
|
||||
pub fn mutable_from<T: Into<Value>>(into_value: T) -> Value {
|
||||
Value::Mutable(Arc::new(RwLock::new(into_value.into())))
|
||||
}
|
||||
|
||||
pub fn byte_range(start: u8, end: u8) -> Value {
|
||||
Value::Range(Rangeable::Byte(start)..Rangeable::Byte(end))
|
||||
}
|
||||
@ -188,7 +192,7 @@ impl Value {
|
||||
Value::Boolean(_) => Type::Boolean,
|
||||
Value::Byte(_) => Type::Byte,
|
||||
Value::Character(_) => Type::Character,
|
||||
Value::Enum { r#type, .. } => Type::Enum(r#type.clone()),
|
||||
Value::Enum(Enum { r#type, .. }) => Type::Enum(r#type.clone()),
|
||||
Value::Float(_) => Type::Float,
|
||||
Value::Function(function) => Type::Function(function.r#type.clone()),
|
||||
Value::Integer(_) => Type::Integer,
|
||||
@ -221,25 +225,31 @@ impl Value {
|
||||
}
|
||||
Value::String(_) => Type::String,
|
||||
Value::Struct(r#struct) => match r#struct {
|
||||
Struct::Unit { .. } => Type::Struct(StructType::Unit),
|
||||
Struct::Tuple { fields, .. } => {
|
||||
Struct::Unit { name } => Type::Struct(StructType::Unit { name: name.clone() }),
|
||||
Struct::Tuple { name, fields } => {
|
||||
let types = fields.iter().map(|field| field.r#type()).collect();
|
||||
|
||||
Type::Struct(StructType::Tuple(TupleType { fields: types }))
|
||||
Type::Struct(StructType::Tuple {
|
||||
name: name.clone(),
|
||||
fields: types,
|
||||
})
|
||||
}
|
||||
Struct::Fields { fields, .. } => {
|
||||
Struct::Fields { name, fields } => {
|
||||
let types = fields
|
||||
.iter()
|
||||
.map(|(identifier, value)| (identifier.clone(), value.r#type()))
|
||||
.collect();
|
||||
|
||||
Type::Struct(StructType::Fields(FieldsStructType { fields: types }))
|
||||
Type::Struct(StructType::Fields {
|
||||
name: name.clone(),
|
||||
fields: types,
|
||||
})
|
||||
}
|
||||
},
|
||||
Value::Tuple(values) => {
|
||||
let fields = values.iter().map(|value| value.r#type()).collect();
|
||||
|
||||
Type::Tuple(TupleType { fields })
|
||||
Type::Tuple(fields)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -248,6 +258,7 @@ impl Value {
|
||||
match self {
|
||||
Value::Mutable(inner) => inner.read().unwrap().get_field(field),
|
||||
Value::Struct(Struct::Fields { fields, .. }) => fields.get(field).cloned(),
|
||||
Value::Map(pairs) => pairs.get(field).cloned(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -762,6 +773,54 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Value {
|
||||
fn from(value: bool) -> Self {
|
||||
Value::Boolean(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for Value {
|
||||
fn from(value: u8) -> Self {
|
||||
Value::Byte(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<char> for Value {
|
||||
fn from(value: char) -> Self {
|
||||
Value::Character(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Value {
|
||||
fn from(value: f64) -> Self {
|
||||
Value::Float(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Value {
|
||||
fn from(value: i32) -> Self {
|
||||
Value::Integer(value as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Value {
|
||||
fn from(value: i64) -> Self {
|
||||
Value::Integer(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Value {
|
||||
fn from(value: String) -> Self {
|
||||
Value::String(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Value {
|
||||
fn from(value: &str) -> Self {
|
||||
Value::String(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Value {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
@ -773,7 +832,7 @@ impl Display for Value {
|
||||
Value::Boolean(boolean) => write!(f, "{boolean}"),
|
||||
Value::Byte(byte) => write!(f, "{byte}"),
|
||||
Value::Character(character) => write!(f, "{character}"),
|
||||
Value::Enum { name, r#type } => write!(f, "{name}::{type}"),
|
||||
Value::Enum(r#enum) => write!(f, "{enum}"),
|
||||
Value::Float(float) => write!(f, "{float}"),
|
||||
Value::Function(function) => write!(f, "{function}"),
|
||||
Value::Integer(integer) => write!(f, "{integer}"),
|
||||
@ -813,7 +872,7 @@ impl Display for Value {
|
||||
write!(f, "{start}..={end}")
|
||||
}
|
||||
Value::String(string) => write!(f, "{string}"),
|
||||
Value::Struct(structure) => write!(f, "{structure}"),
|
||||
Value::Struct(r#struct) => write!(f, "{struct}"),
|
||||
Value::Tuple(fields) => {
|
||||
write!(f, "(")?;
|
||||
|
||||
@ -934,14 +993,7 @@ impl Serialize for Value {
|
||||
Value::Boolean(boolean) => serializer.serialize_bool(*boolean),
|
||||
Value::Byte(byte) => serializer.serialize_u8(*byte),
|
||||
Value::Character(character) => serializer.serialize_char(*character),
|
||||
Value::Enum { name, r#type } => {
|
||||
let mut ser = serializer.serialize_struct_variant("Value", 4, "Enum", 2)?;
|
||||
|
||||
ser.serialize_field("name", name)?;
|
||||
ser.serialize_field("type", r#type)?;
|
||||
|
||||
ser.end()
|
||||
}
|
||||
Value::Enum(r#emum) => r#emum.serialize(serializer),
|
||||
Value::Float(float) => serializer.serialize_f64(*float),
|
||||
Value::Function(function) => function.serialize(serializer),
|
||||
Value::Integer(integer) => serializer.serialize_i64(*integer),
|
||||
@ -1021,13 +1073,13 @@ impl Function {
|
||||
value_arguments: Option<Vec<Value>>,
|
||||
context: &Context,
|
||||
) -> Result<Option<Value>, RuntimeError> {
|
||||
let new_context = Context::with_variables_from(context);
|
||||
let new_context = Context::with_data_from(context);
|
||||
|
||||
if let (Some(value_parameters), Some(value_arguments)) =
|
||||
(&self.r#type.value_parameters, value_arguments)
|
||||
{
|
||||
for ((identifier, _), value) in value_parameters.iter().zip(value_arguments) {
|
||||
new_context.set_value(identifier.clone(), value);
|
||||
new_context.set_variable_value(identifier.clone(), value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1226,9 +1278,9 @@ impl Ord for Struct {
|
||||
impl Display for Struct {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Struct::Unit { .. } => write!(f, "()"),
|
||||
Struct::Tuple { fields, .. } => {
|
||||
write!(f, "(")?;
|
||||
Struct::Unit { name } => write!(f, "{name}"),
|
||||
Struct::Tuple { name, fields } => {
|
||||
write!(f, "{name}(")?;
|
||||
|
||||
for (index, field) in fields.iter().enumerate() {
|
||||
if index > 0 {
|
||||
@ -1240,8 +1292,8 @@ impl Display for Struct {
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
Struct::Fields { fields, .. } => {
|
||||
write!(f, "{{ ")?;
|
||||
Struct::Fields { name, fields } => {
|
||||
write!(f, "{name} {{ ")?;
|
||||
|
||||
for (index, (identifier, value)) in fields.iter().enumerate() {
|
||||
if index > 0 {
|
||||
@ -1310,6 +1362,57 @@ impl Ord for Rangeable {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Enum {
|
||||
pub r#type: EnumType,
|
||||
pub name: Identifier,
|
||||
pub variant_data: Struct,
|
||||
}
|
||||
|
||||
impl Display for Enum {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
let Enum {
|
||||
name, variant_data, ..
|
||||
} = self;
|
||||
|
||||
match &variant_data {
|
||||
Struct::Unit { name: variant_name } => write!(f, "{name}::{variant_name}"),
|
||||
Struct::Tuple {
|
||||
name: variant_name,
|
||||
fields,
|
||||
} => {
|
||||
write!(f, "{name}::{variant_name}(")?;
|
||||
|
||||
for (index, field) in fields.iter().enumerate() {
|
||||
if index > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
|
||||
write!(f, "{}", field)?;
|
||||
}
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
Struct::Fields {
|
||||
name: variant_name,
|
||||
fields,
|
||||
} => {
|
||||
write!(f, "{name}::{variant_name} {{ ")?;
|
||||
|
||||
for (index, (identifier, value)) in fields.iter().enumerate() {
|
||||
if index > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
|
||||
write!(f, "{}: {}", identifier, value)?;
|
||||
}
|
||||
|
||||
write!(f, " }}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ValueError {
|
||||
CannotAdd(Value, Value),
|
||||
|
@ -21,8 +21,9 @@ use crate::{
|
||||
OperatorExpression, PrimitiveValueExpression, RangeExpression, RawStringExpression, Span,
|
||||
Statement, StructDefinition, StructExpression,
|
||||
},
|
||||
parse, Analyzer, BuiltInFunctionError, Context, DustError, Expression, FieldsStructType,
|
||||
Identifier, ParseError, Struct, StructType, TupleType, Type, Value, ValueError,
|
||||
parse, Analyzer, BuiltInFunctionError, Constructor, Context, DustError, Enum, EnumType,
|
||||
Expression, Identifier, ParseError, Struct, StructType, TupleConstructor, Type, Value,
|
||||
ValueError,
|
||||
};
|
||||
|
||||
/// Run the source code and return the result.
|
||||
@ -126,11 +127,19 @@ impl Vm {
|
||||
}
|
||||
Statement::StructDefinition(struct_definition) => {
|
||||
let (name, struct_type) = match struct_definition.inner {
|
||||
StructDefinition::Unit { name } => (name.inner.clone(), StructType::Unit),
|
||||
StructDefinition::Unit { name } => {
|
||||
(name.inner.clone(), StructType::Unit { name: name.inner })
|
||||
}
|
||||
StructDefinition::Tuple { name, items } => {
|
||||
let fields = items.into_iter().map(|item| item.inner).collect();
|
||||
|
||||
(name.inner.clone(), StructType::Tuple(TupleType { fields }))
|
||||
(
|
||||
name.inner.clone(),
|
||||
StructType::Tuple {
|
||||
name: name.inner,
|
||||
fields,
|
||||
},
|
||||
)
|
||||
}
|
||||
StructDefinition::Fields { name, fields } => {
|
||||
let fields = fields
|
||||
@ -140,23 +149,25 @@ impl Vm {
|
||||
|
||||
(
|
||||
name.inner.clone(),
|
||||
StructType::Fields(FieldsStructType { fields }),
|
||||
StructType::Fields {
|
||||
name: name.inner,
|
||||
fields,
|
||||
},
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
self.context
|
||||
.set_type(name, Type::Struct(struct_type), struct_definition.position);
|
||||
todo!("Set constructor");
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
};
|
||||
|
||||
if collect_garbage {
|
||||
self.context.collect_garbage(position);
|
||||
// self.context.collect_garbage(position);
|
||||
}
|
||||
|
||||
result.map_err(|error| RuntimeError::Trace {
|
||||
result.map_err(|error| RuntimeError::Statement {
|
||||
error: Box::new(error),
|
||||
position,
|
||||
})
|
||||
@ -174,7 +185,7 @@ impl Vm {
|
||||
.run_expression(value, collect_garbage)?
|
||||
.expect_value(value_position)?;
|
||||
|
||||
self.context.set_value(identifier.inner, value);
|
||||
self.context.set_variable_value(identifier.inner, value);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -185,7 +196,8 @@ impl Vm {
|
||||
.expect_value(value_position)?
|
||||
.into_mutable();
|
||||
|
||||
self.context.set_value(identifier.inner, mutable_value);
|
||||
self.context
|
||||
.set_variable_value(identifier.inner, mutable_value);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -210,7 +222,7 @@ impl Vm {
|
||||
self.run_expression(*expression.inner, collect_garbage)
|
||||
}
|
||||
Expression::Identifier(identifier) => {
|
||||
let get_value = self.context.get_value(&identifier.inner);
|
||||
let get_value = self.context.get_variable_value(&identifier.inner);
|
||||
|
||||
if let Some(value) = get_value {
|
||||
Ok(Evaluation::Return(Some(value)))
|
||||
@ -243,7 +255,7 @@ impl Vm {
|
||||
Expression::TupleAccess(_) => todo!(),
|
||||
};
|
||||
|
||||
evaluation_result.map_err(|error| RuntimeError::Trace {
|
||||
evaluation_result.map_err(|error| RuntimeError::Expression {
|
||||
error: Box::new(error),
|
||||
position,
|
||||
})
|
||||
@ -257,20 +269,20 @@ impl Vm {
|
||||
match struct_expression {
|
||||
StructExpression::Unit { name } => {
|
||||
let position = name.position;
|
||||
let r#type = self.context.get_type(&name.inner).ok_or_else(|| {
|
||||
let r#type = self.context.get_variable_type(&name.inner).ok_or_else(|| {
|
||||
RuntimeError::UndefinedType {
|
||||
identifier: name.inner.clone(),
|
||||
position,
|
||||
}
|
||||
})?;
|
||||
|
||||
if let Type::Struct(StructType::Unit) = r#type {
|
||||
if let Type::Struct(StructType::Unit { name }) = r#type {
|
||||
Ok(Evaluation::Return(Some(Value::Struct(Struct::Unit {
|
||||
name: name.inner,
|
||||
name,
|
||||
}))))
|
||||
} else {
|
||||
Err(RuntimeError::ExpectedType {
|
||||
expected: Type::Struct(StructType::Unit),
|
||||
expected: Type::Struct(StructType::Unit { name: name.inner }),
|
||||
actual: r#type,
|
||||
position,
|
||||
})
|
||||
@ -281,14 +293,14 @@ impl Vm {
|
||||
fields: expressions,
|
||||
} => {
|
||||
let position = name.position;
|
||||
let r#type = self.context.get_type(&name.inner).ok_or_else(|| {
|
||||
let r#type = self.context.get_variable_type(&name.inner).ok_or_else(|| {
|
||||
RuntimeError::UndefinedType {
|
||||
identifier: name.inner.clone(),
|
||||
position,
|
||||
}
|
||||
})?;
|
||||
|
||||
if let Type::Struct(StructType::Fields(FieldsStructType { .. })) = r#type {
|
||||
if let Type::Struct(StructType::Fields { name, .. }) = r#type {
|
||||
let mut values = HashMap::with_capacity(expressions.len());
|
||||
|
||||
for (identifier, expression) in expressions {
|
||||
@ -301,14 +313,15 @@ impl Vm {
|
||||
}
|
||||
|
||||
Ok(Evaluation::Return(Some(Value::Struct(Struct::Fields {
|
||||
name: name.inner,
|
||||
name,
|
||||
fields: values,
|
||||
}))))
|
||||
} else {
|
||||
Err(RuntimeError::ExpectedType {
|
||||
expected: Type::Struct(StructType::Fields(FieldsStructType {
|
||||
fields: HashMap::new(),
|
||||
})),
|
||||
expected: Type::Struct(StructType::Fields {
|
||||
name: name.inner,
|
||||
fields: Default::default(),
|
||||
}),
|
||||
actual: r#type,
|
||||
position,
|
||||
})
|
||||
@ -609,11 +622,7 @@ impl Vm {
|
||||
|
||||
Ok(Evaluation::Return(None))
|
||||
}
|
||||
LoopExpression::For {
|
||||
identifier,
|
||||
iterator,
|
||||
block,
|
||||
} => todo!(),
|
||||
LoopExpression::For { .. } => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -668,8 +677,47 @@ impl Vm {
|
||||
let CallExpression { invoker, arguments } = call_expression;
|
||||
|
||||
let invoker_position = invoker.position();
|
||||
let invoker_value =
|
||||
if let Some(value) = self.run_expression(invoker, collect_garbage)?.value() {
|
||||
let run_invoker = self.run_expression(invoker, collect_garbage)?;
|
||||
|
||||
if let Evaluation::Constructor(Type::Struct(StructType::Tuple { name, fields })) =
|
||||
&run_invoker
|
||||
{
|
||||
let struct_type = fields
|
||||
.iter()
|
||||
.find(|r#type| {
|
||||
if let Type::Struct(struct_type) = r#type {
|
||||
struct_type.name() == name
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.ok_or(RuntimeError::EnumVariantNotFound {
|
||||
identifier: name.clone(),
|
||||
position: invoker_position,
|
||||
})?;
|
||||
|
||||
if let Type::Struct(StructType::Tuple { name, fields }) = struct_type {
|
||||
let mut values = Vec::with_capacity(arguments.len());
|
||||
|
||||
for argument in arguments {
|
||||
let position = argument.position();
|
||||
let value = self
|
||||
.run_expression(argument, collect_garbage)?
|
||||
.expect_value(position)?;
|
||||
|
||||
values.push(value);
|
||||
}
|
||||
|
||||
let r#struct = Struct::Tuple {
|
||||
name: name.clone(),
|
||||
fields: values,
|
||||
};
|
||||
|
||||
return Ok(Evaluation::Return(Some(Value::Struct(r#struct))));
|
||||
}
|
||||
}
|
||||
|
||||
let invoker_value = if let Some(value) = run_invoker.value() {
|
||||
value
|
||||
} else {
|
||||
return Err(RuntimeError::ExpectedValue {
|
||||
@ -814,7 +862,7 @@ impl Vm {
|
||||
previous_value = self.run_statement(statement, collect_garbage)?;
|
||||
|
||||
if collect_garbage {
|
||||
self.context.collect_garbage(position);
|
||||
// self.context.collect_garbage(position);
|
||||
}
|
||||
}
|
||||
|
||||
@ -864,7 +912,7 @@ impl Vm {
|
||||
|
||||
match r#else {
|
||||
ElseExpression::If(if_expression) => {
|
||||
self.run_expression(Expression::If(if_expression), collect_garbage)
|
||||
self.run_if(*if_expression.inner, collect_garbage)
|
||||
}
|
||||
ElseExpression::Block(block) => self.run_block(block.inner, collect_garbage),
|
||||
}
|
||||
@ -875,14 +923,15 @@ impl Vm {
|
||||
|
||||
enum Evaluation {
|
||||
Break,
|
||||
Constructor(Type),
|
||||
Return(Option<Value>),
|
||||
}
|
||||
|
||||
impl Evaluation {
|
||||
pub fn value(self) -> Option<Value> {
|
||||
match self {
|
||||
Evaluation::Break => None,
|
||||
Evaluation::Return(value_option) => value_option,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -898,7 +947,11 @@ impl Evaluation {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum RuntimeError {
|
||||
ParseError(ParseError),
|
||||
Trace {
|
||||
Expression {
|
||||
error: Box<RuntimeError>,
|
||||
position: Span,
|
||||
},
|
||||
Statement {
|
||||
error: Box<RuntimeError>,
|
||||
position: Span,
|
||||
},
|
||||
@ -914,9 +967,17 @@ pub enum RuntimeError {
|
||||
error: BuiltInFunctionError,
|
||||
position: Span,
|
||||
},
|
||||
EnumVariantNotFound {
|
||||
identifier: Identifier,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedBoolean {
|
||||
position: Span,
|
||||
},
|
||||
ExpectedConstructor {
|
||||
actual: Type,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedIdentifier {
|
||||
position: Span,
|
||||
},
|
||||
@ -974,14 +1035,17 @@ impl RuntimeError {
|
||||
pub fn position(&self) -> Span {
|
||||
match self {
|
||||
Self::ParseError(parse_error) => parse_error.position(),
|
||||
Self::Trace { position, .. } => *position,
|
||||
Self::Expression { position, .. } => *position,
|
||||
Self::Statement { position, .. } => *position,
|
||||
Self::ValueError {
|
||||
left_position,
|
||||
right_position,
|
||||
..
|
||||
} => (left_position.0, right_position.1),
|
||||
Self::BuiltInFunctionError { position, .. } => *position,
|
||||
Self::EnumVariantNotFound { position, .. } => *position,
|
||||
Self::ExpectedBoolean { position } => *position,
|
||||
Self::ExpectedConstructor { position, .. } => *position,
|
||||
Self::ExpectedFunction { position, .. } => *position,
|
||||
Self::ExpectedIdentifier { position } => *position,
|
||||
Self::ExpectedIdentifierOrString { position } => *position,
|
||||
@ -1016,10 +1080,17 @@ impl Display for RuntimeError {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::ParseError(parse_error) => write!(f, "{}", parse_error),
|
||||
Self::Trace { error, position } => {
|
||||
Self::Expression { error, position } => {
|
||||
write!(
|
||||
f,
|
||||
"Error during execution at position: {:?}\n{}",
|
||||
"Error while running expression at {:?}: {}",
|
||||
position, error
|
||||
)
|
||||
}
|
||||
Self::Statement { error, position } => {
|
||||
write!(
|
||||
f,
|
||||
"Error while running statement at {:?}: {}",
|
||||
position, error
|
||||
)
|
||||
}
|
||||
@ -1037,9 +1108,26 @@ impl Display for RuntimeError {
|
||||
Self::BuiltInFunctionError { error, .. } => {
|
||||
write!(f, "{}", error)
|
||||
}
|
||||
Self::EnumVariantNotFound {
|
||||
identifier,
|
||||
position,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"Enum variant not found: {} at position: {:?}",
|
||||
identifier, position
|
||||
)
|
||||
}
|
||||
Self::ExpectedBoolean { position } => {
|
||||
write!(f, "Expected a boolean at position: {:?}", position)
|
||||
}
|
||||
Self::ExpectedConstructor { actual, position } => {
|
||||
write!(
|
||||
f,
|
||||
"Expected a constructor, but got {} at position: {:?}",
|
||||
actual, position
|
||||
)
|
||||
}
|
||||
Self::ExpectedFunction { actual, position } => {
|
||||
write!(
|
||||
f,
|
||||
@ -1110,7 +1198,7 @@ impl Display for RuntimeError {
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"Undefined variable {} at position: {:?}",
|
||||
"Undefined value {} at position: {:?}",
|
||||
identifier, position
|
||||
)
|
||||
}
|
||||
@ -1187,8 +1275,8 @@ mod tests {
|
||||
#[test]
|
||||
fn assign_tuple_struct_variable() {
|
||||
let input = "
|
||||
struct Foo(int)
|
||||
x = Foo(42)
|
||||
struct Foo(int);
|
||||
let x = Foo(42);
|
||||
x
|
||||
";
|
||||
|
||||
@ -1203,7 +1291,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn define_and_instantiate_tuple_struct() {
|
||||
let input = "struct Foo(int) Foo(42)";
|
||||
let input = "struct Foo(int); Foo(42)";
|
||||
|
||||
assert_eq!(
|
||||
run(input),
|
||||
@ -1232,7 +1320,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn define_and_instantiate_unit_struct() {
|
||||
let input = "struct Foo Foo";
|
||||
let input = "struct Foo; Foo";
|
||||
|
||||
assert_eq!(
|
||||
run(input),
|
||||
@ -1271,6 +1359,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn negate_expression() {
|
||||
env_logger::builder().is_test(true).try_init().unwrap();
|
||||
|
||||
let input = "let x = -42; -x";
|
||||
|
||||
assert_eq!(run(input), Ok(Some(Value::Integer(42))));
|
||||
@ -1292,7 +1382,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn map_property_access() {
|
||||
let input = "{ a = 42 }.a";
|
||||
let input = "map { a = 42 }.a";
|
||||
|
||||
assert_eq!(run(input), Ok(Some(Value::Integer(42))));
|
||||
}
|
||||
@ -1343,21 +1433,21 @@ mod tests {
|
||||
fn while_loop() {
|
||||
let input = "let 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::mutable_from(5))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subtract_assign() {
|
||||
let input = "let mut x = 1; x -= 1; x";
|
||||
|
||||
assert_eq!(run(input), Ok(Some(Value::mutable(Value::Integer(0)))));
|
||||
assert_eq!(run(input), Ok(Some(Value::mutable_from(0))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_assign() {
|
||||
let input = "let mut x = 1; x += 1; x";
|
||||
|
||||
assert_eq!(run(input), Ok(Some(Value::mutable(Value::Integer(2)))));
|
||||
assert_eq!(run(input), Ok(Some(Value::mutable_from(2))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -10,3 +10,4 @@ repository.workspace = true
|
||||
[dependencies]
|
||||
clap = { version = "4.5.14", features = ["derive"] }
|
||||
dust-lang = { path = "../dust-lang" }
|
||||
env_logger = "0.11.5"
|
||||
|
@ -8,21 +8,39 @@ struct Cli {
|
||||
#[arg(short, long)]
|
||||
command: Option<String>,
|
||||
|
||||
#[arg(short, long)]
|
||||
parse: bool,
|
||||
|
||||
path: Option<String>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
let args = Cli::parse();
|
||||
|
||||
if let Some(command) = &args.command {
|
||||
if args.parse {
|
||||
parse_and_display_errors(command);
|
||||
} else {
|
||||
run_and_display_errors(command);
|
||||
}
|
||||
} else if let Some(path) = &args.path {
|
||||
let source = read_to_string(path).expect("Failed to read file");
|
||||
|
||||
run_and_display_errors(&source)
|
||||
if args.parse {
|
||||
parse_and_display_errors(&source);
|
||||
} else {
|
||||
panic!("No command or path provided");
|
||||
};
|
||||
run_and_display_errors(&source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_and_display_errors(source: &str) {
|
||||
match dust_lang::parse(source) {
|
||||
Ok(ast) => println!("{:#?}", ast),
|
||||
Err(error) => eprintln!("{}", error.report()),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_and_display_errors(source: &str) {
|
||||
|
Loading…
Reference in New Issue
Block a user