diff --git a/src/platform-independent/SpiceNetlist.SpiceSharpConnector/Processors/Evaluation/SpiceExpression.cs b/src/platform-independent/SpiceNetlist.SpiceSharpConnector/Processors/Evaluation/SpiceExpression.cs index e4f94a5e..840084aa 100644 --- a/src/platform-independent/SpiceNetlist.SpiceSharpConnector/Processors/Evaluation/SpiceExpression.cs +++ b/src/platform-independent/SpiceNetlist.SpiceSharpConnector/Processors/Evaluation/SpiceExpression.cs @@ -5,7 +5,7 @@ namespace SpiceNetlist.SpiceSharpConnector.Processors.Evaluation { /// - /// @author: Sven Boulanger + /// @author: Sven Boulanger /// A very light-weight and fast expression parser made for parsing Spice expressions /// It is based on Dijkstra's Shunting Yard algorithm. It is very fast for parsing expressions only once. /// The parser is also not very expressive for errors, so only use it for relatively simple expressions. @@ -30,192 +30,154 @@ namespace SpiceNetlist.SpiceSharpConnector.Processors.Evaluation /// public class SpiceExpression { - /// - /// Operator description - /// - private class Operator - { - public byte ID; - public byte Precedence; - public bool leftAssociative; - - /// - /// Constructor - /// - /// The operator ID - /// The operator precedence - /// Is the operator left-associative? - public Operator(byte id, byte precedence, bool la) - { - ID = id; - Precedence = precedence; - leftAssociative = la; - } - } - - /// - /// Function description - /// - private class FunctionOperator : Operator - { - public Function Func; - - /// - /// Constructor - /// - /// The function - public FunctionOperator(Function func) - : base(ID_FUNCTION, byte.MaxValue, false) - { - Func = func; - } - } - - /// - /// Delegate for functions - /// - /// The output stack - /// - private delegate double Function(Stack output); - /// /// Precedence levels /// - private const byte PRE_CONDITIONAL = 1; - private const byte PRE_CONDITIONAL_OR = 2; - private const byte PRE_CONDITIONAL_AND = 3; - private const byte PRE_LOGICAL_OR = 4; - private const byte PRE_LOGICAL_XOR = 5; - private const byte PRE_LOGICAL_AND = 6; - private const byte PRE_EQUALITY = 7; - private const byte PRE_RELATIONAL = 8; - private const byte PRE_SHIFT = 9; - private const byte PRE_ADDITIVE = 10; - private const byte PRE_MULTIPLICATIVE = 11; - private const byte PRE_UNARY = 12; - private const byte PRE_PRIMARY = 13; + private const byte PrecedenceConditional = 1; + private const byte PrecedenceConditionalOr = 2; + private const byte PrecedenceConditionalAnd = 3; + private const byte PrecedenceLogicalOr = 4; + private const byte PrecedenceLogicalXor = 5; + private const byte PrecedenceLogicalAnd = 6; + private const byte PrecedenceEquality = 7; + private const byte PrecedenceRelational = 8; + private const byte PrecedenceShift = 9; + private const byte PrecedenceAdditive = 10; + private const byte PrecedenceMultiplicative = 11; + private const byte PrecedenceUnary = 12; + private const byte PrecedencePrimary = 13; /// /// Operator ID's /// - private const byte ID_POSITIVE = 0; - private const byte ID_NEGATIVE = 1; - private const byte ID_NOT = 2; - private const byte ID_ADD = 3; - private const byte ID_SUBTRACT = 4; - private const byte ID_MULTIPLY = 5; - private const byte ID_DIVIDE = 6; - private const byte ID_MODULO = 7; - private const byte ID_EQUALS = 8; - private const byte ID_INEQUALS = 9; - private const byte ID_OPENCONDITIONAL = 10; - private const byte ID_CLOSEDCONDITIONAL = 11; - private const byte ID_CONDITIONAL_OR = 12; - private const byte ID_CONDITIONAL_AND = 13; - private const byte ID_LESS = 14; - private const byte ID_LESSEQUAL = 15; - private const byte ID_GREATER = 16; - private const byte ID_GREATEREQUAL = 17; - private const byte ID_LEFTBRACKET = 18; - private const byte ID_FUNCTION = 19; + private const byte IdPositive = 0; + private const byte IdNegative = 1; + private const byte IdNot = 2; + private const byte IdAdd = 3; + private const byte IdSubtract = 4; + private const byte IdMultiply = 5; + private const byte IdDivide = 6; + private const byte IdModulo = 7; + private const byte IdEquals = 8; + private const byte IdInequals = 9; + private const byte IdOpenConditional = 10; + private const byte IdClosedConditional = 11; + private const byte IdConditionalOr = 12; + private const byte IdConditionalAnd = 13; + private const byte IdLess = 14; + private const byte IdLessOrEqual = 15; + private const byte IdGreater = 16; + private const byte IdGreaterOrEqual = 17; + private const byte IdLeftBracket = 18; + private const byte IdFunction = 19; /// /// Operators /// - private static Operator OP_POSITIVE = new Operator(ID_POSITIVE, PRE_UNARY, false); - private static Operator OP_NEGATIVE = new Operator(ID_NEGATIVE, PRE_UNARY, false); - private static Operator OP_NOT = new Operator(ID_NOT, PRE_UNARY, false); - - private static Operator OP_ADD = new Operator(ID_ADD, PRE_ADDITIVE, true); - private static Operator OP_SUBTRACT = new Operator(ID_SUBTRACT, PRE_ADDITIVE, true); - private static Operator OP_MULTIPLY = new Operator(ID_MULTIPLY, PRE_MULTIPLICATIVE, true); - private static Operator OP_DIVIDE = new Operator(ID_DIVIDE, PRE_MULTIPLICATIVE, true); - private static Operator OP_MODULO = new Operator(ID_MODULO, PRE_MULTIPLICATIVE, true); - private static Operator OP_EQUALS = new Operator(ID_EQUALS, PRE_EQUALITY, true); - private static Operator OP_INEQUALS = new Operator(ID_INEQUALS, PRE_EQUALITY, true); - private static Operator OP_OPENCONDITIONAL = new Operator(ID_OPENCONDITIONAL, PRE_CONDITIONAL, false); - private static Operator OP_CLOSEDCONDITIONAL = new Operator(ID_CLOSEDCONDITIONAL, PRE_CONDITIONAL, false); - private static Operator OP_CONDITIONAL_OR = new Operator(ID_CONDITIONAL_OR, PRE_CONDITIONAL_OR, true); - private static Operator OP_CONDITIONAL_AND = new Operator(ID_CONDITIONAL_AND, PRE_CONDITIONAL_AND, true); - private static Operator OP_LESS = new Operator(ID_LESS, PRE_RELATIONAL, true); - private static Operator OP_LESSEQUAL = new Operator(ID_LESSEQUAL, PRE_RELATIONAL, true); - private static Operator OP_GREATER = new Operator(ID_GREATER, PRE_RELATIONAL, true); - private static Operator OP_GREATEREQUAL = new Operator(ID_GREATEREQUAL, PRE_RELATIONAL, true); - private static Operator OP_LEFTBRACKET = new Operator(ID_LEFTBRACKET, byte.MaxValue, false); + private static readonly Operator OperatorPositive = new Operator(IdPositive, PrecedenceUnary, false); + private static readonly Operator OperatorNegative = new Operator(IdNegative, PrecedenceUnary, false); + private static readonly Operator OperatorNot = new Operator(IdNot, PrecedenceUnary, false); + private static readonly Operator OperatorAdd = new Operator(IdAdd, PrecedenceAdditive, true); + private static readonly Operator OperatorSubtract = new Operator(IdSubtract, PrecedenceAdditive, true); + private static readonly Operator OperatorMultiply = new Operator(IdMultiply, PrecedenceMultiplicative, true); + private static readonly Operator OperatorDivide = new Operator(IdDivide, PrecedenceMultiplicative, true); + private static readonly Operator OperatorModulo = new Operator(IdModulo, PrecedenceMultiplicative, true); + private static readonly Operator OperatorEquals = new Operator(IdEquals, PrecedenceEquality, true); + private static readonly Operator OperatorInequals = new Operator(IdInequals, PrecedenceEquality, true); + private static readonly Operator OperatorOpenConditional = new Operator(IdOpenConditional, PrecedenceConditional, false); + private static readonly Operator OperatorClosedConditional = new Operator(IdClosedConditional, PrecedenceConditional, false); + private static readonly Operator OperatorConditionalOr = new Operator(IdConditionalOr, PrecedenceConditionalOr, true); + private static readonly Operator OperatorConditionalAnd = new Operator(IdConditionalAnd, PrecedenceConditionalAnd, true); + private static readonly Operator OperatorLess = new Operator(IdLess, PrecedenceRelational, true); + private static readonly Operator OperatorLessOrEqual = new Operator(IdLessOrEqual, PrecedenceRelational, true); + private static readonly Operator OperatorGreater = new Operator(IdGreater, PrecedenceRelational, true); + private static readonly Operator OperatorGreaterOrEqual = new Operator(IdGreaterOrEqual, PrecedenceRelational, true); + private static readonly Operator OperatorLeftBracket = new Operator(IdLeftBracket, byte.MaxValue, false); /// - /// The parameters for expressions + /// Private variables /// - public Dictionary Parameters { get; set; } + private readonly Stack outputStack = new Stack(); + private readonly Stack operatorStack = new Stack(); + private readonly StringBuilder sb = new StringBuilder(); + private int index; + private string input; + private bool infixPostfix; + private int count; /// - /// Functions + /// Gets or sets the parameters used for expressions /// - private Dictionary Functions { get; } = new Dictionary() - { - { "min", new FunctionOperator((Stack output) => Math.Min(output.Pop(), output.Pop())) }, - { "max", new FunctionOperator((Stack output) => Math.Max(output.Pop(), output.Pop())) }, - { "abs", new FunctionOperator((Stack output) => Math.Abs(output.Pop())) }, - { "sqrt", new FunctionOperator((Stack output) => Math.Sqrt(output.Pop())) }, - { "exp", new FunctionOperator((Stack output) => Math.Exp(output.Pop())) }, - { "log", new FunctionOperator((Stack output) => Math.Log(output.Pop())) }, - { "log10", new FunctionOperator((Stack output) => Math.Log10(output.Pop())) }, - { "pow", new FunctionOperator((Stack output) => { - double b = output.Pop(); - double a = output.Pop(); - return Math.Pow(a, b); - }) }, - { "cos", new FunctionOperator((Stack output) => Math.Cos(output.Pop())) }, - { "sin", new FunctionOperator((Stack output) => Math.Sin(output.Pop())) }, - { "tan", new FunctionOperator((Stack output) => Math.Tan(output.Pop())) }, - { "cosh", new FunctionOperator((Stack output) => Math.Cosh(output.Pop())) }, - { "sinh", new FunctionOperator((Stack output) => Math.Sinh(output.Pop())) }, - { "tanh", new FunctionOperator((Stack output) => Math.Tanh(output.Pop())) }, - { "acos", new FunctionOperator((Stack output) => Math.Acos(output.Pop())) }, - { "asin", new FunctionOperator((Stack output) => Math.Asin(output.Pop())) }, - { "atan", new FunctionOperator((Stack output) => Math.Atan(output.Pop())) }, - { "atan2", new FunctionOperator((Stack output) => { - double b = output.Pop(); - double a = output.Pop(); - return Math.Atan2(a, b); - }) } - }; + public Dictionary Parameters { get; set; } /// - /// Private variables + /// Gets all supported functions /// - private int i = 0; - private string input; - private StringBuilder sb = new StringBuilder(); - private bool infixPostfix = false; - Stack output = new Stack(); - Stack operators = new Stack(); - private int count = 0; + private Dictionary Functions { get; } = new Dictionary + { + { "min", new FunctionOperator(stack => Math.Min(stack.Pop(), stack.Pop())) }, + { "max", new FunctionOperator(stack => Math.Max(stack.Pop(), stack.Pop())) }, + { "abs", new FunctionOperator(stack => Math.Abs(stack.Pop())) }, + { "sqrt", new FunctionOperator(stack => Math.Sqrt(stack.Pop())) }, + { "exp", new FunctionOperator(stack => Math.Exp(stack.Pop())) }, + { "log", new FunctionOperator(stack => Math.Log(stack.Pop())) }, + { "log10", new FunctionOperator(stack => Math.Log10(stack.Pop())) }, + { + "pow", new FunctionOperator(stack => + { + var b = stack.Pop(); + var a = stack.Pop(); + return Math.Pow(a, b); + }) + }, + { "cos", new FunctionOperator(stack => Math.Cos(stack.Pop())) }, + { "sin", new FunctionOperator(stack => Math.Sin(stack.Pop())) }, + { "tan", new FunctionOperator(stack => Math.Tan(stack.Pop())) }, + { "cosh", new FunctionOperator(stack => Math.Cosh(stack.Pop())) }, + { "sinh", new FunctionOperator(stack => Math.Sinh(stack.Pop())) }, + { "tanh", new FunctionOperator(stack => Math.Tanh(stack.Pop())) }, + { "acos", new FunctionOperator(stack => Math.Acos(stack.Pop())) }, + { "asin", new FunctionOperator(stack => Math.Asin(stack.Pop())) }, + { "atan", new FunctionOperator(stack => Math.Atan(stack.Pop())) }, + { + "atan2", new FunctionOperator(stack => + { + var b = stack.Pop(); + var a = stack.Pop(); + return Math.Atan2(a, b); + }) + } + }; /// /// Parse an expression /// - /// + /// The expression + /// Parameters appearing in the expression + /// Returns the result of the expression public double Parse(string expression, out List expressionParameters) { expressionParameters = new List(); + // Initialize for parsing the expression - i = 0; - input = expression; + index = 0; + input = expression ?? throw new ArgumentNullException(nameof(expression)); infixPostfix = false; - output.Clear(); - operators.Clear(); + outputStack.Clear(); + operatorStack.Clear(); count = input.Length; // Parse the expression - while (i < count) + while (index < count) { // Skip spaces - while (i < count && input[i] == ' ') - i++; + while (index < count && input[index] == ' ') + { + index++; + } // Parse a double - char c = input[i]; + char c = input[index]; // Parse a binary operator if (infixPostfix) @@ -224,102 +186,141 @@ public double Parse(string expression, out List expressionParameters) infixPostfix = false; switch (c) { - case '+': PushOperator(OP_ADD); break; - case '-': PushOperator(OP_SUBTRACT); break; - case '*': PushOperator(OP_MULTIPLY); break; - case '/': PushOperator(OP_DIVIDE); break; - case '%': PushOperator(OP_MODULO); break; + case '+': PushOperator(OperatorAdd); break; + case '-': PushOperator(OperatorSubtract); break; + case '*': PushOperator(OperatorMultiply); break; + case '/': PushOperator(OperatorDivide); break; + case '%': PushOperator(OperatorModulo); break; case '=': - i++; - if (i < count && input[i] == '=') - PushOperator(OP_EQUALS); + index++; + if (index < count && input[index] == '=') + { + PushOperator(OperatorEquals); + } else + { goto default; + } + break; case '!': - i++; - if (i < count && input[i] == '=') - PushOperator(OP_INEQUALS); + index++; + if (index < count && input[index] == '=') + { + PushOperator(OperatorInequals); + } else + { goto default; + } + break; - case '?': PushOperator(OP_OPENCONDITIONAL); break; + case '?': PushOperator(OperatorOpenConditional); break; case ':': // Evaluate to an open conditional - while (operators.Count > 0) + while (operatorStack.Count > 0) { - if (operators.Peek().ID == ID_OPENCONDITIONAL) + if (operatorStack.Peek().Id == IdOpenConditional) + { break; - Evaluate(operators.Pop()); + } + + Evaluate(operatorStack.Pop()); } - operators.Pop(); - operators.Push(OP_CLOSEDCONDITIONAL); + + operatorStack.Pop(); + operatorStack.Push(OperatorClosedConditional); break; case '|': - i++; - if (i < count && input[i] == '|') - PushOperator(OP_CONDITIONAL_OR); + index++; + if (index < count && input[index] == '|') + { + PushOperator(OperatorConditionalOr); + } else + { goto default; + } + break; case '&': - i++; - if (i < count && input[i] == '&') - PushOperator(OP_CONDITIONAL_AND); + index++; + if (index < count && input[index] == '&') + { + PushOperator(OperatorConditionalAnd); + } else + { goto default; + } + break; case '<': - if (i + 1 < count && input[i + 1] == '=') + if (index + 1 < count && input[index + 1] == '=') { - PushOperator(OP_LESSEQUAL); - i++; + PushOperator(OperatorLessOrEqual); + index++; } else - PushOperator(OP_LESS); + { + PushOperator(OperatorLess); + } + break; case '>': - if (i + 1 < count && input[i + 1] == '=') + if (index + 1 < count && input[index + 1] == '=') { - PushOperator(OP_GREATEREQUAL); - i++; + PushOperator(OperatorGreaterOrEqual); + index++; } else - PushOperator(OP_GREATER); + { + PushOperator(OperatorGreater); + } + break; case ')': // Evaluate until the matching opening bracket - while (operators.Count > 0) + while (operatorStack.Count > 0) { - if (operators.Peek().ID == ID_LEFTBRACKET) + if (operatorStack.Peek().Id == IdLeftBracket) { - operators.Pop(); + operatorStack.Pop(); break; - } else if (operators.Peek().ID == ID_FUNCTION) + } + + if (operatorStack.Peek().Id == IdFunction) { - FunctionOperator op = (FunctionOperator)operators.Pop(); - output.Push(op.Func(output)); + FunctionOperator op = (FunctionOperator)operatorStack.Pop(); + outputStack.Push(op.Function(outputStack)); break; } - Evaluate(operators.Pop()); + + Evaluate(operatorStack.Pop()); } + infixPostfix = true; break; case ',': // Function argument - while (operators.Count > 0) + while (operatorStack.Count > 0) { - if (operators.Peek().ID == ID_FUNCTION) + if (operatorStack.Peek().Id == IdFunction) + { break; - Evaluate(operators.Pop()); + } + + Evaluate(operatorStack.Pop()); } + break; default: throw new Exception("Unrecognized operator"); } - i++; + + index++; } // Parse a unary operator @@ -327,7 +328,7 @@ public double Parse(string expression, out List expressionParameters) { if (c == '.' || (c >= '0' && c <= '9')) { - output.Push(ParseDouble()); + outputStack.Push(ParseDouble()); infixPostfix = true; } @@ -335,40 +336,45 @@ public double Parse(string expression, out List expressionParameters) else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { sb.Clear(); - sb.Append(input[i++]); - while (i < count) + sb.Append(input[index++]); + while (index < count) { - c = input[i]; + c = input[index]; if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') { sb.Append(c); - i++; + index++; } else + { break; + } } - if (i < count && input[i] == '(') + + if (index < count && input[index] == '(') { - i++; + index++; // "cos(10)+sin(-1)*max(tanh(1),sinh(1))" // Benchmark switch statements on function name: 1,000,000 -> 2400ms // Benchmark switch on first character + if/else function name: 1,000,000 -> 2200ms // Benchmark using Dictionary<>: 1,000,000 -> 2200ms -- I chose this option - operators.Push(Functions[sb.ToString()]); + operatorStack.Push(Functions[sb.ToString()]); } else if (Parameters != null) { string id = sb.ToString(); expressionParameters.Add(id); - output.Push(Parameters[id]); + outputStack.Push(Parameters[id]); infixPostfix = true; } else + { throw new Exception("No parameters"); + } } // Prefix operators @@ -376,96 +382,122 @@ public double Parse(string expression, out List expressionParameters) { switch (c) { - case '+': PushOperator(OP_POSITIVE); break; - case '-': PushOperator(OP_NEGATIVE); break; - case '!': PushOperator(OP_NOT); break; - case '(': PushOperator(OP_LEFTBRACKET); break; + case '+': PushOperator(OperatorPositive); break; + case '-': PushOperator(OperatorNegative); break; + case '!': PushOperator(OperatorNot); break; + case '(': PushOperator(OperatorLeftBracket); break; default: throw new Exception("Unrecognized unary operator"); } - i++; + + index++; } } } // Evaluate all that is left on the stack - while (operators.Count > 0) - Evaluate(operators.Pop()); - if (output.Count > 1) + while (operatorStack.Count > 0) + { + Evaluate(operatorStack.Pop()); + } + + if (outputStack.Count > 1) + { throw new Exception("Invalid expression"); - return output.Pop(); + } + + return outputStack.Pop(); } /// /// Evaluate operators with precedence /// - /// Precedence + /// Operator private void PushOperator(Operator op) { - while (operators.Count > 0) + while (operatorStack.Count > 0) { // Stop evaluation - Operator o = operators.Peek(); - if (o.Precedence < op.Precedence || !o.leftAssociative) + Operator o = operatorStack.Peek(); + if (o.Precedence < op.Precedence || !o.LeftAssociative) + { break; - Evaluate(operators.Pop()); + } + + Evaluate(operatorStack.Pop()); } - operators.Push(op); + + operatorStack.Push(op); } - + /// /// Evaluate an operator /// /// Operator private void Evaluate(Operator op) { - double a, b, c; - switch (op.ID) + double a, b; + switch (op.Id) { - case ID_POSITIVE: break; - case ID_NEGATIVE: output.Push(-output.Pop()); break; - case ID_NOT: - a = output.Pop(); - output.Push(a == 0.0 ? 1.0 : 0.0); + case IdPositive: break; + case IdNegative: outputStack.Push(-outputStack.Pop()); break; + case IdNot: + a = outputStack.Pop(); + outputStack.Push(a.Equals(0.0) ? 1.0 : 0.0); break; - case ID_ADD: output.Push(output.Pop() + output.Pop()); break; - case ID_SUBTRACT: - b = output.Pop(); - a = output.Pop(); - output.Push(a - b); + case IdAdd: outputStack.Push(outputStack.Pop() + outputStack.Pop()); break; + case IdSubtract: + b = outputStack.Pop(); + a = outputStack.Pop(); + outputStack.Push(a - b); break; - case ID_MULTIPLY: output.Push(output.Pop() * output.Pop()); break; - case ID_DIVIDE: - b = output.Pop(); - a = output.Pop(); - output.Push(a / b); + case IdMultiply: outputStack.Push(outputStack.Pop() * outputStack.Pop()); break; + case IdDivide: + b = outputStack.Pop(); + a = outputStack.Pop(); + outputStack.Push(a / b); break; - case ID_MODULO: - b = output.Pop(); - a = output.Pop(); - output.Push(a % b); + case IdModulo: + b = outputStack.Pop(); + a = outputStack.Pop(); + outputStack.Push(a % b); break; - case ID_EQUALS: output.Push(output.Pop() == output.Pop() ? 1.0 : 0.0); break; - case ID_INEQUALS: output.Push(output.Pop() != output.Pop() ? 1.0 : 0.0); break; - case ID_CONDITIONAL_AND: - b = output.Pop(); - a = output.Pop(); - output.Push((a != 0.0) && (b != 0.0) ? 1.0 : 0.0); break; - case ID_CONDITIONAL_OR: - b = output.Pop(); - a = output.Pop(); - output.Push((a != 0.0) || (b != 0.0) ? 1.0 : 0.0); break; - case ID_LESS: output.Push(output.Pop() > output.Pop() ? 1.0 : 0.0); break; - case ID_LESSEQUAL: output.Push(output.Pop() >= output.Pop() ? 1.0 : 0.0); break; - case ID_GREATER: output.Push(output.Pop() < output.Pop() ? 1.0 : 0.0); break; - case ID_GREATEREQUAL: output.Push(output.Pop() <= output.Pop() ? 1.0 : 0.0); break; - case ID_CLOSEDCONDITIONAL: - c = output.Pop(); - b = output.Pop(); - a = output.Pop(); - output.Push(a > 0.0 ? b : c); + case IdEquals: outputStack.Push(outputStack.Pop().Equals(outputStack.Pop()) ? 1.0 : 0.0); break; + case IdInequals: outputStack.Push(!outputStack.Pop().Equals(outputStack.Pop()) ? 1.0 : 0.0); break; + case IdConditionalAnd: + b = outputStack.Pop(); + a = outputStack.Pop(); + outputStack.Push(!a.Equals(0.0) && !b.Equals(0.0) ? 1.0 : 0.0); break; + case IdConditionalOr: + b = outputStack.Pop(); + a = outputStack.Pop(); + outputStack.Push(!a.Equals(0.0) || !b.Equals(0.0) ? 1.0 : 0.0); break; + case IdLess: + b = outputStack.Pop(); + a = outputStack.Pop(); + outputStack.Push(a < b ? 1.0 : 0.0); break; - case ID_OPENCONDITIONAL: throw new Exception("Unmatched conditional"); + case IdLessOrEqual: + b = outputStack.Pop(); + a = outputStack.Pop(); + outputStack.Push(a <= b ? 1.0 : 0.0); + break; + case IdGreater: + b = outputStack.Pop(); + a = outputStack.Pop(); + outputStack.Push(a > b ? 1.0 : 0.0); + break; + case IdGreaterOrEqual: + b = outputStack.Pop(); + a = outputStack.Pop(); + outputStack.Push(a >= b ? 1.0 : 0.0); break; + case IdClosedConditional: + var c = outputStack.Pop(); + b = outputStack.Pop(); + a = outputStack.Pop(); + outputStack.Push(a > 0.0 ? b : c); + break; + case IdOpenConditional: throw new Exception("Unmatched conditional"); default: throw new Exception("Unrecognized operator"); } @@ -474,111 +506,187 @@ private void Evaluate(Operator op) /// /// Parse a double value at the current position /// - /// + /// Parse result private double ParseDouble() { // Read integer part double value = 0.0; - while (i < count && (input[i] >= '0' && input[i] <= '9')) - value = value * 10.0 + (input[i++] - '0'); + while (index < count && (input[index] >= '0' && input[index] <= '9')) + { + value = (value * 10.0) + (input[index++] - '0'); + } // Read decimal part - if (i < count && input[i] == '.') + if (index < count && input[index] == '.') { - i++; + index++; double mult = 1.0; - while (i < count && (input[i] >= '0' && input[i] <= '9')) + while (index < count && (input[index] >= '0' && input[index] <= '9')) { - value = value * 10.0 + (input[i++] - '0'); + value = (value * 10.0) + (input[index++] - '0'); mult = mult * 10.0; } + value /= mult; } - if (i < count) + if (index < count) { // Scientific notation - if (input[i] == 'e' || input[i] == 'E') + if (input[index] == 'e' || input[index] == 'E') { - i++; - int exponent = 0; - bool neg = false; - if (i < count && (input[i] == '+' || input[i] == '-')) + index++; + var exponent = 0; + var neg = false; + if (index < count && (input[index] == '+' || input[index] == '-')) { - if (input[i] == '-') + if (input[index] == '-') + { neg = true; - i++; + } + + index++; } // Get the exponent - while (i < count && (input[i] >= '0' && input[i] <= '9')) - exponent = exponent * 10 + (input[i++] - '0'); + while (index < count && (input[index] >= '0' && input[index] <= '9')) + { + exponent = (exponent * 10) + (input[index++] - '0'); + } // Integer exponentation - double mult = 1.0; - double b = 10.0; + var mult = 1.0; + var b = 10.0; while (exponent != 0) { if ((exponent & 0x01) == 0x01) + { mult *= b; + } + b *= b; exponent >>= 1; } + if (neg) + { value /= mult; + } else + { value *= mult; - + } } else { // Spice modifiers - switch (input[i]) + switch (input[index]) { case 't': - case 'T': value *= 1.0e12; i++; break; + case 'T': value *= 1.0e12; index++; break; case 'g': - case 'G': value *= 1.0e9; i++; break; + case 'G': value *= 1.0e9; index++; break; + case 'x': + case 'X': value *= 1.0e6; index++; break; case 'k': - case 'K': value *= 1.0e3; i++; break; + case 'K': value *= 1.0e3; index++; break; case 'u': - case 'U': value /= 1.0e6; i++; break; + case 'U': value /= 1.0e6; index++; break; case 'n': - case 'N': value /= 1.0e9; i++; break; + case 'N': value /= 1.0e9; index++; break; case 'p': - case 'P': value /= 1.0e12; i++; break; + case 'P': value /= 1.0e12; index++; break; case 'f': - case 'F': value /= 1.0e15; i++; break; + case 'F': value /= 1.0e15; index++; break; case 'm': case 'M': - if (i + 2 < count && - (input[i + 1] == 'e' || input[i + 1] == 'E') && - (input[i + 2] == 'g' || input[i + 2] == 'G')) + if (index + 2 < count && + (input[index + 1] == 'e' || input[index + 1] == 'E') && + (input[index + 2] == 'g' || input[index + 2] == 'G')) { value *= 1.0e6; - i += 3; + index += 3; } - else if (i + 2 < count && - (input[i + 1] == 'i' || input[i + 1] == 'I') && - (input[i + 2] == 'l' || input[i + 2] == 'L')) + else if (index + 2 < count && + (input[index + 1] == 'i' || input[index + 1] == 'I') && + (input[index + 2] == 'l' || input[index + 2] == 'L')) { value *= 25.4e-6; - i += 3; + index += 3; } else { value /= 1.0e3; - i++; + index++; } + break; } } // Any trailing letters are ignored - while (i < count && ((input[i] >= 'a' && input[i] <= 'z') || (input[i] >= 'A' && input[i] <= 'Z'))) - i++; + while (index < count && ((input[index] >= 'a' && input[index] <= 'z') || (input[index] >= 'A' && input[index] <= 'Z'))) + { + index++; + } } + return value; } + + /// + /// Operator description + /// + private class Operator + { + /// + /// Initializes a new instance of the class. + /// + /// The operator ID + /// The operator precedence + /// Is the operator left-associative? + public Operator(byte id, byte precedence, bool la) + { + Id = id; + Precedence = precedence; + LeftAssociative = la; + } + + /// + /// Gets operator identifier + /// + public byte Id { get; } + + /// + /// Gets operator precedence + /// + public byte Precedence { get; } + + /// + /// Gets a value indicating whether the operator is left-associative or not + /// + public bool LeftAssociative { get; } + } + + /// + /// Function description + /// + private class FunctionOperator : Operator + { + /// + /// Initializes a new instance of the class. + /// + /// The function + public FunctionOperator(Func, double> func) + : base(IdFunction, byte.MaxValue, false) + { + Function = func ?? throw new ArgumentNullException(nameof(func)); + } + + /// + /// Gets the function evaluation + /// + public Func, double> Function { get; } + } } }