Skip to content

Commit

Permalink
Improve RecordParentNodeInUserData and RecordScopeInfoInUserData + ad…
Browse files Browse the repository at this point in the history
…d tests
  • Loading branch information
adams85 committed Jun 15, 2024
1 parent 67f1938 commit bc1416a
Show file tree
Hide file tree
Showing 4 changed files with 244 additions and 67 deletions.
115 changes: 58 additions & 57 deletions src/Acornima.Extras/ParserOptionsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,10 @@ public static class ParserOptionsExtensions
public static TOptions RecordParentNodeInUserData<TOptions>(this TOptions options, bool enable = true)
where TOptions : ParserOptions
{
var helper = options._onNode?.GetInvocationList() is { } invocationList
? (OnNodeHelper?)Array.Find(invocationList, invocation => invocation.Target is OnNodeHelper)?.Target
: null;

var helper = options._onNode?.Target as OnNodeHelper;
if (enable)
{
(helper ??= new OnNodeHelper()).EnableParentNodeRecoding(options);
(helper ?? new OnNodeHelper()).EnableParentNodeRecoding(options);
}
else
{
Expand All @@ -28,13 +25,10 @@ public static TOptions RecordParentNodeInUserData<TOptions>(this TOptions option
public static TOptions RecordScopeInfoInUserData<TOptions>(this TOptions options, bool enable = true)
where TOptions : ParserOptions
{
var helper = options._onNode?.GetInvocationList() is { } invocationList
? (OnNodeHelper?)Array.Find(invocationList, invocation => invocation.Target is OnNodeHelper)?.Target
: null;

var helper = options._onNode?.Target as OnNodeHelper;
if (enable)
{
(helper ??= new OnNodeHelper()).EnableScopeInfoRecoding(options);
(helper ?? new OnNodeHelper()).EnableScopeInfoRecoding(options);
}
else
{
Expand All @@ -44,12 +38,14 @@ public static TOptions RecordScopeInfoInUserData<TOptions>(this TOptions options
return options;
}

private sealed class OnNodeHelper
private sealed class OnNodeHelper : IOnNodeHandlerWrapper
{
private OnNodeHandler? _handler;
private OnNodeHandler? _onNode;
public OnNodeHandler? OnNode { get => _onNode; set => _onNode = value; }

private ArrayList<ScopeInfo> _scopes;

private void ReleaseLargeBuffers()
public void ReleaseLargeBuffers()
{
_scopes.Clear();
if (_scopes.Capacity > 64)
Expand All @@ -60,59 +56,51 @@ private void ReleaseLargeBuffers()

public void EnableParentNodeRecoding(ParserOptions options)
{
if (_handler is null)
if (!ReferenceEquals(options._onNode?.Target, this))
{
options._onReleaseLargeBuffers += ReleaseLargeBuffers;
options._onNode += _handler = SetParentNode;
_onNode = options._onNode;
options._onNode = SetParentNode;
}
else if (_handler == SetScopeInfo)
else if (options._onNode == SetScopeInfo)
{
options._onNode -= _handler;
options._onNode += _handler = SetParentNodeAndScopeInfo;
options._onNode = SetParentNodeAndScopeInfo;
}
}

public void DisableParentNodeRecoding(ParserOptions options)
{
if (_handler == SetParentNodeAndScopeInfo)
if (options._onNode == SetParentNodeAndScopeInfo)
{
options._onNode -= _handler;
options._onNode += _handler = SetScopeInfo;
options._onNode = SetScopeInfo;
}
else if (_handler == SetParentNode)
else if (options._onNode == SetParentNode)
{
options._onNode -= _handler;
options._onReleaseLargeBuffers -= ReleaseLargeBuffers;
ReleaseLargeBuffers();
options._onNode = _onNode;
}
}

public void EnableScopeInfoRecoding(ParserOptions options)
{
if (_handler is null)
if (!ReferenceEquals(options._onNode?.Target, this))
{
options._onReleaseLargeBuffers += ReleaseLargeBuffers;
options._onNode += _handler = SetScopeInfo;
_onNode = options._onNode;
options._onNode = SetScopeInfo;
}
else if (_handler == SetParentNode)
else if (options._onNode == SetParentNode)
{
options._onNode -= _handler;
options._onNode += _handler = SetParentNodeAndScopeInfo;
options._onNode = SetParentNodeAndScopeInfo;
}
}

public void DisableScopeInfoRecoding(ParserOptions options)
{
if (_handler == SetParentNodeAndScopeInfo)
if (options._onNode == SetParentNodeAndScopeInfo)
{
options._onNode -= _handler;
options._onNode += _handler = SetParentNode;
options._onNode = SetParentNode;
}
else if (_handler == SetScopeInfo)
else if (options._onNode == SetScopeInfo)
{
options._onNode -= _handler;
options._onReleaseLargeBuffers -= ReleaseLargeBuffers;
ReleaseLargeBuffers();
options._onNode = _onNode;
}
}

Expand All @@ -122,35 +110,46 @@ private void SetParentNode(Node node, OnNodeContext context)
{
child.UserData = node;
}

_onNode?.Invoke(node, context);
}

private void SetScopeInfoCore(Node node, in Scope scope, ReadOnlySpan<Scope> scopeStack)
{
for (var n = scope.Id - _scopes.Count; n >= 0; n--)
{
ref var scopeInfoRef = ref _scopes.PushRef();
scopeInfoRef ??= new ScopeInfo();
}

var scopeInfo = _scopes[scope.Id];
scopeInfo.Initialize(node,
parent: scope.Id != scopeStack[0].Id ? _scopes[scopeStack.Last().Id] : null,
varScope: scope.CurrentVarScopeIndex == scopeStack.Length ? scopeInfo : _scopes[scopeStack[scope.CurrentVarScopeIndex].Id],
thisScope: scope.CurrentThisScopeIndex == scopeStack.Length ? scopeInfo : _scopes[scopeStack[scope.CurrentThisScopeIndex].Id],
varNames: scope.VarNames,
lexicalNames: scope.LexicalNames,
functionNames: scope.FunctionNames);

node.UserData = scopeInfo;
}

private void SetScopeInfo(Node node, OnNodeContext context)
{
if (context.HasScope)
{
var scope = context.Scope;
for (var n = scope.Id - _scopes.Count; n >= 0; n--)
{
ref var scopeInfoRef = ref _scopes.PushRef();
scopeInfoRef ??= new ScopeInfo();
}

var scopeStack = context.ScopeStack;
var scopeInfo = _scopes[scope.Id];
scopeInfo.Initialize(node,
parent: scope.Id != scopeStack[0].Id ? _scopes[scopeStack.Last().Id] : null,
varScope: scope.CurrentVarScopeIndex == scopeStack.Length ? scopeInfo : _scopes[scopeStack[scope.CurrentVarScopeIndex].Id],
thisScope: scope.CurrentThisScopeIndex == scopeStack.Length ? scopeInfo : _scopes[scopeStack[scope.CurrentThisScopeIndex].Id],
varNames: scope.VarNames,
lexicalNames: scope.LexicalNames,
functionNames: scope.FunctionNames);
node.UserData = scopeInfo;
SetScopeInfoCore(node, context._scope.Value, context.ScopeStack);
}

_onNode?.Invoke(node, context);
}

private void SetParentNodeAndScopeInfo(Node node, OnNodeContext context)
{
SetScopeInfo(node, context);
if (context.HasScope)
{
SetScopeInfoCore(node, context._scope.Value, context.ScopeStack);
}

foreach (var child in node.ChildNodes)
{
Expand All @@ -163,6 +162,8 @@ private void SetParentNodeAndScopeInfo(Node node, OnNodeContext context)
child.UserData = node;
}
}

_onNode?.Invoke(node, context);
}
}
}
2 changes: 1 addition & 1 deletion src/Acornima/Parser.State.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ private void ReleaseLargeBuffers()

_tokenizer.ReleaseLargeBuffers();

_options._onReleaseLargeBuffers?.Invoke();
(_options._onNode?.Target as IOnNodeHandlerWrapper)?.ReleaseLargeBuffers();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
25 changes: 22 additions & 3 deletions src/Acornima/ParserOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ namespace Acornima;

public delegate void OnNodeHandler(Node node, OnNodeContext context);

internal interface IOnNodeHandlerWrapper
{
OnNodeHandler? OnNode { get; set; }

void ReleaseLargeBuffers();
}

public record class ParserOptions
{
public static readonly ParserOptions Default = new();
Expand Down Expand Up @@ -221,7 +228,19 @@ public ParseErrorHandler ErrorHandler
/// Please note that the callback is also executed on nodes which are reinterpreted
/// later during parsing, that is, on nodes which won't become a part of the final AST.
/// </remarks>
public OnNodeHandler? OnNode { get => _onNode; init => _onNode = value; }

internal Action? _onReleaseLargeBuffers;
public OnNodeHandler? OnNode
{
get => _onNode?.Target is IOnNodeHandlerWrapper wrapper ? wrapper.OnNode : _onNode;
init
{
if (_onNode?.Target is IOnNodeHandlerWrapper wrapper)
{
wrapper.OnNode = value;
}
else
{
_onNode = value;
}
}
}
}
Loading

0 comments on commit bc1416a

Please sign in to comment.