Skip to content

Commit

Permalink
Merge pull request #3899 from microsoft/hond/lambda
Browse files Browse the repository at this point in the history
[Expression] Support lambda style expression
  • Loading branch information
Tom Laird-McConnell authored Jun 14, 2020
2 parents 2963d0d + 0e63364 commit 2f0e402
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 7 deletions.
2 changes: 2 additions & 0 deletions libraries/AdaptiveExpressions/parser/ExpressionAntlrLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ COMMA: ',';
COLON: ':';
ARROW: '=>';
NUMBER : DIGIT + ( '.' DIGIT +)? ;
WHITESPACE : (' '|'\t'|'\ufeff'|'\u00a0') {ignoreWS}? -> skip;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ textContent
;

argsList
: expression (COMMA expression)*
: (lambda|expression) (COMMA (lambda|expression))*
;

lambda
: IDENTIFIER ARROW expression
;

keyValuePairList
Expand Down
27 changes: 21 additions & 6 deletions libraries/AdaptiveExpressions/parser/ExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public override Expression VisitBinaryOpExp([NotNull] ExpressionAntlrParser.Bina

public override Expression VisitFuncInvokeExp([NotNull] ExpressionAntlrParser.FuncInvokeExpContext context)
{
var parameters = ProcessArgsList(context.argsList()).ToList();
var parameters = ProcessArgsList(context.argsList());

// Remove the check to check primaryExpression is just an IDENTIFIER to support "." in template name
var functionName = context.primaryExpression().GetText();
Expand Down Expand Up @@ -191,7 +191,7 @@ public override Expression VisitNumericAtom([NotNull] ExpressionAntlrParser.Nume

public override Expression VisitArrayCreationExp([NotNull] ExpressionAntlrParser.ArrayCreationExpContext context)
{
var parameters = ProcessArgsList(context.argsList()).ToList();
var parameters = ProcessArgsList(context.argsList());
return MakeExpression(ExpressionType.CreateArray, parameters.ToArray());
}

Expand Down Expand Up @@ -276,15 +276,30 @@ public override Expression VisitStringInterpolationAtom([NotNull] ExpressionAntl
private Expression MakeExpression(string functionType, params Expression[] children)
=> Expression.MakeExpression(_lookupFunction(functionType) ?? throw new SyntaxErrorException($"{functionType} does not have an evaluator, it's not a built-in function or a custom function."), children);

private IEnumerable<Expression> ProcessArgsList(ExpressionAntlrParser.ArgsListContext context)
private IList<Expression> ProcessArgsList(ExpressionAntlrParser.ArgsListContext context)
{
if (context != null)
var result = new List<Expression>();
if (context == null)
{
foreach (var expression in context.expression())
return result;
}

foreach (var child in context.children)
{
if (child is ExpressionAntlrParser.LambdaContext lambda)
{
yield return Visit(expression);
var evalParam = MakeExpression(ExpressionType.Accessor, Expression.ConstantExpression(lambda.IDENTIFIER().GetText()));
var evalFun = Visit(lambda.expression());
result.Add(evalParam);
result.Add(evalFun);
}
else if (child is ExpressionAntlrParser.ExpressionContext expression)
{
result.Add(Visit(expression));
}
}

return result;
}

private string EvalEscape(string text)
Expand Down
6 changes: 6 additions & 0 deletions tests/AdaptiveExpressions.Tests/ExpressionParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -830,15 +830,20 @@ public class ExpressionParserTests
Test("join(createArray('a', 'b'), ',', ' and ')", "a and b"),
Test("join(createArray(\r\n'a',\r\n 'b'), ','\r\n,\r\n ' and ')", "a and b"),
Test("join(foreach(dialog, item, item.key), ',')", "x,instance,options,title,subTitle"),
Test("join(foreach(dialog, item => item.key), ',')", "x,instance,options,title,subTitle"),
Test("foreach(dialog, item, item.value)[1].xxx", "instance"),
Test("foreach(dialog, item=>item.value)[1].xxx", "instance"),
Test("join(foreach(items, item, item), ',')", "zero,one,two"),
Test("join(foreach(items, item=>item), ',')", "zero,one,two"),
Test("join(foreach(indicesAndValues(items), item, item.value), ',')", "zero,one,two"),
Test("join(foreach(nestedItems, i, i.x + first(nestedItems).x), ',')", "2,3,4", new HashSet<string> { "nestedItems" }),
Test("join(foreach(items, item, concat(item, string(count(items)))), ',')", "zero3,one3,two3", new HashSet<string> { "items" }),
Test("join(select(items, item, item), ',')", "zero,one,two"),
Test("join(select(items, item=> item), ',')", "zero,one,two"),
Test("join(select(nestedItems, i, i.x + first(nestedItems).x), ',')", "2,3,4", new HashSet<string> { "nestedItems" }),
Test("join(select(items, item, concat(item, string(count(items)))), ',')", "zero3,one3,two3", new HashSet<string> { "items" }),
Test("join(where(items, item, item == 'two'), ',')", "two"),
Test("join(where(items, item => item == 'two'), ',')", "two"),
Test("string(where(dialog, item, item.value=='Dialog Title'))", "{\"title\":\"Dialog Title\"}"),
Test("first(where(indicesAndValues(items), elt, elt.index > 1)).value", "two"),
Test("first(where(indicesAndValues(bag), elt, elt.index == \"three\")).value", 3),
Expand Down Expand Up @@ -899,6 +904,7 @@ public class ExpressionParserTests
Test("{text: 'hello'}.text", "hello"),
Test("string(addProperty({'key1':'value1'}, 'key2','value2'))", "{\"key1\":\"value1\",\"key2\":\"value2\"}"),
Test("foreach(items, x, addProperty({}, 'a', x))[0].a", "zero"),
Test("foreach(items, x => addProperty({}, 'a', x))[0].a", "zero"),
Test("string(setProperty({'key1':'value1'}, 'key1','value2'))", "{\"key1\":\"value2\"}"),
Test("string(setProperty({}, 'key1','value2'))", "{\"key1\":\"value2\"}"),
Test("string([{a: 1}, {b: 2}, {c: 3}][0])", "{\"a\":1}"),
Expand Down

0 comments on commit 2f0e402

Please sign in to comment.