mirror of
https://github.com/MathiasLui/CSGO-Projects.git
synced 2025-05-06 22:01:18 +00:00
Add hacky damage displayer (Will get refactored)
* Change Shared namespace to SteamShared * Add damage displayer which also calculates, gets local weather and fuel prices * API Key for tankerkoenig has to be supplied because I didn't push it
This commit is contained in:
parent
d1652aa588
commit
50ac34fb28
61 changed files with 2982 additions and 38 deletions
65
DamagePrinter/DamagePrinter.sln
Normal file
65
DamagePrinter/DamagePrinter.sln
Normal file
|
@ -0,0 +1,65 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.1.32407.343
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DamagePrinter", "DamagePrinter\DamagePrinter.csproj", "{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SteamShared", "..\Shared\SteamHelpers\SteamHelpers\SteamShared.csproj", "{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoreSoft.MathExpressions", "..\Shared\LoreSoft.MathExpressions\LoreSoft.MathExpressions.csproj", "{B6813448-FCE2-429C-81B5-9722FEA839B8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Debug|x64.Build.0 = Debug|x64
|
||||
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Release|x64.ActiveCfg = Release|x64
|
||||
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Release|x64.Build.0 = Release|x64
|
||||
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Debug|x64.Build.0 = Debug|x64
|
||||
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Release|x64.ActiveCfg = Release|x64
|
||||
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Release|x64.Build.0 = Release|x64
|
||||
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Release|x86.Build.0 = Release|Any CPU
|
||||
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Debug|x64.Build.0 = Debug|x64
|
||||
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Debug|x86.Build.0 = Debug|x86
|
||||
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Release|x64.ActiveCfg = Release|x64
|
||||
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Release|x64.Build.0 = Release|x64
|
||||
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Release|x86.ActiveCfg = Release|x86
|
||||
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B75CC20D-F989-4D9F-86F7-9610737FCBE9}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
20
DamagePrinter/DamagePrinter/DamagePrinter.csproj
Normal file
20
DamagePrinter/DamagePrinter/DamagePrinter.csproj
Normal file
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Shared\LoreSoft.MathExpressions\LoreSoft.MathExpressions.csproj" />
|
||||
<ProjectReference Include="..\..\Shared\SteamHelpers\SteamHelpers\SteamShared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
363
DamagePrinter/DamagePrinter/Program.cs
Normal file
363
DamagePrinter/DamagePrinter/Program.cs
Normal file
|
@ -0,0 +1,363 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using SteamShared;
|
||||
using LoreSoft.MathExpressions;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json.Nodes;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
static class Program
|
||||
{
|
||||
static readonly uint WM_COPYDATA = 0x004A;
|
||||
static SteamHelper steamHelper = new SteamHelper();
|
||||
static MathEvaluator mathEval = new MathEvaluator();
|
||||
static HttpClient httpClient = new HttpClient();
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
static extern int SendMessage(IntPtr windowHandle, uint message, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[STAThread]
|
||||
static void Main(string[] args)
|
||||
{
|
||||
string? gamePath = steamHelper.GetGamePathFromExactName("Counter-Strike: Global Offensive");
|
||||
|
||||
if (gamePath == null)
|
||||
return;
|
||||
|
||||
string consoleLogPath = Path.Combine(gamePath, "csgo", "console.log");
|
||||
bool consoleLogExists = false;
|
||||
|
||||
long oldFileSize = 0;
|
||||
try
|
||||
{
|
||||
oldFileSize = new FileInfo(consoleLogPath).Length;
|
||||
} catch { }
|
||||
|
||||
long nextLineOffset = oldFileSize; // bytes from the start of the file, by default not to read anything
|
||||
int minDamageToCount = 20;
|
||||
bool initialScan = true;
|
||||
var prevTaggedPlayers = new List<Tuple<string, int, int>>();
|
||||
DateTime lastFuelRequestTime = DateTime.MinValue;
|
||||
|
||||
if (FindWindow("Valve001", null) == IntPtr.Zero)
|
||||
File.Delete(consoleLogPath);
|
||||
|
||||
// here we start fresh at line 0 cause it got deleted
|
||||
while (true)
|
||||
{
|
||||
if (File.Exists(consoleLogPath))
|
||||
{
|
||||
bool update = false;
|
||||
|
||||
if (!consoleLogExists && initialScan) {
|
||||
consoleLogExists = true;
|
||||
initialScan = false;
|
||||
}
|
||||
|
||||
long curFileSize = new FileInfo(consoleLogPath).Length;
|
||||
if (curFileSize != oldFileSize)
|
||||
{
|
||||
update = true;
|
||||
oldFileSize = curFileSize;
|
||||
}
|
||||
|
||||
if (update)
|
||||
{
|
||||
int damageTakenTotal = 0;
|
||||
|
||||
List<string> lines = new List<string>();
|
||||
using (var fs = File.Open(consoleLogPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
fs.Position = nextLineOffset;
|
||||
bool endOfFile = false;
|
||||
|
||||
while (!endOfFile)
|
||||
{
|
||||
string line = "";
|
||||
bool endOfLine = false;
|
||||
while (!endOfLine)
|
||||
{
|
||||
int nextByte = fs.ReadByte();
|
||||
|
||||
if (nextByte == -1 && line != "")
|
||||
{
|
||||
lines.Add(line);
|
||||
endOfFile = true;
|
||||
break;
|
||||
}
|
||||
|
||||
char nextChar = (char)nextByte;
|
||||
|
||||
if (nextChar == '\n' && line != "")
|
||||
{
|
||||
lines.Add(line);
|
||||
endOfLine = true;
|
||||
}
|
||||
else
|
||||
if(nextChar != '\r')
|
||||
line += nextChar;
|
||||
}
|
||||
}
|
||||
|
||||
nextLineOffset = fs.Position;
|
||||
}
|
||||
|
||||
var taggedPlayers = new List<Tuple<string, int, int>>();
|
||||
string calcKeyWord = "!calc";
|
||||
string fuelPriceKeyWord = "!fuel";
|
||||
string weatherKeyWord = "!weather";
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
/* -------------------------
|
||||
Damage Given to "BOT Eugene" - 65 in 2 hits
|
||||
-------------------------
|
||||
Damage Taken from "BOT Eugene" - 117 in 4 hits*/
|
||||
if (line.StartsWith("Damage Taken"))
|
||||
{
|
||||
Regex regexTaken = new Regex("Damage Taken from \"(.+?)\" - (\\d+) in \\d+ hits?");
|
||||
Match takenMatch = regexTaken.Match(line);
|
||||
|
||||
if (takenMatch.Success)
|
||||
{
|
||||
damageTakenTotal += int.Parse(takenMatch.Groups[2].Value);
|
||||
}
|
||||
}
|
||||
else if (line.StartsWith("Damage Given"))
|
||||
{
|
||||
Regex regexTaken = new Regex("Damage Given to \"(.+?)\" - (\\d+) in (\\d+) hits?");
|
||||
Match givenMatch = regexTaken.Match(line);
|
||||
|
||||
if (givenMatch.Success)
|
||||
{
|
||||
string name = givenMatch.Groups[1].Value;
|
||||
int damage = int.Parse(givenMatch.Groups[2].Value);
|
||||
int hits = int.Parse(givenMatch.Groups[3].Value);
|
||||
|
||||
if(damage < 100 && damage > minDamageToCount && taggedPlayers.FirstOrDefault(player => player.Item1 == name) == null)
|
||||
// not in list yet so add
|
||||
taggedPlayers.Add(new Tuple<string, int, int>(name, damage, hits));
|
||||
}
|
||||
}
|
||||
else if (line.ToLower().Contains(calcKeyWord + ' '))
|
||||
{
|
||||
// Calculate
|
||||
string expression = line.Substring(line.IndexOf(calcKeyWord + ' ') + calcKeyWord.Length);
|
||||
if (String.IsNullOrWhiteSpace(expression))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
double res = mathEval.Evaluate(expression);
|
||||
Thread.Sleep(700); // so the chat message is shown if we requested it ourselves
|
||||
ExecuteCommands(true, $"say \"Answer: {res}\"");
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
else if (line.ToLower().Contains(fuelPriceKeyWord))
|
||||
{
|
||||
if (DateTime.Now - lastFuelRequestTime < TimeSpan.FromMinutes(1))
|
||||
continue;
|
||||
|
||||
string apiKey = "";
|
||||
string nordOelID = "69ad1928-e972-421b-a33c-4319da73deaa";
|
||||
string shellID = "a507dd35-4a7f-46d1-86b6-accc4769a47b";
|
||||
string request = $"https://creativecommons.tankerkoenig.de/json/prices.php?ids={nordOelID},{shellID}&apikey={apiKey}";
|
||||
|
||||
HttpResponseMessage response;
|
||||
try
|
||||
{
|
||||
response = httpClient.GetAsync(request).Result;
|
||||
|
||||
lastFuelRequestTime = DateTime.Now;
|
||||
}
|
||||
catch { continue; }
|
||||
|
||||
var responseString = response.Content.ReadAsStringAsync().Result;
|
||||
|
||||
dynamic? jsonResponse = JsonConvert.DeserializeObject(responseString);
|
||||
|
||||
if (jsonResponse == null || (bool)jsonResponse!.ok == false)
|
||||
continue;
|
||||
|
||||
var cmds = new List<string>();
|
||||
|
||||
if((string)jsonResponse!.prices[nordOelID].status == "open")
|
||||
{
|
||||
cmds.Add($"say \"NORDOEL: Super: {(string)jsonResponse!.prices[nordOelID].e5} Euro, Diesel: {(string)jsonResponse!.prices[nordOelID].diesel} Euro\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
cmds.Add("say \"NORDOEL: Geschlossen\"");
|
||||
}
|
||||
|
||||
if ((string)jsonResponse.prices[shellID].status == "open")
|
||||
{
|
||||
cmds.Add($"say \"Shell: Super: {(string)jsonResponse!.prices[shellID].e5} Euro, Diesel: {(string)jsonResponse!.prices[shellID].diesel} Euro\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
cmds.Add("say \"Shell: Geschlossen\"");
|
||||
}
|
||||
|
||||
ExecuteCommands(true, cmds.ToArray());
|
||||
}
|
||||
else if (line.ToLower().Contains(weatherKeyWord))
|
||||
{
|
||||
string request = $"https://api.open-meteo.com/v1/forecast?latitude=54.2335&longitude=10.3397&hourly=temperature_2m,cloudcover&daily=precipitation_hours&timezone=Europe%2FBerlin";
|
||||
|
||||
HttpResponseMessage response;
|
||||
try
|
||||
{
|
||||
response = httpClient.GetAsync(request).Result;
|
||||
}
|
||||
catch { continue; }
|
||||
|
||||
var responseString = response.Content.ReadAsStringAsync().Result;
|
||||
|
||||
dynamic? jsonResponse = JsonConvert.DeserializeObject(responseString);
|
||||
|
||||
if (jsonResponse == null)
|
||||
continue;
|
||||
|
||||
string cmd = $"say \"{((double)jsonResponse!.hourly.temperature_2m[0]).ToString(System.Globalization.CultureInfo.InvariantCulture)} C, Wolkendecke {(double)jsonResponse!.hourly.cloudcover[0]} %, Stunden Regen: {Math.Round((double)jsonResponse!.daily.precipitation_hours[0] * 100 / 24)} %\"";
|
||||
|
||||
ExecuteCommands(true, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
bool didPlayerDie = damageTakenTotal >= 100;
|
||||
|
||||
if (!didPlayerDie)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (areListsEqual(taggedPlayers, prevTaggedPlayers))
|
||||
{
|
||||
// Last is the same as previous, likely dealt damage, died and now the round ended and it was printed again
|
||||
Console.WriteLine("Double console output.");
|
||||
continue;
|
||||
}
|
||||
|
||||
prevTaggedPlayers = taggedPlayers;
|
||||
|
||||
if (taggedPlayers.Count < 1)
|
||||
continue;
|
||||
|
||||
string[] commands = new string[taggedPlayers.Count];
|
||||
|
||||
// We didn't have a round end, but got killed
|
||||
for (int i = 0; i < commands.Length; i++)
|
||||
{
|
||||
commands[i] += "say_team \"";
|
||||
if (taggedPlayers[i].Item2 > 90)
|
||||
{
|
||||
// One-shot
|
||||
commands[i] += $"{taggedPlayers[i].Item1} is one-shot {taggedPlayers[i].Item2}";
|
||||
}
|
||||
else if (taggedPlayers[i].Item2 >= 70)
|
||||
{
|
||||
// Lit
|
||||
commands[i] += $"{taggedPlayers[i].Item1} is lit for {taggedPlayers[i].Item2}";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Tagged
|
||||
commands[i] += $"{taggedPlayers[i].Item1} is tagged for {taggedPlayers[i].Item2}";
|
||||
}
|
||||
|
||||
commands[i] += "\"";
|
||||
}
|
||||
|
||||
// Notify players
|
||||
ExecuteCommands(false, commands);
|
||||
}
|
||||
}
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
static bool areListsEqual(List<Tuple<string, int, int>> list1, List<Tuple<string, int, int>> list2)
|
||||
{
|
||||
if (list1.Count != list2.Count)
|
||||
return false;
|
||||
|
||||
// still same length
|
||||
for (int i = 0; i < list1.Count; i++)
|
||||
{
|
||||
if (list1[i].Item1 != list2[i].Item1)
|
||||
return false;
|
||||
if (list1[i].Item2 != list2[i].Item2)
|
||||
return false;
|
||||
if (list1[i].Item3 != list2[i].Item3)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ExecuteCommands(bool triggeredByCommand, params string[] cmds)
|
||||
{
|
||||
if (cmds == null)
|
||||
return false;
|
||||
|
||||
IntPtr hWnd = FindWindow("Valve001", null!);
|
||||
|
||||
if (hWnd == IntPtr.Zero)
|
||||
return false;
|
||||
|
||||
int chatTimeoutMs = 700;
|
||||
int commandsHandled = 0;
|
||||
|
||||
for (int i = 0; i < cmds.Length; i++)
|
||||
{
|
||||
if (cmds[i] == null)
|
||||
continue;
|
||||
|
||||
cmds[i] = cmds[i].Trim();
|
||||
|
||||
COPYDATASTRUCT data;
|
||||
data.dwData = 0;
|
||||
data.cbData = (uint)cmds[i].Length + 1;
|
||||
data.lpData = cmds[i];
|
||||
|
||||
if (triggeredByCommand)
|
||||
Thread.Sleep(chatTimeoutMs);
|
||||
|
||||
// Allocate for data
|
||||
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(data));
|
||||
Marshal.StructureToPtr(data, ptr, false);
|
||||
|
||||
int ret = SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, ptr);
|
||||
|
||||
Console.WriteLine(cmds[i]);
|
||||
|
||||
// Free data
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
|
||||
if (ret == 0)
|
||||
commandsHandled++;
|
||||
|
||||
if (cmds[i].StartsWith("say") || cmds[i].StartsWith("say_team"))
|
||||
Thread.Sleep(chatTimeoutMs);
|
||||
}
|
||||
|
||||
return cmds.Length > 0 && commandsHandled == cmds.Length;
|
||||
}
|
||||
}
|
||||
|
||||
struct COPYDATASTRUCT
|
||||
{
|
||||
public ulong dwData;
|
||||
public uint cbData;
|
||||
public string lpData;
|
||||
}
|
225
Shared/LoreSoft.MathExpressions/ConvertExpression.cs
Normal file
225
Shared/LoreSoft.MathExpressions/ConvertExpression.cs
Normal file
|
@ -0,0 +1,225 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LoreSoft.MathExpressions.Properties;
|
||||
using LoreSoft.MathExpressions.UnitConversion;
|
||||
using System.Globalization;
|
||||
using LoreSoft.MathExpressions.Metadata;
|
||||
using System.Reflection;
|
||||
|
||||
namespace LoreSoft.MathExpressions
|
||||
{
|
||||
/// <summary>
|
||||
/// A class representing unit convertion expressions.
|
||||
/// </summary>
|
||||
public class ConvertExpression : ExpressionBase
|
||||
{
|
||||
private static Dictionary<string, ConvertionMap> convertionCache;
|
||||
private static object cacheLock = new object();
|
||||
|
||||
private ConvertionMap current;
|
||||
private string expression;
|
||||
|
||||
/// <summary>The format of a convertion expression.</summary>
|
||||
public const string ExpressionFormat = "[{0}->{1}]";
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="ConvertExpression"/> class.</summary>
|
||||
/// <param name="expression">The convertion expression for this instance.</param>
|
||||
public ConvertExpression(string expression)
|
||||
{
|
||||
VerifyCache();
|
||||
if (!convertionCache.ContainsKey(expression))
|
||||
throw new ArgumentException(Resources.InvalidConvertionExpression + expression, "expression");
|
||||
|
||||
this.expression = expression;
|
||||
current = convertionCache[expression];
|
||||
base.Evaluate = new MathEvaluate(Convert);
|
||||
}
|
||||
|
||||
/// <summary>Gets the number of arguments this expression uses.</summary>
|
||||
/// <value>The argument count.</value>
|
||||
public override int ArgumentCount
|
||||
{
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
/// <summary>Convert the numbers to the new unit.</summary>
|
||||
/// <param name="numbers">The numbers used in the convertion.</param>
|
||||
/// <returns>The result of the convertion execution.</returns>
|
||||
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
|
||||
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
|
||||
public double Convert(double[] numbers)
|
||||
{
|
||||
base.Validate(numbers);
|
||||
double fromValue = numbers[0];
|
||||
|
||||
switch (current.UnitType)
|
||||
{
|
||||
case UnitType.Length:
|
||||
return LengthConverter.Convert(
|
||||
(LengthUnit) current.FromUnit,
|
||||
(LengthUnit) current.ToUnit,
|
||||
fromValue);
|
||||
case UnitType.Mass:
|
||||
return MassConverter.Convert(
|
||||
(MassUnit) current.FromUnit,
|
||||
(MassUnit) current.ToUnit,
|
||||
fromValue);
|
||||
case UnitType.Speed:
|
||||
return SpeedConverter.Convert(
|
||||
(SpeedUnit) current.FromUnit,
|
||||
(SpeedUnit) current.ToUnit,
|
||||
fromValue);
|
||||
case UnitType.Temperature:
|
||||
return TemperatureConverter.Convert(
|
||||
(TemperatureUnit) current.FromUnit,
|
||||
(TemperatureUnit) current.ToUnit,
|
||||
fromValue);
|
||||
case UnitType.Time:
|
||||
return TimeConverter.Convert(
|
||||
(TimeUnit) current.FromUnit,
|
||||
(TimeUnit) current.ToUnit,
|
||||
fromValue);
|
||||
case UnitType.Volume:
|
||||
return VolumeConverter.Convert(
|
||||
(VolumeUnit) current.FromUnit,
|
||||
(VolumeUnit) current.ToUnit,
|
||||
fromValue);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("numbers");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///<summary>
|
||||
/// Determines whether the specified expression name is for unit convertion.
|
||||
///</summary>
|
||||
///<param name="expression">The expression to check.</param>
|
||||
///<returns><c>true</c> if the specified expression is a unit convertion; otherwise, <c>false</c>.</returns>
|
||||
public static bool IsConvertExpression(string expression)
|
||||
{
|
||||
//do basic checks before creating cache
|
||||
if (string.IsNullOrEmpty(expression))
|
||||
return false;
|
||||
|
||||
if (expression[0] != '[')
|
||||
return false;
|
||||
|
||||
VerifyCache();
|
||||
return convertionCache.ContainsKey(expression);
|
||||
}
|
||||
|
||||
|
||||
private static void VerifyCache()
|
||||
{
|
||||
if (convertionCache != null)
|
||||
return;
|
||||
|
||||
lock (cacheLock)
|
||||
{
|
||||
if (convertionCache != null)
|
||||
return;
|
||||
|
||||
convertionCache = new Dictionary<string, ConvertionMap>(
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
AddToCache<LengthUnit>(UnitType.Length);
|
||||
AddToCache<MassUnit>(UnitType.Mass);
|
||||
AddToCache<SpeedUnit>(UnitType.Speed);
|
||||
AddToCache<TemperatureUnit>(UnitType.Temperature);
|
||||
AddToCache<TimeUnit>(UnitType.Time);
|
||||
AddToCache<VolumeUnit>(UnitType.Volume);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddToCache<T>(UnitType unitType)
|
||||
where T : struct, IComparable, IFormattable, IConvertible
|
||||
{
|
||||
Type enumType = typeof(T);
|
||||
int[] a = (int[])Enum.GetValues(enumType);
|
||||
|
||||
for (int x = 0; x < a.Length; x++)
|
||||
{
|
||||
MemberInfo parentInfo = GetMemberInfo(enumType, Enum.GetName(enumType, x));
|
||||
string parrentKey = AttributeReader.GetAbbreviation(parentInfo);
|
||||
|
||||
for (int i = 0; i < a.Length; i++)
|
||||
{
|
||||
if (x == i)
|
||||
continue;
|
||||
|
||||
MemberInfo info = GetMemberInfo(enumType, Enum.GetName(enumType, i));
|
||||
|
||||
string key = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
ExpressionFormat,
|
||||
parrentKey,
|
||||
AttributeReader.GetAbbreviation(info));
|
||||
|
||||
convertionCache.Add(
|
||||
key, new ConvertionMap(unitType, x, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static MemberInfo GetMemberInfo(Type type, string name)
|
||||
{
|
||||
MemberInfo[] info = type.GetMember(name);
|
||||
if (info == null || info.Length == 0)
|
||||
return null;
|
||||
|
||||
return info[0];
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
|
||||
/// </returns>
|
||||
/// <filterPriority>2</filterPriority>
|
||||
public override string ToString()
|
||||
{
|
||||
return expression;
|
||||
}
|
||||
|
||||
private class ConvertionMap
|
||||
{
|
||||
public ConvertionMap(UnitType unitType, int fromUnit, int toUnit)
|
||||
{
|
||||
_unitType = unitType;
|
||||
_fromUnit = fromUnit;
|
||||
_toUnit = toUnit;
|
||||
}
|
||||
|
||||
private UnitType _unitType;
|
||||
|
||||
public UnitType UnitType
|
||||
{
|
||||
get { return _unitType; }
|
||||
}
|
||||
|
||||
private int _fromUnit;
|
||||
|
||||
public int FromUnit
|
||||
{
|
||||
get { return _fromUnit; }
|
||||
}
|
||||
|
||||
private int _toUnit;
|
||||
|
||||
public int ToUnit
|
||||
{
|
||||
get { return _toUnit; }
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
"{0}, [{1}->{2}]",
|
||||
_unitType,
|
||||
_fromUnit,
|
||||
_toUnit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
35
Shared/LoreSoft.MathExpressions/ExpressionBase.cs
Normal file
35
Shared/LoreSoft.MathExpressions/ExpressionBase.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using LoreSoft.MathExpressions.Properties;
|
||||
|
||||
namespace LoreSoft.MathExpressions
|
||||
{
|
||||
/// <summary>The base class for expressions</summary>
|
||||
public abstract class ExpressionBase : IExpression
|
||||
{
|
||||
/// <summary>Gets the number of arguments this expression uses.</summary>
|
||||
/// <value>The argument count.</value>
|
||||
public abstract int ArgumentCount { get; }
|
||||
|
||||
private MathEvaluate _evaluateDelegate;
|
||||
|
||||
/// <summary>Gets or sets the evaluate delegate.</summary>
|
||||
/// <value>The evaluate delegate.</value>
|
||||
public virtual MathEvaluate Evaluate
|
||||
{
|
||||
get { return _evaluateDelegate; }
|
||||
set { _evaluateDelegate = value; }
|
||||
}
|
||||
|
||||
/// <summary>Validates the specified numbers for the expression.</summary>
|
||||
/// <param name="numbers">The numbers to validate.</param>
|
||||
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
|
||||
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
|
||||
protected void Validate(double[] numbers)
|
||||
{
|
||||
if (numbers == null)
|
||||
throw new ArgumentNullException("numbers");
|
||||
if (numbers.Length != ArgumentCount)
|
||||
throw new ArgumentException(Resources.InvalidLengthOfArray, "numbers");
|
||||
}
|
||||
}
|
||||
}
|
113
Shared/LoreSoft.MathExpressions/FunctionExpression.cs
Normal file
113
Shared/LoreSoft.MathExpressions/FunctionExpression.cs
Normal file
|
@ -0,0 +1,113 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using LoreSoft.MathExpressions.Properties;
|
||||
using System.Globalization;
|
||||
|
||||
namespace LoreSoft.MathExpressions
|
||||
{
|
||||
/// <summary>
|
||||
/// A class representing the System.Math function expressions
|
||||
/// </summary>
|
||||
public class FunctionExpression : ExpressionBase
|
||||
{
|
||||
// must be sorted
|
||||
/// <summary>The supported math functions by this class.</summary>
|
||||
private static readonly string[] mathFunctions = new string[]
|
||||
{
|
||||
"abs", "acos", "asin", "atan", "ceiling", "cos", "cosh", "exp",
|
||||
"floor", "log", "log10", "sin", "sinh", "sqrt", "tan", "tanh"
|
||||
};
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="FunctionExpression"/> class.</summary>
|
||||
/// <param name="function">The function name for this instance.</param>
|
||||
public FunctionExpression(string function) : this(function, true)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="FunctionExpression"/> class.</summary>
|
||||
/// <param name="function">The function.</param>
|
||||
/// <param name="validate">if set to <c>true</c> to validate the function name.</param>
|
||||
internal FunctionExpression(string function, bool validate)
|
||||
{
|
||||
function = function.ToLowerInvariant();
|
||||
|
||||
if (validate && !IsFunction(function))
|
||||
throw new ArgumentException(
|
||||
string.Format(CultureInfo.CurrentCulture, Resources.InvalidFunctionName, _function),
|
||||
"function");
|
||||
|
||||
_function = function;
|
||||
base.Evaluate = new MathEvaluate(Execute);
|
||||
}
|
||||
|
||||
private string _function;
|
||||
|
||||
/// <summary>Gets the name function for this instance.</summary>
|
||||
/// <value>The function name.</value>
|
||||
public string Function
|
||||
{
|
||||
get { return _function; }
|
||||
}
|
||||
|
||||
/// <summary>Executes the function on specified numbers.</summary>
|
||||
/// <param name="numbers">The numbers used in the function.</param>
|
||||
/// <returns>The result of the function execution.</returns>
|
||||
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
|
||||
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
|
||||
public double Execute(double[] numbers)
|
||||
{
|
||||
base.Validate(numbers);
|
||||
|
||||
string function = char.ToUpperInvariant(_function[0]) + _function.Substring(1);
|
||||
MethodInfo method = typeof (Math).GetMethod(
|
||||
function,
|
||||
BindingFlags.Static | BindingFlags.Public,
|
||||
null,
|
||||
new Type[] { typeof(double) },
|
||||
null);
|
||||
|
||||
if (method == null)
|
||||
throw new InvalidOperationException(
|
||||
string.Format(CultureInfo.CurrentCulture,
|
||||
Resources.InvalidFunctionName, _function));
|
||||
|
||||
object[] parameters = new object[numbers.Length];
|
||||
Array.Copy(numbers, parameters, numbers.Length);
|
||||
return (double) method.Invoke(null, parameters);
|
||||
}
|
||||
|
||||
/// <summary>Gets the number of arguments this expression uses.</summary>
|
||||
/// <value>The argument count.</value>
|
||||
public override int ArgumentCount
|
||||
{
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
/// <summary>Determines whether the specified function name is a function.</summary>
|
||||
/// <param name="function">The function name.</param>
|
||||
/// <returns><c>true</c> if the specified name is a function; otherwise, <c>false</c>.</returns>
|
||||
public static bool IsFunction(string function)
|
||||
{
|
||||
return (Array.BinarySearch(
|
||||
mathFunctions, function,
|
||||
StringComparer.OrdinalIgnoreCase) >= 0);
|
||||
}
|
||||
|
||||
/// <summary>Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.</summary>
|
||||
/// <returns>A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.</returns>
|
||||
/// <filterPriority>2</filterPriority>
|
||||
public override string ToString()
|
||||
{
|
||||
return _function;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the function names.
|
||||
/// </summary>
|
||||
/// <returns>An array of function names.</returns>
|
||||
public static string[] GetFunctionNames()
|
||||
{
|
||||
return (string[])mathFunctions.Clone();
|
||||
}
|
||||
}
|
||||
}
|
2
Shared/LoreSoft.MathExpressions/GlobalSuppressions.cs
Normal file
2
Shared/LoreSoft.MathExpressions/GlobalSuppressions.cs
Normal file
|
@ -0,0 +1,2 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "LoreSoft.MathExpressions.Metadata")]
|
15
Shared/LoreSoft.MathExpressions/IExpression.cs
Normal file
15
Shared/LoreSoft.MathExpressions/IExpression.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
namespace LoreSoft.MathExpressions
|
||||
{
|
||||
/// <summary>
|
||||
/// The interface used when running expressions
|
||||
/// </summary>
|
||||
public interface IExpression
|
||||
{
|
||||
/// <summary>Gets the number of arguments this expression uses.</summary>
|
||||
/// <value>The argument count.</value>
|
||||
int ArgumentCount { get; }
|
||||
/// <summary>Gets or sets the evaluate delegate.</summary>
|
||||
/// <value>The evaluate delegate.</value>
|
||||
MathEvaluate Evaluate { get; set; }
|
||||
}
|
||||
}
|
185
Shared/LoreSoft.MathExpressions/LoreSoft.MathExpressions.csproj
Normal file
185
Shared/LoreSoft.MathExpressions/LoreSoft.MathExpressions.csproj
Normal file
|
@ -0,0 +1,185 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{B6813448-FCE2-429C-81B5-9722FEA839B8}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>LoreSoft.MathExpressions</RootNamespace>
|
||||
<AssemblyName>LoreSoft.MathExpressions</AssemblyName>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>MathExpressions.snk</AssemblyOriginatorKeyFile>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<OldToolsVersion>3.5</OldToolsVersion>
|
||||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Build\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>..\..\Build\Debug\LoreSoft.MathExpressions.XML</DocumentationFile>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\..\Build\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>..\..\Build\Release\LoreSoft.MathExpressions.XML</DocumentationFile>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>..\..\Build\x86\Debug\</OutputPath>
|
||||
<DefineConstants>CODE_ANALYSIS;DEBUG;TRACE</DefineConstants>
|
||||
<DocumentationFile>..\..\Build\Debug\LoreSoft.MathExpressions.XML</DocumentationFile>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
|
||||
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<OutputPath>..\..\Build\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<DocumentationFile>..\..\Build\Release\LoreSoft.MathExpressions.XML</DocumentationFile>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
|
||||
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>..\..\Build\x64\Debug\</OutputPath>
|
||||
<DefineConstants>CODE_ANALYSIS;DEBUG;TRACE</DefineConstants>
|
||||
<DocumentationFile>..\..\Build\Debug\LoreSoft.MathExpressions.XML</DocumentationFile>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
|
||||
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
|
||||
<OutputPath>..\..\Build\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<DocumentationFile>..\..\Build\Release\LoreSoft.MathExpressions.XML</DocumentationFile>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
|
||||
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ConvertExpression.cs" />
|
||||
<Compile Include="ExpressionBase.cs" />
|
||||
<Compile Include="FunctionExpression.cs" />
|
||||
<Compile Include="GlobalSuppressions.cs" />
|
||||
<Compile Include="IExpression.cs" />
|
||||
<Compile Include="MathEvaluate.cs" />
|
||||
<Compile Include="MathEvaluator.cs" />
|
||||
<Compile Include="MathOperators.cs" />
|
||||
<Compile Include="Metadata\AttributeReader.cs" />
|
||||
<Compile Include="NumberExpression.cs" />
|
||||
<Compile Include="OperatorExpression.cs" />
|
||||
<Compile Include="ParseException.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Metadata\AbbreviationAttribute.cs" />
|
||||
<Compile Include="UnitConversion\LengthConverter.cs" />
|
||||
<Compile Include="UnitConversion\MassConverter.cs" />
|
||||
<Compile Include="UnitConversion\SpeedConverter.cs" />
|
||||
<Compile Include="UnitConversion\TemperatureConverter.cs" />
|
||||
<Compile Include="UnitConversion\TimeConverter.cs" />
|
||||
<Compile Include="UnitConversion\UnitType.cs" />
|
||||
<Compile Include="UnitConversion\VolumeConverter.cs" />
|
||||
<Compile Include="VariableDictionary.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="MathExpressions.snk" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Windows Installer 3.1</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
7
Shared/LoreSoft.MathExpressions/MathEvaluate.cs
Normal file
7
Shared/LoreSoft.MathExpressions/MathEvaluate.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace LoreSoft.MathExpressions
|
||||
{
|
||||
/// <summary>Delegate used by an expression to do the math evaluation.</summary>
|
||||
/// <param name="numbers">The numbers to evaluate.</param>
|
||||
/// <returns>The result of the evaluated numbers.</returns>
|
||||
public delegate double MathEvaluate(double[] numbers);
|
||||
}
|
458
Shared/LoreSoft.MathExpressions/MathEvaluator.cs
Normal file
458
Shared/LoreSoft.MathExpressions/MathEvaluator.cs
Normal file
|
@ -0,0 +1,458 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using LoreSoft.MathExpressions.Properties;
|
||||
using System.Globalization;
|
||||
|
||||
namespace LoreSoft.MathExpressions
|
||||
{
|
||||
/// <summary>
|
||||
/// Evaluate math expressions
|
||||
/// </summary>
|
||||
/// <example>Using the MathEvaluator to calculate a math expression.
|
||||
/// <code>
|
||||
/// MathEvaluator eval = new MathEvaluator();
|
||||
/// //basic math
|
||||
/// double result = eval.Evaluate("(2 + 1) * (1 + 2)");
|
||||
/// //calling a function
|
||||
/// result = eval.Evaluate("sqrt(4)");
|
||||
/// //evaluate trigonometric
|
||||
/// result = eval.Evaluate("cos(pi * 45 / 180.0)");
|
||||
/// //convert inches to feet
|
||||
/// result = eval.Evaluate("12 [in->ft]");
|
||||
/// //use variable
|
||||
/// result = eval.Evaluate("answer * 10");
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class MathEvaluator : IDisposable
|
||||
{
|
||||
/// <summary>The name of the answer variable.</summary>
|
||||
/// <seealso cref="Variables"/>
|
||||
public const string AnswerVariable = "answer";
|
||||
|
||||
//instance scope to optimize reuse
|
||||
private Stack<string> _symbolStack;
|
||||
private Queue<IExpression> _expressionQueue;
|
||||
private Dictionary<string, IExpression> _expressionCache;
|
||||
private StringBuilder _buffer;
|
||||
private Stack<double> _calculationStack;
|
||||
private Stack<double> _parameters;
|
||||
private List<string> _innerFunctions;
|
||||
|
||||
private StringReader _expressionReader;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MathEvaluator"/> class.
|
||||
/// </summary>
|
||||
public MathEvaluator()
|
||||
{
|
||||
_variables = new VariableDictionary(this);
|
||||
_innerFunctions = new List<string>(FunctionExpression.GetFunctionNames());
|
||||
_functions = new ReadOnlyCollection<string>(_innerFunctions);
|
||||
_expressionCache = new Dictionary<string, IExpression>(StringComparer.OrdinalIgnoreCase);
|
||||
_symbolStack = new Stack<string>();
|
||||
_expressionQueue = new Queue<IExpression>();
|
||||
_buffer = new StringBuilder();
|
||||
_calculationStack = new Stack<double>();
|
||||
_parameters = new Stack<double>(2);
|
||||
}
|
||||
|
||||
private VariableDictionary _variables;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the variables collections.
|
||||
/// </summary>
|
||||
/// <value>The variables for <see cref="MathEvaluator"/>.</value>
|
||||
public VariableDictionary Variables
|
||||
{
|
||||
get { return _variables; }
|
||||
}
|
||||
|
||||
private ReadOnlyCollection<string> _functions;
|
||||
|
||||
/// <summary>Gets the functions available to <see cref="MathEvaluator"/>.</summary>
|
||||
/// <value>The functions for <see cref="MathEvaluator"/>.</value>
|
||||
/// <seealso cref="RegisterFunction"/>
|
||||
public ReadOnlyCollection<string> Functions
|
||||
{
|
||||
get { return _functions; }
|
||||
}
|
||||
|
||||
/// <summary>Gets the answer from the last evaluation.</summary>
|
||||
/// <value>The answer variable value.</value>
|
||||
/// <seealso cref="Variables"/>
|
||||
public double Answer
|
||||
{
|
||||
get { return _variables[AnswerVariable]; }
|
||||
}
|
||||
|
||||
/// <summary>Evaluates the specified expression.</summary>
|
||||
/// <param name="expression">The expression to evaluate.</param>
|
||||
/// <returns>The result of the evaluated expression.</returns>
|
||||
/// <exception cref="ArgumentNullException">When expression is null or empty.</exception>
|
||||
/// <exception cref="ParseException">When there is an error parsing the expression.</exception>
|
||||
public double Evaluate(string expression)
|
||||
{
|
||||
if (string.IsNullOrEmpty(expression))
|
||||
throw new ArgumentNullException("expression");
|
||||
|
||||
_expressionReader = new StringReader(expression);
|
||||
_symbolStack.Clear();
|
||||
_expressionQueue.Clear();
|
||||
|
||||
ParseExpressionToQueue();
|
||||
|
||||
double result = CalculateFromQueue();
|
||||
|
||||
_variables[AnswerVariable] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>Registers a function for the <see cref="MathEvaluator"/>.</summary>
|
||||
/// <param name="functionName">Name of the function.</param>
|
||||
/// <param name="expression">An instance of <see cref="IExpression"/> for the function.</param>
|
||||
/// <exception cref="ArgumentNullException">When functionName or expression are null.</exception>
|
||||
/// <exception cref="ArgumentException">When IExpression.Evaluate property is null or the functionName is already registered.</exception>
|
||||
/// <seealso cref="Functions"/>
|
||||
/// <seealso cref="IExpression"/>
|
||||
public void RegisterFunction(string functionName, IExpression expression)
|
||||
{
|
||||
if (string.IsNullOrEmpty(functionName))
|
||||
throw new ArgumentNullException("functionName");
|
||||
if (expression == null)
|
||||
throw new ArgumentNullException("expression");
|
||||
if (expression.Evaluate == null)
|
||||
throw new ArgumentException(Resources.EvaluatePropertyCanNotBeNull, "expression");
|
||||
if (_innerFunctions.BinarySearch(functionName) >= 0)
|
||||
throw new ArgumentException(
|
||||
string.Format(CultureInfo.CurrentCulture,
|
||||
Resources.FunctionNameRegistered, functionName), "functionName");
|
||||
|
||||
_innerFunctions.Add(functionName);
|
||||
_innerFunctions.Sort();
|
||||
_expressionCache.Add(functionName, expression);
|
||||
}
|
||||
|
||||
/// <summary>Determines whether the specified name is a function.</summary>
|
||||
/// <param name="name">The name of the function.</param>
|
||||
/// <returns><c>true</c> if the specified name is function; otherwise, <c>false</c>.</returns>
|
||||
internal bool IsFunction(string name)
|
||||
{
|
||||
return (_innerFunctions.BinarySearch(name, StringComparer.OrdinalIgnoreCase) >= 0);
|
||||
}
|
||||
|
||||
private void ParseExpressionToQueue()
|
||||
{
|
||||
char l = '\0';
|
||||
char c = '\0';
|
||||
|
||||
do
|
||||
{
|
||||
// last non white space char
|
||||
if (!char.IsWhiteSpace(c))
|
||||
l = c;
|
||||
|
||||
c = (char)_expressionReader.Read();
|
||||
|
||||
if (char.IsWhiteSpace(c))
|
||||
continue;
|
||||
|
||||
if (TryNumber(c, l))
|
||||
continue;
|
||||
|
||||
if (TryString(c))
|
||||
continue;
|
||||
|
||||
if (TryStartGroup(c))
|
||||
continue;
|
||||
|
||||
if (TryOperator(c))
|
||||
continue;
|
||||
|
||||
if (TryEndGroup(c))
|
||||
continue;
|
||||
|
||||
if (TryConvert(c))
|
||||
continue;
|
||||
|
||||
throw new ParseException(Resources.InvalidCharacterEncountered + c);
|
||||
} while (_expressionReader.Peek() != -1);
|
||||
|
||||
ProcessSymbolStack();
|
||||
}
|
||||
|
||||
private bool TryConvert(char c)
|
||||
{
|
||||
if (c != '[')
|
||||
return false;
|
||||
|
||||
_buffer.Length = 0;
|
||||
_buffer.Append(c);
|
||||
|
||||
char p = (char)_expressionReader.Peek();
|
||||
while (char.IsLetter(p) || char.IsWhiteSpace(p) || p == '-' || p == '>' || p == ']')
|
||||
{
|
||||
if (!char.IsWhiteSpace(p))
|
||||
_buffer.Append((char)_expressionReader.Read());
|
||||
else
|
||||
_expressionReader.Read();
|
||||
|
||||
if (p == ']')
|
||||
break;
|
||||
|
||||
p = (char)_expressionReader.Peek();
|
||||
}
|
||||
|
||||
if (ConvertExpression.IsConvertExpression(_buffer.ToString()))
|
||||
{
|
||||
IExpression e = GetExpressionFromSymbol(_buffer.ToString());
|
||||
_expressionQueue.Enqueue(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new ParseException(Resources.InvalidConvertionExpression + _buffer);
|
||||
}
|
||||
|
||||
private bool TryString(char c)
|
||||
{
|
||||
if (!char.IsLetter(c))
|
||||
return false;
|
||||
|
||||
_buffer.Length = 0;
|
||||
_buffer.Append(c);
|
||||
|
||||
char p = (char)_expressionReader.Peek();
|
||||
while (char.IsLetter(p))
|
||||
{
|
||||
_buffer.Append((char)_expressionReader.Read());
|
||||
p = (char)_expressionReader.Peek();
|
||||
}
|
||||
|
||||
if (_variables.ContainsKey(_buffer.ToString()))
|
||||
{
|
||||
double value = _variables[_buffer.ToString()];
|
||||
NumberExpression expression = new NumberExpression(value);
|
||||
_expressionQueue.Enqueue(expression);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsFunction(_buffer.ToString()))
|
||||
{
|
||||
_symbolStack.Push(_buffer.ToString());
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new ParseException(Resources.InvalidVariableEncountered + _buffer);
|
||||
}
|
||||
|
||||
private bool TryStartGroup(char c)
|
||||
{
|
||||
if (c != '(')
|
||||
return false;
|
||||
|
||||
_symbolStack.Push(c.ToString());
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryEndGroup(char c)
|
||||
{
|
||||
if (c != ')')
|
||||
return false;
|
||||
|
||||
bool hasStart = false;
|
||||
|
||||
while (_symbolStack.Count > 0)
|
||||
{
|
||||
string p = _symbolStack.Pop();
|
||||
if (p == "(")
|
||||
{
|
||||
hasStart = true;
|
||||
|
||||
if (_symbolStack.Count == 0)
|
||||
break;
|
||||
|
||||
string n = _symbolStack.Peek();
|
||||
if (FunctionExpression.IsFunction(n))
|
||||
{
|
||||
p = _symbolStack.Pop();
|
||||
IExpression f = GetExpressionFromSymbol(p);
|
||||
_expressionQueue.Enqueue(f);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
IExpression e = GetExpressionFromSymbol(p);
|
||||
_expressionQueue.Enqueue(e);
|
||||
}
|
||||
|
||||
if (!hasStart)
|
||||
throw new ParseException(Resources.UnbalancedParentheses);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryOperator(char c)
|
||||
{
|
||||
if (!OperatorExpression.IsSymbol(c))
|
||||
return false;
|
||||
|
||||
bool repeat;
|
||||
string s = c.ToString();
|
||||
|
||||
do
|
||||
{
|
||||
string p = _symbolStack.Count == 0 ? string.Empty : _symbolStack.Peek();
|
||||
repeat = false;
|
||||
if (_symbolStack.Count == 0)
|
||||
_symbolStack.Push(s);
|
||||
else if (p == "(")
|
||||
_symbolStack.Push(s);
|
||||
else if (Precedence(s) > Precedence(p))
|
||||
_symbolStack.Push(s);
|
||||
else
|
||||
{
|
||||
IExpression e = GetExpressionFromSymbol(_symbolStack.Pop());
|
||||
_expressionQueue.Enqueue(e);
|
||||
repeat = true;
|
||||
}
|
||||
} while (repeat);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryNumber(char c, char l)
|
||||
{
|
||||
bool isNumber = NumberExpression.IsNumber(c);
|
||||
// only negative when last char is group start or symbol
|
||||
bool isNegative = NumberExpression.IsNegativeSign(c) &&
|
||||
(l == '\0' || l == '(' || OperatorExpression.IsSymbol(l));
|
||||
|
||||
if (!isNumber && !isNegative)
|
||||
return false;
|
||||
|
||||
_buffer.Length = 0;
|
||||
_buffer.Append(c);
|
||||
|
||||
char p = (char)_expressionReader.Peek();
|
||||
while (NumberExpression.IsNumber(p))
|
||||
{
|
||||
_buffer.Append((char)_expressionReader.Read());
|
||||
p = (char)_expressionReader.Peek();
|
||||
}
|
||||
|
||||
double value;
|
||||
if (!(double.TryParse(_buffer.ToString(), out value)))
|
||||
throw new ParseException(Resources.InvalidNumberFormat + _buffer);
|
||||
|
||||
NumberExpression expression = new NumberExpression(value);
|
||||
_expressionQueue.Enqueue(expression);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ProcessSymbolStack()
|
||||
{
|
||||
while (_symbolStack.Count > 0)
|
||||
{
|
||||
string p = _symbolStack.Pop();
|
||||
if (p.Length == 1 && p == "(")
|
||||
throw new ParseException(Resources.UnbalancedParentheses);
|
||||
|
||||
IExpression e = GetExpressionFromSymbol(p);
|
||||
_expressionQueue.Enqueue(e);
|
||||
}
|
||||
}
|
||||
|
||||
private IExpression GetExpressionFromSymbol(string p)
|
||||
{
|
||||
IExpression e;
|
||||
|
||||
if (_expressionCache.ContainsKey(p))
|
||||
e = _expressionCache[p];
|
||||
else if (OperatorExpression.IsSymbol(p))
|
||||
{
|
||||
e = new OperatorExpression(p);
|
||||
_expressionCache.Add(p, e);
|
||||
}
|
||||
else if (FunctionExpression.IsFunction(p))
|
||||
{
|
||||
e = new FunctionExpression(p, false);
|
||||
_expressionCache.Add(p, e);
|
||||
}
|
||||
else if (ConvertExpression.IsConvertExpression(p))
|
||||
{
|
||||
e = new ConvertExpression(p);
|
||||
_expressionCache.Add(p, e);
|
||||
}
|
||||
else
|
||||
throw new ParseException(Resources.InvalidSymbolOnStack + p);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
private static int Precedence(string c)
|
||||
{
|
||||
if (c.Length == 1 && (c[0] == '*' || c[0] == '/' || c[0] == '%'))
|
||||
return 2;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private double CalculateFromQueue()
|
||||
{
|
||||
double result;
|
||||
_calculationStack.Clear();
|
||||
|
||||
foreach (IExpression expression in _expressionQueue)
|
||||
{
|
||||
if (_calculationStack.Count < expression.ArgumentCount)
|
||||
throw new ParseException(Resources.NotEnoughNumbers + expression);
|
||||
|
||||
_parameters.Clear();
|
||||
for (int i = 0; i < expression.ArgumentCount; i++)
|
||||
_parameters.Push(_calculationStack.Pop());
|
||||
|
||||
_calculationStack.Push(expression.Evaluate.Invoke(_parameters.ToArray()));
|
||||
}
|
||||
|
||||
result = _calculationStack.Pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and managed resources
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <c>true</c> to release both managed and unmanaged resources;
|
||||
/// <c>false</c> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (_expressionReader != null)
|
||||
{
|
||||
_expressionReader.Dispose();
|
||||
_expressionReader = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
BIN
Shared/LoreSoft.MathExpressions/MathExpressions.snk
Normal file
BIN
Shared/LoreSoft.MathExpressions/MathExpressions.snk
Normal file
Binary file not shown.
19
Shared/LoreSoft.MathExpressions/MathOperators.cs
Normal file
19
Shared/LoreSoft.MathExpressions/MathOperators.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
namespace LoreSoft.MathExpressions
|
||||
{
|
||||
/// <summary>Math Operators</summary>
|
||||
public enum MathOperators
|
||||
{
|
||||
/// <summary>Add Operator</summary>
|
||||
Add,
|
||||
/// <summary>Subtract Operator</summary>
|
||||
Subtract,
|
||||
/// <summary>Multiple Operator</summary>
|
||||
Multiple,
|
||||
/// <summary>Divide Operator</summary>
|
||||
Divide,
|
||||
/// <summary>Modulo Operator</summary>
|
||||
Modulo,
|
||||
/// <summary>Power Operator</summary>
|
||||
Power
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LoreSoft.MathExpressions.Metadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies an abbreviation for a instance.
|
||||
/// </summary>
|
||||
[AttributeUsageAttribute(AttributeTargets.All)]
|
||||
public sealed class AbbreviationAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbbreviationAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="text">The abbreviation text.</param>
|
||||
public AbbreviationAttribute(string text)
|
||||
{
|
||||
_text = text;
|
||||
}
|
||||
|
||||
private string _text;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the abbreviation text.
|
||||
/// </summary>
|
||||
/// <value>The abbreviation text.</value>
|
||||
public string Text
|
||||
{
|
||||
get { return _text; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
|
||||
/// </returns>
|
||||
/// <filterPriority>2</filterPriority>
|
||||
public override string ToString()
|
||||
{
|
||||
return _text;
|
||||
}
|
||||
}
|
||||
}
|
108
Shared/LoreSoft.MathExpressions/Metadata/AttributeReader.cs
Normal file
108
Shared/LoreSoft.MathExpressions/Metadata/AttributeReader.cs
Normal file
|
@ -0,0 +1,108 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace LoreSoft.MathExpressions.Metadata
|
||||
{
|
||||
/// <summary>
|
||||
/// A class to read attributes from type members.
|
||||
/// </summary>
|
||||
public static class AttributeReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the description from the <see cref="DescriptionAttribute"/> on an enum.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">An enum type.</typeparam>
|
||||
/// <param name="instance">The value to get the description from.</param>
|
||||
/// <returns>The <see cref="DescriptionAttribute.Description"/> or the name of the instance.</returns>
|
||||
/// <seealso cref="DescriptionAttribute"/>
|
||||
public static string GetDescription<T>(T instance)
|
||||
{
|
||||
if (instance == null)
|
||||
throw new ArgumentNullException("instance");
|
||||
|
||||
string result = instance.ToString();
|
||||
|
||||
Type type = instance.GetType();
|
||||
MemberInfo[] members = type.GetMember(result);
|
||||
|
||||
if (members == null || members.Length == 0)
|
||||
return result;
|
||||
|
||||
return GetDescription(members[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description from the <see cref="DescriptionAttribute"/> on a MemberInfo.
|
||||
/// </summary>
|
||||
/// <param name="info">The member info to look for the description.</param>
|
||||
/// <returns>The <see cref="DescriptionAttribute.Description"/> or the name of the member.</returns>
|
||||
/// <seealso cref="DescriptionAttribute"/>
|
||||
public static string GetDescription(MemberInfo info)
|
||||
{
|
||||
if (info == null)
|
||||
throw new ArgumentNullException("info");
|
||||
|
||||
string result = info.Name;
|
||||
|
||||
object[] attributes = info.GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||
if (attributes == null || attributes.Length == 0)
|
||||
return result;
|
||||
|
||||
DescriptionAttribute description = attributes[0] as DescriptionAttribute;
|
||||
if (description == null || string.IsNullOrEmpty(description.Description))
|
||||
return result;
|
||||
|
||||
return description.Description;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the abbreviation from the <see cref="AbbreviationAttribute"/> on an enum.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">An enum type.</typeparam>
|
||||
/// <param name="instance">The enum to get the abbreviation from.</param>
|
||||
/// <returns>The <see cref="AbbreviationAttribute.Text"/> or the name of the memeber.</returns>
|
||||
/// <seealso cref="AbbreviationAttribute"/>
|
||||
public static string GetAbbreviation<T>(T instance)
|
||||
{
|
||||
if (instance == null)
|
||||
throw new ArgumentNullException("instance");
|
||||
|
||||
string result = instance.ToString();
|
||||
|
||||
Type type = instance.GetType();
|
||||
MemberInfo[] members = type.GetMember(result);
|
||||
|
||||
if (members == null || members.Length == 0)
|
||||
return result;
|
||||
|
||||
return GetAbbreviation(members[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the abbreviation from the <see cref="AbbreviationAttribute"/> on a instance.
|
||||
/// </summary>
|
||||
/// <param name="info">The instance info look for the abbreviation.</param>
|
||||
/// <returns>The <see cref="AbbreviationAttribute.Text"/> or the name of the instance.</returns>
|
||||
/// <seealso cref="AbbreviationAttribute"/>
|
||||
public static string GetAbbreviation(MemberInfo info)
|
||||
{
|
||||
if (info == null)
|
||||
throw new ArgumentNullException("info");
|
||||
|
||||
string result = info.Name;
|
||||
|
||||
object[] attributes = info.GetCustomAttributes(typeof(AbbreviationAttribute), false);
|
||||
if (attributes == null || attributes.Length == 0)
|
||||
return result;
|
||||
|
||||
AbbreviationAttribute abbreviation = attributes[0] as AbbreviationAttribute;
|
||||
if (abbreviation == null || string.IsNullOrEmpty(abbreviation.Text))
|
||||
return result;
|
||||
|
||||
return abbreviation.Text;
|
||||
}
|
||||
}
|
||||
}
|
68
Shared/LoreSoft.MathExpressions/NumberExpression.cs
Normal file
68
Shared/LoreSoft.MathExpressions/NumberExpression.cs
Normal file
|
@ -0,0 +1,68 @@
|
|||
using System.Globalization;
|
||||
|
||||
namespace LoreSoft.MathExpressions
|
||||
{
|
||||
/// <summary>
|
||||
/// Class representing a constant number expression.
|
||||
/// </summary>
|
||||
public class NumberExpression : ExpressionBase
|
||||
{
|
||||
/// <summary>Initializes a new instance of the <see cref="NumberExpression"/> class.</summary>
|
||||
/// <param name="value">The number value for this expression.</param>
|
||||
public NumberExpression(double value)
|
||||
{
|
||||
_value = value;
|
||||
base.Evaluate = delegate
|
||||
{
|
||||
return Value;
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>Gets the number of arguments this expression uses.</summary>
|
||||
/// <value>The argument count.</value>
|
||||
public override int ArgumentCount
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
private double _value;
|
||||
|
||||
/// <summary>Gets the number value for this expression.</summary>
|
||||
/// <value>The number value.</value>
|
||||
public double Value
|
||||
{
|
||||
get { return _value; }
|
||||
}
|
||||
|
||||
/// <summary>Determines whether the specified char is a number.</summary>
|
||||
/// <param name="c">The char to test.</param>
|
||||
/// <returns><c>true</c> if the specified char is a number; otherwise, <c>false</c>.</returns>
|
||||
/// <remarks>This method checks if the char is a digit or a decimal separator.</remarks>
|
||||
public static bool IsNumber(char c)
|
||||
{
|
||||
NumberFormatInfo f = CultureInfo.CurrentUICulture.NumberFormat;
|
||||
return char.IsDigit(c) || f.NumberDecimalSeparator.IndexOf(c) >= 0;
|
||||
}
|
||||
|
||||
/// <summary>Determines whether the specified char is negative sign.</summary>
|
||||
/// <param name="c">The char to check.</param>
|
||||
/// <returns><c>true</c> if the specified char is negative sign; otherwise, <c>false</c>.</returns>
|
||||
public static bool IsNegativeSign(char c)
|
||||
{
|
||||
NumberFormatInfo f = CultureInfo.CurrentUICulture.NumberFormat;
|
||||
return f.NegativeSign.IndexOf(c) >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
|
||||
/// </returns>
|
||||
/// <filterPriority>2</filterPriority>
|
||||
public override string ToString()
|
||||
{
|
||||
return _value.ToString(CultureInfo.CurrentCulture);
|
||||
}
|
||||
}
|
||||
}
|
203
Shared/LoreSoft.MathExpressions/OperatorExpression.cs
Normal file
203
Shared/LoreSoft.MathExpressions/OperatorExpression.cs
Normal file
|
@ -0,0 +1,203 @@
|
|||
using System;
|
||||
using LoreSoft.MathExpressions.Properties;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace LoreSoft.MathExpressions
|
||||
{
|
||||
/// <summary>
|
||||
/// Class representing a math operator expression.
|
||||
/// </summary>
|
||||
public class OperatorExpression : ExpressionBase
|
||||
{
|
||||
/// <summary>The supported math operators by this class.</summary>
|
||||
private static readonly char[] operatorSymbols = new char[] {'+', '-', '*', '/', '%', '^'};
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="OperatorExpression"/> class.</summary>
|
||||
/// <param name="operator">The operator to use for this class.</param>
|
||||
/// <exception cref="ArgumentNullException">When the operator is null or empty.</exception>
|
||||
/// <exception cref="ArgumentException">When the operator is invalid.</exception>
|
||||
public OperatorExpression(string @operator)
|
||||
{
|
||||
if (string.IsNullOrEmpty(@operator))
|
||||
throw new ArgumentNullException("operator");
|
||||
|
||||
switch (@operator)
|
||||
{
|
||||
case "+":
|
||||
base.Evaluate = new MathEvaluate(Add);
|
||||
_mathOperator = MathOperators.Add;
|
||||
break;
|
||||
case "-":
|
||||
base.Evaluate = new MathEvaluate(Subtract);
|
||||
_mathOperator = MathOperators.Subtract;
|
||||
break;
|
||||
case "*":
|
||||
base.Evaluate = new MathEvaluate(Multiple);
|
||||
_mathOperator = MathOperators.Multiple;
|
||||
break;
|
||||
case "/":
|
||||
base.Evaluate = new MathEvaluate(Divide);
|
||||
_mathOperator = MathOperators.Divide;
|
||||
break;
|
||||
case "%":
|
||||
base.Evaluate = new MathEvaluate(Modulo);
|
||||
_mathOperator = MathOperators.Modulo;
|
||||
break;
|
||||
case "^":
|
||||
base.Evaluate = new MathEvaluate(Power);
|
||||
_mathOperator = MathOperators.Power;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException(Resources.InvalidOperator + @operator, "operator");
|
||||
}
|
||||
}
|
||||
|
||||
private MathOperators _mathOperator;
|
||||
|
||||
/// <summary>Gets the math operator.</summary>
|
||||
/// <value>The math operator.</value>
|
||||
public MathOperators MathOperator
|
||||
{
|
||||
get { return _mathOperator; }
|
||||
}
|
||||
|
||||
/// <summary>Gets the number of arguments this expression uses.</summary>
|
||||
/// <value>The argument count.</value>
|
||||
public override int ArgumentCount
|
||||
{
|
||||
get { return 2; }
|
||||
}
|
||||
|
||||
/// <summary>Adds the specified numbers.</summary>
|
||||
/// <param name="numbers">The numbers.</param>
|
||||
/// <returns>The result of the operation.</returns>
|
||||
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
|
||||
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
|
||||
public double Add(double[] numbers)
|
||||
{
|
||||
base.Validate(numbers);
|
||||
double result = 0;
|
||||
foreach (double n in numbers)
|
||||
result += n;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>Subtracts the specified numbers.</summary>
|
||||
/// <param name="numbers">The numbers.</param>
|
||||
/// <returns>The result of the operation.</returns>
|
||||
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
|
||||
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
|
||||
public double Subtract(double[] numbers)
|
||||
{
|
||||
base.Validate(numbers);
|
||||
double? result = null;
|
||||
foreach (double n in numbers)
|
||||
if (result.HasValue)
|
||||
result -= n;
|
||||
else
|
||||
result = n;
|
||||
|
||||
return result ?? 0;
|
||||
}
|
||||
|
||||
/// <summary>Multiples the specified numbers.</summary>
|
||||
/// <param name="numbers">The numbers.</param>
|
||||
/// <returns>The result of the operation.</returns>
|
||||
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
|
||||
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
|
||||
public double Multiple(double[] numbers)
|
||||
{
|
||||
base.Validate(numbers);
|
||||
double? result = null;
|
||||
foreach (double n in numbers)
|
||||
if (result.HasValue)
|
||||
result *= n;
|
||||
else
|
||||
result = n;
|
||||
|
||||
return result ?? 0;
|
||||
}
|
||||
|
||||
/// <summary>Divides the specified numbers.</summary>
|
||||
/// <param name="numbers">The numbers.</param>
|
||||
/// <returns>The result of the operation.</returns>
|
||||
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
|
||||
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
|
||||
public double Divide(double[] numbers)
|
||||
{
|
||||
base.Validate(numbers);
|
||||
double? result = null;
|
||||
foreach (double n in numbers)
|
||||
if (result.HasValue)
|
||||
result /= n;
|
||||
else
|
||||
result = n;
|
||||
|
||||
return result ?? 0;
|
||||
}
|
||||
|
||||
/// <summary>Modulo the specified numbers.</summary>
|
||||
/// <param name="numbers">The numbers.</param>
|
||||
/// <returns>The result of the operation.</returns>
|
||||
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
|
||||
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
|
||||
public double Modulo(double[] numbers)
|
||||
{
|
||||
base.Validate(numbers);
|
||||
double? result = null;
|
||||
foreach (double n in numbers)
|
||||
if (result.HasValue)
|
||||
result %= n;
|
||||
else
|
||||
result = n;
|
||||
|
||||
return result ?? 0;
|
||||
}
|
||||
|
||||
/// <summary>Power for the specified numbers.</summary>
|
||||
/// <param name="numbers">The numbers.</param>
|
||||
/// <returns>The result of the operation.</returns>
|
||||
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
|
||||
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
|
||||
public double Power(double[] numbers)
|
||||
{
|
||||
base.Validate(numbers);
|
||||
return Math.Pow(numbers[0], numbers[1]);
|
||||
}
|
||||
|
||||
/// <summary>Determines whether the specified string is a math symbol.</summary>
|
||||
/// <param name="s">The string to check.</param>
|
||||
/// <returns><c>true</c> if the specified string is a math symbol; otherwise, <c>false</c>.</returns>
|
||||
[SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")]
|
||||
public static bool IsSymbol(string s)
|
||||
{
|
||||
if (s == null || s.Length != 1)
|
||||
return false;
|
||||
|
||||
char c = s[0];
|
||||
return IsSymbol(c);
|
||||
}
|
||||
|
||||
/// <summary>Determines whether the specified char is a math symbol.</summary>
|
||||
/// <param name="c">The char to check.</param>
|
||||
/// <returns><c>true</c> if the specified char is a math symbol; otherwise, <c>false</c>.</returns>
|
||||
public static bool IsSymbol(char c)
|
||||
{
|
||||
return Array.Exists(operatorSymbols, delegate(char s) { return s == c; });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
|
||||
/// </returns>
|
||||
/// <filterPriority>2</filterPriority>
|
||||
public override string ToString()
|
||||
{
|
||||
return _mathOperator.ToString();
|
||||
}
|
||||
}
|
||||
}
|
40
Shared/LoreSoft.MathExpressions/ParseException.cs
Normal file
40
Shared/LoreSoft.MathExpressions/ParseException.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace LoreSoft.MathExpressions
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown when there is an error parsing a math expression.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ParseException : Exception
|
||||
{
|
||||
/// <summary>Initializes a new instance of the <see cref="ParseException"/> class.</summary>
|
||||
public ParseException()
|
||||
: base()
|
||||
{ }
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="ParseException"/> class.</summary>
|
||||
/// <param name="message">The message.</param>
|
||||
public ParseException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="ParseException"/> class.</summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="innerException">The inner exception.</param>
|
||||
public ParseException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ParseException"/> class with serialized data.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
|
||||
protected ParseException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
|
||||
}
|
||||
}
|
50
Shared/LoreSoft.MathExpressions/Properties/AssemblyInfo.cs
Normal file
50
Shared/LoreSoft.MathExpressions/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.34209
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[assembly: System.Reflection.AssemblyTitle("LoreSoft.MathExpressions")]
|
||||
[assembly: System.Reflection.AssemblyProduct("LoreSoft.MathExpressions")]
|
||||
[assembly: System.Reflection.AssemblyDescription("Math Expression Parser.")]
|
||||
[assembly: System.Reflection.AssemblyCompany("LoreSoft")]
|
||||
[assembly: System.Reflection.AssemblyCopyright("Copyright © 2015 Paul Welter")]
|
||||
[assembly: System.Runtime.InteropServices.Guid("348985f4-3fcf-4ec3-b207-6f09e918b297")]
|
||||
[assembly: System.Runtime.InteropServices.ComVisible(false)]
|
||||
[assembly: System.CLSCompliant(true)]
|
||||
[assembly: System.Reflection.AssemblyConfiguration("Release")]
|
||||
[assembly: System.Reflection.AssemblyVersion("1.2.0.0")]
|
||||
[assembly: System.Reflection.AssemblyFileVersion("1.2.0.36")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersion("1.2.0.36")]
|
||||
|
||||
|
||||
|
||||
internal sealed partial class ThisAssembly {
|
||||
|
||||
internal const string AssemblyTitle = "LoreSoft.MathExpressions";
|
||||
|
||||
internal const string AssemblyProduct = "LoreSoft.MathExpressions";
|
||||
|
||||
internal const string AssemblyDescription = "Math Expression Parser.";
|
||||
|
||||
internal const string AssemblyCompany = "LoreSoft";
|
||||
|
||||
internal const string AssemblyCopyright = "Copyright © 2015 Paul Welter";
|
||||
|
||||
internal const string Guid = "348985f4-3fcf-4ec3-b207-6f09e918b297";
|
||||
|
||||
internal const string AssemblyConfiguration = "Release";
|
||||
|
||||
internal const string AssemblyVersion = "1.2.0.0";
|
||||
|
||||
internal const string AssemblyFileVersion = "1.2.0.36";
|
||||
|
||||
internal const string AssemblyInformationalVersion = "1.2.0.36";
|
||||
|
||||
private ThisAssembly() {
|
||||
}
|
||||
}
|
198
Shared/LoreSoft.MathExpressions/Properties/Resources.Designer.cs
generated
Normal file
198
Shared/LoreSoft.MathExpressions/Properties/Resources.Designer.cs
generated
Normal file
|
@ -0,0 +1,198 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace LoreSoft.MathExpressions.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LoreSoft.MathExpressions.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The IExpression.Evaluate property can not be null..
|
||||
/// </summary>
|
||||
internal static string EvaluatePropertyCanNotBeNull {
|
||||
get {
|
||||
return ResourceManager.GetString("EvaluatePropertyCanNotBeNull", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The function name '{0}' is already registered..
|
||||
/// </summary>
|
||||
internal static string FunctionNameRegistered {
|
||||
get {
|
||||
return ResourceManager.GetString("FunctionNameRegistered", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid character: .
|
||||
/// </summary>
|
||||
internal static string InvalidCharacterEncountered {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidCharacterEncountered", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid convertion expression: .
|
||||
/// </summary>
|
||||
internal static string InvalidConvertionExpression {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidConvertionExpression", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid function name '{0}'..
|
||||
/// </summary>
|
||||
internal static string InvalidFunctionName {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidFunctionName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid length of array..
|
||||
/// </summary>
|
||||
internal static string InvalidLengthOfArray {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidLengthOfArray", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid math expression..
|
||||
/// </summary>
|
||||
internal static string InvalidMathExpression {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidMathExpression", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid number format: .
|
||||
/// </summary>
|
||||
internal static string InvalidNumberFormat {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidNumberFormat", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid operator: .
|
||||
/// </summary>
|
||||
internal static string InvalidOperator {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidOperator", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid symbol on stack: .
|
||||
/// </summary>
|
||||
internal static string InvalidSymbolOnStack {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidSymbolOnStack", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid function or variable: .
|
||||
/// </summary>
|
||||
internal static string InvalidVariableEncountered {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidVariableEncountered", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid number for expression: .
|
||||
/// </summary>
|
||||
internal static string NotEnoughNumbers {
|
||||
get {
|
||||
return ResourceManager.GetString("NotEnoughNumbers", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Unbalanced parentheses..
|
||||
/// </summary>
|
||||
internal static string UnbalancedParentheses {
|
||||
get {
|
||||
return ResourceManager.GetString("UnbalancedParentheses", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The variable name conflicts with function '{0}'..
|
||||
/// </summary>
|
||||
internal static string VariableNameConflict {
|
||||
get {
|
||||
return ResourceManager.GetString("VariableNameConflict", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The variable name can only contain letters..
|
||||
/// </summary>
|
||||
internal static string VariableNameContainsLetters {
|
||||
get {
|
||||
return ResourceManager.GetString("VariableNameContainsLetters", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
165
Shared/LoreSoft.MathExpressions/Properties/Resources.resx
Normal file
165
Shared/LoreSoft.MathExpressions/Properties/Resources.resx
Normal file
|
@ -0,0 +1,165 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="InvalidLengthOfArray" xml:space="preserve">
|
||||
<value>Invalid length of array.</value>
|
||||
</data>
|
||||
<data name="InvalidFunctionName" xml:space="preserve">
|
||||
<value>Invalid function name '{0}'.</value>
|
||||
</data>
|
||||
<data name="EvaluatePropertyCanNotBeNull" xml:space="preserve">
|
||||
<value>The IExpression.Evaluate property can not be null.</value>
|
||||
</data>
|
||||
<data name="FunctionNameRegistered" xml:space="preserve">
|
||||
<value>The function name '{0}' is already registered.</value>
|
||||
</data>
|
||||
<data name="InvalidCharacterEncountered" xml:space="preserve">
|
||||
<value>Invalid character: </value>
|
||||
</data>
|
||||
<data name="InvalidVariableEncountered" xml:space="preserve">
|
||||
<value>Invalid function or variable: </value>
|
||||
</data>
|
||||
<data name="UnbalancedParentheses" xml:space="preserve">
|
||||
<value>Unbalanced parentheses.</value>
|
||||
</data>
|
||||
<data name="InvalidNumberFormat" xml:space="preserve">
|
||||
<value>Invalid number format: </value>
|
||||
</data>
|
||||
<data name="InvalidSymbolOnStack" xml:space="preserve">
|
||||
<value>Invalid symbol on stack: </value>
|
||||
</data>
|
||||
<data name="InvalidOperator" xml:space="preserve">
|
||||
<value>Invalid operator: </value>
|
||||
</data>
|
||||
<data name="VariableNameConflict" xml:space="preserve">
|
||||
<value>The variable name conflicts with function '{0}'.</value>
|
||||
</data>
|
||||
<data name="VariableNameContainsLetters" xml:space="preserve">
|
||||
<value>The variable name can only contain letters.</value>
|
||||
</data>
|
||||
<data name="InvalidMathExpression" xml:space="preserve">
|
||||
<value>Invalid math expression.</value>
|
||||
</data>
|
||||
<data name="InvalidConvertionExpression" xml:space="preserve">
|
||||
<value>Invalid convertion expression: </value>
|
||||
</data>
|
||||
<data name="NotEnoughNumbers" xml:space="preserve">
|
||||
<value>Invalid number for expression: </value>
|
||||
</data>
|
||||
</root>
|
|
@ -0,0 +1,75 @@
|
|||
using LoreSoft.MathExpressions.Metadata;
|
||||
|
||||
namespace LoreSoft.MathExpressions.UnitConversion
|
||||
{
|
||||
/// <summary>Units for Length</summary>
|
||||
public enum LengthUnit
|
||||
{
|
||||
/// <summary>Millimeter unit (mm)</summary>
|
||||
[Abbreviation("mm")]
|
||||
Millimeter = 0,
|
||||
/// <summary>Centimeter unit (cm)</summary>
|
||||
[Abbreviation("cm")]
|
||||
Centimeter = 1,
|
||||
/// <summary>Meter unit (m)</summary>
|
||||
[Abbreviation("m")]
|
||||
Meter = 2,
|
||||
/// <summary>Kilometer unit (km)</summary>
|
||||
[Abbreviation("km")]
|
||||
Kilometer = 3,
|
||||
/// <summary>Inch unit (in)</summary>
|
||||
[Abbreviation("in")]
|
||||
Inch = 4,
|
||||
/// <summary>Feet unit (ft)</summary>
|
||||
[Abbreviation("ft")]
|
||||
Feet = 5,
|
||||
/// <summary>Yard unit (yd)</summary>
|
||||
[Abbreviation("yd")]
|
||||
Yard = 6,
|
||||
/// <summary>Mile unit (mile)</summary>
|
||||
[Abbreviation("mile")]
|
||||
Mile = 7
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class representing length convertion.
|
||||
/// </summary>
|
||||
public static class LengthConverter
|
||||
{
|
||||
// In enum order
|
||||
private static readonly double[] factors = new double[]
|
||||
{
|
||||
0.001d, //millimeter
|
||||
0.01d, //centimeter
|
||||
1d, //meter
|
||||
1000d, //kilometer
|
||||
0.3048d/12d, //inch
|
||||
0.3048d, //feet
|
||||
0.9144d, //yard
|
||||
0.3048d*5280d, //mile
|
||||
};
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified from unit to the specified unit.
|
||||
/// </summary>
|
||||
/// <param name="fromUnit">Covert from unit.</param>
|
||||
/// <param name="toUnit">Covert to unit.</param>
|
||||
/// <param name="fromValue">Covert from value.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static double Convert(
|
||||
LengthUnit fromUnit,
|
||||
LengthUnit toUnit,
|
||||
double fromValue)
|
||||
{
|
||||
if (fromUnit == toUnit)
|
||||
return fromValue;
|
||||
|
||||
double fromFactor = factors[(int)fromUnit];
|
||||
double toFactor = factors[(int) toUnit];
|
||||
double result = fromFactor * fromValue / toFactor;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
using LoreSoft.MathExpressions.Metadata;
|
||||
namespace LoreSoft.MathExpressions.UnitConversion
|
||||
{
|
||||
/// <summary>Units for Mass</summary>
|
||||
public enum MassUnit
|
||||
{
|
||||
/// <summary>Milligram unit (mg)</summary>
|
||||
[Abbreviation("mg")]
|
||||
Milligram = 0,
|
||||
/// <summary>Gram unit (g)</summary>
|
||||
[Abbreviation("g")]
|
||||
Gram = 1,
|
||||
/// <summary>Kilogram unit (kg)</summary>
|
||||
[Abbreviation("kg")]
|
||||
Kilogram = 2,
|
||||
/// <summary>Ounce unit (oz)</summary>
|
||||
[Abbreviation("oz")]
|
||||
Ounce = 3,
|
||||
/// <summary>Pound unit (lb)</summary>
|
||||
[Abbreviation("lb")]
|
||||
Pound = 4,
|
||||
/// <summary>Ton unit (ton)</summary>
|
||||
[Abbreviation("ton")]
|
||||
Ton = 5,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class representing mass convertion.
|
||||
/// </summary>
|
||||
public static class MassConverter
|
||||
{
|
||||
// In enum order
|
||||
private static readonly double[] factors = new double[]
|
||||
{
|
||||
0.000001d, //milligram
|
||||
0.001d, //gram
|
||||
1d, //kilogram
|
||||
0.45359237d/16d, //ounce
|
||||
0.45359237d, //pound
|
||||
0.45359237d*2000d, //ton [short, US]
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified from unit to the specified unit.
|
||||
/// </summary>
|
||||
/// <param name="fromUnit">Covert from unit.</param>
|
||||
/// <param name="toUnit">Covert to unit.</param>
|
||||
/// <param name="fromValue">Covert from value.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static double Convert(
|
||||
MassUnit fromUnit,
|
||||
MassUnit toUnit,
|
||||
double fromValue)
|
||||
{
|
||||
if (fromUnit == toUnit)
|
||||
return fromValue;
|
||||
|
||||
double fromFactor = factors[(int)fromUnit];
|
||||
double toFactor = factors[(int)toUnit];
|
||||
double result = fromFactor * fromValue / toFactor;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
using LoreSoft.MathExpressions.Metadata;
|
||||
using System.ComponentModel;
|
||||
namespace LoreSoft.MathExpressions.UnitConversion
|
||||
{
|
||||
/// <summary>Units for Speed</summary>
|
||||
public enum SpeedUnit
|
||||
{
|
||||
/// <summary>Meter/Second unit (m/s)</summary>
|
||||
[Abbreviation("m/s")]
|
||||
[Description("Meter/Second")]
|
||||
MeterPerSecond = 0,
|
||||
/// <summary>Kilometer/Hour unit (kph)</summary>
|
||||
[Abbreviation("kph")]
|
||||
[Description("Kilometer/Hour")]
|
||||
KilometerPerHour = 1,
|
||||
/// <summary>Foot/Second unit (ft/s)</summary>
|
||||
[Abbreviation("ft/s")]
|
||||
[Description("Foot/Second")]
|
||||
FootPerSecond = 2,
|
||||
/// <summary>Mile/Hour unit (mph)</summary>
|
||||
[Abbreviation("mph")]
|
||||
[Description("Mile/Hour")]
|
||||
MilePerHour = 3,
|
||||
/// <summary>Knot unit (knot)</summary>
|
||||
[Abbreviation("knot")]
|
||||
Knot = 4,
|
||||
/// <summary>Mach unit (mach)</summary>
|
||||
[Abbreviation("mach")]
|
||||
Mach = 5,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class representing speed convertion.
|
||||
/// </summary>
|
||||
public static class SpeedConverter
|
||||
{
|
||||
// In enum order
|
||||
private static readonly double[] factors = new double[]
|
||||
{
|
||||
1d, //meter/second
|
||||
1000d/3600d, //kilometer/hour
|
||||
0.3048d, //foot/second
|
||||
(0.3048d*5280d)/3600d, //mile/hour (mph)
|
||||
1852d/3600d, //knot
|
||||
340.29d, //mach
|
||||
};
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified from unit to the specified unit.
|
||||
/// </summary>
|
||||
/// <param name="fromUnit">Covert from unit.</param>
|
||||
/// <param name="toUnit">Covert to unit.</param>
|
||||
/// <param name="fromValue">Covert from value.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static double Convert(
|
||||
SpeedUnit fromUnit,
|
||||
SpeedUnit toUnit,
|
||||
double fromValue)
|
||||
{
|
||||
if (fromUnit == toUnit)
|
||||
return fromValue;
|
||||
|
||||
double fromFactor = factors[(int)fromUnit];
|
||||
double toFactor = factors[(int)toUnit];
|
||||
double result = fromFactor * fromValue / toFactor;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
using LoreSoft.MathExpressions.Metadata;
|
||||
namespace LoreSoft.MathExpressions.UnitConversion
|
||||
{
|
||||
/// <summary>Units for Temperature</summary>
|
||||
public enum TemperatureUnit
|
||||
{
|
||||
/// <summary>Degrees Celsius unit (c)</summary>
|
||||
[Abbreviation("c")]
|
||||
Celsius = 0,
|
||||
/// <summary>Degrees Fahrenheit unit (f)</summary>
|
||||
[Abbreviation("f")]
|
||||
Fahrenheit = 1,
|
||||
/// <summary>Degrees Kelvin unit (k)</summary>
|
||||
[Abbreviation("k")]
|
||||
Kelvin = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class representing temperature convertion.
|
||||
/// </summary>
|
||||
public static class TemperatureConverter
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified from unit to the specified unit.
|
||||
/// </summary>
|
||||
/// <param name="fromUnit">Covert from unit.</param>
|
||||
/// <param name="toUnit">Covert to unit.</param>
|
||||
/// <param name="fromValue">Covert from value.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static double Convert(
|
||||
TemperatureUnit fromUnit,
|
||||
TemperatureUnit toUnit,
|
||||
double fromValue)
|
||||
{
|
||||
if (fromUnit == toUnit)
|
||||
return fromValue;
|
||||
|
||||
double result = 0;
|
||||
|
||||
if (fromUnit == TemperatureUnit.Celsius)
|
||||
{
|
||||
if (toUnit == TemperatureUnit.Kelvin)
|
||||
result = fromValue + 273.15d;
|
||||
else if (toUnit == TemperatureUnit.Fahrenheit)
|
||||
//(9/5 * C) + 32 = F
|
||||
result = (9.0d/5.0d*fromValue) + 32d;
|
||||
}
|
||||
else if (fromUnit == TemperatureUnit.Kelvin)
|
||||
{
|
||||
if (toUnit == TemperatureUnit.Celsius)
|
||||
result = fromValue - 273.15d;
|
||||
else if (toUnit == TemperatureUnit.Fahrenheit)
|
||||
result = 5.0d/9.0d*((fromValue - 273.15d) + 32d);
|
||||
}
|
||||
else if (fromUnit == TemperatureUnit.Fahrenheit)
|
||||
{
|
||||
if (toUnit == TemperatureUnit.Celsius)
|
||||
//(F - 32) * 5/9 = C
|
||||
result = 5.0d/9.0d*(fromValue - 32d);
|
||||
else if (toUnit == TemperatureUnit.Kelvin)
|
||||
result = (5.0d/9.0d*(fromValue - 32d)) + 273.15;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
using System;
|
||||
using LoreSoft.MathExpressions.Metadata;
|
||||
|
||||
namespace LoreSoft.MathExpressions.UnitConversion
|
||||
{
|
||||
/// <summary>Units for Time</summary>
|
||||
public enum TimeUnit
|
||||
{
|
||||
/// <summary>Millisecond unit (ms)</summary>
|
||||
[Abbreviation("ms")]
|
||||
Millisecond = 0,
|
||||
/// <summary>Second unit (sec)</summary>
|
||||
[Abbreviation("sec")]
|
||||
Second = 1,
|
||||
/// <summary>Minute unit (min)</summary>
|
||||
[Abbreviation("min")]
|
||||
Minute = 2,
|
||||
/// <summary>Hour unit (hr)</summary>
|
||||
[Abbreviation("hr")]
|
||||
Hour = 3,
|
||||
/// <summary>Day unit (d)</summary>
|
||||
[Abbreviation("d")]
|
||||
Day = 4,
|
||||
/// <summary>Week unit (wk)</summary>
|
||||
[Abbreviation("wk")]
|
||||
Week = 5
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class representing time convertion.
|
||||
/// </summary>
|
||||
public static class TimeConverter
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified from unit to the specified unit.
|
||||
/// </summary>
|
||||
/// <param name="fromUnit">Covert from unit.</param>
|
||||
/// <param name="toUnit">Covert to unit.</param>
|
||||
/// <param name="fromValue">Covert from value.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static double Convert(
|
||||
TimeUnit fromUnit,
|
||||
TimeUnit toUnit,
|
||||
double fromValue)
|
||||
{
|
||||
if (fromUnit == toUnit)
|
||||
return fromValue;
|
||||
|
||||
TimeSpan span;
|
||||
switch (fromUnit)
|
||||
{
|
||||
case TimeUnit.Millisecond:
|
||||
span = TimeSpan.FromMilliseconds(fromValue);
|
||||
break;
|
||||
case TimeUnit.Second:
|
||||
span = TimeSpan.FromSeconds(fromValue);
|
||||
break;
|
||||
case TimeUnit.Minute:
|
||||
span = TimeSpan.FromMinutes(fromValue);
|
||||
break;
|
||||
case TimeUnit.Hour:
|
||||
span = TimeSpan.FromHours(fromValue);
|
||||
break;
|
||||
case TimeUnit.Day:
|
||||
span = TimeSpan.FromDays(fromValue);
|
||||
break;
|
||||
case TimeUnit.Week:
|
||||
span = TimeSpan.FromDays(fromValue * 7d);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("fromUnit");
|
||||
}
|
||||
|
||||
switch (toUnit)
|
||||
{
|
||||
case TimeUnit.Millisecond:
|
||||
return span.TotalMilliseconds;
|
||||
case TimeUnit.Second:
|
||||
return span.TotalSeconds;
|
||||
case TimeUnit.Minute:
|
||||
return span.TotalMinutes;
|
||||
case TimeUnit.Hour:
|
||||
return span.TotalHours;
|
||||
case TimeUnit.Day:
|
||||
return span.TotalDays;
|
||||
case TimeUnit.Week:
|
||||
return span.TotalDays / 7d;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("toUnit");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
Shared/LoreSoft.MathExpressions/UnitConversion/UnitType.cs
Normal file
19
Shared/LoreSoft.MathExpressions/UnitConversion/UnitType.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
namespace LoreSoft.MathExpressions.UnitConversion
|
||||
{
|
||||
///<summary>The unit types available for conversion.</summary>
|
||||
public enum UnitType
|
||||
{
|
||||
///<summary>Length unit types.</summary>
|
||||
Length,
|
||||
///<summary>Mass unit types.</summary>
|
||||
Mass,
|
||||
///<summary>Speed unit types.</summary>
|
||||
Speed,
|
||||
///<summary>Temperature unit types.</summary>
|
||||
Temperature,
|
||||
///<summary>Time unit types.</summary>
|
||||
Time,
|
||||
///<summary>Volume unit types.</summary>
|
||||
Volume
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
using LoreSoft.MathExpressions.Metadata;
|
||||
using System.ComponentModel;
|
||||
namespace LoreSoft.MathExpressions.UnitConversion
|
||||
{
|
||||
/// <summary>Units for Liquid Volume</summary>
|
||||
public enum VolumeUnit
|
||||
{
|
||||
/// <summary>Milliliter unit (ml)</summary>
|
||||
[Abbreviation("ml")]
|
||||
Milliliter = 0,
|
||||
/// <summary>Liter unit (l)</summary>
|
||||
[Abbreviation("l")]
|
||||
Liter = 1,
|
||||
/// <summary>Kiloliter unit (kl)</summary>
|
||||
[Abbreviation("kl")]
|
||||
Kiloliter = 2,
|
||||
/// <summary>Fluid ounce unit (oz)</summary>
|
||||
[Abbreviation("oz")]
|
||||
[Description("Fluid Ounce")]
|
||||
FluidOunce = 3,
|
||||
/// <summary>Cup unit (cup)</summary>
|
||||
[Abbreviation("cup")]
|
||||
Cup = 4,
|
||||
/// <summary>Pint unit (pt)</summary>
|
||||
[Abbreviation("pt")]
|
||||
Pint = 5,
|
||||
/// <summary>Quart unit (qt)</summary>
|
||||
[Abbreviation("qt")]
|
||||
Quart = 6,
|
||||
/// <summary>Gallon unit (gal)</summary>
|
||||
[Abbreviation("gal")]
|
||||
Gallon = 7
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class representing liquid volume convertion.
|
||||
/// </summary>
|
||||
public static class VolumeConverter
|
||||
{
|
||||
// In enum order
|
||||
private static readonly double[] factors = new double[]
|
||||
{
|
||||
0.000001d, //milliliter
|
||||
0.001d, //liter
|
||||
1d, //kiloliter
|
||||
0.0037854118d/128d, //ounce [US, liquid]
|
||||
0.0037854118d/16d, //cup [US]
|
||||
0.0037854118d/8d, //pint [US, liquid]
|
||||
0.0037854118d/4d, //quart [US, liquid]
|
||||
0.0037854118d, //gallon [US, liquid]
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified from unit to the specified unit.
|
||||
/// </summary>
|
||||
/// <param name="fromUnit">Covert from unit.</param>
|
||||
/// <param name="toUnit">Covert to unit.</param>
|
||||
/// <param name="fromValue">Covert from value.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static double Convert(
|
||||
VolumeUnit fromUnit,
|
||||
VolumeUnit toUnit,
|
||||
double fromValue)
|
||||
{
|
||||
if (fromUnit == toUnit)
|
||||
return fromValue;
|
||||
|
||||
double fromFactor = factors[(int)fromUnit];
|
||||
double toFactor = factors[(int)toUnit];
|
||||
double result = fromFactor * fromValue / toFactor;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
82
Shared/LoreSoft.MathExpressions/VariableDictionary.cs
Normal file
82
Shared/LoreSoft.MathExpressions/VariableDictionary.cs
Normal file
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LoreSoft.MathExpressions.Properties;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Security.Permissions;
|
||||
|
||||
namespace LoreSoft.MathExpressions
|
||||
{
|
||||
/// <summary>
|
||||
/// Class representing a collection of variable names and values.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Variable names can only contain letters, numbers and symbols are not allowed.
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
public class VariableDictionary : Dictionary<string, double>
|
||||
{
|
||||
private MathEvaluator _evaluator;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="VariableDictionary"/> class.</summary>
|
||||
/// <param name="evaluator">The evaluator.</param>
|
||||
internal VariableDictionary(MathEvaluator evaluator)
|
||||
: base(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
_evaluator = evaluator;
|
||||
base.Add(MathEvaluator.AnswerVariable, 0);
|
||||
base.Add("pi", Math.PI);
|
||||
base.Add("e", Math.E);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VariableDictionary"/> class.
|
||||
/// </summary>
|
||||
/// <param name="info">A <see cref="T:System.Runtime.Serialization.SerializationInfo"/> object containing the information required to serialize the <see cref="T:System.Collections.Generic.Dictionary`2"/>.</param>
|
||||
/// <param name="context">A <see cref="T:System.Runtime.Serialization.StreamingContext"/> structure containing the source and destination of the serialized stream associated with the <see cref="T:System.Collections.Generic.Dictionary`2"/>.</param>
|
||||
protected VariableDictionary(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
|
||||
/// <summary>Adds the specified variable and value to the dictionary.</summary>
|
||||
/// <param name="name">The name of the variable to add.</param>
|
||||
/// <param name="value">The value of the variable.</param>
|
||||
/// <exception cref="ArgumentNullException">When variable name is null.</exception>
|
||||
/// <exception cref="ArgumentException">When variable name contains non-letters or the name exists in the <see cref="MathEvaluator.Functions"/> list.</exception>
|
||||
/// <seealso cref="MathEvaluator"/>
|
||||
/// <seealso cref="MathEvaluator.Variables"/>
|
||||
/// <seealso cref="MathEvaluator.Functions"/>
|
||||
public new void Add(string name, double value)
|
||||
{
|
||||
Validate(name);
|
||||
base.Add(name, value);
|
||||
}
|
||||
|
||||
private void Validate(string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
throw new ArgumentNullException("name");
|
||||
|
||||
if (_evaluator.IsFunction(name))
|
||||
throw new ArgumentException(
|
||||
string.Format(CultureInfo.CurrentCulture,
|
||||
Resources.VariableNameConflict, name), "name");
|
||||
|
||||
for (int i = 0; i < name.Length; i++)
|
||||
if (!char.IsLetter(name[i]))
|
||||
throw new ArgumentException(Resources.VariableNameContainsLetters, "name");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the <see cref="T:System.Runtime.Serialization.ISerializable"/> interface and returns the data needed to serialize the <see cref="T:System.Collections.Generic.Dictionary`2"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="info">A <see cref="T:System.Runtime.Serialization.SerializationInfo"/> object that contains the information required to serialize the <see cref="T:System.Collections.Generic.Dictionary`2"/> instance.</param>
|
||||
/// <param name="context">A <see cref="T:System.Runtime.Serialization.StreamingContext"/> structure that contains the source and destination of the serialized stream associated with the <see cref="T:System.Collections.Generic.Dictionary`2"/> instance.</param>
|
||||
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
base.GetObjectData(info, context);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
using Shared.Models;
|
||||
using Shared.ZatVdfParser;
|
||||
using SteamShared.Models;
|
||||
using SteamShared.ZatVdfParser;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
@ -9,7 +9,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared
|
||||
namespace SteamShared
|
||||
{
|
||||
public class CsgoHelper
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@ using System.Drawing.Imaging;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.IO;
|
||||
|
||||
namespace Shared
|
||||
namespace SteamShared
|
||||
{
|
||||
#region DDSImage Class
|
||||
public class DDSImage : IDisposable
|
||||
|
|
|
@ -7,7 +7,7 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace Shared
|
||||
namespace SteamShared
|
||||
{
|
||||
public static class Globals
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
internal class BSPHeader
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
internal class BSPLump
|
||||
{
|
||||
|
|
|
@ -6,7 +6,7 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class CsgoMap
|
||||
{
|
||||
|
|
|
@ -6,7 +6,7 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class CsgoWeapon
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class MapCustomOverwriteMapping
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class MapPoint
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class NavApproachSpot
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class NavArea
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class NavAreaBind
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class NavConnectionData
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class NavEncounterPath
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class NavEncounterSpot
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class NavHeader
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
[Flags]
|
||||
public enum HidingSpotAttribute
|
||||
|
|
|
@ -5,7 +5,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class NavLadder
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class NavLadderIDSequence
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class NavMesh
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class PlayerSpawn
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class Settings
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class SteamGame
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class SteamLibrary
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Shared.Models
|
||||
namespace SteamShared.Models
|
||||
{
|
||||
public class Vector3
|
||||
{
|
||||
|
|
|
@ -4,9 +4,9 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Shared.Models;
|
||||
using SteamShared.Models;
|
||||
|
||||
namespace Shared
|
||||
namespace SteamShared
|
||||
{
|
||||
public enum NavDisplayModes { None, Wireframe, Filled }
|
||||
|
||||
|
|
12
Shared/SteamHelpers/SteamHelpers/SourceCFG/SourceCFG.cs
Normal file
12
Shared/SteamHelpers/SteamHelpers/SourceCFG/SourceCFG.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SteamShared
|
||||
{
|
||||
public class SourceCFG
|
||||
{
|
||||
}
|
||||
}
|
|
@ -4,11 +4,11 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Shared.ZatVdfParser;
|
||||
using SteamShared.ZatVdfParser;
|
||||
using Microsoft.Win32;
|
||||
using Shared.Models;
|
||||
using SteamShared.Models;
|
||||
|
||||
namespace Shared
|
||||
namespace SteamShared
|
||||
{
|
||||
public class SteamHelper
|
||||
{
|
||||
|
@ -75,7 +75,7 @@ namespace Shared
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forcefully tries to update the <see cref="SteamPath"/> property with the current Steam path, even if it should be already set.
|
||||
/// Forcefully tries to update the <see cref="SteamLibraries"/> property with the current Steam libraries, even if they should be already set.
|
||||
/// </summary>
|
||||
public void UpdateSteamLibraries()
|
||||
{
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWPF>true</UseWPF>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<RuntimeHostConfigurationOption Include="System.Globalization.UseNls" Value="true" />
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace Shared.ZatVdfParser
|
||||
namespace SteamShared.ZatVdfParser
|
||||
{
|
||||
/// <summary>
|
||||
/// This class was copied from <see cref="https://stackoverflow.com/questions/39065573/reading-values-from-an-acf-manifest-file">here</see> as a backup, in case <see cref="VDFFile"/> didn't work well enough.
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Shared.ZatVdfParser
|
||||
namespace SteamShared.ZatVdfParser
|
||||
{
|
||||
public class Element
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Shared.ZatVdfParser
|
||||
namespace SteamShared.ZatVdfParser
|
||||
{
|
||||
public class VDFFile
|
||||
{
|
||||
|
@ -55,7 +55,7 @@ namespace Shared.ZatVdfParser
|
|||
string? line = null;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
if (line.StartsWith("\0"))
|
||||
if (line.StartsWith("\0", StringComparison.Ordinal))
|
||||
return;
|
||||
|
||||
line = line.Trim();
|
||||
|
|
Loading…
Add table
Reference in a new issue