From 836d3a849f494470cf010ecc4311d204a861422b Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Mon, 30 Sep 2019 16:40:32 +0200
Subject: [PATCH] ENH: add stringOps::toScalar and dictionary #eval directive

- the #eval directive is similar to the #calc directive, but for evaluating
  string expressions into scalar values. It uses an internal parser for
  the evaluation instead of dynamic code compilation. This can make it
  more suitable for 'quick' evaluations.

  The evaluation supports the following:
    - operations:  - + * /
    - functions:  exp, log, log10, pow, sqrt, cbrt, sqr, mag, magSqr
    - trigonometric:  sin, cos, tan, asin, acos, atan, atan2, hypot
    - hyperbolic:  sinh, cosh, tanh
    - conversions:  degToRad, radToDeg
    - constants:  pi()
    - misc: rand(), rand(seed)
---
 applications/test/dictionary/testDictCalc1    |   35 +
 applications/test/dictionary/testDictEval1    |   44 +
 src/OpenFOAM/Make/files                       |    7 +-
 .../functionEntries/evalEntry/evalEntry.C     |  163 ++
 .../functionEntries/evalEntry/evalEntry.H     |  101 ++
 .../strings/parsing/genericRagelLemonDriver.C |  192 +++
 .../strings/parsing/genericRagelLemonDriver.H |  150 ++
 .../primitives/strings/parsing/parsing.H      |    3 +-
 .../primitives/strings/stringOps/stringOps.H  |    3 +
 .../strings/stringOps/toScalar/createCode     |   11 +
 .../stringOps/toScalar/evalStringToScalar.H   |   69 +
 .../toScalar/evalStringToScalarDriver.C       |   72 +
 .../toScalar/evalStringToScalarDriver.H       |  120 ++
 .../toScalar/evalStringToScalarLemonParser.h  |   35 +
 .../evalStringToScalarLemonParser.lyy         |  290 ++++
 .../toScalar/evalStringToScalarParser.H       |   96 ++
 .../toScalar/evalStringToScalarScanner.H      |   99 ++
 .../toScalar/evalStringToScalarScanner.cc     | 1426 +++++++++++++++++
 .../toScalar/evalStringToScalarScanner.rl     |  229 +++
 19 files changed, 3143 insertions(+), 2 deletions(-)
 create mode 100644 applications/test/dictionary/testDictCalc1
 create mode 100644 applications/test/dictionary/testDictEval1
 create mode 100644 src/OpenFOAM/db/dictionary/functionEntries/evalEntry/evalEntry.C
 create mode 100644 src/OpenFOAM/db/dictionary/functionEntries/evalEntry/evalEntry.H
 create mode 100644 src/OpenFOAM/primitives/strings/parsing/genericRagelLemonDriver.C
 create mode 100644 src/OpenFOAM/primitives/strings/parsing/genericRagelLemonDriver.H
 create mode 100755 src/OpenFOAM/primitives/strings/stringOps/toScalar/createCode
 create mode 100644 src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalar.H
 create mode 100644 src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarDriver.C
 create mode 100644 src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarDriver.H
 create mode 100644 src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarLemonParser.h
 create mode 100644 src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarLemonParser.lyy
 create mode 100644 src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarParser.H
 create mode 100644 src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarScanner.H
 create mode 100644 src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarScanner.cc
 create mode 100644 src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarScanner.rl

diff --git a/applications/test/dictionary/testDictCalc1 b/applications/test/dictionary/testDictCalc1
new file mode 100644
index 00000000000..93b52a9fb8e
--- /dev/null
+++ b/applications/test/dictionary/testDictCalc1
@@ -0,0 +1,35 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  Any                                   |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      testDictCalc1;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+calc1 #calc #{    degToRad() *
+        constant::mathematical::pi *
+        cos(100.0) * mag(19) + 1 * constant::mathematical::pi
+            +
+        // Skip comments
+        constant::mathematical::pi * 15 + hypot(3.0,4.0) #};
+
+calc3   (#calc"hypot(3.0,4.0)", #calc"sqrt(2.0)", #calc"constant::mathematical::pi");
+
+calc4   #calc"$calc1 * 10";
+
+pi()    #calc #{ constant::mathematical::pi #};
+
+sinh(0.1)  #calc #{ sinh(0.1) #};
+cosh(0.1)  #calc #{ cosh(0.1) #};
+
+sqrt100    #calc #{((sqrt(100.0)))#};
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/applications/test/dictionary/testDictEval1 b/applications/test/dictionary/testDictEval1
new file mode 100644
index 00000000000..2fccaa7f418
--- /dev/null
+++ b/applications/test/dictionary/testDictEval1
@@ -0,0 +1,44 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  Any                                   |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      testDictEval1;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+eval1 #eval #{    degToRad() *
+        pi() *
+        cos(100) * mag(19) + 1 * pi()
+            +
+        // Skip comments
+        pi() * 15 + hypot(3,4) #};
+
+eval2 #eval{    degToRad() *
+        pi() *
+        cos(100) * mag(19) + 1 * pi()
+            +
+        // Skip comments
+        pi() * 15  + hypot(3,4) };
+
+eval3   (#eval{hypot(3,4)}, #eval{sqrt(2)}, #eval{pi()});
+
+eval4   #eval{$eval1 * 10};
+
+pi()    #eval{pi()};
+
+rand()  #eval{100*rand(123456)};
+
+sinh(0.1)  #eval{sinh(0.1)};
+cosh(0.1)  #eval{cosh(0.1)};
+
+sqrt100    #eval{((sqrt(100)))};
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/OpenFOAM/Make/files b/src/OpenFOAM/Make/files
index 92e60ba0562..c8e5d89eb0d 100644
--- a/src/OpenFOAM/Make/files
+++ b/src/OpenFOAM/Make/files
@@ -130,9 +130,13 @@ $(strings)/wordRe/wordRe.C
 $(strings)/wordRes/wordRes.C
 $(strings)/lists/CStringList.C
 $(strings)/lists/hashedWordList.C
+$(strings)/parsing/parsing.C
+$(strings)/parsing/genericRagelLemonDriver.C
 $(strings)/stringOps/stringOps.C
 $(strings)/stringOps/stringOpsSort.C
-$(strings)/parsing/parsing.C
+$(strings)/stringOps/toScalar/evalStringToScalarDriver.C
+$(strings)/stringOps/toScalar/evalStringToScalarLemonParser.lyy
+$(strings)/stringOps/toScalar/evalStringToScalarScanner.cc
 
 ops = primitives/ops
 $(ops)/flipOp.C
@@ -248,6 +252,7 @@ $(dictionaryListEntry)/dictionaryListEntryIO.C
 functionEntries = $(dictionary)/functionEntries
 $(functionEntries)/calcEntry/calcEntry.C
 $(functionEntries)/codeStream/codeStream.C
+$(functionEntries)/evalEntry/evalEntry.C
 $(functionEntries)/functionEntry/functionEntry.C
 $(functionEntries)/includeEntry/includeEntry.C
 $(functionEntries)/includeEtcEntry/includeEtcEntry.C
diff --git a/src/OpenFOAM/db/dictionary/functionEntries/evalEntry/evalEntry.C b/src/OpenFOAM/db/dictionary/functionEntries/evalEntry/evalEntry.C
new file mode 100644
index 00000000000..5599f771a83
--- /dev/null
+++ b/src/OpenFOAM/db/dictionary/functionEntries/evalEntry/evalEntry.C
@@ -0,0 +1,163 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "evalEntry.H"
+#include "dictionary.H"
+#include "stringOps.H"
+#include "addToMemberFunctionSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionEntries
+{
+    addNamedToMemberFunctionSelectionTable
+    (
+        functionEntry,
+        evalEntry,
+        execute,
+        dictionaryIstream,
+        eval
+    );
+
+    addNamedToMemberFunctionSelectionTable
+    (
+        functionEntry,
+        evalEntry,
+        execute,
+        primitiveEntryIstream,
+        eval
+    );
+
+} // End namespace functionEntry
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+Foam::scalar Foam::functionEntries::evalEntry::evaluate
+(
+    const dictionary& parentDict,
+    Istream& is
+)
+{
+    DetailInfo
+        << "Using #eval at line " << is.lineNumber()
+        << " in file " <<  parentDict.name() << nl;
+
+    // String to evaluate
+    string s;
+
+    token tok(is);
+
+    if (!tok.good())
+    {
+        FatalIOErrorInFunction(is)
+            << "Bad token - could not get string to evaluate"
+            << exit(FatalIOError);
+        return 0;
+    }
+
+    if (tok.isString())
+    {
+        s = tok.stringToken();
+    }
+    else if (tok == token::BEGIN_BLOCK)
+    {
+        dynamic_cast<ISstream&>(is).getLine(s, token::END_BLOCK);
+    }
+    else
+    {
+        is.putBack(tok);
+
+        FatalIOErrorInFunction(is)
+            << "Invalid input for #eval" << nl
+            << exit(FatalIOError);
+    }
+
+    #ifdef FULLDEBUG
+    DetailInfo
+        << "input: " << s << endl;
+    #endif
+
+    stringOps::inplaceRemoveComments(s);
+    stringOps::inplaceExpand(s, parentDict);
+
+    #ifdef FULLDEBUG
+    DetailInfo
+        << "expanded: " << s << endl;
+    #endif
+
+    return stringOps::toScalar(s);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::functionEntries::evalEntry::execute
+(
+    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)})
+    );
+
+    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);
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/db/dictionary/functionEntries/evalEntry/evalEntry.H b/src/OpenFOAM/db/dictionary/functionEntries/evalEntry/evalEntry.H
new file mode 100644
index 00000000000..ce19a2f8b8a
--- /dev/null
+++ b/src/OpenFOAM/db/dictionary/functionEntries/evalEntry/evalEntry.H
@@ -0,0 +1,101 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+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::functionEntries::evalEntry
+
+Description
+    Uses stringOps::toScalar to evaluate mathematical expressions.
+
+    The input can any form of string or, for convenience,
+    a '{}' delimited string literal.  In all cases, C/C++ comment stripping
+    is also performed.
+
+    For example,
+
+    \verbatim
+    a 1;
+    b 3;
+    c #eval "sin(pi()*$a/$b)";
+
+    d #eval{
+        // ignore: sin(pi()*$a/$b)
+        sin(degToRad(45))
+    };
+    \endverbatim
+
+SourceFiles
+    evalEntry.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef evalEntry_H
+#define evalEntry_H
+
+#include "functionEntry.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionEntries
+{
+
+/*---------------------------------------------------------------------------*\
+                          Class evalEntry Declaration
+\*---------------------------------------------------------------------------*/
+
+class evalEntry
+:
+    public functionEntry
+{
+
+    //- Evaluate and return a scalar
+    static scalar evaluate(const dictionary& parentDict, Istream& is);
+
+
+public:
+
+    //- Execute in a primitiveEntry context
+    static bool execute
+    (
+        const dictionary& parentDict,
+        primitiveEntry& thisEntry,
+        Istream& is
+    );
+
+    //- Execute in a sub-dict context
+    static bool execute(dictionary& parentDict, Istream& is);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace functionEntries
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/strings/parsing/genericRagelLemonDriver.C b/src/OpenFOAM/primitives/strings/parsing/genericRagelLemonDriver.C
new file mode 100644
index 00000000000..1ce3749cfde
--- /dev/null
+++ b/src/OpenFOAM/primitives/strings/parsing/genericRagelLemonDriver.C
@@ -0,0 +1,192 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "genericRagelLemonDriver.H"
+#include "evalStringToScalarDriver.H"
+#include "error.H"
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::parsing::genericRagelLemonDriver::genericRagelLemonDriver()
+:
+    content_(std::cref<std::string>(string::null)),
+    position_(0)
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::Ostream& Foam::parsing::genericRagelLemonDriver::printBuffer
+(
+    Ostream& os
+) const
+{
+    const std::string& s = content_.get();
+
+    for (char c : s)
+    {
+        // if (!c) break;
+
+        if (c == '\t')
+        {
+            // Flatten tab to single space for better alignment
+            os  << ' ';
+        }
+        else
+        {
+            os  << c;
+        }
+    }
+
+    return os;
+}
+
+
+void Foam::parsing::genericRagelLemonDriver::reportFatal
+(
+    const std::string& msg
+) const
+{
+    if (position_)
+    {
+        reportFatal(msg, position_);
+    }
+    else
+    {
+        auto& os = FatalIOError
+        (
+            FUNCTION_NAME,
+            __FILE__,
+            __LINE__,
+            ""
+        );
+
+        os  << nl << msg.c_str() << " in expression\n"
+            << "<<<<\n";
+
+        printBuffer(os)
+            << "\n>>>>\n"
+            << exit(Foam::FatalIOError);
+    }
+}
+
+
+void Foam::parsing::genericRagelLemonDriver::reportFatal
+(
+    const std::string& msg,
+    size_t pos
+) const
+{
+    auto& os = Foam::FatalIOError
+    (
+        FUNCTION_NAME,
+        __FILE__,
+         __LINE__,
+        ""
+    );
+
+    os  << nl << msg.c_str()
+        << " in expression at position:" << long(pos) << nl
+        << "<<<<\n";
+
+    const auto begIter = content().cbegin();
+    const auto endIter = content().cend();
+
+    size_t newline0 = 0, newline1 = 0;
+
+    auto iter = begIter;
+
+    for (/*nil*/; iter != endIter; ++iter)
+    {
+        char c(*iter);
+
+        if ('\t' == c)
+        {
+            // Flatten tab to single space for better alignment
+            os  << ' ';
+        }
+        else if ('\n' == c)
+        {
+            os  << c;
+
+            newline1 = (iter-begIter);
+
+            if (newline1 < pos)
+            {
+                newline0 = newline1;
+            }
+            else
+            {
+                ++iter;
+                break;
+            }
+        }
+        else
+        {
+            os  << c;
+        }
+    }
+
+    if (newline0 == newline1 || newline1 == pos)
+    {
+        os  << '\n';
+    }
+
+    size_t col = std::min(newline0, newline1);
+    if (col < pos)
+    {
+        col = pos - col;
+        if (col) --col;
+
+        for (/*nil*/; col; --col)
+        {
+            os  << ' ';
+        }
+    }
+
+    os  << "^^^^ near here\n";
+
+    // Finish output
+    for (/*nil*/; iter != endIter; ++iter)
+    {
+        char c(*iter);
+
+        if ('\t' == c)
+        {
+            // Flatten tab to single space for better alignment
+            os  << ' ';
+        }
+        else
+        {
+            os  << c;
+        }
+    }
+
+    os  << "\n>>>>\n"
+        << exit(Foam::FatalIOError);
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/strings/parsing/genericRagelLemonDriver.H b/src/OpenFOAM/primitives/strings/parsing/genericRagelLemonDriver.H
new file mode 100644
index 00000000000..b093178f165
--- /dev/null
+++ b/src/OpenFOAM/primitives/strings/parsing/genericRagelLemonDriver.H
@@ -0,0 +1,150 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+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::genericRagelLemonDriver
+
+Description
+    Generic interface code for Ragel/Lemon combination
+    Subclasses should implement the process() method.
+
+    The scanner will often be implemented as localized lexer class.
+    The parser may be embedded into the scanner as file-scope, or
+    use a separate interface class.
+
+SourceFiles
+    genericRagelLemonDriver.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef genericRagelLemonDriver_H
+#define genericRagelLemonDriver_H
+
+#include "error.H"
+#include "className.H"
+#include <functional>
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace parsing
+{
+
+/*---------------------------------------------------------------------------*\
+                   Class genericRagelLemonDriver Declaration
+\*---------------------------------------------------------------------------*/
+
+class genericRagelLemonDriver
+{
+protected:
+
+    // Protected Data
+
+        //- Reference to the input string
+        std::reference_wrapper<const std::string> content_;
+
+        //- The last known parser position
+        size_t position_;
+
+
+public:
+
+    // Public Typedefs
+
+        //- Type for linear addressing within parse content
+        //  Naming as per bison
+        typedef size_t location_type;
+
+
+    // Constructors
+
+        //- Construct null
+        genericRagelLemonDriver();
+
+        //- Copy construct
+        genericRagelLemonDriver(const genericRagelLemonDriver& rhs) = default;
+
+        //- Move construct
+        genericRagelLemonDriver(genericRagelLemonDriver&& rhs) = default;
+
+
+    //- Destructor
+    virtual ~genericRagelLemonDriver() = default;
+
+
+    // Member Functions
+
+        //- Reset references
+        void clear()
+        {
+            content_ = std::cref<std::string>(string::null);
+            position_ = 0;
+        }
+
+        //- Get reference to the input buffer content
+        const std::string& content() const
+        {
+            return content_.get();
+        }
+
+        //- Set reference to the input buffer content
+        void content(const std::string& s)
+        {
+            content_ = std::cref<std::string>(s);
+            position_ = 0;
+        }
+
+        //- The last parse position
+        size_t parsePosition() const
+        {
+            return position_;
+        }
+
+        //- The last parse position
+        size_t& parsePosition()
+        {
+            return position_;
+        }
+
+        //- Output the input buffer string content
+        Ostream& printBuffer(Ostream& os) const;
+
+        //- Report FatalError
+        void reportFatal(const std::string& msg) const;
+
+        //- Report FatalError at parser position
+        void reportFatal(const std::string& msg, size_t pos) const;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace parsing
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/strings/parsing/parsing.H b/src/OpenFOAM/primitives/strings/parsing/parsing.H
index ee069c5d017..cc25fb67bd7 100644
--- a/src/OpenFOAM/primitives/strings/parsing/parsing.H
+++ b/src/OpenFOAM/primitives/strings/parsing/parsing.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2017 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2017-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -26,6 +26,7 @@ Namespace
 
 Description
     Collection of static functions and data related to parsing
+    and an isolated namespace for lexers, parsers, scanners.
 
 SourceFiles
     parsing.C
diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOps.H b/src/OpenFOAM/primitives/strings/stringOps/stringOps.H
index 6eb2499f9a6..516b00d0cf4 100644
--- a/src/OpenFOAM/primitives/strings/stringOps/stringOps.H
+++ b/src/OpenFOAM/primitives/strings/stringOps/stringOps.H
@@ -38,6 +38,7 @@ SourceFiles
 #ifndef stringOps_H
 #define stringOps_H
 
+#include "scalar.H"
 #include "string.H"
 #include "SubStrings.H"
 #include "word.H"
@@ -46,6 +47,8 @@ SourceFiles
 #include "stringOpsSort.H"
 #include "wordRes.H"
 
+#include "evalStringToScalar.H"
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 namespace Foam
diff --git a/src/OpenFOAM/primitives/strings/stringOps/toScalar/createCode b/src/OpenFOAM/primitives/strings/stringOps/toScalar/createCode
new file mode 100755
index 00000000000..3be1331c8d9
--- /dev/null
+++ b/src/OpenFOAM/primitives/strings/stringOps/toScalar/createCode
@@ -0,0 +1,11 @@
+#!/bin/sh
+cd ${0%/*} || exit 1                        # Run from this directory
+
+# Manually create ragel scanner and the lemon parser header
+
+"$WM_PROJECT_DIR/wmake/scripts/makeParser" \
+    -scanner=evalStringToScalarScanner.rl \
+    -parser=evalStringToScalarLemonParser.lyy \
+    ;
+
+#------------------------------------------------------------------------------
diff --git a/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalar.H b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalar.H
new file mode 100644
index 00000000000..2415a2248c6
--- /dev/null
+++ b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalar.H
@@ -0,0 +1,69 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+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/>.
+
+Namespace
+    Foam::parsing::evalStringToScalar
+
+Description
+    Parsing encapsulation for stringOps::toScalar
+
+\*---------------------------------------------------------------------------*/
+#ifndef evalStringToScalar_H
+#define evalStringToScalar_H
+
+#include "scalar.H"
+#include "string.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace stringOps
+{
+
+    //- A simple string to scalar evaluation that handles various basic
+    //- expressions. For trivial input, use readScalar instead (faster).
+    //
+    //  The evaluation supports the following:
+    //  - operations:  - + * /
+    //  - functions:  exp, log, log10, pow, sqrt, cbrt, sqr, mag, magSqr
+    //  - trigonometric:  sin, cos, tan, asin, acos, atan, atan2, hypot
+    //  - hyperbolic:  sinh, cosh, tanh
+    //  - conversions:  degToRad, radToDeg
+    //  - constants:  pi()
+    //  - misc: rand(), rand(seed)
+    //
+    //  \note The rand() function returns a uniform scalar on [0-1] interval
+    scalar toScalar(const std::string& s);
+
+} // End namespace stringOps
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarDriver.C b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarDriver.C
new file mode 100644
index 00000000000..f6bbf9be44a
--- /dev/null
+++ b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarDriver.C
@@ -0,0 +1,72 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+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 "evalStringToScalar.H"
+#include "evalStringToScalarDriver.H"
+#include "evalStringToScalarScanner.H"
+#include "error.H"
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::parsing::evalStringToScalar::parseDriver::parseDriver()
+:
+    genericRagelLemonDriver(),
+    value_(0)
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::scalar Foam::parsing::evalStringToScalar::parseDriver::execute
+(
+    const std::string& s
+)
+{
+    // scanner::debug = 1;
+
+    scanner().process(s, *this);
+
+    return value_;
+}
+
+
+// * * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * //
+
+Foam::scalar Foam::stringOps::toScalar(const std::string& s)
+{
+    Foam::parsing::evalStringToScalar::parseDriver driver;
+
+    scalar val = driver.execute(s);
+    // val = driver.value();
+
+    return val;
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarDriver.H b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarDriver.H
new file mode 100644
index 00000000000..58ed93e1631
--- /dev/null
+++ b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarDriver.H
@@ -0,0 +1,120 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+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
+{
+
+/*---------------------------------------------------------------------------*\
+                         Class parseDriver Declaration
+\*---------------------------------------------------------------------------*/
+
+class parseDriver
+:
+    public genericRagelLemonDriver
+{
+protected:
+
+    // Protected Data
+
+        //- The result
+        scalar value_;
+
+
+    // Protected Member Functions
+
+        // No copy copy construct
+        parseDriver(const parseDriver&) = delete;
+
+        // No copy assignment
+        void operator=(const parseDriver&) = delete;
+
+
+public:
+
+    ClassName("evalStringToScalar::driver");
+
+    // Constructors
+
+        //- Construct null
+        parseDriver();
+
+
+    //- Destructor
+    virtual ~parseDriver() = default;
+
+
+    // Member Functions
+
+        // Perform parsing on string
+        scalar execute(const std::string& s);
+
+        //- 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
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarLemonParser.h b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarLemonParser.h
new file mode 100644
index 00000000000..e068ad5c1d9
--- /dev/null
+++ b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarLemonParser.h
@@ -0,0 +1,35 @@
+#define TOK_PLUS                             1
+#define TOK_MINUS                            2
+#define TOK_TIMES                            3
+#define TOK_DIVIDE                           4
+#define TOK_NEGATE                           5
+#define TOK_NUMBER                           6
+#define TOK_LPAREN                           7
+#define TOK_RPAREN                           8
+#define TOK_PI                               9
+#define TOK_DEG_TO_RAD                      10
+#define TOK_RAD_TO_DEG                      11
+#define TOK_EXP                             12
+#define TOK_LOG                             13
+#define TOK_LOG10                           14
+#define TOK_POW                             15
+#define TOK_COMMA                           16
+#define TOK_SQR                             17
+#define TOK_SQRT                            18
+#define TOK_CBRT                            19
+#define TOK_SIN                             20
+#define TOK_COS                             21
+#define TOK_TAN                             22
+#define TOK_ASIN                            23
+#define TOK_ACOS                            24
+#define TOK_ATAN                            25
+#define TOK_ATAN2                           26
+#define TOK_HYPOT                           27
+#define TOK_SINH                            28
+#define TOK_COSH                            29
+#define TOK_TANH                            30
+#define TOK_MIN                             31
+#define TOK_MAX                             32
+#define TOK_MAG                             33
+#define TOK_MAGSQR                          34
+#define TOK_RAND                            35
diff --git a/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarLemonParser.lyy b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarLemonParser.lyy
new file mode 100644
index 00000000000..2b89c899863
--- /dev/null
+++ b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarLemonParser.lyy
@@ -0,0 +1,290 @@
+%include {
+/*--------------------------------*- C++ -*----------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+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.
+
+\*---------------------------------------------------------------------------*/
+
+#include "evalStringToScalarDriver.H"
+#include "evalStringToScalarParser.H"
+#include "unitConversion.H"
+#include "Random.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"
+}
+
+%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_
+
+%token_type     { Foam::scalar }
+
+%left PLUS MINUS.
+%left TIMES DIVIDE.
+%left NEGATE.
+
+
+eval(lhs) ::= exp(a).
+{
+    lhs = a;
+    driver->setValue(lhs);
+}
+
+exp(lhs) ::= NUMBER(a).
+{
+    lhs = a;
+}
+
+exp(lhs) ::= MINUS exp(a).
+{
+    lhs = -a;
+}
+
+exp(lhs) ::= exp(a) PLUS exp(b).
+{
+    lhs = a + b;
+}
+
+exp(lhs) ::= exp(a) MINUS exp(b).
+{
+    lhs = a - b;
+}
+
+exp(lhs) ::= exp(a) TIMES exp(b).
+{
+    lhs = a * b;
+}
+
+exp(lhs) ::= exp(a) DIVIDE exp(b).
+{
+    lhs = a / b;
+}
+
+exp(lhs) ::= LPAREN exp(a) RPAREN.
+{
+    lhs = a;
+}
+
+
+// Functions
+
+exp(lhs) ::= PI LPAREN RPAREN.
+{
+    lhs = Foam::constant::mathematical::pi;
+}
+
+exp(lhs) ::= DEG_TO_RAD LPAREN RPAREN.
+{
+    lhs = Foam::degToRad();
+}
+
+exp(lhs) ::= RAD_TO_DEG LPAREN RPAREN.
+{
+    lhs = Foam::radToDeg();
+}
+
+exp(lhs) ::= DEG_TO_RAD LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::degToRad(a);
+}
+
+exp(lhs) ::= RAD_TO_DEG LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::radToDeg(a);
+}
+
+exp(lhs) ::= EXP LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::exp(a);
+}
+
+exp(lhs) ::= LOG LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::log(a);
+}
+
+exp(lhs) ::= LOG10 LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::log10(a);
+}
+
+exp(lhs) ::= POW LPAREN exp(a) COMMA exp(b) RPAREN.
+{
+    lhs = Foam::pow(a, b);
+}
+
+exp(lhs) ::= SQR LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::sqr(a);
+}
+
+exp(lhs) ::= SQRT LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::sqrt(a);
+}
+
+exp(lhs) ::= CBRT LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::cbrt(a);
+}
+
+exp(lhs) ::= SIN LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::sin(a);
+}
+
+exp(lhs) ::= COS LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::cos(a);
+}
+
+exp(lhs) ::= TAN LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::tan(a);
+}
+
+exp(lhs) ::= ASIN LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::asin(a);
+}
+
+exp(lhs) ::= ACOS LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::acos(a);
+}
+
+exp(lhs) ::= ATAN LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::atan(a);
+}
+
+exp(lhs) ::= ATAN2 LPAREN exp(a) COMMA exp(b) RPAREN.
+{
+    lhs = Foam::atan2(a, b);
+}
+
+exp(lhs) ::= HYPOT LPAREN exp(a) COMMA exp(b) RPAREN.
+{
+    lhs = Foam::hypot(a, b);
+}
+
+exp(lhs) ::= SINH LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::sinh(a);
+}
+
+exp(lhs) ::= COSH LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::cosh(a);
+}
+
+exp(lhs) ::= TANH LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::tanh(a);
+}
+
+exp(lhs) ::= MIN LPAREN exp(a) COMMA exp(b) RPAREN.
+{
+    lhs = Foam::min(a, b);
+}
+
+exp(lhs) ::= MAX LPAREN exp(a) COMMA exp(b) RPAREN.
+{
+    lhs = Foam::max(a, b);
+}
+
+exp(lhs) ::= MAG LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::mag(a);
+}
+
+exp(lhs) ::= MAGSQR LPAREN exp(a) RPAREN.
+{
+    lhs = Foam::magSqr(a);
+}
+
+exp(lhs) ::= RAND LPAREN RPAREN.
+{
+    lhs = Foam::Random().sample01<Foam::scalar>();
+}
+
+exp(lhs) ::= RAND LPAREN exp(seed) RPAREN.
+{
+    lhs = Foam::Random(seed).sample01<Foam::scalar>();
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+%code
+{
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::parsing::evalStringToScalar::parser::stop()
+{
+    if (lemon_)
+    {
+        ParseFree(lemon_, ::operator delete);
+        lemon_ = nullptr;
+    }
+}
+
+
+void Foam::parsing::evalStringToScalar::parser::start(parseDriver& driver)
+{
+    this->stop();
+    lemon_ = ParseAlloc(::operator new, &driver);
+}
+
+
+void Foam::parsing::evalStringToScalar::parser::parse
+(
+    int tokenId,
+    Foam::scalar val    /* The value for the token */
+)
+{
+    Parse(lemon_, tokenId, val);
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+}  // Code
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarParser.H b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarParser.H
new file mode 100644
index 00000000000..a82a8791f1c
--- /dev/null
+++ b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarParser.H
@@ -0,0 +1,96 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+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
+
+        //- 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);
+};
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace evalStringToScalar
+} // End namespace parsing
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarScanner.H b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarScanner.H
new file mode 100644
index 00000000000..84e4492f338
--- /dev/null
+++ b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarScanner.H
@@ -0,0 +1,99 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+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:
+
+    //- Debug/tracing of scan
+    static int debug;
+
+
+    // Constructors
+
+        //- Construct null
+        scanner() : parser_(nullptr) {}
+
+
+    //- Destructor, deletes parser
+    ~scanner();
+
+
+    // Member Functions
+
+        //- Evaluate string
+        bool process(const std::string& str, parseDriver& driver);
+};
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace evalStringToScalar
+} // End namespace parsing
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarScanner.cc b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarScanner.cc
new file mode 100644
index 00000000000..4548dd8dd65
--- /dev/null
+++ b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarScanner.cc
@@ -0,0 +1,1426 @@
+
+#line 1 "evalStringToScalarScanner.rl"
+/*--------------------------------*- C++ -*----------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+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"
+
+#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+
+#ifndef FULLDEBUG
+#define NDEBUG
+#endif
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+int Foam::parsing::evalStringToScalar::scanner::debug = 0;
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+// 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
+
+
+#line 61 "evalStringToScalarScanner.cc"
+static const int evalScanner_start = 86;
+static const int evalScanner_first_final = 86;
+static const int evalScanner_error = 0;
+
+static const int evalScanner_en_main = 86;
+
+
+#line 60 "evalStringToScalarScanner.rl"
+
+
+#define TOKEN_OF(T)         TOK_##T
+#define EMIT_TOKEN(T)                                                         \
+    /* Inform driver of last position */                                      \
+    driver.parsePosition() = (p-buf);                                         \
+    DebugInfo<< "TOKEN_" #T << " at " << driver.parsePosition() << nl;        \
+    parser_->parse(TOKEN_OF(T), 0)
+
+
+
+#line 163 "evalStringToScalarScanner.rl"
+
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::parsing::evalStringToScalar::scanner::~scanner()
+{
+    if (parser_)
+    {
+        delete parser_;
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+bool Foam::parsing::evalStringToScalar::scanner::process
+(
+    const std::string& str,
+    parseDriver& driver
+)
+{
+    if (!parser_)
+    {
+        parser_ = new parser();
+    }
+
+    driver.content(str);
+    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[0]);
+    const char* eof = &(str[str.length()]);
+    const char* p = buf;
+    const char* pe = eof;
+
+    // Initialize FSM variables
+    
+#line 127 "evalStringToScalarScanner.cc"
+	{
+	cs = evalScanner_start;
+	ts = 0;
+	te = 0;
+	act = 0;
+	}
+
+#line 207 "evalStringToScalarScanner.rl"
+   /* ^^^ FSM initialization here ^^^ */;
+
+    
+#line 139 "evalStringToScalarScanner.cc"
+	{
+	if ( p == pe )
+		goto _test_eof;
+	switch ( cs )
+	{
+tr2:
+#line 86 "evalStringToScalarScanner.rl"
+	{{p = ((te))-1;}{
+        // Inform driver of last position
+        driver.parsePosition() = (p-buf);
+
+        DebugInfo
+            << "NUMBER:" << std::string(ts, te-ts).c_str()
+            << " at " << driver.parsePosition() << nl;
+
+        scalar val;
+
+        if (readScalar(std::string(ts, te-ts), val))
+        {
+            // Emit number
+            parser_->parse(TOKEN_OF(NUMBER), val);
+        }
+        else
+        {
+            driver.reportFatal("Error reading scalar value");
+        }
+    }}
+	goto st86;
+tr5:
+#line 157 "evalStringToScalarScanner.rl"
+	{{p = ((te))-1;}}
+	goto st86;
+tr8:
+#line 74 "evalStringToScalarScanner.rl"
+	{
+        // Inform driver of last position
+        driver.parsePosition() = (p-buf);
+    }
+#line 159 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr14:
+#line 127 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(ACOS); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr17:
+#line 126 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(ASIN); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr21:
+#line 128 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(ATAN); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr23:
+#line 129 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(ATAN2); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr28:
+#line 122 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(CBRT); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr31:
+#line 124 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(COS); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr33:
+#line 132 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(COSH); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr41:
+#line 114 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(DEG_TO_RAD); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr44:
+#line 116 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(EXP); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr49:
+#line 130 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(HYPOT); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr53:
+#line 117 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(LOG); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr56:
+#line 118 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(LOG10); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr62:
+#line 136 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(MAG); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr66:
+#line 137 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(MAGSQR); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr67:
+#line 135 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(MAX); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr69:
+#line 134 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(MIN); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr72:
+#line 113 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(PI); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr74:
+#line 119 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(POW); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr83:
+#line 115 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(RAD_TO_DEG); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr85:
+#line 138 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(RAND); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr90:
+#line 123 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(SIN); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr92:
+#line 131 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(SINH); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr95:
+#line 120 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(SQR); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr97:
+#line 121 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(SQRT); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr101:
+#line 125 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(TAN); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr103:
+#line 133 "evalStringToScalarScanner.rl"
+	{ p--; EMIT_TOKEN(TANH); }
+#line 156 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr105:
+#line 142 "evalStringToScalarScanner.rl"
+	{ EMIT_TOKEN(LPAREN); }
+#line 157 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr106:
+#line 143 "evalStringToScalarScanner.rl"
+	{ EMIT_TOKEN(RPAREN); }
+#line 157 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr107:
+#line 146 "evalStringToScalarScanner.rl"
+	{ EMIT_TOKEN(TIMES); }
+#line 157 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr108:
+#line 144 "evalStringToScalarScanner.rl"
+	{ EMIT_TOKEN(PLUS); }
+#line 157 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr109:
+#line 148 "evalStringToScalarScanner.rl"
+	{ EMIT_TOKEN(COMMA); }
+#line 157 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr110:
+#line 145 "evalStringToScalarScanner.rl"
+	{ EMIT_TOKEN(MINUS); }
+#line 157 "evalStringToScalarScanner.rl"
+	{te = p+1;}
+	goto st86;
+tr125:
+#line 153 "evalStringToScalarScanner.rl"
+	{te = p;p--;}
+	goto st86;
+tr126:
+#line 86 "evalStringToScalarScanner.rl"
+	{te = p;p--;{
+        // Inform driver of last position
+        driver.parsePosition() = (p-buf);
+
+        DebugInfo
+            << "NUMBER:" << std::string(ts, te-ts).c_str()
+            << " at " << driver.parsePosition() << nl;
+
+        scalar val;
+
+        if (readScalar(std::string(ts, te-ts), val))
+        {
+            // Emit number
+            parser_->parse(TOKEN_OF(NUMBER), val);
+        }
+        else
+        {
+            driver.reportFatal("Error reading scalar value");
+        }
+    }}
+	goto st86;
+tr128:
+#line 157 "evalStringToScalarScanner.rl"
+	{te = p;p--;}
+	goto st86;
+tr130:
+#line 160 "evalStringToScalarScanner.rl"
+	{te = p;p--;}
+	goto st86;
+st86:
+#line 1 "NONE"
+	{ts = 0;}
+	if ( ++p == pe )
+		goto _test_eof86;
+case 86:
+#line 1 "NONE"
+	{ts = p;}
+#line 416 "evalStringToScalarScanner.cc"
+	switch( (*p) ) {
+		case 32: goto st87;
+		case 40: goto tr105;
+		case 41: goto tr106;
+		case 42: goto tr107;
+		case 43: goto tr108;
+		case 44: goto tr109;
+		case 45: goto tr110;
+		case 46: goto st1;
+		case 47: goto tr112;
+		case 97: goto st6;
+		case 99: goto st18;
+		case 100: goto st26;
+		case 101: goto st34;
+		case 104: goto st37;
+		case 108: goto st42;
+		case 109: goto st48;
+		case 112: goto st58;
+		case 114: goto st62;
+		case 115: goto st72;
+		case 116: goto st81;
+	}
+	if ( (*p) > 13 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr113;
+	} else if ( (*p) >= 9 )
+		goto st87;
+	goto st0;
+st0:
+cs = 0;
+	goto _out;
+st87:
+	if ( ++p == pe )
+		goto _test_eof87;
+case 87:
+	if ( (*p) == 32 )
+		goto st87;
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st87;
+	goto tr125;
+st1:
+	if ( ++p == pe )
+		goto _test_eof1;
+case 1:
+	if ( 48 <= (*p) && (*p) <= 57 )
+		goto tr0;
+	goto st0;
+tr0:
+#line 1 "NONE"
+	{te = p+1;}
+	goto st88;
+st88:
+	if ( ++p == pe )
+		goto _test_eof88;
+case 88:
+#line 472 "evalStringToScalarScanner.cc"
+	switch( (*p) ) {
+		case 69: goto st2;
+		case 101: goto st2;
+	}
+	if ( 48 <= (*p) && (*p) <= 57 )
+		goto tr0;
+	goto tr126;
+st2:
+	if ( ++p == pe )
+		goto _test_eof2;
+case 2:
+	switch( (*p) ) {
+		case 43: goto st3;
+		case 45: goto st3;
+	}
+	if ( 48 <= (*p) && (*p) <= 57 )
+		goto st89;
+	goto tr2;
+st3:
+	if ( ++p == pe )
+		goto _test_eof3;
+case 3:
+	if ( 48 <= (*p) && (*p) <= 57 )
+		goto st89;
+	goto tr2;
+st89:
+	if ( ++p == pe )
+		goto _test_eof89;
+case 89:
+	if ( 48 <= (*p) && (*p) <= 57 )
+		goto st89;
+	goto tr126;
+tr112:
+#line 1 "NONE"
+	{te = p+1;}
+#line 147 "evalStringToScalarScanner.rl"
+	{ EMIT_TOKEN(DIVIDE); }
+	goto st90;
+st90:
+	if ( ++p == pe )
+		goto _test_eof90;
+case 90:
+#line 515 "evalStringToScalarScanner.cc"
+	switch( (*p) ) {
+		case 42: goto st4;
+		case 47: goto st91;
+	}
+	goto tr128;
+st4:
+	if ( ++p == pe )
+		goto _test_eof4;
+case 4:
+	if ( (*p) == 42 )
+		goto st5;
+	goto st4;
+st5:
+	if ( ++p == pe )
+		goto _test_eof5;
+case 5:
+	switch( (*p) ) {
+		case 42: goto st5;
+		case 47: goto tr8;
+	}
+	goto st4;
+st91:
+	if ( ++p == pe )
+		goto _test_eof91;
+case 91:
+	if ( (*p) == 10 )
+		goto tr131;
+	goto st91;
+tr131:
+#line 74 "evalStringToScalarScanner.rl"
+	{
+        // Inform driver of last position
+        driver.parsePosition() = (p-buf);
+    }
+	goto st92;
+st92:
+	if ( ++p == pe )
+		goto _test_eof92;
+case 92:
+#line 555 "evalStringToScalarScanner.cc"
+	if ( (*p) == 10 )
+		goto tr131;
+	goto tr130;
+tr113:
+#line 1 "NONE"
+	{te = p+1;}
+	goto st93;
+st93:
+	if ( ++p == pe )
+		goto _test_eof93;
+case 93:
+#line 567 "evalStringToScalarScanner.cc"
+	switch( (*p) ) {
+		case 46: goto tr0;
+		case 69: goto st2;
+		case 101: goto st2;
+	}
+	if ( 48 <= (*p) && (*p) <= 57 )
+		goto tr113;
+	goto tr126;
+st6:
+	if ( ++p == pe )
+		goto _test_eof6;
+case 6:
+	switch( (*p) ) {
+		case 99: goto st7;
+		case 115: goto st10;
+		case 116: goto st13;
+	}
+	goto st0;
+st7:
+	if ( ++p == pe )
+		goto _test_eof7;
+case 7:
+	if ( (*p) == 111 )
+		goto st8;
+	goto st0;
+st8:
+	if ( ++p == pe )
+		goto _test_eof8;
+case 8:
+	if ( (*p) == 115 )
+		goto st9;
+	goto st0;
+st9:
+	if ( ++p == pe )
+		goto _test_eof9;
+case 9:
+	switch( (*p) ) {
+		case 32: goto st9;
+		case 40: goto tr14;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st9;
+	goto st0;
+st10:
+	if ( ++p == pe )
+		goto _test_eof10;
+case 10:
+	if ( (*p) == 105 )
+		goto st11;
+	goto st0;
+st11:
+	if ( ++p == pe )
+		goto _test_eof11;
+case 11:
+	if ( (*p) == 110 )
+		goto st12;
+	goto st0;
+st12:
+	if ( ++p == pe )
+		goto _test_eof12;
+case 12:
+	switch( (*p) ) {
+		case 32: goto st12;
+		case 40: goto tr17;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st12;
+	goto st0;
+st13:
+	if ( ++p == pe )
+		goto _test_eof13;
+case 13:
+	if ( (*p) == 97 )
+		goto st14;
+	goto st0;
+st14:
+	if ( ++p == pe )
+		goto _test_eof14;
+case 14:
+	if ( (*p) == 110 )
+		goto st15;
+	goto st0;
+st15:
+	if ( ++p == pe )
+		goto _test_eof15;
+case 15:
+	switch( (*p) ) {
+		case 32: goto st16;
+		case 40: goto tr21;
+		case 50: goto st17;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st16;
+	goto st0;
+st16:
+	if ( ++p == pe )
+		goto _test_eof16;
+case 16:
+	switch( (*p) ) {
+		case 32: goto st16;
+		case 40: goto tr21;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st16;
+	goto st0;
+st17:
+	if ( ++p == pe )
+		goto _test_eof17;
+case 17:
+	switch( (*p) ) {
+		case 32: goto st17;
+		case 40: goto tr23;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st17;
+	goto st0;
+st18:
+	if ( ++p == pe )
+		goto _test_eof18;
+case 18:
+	switch( (*p) ) {
+		case 98: goto st19;
+		case 111: goto st22;
+	}
+	goto st0;
+st19:
+	if ( ++p == pe )
+		goto _test_eof19;
+case 19:
+	if ( (*p) == 114 )
+		goto st20;
+	goto st0;
+st20:
+	if ( ++p == pe )
+		goto _test_eof20;
+case 20:
+	if ( (*p) == 116 )
+		goto st21;
+	goto st0;
+st21:
+	if ( ++p == pe )
+		goto _test_eof21;
+case 21:
+	switch( (*p) ) {
+		case 32: goto st21;
+		case 40: goto tr28;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st21;
+	goto st0;
+st22:
+	if ( ++p == pe )
+		goto _test_eof22;
+case 22:
+	if ( (*p) == 115 )
+		goto st23;
+	goto st0;
+st23:
+	if ( ++p == pe )
+		goto _test_eof23;
+case 23:
+	switch( (*p) ) {
+		case 32: goto st24;
+		case 40: goto tr31;
+		case 104: goto st25;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st24;
+	goto st0;
+st24:
+	if ( ++p == pe )
+		goto _test_eof24;
+case 24:
+	switch( (*p) ) {
+		case 32: goto st24;
+		case 40: goto tr31;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st24;
+	goto st0;
+st25:
+	if ( ++p == pe )
+		goto _test_eof25;
+case 25:
+	switch( (*p) ) {
+		case 32: goto st25;
+		case 40: goto tr33;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st25;
+	goto st0;
+st26:
+	if ( ++p == pe )
+		goto _test_eof26;
+case 26:
+	if ( (*p) == 101 )
+		goto st27;
+	goto st0;
+st27:
+	if ( ++p == pe )
+		goto _test_eof27;
+case 27:
+	if ( (*p) == 103 )
+		goto st28;
+	goto st0;
+st28:
+	if ( ++p == pe )
+		goto _test_eof28;
+case 28:
+	if ( (*p) == 84 )
+		goto st29;
+	goto st0;
+st29:
+	if ( ++p == pe )
+		goto _test_eof29;
+case 29:
+	if ( (*p) == 111 )
+		goto st30;
+	goto st0;
+st30:
+	if ( ++p == pe )
+		goto _test_eof30;
+case 30:
+	if ( (*p) == 82 )
+		goto st31;
+	goto st0;
+st31:
+	if ( ++p == pe )
+		goto _test_eof31;
+case 31:
+	if ( (*p) == 97 )
+		goto st32;
+	goto st0;
+st32:
+	if ( ++p == pe )
+		goto _test_eof32;
+case 32:
+	if ( (*p) == 100 )
+		goto st33;
+	goto st0;
+st33:
+	if ( ++p == pe )
+		goto _test_eof33;
+case 33:
+	switch( (*p) ) {
+		case 32: goto st33;
+		case 40: goto tr41;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st33;
+	goto st0;
+st34:
+	if ( ++p == pe )
+		goto _test_eof34;
+case 34:
+	if ( (*p) == 120 )
+		goto st35;
+	goto st0;
+st35:
+	if ( ++p == pe )
+		goto _test_eof35;
+case 35:
+	if ( (*p) == 112 )
+		goto st36;
+	goto st0;
+st36:
+	if ( ++p == pe )
+		goto _test_eof36;
+case 36:
+	switch( (*p) ) {
+		case 32: goto st36;
+		case 40: goto tr44;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st36;
+	goto st0;
+st37:
+	if ( ++p == pe )
+		goto _test_eof37;
+case 37:
+	if ( (*p) == 121 )
+		goto st38;
+	goto st0;
+st38:
+	if ( ++p == pe )
+		goto _test_eof38;
+case 38:
+	if ( (*p) == 112 )
+		goto st39;
+	goto st0;
+st39:
+	if ( ++p == pe )
+		goto _test_eof39;
+case 39:
+	if ( (*p) == 111 )
+		goto st40;
+	goto st0;
+st40:
+	if ( ++p == pe )
+		goto _test_eof40;
+case 40:
+	if ( (*p) == 116 )
+		goto st41;
+	goto st0;
+st41:
+	if ( ++p == pe )
+		goto _test_eof41;
+case 41:
+	switch( (*p) ) {
+		case 32: goto st41;
+		case 40: goto tr49;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st41;
+	goto st0;
+st42:
+	if ( ++p == pe )
+		goto _test_eof42;
+case 42:
+	if ( (*p) == 111 )
+		goto st43;
+	goto st0;
+st43:
+	if ( ++p == pe )
+		goto _test_eof43;
+case 43:
+	if ( (*p) == 103 )
+		goto st44;
+	goto st0;
+st44:
+	if ( ++p == pe )
+		goto _test_eof44;
+case 44:
+	switch( (*p) ) {
+		case 32: goto st45;
+		case 40: goto tr53;
+		case 49: goto st46;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st45;
+	goto st0;
+st45:
+	if ( ++p == pe )
+		goto _test_eof45;
+case 45:
+	switch( (*p) ) {
+		case 32: goto st45;
+		case 40: goto tr53;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st45;
+	goto st0;
+st46:
+	if ( ++p == pe )
+		goto _test_eof46;
+case 46:
+	if ( (*p) == 48 )
+		goto st47;
+	goto st0;
+st47:
+	if ( ++p == pe )
+		goto _test_eof47;
+case 47:
+	switch( (*p) ) {
+		case 32: goto st47;
+		case 40: goto tr56;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st47;
+	goto st0;
+st48:
+	if ( ++p == pe )
+		goto _test_eof48;
+case 48:
+	switch( (*p) ) {
+		case 97: goto st49;
+		case 105: goto st56;
+	}
+	goto st0;
+st49:
+	if ( ++p == pe )
+		goto _test_eof49;
+case 49:
+	switch( (*p) ) {
+		case 103: goto st50;
+		case 120: goto st55;
+	}
+	goto st0;
+st50:
+	if ( ++p == pe )
+		goto _test_eof50;
+case 50:
+	switch( (*p) ) {
+		case 32: goto st51;
+		case 40: goto tr62;
+		case 83: goto st52;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st51;
+	goto st0;
+st51:
+	if ( ++p == pe )
+		goto _test_eof51;
+case 51:
+	switch( (*p) ) {
+		case 32: goto st51;
+		case 40: goto tr62;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st51;
+	goto st0;
+st52:
+	if ( ++p == pe )
+		goto _test_eof52;
+case 52:
+	if ( (*p) == 113 )
+		goto st53;
+	goto st0;
+st53:
+	if ( ++p == pe )
+		goto _test_eof53;
+case 53:
+	if ( (*p) == 114 )
+		goto st54;
+	goto st0;
+st54:
+	if ( ++p == pe )
+		goto _test_eof54;
+case 54:
+	switch( (*p) ) {
+		case 32: goto st54;
+		case 40: goto tr66;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st54;
+	goto st0;
+st55:
+	if ( ++p == pe )
+		goto _test_eof55;
+case 55:
+	switch( (*p) ) {
+		case 32: goto st55;
+		case 40: goto tr67;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st55;
+	goto st0;
+st56:
+	if ( ++p == pe )
+		goto _test_eof56;
+case 56:
+	if ( (*p) == 110 )
+		goto st57;
+	goto st0;
+st57:
+	if ( ++p == pe )
+		goto _test_eof57;
+case 57:
+	switch( (*p) ) {
+		case 32: goto st57;
+		case 40: goto tr69;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st57;
+	goto st0;
+st58:
+	if ( ++p == pe )
+		goto _test_eof58;
+case 58:
+	switch( (*p) ) {
+		case 105: goto st59;
+		case 111: goto st60;
+	}
+	goto st0;
+st59:
+	if ( ++p == pe )
+		goto _test_eof59;
+case 59:
+	switch( (*p) ) {
+		case 32: goto st59;
+		case 40: goto tr72;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st59;
+	goto st0;
+st60:
+	if ( ++p == pe )
+		goto _test_eof60;
+case 60:
+	if ( (*p) == 119 )
+		goto st61;
+	goto st0;
+st61:
+	if ( ++p == pe )
+		goto _test_eof61;
+case 61:
+	switch( (*p) ) {
+		case 32: goto st61;
+		case 40: goto tr74;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st61;
+	goto st0;
+st62:
+	if ( ++p == pe )
+		goto _test_eof62;
+case 62:
+	if ( (*p) == 97 )
+		goto st63;
+	goto st0;
+st63:
+	if ( ++p == pe )
+		goto _test_eof63;
+case 63:
+	switch( (*p) ) {
+		case 100: goto st64;
+		case 110: goto st70;
+	}
+	goto st0;
+st64:
+	if ( ++p == pe )
+		goto _test_eof64;
+case 64:
+	if ( (*p) == 84 )
+		goto st65;
+	goto st0;
+st65:
+	if ( ++p == pe )
+		goto _test_eof65;
+case 65:
+	if ( (*p) == 111 )
+		goto st66;
+	goto st0;
+st66:
+	if ( ++p == pe )
+		goto _test_eof66;
+case 66:
+	if ( (*p) == 68 )
+		goto st67;
+	goto st0;
+st67:
+	if ( ++p == pe )
+		goto _test_eof67;
+case 67:
+	if ( (*p) == 101 )
+		goto st68;
+	goto st0;
+st68:
+	if ( ++p == pe )
+		goto _test_eof68;
+case 68:
+	if ( (*p) == 103 )
+		goto st69;
+	goto st0;
+st69:
+	if ( ++p == pe )
+		goto _test_eof69;
+case 69:
+	switch( (*p) ) {
+		case 32: goto st69;
+		case 40: goto tr83;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st69;
+	goto st0;
+st70:
+	if ( ++p == pe )
+		goto _test_eof70;
+case 70:
+	if ( (*p) == 100 )
+		goto st71;
+	goto st0;
+st71:
+	if ( ++p == pe )
+		goto _test_eof71;
+case 71:
+	switch( (*p) ) {
+		case 32: goto st71;
+		case 40: goto tr85;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st71;
+	goto st0;
+st72:
+	if ( ++p == pe )
+		goto _test_eof72;
+case 72:
+	switch( (*p) ) {
+		case 105: goto st73;
+		case 113: goto st77;
+	}
+	goto st0;
+st73:
+	if ( ++p == pe )
+		goto _test_eof73;
+case 73:
+	if ( (*p) == 110 )
+		goto st74;
+	goto st0;
+st74:
+	if ( ++p == pe )
+		goto _test_eof74;
+case 74:
+	switch( (*p) ) {
+		case 32: goto st75;
+		case 40: goto tr90;
+		case 104: goto st76;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st75;
+	goto st0;
+st75:
+	if ( ++p == pe )
+		goto _test_eof75;
+case 75:
+	switch( (*p) ) {
+		case 32: goto st75;
+		case 40: goto tr90;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st75;
+	goto st0;
+st76:
+	if ( ++p == pe )
+		goto _test_eof76;
+case 76:
+	switch( (*p) ) {
+		case 32: goto st76;
+		case 40: goto tr92;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st76;
+	goto st0;
+st77:
+	if ( ++p == pe )
+		goto _test_eof77;
+case 77:
+	if ( (*p) == 114 )
+		goto st78;
+	goto st0;
+st78:
+	if ( ++p == pe )
+		goto _test_eof78;
+case 78:
+	switch( (*p) ) {
+		case 32: goto st79;
+		case 40: goto tr95;
+		case 116: goto st80;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st79;
+	goto st0;
+st79:
+	if ( ++p == pe )
+		goto _test_eof79;
+case 79:
+	switch( (*p) ) {
+		case 32: goto st79;
+		case 40: goto tr95;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st79;
+	goto st0;
+st80:
+	if ( ++p == pe )
+		goto _test_eof80;
+case 80:
+	switch( (*p) ) {
+		case 32: goto st80;
+		case 40: goto tr97;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st80;
+	goto st0;
+st81:
+	if ( ++p == pe )
+		goto _test_eof81;
+case 81:
+	if ( (*p) == 97 )
+		goto st82;
+	goto st0;
+st82:
+	if ( ++p == pe )
+		goto _test_eof82;
+case 82:
+	if ( (*p) == 110 )
+		goto st83;
+	goto st0;
+st83:
+	if ( ++p == pe )
+		goto _test_eof83;
+case 83:
+	switch( (*p) ) {
+		case 32: goto st84;
+		case 40: goto tr101;
+		case 104: goto st85;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st84;
+	goto st0;
+st84:
+	if ( ++p == pe )
+		goto _test_eof84;
+case 84:
+	switch( (*p) ) {
+		case 32: goto st84;
+		case 40: goto tr101;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st84;
+	goto st0;
+st85:
+	if ( ++p == pe )
+		goto _test_eof85;
+case 85:
+	switch( (*p) ) {
+		case 32: goto st85;
+		case 40: goto tr103;
+	}
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st85;
+	goto st0;
+	}
+	_test_eof86: cs = 86; goto _test_eof; 
+	_test_eof87: cs = 87; goto _test_eof; 
+	_test_eof1: cs = 1; goto _test_eof; 
+	_test_eof88: cs = 88; goto _test_eof; 
+	_test_eof2: cs = 2; goto _test_eof; 
+	_test_eof3: cs = 3; goto _test_eof; 
+	_test_eof89: cs = 89; goto _test_eof; 
+	_test_eof90: cs = 90; goto _test_eof; 
+	_test_eof4: cs = 4; goto _test_eof; 
+	_test_eof5: cs = 5; goto _test_eof; 
+	_test_eof91: cs = 91; goto _test_eof; 
+	_test_eof92: cs = 92; goto _test_eof; 
+	_test_eof93: cs = 93; goto _test_eof; 
+	_test_eof6: cs = 6; goto _test_eof; 
+	_test_eof7: cs = 7; goto _test_eof; 
+	_test_eof8: cs = 8; goto _test_eof; 
+	_test_eof9: cs = 9; goto _test_eof; 
+	_test_eof10: cs = 10; goto _test_eof; 
+	_test_eof11: cs = 11; goto _test_eof; 
+	_test_eof12: cs = 12; goto _test_eof; 
+	_test_eof13: cs = 13; goto _test_eof; 
+	_test_eof14: cs = 14; goto _test_eof; 
+	_test_eof15: cs = 15; goto _test_eof; 
+	_test_eof16: cs = 16; goto _test_eof; 
+	_test_eof17: cs = 17; goto _test_eof; 
+	_test_eof18: cs = 18; goto _test_eof; 
+	_test_eof19: cs = 19; goto _test_eof; 
+	_test_eof20: cs = 20; goto _test_eof; 
+	_test_eof21: cs = 21; goto _test_eof; 
+	_test_eof22: cs = 22; goto _test_eof; 
+	_test_eof23: cs = 23; goto _test_eof; 
+	_test_eof24: cs = 24; goto _test_eof; 
+	_test_eof25: cs = 25; goto _test_eof; 
+	_test_eof26: cs = 26; goto _test_eof; 
+	_test_eof27: cs = 27; goto _test_eof; 
+	_test_eof28: cs = 28; goto _test_eof; 
+	_test_eof29: cs = 29; goto _test_eof; 
+	_test_eof30: cs = 30; goto _test_eof; 
+	_test_eof31: cs = 31; goto _test_eof; 
+	_test_eof32: cs = 32; goto _test_eof; 
+	_test_eof33: cs = 33; goto _test_eof; 
+	_test_eof34: cs = 34; goto _test_eof; 
+	_test_eof35: cs = 35; goto _test_eof; 
+	_test_eof36: cs = 36; goto _test_eof; 
+	_test_eof37: cs = 37; goto _test_eof; 
+	_test_eof38: cs = 38; goto _test_eof; 
+	_test_eof39: cs = 39; goto _test_eof; 
+	_test_eof40: cs = 40; goto _test_eof; 
+	_test_eof41: cs = 41; goto _test_eof; 
+	_test_eof42: cs = 42; goto _test_eof; 
+	_test_eof43: cs = 43; goto _test_eof; 
+	_test_eof44: cs = 44; goto _test_eof; 
+	_test_eof45: cs = 45; goto _test_eof; 
+	_test_eof46: cs = 46; goto _test_eof; 
+	_test_eof47: cs = 47; goto _test_eof; 
+	_test_eof48: cs = 48; goto _test_eof; 
+	_test_eof49: cs = 49; goto _test_eof; 
+	_test_eof50: cs = 50; goto _test_eof; 
+	_test_eof51: cs = 51; goto _test_eof; 
+	_test_eof52: cs = 52; goto _test_eof; 
+	_test_eof53: cs = 53; goto _test_eof; 
+	_test_eof54: cs = 54; goto _test_eof; 
+	_test_eof55: cs = 55; goto _test_eof; 
+	_test_eof56: cs = 56; goto _test_eof; 
+	_test_eof57: cs = 57; goto _test_eof; 
+	_test_eof58: cs = 58; goto _test_eof; 
+	_test_eof59: cs = 59; goto _test_eof; 
+	_test_eof60: cs = 60; goto _test_eof; 
+	_test_eof61: cs = 61; goto _test_eof; 
+	_test_eof62: cs = 62; goto _test_eof; 
+	_test_eof63: cs = 63; goto _test_eof; 
+	_test_eof64: cs = 64; goto _test_eof; 
+	_test_eof65: cs = 65; goto _test_eof; 
+	_test_eof66: cs = 66; goto _test_eof; 
+	_test_eof67: cs = 67; goto _test_eof; 
+	_test_eof68: cs = 68; goto _test_eof; 
+	_test_eof69: cs = 69; goto _test_eof; 
+	_test_eof70: cs = 70; goto _test_eof; 
+	_test_eof71: cs = 71; goto _test_eof; 
+	_test_eof72: cs = 72; goto _test_eof; 
+	_test_eof73: cs = 73; goto _test_eof; 
+	_test_eof74: cs = 74; goto _test_eof; 
+	_test_eof75: cs = 75; goto _test_eof; 
+	_test_eof76: cs = 76; goto _test_eof; 
+	_test_eof77: cs = 77; goto _test_eof; 
+	_test_eof78: cs = 78; goto _test_eof; 
+	_test_eof79: cs = 79; goto _test_eof; 
+	_test_eof80: cs = 80; goto _test_eof; 
+	_test_eof81: cs = 81; goto _test_eof; 
+	_test_eof82: cs = 82; goto _test_eof; 
+	_test_eof83: cs = 83; goto _test_eof; 
+	_test_eof84: cs = 84; goto _test_eof; 
+	_test_eof85: cs = 85; goto _test_eof; 
+
+	_test_eof: {}
+	if ( p == eof )
+	{
+	switch ( cs ) {
+	case 87: goto tr125;
+	case 88: goto tr126;
+	case 2: goto tr2;
+	case 3: goto tr2;
+	case 89: goto tr126;
+	case 90: goto tr128;
+	case 4: goto tr5;
+	case 5: goto tr5;
+	case 91: goto tr130;
+	case 92: goto tr130;
+	case 93: goto tr126;
+	}
+	}
+
+	_out: {}
+	}
+
+#line 209 "evalStringToScalarScanner.rl"
+  /* ^^^ FSM execution here ^^^ */;
+
+    if (0 == 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;
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarScanner.rl b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarScanner.rl
new file mode 100644
index 00000000000..5f540dee34b
--- /dev/null
+++ b/src/OpenFOAM/primitives/strings/stringOps/toScalar/evalStringToScalarScanner.rl
@@ -0,0 +1,229 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+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"
+
+#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+
+#ifndef FULLDEBUG
+#define NDEBUG
+#endif
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+int Foam::parsing::evalStringToScalar::scanner::debug = 0;
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+// 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;
+}%%
+
+#define TOKEN_OF(T)         TOK_##T
+#define EMIT_TOKEN(T)                                                         \
+    /* Inform driver of last position */                                      \
+    driver.parsePosition() = (p-buf);                                         \
+    DebugInfo<< "TOKEN_" #T << " at " << driver.parsePosition() << nl;        \
+    parser_->parse(TOKEN_OF(T), 0)
+
+
+%%{
+    machine evalScanner;
+
+    action setPosition
+    {
+        // Inform driver of last position
+        driver.parsePosition() = (p-buf);
+    }
+
+    action truncated
+    {
+        // Inform driver of last position
+        driver.parsePosition() = 0;
+        driver.reportFatal("Truncated input");
+    }
+
+    action emit_number {
+        // Inform driver of last position
+        driver.parsePosition() = (p-buf);
+
+        DebugInfo
+            << "NUMBER:" << std::string(ts, te-ts).c_str()
+            << " at " << driver.parsePosition() << nl;
+
+        scalar val;
+
+        if (readScalar(std::string(ts, te-ts), val))
+        {
+            // Emit number
+            parser_->parse(TOKEN_OF(NUMBER), val);
+        }
+        else
+        {
+            driver.reportFatal("Error reading scalar value");
+        }
+    }
+
+    decimal = ((digit* '.' digit+) | (digit+ '.'?)) ;
+    number  = (digit+ | decimal) ([Ee][\-+]? digit+)? ;
+    dnl     = (any* -- '\n') '\n';  # Discard up to and including newline
+    lfunc   = space* '(';           # Require functions to have '('
+
+    functions = (
+        'pi'         lfunc  @{ fhold; EMIT_TOKEN(PI); }
+      | 'degToRad'   lfunc  @{ fhold; EMIT_TOKEN(DEG_TO_RAD); }
+      | 'radToDeg'   lfunc  @{ fhold; EMIT_TOKEN(RAD_TO_DEG); }
+      | 'exp'        lfunc  @{ fhold; EMIT_TOKEN(EXP); }
+      | 'log'        lfunc  @{ fhold; EMIT_TOKEN(LOG); }
+      | 'log10'      lfunc  @{ fhold; EMIT_TOKEN(LOG10); }
+      | 'pow'        lfunc  @{ fhold; EMIT_TOKEN(POW); }
+      | 'sqr'        lfunc  @{ fhold; EMIT_TOKEN(SQR); }
+      | 'sqrt'       lfunc  @{ fhold; EMIT_TOKEN(SQRT); }
+      | 'cbrt'       lfunc  @{ fhold; EMIT_TOKEN(CBRT); }
+      | 'sin'        lfunc  @{ fhold; EMIT_TOKEN(SIN); }
+      | 'cos'        lfunc  @{ fhold; EMIT_TOKEN(COS); }
+      | 'tan'        lfunc  @{ fhold; EMIT_TOKEN(TAN); }
+      | 'asin'       lfunc  @{ fhold; EMIT_TOKEN(ASIN); }
+      | 'acos'       lfunc  @{ fhold; EMIT_TOKEN(ACOS); }
+      | 'atan'       lfunc  @{ fhold; EMIT_TOKEN(ATAN); }
+      | 'atan2'      lfunc  @{ fhold; EMIT_TOKEN(ATAN2); }
+      | 'hypot'      lfunc  @{ fhold; EMIT_TOKEN(HYPOT); }
+      | 'sinh'       lfunc  @{ fhold; EMIT_TOKEN(SINH); }
+      | 'cosh'       lfunc  @{ fhold; EMIT_TOKEN(COSH); }
+      | 'tanh'       lfunc  @{ fhold; EMIT_TOKEN(TANH); }
+      | 'min'        lfunc  @{ fhold; EMIT_TOKEN(MIN); }
+      | 'max'        lfunc  @{ fhold; EMIT_TOKEN(MAX); }
+      | 'mag'        lfunc  @{ fhold; EMIT_TOKEN(MAG); }
+      | 'magSqr'     lfunc  @{ fhold; EMIT_TOKEN(MAGSQR); }
+      | 'rand'       lfunc  @{ fhold; EMIT_TOKEN(RAND); }
+    );
+
+    operators = (
+        '('  @{ EMIT_TOKEN(LPAREN); }
+      | ')'  @{ EMIT_TOKEN(RPAREN); }
+      | '+'  @{ EMIT_TOKEN(PLUS); }
+      | '-'  @{ EMIT_TOKEN(MINUS); }
+      | '*'  @{ EMIT_TOKEN(TIMES); }
+      | '/'  @{ EMIT_TOKEN(DIVIDE); }
+      | ','  @{ EMIT_TOKEN(COMMA); }
+    );
+
+
+    main := |*
+        space*;
+
+        number => emit_number;
+        functions;
+        operators;
+
+        '/*' any* :>> '*/' @setPosition;        # Multi-line comment
+        '//' (any* -- '\n') '\n'* @setPosition; # (sloppy) 1-line comment
+        space*;
+    *|;
+}%%
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::parsing::evalStringToScalar::scanner::~scanner()
+{
+    if (parser_)
+    {
+        delete parser_;
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+bool Foam::parsing::evalStringToScalar::scanner::process
+(
+    const std::string& str,
+    parseDriver& driver
+)
+{
+    if (!parser_)
+    {
+        parser_ = new parser();
+    }
+
+    driver.content(str);
+    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[0]);
+    const char* eof = &(str[str.length()]);
+    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;
+}
+
+
+// ************************************************************************* //
-- 
GitLab