Belle II Software  release-08-01-10
FormulaParser.h
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #pragma once
10 
11 #include <framework/utilities/Utils.h>
12 #include <sstream>
13 #include <variant>
14 #include <stack>
15 #include <string>
16 
17 
18 namespace Belle2 {
28  public:
31  enum class EOperator : unsigned char {
32  c_noop = 0x00,
33  c_roundBracketOpen = 0x01,
34  c_roundBracketClose = 0x02,
35  c_squareBracketOpen = 0x03,
36  c_squareBracketClose = 0x04,
37  c_plus = 0x11,
38  c_minus = 0x12,
39  c_multiply = 0x21,
40  c_divide = 0x22,
41  c_power = 0x31
42  };
43 
45  enum class ENumberStatus {
46  c_Invalid,
47  c_Empty,
48  c_Sign,
49  c_Int,
50  c_Dot,
51  c_LeadingDot,
52  c_Float,
53  c_Exponent,
56  };
57 
59  typedef std::variant<std::string, double> InputToken;
60 
62  static char operatorToChar(EOperator op) noexcept;
63 
65  static double applyOperator(EOperator op, double a, double b);
66 
69  static ENumberStatus checkNumber(ENumberStatus current, char next);
70 
72  FormulaParserBase() = default;
73 
75  virtual ~FormulaParserBase() = default;
76  protected:
78  static void assertOperatorUsable(size_t stacksize);
80  virtual void executeOperator(EOperator op) = 0;
82  virtual void addVariable(const InputToken& token) = 0;
84  void processString(const std::string& formula);
87  [[noreturn]] void raiseError(const std::runtime_error& e);
88  private:
92  void addOperator(EOperator op);
95  void flushCurrentVariable();
97  void flushPendingOperators();
99  EOperator checkForOperator(char next);
103  std::istringstream m_buffer;
109  std::stack<EOperator> m_operatorStack;
110  };
111 
112 
134  template<class VariableConstructor> class FormulaParser: public FormulaParserBase {
135  public:
137  typedef typename VariableConstructor::type VariableType;
139  typedef std::variant<VariableType, double> OutputToken;
140 
144  VariableType parse(const std::string& formula)
145  {
146  try {
147  // clear stacks
148  std::stack<OutputToken>().swap(m_outputStack);
149  // process
150  processString(formula);
151  // sanity checks
152  if (m_outputStack.empty())
153  throw std::runtime_error("empty string");
154  if (m_outputStack.size() != 1)
155  throw std::runtime_error("could not parse, stack size not 1, probably a bug, please report");
156  // so return the object
157  return std::visit(Utils::VisitOverload{
158  [](const VariableType & v) -> VariableType {return v;},
159  [](double d) -> VariableType { return VariableConstructor()(d); }
160  }, m_outputStack.top());
161  } catch (std::runtime_error& e) {
162  // add context to error
163  raiseError(e);
164  }
165  }
166  protected:
168  void executeOperator(EOperator op) override
169  {
171  // so far all a binary operators
172  // cppcheck-suppress unreadVariable; used in lambda below
173  OutputToken op2 = m_outputStack.top(); m_outputStack.pop();
174  // cppcheck-suppress unreadVariable; used in lambda below
175  OutputToken op1 = m_outputStack.top(); m_outputStack.pop();
176  // and apply ...
177  m_outputStack.push(std::visit(Utils::VisitOverload {
178  // eagerly apply operations if both operands are numbers
179  [op](double a, double b) -> OutputToken { return OutputToken(applyOperator(op, a, b)); },
180  // otherwise defer to variable constructor
181  [op](auto a, auto b) -> OutputToken { return VariableConstructor()(op, a, b); },
182  }, op1, op2));
183  }
184 
186  void addVariable(const InputToken& var) override
187  {
188  m_outputStack.push(std::visit(Utils::VisitOverload {
189  // if the variable is a string its an identifier and we have to construct a variable from it
190  [](const std::string & s) -> OutputToken { return VariableConstructor()(s); },
191  // otherwise keep as is
192  [](auto s) -> OutputToken { return s; }
193  }, var));
194  }
195 
197  std::stack<OutputToken> m_outputStack;
198  };
199 
205  typedef std::string type;
208 
210  type operator()(const std::string& name) { return "'" + name + "'"; }
212  type operator()(double value) { return std::to_string(value); }
214  type operator()(Op op, const type& a, const type& b)
215  {
216  return "(" + a + FormulaParserBase::operatorToChar(op) + b + ")";
217  }
219  type operator()(Op op, double a, const type& b) { return (*this)(op, std::to_string(a), b); }
221  type operator()(Op op, const type& a, double b) { return (*this)(op, a, std::to_string(b)); }
222  };
223 
225 }
Base class with the non-templated part of the formula parser.
Definition: FormulaParser.h:27
FormulaParserBase()=default
Default constructor.
std::string m_currentVariableName
collect characters into a variable name
virtual void executeOperator(EOperator op)=0
Execute an operator on the current state.
virtual void addVariable(const InputToken &token)=0
Add a variable token to the current state.
std::stack< EOperator > m_operatorStack
Stack of operators for the Shunting-yard algorithm.
EOperator
List of known operators.
Definition: FormulaParser.h:31
@ c_roundBracketClose
Close round bracket.
@ c_roundBracketOpen
Open round bracket.
@ c_squareBracketOpen
Open square bracket.
@ c_squareBracketClose
Close square bracket.
std::variant< std::string, double > InputToken
Input token type: an input tokein is either a string or a float variable.
Definition: FormulaParser.h:59
std::istringstream m_buffer
Buffer for the formula.
ENumberStatus
States of a string literal when checking if it is a valid number.
Definition: FormulaParser.h:45
@ c_LeadingDot
leading dot without preceding digits
@ c_Float
[leading sign] + [digits] + dot + digits
@ c_Scientific
exponent followed by sign and digits
@ c_Dot
[leading sign] + digits + dot
@ c_ExponentSign
exponent followed by plus or minus
ENumberStatus m_currentVariableNameNumberStatus
State of the current variable name being a valid float literal.
virtual ~FormulaParserBase()=default
virtual, but empty destructor
bool m_lastTokenWasOperator
Bool to check whether there were consecutive operators or variables.
FormulaParser to parse a text formula like "a + b * c ^ d" where the separate parts can be either var...
std::variant< VariableType, double > OutputToken
typedef for output tokens on the stack: either a variable or a double
void addVariable(const InputToken &var) override
Add the variable to the output token stack, create it from a string or keep it as it is.
VariableType parse(const std::string &formula)
Parse the formula and return a varible object of the correct type.
std::stack< OutputToken > m_outputStack
Stack of output tokens in the reversh polish notation.
void executeOperator(EOperator op) override
Execute the given operator by taking the operands from the stack and applying the operator to them.
VariableConstructor::type VariableType
Type of the return variable object.
EOperator checkForOperator(char next)
Check if the next character is a operator.
void flushPendingOperators()
Flush all pending operators at the end of processing.
void addOperator(EOperator op)
Add an operator to the internal state, convert them to reverse polish notation using the shunting yar...
void processString(const std::string &formula)
Process the given formula and store the final state.
static double applyOperator(EOperator op, double a, double b)
Apply operator on two values.
static void assertOperatorUsable(size_t stacksize)
Make sure we have enough operands to use an operator.
void raiseError(const std::runtime_error &e)
Format the given runtime_error with context information and rethrow a new one.
void flushCurrentVariable()
Flush the currently parsed variable name and add it to the state either as variable or number.
static char operatorToChar(EOperator op) noexcept
Convert operator code to character.
static ENumberStatus checkNumber(ENumberStatus current, char next)
Check if a string literal with a given number status continues to be a valid number if next is append...
Abstract base class for different kinds of events.
Example struct to be used with the FormulaParser to create a string representation of the formula,...
type operator()(Op op, const type &a, double b)
Apply operator to output type and double.
FormulaParserBase::EOperator Op
Shorthand for the operator enum.
std::string type
Create strings as output of FormulaParser.
type operator()(Op op, const type &a, const type &b)
Apply operator to two output types: just add brackets and operator.
type operator()(const std::string &name)
Create output type from a string identifier: let's quote it.
type operator()(Op op, double a, const type &b)
Apply operator to double and output type.
type operator()(double value)
Create output type from a double: just convert to string.
Helper struct for the C++17 std::visit overload pattern to allow simple use of variants.
Definition: Utils.h:26