From fcf3130ff6f40b0160f26b093e665a6de4e49597 Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Sun, 17 Nov 2024 22:01:55 +0100 Subject: [PATCH] Catch exceptions from lazily evaluated code when stringifying evaluation result for REPL --- src/ShellSession.cs | 30 +++++++++++++++++-- src/Std/DataTypes/RuntimeList.cs | 2 +- .../RuntimeObjectJsonConverter.cs | 8 ++--- src/Vm/InstructionExecutor.cs | 10 ++++++- 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/ShellSession.cs b/src/ShellSession.cs index 8918a674..c09e4472 100644 --- a/src/ShellSession.cs +++ b/src/ShellSession.cs @@ -7,6 +7,8 @@ using System.Reflection.Metadata.Ecma335; using System.Text; using Elk.Analysis; +using Elk.Exceptions; +using Elk.Lexing; using Elk.Parsing; using Elk.Scoping; using Elk.Std.DataTypes; @@ -126,9 +128,11 @@ public void RunCommand( } else { - resultBuilder.AppendLine( - evaluationResult.Value.ToString() ?? "" - ); + var evaluationResultValue = GetEvaluationResultValue(evaluationResult); + if (evaluationResultValue.isError) + textWriter = Console.Error; + + resultBuilder.AppendLine(evaluationResultValue.value); } if (!printReturnedValue && !evaluationResult.Diagnostics.Any()) @@ -151,6 +155,26 @@ public void RunCommand( Console.ResetColor(); } + private (string value, bool isError) GetEvaluationResultValue(EvaluationResult evaluationResult) + { + try + { + var value = evaluationResult.Value?.ToString() ?? ""; + + return (value, isError: false); + } + catch (RuntimeException ex) + { + var diagnostic = new DiagnosticMessage(ex.Message, ex.StartPosition ?? TextPos.Default, ex.EndPosition ?? TextPos.Default) + { + StackTrace = ex.ElkStackTrace, + }; + var value = diagnostic.ToString(includePosition: false).Trim(); + + return (value, isError: true); + } + } + public void RunFile(string filePath, IEnumerable? arguments) { arguments ??= new List(); diff --git a/src/Std/DataTypes/RuntimeList.cs b/src/Std/DataTypes/RuntimeList.cs index b634f69a..d83830da 100644 --- a/src/Std/DataTypes/RuntimeList.cs +++ b/src/Std/DataTypes/RuntimeList.cs @@ -97,7 +97,7 @@ public override string ToString() var totalIndentationLength = 3 * Values.Count; return json.Length - totalIndentationLength < lineLimit - ? $"[{string.Join(", ", Values.Select(x => x.ToDisplayString()))}]" + ? $"[{string.Join(", ", Values.Select(x => x?.ToDisplayString()))}]" : json; } } \ No newline at end of file diff --git a/src/Std/Serialization/RuntimeObjectJsonConverter.cs b/src/Std/Serialization/RuntimeObjectJsonConverter.cs index 2dbb197d..af68cc7a 100644 --- a/src/Std/Serialization/RuntimeObjectJsonConverter.cs +++ b/src/Std/Serialization/RuntimeObjectJsonConverter.cs @@ -73,11 +73,11 @@ private JValue BuildValue(RuntimeObject value) { return value switch { - RuntimeBoolean boolean => new(boolean.IsTrue), - RuntimeFloat floatValue => new(floatValue.Value), - RuntimeInteger integerValue => new(integerValue.Value), + RuntimeBoolean boolean => new JValue(boolean.IsTrue), + RuntimeFloat floatValue => new JValue(floatValue.Value), + RuntimeInteger integerValue => new JValue(integerValue.Value), RuntimeNil => JValue.CreateNull(), - _ => new(value.ToString() ?? ""), + _ => new JValue(value?.ToString() ?? ""), }; } diff --git a/src/Vm/InstructionExecutor.cs b/src/Vm/InstructionExecutor.cs index f7eb346c..74689207 100644 --- a/src/Vm/InstructionExecutor.cs +++ b/src/Vm/InstructionExecutor.cs @@ -190,12 +190,20 @@ private void ExecuteCurrentPage() throw; var exceptionFrame = _context.ExceptionStack.Pop(); - while (_currentPage != exceptionFrame.Page) + while (_callStack.Any() && _currentPage != exceptionFrame.Page) { ex.ElkStackTrace.Add(CreateTrace(_currentPage.Name)); PopFrame(); } + // If the current context doesn't have the exception frame, + // add it back and throw, to let the parent context handle it + if (_currentPage != exceptionFrame.Page) + { + _context.ExceptionStack.Push(exceptionFrame); + throw; + } + while (_stack.Count > exceptionFrame.StackSize) _stack.PopObject();