Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up User-defined type related parser flag #2697

Merged
merged 12 commits into from
Oct 22, 2024
5 changes: 5 additions & 0 deletions releasenotes/releasenotes-1.3.0-rc.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
`Patch(DS, table_of_rows_with_updates)`\
`Patch(Record, Updates1, Updates2,…)`

- ParseJSON, IsType, AsType (https://github.com/microsoft/Power-Fx/pull/2569): ParseJSON, IsType, AsType functions now supports in-lined and user-defined types as argument.\
`ParseJSON(Text, Type)`\
`IsType(UntypedObject, Type)`\
`AsType(UntypedObject, Type)`

## Other:
- Untyped object
- Read a field from an untyped object by index (https://github.com/microsoft/Power-Fx/pull/2555):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,6 @@ public class ParserOptions
/// </summary>
public int MaxExpressionLength { get; set; }

/// <summary>
/// Flag for parse type literals.
/// </summary>
internal bool AllowParseAsTypeLiteral { get; set; }

/// <summary>
/// Allow parsing of attributes on user definitions
/// This is an early prototype, and so is internal.
Expand Down Expand Up @@ -88,7 +83,6 @@ internal ParseResult Parse(string script, Features features)
(NumberIsFloat ? TexlParser.Flags.NumberIsFloat : 0) |
(DisableReservedKeywords ? TexlParser.Flags.DisableReservedKeywords : 0) |
(TextFirst ? TexlParser.Flags.TextFirst : 0) |
(AllowParseAsTypeLiteral ? TexlParser.Flags.AllowTypeLiteral : 0) |
(features.PowerFxV1CompatibilityRules ? TexlParser.Flags.PFxV1 : 0);

var result = TexlParser.ParseScript(script, features, Culture, flags);
Expand Down
22 changes: 11 additions & 11 deletions src/libraries/Microsoft.PowerFx.Core/Parser/TexlParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ public enum Flags
// When specified, allows reserved keywords to be used as identifiers.
DisableReservedKeywords = 1 << 3,

// When specified, allows type literals to be parsed.
AllowTypeLiteral = 1 << 4,

// Text first. Implemented entirely in the Lexer.
TextFirst = 1 << 5,

Expand Down Expand Up @@ -90,16 +87,16 @@ private TexlParser(IReadOnlyList<Token> tokens, Flags flags, Features features =
/// </summary>
/// <param name="script">Script to be parsed.</param>
/// <param name="parserOptions">Options for parsing an expression.</param>
/// <param name="features">Power Fx feature flags.</param>
/// <returns><see cref="ParseUserDefinitionResult"/>.</returns>
public static ParseUserDefinitionResult ParseUserDefinitionScript(string script, ParserOptions parserOptions)
public static ParseUserDefinitionResult ParseUserDefinitionScript(string script, ParserOptions parserOptions, Features features = null)
adithyaselv marked this conversation as resolved.
Show resolved Hide resolved
{
Contracts.AssertValue(parserOptions);
var flags = Flags.NamedFormulas |
(parserOptions.NumberIsFloat ? Flags.NumberIsFloat : 0) |
(parserOptions.AllowParseAsTypeLiteral ? Flags.AllowTypeLiteral : 0) |
(parserOptions.AllowAttributes ? Flags.AllowAttributes : 0);
var formulaTokens = TokenizeScript(script, parserOptions.Culture, flags);
var parser = new TexlParser(formulaTokens, flags);
var parser = new TexlParser(formulaTokens, flags, features);

return parser.ParseUDFsAndNamedFormulas(script, parserOptions);
}
Expand Down Expand Up @@ -300,7 +297,7 @@ private ParseUserDefinitionResult ParseUDFsAndNamedFormulas(string script, Parse
continue;
}

if (_curs.TidCur == TokKind.ColonEqual && _flagsMode.Peek().HasFlag(Flags.AllowTypeLiteral))
if (_curs.TidCur == TokKind.ColonEqual && _features.IsUserDefinedTypesEnabled)
{
var declaration = script.Substring(declarationStart, _curs.TokCur.Span.Min - declarationStart);
_curs.TokMove();
Expand Down Expand Up @@ -1268,7 +1265,7 @@ private TexlNode ParseOperand()

if (AfterSpaceTokenId() == TokKind.ParenOpen)
{
if (ident.Token.As<IdentToken>().Name.Value == "Type" && _flagsMode.Peek().HasFlag(Flags.AllowTypeLiteral))
if (ident.Token.As<IdentToken>().Name.Value == LanguageConstants.TypeLiteralInvariantName && _features.IsUserDefinedTypesEnabled)
adithyaselv marked this conversation as resolved.
Show resolved Hide resolved
{
var typeLiteralNode = ParseTypeLiteral();

Expand Down Expand Up @@ -2048,11 +2045,13 @@ internal static string GetTokString(TokKind kind)
/// </summary>
/// <param name="text">Expression text to format.</param>
/// <param name="flags">Optional flags to customize the behavior of underlying lexer and parser. By default, expression chaining is enabled.</param>
/// <param name="features">Power Fx features.</param>
/// <returns>Formatted expression text.</returns>
public static string Format(string text, Flags flags = Flags.EnableExpressionChaining)
public static string Format(string text, Flags flags = Flags.EnableExpressionChaining, Features features = null)
adithyaselv marked this conversation as resolved.
Show resolved Hide resolved
{
var result = ParseScript(
text,
features ?? Features.None,
flags: flags);

// Can't pretty print a script with errors.
Expand All @@ -2064,11 +2063,12 @@ public static string Format(string text, Flags flags = Flags.EnableExpressionCha
return PrettyPrintVisitor.Format(result.Root, result.Before, result.After, text);
}

public static string FormatUserDefinitions(string text, ParserOptions options)
public static string FormatUserDefinitions(string text, ParserOptions options, Features features = null)
{
var result = ParseUserDefinitionScript(
text,
options);
options,
features ?? Features.None);

// Can't pretty print a script with errors.
if (result.HasErrors)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public class DefinitionsCheckResult : IOperationStatus

private ParseUserDefinitionResult _parse;

private readonly Features _features;

// Local symboltable to store new symbols in a given script and use in binding.
private readonly SymbolTable _localSymbolTable;

Expand All @@ -49,9 +51,10 @@ public class DefinitionsCheckResult : IOperationStatus
// All errors accumulated.
private readonly List<ExpressionError> _errors = new List<ExpressionError>();

public DefinitionsCheckResult()
public DefinitionsCheckResult(Features features = null)
adithyaselv marked this conversation as resolved.
Show resolved Hide resolved
{
_localSymbolTable = new SymbolTable { DebugName = "LocalUserDefinitions" };
_features = features ?? Features.None;
adithyaselv marked this conversation as resolved.
Show resolved Hide resolved
}

internal DefinitionsCheckResult SetBindingInfo(ReadOnlySymbolTable symbols)
Expand Down Expand Up @@ -93,7 +96,7 @@ internal ParseUserDefinitionResult ApplyParse()

if (_parse == null)
{
_parse = UserDefinitions.Parse(_definitions, _parserOptions);
_parse = UserDefinitions.Parse(_definitions, _parserOptions, _features);

if (_parse.HasErrors)
{
Expand Down
4 changes: 2 additions & 2 deletions src/libraries/Microsoft.PowerFx.Core/Syntax/Formula.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ private void AssertValid()
// True if the formula has already been parsed.
public bool IsParsed => ParseTree != null;

public bool EnsureParsed(TexlParser.Flags flags)
public bool EnsureParsed(TexlParser.Flags flags, Features features = null)
{
AssertValid();

if (ParseTree == null)
{
var result = TexlParser.ParseScript(Script, loc: Loc, flags: flags);
var result = TexlParser.ParseScript(Script, features ?? Features.None, culture: Loc, flags: flags);
ApplyParse(result);
}

Expand Down
8 changes: 3 additions & 5 deletions src/libraries/Microsoft.PowerFx.Core/Syntax/TexlPretty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ public override LazyList<string> Visit(TypeLiteralNode node, Precedence parentPr

result = result
.With(
"Type",
LanguageConstants.TypeLiteralInvariantName,
TexlLexer.PunctuatorParenOpen)
.With(node.TypeRoot.Accept(this, Precedence.Atomic))
.With(TexlLexer.PunctuatorParenClose);
Expand Down Expand Up @@ -466,9 +466,7 @@ private string SpacedOper(string op)

internal sealed class PrettyPrintVisitor : TexlFunctionalVisitor<LazyList<string>, PrettyPrintVisitor.Context>
{
private readonly string _script;

public const string TypeInvariantFunctionName = "Type";
private readonly string _script;

private static readonly Dictionary<BinaryOp, Precedence> BinaryPrecedence =
new Dictionary<BinaryOp, Precedence>()
Expand Down Expand Up @@ -542,7 +540,7 @@ public static string FormatUserDefinitions(ParseUserDefinitionResult result, str
case UserDefinitionType.DefinedType:
var type = result.DefinedTypes.First(type => type.Ident == name);

definitions.Add(declaration + $" = {TypeInvariantFunctionName}(" + string.Concat(visitor.CommentsOf(before).With(type.Type.Accept(visitor, new Context(0)).With(visitor.CommentsOf(after)))) + ")");
definitions.Add(declaration + $" = {LanguageConstants.TypeLiteralInvariantName}(" + string.Concat(visitor.CommentsOf(before).With(type.Type.Accept(visitor, new Context(0)).With(visitor.CommentsOf(after)))) + ")");
break;
default:
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,15 @@ private UserDefinitions(string script, ParserOptions parserOptions, Features fea
/// </summary>
/// <param name="script">Script with named formulas, user-defined functions and user-defined types.</param>
/// <param name="parserOptions">Options for parsing an expression.</param>
/// <param name="features">Power Fx feature flags.</param>
/// <returns><see cref="ParseUserDefinitionResult"/>.</returns>
public static ParseUserDefinitionResult Parse(string script, ParserOptions parserOptions)
public static ParseUserDefinitionResult Parse(string script, ParserOptions parserOptions, Features features = null)
{
var parseResult = TexlParser.ParseUserDefinitionScript(script, parserOptions);
var parseResult = TexlParser.ParseUserDefinitionScript(script, parserOptions, features);

if (parserOptions.AllowAttributes)
{
var userDefinitions = new UserDefinitions(script, parserOptions);
var userDefinitions = new UserDefinitions(script, parserOptions, features);
parseResult = userDefinitions.ProcessPartialAttributes(parseResult);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ internal class BuiltinFunctionsCore
public static readonly TexlFunction And = _library.Add(new VariadicLogicalFunction(isAnd: true));
public static readonly TexlFunction Asin = _library.Add(new AsinFunction());
public static readonly TexlFunction AsinT = _library.Add(new AsinTableFunction());
public static readonly TexlFunction AsType = _library.Add(new AsTypeFunction());
public static readonly TexlFunction AsType = _library.Add(new AsTypeFunction());
public static readonly TexlFunction AsType_UO = _library.Add(new AsTypeFunction_UO());
public static readonly TexlFunction Atan = _library.Add(new AtanFunction());
public static readonly TexlFunction Atan2 = _library.Add(new Atan2Function());
public static readonly TexlFunction AtanT = _library.Add(new AtanTableFunction());
Expand Down Expand Up @@ -132,7 +133,8 @@ internal class BuiltinFunctionsCore
public static readonly TexlFunction IsError = _library.Add(new IsErrorFunction());
public static readonly TexlFunction IsNumeric = _library.Add(new IsNumericFunction());
public static readonly TexlFunction ISOWeekNum = _library.Add(new ISOWeekNumFunction());
public static readonly TexlFunction IsToday = _library.Add(new IsTodayFunction());
public static readonly TexlFunction IsToday = _library.Add(new IsTodayFunction());
public static readonly TexlFunction IsType_UO = _library.Add(new IsTypeFunction_UO());
public static readonly TexlFunction Language = _library.Add(new LanguageFunction());
public static readonly TexlFunction Last = _library.Add(new FirstLastFunction(isFirst: false));
public static readonly TexlFunction Last_UO = _library.Add(new FirstLastFunction_UO(isFirst: false));
Expand Down Expand Up @@ -231,6 +233,7 @@ internal class BuiltinFunctionsCore
public static readonly TexlFunction TrimT = _library.Add(new TrimTFunction());
public static readonly TexlFunction Trunc = _library.Add(new TruncFunction());
public static readonly TexlFunction TruncT = _library.Add(new TruncTableFunction());
public static readonly TexlFunction TypedParseJSON = _library.Add(new TypedParseJSONFunction());
public static readonly TexlFunction UniChar = _library.Add(new UniCharFunction());
public static readonly TexlFunction UniCharT = _library.Add(new UniCharTFunction());
public static readonly TexlFunction Upper = _library.Add(new LowerUpperFunction(isLower: false));
Expand Down Expand Up @@ -260,10 +263,7 @@ internal class BuiltinFunctionsCore
public static readonly TexlFunction UTCToday = _featureGateFunctions.Add(new UTCTodayFunction());
public static readonly TexlFunction BooleanL = _featureGateFunctions.Add(new BooleanLFunction());
public static readonly TexlFunction BooleanL_T = _featureGateFunctions.Add(new BooleanLFunction_T());
public static readonly TexlFunction Summarize = _featureGateFunctions.Add(new SummarizeFunction());
public static readonly TexlFunction AsType_UO = _featureGateFunctions.Add(new AsTypeFunction_UO());
public static readonly TexlFunction IsType_UO = _featureGateFunctions.Add(new IsTypeFunction_UO());
public static readonly TexlFunction TypedParseJSON = _featureGateFunctions.Add(new TypedParseJSONFunction());
public static readonly TexlFunction Summarize = _featureGateFunctions.Add(new SummarizeFunction());

// Slow API, only use for backward compatibility
#pragma warning disable CS0618 // Type or member is obsolete
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,10 @@ internal class LanguageConstants
/// The string value representing the JSON format.
/// </summary>
internal const string JSONFormatEnumString = "JSONFormat";

/// <summary>
/// The string value representing the Type literal keyword.
/// </summary>
public const string TypeLiteralInvariantName = "Type";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -397,13 +397,12 @@ public void AddUserDefinitions(string script, CultureInfo parseCulture = null, A
var options = new ParserOptions()
{
AllowsSideEffects = false,
AllowParseAsTypeLiteral = true,
Culture = parseCulture ?? CultureInfo.InvariantCulture
};

var sb = new StringBuilder();

var checkResult = new DefinitionsCheckResult()
var checkResult = new DefinitionsCheckResult(this.Config.Features)
.SetText(script, options);

var parseResult = checkResult.ApplyParse();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,10 @@ private bool ResolveBody(ModulePoco poco, SymbolTable moduleExports, ReadOnlySym
bool allowSideEffects = true;
var options = new ParserOptions
{
AllowParseAsTypeLiteral = true,
AllowsSideEffects = true
};

var parseResult = UserDefinitions.Parse(str, options);
var parseResult = UserDefinitions.Parse(str, options, Features.PowerFxV1);

var fragmentLocation = poco.Formulas.Location;

Expand Down
2 changes: 1 addition & 1 deletion src/libraries/Microsoft.PowerFx.Repl/Repl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ await this.Output.WriteLineAsync($"Error: Can't set '{name}' to a Void value.",
var errors = check.ApplyErrors();
if (!check.IsSuccess)
{
var definitionsCheckResult = new DefinitionsCheckResult();
var definitionsCheckResult = new DefinitionsCheckResult(this.Engine.Config.Features);

definitionsCheckResult.SetText(expression, this.ParserOptions)
.ApplyParseErrors();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#SETUP: AllowTypeLiteral, TimeZoneInfo("Pacific Standard Time")

// Primitives
>> AsType(ParseJSON("987654321"), Number)
987654321
Expand All @@ -25,21 +23,6 @@ true
>> AsType(ParseJSON("""1984-01-01"""), Date)
Date(1984,1,1)

>> AsType(ParseJSON("""1900-12-31T23:59:59.999Z"""), DateTime)
DateTime(1900,12,31,15,59,59,999)

>> AsType(ParseJSON("""1900-12-31T23:59:59.999"""), DateTime)
DateTime(1900,12,31,23,59,59,999)

adithyaselv marked this conversation as resolved.
Show resolved Hide resolved
>> AsType(ParseJSON("""1900-12-31T23:59:59.999+00:00"""), DateTime)
DateTime(1900,12,31,15,59,59,999)

>> AsType(ParseJSON("""1900-12-31T23:59:59.999-08:00"""), DateTime)
DateTime(1900,12,31,23,59,59,999)

>> AsType(ParseJSON("""1900-12-31"""), DateTime)
DateTime(1900,12,31,0,0,0,0)

>> AsType(ParseJSON("""1900-12-31T23:59:59.999"""), Date)
Date(1900,12,31)

Expand All @@ -64,12 +47,6 @@ DateTime(1900,12,31,0,0,0,0)
>> AsType(ParseJSON("""1900-12-31T00:00:00.000-08:00"""), DateTimeTZInd)
DateTime(1900,12,31,8,0,0,0)

>> DateTimeValue(AsType(ParseJSON("""1900-12-31T00:00:00.000Z"""), UntypedObject))
DateTime(1900,12,30,16,0,0,0)

>> DateValue(AsType(ParseJSON("""1900-12-31T00:00:00.000Z"""), UntypedObject))
Date(1900,12,30)

>> Value(AsType(ParseJSON("42"), UntypedObject))
42

Expand Down Expand Up @@ -111,9 +88,6 @@ Error({Kind:ErrorKind.InvalidArgument})
>> AsType(ParseJSON("""42"""), Number)
Error({Kind:ErrorKind.InvalidArgument})

>> AsType(ParseJSON("""1900-12-31T24:59:59.1002Z"""), DateTime)
Error({Kind:ErrorKind.InvalidArgument})

>> AsType(ParseJSON("""24:59:59.12345678"""), Time)
Error({Kind:ErrorKind.InvalidArgument})

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#SETUP: TimeZoneInfo("Pacific Standard Time")

>> AsType(ParseJSON("""1900-12-31T23:59:59.999Z"""), DateTime)
DateTime(1900,12,31,15,59,59,999)

>> AsType(ParseJSON("""1900-12-31T23:59:59.999"""), DateTime)
DateTime(1900,12,31,23,59,59,999)
adithyaselv marked this conversation as resolved.
Show resolved Hide resolved

>> AsType(ParseJSON("""1900-12-31T23:59:59.999+00:00"""), DateTime)
DateTime(1900,12,31,15,59,59,999)

>> AsType(ParseJSON("""1900-12-31T23:59:59.999-08:00"""), DateTime)
DateTime(1900,12,31,23,59,59,999)

>> AsType(ParseJSON("""1900-12-31"""), DateTime)
DateTime(1900,12,31,0,0,0,0)
adithyaselv marked this conversation as resolved.
Show resolved Hide resolved

>> DateTimeValue(AsType(ParseJSON("""1900-12-31T00:00:00.000Z"""), UntypedObject))
DateTime(1900,12,30,16,0,0,0)

>> DateValue(AsType(ParseJSON("""1900-12-31T00:00:00.000Z"""), UntypedObject))
Date(1900,12,30)

>> AsType(ParseJSON("""1900-12-31T24:59:59.1002Z"""), DateTime)
Error({Kind:ErrorKind.InvalidArgument})
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#SETUP: AllowTypeLiteral, TimeZoneInfo("Pacific Standard Time")

// Primitives
>> IsType(ParseJSON("987654321"), Number)
true
Expand Down
Loading