Some initial work for debugging

This commit is contained in:
Deukhoofd 2018-12-11 18:31:54 +01:00
parent 590614c34d
commit a4cdaa5b05
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
7 changed files with 140 additions and 11 deletions

View File

@ -7,5 +7,7 @@ namespace Upsilon.Binder
protected BoundStatement(TextSpan span) : base(span)
{
}
internal bool HasBreakpoint { get; set; }
}
}

View File

@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace Upsilon.Evaluator.Debugging
{
public class BreakpointInfo
{
private Dictionary<string, object> _variables;
public BreakpointInfo(EvaluationScope scope)
{
_variables = new Dictionary<string, object>();
while (scope != null)
{
foreach (var v in scope.Variables)
{
_variables.Add(v.Key, v.Value);
}
scope = scope.ParentScope;
}
}
}
}

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace Upsilon.Evaluator.Debugging
{
public class DebugFile
{
public List<Breakpoint> Breakpoints = new List<Breakpoint>();
}
public class Breakpoint
{
public int Line { get; set; }
}
}

View File

@ -0,0 +1,54 @@
using System.Collections.Generic;
using System.Xml.Linq;
namespace Upsilon.Evaluator.Debugging
{
public static class DebugSession
{
public static bool DebuggerAttached { get; set; }
internal static bool Debugging { get; set; }
private static Dictionary<string, DebugFile> Files { get; set; }
public static void AttachDebugger()
{
DebuggerAttached = true;
}
public static DebugFile GetFileInfo(string file)
{
return Files.TryGetValue(file, out var info) ? info : null;
}
public static void SetBreakpoint(string file, int line)
{
if (!Files.TryGetValue(file, out var fileObj))
{
fileObj = new DebugFile();
Files.Add(file, fileObj);
}
fileObj.Breakpoints.Add(new Breakpoint()
{
Line = line
});
}
public static void RemoveBreakpoint(string file, int line)
{
if (Files.TryGetValue(file, out var fileObj))
{
fileObj = new DebugFile();
fileObj.Breakpoints.RemoveAll(x => x.Line == line);
if (fileObj.Breakpoints.Count <= 0)
Files.Remove(file);
}
}
internal static void TriggerBreakpoint(EvaluationScope scope)
{
OnBreakpoint?.Invoke(new BreakpointInfo(scope));
}
public delegate void BreakpointEvent(BreakpointInfo info);
public static event BreakpointEvent OnBreakpoint;
}
}

View File

@ -8,7 +8,7 @@ namespace Upsilon.Evaluator
{
public class EvaluationScope : IDisposable
{
private EvaluationScope _parentScope;
public EvaluationScope ParentScope { get; private set; }
private EvaluationScope _getOnlyParentScope;
public readonly Dictionary<string, ScriptType> Variables;
@ -16,7 +16,7 @@ namespace Upsilon.Evaluator
internal EvaluationScope(EvaluationScope parentScope)
{
_parentScope = parentScope;
ParentScope = parentScope;
Variables = new Dictionary<string, ScriptType>();
}
@ -33,8 +33,8 @@ namespace Upsilon.Evaluator
public void Dispose()
{
_parentScope?.Dispose();
_parentScope = null;
ParentScope?.Dispose();
ParentScope = null;
Variables.Clear();
}
@ -44,9 +44,9 @@ namespace Upsilon.Evaluator
{
Variables[symbol.Name] = value;
}
else if (_parentScope != null)
else if (ParentScope != null)
{
_parentScope.AssignToNearest(symbol, value);
ParentScope.AssignToNearest(symbol, value);
}
else
{
@ -74,8 +74,8 @@ namespace Upsilon.Evaluator
{
if (Variables.TryGetValue(symbol.Name, out obj))
return true;
if (_parentScope != null)
if (_parentScope.TryGet(symbol, out obj))
if (ParentScope != null)
if (ParentScope.TryGet(symbol, out obj))
return true;
if (_getOnlyParentScope != null)
if (_getOnlyParentScope.TryGet(symbol, out obj))
@ -87,8 +87,8 @@ namespace Upsilon.Evaluator
{
if (Variables.TryGetValue(variable, out obj))
return true;
if (_parentScope != null)
if (_parentScope.TryGet(variable, out obj))
if (ParentScope != null)
if (ParentScope.TryGet(variable, out obj))
return true;
if (_getOnlyParentScope != null)
if (_getOnlyParentScope.TryGet(variable, out obj))

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading;
using Upsilon.BaseTypes;
using Upsilon.BaseTypes.Number;
using Upsilon.BaseTypes.ScriptFunction;
@ -8,8 +9,10 @@ using Upsilon.BaseTypes.ScriptTypeInterfaces;
using Upsilon.BaseTypes.UserData;
using Upsilon.Binder;
using Upsilon.Binder.VariableSymbols;
using Upsilon.Evaluator.Debugging;
using Upsilon.Exceptions;
using Upsilon.Text;
using Upsilon.Utilities;
using Type = Upsilon.BaseTypes.Type;
namespace Upsilon.Evaluator
@ -57,6 +60,19 @@ namespace Upsilon.Evaluator
public ScriptType Evaluate(BoundScript e)
{
if (DebugSession.DebuggerAttached && !string.IsNullOrWhiteSpace(e.FileName))
{
var file = DebugSession.GetFileInfo(e.FileName);
if (file != null)
{
foreach (var breakpoint in file.Breakpoints)
{
var lineStart = _script.ScriptString.GetLineInfo(breakpoint.Line).Start;
var debugStatement = e.GetBottomStatementAtPosition(lineStart);
debugStatement.HasBreakpoint = true;
}
}
}
EvaluateNode(e.Statement);
if (_returnValue == null)
return _lastValue;
@ -474,14 +490,30 @@ namespace Upsilon.Evaluator
throw new Exception($"Cannot find variable: '{e.Variable.VariableSymbol.Name}'");
}
private Queue<BoundStatement> _todoStatements;
private void EvaluateBoundBlockStatement(BoundBlockStatement boundBlockStatement)
{
foreach (var boundStatement in boundBlockStatement.Statements)
_todoStatements = new Queue<BoundStatement>(boundBlockStatement.Statements);
while (true)
{
if (DebugSession.Debugging)
{
Thread.Sleep(10);
continue;
}
if (HasReturned)
return;
if (HasBroken)
return;
if (_todoStatements.Count == 0)
return;
var boundStatement = _todoStatements.Dequeue();
if (DebugSession.DebuggerAttached && boundStatement.HasBreakpoint)
{
DebugSession.TriggerBreakpoint(Scope);
continue;
}
EvaluateStatement(boundStatement);
}
}

View File

@ -23,6 +23,11 @@ namespace Upsilon.Utilities
{
return node.GetNodeAtPosition(characterPosition).First();
}
public static BoundStatement GetBottomStatementAtPosition(this BoundNode node, int characterPosition)
{
return (BoundStatement) node.GetNodeAtPosition(characterPosition).First(x => x is BoundStatement);
}
public static Dictionary<string, VariableSymbol> GetBoundScopeVisibleVariables(this BoundScope scope)
{