Add loop and break

This commit is contained in:
Jeff 2024-08-20 14:45:43 -04:00
parent 169c1a9e3f
commit f9480ddc24
9 changed files with 197 additions and 57 deletions

View File

@ -153,6 +153,11 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
fn analyze_expression(&self, expression: &Expression) -> Result<(), AnalysisError> { fn analyze_expression(&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::Break(break_node) => {
if let Some(expression) = &break_node.inner {
self.analyze_expression(expression)?;
}
}
Expression::Call(call_expression) => { Expression::Call(call_expression) => {
let CallExpression { invoker, arguments } = call_expression.inner.as_ref(); let CallExpression { invoker, arguments } = call_expression.inner.as_ref();

View File

@ -2,20 +2,19 @@ use std::{
cmp::Ordering, cmp::Ordering,
collections::HashMap, collections::HashMap,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
rc::Weak,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{BuiltInFunction, Context, FunctionType, Identifier, RangeableType, StructType, Type};
BuiltInFunction, Context, ContextError, FunctionType, Identifier, RangeableType, StructType,
Type,
};
use super::{AstError, 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 {
Block(Node<Box<BlockExpression>>), Block(Node<Box<BlockExpression>>),
Break(Node<Option<Box<Expression>>>),
Call(Node<Box<CallExpression>>), Call(Node<Box<CallExpression>>),
FieldAccess(Node<Box<FieldAccessExpression>>), FieldAccess(Node<Box<FieldAccessExpression>>),
Grouped(Node<Box<Expression>>), Grouped(Node<Box<Expression>>),
@ -33,6 +32,10 @@ pub enum Expression {
} }
impl Expression { impl Expression {
pub fn r#break(expression: Option<Expression>, position: Span) -> Self {
Self::Break(Node::new(expression.map(Box::new), position))
}
pub fn map<T: Into<Vec<(Node<Identifier>, Expression)>>>(pairs: T, position: Span) -> Self { pub fn map<T: Into<Vec<(Node<Identifier>, Expression)>>>(pairs: T, position: Span) -> Self {
Self::Map(Node::new( Self::Map(Node::new(
Box::new(MapExpression { Box::new(MapExpression {
@ -266,13 +269,15 @@ impl Expression {
} }
} }
pub fn return_type<'recovered>( pub fn return_type(&self, context: &Context) -> Result<Option<Type>, AstError> {
&self,
context: &'recovered Context,
) -> Result<Option<Type>, AstError> {
let return_type = match self { let return_type = match self {
Expression::Block(block_expression) => { Expression::Block(block_expression) => block_expression.inner.return_type(context)?,
return block_expression.inner.return_type(context) Expression::Break(expression_node) => {
if let Some(expression) = expression_node.inner.as_ref() {
expression.return_type(context)?
} else {
None
}
} }
Expression::Call(call_expression) => { Expression::Call(call_expression) => {
let CallExpression { invoker, .. } = call_expression.inner.as_ref(); let CallExpression { invoker, .. } = call_expression.inner.as_ref();
@ -479,6 +484,7 @@ impl Expression {
pub fn position(&self) -> Span { pub fn position(&self) -> Span {
match self { match self {
Expression::Block(block) => block.position, Expression::Block(block) => block.position,
Expression::Break(expression_node) => expression_node.position,
Expression::Call(call) => call.position, Expression::Call(call) => call.position,
Expression::FieldAccess(field_access) => field_access.position, Expression::FieldAccess(field_access) => field_access.position,
Expression::Grouped(grouped) => grouped.position, Expression::Grouped(grouped) => grouped.position,
@ -501,6 +507,13 @@ impl Display for Expression {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
Expression::Block(block) => write!(f, "{}", block.inner), Expression::Block(block) => write!(f, "{}", block.inner),
Expression::Break(break_node) => {
if let Some(expression_node) = &break_node.inner {
write!(f, "break {};", expression_node)
} else {
write!(f, "break;")
}
}
Expression::Call(call) => write!(f, "{}", call.inner), Expression::Call(call) => write!(f, "{}", call.inner),
Expression::FieldAccess(field_access) => write!(f, "{}", field_access.inner), Expression::FieldAccess(field_access) => write!(f, "{}", field_access.inner),
Expression::Grouped(grouped) => write!(f, "({})", grouped.inner), Expression::Grouped(grouped) => write!(f, "({})", grouped.inner),
@ -1027,10 +1040,7 @@ pub enum BlockExpression {
} }
impl BlockExpression { impl BlockExpression {
fn return_type<'recovered>( fn return_type(&self, context: &Context) -> Result<Option<Type>, AstError> {
&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() {

View File

@ -12,7 +12,7 @@ use std::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ContextError, Type}; use crate::ContextError;
pub type Span = (usize, usize); pub type Span = (usize, usize);

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{Context, ContextError, Identifier, Type}; use crate::{Context, Identifier, Type};
use super::{AstError, Expression, Node, Span}; use super::{AstError, Expression, Node, Span};
@ -28,10 +28,7 @@ impl Statement {
} }
} }
pub fn return_type<'recovered>( pub fn return_type(&self, context: &Context) -> Result<Option<Type>, AstError> {
&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(_) => Ok(None), Statement::ExpressionNullified(_) => Ok(None),

View File

@ -1,5 +1,5 @@
//! Top-level error handling for the Dust language. //! Top-level error handling for the Dust language.
use annotate_snippets::{Level, Message, Renderer, Snippet}; use annotate_snippets::{Level, Renderer, Snippet};
use std::fmt::Display; use std::fmt::Display;
use crate::{ast::Span, AnalysisError, LexError, ParseError, RuntimeError}; use crate::{ast::Span, AnalysisError, LexError, ParseError, RuntimeError};

View File

@ -424,12 +424,14 @@ impl<'src> Lexer<'src> {
"NaN" => Token::Float("NaN"), "NaN" => Token::Float("NaN"),
"async" => Token::Async, "async" => Token::Async,
"bool" => Token::Bool, "bool" => Token::Bool,
"break" => Token::Break,
"else" => Token::Else, "else" => Token::Else,
"false" => Token::Boolean("false"), "false" => Token::Boolean("false"),
"float" => Token::FloatKeyword, "float" => Token::FloatKeyword,
"if" => Token::If, "if" => Token::If,
"int" => Token::Int, "int" => Token::Int,
"let" => Token::Let, "let" => Token::Let,
"loop" => Token::Loop,
"map" => Token::Map, "map" => Token::Map,
"mut" => Token::Mut, "mut" => Token::Mut,
"struct" => Token::Struct, "struct" => Token::Struct,

View File

@ -413,9 +413,33 @@ impl<'src> Parser<'src> {
error, error,
position: start_position, position: start_position,
})?; })?;
let statement = Expression::literal(boolean, start_position); let expression = Expression::literal(boolean, start_position);
Ok(statement) Ok(expression)
}
Token::Break => {
let break_end = self.current_position.1;
self.next_token()?;
let (expression_option, end) = if let Token::Semicolon = self.current_token {
// Do not consume the semicolon, allowing it to nullify the expression
(None, break_end)
} else if let Ok(expression) = self.parse_expression(0) {
let end = expression.position().1;
(Some(expression), end)
} else {
return Err(ParseError::ExpectedToken {
expected: TokenKind::Semicolon,
actual: self.current_token.to_owned(),
position: self.current_position,
});
};
let position = (start_position.0, end);
Ok(Expression::r#break(expression_option, position))
} }
Token::Float(text) => { Token::Float(text) => {
self.next_token()?; self.next_token()?;
@ -586,6 +610,14 @@ impl<'src> Parser<'src> {
expressions.push(expression); expressions.push(expression);
} }
} }
Token::Loop => {
self.next_token()?;
let block = self.parse_block()?;
let position = (start_position.0, block.position.1);
Ok(Expression::infinite_loop(block, position))
}
Token::Map => { Token::Map => {
self.next_token()?; self.next_token()?;
@ -1145,6 +1177,27 @@ mod tests {
use super::*; use super::*;
#[test]
fn break_loop() {
let source = "loop { break; }";
assert_eq!(
parse(source),
Ok(AbstractSyntaxTree::with_statements([
Statement::Expression(Expression::infinite_loop(
Node::new(
BlockExpression::Sync(vec![Statement::ExpressionNullified(Node::new(
Expression::r#break(None, (7, 12)),
(7, 13)
))]),
(5, 15)
),
(0, 15)
))
]))
);
}
#[test] #[test]
fn built_in_function() { fn built_in_function() {
let source = "42.to_string()"; let source = "42.to_string()";

View File

@ -3,10 +3,6 @@ use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub struct Raw<'src> {
data: &'src str,
}
/// Source code token. /// Source code token.
#[derive(Debug, Serialize, Deserialize, PartialEq)] #[derive(Debug, Serialize, Deserialize, PartialEq)]
pub enum Token<'src> { pub enum Token<'src> {
@ -23,11 +19,13 @@ pub enum Token<'src> {
// Keywords // Keywords
Async, Async,
Bool, Bool,
Break,
Else, Else,
FloatKeyword, FloatKeyword,
If, If,
Int, Int,
Let, Let,
Loop,
Map, Map,
Mut, Mut,
Str, Str,
@ -73,6 +71,7 @@ impl<'src> Token<'src> {
Token::Bang => TokenOwned::Bang, Token::Bang => TokenOwned::Bang,
Token::Bool => TokenOwned::Bool, Token::Bool => TokenOwned::Bool,
Token::Boolean(boolean) => TokenOwned::Boolean(boolean.to_string()), Token::Boolean(boolean) => TokenOwned::Boolean(boolean.to_string()),
Token::Break => TokenOwned::Break,
Token::Colon => TokenOwned::Colon, Token::Colon => TokenOwned::Colon,
Token::Comma => TokenOwned::Comma, Token::Comma => TokenOwned::Comma,
Token::Dot => TokenOwned::Dot, Token::Dot => TokenOwned::Dot,
@ -97,6 +96,7 @@ impl<'src> Token<'src> {
Token::Let => TokenOwned::Let, Token::Let => TokenOwned::Let,
Token::Less => TokenOwned::Less, Token::Less => TokenOwned::Less,
Token::LessEqual => TokenOwned::LessOrEqual, Token::LessEqual => TokenOwned::LessOrEqual,
Token::Loop => TokenOwned::Loop,
Token::Map => TokenOwned::Map, Token::Map => TokenOwned::Map,
Token::Minus => TokenOwned::Minus, Token::Minus => TokenOwned::Minus,
Token::MinusEqual => TokenOwned::MinusEqual, Token::MinusEqual => TokenOwned::MinusEqual,
@ -129,6 +129,7 @@ impl<'src> Token<'src> {
Token::BangEqual => "!=", Token::BangEqual => "!=",
Token::Bang => "!", Token::Bang => "!",
Token::Bool => "bool", Token::Bool => "bool",
Token::Break => "break",
Token::Colon => ":", Token::Colon => ":",
Token::Comma => ",", Token::Comma => ",",
Token::Dot => ".", Token::Dot => ".",
@ -150,6 +151,7 @@ impl<'src> Token<'src> {
Token::Let => "let", Token::Let => "let",
Token::Less => "<", Token::Less => "<",
Token::LessEqual => "<=", Token::LessEqual => "<=",
Token::Loop => "loop",
Token::Map => "map", Token::Map => "map",
Token::Minus => "-", Token::Minus => "-",
Token::MinusEqual => "-=", Token::MinusEqual => "-=",
@ -176,6 +178,7 @@ impl<'src> Token<'src> {
Token::Bang => TokenKind::Bang, Token::Bang => TokenKind::Bang,
Token::Bool => TokenKind::Bool, Token::Bool => TokenKind::Bool,
Token::Boolean(_) => TokenKind::Boolean, Token::Boolean(_) => TokenKind::Boolean,
Token::Break => TokenKind::Break,
Token::Colon => TokenKind::Colon, Token::Colon => TokenKind::Colon,
Token::Comma => TokenKind::Comma, Token::Comma => TokenKind::Comma,
Token::Dot => TokenKind::Dot, Token::Dot => TokenKind::Dot,
@ -200,6 +203,7 @@ impl<'src> Token<'src> {
Token::Let => TokenKind::Let, Token::Let => TokenKind::Let,
Token::Less => TokenKind::Less, Token::Less => TokenKind::Less,
Token::LessEqual => TokenKind::LessOrEqual, Token::LessEqual => TokenKind::LessOrEqual,
Token::Loop => TokenKind::Loop,
Token::Map => TokenKind::Map, Token::Map => TokenKind::Map,
Token::Minus => TokenKind::Minus, Token::Minus => TokenKind::Minus,
Token::MinusEqual => TokenKind::MinusEqual, Token::MinusEqual => TokenKind::MinusEqual,
@ -296,11 +300,13 @@ pub enum TokenOwned {
// Keywords // Keywords
Bool, Bool,
Break,
Else, Else,
FloatKeyword, FloatKeyword,
If, If,
Int, Int,
Let, Let,
Loop,
Map, Map,
Mut, Mut,
Str, Str,
@ -347,6 +353,7 @@ impl Display for TokenOwned {
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) => Token::Boolean(boolean).fmt(f), TokenOwned::Boolean(boolean) => Token::Boolean(boolean).fmt(f),
TokenOwned::Break => Token::Break.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),
@ -371,6 +378,7 @@ impl Display for TokenOwned {
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),
TokenOwned::Loop => Token::Loop.fmt(f),
TokenOwned::Map => Token::Map.fmt(f), TokenOwned::Map => Token::Map.fmt(f),
TokenOwned::Minus => Token::Minus.fmt(f), TokenOwned::Minus => Token::Minus.fmt(f),
TokenOwned::MinusEqual => Token::MinusEqual.fmt(f), TokenOwned::MinusEqual => Token::MinusEqual.fmt(f),
@ -385,7 +393,7 @@ impl Display for TokenOwned {
TokenOwned::Star => Token::Star.fmt(f), TokenOwned::Star => Token::Star.fmt(f),
TokenOwned::Slash => Token::Slash.fmt(f), TokenOwned::Slash => Token::Slash.fmt(f),
TokenOwned::Str => Token::Str.fmt(f), TokenOwned::Str => Token::Str.fmt(f),
TokenOwned::String(string) => write!(f, "{string}"), TokenOwned::String(string) => Token::String(string).fmt(f),
TokenOwned::Struct => Token::Struct.fmt(f), TokenOwned::Struct => Token::Struct.fmt(f),
TokenOwned::While => Token::While.fmt(f), TokenOwned::While => Token::While.fmt(f),
} }
@ -408,11 +416,13 @@ pub enum TokenKind {
// Keywords // Keywords
Async, Async,
Bool, Bool,
Break,
Else, Else,
FloatKeyword, FloatKeyword,
If, If,
Int, Int,
Let, Let,
Loop,
Map, Map,
Str, Str,
While, While,
@ -458,6 +468,7 @@ impl Display for TokenKind {
TokenKind::BangEqual => Token::BangEqual.fmt(f), TokenKind::BangEqual => Token::BangEqual.fmt(f),
TokenKind::Bool => Token::Bool.fmt(f), TokenKind::Bool => Token::Bool.fmt(f),
TokenKind::Boolean => write!(f, "boolean value"), TokenKind::Boolean => write!(f, "boolean value"),
TokenKind::Break => Token::Break.fmt(f),
TokenKind::Colon => Token::Colon.fmt(f), TokenKind::Colon => Token::Colon.fmt(f),
TokenKind::Comma => Token::Comma.fmt(f), TokenKind::Comma => Token::Comma.fmt(f),
TokenKind::Dot => Token::Dot.fmt(f), TokenKind::Dot => Token::Dot.fmt(f),
@ -482,6 +493,7 @@ impl Display for TokenKind {
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),
TokenKind::Loop => Token::Loop.fmt(f),
TokenKind::Map => Token::Map.fmt(f), TokenKind::Map => Token::Map.fmt(f),
TokenKind::Minus => Token::Minus.fmt(f), TokenKind::Minus => Token::Minus.fmt(f),
TokenKind::MinusEqual => Token::MinusEqual.fmt(f), TokenKind::MinusEqual => Token::MinusEqual.fmt(f),
@ -494,7 +506,7 @@ impl Display for TokenKind {
TokenKind::RightSquareBrace => Token::RightSquareBrace.fmt(f), TokenKind::RightSquareBrace => Token::RightSquareBrace.fmt(f),
TokenKind::Semicolon => Token::Semicolon.fmt(f), TokenKind::Semicolon => Token::Semicolon.fmt(f),
TokenKind::Star => Token::Star.fmt(f), TokenKind::Star => Token::Star.fmt(f),
TokenKind::Str => write!(f, "str"), TokenKind::Str => Token::Str.fmt(f),
TokenKind::Slash => Token::Slash.fmt(f), TokenKind::Slash => Token::Slash.fmt(f),
TokenKind::String => write!(f, "string value"), TokenKind::String => write!(f, "string value"),
TokenKind::Struct => Token::Struct.fmt(f), TokenKind::Struct => Token::Struct.fmt(f),
@ -507,17 +519,13 @@ impl Display for TokenKind {
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;
pub fn all_tokens<'src>() -> [Token<'src>; 46] { pub fn all_tokens<'src>() -> [Token<'src>; 47] {
[ [
Token::Identifier("foobar"),
Token::Boolean("true"),
Token::Float("1.0"),
Token::Integer("1"),
Token::String("string"),
Token::Async, Token::Async,
Token::Bang, Token::Bang,
Token::BangEqual, Token::BangEqual,
Token::Bool, Token::Bool,
Token::Break,
Token::Colon, Token::Colon,
Token::Comma, Token::Comma,
Token::Dot, Token::Dot,
@ -536,9 +544,9 @@ pub(crate) mod tests {
Token::LeftCurlyBrace, Token::LeftCurlyBrace,
Token::LeftParenthesis, Token::LeftParenthesis,
Token::LeftSquareBrace, Token::LeftSquareBrace,
Token::Let,
Token::Less, Token::Less,
Token::LessEqual, Token::LessEqual,
Token::Let,
Token::Map, Token::Map,
Token::Minus, Token::Minus,
Token::MinusEqual, Token::MinusEqual,
@ -550,9 +558,14 @@ pub(crate) mod tests {
Token::RightParenthesis, Token::RightParenthesis,
Token::RightSquareBrace, Token::RightSquareBrace,
Token::Semicolon, Token::Semicolon,
Token::Slash,
Token::Star, Token::Star,
Token::Str, Token::Str,
Token::Slash,
Token::Boolean("true"),
Token::Float("0.0"),
Token::Integer("0"),
Token::String("string"),
Token::Identifier("foobar"),
Token::Struct, Token::Struct,
Token::While, Token::While,
] ]

View File

@ -95,31 +95,39 @@ impl Vm {
} }
pub fn run(&mut self) -> Result<Option<Value>, RuntimeError> { pub fn run(&mut self) -> Result<Option<Value>, RuntimeError> {
let mut previous_value = None; let mut previous_evaluation = None;
while let Some(statement) = self.abstract_tree.statements.pop_front() { while let Some(statement) = self.abstract_tree.statements.pop_front() {
previous_value = self.run_statement(statement, true)?; previous_evaluation = self.run_statement(statement, true)?;
} }
Ok(previous_value) match previous_evaluation {
Some(Evaluation::Break(value_option)) => Ok(value_option),
Some(Evaluation::Return(value_option)) => Ok(value_option),
_ => Ok(None),
}
} }
fn run_statement( fn run_statement(
&self, &self,
statement: Statement, statement: Statement,
collect_garbage: bool, collect_garbage: bool,
) -> Result<Option<Value>, RuntimeError> { ) -> Result<Option<Evaluation>, RuntimeError> {
log::debug!("Running statement: {}", statement); log::debug!("Running statement: {}", statement);
let position = statement.position(); let position = statement.position();
let result = match statement { let result = match statement {
Statement::Expression(expression) => self Statement::Expression(expression) => {
.run_expression(expression, collect_garbage) Ok(Some(self.run_expression(expression, collect_garbage)?))
.map(|evaluation| evaluation.value()), }
Statement::ExpressionNullified(expression) => { Statement::ExpressionNullified(expression) => {
self.run_expression(expression.inner, collect_garbage)?; let evaluation = self.run_expression(expression.inner, collect_garbage)?;
Ok(None) if let Evaluation::Break(_) = evaluation {
Ok(Some(evaluation))
} else {
Ok(None)
}
} }
Statement::Let(let_statement) => { Statement::Let(let_statement) => {
self.run_let_statement(let_statement.inner, collect_garbage)?; self.run_let_statement(let_statement.inner, collect_garbage)?;
@ -218,6 +226,23 @@ impl Vm {
let position = expression.position(); let position = expression.position();
let evaluation_result = match expression { let evaluation_result = match expression {
Expression::Block(Node { inner, .. }) => self.run_block(*inner, collect_garbage), Expression::Block(Node { inner, .. }) => self.run_block(*inner, collect_garbage),
Expression::Break(Node { inner, .. }) => {
let break_expression = if let Some(expression) = inner {
*expression
} else {
return Ok(Evaluation::Break(None));
};
let run_break = self.run_expression(break_expression, collect_garbage)?;
let evaluation = match run_break {
Evaluation::Break(value_option) => Evaluation::Break(value_option),
Evaluation::Return(value_option) => Evaluation::Break(value_option),
Evaluation::Constructor(_) => {
return Err(RuntimeError::ExpectedValue { position })
}
};
Ok(evaluation)
}
Expression::Call(call) => self.run_call(*call.inner, collect_garbage), Expression::Call(call) => self.run_call(*call.inner, collect_garbage),
Expression::FieldAccess(field_access) => { Expression::FieldAccess(field_access) => {
self.run_field_access(*field_access.inner, collect_garbage) self.run_field_access(*field_access.inner, collect_garbage)
@ -585,8 +610,19 @@ impl Vm {
fn run_loop(&self, loop_expression: LoopExpression) -> Result<Evaluation, RuntimeError> { fn run_loop(&self, loop_expression: LoopExpression) -> Result<Evaluation, RuntimeError> {
match loop_expression { match loop_expression {
LoopExpression::Infinite { block } => loop { LoopExpression::Infinite {
self.run_block(block.inner.clone(), false)?; block: Node { inner, .. },
} => match inner {
BlockExpression::Sync(statements) => 'outer: loop {
for statement in statements.clone() {
let evaluation = self.run_statement(statement, false)?;
if let Some(Evaluation::Break(value_option)) = evaluation {
break 'outer Ok(Evaluation::Return(value_option));
}
}
},
BlockExpression::Async(_) => todo!(),
}, },
LoopExpression::While { condition, block } => { LoopExpression::While { condition, block } => {
while self while self
@ -849,11 +885,11 @@ impl Vm {
let evaluation_result = self.run_statement(statement, false); let evaluation_result = self.run_statement(statement, false);
match evaluation_result { match evaluation_result {
Ok(evaluation) => { Ok(evaluation_option) => {
if i == statements_length - 1 { if i == statements_length - 1 {
let mut final_result = final_result.lock().unwrap(); let mut final_result = final_result.lock().unwrap();
*final_result = evaluation; *final_result = evaluation_option;
} }
None None
@ -864,18 +900,27 @@ impl Vm {
if let Some(error) = error_option { if let Some(error) = error_option {
Err(error) Err(error)
} else if let Some(evaluation) = final_result.lock().unwrap().take() {
Ok(evaluation)
} else { } else {
Ok(Evaluation::Return(final_result.lock().unwrap().clone())) Ok(Evaluation::Return(None))
} }
} }
BlockExpression::Sync(statements) => { BlockExpression::Sync(statements) => {
let mut previous_value = None; let mut previous_evaluation = None;
for statement in statements { for statement in statements {
previous_value = self.run_statement(statement, collect_garbage)?; previous_evaluation = self.run_statement(statement, collect_garbage)?;
if let Some(Evaluation::Break(value_option)) = previous_evaluation {
return Ok(Evaluation::Break(value_option));
}
} }
Ok(Evaluation::Return(previous_value)) match previous_evaluation {
Some(evaluation) => Ok(evaluation),
None => Ok(Evaluation::Return(None)),
}
} }
} }
} }
@ -898,7 +943,11 @@ impl Vm {
.ok_or(RuntimeError::ExpectedBoolean { position })?; .ok_or(RuntimeError::ExpectedBoolean { position })?;
if boolean { if boolean {
self.run_block(if_block.inner, collect_garbage)?; let evaluation = self.run_block(if_block.inner, collect_garbage)?;
if let Evaluation::Break(_) = evaluation {
return Ok(evaluation);
}
} }
Ok(Evaluation::Return(None)) Ok(Evaluation::Return(None))
@ -916,7 +965,11 @@ impl Vm {
.ok_or(RuntimeError::ExpectedBoolean { position })?; .ok_or(RuntimeError::ExpectedBoolean { position })?;
if boolean { if boolean {
self.run_block(if_block.inner, collect_garbage)?; let evaluation = self.run_block(if_block.inner, collect_garbage)?;
if let Evaluation::Break(_) = evaluation {
return Ok(evaluation);
}
} }
match r#else { match r#else {
@ -931,7 +984,7 @@ impl Vm {
} }
enum Evaluation { enum Evaluation {
Break, Break(Option<Value>),
Constructor(Constructor), Constructor(Constructor),
Return(Option<Value>), Return(Option<Value>),
} }
@ -1262,6 +1315,13 @@ mod tests {
use super::*; use super::*;
#[test]
fn break_loop() {
let input = "let mut x = 0; loop { x += 1; if x == 10 { break; } } x";
assert_eq!(run(input), Ok(Some(Value::mutable(Value::Integer(10)))));
}
#[test] #[test]
fn string_index() { fn string_index() {
let input = "'foo'[0]"; let input = "'foo'[0]";