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 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; IsCoroutine = isCoroutine;
} }
@ -24,7 +27,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
public bool IsCoroutine { get; } public bool IsCoroutine { get; }
public abstract ScriptType Run(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, Script script, EvaluationScope scope, TextSpan span); 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 class ScriptMethodInfoFunction : ScriptFunction
{ {
public ScriptMethodInfoFunction(UserDataMethod method, object o, bool directTypeManipulation, 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; Method = method;
_object = o; _object = o;
@ -46,31 +46,36 @@ namespace Upsilon.BaseTypes.ScriptFunction
return Method.GetMethods().First().Parameters; 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) if (_directTypeManipulation)
return (ScriptType)result; return (ScriptType)result;
return result.ToScriptType(); return result.ToScriptType();
} }
public override IEnumerator RunCoroutine(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, public override IEnumerator RunCoroutine(Diagnostics diagnostics, ScriptType[] variables, EvaluationScope scope,
TextSpan span) 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) if (result is IEnumerator enumerator)
{ {
while (enumerator.MoveNext()) while (enumerator.MoveNext())
{ {
yield return enumerator.Current; yield return enumerator.Current;
} }
state.Stacktrace.Pop();
yield break; yield break;
} }
yield return result; 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) if (_passScriptReference)
@ -132,9 +137,9 @@ namespace Upsilon.BaseTypes.ScriptFunction
// if we haven't found a method, we throw an exception // if we haven't found a method, we throw an exception
if (method == null) 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}", $"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 // get the method parameters
var parameters = method.GetParameters(); var parameters = method.GetParameters();

View File

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

View File

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

View File

@ -48,7 +48,7 @@ namespace Upsilon.Binder
ls.Add(evaluate); 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; return val;
} }
} }

View File

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

View File

@ -28,6 +28,7 @@ namespace Upsilon.Binder
internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state)
{ {
var func = (ScriptRuntimeFunction)Func.Evaluate(scope, diagnostics, ref state); var func = (ScriptRuntimeFunction)Func.Evaluate(scope, diagnostics, ref state);
func.SetName(Variable.Name);
if (Variable.Local) if (Variable.Local)
{ {
if (scope.Variables.TryGetValue(Variable.Name, out var f) && f is ScriptRuntimeFunction scriptRuntimeFunction) 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) internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state)
{ {
throw new ScriptRuntimeException(state.Script.FileName, throw new EvaluationException(state.Script.FileName,
"Yielding in a function that's not executed as a coroutine is not possible.", Span, "Yielding in a function that's not executed as a coroutine is not possible.", Span);
diagnostics.ScriptString.GetLine(Span.StartLine));
} }
internal override IEnumerator EvaluateCoroutine(EvaluationScope scope, Diagnostics diagnostics, EvaluationState state) 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); var evaluate = t.Evaluate(scope, diagnostics, ref state);
ls.Add(evaluate); 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()) while (coroutine.MoveNext())
{ {
yield return coroutine.Current; yield return coroutine.Current;

View File

@ -2,12 +2,13 @@ using Upsilon.BaseTypes;
namespace Upsilon.Evaluator namespace Upsilon.Evaluator
{ {
internal class EvaluationState public class EvaluationState
{ {
public Script Script; public Script Script;
public bool Returned; public bool Returned;
public bool HasBroken; public bool HasBroken;
public ScriptType ReturnValue; public ScriptType ReturnValue;
public Stacktrace Stacktrace = new Stacktrace();
public ScriptType LastValue; 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;
using System.Text;
using Upsilon.Evaluator;
using Upsilon.Text; using Upsilon.Text;
namespace Upsilon.Exceptions namespace Upsilon.Exceptions
{ {
public class EvaluationException : Exception public class EvaluationException : Exception
{ {
private readonly Stacktrace _stacktrace;
public string FileName { get; } public string FileName { get; }
public string ErrorMessage { get; } public string ErrorMessage { get; }
public TextSpan Span { 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; FileName = fileName;
ErrorMessage = message; ErrorMessage = message;
Span = span; Span = span;
@ -18,7 +22,15 @@ namespace Upsilon.Exceptions
public override string ToString() 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(); 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();
}
}