Refine errors and error propagation
This commit is contained in:
parent
cf9a9837c8
commit
83f856385b
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)?;
|
||||||
|
|
||||||
Some(Type::ListOf {
|
if let Some(r#type) = item_type {
|
||||||
item_type: Box::new(item_type),
|
Some(Type::ListOf {
|
||||||
})
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 let Some(integer) = value_arguments[0].as_integer() {
|
||||||
if value_arguments.len() == 1 {
|
Ok(Some(Value::Boolean(integer % 2 == 0)))
|
||||||
if let Some(integer) = value_arguments[0].as_integer() {
|
|
||||||
Ok(Some(Value::Boolean(integer % 2 == 0)))
|
|
||||||
} else {
|
|
||||||
Err(BuiltInFunctionError::ExpectedInteger)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
Err(BuiltInFunctionError::ExpectedInteger)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BuiltInFunction::IsOdd => {
|
BuiltInFunction::IsOdd => {
|
||||||
if let Some(value_arguments) = value_arguments {
|
if let Some(integer) = value_arguments[0].as_integer() {
|
||||||
if value_arguments.len() == 1 {
|
Ok(Some(Value::Boolean(integer % 2 != 0)))
|
||||||
if let Some(integer) = value_arguments[0].as_integer() {
|
|
||||||
Ok(Some(Value::Boolean(integer % 2 != 0)))
|
|
||||||
} else {
|
|
||||||
Err(BuiltInFunctionError::ExpectedInteger)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
Err(BuiltInFunctionError::ExpectedInteger)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BuiltInFunction::Length => {
|
BuiltInFunction::Length => {
|
||||||
if let Some(value_arguments) = value_arguments {
|
if let Value::List(list) = &value_arguments[0] {
|
||||||
if value_arguments.len() == 1 {
|
Ok(Some(Value::Integer(list.len() as i64)))
|
||||||
if let Value::List(list) = &value_arguments[0] {
|
|
||||||
Ok(Some(Value::Integer(list.len() as i64)))
|
|
||||||
} else {
|
|
||||||
Err(BuiltInFunctionError::ExpectedInteger)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
Err(BuiltInFunctionError::ExpectedList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 {
|
|
||||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
Err(BuiltInFunctionError::ExpectedString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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> {
|
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()),
|
||||||
}
|
}
|
||||||
|
@ -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)),
|
||||||
|
])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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};
|
||||||
|
@ -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,
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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]
|
||||||
|
Loading…
Reference in New Issue
Block a user