Implemented very basic stacktrace
This commit is contained in:
parent
668d60ce46
commit
834d65f38e
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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();
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue