Skip to content

Commit

Permalink
Public API to get list of functions call (#2474)
Browse files Browse the repository at this point in the history
Issue #2455.
  • Loading branch information
anderson-joyle authored Jun 12, 2024
1 parent 5126fb4 commit c240146
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/libraries/Microsoft.PowerFx.Core/Public/CheckResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,11 @@ public CheckContextSummary ApplyGetContextSummary()

return summary;
}

public IEnumerable<string> GetFunctionNames()
{
return ListFunctionVisitor.Run(ApplyParse());
}
}

// Internal interface to ensure that Result objects have a common contract
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

using System.Collections.Generic;

namespace Microsoft.PowerFx.Syntax
{
internal class ListFunctionVisitor : IdentityTexlVisitor
{
// FullName --> Name
// Use Fullname as key because it's unique.
private readonly HashSet<string> _functionNames = new HashSet<string>();

public static IEnumerable<string> Run(ParseResult parse)
{
var visitor = new ListFunctionVisitor();
parse.Root.Accept(visitor);
return visitor._functionNames;
}

public override bool PreVisit(CallNode node)
{
var hasNamespace = node.Head.Namespace.Length > 0;

var name = node.Head.Name;
var fullName = hasNamespace ?
node.Head.Namespace + "." + name :
name;

_functionNames.Add(fullName);

return base.PreVisit(node);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

using System;
using Xunit;

namespace Microsoft.PowerFx.Core.Tests
{
public class ListFunctionVisitorTests : PowerFxTest
{
[Theory]
[InlineData("Abs(1)", "Abs")]
[InlineData("Abs(Abs(Abs(Abs(Abs(1)))))", "Abs")]
[InlineData("With({x:Today()}, x+1)", "With,Today")]
[InlineData("SomeNameSpace.Foo() + SomeNameSpace.Bar()", "SomeNameSpace.Foo,SomeNameSpace.Bar")]
[InlineData("true And true", "")]
[InlineData("If(true, Blank(),Error())", "If,Blank,Error")]
public void ListFunctionNamesTest(string expression, string expectedNames)
{
foreach (var textFirst in new bool[] { false, true })
{
if (textFirst)
{
expression = $"={expression}";
}

CheckFunctionNames(textFirst, expression, expectedNames);
}
}

[Theory]
[InlineData("Hello ${Sum(1,Sqrt(2))} world", "Sum,Sqrt")]
[InlineData("3 ' {} ${Upper(3+3)} \" ${Lower($\"{7+7}\")}", "Upper,Lower")]
public void ListFunctionNamesTextFirstTest(string expression, string expectedNames)
{
CheckFunctionNames(true, expression, expectedNames);
}

[Fact]
public void ListFunctionNamesErrorTest()
{
var checkResult = new CheckResult(new Engine());
Assert.Throws<InvalidOperationException>(() => checkResult.GetFunctionNames());
}

private static void CheckFunctionNames(bool textFirst, string expression, string expectedNames)
{
var options = new ParserOptions() { TextFirst = textFirst };
var engine = new Engine();
var check = engine.Check(expression, options);
var checkResult = new CheckResult(engine).SetText(expression, options);

var functionsNames1 = check.GetFunctionNames();
var functionsNames2 = checkResult.GetFunctionNames();

var actualNames1 = string.Join(",", functionsNames1);
var actualNames2 = string.Join(",", functionsNames2);

Assert.Equal(expectedNames, actualNames1);
Assert.Equal(expectedNames, actualNames2);
}
}
}

0 comments on commit c240146

Please sign in to comment.