diff --git a/Upsilon/Binder/BoundStatements/BoundStatement.cs b/Upsilon/Binder/BoundStatements/BoundStatement.cs index 9977728..d5d71ad 100644 --- a/Upsilon/Binder/BoundStatements/BoundStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundStatement.cs @@ -7,5 +7,7 @@ namespace Upsilon.Binder protected BoundStatement(TextSpan span) : base(span) { } + + internal bool HasBreakpoint { get; set; } } } \ No newline at end of file diff --git a/Upsilon/Evaluator/Debugging/BreakpointInfo.cs b/Upsilon/Evaluator/Debugging/BreakpointInfo.cs new file mode 100644 index 0000000..f34f2a8 --- /dev/null +++ b/Upsilon/Evaluator/Debugging/BreakpointInfo.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace Upsilon.Evaluator.Debugging +{ + public class BreakpointInfo + { + private Dictionary _variables; + public BreakpointInfo(EvaluationScope scope) + { + _variables = new Dictionary(); + while (scope != null) + { + foreach (var v in scope.Variables) + { + _variables.Add(v.Key, v.Value); + } + + scope = scope.ParentScope; + } + } + } +} \ No newline at end of file diff --git a/Upsilon/Evaluator/Debugging/DebugFile.cs b/Upsilon/Evaluator/Debugging/DebugFile.cs new file mode 100644 index 0000000..0dc3ff7 --- /dev/null +++ b/Upsilon/Evaluator/Debugging/DebugFile.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Upsilon.Evaluator.Debugging +{ + public class DebugFile + { + public List Breakpoints = new List(); + } + + public class Breakpoint + { + public int Line { get; set; } + } +} \ No newline at end of file diff --git a/Upsilon/Evaluator/Debugging/DebugSession.cs b/Upsilon/Evaluator/Debugging/DebugSession.cs new file mode 100644 index 0000000..87199b3 --- /dev/null +++ b/Upsilon/Evaluator/Debugging/DebugSession.cs @@ -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 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; + } +} \ No newline at end of file diff --git a/Upsilon/Evaluator/EvaluationScope.cs b/Upsilon/Evaluator/EvaluationScope.cs index 4efcc69..7cd6504 100644 --- a/Upsilon/Evaluator/EvaluationScope.cs +++ b/Upsilon/Evaluator/EvaluationScope.cs @@ -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 Variables; @@ -16,7 +16,7 @@ namespace Upsilon.Evaluator internal EvaluationScope(EvaluationScope parentScope) { - _parentScope = parentScope; + ParentScope = parentScope; Variables = new Dictionary(); } @@ -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)) diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index 6670f9a..c154bfb 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -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 _todoStatements; + private void EvaluateBoundBlockStatement(BoundBlockStatement boundBlockStatement) { - foreach (var boundStatement in boundBlockStatement.Statements) + _todoStatements = new Queue(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); } } diff --git a/Upsilon/Utilities/SearchHelper.cs b/Upsilon/Utilities/SearchHelper.cs index 338ca20..0602c00 100644 --- a/Upsilon/Utilities/SearchHelper.cs +++ b/Upsilon/Utilities/SearchHelper.cs @@ -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 GetBoundScopeVisibleVariables(this BoundScope scope) {