Skip to content

Commit 5c531c2

Browse files
committed
Merge pull request #1265 from Vogel612/ParserStateCleanup
WIP Parser cleanup and Multithreading
2 parents f53ac6a + b576334 commit 5c531c2

File tree

10 files changed

+495
-350
lines changed

10 files changed

+495
-350
lines changed

RetailCoder.VBE/Common/RubberduckHooks.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -176,29 +176,29 @@ private bool HandleHotkeyMessage(IntPtr wParam)
176176
if (hook != null)
177177
{
178178
hook.OnMessageReceived();
179-
processed = true;
180-
}
179+
processed = true;
180+
}
181181
}
182182
return processed;
183-
}
183+
}
184184

185185
private void HandleActivateAppMessage(IntPtr wParam)
186186
{
187187
const int WA_INACTIVE = 0;
188188
const int WA_ACTIVE = 1;
189189
const int WA_CLICKACTIVE = 2;
190190

191-
switch (LoWord(wParam))
192-
{
193-
case WA_ACTIVE:
191+
switch (LoWord(wParam))
192+
{
193+
case WA_ACTIVE:
194194
case WA_CLICKACTIVE:
195-
Attach();
196-
break;
195+
Attach();
196+
break;
197197

198-
case WA_INACTIVE:
199-
Detach();
200-
break;
201-
}
198+
case WA_INACTIVE:
199+
Detach();
200+
break;
201+
}
202202
}
203203

204204
private static int LoWord(IntPtr dw)
@@ -214,4 +214,4 @@ private IntPtr GetWindowThread(IntPtr hWnd)
214214
return (IntPtr)hThread;
215215
}
216216
}
217-
}
217+
}

Rubberduck.Parsing/IRubberduckParser.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ public interface IRubberduckParser
1010
{
1111
RubberduckParserState State { get; }
1212
void ParseComponent(VBComponent component, TokenStreamRewriter rewriter = null);
13-
//Task ParseAsync(VBComponent component, CancellationToken token, TokenStreamRewriter rewriter = null);
14-
//void Resolve(CancellationToken token);
13+
Task ParseAsync(VBComponent component, CancellationToken token, TokenStreamRewriter rewriter = null);
14+
void Cancel(VBComponent component = null);
15+
void Resolve(CancellationToken token);
1516
}
1617
}

Rubberduck.Parsing/Rubberduck.Parsing.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@
179179
<Compile Include="Symbols\ValuedDeclaration.cs" />
180180
<Compile Include="VBA\AttributeParser.cs" />
181181
<Compile Include="VBA\Attributes.cs" />
182+
<Compile Include="VBA\CombinedParseTreeListener.cs" />
183+
<Compile Include="VBA\ComponentParseTask.cs" />
182184
<Compile Include="VBA\EnumerableExtensions.cs" />
183185
<Compile Include="VBA\IAttributeParser.cs" />
184186
<Compile Include="VBA\IModuleExporter.cs" />
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using Antlr4.Runtime;
2+
using Antlr4.Runtime.Tree;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
6+
namespace Rubberduck.Parsing.VBA
7+
{
8+
/// <summary>
9+
/// A Class combining an arbitrary number of IParseTreeListener instances into one single instance
10+
/// </summary>
11+
public class CombinedParseTreeListener : IParseTreeListener
12+
{
13+
private readonly IReadOnlyList<IParseTreeListener> _listeners;
14+
public CombinedParseTreeListener(IEnumerable<IParseTreeListener> listeners)
15+
{
16+
_listeners = listeners.ToList();
17+
}
18+
19+
public void EnterEveryRule(ParserRuleContext ctx)
20+
{
21+
foreach (var listener in _listeners)
22+
{
23+
listener.EnterEveryRule(ctx);
24+
ctx.EnterRule(listener);
25+
}
26+
}
27+
28+
public void ExitEveryRule(ParserRuleContext ctx)
29+
{
30+
foreach (var listener in _listeners)
31+
{
32+
listener.ExitEveryRule(ctx);
33+
ctx.ExitRule(listener);
34+
}
35+
}
36+
37+
public void VisitErrorNode(IErrorNode node)
38+
{
39+
foreach (var listener in _listeners)
40+
{
41+
listener.VisitErrorNode(node);
42+
}
43+
}
44+
45+
public void VisitTerminal(ITerminalNode node)
46+
{
47+
foreach (var listener in _listeners)
48+
{
49+
listener.VisitTerminal(node);
50+
}
51+
}
52+
}
53+
}
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
using Antlr4.Runtime;
2+
using Antlr4.Runtime.Misc;
3+
using Antlr4.Runtime.Tree;
4+
using Microsoft.Vbe.Interop;
5+
using Rubberduck.Parsing.Grammar;
6+
using Rubberduck.Parsing.Nodes;
7+
using Rubberduck.Parsing.Preprocessing;
8+
using Rubberduck.Parsing.Symbols;
9+
using Rubberduck.VBEditor;
10+
using Rubberduck.VBEditor.Extensions;
11+
using System;
12+
using System.Collections.Generic;
13+
using System.Diagnostics;
14+
using System.Linq;
15+
using System.Runtime.InteropServices;
16+
using System.Threading;
17+
using System.Threading.Tasks;
18+
19+
namespace Rubberduck.Parsing.VBA
20+
{
21+
class ComponentParseTask
22+
{
23+
private readonly VBComponent _component;
24+
private readonly QualifiedModuleName _qualifiedName;
25+
private readonly TokenStreamRewriter _rewriter;
26+
private readonly IAttributeParser _attributeParser;
27+
private readonly VBAPreprocessor _preprocessor;
28+
29+
public event EventHandler<ParseCompletionArgs> ParseCompleted;
30+
public event EventHandler<ParseFailureArgs> ParseFailure;
31+
32+
public ComponentParseTask(VBComponent vbComponent, VBAPreprocessor preprocessor, IAttributeParser attributeParser, TokenStreamRewriter rewriter = null)
33+
{
34+
_attributeParser = attributeParser;
35+
_preprocessor = preprocessor;
36+
_component = vbComponent;
37+
_rewriter = rewriter;
38+
_qualifiedName = new QualifiedModuleName(vbComponent);
39+
}
40+
41+
public void Start(CancellationToken token)
42+
{
43+
try
44+
{
45+
var code = RewriteAndPreprocess();
46+
token.ThrowIfCancellationRequested();
47+
48+
var attributes = _attributeParser.Parse(_component);
49+
50+
token.ThrowIfCancellationRequested();
51+
52+
// temporal coupling... comments must be acquired before we walk the parse tree for declarations
53+
// otherwise none of the annotations get associated to their respective Declaration
54+
var commentListener = new CommentListener();
55+
56+
var stopwatch = Stopwatch.StartNew();
57+
ITokenStream stream;
58+
var tree = ParseInternal(code, new IParseTreeListener[]{ commentListener }, out stream);
59+
stopwatch.Stop();
60+
if (tree != null)
61+
{
62+
Debug.Print("IParseTree for component '{0}' acquired in {1}ms (thread {2})", _component.Name, stopwatch.ElapsedMilliseconds, Thread.CurrentThread.ManagedThreadId);
63+
}
64+
65+
var comments = QualifyAndUnionComments(_qualifiedName, commentListener.Comments, commentListener.RemComments);
66+
token.ThrowIfCancellationRequested();
67+
68+
ParseCompleted.Invoke(this, new ParseCompletionArgs
69+
{
70+
ParseTree = tree,
71+
Tokens = stream,
72+
Attributes = attributes,
73+
Comments = comments,
74+
});
75+
}
76+
catch (COMException exception)
77+
{
78+
Debug.WriteLine("Exception thrown in thread {0}:\n{1}", Thread.CurrentThread.ManagedThreadId, exception);
79+
ParseFailure.Invoke(this, new ParseFailureArgs
80+
{
81+
Cause = exception
82+
});
83+
}
84+
catch (SyntaxErrorException exception)
85+
{
86+
Debug.WriteLine("Exception thrown in thread {0}:\n{1}", Thread.CurrentThread.ManagedThreadId, exception);
87+
ParseFailure.Invoke(this, new ParseFailureArgs
88+
{
89+
Cause = exception
90+
});
91+
}
92+
catch (OperationCanceledException cancel)
93+
{
94+
Debug.WriteLine("Operation was Cancelled", cancel);
95+
// no results to be used, so no results "returned"
96+
//ParseCompleted.Invoke(this, new ParseCompletionArgs());
97+
}
98+
}
99+
100+
private string RewriteAndPreprocess()
101+
{
102+
var code = _rewriter == null ? string.Join(Environment.NewLine, _component.CodeModule.GetSanitizedCode()) : _rewriter.GetText();
103+
string processed;
104+
try
105+
{
106+
processed = _preprocessor.Execute(code);
107+
}
108+
catch (VBAPreprocessorException)
109+
{
110+
Debug.WriteLine("Falling back to no preprocessing");
111+
processed = code;
112+
}
113+
return processed;
114+
}
115+
116+
private static IParseTree ParseInternal(string code, IParseTreeListener[] listeners, out ITokenStream outStream)
117+
{
118+
var stream = new AntlrInputStream(code);
119+
var lexer = new VBALexer(stream);
120+
var tokens = new CommonTokenStream(lexer);
121+
var parser = new VBAParser(tokens);
122+
123+
parser.AddErrorListener(new ExceptionErrorListener());
124+
foreach (var l in listeners)
125+
{
126+
parser.AddParseListener(l);
127+
}
128+
129+
outStream = tokens;
130+
return parser.startRule();
131+
}
132+
133+
private IEnumerable<CommentNode> QualifyAndUnionComments(QualifiedModuleName qualifiedName, IEnumerable<VBAParser.CommentContext> comments, IEnumerable<VBAParser.RemCommentContext> remComments)
134+
{
135+
var commentNodes = comments.Select(comment => new CommentNode(comment.GetComment(), Tokens.CommentMarker, new QualifiedSelection(qualifiedName, comment.GetSelection())));
136+
var remCommentNodes = remComments.Select(comment => new CommentNode(comment.GetComment(), Tokens.Rem, new QualifiedSelection(qualifiedName, comment.GetSelection())));
137+
var allCommentNodes = commentNodes.Union(remCommentNodes);
138+
return allCommentNodes;
139+
}
140+
141+
public class ParseCompletionArgs
142+
{
143+
public ITokenStream Tokens { get; internal set; }
144+
public IParseTree ParseTree { get; internal set; }
145+
public IDictionary<Tuple<string, DeclarationType>, Attributes> Attributes { get; internal set; }
146+
public IEnumerable<CommentNode> Comments { get; internal set; }
147+
}
148+
149+
public class ParseFailureArgs
150+
{
151+
public Exception Cause { get; internal set; }
152+
}
153+
154+
private class CommentListener : VBAParserBaseListener
155+
{
156+
private readonly IList<VBAParser.RemCommentContext> _remComments = new List<VBAParser.RemCommentContext>();
157+
public IEnumerable<VBAParser.RemCommentContext> RemComments { get { return _remComments; } }
158+
159+
private readonly IList<VBAParser.CommentContext> _comments = new List<VBAParser.CommentContext>();
160+
public IEnumerable<VBAParser.CommentContext> Comments { get { return _comments; } }
161+
162+
public override void ExitRemComment([NotNull] VBAParser.RemCommentContext context)
163+
{
164+
_remComments.Add(context);
165+
}
166+
167+
public override void ExitComment([NotNull] VBAParser.CommentContext context)
168+
{
169+
_comments.Add(context);
170+
}
171+
}
172+
}
173+
}

Rubberduck.Parsing/VBA/ParserState.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,34 @@ public enum ParserState
55
/// <summary>
66
/// Parse was requested but hasn't started yet.
77
/// </summary>
8-
Pending,
8+
Pending = 0,
99
/// <summary>
1010
/// Project references are being loaded into parser state.
1111
/// </summary>
12-
LoadingReference,
12+
LoadingReference = 1,
1313
/// <summary>
1414
/// Code from modified modules is being parsed.
1515
/// </summary>
16-
Parsing,
16+
Parsing = 2,
1717
/// <summary>
1818
/// Parse tree is waiting to be walked for identifier resolution.
1919
/// </summary>
20-
Parsed,
20+
Parsed = 3,
2121
/// <summary>
2222
/// Resolving identifier references.
2323
/// </summary>
24-
Resolving,
24+
Resolving = 4,
2525
/// <summary>
2626
/// Parser state is in sync with the actual code in the VBE.
2727
/// </summary>
28-
Ready,
28+
Ready = 5,
2929
/// <summary>
3030
/// Parsing could not be completed for one or more modules.
3131
/// </summary>
32-
Error,
32+
Error = 99,
3333
/// <summary>
3434
/// Parsing completed, but identifier references could not be resolved for one or more modules.
3535
/// </summary>
36-
ResolverError,
36+
ResolverError = 6,
3737
}
3838
}

0 commit comments

Comments
 (0)