-
Notifications
You must be signed in to change notification settings - Fork 354
Configurable named types for UDF/UDT and configurable Number type #3014
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
base: main
Are you sure you want to change the base?
Conversation
| } | ||
|
|
||
| [Obsolete("Use WithBuiltInNamedTypes instead and pass in the list of named types.")] | ||
| public static SymbolTable WithBPrimitiveTypes() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, fixed.
| /// Builtin Types supported by this engine. | ||
| /// </summary> | ||
| public ReadOnlySymbolTable PrimitiveTypes { get; protected internal set; } = ReadOnlySymbolTable.PrimitiveTypesTableInstance; | ||
| public ReadOnlySymbolTable BuiltInNamedTypes { get; protected internal set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this so that Engine constructor sets it after validating it is sound. So, init has been removed.
|
|
||
| if (!nameResolver.LookupType(returnTypeToken.Name, out var returnTypeFormulaType)) | ||
| // only the return type from a UDF can be a void type, it is not valid in other type contexts | ||
| if (returnTypeToken.Name == FormulaType.Void.Name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Things could go haywire if someone added Void as a type in the builtinnamedtypes or as a UDT, but there are checks to prevent both from happening now. Is there another way you think it could be done?
| { | ||
| errors.Add(new TexlError(arg.TypeIdent, DocumentErrorSeverity.Severe, TexlStrings.ErrUDF_InvalidParamType, arg.TypeIdent.Name)); | ||
| isParamCheckSuccessful = false; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we not need to block Void Type in parameter? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No because it shouldn't be in the built in types list. I have a check in the Engine constructor that Void is not present.
| { | ||
| } | ||
|
|
||
| public RecalcEngine(PowerFxConfig powerFxConfig, bool numberTypeIsFloat) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried this approach first and it quickly becomes a mess. The binder doesn't have information about the parser options that were used. This flag and line 91 below take care of it completely by modifying the symbol table up front. We needed to parameterize the symbol table anyway for the Engine since not every host has the same built in types, Number is just taking advantage of this.
| /// Builtin Types supported by this engine. | ||
| /// </summary> | ||
| public ReadOnlySymbolTable PrimitiveTypes { get; protected internal set; } = ReadOnlySymbolTable.PrimitiveTypesTableInstance; | ||
| public ReadOnlySymbolTable BuiltInNamedTypes { get; protected internal set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've changed this further so that only the Engine constructor can set it, and yes you are correct, it does not need to be init anymore.
| @@ -86,7 +86,7 @@ public Engine(PowerFxConfig powerFxConfig) | |||
| /// <summary> | |||
| /// Builtin Types supported by this engine. | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| return argIndex == 1; | ||
| } | ||
|
|
||
| // This list is effectively intersected with the BuiltInNamedTypes in the Engine, with outliers generating errors before this list is checked. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand the question exactly. We need DType.Number (Float) in this list so that if a host supports Float, then it would be OK as a supported JSON type. DType.Decimal is a good example where it exists in this list, but wouldn't work in Canvas today.
| /// Helper to create a symbol table with primitive types. | ||
| /// </summary> | ||
| /// <returns>SymbolTable with primitive types.</returns> | ||
| public static SymbolTable WithPrimitiveTypes() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's already a breaking change because it now requires an argument. I left the original in place and deprecated. I doubt anyone was calling this outside of these libraries.
| internal DType _type { get; private protected init; } | ||
| #pragma warning restore SA1300 // Element should begin with upper-case letter | ||
|
|
||
| public virtual DName Name { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved this to a separate static class with readonly members, I think this may make it higher performance. I do not want to use raw strings that are not type checked and a typo could be hard to find.
|
|
||
| // TODO: Add more future type names | ||
| private static readonly ISet<string> _restrictedTypeNames = new HashSet<string> { "Record", "Table", "Currency" }; | ||
| private static readonly ISet<string> _restrictedTypeNames = new HashSet<string> { "Record", "Table", "Currency", "Number", "Object", "Null", "None", "Blank", "Void", "Array", "Untyped", "UnTyped", "Int", "Integer" }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| "Email", "Phone", "Address", | ||
| "Language", "Locale", | ||
| "MultilineText", "MultiLineText", "TextArea", | ||
| "SingleineText", "SingleLineText", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, fixed.
|
✅ No public API change. |
Currently, there is a named types table in the Engine, that contains Number, Text, Boolean, etc. The problem is that there isn't one list of named types that are appropriate for consumers of the Engine: for example, Canvas doesn't support Decimal, while Dataverse formula columns support DateTimeNoTZ but nobody else does.
This change pulls the named types out and makes that something that needs to be setup in order to use UDF/UDTs. RecalcEngine does this automatically, with the correct types, for all consumers of the C# interpreter. Dataverse doesn't need to worry about it as UDF/UDTs aren't usable there. Canvas will require an update.
In addition, the Number type name needs to be added to the symbol table of the Engine as an alias for Float or Decimal as appropriate. Number should be setup with the default numeric type, allowing UDT/UDFs to be type agnostic for situations where it doesn't matter. Using Float or Decimal explicitly is always specific. Setting this up is in addition to passing the NumberIsFloat flag into the Parser. As Canvas doesn't have Decimal yet, and has only ever had Float, and it is the only product with UDT/UDFs today, this is not a breaking change.
Finally, the list of reserved type names has been updated, attempting a small amount of future proofing.