1
0

Refine errors and error propagation

This commit is contained in:
Jeff 2024-08-20 11:07:13 -04:00
parent cf9a9837c8
commit 83f856385b
13 changed files with 587 additions and 353 deletions

View File

@ -11,11 +11,12 @@ use std::{
use crate::{ use crate::{
ast::{ ast::{
AbstractSyntaxTree, BlockExpression, CallExpression, ElseExpression, FieldAccessExpression, AbstractSyntaxTree, AstError, BlockExpression, CallExpression, ElseExpression,
IfExpression, LetStatement, ListExpression, ListIndexExpression, LoopExpression, FieldAccessExpression, IfExpression, LetStatement, ListExpression, ListIndexExpression,
MapExpression, Node, OperatorExpression, RangeExpression, Span, Statement, LoopExpression, MapExpression, Node, OperatorExpression, RangeExpression, Span, Statement,
StructDefinition, StructExpression, TupleAccessExpression, StructDefinition, StructExpression, TupleAccessExpression,
}, },
context::ContextError,
parse, Context, DustError, Expression, Identifier, StructType, Type, parse, Context, DustError, Expression, Identifier, StructType, Type,
}; };
@ -33,7 +34,7 @@ use crate::{
pub fn analyze(source: &str) -> Result<(), DustError> { pub fn analyze(source: &str) -> Result<(), DustError> {
let abstract_tree = parse(source)?; let abstract_tree = parse(source)?;
let context = Context::new(); let context = Context::new();
let mut analyzer = Analyzer::new(&abstract_tree, &context); let analyzer = Analyzer::new(&abstract_tree, context);
analyzer analyzer
.analyze() .analyze()
@ -58,18 +59,18 @@ pub fn analyze(source: &str) -> Result<(), DustError> {
/// assert!(result.is_err()); /// assert!(result.is_err());
pub struct Analyzer<'a> { pub struct Analyzer<'a> {
abstract_tree: &'a AbstractSyntaxTree, abstract_tree: &'a AbstractSyntaxTree,
context: &'a Context, context: Context,
} }
impl<'a> Analyzer<'a> { impl<'recovered, 'a: 'recovered> Analyzer<'a> {
pub fn new(abstract_tree: &'a AbstractSyntaxTree, context: &'a Context) -> Self { pub fn new(abstract_tree: &'a AbstractSyntaxTree, context: Context) -> Self {
Self { Self {
abstract_tree, abstract_tree,
context, context,
} }
} }
pub fn analyze(&mut self) -> Result<(), AnalysisError> { pub fn analyze(&'recovered self) -> Result<(), AnalysisError> {
for statement in &self.abstract_tree.statements { for statement in &self.abstract_tree.statements {
self.analyze_statement(statement)?; self.analyze_statement(statement)?;
} }
@ -77,7 +78,7 @@ impl<'a> Analyzer<'a> {
Ok(()) Ok(())
} }
fn analyze_statement(&mut self, statement: &Statement) -> Result<(), AnalysisError> { fn analyze_statement(&'recovered self, statement: &Statement) -> Result<(), AnalysisError> {
match statement { match statement {
Statement::Expression(expression) => self.analyze_expression(expression)?, Statement::Expression(expression) => self.analyze_expression(expression)?,
Statement::ExpressionNullified(expression_node) => { Statement::ExpressionNullified(expression_node) => {
@ -86,7 +87,7 @@ impl<'a> Analyzer<'a> {
Statement::Let(let_statement) => match &let_statement.inner { Statement::Let(let_statement) => match &let_statement.inner {
LetStatement::Let { identifier, value } LetStatement::Let { identifier, value }
| LetStatement::LetMut { identifier, value } => { | LetStatement::LetMut { identifier, value } => {
let r#type = value.return_type(self.context); let r#type = value.return_type(&self.context)?;
if let Some(r#type) = r#type { if let Some(r#type) = r#type {
self.context.set_variable_type( self.context.set_variable_type(
@ -113,7 +114,7 @@ impl<'a> Analyzer<'a> {
name: name.inner.clone(), name: name.inner.clone(),
}, },
name.position, name.position,
), )?,
StructDefinition::Tuple { name, items } => { StructDefinition::Tuple { name, items } => {
let fields = items.iter().map(|item| item.inner.clone()).collect(); let fields = items.iter().map(|item| item.inner.clone()).collect();
@ -124,7 +125,7 @@ impl<'a> Analyzer<'a> {
fields, fields,
}, },
name.position, name.position,
); )?;
} }
StructDefinition::Fields { name, fields } => { StructDefinition::Fields { name, fields } => {
let fields = fields let fields = fields
@ -141,7 +142,7 @@ impl<'a> Analyzer<'a> {
fields, fields,
}, },
name.position, name.position,
); )?;
} }
}, },
} }
@ -149,7 +150,7 @@ impl<'a> Analyzer<'a> {
Ok(()) Ok(())
} }
fn analyze_expression(&mut self, expression: &Expression) -> Result<(), AnalysisError> { fn analyze_expression(&'recovered self, expression: &Expression) -> Result<(), AnalysisError> {
match expression { match expression {
Expression::Block(block_expression) => self.analyze_block(&block_expression.inner)?, Expression::Block(block_expression) => self.analyze_block(&block_expression.inner)?,
Expression::Call(call_expression) => { Expression::Call(call_expression) => {
@ -235,8 +236,8 @@ impl<'a> Analyzer<'a> {
self.analyze_expression(assignee)?; self.analyze_expression(assignee)?;
self.analyze_expression(modifier)?; self.analyze_expression(modifier)?;
let expected_type = assignee.return_type(self.context); let expected_type = assignee.return_type(&self.context)?;
let actual_type = modifier.return_type(self.context); let actual_type = modifier.return_type(&self.context)?;
if expected_type.is_none() { if expected_type.is_none() {
return Err(AnalysisError::ExpectedValueFromExpression { return Err(AnalysisError::ExpectedValueFromExpression {
@ -308,7 +309,10 @@ impl<'a> Analyzer<'a> {
Ok(()) Ok(())
} }
fn analyze_block(&mut self, block_expression: &BlockExpression) -> Result<(), AnalysisError> { fn analyze_block(
&'recovered self,
block_expression: &BlockExpression,
) -> Result<(), AnalysisError> {
match block_expression { match block_expression {
BlockExpression::Async(statements) => { BlockExpression::Async(statements) => {
for statement in statements { for statement in statements {
@ -325,7 +329,7 @@ impl<'a> Analyzer<'a> {
Ok(()) Ok(())
} }
fn analyze_if(&mut self, if_expression: &IfExpression) -> Result<(), AnalysisError> { fn analyze_if(&'recovered self, if_expression: &IfExpression) -> Result<(), AnalysisError> {
match if_expression { match if_expression {
IfExpression::If { IfExpression::If {
condition, condition,
@ -359,6 +363,8 @@ impl<'a> Analyzer<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum AnalysisError { pub enum AnalysisError {
AstError(AstError),
ContextError(ContextError),
ExpectedBoolean { ExpectedBoolean {
actual: Statement, actual: Statement,
}, },
@ -418,9 +424,24 @@ pub enum AnalysisError {
}, },
} }
impl From<AstError> for AnalysisError {
fn from(v: AstError) -> Self {
Self::AstError(v)
}
}
impl From<ContextError> for AnalysisError {
fn from(context_error: ContextError) -> Self {
Self::ContextError(context_error)
}
}
impl AnalysisError { impl AnalysisError {
pub fn position(&self) -> Span { pub fn position(&self) -> Option<Span> {
match self { let position = match self {
AnalysisError::AstError(ast_error) => return None,
AnalysisError::ContextError(context_error) => return None,
AnalysisError::ExpectedBoolean { actual } => actual.position(), AnalysisError::ExpectedBoolean { actual } => actual.position(),
AnalysisError::ExpectedIdentifier { actual } => actual.position(), AnalysisError::ExpectedIdentifier { actual } => actual.position(),
AnalysisError::ExpectedIdentifierOrString { actual } => actual.position(), AnalysisError::ExpectedIdentifierOrString { actual } => actual.position(),
@ -439,7 +460,9 @@ impl AnalysisError {
AnalysisError::UndefinedVariable { identifier } => identifier.position, AnalysisError::UndefinedVariable { identifier } => identifier.position,
AnalysisError::UnexpectedIdentifier { identifier } => identifier.position, AnalysisError::UnexpectedIdentifier { identifier } => identifier.position,
AnalysisError::UnexectedString { actual } => actual.position(), AnalysisError::UnexectedString { actual } => actual.position(),
} };
Some(position)
} }
} }
@ -448,6 +471,8 @@ impl Error for AnalysisError {}
impl Display for AnalysisError { impl Display for AnalysisError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
AnalysisError::AstError(ast_error) => write!(f, "{}", ast_error),
AnalysisError::ContextError(context_error) => write!(f, "{}", context_error),
AnalysisError::ExpectedBoolean { actual, .. } => { AnalysisError::ExpectedBoolean { actual, .. } => {
write!(f, "Expected boolean, found {}", actual) write!(f, "Expected boolean, found {}", actual)
} }

View File

@ -6,9 +6,12 @@ use std::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{BuiltInFunction, Context, FunctionType, Identifier, RangeableType, StructType, Type}; use crate::{
BuiltInFunction, Context, ContextError, FunctionType, Identifier, RangeableType, StructType,
Type,
};
use super::{Node, Span, Statement}; use super::{AstError, Node, Span, Statement};
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Expression { pub enum Expression {
@ -263,15 +266,18 @@ impl Expression {
} }
} }
pub fn return_type(&self, context: &Context) -> Option<Type> { pub fn return_type<'recovered>(
match self { &self,
context: &'recovered Context,
) -> Result<Option<Type>, AstError> {
let return_type = match self {
Expression::Block(block_expression) => { Expression::Block(block_expression) => {
Some(block_expression.inner.return_type(context)?) return block_expression.inner.return_type(context)
} }
Expression::Call(call_expression) => { Expression::Call(call_expression) => {
let CallExpression { invoker, .. } = call_expression.inner.as_ref(); let CallExpression { invoker, .. } = call_expression.inner.as_ref();
let invoker_type = invoker.return_type(context); let invoker_type = invoker.return_type(context)?;
if let Some(Type::Function(FunctionType { return_type, .. })) = invoker_type { if let Some(Type::Function(FunctionType { return_type, .. })) = invoker_type {
return_type.map(|r#type| *r#type) return_type.map(|r#type| *r#type)
@ -285,7 +291,7 @@ impl Expression {
let FieldAccessExpression { container, field } = let FieldAccessExpression { container, field } =
field_access_expression.inner.as_ref(); field_access_expression.inner.as_ref();
let container_type = container.return_type(context); let container_type = container.return_type(context)?;
if let Some(Type::Struct(StructType::Fields { fields, .. })) = container_type { if let Some(Type::Struct(StructType::Fields { fields, .. })) = container_type {
fields fields
@ -296,28 +302,41 @@ impl Expression {
None None
} }
} }
Expression::Grouped(expression) => expression.inner.return_type(context), Expression::Grouped(expression) => expression.inner.return_type(context)?,
Expression::Identifier(identifier) => context.get_type(&identifier.inner), Expression::Identifier(identifier) => context.get_type(&identifier.inner)?,
Expression::If(if_expression) => { Expression::If(if_expression) => match if_expression.inner.as_ref() {
return match if_expression.inner.as_ref() {
IfExpression::If { .. } => None, IfExpression::If { .. } => None,
IfExpression::IfElse { if_block, .. } => if_block.inner.return_type(context), IfExpression::IfElse { if_block, .. } => if_block.inner.return_type(context)?,
} },
}
Expression::List(list_expression) => match list_expression.inner.as_ref() { Expression::List(list_expression) => match list_expression.inner.as_ref() {
ListExpression::AutoFill { repeat_operand, .. } => { ListExpression::AutoFill { repeat_operand, .. } => {
let item_type = repeat_operand.return_type(context)?; let item_type = repeat_operand.return_type(context)?;
if let Some(r#type) = item_type {
Some(Type::ListOf { Some(Type::ListOf {
item_type: Box::new(item_type), item_type: Box::new(r#type),
}) })
} else {
return Err(AstError::ExpectedType {
position: repeat_operand.position(),
});
}
} }
ListExpression::Ordered(expressions) => { ListExpression::Ordered(expressions) => {
if expressions.is_empty() { if expressions.is_empty() {
return Some(Type::ListEmpty); return Ok(Some(Type::ListEmpty));
} }
let item_type = expressions.last().unwrap().return_type(context)?; let item_type = expressions
.first()
.ok_or_else(|| AstError::ExpectedNonEmptyList {
position: self.position(),
})?
.return_type(context)?
.ok_or_else(|| AstError::ExpectedType {
position: expressions.first().unwrap().position(),
})?;
let length = expressions.len(); let length = expressions.len();
Some(Type::List { Some(Type::List {
@ -329,7 +348,11 @@ impl Expression {
Expression::ListIndex(list_index_expression) => { Expression::ListIndex(list_index_expression) => {
let ListIndexExpression { list, .. } = list_index_expression.inner.as_ref(); let ListIndexExpression { list, .. } = list_index_expression.inner.as_ref();
let list_type = list.return_type(context)?; let list_type =
list.return_type(context)?
.ok_or_else(|| AstError::ExpectedType {
position: list.position(),
})?;
if let Type::List { item_type, .. } = list_type { if let Type::List { item_type, .. } = list_type {
Some(*item_type) Some(*item_type)
@ -350,9 +373,9 @@ impl Expression {
LiteralExpression::String(_) => Some(Type::String), LiteralExpression::String(_) => Some(Type::String),
}, },
Expression::Loop(loop_expression) => match loop_expression.inner.as_ref() { Expression::Loop(loop_expression) => match loop_expression.inner.as_ref() {
LoopExpression::For { block, .. } => block.inner.return_type(context), LoopExpression::For { block, .. } => block.inner.return_type(context)?,
LoopExpression::Infinite { .. } => None, LoopExpression::Infinite { .. } => None,
LoopExpression::While { block, .. } => block.inner.return_type(context), LoopExpression::While { block, .. } => block.inner.return_type(context)?,
}, },
Expression::Map(map_expression) => { Expression::Map(map_expression) => {
let MapExpression { pairs } = map_expression.inner.as_ref(); let MapExpression { pairs } = map_expression.inner.as_ref();
@ -360,7 +383,12 @@ impl Expression {
let mut types = HashMap::with_capacity(pairs.len()); let mut types = HashMap::with_capacity(pairs.len());
for (key, value) in pairs { for (key, value) in pairs {
let value_type = value.return_type(context)?; let value_type =
value
.return_type(context)?
.ok_or_else(|| AstError::ExpectedType {
position: value.position(),
})?;
types.insert(key.inner.clone(), value_type); types.insert(key.inner.clone(), value_type);
} }
@ -371,10 +399,12 @@ impl Expression {
OperatorExpression::Assignment { .. } => None, OperatorExpression::Assignment { .. } => None,
OperatorExpression::Comparison { .. } => Some(Type::Boolean), OperatorExpression::Comparison { .. } => Some(Type::Boolean),
OperatorExpression::CompoundAssignment { .. } => None, OperatorExpression::CompoundAssignment { .. } => None,
OperatorExpression::ErrorPropagation(expression) => expression.return_type(context), OperatorExpression::ErrorPropagation(expression) => {
OperatorExpression::Negation(expression) => expression.return_type(context), expression.return_type(context)?
}
OperatorExpression::Negation(expression) => expression.return_type(context)?,
OperatorExpression::Not(_) => Some(Type::Boolean), OperatorExpression::Not(_) => Some(Type::Boolean),
OperatorExpression::Math { left, .. } => left.return_type(context), OperatorExpression::Math { left, .. } => left.return_type(context)?,
OperatorExpression::Logic { .. } => Some(Type::Boolean), OperatorExpression::Logic { .. } => Some(Type::Boolean),
}, },
Expression::Range(range_expression) => { Expression::Range(range_expression) => {
@ -382,13 +412,22 @@ impl Expression {
RangeExpression::Exclusive { start, .. } => start, RangeExpression::Exclusive { start, .. } => start,
RangeExpression::Inclusive { start, .. } => start, RangeExpression::Inclusive { start, .. } => start,
}; };
let start_type = start.return_type(context)?; let start_type =
start
.return_type(context)?
.ok_or_else(|| AstError::ExpectedType {
position: start.position(),
})?;
let rangeable_type = match start_type { let rangeable_type = match start_type {
Type::Byte => RangeableType::Byte, Type::Byte => RangeableType::Byte,
Type::Character => RangeableType::Character, Type::Character => RangeableType::Character,
Type::Float => RangeableType::Float, Type::Float => RangeableType::Float,
Type::Integer => RangeableType::Integer, Type::Integer => RangeableType::Integer,
_ => return None, _ => {
return Err(AstError::ExpectedRangeableType {
position: start.position(),
})
}
}; };
Some(Type::Range { Some(Type::Range {
@ -400,7 +439,11 @@ impl Expression {
let mut types = HashMap::with_capacity(fields.len()); let mut types = HashMap::with_capacity(fields.len());
for (field, expression) in fields { for (field, expression) in fields {
let r#type = expression.return_type(context)?; let r#type = expression.return_type(context)?.ok_or_else(|| {
AstError::ExpectedType {
position: expression.position(),
}
})?;
types.insert(field.inner.clone(), r#type); types.insert(field.inner.clone(), r#type);
} }
@ -413,15 +456,24 @@ impl Expression {
}, },
Expression::TupleAccess(tuple_access_expression) => { Expression::TupleAccess(tuple_access_expression) => {
let TupleAccessExpression { tuple, index } = tuple_access_expression.inner.as_ref(); let TupleAccessExpression { tuple, index } = tuple_access_expression.inner.as_ref();
let tuple_value = tuple.return_type(context)?; let tuple_value =
tuple
.return_type(context)?
.ok_or_else(|| AstError::ExpectedType {
position: tuple.position(),
})?;
if let Type::Tuple(fields) = tuple_value { if let Type::Tuple(fields) = tuple_value {
fields.get(index.inner).cloned() fields.get(index.inner).cloned()
} else { } else {
None Err(AstError::ExpectedTupleType {
} position: tuple.position(),
})?
} }
} }
};
Ok(return_type)
} }
pub fn position(&self) -> Span { pub fn position(&self) -> Span {
@ -975,13 +1027,16 @@ pub enum BlockExpression {
} }
impl BlockExpression { impl BlockExpression {
fn return_type(&self, context: &Context) -> Option<Type> { fn return_type<'recovered>(
&self,
context: &'recovered Context,
) -> Result<Option<Type>, AstError> {
match self { match self {
BlockExpression::Async(statements) | BlockExpression::Sync(statements) => { BlockExpression::Async(statements) | BlockExpression::Sync(statements) => {
if let Some(statement) = statements.last() { if let Some(statement) = statements.last() {
statement.return_type(context) statement.return_type(context)
} else { } else {
None Ok(None)
} }
} }
} }

View File

@ -12,6 +12,8 @@ use std::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ContextError, Type};
pub type Span = (usize, usize); pub type Span = (usize, usize);
/// In-memory representation of a Dust program. /// In-memory representation of a Dust program.
@ -57,3 +59,48 @@ impl<T: Display> Display for Node<T> {
write!(f, "{}", self.inner) write!(f, "{}", self.inner)
} }
} }
#[derive(Debug, Clone, PartialEq)]
pub enum AstError {
ContextError(ContextError),
ExpectedType { position: Span },
ExpectedTupleType { position: Span },
ExpectedNonEmptyList { position: Span },
ExpectedRangeableType { position: Span },
}
impl AstError {
pub fn position(&self) -> Option<Span> {
match self {
AstError::ContextError(error) => None,
AstError::ExpectedType { position } => Some(*position),
AstError::ExpectedTupleType { position } => Some(*position),
AstError::ExpectedNonEmptyList { position } => Some(*position),
AstError::ExpectedRangeableType { position } => Some(*position),
}
}
}
impl From<ContextError> for AstError {
fn from(v: ContextError) -> Self {
Self::ContextError(v)
}
}
impl Display for AstError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
AstError::ContextError(error) => write!(f, "{}", error),
AstError::ExpectedType { position } => write!(f, "Expected a type at {:?}", position),
AstError::ExpectedTupleType { position } => {
write!(f, "Expected a tuple type at {:?}", position)
}
AstError::ExpectedNonEmptyList { position } => {
write!(f, "Expected a non-empty list at {:?}", position)
}
AstError::ExpectedRangeableType { position } => {
write!(f, "Expected a rangeable type at {:?}", position)
}
}
}
}

View File

@ -2,9 +2,9 @@ use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{Context, Identifier, Type}; use crate::{Context, ContextError, Identifier, Type};
use super::{Expression, Node, Span}; use super::{AstError, Expression, Node, Span};
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Statement { pub enum Statement {
@ -28,12 +28,15 @@ impl Statement {
} }
} }
pub fn return_type(&self, context: &Context) -> Option<Type> { pub fn return_type<'recovered>(
&self,
context: &'recovered Context,
) -> Result<Option<Type>, AstError> {
match self { match self {
Statement::Expression(expression) => expression.return_type(context), Statement::Expression(expression) => expression.return_type(context),
Statement::ExpressionNullified(_) => None, Statement::ExpressionNullified(_) => Ok(None),
Statement::Let(_) => None, Statement::Let(_) => Ok(None),
Statement::StructDefinition(_) => None, Statement::StructDefinition(_) => Ok(None),
} }
} }
} }

View File

@ -2,7 +2,7 @@
use std::{ use std::{
error::Error, error::Error,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
io::{self, stdin}, io::{self, stdin, stdout, Write},
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -13,7 +13,7 @@ use crate::{Identifier, Type, Value};
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum BuiltInFunction { pub enum BuiltInFunction {
// String tools // String tools
ToString { argument: Box<Value> }, ToString,
// Integer and float tools // Integer and float tools
IsEven, IsEven,
@ -82,75 +82,57 @@ impl BuiltInFunction {
_type_arguments: Option<Vec<Type>>, _type_arguments: Option<Vec<Type>>,
value_arguments: Option<Vec<Value>>, value_arguments: Option<Vec<Value>>,
) -> Result<Option<Value>, BuiltInFunctionError> { ) -> Result<Option<Value>, BuiltInFunctionError> {
match (self.value_parameters(), &value_arguments) {
(Some(value_parameters), Some(value_arguments)) => {
if value_parameters.len() != value_arguments.len() {
return Err(BuiltInFunctionError::WrongNumberOfValueArguments);
}
}
(Some(_), None) | (None, Some(_)) => {
return Err(BuiltInFunctionError::WrongNumberOfValueArguments);
}
(None, None) => {}
}
let value_arguments = value_arguments.unwrap();
match self { match self {
BuiltInFunction::ToString { argument } => Ok(Some(Value::string(argument))), BuiltInFunction::ToString => Ok(Some(Value::String(value_arguments[0].to_string()))),
BuiltInFunction::IsEven => { BuiltInFunction::IsEven => {
if let Some(value_arguments) = value_arguments {
if value_arguments.len() == 1 {
if let Some(integer) = value_arguments[0].as_integer() { if let Some(integer) = value_arguments[0].as_integer() {
Ok(Some(Value::Boolean(integer % 2 == 0))) Ok(Some(Value::Boolean(integer % 2 == 0)))
} else { } else {
Err(BuiltInFunctionError::ExpectedInteger) Err(BuiltInFunctionError::ExpectedInteger)
} }
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
}
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
}
} }
BuiltInFunction::IsOdd => { BuiltInFunction::IsOdd => {
if let Some(value_arguments) = value_arguments {
if value_arguments.len() == 1 {
if let Some(integer) = value_arguments[0].as_integer() { if let Some(integer) = value_arguments[0].as_integer() {
Ok(Some(Value::Boolean(integer % 2 != 0))) Ok(Some(Value::Boolean(integer % 2 != 0)))
} else { } else {
Err(BuiltInFunctionError::ExpectedInteger) Err(BuiltInFunctionError::ExpectedInteger)
} }
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
}
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
}
} }
BuiltInFunction::Length => { BuiltInFunction::Length => {
if let Some(value_arguments) = value_arguments {
if value_arguments.len() == 1 {
if let Value::List(list) = &value_arguments[0] { if let Value::List(list) = &value_arguments[0] {
Ok(Some(Value::Integer(list.len() as i64))) Ok(Some(Value::Integer(list.len() as i64)))
} else { } else {
Err(BuiltInFunctionError::ExpectedInteger) Err(BuiltInFunctionError::ExpectedList)
}
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
}
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
} }
} }
BuiltInFunction::ReadLine => { BuiltInFunction::ReadLine => {
if value_arguments.is_none() {
let mut input = String::new(); let mut input = String::new();
stdin().read_line(&mut input)?; stdin().read_line(&mut input)?;
Ok(Some(Value::string(input.trim_end_matches('\n')))) Ok(Some(Value::string(input.trim_end_matches('\n'))))
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
}
} }
BuiltInFunction::WriteLine => { BuiltInFunction::WriteLine => {
if let Some(value_arguments) = value_arguments { if let Value::String(string) = &value_arguments[0] {
if value_arguments.len() == 1 { stdout().write_all(string.as_bytes())?;
println!("{}", value_arguments[0]);
Ok(None) Ok(None)
} else { } else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments) Err(BuiltInFunctionError::ExpectedString)
}
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
} }
} }
} }
@ -165,8 +147,12 @@ impl Display for BuiltInFunction {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum BuiltInFunctionError { pub enum BuiltInFunctionError {
ExpectedInteger,
Io(io::ErrorKind), Io(io::ErrorKind),
ExpectedString,
ExpectedList,
ExpectedInteger,
WrongNumberOfValueArguments, WrongNumberOfValueArguments,
} }
@ -181,8 +167,10 @@ impl Error for BuiltInFunctionError {}
impl Display for BuiltInFunctionError { impl Display for BuiltInFunctionError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self { match self {
BuiltInFunctionError::ExpectedInteger => write!(f, "Expected an integer"),
BuiltInFunctionError::Io(error_kind) => write!(f, "I/O error: {}", error_kind), BuiltInFunctionError::Io(error_kind) => write!(f, "I/O error: {}", error_kind),
BuiltInFunctionError::ExpectedInteger => write!(f, "Expected an integer"),
BuiltInFunctionError::ExpectedString => write!(f, "Expected a string"),
BuiltInFunctionError::ExpectedList => write!(f, "Expected a list"),
BuiltInFunctionError::WrongNumberOfValueArguments => { BuiltInFunctionError::WrongNumberOfValueArguments => {
write!(f, "Wrong number of value arguments") write!(f, "Wrong number of value arguments")
} }

View File

@ -1,150 +1,187 @@
//! Garbage-collecting context for variables. //! Garbage-collecting context for variables.
use std::{ use std::{
collections::HashMap, collections::HashMap,
sync::{Arc, PoisonError as StdPoisonError, RwLock, RwLockWriteGuard}, fmt::{self, Display, Formatter},
sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard},
}; };
use crate::{ast::Span, Constructor, Identifier, StructType, Type, Value}; use crate::{ast::Span, Constructor, Identifier, StructType, Type, Value};
pub type Variables = HashMap<Identifier, (ContextData, Span)>; pub type Associations = HashMap<Identifier, (ContextData, Span)>;
/// Garbage-collecting context for variables. /// Garbage-collecting context for variables.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Context { pub struct Context {
variables: Arc<RwLock<Variables>>, associations: Arc<RwLock<Associations>>,
} }
impl Context { impl Context {
pub fn new() -> Self { pub fn new() -> Self {
Self::with_data(HashMap::new())
}
pub fn with_data(data: Associations) -> Self {
Self { Self {
variables: Arc::new(RwLock::new(HashMap::new())), associations: Arc::new(RwLock::new(data)),
} }
} }
/// Creates a deep copy of another context. /// Creates a deep copy of another context.
pub fn with_data_from(other: &Self) -> Self { pub fn with_data_from(other: &Self) -> Result<Self, ContextError> {
Self { Ok(Self::with_data(other.associations.read()?.clone()))
variables: Arc::new(RwLock::new(other.variables.read().unwrap().clone())),
}
} }
/// Returns the number of variables in the context. /// Returns the number of associated identifiers in the context.
pub fn variable_count(&self) -> usize { pub fn association_count(&self) -> Result<usize, ContextError> {
self.variables.read().unwrap().len() Ok(self.associations.read()?.len())
} }
/// Returns a boolean indicating whether the context contains the variable. /// Returns a boolean indicating whether the identifier is in the context.
pub fn contains(&self, identifier: &Identifier) -> bool { pub fn contains(&self, identifier: &Identifier) -> Result<bool, ContextError> {
self.variables.read().unwrap().contains_key(identifier) Ok(self.associations.read()?.contains_key(identifier))
} }
/// Returns the full VariableData and Span if the context contains the given identifier. /// Returns the full ContextData and Span if the context contains the given identifier.
pub fn get(&self, identifier: &Identifier) -> Option<(ContextData, Span)> { pub fn get(
self.variables.read().unwrap().get(identifier).cloned() &self,
identifier: &Identifier,
) -> Result<Option<(ContextData, Span)>, ContextError> {
let associations = self.associations.read()?;
Ok(associations.get(identifier).cloned())
} }
/// Returns the type of the variable with the given identifier. /// Returns the type associated with the given identifier.
pub fn get_type(&self, identifier: &Identifier) -> Option<Type> { pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, ContextError> {
match self.variables.read().unwrap().get(identifier) { let r#type = match self.associations.read()?.get(identifier) {
Some((ContextData::VariableType(r#type), _)) => Some(r#type.clone()), Some((ContextData::VariableType(r#type), _)) => r#type.clone(),
Some((ContextData::VariableValue(value), _)) => Some(value.r#type()), Some((ContextData::VariableValue(value), _)) => value.r#type(),
Some((ContextData::ConstructorType(struct_type), _)) => { Some((ContextData::ConstructorType(struct_type), _)) => {
Some(Type::Struct(struct_type.clone())) Type::Struct(struct_type.clone())
} }
_ => None, _ => return Ok(None),
};
Ok(Some(r#type))
}
/// Returns the ContextData associated with the identifier.
pub fn get_data(&self, identifier: &Identifier) -> Result<Option<ContextData>, ContextError> {
match self.associations.read()?.get(identifier) {
Some((variable_data, _)) => Ok(Some(variable_data.clone())),
_ => Ok(None),
} }
} }
/// Returns the VariableData of the variable with the given identifier. /// Returns the value associated with the identifier.
pub fn get_data(&self, identifier: &Identifier) -> Option<ContextData> { pub fn get_variable_value(
match self.variables.read().unwrap().get(identifier) { &self,
Some((variable_data, _)) => Some(variable_data.clone()), identifier: &Identifier,
_ => None, ) -> Result<Option<Value>, ContextError> {
match self.associations.read().unwrap().get(identifier) {
Some((ContextData::VariableValue(value), _)) => Ok(Some(value.clone())),
_ => Ok(None),
} }
} }
/// Returns the value of the variable with the given identifier. /// Returns the constructor associated with the identifier.
pub fn get_variable_value(&self, identifier: &Identifier) -> Option<Value> { pub fn get_constructor(
match self.variables.read().unwrap().get(identifier) { &self,
Some((ContextData::VariableValue(value), _)) => Some(value.clone()), identifier: &Identifier,
_ => None, ) -> Result<Option<Constructor>, ContextError> {
match self.associations.read().unwrap().get(identifier) {
Some((ContextData::Constructor(constructor), _)) => Ok(Some(constructor.clone())),
_ => Ok(None),
} }
} }
/// Returns the constructor associated with the given identifier. /// Associates an identifier with a variable type, with a position given for garbage collection.
pub fn get_constructor(&self, identifier: &Identifier) -> Option<Constructor> { pub fn set_variable_type(
match self.variables.read().unwrap().get(identifier) { &self,
Some((ContextData::Constructor(constructor), _)) => Some(constructor.clone()), identifier: Identifier,
_ => None, r#type: Type,
} position: Span,
} ) -> Result<(), ContextError> {
/// Sets a variable to a type, with a position given for garbage collection.
pub fn set_variable_type(&self, identifier: Identifier, r#type: Type, position: Span) {
log::trace!("Setting {identifier} to type {type} at {position:?}"); log::trace!("Setting {identifier} to type {type} at {position:?}");
self.variables self.associations
.write() .write()?
.unwrap()
.insert(identifier, (ContextData::VariableType(r#type), position)); .insert(identifier, (ContextData::VariableType(r#type), position));
Ok(())
} }
/// Sets a variable to a value. /// Associates an identifier with a variable value.
pub fn set_variable_value(&self, identifier: Identifier, value: Value) { pub fn set_variable_value(
&self,
identifier: Identifier,
value: Value,
) -> Result<(), ContextError> {
log::trace!("Setting {identifier} to value {value}"); log::trace!("Setting {identifier} to value {value}");
let mut variables = self.variables.write().unwrap(); let mut associations = self.associations.write()?;
let last_position = variables let last_position = associations
.get(&identifier) .get(&identifier)
.map(|(_, last_position)| *last_position) .map(|(_, last_position)| *last_position)
.unwrap_or_default(); .unwrap_or_default();
variables.insert( associations.insert(
identifier, identifier,
(ContextData::VariableValue(value), last_position), (ContextData::VariableValue(value), last_position),
); );
Ok(())
} }
/// Associates a constructor with an identifier. /// Associates an identifier with a constructor.
pub fn set_constructor(&self, identifier: Identifier, constructor: Constructor) { pub fn set_constructor(
&self,
identifier: Identifier,
constructor: Constructor,
) -> Result<(), ContextError> {
log::trace!("Setting {identifier} to constructor {constructor}"); log::trace!("Setting {identifier} to constructor {constructor}");
let mut variables = self.variables.write().unwrap(); let mut associations = self.associations.write()?;
let last_position = variables let last_position = associations
.get(&identifier) .get(&identifier)
.map(|(_, last_position)| *last_position) .map(|(_, last_position)| *last_position)
.unwrap_or_default(); .unwrap_or_default();
variables.insert( associations.insert(
identifier, identifier,
(ContextData::Constructor(constructor), last_position), (ContextData::Constructor(constructor), last_position),
); );
Ok(())
} }
/// Associates a constructor type with an identifier. /// Associates an identifier with a constructor type, with a position given for garbage
/// collection.
pub fn set_constructor_type( pub fn set_constructor_type(
&self, &self,
identifier: Identifier, identifier: Identifier,
struct_type: StructType, struct_type: StructType,
position: Span, position: Span,
) { ) -> Result<(), ContextError> {
log::trace!("Setting {identifier} to constructor of type {struct_type}"); log::trace!("Setting {identifier} to constructor of type {struct_type}");
let mut variables = self.variables.write().unwrap(); let mut variables = self.associations.write()?;
variables.insert( variables.insert(
identifier, identifier,
(ContextData::ConstructorType(struct_type), position), (ContextData::ConstructorType(struct_type), position),
); );
Ok(())
} }
/// Collects garbage up to the given position, removing all variables with lesser positions. /// Collects garbage up to the given position, removing all variables with lesser positions.
pub fn collect_garbage(&self, position: Span) { pub fn collect_garbage(&self, position: Span) -> Result<(), ContextError> {
log::trace!("Collecting garbage up to {position:?}"); log::trace!("Collecting garbage up to {position:?}");
let mut variables = self.variables.write().unwrap(); let mut variables = self.associations.write()?;
variables.retain(|identifier, (_, last_used)| { variables.retain(|identifier, (_, last_used)| {
let should_drop = position.0 > last_used.0 && position.1 > last_used.1; let should_drop = position.0 > last_used.0 && position.1 > last_used.1;
@ -156,19 +193,25 @@ impl Context {
!should_drop !should_drop
}); });
variables.shrink_to_fit(); variables.shrink_to_fit();
Ok(())
} }
/// Updates a variable's last known position, allowing it to live longer in the program. /// Updates an associated identifier's last known position, allowing it to live longer in the
/// Returns a boolean indicating whether the variable was found. /// program. Returns a boolean indicating whether the identifier.
pub fn update_last_position(&self, identifier: &Identifier, position: Span) -> bool { pub fn update_last_position(
if let Some((_, last_position)) = self.variables.write().unwrap().get_mut(identifier) { &self,
identifier: &Identifier,
position: Span,
) -> Result<bool, ContextError> {
if let Some((_, last_position)) = self.associations.write()?.get_mut(identifier) {
*last_position = position; *last_position = position;
log::trace!("Updating {identifier}'s last position to {position:?}"); log::trace!("Updating {identifier}'s last position to {position:?}");
true Ok(true)
} else { } else {
false Ok(false)
} }
} }
@ -176,17 +219,18 @@ impl Context {
/// ///
/// This method is not used. The context's other methods do not return poison errors because /// This method is not used. The context's other methods do not return poison errors because
/// they are infallible. /// they are infallible.
pub fn _recover_from_poison(&mut self, error: &ContextPoisonError) { pub fn recover_from_poison(&mut self, error: &ContextError) {
log::debug!("Context is recovering from poison error"); log::debug!("Context is recovering from poison error");
let recovered = error.get_ref(); let ContextError::PoisonErrorRecovered(recovered) = error;
let mut new_variables = HashMap::new();
for (identifier, (variable_data, position)) in recovered.iter() { let mut new_associations = HashMap::new();
new_variables.insert(identifier.clone(), (variable_data.clone(), *position));
for (identifier, (context_data, position)) in recovered.as_ref() {
new_associations.insert(identifier.clone(), (context_data.clone(), *position));
} }
self.variables = Arc::new(RwLock::new(new_variables)); self.associations = Arc::new(RwLock::new(new_associations));
} }
} }
@ -204,7 +248,50 @@ pub enum ContextData {
VariableType(Type), VariableType(Type),
} }
pub type ContextPoisonError<'err> = StdPoisonError<RwLockWriteGuard<'err, Variables>>; #[derive(Debug, Clone)]
pub enum ContextError {
PoisonErrorRecovered(Arc<Associations>),
}
impl From<PoisonError<RwLockWriteGuard<'_, Associations>>> for ContextError {
fn from(error: PoisonError<RwLockWriteGuard<'_, Associations>>) -> Self {
let associations = error.into_inner().clone();
Self::PoisonErrorRecovered(Arc::new(associations))
}
}
impl From<PoisonError<RwLockReadGuard<'_, Associations>>> for ContextError {
fn from(error: PoisonError<RwLockReadGuard<'_, Associations>>) -> Self {
let associations = error.into_inner().clone();
Self::PoisonErrorRecovered(Arc::new(associations))
}
}
impl PartialEq for ContextError {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::PoisonErrorRecovered(left), Self::PoisonErrorRecovered(right)) => {
Arc::ptr_eq(left, right)
}
}
}
}
impl Display for ContextError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::PoisonErrorRecovered(associations) => {
write!(
f,
"Context poisoned with {} associations recovered",
associations.len()
)
}
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -226,7 +313,7 @@ mod tests {
run_with_context(source, context.clone()).unwrap(); run_with_context(source, context.clone()).unwrap();
assert_eq!(context.variable_count(), 0); assert_eq!(context.association_count().unwrap(), 0);
} }
#[test] #[test]
@ -242,6 +329,6 @@ mod tests {
run_with_context(source, context.clone()).unwrap(); run_with_context(source, context.clone()).unwrap();
assert_eq!(context.variable_count(), 0); assert_eq!(context.association_count().unwrap(), 0);
} }
} }

View File

@ -0,0 +1,30 @@
use std::{collections::HashMap, sync::OnceLock};
use crate::{BuiltInFunction, Context, ContextData, Function, Identifier, Value};
static CORE_LIBRARY: OnceLock<Context> = OnceLock::new();
pub fn core_library() -> &'static Context {
CORE_LIBRARY.get_or_init(|| {
Context::with_data(HashMap::from([
(
Identifier::new("to_string"),
(
ContextData::VariableValue(Value::Function(Function::BuiltIn(
BuiltInFunction::ToString,
))),
(0, 0),
),
),
(
Identifier::new("is_even"),
(
ContextData::VariableValue(Value::Function(Function::BuiltIn(
BuiltInFunction::IsEven,
))),
(0, 0),
),
),
]))
})
}

View File

@ -64,7 +64,7 @@ impl<'src> DustError<'src> {
pub fn position(&self) -> Option<Span> { pub fn position(&self) -> Option<Span> {
match self { match self {
DustError::Runtime { runtime_error, .. } => runtime_error.position(), DustError::Runtime { runtime_error, .. } => runtime_error.position(),
DustError::Analysis { analysis_error, .. } => Some(analysis_error.position()), DustError::Analysis { analysis_error, .. } => analysis_error.position(),
DustError::Parse { parse_error, .. } => Some(parse_error.position()), DustError::Parse { parse_error, .. } => Some(parse_error.position()),
DustError::Lex { lex_error, .. } => Some(lex_error.position()), DustError::Lex { lex_error, .. } => Some(lex_error.position()),
} }

View File

@ -429,17 +429,12 @@ impl<'src> Lexer<'src> {
"float" => Token::FloatKeyword, "float" => Token::FloatKeyword,
"if" => Token::If, "if" => Token::If,
"int" => Token::Int, "int" => Token::Int,
"is_even" => Token::IsEven,
"is_odd" => Token::IsOdd,
"length" => Token::Length,
"let" => Token::Let, "let" => Token::Let,
"map" => Token::Map, "map" => Token::Map,
"mut" => Token::Mut, "mut" => Token::Mut,
"read_line" => Token::ReadLine,
"struct" => Token::Struct, "struct" => Token::Struct,
"true" => Token::Boolean("true"), "true" => Token::Boolean("true"),
"while" => Token::While, "while" => Token::While,
"write_line" => Token::WriteLine,
_ => Token::Identifier(string), _ => Token::Identifier(string),
}; };
@ -1023,7 +1018,7 @@ mod tests {
assert_eq!( assert_eq!(
lex(input), lex(input),
Ok(vec![ Ok(vec![
(Token::ReadLine, (0, 9)), (Token::Identifier("read_line"), (0, 9)),
(Token::LeftParenthesis, (9, 10)), (Token::LeftParenthesis, (9, 10)),
(Token::RightParenthesis, (10, 11)), (Token::RightParenthesis, (10, 11)),
(Token::Eof, (11, 11)), (Token::Eof, (11, 11)),
@ -1038,7 +1033,7 @@ mod tests {
assert_eq!( assert_eq!(
lex(input), lex(input),
Ok(vec![ Ok(vec![
(Token::WriteLine, (0, 10)), (Token::Identifier("write_line"), (0, 10)),
(Token::LeftParenthesis, (10, 11)), (Token::LeftParenthesis, (10, 11)),
(Token::String("Hello, world!"), (11, 26)), (Token::String("Hello, world!"), (11, 26)),
(Token::RightParenthesis, (26, 27)), (Token::RightParenthesis, (26, 27)),
@ -1107,7 +1102,7 @@ mod tests {
Ok(vec![ Ok(vec![
(Token::Integer("42"), (0, 2)), (Token::Integer("42"), (0, 2)),
(Token::Dot, (2, 3)), (Token::Dot, (2, 3)),
(Token::IsEven, (3, 10)), (Token::Identifier("is_even"), (3, 10)),
(Token::LeftParenthesis, (10, 11)), (Token::LeftParenthesis, (10, 11)),
(Token::RightParenthesis, (11, 12)), (Token::RightParenthesis, (11, 12)),
(Token::Eof, (12, 12)), (Token::Eof, (12, 12)),
@ -1128,7 +1123,10 @@ mod tests {
assert_eq!( assert_eq!(
lex(input), lex(input),
Ok(vec![(Token::Length, (0, 6)), (Token::Eof, (6, 6)),]) Ok(vec![
(Token::Identifier("length"), (0, 6)),
(Token::Eof, (6, 6)),
])
) )
} }

View File

@ -20,6 +20,7 @@ pub mod ast;
pub mod built_in_function; pub mod built_in_function;
pub mod constructor; pub mod constructor;
pub mod context; pub mod context;
pub mod core_library;
pub mod dust_error; pub mod dust_error;
pub mod identifier; pub mod identifier;
pub mod lexer; pub mod lexer;
@ -30,10 +31,10 @@ pub mod value;
pub mod vm; pub mod vm;
pub use analyzer::{analyze, AnalysisError, Analyzer}; pub use analyzer::{analyze, AnalysisError, Analyzer};
pub use ast::{AbstractSyntaxTree, Expression, Statement}; pub use ast::{AbstractSyntaxTree, Expression, Span, Statement};
pub use built_in_function::{BuiltInFunction, BuiltInFunctionError}; pub use built_in_function::{BuiltInFunction, BuiltInFunctionError};
pub use constructor::Constructor; pub use constructor::Constructor;
pub use context::{Context, ContextData}; pub use context::{Context, ContextData, ContextError};
pub use dust_error::DustError; pub use dust_error::DustError;
pub use identifier::Identifier; pub use identifier::Identifier;
pub use lexer::{lex, LexError, Lexer}; pub use lexer::{lex, LexError, Lexer};

View File

@ -27,17 +27,12 @@ pub enum Token<'src> {
FloatKeyword, FloatKeyword,
If, If,
Int, Int,
IsEven,
IsOdd,
Length,
Let, Let,
Map, Map,
Mut, Mut,
ReadLine,
Str, Str,
Struct, Struct,
While, While,
WriteLine,
// Symbols // Symbols
BangEqual, BangEqual,
@ -96,12 +91,9 @@ impl<'src> Token<'src> {
Token::If => TokenOwned::If, Token::If => TokenOwned::If,
Token::Int => TokenOwned::Int, Token::Int => TokenOwned::Int,
Token::Integer(integer) => TokenOwned::Integer(integer.to_string()), Token::Integer(integer) => TokenOwned::Integer(integer.to_string()),
Token::IsEven => TokenOwned::IsEven,
Token::IsOdd => TokenOwned::IsOdd,
Token::LeftCurlyBrace => TokenOwned::LeftCurlyBrace, Token::LeftCurlyBrace => TokenOwned::LeftCurlyBrace,
Token::LeftParenthesis => TokenOwned::LeftParenthesis, Token::LeftParenthesis => TokenOwned::LeftParenthesis,
Token::LeftSquareBrace => TokenOwned::LeftSquareBrace, Token::LeftSquareBrace => TokenOwned::LeftSquareBrace,
Token::Length => TokenOwned::Length,
Token::Let => TokenOwned::Let, Token::Let => TokenOwned::Let,
Token::Less => TokenOwned::Less, Token::Less => TokenOwned::Less,
Token::LessEqual => TokenOwned::LessOrEqual, Token::LessEqual => TokenOwned::LessOrEqual,
@ -112,7 +104,6 @@ impl<'src> Token<'src> {
Token::Percent => TokenOwned::Percent, Token::Percent => TokenOwned::Percent,
Token::Plus => TokenOwned::Plus, Token::Plus => TokenOwned::Plus,
Token::PlusEqual => TokenOwned::PlusEqual, Token::PlusEqual => TokenOwned::PlusEqual,
Token::ReadLine => TokenOwned::ReadLine,
Token::RightCurlyBrace => TokenOwned::RightCurlyBrace, Token::RightCurlyBrace => TokenOwned::RightCurlyBrace,
Token::RightParenthesis => TokenOwned::RightParenthesis, Token::RightParenthesis => TokenOwned::RightParenthesis,
Token::RightSquareBrace => TokenOwned::RightSquareBrace, Token::RightSquareBrace => TokenOwned::RightSquareBrace,
@ -123,7 +114,6 @@ impl<'src> Token<'src> {
Token::Str => TokenOwned::Str, Token::Str => TokenOwned::Str,
Token::Struct => TokenOwned::Struct, Token::Struct => TokenOwned::Struct,
Token::While => TokenOwned::While, Token::While => TokenOwned::While,
Token::WriteLine => TokenOwned::WriteLine,
} }
} }
@ -154,13 +144,10 @@ impl<'src> Token<'src> {
Token::GreaterEqual => ">=", Token::GreaterEqual => ">=",
Token::If => "if", Token::If => "if",
Token::Int => "int", Token::Int => "int",
Token::IsEven => "is_even",
Token::IsOdd => "is_odd",
Token::LeftCurlyBrace => "{", Token::LeftCurlyBrace => "{",
Token::LeftParenthesis => "(", Token::LeftParenthesis => "(",
Token::LeftSquareBrace => "[", Token::LeftSquareBrace => "[",
Token::Let => "let", Token::Let => "let",
Token::Length => "length",
Token::Less => "<", Token::Less => "<",
Token::LessEqual => "<=", Token::LessEqual => "<=",
Token::Map => "map", Token::Map => "map",
@ -170,7 +157,6 @@ impl<'src> Token<'src> {
Token::Percent => "%", Token::Percent => "%",
Token::Plus => "+", Token::Plus => "+",
Token::PlusEqual => "+=", Token::PlusEqual => "+=",
Token::ReadLine => "read_line",
Token::RightCurlyBrace => "}", Token::RightCurlyBrace => "}",
Token::RightParenthesis => ")", Token::RightParenthesis => ")",
Token::RightSquareBrace => "]", Token::RightSquareBrace => "]",
@ -180,7 +166,6 @@ impl<'src> Token<'src> {
Token::Str => "str", Token::Str => "str",
Token::Struct => "struct", Token::Struct => "struct",
Token::While => "while", Token::While => "while",
Token::WriteLine => "write_line",
} }
} }
@ -209,13 +194,10 @@ impl<'src> Token<'src> {
Token::If => TokenKind::If, Token::If => TokenKind::If,
Token::Int => TokenKind::Int, Token::Int => TokenKind::Int,
Token::Integer(_) => TokenKind::Integer, Token::Integer(_) => TokenKind::Integer,
Token::IsEven => TokenKind::IsEven,
Token::IsOdd => TokenKind::IsOdd,
Token::LeftCurlyBrace => TokenKind::LeftCurlyBrace, Token::LeftCurlyBrace => TokenKind::LeftCurlyBrace,
Token::LeftParenthesis => TokenKind::LeftParenthesis, Token::LeftParenthesis => TokenKind::LeftParenthesis,
Token::LeftSquareBrace => TokenKind::LeftSquareBrace, Token::LeftSquareBrace => TokenKind::LeftSquareBrace,
Token::Let => TokenKind::Let, Token::Let => TokenKind::Let,
Token::Length => TokenKind::Length,
Token::Less => TokenKind::Less, Token::Less => TokenKind::Less,
Token::LessEqual => TokenKind::LessOrEqual, Token::LessEqual => TokenKind::LessOrEqual,
Token::Map => TokenKind::Map, Token::Map => TokenKind::Map,
@ -225,7 +207,6 @@ impl<'src> Token<'src> {
Token::Percent => TokenKind::Percent, Token::Percent => TokenKind::Percent,
Token::Plus => TokenKind::Plus, Token::Plus => TokenKind::Plus,
Token::PlusEqual => TokenKind::PlusEqual, Token::PlusEqual => TokenKind::PlusEqual,
Token::ReadLine => TokenKind::ReadLine,
Token::RightCurlyBrace => TokenKind::RightCurlyBrace, Token::RightCurlyBrace => TokenKind::RightCurlyBrace,
Token::RightParenthesis => TokenKind::RightParenthesis, Token::RightParenthesis => TokenKind::RightParenthesis,
Token::RightSquareBrace => TokenKind::RightSquareBrace, Token::RightSquareBrace => TokenKind::RightSquareBrace,
@ -236,7 +217,6 @@ impl<'src> Token<'src> {
Token::String(_) => TokenKind::String, Token::String(_) => TokenKind::String,
Token::Struct => TokenKind::Struct, Token::Struct => TokenKind::Struct,
Token::While => TokenKind::While, Token::While => TokenKind::While,
Token::WriteLine => TokenKind::WriteLine,
} }
} }
@ -320,16 +300,11 @@ pub enum TokenOwned {
FloatKeyword, FloatKeyword,
If, If,
Int, Int,
IsEven,
IsOdd,
Let, Let,
Length,
Map, Map,
Mut, Mut,
ReadLine,
Str, Str,
While, While,
WriteLine,
// Symbols // Symbols
Async, Async,
@ -371,7 +346,7 @@ impl Display for TokenOwned {
TokenOwned::Bang => Token::Bang.fmt(f), TokenOwned::Bang => Token::Bang.fmt(f),
TokenOwned::BangEqual => Token::BangEqual.fmt(f), TokenOwned::BangEqual => Token::BangEqual.fmt(f),
TokenOwned::Bool => Token::Bool.fmt(f), TokenOwned::Bool => Token::Bool.fmt(f),
TokenOwned::Boolean(boolean) => write!(f, "{boolean}"), TokenOwned::Boolean(boolean) => Token::Boolean(boolean).fmt(f),
TokenOwned::Colon => Token::Colon.fmt(f), TokenOwned::Colon => Token::Colon.fmt(f),
TokenOwned::Comma => Token::Comma.fmt(f), TokenOwned::Comma => Token::Comma.fmt(f),
TokenOwned::Dot => Token::Dot.fmt(f), TokenOwned::Dot => Token::Dot.fmt(f),
@ -382,20 +357,17 @@ impl Display for TokenOwned {
TokenOwned::Else => Token::Else.fmt(f), TokenOwned::Else => Token::Else.fmt(f),
TokenOwned::Eof => Token::Eof.fmt(f), TokenOwned::Eof => Token::Eof.fmt(f),
TokenOwned::Equal => Token::Equal.fmt(f), TokenOwned::Equal => Token::Equal.fmt(f),
TokenOwned::Float(float) => write!(f, "{float}"), TokenOwned::Float(float) => Token::Float(float).fmt(f),
TokenOwned::FloatKeyword => Token::FloatKeyword.fmt(f), TokenOwned::FloatKeyword => Token::FloatKeyword.fmt(f),
TokenOwned::Greater => Token::Greater.fmt(f), TokenOwned::Greater => Token::Greater.fmt(f),
TokenOwned::GreaterOrEqual => Token::GreaterEqual.fmt(f), TokenOwned::GreaterOrEqual => Token::GreaterEqual.fmt(f),
TokenOwned::Identifier(text) => write!(f, "{text}"), TokenOwned::Identifier(text) => Token::Identifier(text).fmt(f),
TokenOwned::If => Token::If.fmt(f), TokenOwned::If => Token::If.fmt(f),
TokenOwned::Int => Token::Int.fmt(f), TokenOwned::Int => Token::Int.fmt(f),
TokenOwned::Integer(integer) => write!(f, "{integer}"), TokenOwned::Integer(integer) => Token::Integer(integer).fmt(f),
TokenOwned::IsEven => Token::IsEven.fmt(f),
TokenOwned::IsOdd => Token::IsOdd.fmt(f),
TokenOwned::LeftCurlyBrace => Token::LeftCurlyBrace.fmt(f), TokenOwned::LeftCurlyBrace => Token::LeftCurlyBrace.fmt(f),
TokenOwned::LeftParenthesis => Token::LeftParenthesis.fmt(f), TokenOwned::LeftParenthesis => Token::LeftParenthesis.fmt(f),
TokenOwned::LeftSquareBrace => Token::LeftSquareBrace.fmt(f), TokenOwned::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
TokenOwned::Length => Token::Length.fmt(f),
TokenOwned::Let => Token::Let.fmt(f), TokenOwned::Let => Token::Let.fmt(f),
TokenOwned::Less => Token::Less.fmt(f), TokenOwned::Less => Token::Less.fmt(f),
TokenOwned::LessOrEqual => Token::LessEqual.fmt(f), TokenOwned::LessOrEqual => Token::LessEqual.fmt(f),
@ -406,7 +378,6 @@ impl Display for TokenOwned {
TokenOwned::Percent => Token::Percent.fmt(f), TokenOwned::Percent => Token::Percent.fmt(f),
TokenOwned::Plus => Token::Plus.fmt(f), TokenOwned::Plus => Token::Plus.fmt(f),
TokenOwned::PlusEqual => Token::PlusEqual.fmt(f), TokenOwned::PlusEqual => Token::PlusEqual.fmt(f),
TokenOwned::ReadLine => Token::ReadLine.fmt(f),
TokenOwned::RightCurlyBrace => Token::RightCurlyBrace.fmt(f), TokenOwned::RightCurlyBrace => Token::RightCurlyBrace.fmt(f),
TokenOwned::RightParenthesis => Token::RightParenthesis.fmt(f), TokenOwned::RightParenthesis => Token::RightParenthesis.fmt(f),
TokenOwned::RightSquareBrace => Token::RightSquareBrace.fmt(f), TokenOwned::RightSquareBrace => Token::RightSquareBrace.fmt(f),
@ -417,7 +388,6 @@ impl Display for TokenOwned {
TokenOwned::String(string) => write!(f, "{string}"), TokenOwned::String(string) => write!(f, "{string}"),
TokenOwned::Struct => Token::Struct.fmt(f), TokenOwned::Struct => Token::Struct.fmt(f),
TokenOwned::While => Token::While.fmt(f), TokenOwned::While => Token::While.fmt(f),
TokenOwned::WriteLine => Token::WriteLine.fmt(f),
} }
} }
} }
@ -442,15 +412,10 @@ pub enum TokenKind {
FloatKeyword, FloatKeyword,
If, If,
Int, Int,
IsEven,
IsOdd,
Length,
Let, Let,
Map, Map,
ReadLine,
Str, Str,
While, While,
WriteLine,
// Symbols // Symbols
BangEqual, BangEqual,
@ -511,12 +476,9 @@ impl Display for TokenKind {
TokenKind::If => Token::If.fmt(f), TokenKind::If => Token::If.fmt(f),
TokenKind::Int => Token::Int.fmt(f), TokenKind::Int => Token::Int.fmt(f),
TokenKind::Integer => write!(f, "integer value"), TokenKind::Integer => write!(f, "integer value"),
TokenKind::IsEven => Token::IsEven.fmt(f),
TokenKind::IsOdd => Token::IsOdd.fmt(f),
TokenKind::LeftCurlyBrace => Token::LeftCurlyBrace.fmt(f), TokenKind::LeftCurlyBrace => Token::LeftCurlyBrace.fmt(f),
TokenKind::LeftParenthesis => Token::LeftParenthesis.fmt(f), TokenKind::LeftParenthesis => Token::LeftParenthesis.fmt(f),
TokenKind::LeftSquareBrace => Token::LeftSquareBrace.fmt(f), TokenKind::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
TokenKind::Length => Token::Length.fmt(f),
TokenKind::Let => Token::Let.fmt(f), TokenKind::Let => Token::Let.fmt(f),
TokenKind::Less => Token::Less.fmt(f), TokenKind::Less => Token::Less.fmt(f),
TokenKind::LessOrEqual => Token::LessEqual.fmt(f), TokenKind::LessOrEqual => Token::LessEqual.fmt(f),
@ -527,7 +489,6 @@ impl Display for TokenKind {
TokenKind::Percent => Token::Percent.fmt(f), TokenKind::Percent => Token::Percent.fmt(f),
TokenKind::Plus => Token::Plus.fmt(f), TokenKind::Plus => Token::Plus.fmt(f),
TokenKind::PlusEqual => Token::PlusEqual.fmt(f), TokenKind::PlusEqual => Token::PlusEqual.fmt(f),
TokenKind::ReadLine => Token::ReadLine.fmt(f),
TokenKind::RightCurlyBrace => Token::RightCurlyBrace.fmt(f), TokenKind::RightCurlyBrace => Token::RightCurlyBrace.fmt(f),
TokenKind::RightParenthesis => Token::RightParenthesis.fmt(f), TokenKind::RightParenthesis => Token::RightParenthesis.fmt(f),
TokenKind::RightSquareBrace => Token::RightSquareBrace.fmt(f), TokenKind::RightSquareBrace => Token::RightSquareBrace.fmt(f),
@ -538,7 +499,6 @@ impl Display for TokenKind {
TokenKind::String => write!(f, "string value"), TokenKind::String => write!(f, "string value"),
TokenKind::Struct => Token::Struct.fmt(f), TokenKind::Struct => Token::Struct.fmt(f),
TokenKind::While => Token::While.fmt(f), TokenKind::While => Token::While.fmt(f),
TokenKind::WriteLine => Token::WriteLine.fmt(f),
} }
} }
} }
@ -547,7 +507,7 @@ impl Display for TokenKind {
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;
pub fn all_tokens<'src>() -> [Token<'src>; 51] { pub fn all_tokens<'src>() -> [Token<'src>; 46] {
[ [
Token::Identifier("foobar"), Token::Identifier("foobar"),
Token::Boolean("true"), Token::Boolean("true"),
@ -573,12 +533,9 @@ pub(crate) mod tests {
Token::GreaterEqual, Token::GreaterEqual,
Token::If, Token::If,
Token::Int, Token::Int,
Token::IsEven,
Token::IsOdd,
Token::LeftCurlyBrace, Token::LeftCurlyBrace,
Token::LeftParenthesis, Token::LeftParenthesis,
Token::LeftSquareBrace, Token::LeftSquareBrace,
Token::Length,
Token::Less, Token::Less,
Token::LessEqual, Token::LessEqual,
Token::Let, Token::Let,
@ -589,7 +546,6 @@ pub(crate) mod tests {
Token::Percent, Token::Percent,
Token::Plus, Token::Plus,
Token::PlusEqual, Token::PlusEqual,
Token::ReadLine,
Token::RightCurlyBrace, Token::RightCurlyBrace,
Token::RightParenthesis, Token::RightParenthesis,
Token::RightSquareBrace, Token::RightSquareBrace,
@ -599,7 +555,6 @@ pub(crate) mod tests {
Token::Str, Token::Str,
Token::Struct, Token::Struct,
Token::While, Token::While,
Token::WriteLine,
] ]
} }

View File

@ -4,16 +4,11 @@ use std::{
collections::HashMap, collections::HashMap,
error::Error, error::Error,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
ops::{Index, Range, RangeInclusive}, ops::{Range, RangeInclusive},
rc::Weak,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
}; };
use serde::{ use serde::{de::Visitor, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
de::{self, MapAccess, Visitor},
ser::{SerializeMap, SerializeStruct, SerializeStructVariant},
Deserialize, Deserializer, Serialize, Serializer,
};
use crate::{ use crate::{
AbstractSyntaxTree, BuiltInFunction, Context, EnumType, FunctionType, Identifier, AbstractSyntaxTree, BuiltInFunction, Context, EnumType, FunctionType, Identifier,
@ -84,36 +79,12 @@ impl Value {
Value::Mutable(Arc::new(RwLock::new(into_value.into()))) Value::Mutable(Arc::new(RwLock::new(into_value.into())))
} }
pub fn byte_range(start: u8, end: u8) -> Value { pub fn range<T: Into<Rangeable>>(start: T, end: T) -> Value {
Value::Range(Rangeable::Byte(start)..Rangeable::Byte(end)) Value::Range(start.into()..end.into())
} }
pub fn byte_range_inclusive(start: u8, end: u8) -> Value { pub fn range_inclusive<T: Into<Rangeable>>(start: T, end: T) -> Value {
Value::RangeInclusive(Rangeable::Byte(start)..=Rangeable::Byte(end)) Value::RangeInclusive(start.into()..=end.into())
}
pub fn character_range(start: char, end: char) -> Value {
Value::Range(Rangeable::Character(start)..Rangeable::Character(end))
}
pub fn character_range_inclusive(start: char, end: char) -> Value {
Value::RangeInclusive(Rangeable::Character(start)..=Rangeable::Character(end))
}
pub fn float_range(start: f64, end: f64) -> Value {
Value::Range(Rangeable::Float(start)..Rangeable::Float(end))
}
pub fn float_range_inclusive(start: f64, end: f64) -> Value {
Value::RangeInclusive(Rangeable::Float(start)..=Rangeable::Float(end))
}
pub fn integer_range(start: i64, end: i64) -> Value {
Value::Range(Rangeable::Integer(start)..Rangeable::Integer(end))
}
pub fn integer_range_inclusive(start: i64, end: i64) -> Value {
Value::RangeInclusive(Rangeable::Integer(start)..=Rangeable::Integer(end))
} }
pub fn string<T: ToString>(to_string: T) -> Value { pub fn string<T: ToString>(to_string: T) -> Value {
@ -200,9 +171,7 @@ impl Value {
value_parameters: built_in_function.value_parameters(), value_parameters: built_in_function.value_parameters(),
return_type: built_in_function.return_type().map(Box::new), return_type: built_in_function.return_type().map(Box::new),
}), }),
Value::Function(Function::Parsed { name, r#type, body }) => { Value::Function(Function::Parsed { r#type, .. }) => Type::Function(r#type.clone()),
Type::Function(r#type.clone())
}
Value::Integer(_) => Type::Integer, Value::Integer(_) => Type::Integer,
Value::List(values) => { Value::List(values) => {
let item_type = values.first().unwrap().r#type(); let item_type = values.first().unwrap().r#type();
@ -265,9 +234,7 @@ impl Value {
pub fn get_field(&self, field: &Identifier) -> Option<Value> { pub fn get_field(&self, field: &Identifier) -> Option<Value> {
if let "to_string" = field.as_str() { if let "to_string" = field.as_str() {
return Some(Value::Function(Function::BuiltIn( return Some(Value::Function(Function::BuiltIn(
BuiltInFunction::ToString { BuiltInFunction::ToString,
argument: Box::new(self.clone()),
},
))); )));
} }
@ -1097,13 +1064,13 @@ impl Function {
.call(_type_arguments, value_arguments) .call(_type_arguments, value_arguments)
.map_err(|error| RuntimeError::BuiltInFunctionError { error }), .map_err(|error| RuntimeError::BuiltInFunctionError { error }),
Function::Parsed { r#type, body, .. } => { Function::Parsed { r#type, body, .. } => {
let new_context = Context::with_data_from(context); let new_context = Context::with_data_from(context)?;
if let (Some(value_parameters), Some(value_arguments)) = if let (Some(value_parameters), Some(value_arguments)) =
(&r#type.value_parameters, value_arguments) (&r#type.value_parameters, value_arguments)
{ {
for ((identifier, _), value) in value_parameters.iter().zip(value_arguments) { for ((identifier, _), value) in value_parameters.iter().zip(value_arguments) {
new_context.set_variable_value(identifier.clone(), value); new_context.set_variable_value(identifier.clone(), value)?;
} }
} }
@ -1221,7 +1188,7 @@ impl Ord for Struct {
return type_cmp; return type_cmp;
} }
left_fields.into_iter().cmp(right_fields.into_iter()) left_fields.iter().cmp(right_fields.iter())
} }
(Struct::Fields { .. }, _) => Ordering::Greater, (Struct::Fields { .. }, _) => Ordering::Greater,
} }
@ -1258,7 +1225,6 @@ impl Display for Struct {
write!(f, " }}") write!(f, " }}")
} }
_ => Ok(()),
} }
} }
} }
@ -1271,6 +1237,36 @@ pub enum Rangeable {
Integer(i64), Integer(i64),
} }
impl From<u8> for Rangeable {
fn from(value: u8) -> Self {
Rangeable::Byte(value)
}
}
impl From<char> for Rangeable {
fn from(value: char) -> Self {
Rangeable::Character(value)
}
}
impl From<f64> for Rangeable {
fn from(value: f64) -> Self {
Rangeable::Float(value)
}
}
impl From<i32> for Rangeable {
fn from(value: i32) -> Self {
Rangeable::Integer(value as i64)
}
}
impl From<i64> for Rangeable {
fn from(value: i64) -> Self {
Rangeable::Integer(value)
}
}
impl Rangeable { impl Rangeable {
fn r#type(&self) -> RangeableType { fn r#type(&self) -> RangeableType {
match self { match self {

View File

@ -20,8 +20,8 @@ use crate::{
OperatorExpression, PrimitiveValueExpression, RangeExpression, Span, Statement, OperatorExpression, PrimitiveValueExpression, RangeExpression, Span, Statement,
StructDefinition, StructExpression, StructDefinition, StructExpression,
}, },
parse, Analyzer, BuiltInFunctionError, Constructor, Context, ContextData, DustError, parse, Analyzer, BuiltInFunctionError, Constructor, Context, ContextData, ContextError,
Expression, Function, Identifier, ParseError, StructType, Type, Value, ValueError, DustError, Expression, Function, Identifier, ParseError, StructType, Type, Value, ValueError,
}; };
/// Run the source code and return the result. /// Run the source code and return the result.
@ -56,7 +56,7 @@ pub fn run(source: &str) -> Result<Option<Value>, DustError> {
/// ``` /// ```
pub fn run_with_context(source: &str, context: Context) -> Result<Option<Value>, DustError> { pub fn run_with_context(source: &str, context: Context) -> Result<Option<Value>, DustError> {
let abstract_syntax_tree = parse(source)?; let abstract_syntax_tree = parse(source)?;
let mut analyzer = Analyzer::new(&abstract_syntax_tree, &context); let mut analyzer = Analyzer::new(&abstract_syntax_tree, context.clone());
analyzer analyzer
.analyze() .analyze()
@ -224,28 +224,7 @@ impl Vm {
Expression::Grouped(expression) => { Expression::Grouped(expression) => {
self.run_expression(*expression.inner, collect_garbage) self.run_expression(*expression.inner, collect_garbage)
} }
Expression::Identifier(identifier) => { Expression::Identifier(identifier) => self.run_identifier(identifier.inner),
log::debug!("Running identifier: {}", identifier.inner);
let get_data = self.context.get_data(&identifier.inner);
if let Some(ContextData::VariableValue(value)) = get_data {
return Ok(Evaluation::Return(Some(value)));
}
if let Some(ContextData::Constructor(constructor)) = get_data {
if let Constructor::Unit(unit_constructor) = constructor {
return Ok(Evaluation::Return(Some(unit_constructor.construct())));
}
return Ok(Evaluation::Constructor(constructor));
}
Err(RuntimeError::UndefinedValue {
identifier: identifier.inner,
position,
})
}
Expression::If(if_expression) => self.run_if(*if_expression.inner, collect_garbage), Expression::If(if_expression) => self.run_if(*if_expression.inner, collect_garbage),
Expression::List(list_expression) => { Expression::List(list_expression) => {
self.run_list(*list_expression.inner, collect_garbage) self.run_list(*list_expression.inner, collect_garbage)
@ -274,6 +253,26 @@ impl Vm {
}) })
} }
fn run_identifier(&self, identifier: Identifier) -> Result<Evaluation, RuntimeError> {
log::debug!("Running identifier: {}", identifier);
let get_data = self.context.get_data(&identifier)?;
if let Some(ContextData::VariableValue(value)) = get_data {
return Ok(Evaluation::Return(Some(value)));
}
if let Some(ContextData::Constructor(constructor)) = get_data {
if let Constructor::Unit(unit_constructor) = constructor {
return Ok(Evaluation::Return(Some(unit_constructor.construct())));
}
return Ok(Evaluation::Constructor(constructor));
}
Err(RuntimeError::UnassociatedIdentifier { identifier })
}
fn run_struct( fn run_struct(
&self, &self,
struct_expression: StructExpression, struct_expression: StructExpression,
@ -284,7 +283,7 @@ impl Vm {
let StructExpression::Fields { name, fields } = struct_expression; let StructExpression::Fields { name, fields } = struct_expression;
let position = name.position; let position = name.position;
let constructor = self.context.get_constructor(&name.inner); let constructor = self.context.get_constructor(&name.inner)?;
if let Some(constructor) = constructor { if let Some(constructor) = constructor {
if let Constructor::Fields(fields_constructor) = constructor { if let Constructor::Fields(fields_constructor) = constructor {
@ -328,16 +327,16 @@ impl Vm {
match (start, end) { match (start, end) {
(Value::Byte(start), Value::Byte(end)) => { (Value::Byte(start), Value::Byte(end)) => {
Ok(Evaluation::Return(Some(Value::byte_range(start, end)))) Ok(Evaluation::Return(Some(Value::range(start, end))))
} }
(Value::Character(start), Value::Character(end)) => { (Value::Character(start), Value::Character(end)) => {
Ok(Evaluation::Return(Some(Value::character_range(start, end)))) Ok(Evaluation::Return(Some(Value::range(start, end))))
} }
(Value::Float(start), Value::Float(end)) => { (Value::Float(start), Value::Float(end)) => {
Ok(Evaluation::Return(Some(Value::float_range(start, end)))) Ok(Evaluation::Return(Some(Value::range(start, end))))
} }
(Value::Integer(start), Value::Integer(end)) => { (Value::Integer(start), Value::Integer(end)) => {
Ok(Evaluation::Return(Some(Value::integer_range(start, end)))) Ok(Evaluation::Return(Some(Value::range(start, end))))
} }
_ => Err(RuntimeError::InvalidRange { _ => Err(RuntimeError::InvalidRange {
start_position, start_position,
@ -356,18 +355,18 @@ impl Vm {
.expect_value(end_position)?; .expect_value(end_position)?;
match (start, end) { match (start, end) {
(Value::Byte(start), Value::Byte(end)) => Ok(Evaluation::Return(Some( (Value::Byte(start), Value::Byte(end)) => {
Value::byte_range_inclusive(start, end), Ok(Evaluation::Return(Some(Value::range_inclusive(start, end))))
))), }
(Value::Character(start), Value::Character(end)) => Ok(Evaluation::Return( (Value::Character(start), Value::Character(end)) => {
Some(Value::character_range_inclusive(start, end)), Ok(Evaluation::Return(Some(Value::range_inclusive(start, end))))
)), }
(Value::Float(start), Value::Float(end)) => Ok(Evaluation::Return(Some( (Value::Float(start), Value::Float(end)) => {
Value::float_range_inclusive(start, end), Ok(Evaluation::Return(Some(Value::range_inclusive(start, end))))
))), }
(Value::Integer(start), Value::Integer(end)) => Ok(Evaluation::Return(Some( (Value::Integer(start), Value::Integer(end)) => {
Value::integer_range_inclusive(start, end), Ok(Evaluation::Return(Some(Value::range_inclusive(start, end))))
))), }
_ => Err(RuntimeError::InvalidRange { _ => Err(RuntimeError::InvalidRange {
start_position, start_position,
end_position, end_position,
@ -661,6 +660,50 @@ impl Vm {
let CallExpression { invoker, arguments } = call_expression; let CallExpression { invoker, arguments } = call_expression;
if let Expression::FieldAccess(field_access) = invoker {
let FieldAccessExpression { container, field } = *field_access.inner;
let container_position = container.position();
let container_value = self
.run_expression(container, collect_garbage)?
.expect_value(container_position)?;
let function = if let Some(value) = container_value.get_field(&field.inner) {
if let Value::Function(function) = value {
function
} else {
return Err(RuntimeError::ExpectedFunction {
actual: value,
position: container_position,
});
}
} else {
return Err(RuntimeError::UndefinedProperty {
value: container_value,
value_position: container_position,
property: field.inner,
property_position: field.position,
});
};
let mut value_arguments = vec![container_value];
for argument in arguments {
let position = argument.position();
let value = self
.run_expression(argument, collect_garbage)?
.expect_value(position)?;
value_arguments.push(value);
}
let context = Context::new();
return function
.call(None, Some(value_arguments), &context)
.map(Evaluation::Return);
}
let invoker_position = invoker.position(); let invoker_position = invoker.position();
let run_invoker = self.run_expression(invoker, collect_garbage)?; let run_invoker = self.run_expression(invoker, collect_garbage)?;
@ -917,6 +960,7 @@ impl Evaluation {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum RuntimeError { pub enum RuntimeError {
ContextError(ContextError),
ParseError(ParseError), ParseError(ParseError),
Expression { Expression {
error: Box<RuntimeError>, error: Box<RuntimeError>,
@ -990,11 +1034,10 @@ pub enum RuntimeError {
start_position: Span, start_position: Span,
end_position: Span, end_position: Span,
}, },
UndefinedType { UnassociatedIdentifier {
identifier: Identifier, identifier: Identifier,
position: Span,
}, },
UndefinedValue { UndefinedType {
identifier: Identifier, identifier: Identifier,
position: Span, position: Span,
}, },
@ -1006,10 +1049,19 @@ pub enum RuntimeError {
}, },
} }
impl From<ContextError> for RuntimeError {
fn from(error: ContextError) -> Self {
Self::ContextError(error)
}
}
impl RuntimeError { impl RuntimeError {
pub fn position(&self) -> Option<Span> { pub fn position(&self) -> Option<Span> {
let position = match self { let position = match self {
Self::ContextError(_) => return None,
Self::BuiltInFunctionError { .. } => return None, Self::BuiltInFunctionError { .. } => return None,
Self::UnassociatedIdentifier { .. } => return None,
Self::ParseError(parse_error) => parse_error.position(), Self::ParseError(parse_error) => parse_error.position(),
Self::Expression { position, .. } => *position, Self::Expression { position, .. } => *position,
Self::Statement { position, .. } => *position, Self::Statement { position, .. } => *position,
@ -1032,14 +1084,14 @@ impl RuntimeError {
Self::ExpectedNumber { position } => *position, Self::ExpectedNumber { position } => *position,
Self::ExpectedType { position, .. } => *position, Self::ExpectedType { position, .. } => *position,
Self::ExpectedValue { position } => *position, Self::ExpectedValue { position } => *position,
Self::ExpectedValueOrConstructor { position } => *position,
Self::InvalidRange { Self::InvalidRange {
start_position, start_position,
end_position, end_position,
.. ..
} => (start_position.0, end_position.1), } => (start_position.0, end_position.1),
Self::UndefinedType { position, .. } => *position, Self::UndefinedType { position, .. } => *position,
Self::UndefinedValue { position, .. } => *position,
Self::ExpectedValueOrConstructor { position } => *position,
Self::UndefinedProperty { Self::UndefinedProperty {
property_position, .. property_position, ..
} => *property_position, } => *property_position,
@ -1058,6 +1110,7 @@ impl From<ParseError> for RuntimeError {
impl Display for RuntimeError { impl Display for RuntimeError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
Self::ContextError(context_error) => write!(f, "{}", context_error),
Self::ParseError(parse_error) => write!(f, "{}", parse_error), Self::ParseError(parse_error) => write!(f, "{}", parse_error),
Self::Expression { error, position } => { Self::Expression { error, position } => {
write!( write!(
@ -1181,14 +1234,10 @@ impl Display for RuntimeError {
start_position, end_position start_position, end_position
) )
} }
Self::UndefinedValue { Self::UnassociatedIdentifier { identifier } => {
identifier,
position,
} => {
write!( write!(
f, f,
"Undefined value {} at position: {:?}", "Identifier \"{identifier}\" is not associated with a value or constructor"
identifier, position
) )
} }
Self::UndefinedProperty { Self::UndefinedProperty {
@ -1343,7 +1392,7 @@ mod tests {
fn range() { fn range() {
let input = "1..5"; let input = "1..5";
assert_eq!(run(input), Ok(Some(Value::integer_range(1, 5)))); assert_eq!(run(input), Ok(Some(Value::range(1, 5))));
} }
#[test] #[test]