зеркало из https://github.com/microsoft/Power-Fx.git
Malek/udf grammar changes (#521)
* 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:
Родитель
d8b91bbda2
Коммит
a77b63ba4c
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче