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.
This commit is contained in:
Mark Olesen
2019-12-09 14:10:41 +01:00
parent f84ebb9ad3
commit c2123452b1
19 changed files with 141 additions and 3409 deletions

View File

@ -58,5 +58,7 @@ eval8a #eval " pi() * 2 * ${{ ${factor$index} + ${factor10} }}";
eval8b #eval " pi() * 2 * $factor * ${{ 100 }}";
eval10a #eval "vector(1,2,3) * 5";
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

View File

@ -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=${{ }}=",
}
)
{

View File

@ -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

View File

@ -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,13 +128,35 @@ 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());
}
if (!result.hasValue() || !result.size())
{
InfoErr
<< "Failed #eval - line "
<< is.lineNumber() << " in file " << parentDict.name() << nl;
return tokenList();
}
// Could average/reduce to a single value, but probably not needed
//// result.testIfSingleValue(false); // No parallel check
OTstream toks;
result.writeValue(toks);
return toks;
}
@ -147,37 +169,9 @@ bool Foam::functionEntries::evalEntry::execute
Istream& is
)
{
const scalar value = evaluate(parentDict, is);
tokenList toks(evaluate(parentDict, is));
// The result as terminated token entry
ITstream result
(
"eval",
tokenList({token(value), token(token::END_STATEMENT)})
);
entry.read(parentDict, result);
return true;
}
bool Foam::functionEntries::evalEntry::execute
(
dictionary& parentDict,
Istream& is
)
{
const scalar value = evaluate(parentDict, is);
// The result as terminated token entry
ITstream result
(
"eval",
tokenList({token(value), token(token::END_STATEMENT)})
);
parentDict.read(result);
entry.append(std::move(toks), true); // Lazy resizing
return true;
}

View File

@ -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);
};

View File

@ -26,7 +26,7 @@ License
\*---------------------------------------------------------------------------*/
#include "genericRagelLemonDriver.H"
#include "evalStringToScalarDriver.H"
#include "string.H"
#include "error.H"
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //

View File

@ -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;
}

View File

@ -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.

View File

@ -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;
/// InfoErr<< "Evaluate " << str.substr(pos, len) << nl;
driver.execute(s, pos, len);
size_t end = str.length();
if (pos > end)
{
pos = end;
}
else if (len != std::string::npos)
{
len += pos;
return driver.value();
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();
}

View File

@ -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,

View File

@ -1,13 +0,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 \
"$@"
#------------------------------------------------------------------------------

View File

@ -1,122 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / 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
// ************************************************************************* //

View File

@ -1,53 +0,0 @@
#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
#define TOK_NEGATE 16
#define TOK_NOT 17
#define TOK_NUMBER 18
#define TOK_LPAREN 19
#define TOK_RPAREN 20
#define TOK_MAG 21
#define TOK_PI 22
#define TOK_DEG_TO_RAD 23
#define TOK_RAD_TO_DEG 24
#define TOK_EXP 25
#define TOK_LOG 26
#define TOK_LOG10 27
#define TOK_SQR 28
#define TOK_SQRT 29
#define TOK_CBRT 30
#define TOK_SIN 31
#define TOK_COS 32
#define TOK_TAN 33
#define TOK_ASIN 34
#define TOK_ACOS 35
#define TOK_ATAN 36
#define TOK_SINH 37
#define TOK_COSH 38
#define TOK_TANH 39
#define TOK_MAGSQR 40
#define TOK_FLOOR 41
#define TOK_CEIL 42
#define TOK_ROUND 43
#define TOK_POW 44
#define TOK_COMMA 45
#define TOK_ATAN2 46
#define TOK_HYPOT 47
#define TOK_MIN 48
#define TOK_MAX 49
#define TOK_RAND 50
#define TOK_BOOL_FALSE 51
#define TOK_BOOL_TRUE 52
#define TOK_BOOL 53

View File

@ -1,399 +0,0 @@
%include
{
/*--------------------------------*- C++ -*----------------------------------*\
========= |
\\ / 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/>.
Description
Lemon grammar of a simple string to scalar evaluation.
The generated parser is localized in an anonymous namespace.
Interface code wrapping is near the bottom of the file.
\*---------------------------------------------------------------------------*/
}
/*---------------------------------------------------------------------------*\
dnl Begin m4 definitions
dnl
undefine(`substr')dnl> Avoid collision with C++ string substr() method
dnl
dnl
dnl ---------------------------------------------------------------------------
dnl rule_binary_op(out, in1, in2, tok, op)
dnl Production rule for binary field operations
dnl
dnl Example:
dnl rule_binary_op(sfield, sfield, sfield, PLUS, +)
dnl
dnl sfield (lhs) ::= sfield (a) PLUS sfield (b) .
dnl {
dnl lhs = (a) + (b);
dnl }
dnl
dnl ---------------------------------------------------------------------------
define(`rule_binary_op',
`$1 (lhs) ::= $2 (a) $4 $3 (b) .
{
lhs = (a) $5 (b);
}'
)dnl>
dnl
dnl ---------------------------------------------------------------------------
dnl rule_binary_logical_op(type, valType, compare, tok, op)
dnl Production rule for binary logical field operations
dnl Operates on identical field types, producing an lfield.
dnl
dnl Example:
dnl rule_binary_logical_op(vfield, Foam::vector, greaterOp, GREATER, >)
dnl
dnl lfield (lhs) ::= vfield (a) GREATER vfield (b) .
dnl {
dnl lhs = Foam::greaterOp<Foam::vector>()(a, b);
dnl }
dnl ---------------------------------------------------------------------------
define(`rule_binary_logical_op',
`lfield (lhs) ::= $1 (a) $4 $1 (b) .
{
lhs = Foam::$3<$2>()(a, b);
}'
)dnl>
dnl
dnl ---------------------------------------------------------------------------
dnl rule_ternary_op(inOut)
dnl Production rule for ternary field operations
dnl
dnl Example:
dnl rule_ternary_op(sfield)
dnl
dnl sfield (lhs) ::= lfield(cond) QUESTION sfield (a) COLON sfield (b) .
dnl {
dnl lhs = (cond ? a : b);
dnl }
dnl
define(`rule_ternary_op',
`$1 (lhs) ::= lfield(cond) QUESTION $1 (a) COLON $1 (b) .
{
lhs = (cond ? a : b);
}'
)dnl>
dnl
dnl ---------------------------------------------------------------------------
dnl rule_unary_func(out, in1, tok, func)
dnl Production rule for unary functions:
dnl
dnl Example:
dnl rule_unary_func(sfield, vfield, MAG, Foam::mag)
dnl
dnl sfield (lhs) ::= MAG LPAREN vfield (a) RPAREN .
dnl {
dnl lhs = Foam::mag(a);
dnl }
dnl
define(`rule_unary_func',
`$1 (lhs) ::= $3 LPAREN $2 (a) RPAREN .
{
lhs = $4 (a);
}'
)dnl>
dnl
dnl ---------------------------------------------------------------------------
dnl rule_binary_func(out, in1, in2, tok, func)
dnl Production rule for binary functions:
dnl
dnl Example:
dnl rule_binary_func(sfield, sfield, sfield, POW, Foam::pow)
dnl
dnl sfield (lhs) ::= POW LPAREN sfield (a) COMMA sfield (b) RPAREN .
dnl {
dnl lhs = Foam::pow((a), (b));
dnl }
dnl
define(`rule_binary_func',
`$1 (lhs) ::= $4 LPAREN $2 (a) COMMA $3 (b) RPAREN .
{
lhs = $5((a), (b));
}'
)dnl>
dnl
dnl End m4 definitions
\*---------------------------------------------------------------------------*/
%include
{
#include "evalStringToScalarDriver.H"
#include "evalStringToScalarParser.H"
#include "unitConversion.H"
#include "Random.H"
#include "Switch.H"
#include "error.H"
#pragma GCC diagnostic ignored "-Wold-style-cast"
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wsign-compare"
// Enable ParseTrace
#undef NDEBUG
// Local Functions
//- Test for value close to zero
template<class T>
bool equalZero(const T& val)
{
return (Foam::mag(val) < Foam::ROOTVSMALL);
}
} // %include
// ------------------------------------------------------------------------- //
%namespace {}
// Use extra argument for the return value
%extra_context { Foam::parsing::evalStringToScalar::parseDriver* driver }
%parse_failure { driver->reportFatal("Parse failure, giving up..."); }
%syntax_error { driver->reportFatal("Syntax error"); }
%token_prefix TOK_
// Terminals
%token_type {Foam::scalar}
// Non-terminals
%type lfield {bool}
%type sfield {Foam::scalar}
// (https://en.cppreference.com/w/cpp/language/operator_precedence)
%right QUESTION COLON . // 16: right-to-left
%left LOR . // 15:
%left LAND . // 14:
// %left BIT_OR . // 13
// %left BIT_XOR . // 12
// %left BIT_AND . // 11
%left EQUAL NOT_EQUAL . // 10
%left LESS_EQ GREATER_EQ LESS GREATER . // 9
%left PLUS MINUS . // 6
%left TIMES DIVIDE PERCENT . // 5
%right NEGATE NOT . // 3: right-to-left
%start_symbol evaluate
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
/*---------------------------------------------------------------------------*\
* Productions (Foam::scalar)
\*---------------------------------------------------------------------------*/
evaluate ::= sfield(a).
{
driver->setValue(a);
}
// Basics
sfield(lhs) ::= NUMBER(a). { lhs = a; } // From scanToken
sfield(lhs) ::= LPAREN sfield(a) RPAREN. { lhs = a; }
sfield(lhs) ::= MINUS sfield(a). [NEGATE] { lhs = -a; }
// Conversion (cast)
sfield(lhs) ::= MAG LPAREN lfield(a) RPAREN . { lhs = Foam::scalar(a); }
// Constants
sfield(lhs) ::= PI LPAREN RPAREN. { lhs = Foam::constant::mathematical::pi; }
sfield(lhs) ::= DEG_TO_RAD LPAREN RPAREN. { lhs = Foam::degToRad(); }
sfield(lhs) ::= RAD_TO_DEG LPAREN RPAREN. { lhs = Foam::radToDeg(); }
// Operations
rule_ternary_op(sfield)
rule_binary_op(sfield, sfield, sfield, PLUS, +)
rule_binary_op(sfield, sfield, sfield, MINUS, -)
rule_binary_op(sfield, sfield, sfield, TIMES, *)
sfield(lhs) ::= sfield(a) DIVIDE sfield(b).
{
lhs = equalZero(b) ? Foam::scalar(0) : (a / b);
}
sfield(lhs) ::= sfield(a) PERCENT sfield(b).
{
lhs = equalZero(b) ? Foam::scalar(0) : std::fmod(a, b);
}
// Functions
rule_unary_func(sfield, sfield, DEG_TO_RAD, Foam::degToRad)
rule_unary_func(sfield, sfield, RAD_TO_DEG, Foam::radToDeg)
rule_unary_func(sfield, sfield, EXP, Foam::exp)
rule_unary_func(sfield, sfield, LOG, Foam::log)
rule_unary_func(sfield, sfield, LOG10, Foam::log10)
rule_unary_func(sfield, sfield, SQR, Foam::sqr)
rule_unary_func(sfield, sfield, SQRT, Foam::sqrt)
rule_unary_func(sfield, sfield, CBRT, Foam::cbrt)
rule_unary_func(sfield, sfield, SIN, Foam::sin)
rule_unary_func(sfield, sfield, COS, Foam::cos)
rule_unary_func(sfield, sfield, TAN, Foam::tan)
rule_unary_func(sfield, sfield, ASIN, Foam::asin)
rule_unary_func(sfield, sfield, ACOS, Foam::acos)
rule_unary_func(sfield, sfield, ATAN, Foam::atan)
rule_unary_func(sfield, sfield, SINH, Foam::sinh)
rule_unary_func(sfield, sfield, COSH, Foam::cosh)
rule_unary_func(sfield, sfield, TANH, Foam::tanh)
rule_unary_func(sfield, sfield, MAG, Foam::mag)
rule_unary_func(sfield, sfield, MAGSQR, Foam::magSqr)
rule_unary_func(sfield, sfield, FLOOR, std::floor)
rule_unary_func(sfield, sfield, CEIL, std::ceil)
rule_unary_func(sfield, sfield, ROUND, std::round)
rule_binary_func(sfield, sfield, sfield, POW, Foam::pow)
rule_binary_func(sfield, sfield, sfield, ATAN2, Foam::atan2)
rule_binary_func(sfield, sfield, sfield, HYPOT, Foam::hypot)
rule_binary_func(sfield, sfield, sfield, MIN, Foam::min)
rule_binary_func(sfield, sfield, sfield, MAX, Foam::max)
sfield(lhs) ::= RAND LPAREN RPAREN.
{
lhs = Foam::Random().sample01<Foam::scalar>();
}
sfield(lhs) ::= RAND LPAREN sfield(seed) RPAREN.
{
lhs = Foam::Random(seed).sample01<Foam::scalar>();
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Productions for bool (logical) fields
evaluate ::= lfield(a).
{
driver->setValue(Foam::scalar(a));
}
// Basics
lfield(lhs) ::= BOOL_FALSE . { lhs = false; }
lfield(lhs) ::= BOOL_TRUE . { lhs = true; }
lfield(lhs) ::= LPAREN lfield(a) RPAREN . { lhs = a; }
lfield(lhs) ::= NOT lfield(a). [NEGATE] { lhs = !a; }
// Conversion (cast)
lfield(lhs) ::= BOOL LPAREN lfield(a) RPAREN . { lhs = a; }
lfield(lhs) ::= BOOL LPAREN sfield(a) RPAREN . { lhs = Foam::Switch(a); }
// Operations
rule_ternary_op(lfield)
rule_binary_logical_op(lfield, bool, andOp, LAND, &&)
rule_binary_logical_op(lfield, bool, orOp, LOR, ||)
rule_binary_logical_op(sfield, Foam::scalar, equalOp, EQUAL, ==)
rule_binary_logical_op(sfield, Foam::scalar, notEqualOp, NOT_EQUAL, !=)
rule_binary_logical_op(sfield, Foam::scalar, lessOp, LESS, <)
rule_binary_logical_op(sfield, Foam::scalar, lessEqOp, LESS_EQ, <=)
rule_binary_logical_op(sfield, Foam::scalar, greaterOp, GREATER, >)
rule_binary_logical_op(sfield, Foam::scalar, greaterEqOp, GREATER_EQ, >=)
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
%code
{
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::parsing::evalStringToScalar::parser::stop()
{
if (lemon_)
{
ParseFree(lemon_, ::operator delete);
#ifndef NDEBUG
ParseTrace(nullptr, nullptr);
#endif
lemon_ = nullptr;
}
}
void Foam::parsing::evalStringToScalar::parser::start(parseDriver& driver)
{
this->stop();
lemon_ = ParseAlloc(::operator new, &driver);
if (debug & 2)
{
#ifndef NDEBUG
ParseTrace(stderr, const_cast<char*>(prompt_));
#endif
}
}
void Foam::parsing::evalStringToScalar::parser::parse
(
int tokenId,
Foam::scalar val /* The value for the token */
)
{
Parse(lemon_, tokenId, val);
}
Foam::word Foam::parsing::evalStringToScalar::parser::nameOfToken
(
int tokenId
) const
{
#ifndef NDEBUG
if
(
tokenId > 0
&& unsigned(tokenId) < (sizeof(yyTokenName) / sizeof(char*))
)
{
return yyTokenName[tokenId];
}
return "<invalid>";
#else
return word();
#endif
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End of %code
// ************************************************************************* //

View File

@ -1,105 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / 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::parser
Description
Interface to lemon parser to simple string to scalar evaluation, which
is used by stringOps::toScalar
\*---------------------------------------------------------------------------*/
#ifndef evalStringToScalarParser_H
#define evalStringToScalarParser_H
namespace Foam
{
namespace parsing
{
namespace evalStringToScalar
{
// Forward Declarations
class parseDriver;
/*---------------------------------------------------------------------------*\
Class parser Declaration
\*---------------------------------------------------------------------------*/
class parser
{
// Private Data
//- Prompt for parser tracing
static constexpr const char* const prompt_ = "#eval:";
//- The lemon parser (demand-driven)
void* lemon_;
public:
// Constructors
//- Construct null
parser() : lemon_(nullptr) {}
//- Destructor, delete lemon parser
~parser()
{
stop();
}
// Member Functions
//- Start parsing, with given driver context
void start(parseDriver& driver);
//- Stop parsing, freeing the allocated parser
void stop();
//- Push token/value to parser
void parse(int tokenId, Foam::scalar val);
//- The text name corresponding to the tokenId
word nameOfToken(int tokenId) const;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace evalStringToScalar
} // End namespace parsing
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -1,118 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / 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::scanner
Description
Ragel lexer interface for lemon grammar of a simple string to
scalar evaluation, which is used by stringOps::toScalar
Note
Ragel code generated with the ./createCode script.
\*---------------------------------------------------------------------------*/
#ifndef evalStringToScalarScanner_H
#define evalStringToScalarScanner_H
#include <string>
namespace Foam
{
namespace parsing
{
namespace evalStringToScalar
{
// Forward Declarations
class parser;
class parseDriver;
/*---------------------------------------------------------------------------*\
Class scanner Declaration
\*---------------------------------------------------------------------------*/
class scanner
{
// Private Data
//- Wrapped lemon parser
parser* parser_;
// Ragel code state, action
int cs, act;
public:
// Constructors
//- Construct null
scanner() : parser_(nullptr) {}
//- Destructor, deletes parser
~scanner();
// Member Functions
//- Evaluate sub-string
bool process
(
const std::string& str, size_t pos, size_t len,
parseDriver& driver
);
//- Evaluate sub-string
bool process
(
const std::string& str, size_t pos,
parseDriver& driver
)
{
return process(str, pos, std::string::npos, driver);
}
//- Evaluate string
bool process(const std::string& str, parseDriver& driver)
{
return process(str, 0, std::string::npos, driver);
}
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace evalStringToScalar
} // End namespace parsing
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -1,267 +0,0 @@
/*--------------------------------*- C++ -*----------------------------------*\
========= |
\\ / 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/>.
Description
Ragel lexer interface for lemon grammar of a simple string to
scalar evaluation
\*---------------------------------------------------------------------------*/
#include "evalStringToScalarScanner.H"
#include "evalStringToScalarDriver.H"
#include "evalStringToScalarLemonParser.h"
#include "evalStringToScalarParser.H"
#include "error.H"
#include "macros.H"
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#pragma GCC diagnostic ignored "-Wold-style-cast"
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Ragel lexer with lemon parser integration
// Ragel machine definition
// Ragel variables (p, pe, eof, cs, top, stack, ts, te, act) defined later...
//
// Can use 'variable p xxx;' etc to change these names
%%{
machine evalScanner;
write data;
}%%
#undef DebugScannerInfo
#define DebugScannerInfo if (debug & 4) InfoErr
#define TOKEN_OF(T) TOK_##T
#define EMIT_TOKEN(T) \
driver.parsePosition() = (ts-buf); \
DebugScannerInfo << STRINGIFY(T) << ": "<< driver.parsePosition() << nl; \
parser_->parse(TOKEN_OF(T), 0); \
driver.parsePosition() = (p-buf);
%%{
machine evalScanner;
action emit_number {
driver.parsePosition() = (ts-buf);
DebugScannerInfo
<< "Number:" << std::string(ts, te-ts).c_str()
<< " at " << driver.parsePosition() << nl;
scalar val(0);
if (readScalar(std::string(ts, te-ts), val))
{
// Emit number
parser_->parse(TOKEN_OF(NUMBER), val);
}
else
{
// Range error
driver.reportFatal("Error parsing number");
}
driver.parsePosition() = (p-buf);
}
action emit_ident {
driver.parsePosition() = (ts-buf);
const word ident = word::validate(ts, te);
driver.reportFatal("Unknown function/type: " + ident);
driver.parsePosition() = (p-buf);
}
decimal = ((digit* '.' digit+) | (digit+ '.'?)) ;
number = (digit+ | decimal) ([Ee][\-+]? digit+)? ;
ident = ((alpha|'_') . ((alnum|'_')**)) ;
## The scanner
main := |*
space*;
number => emit_number;
## operators
'!' => { EMIT_TOKEN(NOT); };
'%' => { EMIT_TOKEN(PERCENT); };
'(' => { EMIT_TOKEN(LPAREN); };
')' => { EMIT_TOKEN(RPAREN); };
'*' => { EMIT_TOKEN(TIMES); };
'+' => { EMIT_TOKEN(PLUS); };
'-' => { EMIT_TOKEN(MINUS); };
',' => { EMIT_TOKEN(COMMA); };
'/' => { EMIT_TOKEN(DIVIDE); };
'!' => { EMIT_TOKEN(NOT); };
'?' => { EMIT_TOKEN(QUESTION); };
':' => { EMIT_TOKEN(COLON); };
'<' => { EMIT_TOKEN(LESS); };
'<=' => { EMIT_TOKEN(LESS_EQ); };
'>' => { EMIT_TOKEN(GREATER); };
'>=' => { EMIT_TOKEN(GREATER_EQ); };
'==' => { EMIT_TOKEN(EQUAL); };
'!=' => { EMIT_TOKEN(NOT_EQUAL); };
'&&' => { EMIT_TOKEN(LAND); };
'||' => { EMIT_TOKEN(LOR); };
## Not needed '&' => { EMIT_TOKEN(BIT_AND); };
## Not needed '|' => { EMIT_TOKEN(BIT_OR); };
## Not needed '^' => { EMIT_TOKEN(BIT_XOR); };
## Regular functions
'pi' => { EMIT_TOKEN(PI); };
'degToRad' => { EMIT_TOKEN(DEG_TO_RAD); };
'radToDeg' => { EMIT_TOKEN(RAD_TO_DEG); };
'exp' => { EMIT_TOKEN(EXP); };
'log' => { EMIT_TOKEN(LOG); };
'log10' => { EMIT_TOKEN(LOG10); };
'pow' => { EMIT_TOKEN(POW); };
'sqr' => { EMIT_TOKEN(SQR); };
'sqrt' => { EMIT_TOKEN(SQRT); };
'cbrt' => { EMIT_TOKEN(CBRT); };
'sin' => { EMIT_TOKEN(SIN); };
'cos' => { EMIT_TOKEN(COS); };
'tan' => { EMIT_TOKEN(TAN); };
'asin' => { EMIT_TOKEN(ASIN); };
'acos' => { EMIT_TOKEN(ACOS); };
'atan' => { EMIT_TOKEN(ATAN); };
'atan2' => { EMIT_TOKEN(ATAN2); };
'hypot' => { EMIT_TOKEN(HYPOT); };
'sinh' => { EMIT_TOKEN(SINH); };
'cosh' => { EMIT_TOKEN(COSH); };
'tanh' => { EMIT_TOKEN(TANH); };
'min' => { EMIT_TOKEN(MIN); };
'max' => { EMIT_TOKEN(MAX); };
'mag' => { EMIT_TOKEN(MAG); };
'magSqr' => { EMIT_TOKEN(MAGSQR); };
'floor' => { EMIT_TOKEN(FLOOR); };
'ceil' => { EMIT_TOKEN(CEIL); };
'round' => { EMIT_TOKEN(ROUND); };
'rand' => { EMIT_TOKEN(RAND); };
'bool' => { EMIT_TOKEN(BOOL); };
## Constants
'false' =>{ EMIT_TOKEN(BOOL_FALSE); };
'true' =>{ EMIT_TOKEN(BOOL_TRUE); };
## Catch-all for identifiers/errors
ident => emit_ident;
space*;
*|;
}%%
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::parsing::evalStringToScalar::scanner::~scanner()
{
if (parser_)
{
delete parser_;
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::parsing::evalStringToScalar::scanner::process
(
const std::string& str,
size_t strBeg,
size_t strLen,
parseDriver& driver
)
{
if (!parser_)
{
parser_ = new parser();
}
driver.content(str, strBeg, strLen);
size_t strEnd = str.length();
if (strBeg > str.length())
{
strBeg = str.length();
}
else if (strLen != std::string::npos)
{
strLen += strBeg;
if (strLen < str.length())
{
strEnd = strLen;
}
}
parser_->start(driver);
// Ragel token start/end (required naming)
const char* ts;
const char* te;
// Local buffer data.
// - p, pe, eof are required Ragel naming
// - buf is our own naming
const char* buf = &(str[strBeg]);
const char* eof = &(str[strEnd]);
const char* p = buf;
const char* pe = eof;
// Initialize FSM variables
%%{write init;}%% /* ^^^ FSM initialization here ^^^ */;
%%{write exec;}%% /* ^^^ FSM execution here ^^^ */;
if (%%{write error;}%% == cs)
{
driver.reportFatal("Parse error while scanning", (p-buf));
}
if (p != eof)
{
driver.reportFatal("Parsing failed with remaining content", (p-buf));
}
// Terminate parser execution
parser_->parse(0, 0);
parser_->stop();
return true;
}
// ************************************************************************* //

View File

@ -36,4 +36,13 @@ FoamFile
#endif
// Silly example, but can also force cast from scalar to bool
#if #eval "bool(2 * pi())"
condition true;
#else
condition false;
#endif
// ************************************************************************* //