diff --git a/CHANGELOG.md b/CHANGELOG.md index 444aecc..debb6cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,33 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.0.0] 2017.09.?? + +### Added + +- Added binary operators `<<`, `>>`, `&` and `|`. +- Added comparison operator `!=` (with alias `ne`). +- Added new operators `floor`, `ceil`, `trunc`. +- Added new operators `e`, `log`, `log10`. +- Added aliases to existing operators `<`, `<=`, `>` and `>=`. + +### Changed + +- Operators priority is now from lowest (0) to highest (_int.Max_); +- Formula syntax: Alphanumeric operators should now be separated from operants by a blanck space or brackets (e.g.: `e2` should be written `e 2` or `e(2)`). + +### Removed + +- Removed some deprecated operation aliases (`degrad` and `raddeg`). +- Removed some deprecated functions related to unnamed contexts : + - Constructor `Interpreter(IInterpreterContext)`. + - Function `SetContext(IInterpreterContext)`. + +### Fixed + +- Operators that contain digits in their names no longer fail (e.g.: `deg2rad`). +- Fixed an issue related to mixed operators (i.e.: `!` was mixed to `!=`). + ## [0.2.0-alpha] - 2017.08.29 ### Added diff --git a/Library/Interpreter.Operators.cs b/Library/Interpreter.Operators.cs index 461c9e7..9dc244e 100644 --- a/Library/Interpreter.Operators.cs +++ b/Library/Interpreter.Operators.cs @@ -101,7 +101,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("pi", 90)] + [Operator("pi", 0)] private class PiNode : ZeroNode { public override double GetValue(Interpreter interpreter) @@ -110,7 +110,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("rand", 90)] + [Operator("rand", 0)] private class RandNode : ZeroNode { public override double GetValue(Interpreter interpreter) @@ -119,7 +119,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("true", 90)] + [Operator("true", 0)] private class TrueNode : ZeroNode { public override double GetValue(Interpreter interpreter) @@ -128,7 +128,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("false", 90)] + [Operator("false", 0)] private class FalseNode : ZeroNode { public override double GetValue(Interpreter interpreter) @@ -141,7 +141,7 @@ public override double GetValue(Interpreter interpreter) #region UnaryNode - [Operator("±", 99)] + [Operator("±", 1)] private class SignNode : UnaryNode { public SignNode(Node input) : @@ -155,7 +155,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("sqrt", 15)] + [Operator("sqrt")] private class SqrtNode : UnaryNode { public SqrtNode(Node input) @@ -169,7 +169,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("cos", 12)] + [Operator("cos")] private class CosNode : UnaryNode { public CosNode(Node input) @@ -183,7 +183,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("sin", 12)] + [Operator("sin")] private class SinNode : UnaryNode { public SinNode(Node input) @@ -197,7 +197,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("tan", 12)] + [Operator("tan")] private class TanNode : UnaryNode { public TanNode(Node input) @@ -211,7 +211,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("acos", 12)] + [Operator("acos")] private class AcosNode : UnaryNode { public AcosNode(Node input) @@ -225,7 +225,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("asin", 12)] + [Operator("asin")] private class AsinNode : UnaryNode { public AsinNode(Node input) @@ -239,7 +239,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("atan", 12)] + [Operator("atan")] private class AtanNode : UnaryNode { public AtanNode(Node input) @@ -253,7 +253,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("cosh", 12)] + [Operator("cosh")] private class CoshNode : UnaryNode { public CoshNode(Node input) @@ -267,7 +267,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("sinh", 12)] + [Operator("sinh")] private class SinhNode : UnaryNode { public SinhNode(Node input) @@ -281,7 +281,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("tanh", 12)] + [Operator("tanh")] private class TanhNode : UnaryNode { public TanhNode(Node input) @@ -295,7 +295,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("degrad", 13)] + [Operator("deg2rad")] private class Deg2RadNode : UnaryNode { public Deg2RadNode(Node input) @@ -309,7 +309,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("raddeg", 13)] + [Operator("rad2deg")] private class Rad2DegNode : UnaryNode { public Rad2DegNode(Node input) @@ -323,7 +323,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("abs", 8)] + [Operator("abs")] private class AbsNode : UnaryNode { public AbsNode(Node input) @@ -337,7 +337,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("round", 8)] + [Operator("round")] private class RoundNode : UnaryNode { public RoundNode(Node input) @@ -351,7 +351,8 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("!", 50)] + [Operator("!", 3)] + [Operator("not", 3)] private class NegNode : UnaryNode { public NegNode(Node input) @@ -365,11 +366,96 @@ public override double GetValue(Interpreter interpreter) } } + [Operator("ceil")] + private class CeilNode : UnaryNode + { + public CeilNode(Node input) + : base(input) + { + } + + public override double GetValue(Interpreter interpreter) + { + return System.Math.Ceiling(this.input.GetValue(interpreter)); + } + } + + [Operator("floor")] + private class FlorrNode : UnaryNode + { + public FlorrNode(Node input) + : base(input) + { + } + + public override double GetValue(Interpreter interpreter) + { + return System.Math.Floor(this.input.GetValue(interpreter)); + } + } + + [Operator("trunc")] + private class TruncNode : UnaryNode + { + public TruncNode(Node input) + : base(input) + { + } + + public override double GetValue(Interpreter interpreter) + { + return System.Math.Truncate(this.input.GetValue(interpreter)); + } + } + + [Operator("log")] + private class LogNode : UnaryNode + { + public LogNode(Node input) + : base(input) + { + } + + public override double GetValue(Interpreter interpreter) + { + return System.Math.Log(this.input.GetValue(interpreter)); + } + } + + [Operator("log10")] + private class Log10Node : UnaryNode + { + public Log10Node(Node input) + : base(input) + { + } + + public override double GetValue(Interpreter interpreter) + { + return System.Math.Log10(this.input.GetValue(interpreter)); + } + } + + [Operator("e")] + [Operator("exp")] + private class ExpNode : UnaryNode + { + public ExpNode(Node input) + : base(input) + { + } + + public override double GetValue(Interpreter interpreter) + { + return System.Math.Exp(this.input.GetValue(interpreter)); + } + } + #endregion #region BinaryNode - [Operator("+", 2)] + [Operator("+", 6)] private class AddNode : BinaryNode { public AddNode(Node leftInput, Node rightInput) @@ -383,7 +469,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("-", 2)] + [Operator("-", 6)] private class SubNode : BinaryNode { public SubNode(Node leftInput, Node rightInput) @@ -425,7 +511,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("%", 10)] + [Operator("%", 5)] private class ModNode : BinaryNode { public ModNode(Node leftInput, Node rightInput) @@ -439,7 +525,8 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("^", 15)] + [Operator("^")] + [Operator("pow")] private class PowNode : BinaryNode { public PowNode(Node leftInput, Node rightInput) @@ -453,7 +540,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("min", 80)] + [Operator("min")] private class MinNode : BinaryNode { public MinNode(Node leftInput, Node rightInput) @@ -467,7 +554,7 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("max", 90)] + [Operator("max")] private class MaxNode : BinaryNode { public MaxNode(Node leftInput, Node rightInput) @@ -481,8 +568,8 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("==", 0)] - [Operator("eq", 0)] + [Operator("==", 9)] + [Operator("eq", 9)] private class EqualNode : BinaryNode { public EqualNode(Node leftInput, Node rightInput) @@ -496,7 +583,23 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("lt", 0)] + [Operator("!=", 9)] + [Operator("ne", 9)] + private class NonEqualNode : BinaryNode + { + public NonEqualNode(Node leftInput, Node rightInput) + : base(leftInput, rightInput) + { + } + + public override double GetValue(Interpreter interpreter) + { + return System.Math.Abs(this.leftInput.GetValue(interpreter) - this.rightInput.GetValue(interpreter)) < double.Epsilon ? FALSE : TRUE; + } + } + + [Operator("lt", 8)] + [Operator("<", 8)] private class LtNode : BinaryNode { public LtNode(Node leftInput, Node rightInput) @@ -510,7 +613,8 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("lte", 0)] + [Operator("lte", 8)] + [Operator("<=", 8)] private class LteNode : BinaryNode { public LteNode(Node leftInput, Node rightInput) @@ -524,7 +628,8 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("gt", 0)] + [Operator("gt", 8)] + [Operator(">", 8)] private class GtNode : BinaryNode { public GtNode(Node leftInput, Node rightInput) @@ -538,7 +643,8 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("gte", 0)] + [Operator("gte", 8)] + [Operator(">=", 8)] private class GteNode : BinaryNode { public GteNode(Node leftInput, Node rightInput) @@ -552,8 +658,8 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("d", 90)] - [Operator("D", 90)] + [Operator("d")] + [Operator("D")] private class DiceNode : BinaryNode { public DiceNode(Node leftInput, Node rightInput) @@ -576,8 +682,8 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("&", 0)] - [Operator("and", 0)] + [Operator("&&", 13)] + [Operator("and", 13)] private class AndNode : BinaryNode { public AndNode(Node leftInput, Node rightInput) @@ -591,8 +697,8 @@ public override double GetValue(Interpreter interpreter) } } - [Operator("|", 0)] - [Operator("or", 0)] + [Operator("||", 14)] + [Operator("or", 14)] private class OrNode : BinaryNode { public OrNode(Node leftInput, Node rightInput) @@ -606,11 +712,65 @@ public override double GetValue(Interpreter interpreter) } } + [Operator("<<", 7)] + private class LeftShiftNode : BinaryNode + { + public LeftShiftNode(Node leftInput, Node rightInput) : base(leftInput, rightInput) + { + } + + public override double GetValue(Interpreter interpreter) + { + return (double)((int)this.leftInput.GetValue(interpreter) << (int)this.rightInput.GetValue(interpreter)); + } + } + + [Operator(">>", 7)] + private class RightShiftNode : BinaryNode + { + public RightShiftNode(Node leftInput, Node rightInput) : base(leftInput, rightInput) + { + } + + public override double GetValue(Interpreter interpreter) + { + return (double)((int)this.leftInput.GetValue(interpreter) >> (int)this.rightInput.GetValue(interpreter)); + } + } + + [Operator("|", 12)] + private class BitOrNode : BinaryNode + { + public BitOrNode(Node leftInput, Node rightInput) : base(leftInput, rightInput) + { + } + + public override double GetValue(Interpreter interpreter) + { + return (double)((int)this.leftInput.GetValue(interpreter) | (int)this.rightInput.GetValue(interpreter)); + } + } + + [Operator("&", 10)] + private class BitAndNode : BinaryNode + { + public BitAndNode(Node leftInput, Node rightInput) : base(leftInput, rightInput) + { + } + + public override double GetValue(Interpreter interpreter) + { + return (double)((int)this.leftInput.GetValue(interpreter) & (int)this.rightInput.GetValue(interpreter)); + } + } + #endregion [System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = true)] private class OperatorAttribute : System.Attribute { + private const int FunctionPriority = 2; + public string Symbol; public int Priority; @@ -619,6 +779,11 @@ public OperatorAttribute(string symbol, int priority) Symbol = symbol; Priority = priority; } + + public OperatorAttribute(string symbol) + : this(symbol, OperatorAttribute.FunctionPriority) + { + } } } } diff --git a/Library/Interpreter.cs b/Library/Interpreter.cs index 17656c4..de71f49 100644 --- a/Library/Interpreter.cs +++ b/Library/Interpreter.cs @@ -56,10 +56,7 @@ public partial class Interpreter private readonly System.Collections.Generic.Dictionary variables; private System.Collections.Generic.Dictionary namedContext; - - [System.Obsolete("use Interpreter.namedContext instead.")] - private IInterpreterContext interpreterContext; - + #endregion #region Enumerations @@ -88,14 +85,7 @@ public Interpreter() this.variables = new System.Collections.Generic.Dictionary(); this.namedContext = new System.Collections.Generic.Dictionary(); } - - [System.Obsolete("Use Interpreter.SetContext(string, IINterpreterContext) instead.")] - public Interpreter(IInterpreterContext interpreterContext) - : this() - { - this.SetContext(interpreterContext); - } - + #endregion #region Public Functions @@ -114,16 +104,6 @@ public void SetVar(string name, double value) } } - /// - /// Sets an interpreter context to be use un variables resolution. - /// - /// An object that implements Hef.Math.IInterpreterContext. - [System.Obsolete("Use Interpreter.SetContext(string, IINterpreterContext) instead.")] - public void SetContext(IInterpreterContext interpreterContext) - { - this.interpreterContext = interpreterContext; - } - /// /// Sets an interpreter context to be use un variables resolution. /// @@ -191,7 +171,7 @@ private static int ComparePrecedence(string a, string b) throw new System.Exception(string.Format("Operator '{0}' is not registered.", b)); } - return Interpreter.operators[a].Priority - Interpreter.operators[b].Priority; + return Interpreter.operators[b].Priority - Interpreter.operators[a].Priority; } private static bool IsAlpha(char c) @@ -207,14 +187,30 @@ private static bool IsNumeric(char c) private static int SkipString(string value, int index) { // Also alow dots for names context cariable access `$xxx.yyy`. - while (index < value.Length && (Interpreter.IsAlpha(value[index]) || value[index] == '.')) + // [#12] Operators with names containing digits fail -> Fixed by adding IsNumeric(). + while (index < value.Length && (Interpreter.IsAlpha(value[index]) || Interpreter.IsNumeric(value[index]) || value[index] == '.')) { ++index; } return index; } - + + private static bool IsSpecial(char c) + { + return !IsNumeric(c) && !IsAlpha(c) && c != OpenBracketChar && c != ClosingBracketChar && c != VarPrefixChar && c != WhiteSpaceChar && c != '.' && c != '±'; + } + + private static int SkipSpecial(string value, int index) + { + while (index < value.Length && IsSpecial(value[index])) + { + ++index; + } + + return index; + } + private static string InfixToRpn(string infix) { // Replace comma separator with white space for function-like use of operators. @@ -233,6 +229,12 @@ private static string InfixToRpn(string infix) index = Interpreter.SkipString(infix, index + 2); infix = infix.Insert(index, Interpreter.LongOpMark1Str); } + else if (Interpreter.IsSpecial(infix[index])) + { + infix = infix.Insert(index, Interpreter.LongOpMark0Str); + index = Interpreter.SkipSpecial(infix, index + 2); + infix = infix.Insert(index, Interpreter.LongOpMark1Str); + } } // Add blank spaces where needed. @@ -411,12 +413,6 @@ private bool TryGetVariableValue(string varName, out double value) return true; } - if (this.interpreterContext != null && - this.interpreterContext.TryGetVariable(varName.TrimStart(Interpreter.VarPrefixChar), out value)) - { - return true; - } - if (System.Text.RegularExpressions.Regex.IsMatch(varName, @"\$\w+.\w+")) { string contextName = varName.Substring(varName.IndexOf('$') + 1, varName.IndexOf('.') - 1); diff --git a/NuGet/Hef.Math.Interpreter.nuspec b/NuGet/Hef.Math.Interpreter.nuspec index 4404404..bb6e1af 100644 --- a/NuGet/Hef.Math.Interpreter.nuspec +++ b/NuGet/Hef.Math.Interpreter.nuspec @@ -2,7 +2,7 @@ Hef.Math.Interpreter - 0.2.0-alpha + 1.0.0 François Ségaud fseg https://opensource.org/licenses/MIT diff --git a/README.md b/README.md index 54b2582..00584c4 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ You can also install the correpsonding _NuGet_ package at [https://www.nuget.org Or install it using the _NuGet_ console. ``` -Install-Package Hef.Math.Interpreter -Version 0.2.0-alpha +Install-Package Hef.Math.Interpreter -Version 1.0.0-alpha ``` ### Examples @@ -40,7 +40,7 @@ Here is the simplest example. ```csharp Interpreter interpreter = new Interpreter(); double result = interpreter.Calculate("sqrt(4) + 2"); // -> 4 -// "sqrt 4 + 2" or "sqrt4+2" would work as well +// "sqrt 4 + 2" would work as well ``` The following example highlights the use of manually registered variables. @@ -82,66 +82,82 @@ class Player : Hef.Math.IInterpreterContext #### Basic Operators -| Symbol | Operation | Comment | Version | -|:------:|-------------|------------------------------------|:-------:| -| `±` | Sign Change | -1 should be written ±1 (atl+0177) | 0.1.0 | -| `+` | Addition | | 0.1.0 | -| `-` | Subtraction | | 0.1.0 | -| `*` | Product | | 0.1.0 | -| `/` | Division | | 0.1.0 | -| `%` | Modulo | | 0.1.0 | -| `^` | Power | | 0.1.0 | -| `sqrt` | Square Root | | 0.1.0 | +| Symbol | Operation | Comment | Version | +|:-------------:|-------------|------------------------------------|:-------:| +| `±` | Sign Change | -1 should be written ±1 (atl+0177) | 0.1.0 | +| `+` | Addition | | 0.1.0 | +| `-` | Subtraction | | 0.1.0 | +| `*` | Product | | 0.1.0 | +| `/` | Division | | 0.1.0 | +| `%` | Modulo | | 0.1.0 | #### Advanced Operators -| Symbol | Operation | Comment | Version | -|:-------:|----------------|---------|:-------:| -| `abs` | Absolute Value | | 0.1.0 | -| `round` | Round | | 0.1.0 | -| `min` | Minimum | | 0.1.0 | -| `max` | Maximum | | 0.1.0 | +| Symbol | Operation | Comment | Version | +|:------------:|---------------------------|---------|:-------:| +| `^` or `pow` | Power | | 0.1.0 | +| `sqrt` | Square Root | | 0.1.0 | +| `abs` | Absolute Value | | 0.1.0 | +| `round` | Round | | 0.1.0 | +| `min` | Minimum | | 0.1.0 | +| `max` | Maximum | | 0.1.0 | +| `ceil` | Ceil to upper integer | | 1.0.0 | +| `floor` | Floot to lower integer | | 1.0.0 | +| `trunc` | Truncate the decimal part | | 1.0.0 | +| `log` | Logarithm | | 1.0.0 | +| `log10` | Logarithm base 10 | | 1.0.0 | +| `e` or `exp` | Exponential | | 1.0.0 | #### Comparison Operators -| Symbol | Operation | Comment | Version | -|:------------:|------------------|----------------------------|:-------:| -| `==` or `eq` | Equal | 1 == 1 -> 1, 1 eq 2 -> 0 | 0.1.0 | -| `gt` | Greater Than | 1 gt 1 -> 0, 1 gt 2 -> 1 | 0.1.0 | -| `gte` | Greater Or Equal | 1 gte 0 -> 0, 1 gte 1 -> 1 | 0.1.0 | -| `lt` | Less Than | 1 lt 1 -> 0, 1 lt 2 -> 1 | 0.1.0 | -| `lte` | Less Or Equal | 1 lte 1 -> 1, 1 lte 0 -> 0 | 0.1.0 | +| Symbol | Operation | Comment | Version | +|:-------------:|------------------|----------------------------|:-------:| +| `==` or `eq` | Equal | 1 == 1 -> 1, 1 eq 2 -> 0 | 0.1.0 | +| `gt` or `>` | Greater Than | 1 gt 1 -> 0, 1 gt 2 -> 1 | 0.1.0 | +| `gte` or `>=` | Greater Or Equal | 1 gte 0 -> 0, 1 gte 1 -> 1 | 0.1.0 | +| `lt` or `<` | Less Than | 1 lt 1 -> 0, 1 lt 2 -> 1 | 0.1.0 | +| `lte` or `<=` | Less Or Equal | 1 lte 1 -> 1, 1 lte 0 -> 0 | 0.1.0 | +| `!=` or `ne` | Equal | 1 != 1 -> 0, 1 ne 2 -> 1 | 1.0.0 | #### Logical Operators -| Symbol | Operation | Comment | Version | -|:------------:|------------------|----------------------------|:-------:| -| `!` | Not | !0 -> 1, !1 -> 0 | 0.1.0 | -| `&` or `and` | And | true & true -> true | 0.1.1 | -| `\|` or `or` | Or | true & false -> true | 0.1.1 | +| Symbol | Operation | Comment | Version | +|:--------------:|------------------|----------------------------|:-------:| +| `!` | Not | !0 -> 1, !1 -> 0 | 0.1.0 | +| `&&` or `and` | And | true & true -> true | 1.0.0 | +| `\|\|` or `or` | Or | true & false -> true | 1.0.0 | + +#### Bitwise Operators + +| Symbol | Operation | Comment | Version | +|:------:|-----------------|----------------------------|:-------:| +| `<<` | Left Bitshift | | 1.0.0 | +| `>>` | Right Bitshift | | 1.0.0 | +| `&` | And | | 1.0.0 | +| `\|` | Or | | 1.0.0 | #### Trigonometry -| Symbol | Operation | Comment | Version | -|:--------:|--------------------|-----------------------------|:-------:| -| `cos` | Cosine | | 0.1.0 | -| `sin` | Sine | | 0.1.0 | -| `tan` | Tangent | | 0.1.0 | -| `acos` | Arccosine | | 0.1.1 | -| `asin` | Arcsine | | 0.1.1 | -| `atan` | Arctangent | | 0.1.1 | -| `cosh` | Hyperbolic Cosine | | 0.1.1 | -| `sinh` | Hyperbolic Sine | | 0.1.1 | -| `tanh` | Hyperbolic Tangent | | 0.1.1 | -| `degrad` | Deg2Rad | Converts degrees to radians | 0.1.1 | -| `raddeg` | Rad2Deg | Converts radians to degrees | 0.1.1 | +| Symbol | Operation | Comment | Version | +|:---------:|--------------------|-----------------------------|:-------:| +| `cos` | Cosine | | 0.1.0 | +| `sin` | Sine | | 0.1.0 | +| `tan` | Tangent | | 0.1.0 | +| `acos` | Arccosine | | 0.1.1 | +| `asin` | Arcsine | | 0.1.1 | +| `atan` | Arctangent | | 0.1.1 | +| `cosh` | Hyperbolic Cosine | | 0.1.1 | +| `sinh` | Hyperbolic Sine | | 0.1.1 | +| `tanh` | Hyperbolic Tangent | | 0.1.1 | +| `deg2rad` | Deg2Rad | Converts degrees to radians | 1.0.0 | +| `rad2deg` | Rad2Deg | Converts radians to degrees | 1.0.0 | #### Randomization -| Symbol | Operation | Comment | Version | -|:----------:|-----------|-----------------|:-------:| -| `rand` | Random | rand 5 -> [0,5] | 0.1.0 | -| `d` or `D` | Dice | 2d6 -> [2,12] | 0.1.0 | +| Symbol | Operation | Comment | Version | +|:----------:|-----------|-------------------|:-------:| +| `rand` | Random | rand 5 -> [0,5] | 0.1.0 | +| `d` or `D` | Dice | 2 d 6 -> [2,12] | 0.1.0 | #### Constants @@ -176,13 +192,13 @@ Then add the `OperatorAttribute` and fill the symbol and priority. > INFO: The `OperatorAttribute` is stackable. -> INFO: Highest priorities are executed first. +> INFO: Lowest priorities are executed first. Default priority is 2 (functions). The following example show the implementation of an operator that halves an operand (unary operator). Its symbols will be `#` and `half`. ```csharp -[Operator("#", 5)] -[Operator("half", 5)] +[Operator("#", 2)] +[Operator("half", 2)] private class HalfNode : UnaryNode { public HalfNode(Node input) diff --git a/Test/Program.cs b/Test/Program.cs index 2877fcd..425c4ec 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -20,6 +20,8 @@ // SOFTWARE. #endregion +using System; + namespace Hef.Math.Test { class Program @@ -45,7 +47,7 @@ static void Main(string[] args) interpreter.SetVar("hundred", hundred); bool success = true; - + // Old tests. success &= Test("±1", -1d); success &= Test("1-1", 1d - 1d); @@ -53,13 +55,14 @@ static void Main(string[] args) success &= Test("2 + 2", 2 + 2d); success &= Test("2+2", 2d + 2d); success &= Test("(2+2)", 2d + 2d); - success &= Test("sqrt4+3*4", System.Math.Sqrt(4) + 3 * 4); - success &= Test("(sqrt4+3)*4", (System.Math.Sqrt(4) + 3) * 4); + success &= Test("sqrt 4 + 3 * 4", System.Math.Sqrt(4) + 3 * 4); + success &= Test("sqrt 4+3*4", System.Math.Sqrt(4) + 3 * 4); + success &= Test("(sqrt 4+3)*4", (System.Math.Sqrt(4) + 3) * 4); success &= Test("5 * ±1", 5 * -1d); success &= Test("abs ±1", System.Math.Abs(-1d)); success &= Test("sin(1+2)", System.Math.Sin(1 + 2)); - success &= Test("sin1+2", System.Math.Sin(1) + 2); - success &= Test("sin1*cos2+cos1*sin2", System.Math.Sin(1) * System.Math.Cos(2) + System.Math.Cos(1) * System.Math.Sin(2)); + success &= Test("sin 1+2", System.Math.Sin(1) + 2); + success &= Test("sin 1*cos 2+cos 1*sin 2", System.Math.Sin(1) * System.Math.Cos(2) + System.Math.Cos(1) * System.Math.Sin(2)); success &= Test("(2 * 5 == 10) * 5", (2d * 5d == 10 ? 1d : 0d) * 5d); success &= Test("min 4 6", System.Math.Min(4d, 6d)); success &= Test("max 4 6", System.Math.Max(4d, 6d)); @@ -71,13 +74,19 @@ static void Main(string[] args) success &= Test("sqrt($hundred^2)", System.Math.Sqrt(hundred * hundred)); success &= Test("$Foo + $bar", foo + bar); success &= Test("round (rand * 10 + 90)"); - success &= Test("1d4+1 + 1D6+1"); + success &= Test("1 d 4+1 + 1 D 6+1"); + success &= Test("10^2"); + success &= Test("pow(10, 2)"); // Comparison. success &= Test("1 == 0", BoolToDouble(1d == 0d)); success &= Test("1 == 1", BoolToDouble(1d == 1d)); success &= Test("1 eq 0", BoolToDouble(1d == 0d)); success &= Test("1 eq 1", BoolToDouble(1d == 1d)); + success &= Test("1 != 0", BoolToDouble(1d != 0d)); + success &= Test("1 != 1", BoolToDouble(1d != 1d)); + success &= Test("1 ne 0", BoolToDouble(1d != 0d)); + success &= Test("1 ne 1", BoolToDouble(1d != 1d)); success &= Test("1 gt 0", BoolToDouble(1d > 0d)); success &= Test("1 gt 1", BoolToDouble(1d > 1d)); success &= Test("1 gt 2", BoolToDouble(1d > 2d)); @@ -90,6 +99,22 @@ static void Main(string[] args) success &= Test("1 lte 0", BoolToDouble(1d <= 0d)); success &= Test("1 lte 1", BoolToDouble(1d <= 1d)); success &= Test("1 lte 2", BoolToDouble(1d <= 2d)); + success &= Test("1 > 0", BoolToDouble(1d > 0d)); + success &= Test("1 > 1", BoolToDouble(1d > 1d)); + success &= Test("1 > 2", BoolToDouble(1d > 2d)); + success &= Test("1 >= 0", BoolToDouble(1d >= 0d)); + success &= Test("1 >= 1", BoolToDouble(1d >= 1d)); + success &= Test("1 >= 2", BoolToDouble(1d >= 2d)); + success &= Test("1 < 0", BoolToDouble(1d < 0d)); + success &= Test("1 < 1", BoolToDouble(1d < 1d)); + success &= Test("1 < 2", BoolToDouble(1d < 2d)); + success &= Test("1 <= 0", BoolToDouble(1d <= 0d)); + success &= Test("1 <= 1", BoolToDouble(1d <= 1d)); + success &= Test("1 <= 2", BoolToDouble(1d <= 2d)); + success &= Test("(1 eq 1) == (1 == 1)", TRUE); + success &= Test("(1 eq 0) == (1 == 0)", TRUE); + success &= Test("(1 eq 1) eq (0 == 0)", TRUE); + success &= Test("(1 eq 0) eq (0 == 1)", TRUE); // Boolean. success &= Test("!1", FALSE); @@ -100,23 +125,33 @@ static void Main(string[] args) success &= Test("false", BoolToDouble(false)); success &= Test("!true", BoolToDouble(!true)); success &= Test("!false", BoolToDouble(!false)); - success &= Test("true & true", BoolToDouble(true && true)); - success &= Test("true & false", BoolToDouble(true && false)); - success &= Test("false & true", BoolToDouble(false && true)); - success &= Test("false & false", BoolToDouble(false && false)); + success &= Test("true && true", BoolToDouble(true && true)); + success &= Test("true && false", BoolToDouble(true && false)); + success &= Test("false && true", BoolToDouble(false && true)); + success &= Test("false && false", BoolToDouble(false && false)); success &= Test("true and true", BoolToDouble(true && true)); success &= Test("true and false", BoolToDouble(true && false)); success &= Test("false and true", BoolToDouble(false && true)); success &= Test("false and false", BoolToDouble(false && false)); - success &= Test("true | true", BoolToDouble(true || true)); - success &= Test("true | false", BoolToDouble(true || false)); - success &= Test("false | true", BoolToDouble(false || true)); - success &= Test("false | false", BoolToDouble(false || false)); + success &= Test("true || true", BoolToDouble(true || true)); + success &= Test("true || false", BoolToDouble(true || false)); + success &= Test("false || true", BoolToDouble(false || true)); + success &= Test("false || false", BoolToDouble(false || false)); success &= Test("true or true", BoolToDouble(true || true)); success &= Test("true or false", BoolToDouble(true || false)); success &= Test("false or true", BoolToDouble(false || true)); success &= Test("false or false", BoolToDouble(false || false)); + // Binary + success &= Test("1 << 4", 1 << 4); + success &= Test("32 >> 4", 32 >> 4); + success &= Test("1 << 4 >> 4", 1 << 4 >> 4); + success &= Test("32 >> 4 << 4", 32 >> 4 << 4); + success &= Test("4 | 2", 4 | 2); + success &= Test("6 | 2", 6 | 2); + success &= Test("4 & 2", 4 & 2); + success &= Test("6 & 2", 6 & 2); + // Trigonometry. success &= Test("cos 0", System.Math.Cos(0d)); success &= Test("cos (pi / 2)", System.Math.Cos(System.Math.PI / 2d)); @@ -134,16 +169,16 @@ static void Main(string[] args) success &= Test("tan pi", System.Math.Tan(System.Math.PI)); success &= Test("tan (pi / 4)", System.Math.Tan(System.Math.PI / 4d)); success &= Test("tan (3 * pi / 4)", System.Math.Tan(3 * System.Math.PI / 4d)); - success &= Test("degrad 0", 0d); - success &= Test("degrad 90", System.Math.PI * .5d); - success &= Test("degrad 180", System.Math.PI); - success &= Test("degrad 270", System.Math.PI * 1.5d); - success &= Test("degrad 360", System.Math.PI * 2d); - success &= Test("raddeg (0)", 0d); - success &= Test("raddeg (pi * 0.5)", 90d); - success &= Test("raddeg (pi)", 180d); - success &= Test("raddeg (pi * 1.5)", 270d); - success &= Test("raddeg (pi * 2)", 360d); + success &= Test("deg2rad 0", 0d); + success &= Test("deg2rad 90", System.Math.PI * .5d); + success &= Test("deg2rad 180", System.Math.PI); + success &= Test("deg2rad 270", System.Math.PI * 1.5d); + success &= Test("deg2rad 360", System.Math.PI * 2d); + success &= Test("rad2deg (0)", 0d); + success &= Test("rad2deg (pi * 0.5)", 90d); + success &= Test("rad2deg (pi)", 180d); + success &= Test("rad2deg (pi * 1.5)", 270d); + success &= Test("rad2deg (pi * 2)", 360d); // Writing style and comma separator. success &= Test("min(1,2)", System.Math.Min(1, 2)); @@ -152,18 +187,46 @@ static void Main(string[] args) success &= Test("min 1 2", System.Math.Min(1, 2)); success &= Test("min 1,2", System.Math.Min(1, 2)); success &= Test("min 1, 2", System.Math.Min(1, 2)); - success &= Test("min1, 2", System.Math.Min(1, 2)); - success &= Test("min1,2", System.Math.Min(1, 2)); - success &= Test("min1 2", System.Math.Min(1, 2)); success &= Test("min(2,1)", System.Math.Min(1, 2)); success &= Test("min(2, 1)", System.Math.Min(1, 2)); success &= Test("min(2 1)", System.Math.Min(1, 2)); success &= Test("min 2 1", System.Math.Min(1, 2)); success &= Test("min 2,1", System.Math.Min(1, 2)); success &= Test("min 2, 1", System.Math.Min(1, 2)); - success &= Test("min2, 1", System.Math.Min(1, 2)); - success &= Test("min2,1", System.Math.Min(1, 2)); - success &= Test("min2 1", System.Math.Min(1, 2)); + + // Rounding + success &= Test("round(1.0)", System.Math.Round(1.0d)); + success &= Test("round(1.1)", System.Math.Round(1.1d)); + success &= Test("round(1.5)", System.Math.Round(1.5d)); + success &= Test("round(1.9)", System.Math.Round(1.9d)); + success &= Test("trunc(1.0)", System.Math.Truncate(1.0d)); + success &= Test("trunc(1.1)", System.Math.Truncate(1.1d)); + success &= Test("trunc(1.5)", System.Math.Truncate(1.5d)); + success &= Test("trunc(1.9)", System.Math.Truncate(1.9d)); + success &= Test("floor(1.0)", System.Math.Floor(1.0d)); + success &= Test("floor(1.1)", System.Math.Floor(1.1d)); + success &= Test("floor(1.5)", System.Math.Floor(1.5d)); + success &= Test("floor(1.9)", System.Math.Floor(1.9d)); + success &= Test("ceil(1.0)", System.Math.Ceiling(1.0d)); + success &= Test("ceil(1.1)", System.Math.Ceiling(1.1d)); + success &= Test("ceil(1.5)", System.Math.Ceiling(1.5d)); + success &= Test("ceil(1.9)", System.Math.Ceiling(1.9d)); + + // Algebra. + success &= Test("log(0.5)", System.Math.Log(.5d)); + success &= Test("log(1.0)", System.Math.Log(1d)); + success &= Test("log(2.0)", System.Math.Log(2d)); + success &= Test("log10(0.5)", System.Math.Log10(.5d)); + success &= Test("log10(1.0)", System.Math.Log10(1d)); + success &= Test("log10(2.0)", System.Math.Log10(2d)); + success &= Test("e(0.0)", System.Math.Exp(0d)); + success &= Test("e(0.5)", System.Math.Exp(.5d)); + success &= Test("e(1.0)", System.Math.Exp(1d)); + success &= Test("e(2.0)", System.Math.Exp(2d)); + success &= Test("exp(0.0)", System.Math.Exp(0d)); + success &= Test("exp(0.5)", System.Math.Exp(.5d)); + success &= Test("exp(1.0)", System.Math.Exp(1d)); + success &= Test("exp(2.0)", System.Math.Exp(2d)); System.Console.WriteLine("--------------------\nOVERALL RESULT: " + success); }