private static void EvaluateSpiceSharpParser(string expression, int count) { Console.WriteLine("---------- Spice# Parser ----------"); var sw = new Stopwatch(); // Setup var parser = new SpiceSharpParser.Parsers.Expression.SpiceExpressionParser(); var ectxt = new SpiceExpressionContext(SpiceExpressionMode.LtSpice); var pctxt = new ExpressionParserContext() { Functions = ectxt.Functions }; var evctxt = new ExpressionEvaluationContext(); // Parse ExpressionParseResult pr = null; for (var i = 0; i < 5; i++) { sw.Restart(); pr = parser.Parse(expression, pctxt); sw.Stop(); Console.WriteLine($"Parsing (run {i + 1}): {sw.ElapsedTicks} ({sw.ElapsedMilliseconds} ms)"); } // Evaluate a first time evctxt.ExpressionContext.Parameters["x"] = new ConstantExpression(1.0); Console.WriteLine($"Evaluation: {pr.Value(evctxt)}"); // Test bulk runs sw.Restart(); for (var i = 0; i < count; i++) { pr.Value(evctxt); } sw.Stop(); Console.WriteLine($"Execution: {sw.ElapsedTicks} ({sw.ElapsedMilliseconds} ms)"); Console.WriteLine(); }
public static Result Roll(string expression) { if (string.IsNullOrWhiteSpace(expression)) { throw new DiceRollException("[ERROR]: The roll expression is empty.", expression, 0); } char[] diceExpression = expression.Trim().ToCharArray(); for (int i = 0; i < diceExpression.Length; ++i) { char c = diceExpression[i]; if (c == 'D') { diceExpression[i] = 'd'; } } int offset = 0; void Panic(string errorMessage) { throw new DiceRollException(errorMessage, expression, offset); } char Peek() { return(diceExpression[offset]); } char Pop() { char result = diceExpression[offset]; ++offset; return(result); } bool EOF() { return(offset >= diceExpression.Length); } void GobbleWhitespace() { while (!EOF() && char.IsWhiteSpace(Peek())) { ++offset; } } bool Expect(char c) { GobbleWhitespace(); if (diceExpression[offset] == c) { ++offset; return(true); } else { return(false); } } int ConsumeNumber() { int result = 0; GobbleWhitespace(); while (!EOF() && char.IsDigit(Peek())) { char c = Pop(); int i = c - '0'; result = result * 10 + i; } return(result); } int ExpectNonZeroNumber() { int result = ConsumeNumber(); if (result <= 0) { Panic("Expected a non-zero number."); } return(result); } // Can be just [number] or [number]?d[number] ExpressionParseResult ConsumeExpression() { var result = new ExpressionParseResult(); GobbleWhitespace(); if (char.IsDigit(Peek())) { int number = ConsumeNumber(); GobbleWhitespace(); if (!EOF() && Peek() == 'd') { ++offset; int number2 = ExpectNonZeroNumber(); result.NumDice = number; result.DiceFaces = number2; } else { result.Constant = number; } } else if (Expect('d')) { int number2 = ExpectNonZeroNumber(); result.NumDice = 1; result.DiceFaces = number2; } else { Panic("Expected dice expression."); } return(result); } var rng = new Random(); var result = new Result(); result.Rolls = new Dictionary <string, List <int> >(); int RollDice(int numFaces) { int result = 1 + rng.Next(numFaces); return(result); } int sign = 1; // The sign of the "connecting operator" // Expressions might start with an '-' or an explicit '+' // that we should allow. if (Peek() == '-') { sign = -1; ++offset; } else if (Peek() == '+') { ++offset; } do { var expressionResult = ConsumeExpression(); if (expressionResult.NumDice > 0) { string dieKey = "d" + expressionResult.DiceFaces; var results = new List <int>(expressionResult.NumDice); for (int i = 0; i < expressionResult.NumDice; ++i) { int roll = RollDice(expressionResult.DiceFaces); results.Add(roll); result.Total += roll * sign; } if (result.Rolls.ContainsKey(dieKey)) { result.Rolls[dieKey].AddRange(results); } else { result.Rolls[dieKey] = results; } } else { result.Modifier += expressionResult.Constant * sign; } GobbleWhitespace(); if (!EOF()) { if (Peek() == '+') { ++offset; sign = 1; } else if (Peek() == '-') { ++offset; sign = -1; } else { // NOTE(istarnion): Panic here if we // don't want to allow chain expressions sign = 1; } } }while (!EOF()); result.Total += result.Modifier; return(result); }