diff --git a/src/MoonSharp.Interpreter/Script.cs b/src/MoonSharp.Interpreter/Script.cs
index 3d41e2f8..000b803e 100755
--- a/src/MoonSharp.Interpreter/Script.cs
+++ b/src/MoonSharp.Interpreter/Script.cs
@@ -12,6 +12,7 @@
using MoonSharp.Interpreter.Interop;
using MoonSharp.Interpreter.IO;
using MoonSharp.Interpreter.Platforms;
+using MoonSharp.Interpreter.Tree;
using MoonSharp.Interpreter.Tree.Expressions;
using MoonSharp.Interpreter.Tree.Fast_Interface;
@@ -23,6 +24,32 @@ namespace MoonSharp.Interpreter
///
public class Script : IScriptPrivateResource
{
+ public enum ScriptParserMessageType
+ {
+ Error,
+ Warning,
+ Info
+ }
+
+ public class ScriptParserMessage
+ {
+ public string Msg { get; set; }
+ private Token Token { get; set; }
+ public ScriptParserMessageType Type { get; set; }
+
+ internal ScriptParserMessage(Token token)
+ {
+ Token = token;
+ Msg = $"unexpected symbol near '{token}'";
+ }
+
+ internal ScriptParserMessage(Token token, string msg)
+ {
+ Token = token;
+ Msg = msg;
+ }
+ }
+
///
/// The version of the MoonSharp engine
///
@@ -39,6 +66,7 @@ public class Script : IScriptPrivateResource
Table m_GlobalTable;
IDebugger m_Debugger;
Table[] m_TypeMetatables = new Table[(int)LuaTypeExtensions.MaxMetaTypes];
+ internal List i_ParserMessages { get; set; } = new List();
///
/// Initializes the class.
@@ -838,5 +866,7 @@ Script IScriptPrivateResource.OwnerScript
{
get { return this; }
}
+
+ public List ParserMessages => i_ParserMessages;
}
}
diff --git a/src/MoonSharp.Interpreter/ScriptOptions.cs b/src/MoonSharp.Interpreter/ScriptOptions.cs
index 901d3c4b..238f1a06 100644
--- a/src/MoonSharp.Interpreter/ScriptOptions.cs
+++ b/src/MoonSharp.Interpreter/ScriptOptions.cs
@@ -29,6 +29,12 @@ internal ScriptOptions(ScriptOptions defaults)
this.CheckThreadAccess = defaults.CheckThreadAccess;
}
+
+ public enum ParserErrorModes
+ {
+ Throw,
+ Report
+ }
///
/// Gets or sets the current script-loader.
@@ -127,5 +133,11 @@ internal ScriptOptions(ScriptOptions defaults)
/// These directions will store the RHS as a string annotation on the chunk.
///
public HashSet Directives { get; set; } = new HashSet();
+
+ ///
+ /// Specifies how parser reacts to errors while parsing.
+ /// Options are: Throw (paring is aborted after first error), Report (errors are stashed and available in Script.ParserMessages)
+ ///
+ public ParserErrorModes ParserErrorMode { get; set; } = ParserErrorModes.Throw;
}
}
diff --git a/src/MoonSharp.Interpreter/Tree/Statements/CompositeStatement.cs b/src/MoonSharp.Interpreter/Tree/Statements/CompositeStatement.cs
index e31760b6..e3673e48 100644
--- a/src/MoonSharp.Interpreter/Tree/Statements/CompositeStatement.cs
+++ b/src/MoonSharp.Interpreter/Tree/Statements/CompositeStatement.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using MoonSharp.Interpreter.Execution;
@@ -14,30 +15,85 @@ class CompositeStatement : Statement
{
List m_Statements = new List();
- public Token EndToken;
-
public CompositeStatement(ScriptLoadingContext lcontext, BlockEndType endType)
: base(lcontext)
{
while (true)
{
- ParseAnnotations(lcontext);
- Token t = lcontext.Lexer.Current;
- EndToken = lcontext.Lexer.Current;
- if (t.IsEndOfBlock()) break;
- if (endType == BlockEndType.CloseCurly && t.Type == TokenType.Brk_Close_Curly) break;
- bool forceLast;
-
- Statement s = Statement.CreateStatement(lcontext, out forceLast);
- m_Statements.Add(s);
- EndToken = lcontext.Lexer.Current;
- if (forceLast) break;
+ try
+ {
+ ParseAnnotations(lcontext);
+ Token t = lcontext.Lexer.Current;
+ if (t.IsEndOfBlock()) break;
+ if (endType == BlockEndType.CloseCurly && t.Type == TokenType.Brk_Close_Curly) break;
+
+ Statement s = CreateStatement(lcontext, out bool forceLast);
+ m_Statements.Add(s);
+ if (forceLast) break;
+ }
+ catch (InterpreterException e)
+ {
+ if (lcontext.Script.Options.ParserErrorMode == ScriptOptions.ParserErrorModes.Report)
+ {
+ Token token = null;
+ if (e is SyntaxErrorException se)
+ {
+ token = se.Token;
+ }
+
+ lcontext.Script.i_ParserMessages.Add(new Script.ScriptParserMessage(token, e.Message));
+ Synchronize(lcontext);
+
+ if (lcontext.Lexer.PeekNext().Type == TokenType.Eof)
+ {
+ lcontext.Lexer.Next();
+ break;
+ }
+ }
+ else
+ {
+ throw;
+ }
+ }
}
// eat away all superfluos ';'s
while (lcontext.Lexer.Current.Type == TokenType.SemiColon)
lcontext.Lexer.Next();
}
+
+ private void Synchronize(ScriptLoadingContext lcontext)
+ {
+ while (lcontext.Lexer.PeekNext().Type != TokenType.Eof)
+ {
+ lcontext.Lexer.Next();
+ Token tkn = lcontext.Lexer.Current;
+
+ switch (tkn.Type)
+ {
+ case TokenType.ChunkAnnotation:
+ case TokenType.Local:
+ case TokenType.Until:
+ case TokenType.Break:
+ case TokenType.Continue:
+ case TokenType.While:
+ case TokenType.For:
+ case TokenType.ElseIf:
+ case TokenType.Else:
+ case TokenType.Function:
+ case TokenType.Goto:
+ case TokenType.Directive:
+ case TokenType.Do:
+ case TokenType.If:
+ {
+ goto endSynchronize;
+ }
+ }
+ }
+
+ endSynchronize: ;
+ }
+
public override void ResolveScope(ScriptLoadingContext lcontext)
{
diff --git a/src/MoonSharp.Tests/EndToEnd/CLike/SyntaxCLike/Errors/1-common-invalid.lua b/src/MoonSharp.Tests/EndToEnd/CLike/SyntaxCLike/Errors/1-common-invalid.lua
new file mode 100644
index 00000000..46dd1292
--- /dev/null
+++ b/src/MoonSharp.Tests/EndToEnd/CLike/SyntaxCLike/Errors/1-common-invalid.lua
@@ -0,0 +1,4 @@
+if (
+
+x = 0
+x1 = []
\ No newline at end of file
diff --git a/src/MoonSharp.Tests/EndToEnd/CLike/SyntaxCLike/Errors/1-common-invalid.txt b/src/MoonSharp.Tests/EndToEnd/CLike/SyntaxCLike/Errors/1-common-invalid.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/src/MoonSharp.Tests/EndToEnd/CLike/SyntaxCLike/Errors/2-resync-invalid.lua b/src/MoonSharp.Tests/EndToEnd/CLike/SyntaxCLike/Errors/2-resync-invalid.lua
new file mode 100644
index 00000000..76f75a93
--- /dev/null
+++ b/src/MoonSharp.Tests/EndToEnd/CLike/SyntaxCLike/Errors/2-resync-invalid.lua
@@ -0,0 +1,6 @@
+if (
+
+x = 0
+x1 = []
+
+local x1 = = =
\ No newline at end of file
diff --git a/src/MoonSharp.Tests/EndToEnd/CLike/SyntaxCLike/Errors/2-resync-invalid.txt b/src/MoonSharp.Tests/EndToEnd/CLike/SyntaxCLike/Errors/2-resync-invalid.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/src/MoonSharp.Tests/EndToEnd/CLikeTestRunner.cs b/src/MoonSharp.Tests/EndToEnd/CLikeTestRunner.cs
index 988ca721..4173af85 100644
--- a/src/MoonSharp.Tests/EndToEnd/CLikeTestRunner.cs
+++ b/src/MoonSharp.Tests/EndToEnd/CLikeTestRunner.cs
@@ -16,9 +16,20 @@ static string[] GetTestCases()
return files;
}
+
+ [Test, TestCaseSource(nameof(GetTestCases))]
+ public async Task RunThrowErros(string path)
+ {
+ await RunCore(path);
+ }
[Test, TestCaseSource(nameof(GetTestCases))]
- public async Task Run(string path)
+ public async Task RunReportErrors(string path)
+ {
+ await RunCore(path, true);
+ }
+
+ public async Task RunCore(string path, bool reportErrors = false)
{
string outputPath = path.Replace(".lua", ".txt");
@@ -36,14 +47,21 @@ public async Task Run(string path)
script.Options.DebugPrint = s => stdOut.AppendLine(s);
script.Options.IndexTablesFrom = 0;
+ if (path.Contains("flaky"))
+ {
+ Assert.Inconclusive($"Test {path} marked as flaky");
+ return;
+ }
+
if (path.Contains("SyntaxCLike"))
{
script.Options.Syntax = ScriptSyntax.CLike;
}
- if (path.Contains("flaky"))
+ if (reportErrors)
{
- Assert.Inconclusive($"Test {path} marked as flaky");
+ script.Options.ParserErrorMode = ScriptOptions.ParserErrorModes.Report;
+ await script.DoStringAsync(code);
return;
}