Decompiled source of FullHealth v1.8.0
Expressive.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Expressive.Exceptions; using Expressive.Expressions; using Expressive.Expressions.Binary.Additive; using Expressive.Expressions.Binary.Bitwise; using Expressive.Expressions.Binary.Conditional; using Expressive.Expressions.Binary.Logical; using Expressive.Expressions.Binary.Multiplicative; using Expressive.Expressions.Binary.Relational; using Expressive.Expressions.Unary.Additive; using Expressive.Expressions.Unary.Logical; using Expressive.Functions; using Expressive.Functions.Conversion; using Expressive.Functions.Date; using Expressive.Functions.Logical; using Expressive.Functions.Mathematical; using Expressive.Functions.Relational; using Expressive.Functions.Statistical; using Expressive.Functions.String; using Expressive.Helpers; using Expressive.Operators; using Expressive.Operators.Additive; using Expressive.Operators.Bitwise; using Expressive.Operators.Conditional; using Expressive.Operators.Grouping; using Expressive.Operators.Logical; using Expressive.Operators.Multiplicative; using Expressive.Operators.Relational; using Expressive.Tokenisation; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: ComVisible(false)] [assembly: Guid("9571bbbb-3f9b-4f46-8a5c-efd5ec13e5b1")] [assembly: InternalsVisibleTo("Expressive.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100495afe3ea98fd0459102e8a5201f106f053d747699b49c82f4c08ae2617ffd1ab133db21560cc1d1721e7f193c472fbbe6d576b4a7690a897083fa55ae41c3d105f36c2063699095eba800c78448af0796cfd6e887308a2bf5a745aeb595450b5aa96c9f5d511d61d9f5c67faa6aa8e56bb6ddd641c38abcec30b714e89f2bbc")] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Shaun Lawrence")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright(c) 2024 Shaun Lawrence")] [assembly: AssemblyDescription("A multi-platform expression parsing and evaluating framework.")] [assembly: AssemblyFileVersion("3.0.1")] [assembly: AssemblyInformationalVersion("3.0.1+61facd21ddd63b11b5f7b27d2323b6b361fd2626")] [assembly: AssemblyProduct("Expressive Parser")] [assembly: AssemblyTitle("Expressive")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/bijington/expressive")] [assembly: AssemblyVersion("3.0.1.0")] namespace Expressive { public class Context { internal const char DateSeparator = '#'; internal const char ParameterSeparator = ','; private readonly IDictionary<string, Func<IExpression[], IDictionary<string, object>, object>> registeredFunctions; private readonly IDictionary<string, IOperator> registeredOperators; internal ExpressiveOptions Options { get; } internal CultureInfo CurrentCulture { get; } internal CultureInfo DecimalCurrentCulture { get; } internal char DecimalSeparator { get; } public IEnumerable<IFunctionMetadata> RegisteredFunctions => registeredFunctions.Values.OfType<IFunctionMetadata>(); public IEnumerable<IOperatorMetadata> RegisteredOperators => registeredFunctions.Values.OfType<IOperatorMetadata>(); internal IEnumerable<string> FunctionNames => registeredFunctions.Keys.OrderByDescending((string k) => k.Length); internal IEnumerable<string> OperatorNames => registeredOperators.Keys.OrderByDescending((string k) => k.Length); private bool IsCaseInsensitiveEqualityEnabled { get { if (!Options.HasFlag(ExpressiveOptions.IgnoreCase)) { return Options.HasFlag(ExpressiveOptions.IgnoreCaseForEquality); } return true; } } internal StringComparison EqualityStringComparison { get { if (!IsCaseInsensitiveEqualityEnabled) { return StringComparison.Ordinal; } return StringComparison.OrdinalIgnoreCase; } } internal bool IsCaseInsensitiveParsingEnabled { get { if (!Options.HasFlag(ExpressiveOptions.IgnoreCase)) { return Options.HasFlag(ExpressiveOptions.IgnoreCaseForParsing); } return true; } } internal IEqualityComparer<string> ParsingStringComparer { get { if (!IsCaseInsensitiveParsingEnabled) { return EqualityComparer<string>.Default; } return StringComparer.OrdinalIgnoreCase; } } internal StringComparison ParsingStringComparison { get { if (!IsCaseInsensitiveParsingEnabled) { return StringComparison.Ordinal; } return StringComparison.OrdinalIgnoreCase; } } public Context(ExpressiveOptions options) : this(options, CultureInfo.CurrentCulture, CultureInfo.InvariantCulture) { } public Context(ExpressiveOptions options, CultureInfo mainCurrentCulture, CultureInfo decimalCurrentCulture) { Options = options; CurrentCulture = mainCurrentCulture ?? throw new ArgumentNullException("mainCurrentCulture"); DecimalCurrentCulture = decimalCurrentCulture ?? throw new ArgumentNullException("decimalCurrentCulture"); DecimalSeparator = Convert.ToChar(DecimalCurrentCulture.NumberFormat.NumberDecimalSeparator, DecimalCurrentCulture); registeredFunctions = new Dictionary<string, Func<IExpression[], IDictionary<string, object>, object>>(ParsingStringComparer); registeredOperators = new Dictionary<string, IOperator>(ParsingStringComparer); RegisterOperator(new PlusOperator()); RegisterOperator(new SubtractOperator()); RegisterOperator(new BitwiseAndOperator()); RegisterOperator(new BitwiseOrOperator()); RegisterOperator(new BitwiseExclusiveOrOperator()); RegisterOperator(new LeftShiftOperator()); RegisterOperator(new RightShiftOperator()); RegisterOperator(new NullCoalescingOperator()); RegisterOperator(new ParenthesisCloseOperator()); RegisterOperator(new ParenthesisOpenOperator()); RegisterOperator(new AndOperator()); RegisterOperator(new NotOperator()); RegisterOperator(new OrOperator()); RegisterOperator(new DivideOperator()); RegisterOperator(new ModulusOperator()); RegisterOperator(new MultiplyOperator()); RegisterOperator(new EqualOperator()); RegisterOperator(new GreaterThanOperator()); RegisterOperator(new GreaterThanOrEqualOperator()); RegisterOperator(new LessThanOperator()); RegisterOperator(new LessThanOrEqualOperator()); RegisterOperator(new NotEqualOperator()); RegisterFunction(new DateFunction()); RegisterFunction(new DecimalFunction()); RegisterFunction(new DoubleFunction()); RegisterFunction(new IntegerFunction()); RegisterFunction(new LongFunction()); RegisterFunction(new StringFunction()); RegisterFunction(new AddDaysFunction()); RegisterFunction(new AddHoursFunction()); RegisterFunction(new AddMillisecondsFunction()); RegisterFunction(new AddMinutesFunction()); RegisterFunction(new AddMonthsFunction()); RegisterFunction(new AddSecondsFunction()); RegisterFunction(new AddYearsFunction()); RegisterFunction(new DayOfFunction()); RegisterFunction(new DaysBetweenFunction()); RegisterFunction(new HourOfFunction()); RegisterFunction(new HoursBetweenFunction()); RegisterFunction(new MillisecondOfFunction()); RegisterFunction(new MillisecondsBetweenFunction()); RegisterFunction(new MinuteOfFunction()); RegisterFunction(new MinutesBetweenFunction()); RegisterFunction(new MonthOfFunction()); RegisterFunction(new SecondOfFunction()); RegisterFunction(new SecondsBetweenFunction()); RegisterFunction(new YearOfFunction()); RegisterFunction(new AbsFunction()); RegisterFunction(new AcosFunction()); RegisterFunction(new AsinFunction()); RegisterFunction(new AtanFunction()); RegisterFunction(new CeilingFunction()); RegisterFunction(new CosFunction()); RegisterFunction(new CountFunction()); RegisterFunction(new ExpFunction()); RegisterFunction(new FloorFunction()); RegisterFunction(new IEEERemainderFunction()); RegisterFunction(new Log10Function()); RegisterFunction(new LogFunction()); RegisterFunction(new PowFunction()); RegisterFunction(new RandomFunction()); RegisterFunction(new RoundFunction()); RegisterFunction(new SignFunction()); RegisterFunction(new SinFunction()); RegisterFunction(new SqrtFunction()); RegisterFunction(new SumFunction()); RegisterFunction(new TanFunction()); RegisterFunction(new TruncateFunction()); RegisterFunction(new EFunction()); RegisterFunction(new PIFunction()); RegisterFunction(new IfFunction()); RegisterFunction(new InFunction()); RegisterFunction(new MaxFunction()); RegisterFunction(new MinFunction()); RegisterFunction(new AverageFunction()); RegisterFunction(new MeanFunction()); RegisterFunction(new MedianFunction()); RegisterFunction(new ModeFunction()); RegisterFunction(new ContainsFunction()); RegisterFunction(new EndsWithFunction()); RegisterFunction(new LengthFunction()); RegisterFunction(new PadLeftFunction()); RegisterFunction(new PadRightFunction()); RegisterFunction(new RegexFunction()); RegisterFunction(new StartsWithFunction()); RegisterFunction(new SubstringFunction()); RegisterFunction(new ConcatFunction()); RegisterFunction(new IndexOfFunction()); } public void RegisterFunction(string functionName, Func<IExpression[], IDictionary<string, object>, object> function, bool force = false) { CheckForExistingFunctionName(functionName, force); registeredFunctions[functionName] = function; } public void RegisterFunction(IFunction function, bool force = false) { if (function == null) { throw new ArgumentNullException("function"); } RegisterFunction(function.Name, delegate(IExpression[] p, IDictionary<string, object> a) { function.Variables = a; return function.Evaluate(p, this); }, force); } public void RegisterOperator(IOperator op, bool force = false) { if (op == null) { throw new ArgumentNullException("op"); } foreach (string tag in op.Tags) { if (!force && registeredOperators.ContainsKey(tag)) { throw new OperatorNameAlreadyRegisteredException(tag); } registeredOperators[tag] = op; } } public void UnregisterFunction(string functionName) { registeredFunctions.ContainsKey(functionName); registeredFunctions.Remove(functionName); } public void UnregisterOperator(string tag) { registeredOperators.ContainsKey(tag); registeredOperators.Remove(tag); } internal bool TryGetFunction(string functionName, out Func<IExpression[], IDictionary<string, object>, object> value) { return registeredFunctions.TryGetValue(functionName, out value); } internal bool TryGetOperator(string operatorName, out IOperator value) { return registeredOperators.TryGetValue(operatorName, out value); } private void CheckForExistingFunctionName(string functionName, bool force) { if (!force && registeredFunctions.ContainsKey(functionName)) { throw new FunctionNameAlreadyRegisteredException(functionName); } } } public sealed class Expression : IExpression { private IExpression compiledExpression; private readonly Context context; private readonly string originalExpression; private readonly ExpressionParser parser; private string[] referencedVariables; public IReadOnlyCollection<string> ReferencedVariables { get { CompileExpression(); return (IReadOnlyCollection<string>)(object)referencedVariables; } } public IEnumerable<IFunctionMetadata> RegisteredFunctions => context.RegisteredFunctions; public IEnumerable<IOperatorMetadata> RegisteredOperators => context.RegisteredOperators; public Expression(string expression, ExpressiveOptions options = ExpressiveOptions.None) : this(expression, new Context(options)) { } public Expression(string expression, Context context) { originalExpression = expression; this.context = context ?? throw new ArgumentNullException("context"); parser = new ExpressionParser(this.context); } public object Evaluate(IDictionary<string, object> variables = null) { try { CompileExpression(); return compiledExpression?.Evaluate(ApplyStringComparerSettings(variables, context.ParsingStringComparer)); } catch (Exception innerException) { throw new ExpressiveException(innerException); } } public T Evaluate<T>(IDictionary<string, object> variables = null) { try { return (T)Evaluate(variables); } catch (ExpressiveException) { throw; } catch (Exception innerException) { throw new ExpressiveException(innerException); } } public object Evaluate(IVariableProvider variableProvider) { if (variableProvider == null) { throw new ArgumentNullException("variableProvider"); } return Evaluate(new VariableProviderDictionary(variableProvider)); } public T Evaluate<T>(IVariableProvider variableProvider) { if (variableProvider == null) { throw new ArgumentNullException("variableProvider"); } try { return (T)Evaluate(variableProvider); } catch (ExpressiveException) { throw; } catch (Exception innerException) { throw new ExpressiveException(innerException); } } public void EvaluateAsync(Action<string, object> callback, IDictionary<string, object> variables = null) { this.EvaluateAsync<object>(callback, variables); } public void EvaluateAsync<T>(Action<string, T> callback, IDictionary<string, object> variables = null) { if (callback == null) { throw new ArgumentNullException("callback"); } Task.Run(delegate { T arg = default(T); string arg2 = null; try { arg = Evaluate<T>(variables); } catch (ExpressiveException ex) { arg2 = ex.Message; } callback(arg2, arg); }); } public void RegisterFunction(string functionName, Func<IExpression[], IDictionary<string, object>, object> function) { context.RegisterFunction(functionName, function); } public void RegisterFunction(IFunction function) { context.RegisterFunction(function); } public void RegisterOperator(IOperator op, bool force = false) { context.RegisterOperator(op, force); } public void UnregisterFunction(string functionName) { context.UnregisterFunction(functionName); } public void UnregisterOperator(string tag) { context.UnregisterOperator(tag); } private void CompileExpression() { if (compiledExpression == null || context.Options.HasFlag(ExpressiveOptions.NoCache)) { List<string> list = new List<string>(); compiledExpression = parser.CompileExpression(originalExpression, list); referencedVariables = list.ToArray(); } } private static IDictionary<string, object> ApplyStringComparerSettings(IDictionary<string, object> variables, IEqualityComparer<string> desiredStringComparer) { if (variables != null) { if (!(variables is Dictionary<string, object> dictionary)) { if (variables is VariableProviderDictionary) { return variables; } } else if (dictionary.Comparer.Equals(desiredStringComparer)) { return dictionary; } return new Dictionary<string, object>(variables, desiredStringComparer); } return null; } } internal sealed class ExpressionParser { private readonly Context context; private readonly Tokeniser tokeniser; internal ExpressionParser(Context context) { this.context = context; tokeniser = new Tokeniser(this.context, new List<ITokenExtractor> { new KeywordTokenExtractor(this.context.FunctionNames), new KeywordTokenExtractor(this.context.OperatorNames), new ParenthesisedTokenExtractor('[', ']'), new NumericTokenExtractor(), new ParenthesisedTokenExtractor('#'), new ValueTokenExtractor(","), new ParenthesisedTokenExtractor('"'), new ParenthesisedTokenExtractor('\''), new ValueTokenExtractor("true"), new ValueTokenExtractor("TRUE"), new ValueTokenExtractor("false"), new ValueTokenExtractor("FALSE"), new ValueTokenExtractor("null"), new ValueTokenExtractor("NULL") }); } internal IExpression CompileExpression(string expression, IList<string> variables) { if (string.IsNullOrWhiteSpace(expression)) { throw new ExpressiveException("An Expression cannot be empty."); } IList<Token> list = tokeniser.Tokenise(expression); int num = list.Select((Token t) => t.CurrentToken).Count((string t) => string.Equals(t, "(", StringComparison.Ordinal)); int num2 = list.Select((Token t) => t.CurrentToken).Count((string t) => string.Equals(t, ")", StringComparison.Ordinal)); if (num > num2) { throw new ArgumentException("There aren't enough ')' symbols. Expected " + num + " but there is only " + num2); } if (num < num2) { throw new ArgumentException("There are too many ')' symbols. Expected " + num + " but there is " + num2); } return CompileExpression(new Queue<Token>(list), OperatorPrecedence.Minimum, variables, isWithinFunction: false); } private IExpression CompileExpression(Queue<Token> tokens, OperatorPrecedence minimumPrecedence, IList<string> variables, bool isWithinFunction) { if (tokens == null) { throw new ArgumentNullException("tokens", "You must call Tokenise before compiling"); } IExpression expression = null; Token token = tokens.PeekOrDefault(); Token previousToken = null; while (token != null) { Func<IExpression[], IDictionary<string, object>, object> value2; if (context.TryGetOperator(token.CurrentToken, out var value)) { OperatorPrecedence precedence = value.GetPrecedence(previousToken); if (precedence <= minimumPrecedence) { break; } tokens.Dequeue(); if (!value.CanGetCaptiveTokens(previousToken, token, tokens)) { value.GetCaptiveTokens(previousToken, token, tokens); break; } IExpression expression2 = null; Token[] captiveTokens = value.GetCaptiveTokens(previousToken, token, tokens); if (captiveTokens.Length > 1) { Token[] innerCaptiveTokens = value.GetInnerCaptiveTokens(captiveTokens); expression2 = CompileExpression(new Queue<Token>(innerCaptiveTokens), OperatorPrecedence.Minimum, variables, isWithinFunction); token = captiveTokens[^1]; } else { expression2 = CompileExpression(tokens, precedence, variables, isWithinFunction); token = new Token(")", -1); } expression = value.BuildExpression(previousToken, new IExpression[2] { expression, expression2 }, context); } else if (context.TryGetFunction(token.CurrentToken, out value2)) { CheckForExistingParticipant(expression, token, isWithinFunction); List<IExpression> list = new List<IExpression>(); Queue<Token> queue = new Queue<Token>(); int num = 0; tokens.Dequeue(); while (tokens.Count > 0) { Token token2 = tokens.Dequeue(); if (string.Equals(token2.CurrentToken, "(", StringComparison.Ordinal)) { num++; } else if (string.Equals(token2.CurrentToken, ")", StringComparison.Ordinal)) { num--; } if ((num != 1 || !(token2.CurrentToken == "(")) && (num != 0 || !(token2.CurrentToken == ")"))) { queue.Enqueue(token2); } if (num == 0 && queue.Any()) { list.Add(CompileExpression(queue, OperatorPrecedence.Minimum, variables, isWithinFunction: true)); queue.Clear(); } else if (string.Equals(token2.CurrentToken, ','.ToString(), StringComparison.Ordinal) && num == 1) { list.Add(CompileExpression(queue, OperatorPrecedence.Minimum, variables, isWithinFunction: true)); queue.Clear(); } if (num <= 0) { break; } } expression = new FunctionExpression(token.CurrentToken, value2, list.ToArray()); } else if (token.CurrentToken.IsNumeric(context.DecimalCurrentCulture)) { CheckForExistingParticipant(expression, token, isWithinFunction); tokens.Dequeue(); decimal result2; double result3; float result4; long result5; if (int.TryParse(token.CurrentToken, NumberStyles.Any, context.DecimalCurrentCulture, out var result)) { expression = new ConstantValueExpression(result); } else if (decimal.TryParse(token.CurrentToken, NumberStyles.Any, context.DecimalCurrentCulture, out result2)) { expression = new ConstantValueExpression(result2); } else if (double.TryParse(token.CurrentToken, NumberStyles.Any, context.DecimalCurrentCulture, out result3)) { expression = new ConstantValueExpression(result3); } else if (float.TryParse(token.CurrentToken, NumberStyles.Any, context.DecimalCurrentCulture, out result4)) { expression = new ConstantValueExpression(result4); } else if (long.TryParse(token.CurrentToken, NumberStyles.Any, context.DecimalCurrentCulture, out result5)) { expression = new ConstantValueExpression(result5); } } else if (token.CurrentToken.StartsWith("[") && token.CurrentToken.EndsWith("]")) { CheckForExistingParticipant(expression, token, isWithinFunction); tokens.Dequeue(); string text = token.CurrentToken.Replace("[", "").Replace("]", ""); expression = new VariableExpression(text); if (!variables.Contains(text, context.ParsingStringComparer)) { variables.Add(text); } } else if (string.Equals(token.CurrentToken, "true", StringComparison.OrdinalIgnoreCase)) { CheckForExistingParticipant(expression, token, isWithinFunction); tokens.Dequeue(); expression = new ConstantValueExpression(true); } else if (string.Equals(token.CurrentToken, "false", StringComparison.OrdinalIgnoreCase)) { CheckForExistingParticipant(expression, token, isWithinFunction); tokens.Dequeue(); expression = new ConstantValueExpression(false); } else if (string.Equals(token.CurrentToken, "null", StringComparison.OrdinalIgnoreCase)) { CheckForExistingParticipant(expression, token, isWithinFunction); tokens.Dequeue(); expression = new ConstantValueExpression(null); } else if (token.CurrentToken.StartsWith('#'.ToString()) && token.CurrentToken.EndsWith('#'.ToString())) { CheckForExistingParticipant(expression, token, isWithinFunction); tokens.Dequeue(); string text2 = token.CurrentToken.Replace('#'.ToString(), ""); if (!DateTime.TryParse(text2, out var result6)) { if (string.Equals("TODAY", text2, StringComparison.OrdinalIgnoreCase)) { result6 = DateTime.Today; } else { if (!string.Equals("NOW", text2, StringComparison.OrdinalIgnoreCase)) { throw new UnrecognisedTokenException(text2); } result6 = DateTime.Now; } } expression = new ConstantValueExpression(result6); } else if ((token.CurrentToken.StartsWith("'") && token.CurrentToken.EndsWith("'")) || (token.CurrentToken.StartsWith("\"") && token.CurrentToken.EndsWith("\""))) { CheckForExistingParticipant(expression, token, isWithinFunction); tokens.Dequeue(); expression = new ConstantValueExpression(CleanString(token.CurrentToken.Substring(1, token.Length - 2))); } else { if (!string.Equals(token.CurrentToken, ','.ToString(), StringComparison.Ordinal)) { tokens.Dequeue(); throw new UnrecognisedTokenException(token.CurrentToken); } if (!isWithinFunction) { throw new ExpressiveException($"Unexpected token '{token}'"); } tokens.Dequeue(); } previousToken = token; token = tokens.PeekOrDefault(); } return expression; } private static string CleanString(string input) { if (input.Length <= 1) { return input; } char[] array = new char[input.Length]; int length = 0; for (int i = 0; i < input.Length; i++) { char c = input[i]; if (c == '\\' && i < input.Length - 1) { switch (input[i + 1]) { case 'n': array[length++] = '\n'; i++; continue; case 'r': array[length++] = '\r'; i++; continue; case 't': array[length++] = '\t'; i++; continue; case '\'': array[length++] = '\''; i++; continue; case '"': array[length++] = '"'; i++; continue; case '\\': array[length++] = '\\'; i++; continue; } } array[length++] = c; } return new string(array, 0, length); } private static void CheckForExistingParticipant(IExpression participant, Token token, bool isWithinFunction) { if (participant != null) { if (isWithinFunction) { throw new MissingTokenException("Missing token, expecting ','.", ','); } throw new ExpressiveException($"Unexpected token '{token.CurrentToken}' at index {token.StartIndex}"); } } } [Flags] public enum ExpressiveOptions { None = 1, [Obsolete("This will be removed in future versions with IgnoreCaseForParsing, IgnoreCaseForEquality and IgnoreCaseAll replacing it.")] IgnoreCase = 2, NoCache = 4, RoundAwayFromZero = 8, IgnoreCaseForParsing = 0x10, IgnoreCaseForEquality = 0x20, IgnoreCaseAll = 0x30, All = 0x3E } internal static class ExtensionMethods { internal static bool IsArithmeticOperator(this string source) { if (!string.Equals(source, "+", StringComparison.Ordinal) && !string.Equals(source, "-", StringComparison.Ordinal) && !string.Equals(source, "−", StringComparison.Ordinal) && !string.Equals(source, "/", StringComparison.Ordinal) && !string.Equals(source, "÷", StringComparison.Ordinal) && !string.Equals(source, "*", StringComparison.Ordinal) && !string.Equals(source, "×", StringComparison.Ordinal) && !string.Equals(source, "+", StringComparison.Ordinal) && !string.Equals(source, "+", StringComparison.Ordinal)) { return string.Equals(source, "+", StringComparison.Ordinal); } return true; } internal static bool IsNumeric(this string source, CultureInfo cultureInfo) { double result; return double.TryParse(source, NumberStyles.Any, cultureInfo, out result); } internal static T PeekOrDefault<T>(this Queue<T> queue) { if (queue.Count <= 0) { return default(T); } return queue.Peek(); } internal static string SubstringUpTo(this string source, int startIndex, char character) { if (startIndex != 0) { string text = source.Substring(startIndex); return text.Substring(0, text.IndexOf(character) + 1); } return source.Substring(startIndex, source.IndexOf(character) + 1); } } public interface IVariableProvider { bool TryGetValue(string variableName, out object value); } public sealed class Token { public string CurrentToken { get; } public int Length { get; } public int StartIndex { get; } public Token(string currentToken, int startIndex) { CurrentToken = currentToken; StartIndex = startIndex; Length = CurrentToken?.Length ?? 0; } } internal class VariableProviderDictionary : IDictionary<string, object>, ICollection<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>, IEnumerable { private readonly IVariableProvider variableProvider; public int Count => ThrowNotSupported<int>(); public bool IsReadOnly => ThrowNotSupported<bool>(); public object this[string key] { get { return ThrowNotSupported<object>(); } set { ThrowNotSupported<object>(); } } public ICollection<string> Keys => ThrowNotSupported<ICollection<string>>(); public ICollection<object> Values => ThrowNotSupported<ICollection<object>>(); public VariableProviderDictionary(IVariableProvider variableProvider) { this.variableProvider = variableProvider; } public bool TryGetValue(string key, out object value) { return variableProvider.TryGetValue(key, out value); } public IEnumerator<KeyValuePair<string, object>> GetEnumerator() { return ThrowNotSupported<IEnumerator<KeyValuePair<string, object>>>(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Add(KeyValuePair<string, object> item) { ThrowNotSupported<bool>(); } public void Clear() { ThrowNotSupported<bool>(); } public bool Contains(KeyValuePair<string, object> item) { return ThrowNotSupported<bool>(); } public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex) { ThrowNotSupported<bool>(); } public bool Remove(KeyValuePair<string, object> item) { return ThrowNotSupported<bool>(); } public void Add(string key, object value) { ThrowNotSupported<bool>(); } public bool ContainsKey(string key) { return ThrowNotSupported<bool>(); } public bool Remove(string key) { return ThrowNotSupported<bool>(); } private static TReturn ThrowNotSupported<TReturn>() { throw new NotSupportedException(); } } } namespace Expressive.Tokenisation { public interface ITokenExtractor { Token ExtractToken(string expression, int currentIndex, Context context); } internal class KeywordTokenExtractor : ITokenExtractor { private readonly IEnumerable<string> keywords; public KeywordTokenExtractor(IEnumerable<string> keywords) { if (keywords == null) { throw new ArgumentNullException("keywords"); } this.keywords = keywords; } public Token ExtractToken(string expression, int currentIndex, Context context) { int length = expression.Length; foreach (string keyword in keywords) { string text = expression.Substring(currentIndex, Math.Min(keyword.Length, length - currentIndex)); if (string.Equals(text, keyword, context.ParsingStringComparison)) { return new Token(text, currentIndex); } } return null; } } internal class NumericTokenExtractor : ITokenExtractor { private class Location { public int End { get; } public int Length => End - Start; public int Start { get; } public Location(int start, int end) { Start = start; End = end; } } public Token ExtractToken(string expression, int currentIndex, Context context) { if (!IsValidStart(expression[currentIndex], context, NumberStyles.Any)) { return null; } Location numberLocation = GetNumberLocation(expression, currentIndex, context, NumberStyles.Any); return new Token(expression.Substring(numberLocation.Start, numberLocation.Length), currentIndex); } private static Location GetNumberLocation(string expression, int startIndex, Context context, NumberStyles numberStyles) { int num = startIndex; char character = expression[num]; int length = expression.Length; Location exponentialLocation; while (IsAllowableCharacter(character, expression, num, startIndex, length, context, ref numberStyles, out exponentialLocation) && num < length) { if (exponentialLocation != null) { return new Location(startIndex, exponentialLocation.End); } num++; if (num == expression.Length) { break; } character = expression[num]; } return new Location(startIndex, num); } private static bool IsAllowableCharacter(char character, string expression, int index, int startIndex, int expressionLength, Context context, ref NumberStyles numberStyles, out Location exponentialLocation) { exponentialLocation = null; if (char.IsDigit(character) || (IsValidStart(character, context, numberStyles) && index == startIndex)) { return true; } if (!numberStyles.HasFlag(NumberStyles.AllowExponent)) { return false; } if ((character == 'e' || character == 'E') && char.IsDigit(expression[index - 1]) && index + 1 < expressionLength && IsValidStart(expression[index + 1], context, NumberStyles.Integer)) { exponentialLocation = GetNumberLocation(expression, index + 1, context, NumberStyles.Integer); if (exponentialLocation != null) { return true; } } if (numberStyles.HasFlag(NumberStyles.AllowDecimalPoint) && character == context.DecimalSeparator) { numberStyles &= ~NumberStyles.AllowDecimalPoint; return true; } return false; } private static bool IsSignCharacter(char character) { if (character != '-' && character != '−') { return character == '+'; } return true; } private static bool IsValidStart(char character, Context context, NumberStyles numberStyles) { if (!char.IsDigit(character) && (!numberStyles.HasFlag(NumberStyles.AllowLeadingSign) || !IsSignCharacter(character))) { if (numberStyles.HasFlag(NumberStyles.AllowDecimalPoint)) { return character == context.DecimalSeparator; } return false; } return true; } } internal class ParenthesisedTokenExtractor : ITokenExtractor { private readonly char endingCharacter; private readonly char startingCharacter; public ParenthesisedTokenExtractor(char singleCharacter) : this(singleCharacter, singleCharacter) { } public ParenthesisedTokenExtractor(char startingCharacter, char endingCharacter) { this.startingCharacter = startingCharacter; this.endingCharacter = endingCharacter; } public Token ExtractToken(string expression, int currentIndex, Context context) { if (expression[currentIndex] != startingCharacter) { return null; } string @string = GetString(expression, currentIndex, endingCharacter); if (string.IsNullOrWhiteSpace(@string)) { throw new MissingTokenException($"Missing closing token '{endingCharacter}'", endingCharacter); } return new Token(@string, currentIndex); } private static string GetString(string expression, int startIndex, char expectedEndingCharacter) { int num = startIndex; bool flag = false; char c = expression[num]; bool flag2 = false; while (num < expression.Length && !flag) { if (num != startIndex && c == expectedEndingCharacter && !flag2) { flag = true; } flag2 = ((c == '\\' && !flag2) ? true : false); num++; if (num == expression.Length) { break; } c = expression[num]; } if (!flag) { return null; } return expression.Substring(startIndex, num - startIndex); } } internal sealed class Tokeniser { private readonly Context context; private readonly IEnumerable<ITokenExtractor> tokenExtractors; public Tokeniser(Context context, IEnumerable<ITokenExtractor> tokenExtractors) { this.context = context; this.tokenExtractors = tokenExtractors; } internal IList<Token> Tokenise(string expression) { if (string.IsNullOrWhiteSpace(expression)) { return null; } int length = expression.Length; List<Token> list = new List<Token>(); IList<char> list2 = null; int index; Token token; for (index = 0; index < length; index += token?.Length ?? 1) { token = tokenExtractors.Select((ITokenExtractor t) => t.ExtractToken(expression, index, context)).FirstOrDefault((Token t) => t != null); if (token != null) { CheckForUnrecognised(list2, list, index); list2 = null; list.Add(token); continue; } char c = expression[index]; if (!char.IsWhiteSpace(c)) { if (list2 == null) { list2 = new List<char>(); } list2.Add(c); } else { CheckForUnrecognised(list2, list, index); list2 = null; } } CheckForUnrecognised(list2, list, index); return list; } private static void CheckForUnrecognised(IList<char> unrecognised, ICollection<Token> tokens, int index) { if (unrecognised != null) { string text = new string(unrecognised.ToArray()); tokens.Add(new Token(text, index - text.Length)); } } } internal class ValueTokenExtractor : ITokenExtractor { private readonly string value; public ValueTokenExtractor(string value) { this.value = value ?? throw new ArgumentNullException("value"); } public Token ExtractToken(string expression, int currentIndex, Context context) { char c = expression[currentIndex]; int length = expression.Length; char c2 = value.First(); if (string.Equals(c.ToString(), c2.ToString(), context.ParsingStringComparison) && CanExtractValue(expression, length, currentIndex, value, context)) { return new Token(value, currentIndex); } return null; } private static bool CanExtractValue(string expression, int expressionLength, int index, string expectedValue, Context context) { return string.Equals(expectedValue, ExtractValue(expression, expressionLength, index, expectedValue, context), context.ParsingStringComparison); } private static string ExtractValue(string expression, int expressionLength, int index, string expectedValue, Context context) { string result = null; int length = expectedValue.Length; if (expressionLength >= index + length) { string text = expression.Substring(index, length); bool flag = true; if (expressionLength > index + length) { flag = !char.IsLetterOrDigit(expression[index + length]) || string.Equals(expectedValue, ','.ToString(), context.ParsingStringComparison); } if (string.Equals(text, expectedValue, context.ParsingStringComparison) && flag) { result = text; } } return result; } } } namespace Expressive.Operators { public interface IOperator : IOperatorMetadata { IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context); bool CanGetCaptiveTokens(Token previousToken, Token token, Queue<Token> remainingTokens); Token[] GetCaptiveTokens(Token previousToken, Token token, Queue<Token> remainingTokens); Token[] GetInnerCaptiveTokens(Token[] allCaptiveTokens); OperatorPrecedence GetPrecedence(Token previousToken); } public interface IOperatorMetadata { IEnumerable<string> Tags { get; } } public abstract class OperatorBase : IOperator, IOperatorMetadata { public abstract IEnumerable<string> Tags { get; } public abstract IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context); public virtual bool CanGetCaptiveTokens(Token previousToken, Token token, Queue<Token> remainingTokens) { return true; } public virtual Token[] GetCaptiveTokens(Token previousToken, Token token, Queue<Token> remainingTokens) { return new Token[1] { token }; } public virtual Token[] GetInnerCaptiveTokens(Token[] allCaptiveTokens) { return new Token[0]; } public abstract OperatorPrecedence GetPrecedence(Token previousToken); } public enum OperatorPrecedence { Minimum, Or, And, Equal, NotEqual, LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual, Not, BitwiseOr, BitwiseXOr, BitwiseAnd, LeftShift, RightShift, Add, Subtract, Multiply, Modulus, Divide, NullCoalescing, UnaryPlus, UnaryMinus, ParenthesisOpen, ParenthesisClose } } namespace Expressive.Operators.Relational { internal class EqualOperator : OperatorBase { public override IEnumerable<string> Tags => new string[2] { "=", "==" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new EqualExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.Equal; } } internal class GreaterThanOperator : OperatorBase { public override IEnumerable<string> Tags => new string[1] { ">" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new GreaterThanExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.GreaterThan; } } internal class GreaterThanOrEqualOperator : OperatorBase { public override IEnumerable<string> Tags => new string[1] { ">=" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new GreaterThanOrEqualExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.GreaterThanOrEqual; } } internal class LessThanOperator : OperatorBase { public override IEnumerable<string> Tags => new string[1] { "<" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new LessThanExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.LessThan; } } internal class LessThanOrEqualOperator : OperatorBase { public override IEnumerable<string> Tags => new string[1] { "<=" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new LessThanOrEqualExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.LessThanOrEqual; } } internal class NotEqualOperator : OperatorBase { public override IEnumerable<string> Tags => new string[2] { "!=", "<>" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new NotEqualExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.NotEqual; } } } namespace Expressive.Operators.Multiplicative { internal class DivideOperator : OperatorBase { public override IEnumerable<string> Tags => new string[2] { "/", "÷" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new DivideExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.Divide; } } internal class ExponentOperator : OperatorBase { public override IEnumerable<string> Tags => new string[2] { "^", "‸" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new ExponentExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.Multiply; } } internal class ModulusOperator : OperatorBase { public override IEnumerable<string> Tags => new string[2] { "%", "mod" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new ModulusExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.Modulus; } } internal class MultiplyOperator : OperatorBase { public override IEnumerable<string> Tags => new string[2] { "*", "×" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new MultiplyExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.Multiply; } } } namespace Expressive.Operators.Logical { internal class AndOperator : OperatorBase { public override IEnumerable<string> Tags => new string[2] { "&&", "and" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new AndExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.And; } } internal class NotOperator : OperatorBase { public override IEnumerable<string> Tags => new string[2] { "!", "not" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new NotExpression(expressions[0] ?? expressions[1]); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.Not; } } internal class OrOperator : OperatorBase { public override IEnumerable<string> Tags => new string[2] { "||", "or" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new OrExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.Or; } } } namespace Expressive.Operators.Grouping { internal class ParenthesisCloseOperator : OperatorBase { public override IEnumerable<string> Tags => new string[1] { ")" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return expressions[0] ?? expressions[1]; } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.ParenthesisClose; } } internal class ParenthesisOpenOperator : IOperator, IOperatorMetadata { public IEnumerable<string> Tags => new string[1] { "(" }; public IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new ParenthesisedExpression(expressions[0] ?? expressions[1]); } public bool CanGetCaptiveTokens(Token previousToken, Token token, Queue<Token> remainingTokens) { Queue<Token> remainingTokens2 = new Queue<Token>(remainingTokens.ToArray()); return GetCaptiveTokens(previousToken, token, remainingTokens2).Any(); } public Token[] GetCaptiveTokens(Token previousToken, Token token, Queue<Token> remainingTokens) { IList<Token> list = new List<Token>(); list.Add(token); int num = 1; while (remainingTokens.Any()) { Token token2 = remainingTokens.Dequeue(); list.Add(token2); if (string.Equals(token2.CurrentToken, "(", StringComparison.Ordinal)) { num++; } else if (string.Equals(token2.CurrentToken, ")", StringComparison.Ordinal)) { num--; } if (num <= 0) { break; } } return list.ToArray(); } public Token[] GetInnerCaptiveTokens(Token[] allCaptiveTokens) { return allCaptiveTokens.Skip(1).Take(allCaptiveTokens.Length - 2).ToArray(); } public OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.ParenthesisOpen; } } } namespace Expressive.Operators.Conditional { internal class NullCoalescingOperator : OperatorBase { public override IEnumerable<string> Tags => new string[1] { "??" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new NullCoalescingExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.NullCoalescing; } } } namespace Expressive.Operators.Bitwise { internal class BitwiseAndOperator : OperatorBase { public override IEnumerable<string> Tags => new string[1] { "&" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new BitwiseAndExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.BitwiseAnd; } } internal class BitwiseExclusiveOrOperator : OperatorBase { public override IEnumerable<string> Tags => new string[1] { "^" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new BitwiseExclusiveOrExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.BitwiseXOr; } } internal class BitwiseOrOperator : OperatorBase { public override IEnumerable<string> Tags => new string[1] { "|" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new BitwiseOrExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.BitwiseOr; } } internal class LeftShiftOperator : OperatorBase { public override IEnumerable<string> Tags => new string[1] { "<<" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new LeftShiftExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.LeftShift; } } internal class RightShiftOperator : OperatorBase { public override IEnumerable<string> Tags => new string[1] { ">>" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { return new RightShiftExpression(expressions[0], expressions[1], context); } public override OperatorPrecedence GetPrecedence(Token previousToken) { return OperatorPrecedence.RightShift; } } } namespace Expressive.Operators.Additive { internal class PlusOperator : OperatorBase { public override IEnumerable<string> Tags => new string[1] { "+" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { if (IsUnary(previousToken)) { return new PlusExpression(expressions[0] ?? expressions[1]); } return new AddExpression(expressions[0], expressions[1], context); } public override bool CanGetCaptiveTokens(Token previousToken, Token token, Queue<Token> remainingTokens) { Queue<Token> remainingTokens2 = new Queue<Token>(remainingTokens.ToArray()); return GetCaptiveTokens(previousToken, token, remainingTokens2).Any(); } public override Token[] GetInnerCaptiveTokens(Token[] allCaptiveTokens) { return allCaptiveTokens.Skip(1).ToArray(); } public override OperatorPrecedence GetPrecedence(Token previousToken) { if (!IsUnary(previousToken)) { return OperatorPrecedence.Add; } return OperatorPrecedence.UnaryPlus; } private static bool IsUnary(Token previousToken) { if (!string.IsNullOrEmpty(previousToken?.CurrentToken) && !string.Equals(previousToken.CurrentToken, "(", StringComparison.Ordinal)) { return previousToken.CurrentToken.IsArithmeticOperator(); } return true; } } internal class SubtractOperator : OperatorBase { public override IEnumerable<string> Tags => new string[2] { "-", "−" }; public override IExpression BuildExpression(Token previousToken, IExpression[] expressions, Context context) { if (IsUnary(previousToken)) { return new MinusExpression(expressions[0] ?? expressions[1]); } return new SubtractExpression(expressions[0], expressions[1], context); } public override bool CanGetCaptiveTokens(Token previousToken, Token token, Queue<Token> remainingTokens) { Queue<Token> remainingTokens2 = new Queue<Token>(remainingTokens.ToArray()); return GetCaptiveTokens(previousToken, token, remainingTokens2).Any(); } public override Token[] GetInnerCaptiveTokens(Token[] allCaptiveTokens) { return allCaptiveTokens.Skip(1).ToArray(); } public override OperatorPrecedence GetPrecedence(Token previousToken) { if (!IsUnary(previousToken)) { return OperatorPrecedence.Subtract; } return OperatorPrecedence.UnaryMinus; } private static bool IsUnary(Token previousToken) { if (!string.IsNullOrEmpty(previousToken?.CurrentToken) && !string.Equals(previousToken.CurrentToken, "(", StringComparison.Ordinal)) { return previousToken.CurrentToken.IsArithmeticOperator(); } return true; } } } namespace Expressive.Helpers { public static class Comparison { private static readonly Type[] CommonTypes = new Type[7] { typeof(DateTime), typeof(decimal), typeof(double), typeof(long), typeof(int), typeof(bool), typeof(string) }; public static int CompareUsingMostPreciseType(object lhs, object rhs, Context context) { if (context == null) { throw new ArgumentNullException("context"); } Type mostPreciseType = GetMostPreciseType(lhs?.GetType(), rhs?.GetType()); if (mostPreciseType == typeof(string)) { return string.Compare((string)Convert.ChangeType(lhs, mostPreciseType, context.CurrentCulture), (string)Convert.ChangeType(rhs, mostPreciseType, context.CurrentCulture), context.EqualityStringComparison); } return Compare(lhs, rhs, mostPreciseType, context); } private static Type GetMostPreciseType(Type a, Type b) { if (a == b) { return a; } Type[] commonTypes = CommonTypes; foreach (Type type in commonTypes) { if (a == type || b == type) { return type; } } return a; } private static int Compare(object lhs, object rhs, Type mostPreciseType, Context context) { if (lhs == null && rhs == null) { return 0; } if (lhs == null) { return -1; } if (rhs == null) { return 1; } Type type = lhs.GetType(); Type type2 = rhs.GetType(); if (type == type2) { return Comparer<object>.Default.Compare(lhs, rhs); } try { if (type == mostPreciseType) { rhs = Convert.ChangeType(rhs, mostPreciseType, context.CurrentCulture); } else { lhs = Convert.ChangeType(lhs, mostPreciseType, context.CurrentCulture); } return Comparer<object>.Default.Compare(lhs, rhs); } catch (Exception) { } try { return Comparer<object>.Default.Compare(lhs, Convert.ChangeType(rhs, type, context.CurrentCulture)); } catch (Exception) { } try { return Comparer<object>.Default.Compare(lhs, Convert.ChangeType(rhs, type, context.CurrentCulture)); } catch (Exception) { } try { return string.Compare((string)Convert.ChangeType(lhs, typeof(string), context.CurrentCulture), (string)Convert.ChangeType(rhs, typeof(string), context.CurrentCulture), context.EqualityStringComparison); } catch (Exception) { } return 0; } } public static class Numbers { private static object ConvertIfString(object s) { if (s is string || s is char) { return decimal.Parse(s.ToString()); } return s; } public static object Add(object a, object b) { if (a == null || b == null) { return null; } a = ConvertIfString(a); b = ConvertIfString(b); if (a is double && double.IsNaN((double)a)) { return a; } if (b is double && double.IsNaN((double)b)) { return b; } TypeCode typeCode = TypeHelper.GetTypeCode(a); TypeCode typeCode2 = TypeHelper.GetTypeCode(b); switch (typeCode) { case TypeCode.Boolean: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'bool' and 'bool'"); case TypeCode.Byte: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.SByte: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.Int16: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.UInt16: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.Int32: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.UInt32: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.Int64: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.UInt64: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.Single: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.Double: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.Decimal: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'bool' and 'byte'"); } break; case TypeCode.Byte: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'byte' and 'bool'"); case TypeCode.Byte: return (byte)a + (byte)b; case TypeCode.SByte: return (byte)a + (sbyte)b; case TypeCode.Int16: return (byte)a + (short)b; case TypeCode.UInt16: return (byte)a + (ushort)b; case TypeCode.Int32: return (byte)a + (int)b; case TypeCode.UInt32: return (byte)a + (uint)b; case TypeCode.Int64: return (byte)a + (long)b; case TypeCode.UInt64: return (byte)a + (ulong)b; case TypeCode.Single: return (float)(int)(byte)a + (float)b; case TypeCode.Double: return (double)(int)(byte)a + (double)b; case TypeCode.Decimal: return (decimal)(byte)a + (decimal)b; } break; case TypeCode.SByte: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'sbyte' and 'bool'"); case TypeCode.Byte: return (sbyte)a + (byte)b; case TypeCode.SByte: return (sbyte)a + (sbyte)b; case TypeCode.Int16: return (sbyte)a + (short)b; case TypeCode.UInt16: return (sbyte)a + (ushort)b; case TypeCode.Int32: return (sbyte)a + (int)b; case TypeCode.UInt32: return (sbyte)a + (uint)b; case TypeCode.Int64: return (sbyte)a + (long)b; case TypeCode.UInt64: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'sbyte' and 'ulong'"); case TypeCode.Single: return (float)(sbyte)a + (float)b; case TypeCode.Double: return (double)(sbyte)a + (double)b; case TypeCode.Decimal: return (decimal)(sbyte)a + (decimal)b; } break; case TypeCode.Int16: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'short' and 'bool'"); case TypeCode.Byte: return (short)a + (byte)b; case TypeCode.SByte: return (short)a + (sbyte)b; case TypeCode.Int16: return (short)a + (short)b; case TypeCode.UInt16: return (short)a + (ushort)b; case TypeCode.Int32: return (short)a + (int)b; case TypeCode.UInt32: return (short)a + (uint)b; case TypeCode.Int64: return (short)a + (long)b; case TypeCode.UInt64: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'short' and 'ulong'"); case TypeCode.Single: return (float)(short)a + (float)b; case TypeCode.Double: return (double)(short)a + (double)b; case TypeCode.Decimal: return (decimal)(short)a + (decimal)b; } break; case TypeCode.UInt16: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'ushort' and 'bool'"); case TypeCode.Byte: return (ushort)a + (byte)b; case TypeCode.SByte: return (ushort)a + (sbyte)b; case TypeCode.Int16: return (ushort)a + (short)b; case TypeCode.UInt16: return (ushort)a + (ushort)b; case TypeCode.Int32: return (ushort)a + (int)b; case TypeCode.UInt32: return (ushort)a + (uint)b; case TypeCode.Int64: return (ushort)a + (long)b; case TypeCode.UInt64: return (ushort)a + (ulong)b; case TypeCode.Single: return (float)(int)(ushort)a + (float)b; case TypeCode.Double: return (double)(int)(ushort)a + (double)b; case TypeCode.Decimal: return (decimal)(ushort)a + (decimal)b; } break; case TypeCode.Int32: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'int' and 'bool'"); case TypeCode.Byte: return (int)a + (byte)b; case TypeCode.SByte: return (int)a + (sbyte)b; case TypeCode.Int16: return (int)a + (short)b; case TypeCode.UInt16: return (int)a + (ushort)b; case TypeCode.Int32: return (int)a + (int)b; case TypeCode.UInt32: return (int)a + (uint)b; case TypeCode.Int64: return (int)a + (long)b; case TypeCode.UInt64: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'int' and 'ulong'"); case TypeCode.Single: return (float)(int)a + (float)b; case TypeCode.Double: return (double)(int)a + (double)b; case TypeCode.Decimal: return (decimal)(int)a + (decimal)b; } break; case TypeCode.UInt32: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'unit' and 'bool'"); case TypeCode.Byte: return (uint)a + (byte)b; case TypeCode.SByte: return (uint)a + (sbyte)b; case TypeCode.Int16: return (uint)a + (short)b; case TypeCode.UInt16: return (uint)a + (ushort)b; case TypeCode.Int32: return (uint)a + (int)b; case TypeCode.UInt32: return (uint)a + (uint)b; case TypeCode.Int64: return (uint)a + (long)b; case TypeCode.UInt64: return (uint)a + (ulong)b; case TypeCode.Single: return (float)(uint)a + (float)b; case TypeCode.Double: return (double)(uint)a + (double)b; case TypeCode.Decimal: return (decimal)(uint)a + (decimal)b; } break; case TypeCode.Int64: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'long' and 'bool'"); case TypeCode.Byte: return (long)a + (byte)b; case TypeCode.SByte: return (long)a + (sbyte)b; case TypeCode.Int16: return (long)a + (short)b; case TypeCode.UInt16: return (long)a + (ushort)b; case TypeCode.Int32: return (long)a + (int)b; case TypeCode.UInt32: return (long)a + (uint)b; case TypeCode.Int64: return (long)a + (long)b; case TypeCode.UInt64: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'long' and 'ulong'"); case TypeCode.Single: return (float)(long)a + (float)b; case TypeCode.Double: return (double)(long)a + (double)b; case TypeCode.Decimal: return (decimal)(long)a + (decimal)b; } break; case TypeCode.UInt64: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'ulong' and 'bool'"); case TypeCode.Byte: return (ulong)a + (byte)b; case TypeCode.SByte: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'ulong' and 'sbyte'"); case TypeCode.Int16: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'ulong' and 'short'"); case TypeCode.UInt16: return (ulong)a + (ushort)b; case TypeCode.Int32: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'ulong' and 'int'"); case TypeCode.UInt32: return (ulong)a + (uint)b; case TypeCode.Int64: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'ulong' and 'long'"); case TypeCode.UInt64: return (ulong)a + (ulong)b; case TypeCode.Single: return (float)(ulong)a + (float)b; case TypeCode.Double: return (double)(ulong)a + (double)b; case TypeCode.Decimal: return (decimal)(ulong)a + (decimal)b; } break; case TypeCode.Single: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'float' and 'bool'"); case TypeCode.Byte: return (float)a + (float)(int)(byte)b; case TypeCode.SByte: return (float)a + (float)(sbyte)b; case TypeCode.Int16: return (float)a + (float)(short)b; case TypeCode.UInt16: return (float)a + (float)(int)(ushort)b; case TypeCode.Int32: return (float)a + (float)(int)b; case TypeCode.UInt32: return (float)a + (float)(uint)b; case TypeCode.Int64: return (float)a + (float)(long)b; case TypeCode.UInt64: return (float)a + (float)(ulong)b; case TypeCode.Single: return (float)a + (float)b; case TypeCode.Double: return (double)(float)a + (double)b; case TypeCode.Decimal: return Convert.ToDecimal(a) + (decimal)b; } break; case TypeCode.Double: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'double' and 'bool'"); case TypeCode.Byte: return (double)a + (double)(int)(byte)b; case TypeCode.SByte: return (double)a + (double)(sbyte)b; case TypeCode.Int16: return (double)a + (double)(short)b; case TypeCode.UInt16: return (double)a + (double)(int)(ushort)b; case TypeCode.Int32: return (double)a + (double)(int)b; case TypeCode.UInt32: return (double)a + (double)(uint)b; case TypeCode.Int64: return (double)a + (double)(long)b; case TypeCode.UInt64: return (double)a + (double)(ulong)b; case TypeCode.Single: return (double)a + (double)(float)b; case TypeCode.Double: return (double)a + (double)b; case TypeCode.Decimal: return Convert.ToDecimal(a) + (decimal)b; } break; case TypeCode.Decimal: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '+' can't be applied to operands of types 'decimal' and 'bool'"); case TypeCode.Byte: return (decimal)a + (decimal)(byte)b; case TypeCode.SByte: return (decimal)a + (decimal)(sbyte)b; case TypeCode.Int16: return (decimal)a + (decimal)(short)b; case TypeCode.UInt16: return (decimal)a + (decimal)(ushort)b; case TypeCode.Int32: return (decimal)a + (decimal)(int)b; case TypeCode.UInt32: return (decimal)a + (decimal)(uint)b; case TypeCode.Int64: return (decimal)a + (decimal)(long)b; case TypeCode.UInt64: return (decimal)a + (decimal)(ulong)b; case TypeCode.Single: return (decimal)a + Convert.ToDecimal(b); case TypeCode.Double: return (decimal)a + Convert.ToDecimal(b); case TypeCode.Decimal: return (decimal)a + (decimal)b; } break; } return null; } public static object Divide(object a, object b) { if (a == null || b == null) { return null; } a = ConvertIfString(a); b = ConvertIfString(b); if (a is double && double.IsNaN((double)a)) { return a; } if (b is double && double.IsNaN((double)b)) { return b; } TypeCode typeCode = TypeHelper.GetTypeCode(a); TypeCode typeCode2 = TypeHelper.GetTypeCode(b); switch (typeCode) { case TypeCode.Byte: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'byte' and 'bool'"); case TypeCode.SByte: return (byte)a / (sbyte)b; case TypeCode.Int16: return (byte)a / (short)b; case TypeCode.UInt16: return (byte)a / (ushort)b; case TypeCode.Int32: return (byte)a / (int)b; case TypeCode.UInt32: return (byte)a / (uint)b; case TypeCode.Int64: return (byte)a / (long)b; case TypeCode.UInt64: return (byte)a / (ulong)b; case TypeCode.Single: return (float)(int)(byte)a / (float)b; case TypeCode.Double: return (double)(int)(byte)a / (double)b; case TypeCode.Decimal: return (decimal)(byte)a / (decimal)b; } break; case TypeCode.SByte: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'sbyte' and 'bool'"); case TypeCode.SByte: return (sbyte)a / (sbyte)b; case TypeCode.Int16: return (sbyte)a / (short)b; case TypeCode.UInt16: return (sbyte)a / (ushort)b; case TypeCode.Int32: return (sbyte)a / (int)b; case TypeCode.UInt32: return (sbyte)a / (uint)b; case TypeCode.Int64: return (sbyte)a / (long)b; case TypeCode.UInt64: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'sbyte' and 'ulong'"); case TypeCode.Single: return (float)(sbyte)a / (float)b; case TypeCode.Double: return (double)(sbyte)a / (double)b; case TypeCode.Decimal: return (decimal)(sbyte)a / (decimal)b; } break; case TypeCode.Int16: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'short' and 'bool'"); case TypeCode.SByte: return (short)a / (sbyte)b; case TypeCode.Int16: return (short)a / (short)b; case TypeCode.UInt16: return (short)a / (ushort)b; case TypeCode.Int32: return (short)a / (int)b; case TypeCode.UInt32: return (short)a / (uint)b; case TypeCode.Int64: return (short)a / (long)b; case TypeCode.UInt64: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'short' and 'ulong'"); case TypeCode.Single: return (float)(short)a / (float)b; case TypeCode.Double: return (double)(short)a / (double)b; case TypeCode.Decimal: return (decimal)(short)a / (decimal)b; } break; case TypeCode.UInt16: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'ushort' and 'bool'"); case TypeCode.SByte: return (ushort)a / (sbyte)b; case TypeCode.Int16: return (ushort)a / (short)b; case TypeCode.UInt16: return (ushort)a / (ushort)b; case TypeCode.Int32: return (ushort)a / (int)b; case TypeCode.UInt32: return (ushort)a / (uint)b; case TypeCode.Int64: return (ushort)a / (long)b; case TypeCode.UInt64: return (ushort)a / (ulong)b; case TypeCode.Single: return (float)(int)(ushort)a / (float)b; case TypeCode.Double: return (double)(int)(ushort)a / (double)b; case TypeCode.Decimal: return (decimal)(ushort)a / (decimal)b; } break; case TypeCode.Int32: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'int' and 'bool'"); case TypeCode.SByte: return (int)a / (sbyte)b; case TypeCode.Int16: return (int)a / (short)b; case TypeCode.UInt16: return (int)a / (ushort)b; case TypeCode.Int32: return (int)a / (int)b; case TypeCode.UInt32: return (int)a / (uint)b; case TypeCode.Int64: return (int)a / (long)b; case TypeCode.UInt64: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'int' and 'ulong'"); case TypeCode.Single: return (float)(int)a / (float)b; case TypeCode.Double: return (double)(int)a / (double)b; case TypeCode.Decimal: return (decimal)(int)a / (decimal)b; } break; case TypeCode.UInt32: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'uint' and 'bool'"); case TypeCode.SByte: return (uint)a / (sbyte)b; case TypeCode.Int16: return (uint)a / (short)b; case TypeCode.UInt16: return (uint)a / (ushort)b; case TypeCode.Int32: return (uint)a / (int)b; case TypeCode.UInt32: return (uint)a / (uint)b; case TypeCode.Int64: return (uint)a / (long)b; case TypeCode.UInt64: return (uint)a / (ulong)b; case TypeCode.Single: return (float)(uint)a / (float)b; case TypeCode.Double: return (double)(uint)a / (double)b; case TypeCode.Decimal: return (decimal)(uint)a / (decimal)b; } break; case TypeCode.Int64: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'long' and 'bool'"); case TypeCode.SByte: return (long)a / (sbyte)b; case TypeCode.Int16: return (long)a / (short)b; case TypeCode.UInt16: return (long)a / (ushort)b; case TypeCode.Int32: return (long)a / (int)b; case TypeCode.UInt32: return (long)a / (uint)b; case TypeCode.Int64: return (long)a / (long)b; case TypeCode.UInt64: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'long' and 'ulong'"); case TypeCode.Single: return (float)(long)a / (float)b; case TypeCode.Double: return (double)(long)a / (double)b; case TypeCode.Decimal: return (decimal)(long)a / (decimal)b; } break; case TypeCode.UInt64: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'ulong' and 'bool'"); case TypeCode.SByte: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'ulong' and 'sbyte'"); case TypeCode.Int16: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'ulong' and 'short'"); case TypeCode.UInt16: return (ulong)a / (ushort)b; case TypeCode.Int32: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'ulong' and 'int'"); case TypeCode.UInt32: return (ulong)a / (uint)b; case TypeCode.Int64: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'ulong' and 'long'"); case TypeCode.UInt64: return (ulong)a / (ulong)b; case TypeCode.Single: return (float)(ulong)a / (float)b; case TypeCode.Double: return (double)(ulong)a / (double)b; case TypeCode.Decimal: return (decimal)(ulong)a / (decimal)b; } break; case TypeCode.Single: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'float' and 'bool'"); case TypeCode.SByte: return (float)a / (float)(sbyte)b; case TypeCode.Int16: return (float)a / (float)(short)b; case TypeCode.UInt16: return (float)a / (float)(int)(ushort)b; case TypeCode.Int32: return (float)a / (float)(int)b; case TypeCode.UInt32: return (float)a / (float)(uint)b; case TypeCode.Int64: return (float)a / (float)(long)b; case TypeCode.UInt64: return (float)a / (float)(ulong)b; case TypeCode.Single: return (float)a / (float)b; case TypeCode.Double: return (double)(float)a / (double)b; case TypeCode.Decimal: return Convert.ToDecimal(a) / (decimal)b; } break; case TypeCode.Double: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'double' and 'bool'"); case TypeCode.SByte: return (double)a / (double)(sbyte)b; case TypeCode.Int16: return (double)a / (double)(short)b; case TypeCode.UInt16: return (double)a / (double)(int)(ushort)b; case TypeCode.Int32: return (double)a / (double)(int)b; case TypeCode.UInt32: return (double)a / (double)(uint)b; case TypeCode.Int64: return (double)a / (double)(long)b; case TypeCode.UInt64: return (double)a / (double)(ulong)b; case TypeCode.Single: return (double)a / (double)(float)b; case TypeCode.Double: return (double)a / (double)b; case TypeCode.Decimal: return Convert.ToDecimal(a) / (decimal)b; } break; case TypeCode.Decimal: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '/' can't be applied to operands of types 'decimal' and 'bool'"); case TypeCode.SByte: return (decimal)a / (decimal)(sbyte)b; case TypeCode.Int16: return (decimal)a / (decimal)(short)b; case TypeCode.UInt16: return (decimal)a / (decimal)(ushort)b; case TypeCode.Int32: return (decimal)a / (decimal)(int)b; case TypeCode.UInt32: return (decimal)a / (decimal)(uint)b; case TypeCode.Int64: return (decimal)a / (decimal)(long)b; case TypeCode.UInt64: return (decimal)a / (decimal)(ulong)b; case TypeCode.Single: return (decimal)a / Convert.ToDecimal(b); case TypeCode.Double: return (decimal)a / Convert.ToDecimal(b); case TypeCode.Decimal: return (decimal)a / (decimal)b; } break; } return null; } public static object Multiply(object a, object b) { if (a == null || b == null) { return null; } a = ConvertIfString(a); b = ConvertIfString(b); if (a is double && double.IsNaN((double)a)) { return a; } if (b is double && double.IsNaN((double)b)) { return b; } TypeCode typeCode = TypeHelper.GetTypeCode(a); TypeCode typeCode2 = TypeHelper.GetTypeCode(b); switch (typeCode) { case TypeCode.Byte: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'byte' and 'bool'"); case TypeCode.SByte: return (byte)a * (sbyte)b; case TypeCode.Int16: return (byte)a * (short)b; case TypeCode.UInt16: return (byte)a * (ushort)b; case TypeCode.Int32: return (byte)a * (int)b; case TypeCode.UInt32: return (byte)a * (uint)b; case TypeCode.Int64: return (byte)a * (long)b; case TypeCode.UInt64: return (byte)a * (ulong)b; case TypeCode.Single: return (float)(int)(byte)a * (float)b; case TypeCode.Double: return (double)(int)(byte)a * (double)b; case TypeCode.Decimal: return (decimal)(byte)a * (decimal)b; } break; case TypeCode.SByte: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'sbyte' and 'bool'"); case TypeCode.SByte: return (sbyte)a * (sbyte)b; case TypeCode.Int16: return (sbyte)a * (short)b; case TypeCode.UInt16: return (sbyte)a * (ushort)b; case TypeCode.Int32: return (sbyte)a * (int)b; case TypeCode.UInt32: return (sbyte)a * (uint)b; case TypeCode.Int64: return (sbyte)a * (long)b; case TypeCode.UInt64: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'sbyte' and 'ulong'"); case TypeCode.Single: return (float)(sbyte)a * (float)b; case TypeCode.Double: return (double)(sbyte)a * (double)b; case TypeCode.Decimal: return (decimal)(sbyte)a * (decimal)b; } break; case TypeCode.Int16: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'short' and 'bool'"); case TypeCode.SByte: return (short)a * (sbyte)b; case TypeCode.Int16: return (short)a * (short)b; case TypeCode.UInt16: return (short)a * (ushort)b; case TypeCode.Int32: return (short)a * (int)b; case TypeCode.UInt32: return (short)a * (uint)b; case TypeCode.Int64: return (short)a * (long)b; case TypeCode.UInt64: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'short' and 'ulong'"); case TypeCode.Single: return (float)(short)a * (float)b; case TypeCode.Double: return (double)(short)a * (double)b; case TypeCode.Decimal: return (decimal)(short)a * (decimal)b; } break; case TypeCode.UInt16: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'ushort' and 'bool'"); case TypeCode.SByte: return (ushort)a * (sbyte)b; case TypeCode.Int16: return (ushort)a * (short)b; case TypeCode.UInt16: return (ushort)a * (ushort)b; case TypeCode.Int32: return (ushort)a * (int)b; case TypeCode.UInt32: return (ushort)a * (uint)b; case TypeCode.Int64: return (ushort)a * (long)b; case TypeCode.UInt64: return (ushort)a * (ulong)b; case TypeCode.Single: return (float)(int)(ushort)a * (float)b; case TypeCode.Double: return (double)(int)(ushort)a * (double)b; case TypeCode.Decimal: return (decimal)(ushort)a * (decimal)b; } break; case TypeCode.Int32: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'int' and 'bool'"); case TypeCode.SByte: return (int)a * (sbyte)b; case TypeCode.Int16: return (int)a * (short)b; case TypeCode.UInt16: return (int)a * (ushort)b; case TypeCode.Int32: return (int)a * (int)b; case TypeCode.UInt32: return (int)a * (uint)b; case TypeCode.Int64: return (int)a * (long)b; case TypeCode.UInt64: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'int' and 'ulong'"); case TypeCode.Single: return (float)(int)a * (float)b; case TypeCode.Double: return (double)(int)a * (double)b; case TypeCode.Decimal: return (decimal)(int)a * (decimal)b; } break; case TypeCode.UInt32: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'uint' and 'bool'"); case TypeCode.SByte: return (uint)a * (sbyte)b; case TypeCode.Int16: return (uint)a * (short)b; case TypeCode.UInt16: return (uint)a * (ushort)b; case TypeCode.Int32: return (uint)a * (int)b; case TypeCode.UInt32: return (uint)a * (uint)b; case TypeCode.Int64: return (uint)a * (long)b; case TypeCode.UInt64: return (uint)a * (ulong)b; case TypeCode.Single: return (float)(uint)a * (float)b; case TypeCode.Double: return (double)(uint)a * (double)b; case TypeCode.Decimal: return (decimal)(uint)a * (decimal)b; } break; case TypeCode.Int64: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'long' and 'bool'"); case TypeCode.SByte: return (long)a * (sbyte)b; case TypeCode.Int16: return (long)a * (short)b; case TypeCode.UInt16: return (long)a * (ushort)b; case TypeCode.Int32: return (long)a * (int)b; case TypeCode.UInt32: return (long)a * (uint)b; case TypeCode.Int64: return (long)a * (long)b; case TypeCode.UInt64: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'long' and 'ulong'"); case TypeCode.Single: return (float)(long)a * (float)b; case TypeCode.Double: return (double)(long)a * (double)b; case TypeCode.Decimal: return (decimal)(long)a * (decimal)b; } break; case TypeCode.UInt64: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'ulong' and 'bool'"); case TypeCode.SByte: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'ulong' and 'sbyte'"); case TypeCode.Int16: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'ulong' and 'short'"); case TypeCode.UInt16: return (ulong)a * (ushort)b; case TypeCode.Int32: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'ulong' and 'int'"); case TypeCode.UInt32: return (ulong)a * (uint)b; case TypeCode.Int64: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'ulong' and 'long'"); case TypeCode.UInt64: return (ulong)a * (ulong)b; case TypeCode.Single: return (float)(ulong)a * (float)b; case TypeCode.Double: return (double)(ulong)a * (double)b; case TypeCode.Decimal: return (decimal)(ulong)a * (decimal)b; } break; case TypeCode.Single: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'float' and 'bool'"); case TypeCode.SByte: return (float)a * (float)(sbyte)b; case TypeCode.Int16: return (float)a * (float)(short)b; case TypeCode.UInt16: return (float)a * (float)(int)(ushort)b; case TypeCode.Int32: return (float)a * (float)(int)b; case TypeCode.UInt32: return (float)a * (float)(uint)b; case TypeCode.Int64: return (float)a * (float)(long)b; case TypeCode.UInt64: return (float)a * (float)(ulong)b; case TypeCode.Single: return (float)a * (float)b; case TypeCode.Double: return (double)(float)a * (double)b; case TypeCode.Decimal: return Convert.ToDecimal(a) * (decimal)b; } break; case TypeCode.Double: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'double' and 'bool'"); case TypeCode.SByte: return (double)a * (double)(sbyte)b; case TypeCode.Int16: return (double)a * (double)(short)b; case TypeCode.UInt16: return (double)a * (double)(int)(ushort)b; case TypeCode.Int32: return (double)a * (double)(int)b; case TypeCode.UInt32: return (double)a * (double)(uint)b; case TypeCode.Int64: return (double)a * (double)(long)b; case TypeCode.UInt64: return (double)a * (double)(ulong)b; case TypeCode.Single: return (double)a * (double)(float)b; case TypeCode.Double: return (double)a * (double)b; case TypeCode.Decimal: return Convert.ToDecimal(a) * (decimal)b; } break; case TypeCode.Decimal: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '*' can't be applied to operands of types 'decimal' and 'bool'"); case TypeCode.SByte: return (decimal)a * (decimal)(sbyte)b; case TypeCode.Int16: return (decimal)a * (decimal)(short)b; case TypeCode.UInt16: return (decimal)a * (decimal)(ushort)b; case TypeCode.Int32: return (decimal)a * (decimal)(int)b; case TypeCode.UInt32: return (decimal)a * (decimal)(uint)b; case TypeCode.Int64: return (decimal)a * (decimal)(long)b; case TypeCode.UInt64: return (decimal)a * (decimal)(ulong)b; case TypeCode.Single: return (decimal)a * Convert.ToDecimal(b); case TypeCode.Double: return (decimal)a * Convert.ToDecimal(b); case TypeCode.Decimal: return (decimal)a * (decimal)b; } break; } return null; } public static object Subtract(object a, object b) { if (a == null || b == null) { return null; } a = ConvertIfString(a); b = ConvertIfString(b); if (a is double && double.IsNaN((double)a)) { return a; } if (b is double && double.IsNaN((double)b)) { return b; } TypeCode typeCode = TypeHelper.GetTypeCode(a); TypeCode typeCode2 = TypeHelper.GetTypeCode(b); switch (typeCode) { case TypeCode.Boolean: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'bool' and 'bool'"); case TypeCode.Byte: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.SByte: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.Int16: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.UInt16: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.Int32: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.UInt32: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.Int64: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.UInt64: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.Single: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.Double: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'bool' and 'byte'"); case TypeCode.Decimal: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'bool' and 'byte'"); } break; case TypeCode.Byte: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'byte' and 'bool'"); case TypeCode.SByte: return (byte)a - (sbyte)b; case TypeCode.Int16: return (byte)a - (short)b; case TypeCode.UInt16: return (byte)a - (ushort)b; case TypeCode.Int32: return (byte)a - (int)b; case TypeCode.UInt32: return (byte)a - (uint)b; case TypeCode.Int64: return (byte)a - (long)b; case TypeCode.UInt64: return (byte)a - (ulong)b; case TypeCode.Single: return (float)(int)(byte)a - (float)b; case TypeCode.Double: return (double)(int)(byte)a - (double)b; case TypeCode.Decimal: return (decimal)(byte)a - (decimal)b; } break; case TypeCode.SByte: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'sbyte' and 'bool'"); case TypeCode.SByte: return (sbyte)a - (sbyte)b; case TypeCode.Int16: return (sbyte)a - (short)b; case TypeCode.UInt16: return (sbyte)a - (ushort)b; case TypeCode.Int32: return (sbyte)a - (int)b; case TypeCode.UInt32: return (sbyte)a - (uint)b; case TypeCode.Int64: return (sbyte)a - (long)b; case TypeCode.UInt64: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'sbyte' and 'ulong'"); case TypeCode.Single: return (float)(sbyte)a - (float)b; case TypeCode.Double: return (double)(sbyte)a - (double)b; case TypeCode.Decimal: return (decimal)(sbyte)a - (decimal)b; } break; case TypeCode.Int16: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'short' and 'bool'"); case TypeCode.SByte: return (short)a - (sbyte)b; case TypeCode.Int16: return (short)a - (short)b; case TypeCode.UInt16: return (short)a - (ushort)b; case TypeCode.Int32: return (short)a - (int)b; case TypeCode.UInt32: return (short)a - (uint)b; case TypeCode.Int64: return (short)a - (long)b; case TypeCode.UInt64: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'short' and 'ulong'"); case TypeCode.Single: return (float)(short)a - (float)b; case TypeCode.Double: return (double)(short)a - (double)b; case TypeCode.Decimal: return (decimal)(short)a - (decimal)b; } break; case TypeCode.UInt16: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'ushort' and 'bool'"); case TypeCode.SByte: return (ushort)a - (sbyte)b; case TypeCode.Int16: return (ushort)a - (short)b; case TypeCode.UInt16: return (ushort)a - (ushort)b; case TypeCode.Int32: return (ushort)a - (int)b; case TypeCode.UInt32: return (ushort)a - (uint)b; case TypeCode.Int64: return (ushort)a - (long)b; case TypeCode.UInt64: return (ushort)a - (ulong)b; case TypeCode.Single: return (float)(int)(ushort)a - (float)b; case TypeCode.Double: return (double)(int)(ushort)a - (double)b; case TypeCode.Decimal: return (decimal)(ushort)a - (decimal)b; } break; case TypeCode.Int32: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'int' and 'bool'"); case TypeCode.SByte: return (int)a - (sbyte)b; case TypeCode.Int16: return (int)a - (short)b; case TypeCode.UInt16: return (int)a - (ushort)b; case TypeCode.Int32: return (int)a - (int)b; case TypeCode.UInt32: return (int)a - (uint)b; case TypeCode.Int64: return (int)a - (long)b; case TypeCode.UInt64: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'int' and 'ulong'"); case TypeCode.Single: return (float)(int)a - (float)b; case TypeCode.Double: return (double)(int)a - (double)b; case TypeCode.Decimal: return (decimal)(int)a - (decimal)b; } break; case TypeCode.UInt32: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'uint' and 'bool'"); case TypeCode.SByte: return (uint)a - (sbyte)b; case TypeCode.Int16: return (uint)a - (short)b; case TypeCode.UInt16: return (uint)a - (ushort)b; case TypeCode.Int32: return (uint)a - (int)b; case TypeCode.UInt32: return (uint)a - (uint)b; case TypeCode.Int64: return (uint)a - (long)b; case TypeCode.UInt64: return (uint)a - (ulong)b; case TypeCode.Single: return (float)(uint)a - (float)b; case TypeCode.Double: return (double)(uint)a - (double)b; case TypeCode.Decimal: return (decimal)(uint)a - (decimal)b; } break; case TypeCode.Int64: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'long' and 'bool'"); case TypeCode.SByte: return (long)a - (sbyte)b; case TypeCode.Int16: return (long)a - (short)b; case TypeCode.UInt16: return (long)a - (ushort)b; case TypeCode.Int32: return (long)a - (int)b; case TypeCode.UInt32: return (long)a - (uint)b; case TypeCode.Int64: return (long)a - (long)b; case TypeCode.UInt64: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'long' and 'ulong'"); case TypeCode.Single: return (float)(long)a - (float)b; case TypeCode.Double: return (double)(long)a - (double)b; case TypeCode.Decimal: return (decimal)(long)a - (decimal)b; } break; case TypeCode.UInt64: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'ulong' and 'bool'"); case TypeCode.SByte: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'ulong' and 'double'"); case TypeCode.Int16: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'ulong' and 'short'"); case TypeCode.UInt16: return (ulong)a - (ushort)b; case TypeCode.Int32: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'ulong' and 'int'"); case TypeCode.UInt32: return (ulong)a - (uint)b; case TypeCode.Int64: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'ulong' and 'long'"); case TypeCode.UInt64: return (ulong)a - (ulong)b; case TypeCode.Single: return (float)(ulong)a - (float)b; case TypeCode.Double: return (double)(ulong)a - (double)b; case TypeCode.Decimal: return (decimal)(ulong)a - (decimal)b; } break; case TypeCode.Single: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'float' and 'bool'"); case TypeCode.SByte: return (float)a - (float)(sbyte)b; case TypeCode.Int16: return (float)a - (float)(short)b; case TypeCode.UInt16: return (float)a - (float)(int)(ushort)b; case TypeCode.Int32: return (float)a - (float)(int)b; case TypeCode.UInt32: return (float)a - (float)(uint)b; case TypeCode.Int64: return (float)a - (float)(long)b; case TypeCode.UInt64: return (float)a - (float)(ulong)b; case TypeCode.Single: return (float)a - (float)b; case TypeCode.Double: return (double)(float)a - (double)b; case TypeCode.Decimal: return Convert.ToDecimal(a) - (decimal)b; } break; case TypeCode.Double: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'double' and 'bool'"); case TypeCode.SByte: return (double)a - (double)(sbyte)b; case TypeCode.Int16: return (double)a - (double)(short)b; case TypeCode.UInt16: return (double)a - (double)(int)(ushort)b; case TypeCode.Int32: return (double)a - (double)(int)b; case TypeCode.UInt32: return (double)a - (double)(uint)b; case TypeCode.Int64: return (double)a - (double)(long)b; case TypeCode.UInt64: return (double)a - (double)(ulong)b; case TypeCode.Single: return (double)a - (double)(float)b; case TypeCode.Double: return (double)a - (double)b; case TypeCode.Decimal: return Convert.ToDecimal(a) - (decimal)b; } break; case TypeCode.Decimal: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '-' can't be applied to operands of types 'decimal' and 'bool'"); case TypeCode.SByte: return (decimal)a - (decimal)(sbyte)b; case TypeCode.Int16: return (decimal)a - (decimal)(short)b; case TypeCode.UInt16: return (decimal)a - (decimal)(ushort)b; case TypeCode.Int32: return (decimal)a - (decimal)(int)b; case TypeCode.UInt32: return (decimal)a - (decimal)(uint)b; case TypeCode.Int64: return (decimal)a - (decimal)(long)b; case TypeCode.UInt64: return (decimal)a - (decimal)(ulong)b; case TypeCode.Single: return (decimal)a - Convert.ToDecimal(b); case TypeCode.Double: return (decimal)a - Convert.ToDecimal(b); case TypeCode.Decimal: return (decimal)a - (decimal)b; } break; } return null; } public static object Modulus(object a, object b) { if (a == null || b == null) { return null; } a = ConvertIfString(a); b = ConvertIfString(b); if (a is double && double.IsNaN((double)a)) { return a; } if (b is double && double.IsNaN((double)b)) { return b; } TypeCode typeCode = TypeHelper.GetTypeCode(a); TypeCode typeCode2 = TypeHelper.GetTypeCode(b); switch (typeCode) { case TypeCode.Byte: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '%' can't be applied to operands of types 'byte' and 'bool'"); case TypeCode.SByte: return (byte)a % (sbyte)b; case TypeCode.Int16: return (byte)a % (short)b; case TypeCode.UInt16: return (byte)a % (ushort)b; case TypeCode.Int32: return (byte)a % (int)b; case TypeCode.UInt32: return (byte)a % (uint)b; case TypeCode.Int64: return (byte)a % (long)b; case TypeCode.UInt64: return (byte)a % (ulong)b; case TypeCode.Single: return (float)(int)(byte)a % (float)b; case TypeCode.Double: return (double)(int)(byte)a % (double)b; case TypeCode.Decimal: return (decimal)(byte)a % (decimal)b; } break; case TypeCode.SByte: switch (typeCode2) { case TypeCode.Boolean: throw new InvalidOperationException("Operator '%' can't be applied to operands of types
FullHealth.dll
Decompiled 2 weeks agousing System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Expressive; using Expressive.Expressions; using Expressive.Functions; using FullHealth.Expressions; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("FullHealth")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.8.0.0")] [assembly: AssemblyInformationalVersion("1.8.0+607ac538ab211ad0cfe094c4e91c070aa7223984")] [assembly: AssemblyProduct("FullHealth")] [assembly: AssemblyTitle("FullHealth")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.8.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace FullHealth { public static class Configuration { public static ConfigEntry<bool> Enabled { get; set; } public static ConfigEntry<decimal> ToMaxHealthPercentage { get; set; } public static ConfigEntry<decimal> ByMaxHealthPercentage { get; set; } public static ConfigEntry<WorkMode> WorkMode { get; set; } public static ConfigEntry<HealMode> HealMode { get; set; } public static ConfigEntry<PhaseMode> PhaseMode { get; set; } public static ConfigEntry<int> ExactValue { get; set; } public static ConfigEntry<HealthPackMode> HealthPackMode { get; set; } public static ConfigEntry<HealRequirementMode> HealRequirementMode { get; set; } public static ConfigEntry<string> Expression { get; set; } internal static int[] HealthPackModeValues { get; set; } = Array.Empty<int>(); } public static class ConfigurationHelper { private static readonly HealthPackMode[] HealthPackModes = new HealthPackMode[3] { HealthPackMode.Small, HealthPackMode.Medium, HealthPackMode.Large }; public static void Bind(ConfigFile config) { //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Expected O, but got Unknown Configuration.Enabled = config.Bind<bool>("General", "Enabled", true, "Indicates whether the mod is enabled."); Configuration.ToMaxHealthPercentage = config.Bind<decimal>("General", "ToMaxHealthPercentage", 100m, "The percentage of the player's maximum health to which he will be healed (Min: 0, Max: 100).\r\nAttention! You must set the `PhaseMode` parameter to only one of `Level` or `Lobby`, not both. Otherwise, the healing will be done twice."); Configuration.ByMaxHealthPercentage = config.Bind<decimal>("General", "ByMaxHealthPercentage", 0m, "The percentage of the player's maximum health that the player will be healed to (Min: 0, Max: 100).\r\nAttention! You must set the `PhaseMode` parameter to only one of `Level` or `Lobby`, not both. Otherwise, the healing will be done twice.\r\nIt has priority over the `ToMaxHealthPercentage` parameter."); Configuration.ExactValue = config.Bind<int>("General", "ExactValue", 0, "Sets the exact value to heal.\r\nAttention! You must set the `PhaseMode` parameter to only one of `Level` or `Lobby`, not both. Otherwise, the healing will be done twice.\r\nIf the value is `0`, then it is disabled.\r\nIt has priority over the `ToMaxHealthPercentage` and `ByMaxHealthPercentage` parameters.\r\nFor example, if you set the value to `15`, you will always receive 15 HP."); Configuration.HealthPackMode = config.Bind<HealthPackMode>("General", "HealthPackMode", HealthPackMode.None, "Sets healing value options to the same as in health packs, one of which will be applied randomly.\r\nAttention! You must set the `PhaseMode` parameter to only one of `Level` or `Lobby`, not both. Otherwise, the healing will be done twice.\r\nIf the value is `None`, then it is disabled.\r\nIt has priority over the `ToMaxHealthPercentage` and `ByMaxHealthPercentage` and `ExactValue` parameters.\r\nFor example, if you set the value to `Medium, Large`, you will receive a random healing of 50HP or 100HP.\r\n`Small` - heal 25 HP.\r\n`Medium` - heal 50 HP.\r\n`Large` - heal 100 HP."); Configuration.Expression = config.Bind<string>("General", "Expression", (string)null, "The expression used to calculate the healing value.\r\nAttention! You must set the `PhaseMode` parameter to only one of `Level` or `Lobby`, not both. Otherwise, the healing will be done twice.\r\nIf the value is null or empty or whitespace, then it is disabled.\r\nIt has priority over the `ToMaxHealthPercentage` and `ByMaxHealthPercentage` and `ExactValue` and `HealthPackMode` parameters.\r\nThe library used to parse expression is https://github.com/bijington/expressive. You can find detailed information in the documentation.\r\nVariables:\r\n`[PlayerName]` - player's name.\r\n`[PlayerMaxHealth]` - player's maximum health.\r\n`[PlayerHealth]` - player's health.\r\n`[PlayerSurvived]` - whether the player survived the last round.\r\n`[PlayerCount]` - number of players.\r\n`[PlayerUpgradeHealth]` - player's upgrade health level.\r\n`[PlayerUpgradeStamina]` - player's upgrade stamina level.\r\n`[PlayerUpgradeExtraJump]` - player's upgrade extra jump level.\r\n`[PlayerUpgradeLaunch]` - player's upgrade launch level.\r\n`[PlayerUpgradeMapPlayerCount]` - player's upgrade map player count level.\r\n`[PlayerUpgradeSpeed]` - player's upgrade speed level.\r\n`[PlayerUpgradeStrength]` - player's upgrade strength level.\r\n`[PlayerUpgradeThrow]` - player's upgrade throw level.\r\n`[PlayerUpgradeRange]` - player's upgrade range level.\r\nCustom functions (Standard functions can be found here https://github.com/bijington/expressive/wiki/Functions):\r\n`RandomList(x1, x2, x3...)` - selects a random number from a list. For example, `RandomList(10, 20)` will randomly return `10` or `20`\r\nExamples (quotation marks should be removed):\r\n`Max((([PlayerMaxHealth]*0.8)-[PlayerHealth]),0)` - same as the `ToMaxHealthPercentage` parameter. The player will be healed to 80% of maximum health.\r\n`[PlayerMaxHealth]*0.3` - same as `ByMaxHealthPercentage` parameter. The player will be healed by 30% of maximum health.\r\n`40` - same as `ExactValue` parameter. The player will be healed for 40HP.\r\n`RandomList(25, 50, 100)` - same as `HealthPackMode` parameter. The player will be randomly healed for 25HP or 50HP or 100HP.\r\n`[PlayerMaxHealth] * (If([PlayerCount] <= 2, Random(25, 30), If([PlayerCount] <= 4, Random(35, 40), If([PlayerCount] <= 6, Random(45, 50), Random(55, 60))))/100)` - if there are up to two players, it heals randomly by 25-30 HP. If there are up to four players, it heals randomly by 35-40 HP, and so on."); ExpressionHelper.UpdateExpression(Configuration.Expression.Value); Configuration.Expression.SettingChanged += delegate { ExpressionHelper.UpdateExpression(Configuration.Expression.Value); }; Configuration.WorkMode = config.Bind<WorkMode>("General", "WorkMode", WorkMode.Host, "Configures the work mode.\r\nWarning: You are using `All` or `Client` values for this parameter at your own risk.\r\nIt may stop working after future game updates.\r\nIf another lobby players do not agree with this parameter, do not use it.\r\nAlso, the incomplete heal may not work correctly with this parameter, since the host or other clients may also perform the heal.\r\n`Host` - works only on host.\r\n`Client` - works only on client.\r\n`All` - works on host and client."); Configuration.HealMode = config.Bind<HealMode>("General", "HealMode", HealMode.All, "Configures the heal mode.\r\n`Self` - heal self.\r\n`Others` - heal others.\r\n`All` - heal all."); Configuration.PhaseMode = config.Bind<PhaseMode>("General", "PhaseMode", PhaseMode.All, "Configures in which phases of the game health restoration will be triggered.\r\n`Level` - when spawning in the level.\r\n`Shop` - when spawning in the shop. Attention! Healing in the shop does not affect the game.\r\n`Lobby` - when spawning in the truck (not menu) before starting the next level.\r\n`Arena` - when spawning in the arena.\r\n`All` - in all phases."); Configuration.HealRequirementMode = config.Bind<HealRequirementMode>("General", "HealRequirementMode", HealRequirementMode.None, "Sets requirements for performing heal.\r\n`None` - no requirements.\r\n`Survive` - the player must return to the truck at the end of the round and survive."); UpdateHealthPackModeValues(); Configuration.HealthPackMode.SettingChanged += delegate { UpdateHealthPackModeValues(); }; decimal num = Math.Min(100m, Math.Max(0m, Configuration.ToMaxHealthPercentage.Value)); if (num != Configuration.ToMaxHealthPercentage.Value) { Configuration.ToMaxHealthPercentage.Value = num; } decimal num2 = Math.Min(100m, Math.Max(0m, Configuration.ByMaxHealthPercentage.Value)); if (num2 != Configuration.ByMaxHealthPercentage.Value) { Configuration.ByMaxHealthPercentage.Value = num2; } if (Configuration.ExactValue.Value < 0) { Configuration.ExactValue.Value = 0; } Dictionary<ConfigDefinition, string> dictionary = (Dictionary<ConfigDefinition, string>)(typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(config)); if (dictionary != null && dictionary.Remove(new ConfigDefinition("General", "Percent"), out var value) && decimal.TryParse(value, out var result)) { Configuration.ToMaxHealthPercentage.Value = Math.Min(100m, Math.Max(0m, result)); config.Save(); Plugin.Logger.LogInfo((object)"The value of the old variable 'General.Percent' has been copied to the new variable 'General.ToMaxHealthPercentage'"); } if (!string.IsNullOrWhiteSpace(Configuration.Expression.Value)) { try { int num3 = ExpressionHelper.Calculate("TestPlayer", 100, 30, Guid.NewGuid().ToString(), 4); Plugin.Logger.LogInfo((object)("Calculation test of the healing completed. Variables: " + string.Format("{0}='{1}', ", "PlayerMaxHealth", 100) + string.Format("{0}='{1}', ", "PlayerHealth", 30) + string.Format("{0}='{1}'. ", "PlayerCount", 4) + $"Result: '{num3}'")); } catch (Exception ex) { Plugin.Logger.LogError((object)("An error occurred while calculating the heal value. Please check the expression: " + ex.Message)); } } } private static void UpdateHealthPackModeValues() { List<int> list = new List<int>(); using (IEnumerator<HealthPackMode> enumerator = HealthPackModes.Where((HealthPackMode x) => Configuration.HealthPackMode.Value.HasFlag(x)).GetEnumerator()) { while (enumerator.MoveNext()) { switch (enumerator.Current) { case HealthPackMode.Small: list.Add(25); break; case HealthPackMode.Medium: list.Add(50); break; case HealthPackMode.Large: list.Add(100); break; } } } Configuration.HealthPackModeValues = list.ToArray(); } } [Flags] public enum HealMode : byte { Self = 1, Others = 2, All = 3 } [Flags] public enum HealRequirementMode : byte { None = 0, Survive = 1 } [Flags] public enum HealthPackMode : byte { None = 0, Small = 1, Medium = 2, Large = 4, All = 7 } [Flags] public enum PhaseMode : byte { Level = 1, Shop = 2, Lobby = 4, Arena = 8, All = 0xF } public enum WorkMode : byte { Host = 1, Client = 2, All = 3, [Obsolete] HostAndClient = 3 } public static class ExpressionHelper { private class VariableProvider : IVariableProvider { private readonly string _playerName; private readonly int _playerMaxHealth; private readonly int _playerHealth; private readonly string _playerSteamID; private readonly int _playerCount; private readonly ConcurrentDictionary<string, object> _cache = new ConcurrentDictionary<string, object>(); public VariableProvider(string playerName, int playerMaxHealth, int playerHealth, string playerSteamID, int playerCount) { _playerName = playerName; _playerMaxHealth = playerMaxHealth; _playerHealth = playerHealth; _playerSteamID = playerSteamID; _playerCount = playerCount; } public bool TryGetValue(string variableName, out object value) { if (_cache.TryGetValue(variableName, out value)) { return true; } switch (variableName) { case "PlayerName": value = _cache.GetOrAdd("PlayerName", _playerName); return true; case "PlayerMaxHealth": value = _cache.GetOrAdd("PlayerMaxHealth", _playerMaxHealth); return true; case "PlayerHealth": value = _cache.GetOrAdd("PlayerHealth", _playerHealth); return true; case "PlayerSurvived": value = _cache.GetOrAdd("PlayerSurvived", SurviveHelper.Check(_playerSteamID)); return true; case "PlayerCount": value = _cache.GetOrAdd("PlayerCount", _playerCount); return true; case "PlayerUpgradeHealth": value = _cache.GetOrAdd("PlayerUpgradeHealth", (StatsManager.instance?.playerUpgradeHealth?.GetValueOrDefault(_playerSteamID, 0)).GetValueOrDefault()); return true; case "PlayerUpgradeStamina": value = _cache.GetOrAdd("PlayerUpgradeStamina", (StatsManager.instance?.playerUpgradeStamina?.GetValueOrDefault(_playerSteamID, 0)).GetValueOrDefault()); return true; case "PlayerUpgradeExtraJump": value = _cache.GetOrAdd("PlayerUpgradeExtraJump", (StatsManager.instance?.playerUpgradeExtraJump?.GetValueOrDefault(_playerSteamID, 0)).GetValueOrDefault()); return true; case "PlayerUpgradeLaunch": value = _cache.GetOrAdd("PlayerUpgradeLaunch", (StatsManager.instance?.playerUpgradeLaunch?.GetValueOrDefault(_playerSteamID, 0)).GetValueOrDefault()); return true; case "PlayerUpgradeMapPlayerCount": value = _cache.GetOrAdd("PlayerUpgradeMapPlayerCount", (StatsManager.instance?.playerUpgradeMapPlayerCount?.GetValueOrDefault(_playerSteamID, 0)).GetValueOrDefault()); return true; case "PlayerUpgradeSpeed": value = _cache.GetOrAdd("PlayerUpgradeSpeed", (StatsManager.instance?.playerUpgradeSpeed?.GetValueOrDefault(_playerSteamID, 0)).GetValueOrDefault()); return true; case "PlayerUpgradeStrength": value = _cache.GetOrAdd("PlayerUpgradeStrength", (StatsManager.instance?.playerUpgradeStrength?.GetValueOrDefault(_playerSteamID, 0)).GetValueOrDefault()); return true; case "PlayerUpgradeThrow": value = _cache.GetOrAdd("PlayerUpgradeThrow", (StatsManager.instance?.playerUpgradeThrow?.GetValueOrDefault(_playerSteamID, 0)).GetValueOrDefault()); return true; case "PlayerUpgradeRange": value = _cache.GetOrAdd("PlayerUpgradeRange", (StatsManager.instance?.playerUpgradeRange?.GetValueOrDefault(_playerSteamID, 0)).GetValueOrDefault()); return true; default: return false; } } } public const string PlayerName = "PlayerName"; public const string PlayerMaxHealthName = "PlayerMaxHealth"; public const string PlayerHealthName = "PlayerHealth"; public const string PlayerSurvivedName = "PlayerSurvived"; public const string PlayerCountName = "PlayerCount"; public const string PlayerUpgradeHealthName = "PlayerUpgradeHealth"; public const string PlayerUpgradeStaminaName = "PlayerUpgradeStamina"; public const string PlayerUpgradeExtraJumpName = "PlayerUpgradeExtraJump"; public const string PlayerUpgradeLaunchName = "PlayerUpgradeLaunch"; public const string PlayerUpgradeMapPlayerCountName = "PlayerUpgradeMapPlayerCount"; public const string PlayerUpgradeSpeedName = "PlayerUpgradeSpeed"; public const string PlayerUpgradeStrengthName = "PlayerUpgradeStrength"; public const string PlayerUpgradeThrowName = "PlayerUpgradeThrow"; public const string PlayerUpgradeRangeName = "PlayerUpgradeRange"; private static readonly object LockObject = new object(); private static Expression _expression = new Expression(string.Empty, (ExpressiveOptions)1); public static void UpdateExpression(string expression) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown lock (LockObject) { _expression = new Expression(expression, (ExpressiveOptions)1); _expression.RegisterFunction((IFunction)(object)RandomListFunction.Instance); } } public static int Calculate(PlayerAvatar player, int playerCount) { return Calculate(player.playerName, player.playerHealth.maxHealth, player.playerHealth.health, player.steamID, playerCount); } public static int Calculate(string playerName, int playerMaxHealth, int playerHealth, string playerSteamId, int playerCount) { lock (LockObject) { VariableProvider variableProvider = new VariableProvider(playerName, playerMaxHealth, playerHealth, playerSteamId, playerCount); return Convert.ToInt32(_expression.Evaluate((IVariableProvider)(object)variableProvider)); } } } public static class GamePhaseValidateHelper { public static bool IsValid() { PhaseMode? gamePhaseMode = GetGamePhaseMode(); if (!gamePhaseMode.HasValue) { Plugin.Logger.LogDebug((object)"This is not a game phase"); return false; } if (!Configuration.PhaseMode.Value.HasFlag(gamePhaseMode.Value)) { Plugin.Logger.LogDebug((object)"Healing in this phase is disabled in the configuration"); return false; } return true; } private static PhaseMode? GetGamePhaseMode() { if (SemiFunc.RunIsLevel()) { return PhaseMode.Level; } if (SemiFunc.RunIsShop()) { return PhaseMode.Shop; } if (SemiFunc.RunIsLobby()) { return PhaseMode.Lobby; } if (SemiFunc.RunIsArena()) { return PhaseMode.Arena; } return null; } } public static class HealHelper { public static async Task Heal() { try { await InternalHeal(); } finally { SurviveHelper.AddAllOnLevelStart(); } } private static async Task InternalHeal() { if (!(await WaitHelper.Wait())) { return; } List<PlayerAvatar> list = PlayersHelper.Get(); foreach (PlayerAvatar item in list) { if (item.isLocal) { if (!Configuration.HealMode.Value.HasFlag(HealMode.Self)) { Plugin.Logger.LogDebug((object)"Self-healing disabled"); break; } } else if (!Configuration.HealMode.Value.HasFlag(HealMode.Others)) { Plugin.Logger.LogDebug((object)"Heal others disabled"); break; } HealPlayerHelper.Heal(item, list.Count); } } } public static class HealPlayerHelper { private static readonly Random Random = new Random(); public static void Heal(PlayerAvatar player, int playerCount) { if ((Object)(object)player?.playerHealth == (Object)null) { Plugin.Logger.LogWarning((object)"Player or his health is not set"); return; } if (Configuration.HealRequirementMode.Value.HasFlag(HealRequirementMode.Survive) && !SurviveHelper.Check(player)) { Plugin.Logger.LogDebug((object)("The player '" + player.playerName + "' did not survive and will not be healed")); return; } int health = player.playerHealth.health; int num; if (!string.IsNullOrWhiteSpace(Configuration.Expression.Value)) { try { num = ExpressionHelper.Calculate(player, playerCount); } catch (Exception ex) { Plugin.Logger.LogError((object)("An error occurred while calculating the heal value. Please check the expression: " + ex.Message)); return; } } else { int[] healthPackModeValues = Configuration.HealthPackModeValues; if (healthPackModeValues != null && healthPackModeValues.Length > 0) { int num2 = ((Configuration.HealthPackModeValues.Length != 1) ? Random.Next(0, Configuration.HealthPackModeValues.Length) : 0); num = Configuration.HealthPackModeValues[num2]; } else if (Configuration.ExactValue.Value > 0) { num = Configuration.ExactValue.Value; } else if (Configuration.ByMaxHealthPercentage.Value > 0m) { num = decimal.ToInt32((decimal)player.playerHealth.maxHealth * (Configuration.ByMaxHealthPercentage.Value / 100m)); } else { int num3 = decimal.ToInt32((decimal)player.playerHealth.maxHealth * (Configuration.ToMaxHealthPercentage.Value / 100m)); if (health >= num3) { Plugin.Logger.LogDebug((object)($"The health of the player '{player.playerName}' is '{health}' " + $"and this is is more or equal than expected '{num3}', so health is not changed")); return; } num = num3 - health; } } player.playerHealth.HealOther(num, true); Plugin.Logger.LogDebug((object)($"Player '{player.playerName}' with '{health}' HP received '{num}' HP. " + $"So player should have '{Math.Min(player.playerHealth.maxHealth, health + num)}' HP")); } } public static class PlayersHelper { public static List<PlayerAvatar> Get() { try { List<PlayerAvatar> list = SemiFunc.PlayerGetList(); if (list != null && list.Count > 0) { return list; } } catch { Plugin.Logger.LogError((object)"An error occurred while retrieving the list of players"); } try { PlayerAvatar val = SemiFunc.PlayerAvatarLocal(); if ((Object)(object)val != (Object)null) { return new List<PlayerAvatar>(1) { val }; } return new List<PlayerAvatar>(); } catch { Plugin.Logger.LogError((object)"An error occurred while getting the local player"); return new List<PlayerAvatar>(); } } } internal static class SurviveHelper { private static bool _isItGameStart; private static HashSet<string> Players { get; } = new HashSet<string>(); internal static void Add(PlayerAvatar player) { if (SemiFunc.RunIsLevel()) { Players.Add(player.steamID); Plugin.Logger.LogDebug((object)("Player '" + player.playerName + "' has been added to the list")); } } internal static void Delete(PlayerAvatar player) { if (SemiFunc.RunIsLevel()) { Players.Remove(player.steamID); Plugin.Logger.LogDebug((object)("Player '" + player.playerName + "' has been removed from the list.")); } } internal static bool Check(PlayerAvatar player) { return Check(player.steamID); } internal static bool Check(string steamId) { return Players.Contains(steamId); } internal static void AddAllOnGameStart() { if (SemiFunc.RunIsLobbyMenu()) { _isItGameStart = true; Plugin.Logger.LogDebug((object)"Game status is marked as game start"); } else if (_isItGameStart) { AddAll(); _isItGameStart = false; } } internal static void AddAllOnLevelStart() { if (SemiFunc.RunIsLevel()) { AddAll(); } } private static void AddAll() { Players.Clear(); foreach (PlayerAvatar item in PlayersHelper.Get()) { Players.Add(item.steamID); Plugin.Logger.LogDebug((object)("Player '" + item.playerName + "' has been added to the list")); } } } public static class WaitHelper { public static async Task<bool> Wait() { CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromMinutes(1.0)); while (!cts.IsCancellationRequested) { Plugin.Logger.LogDebug((object)"Waiting for all players to load"); await Task.Delay(1000, CancellationToken.None); if (SemiFunc.RunIsLobby()) { return true; } if (PlayersHelper.Get().All((PlayerAvatar x) => x.levelAnimationCompleted)) { return true; } } Plugin.Logger.LogDebug((object)"Timeout exceeded, operation cancelled"); return false; } } public static class WorkModeValidatorHelper { public static bool Validate() { if (SemiFunc.IsMasterClientOrSingleplayer() && !Configuration.WorkMode.Value.HasFlag(WorkMode.Host)) { Plugin.Logger.LogDebug((object)"This is a host, but heals when you host is disabled"); return false; } if (!SemiFunc.IsMasterClientOrSingleplayer() && !Configuration.WorkMode.Value.HasFlag(WorkMode.Client)) { Plugin.Logger.LogDebug((object)"This is a client, but heals when you client is disabled"); return false; } return true; } } [HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathRPC")] public class PlayerAvatarPlayerDeathRPCPatch { [HarmonyPostfix] private static void Postfix(PlayerAvatar __instance) { Plugin.Logger.LogDebug((object)"PlayerAvatar.PlayerDeathRPC patch"); if (!Configuration.Enabled.Value) { Plugin.Logger.LogDebug((object)"The mod is disabled"); } else { SurviveHelper.Delete(__instance); } } } [HarmonyPatch(typeof(PlayerAvatar), "ReviveRPC")] public class PlayerAvatarReviveRPCPatch { [HarmonyPostfix] private static void Postfix(PlayerAvatar __instance) { Plugin.Logger.LogDebug((object)"PlayerAvatar.ReviveRPC patch"); if (!Configuration.Enabled.Value) { Plugin.Logger.LogDebug((object)"The mod is disabled"); } else { SurviveHelper.Add(__instance); } } } [HarmonyPatch(typeof(RoundDirector), "StartRound")] public class RoundDirectorStartRoundPatch { [HarmonyPostfix] private static void Postfix() { Plugin.Logger.LogDebug((object)"RoundDirector.StartRound patch"); if (!Configuration.Enabled.Value) { Plugin.Logger.LogDebug((object)"The mod is disabled"); } else if (SemiFunc.IsMultiplayer()) { Plugin.Logger.LogDebug((object)"This is not a single player"); } else if (WorkModeValidatorHelper.Validate()) { SurviveHelper.AddAllOnGameStart(); if (!GamePhaseValidateHelper.IsValid()) { SurviveHelper.AddAllOnLevelStart(); } else { Task.Run((Func<Task?>)HealHelper.Heal); } } } } [HarmonyPatch(typeof(RoundDirector), "StartRoundRPC")] public class RoundDirectorStartRoundRPCPatch { [HarmonyPostfix] private static void Postfix() { Plugin.Logger.LogDebug((object)"RoundDirector.StartRoundRPC patch"); if (!Configuration.Enabled.Value) { Plugin.Logger.LogDebug((object)"The mod is disabled"); } else if (!SemiFunc.IsMultiplayer()) { Plugin.Logger.LogDebug((object)"This is not a multiplayer"); } else if (WorkModeValidatorHelper.Validate()) { SurviveHelper.AddAllOnGameStart(); if (!GamePhaseValidateHelper.IsValid()) { SurviveHelper.AddAllOnLevelStart(); } else { Task.Run((Func<Task?>)HealHelper.Heal); } } } } [BepInPlugin("FullHealth", "FullHealth", "1.8.0")] public class Plugin : BaseUnityPlugin { public static ManualLogSource Logger; private void Awake() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) Logger = ((BaseUnityPlugin)this).Logger; ConfigurationHelper.Bind(((BaseUnityPlugin)this).Config); new Harmony("FullHealth").PatchAll(); Logger.LogInfo((object)"Mod 'FullHealth' is loaded!"); Logger.LogInfo((object)"Configuration can be changed in 'BepInEx/config/FullHealth.cfg'"); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "FullHealth"; public const string PLUGIN_NAME = "FullHealth"; public const string PLUGIN_VERSION = "1.8.0"; } } namespace FullHealth.Expressions { public class RandomListFunction : FunctionBase { public const string FunctionName = "RandomList"; public static readonly RandomListFunction Instance = new RandomListFunction(); public override string Name => "RandomList"; public override object Evaluate(IExpression[] parameters, Context context) { if (parameters == null || parameters.Length <= 0) { throw new Exception($"{((FunctionBase)this).Name}() expects at least {1} argument(s)"); } List<object> list = parameters.Select((IExpression parameter) => parameter.Evaluate(((FunctionBase)this).Variables)).ToList(); if (list.Count == 1) { return list[0]; } Random random = new Random(DateTime.UtcNow.Millisecond); return list[random.Next(0, list.Count)]; } } }