Add loop and break
This commit is contained in:
parent
169c1a9e3f
commit
f9480ddc24
@ -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();
|
||||||
|
|
||||||
|
@ -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() {
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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),
|
||||||
|
@ -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};
|
||||||
|
@ -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,
|
||||||
|
@ -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()";
|
||||||
|
@ -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,
|
||||||
]
|
]
|
||||||
|
@ -95,32 +95,40 @@ 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)?;
|
||||||
|
|
||||||
|
if let Evaluation::Break(_) = evaluation {
|
||||||
|
Ok(Some(evaluation))
|
||||||
|
} else {
|
||||||
Ok(None)
|
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]";
|
||||||
|
Loading…
Reference in New Issue
Block a user