-
-
Notifications
You must be signed in to change notification settings - Fork 228
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
137 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,8 @@ | ||
namespace System.Linq.Dynamic.Core.Parser | ||
using System.Diagnostics.CodeAnalysis; | ||
|
||
namespace System.Linq.Dynamic.Core.Parser; | ||
|
||
interface IKeywordsHelper | ||
{ | ||
interface IKeywordsHelper | ||
{ | ||
bool TryGetValue(string name, out object type); | ||
} | ||
} | ||
bool TryGetValue(string name, [NotNullWhen(true)] out object? keyWordOrType); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,89 +1,126 @@ | ||
using System.Collections.Generic; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq.Dynamic.Core.Validation; | ||
using System.Linq.Expressions; | ||
|
||
namespace System.Linq.Dynamic.Core.Parser | ||
namespace System.Linq.Dynamic.Core.Parser; | ||
|
||
internal class KeywordsHelper : IKeywordsHelper | ||
{ | ||
internal class KeywordsHelper : IKeywordsHelper | ||
public const string KEYWORD_IT = "it"; | ||
public const string KEYWORD_PARENT = "parent"; | ||
public const string KEYWORD_ROOT = "root"; | ||
|
||
public const string SYMBOL_IT = "$"; | ||
public const string SYMBOL_PARENT = "^"; | ||
public const string SYMBOL_ROOT = "~"; | ||
|
||
public const string FUNCTION_IIF = "iif"; | ||
public const string FUNCTION_ISNULL = "isnull"; | ||
public const string FUNCTION_NEW = "new"; | ||
public const string FUNCTION_NULLPROPAGATION = "np"; | ||
public const string FUNCTION_IS = "is"; | ||
public const string FUNCTION_AS = "as"; | ||
public const string FUNCTION_CAST = "cast"; | ||
|
||
private readonly ParsingConfig _config; | ||
|
||
// Keywords are IgnoreCase | ||
private readonly Dictionary<string, object> _keywordMapping = new(StringComparer.OrdinalIgnoreCase) | ||
{ | ||
public const string SYMBOL_IT = "$"; | ||
public const string SYMBOL_PARENT = "^"; | ||
public const string SYMBOL_ROOT = "~"; | ||
|
||
public const string KEYWORD_IT = "it"; | ||
public const string KEYWORD_PARENT = "parent"; | ||
public const string KEYWORD_ROOT = "root"; | ||
|
||
public const string FUNCTION_IIF = "iif"; | ||
public const string FUNCTION_ISNULL = "isnull"; | ||
public const string FUNCTION_NEW = "new"; | ||
public const string FUNCTION_NULLPROPAGATION = "np"; | ||
public const string FUNCTION_IS = "is"; | ||
public const string FUNCTION_AS = "as"; | ||
public const string FUNCTION_CAST = "cast"; | ||
|
||
private readonly IDictionary<string, object> _keywords = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) | ||
{ | ||
{ "true", Expression.Constant(true) }, | ||
{ "false", Expression.Constant(false) }, | ||
{ "null", Expression.Constant(null) } | ||
}; | ||
{ "true", Expression.Constant(true) }, | ||
{ "false", Expression.Constant(false) }, | ||
{ "null", Expression.Constant(null) }, | ||
|
||
{ SYMBOL_IT, SYMBOL_IT }, | ||
{ SYMBOL_PARENT, SYMBOL_PARENT }, | ||
{ SYMBOL_ROOT, SYMBOL_ROOT }, | ||
|
||
{ FUNCTION_IIF, FUNCTION_IIF }, | ||
{ FUNCTION_ISNULL, FUNCTION_ISNULL }, | ||
{ FUNCTION_NEW, FUNCTION_NEW }, | ||
{ FUNCTION_NULLPROPAGATION, FUNCTION_NULLPROPAGATION }, | ||
{ FUNCTION_IS, FUNCTION_IS }, | ||
{ FUNCTION_AS, FUNCTION_AS }, | ||
{ FUNCTION_CAST, FUNCTION_CAST } | ||
}; | ||
|
||
// PreDefined Types are not IgnoreCase | ||
private static readonly Dictionary<string, object> _preDefinedTypeMapping = new(); | ||
|
||
// Custom DefinedTypes are not IgnoreCase | ||
private readonly Dictionary<string, object> _customTypeMapping = new(); | ||
|
||
public KeywordsHelper(ParsingConfig config) | ||
static KeywordsHelper() | ||
{ | ||
foreach (var type in PredefinedTypesHelper.PredefinedTypes.OrderBy(kvp => kvp.Value).Select(kvp => kvp.Key)) | ||
{ | ||
if (config.AreContextKeywordsEnabled) | ||
{ | ||
_keywords.Add(KEYWORD_IT, KEYWORD_IT); | ||
_keywords.Add(KEYWORD_PARENT, KEYWORD_PARENT); | ||
_keywords.Add(KEYWORD_ROOT, KEYWORD_ROOT); | ||
} | ||
_preDefinedTypeMapping[type.FullName!] = type; | ||
_preDefinedTypeMapping[type.Name] = type; | ||
} | ||
} | ||
|
||
_keywords.Add(SYMBOL_IT, SYMBOL_IT); | ||
_keywords.Add(SYMBOL_PARENT, SYMBOL_PARENT); | ||
_keywords.Add(SYMBOL_ROOT, SYMBOL_ROOT); | ||
public KeywordsHelper(ParsingConfig config) | ||
{ | ||
_config = Check.NotNull(config); | ||
|
||
_keywords.Add(FUNCTION_IIF, FUNCTION_IIF); | ||
_keywords.Add(FUNCTION_ISNULL, FUNCTION_ISNULL); | ||
_keywords.Add(FUNCTION_NEW, FUNCTION_NEW); | ||
_keywords.Add(FUNCTION_NULLPROPAGATION, FUNCTION_NULLPROPAGATION); | ||
_keywords.Add(FUNCTION_IS, FUNCTION_IS); | ||
_keywords.Add(FUNCTION_AS, FUNCTION_AS); | ||
_keywords.Add(FUNCTION_CAST, FUNCTION_CAST); | ||
if (config.AreContextKeywordsEnabled) | ||
{ | ||
_keywordMapping.Add(KEYWORD_IT, KEYWORD_IT); | ||
_keywordMapping.Add(KEYWORD_PARENT, KEYWORD_PARENT); | ||
_keywordMapping.Add(KEYWORD_ROOT, KEYWORD_ROOT); | ||
} | ||
|
||
foreach (Type type in PredefinedTypesHelper.PredefinedTypes.OrderBy(kvp => kvp.Value).Select(kvp => kvp.Key)) | ||
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract | ||
if (config.CustomTypeProvider != null) | ||
{ | ||
foreach (var type in config.CustomTypeProvider.GetCustomTypes()) | ||
{ | ||
if (!string.IsNullOrEmpty(type.FullName)) | ||
{ | ||
_keywords[type.FullName] = type; | ||
} | ||
_keywords[type.Name] = type; | ||
_customTypeMapping[type.FullName!] = type; | ||
_customTypeMapping[type.Name] = type; | ||
} | ||
} | ||
} | ||
|
||
foreach (var pair in PredefinedTypesHelper.PredefinedTypesShorthands) | ||
{ | ||
_keywords.Add(pair.Key, pair.Value); | ||
} | ||
public bool TryGetValue(string name, [NotNullWhen(true)] out object? keyWordOrType) | ||
{ | ||
// 1. Try to get as keyword | ||
if (_keywordMapping.TryGetValue(name, out var keyWord)) | ||
{ | ||
keyWordOrType = keyWord; | ||
return true; | ||
} | ||
|
||
if (config.SupportEnumerationsFromSystemNamespace) | ||
{ | ||
foreach (var pair in EnumerationsFromMscorlib.PredefinedEnumerationTypes) | ||
{ | ||
_keywords.Add(pair.Key, pair.Value); | ||
} | ||
} | ||
// 2. Try to get as predefined shorttype ("bool", "char", ...) | ||
if (PredefinedTypesHelper.PredefinedTypesShorthands.TryGetValue(name, out var predefinedShortHandType)) | ||
{ | ||
keyWordOrType = predefinedShortHandType; | ||
return true; | ||
} | ||
|
||
if (config.CustomTypeProvider != null) | ||
{ | ||
foreach (Type type in config.CustomTypeProvider.GetCustomTypes()) | ||
{ | ||
_keywords[type.FullName] = type; | ||
_keywords[type.Name] = type; | ||
} | ||
} | ||
// 3. Try to get as predefined type ("Boolean", "System.Boolean", ..., "DateTime", "System.DateTime", ...) | ||
if (_preDefinedTypeMapping.TryGetValue(name, out var predefinedType)) | ||
{ | ||
keyWordOrType = predefinedType; | ||
return true; | ||
} | ||
|
||
public bool TryGetValue(string name, out object type) | ||
// 4. Try to get as an enum from the system namespace | ||
if (_config.SupportEnumerationsFromSystemNamespace && EnumerationsFromMscorlib.PredefinedEnumerationTypes.TryGetValue(name, out var predefinedEnumType)) | ||
{ | ||
return _keywords.TryGetValue(name, out type); | ||
keyWordOrType = predefinedEnumType; | ||
return true; | ||
} | ||
|
||
// 5. Try to get as custom type | ||
if (_customTypeMapping.TryGetValue(name, out var customType)) | ||
{ | ||
keyWordOrType = customType; | ||
return true; | ||
} | ||
|
||
// 6. Not found, return false | ||
keyWordOrType = null; | ||
return false; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters