diff --git a/release-notes/v0.5.0.md b/release-notes/v0.5.0.md index a4dc80a4..2988092f 100644 --- a/release-notes/v0.5.0.md +++ b/release-notes/v0.5.0.md @@ -34,6 +34,7 @@ ### Tests - Add `AsciiString_indexed_outside_string` test [[#449][449]] +- Implement more `EvalHelper` methods for compiled mode [[#463][463]] [446]: https://github.com/perlang-org/perlang/pull/446 [447]: https://github.com/perlang-org/perlang/pull/447 @@ -46,4 +47,5 @@ [458]: https://github.com/perlang-org/perlang/pull/458 [459]: https://github.com/perlang-org/perlang/pull/459 [462]: https://github.com/perlang-org/perlang/pull/462 +[463]: https://github.com/perlang-org/perlang/pull/463 [464]: https://github.com/perlang-org/perlang/pull/464 diff --git a/src/Perlang.Interpreter/Compiler/PerlangCompiler.cs b/src/Perlang.Interpreter/Compiler/PerlangCompiler.cs index e956d880..04598026 100644 --- a/src/Perlang.Interpreter/Compiler/PerlangCompiler.cs +++ b/src/Perlang.Interpreter/Compiler/PerlangCompiler.cs @@ -644,6 +644,13 @@ private void RegisterGlobalClasses() // Enable warnings on e.g. narrowing conversion from `long long` to `unsigned long long`. "-Wconversion", + // This could be added, but the problem is that division by zero has undefined behavior in C++. :/ I + // think the long-term goal for Perlang in this regard is to use proper hardware exceptions for this + // (which is natively supported on x86 and amd64, at least), and propagate it as a Perlang exception. + // For platforms without hardware support, we might have to emulate this in software for a coherent + // dev experience. For now, let Clang warn about it when dividing with a zero constant. + //"-Wno-division-by-zero", + // ...but do not warn on implicit conversion from `int` to `float` or `double`. For now, we are // aiming at mimicking the C# semantics in this. "-Wno-implicit-int-float-conversion", diff --git a/src/Perlang.Tests.Integration/Assignment/AssignmentTests.cs b/src/Perlang.Tests.Integration/Assignment/AssignmentTests.cs index 0f8033ee..4595cbf7 100644 --- a/src/Perlang.Tests.Integration/Assignment/AssignmentTests.cs +++ b/src/Perlang.Tests.Integration/Assignment/AssignmentTests.cs @@ -1,4 +1,7 @@ +using System; using System.Linq; +using FluentAssertions; +using Perlang.Compiler; using Xunit; using static Perlang.Tests.Integration.EvalHelper; @@ -37,8 +40,7 @@ public void assignment_is_right_associative() }, output); } - // TODO: This is a bug; create a GitHub issue for it so we can look into it at some point. - [Fact(Skip = "Gives a TypeValidationError: Internal compiler error: var c initializer a = var inference has not been attempted")] + [Fact] public void assignment_on_right_hand_side_of_variable_is_right_associative() { string source = @" @@ -182,11 +184,19 @@ public void undefined_variable_cannot_be_reassigned() unknown = ""what""; "; - var result = EvalWithRuntimeErrorCatch(source); - var exception = result.Errors.First(); + if (PerlangMode.ExperimentalCompilation) { + Action action = () => EvalReturningOutput(source); - Assert.Single(result.Errors); - Assert.Matches("Undefined variable 'unknown'.", exception.Message); + action.Should().Throw() + .WithMessage("*use of undeclared identifier 'unknown'*"); + } + else { + var result = EvalWithRuntimeErrorCatch(source); + var exception = result.Errors.First(); + + Assert.Single(result.Errors); + Assert.Matches("Undefined variable 'unknown'.", exception.Message); + } } } } diff --git a/src/Perlang.Tests.Integration/EvalHelper.cs b/src/Perlang.Tests.Integration/EvalHelper.cs index 502b1af1..6d12d565 100644 --- a/src/Perlang.Tests.Integration/EvalHelper.cs +++ b/src/Perlang.Tests.Integration/EvalHelper.cs @@ -71,23 +71,61 @@ internal static object Eval(string source) /// will be set to `null`. internal static EvalResult EvalWithRuntimeErrorCatch(string source, params string[] arguments) { - var result = new EvalResult(); - - var interpreter = new PerlangInterpreter( - result.ErrorHandler, result.OutputHandler, null, arguments - ); - - result.Value = interpreter.Eval( - source, - AssertFailScanErrorHandler, - AssertFailParseErrorHandler, - AssertFailNameResolutionErrorHandler, - AssertFailValidationErrorHandler, - AssertFailValidationErrorHandler, - result.WarningHandler - ); - - return result; + if (PerlangMode.ExperimentalCompilation) + { + var result = new EvalResult(); + + var compiler = new PerlangCompiler( + result.ErrorHandler, result.OutputHandler, null, arguments + ); + + try + { + result.Value = compiler.CompileAndRun( + source, + CreateTemporaryPath(source), + CompilerFlags.None, + AssertFailScanErrorHandler, + AssertFailParseErrorHandler, + AssertFailNameResolutionErrorHandler, + AssertFailValidationErrorHandler, + AssertFailValidationErrorHandler, + result.WarningHandler + ); + } + catch (NotImplementedInCompiledModeException e) + { + // This exception is thrown to make it possible for integration tests to skip tests for code which + // is known to not yet work. + throw new SkipException(e.Message, e); + } + + // Return something else than `null` to make it reasonable for callers to distinguish that compiled mode + // (with no native "result") is being used, if needed. + result.Value = VoidObject.Void; + + return result; + } + else + { + var result = new EvalResult(); + + var interpreter = new PerlangInterpreter( + result.ErrorHandler, result.OutputHandler, null, arguments + ); + + result.Value = interpreter.Eval( + source, + AssertFailScanErrorHandler, + AssertFailParseErrorHandler, + AssertFailNameResolutionErrorHandler, + AssertFailValidationErrorHandler, + AssertFailValidationErrorHandler, + result.WarningHandler + ); + + return result; + } } /// @@ -108,20 +146,55 @@ internal static EvalResult EvalWithRuntimeErrorCatch(string source /// will be set to `null`. internal static EvalResult EvalWithScanErrorCatch(string source) { - var result = new EvalResult(); - var interpreter = new PerlangInterpreter(AssertFailRuntimeErrorHandler, result.OutputHandler); - - result.Value = interpreter.Eval( - source, - result.ErrorHandler, - AssertFailParseErrorHandler, - AssertFailNameResolutionErrorHandler, - AssertFailValidationErrorHandler, - AssertFailValidationErrorHandler, - result.WarningHandler - ); - - return result; + if (PerlangMode.ExperimentalCompilation) + { + var result = new EvalResult(); + + var compiler = new PerlangCompiler(AssertFailRuntimeErrorHandler, result.OutputHandler); + + try + { + result.Value = compiler.CompileAndRun( + source, + CreateTemporaryPath(source), + CompilerFlags.None, + result.ErrorHandler, + AssertFailParseErrorHandler, + AssertFailNameResolutionErrorHandler, + AssertFailValidationErrorHandler, + AssertFailValidationErrorHandler, + result.WarningHandler + ); + } + catch (NotImplementedInCompiledModeException e) + { + // This exception is thrown to make it possible for integration tests to skip tests for code which + // is known to not yet work. + throw new SkipException(e.Message, e); + } + + // Return something else than `null` to make it reasonable for callers to distinguish that compiled mode + // (with no native "result") is being used, if needed. + result.Value = VoidObject.Void; + + return result; + } + else { + var result = new EvalResult(); + var interpreter = new PerlangInterpreter(AssertFailRuntimeErrorHandler, result.OutputHandler); + + result.Value = interpreter.Eval( + source, + result.ErrorHandler, + AssertFailParseErrorHandler, + AssertFailNameResolutionErrorHandler, + AssertFailValidationErrorHandler, + AssertFailValidationErrorHandler, + result.WarningHandler + ); + + return result; + } } /// @@ -142,20 +215,52 @@ internal static EvalResult EvalWithScanErrorCatch(string source) /// will be set to `null`. internal static EvalResult EvalWithParseErrorCatch(string source) { - var result = new EvalResult(); - var interpreter = new PerlangInterpreter(AssertFailRuntimeErrorHandler, result.OutputHandler); - - result.Value = interpreter.Eval( - source, - AssertFailScanErrorHandler, - result.ErrorHandler, - AssertFailNameResolutionErrorHandler, - AssertFailValidationErrorHandler, - AssertFailValidationErrorHandler, - result.WarningHandler - ); - - return result; + if (PerlangMode.ExperimentalCompilation) { + var result = new EvalResult(); + + var compiler = new PerlangCompiler(AssertFailRuntimeErrorHandler, result.OutputHandler); + + try { + result.Value = compiler.CompileAndRun( + source, + CreateTemporaryPath(source), + CompilerFlags.None, + AssertFailScanErrorHandler, + result.ErrorHandler, + AssertFailNameResolutionErrorHandler, + AssertFailValidationErrorHandler, + AssertFailValidationErrorHandler, + result.WarningHandler + ); + } + catch (NotImplementedInCompiledModeException e) { + // This exception is thrown to make it possible for integration tests to skip tests for code which + // is known to not yet work. + throw new SkipException(e.Message, e); + } + + // Return something else than `null` to make it reasonable for callers to distinguish that compiled mode + // (with no native "result") is being used, if needed. + result.Value = VoidObject.Void; + + return result; + } + else { + var result = new EvalResult(); + var interpreter = new PerlangInterpreter(AssertFailRuntimeErrorHandler, result.OutputHandler); + + result.Value = interpreter.Eval( + source, + AssertFailScanErrorHandler, + result.ErrorHandler, + AssertFailNameResolutionErrorHandler, + AssertFailValidationErrorHandler, + AssertFailValidationErrorHandler, + result.WarningHandler + ); + + return result; + } } /// @@ -176,20 +281,52 @@ internal static EvalResult EvalWithParseErrorCatch(string source) /// will be set to `null`. internal static EvalResult EvalWithNameResolutionErrorCatch(string source) { - var result = new EvalResult(); - var interpreter = new PerlangInterpreter(AssertFailRuntimeErrorHandler, result.OutputHandler); - - result.Value = interpreter.Eval( - source, - AssertFailScanErrorHandler, - AssertFailParseErrorHandler, - result.ErrorHandler, - AssertFailValidationErrorHandler, - AssertFailValidationErrorHandler, - result.WarningHandler - ); - - return result; + if (PerlangMode.ExperimentalCompilation) { + var result = new EvalResult(); + + var compiler = new PerlangCompiler(AssertFailRuntimeErrorHandler, result.OutputHandler); + + try { + result.Value = compiler.CompileAndRun( + source, + CreateTemporaryPath(source), + CompilerFlags.None, + AssertFailScanErrorHandler, + AssertFailParseErrorHandler, + result.ErrorHandler, + AssertFailValidationErrorHandler, + AssertFailValidationErrorHandler, + result.WarningHandler + ); + } + catch (NotImplementedInCompiledModeException e) { + // This exception is thrown to make it possible for integration tests to skip tests for code which + // is known to not yet work. + throw new SkipException(e.Message, e); + } + + // Return something else than `null` to make it reasonable for callers to distinguish that compiled mode + // (with no native "result") is being used, if needed. + result.Value = VoidObject.Void; + + return result; + } + else { + var result = new EvalResult(); + var interpreter = new PerlangInterpreter(AssertFailRuntimeErrorHandler, result.OutputHandler); + + result.Value = interpreter.Eval( + source, + AssertFailScanErrorHandler, + AssertFailParseErrorHandler, + result.ErrorHandler, + AssertFailValidationErrorHandler, + AssertFailValidationErrorHandler, + result.WarningHandler + ); + + return result; + } } /// @@ -210,20 +347,52 @@ internal static EvalResult EvalWithNameResolutionErrorCatch /// will be set to `null`. internal static EvalResult EvalWithValidationErrorCatch(string source) { - var result = new EvalResult(); - var interpreter = new PerlangInterpreter(AssertFailRuntimeErrorHandler, result.OutputHandler); - - result.Value = interpreter.Eval( - source, - AssertFailScanErrorHandler, - AssertFailParseErrorHandler, - AssertFailNameResolutionErrorHandler, - result.ErrorHandler, - result.ErrorHandler, - result.WarningHandler - ); - - return result; + if (PerlangMode.ExperimentalCompilation) { + var result = new EvalResult(); + + var compiler = new PerlangCompiler(AssertFailRuntimeErrorHandler, result.OutputHandler); + + try { + result.Value = compiler.CompileAndRun( + source, + CreateTemporaryPath(source), + CompilerFlags.None, + AssertFailScanErrorHandler, + AssertFailParseErrorHandler, + AssertFailNameResolutionErrorHandler, + result.ErrorHandler, + result.ErrorHandler, + result.WarningHandler + ); + } + catch (NotImplementedInCompiledModeException e) { + // This exception is thrown to make it possible for integration tests to skip tests for code which + // is known to not yet work. + throw new SkipException(e.Message, e); + } + + // Return something else than `null` to make it reasonable for callers to distinguish that compiled mode + // (with no native "result") is being used, if needed. + result.Value = VoidObject.Void; + + return result; + } + else { + var result = new EvalResult(); + var interpreter = new PerlangInterpreter(AssertFailRuntimeErrorHandler, result.OutputHandler); + + result.Value = interpreter.Eval( + source, + AssertFailScanErrorHandler, + AssertFailParseErrorHandler, + AssertFailNameResolutionErrorHandler, + result.ErrorHandler, + result.ErrorHandler, + result.WarningHandler + ); + + return result; + } } /// diff --git a/src/Perlang.Tests.Integration/Immutability/Function.cs b/src/Perlang.Tests.Integration/Immutability/Function.cs index b2c10645..bf07968e 100644 --- a/src/Perlang.Tests.Integration/Immutability/Function.cs +++ b/src/Perlang.Tests.Integration/Immutability/Function.cs @@ -1,4 +1,7 @@ +using System; using System.Linq; +using FluentAssertions; +using Perlang.Compiler; using Xunit; using static Perlang.Tests.Integration.EvalHelper; @@ -16,16 +19,26 @@ fun f(): void {} fun f(): void {} "; - var result = EvalWithRuntimeErrorCatch(source); - var exception = result.Errors.First(); + if (PerlangMode.ExperimentalCompilation) { + Action action = () => EvalReturningOutput(source); - Assert.Single(result.Errors); - Assert.Matches("Variable with this name already declared in this scope.", exception.Message); + action.Should().Throw() + .WithMessage("*Function 'f' is already defined*"); + } + else { + var result = EvalWithRuntimeErrorCatch(source); + var exception = result.Errors.First(); + + Assert.Single(result.Errors); + Assert.Matches("Variable with this name already declared in this scope.", exception.Message); + } } - [Fact] + [SkippableFact] public void function_cannot_be_overwritten_by_a_variable() { + Skip.If(PerlangMode.ExperimentalCompilation, "In compiled mode, functions are shadowed by variables."); + string source = @" fun f(): void {} var f = 42; diff --git a/src/Perlang.Tests.Integration/IndexOperator/DictionaryIndexing.cs b/src/Perlang.Tests.Integration/IndexOperator/DictionaryIndexing.cs index 692a4f09..65b6f55b 100644 --- a/src/Perlang.Tests.Integration/IndexOperator/DictionaryIndexing.cs +++ b/src/Perlang.Tests.Integration/IndexOperator/DictionaryIndexing.cs @@ -51,9 +51,11 @@ public void dictionary_with_string_key_can_be_indexed_multiple_times() #endif } - [Fact] + [SkippableFact] public void dictionary_with_string_key_throws_expected_error_when_indexed_by_not_present_key() { + Skip.If(PerlangMode.ExperimentalCompilation, "Not supported in compiled mode"); + string source = @" var env = Libc.environ(); print env[""NOT_PRESENT_DICTIONARY_KEY""]; diff --git a/src/Perlang.Tests.Integration/Operator/Binary/DivisionTests.cs b/src/Perlang.Tests.Integration/Operator/Binary/DivisionTests.cs index 101ca0b8..473e95d4 100644 --- a/src/Perlang.Tests.Integration/Operator/Binary/DivisionTests.cs +++ b/src/Perlang.Tests.Integration/Operator/Binary/DivisionTests.cs @@ -1,7 +1,9 @@ +using System; using System.Globalization; using System.Linq; using System.Threading.Tasks; using FluentAssertions; +using Perlang.Compiler; using Xunit; using static Perlang.Tests.Integration.EvalHelper; @@ -104,9 +106,6 @@ public void dividing_number_with_non_number_throws_expected_error() Assert.Matches("Unsupported / operand types: 'int' and 'AsciiString'", exception.Message); } - // TODO: This should definitely be a compile-time error (constant division by zero). We should aim for - // TODO: evaluating constant expressions completely at compile-time, which may have other benefits also. - [Fact] public void division_by_zero_throws_expected_runtime_error() { @@ -114,16 +113,27 @@ public void division_by_zero_throws_expected_runtime_error() 1 / 0 "; - var result = EvalWithRuntimeErrorCatch(source); - var exception = result.Errors.FirstOrDefault(); + if (PerlangMode.ExperimentalCompilation) { + Action action = () => EvalReturningOutput(source); - Assert.Single(result.Errors); - Assert.Matches("Attempted to divide by zero.", exception.Message); + // This is horribly hardwired for (a particular version of) CLang, but it will have to do for now. + action.Should().Throw() + .WithMessage("*division by zero is undefined*"); + } + else { + var result = EvalWithRuntimeErrorCatch(source); + var exception = result.Errors.FirstOrDefault(); + + Assert.Single(result.Errors); + Assert.Matches("Attempted to divide by zero.", exception.Message); + } } - [Fact] + [SkippableFact] public void division_by_zero_halts_execution() { + Skip.If(PerlangMode.ExperimentalCompilation, "Not supported in compiled mode"); + string source = @" 1 / 0; print ""hejhej""; diff --git a/src/Perlang.Tests.Integration/Operator/Binary/ExponentialTests.cs b/src/Perlang.Tests.Integration/Operator/Binary/ExponentialTests.cs index d038f39c..37421cc3 100644 --- a/src/Perlang.Tests.Integration/Operator/Binary/ExponentialTests.cs +++ b/src/Perlang.Tests.Integration/Operator/Binary/ExponentialTests.cs @@ -1,8 +1,10 @@ +using System; using System.Globalization; using System.Linq; using System.Numerics; using System.Threading.Tasks; using FluentAssertions; +using Perlang.Compiler; using Xunit; using static Perlang.Tests.Integration.EvalHelper; @@ -76,8 +78,18 @@ public void exponential_positive_and_negative_integer_literals_throws_expected_e var result = EvalWithRuntimeErrorCatch(source); var exception = result.Errors.First(); - Assert.Single(result.Errors); - Assert.Equal("The number must be greater than or equal to zero. (Parameter 'exponent')", exception.Message); + if (PerlangMode.ExperimentalCompilation) { + result.Errors + .Should().ContainSingle() + .Which.Message.Should().Contain("exited with exit code 134"); + + result.Output.Should() + .ContainMatch("*The exponent must be greater than or equal to zero*"); + } + else { + Assert.Single(result.Errors); + Assert.Equal("The number must be greater than or equal to zero. (Parameter 'exponent')", exception.Message); + } } [SkippableFact] @@ -288,10 +300,20 @@ public void exponential_bigint_and_negative_int_throws_expected_runtime_error() "; var result = EvalWithRuntimeErrorCatch(source); - var exception = result.Errors.First(); - Assert.Single(result.Errors); - Assert.Equal("The number must be greater than or equal to zero. (Parameter 'exponent')", exception.Message); + if (PerlangMode.ExperimentalCompilation) { + result.Errors + .Should().ContainSingle() + .Which.Message.Should().Contain("exited with exit code 134"); + + result.Output.Should() + .ContainMatch("*The exponent must be greater than or equal to zero*"); + } + else { + result.Errors + .Should().ContainSingle() + .Which.Message.Should().Contain("The number must be greater than or equal to zero. (Parameter 'exponent')"); + } } [Fact] @@ -331,13 +353,19 @@ public void exponential_function_reference_and_literal_throws_expected_error() print base ** 8; "; - // TODO: Should not be runtime error but rather a compile-time error from the TypeResolver class. See the - // TODO: comment in TypeResolver.VisitBinaryExpr() for more details about why this is not currently doable. - var result = EvalWithRuntimeErrorCatch(source); - var exception = result.Errors.First(); + if (PerlangMode.ExperimentalCompilation) { + Action action = () => EvalReturningOutput(source); - Assert.Single(result.Errors); - Assert.Matches("Operands must be numbers, not function and int", exception.Message); + action.Should().Throw() + .WithMessage("*reference to type 'const BigInt' could not bind to an lvalue of type 'int32_t ()'*"); + } + else { + var result = EvalWithRuntimeErrorCatch(source); + var exception = result.Errors.First(); + + Assert.Single(result.Errors); + Assert.Matches("Operands must be numbers, not function and int", exception.Message); + } } } } diff --git a/src/Perlang.Tests.Integration/Operator/PostfixDecrement.cs b/src/Perlang.Tests.Integration/Operator/PostfixDecrement.cs index 8bf7dcc2..6dabeb0a 100644 --- a/src/Perlang.Tests.Integration/Operator/PostfixDecrement.cs +++ b/src/Perlang.Tests.Integration/Operator/PostfixDecrement.cs @@ -1,6 +1,9 @@ +using System; using System.Globalization; using System.Linq; using System.Threading.Tasks; +using FluentAssertions; +using Perlang.Compiler; using Xunit; using static Perlang.Tests.Integration.EvalHelper; @@ -114,11 +117,19 @@ public void decrementing_null_variable_throws_expected_exception() s--; "; - var result = EvalWithRuntimeErrorCatch(source); - var exception = result.Errors.First(); + if (PerlangMode.ExperimentalCompilation) { + Action action = () => EvalReturningOutput(source); - Assert.Single(result.Errors); - Assert.Matches("can only be used to decrement numbers, not null", exception.Message); + action.Should().Throw() + .WithMessage("*cannot decrement value of type*"); + } + else { + var result = EvalWithRuntimeErrorCatch(source); + var exception = result.Errors.First(); + + Assert.Single(result.Errors); + Assert.Matches("can only be used to decrement numbers, not null", exception.Message); + } } [Fact] @@ -129,11 +140,19 @@ public void decrementing_string_throws_expected_exception() s--; "; - var result = EvalWithRuntimeErrorCatch(source); - var exception = result.Errors.First(); + if (PerlangMode.ExperimentalCompilation) { + Action action = () => EvalReturningOutput(source); - Assert.Single(result.Errors); - Assert.Matches("can only be used to decrement numbers, not AsciiString", exception.Message); + action.Should().Throw() + .WithMessage("*cannot decrement value of type*"); + } + else { + var result = EvalWithRuntimeErrorCatch(source); + var exception = result.Errors.First(); + + Assert.Single(result.Errors); + Assert.Matches("can only be used to decrement numbers, not AsciiString", exception.Message); + } } } } diff --git a/src/Perlang.Tests.Integration/Operator/PostfixIncrement.cs b/src/Perlang.Tests.Integration/Operator/PostfixIncrement.cs index 3609054f..4a40fd56 100644 --- a/src/Perlang.Tests.Integration/Operator/PostfixIncrement.cs +++ b/src/Perlang.Tests.Integration/Operator/PostfixIncrement.cs @@ -1,6 +1,9 @@ +using System; using System.Globalization; using System.Linq; using System.Threading.Tasks; +using FluentAssertions; +using Perlang.Compiler; using Xunit; using static Perlang.Tests.Integration.EvalHelper; @@ -121,11 +124,19 @@ public void incrementing_null_variable_throws_expected_exception() s++; "; - var result = EvalWithRuntimeErrorCatch(source); - var exception = result.Errors.First(); + if (PerlangMode.ExperimentalCompilation) { + Action action = () => EvalReturningOutput(source); - Assert.Single(result.Errors); - Assert.Matches("can only be used to increment numbers, not null", exception.Message); + action.Should().Throw() + .WithMessage("*cannot increment value of type*"); + } + else { + var result = EvalWithRuntimeErrorCatch(source); + var exception = result.Errors.First(); + + Assert.Single(result.Errors); + Assert.Matches("can only be used to increment numbers, not null", exception.Message); + } } [Fact] @@ -136,11 +147,19 @@ public void incrementing_string_throws_expected_exception() i++; "; - var result = EvalWithRuntimeErrorCatch(source); - var exception = result.Errors.First(); + if (PerlangMode.ExperimentalCompilation) { + Action action = () => EvalReturningOutput(source); - Assert.Single(result.Errors); - Assert.Matches("can only be used to increment numbers, not AsciiString", exception.Message); + action.Should().Throw() + .WithMessage("*cannot increment value of type*"); + } + else { + var result = EvalWithRuntimeErrorCatch(source); + var exception = result.Errors.First(); + + Assert.Single(result.Errors); + Assert.Matches("can only be used to increment numbers, not AsciiString", exception.Message); + } } } } diff --git a/src/Perlang.Tests.Integration/Stdlib/ArgvTests.cs b/src/Perlang.Tests.Integration/Stdlib/ArgvTests.cs index ae57136e..e597c93c 100644 --- a/src/Perlang.Tests.Integration/Stdlib/ArgvTests.cs +++ b/src/Perlang.Tests.Integration/Stdlib/ArgvTests.cs @@ -20,9 +20,11 @@ public void ARGV_pop_is_defined() Assert.IsAssignableFrom(Eval("ARGV.pop")); } - [Fact] + [SkippableFact] public void ARGV_pop_with_no_arguments_throws_the_expected_exception() { + Skip.If(PerlangMode.ExperimentalCompilation, "Not supported in compiled mode"); + var result = EvalWithRuntimeErrorCatch("ARGV.pop()"); var exception = result.Errors.FirstOrDefault(); @@ -52,9 +54,11 @@ public void ARGV_pop_with_multiple_arguments_returns_the_expected_result() Assert.Equal("arg2", result); } - [Fact] + [SkippableFact] public void ARGV_pop_too_many_times_throws_the_expected_exception() { + Skip.If(PerlangMode.ExperimentalCompilation, "Not supported in compiled mode"); + var result = EvalWithRuntimeErrorCatch("ARGV.pop(); ARGV.pop();", "arg1"); var exception = result.Errors.FirstOrDefault(); diff --git a/src/Perlang.Tests.Integration/Var/VarTests.cs b/src/Perlang.Tests.Integration/Var/VarTests.cs index 4bd5f114..f204a357 100644 --- a/src/Perlang.Tests.Integration/Var/VarTests.cs +++ b/src/Perlang.Tests.Integration/Var/VarTests.cs @@ -1,4 +1,7 @@ +using System; using System.Linq; +using FluentAssertions; +using Perlang.Compiler; using Xunit; using static Perlang.Tests.Integration.EvalHelper; @@ -176,11 +179,20 @@ public void redeclare_global_throws_expected_error() print a; "; - var result = EvalWithRuntimeErrorCatch(source); - var exception = result.Errors.FirstOrDefault(); + if (PerlangMode.ExperimentalCompilation) { + Action action = () => EvalReturningOutput(source); - Assert.Single(result.Errors); - Assert.Matches("Variable with this name already declared in this scope.", exception.Message); + // This is horribly hardwired for (a particular version of) CLang, but it will have to do for now. + action.Should().Throw() + .WithMessage("*redefinition of 'a'*"); + } + else { + var result = EvalWithRuntimeErrorCatch(source); + var exception = result.Errors.FirstOrDefault(); + + Assert.Single(result.Errors); + Assert.Matches("Variable with this name already declared in this scope.", exception.Message); + } } [Fact] @@ -192,11 +204,20 @@ public void redefine_global_variable_throws_expected_error() print a; "; - var result = EvalWithRuntimeErrorCatch(source); - var exception = result.Errors.FirstOrDefault(); + if (PerlangMode.ExperimentalCompilation) { + Action action = () => EvalReturningOutput(source); - Assert.Single(result.Errors); - Assert.Matches("Variable with this name already declared in this scope.", exception.Message); + // This is horribly hardwired for (a particular version of) CLang, but it will have to do for now. + action.Should().Throw() + .WithMessage("*redefinition of 'a'*"); + } + else { + var result = EvalWithRuntimeErrorCatch(source); + var exception = result.Errors.FirstOrDefault(); + + Assert.Single(result.Errors); + Assert.Matches("Variable with this name already declared in this scope.", exception.Message); + } } [Fact]