* tested and seems to work with parser changes, actually doing imperitive stuff isn't fully implemented yet

* imperative seems to be working for UDFs

* removed unused TexlStrings for errors for UDFs

* some nit changes for UDFs

* removed parse level parameter conflict checking

* some nit changes and tests for exceesive whitespace and comments for UDFs

* removed TokPlainEat, changed Args hashset to use out and changed flags to use a Stack.

* removed second stack push and pop.

* changed where the pop happens

* nit changes
This commit is contained in:
Malek 2022-08-08 14:05:32 -07:00 коммит произвёл GitHub
Родитель d8b91bbda2
Коммит a77b63ba4c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 235 добавлений и 169 удалений

Просмотреть файл

@ -67,6 +67,7 @@ namespace Microsoft.PowerFx.Syntax
public const string PunctuatorColon = ":";
public const string PunctuatorAt = "@";
public const char IdentifierDelimiter = '\'';
public const string PunctuatorDoubleBarrelArrow = "=>";
// These puntuators are related to commenting in the formula bar
public const string PunctuatorBlockComment = "/*";
@ -255,6 +256,7 @@ namespace Microsoft.PowerFx.Syntax
AddPunctuator(punctuators, PunctuatorAmpersand, TokKind.Ampersand);
AddPunctuator(punctuators, PunctuatorPercent, TokKind.PercentSign);
AddPunctuator(punctuators, PunctuatorAt, TokKind.At);
AddPunctuator(punctuators, PunctuatorDoubleBarrelArrow, TokKind.DoubleBarrelArrow);
// Commenting punctuators
AddPunctuator(punctuators, PunctuatorBlockComment, TokKind.Comment);

Просмотреть файл

@ -291,6 +291,12 @@ namespace Microsoft.PowerFx.Syntax
/// End of the string interpolation part (island).
/// <code>}</code>
/// </summary>
IslandEnd
IslandEnd,
/// <summary>
/// Start of body for user defined functions.
/// <code>=></code>
/// </summary>
DoubleBarrelArrow,
}
}

Просмотреть файл

@ -575,15 +575,6 @@ namespace Microsoft.PowerFx.Core.Localization
public static ErrorResourceKey ErrNamedFormula_AlreadyDefined = new ErrorResourceKey("ErrNamedFormula_AlreadyDefined");
public static ErrorResourceKey ErrorResource_NameConflict = new ErrorResourceKey("ErrorResource_NameConflict");
public static ErrorResourceKey ErrUDF_MissingIdentifier = new ErrorResourceKey("ErrUDF_MissingIdentifier");
public static ErrorResourceKey ErrUDF_MissingOpenParen = new ErrorResourceKey("ErrUDF_MissingOpenParen");
public static ErrorResourceKey ErrUDF_MissingEqual = new ErrorResourceKey("ErrUDF_MissingEqual");
public static ErrorResourceKey ErrUDF_MissingCloseParen = new ErrorResourceKey("ErrUDF_MissingCloseParen");
public static ErrorResourceKey ErrUDF_MissingSemicolon = new ErrorResourceKey("ErrUDF_MissingSemicolon");
public static ErrorResourceKey ErrUDF_MissingComma = new ErrorResourceKey("ErrUDF_MissingComma");
public static ErrorResourceKey ErrUDF_MissingColon = new ErrorResourceKey("ErrUDF_MissingColon");
public static ErrorResourceKey ErrUDF_DuplicateArgName = new ErrorResourceKey("ErrUDF_DuplicateArgName");
// ErrorResourceKey for creating an error from an arbitrary string message. The key resolves to "{0}", meaning
// that a single string arg can be supplied representing the entire text of the error.
public static ErrorResourceKey ErrGeneralError = new ErrorResourceKey("ErrGeneralError");

Просмотреть файл

@ -63,12 +63,15 @@ namespace Microsoft.PowerFx.Core.Parser
internal ISet<UDFArg> Args { get; }
public UDF(IdentToken ident, IdentToken returnType, HashSet<UDFArg> args, TexlNode body)
internal bool IsImperative { get; }
public UDF(IdentToken ident, IdentToken returnType, HashSet<UDFArg> args, TexlNode body, bool isImperative)
{
Ident = ident;
ReturnType = returnType;
Args = args;
Body = body;
IsImperative = isImperative;
}
}

Просмотреть файл

@ -27,8 +27,10 @@ namespace Microsoft.PowerFx.Core.Parser
NamedFormulas = 1 << 1
}
private bool _hasSemicolon = false;
private readonly TokenCursor _curs;
private readonly Flags _flags;
private readonly Stack<Flags> _flagsMode;
private List<TexlError> _errors;
// Nodes are assigned an integer id that is used to index into arrays later.
@ -53,7 +55,8 @@ namespace Microsoft.PowerFx.Core.Parser
_depth = 0;
_curs = new TokenCursor(tokens);
_flags = flags;
_flagsMode = new Stack<Flags>();
_flagsMode.Push(flags);
}
public static ParseUDFsResult ParseUDFsScript(string script, CultureInfo loc = null)
@ -71,111 +74,136 @@ namespace Microsoft.PowerFx.Core.Parser
{
var udfs = new List<UDF>();
// <root> ::= (<udf> ';')*
while (_curs.TokCur.Kind != TokKind.Eof)
{
//var args = new HashSet<UDFArg>();
var args = new Dictionary<string, UDFArg>();
ParseTrivia();
// Store the name of the UDF.
var ident = TokEat(TokKind.Ident);
if (ident == null)
if (!ParseUDF(udfs))
{
return new ParseUDFsResult(udfs, _errors);
}
ParseTrivia();
if (TokEat(TokKind.Semicolon) == null)
{
CreateError(_curs.TokCur, TexlStrings.ErrUDF_MissingIdentifier);
break;
}
if (TokEat(TokKind.ParenOpen) == null)
{
CreateError(_curs.TokCur, TexlStrings.ErrUDF_MissingOpenParen);
break;
}
ParseTrivia();
// Parse arguments.
while (_curs.TokCur.Kind != TokKind.ParenClose)
{
ParseTrivia();
var varIdent = TokEat(TokKind.Ident);
ParseTrivia();
if (TokEat(TokKind.Colon) == null)
{
CreateError(_curs.TokCur, TexlStrings.ErrUDF_MissingColon);
break;
}
ParseTrivia();
var varType = TokEat(TokKind.Ident);
ParseTrivia();
if (args.ContainsKey(varIdent.ToString()))
{
CreateError(_curs.TokCur, TexlStrings.ErrUDF_DuplicateArgName);
return new ParseUDFsResult(udfs, _errors);
}
args.Add(varIdent.ToString(), new UDFArg(varIdent.As<IdentToken>(), varType.As<IdentToken>()));
if (_curs.TokCur.Kind != TokKind.ParenClose && _curs.TokCur.Kind != TokKind.Comma)
{
CreateError(_curs.TokCur, TexlStrings.ErrUDF_MissingComma);
return new ParseUDFsResult(udfs, _errors);
}
else if (_curs.TokCur.Kind == TokKind.Comma)
{
TokEat(TokKind.Comma);
}
}
TokEat(TokKind.ParenClose);
ParseTrivia();
if (TokEat(TokKind.Colon) == null)
{
CreateError(_curs.TokCur, TexlStrings.ErrUDF_MissingColon);
break;
}
ParseTrivia();
var returnType = TokEat(TokKind.Ident);
if (returnType == null)
{
CreateError(_curs.TokCur, TexlStrings.ErrUDF_MissingIdentifier);
break;
}
ParseTrivia();
if (TokEat(TokKind.Equ) == null)
{
CreateError(_curs.TokCur, TexlStrings.ErrUDF_MissingEqual);
break;
}
ParseTrivia();
// Parse body
while (_curs.TokCur.Kind != TokKind.Semicolon)
{
// Check if we're at EOF before a semicolon is found
if (_curs.TidCur == TokKind.Eof)
{
CreateError(_curs.TokCur, TexlStrings.ErrUDF_MissingSemicolon);
return new ParseUDFsResult(udfs, _errors);
}
// Parse expression
var result = ParseExpr(Precedence.None);
udfs.Add(new UDF(ident.As<IdentToken>(), returnType.As<IdentToken>(), new HashSet<UDFArg>(args.Values), result));
}
TokEat(TokKind.Semicolon);
}
return new ParseUDFsResult(udfs, _errors);
}
private bool ParseUDFArgs(out HashSet<UDFArg> args)
{
args = new HashSet<UDFArg>();
if (TokEat(TokKind.ParenOpen) == null)
{
return false;
}
ParseTrivia();
while (_curs.TokCur.Kind != TokKind.ParenClose)
{
ParseTrivia();
var varIdent = TokEat(TokKind.Ident);
ParseTrivia();
if (TokEat(TokKind.Colon) == null)
{
break;
}
ParseTrivia();
var varType = TokEat(TokKind.Ident);
ParseTrivia();
args.Add(new UDFArg(varIdent.As<IdentToken>(), varType.As<IdentToken>()));
if (_curs.TokCur.Kind != TokKind.ParenClose && _curs.TokCur.Kind != TokKind.Comma)
{
ErrorTid(_curs.TokCur, TokKind.Comma);
return false;
}
else if (_curs.TokCur.Kind == TokKind.Comma)
{
TokEat(TokKind.Comma);
}
}
ParseTrivia();
TokEat(TokKind.ParenClose);
return true;
}
private bool ParseUDF(List<UDF> udfs)
{
// <udf> ::= IDENT '(' <args> ')' ':' IDENT '=>' <function-body>
ParseTrivia();
var ident = TokEat(TokKind.Ident);
if (ident == null)
{
return false;
}
ParseTrivia();
if (!ParseUDFArgs(out HashSet<UDFArg> args))
{
return false;
}
ParseTrivia();
if (TokEat(TokKind.Colon) == null)
{
return false;
}
ParseTrivia();
var returnType = TokEat(TokKind.Ident);
if (returnType == null)
{
return false;
}
ParseTrivia();
if (TokEat(TokKind.DoubleBarrelArrow) == null)
{
return false;
}
// <function-body> ::= (EXP | <bracs-exp>)
// <bracs-exp> ::= '{' (((<EXP> ';')+ <EXP>) | <EXP>) (';')? '}'
ParseTrivia();
if (_curs.TidCur == TokKind.CurlyOpen)
{
_curs.TokMove();
_hasSemicolon = false;
ParseTrivia();
_flagsMode.Push(Flags.EnableExpressionChaining);
var exp_result = ParseExpr(Precedence.None);
_flagsMode.Pop();
ParseTrivia();
if (TokEat(TokKind.CurlyClose) == null)
{
return false;
}
udfs.Add(new UDF(ident.As<IdentToken>(), returnType.As<IdentToken>(), new HashSet<UDFArg>(args), exp_result, _hasSemicolon));
return true;
}
var result = ParseExpr(Precedence.None);
ParseTrivia();
udfs.Add(new UDF(ident.As<IdentToken>(), returnType.As<IdentToken>(), new HashSet<UDFArg>(args), result, false));
return true;
}
// Parse the script
// Parsing strips out parens used to establish precedence, but these may be helpful to the
// caller, so precedenceTokens provide a list of stripped tokens.
@ -642,13 +670,14 @@ namespace Microsoft.PowerFx.Core.Parser
break;
case TokKind.Semicolon:
if (_flags.HasFlag(Flags.NamedFormulas))
_hasSemicolon = true;
if (_flagsMode.Peek().HasFlag(Flags.NamedFormulas))
{
goto default;
}
// Only allow this when expression chaining is enabled (e.g. in behavior rules).
if ((_flags & Flags.EnableExpressionChaining) == 0)
if ((_flagsMode.Peek() & Flags.EnableExpressionChaining) == 0)
{
goto case TokKind.False;
}
@ -1184,7 +1213,7 @@ namespace Microsoft.PowerFx.Core.Parser
sourceList.Add(new TokenSource(delimiter));
sourceList.Add(ParseTrivia());
if (_curs.TidCur == TokKind.Eof || _curs.TidCur == TokKind.Comma || _curs.TidCur == TokKind.ParenClose)
if (_curs.TidCur == TokKind.Eof || _curs.TidCur == TokKind.Comma || _curs.TidCur == TokKind.ParenClose || _curs.TidCur == TokKind.CurlyClose)
{
break;
}

Просмотреть файл

@ -171,6 +171,7 @@ namespace Microsoft.PowerFx
udf.Ident.ToString(),
udf.Body.ToString(),
FormulaType.GetFromStringOrNull(udf.ReturnType.ToString()),
udf.IsImperative,
udf.Args.Select(arg => new NamedFormulaType(arg.VarIdent.ToString(), FormulaType.GetFromStringOrNull(arg.VarType.ToString()))).ToArray())).ToArray();
return DefineFunctions(udfDefinitions);
}
@ -188,7 +189,7 @@ namespace Microsoft.PowerFx
record = record.Add(p);
}
var check = new CheckWrapper(this, definition.Body, record);
var check = new CheckWrapper(this, definition.Body, record, definition.IsImperative);
var func = new UserDefinedTexlFunction(definition.Name, definition.ReturnType, definition.Parameters, check);
if (_customFuncs.ContainsKey(definition.Name))

Просмотреть файл

@ -16,14 +16,20 @@ namespace Microsoft.PowerFx.Interpreter.UDF
private readonly string _expressionText;
private readonly RecordType _parameterType;
private readonly RecalcEngine _engine;
public readonly ParserOptions ParserOptions;
public CheckWrapper(RecalcEngine engine, string expressionText, RecordType parameterType = null)
public CheckWrapper(RecalcEngine engine, string expressionText, RecordType parameterType = null, bool isImperative = false)
{
_engine = engine;
_expressionText = expressionText;
_parameterType = parameterType;
ParserOptions = new ParserOptions()
{
Culture = _engine.Config.CultureInfo,
AllowsSideEffects = isImperative,
};
}
public CheckResult Get() => _engine.Check(_expressionText, _parameterType);
public CheckResult Get() => _engine.Check(_expressionText, _parameterType, ParserOptions);
}
}

Просмотреть файл

@ -17,16 +17,19 @@ namespace Microsoft.PowerFx.Interpreter
internal readonly IEnumerable<NamedFormulaType> Parameters;
public UDFDefinition(string name, string body, FormulaType returnType, IEnumerable<NamedFormulaType> parameters)
internal readonly bool IsImperative;
public UDFDefinition(string name, string body, FormulaType returnType, bool isImperative, IEnumerable<NamedFormulaType> parameters)
{
Name = name;
Body = body;
ReturnType = returnType;
IsImperative = isImperative;
Parameters = parameters;
}
public UDFDefinition(string name, string body, FormulaType returnType, params NamedFormulaType[] parameters)
: this(name, body, returnType, parameters.AsEnumerable())
public UDFDefinition(string name, string body, FormulaType returnType, bool isImperative, params NamedFormulaType[] parameters)
: this(name, body, returnType, isImperative, parameters.AsEnumerable())
{
}
}

Просмотреть файл

@ -18,6 +18,8 @@ namespace Microsoft.PowerFx.Interpreter
public override bool SupportsParamCoercion => false;
public override bool IsSelfContained => !_check.ParserOptions.AllowsSideEffects;
public UserDefinedTexlFunction(string name, FormulaType returnType, IEnumerable<NamedFormulaType> parameterNames, CheckWrapper lazyCheck)
: base(name, returnType, parameterNames.Select(x => x.Type).ToArray())
{

Просмотреть файл

@ -6509,38 +6509,6 @@
<value>The first argument of '{0}' should be a valid variable name, and cannot conflict with any existing control, screen, collection, or data source names. Found type '{1}'</value>
<comment>Error Message</comment>
</data>
<data name="ErrUDF_MissingIdentifier" xml:space="preserve">
<value>User Defined Function is missing a valid name</value>
<comment>Error Message</comment>
</data>
<data name="ErrUDF_MissingOpenParen" xml:space="preserve">
<value>User Defined Function is missing a '(' here</value>
<comment>Error Message</comment>
</data>
<data name="ErrUDF_MissingEqual" xml:space="preserve">
<value>User Defined Function is missing a '=' here</value>
<comment>Error Message</comment>
</data>
<data name="ErrUDF_MissingCloseParen" xml:space="preserve">
<value>User Defined Function is missing a ')' here</value>
<comment>Error Message</comment>
</data>
<data name="ErrUDF_MissingSemicolon" xml:space="preserve">
<value>User Defined Function is missing a ';' here</value>
<comment>Error Message</comment>
</data>
<data name="ErrUDF_MissingComma" xml:space="preserve">
<value>User Defined Function is missing a ',' here</value>
<comment>Error Message</comment>
</data>
<data name="ErrUDF_MissingColon" xml:space="preserve">
<value>User Defined Function is missing an ':' here</value>
<comment>Error Message</comment>
</data>
<data name="ErrUDF_DuplicateArgName" xml:space="preserve">
<value>User Defined Function has a duplicate argument name</value>
<comment>Error Message</comment>
</data>
<data name="AboutSet" xml:space="preserve">
<value>Create and set a global variable.</value>
<comment>Description of 'Set' function.</comment>

Просмотреть файл

@ -12,7 +12,7 @@ namespace Microsoft.PowerFx.Core.Tests
public class NamedFormulasTests : PowerFxTest
{
[Theory]
[InlineData("Foo(x: Number): Number = Abs(x);")]
[InlineData("Foo(x: Number): Number => Abs(x);")]
public void DefFuncTest(string script)
{
var parsedUDFS = new ParsedUDFs(script);
@ -28,12 +28,53 @@ namespace Microsoft.PowerFx.Core.Tests
}
[Theory]
[InlineData("Foo(x As Number, x As String) As Number = Abs(x);")]
public void DoubleArgDefTest(string script)
[InlineData("Rec4(x: Number): Number => { { force: 1, goo: x } };" +
"Rec5(x: Number): Number => { \"asfd\"; { force: 1, goo: x } };" +
"Rec6(x: Number): Number => x + 1;" +
"Rec7(x: Number): Number => { x + 1 };")]
public void DefFunctionFromDiscussion(string script)
{
var parsedUDFS = new ParsedUDFs(script);
var result = parsedUDFS.GetParsed();
Assert.True(result.HasError);
var parsedUDFs = new ParsedUDFs(script);
var result = parsedUDFs.GetParsed();
Assert.False(result.HasError);
}
[Theory]
[InlineData("Rec4\n(x\n: \nNumber\n)\n: \nNumber \n=>\n \n{\n \n{ force: 1, goo: x }\n \n}\n;\n" +
"Rec5 ( x : Number ) : Number => { \"asfd\"; { force: 1, goo: x } } ; " +
"Rec6/*comment*/(/*comment*/x/*comment*/:/*comment*/ Number/*comment*/)/*comment*/:/*comment*/ Number/*comment*/ =>/*comment*/ x/*comment*/ + 1/*comment*/;/*comment*/" +
"Rec7//comment\n(//comment\nx//comment\n://comment\n Number//comment\n)://comment\n Number//comment\n =>//comment\n { x + 1 }//comment\n;")]
public void DefFunctionWeirdFormatting(string script)
{
var parsedUDFs = new ParsedUDFs(script);
var result = parsedUDFs.GetParsed();
Assert.False(result.HasError);
}
[Theory]
[InlineData("Foo(): Number => { 1+1; 2+2; };")]
public void TestChaining(string script)
{
var parsedUDFs = new ParsedUDFs(script);
var result = parsedUDFs.GetParsed();
Assert.False(result.HasError);
var udf = result.UDFs.First();
Assert.Equal("Foo", udf.Ident.ToString());
Assert.Equal("1 + 1 ; 2 + 2", udf.Body.ToString());
}
[Theory]
[InlineData("Foo(): Number => { Sum(1, 1); Sum(2, 2); };")]
public void TestChaining2(string script)
{
var parsedUDFs = new ParsedUDFs(script);
var result = parsedUDFs.GetParsed();
Assert.False(result.HasError);
var udf = result.UDFs.First();
Assert.Equal("Foo", udf.Ident.ToString());
Assert.Equal("Sum(1, 1) ; Sum(2, 2)", udf.Body.ToString());
}
[Theory]

Просмотреть файл

@ -255,6 +255,7 @@ namespace Microsoft.PowerFx.Tests
"foo",
"x * y",
FormulaType.Number,
false,
new NamedFormulaType("x", FormulaType.Number),
new NamedFormulaType("y", FormulaType.Number))).Errors;
Assert.False(enumerable.Any());
@ -272,6 +273,7 @@ namespace Microsoft.PowerFx.Tests
"foo",
body,
FormulaType.Number,
false,
new NamedFormulaType("x", FormulaType.Number))).Errors;
var result = recalcEngine.Eval("foo(0)");
Assert.Equal(2.0, result.ToObject());
@ -287,7 +289,8 @@ namespace Microsoft.PowerFx.Tests
new UDFDefinition(
"foo",
"foo()",
FormulaType.Blank)).Errors.Any());
FormulaType.Blank,
false)).Errors.Any());
var result = recalcEngine.Eval("foo()");
Assert.IsType<ErrorValue>(result);
}
@ -306,7 +309,7 @@ namespace Microsoft.PowerFx.Tests
var variable = new NamedFormulaType("x", FormulaType.Number);
Assert.False(recalcEngine.DefineFunctions(
new UDFDefinition(funcName, body, returnType, variable)).Errors.Any());
new UDFDefinition(funcName, body, returnType, false, variable)).Errors.Any());
Assert.Equal(1.0, recalcEngine.Eval("hailstone(192)").ToObject());
}
@ -325,11 +328,13 @@ namespace Microsoft.PowerFx.Tests
"odd",
bodyOdd,
FormulaType.Boolean,
false,
new NamedFormulaType("number", FormulaType.Number));
var udfEven = new UDFDefinition(
"even",
bodyEven,
FormulaType.Boolean,
false,
new NamedFormulaType("number", FormulaType.Number));
Assert.False(recalcEngine.DefineFunctions(udfOdd, udfEven).Errors.Any());
@ -344,8 +349,8 @@ namespace Microsoft.PowerFx.Tests
var config = new PowerFxConfig(null);
var recalcEngine = new RecalcEngine(config);
Assert.Throws<InvalidOperationException>(() => recalcEngine.DefineFunctions(
new UDFDefinition("foo", "foo()", FormulaType.Blank),
new UDFDefinition("foo", "x+1", FormulaType.Number)));
new UDFDefinition("foo", "foo()", FormulaType.Blank, false),
new UDFDefinition("foo", "x+1", FormulaType.Number, false)));
}
[Fact]
@ -353,7 +358,7 @@ namespace Microsoft.PowerFx.Tests
{
var config = new PowerFxConfig(null);
var recalcEngine = new RecalcEngine(config);
Assert.True(recalcEngine.DefineFunctions(new UDFDefinition("foo", "x[", FormulaType.Blank)).Errors.Any());
Assert.True(recalcEngine.DefineFunctions(new UDFDefinition("foo", "x[", FormulaType.Blank, false)).Errors.Any());
}
[Fact]
@ -361,7 +366,7 @@ namespace Microsoft.PowerFx.Tests
{
var config = new PowerFxConfig(null);
var recalcEngine = new RecalcEngine(config);
Assert.False(recalcEngine.DefineFunctions(new UDFDefinition("foo", "x+1", FormulaType.Number, new NamedFormulaType("x", FormulaType.Number))).Errors.Any());
Assert.False(recalcEngine.DefineFunctions(new UDFDefinition("foo", "x+1", FormulaType.Number, false, new NamedFormulaType("x", FormulaType.Number))).Errors.Any());
Assert.False(recalcEngine.Check("foo(False)").IsSuccess);
Assert.False(recalcEngine.Check("foo(Table( { Value: \"Strawberry\" }, { Value: \"Vanilla\" } ))").IsSuccess);
Assert.True(recalcEngine.Check("foo(1)").IsSuccess);
@ -713,7 +718,7 @@ namespace Microsoft.PowerFx.Tests
public void UDFRecursionLimitTest()
{
var recalcEngine = new RecalcEngine(new PowerFxConfig(null));
recalcEngine.DefineFunctions("Foo(x: Number): Number = Foo(x);");
recalcEngine.DefineFunctions("Foo(x: Number): Number => Foo(x);");
Assert.IsType<ErrorValue>(recalcEngine.Eval("Foo(1)"));
}
@ -721,7 +726,7 @@ namespace Microsoft.PowerFx.Tests
public void UDFRecursionWorkingTest()
{
var recalcEngine = new RecalcEngine(new PowerFxConfig(null));
recalcEngine.DefineFunctions("Foo(x: Number): Number = If(x = 1, 1, If(Mod(x, 2) = 0, Foo(x/2), Foo(x*3 + 1)));");
recalcEngine.DefineFunctions("Foo(x: Number): Number => If(x = 1, 1, If(Mod(x, 2) = 0, Foo(x/2), Foo(x*3 + 1)));");
Assert.Equal(1.0, recalcEngine.Eval("Foo(5)").ToObject());
}
@ -733,11 +738,11 @@ namespace Microsoft.PowerFx.Tests
MaxCallDepth = 80
});
recalcEngine.DefineFunctions(
"A(x: Number): Number = If(Mod(x, 2) = 0, B(x/2), B(x));" +
"B(x: Number): Number = If(Mod(x, 3) = 0, C(x/3), C(x));" +
"C(x: Number): Number = If(Mod(x, 5) = 0, D(x/5), D(x));" +
"D(x: Number): Number = If(Mod(x, 7) = 0, F(x/7), F(x));" +
"F(x: Number): Number = If(x = 1, 1, A(x+1));");
"A(x: Number): Number => If(Mod(x, 2) = 0, B(x/2), B(x));" +
"B(x: Number): Number => If(Mod(x, 3) = 0, C(x/3), C(x));" +
"C(x: Number): Number => If(Mod(x, 5) = 0, D(x/5), D(x));" +
"D(x: Number): Number => { If(Mod(x, 7) = 0, F(x/7), F(x)) };" +
"F(x: Number): Number => { If(x = 1, 1, A(x+1)) };");
Assert.Equal(1.0, recalcEngine.Eval("A(12654)").ToObject());
}
@ -745,14 +750,23 @@ namespace Microsoft.PowerFx.Tests
public void DoubleDefinitionTest()
{
var recalcEngine = new RecalcEngine(new PowerFxConfig(null));
Assert.Throws<InvalidOperationException>(() => recalcEngine.DefineFunctions("Foo(): Number = 10; Foo(x: Number): String = \"hi\""));
Assert.Throws<InvalidOperationException>(() => recalcEngine.DefineFunctions("Foo(): Number => 10; Foo(x: Number): String => \"hi\";"));
}
[Fact]
public void TestNumberBinding()
{
var recalcEngine = new RecalcEngine(new PowerFxConfig(null));
Assert.True(recalcEngine.DefineFunctions("Foo(): String = 10;").Errors.Any());
Assert.True(recalcEngine.DefineFunctions("Foo(): String => 10;").Errors.Any());
}
[Fact]
public void TestMultiReturn()
{
var recalcEngine = new RecalcEngine(new PowerFxConfig(null));
var str = "Foo(x: Number): Number => { 1+1; 2+2; };";
recalcEngine.DefineFunctions(str);
Assert.Equal(4.0, recalcEngine.Eval("Foo(1)", null, new ParserOptions { AllowsSideEffects = true }).ToObject());
}
#region Test