Commit c2123452 authored by Mark Olesen's avatar Mark Olesen
Browse files

ENH: generalize string expression evaluation

- replace stringOps::toScalar with a more generic stringOps::evaluate
  method that handles scalars, vectors etc.

- improve #eval to handle various mathematical operations.
  Previously only handled scalars. Now produce vectors, tensors etc
  for the entries. These tokens are streamed directly into the entry.
parent f84ebb9a
......@@ -58,5 +58,7 @@ eval8a #eval " pi() * 2 * ${{ ${factor$index} + ${factor10} }}";
eval8b #eval " pi() * 2 * $factor * ${{ 100 }}";
eval10a #eval "vector(1,2,3) * 5";
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
......@@ -132,6 +132,9 @@ int main(int argc, char *argv[])
"sqrt(2) = (${{ sqrt(2) }/* Truncated */",
"sqrt(2) = (${{ sqrt(2) * foo() }})/* bad expr */",
"huge = (${{ sqrt(123E+5000) }})/* range error */",
"vector=${{ 5 * vector(1,2,3) }}=",
"empty=${{ }}=",
}
)
{
......
......@@ -134,10 +134,8 @@ $(strings)/lists/hashedWordList.C
$(strings)/parsing/parsing.C
$(strings)/parsing/genericRagelLemonDriver.C
$(strings)/stringOps/stringOps.C
$(strings)/stringOps/stringOpsEvaluate.C
$(strings)/stringOps/stringOpsSort.C
$(strings)/stringOps/toScalar/evalStringToScalarDriver.C
$(strings)/stringOps/toScalar/evalStringToScalarLemonParser.lyy-m4
$(strings)/stringOps/toScalar/evalStringToScalarScanner.cc
expr = expressions
$(expr)/exprEntry/expressionEntry.C
......
......@@ -25,24 +25,21 @@ License
#include "evalEntry.H"
#include "dictionary.H"
#include "OTstream.H"
#include "stringOps.H"
#include "fieldExprDriver.H"
#include "addToMemberFunctionSelectionTable.H"
#undef DetailInfo
#define DetailInfo if (::Foam::infoDetailLevel > 0) InfoErr
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace functionEntries
{
addNamedToMemberFunctionSelectionTable
(
functionEntry,
evalEntry,
execute,
dictionaryIstream,
eval
);
addNamedToMemberFunctionSelectionTable
(
functionEntry,
......@@ -58,7 +55,7 @@ namespace functionEntries
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::scalar Foam::functionEntries::evalEntry::evaluate
Foam::tokenList Foam::functionEntries::evalEntry::evaluate
(
const dictionary& parentDict,
Istream& is
......@@ -80,7 +77,8 @@ Foam::scalar Foam::functionEntries::evalEntry::evaluate
FatalIOErrorInFunction(is)
<< "Bad token - could not get string to evaluate"
<< exit(FatalIOError);
return 0;
return tokenList();
}
if (tok.isString())
......@@ -111,7 +109,9 @@ Foam::scalar Foam::functionEntries::evalEntry::evaluate
stringOps::inplaceExpand(s, parentDict, true, true);
stringOps::inplaceTrim(s);
// A common input error, so catch it now
// An extraneous trailing ';' is a common input error, catch it now.
// May need to relax in the future, trim or something else
if (std::string::npos != s.find(';'))
{
FatalIOErrorInFunction(is)
......@@ -128,56 +128,50 @@ Foam::scalar Foam::functionEntries::evalEntry::evaluate
if (s.empty())
{
InfoErr
<< "Empty #eval (treat as 0) - line "
<< "Empty #eval - line "
<< is.lineNumber() << " in file " << parentDict.name() << nl;
return scalar(0);
return tokenList();
}
return stringOps::toScalar(s);
}
expressions::exprResult result;
{
expressions::fieldExprDriver driver(1);
driver.parse(s);
result = std::move(driver.result());
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
if (!result.hasValue() || !result.size())
{
InfoErr
<< "Failed #eval - line "
<< is.lineNumber() << " in file " << parentDict.name() << nl;
bool Foam::functionEntries::evalEntry::execute
(
const dictionary& parentDict,
primitiveEntry& entry,
Istream& is
)
{
const scalar value = evaluate(parentDict, is);
return tokenList();
}
// The result as terminated token entry
ITstream result
(
"eval",
tokenList({token(value), token(token::END_STATEMENT)})
);
// Could average/reduce to a single value, but probably not needed
//// result.testIfSingleValue(false); // No parallel check
entry.read(parentDict, result);
OTstream toks;
result.writeValue(toks);
return true;
return toks;
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::functionEntries::evalEntry::execute
(
dictionary& parentDict,
const dictionary& parentDict,
primitiveEntry& entry,
Istream& is
)
{
const scalar value = evaluate(parentDict, is);
// The result as terminated token entry
ITstream result
(
"eval",
tokenList({token(value), token(token::END_STATEMENT)})
);
tokenList toks(evaluate(parentDict, is));
parentDict.read(result);
entry.append(std::move(toks), true); // Lazy resizing
return true;
}
......
......@@ -27,7 +27,8 @@ Class
Foam::functionEntries::evalEntry
Description
Uses stringOps::toScalar to evaluate mathematical expressions.
Uses expressions::fieldExprDriver to evaluate mathematical expressions
with scalars, vectors etc.
The input can any form of string or, for convenience,
a '{}' delimited string literal. In all cases, C/C++ comment stripping
......@@ -81,8 +82,8 @@ class evalEntry
public functionEntry
{
//- Evaluate and return a scalar
static scalar evaluate(const dictionary& parentDict, Istream& is);
//- Evaluate and return a token list
static tokenList evaluate(const dictionary& parentDict, Istream& is);
public:
......@@ -94,9 +95,6 @@ public:
primitiveEntry& thisEntry,
Istream& is
);
//- Execute in a sub-dict context
static bool execute(dictionary& parentDict, Istream& is);
};
......
......@@ -26,7 +26,7 @@ License
\*---------------------------------------------------------------------------*/
#include "genericRagelLemonDriver.H"
#include "evalStringToScalarDriver.H"
#include "string.H"
#include "error.H"
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
......
......@@ -570,9 +570,10 @@ static Foam::string recursiveExpand
///Info<< "eval <" << out << ">" << endl;
// Even with allow empty, expressions need content
const scalar sval = stringOps::toScalar(out);
const word val(Foam::name(sval));
// Even with allow empty, expressions may need content
string val(stringOps::evaluate(out));
stringOps::inplaceTrim(val);
return val;
}
......
......@@ -46,10 +46,9 @@ SourceFiles
#include "dictionary.H"
#include "HashTable.H"
#include "stringOpsSort.H"
#include "stringOpsEvaluate.H"
#include "wordRes.H"
#include "evalStringToScalar.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
......@@ -137,7 +136,7 @@ namespace stringOps
// - \c ${VAR}
// - \c ${VAR:-defValue}
// - \c ${VAR:+altValue}
// -# numeric (scalar) evaluation using stringOps::toScalar
// -# mathematical evaluation using stringOps::evaluate
// - \c ${{EXPR}}
// -# current directory
// - leading "./"
......@@ -183,7 +182,7 @@ namespace stringOps
// \param sigil The leading sigil. Can be changed to avoid conflict
// with other string expansions. (default: '$')
//
// \sa Foam::findEtcEntry(), Foam::findEtcEntries(), stringOps::toScalar()
// \sa Foam::findEtcEntry(), Foam::findEtcEntries(), stringOps::evaluate()
//
// \note this function has too many parameters and should generally
// be avoided in user coding.
......
......@@ -25,73 +25,81 @@ License
\*---------------------------------------------------------------------------*/
#include "evalStringToScalar.H"
#include "evalStringToScalarDriver.H"
#include "evalStringToScalarScanner.H"
#include "stringOpsEvaluate.H"
#include "stringOps.H"
#include "StringStream.H"
#include "fieldExprDriver.H"
#include "error.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace parsing
{
int evalStringToScalar::debug
(
::Foam::debug::debugSwitch("stringToScalar", 0)
);
registerDebugSwitchWithName
(
evalStringToScalar,
evalStringToScalar,
"stringToScalar"
);
} // End namespace parsing
} // End namespace Foam
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::parsing::evalStringToScalar::parseDriver::parseDriver()
:
genericRagelLemonDriver(),
value_(0)
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::scalar Foam::parsing::evalStringToScalar::parseDriver::execute
(
const std::string& s,
size_t pos,
size_t len
)
{
// scanner::debug = 1;
scanner().process(s, pos, len, *this);
return value_;
}
// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
Foam::scalar Foam::stringOps::toScalar
Foam::string Foam::stringOps::evaluate
(
const std::string& s,
const std::string& str,
size_t pos,
size_t len
)
{
parsing::evalStringToScalar::parseDriver driver;
driver.execute(s, pos, len);
return driver.value();
/// InfoErr<< "Evaluate " << str.substr(pos, len) << nl;
size_t end = str.length();
if (pos > end)
{
pos = end;
}
else if (len != std::string::npos)
{
len += pos;
if (len < end)
{
end = len;
}
}
// Adjust like inplaceTrim
// Right
while (pos < end && std::isspace(str[end-1]))
{
--end;
}
// Left
while (pos < end && std::isspace(str[pos]))
{
++pos;
}
if ((pos >= end) || std::isspace(str[pos]))
{
return "";
}
len = (end - pos);
/// InfoErr<< "Evaluate " << str.substr(pos, len) << nl;
expressions::exprResult result;
{
expressions::fieldExprDriver driver(1);
driver.parse(str, pos, len);
result = std::move(driver.result());
}
if (!result.hasValue() || !result.size())
{
InfoErr
<< "Failed evaluation: "
<< str.substr(pos, len) << nl;
return "";
}
OStringStream os;
result.writeValue(os);
return os.str();
}
......
......@@ -23,17 +23,17 @@ License
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Namespace
Foam::parsing::evalStringToScalar
InNamespace
Foam::stringOps
Description
Parsing encapsulation for stringOps::toScalar
String expression evaluation.
\*---------------------------------------------------------------------------*/
#ifndef evalStringToScalar_H
#define evalStringToScalar_H
#include "scalar.H"
#ifndef stringOpsEvaluate_H
#define stringOpsEvaluate_H
#include "string.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
......@@ -42,8 +42,7 @@ namespace Foam
{
namespace stringOps
{
//- A simple string to scalar evaluation that handles various basic
//- A simple string evaluation that handles various basic
//- expressions. For trivial input, use readScalar instead (faster).
//
// The evaluation supports the following:
......@@ -52,8 +51,10 @@ namespace stringOps
// - trigonometric: sin, cos, tan, asin, acos, atan, atan2, hypot
// - hyperbolic: sinh, cosh, tanh
// - conversions: degToRad, radToDeg
// - type conversion: bool, mag
// - constants: pi(), true, false
// - misc: floor, ceil, round, rand, rand(seed), bool()
// - limits: neg, pos, neg0, pos0, sign, floor, ceil, round
// - other: rand, rand(seed)
// - logic: ! ? : == != <= => < >
//
// \note
......@@ -63,7 +64,7 @@ namespace stringOps
// \note
// The rand() function returns a uniform scalar on [0-1] interval
// and uses a constant seed.
scalar toScalar
string evaluate
(
const std::string& s,
size_t pos = 0,
......
#!/bin/sh
cd "${0%/*}" || exit # Run from this directory
# Manually create ragel scanner and lemon parser header
prefix=evalStringToScalar
"${WM_PROJECT_DIR:?}/wmake/scripts/makeParser" \
-prefix="$prefix" \
-scanner=Scanner.rl \
-parser=LemonParser.lyy-m4 \
"$@"
#------------------------------------------------------------------------------
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2019 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM 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.
OpenFOAM 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 OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::parsing::evalStringToScalar::parseDriver
Description
Driver for stringOps::toScalar parsing
SourceFiles
evalStringToScalarDriver.C
\*---------------------------------------------------------------------------*/
#ifndef evalStringToScalarDriver_H
#define evalStringToScalarDriver_H
#include "scalar.H"
#include "className.H"
#include "genericRagelLemonDriver.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace parsing
{
namespace evalStringToScalar
{
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
//- The debug flag. 2 = debug parser, 4 = debug scanner
extern int debug;
/*---------------------------------------------------------------------------*\
Class parseDriver Declaration
\*---------------------------------------------------------------------------*/
class parseDriver
:
public genericRagelLemonDriver
{
// Private Data
//- The result
scalar value_;
public:
ClassName("evalStringToScalar::driver");
// Constructors
//- Construct null
parseDriver();
//- Destructor
virtual ~parseDriver() = default;
// Member Functions
//- Perform parsing on (sub) string
// \return evaluated value
scalar execute
(
const std::string& s,
size_t strPos = 0,
size_t strLen = std::string::npos
);
//- Get value
scalar value() const
{
return value_;
}
//- Set value
void setValue(scalar val)
{
value_ = val;
}
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace evalStringToScalar
} // End namespace parsing
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //
#define TOK_QUESTION 1
#define TOK_COLON 2
#define TOK_LOR 3
#define TOK_LAND 4
#define TOK_EQUAL 5
#define TOK_NOT_EQUAL 6
#define TOK_LESS_EQ 7
#define TOK_GREATER_EQ 8
#define TOK_LESS 9
#define TOK_GREATER 10
#define TOK_PLUS 11
#define TOK_MINUS 12
#define TOK_TIMES 13
#define TOK_DIVIDE 14
#define TOK_PERCENT 15