Skip to content

Commit

Permalink
Add 'retry' control flow statement.
Browse files Browse the repository at this point in the history
  • Loading branch information
vddCore committed Jul 3, 2024
1 parent 4f99b83 commit 75fa2b6
Show file tree
Hide file tree
Showing 17 changed files with 138 additions and 42 deletions.
7 changes: 7 additions & 0 deletions Core/EVIL.Grammar/AST/Statements/RetryStatement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace EVIL.Grammar.AST.Statements;

using Base;

public class RetryStatement : Statement
{
}
2 changes: 1 addition & 1 deletion Core/EVIL.Grammar/EVIL.Grammar.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>

<Version>4.3.0</Version>
<Version>4.4.0</Version>

<IsPackable>false</IsPackable>
</PropertyGroup>
Expand Down
4 changes: 4 additions & 0 deletions Core/EVIL.Grammar/Parsing/Base/Parser.Statement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ private Statement Statement()
node = TryStatement();
break;

case TokenType.Retry:
node = RetryStatement();
break;

case TokenType.Throw:
node = ThrowStatement();
Match(Token.Semicolon);
Expand Down
15 changes: 15 additions & 0 deletions Core/EVIL.Grammar/Parsing/Statements/Parser.RetryStatement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace EVIL.Grammar.Parsing;

using AST.Statements;
using Lexical;

public partial class Parser
{
private RetryStatement RetryStatement()
{
var (line, col) = Match(Token.Retry);
Match(Token.Semicolon);

return new RetryStatement { Line = line, Column = col };
}
}
2 changes: 2 additions & 0 deletions Core/EVIL.Grammar/Traversal/AstVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ protected AstVisitor()
{ typeof(SkipStatement), (n) => Visit((SkipStatement)n) },
{ typeof(TryStatement), (n) => Visit((TryStatement)n) },
{ typeof(ThrowStatement), (n) => Visit((ThrowStatement)n) },
{ typeof(RetryStatement), (n) => Visit((RetryStatement)n) },
{ typeof(TableExpression), (n) => Visit((TableExpression)n) },
{ typeof(ArrayExpression), (n) => Visit((ArrayExpression)n) },
{ typeof(ErrorExpression), (n) => Visit((ErrorExpression)n) },
Expand Down Expand Up @@ -116,6 +117,7 @@ public virtual void Visit(AstNode node)
public abstract void Visit(SkipStatement skipStatement);
public abstract void Visit(TryStatement tryStatement);
public abstract void Visit(ThrowStatement throwStatement);
public abstract void Visit(RetryStatement retryStatement);
public abstract void Visit(TableExpression tableExpression);
public abstract void Visit(ArrayExpression arrayExpression);
public abstract void Visit(ErrorExpression errorExpression);
Expand Down
2 changes: 1 addition & 1 deletion Core/EVIL.Lexical/EVIL.Lexical.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>

<Version>4.1.0</Version>
<Version>4.2.0</Version>

<IsPackable>false</IsPackable>
</PropertyGroup>
Expand Down
2 changes: 1 addition & 1 deletion Core/EVIL.Lexical/Lexer.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Globalization;
using System.Text;
using System.Threading.Tasks;

namespace EVIL.Lexical
{
Expand Down Expand Up @@ -479,6 +478,7 @@ private Token GetIdentifierOrKeyword()
"array" => Token.Array,
"self" => Token.Self,
"error" => Token.Error,
"retry" => Token.Retry,
"Infinity" => Token.Infinity,
"NaN" => Token.NaN,
"Nil" => Token.NilTypeCode,
Expand Down
3 changes: 2 additions & 1 deletion Core/EVIL.Lexical/Token.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ public override int GetHashCode()
public static readonly Token Try = new(TokenType.Try, TokenClass.Keyword, "try");
public static readonly Token Catch = new(TokenType.Catch, TokenClass.Keyword, "catch");
public static readonly Token Throw = new(TokenType.Throw, TokenClass.Keyword, "throw");

public static readonly Token Retry = new(TokenType.Retry, TokenClass.Keyword, "retry");

public static readonly Token Val = new(TokenType.Val, TokenClass.Keyword, "val");
public static readonly Token If = new(TokenType.If, TokenClass.Keyword, "if");
public static readonly Token Elif = new(TokenType.Elif, TokenClass.Keyword, "elif");
Expand Down
1 change: 1 addition & 0 deletions Core/EVIL.Lexical/TokenType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public enum TokenType
Try,
Catch,
Throw,
Retry,
Yield,
Rw,
NaN,
Expand Down
5 changes: 4 additions & 1 deletion VirtualMachine/EVIL.Ceres/EVIL.Ceres.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>

<Version>7.6.0</Version>
<Version>7.7.0</Version>

<IsPackable>false</IsPackable>
</PropertyGroup>
Expand Down Expand Up @@ -175,5 +175,8 @@
<Compile Update="TranslationEngine\Compiler.Statement.FnIndexed.cs">
<DependentUpon>Compiler.cs</DependentUpon>
</Compile>
<Compile Update="TranslationEngine\Compiler.Statement.Retry.cs">
<DependentUpon>Compiler.cs</DependentUpon>
</Compile>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ namespace EVIL.Ceres.ExecutionEngine.Diagnostics
{
public class BlockProtectionInfo
{
public int EnterLabelId { get; internal set; }
public int StartAddress { get; internal set; }
public int Length { get; internal set; }
public int HandlerAddress { get; internal set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,10 @@ public override void Visit(BlockStatement blockStatement)
{
InNewLocalScopeDo(() =>
{
_blockDescent++;
foreach (var node in blockStatement.Statements)
{
foreach (var node in blockStatement.Statements)
{
Visit(node);
}
Visit(node);
}
_blockDescent--;
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ public partial class Compiler
{
public override void Visit(ExpressionBodyStatement expressionBodyStatement)
{
_blockDescent++;
Visit(expressionBodyStatement.Expression);
Chunk.CodeGenerator.Emit(OpCode.RET);
_blockDescent--;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace EVIL.Ceres.TranslationEngine;

using System.Linq;
using Diagnostics;
using ExecutionEngine.Diagnostics;
using Grammar.AST.Statements;

public partial class Compiler
{
public override void Visit(RetryStatement retryStatement)
{
if (!_blockProtectors.Any())
{
Log.TerminateWithFatal(
"`retry' was not expected at this time.",
CurrentFileName,
EvilMessageCode.UnexpectedSyntaxNodeFound,
retryStatement.Line,
retryStatement.Column
);
}

Chunk.CodeGenerator.Emit(
OpCode.JUMP,
BlockProtector.EnterLabelId
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,48 @@ public partial class Compiler
public override void Visit(TryStatement tryStatement)
{
var statementEndLabel = Chunk.CreateLabel();
var enterInsnLabel = Chunk.CreateLabel();

Chunk.CodeGenerator.Emit(OpCode.ENTER, Chunk.ProtectedBlocks.Count);
var protectionInfo = Chunk.AllocateBlockProtector();

protectionInfo.StartAddress = Chunk.CodeGenerator.IP;
Visit(tryStatement.InnerStatement);
protectionInfo.Length = Chunk.CodeGenerator.IP - protectionInfo.StartAddress;
Chunk.CodeGenerator.Emit(OpCode.LEAVE);
Chunk.CodeGenerator.Emit(OpCode.JUMP, statementEndLabel);
protectionInfo.HandlerAddress = Chunk.CodeGenerator.IP;

InNewLocalScopeDo(() =>
_blockProtectors.Push(Chunk.AllocateBlockProtector());
{
BlockProtector.EnterLabelId = enterInsnLabel;
BlockProtector.StartAddress = Chunk.CodeGenerator.IP;
Visit(tryStatement.InnerStatement);
BlockProtector.Length = Chunk.CodeGenerator.IP - BlockProtector.StartAddress;
Chunk.CodeGenerator.Emit(OpCode.LEAVE);
Chunk.CodeGenerator.Emit(OpCode.JUMP, statementEndLabel);
BlockProtector.HandlerAddress = Chunk.CodeGenerator.IP;

if (tryStatement.HandlerExceptionLocal != null)
{
CurrentScope.DefineLocal(
tryStatement.HandlerExceptionLocal.Name,
Chunk.AllocateLocal(),
false,
tryStatement.HandlerExceptionLocal.Line,
tryStatement.HandlerExceptionLocal.Column
);
EmitVarSet(tryStatement.HandlerExceptionLocal.Name);
}
else
InNewLocalScopeDo(() =>
{
Chunk.CodeGenerator.Emit(OpCode.POP);
}
Chunk.CodeGenerator.Emit(OpCode.LEAVE);
if (tryStatement.HandlerExceptionLocal != null)
{
CurrentScope.DefineLocal(
tryStatement.HandlerExceptionLocal.Name,
Chunk.AllocateLocal(),
false,
tryStatement.HandlerExceptionLocal.Line,
tryStatement.HandlerExceptionLocal.Column
);
Visit(tryStatement.HandlerStatement);
});
EmitVarSet(tryStatement.HandlerExceptionLocal.Name);
}
else
{
Chunk.CodeGenerator.Emit(OpCode.POP);
}
Visit(tryStatement.HandlerStatement);
});
}
_blockProtectors.Pop();

Chunk.UpdateLabel(statementEndLabel, Chunk.CodeGenerator.IP);
}

}
}
6 changes: 3 additions & 3 deletions VirtualMachine/EVIL.Ceres/TranslationEngine/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
using EVIL.Grammar.Parsing;
using EVIL.Grammar.Traversal;
using EVIL.Lexical;
using static EVIL.Ceres.TranslationEngine.Loop;

namespace EVIL.Ceres.TranslationEngine
{
Expand All @@ -25,7 +24,7 @@ public partial class Compiler : AstVisitor
private readonly Stack<Chunk> _chunks = new();
private Dictionary<string, List<AttributeProcessor>> _attributeProcessors = new();

private int _blockDescent;
private readonly Stack<BlockProtectionInfo> _blockProtectors = new();
private readonly Stack<Loop> _loopDescent = new();
private readonly List<Scope> _closedScopes = new();

Expand Down Expand Up @@ -54,7 +53,8 @@ private Scope CurrentScope

private Chunk Chunk => _chunks.Peek();
private Loop Loop => _loopDescent.Peek();

private BlockProtectionInfo BlockProtector => _blockProtectors.Peek();

private int Line { get; set; }
private int Column { get; set; }

Expand Down
34 changes: 34 additions & 0 deletions VirtualMachine/Tests/EVIL.Ceres.LanguageTests/tests/023_try.vil
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,38 @@ fn try_no_local {
} catch { caught = true; }

assert(caught);
}

#[test]
fn try_retry_single_level {
rw val retry_count = 0;

try {
throw error { retry_count: ++retry_count };
} catch (e) {
if (e.retry_count < 3) retry;
}

assert.equal(retry_count, 3);
}

#[test]
fn try_retry_nested {
rw val outer_retry_count = 0;
rw val inner_retry_count = 0;

try {
try {
throw error { retry_count: ++inner_retry_count };
} catch (e) {
if (e.retry_count < 5) retry;
}

throw error { retry_count: ++outer_retry_count };
} catch (e) {
if (e.retry_count < 3) retry;
}

assert.equal(outer_retry_count, 3);
assert.equal(inner_retry_count, 7);
}

0 comments on commit 75fa2b6

Please sign in to comment.