Refine errors and error propagation
This commit is contained in:
parent
cf9a9837c8
commit
83f856385b
@ -11,11 +11,12 @@ use std::{
|
||||
|
||||
use crate::{
|
||||
ast::{
|
||||
AbstractSyntaxTree, BlockExpression, CallExpression, ElseExpression, FieldAccessExpression,
|
||||
IfExpression, LetStatement, ListExpression, ListIndexExpression, LoopExpression,
|
||||
MapExpression, Node, OperatorExpression, RangeExpression, Span, Statement,
|
||||
AbstractSyntaxTree, AstError, BlockExpression, CallExpression, ElseExpression,
|
||||
FieldAccessExpression, IfExpression, LetStatement, ListExpression, ListIndexExpression,
|
||||
LoopExpression, MapExpression, Node, OperatorExpression, RangeExpression, Span, Statement,
|
||||
StructDefinition, StructExpression, TupleAccessExpression,
|
||||
},
|
||||
context::ContextError,
|
||||
parse, Context, DustError, Expression, Identifier, StructType, Type,
|
||||
};
|
||||
|
||||
@ -33,7 +34,7 @@ use crate::{
|
||||
pub fn analyze(source: &str) -> Result<(), DustError> {
|
||||
let abstract_tree = parse(source)?;
|
||||
let context = Context::new();
|
||||
let mut analyzer = Analyzer::new(&abstract_tree, &context);
|
||||
let analyzer = Analyzer::new(&abstract_tree, context);
|
||||
|
||||
analyzer
|
||||
.analyze()
|
||||
@ -58,18 +59,18 @@ pub fn analyze(source: &str) -> Result<(), DustError> {
|
||||
/// assert!(result.is_err());
|
||||
pub struct Analyzer<'a> {
|
||||
abstract_tree: &'a AbstractSyntaxTree,
|
||||
context: &'a Context,
|
||||
context: Context,
|
||||
}
|
||||
|
||||
impl<'a> Analyzer<'a> {
|
||||
pub fn new(abstract_tree: &'a AbstractSyntaxTree, context: &'a Context) -> Self {
|
||||
impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
||||
pub fn new(abstract_tree: &'a AbstractSyntaxTree, context: Context) -> Self {
|
||||
Self {
|
||||
abstract_tree,
|
||||
context,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn analyze(&mut self) -> Result<(), AnalysisError> {
|
||||
pub fn analyze(&'recovered self) -> Result<(), AnalysisError> {
|
||||
for statement in &self.abstract_tree.statements {
|
||||
self.analyze_statement(statement)?;
|
||||
}
|
||||
@ -77,7 +78,7 @@ impl<'a> Analyzer<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn analyze_statement(&mut self, statement: &Statement) -> Result<(), AnalysisError> {
|
||||
fn analyze_statement(&'recovered self, statement: &Statement) -> Result<(), AnalysisError> {
|
||||
match statement {
|
||||
Statement::Expression(expression) => self.analyze_expression(expression)?,
|
||||
Statement::ExpressionNullified(expression_node) => {
|
||||
@ -86,7 +87,7 @@ impl<'a> Analyzer<'a> {
|
||||
Statement::Let(let_statement) => match &let_statement.inner {
|
||||
LetStatement::Let { 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 {
|
||||
self.context.set_variable_type(
|
||||
@ -113,7 +114,7 @@ impl<'a> Analyzer<'a> {
|
||||
name: name.inner.clone(),
|
||||
},
|
||||
name.position,
|
||||
),
|
||||
)?,
|
||||
StructDefinition::Tuple { name, items } => {
|
||||
let fields = items.iter().map(|item| item.inner.clone()).collect();
|
||||
|
||||
@ -124,7 +125,7 @@ impl<'a> Analyzer<'a> {
|
||||
fields,
|
||||
},
|
||||
name.position,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
StructDefinition::Fields { name, fields } => {
|
||||
let fields = fields
|
||||
@ -141,7 +142,7 @@ impl<'a> Analyzer<'a> {
|
||||
fields,
|
||||
},
|
||||
name.position,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -149,7 +150,7 @@ impl<'a> Analyzer<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn analyze_expression(&mut self, expression: &Expression) -> Result<(), AnalysisError> {
|
||||
fn analyze_expression(&'recovered self, expression: &Expression) -> Result<(), AnalysisError> {
|
||||
match expression {
|
||||
Expression::Block(block_expression) => self.analyze_block(&block_expression.inner)?,
|
||||
Expression::Call(call_expression) => {
|
||||
@ -235,8 +236,8 @@ 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);
|
||||
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 {
|
||||
@ -308,7 +309,10 @@ impl<'a> Analyzer<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn analyze_block(&mut self, block_expression: &BlockExpression) -> Result<(), AnalysisError> {
|
||||
fn analyze_block(
|
||||
&'recovered self,
|
||||
block_expression: &BlockExpression,
|
||||
) -> Result<(), AnalysisError> {
|
||||
match block_expression {
|
||||
BlockExpression::Async(statements) => {
|
||||
for statement in statements {
|
||||
@ -325,7 +329,7 @@ impl<'a> Analyzer<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn analyze_if(&mut self, if_expression: &IfExpression) -> Result<(), AnalysisError> {
|
||||
fn analyze_if(&'recovered self, if_expression: &IfExpression) -> Result<(), AnalysisError> {
|
||||
match if_expression {
|
||||
IfExpression::If {
|
||||
condition,
|
||||
@ -359,6 +363,8 @@ impl<'a> Analyzer<'a> {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum AnalysisError {
|
||||
AstError(AstError),
|
||||
ContextError(ContextError),
|
||||
ExpectedBoolean {
|
||||
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 {
|
||||
pub fn position(&self) -> Span {
|
||||
match self {
|
||||
pub fn position(&self) -> Option<Span> {
|
||||
let position = match self {
|
||||
AnalysisError::AstError(ast_error) => return None,
|
||||
AnalysisError::ContextError(context_error) => return None,
|
||||
|
||||
AnalysisError::ExpectedBoolean { actual } => actual.position(),
|
||||
AnalysisError::ExpectedIdentifier { actual } => actual.position(),
|
||||
AnalysisError::ExpectedIdentifierOrString { actual } => actual.position(),
|
||||
@ -439,7 +460,9 @@ impl AnalysisError {
|
||||
AnalysisError::UndefinedVariable { identifier } => identifier.position,
|
||||
AnalysisError::UnexpectedIdentifier { identifier } => identifier.position,
|
||||
AnalysisError::UnexectedString { actual } => actual.position(),
|
||||
}
|
||||
};
|
||||
|
||||
Some(position)
|
||||
}
|
||||
}
|
||||
|
||||
@ -448,6 +471,8 @@ impl Error for AnalysisError {}
|
||||
impl Display for AnalysisError {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
AnalysisError::AstError(ast_error) => write!(f, "{}", ast_error),
|
||||
AnalysisError::ContextError(context_error) => write!(f, "{}", context_error),
|
||||
AnalysisError::ExpectedBoolean { actual, .. } => {
|
||||
write!(f, "Expected boolean, found {}", actual)
|
||||
}
|
||||
|
@ -6,9 +6,12 @@ use std::{
|
||||
|
||||
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)]
|
||||
pub enum Expression {
|
||||
@ -263,15 +266,18 @@ impl Expression {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn return_type(&self, context: &Context) -> Option<Type> {
|
||||
match self {
|
||||
pub fn return_type<'recovered>(
|
||||
&self,
|
||||
context: &'recovered Context,
|
||||
) -> Result<Option<Type>, AstError> {
|
||||
let return_type = match self {
|
||||
Expression::Block(block_expression) => {
|
||||
Some(block_expression.inner.return_type(context)?)
|
||||
return 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 Some(Type::Function(FunctionType { return_type, .. })) = invoker_type {
|
||||
return_type.map(|r#type| *r#type)
|
||||
@ -285,7 +291,7 @@ 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 Some(Type::Struct(StructType::Fields { fields, .. })) = container_type {
|
||||
fields
|
||||
@ -296,28 +302,41 @@ impl Expression {
|
||||
None
|
||||
}
|
||||
}
|
||||
Expression::Grouped(expression) => expression.inner.return_type(context),
|
||||
Expression::Identifier(identifier) => context.get_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::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() {
|
||||
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)?;
|
||||
|
||||
Some(Type::ListOf {
|
||||
item_type: Box::new(item_type),
|
||||
})
|
||||
if let Some(r#type) = item_type {
|
||||
Some(Type::ListOf {
|
||||
item_type: Box::new(r#type),
|
||||
})
|
||||
} else {
|
||||
return Err(AstError::ExpectedType {
|
||||
position: repeat_operand.position(),
|
||||
});
|
||||
}
|
||||
}
|
||||
ListExpression::Ordered(expressions) => {
|
||||
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();
|
||||
|
||||
Some(Type::List {
|
||||
@ -329,7 +348,11 @@ impl Expression {
|
||||
Expression::ListIndex(list_index_expression) => {
|
||||
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 {
|
||||
Some(*item_type)
|
||||
@ -350,9 +373,9 @@ impl Expression {
|
||||
LiteralExpression::String(_) => Some(Type::String),
|
||||
},
|
||||
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::While { block, .. } => block.inner.return_type(context),
|
||||
LoopExpression::While { block, .. } => block.inner.return_type(context)?,
|
||||
},
|
||||
Expression::Map(map_expression) => {
|
||||
let MapExpression { pairs } = map_expression.inner.as_ref();
|
||||
@ -360,7 +383,12 @@ impl Expression {
|
||||
let mut types = HashMap::with_capacity(pairs.len());
|
||||
|
||||
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);
|
||||
}
|
||||
@ -371,10 +399,12 @@ impl Expression {
|
||||
OperatorExpression::Assignment { .. } => None,
|
||||
OperatorExpression::Comparison { .. } => Some(Type::Boolean),
|
||||
OperatorExpression::CompoundAssignment { .. } => None,
|
||||
OperatorExpression::ErrorPropagation(expression) => expression.return_type(context),
|
||||
OperatorExpression::Negation(expression) => expression.return_type(context),
|
||||
OperatorExpression::ErrorPropagation(expression) => {
|
||||
expression.return_type(context)?
|
||||
}
|
||||
OperatorExpression::Negation(expression) => expression.return_type(context)?,
|
||||
OperatorExpression::Not(_) => Some(Type::Boolean),
|
||||
OperatorExpression::Math { left, .. } => left.return_type(context),
|
||||
OperatorExpression::Math { left, .. } => left.return_type(context)?,
|
||||
OperatorExpression::Logic { .. } => Some(Type::Boolean),
|
||||
},
|
||||
Expression::Range(range_expression) => {
|
||||
@ -382,13 +412,22 @@ impl Expression {
|
||||
RangeExpression::Exclusive { 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 {
|
||||
Type::Byte => RangeableType::Byte,
|
||||
Type::Character => RangeableType::Character,
|
||||
Type::Float => RangeableType::Float,
|
||||
Type::Integer => RangeableType::Integer,
|
||||
_ => return None,
|
||||
_ => {
|
||||
return Err(AstError::ExpectedRangeableType {
|
||||
position: start.position(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
Some(Type::Range {
|
||||
@ -400,7 +439,11 @@ impl Expression {
|
||||
let mut types = HashMap::with_capacity(fields.len());
|
||||
|
||||
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);
|
||||
}
|
||||
@ -413,15 +456,24 @@ impl Expression {
|
||||
},
|
||||
Expression::TupleAccess(tuple_access_expression) => {
|
||||
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 {
|
||||
fields.get(index.inner).cloned()
|
||||
} else {
|
||||
None
|
||||
Err(AstError::ExpectedTupleType {
|
||||
position: tuple.position(),
|
||||
})?
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(return_type)
|
||||
}
|
||||
|
||||
pub fn position(&self) -> Span {
|
||||
@ -975,13 +1027,16 @@ pub enum 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 {
|
||||
BlockExpression::Async(statements) | BlockExpression::Sync(statements) => {
|
||||
if let Some(statement) = statements.last() {
|
||||
statement.return_type(context)
|
||||
} else {
|
||||
None
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ use std::{
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ContextError, Type};
|
||||
|
||||
pub type Span = (usize, usize);
|
||||
|
||||
/// In-memory representation of a Dust program.
|
||||
@ -57,3 +59,48 @@ impl<T: Display> Display for Node<T> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,9 @@ use std::fmt::{self, Display, Formatter};
|
||||
|
||||
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)]
|
||||
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 {
|
||||
Statement::Expression(expression) => expression.return_type(context),
|
||||
Statement::ExpressionNullified(_) => None,
|
||||
Statement::Let(_) => None,
|
||||
Statement::StructDefinition(_) => None,
|
||||
Statement::ExpressionNullified(_) => Ok(None),
|
||||
Statement::Let(_) => Ok(None),
|
||||
Statement::StructDefinition(_) => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt::{self, Display, Formatter},
|
||||
io::{self, stdin},
|
||||
io::{self, stdin, stdout, Write},
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -13,7 +13,7 @@ use crate::{Identifier, Type, Value};
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub enum BuiltInFunction {
|
||||
// String tools
|
||||
ToString { argument: Box<Value> },
|
||||
ToString,
|
||||
|
||||
// Integer and float tools
|
||||
IsEven,
|
||||
@ -82,75 +82,57 @@ impl BuiltInFunction {
|
||||
_type_arguments: Option<Vec<Type>>,
|
||||
value_arguments: Option<Vec<Value>>,
|
||||
) -> 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 {
|
||||
BuiltInFunction::ToString { argument } => Ok(Some(Value::string(argument))),
|
||||
BuiltInFunction::ToString => Ok(Some(Value::String(value_arguments[0].to_string()))),
|
||||
BuiltInFunction::IsEven => {
|
||||
if let Some(value_arguments) = value_arguments {
|
||||
if value_arguments.len() == 1 {
|
||||
if let Some(integer) = value_arguments[0].as_integer() {
|
||||
Ok(Some(Value::Boolean(integer % 2 == 0)))
|
||||
} else {
|
||||
Err(BuiltInFunctionError::ExpectedInteger)
|
||||
}
|
||||
} else {
|
||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
||||
}
|
||||
if let Some(integer) = value_arguments[0].as_integer() {
|
||||
Ok(Some(Value::Boolean(integer % 2 == 0)))
|
||||
} else {
|
||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
||||
Err(BuiltInFunctionError::ExpectedInteger)
|
||||
}
|
||||
}
|
||||
BuiltInFunction::IsOdd => {
|
||||
if let Some(value_arguments) = value_arguments {
|
||||
if value_arguments.len() == 1 {
|
||||
if let Some(integer) = value_arguments[0].as_integer() {
|
||||
Ok(Some(Value::Boolean(integer % 2 != 0)))
|
||||
} else {
|
||||
Err(BuiltInFunctionError::ExpectedInteger)
|
||||
}
|
||||
} else {
|
||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
||||
}
|
||||
if let Some(integer) = value_arguments[0].as_integer() {
|
||||
Ok(Some(Value::Boolean(integer % 2 != 0)))
|
||||
} else {
|
||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
||||
Err(BuiltInFunctionError::ExpectedInteger)
|
||||
}
|
||||
}
|
||||
BuiltInFunction::Length => {
|
||||
if let Some(value_arguments) = value_arguments {
|
||||
if value_arguments.len() == 1 {
|
||||
if let Value::List(list) = &value_arguments[0] {
|
||||
Ok(Some(Value::Integer(list.len() as i64)))
|
||||
} else {
|
||||
Err(BuiltInFunctionError::ExpectedInteger)
|
||||
}
|
||||
} else {
|
||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
||||
}
|
||||
if let Value::List(list) = &value_arguments[0] {
|
||||
Ok(Some(Value::Integer(list.len() as i64)))
|
||||
} else {
|
||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
||||
Err(BuiltInFunctionError::ExpectedList)
|
||||
}
|
||||
}
|
||||
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'))))
|
||||
} else {
|
||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
||||
}
|
||||
Ok(Some(Value::string(input.trim_end_matches('\n'))))
|
||||
}
|
||||
BuiltInFunction::WriteLine => {
|
||||
if let Some(value_arguments) = value_arguments {
|
||||
if value_arguments.len() == 1 {
|
||||
println!("{}", value_arguments[0]);
|
||||
if let Value::String(string) = &value_arguments[0] {
|
||||
stdout().write_all(string.as_bytes())?;
|
||||
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
||||
}
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
||||
Err(BuiltInFunctionError::ExpectedString)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,8 +147,12 @@ impl Display for BuiltInFunction {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum BuiltInFunctionError {
|
||||
ExpectedInteger,
|
||||
Io(io::ErrorKind),
|
||||
|
||||
ExpectedString,
|
||||
ExpectedList,
|
||||
ExpectedInteger,
|
||||
|
||||
WrongNumberOfValueArguments,
|
||||
}
|
||||
|
||||
@ -181,8 +167,10 @@ impl Error for BuiltInFunctionError {}
|
||||
impl Display for BuiltInFunctionError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
BuiltInFunctionError::ExpectedInteger => write!(f, "Expected an integer"),
|
||||
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 => {
|
||||
write!(f, "Wrong number of value arguments")
|
||||
}
|
||||
|
@ -1,150 +1,187 @@
|
||||
//! Garbage-collecting context for variables.
|
||||
use std::{
|
||||
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};
|
||||
|
||||
pub type Variables = HashMap<Identifier, (ContextData, Span)>;
|
||||
pub type Associations = HashMap<Identifier, (ContextData, Span)>;
|
||||
|
||||
/// Garbage-collecting context for variables.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Context {
|
||||
variables: Arc<RwLock<Variables>>,
|
||||
associations: Arc<RwLock<Associations>>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new() -> Self {
|
||||
Self::with_data(HashMap::new())
|
||||
}
|
||||
|
||||
pub fn with_data(data: Associations) -> Self {
|
||||
Self {
|
||||
variables: Arc::new(RwLock::new(HashMap::new())),
|
||||
associations: Arc::new(RwLock::new(data)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a deep copy of another context.
|
||||
pub fn with_data_from(other: &Self) -> Self {
|
||||
Self {
|
||||
variables: Arc::new(RwLock::new(other.variables.read().unwrap().clone())),
|
||||
}
|
||||
pub fn with_data_from(other: &Self) -> Result<Self, ContextError> {
|
||||
Ok(Self::with_data(other.associations.read()?.clone()))
|
||||
}
|
||||
|
||||
/// Returns the number of variables in the context.
|
||||
pub fn variable_count(&self) -> usize {
|
||||
self.variables.read().unwrap().len()
|
||||
/// Returns the number of associated identifiers in the context.
|
||||
pub fn association_count(&self) -> Result<usize, ContextError> {
|
||||
Ok(self.associations.read()?.len())
|
||||
}
|
||||
|
||||
/// Returns a boolean indicating whether the context contains the variable.
|
||||
pub fn contains(&self, identifier: &Identifier) -> bool {
|
||||
self.variables.read().unwrap().contains_key(identifier)
|
||||
/// Returns a boolean indicating whether the identifier is in the context.
|
||||
pub fn contains(&self, identifier: &Identifier) -> Result<bool, ContextError> {
|
||||
Ok(self.associations.read()?.contains_key(identifier))
|
||||
}
|
||||
|
||||
/// Returns the full VariableData and Span if the context contains the given identifier.
|
||||
pub fn get(&self, identifier: &Identifier) -> Option<(ContextData, Span)> {
|
||||
self.variables.read().unwrap().get(identifier).cloned()
|
||||
/// Returns the full ContextData and Span if the context contains the given identifier.
|
||||
pub fn get(
|
||||
&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.
|
||||
pub fn get_type(&self, identifier: &Identifier) -> Option<Type> {
|
||||
match self.variables.read().unwrap().get(identifier) {
|
||||
Some((ContextData::VariableType(r#type), _)) => Some(r#type.clone()),
|
||||
Some((ContextData::VariableValue(value), _)) => Some(value.r#type()),
|
||||
/// Returns the type associated with the given identifier.
|
||||
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, ContextError> {
|
||||
let r#type = match self.associations.read()?.get(identifier) {
|
||||
Some((ContextData::VariableType(r#type), _)) => r#type.clone(),
|
||||
Some((ContextData::VariableValue(value), _)) => value.r#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.
|
||||
pub fn get_data(&self, identifier: &Identifier) -> Option<ContextData> {
|
||||
match self.variables.read().unwrap().get(identifier) {
|
||||
Some((variable_data, _)) => Some(variable_data.clone()),
|
||||
_ => None,
|
||||
/// Returns the value associated with the identifier.
|
||||
pub fn get_variable_value(
|
||||
&self,
|
||||
identifier: &Identifier,
|
||||
) -> 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.
|
||||
pub fn get_variable_value(&self, identifier: &Identifier) -> Option<Value> {
|
||||
match self.variables.read().unwrap().get(identifier) {
|
||||
Some((ContextData::VariableValue(value), _)) => Some(value.clone()),
|
||||
_ => None,
|
||||
/// Returns the constructor associated with the identifier.
|
||||
pub fn get_constructor(
|
||||
&self,
|
||||
identifier: &Identifier,
|
||||
) -> 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.
|
||||
pub fn get_constructor(&self, identifier: &Identifier) -> Option<Constructor> {
|
||||
match self.variables.read().unwrap().get(identifier) {
|
||||
Some((ContextData::Constructor(constructor), _)) => Some(constructor.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
/// Associates an identifier with a variable type, with a position given for garbage collection.
|
||||
pub fn set_variable_type(
|
||||
&self,
|
||||
identifier: Identifier,
|
||||
r#type: Type,
|
||||
position: Span,
|
||||
) -> Result<(), ContextError> {
|
||||
log::trace!("Setting {identifier} to type {type} at {position:?}");
|
||||
|
||||
self.variables
|
||||
.write()
|
||||
.unwrap()
|
||||
self.associations
|
||||
.write()?
|
||||
.insert(identifier, (ContextData::VariableType(r#type), position));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets a variable to a value.
|
||||
pub fn set_variable_value(&self, identifier: Identifier, value: Value) {
|
||||
/// Associates an identifier with a variable value.
|
||||
pub fn set_variable_value(
|
||||
&self,
|
||||
identifier: Identifier,
|
||||
value: Value,
|
||||
) -> Result<(), ContextError> {
|
||||
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)
|
||||
.map(|(_, last_position)| *last_position)
|
||||
.unwrap_or_default();
|
||||
|
||||
variables.insert(
|
||||
associations.insert(
|
||||
identifier,
|
||||
(ContextData::VariableValue(value), last_position),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Associates a constructor with an identifier.
|
||||
pub fn set_constructor(&self, identifier: Identifier, constructor: Constructor) {
|
||||
/// Associates an identifier with a constructor.
|
||||
pub fn set_constructor(
|
||||
&self,
|
||||
identifier: Identifier,
|
||||
constructor: Constructor,
|
||||
) -> Result<(), ContextError> {
|
||||
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)
|
||||
.map(|(_, last_position)| *last_position)
|
||||
.unwrap_or_default();
|
||||
|
||||
variables.insert(
|
||||
associations.insert(
|
||||
identifier,
|
||||
(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(
|
||||
&self,
|
||||
identifier: Identifier,
|
||||
struct_type: StructType,
|
||||
position: Span,
|
||||
) {
|
||||
) -> Result<(), ContextError> {
|
||||
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(
|
||||
identifier,
|
||||
(ContextData::ConstructorType(struct_type), position),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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:?}");
|
||||
|
||||
let mut variables = self.variables.write().unwrap();
|
||||
let mut variables = self.associations.write()?;
|
||||
|
||||
variables.retain(|identifier, (_, last_used)| {
|
||||
let should_drop = position.0 > last_used.0 && position.1 > last_used.1;
|
||||
@ -156,19 +193,25 @@ impl Context {
|
||||
!should_drop
|
||||
});
|
||||
variables.shrink_to_fit();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Updates a variable's last known position, allowing it to live longer in the program.
|
||||
/// Returns a boolean indicating whether the variable was found.
|
||||
pub fn update_last_position(&self, identifier: &Identifier, position: Span) -> bool {
|
||||
if let Some((_, last_position)) = self.variables.write().unwrap().get_mut(identifier) {
|
||||
/// Updates an associated identifier's last known position, allowing it to live longer in the
|
||||
/// program. Returns a boolean indicating whether the identifier.
|
||||
pub fn update_last_position(
|
||||
&self,
|
||||
identifier: &Identifier,
|
||||
position: Span,
|
||||
) -> Result<bool, ContextError> {
|
||||
if let Some((_, last_position)) = self.associations.write()?.get_mut(identifier) {
|
||||
*last_position = position;
|
||||
|
||||
log::trace!("Updating {identifier}'s last position to {position:?}");
|
||||
|
||||
true
|
||||
Ok(true)
|
||||
} 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
|
||||
/// 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");
|
||||
|
||||
let recovered = error.get_ref();
|
||||
let mut new_variables = HashMap::new();
|
||||
let ContextError::PoisonErrorRecovered(recovered) = error;
|
||||
|
||||
for (identifier, (variable_data, position)) in recovered.iter() {
|
||||
new_variables.insert(identifier.clone(), (variable_data.clone(), *position));
|
||||
let mut new_associations = HashMap::new();
|
||||
|
||||
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),
|
||||
}
|
||||
|
||||
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)]
|
||||
mod tests {
|
||||
@ -226,7 +313,7 @@ mod tests {
|
||||
|
||||
run_with_context(source, context.clone()).unwrap();
|
||||
|
||||
assert_eq!(context.variable_count(), 0);
|
||||
assert_eq!(context.association_count().unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -242,6 +329,6 @@ mod tests {
|
||||
|
||||
run_with_context(source, context.clone()).unwrap();
|
||||
|
||||
assert_eq!(context.variable_count(), 0);
|
||||
assert_eq!(context.association_count().unwrap(), 0);
|
||||
}
|
||||
}
|
||||
|
30
dust-lang/src/core_library.rs
Normal file
30
dust-lang/src/core_library.rs
Normal 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),
|
||||
),
|
||||
),
|
||||
]))
|
||||
})
|
||||
}
|
@ -64,7 +64,7 @@ impl<'src> DustError<'src> {
|
||||
pub fn position(&self) -> Option<Span> {
|
||||
match self {
|
||||
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::Lex { lex_error, .. } => Some(lex_error.position()),
|
||||
}
|
||||
|
@ -429,17 +429,12 @@ impl<'src> Lexer<'src> {
|
||||
"float" => Token::FloatKeyword,
|
||||
"if" => Token::If,
|
||||
"int" => Token::Int,
|
||||
"is_even" => Token::IsEven,
|
||||
"is_odd" => Token::IsOdd,
|
||||
"length" => Token::Length,
|
||||
"let" => Token::Let,
|
||||
"map" => Token::Map,
|
||||
"mut" => Token::Mut,
|
||||
"read_line" => Token::ReadLine,
|
||||
"struct" => Token::Struct,
|
||||
"true" => Token::Boolean("true"),
|
||||
"while" => Token::While,
|
||||
"write_line" => Token::WriteLine,
|
||||
_ => Token::Identifier(string),
|
||||
};
|
||||
|
||||
@ -1023,7 +1018,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
lex(input),
|
||||
Ok(vec![
|
||||
(Token::ReadLine, (0, 9)),
|
||||
(Token::Identifier("read_line"), (0, 9)),
|
||||
(Token::LeftParenthesis, (9, 10)),
|
||||
(Token::RightParenthesis, (10, 11)),
|
||||
(Token::Eof, (11, 11)),
|
||||
@ -1038,7 +1033,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
lex(input),
|
||||
Ok(vec![
|
||||
(Token::WriteLine, (0, 10)),
|
||||
(Token::Identifier("write_line"), (0, 10)),
|
||||
(Token::LeftParenthesis, (10, 11)),
|
||||
(Token::String("Hello, world!"), (11, 26)),
|
||||
(Token::RightParenthesis, (26, 27)),
|
||||
@ -1107,7 +1102,7 @@ mod tests {
|
||||
Ok(vec![
|
||||
(Token::Integer("42"), (0, 2)),
|
||||
(Token::Dot, (2, 3)),
|
||||
(Token::IsEven, (3, 10)),
|
||||
(Token::Identifier("is_even"), (3, 10)),
|
||||
(Token::LeftParenthesis, (10, 11)),
|
||||
(Token::RightParenthesis, (11, 12)),
|
||||
(Token::Eof, (12, 12)),
|
||||
@ -1128,7 +1123,10 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
lex(input),
|
||||
Ok(vec![(Token::Length, (0, 6)), (Token::Eof, (6, 6)),])
|
||||
Ok(vec![
|
||||
(Token::Identifier("length"), (0, 6)),
|
||||
(Token::Eof, (6, 6)),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ pub mod ast;
|
||||
pub mod built_in_function;
|
||||
pub mod constructor;
|
||||
pub mod context;
|
||||
pub mod core_library;
|
||||
pub mod dust_error;
|
||||
pub mod identifier;
|
||||
pub mod lexer;
|
||||
@ -30,10 +31,10 @@ pub mod value;
|
||||
pub mod vm;
|
||||
|
||||
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 constructor::Constructor;
|
||||
pub use context::{Context, ContextData};
|
||||
pub use context::{Context, ContextData, ContextError};
|
||||
pub use dust_error::DustError;
|
||||
pub use identifier::Identifier;
|
||||
pub use lexer::{lex, LexError, Lexer};
|
||||
|
@ -27,17 +27,12 @@ pub enum Token<'src> {
|
||||
FloatKeyword,
|
||||
If,
|
||||
Int,
|
||||
IsEven,
|
||||
IsOdd,
|
||||
Length,
|
||||
Let,
|
||||
Map,
|
||||
Mut,
|
||||
ReadLine,
|
||||
Str,
|
||||
Struct,
|
||||
While,
|
||||
WriteLine,
|
||||
|
||||
// Symbols
|
||||
BangEqual,
|
||||
@ -96,12 +91,9 @@ impl<'src> Token<'src> {
|
||||
Token::If => TokenOwned::If,
|
||||
Token::Int => TokenOwned::Int,
|
||||
Token::Integer(integer) => TokenOwned::Integer(integer.to_string()),
|
||||
Token::IsEven => TokenOwned::IsEven,
|
||||
Token::IsOdd => TokenOwned::IsOdd,
|
||||
Token::LeftCurlyBrace => TokenOwned::LeftCurlyBrace,
|
||||
Token::LeftParenthesis => TokenOwned::LeftParenthesis,
|
||||
Token::LeftSquareBrace => TokenOwned::LeftSquareBrace,
|
||||
Token::Length => TokenOwned::Length,
|
||||
Token::Let => TokenOwned::Let,
|
||||
Token::Less => TokenOwned::Less,
|
||||
Token::LessEqual => TokenOwned::LessOrEqual,
|
||||
@ -112,7 +104,6 @@ impl<'src> Token<'src> {
|
||||
Token::Percent => TokenOwned::Percent,
|
||||
Token::Plus => TokenOwned::Plus,
|
||||
Token::PlusEqual => TokenOwned::PlusEqual,
|
||||
Token::ReadLine => TokenOwned::ReadLine,
|
||||
Token::RightCurlyBrace => TokenOwned::RightCurlyBrace,
|
||||
Token::RightParenthesis => TokenOwned::RightParenthesis,
|
||||
Token::RightSquareBrace => TokenOwned::RightSquareBrace,
|
||||
@ -123,7 +114,6 @@ impl<'src> Token<'src> {
|
||||
Token::Str => TokenOwned::Str,
|
||||
Token::Struct => TokenOwned::Struct,
|
||||
Token::While => TokenOwned::While,
|
||||
Token::WriteLine => TokenOwned::WriteLine,
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,13 +144,10 @@ impl<'src> Token<'src> {
|
||||
Token::GreaterEqual => ">=",
|
||||
Token::If => "if",
|
||||
Token::Int => "int",
|
||||
Token::IsEven => "is_even",
|
||||
Token::IsOdd => "is_odd",
|
||||
Token::LeftCurlyBrace => "{",
|
||||
Token::LeftParenthesis => "(",
|
||||
Token::LeftSquareBrace => "[",
|
||||
Token::Let => "let",
|
||||
Token::Length => "length",
|
||||
Token::Less => "<",
|
||||
Token::LessEqual => "<=",
|
||||
Token::Map => "map",
|
||||
@ -170,7 +157,6 @@ impl<'src> Token<'src> {
|
||||
Token::Percent => "%",
|
||||
Token::Plus => "+",
|
||||
Token::PlusEqual => "+=",
|
||||
Token::ReadLine => "read_line",
|
||||
Token::RightCurlyBrace => "}",
|
||||
Token::RightParenthesis => ")",
|
||||
Token::RightSquareBrace => "]",
|
||||
@ -180,7 +166,6 @@ impl<'src> Token<'src> {
|
||||
Token::Str => "str",
|
||||
Token::Struct => "struct",
|
||||
Token::While => "while",
|
||||
Token::WriteLine => "write_line",
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,13 +194,10 @@ impl<'src> Token<'src> {
|
||||
Token::If => TokenKind::If,
|
||||
Token::Int => TokenKind::Int,
|
||||
Token::Integer(_) => TokenKind::Integer,
|
||||
Token::IsEven => TokenKind::IsEven,
|
||||
Token::IsOdd => TokenKind::IsOdd,
|
||||
Token::LeftCurlyBrace => TokenKind::LeftCurlyBrace,
|
||||
Token::LeftParenthesis => TokenKind::LeftParenthesis,
|
||||
Token::LeftSquareBrace => TokenKind::LeftSquareBrace,
|
||||
Token::Let => TokenKind::Let,
|
||||
Token::Length => TokenKind::Length,
|
||||
Token::Less => TokenKind::Less,
|
||||
Token::LessEqual => TokenKind::LessOrEqual,
|
||||
Token::Map => TokenKind::Map,
|
||||
@ -225,7 +207,6 @@ impl<'src> Token<'src> {
|
||||
Token::Percent => TokenKind::Percent,
|
||||
Token::Plus => TokenKind::Plus,
|
||||
Token::PlusEqual => TokenKind::PlusEqual,
|
||||
Token::ReadLine => TokenKind::ReadLine,
|
||||
Token::RightCurlyBrace => TokenKind::RightCurlyBrace,
|
||||
Token::RightParenthesis => TokenKind::RightParenthesis,
|
||||
Token::RightSquareBrace => TokenKind::RightSquareBrace,
|
||||
@ -236,7 +217,6 @@ impl<'src> Token<'src> {
|
||||
Token::String(_) => TokenKind::String,
|
||||
Token::Struct => TokenKind::Struct,
|
||||
Token::While => TokenKind::While,
|
||||
Token::WriteLine => TokenKind::WriteLine,
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,16 +300,11 @@ pub enum TokenOwned {
|
||||
FloatKeyword,
|
||||
If,
|
||||
Int,
|
||||
IsEven,
|
||||
IsOdd,
|
||||
Let,
|
||||
Length,
|
||||
Map,
|
||||
Mut,
|
||||
ReadLine,
|
||||
Str,
|
||||
While,
|
||||
WriteLine,
|
||||
|
||||
// Symbols
|
||||
Async,
|
||||
@ -371,7 +346,7 @@ impl Display for TokenOwned {
|
||||
TokenOwned::Bang => Token::Bang.fmt(f),
|
||||
TokenOwned::BangEqual => Token::BangEqual.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::Comma => Token::Comma.fmt(f),
|
||||
TokenOwned::Dot => Token::Dot.fmt(f),
|
||||
@ -382,20 +357,17 @@ impl Display for TokenOwned {
|
||||
TokenOwned::Else => Token::Else.fmt(f),
|
||||
TokenOwned::Eof => Token::Eof.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::Greater => Token::Greater.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::Int => Token::Int.fmt(f),
|
||||
TokenOwned::Integer(integer) => write!(f, "{integer}"),
|
||||
TokenOwned::IsEven => Token::IsEven.fmt(f),
|
||||
TokenOwned::IsOdd => Token::IsOdd.fmt(f),
|
||||
TokenOwned::Integer(integer) => Token::Integer(integer).fmt(f),
|
||||
TokenOwned::LeftCurlyBrace => Token::LeftCurlyBrace.fmt(f),
|
||||
TokenOwned::LeftParenthesis => Token::LeftParenthesis.fmt(f),
|
||||
TokenOwned::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
|
||||
TokenOwned::Length => Token::Length.fmt(f),
|
||||
TokenOwned::Let => Token::Let.fmt(f),
|
||||
TokenOwned::Less => Token::Less.fmt(f),
|
||||
TokenOwned::LessOrEqual => Token::LessEqual.fmt(f),
|
||||
@ -406,7 +378,6 @@ impl Display for TokenOwned {
|
||||
TokenOwned::Percent => Token::Percent.fmt(f),
|
||||
TokenOwned::Plus => Token::Plus.fmt(f),
|
||||
TokenOwned::PlusEqual => Token::PlusEqual.fmt(f),
|
||||
TokenOwned::ReadLine => Token::ReadLine.fmt(f),
|
||||
TokenOwned::RightCurlyBrace => Token::RightCurlyBrace.fmt(f),
|
||||
TokenOwned::RightParenthesis => Token::RightParenthesis.fmt(f),
|
||||
TokenOwned::RightSquareBrace => Token::RightSquareBrace.fmt(f),
|
||||
@ -417,7 +388,6 @@ impl Display for TokenOwned {
|
||||
TokenOwned::String(string) => write!(f, "{string}"),
|
||||
TokenOwned::Struct => Token::Struct.fmt(f),
|
||||
TokenOwned::While => Token::While.fmt(f),
|
||||
TokenOwned::WriteLine => Token::WriteLine.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -442,15 +412,10 @@ pub enum TokenKind {
|
||||
FloatKeyword,
|
||||
If,
|
||||
Int,
|
||||
IsEven,
|
||||
IsOdd,
|
||||
Length,
|
||||
Let,
|
||||
Map,
|
||||
ReadLine,
|
||||
Str,
|
||||
While,
|
||||
WriteLine,
|
||||
|
||||
// Symbols
|
||||
BangEqual,
|
||||
@ -511,12 +476,9 @@ impl Display for TokenKind {
|
||||
TokenKind::If => Token::If.fmt(f),
|
||||
TokenKind::Int => Token::Int.fmt(f),
|
||||
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::LeftParenthesis => Token::LeftParenthesis.fmt(f),
|
||||
TokenKind::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
|
||||
TokenKind::Length => Token::Length.fmt(f),
|
||||
TokenKind::Let => Token::Let.fmt(f),
|
||||
TokenKind::Less => Token::Less.fmt(f),
|
||||
TokenKind::LessOrEqual => Token::LessEqual.fmt(f),
|
||||
@ -527,7 +489,6 @@ impl Display for TokenKind {
|
||||
TokenKind::Percent => Token::Percent.fmt(f),
|
||||
TokenKind::Plus => Token::Plus.fmt(f),
|
||||
TokenKind::PlusEqual => Token::PlusEqual.fmt(f),
|
||||
TokenKind::ReadLine => Token::ReadLine.fmt(f),
|
||||
TokenKind::RightCurlyBrace => Token::RightCurlyBrace.fmt(f),
|
||||
TokenKind::RightParenthesis => Token::RightParenthesis.fmt(f),
|
||||
TokenKind::RightSquareBrace => Token::RightSquareBrace.fmt(f),
|
||||
@ -538,7 +499,6 @@ impl Display for TokenKind {
|
||||
TokenKind::String => write!(f, "string value"),
|
||||
TokenKind::Struct => Token::Struct.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 {
|
||||
use super::*;
|
||||
|
||||
pub fn all_tokens<'src>() -> [Token<'src>; 51] {
|
||||
pub fn all_tokens<'src>() -> [Token<'src>; 46] {
|
||||
[
|
||||
Token::Identifier("foobar"),
|
||||
Token::Boolean("true"),
|
||||
@ -573,12 +533,9 @@ pub(crate) mod tests {
|
||||
Token::GreaterEqual,
|
||||
Token::If,
|
||||
Token::Int,
|
||||
Token::IsEven,
|
||||
Token::IsOdd,
|
||||
Token::LeftCurlyBrace,
|
||||
Token::LeftParenthesis,
|
||||
Token::LeftSquareBrace,
|
||||
Token::Length,
|
||||
Token::Less,
|
||||
Token::LessEqual,
|
||||
Token::Let,
|
||||
@ -589,7 +546,6 @@ pub(crate) mod tests {
|
||||
Token::Percent,
|
||||
Token::Plus,
|
||||
Token::PlusEqual,
|
||||
Token::ReadLine,
|
||||
Token::RightCurlyBrace,
|
||||
Token::RightParenthesis,
|
||||
Token::RightSquareBrace,
|
||||
@ -599,7 +555,6 @@ pub(crate) mod tests {
|
||||
Token::Str,
|
||||
Token::Struct,
|
||||
Token::While,
|
||||
Token::WriteLine,
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -4,16 +4,11 @@ use std::{
|
||||
collections::HashMap,
|
||||
error::Error,
|
||||
fmt::{self, Display, Formatter},
|
||||
ops::{Index, Range, RangeInclusive},
|
||||
rc::Weak,
|
||||
ops::{Range, RangeInclusive},
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use serde::{
|
||||
de::{self, MapAccess, Visitor},
|
||||
ser::{SerializeMap, SerializeStruct, SerializeStructVariant},
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
use serde::{de::Visitor, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::{
|
||||
AbstractSyntaxTree, BuiltInFunction, Context, EnumType, FunctionType, Identifier,
|
||||
@ -84,36 +79,12 @@ impl 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))
|
||||
pub fn range<T: Into<Rangeable>>(start: T, end: T) -> Value {
|
||||
Value::Range(start.into()..end.into())
|
||||
}
|
||||
|
||||
pub fn byte_range_inclusive(start: u8, end: u8) -> Value {
|
||||
Value::RangeInclusive(Rangeable::Byte(start)..=Rangeable::Byte(end))
|
||||
}
|
||||
|
||||
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 range_inclusive<T: Into<Rangeable>>(start: T, end: T) -> Value {
|
||||
Value::RangeInclusive(start.into()..=end.into())
|
||||
}
|
||||
|
||||
pub fn string<T: ToString>(to_string: T) -> Value {
|
||||
@ -200,9 +171,7 @@ impl Value {
|
||||
value_parameters: built_in_function.value_parameters(),
|
||||
return_type: built_in_function.return_type().map(Box::new),
|
||||
}),
|
||||
Value::Function(Function::Parsed { name, r#type, body }) => {
|
||||
Type::Function(r#type.clone())
|
||||
}
|
||||
Value::Function(Function::Parsed { r#type, .. }) => Type::Function(r#type.clone()),
|
||||
Value::Integer(_) => Type::Integer,
|
||||
Value::List(values) => {
|
||||
let item_type = values.first().unwrap().r#type();
|
||||
@ -265,9 +234,7 @@ impl Value {
|
||||
pub fn get_field(&self, field: &Identifier) -> Option<Value> {
|
||||
if let "to_string" = field.as_str() {
|
||||
return Some(Value::Function(Function::BuiltIn(
|
||||
BuiltInFunction::ToString {
|
||||
argument: Box::new(self.clone()),
|
||||
},
|
||||
BuiltInFunction::ToString,
|
||||
)));
|
||||
}
|
||||
|
||||
@ -1097,13 +1064,13 @@ impl Function {
|
||||
.call(_type_arguments, value_arguments)
|
||||
.map_err(|error| RuntimeError::BuiltInFunctionError { error }),
|
||||
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)) =
|
||||
(&r#type.value_parameters, 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;
|
||||
}
|
||||
|
||||
left_fields.into_iter().cmp(right_fields.into_iter())
|
||||
left_fields.iter().cmp(right_fields.iter())
|
||||
}
|
||||
(Struct::Fields { .. }, _) => Ordering::Greater,
|
||||
}
|
||||
@ -1258,7 +1225,6 @@ impl Display for Struct {
|
||||
|
||||
write!(f, " }}")
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1271,6 +1237,36 @@ pub enum Rangeable {
|
||||
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 {
|
||||
fn r#type(&self) -> RangeableType {
|
||||
match self {
|
||||
|
@ -20,8 +20,8 @@ use crate::{
|
||||
OperatorExpression, PrimitiveValueExpression, RangeExpression, Span, Statement,
|
||||
StructDefinition, StructExpression,
|
||||
},
|
||||
parse, Analyzer, BuiltInFunctionError, Constructor, Context, ContextData, DustError,
|
||||
Expression, Function, Identifier, ParseError, StructType, Type, Value, ValueError,
|
||||
parse, Analyzer, BuiltInFunctionError, Constructor, Context, ContextData, ContextError,
|
||||
DustError, Expression, Function, Identifier, ParseError, StructType, Type, Value, ValueError,
|
||||
};
|
||||
|
||||
/// 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> {
|
||||
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
|
||||
.analyze()
|
||||
@ -224,28 +224,7 @@ impl Vm {
|
||||
Expression::Grouped(expression) => {
|
||||
self.run_expression(*expression.inner, collect_garbage)
|
||||
}
|
||||
Expression::Identifier(identifier) => {
|
||||
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::Identifier(identifier) => self.run_identifier(identifier.inner),
|
||||
Expression::If(if_expression) => self.run_if(*if_expression.inner, collect_garbage),
|
||||
Expression::List(list_expression) => {
|
||||
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(
|
||||
&self,
|
||||
struct_expression: StructExpression,
|
||||
@ -284,7 +283,7 @@ impl Vm {
|
||||
let StructExpression::Fields { name, fields } = struct_expression;
|
||||
|
||||
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 Constructor::Fields(fields_constructor) = constructor {
|
||||
@ -328,16 +327,16 @@ impl Vm {
|
||||
|
||||
match (start, 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)) => {
|
||||
Ok(Evaluation::Return(Some(Value::character_range(start, end))))
|
||||
Ok(Evaluation::Return(Some(Value::range(start, 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)) => {
|
||||
Ok(Evaluation::Return(Some(Value::integer_range(start, end))))
|
||||
Ok(Evaluation::Return(Some(Value::range(start, end))))
|
||||
}
|
||||
_ => Err(RuntimeError::InvalidRange {
|
||||
start_position,
|
||||
@ -356,18 +355,18 @@ impl Vm {
|
||||
.expect_value(end_position)?;
|
||||
|
||||
match (start, end) {
|
||||
(Value::Byte(start), Value::Byte(end)) => Ok(Evaluation::Return(Some(
|
||||
Value::byte_range_inclusive(start, end),
|
||||
))),
|
||||
(Value::Character(start), Value::Character(end)) => Ok(Evaluation::Return(
|
||||
Some(Value::character_range_inclusive(start, end)),
|
||||
)),
|
||||
(Value::Float(start), Value::Float(end)) => Ok(Evaluation::Return(Some(
|
||||
Value::float_range_inclusive(start, end),
|
||||
))),
|
||||
(Value::Integer(start), Value::Integer(end)) => Ok(Evaluation::Return(Some(
|
||||
Value::integer_range_inclusive(start, end),
|
||||
))),
|
||||
(Value::Byte(start), Value::Byte(end)) => {
|
||||
Ok(Evaluation::Return(Some(Value::range_inclusive(start, end))))
|
||||
}
|
||||
(Value::Character(start), Value::Character(end)) => {
|
||||
Ok(Evaluation::Return(Some(Value::range_inclusive(start, end))))
|
||||
}
|
||||
(Value::Float(start), Value::Float(end)) => {
|
||||
Ok(Evaluation::Return(Some(Value::range_inclusive(start, end))))
|
||||
}
|
||||
(Value::Integer(start), Value::Integer(end)) => {
|
||||
Ok(Evaluation::Return(Some(Value::range_inclusive(start, end))))
|
||||
}
|
||||
_ => Err(RuntimeError::InvalidRange {
|
||||
start_position,
|
||||
end_position,
|
||||
@ -661,6 +660,50 @@ impl Vm {
|
||||
|
||||
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 run_invoker = self.run_expression(invoker, collect_garbage)?;
|
||||
|
||||
@ -917,6 +960,7 @@ impl Evaluation {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum RuntimeError {
|
||||
ContextError(ContextError),
|
||||
ParseError(ParseError),
|
||||
Expression {
|
||||
error: Box<RuntimeError>,
|
||||
@ -990,11 +1034,10 @@ pub enum RuntimeError {
|
||||
start_position: Span,
|
||||
end_position: Span,
|
||||
},
|
||||
UndefinedType {
|
||||
UnassociatedIdentifier {
|
||||
identifier: Identifier,
|
||||
position: Span,
|
||||
},
|
||||
UndefinedValue {
|
||||
UndefinedType {
|
||||
identifier: Identifier,
|
||||
position: Span,
|
||||
},
|
||||
@ -1006,10 +1049,19 @@ pub enum RuntimeError {
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ContextError> for RuntimeError {
|
||||
fn from(error: ContextError) -> Self {
|
||||
Self::ContextError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeError {
|
||||
pub fn position(&self) -> Option<Span> {
|
||||
let position = match self {
|
||||
Self::ContextError(_) => return None,
|
||||
Self::BuiltInFunctionError { .. } => return None,
|
||||
Self::UnassociatedIdentifier { .. } => return None,
|
||||
|
||||
Self::ParseError(parse_error) => parse_error.position(),
|
||||
Self::Expression { position, .. } => *position,
|
||||
Self::Statement { position, .. } => *position,
|
||||
@ -1032,14 +1084,14 @@ impl RuntimeError {
|
||||
Self::ExpectedNumber { position } => *position,
|
||||
Self::ExpectedType { position, .. } => *position,
|
||||
Self::ExpectedValue { position } => *position,
|
||||
Self::ExpectedValueOrConstructor { position } => *position,
|
||||
Self::InvalidRange {
|
||||
start_position,
|
||||
end_position,
|
||||
..
|
||||
} => (start_position.0, end_position.1),
|
||||
|
||||
Self::UndefinedType { position, .. } => *position,
|
||||
Self::UndefinedValue { position, .. } => *position,
|
||||
Self::ExpectedValueOrConstructor { position } => *position,
|
||||
Self::UndefinedProperty {
|
||||
property_position, ..
|
||||
} => *property_position,
|
||||
@ -1058,6 +1110,7 @@ impl From<ParseError> for RuntimeError {
|
||||
impl Display for RuntimeError {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::ContextError(context_error) => write!(f, "{}", context_error),
|
||||
Self::ParseError(parse_error) => write!(f, "{}", parse_error),
|
||||
Self::Expression { error, position } => {
|
||||
write!(
|
||||
@ -1181,14 +1234,10 @@ impl Display for RuntimeError {
|
||||
start_position, end_position
|
||||
)
|
||||
}
|
||||
Self::UndefinedValue {
|
||||
identifier,
|
||||
position,
|
||||
} => {
|
||||
Self::UnassociatedIdentifier { identifier } => {
|
||||
write!(
|
||||
f,
|
||||
"Undefined value {} at position: {:?}",
|
||||
identifier, position
|
||||
"Identifier \"{identifier}\" is not associated with a value or constructor"
|
||||
)
|
||||
}
|
||||
Self::UndefinedProperty {
|
||||
@ -1343,7 +1392,7 @@ mod tests {
|
||||
fn range() {
|
||||
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]
|
||||
|
Loading…
Reference in New Issue
Block a user