Implemented very basic stacktrace

This commit is contained in:
Deukhoofd 2019-02-21 19:53:14 +01:00
parent 668d60ce46
commit 834d65f38e
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
12 changed files with 100 additions and 78 deletions

View File

@ -6,8 +6,11 @@ namespace Upsilon.BaseTypes.ScriptFunction
{
public abstract class ScriptFunction : ScriptType
{
protected ScriptFunction(bool isCoroutine)
public string Name { get; protected set; }
protected ScriptFunction(string name, bool isCoroutine)
{
Name = name;
IsCoroutine = isCoroutine;
}
@ -24,7 +27,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
public bool IsCoroutine { get; }
public abstract ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span);
public abstract IEnumerator RunCoroutine(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span);
public abstract ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, EvaluationScope scope, TextSpan span, EvaluationState state);
public abstract IEnumerator RunCoroutine(Diagnostics diagnostics, ScriptType[] variables, EvaluationScope scope, TextSpan span, EvaluationState state);
}
}

View File

@ -14,7 +14,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
public class ScriptMethodInfoFunction : ScriptFunction
{
public ScriptMethodInfoFunction(UserDataMethod method, object o, bool directTypeManipulation,
bool isCoroutine, bool passScriptReference = false, bool passScopeReference = false) : base(isCoroutine)
bool isCoroutine, bool passScriptReference = false, bool passScopeReference = false) : base(method.Name, isCoroutine)
{
Method = method;
_object = o;
@ -46,33 +46,38 @@ namespace Upsilon.BaseTypes.ScriptFunction
return Method.GetMethods().First().Parameters;
}
public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span)
public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, EvaluationScope scope, TextSpan span, EvaluationState state)
{
var result = Execute(diagnostics, variables, script, scope, span);
state.Stacktrace.Push(this);
var result = Execute(variables, state.Script, scope, span);
state.Stacktrace.Pop();
if (_directTypeManipulation)
return (ScriptType)result;
return result.ToScriptType();
}
public override IEnumerator RunCoroutine(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope,
TextSpan span)
public override IEnumerator RunCoroutine(Diagnostics diagnostics, ScriptType[] variables, EvaluationScope scope,
TextSpan span , EvaluationState state)
{
var result = Execute(diagnostics, variables, script, scope, span);
state.Stacktrace.Push(this);
var result = Execute(variables, state.Script, scope, span);
if (result is IEnumerator enumerator)
{
while (enumerator.MoveNext())
{
yield return enumerator.Current;
}
state.Stacktrace.Pop();
yield break;
}
yield return result;
state.Stacktrace.Pop();
}
private object Execute(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span)
private object Execute(IEnumerable<ScriptType> variables, Script script, EvaluationScope scope, TextSpan span)
{
var objects = new List<object>();
var objects = new List<object>();
if (_passScriptReference)
objects.Add(script);
if (_passScopeReference)
@ -132,9 +137,9 @@ namespace Upsilon.BaseTypes.ScriptFunction
// if we haven't found a method, we throw an exception
if (method == null)
{
throw new ScriptRuntimeException(script.FileName,
throw new EvaluationException(script.FileName,
$"Can't find method {Method.Name} with parameter types {string.Join(", ", convertedTypes.Select(x => x.Name))} on type {_object.GetType().Name}",
span, diagnostics.ScriptString.GetSpan(span));
span);
}
// get the method parameters
var parameters = method.GetParameters();

View File

@ -16,37 +16,46 @@ namespace Upsilon.BaseTypes.ScriptFunction
{
public List<ScriptRuntimeFunctionOption> Options { get; }
public ScriptRuntimeFunction(List<ScriptRuntimeFunctionOption> options, bool isCoroutine) : base(isCoroutine)
public ScriptRuntimeFunction(string name, List<ScriptRuntimeFunctionOption> options, bool isCoroutine) : base(name, isCoroutine)
{
Options = options;
}
public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script,
EvaluationScope scope, TextSpan span)
public void SetName(string name)
{
var option = GetValidOption(variables);
if (option == null)
{
throw new EvaluationException(script.FileName,
$"No valid function found", span);
}
return option.Run(diagnostics, variables, script, scope, span);
Name = name;
}
public override IEnumerator RunCoroutine(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope,
TextSpan span)
public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, EvaluationScope scope, TextSpan span, EvaluationState state)
{
var option = GetValidOption(variables);
if (option == null)
{
throw new EvaluationException(script.FileName,
throw new EvaluationException(state.Script.FileName,
$"No valid function found", span);
}
var coroutine = option.RunCoroutine(diagnostics, variables, script, scope, span);
state.Stacktrace.Push(this);
var result = option.Run(diagnostics, variables, state.Script, scope, span);
state.Stacktrace.Pop();
return result;
}
public override IEnumerator RunCoroutine(Diagnostics diagnostics, ScriptType[] variables, EvaluationScope scope,
TextSpan span, EvaluationState state)
{
var option = GetValidOption(variables);
if (option == null)
{
throw new EvaluationException(state.Script.FileName,
$"No valid function found", span);
}
state.Stacktrace.Push(this);
var coroutine = option.RunCoroutine(diagnostics, variables, state.Script, scope, span);
while (coroutine.MoveNext())
{
yield return coroutine.Current;
}
state.Stacktrace.Pop();
}
public ScriptRuntimeFunctionOption GetValidOption(object[] variables)

View File

@ -149,36 +149,32 @@ namespace Upsilon.Binder
return ((ScriptNumber) left) < ((ScriptNumber) right);
}
throw new ScriptRuntimeException(state.Script.FileName,
$"Can't find operator '{Operator.Kind}' for types '{left.Type}' and '{right.Type}'", Span,
diagnostics.ScriptString.GetSpan(Span));
throw new EvaluationException(state.Script.FileName,
$"Can't find operator '{Operator.Kind}' for types '{left.Type}' and '{right.Type}'", Span);
case BoundBinaryOperator.OperatorKind.LessEquals:
if (left.Type == Type.Number && right.Type == Type.Number)
{
return ((ScriptNumber) left) <= ((ScriptNumber) right);
}
throw new ScriptRuntimeException(state.Script.FileName,
$"Can't find operator '{Operator.Kind}' for types '{left.Type}' and '{right.Type}'", Span,
diagnostics.ScriptString.GetSpan(Span));
throw new EvaluationException(state.Script.FileName,
$"Can't find operator '{Operator.Kind}' for types '{left.Type}' and '{right.Type}'", Span);
case BoundBinaryOperator.OperatorKind.Greater:
if (left.Type == Type.Number && right.Type == Type.Number)
{
return ((ScriptNumber) left) > ((ScriptNumber) right);
}
throw new ScriptRuntimeException(state.Script.FileName,
$"Can't find operator '{Operator.Kind}' for types '{left.Type}' and '{right.Type}'", Span,
diagnostics.ScriptString.GetSpan(Span));
throw new EvaluationException(state.Script.FileName,
$"Can't find operator '{Operator.Kind}' for types '{left.Type}' and '{right.Type}'", Span);
case BoundBinaryOperator.OperatorKind.GreaterEquals:
if (left.Type == Type.Number && right.Type == Type.Number)
{
return ((ScriptNumber) left) >= ((ScriptNumber) right);
}
throw new ScriptRuntimeException(state.Script.FileName,
$"Can't find operator '{Operator.Kind}' for types '{left.Type}' and '{right.Type}'", Span,
diagnostics.ScriptString.GetSpan(Span));
throw new EvaluationException(state.Script.FileName,
$"Can't find operator '{Operator.Kind}' for types '{left.Type}' and '{right.Type}'", Span);
default:
throw new Exception("Invalid Binary Operator: " + Operator.Kind);
}

View File

@ -48,7 +48,7 @@ namespace Upsilon.Binder
ls.Add(evaluate);
}
var val = function.Run(diagnostics, ls.ToArray(), state.Script, scope, Span);
var val = function.Run(diagnostics, ls.ToArray(), scope, Span, state);
return val;
}
}

View File

@ -40,7 +40,7 @@ namespace Upsilon.Binder
internal override ScriptType Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state)
{
var option = new ScriptRuntimeFunction.ScriptRuntimeFunctionOption(Parameters, Block, scope);
var func = new ScriptRuntimeFunction(new List<ScriptRuntimeFunction.ScriptRuntimeFunctionOption>() {option},
var func = new ScriptRuntimeFunction("<anonymous>", new List<ScriptRuntimeFunction.ScriptRuntimeFunctionOption>() {option},
IsCoroutine);
return func;

View File

@ -28,6 +28,7 @@ namespace Upsilon.Binder
internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state)
{
var func = (ScriptRuntimeFunction)Func.Evaluate(scope, diagnostics, ref state);
func.SetName(Variable.Name);
if (Variable.Local)
{
if (scope.Variables.TryGetValue(Variable.Name, out var f) && f is ScriptRuntimeFunction scriptRuntimeFunction)

View File

@ -27,9 +27,8 @@ namespace Upsilon.Binder
internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state)
{
throw new ScriptRuntimeException(state.Script.FileName,
"Yielding in a function that's not executed as a coroutine is not possible.", Span,
diagnostics.ScriptString.GetLine(Span.StartLine));
throw new EvaluationException(state.Script.FileName,
"Yielding in a function that's not executed as a coroutine is not possible.", Span);
}
internal override IEnumerator EvaluateCoroutine(EvaluationScope scope, Diagnostics diagnostics, EvaluationState state)
@ -51,7 +50,7 @@ namespace Upsilon.Binder
var evaluate = t.Evaluate(scope, diagnostics, ref state);
ls.Add(evaluate);
}
var coroutine = function.RunCoroutine(diagnostics, ls.ToArray(), state.Script, scope, Span);
var coroutine = function.RunCoroutine(diagnostics, ls.ToArray(), scope, Span, state);
while (coroutine.MoveNext())
{
yield return coroutine.Current;

View File

@ -2,12 +2,13 @@ using Upsilon.BaseTypes;
namespace Upsilon.Evaluator
{
internal class EvaluationState
public class EvaluationState
{
public Script Script;
public bool Returned;
public bool HasBroken;
public ScriptType ReturnValue;
public Stacktrace Stacktrace = new Stacktrace();
public ScriptType LastValue;
}
}

View File

@ -0,0 +1,26 @@
using System.Collections.Generic;
using System.Linq;
using Upsilon.BaseTypes.ScriptFunction;
namespace Upsilon.Evaluator
{
public class Stacktrace
{
private readonly Stack<ScriptFunction> _stack = new Stack<ScriptFunction>();
public void Push(ScriptFunction statement)
{
_stack.Push(statement);
}
public ScriptFunction Pop()
{
return _stack.Pop();
}
public IEnumerable<ScriptFunction> GetTopStack(int i)
{
return _stack.Take(i);
}
}
}

View File

@ -1,16 +1,20 @@
using System;
using System.Text;
using Upsilon.Evaluator;
using Upsilon.Text;
namespace Upsilon.Exceptions
{
public class EvaluationException : Exception
{
private readonly Stacktrace _stacktrace;
public string FileName { get; }
public string ErrorMessage { get; }
public TextSpan Span { get; }
public EvaluationException(string fileName, string message, TextSpan span)
public EvaluationException(string fileName, string message, TextSpan span, Stacktrace stacktrace = null)
{
_stacktrace = stacktrace;
FileName = fileName;
ErrorMessage = message;
Span = span;
@ -18,7 +22,15 @@ namespace Upsilon.Exceptions
public override string ToString()
{
return $"[{FileName}] ({Span.StartLine},{Span.StartPosition}) {ErrorMessage}";
var err = new StringBuilder($"[{FileName}] ({Span.StartLine},{Span.StartPosition}) {ErrorMessage}");
if (_stacktrace != null)
{
foreach (var statement in _stacktrace.GetTopStack(10))
{
err.Append($"\n{statement.Name}");
}
}
return err.ToString();
}
public override string Message => ToString();

View File

@ -1,30 +0,0 @@
using System;
using Upsilon.Text;
namespace Upsilon.Exceptions
{
public class ScriptRuntimeException : Exception
{
public string FileName { get; }
public string ErrorMessage { get; }
public int Line { get; }
public int Character { get; }
public string ErrorLine { get; }
public ScriptRuntimeException(string fileName, string errorMessage, TextSpan position, string errorLine)
{
FileName = fileName;
ErrorMessage = errorMessage;
Line = position.StartLine;
Character = position.StartPosition;
ErrorLine = errorLine;
}
public override string ToString()
{
return $"[{FileName}] {ErrorMessage} at ({Line}, {Character})\n{ErrorLine}";
}
public override string Message => ToString();
}
}