Numeric expression evaluator
This commit is contained in:
parent
579d3f478d
commit
b5fc6e45cf
|
@ -348,6 +348,8 @@ set( COMMON_SRCS
|
|||
geometry/shape_collisions.cpp
|
||||
geometry/shape_file_io.cpp
|
||||
geometry/convex_hull.cpp
|
||||
|
||||
libeval/numeric_evaluator.cpp
|
||||
)
|
||||
add_library( common STATIC ${COMMON_SRCS} )
|
||||
add_dependencies( common lib-dependencies )
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include <class_title_block.h>
|
||||
#include <common.h>
|
||||
#include <base_units.h>
|
||||
#include "libeval/numeric_evaluator.h"
|
||||
|
||||
|
||||
#if defined( PCBNEW ) || defined( CVPCB ) || defined( EESCHEMA ) || defined( GERBVIEW ) || defined( PL_EDITOR )
|
||||
|
@ -383,8 +384,12 @@ int ValueFromString( const wxString& aTextValue )
|
|||
|
||||
int ValueFromTextCtrl( const wxTextCtrl& aTextCtr )
|
||||
{
|
||||
int value;
|
||||
int value;
|
||||
wxString msg = aTextCtr.GetValue();
|
||||
NumericEvaluator eval;
|
||||
|
||||
if( eval.process( msg.mb_str() ) )
|
||||
msg = wxString::FromUTF8( eval.result() );
|
||||
|
||||
value = ValueFromString( g_UserUnit, msg );
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
This file is part of libeval, a simple math expression evaluator
|
||||
|
||||
Copyright (C) 2017 Michael Geselbracht, mgeselbracht3@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
%token_type { numEval::TokenType }
|
||||
%extra_argument { NumericEvaluator* pEval }
|
||||
|
||||
%nonassoc VAR ASSIGN UNIT SEMCOL.
|
||||
%left PLUS MINUS.
|
||||
%left DIVIDE MULT.
|
||||
|
||||
%include {
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <libeval/numeric_evaluator.h>
|
||||
}
|
||||
|
||||
%syntax_error {
|
||||
pEval->parseError("Syntax error");
|
||||
}
|
||||
|
||||
%parse_accept {
|
||||
pEval->parseOk();
|
||||
}
|
||||
|
||||
main ::= in.
|
||||
|
||||
/* Allow multiple statements in input string: x=1; y=2 */
|
||||
in ::= stmt.
|
||||
in ::= in stmt.
|
||||
|
||||
/* A statement can be empty, an expr or an expr followed by ';' */
|
||||
stmt ::= ENDS.
|
||||
stmt ::= expr(A) ENDS. { pEval->parseSetResult(A.valid ? A.dValue : NAN); }
|
||||
stmt ::= expr SEMCOL. { pEval->parseSetResult(NAN); }
|
||||
|
||||
expr(A) ::= VALUE(B). { A.dValue = B.dValue; A.valid=true; }
|
||||
expr(A) ::= VALUE(B) UNIT(C). { A.dValue = B.dValue * C.dValue; A.valid=true; }
|
||||
expr(A) ::= MINUS expr(B). { A.dValue = -B.dValue; A.valid=B.valid; }
|
||||
expr(A) ::= VAR(B). { A.dValue = pEval->getVar(B.text); A.valid=true; }
|
||||
expr(A) ::= VAR(B) ASSIGN expr(C). { pEval->setVar(B.text, C.dValue); A.dValue = C.dValue; A.valid=false; }
|
||||
expr(A) ::= expr(B) PLUS expr(C). { A.dValue = B.dValue + C.dValue; A.valid=C.valid; }
|
||||
expr(A) ::= expr(B) MINUS expr(C). { A.dValue = B.dValue - C.dValue; A.valid=C.valid; }
|
||||
expr(A) ::= expr(B) MULT expr(C). { A.dValue = B.dValue * C.dValue; A.valid=C.valid; }
|
||||
expr(A) ::= expr(B) DIVIDE expr(C). {
|
||||
if (C.dValue != 0.0) {
|
||||
A.dValue = B.dValue / C.dValue;
|
||||
}
|
||||
else pEval->parseError("Div by zero");
|
||||
A.valid=C.valid;
|
||||
}
|
||||
expr(A) ::= PARENL expr(B) PARENR. { A.dValue = B.dValue; A.valid=B.valid; }
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
This file is part of libeval, a simple math expression evaluator
|
||||
|
||||
Copyright (C) 2017 Michael Geselbracht, mgeselbracht3@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "numeric_evaluator.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
NumericEvaluator eval;
|
||||
|
||||
eval.process("2.54mm+50mil");
|
||||
if (eval.isValid()) printf("%s\n", eval.result());
|
||||
|
||||
eval.process("x=1; y=5;");
|
||||
if (eval.isValid()) printf("%s\n", eval.result());
|
||||
eval.process("x+y");
|
||||
if (eval.isValid()) printf("%s\n", eval.result());
|
||||
|
||||
eval.setVar("posx", -3.14152);
|
||||
bool retval = eval.process("posx");
|
||||
assert(retval == eval.isValid());
|
||||
if (eval.isValid()) printf("%s\n", eval.result());
|
||||
|
||||
eval.process("x=1; y=2");
|
||||
eval.setVar("z", 3);
|
||||
eval.process("x+y+z");
|
||||
printf("x+y+z=%s\n", eval.result());
|
||||
|
||||
eval.process("1\"");
|
||||
printf("1\" = %s\n", eval.result());
|
||||
eval.process("12.7 - 0.1\" - 50mil");
|
||||
printf("12.7 - 0.1\" - 50mil = %s\n", eval.result());
|
||||
}
|
||||
|
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
This file is part of libeval, a simple math expression evaluator
|
||||
|
||||
Copyright (C) 2017 Michael Geselbracht, mgeselbracht3@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define TESTMODE 0
|
||||
|
||||
#include <libeval/numeric_evaluator.h>
|
||||
|
||||
#if !TESTMODE
|
||||
#include <common.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
/* The (generated) lemon parser is written in C.
|
||||
* In order to keep its symbol from the global namespace include the parser code with
|
||||
* a C++ namespace.
|
||||
*/
|
||||
namespace numEval
|
||||
{
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
#pragma GCC diagnostic ignored "-Wsign-compare"
|
||||
#endif
|
||||
|
||||
#include "grammar.c"
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
} /* namespace numEval */
|
||||
|
||||
NumericEvaluator :: NumericEvaluator() : pClParser(0)
|
||||
{
|
||||
cClDecSep = '.';
|
||||
|
||||
bClTextInputStorage = true;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
NumericEvaluator :: ~NumericEvaluator()
|
||||
{
|
||||
numEval::ParseFree(pClParser, free);
|
||||
|
||||
// Allow explicit call to destructor
|
||||
pClParser = nullptr;
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
void
|
||||
NumericEvaluator :: init()
|
||||
{
|
||||
if (pClParser == nullptr)
|
||||
pClParser = numEval::ParseAlloc(malloc);
|
||||
|
||||
//numEval::ParseTrace(stdout, "lib");
|
||||
|
||||
#if TESTMODE
|
||||
eClUnitDefault = Unit::Metric;
|
||||
#else
|
||||
switch (g_UserUnit)
|
||||
{
|
||||
case INCHES : eClUnitDefault = Unit::Inch; break;
|
||||
case MILLIMETRES : eClUnitDefault = Unit::Metric; break;
|
||||
default: eClUnitDefault = Unit::Metric; break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
NumericEvaluator :: clear()
|
||||
{
|
||||
free(clToken.token);
|
||||
clToken.token = nullptr;
|
||||
clToken.input = nullptr;
|
||||
bClError = true;
|
||||
}
|
||||
|
||||
void
|
||||
NumericEvaluator :: parse(int token, numEval::TokenType value)
|
||||
{
|
||||
numEval::Parse(pClParser, token, value, this);
|
||||
}
|
||||
|
||||
void
|
||||
NumericEvaluator :: parseError(const char* s)
|
||||
{
|
||||
bClError = true;
|
||||
}
|
||||
|
||||
void
|
||||
NumericEvaluator :: parseOk()
|
||||
{
|
||||
bClError = false;
|
||||
bClParseFinished = true;
|
||||
}
|
||||
|
||||
void
|
||||
NumericEvaluator :: parseSetResult(double val)
|
||||
{
|
||||
snprintf(clToken.token, clToken.OutLen, "%g", val);
|
||||
}
|
||||
|
||||
const char*
|
||||
NumericEvaluator :: textInput(const void* pObj) const
|
||||
{
|
||||
auto it = clObjMap.find(pObj);
|
||||
if (it != clObjMap.end()) return it->second.c_str();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
NumericEvaluator :: process(const char* s)
|
||||
{
|
||||
/* Process new string.
|
||||
* Feed parser token after token until end of input.
|
||||
*/
|
||||
|
||||
newString(s);
|
||||
|
||||
if (pClParser == nullptr) init();
|
||||
bClParseFinished = false;
|
||||
|
||||
Token tok;
|
||||
numEval::TokenType parseTok;
|
||||
do {
|
||||
tok = getToken();
|
||||
parse(tok.token, tok.value);
|
||||
if (bClParseFinished || tok.token == ENDS) {
|
||||
numEval::Parse(pClParser, 0, parseTok, this);
|
||||
break;
|
||||
}
|
||||
//usleep(200000);
|
||||
} while (tok.token);
|
||||
|
||||
return !bClError;
|
||||
}
|
||||
|
||||
bool
|
||||
NumericEvaluator :: process(const char* s, const void* pObj)
|
||||
{
|
||||
if (bClTextInputStorage) // Store input string for (text entry) pObj.
|
||||
clObjMap[pObj] = s;
|
||||
return process(s);
|
||||
}
|
||||
|
||||
void
|
||||
NumericEvaluator :: newString(const char* s)
|
||||
{
|
||||
clear();
|
||||
auto len = strlen(s);
|
||||
clToken.token = reinterpret_cast<decltype(clToken.token)>(malloc(TokenStat::OutLen+1));
|
||||
clToken.inputLen = len;
|
||||
clToken.pos = 0;
|
||||
clToken.input = s;
|
||||
bClParseFinished = false;
|
||||
}
|
||||
|
||||
NumericEvaluator::Token
|
||||
NumericEvaluator :: getToken()
|
||||
{
|
||||
Token retval;
|
||||
size_t idx;
|
||||
|
||||
retval.token = ENDS;
|
||||
retval.value.dValue = 0;
|
||||
|
||||
if (clToken.token == nullptr) return retval;
|
||||
if (clToken.input == nullptr) return retval;
|
||||
if (clToken.pos >= clToken.inputLen) return retval;
|
||||
|
||||
// Lambda: get value as string, store into clToken.token and update current index.
|
||||
auto extractNumber = [&idx, this]() {
|
||||
short sepCount = 0;
|
||||
idx = 0;
|
||||
auto ch = clToken.input[clToken.pos];
|
||||
do {
|
||||
if (ch == cClDecSep && sepCount) break;
|
||||
clToken.token[idx++] = ch;
|
||||
if (ch == cClDecSep) sepCount++;
|
||||
ch = clToken.input[++clToken.pos];
|
||||
} while (isdigit(ch) || ch == cClDecSep);
|
||||
clToken.token[idx] = 0;
|
||||
};
|
||||
|
||||
// Lamda: Get unit for current token. Returns Unit::Invalid if token is not a unit.
|
||||
auto checkUnit = [this]() -> Unit {
|
||||
// '"' or "mm" or "mil"
|
||||
char ch = clToken.input[clToken.pos];
|
||||
if (ch == '"' || ch == 'm') {
|
||||
Unit convertFrom = Unit::Invalid;
|
||||
if (ch == '"') {
|
||||
convertFrom = Unit::Inch;
|
||||
clToken.pos++;
|
||||
}
|
||||
else {
|
||||
// Do not use strcasecmp() as it is not available on all platforms
|
||||
const char* cptr = &clToken.input[clToken.pos];
|
||||
const auto sizeLeft = clToken.inputLen - clToken.pos;
|
||||
if (sizeLeft >= 2) {
|
||||
if (tolower(cptr[1]) == 'm' && !isalnum(cptr[2])) {
|
||||
convertFrom = Unit::Metric;
|
||||
clToken.pos += 2;
|
||||
}
|
||||
else if (sizeLeft >= 3) {
|
||||
if (tolower(cptr[1]) == 'i' && tolower(cptr[2]) == 'l' && !isalnum(cptr[3])) {
|
||||
convertFrom = Unit::Mil;
|
||||
clToken.pos += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return convertFrom;
|
||||
}
|
||||
return Unit::Invalid;
|
||||
};
|
||||
|
||||
// Start processing of first/next token: Remove whitespace
|
||||
char ch;
|
||||
for (;;) {
|
||||
ch = clToken.input[clToken.pos];
|
||||
if (ch == ' ') {
|
||||
clToken.pos++;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
Unit convertFrom;
|
||||
|
||||
if (ch == 0) {
|
||||
/* End of input */
|
||||
}
|
||||
else if (isdigit(ch) || ch == cClDecSep) { // VALUE
|
||||
extractNumber();
|
||||
retval.token = VALUE;
|
||||
retval.value.dValue = atof(clToken.token);
|
||||
}
|
||||
else if ((convertFrom = checkUnit()) != Unit::Invalid) { // UNIT
|
||||
/* Units are appended to a VALUE.
|
||||
* Determine factor to default unit if unit for value is given.
|
||||
* Example: Default is mm, unit is inch: factor is 25.4
|
||||
* The factor is assigned to the terminal UNIT. The actual
|
||||
* conversion is done within a parser action.
|
||||
*/
|
||||
retval.token = UNIT;
|
||||
if (eClUnitDefault == Unit::Metric)
|
||||
{
|
||||
switch (convertFrom)
|
||||
{
|
||||
case Unit::Inch : retval.value.dValue = 25.4; break;
|
||||
case Unit::Mil : retval.value.dValue = 25.4/1000.0; break;
|
||||
case Unit::Metric : retval.value.dValue = 1.0; break;
|
||||
case Unit::Invalid : break;
|
||||
}
|
||||
}
|
||||
else if (eClUnitDefault == Unit::Inch)
|
||||
{
|
||||
switch (convertFrom)
|
||||
{
|
||||
case Unit::Inch : retval.value.dValue = 1.0; break;
|
||||
case Unit::Mil : retval.value.dValue = 1.0/1000.0; break;
|
||||
case Unit::Metric : retval.value.dValue = 1.0/25.4; break;
|
||||
case Unit::Invalid : break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isalpha(ch)) { // VAR
|
||||
const char* cptr = &clToken.input[clToken.pos];
|
||||
cptr++;
|
||||
while (isalnum(*cptr)) cptr++;
|
||||
retval.token = VAR;
|
||||
size_t bytesToCopy = cptr - &clToken.input[clToken.pos];
|
||||
if (bytesToCopy >= sizeof(retval.value.text)) bytesToCopy = sizeof(retval.value.text)-1;
|
||||
strncpy(retval.value.text, &clToken.input[clToken.pos], bytesToCopy);
|
||||
retval.value.text[bytesToCopy] = 0;
|
||||
clToken.pos += cptr - &clToken.input[clToken.pos];
|
||||
}
|
||||
else { // Single char tokens
|
||||
switch (ch) {
|
||||
case '+' : retval.token = PLUS; break;
|
||||
case '-' : retval.token = MINUS; break;
|
||||
case '*' : retval.token = MULT; break;
|
||||
case '/' : retval.token = DIVIDE; break;
|
||||
case '(' : retval.token = PARENL; break;
|
||||
case ')' : retval.token = PARENR; break;
|
||||
case '=' : retval.token = ASSIGN; break;
|
||||
case ';' : retval.token = SEMCOL; break;
|
||||
}
|
||||
clToken.pos++;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void
|
||||
NumericEvaluator :: setVar(const std::string& s, double value)
|
||||
{
|
||||
clVarMap[s] = value;
|
||||
}
|
||||
|
||||
double
|
||||
NumericEvaluator :: getVar(const std::string& s)
|
||||
{
|
||||
auto result = clVarMap.find(s);
|
||||
if (result != clVarMap.end()) return result->second;
|
||||
return 0.0;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#define VAR 1
|
||||
#define ASSIGN 2
|
||||
#define UNIT 3
|
||||
#define SEMCOL 4
|
||||
#define PLUS 5
|
||||
#define MINUS 6
|
||||
#define DIVIDE 7
|
||||
#define MULT 8
|
||||
#define ENDS 9
|
||||
#define VALUE 10
|
||||
#define PARENL 11
|
||||
#define PARENR 12
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
This file is part of libeval, a simple math expression evaluator
|
||||
|
||||
Copyright (C) 2017 Michael Geselbracht, mgeselbracht3@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
An evaluator object is used to replace an input string that represents
|
||||
a mathematical expression by its result.
|
||||
|
||||
Example: Consider the input "3+4". The result of this expression is "7".
|
||||
The NumericEvaluator can be used like this:
|
||||
|
||||
NumericEvaluator eval;
|
||||
eval.process("3+4");
|
||||
printf("3+4", eval.result());
|
||||
|
||||
The same example with error checking. Please note that even a valid input string may result
|
||||
in an empty output string or "NaN".
|
||||
|
||||
NumericEvaluator eval;
|
||||
bool ret = eval.process("3+4");
|
||||
assert(ret == eval.isValid()); // isValid() reflects return value of process().
|
||||
if (eval.isValid()) printf("3+4=%s\n", eval.result());
|
||||
|
||||
Using variables
|
||||
Expressions can refer to variables if they were defined by previous expressions.
|
||||
A variable can be defined by an expression or by the setVar() method.
|
||||
Expressions that define/set variables do not have a result.
|
||||
|
||||
eval.process("x=1; y=2"); // Result is NaN
|
||||
eval.setVar("z", 3);
|
||||
eval.process("x+y+z");
|
||||
printf("x+y+z=%s\n", eval.result());
|
||||
|
||||
Input string storage
|
||||
An evaluator object can store and retrieve the original input string using a pointer
|
||||
as key. This can be used to restore the input string of a text entry field.
|
||||
|
||||
eval.process("25.4-0.7", &eval);
|
||||
printf("%s = %s\n", eval.textInput(&eval), eval.result());
|
||||
|
||||
Unit conversion
|
||||
The evaluator uses a default unit and constants can be specified with a unit.
|
||||
As long as no units are used the default unit is not relevant. The default
|
||||
unit is taken from the global (Kicad) variable g_UserUnit.
|
||||
Supported units are millimeters (mm), Mil (mil) and inch (")
|
||||
|
||||
eval.process("1\"");
|
||||
printf("1\" = %s\n", eval.result());
|
||||
eval.process("12.7 - 0.1\" - 50mil");
|
||||
printf("12.7 - 0.1\" - 50mil = %s\n", eval.result());
|
||||
*/
|
||||
|
||||
#ifndef NUMERIC_EVALUATOR_H_
|
||||
#define NUMERIC_EVALUATOR_H_
|
||||
|
||||
#include "grammar.h"
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
// This namespace is used for the lemon parser
|
||||
namespace numEval
|
||||
{
|
||||
|
||||
struct TokenType
|
||||
{
|
||||
union {
|
||||
double dValue;
|
||||
int iValue;
|
||||
};
|
||||
bool valid;
|
||||
char text[32];
|
||||
};
|
||||
|
||||
} // namespace numEval
|
||||
|
||||
class NumericEvaluator {
|
||||
enum class Unit { Invalid, Metric, Inch, Mil };
|
||||
|
||||
public:
|
||||
NumericEvaluator();
|
||||
~NumericEvaluator();
|
||||
|
||||
/* Initialization and destruction. init() is invoked be the constructor and should not be needed
|
||||
* by the user.
|
||||
* clear() should be invoked by the user if a new input string is to be processed. It will reset
|
||||
* the parser. User defined variables are retained.
|
||||
*/
|
||||
void init();
|
||||
void clear();
|
||||
|
||||
/* Set the decimal separator for the input string. Defaults to '.' */
|
||||
void setDecimalSeparator(char sep);
|
||||
|
||||
/* Enable or disable support for input string storage.
|
||||
* If enabled the input string is saved if process(const char*, const void*) is used.
|
||||
*/
|
||||
void enableTextInputStorage(bool w) { bClTextInputStorage = w; }
|
||||
|
||||
/* Used by the lemon parser */
|
||||
void parse(int token, numEval::TokenType value);
|
||||
void parseError(const char* s);
|
||||
void parseOk();
|
||||
void parseSetResult(double);
|
||||
|
||||
/* Check if previous invokation of process() was successful */
|
||||
inline bool isValid() const { return !bClError; }
|
||||
|
||||
/* Result of string processing. Undefined if !isValid() */
|
||||
inline const char* result() const { return clToken.token; }
|
||||
|
||||
/* Evaluate input string.
|
||||
* Result can be retrieved by result().
|
||||
* Returns true if input string could be evaluated, otherwise false.
|
||||
*/
|
||||
bool process(const char* s);
|
||||
|
||||
/* Like process(const char*) but also stores input string in a std:map with key pObj. */
|
||||
bool process(const char* s, const void* pObj);
|
||||
|
||||
/* Retrieve old input string with key pObj. */
|
||||
const char* textInput(const void* pObj) const;
|
||||
|
||||
/* Add/set variable with value */
|
||||
void setVar(const std::string&, double value);
|
||||
|
||||
/* Get value of variable. Returns 0.0 if not defined. */
|
||||
double getVar(const std::string&);
|
||||
|
||||
/* Remove single variable */
|
||||
void removeVar(const std::string& s) { clVarMap.erase(s); }
|
||||
|
||||
/* Remove all variables */
|
||||
void clearVar() { clVarMap.clear(); }
|
||||
|
||||
protected:
|
||||
/* Token type used by the tokenizer */
|
||||
struct Token {
|
||||
int token;
|
||||
numEval::TokenType value;
|
||||
};
|
||||
|
||||
/* Begin processing of a new input string */
|
||||
void newString(const char* s);
|
||||
|
||||
/* Tokenizer: Next token/value taken from input string. */
|
||||
Token getToken();
|
||||
|
||||
private:
|
||||
void* pClParser; // the current lemon parser state machine
|
||||
|
||||
/* Token state for input string. */
|
||||
struct TokenStat {
|
||||
enum { OutLen=32 };
|
||||
TokenStat() : input(0), token(0), inputLen(0) { /* empty */ }
|
||||
const char* input; // current input string ("var=4")
|
||||
char* token; // output token ("var", type:VAR; "4", type:VALUE)
|
||||
size_t inputLen; // strlen(input)
|
||||
size_t pos; // current index
|
||||
} clToken;
|
||||
|
||||
char cClDecSep; // decimal separator ('.')
|
||||
|
||||
/* Parse progress. Set by parser actions. */
|
||||
bool bClError;
|
||||
bool bClParseFinished;
|
||||
|
||||
bool bClTextInputStorage; // Enable input string storage used by process(const char*, const void*)
|
||||
|
||||
Unit eClUnitDefault; // Default unit for values
|
||||
|
||||
std::map<const void*, std::string> clObjMap; // Map pointer to text entry -> (original) input string
|
||||
std::map<std::string, double> clVarMap;
|
||||
};
|
||||
|
||||
|
||||
#endif /* NUMERIC_EVALUATOR_H_ */
|
Loading…
Reference in New Issue