From b7d01b02f11be9e578a6ac50b3ba1345416e8994 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Mon, 26 Nov 2018 17:23:56 +0100 Subject: [PATCH] Exception throwing when required, and fixes for unit tests --- Upsilon/Binder/Binder.cs | 6 ---- Upsilon/Diagnostics.cs | 11 +++++++- Upsilon/Evaluator/Script.cs | 8 +++--- Upsilon/Exceptions/ParseException.cs | 28 +++++++++++++++++++ Upsilon/Executor.cs | 4 +-- .../StatementSyntax/IfStatementSyntax.cs | 6 ++-- Upsilon/Text/SourceText.cs | 6 ++++ UpsilonTests/GeneralTests/FunctionTests.cs | 4 +-- UpsilonTests/GeneralTests/TableTests.cs | 3 +- .../GeneralTests/UserDataDictionaryTests.cs | 2 +- .../GeneralTests/UserDataListTests.cs | 4 +-- UpsilonTests/GeneralTests/UserDataTests.cs | 11 ++++---- .../BasicFunctionsTests.cs | 2 +- Ycicle/Program.cs | 7 +++-- 14 files changed, 73 insertions(+), 29 deletions(-) create mode 100644 Upsilon/Exceptions/ParseException.cs diff --git a/Upsilon/Binder/Binder.cs b/Upsilon/Binder/Binder.cs index ca60837..e21d08d 100644 --- a/Upsilon/Binder/Binder.cs +++ b/Upsilon/Binder/Binder.cs @@ -201,12 +201,6 @@ namespace Upsilon.Binder { var variableExpression =(BoundVariableExpression) expression; var function = (FunctionVariableSymbol)variableExpression.Variable.VariableSymbol; - if (function.Parameters.Length != parameters.Count) - { - _diagnostics.LogError($"Invalid number of parameters for function '{function.Name}'. " + - $"Expected '{function.Parameters.Length}', got '{parameters.Count}'", e.Span); - return new BoundLiteralExpression(new ScriptNull(), e.Span); - } if (!function.IsBound) { Scope = new BoundScope(Scope); diff --git a/Upsilon/Diagnostics.cs b/Upsilon/Diagnostics.cs index 0ccdb58..7f0720d 100644 --- a/Upsilon/Diagnostics.cs +++ b/Upsilon/Diagnostics.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Upsilon.BaseTypes; +using Upsilon.Exceptions; using Upsilon.Parser; using Upsilon.Text; @@ -9,10 +10,12 @@ namespace Upsilon { public SourceText ScriptString { get; } public readonly List Messages = new List(); + public bool ThrowsOnError { get; } - public Diagnostics(SourceText scriptString) + public Diagnostics(SourceText scriptString, bool throwsOnError) { ScriptString = scriptString; + ThrowsOnError = throwsOnError; } public void Log(DiagnosticLevel level, string message, TextSpan location) @@ -22,6 +25,12 @@ namespace Upsilon public void LogError(string message, TextSpan location) { + if (ThrowsOnError) + { + var linePos = ScriptString.GetLinePosition(location.Start); + var line = ScriptString.GetLine(linePos.Line); + throw new ParseException(message, linePos.Line, linePos.Pos, line); + } Log(DiagnosticLevel.Error, message, location); } diff --git a/Upsilon/Evaluator/Script.cs b/Upsilon/Evaluator/Script.cs index 36cbff3..85a25b9 100644 --- a/Upsilon/Evaluator/Script.cs +++ b/Upsilon/Evaluator/Script.cs @@ -27,7 +27,7 @@ namespace Upsilon.Evaluator Options = options; _scriptString = scriptString; ScriptString = new SourceText(scriptString); - Diagnostics = new Diagnostics(ScriptString); + Diagnostics = new Diagnostics(ScriptString, options.ThrowExceptionOnError); var staticBoundScope = options.OverrideStaticBoundScope ?? StaticScope.BoundScope; var boundScope = BoundScope.WithReadOnlyScope(staticBoundScope); @@ -38,10 +38,10 @@ namespace Upsilon.Evaluator Evaluator = Evaluator.CreateWithSetScope(Diagnostics, Scope); } - private Script(string scriptString, Binder.Binder binder, Evaluator evaluator) + private Script(string scriptString, Binder.Binder binder, Evaluator evaluator, ScriptOptions options) { ScriptString = new SourceText(scriptString); - Diagnostics = new Diagnostics(ScriptString); + Diagnostics = new Diagnostics(ScriptString, Options.ThrowExceptionOnError); Binder = new Binder.Binder(Diagnostics, binder.Scope.Variables); Evaluator = new Evaluator( Diagnostics, evaluator.Scope.Variables); Scope = Evaluator.Scope; @@ -50,7 +50,7 @@ namespace Upsilon.Evaluator internal static Script ContinueWith(Script previousScript, string scriptString) { - var s = new Script(scriptString, previousScript.Binder, previousScript.Evaluator); + var s = new Script(scriptString, previousScript.Binder, previousScript.Evaluator, previousScript.Options); return s; } diff --git a/Upsilon/Exceptions/ParseException.cs b/Upsilon/Exceptions/ParseException.cs new file mode 100644 index 0000000..c01abdc --- /dev/null +++ b/Upsilon/Exceptions/ParseException.cs @@ -0,0 +1,28 @@ +using System; +using Upsilon.Text; + +namespace Upsilon.Exceptions +{ + public class ParseException : Exception + { + public string ErrorMessage { get; } + public int Line { get; } + public int Character { get; } + public string ErrorLine { get; } + + public ParseException(string errorMessage, int line, int character, string errorLine) + { + ErrorMessage = errorMessage; + Line = line; + Character = character; + ErrorLine = errorLine; + } + + public override string ToString() + { + return $"{ErrorMessage} at ({Line}, {Character})\n{ErrorLine}"; + } + + public override string Message => ToString(); + } +} \ No newline at end of file diff --git a/Upsilon/Executor.cs b/Upsilon/Executor.cs index dbb7854..446c8d9 100644 --- a/Upsilon/Executor.cs +++ b/Upsilon/Executor.cs @@ -44,13 +44,13 @@ namespace Upsilon public static T EvaluateFunction(string input, string function, object[] parameters = null, ScriptOptions options = null) { - var script = ParseInput(input, options); + var script = ParseInputAndEvaluate(input, options); return script.EvaluateFunction(function, parameters); } public static object EvaluateFunction(string input, string function, object[] parameters = null, ScriptOptions options = null) { - var script = ParseInput(input, options); + var script = ParseInputAndEvaluate(input, options); return script.EvaluateFunction(function, parameters); } } diff --git a/Upsilon/Parser/StatementSyntax/IfStatementSyntax.cs b/Upsilon/Parser/StatementSyntax/IfStatementSyntax.cs index 3c1debf..b953478 100644 --- a/Upsilon/Parser/StatementSyntax/IfStatementSyntax.cs +++ b/Upsilon/Parser/StatementSyntax/IfStatementSyntax.cs @@ -16,7 +16,9 @@ namespace Upsilon.Parser ThenToken = thenToken; Block = block; EndToken = endToken; - Span = new TextSpan(IfToken.Span.Start, EndToken.Span.End - IfToken.Span.Start); + var end = Block.Span.End; + if (EndToken != null) end = EndToken.Span.End; + Span = new TextSpan(IfToken.Span.Start, end - IfToken.Span.Start); } public IfStatementSyntax(SyntaxToken ifToken, ExpressionStatementSyntax condition, SyntaxToken thenToken, @@ -27,7 +29,7 @@ namespace Upsilon.Parser ThenToken = thenToken; Block = block; ElseStatement = elseStatement; - Span = new TextSpan(IfToken.Span.Start, EndToken.Span.End - IfToken.Span.Start); + Span = new TextSpan(IfToken.Span.Start, ElseStatement.Span.End - IfToken.Span.Start); } public IfStatementSyntax(SyntaxToken ifToken, ExpressionStatementSyntax condition, SyntaxToken thenToken, diff --git a/Upsilon/Text/SourceText.cs b/Upsilon/Text/SourceText.cs index ce3b232..6e99de0 100644 --- a/Upsilon/Text/SourceText.cs +++ b/Upsilon/Text/SourceText.cs @@ -85,6 +85,12 @@ namespace Upsilon.Text return _text.Length; } + public string GetLine(int linePosition) + { + var line = _lines[linePosition]; + return GetSpan(line.Start, line.LineLength); + } + public SourceTextLine GetLineInfo(int lineIndex) { return _lines[lineIndex]; diff --git a/UpsilonTests/GeneralTests/FunctionTests.cs b/UpsilonTests/GeneralTests/FunctionTests.cs index f9e84fa..20f07ea 100644 --- a/UpsilonTests/GeneralTests/FunctionTests.cs +++ b/UpsilonTests/GeneralTests/FunctionTests.cs @@ -1,5 +1,6 @@ using Upsilon; using Upsilon.Evaluator; +using Upsilon.Exceptions; using Xunit; namespace UpsilonTests.GeneralTests @@ -50,8 +51,7 @@ function testFunc (var1) end testFunc(100) "; - var script = Executor.ParseInputAndEvaluate(input, Options); - Assert.True(script.HasVariable("testFunc")); + Assert.Throws(() => Executor.ParseInputAndEvaluate(input, Options)); } [Fact] diff --git a/UpsilonTests/GeneralTests/TableTests.cs b/UpsilonTests/GeneralTests/TableTests.cs index 08092b6..ad586f6 100644 --- a/UpsilonTests/GeneralTests/TableTests.cs +++ b/UpsilonTests/GeneralTests/TableTests.cs @@ -1,5 +1,6 @@ using Upsilon; using Upsilon.Evaluator; +using Upsilon.Exceptions; using Xunit; namespace UpsilonTests.GeneralTests @@ -49,7 +50,7 @@ table = { } return table[""test""] "; - Executor.EvaluateScript(input, Options); + Assert.Throws(() => Executor.EvaluateScript(input, Options)); } [Fact] diff --git a/UpsilonTests/GeneralTests/UserDataDictionaryTests.cs b/UpsilonTests/GeneralTests/UserDataDictionaryTests.cs index 032aa6a..07c51fa 100644 --- a/UpsilonTests/GeneralTests/UserDataDictionaryTests.cs +++ b/UpsilonTests/GeneralTests/UserDataDictionaryTests.cs @@ -21,7 +21,7 @@ function getValue(arr) return arr[""here""] end "; - var evaluated = Executor.EvaluateScript(input, Options); + var evaluated = Executor.EvaluateFunction(input, "getValue", new[] {arr}, Options); Assert.Equal(1683, evaluated); } diff --git a/UpsilonTests/GeneralTests/UserDataListTests.cs b/UpsilonTests/GeneralTests/UserDataListTests.cs index f4ec860..416353f 100644 --- a/UpsilonTests/GeneralTests/UserDataListTests.cs +++ b/UpsilonTests/GeneralTests/UserDataListTests.cs @@ -16,7 +16,7 @@ function getValue(arr) return arr[3] end "; - var evaluated = Executor.EvaluateScript(input, Options); + var evaluated = Executor.EvaluateFunction(input, "getValue", new []{arr}, Options); Assert.Equal(56, evaluated); } @@ -29,7 +29,7 @@ function getValue(arr) return arr[2] end "; - var evaluated = Executor.EvaluateScript(input, Options); + var evaluated = Executor.EvaluateFunction(input, "getValue", new []{arr}, Options); Assert.Equal(30, evaluated); } diff --git a/UpsilonTests/GeneralTests/UserDataTests.cs b/UpsilonTests/GeneralTests/UserDataTests.cs index 1e39e9b..211816c 100644 --- a/UpsilonTests/GeneralTests/UserDataTests.cs +++ b/UpsilonTests/GeneralTests/UserDataTests.cs @@ -2,6 +2,7 @@ using System; using Upsilon; using Upsilon.BaseTypes.UserData; using Upsilon.Evaluator; +using Upsilon.Exceptions; using Xunit; // ReSharper disable UnusedMember.Local @@ -68,7 +69,7 @@ function test(o) o.FieldStringSet = ""Test"" end "; - var result = Executor.EvaluateFunction(input, "test", new[] {obj}, Options); + Executor.EvaluateFunction(input, "test", new[] {obj}, Options); Assert.Equal("Test", obj.FieldStringSet); } @@ -81,7 +82,7 @@ function test(o) o.testMethod() end "; - Executor.EvaluateFunction(input, "test", new[] {obj}, Options); + Executor.EvaluateFunction(input, "test", new[] {obj}, Options); } [Fact] @@ -106,7 +107,7 @@ function test(o) return o._privateTestField end "; - Executor.EvaluateFunction(input, "test", new[] {obj}, Options); + Assert.Throws(() => Executor.EvaluateFunction(input, "test", new[] {obj}, Options)); } [Fact] @@ -118,7 +119,7 @@ function test(o) o.GetOnly = true end "; - Executor.EvaluateFunction(input, "test", new[] {obj}, Options); + Assert.Throws(() => Executor.EvaluateFunction(input, "test", new[] {obj}, Options)); Assert.False(obj.GetOnly); } @@ -131,7 +132,7 @@ function test(o) o.PrivateSet = true end "; - Executor.EvaluateFunction(input, "test", new[] {obj}, Options); + Assert.Throws(() => Executor.EvaluateFunction(input, "test", new[] {obj}, Options)); Assert.False(obj.PrivateSet); } diff --git a/UpsilonTests/StandardLibraryTests/BasicFunctionsTests.cs b/UpsilonTests/StandardLibraryTests/BasicFunctionsTests.cs index bd6baae..5dd115f 100644 --- a/UpsilonTests/StandardLibraryTests/BasicFunctionsTests.cs +++ b/UpsilonTests/StandardLibraryTests/BasicFunctionsTests.cs @@ -15,7 +15,7 @@ namespace UpsilonTests.StandardLibraryTests public void AssertTest() { Executor.EvaluateScript("assert(true)", Options); - Assert.Throws(() => Executor.EvaluateScript("assert(true)", Options)); + Assert.Throws(() => Executor.EvaluateScript("assert(false)", Options)); } [Fact] diff --git a/Ycicle/Program.cs b/Ycicle/Program.cs index 4c811ee..caaff17 100644 --- a/Ycicle/Program.cs +++ b/Ycicle/Program.cs @@ -13,7 +13,10 @@ namespace Ycicle { Console.WriteLine("Upsilon REPL"); Script script = null; - var (evaluationScope, boundScope) = StaticScope.CreateStandardLibrary(); + var options = new ScriptOptions() + { + ThrowExceptionOnError = false + }; while (true) { @@ -21,7 +24,7 @@ namespace Ycicle var input = Console.ReadLine(); if (input == "exit") return; script = script == null - ? Executor.ParseInput(input) + ? Executor.ParseInput(input, options) : Executor.ContinueWith(script, input); if (script.Diagnostics.Messages.Count > 0)