Belle II Software development
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
18namespace 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,
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,
52 c_Float,
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);
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