diff --git a/applications/utilities/preProcessing/setExprBoundaryFields/Make/files b/applications/utilities/preProcessing/setExprBoundaryFields/Make/files
new file mode 100644
index 0000000000000000000000000000000000000000..c20222837bd1d4fb54a82ed4f4af6e6d5b3bd61e
--- /dev/null
+++ b/applications/utilities/preProcessing/setExprBoundaryFields/Make/files
@@ -0,0 +1,3 @@
+setExprBoundaryFields.C
+
+EXE = $(FOAM_APPBIN)/setExprBoundaryFields
diff --git a/applications/utilities/preProcessing/setExprBoundaryFields/Make/options b/applications/utilities/preProcessing/setExprBoundaryFields/Make/options
new file mode 100644
index 0000000000000000000000000000000000000000..969020c4afaf5d784299462b9e1af282040ba6b4
--- /dev/null
+++ b/applications/utilities/preProcessing/setExprBoundaryFields/Make/options
@@ -0,0 +1,8 @@
+EXE_INC = \
+    -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude
+
+EXE_LIBS = \
+    -lfiniteVolume \
+    -lmeshTools \
+    -lgenericPatchFields
diff --git a/applications/utilities/preProcessing/setExprBoundaryFields/setExprBoundaryFields.C b/applications/utilities/preProcessing/setExprBoundaryFields/setExprBoundaryFields.C
new file mode 100644
index 0000000000000000000000000000000000000000..179361cdc36a2b2b726ac778778bc47011b34b6d
--- /dev/null
+++ b/applications/utilities/preProcessing/setExprBoundaryFields/setExprBoundaryFields.C
@@ -0,0 +1,262 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+Application
+    setExprFields
+
+Group
+    grpPreProcessingUtilities
+
+Description
+    Set boundary values using an expression
+
+Note
+    Based on funkySetBoundaryFields
+    Copyright 2006-2018 Bernhard Gschaider <bgschaid@hfd-research.com>
+
+\*---------------------------------------------------------------------------*/
+
+#include "argList.H"
+#include "Time.H"
+#include "fvMesh.H"
+#include "pointMesh.H"
+#include "volFields.H"
+#include "surfaceFields.H"
+#include "surfaceFields.H"
+#include "pointFields.H"
+#include "patchExprDriver.H"
+#include "timeSelector.H"
+
+using namespace Foam;
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+// Main program:
+
+int main(int argc, char *argv[])
+{
+    argList::noFunctionObjects(true);
+
+    // No -constant, no special treatment for 0/
+    timeSelector::addOptions(false);
+
+    argList::addOption
+    (
+        "dict",
+        "file",
+        "Alternative dictionary for setExprBoundaryFieldsDict"
+    );
+
+    argList::addBoolOption
+    (
+        "cache-fields",
+        "Cache fields between calls",
+        true // Advanced
+    );
+    argList::addBoolOption
+    (
+        "backup",
+        "Preserve sub-entry as .backup",
+        true // Advanced
+    );
+
+    argList::addBoolOption
+    (
+        "dry-run",
+        "Evaluate but do not write"
+    );
+
+    #include "addRegionOption.H"
+    #include "setRootCase.H"
+
+    const bool dryrun      = args.found("dry-run");
+    const bool backup      = args.found("backup");
+    const bool cacheFields = args.found("cache-fields");
+
+    if (cacheFields)
+    {
+        Warning
+            << "The current cache-fields behaviour (caching disk reads) "
+            << "may lead to unexpected behaviour as previous modifications "
+            << "will not be visible."
+            << endl;
+    }
+
+    const word dictName("setExprBoundaryFieldsDict");
+
+    #include "createTime.H"
+
+    instantList times = timeSelector::select0(runTime, args);
+
+    if (times.size() < 1)
+    {
+        FatalErrorInFunction
+            << "No times selected." << exit(FatalError);
+    }
+
+    #include "createNamedMesh.H"
+
+    #include "setSystemMeshDictionaryIO.H"
+    IOdictionary setExprDict(dictIO);
+
+    forAll(times, timei)
+    {
+        runTime.setTime(times[timei], timei);
+
+        Info<< "\nTime = " << runTime.timeName() << endl;
+
+        mesh.readUpdate();
+
+        for (const entry& dEntry : setExprDict)
+        {
+            if (!dEntry.isDict())
+            {
+                Info<< "Ignoring non-dictionary entry "
+                    << dEntry.keyword() << nl;
+                continue;
+            }
+
+            const dictionary& dict = dEntry.dict();
+
+            const word fieldName(dict.get<word>("field"));
+
+            List<dictionary> exprDicts;
+            dict.readEntry("expressions", exprDicts);
+
+            if (exprDicts.empty())
+            {
+                Info<< "No expressions for " << fieldName << nl;
+                continue;
+            }
+
+
+            // Read dictionary
+            // Note: disable class type checking so we can load field
+            const word oldTypeName = IOdictionary::typeName;
+            const_cast<word&>(IOdictionary::typeName) = word::null;
+
+            IOobject fieldHeader
+            (
+                fieldName,
+                mesh.thisDb().time().timeName(),
+                mesh.thisDb(),
+                IOobject::MUST_READ_IF_MODIFIED,
+                IOobject::NO_WRITE,
+                false
+            );
+
+            const bool headOk = fieldHeader.typeHeaderOk<IOdictionary>(false);
+
+            if (!headOk)
+            {
+                // Restore type
+                const_cast<word&>(IOdictionary::typeName) = oldTypeName;
+
+                WarningInFunction
+                    << "Requested field to change " << fieldName
+                    << " does not exist in " << fieldHeader.path() << endl;
+                continue;
+            }
+
+            IOdictionary fieldDict(fieldHeader);
+
+            // Restore type
+            const_cast<word&>(IOdictionary::typeName) = oldTypeName;
+
+            // Fake type back to what was in field
+            const_cast<word&>(fieldDict.type()) = fieldDict.headerClassName();
+
+            Info<< "Processing field " << fieldName << nl;
+
+            dictionary& boundaryFieldDict = fieldDict.subDict("boundaryField");
+
+            for (const dictionary& currDict : exprDicts)
+            {
+                const word targetName = currDict.get<word>("target");
+                const word patchName = currDict.get<word>("patch");
+
+                dictionary& patchDict = boundaryFieldDict.subDict(patchName);
+
+                expressions::exprString expr
+                (
+                    currDict.get<string>("expression"),
+                    currDict,
+                    true  // strip comments
+                );
+
+                Info<< "Set boundaryField/" << patchName << '/'
+                    << targetName << nl
+                    << "with expression" << nl
+                    << "<<<<" << nl
+                    << expr.c_str() << nl
+                    << ">>>>" << nl;
+
+                expressions::patchExprDriver driver(currDict, mesh);
+
+                // Search on disc
+                driver.setSearchBehaviour(cacheFields, false, true);
+
+                driver.clearVariables();
+                driver.parse(expr);
+
+                // Serializing via Field::writeEntry etc
+                OStringStream serialize;
+                driver.result().writeEntry("", serialize);
+
+                if (backup && !dryrun)
+                {
+                    patchDict.changeKeyword
+                    (
+                        targetName,
+                        word(targetName + ".backup"),
+                        true  // Overwrite
+                    );
+                }
+
+                patchDict.set(targetName, serialize.str().c_str());
+
+                if (dryrun)
+                {
+                    Info<< "Evaluated:" << nl
+                        << "<<<<" << nl
+                        << serialize.str().c_str()  // (already includes nl)
+                        << ">>>>" << nl;
+                }
+            }
+
+            if (!dryrun)
+            {
+                Info<< "Write " << fieldDict.filePath() << nl;
+                fieldDict.regIOobject::write();
+            }
+        }
+    }
+
+    Info<< "\nEnd\n" << endl;
+
+    return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/setExprBoundaryFields/setExprBoundaryFieldsDict b/applications/utilities/preProcessing/setExprBoundaryFields/setExprBoundaryFieldsDict
new file mode 100644
index 0000000000000000000000000000000000000000..53724eb90e7baf9a543693485b41561050986dd1
--- /dev/null
+++ b/applications/utilities/preProcessing/setExprBoundaryFields/setExprBoundaryFieldsDict
@@ -0,0 +1,34 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      setExprBoundaryFieldsDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+pattern
+{
+    field   T;
+
+    expressions
+    (
+        {
+            patch   bottom;
+            target  theta0;
+            expression #{ (pos().x() < 1e-4 ? 60 : 120) #};
+        }
+    );
+
+    keepPatches  true;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/setExprFields/Make/files b/applications/utilities/preProcessing/setExprFields/Make/files
new file mode 100644
index 0000000000000000000000000000000000000000..3212112fdc50ac8119e431b38483d06a64182064
--- /dev/null
+++ b/applications/utilities/preProcessing/setExprFields/Make/files
@@ -0,0 +1,3 @@
+setExprFields.C
+
+EXE = $(FOAM_APPBIN)/setExprFields
diff --git a/applications/utilities/preProcessing/setExprFields/Make/options b/applications/utilities/preProcessing/setExprFields/Make/options
new file mode 100644
index 0000000000000000000000000000000000000000..969020c4afaf5d784299462b9e1af282040ba6b4
--- /dev/null
+++ b/applications/utilities/preProcessing/setExprFields/Make/options
@@ -0,0 +1,8 @@
+EXE_INC = \
+    -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude
+
+EXE_LIBS = \
+    -lfiniteVolume \
+    -lmeshTools \
+    -lgenericPatchFields
diff --git a/applications/utilities/preProcessing/setExprFields/setExprFields.C b/applications/utilities/preProcessing/setExprFields/setExprFields.C
new file mode 100644
index 0000000000000000000000000000000000000000..21a609124c86503fcd00029462a02d1e27497e7e
--- /dev/null
+++ b/applications/utilities/preProcessing/setExprFields/setExprFields.C
@@ -0,0 +1,915 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+Application
+    setExprFields
+
+Group
+    grpPreProcessingUtilities
+
+Description
+    Set values on a selected set of cells/patch-faces via a dictionary.
+
+Note
+    Based on funkySetFields
+    Copyright 2006-2018 Bernhard Gschaider <bgschaid@hfd-research.com>
+
+\*---------------------------------------------------------------------------*/
+
+#include "argList.H"
+#include "Time.H"
+#include "fvMesh.H"
+#include "pointMesh.H"
+#include "volFields.H"
+#include "surfaceFields.H"
+#include "surfaceFields.H"
+#include "pointFields.H"
+#include "exprOps.H"
+#include "volumeExprDriver.H"
+#include "timeSelector.H"
+#include "dlLibraryTable.H"
+
+
+using namespace Foam;
+
+using FieldAssociation = expressions::volumeExpr::FieldAssociation;
+
+word fieldGeoType(const FieldAssociation geoType)
+{
+    switch (geoType)
+    {
+        case FieldAssociation::VOLUME_DATA : return "cells"; break;
+        case FieldAssociation::SURFACE_DATA : return "faces"; break;
+        case FieldAssociation::POINT_DATA : return "points"; break;
+        default: break;
+    }
+
+    return "unknown";
+}
+
+
+//- Simple control structure to with collected switches to simplify passing
+struct setExprFieldsControl
+{
+    bool dryRun;
+    bool debugParsing;
+    bool cacheVariables;
+    bool useDimension;
+    bool createNew;
+    bool keepPatches;
+    bool correctPatches;
+    bool correctBCs;
+};
+
+
+template<class Type>
+void doCorrectBoundaryConditions
+(
+    bool correctBCs,
+    GeometricField<Type, fvPatchField, volMesh>& field
+)
+{
+    if (correctBCs)
+    {
+        Info<< "Correcting boundary conditions: " << field.name() << nl;
+        field.correctBoundaryConditions();
+    }
+}
+
+
+template<class Type>
+void doCorrectBoundaryConditions
+(
+    bool correctBCs,
+    GeometricField<Type, pointPatchField, pointMesh>& field
+)
+{
+    if (correctBCs)
+    {
+        Info<< "Correcting boundary conditions: " << field.name() << nl;
+        field.correctBoundaryConditions();
+    }
+}
+
+
+template<class Type>
+void doCorrectBoundaryConditions
+(
+    bool correctBCs,
+    GeometricField<Type, fvsPatchField, surfaceMesh>& field
+)
+{}
+
+
+template<class GeoField, class Mesh>
+void setField
+(
+    const word& fieldName,
+    const Mesh& mesh,
+    const GeoField& result,
+    const scalarField& cond,
+    const dimensionSet& dims,
+    const wordList& valuePatches,
+
+    const setExprFieldsControl& ctrl
+)
+{
+    Info<< "setField(" << fieldName << "): "
+        << pTraits<GeoField>::typeName << endl;
+
+    tmp<GeoField> toutput;
+
+    if (ctrl.createNew)
+    {
+        // Create with zero
+        toutput = GeoField::New
+        (
+            fieldName,
+            mesh,
+            dimensioned<typename GeoField::value_type>(dims)
+        );
+    }
+    else
+    {
+        // Read
+        toutput = tmp<GeoField>::New
+        (
+            IOobject
+            (
+                fieldName,
+                mesh.thisDb().time().timeName(),
+                mesh.thisDb(),
+                IOobject::MUST_READ,
+                IOobject::NO_WRITE,
+                false  // No register
+            ),
+            mesh
+        );
+    }
+
+    auto& output = toutput.ref();
+
+    label setCells = 0;
+
+    if (cond.empty())
+    {
+        // No condition
+        output = result;
+
+        setCells = output.size();
+    }
+    else
+    {
+        forAll(output, celli)
+        {
+            if (expressions::boolOp<scalar>()(cond[celli]))
+            {
+                output[celli] = result[celli];
+                ++setCells;
+            }
+        }
+    }
+
+    const label totalCells = returnReduce(output.size(), plusOp<label>());
+    reduce(setCells, plusOp<label>());
+
+    forAll(result.boundaryField(), patchi)
+    {
+        auto& pf = output.boundaryFieldRef()[patchi];
+
+        if (pf.patch().coupled())
+        {
+            pf == result.boundaryField()[patchi];
+        }
+    }
+
+
+    if (setCells == totalCells)
+    {
+        Info<< "Set all ";
+    }
+    else
+    {
+        Info<< "Set " << setCells << " of ";
+    }
+    Info<< totalCells << " cells" << endl;
+
+
+    doCorrectBoundaryConditions(ctrl.correctBCs, output);
+
+    if (ctrl.useDimension)
+    {
+        Info<< "Setting dimension to " << dims << endl;
+        output.dimensions().reset(dims);
+    }
+
+    if (ctrl.dryRun)
+    {
+        Info<< "(dry-run): Writing to " << output.name() << nl;
+    }
+    else
+    {
+        Info<< "Writing to " << output.name() << nl;
+        output.write();
+    }
+}
+
+
+void evaluate
+(
+    const fvMesh& mesh,
+    const word& fieldName,
+    const expressions::exprString& expression,
+    const expressions::exprString& condition,
+    const dictionary& dict,
+    const dimensionSet& dims,
+    const wordList& valuePatches,
+
+    const setExprFieldsControl& ctrl
+)
+{
+    word oldFieldType;
+
+    if (ctrl.createNew)
+    {
+        Info<< "Set new field: " << fieldName;
+    }
+    else
+    {
+        IOobject io
+        (
+            fieldName,
+            mesh.thisDb().time().timeName(),
+            mesh.thisDb(),
+            IOobject::MUST_READ,
+            IOobject::NO_WRITE
+        );
+        io.typeHeaderOk<IOobject>(false);
+
+        oldFieldType = io.headerClassName();
+
+        if (oldFieldType == IOobject::typeName)
+        {
+            FatalErrorInFunction
+                << "Field " << fieldName << " is  "
+                << oldFieldType
+                << ". Seems that it does not exist. Use 'create'"
+                << nl
+                << exit(FatalError);
+        }
+
+        Info<< "Modify field: " << fieldName
+            << " (type " << oldFieldType << ')';
+    }
+
+    Info<< " time=" << mesh.thisDb().time().timeName() << nl
+        << "Expression:" << nl
+        << ">>>>" << nl
+        << expression.c_str() << nl
+        << "<<<<" << nl
+        << "Condition:" << nl
+        << ">>>>" << nl
+        << condition.c_str() << nl
+        << "<<<<" << nl;
+
+    if (ctrl.keepPatches)
+    {
+        Info<< "Keeping patches unaltered" << endl;
+    }
+    else if (!valuePatches.empty())
+    {
+        Info<< "Setting patches " << flatOutput(valuePatches)
+            << " to fixed value" << endl;
+    }
+
+    Info<< endl;
+
+    expressions::volumeExprDriver driver
+    (
+        mesh,
+        ctrl.cacheVariables
+    );
+
+    driver.readDict(dict);
+
+    if (ctrl.debugParsing)
+    {
+        Info<< "Parsing expression: " << expression << "\nand condition "
+            << condition << nl << endl;
+        driver.setDebugging(true, true);
+    }
+
+
+    driver.clearVariables();
+
+    scalarField conditionField;
+
+    bool evaluatedCondition = false;
+
+    FieldAssociation conditionDataType(FieldAssociation::VOLUME_DATA);
+
+    if (condition.size() && condition != "true")
+    {
+        if (ctrl.debugParsing)
+        {
+            Info<< "Parsing condition:" << condition << endl;
+        }
+
+        driver.parse(condition);
+        if (ctrl.debugParsing)
+        {
+            Info<< "Parsed condition" << endl;
+        }
+
+        // Process any/all scalar fields. May help with diagnosis
+
+        bool goodCond = true;
+        while (goodCond)
+        {
+            // volScalarField
+            {
+                const auto* ptr = driver.isResultType<volScalarField>();
+                if (ptr)
+                {
+                    conditionField = ptr->internalField();
+                    break;
+                }
+            }
+
+            // surfaceScalarField
+            {
+                const auto* ptr = driver.isResultType<surfaceScalarField>();
+                if (ptr)
+                {
+                    conditionField = ptr->internalField();
+                    conditionDataType = FieldAssociation::SURFACE_DATA;
+                    break;
+                }
+            }
+
+            // pointScalarField
+            {
+                const auto* ptr = driver.isResultType<pointScalarField>();
+                if (ptr)
+                {
+                    conditionField = ptr->internalField();
+                    conditionDataType = FieldAssociation::POINT_DATA;
+                    break;
+                }
+            }
+
+            // No matching field types
+            goodCond = false;
+        }
+
+        // Verify that it also logical
+        goodCond = goodCond && driver.isLogical();
+
+        if (!goodCond)
+        {
+            FatalErrorInFunction
+                << " condition: " << condition
+                << " does not evaluate to a logical expression: "
+                << driver.resultType() << nl
+                #ifdef FULLDEBUG
+                << "contents: " << conditionField
+                #endif
+                << exit(FatalError);
+        }
+
+        if (ctrl.debugParsing)
+        {
+            Info<< "Condition evaluates to "
+                << conditionField << nl;
+        }
+
+        evaluatedCondition = true;
+    }
+
+    if (ctrl.debugParsing)
+    {
+        Info<< "Parsing expression:" << expression << endl;
+    }
+
+    driver.parse(expression);
+
+    if (ctrl.debugParsing)
+    {
+        Info<< "Parsed expression" << endl;
+    }
+
+    if (evaluatedCondition)
+    {
+        if (conditionDataType != driver.fieldAssociation())
+        {
+            FatalErrorInFunction
+                << "Mismatch between condition geometric type ("
+                << fieldGeoType(conditionDataType) << ") and" << nl
+                << "expression geometric type ("
+                << fieldGeoType(driver.fieldAssociation()) << ')' << nl
+                << nl
+                << "Expression: " << expression << nl
+                << "Condition: " << condition << nl
+                << nl
+                << exit(FatalError);
+        }
+    }
+
+    if (!ctrl.createNew && driver.resultType() != oldFieldType)
+    {
+        FatalErrorInFunction
+            << "Inconsistent types: " << fieldName << " is  "
+            << oldFieldType
+            << " but the expression evaluates to "
+            << driver.resultType()
+            << exit(FatalError);
+    }
+
+    Info<< "Dispatch ... " << driver.resultType() << nl;
+
+    #undef setFieldDispatch
+    #define setFieldDispatch(FieldType)                                       \
+    {                                                                         \
+        /* FieldType */                                                       \
+        const auto* ptr = driver.isResultType<FieldType>();                   \
+        if (ptr)                                                              \
+        {                                                                     \
+            /* driver.getResult<FieldType>(correctPatches), */                \
+                                                                              \
+            setField                                                          \
+            (                                                                 \
+                fieldName,                                                    \
+                mesh,                                                         \
+                *ptr,                                                         \
+                conditionField,                                               \
+                dims,                                                         \
+                valuePatches,                                                 \
+                ctrl                                                          \
+            );                                                                \
+            return;                                                           \
+        }                                                                     \
+    }                                                                         \
+
+
+    setFieldDispatch(volScalarField);
+    setFieldDispatch(volVectorField);
+    setFieldDispatch(volTensorField);
+    setFieldDispatch(volSymmTensorField);
+    setFieldDispatch(volSphericalTensorField);
+
+    setFieldDispatch(surfaceScalarField);
+    setFieldDispatch(surfaceVectorField);
+    setFieldDispatch(surfaceTensorField);
+    setFieldDispatch(surfaceSymmTensorField);
+    setFieldDispatch(surfaceSphericalTensorField);
+
+    #undef setFieldDispatch
+    #define setFieldDispatch(FieldType)                                       \
+    {                                                                         \
+        /* FieldType */                                                       \
+        const auto* ptr = driver.isResultType<FieldType>();                   \
+                                                                              \
+        if (ptr)                                                              \
+        {                                                                     \
+            /* driver.getResult<FieldType>(correctPatches), */                \
+                                                                              \
+            setField                                                          \
+            (                                                                 \
+                fieldName,                                                    \
+                pointMesh::New(mesh),                                         \
+                *ptr,                                                         \
+                conditionField,                                               \
+                dims,                                                         \
+                valuePatches,                                                 \
+                ctrl                                                          \
+            );                                                                \
+            return;                                                           \
+        }                                                                     \
+    }                                                                         \
+
+    setFieldDispatch(pointScalarField);
+    setFieldDispatch(pointVectorField);
+    setFieldDispatch(pointTensorField);
+    setFieldDispatch(pointSymmTensorField);
+    setFieldDispatch(pointSphericalTensorField);
+
+    #undef setFieldDispatch
+
+    // Nothing dispatched?
+
+    FatalErrorInFunction
+        << "Expression evaluates to an unsupported type: "
+        << driver.resultType() << nl << nl
+        << "Expression " << expression << nl << endl
+        << exit(FatalError);
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+int main(int argc, char *argv[])
+{
+    argList::noFunctionObjects(true);
+
+    // No -constant, no special treatment for 0/
+    timeSelector::addOptions(false);
+
+    argList::addOption
+    (
+        "dict",
+        "file",
+        "Alternative dictionary for setExprFieldsDict"
+    );
+
+    argList::addBoolOption
+    (
+        "dry-run",
+        "Evaluate but do not write"
+    );
+
+    argList::addBoolOption
+    (
+        "verbose",
+        "Additional verbosity",
+        true // Advanced option
+    );
+
+    argList::addOption
+    (
+        "field",
+        "name",
+        "The field to overwrite command-line operation)",
+        true // Advanced option
+    );
+    argList::addOption
+    (
+        "expression",
+        "expr",
+        "The expression to evaluate (command-line operation)",
+        true // Advanced option
+    );
+    argList::addOption
+    (
+        "condition",
+        "logic",
+        "The logical condition when to apply the expression"
+        " (command-line operation)",
+        true // Advanced option
+    );
+    argList::addOption
+    (
+        "dimension",
+        "dims",
+        "The dimensions to apply for created fields"
+        " (command-line operation)",
+        true // Advanced option
+    );
+    argList::addBoolOption
+    (
+        "debug-parser",
+        "Additional debugging information",
+        true // Advanced option
+    );
+    argList::addBoolOption
+    (
+        "no-variable-cache",
+        "Disable caching of expression variables",
+        true // Advanced option
+    );
+    argList::addBoolOption
+    (
+        "create",
+        "Create a new field (command-line operation)",
+        true // Advanced option
+    );
+    argList::addBoolOption
+    (
+        "keepPatches",
+        "Leave patches unaltered"
+        " (command-line operation)",
+        true // Advanced option
+    );
+    argList::addOption
+    (
+        "value-patches",
+        "(patches)",
+        "A list of patches that receive a fixed value"
+        " (command-line operation)",
+        true // Advanced option
+    );
+    argList::addBoolOption
+    (
+        "dummy-phi",
+        "(command-line operation)",
+        true // Advanced option
+    );
+
+    // Future?
+    #if 0
+    argList::addBoolOption
+    (
+        "noCorrectPatches",
+        ""
+    );
+    argList::addBoolOption
+    (
+        "correctResultBoundaryFields",
+        "",
+        true
+    );
+    #endif
+
+    #include "addRegionOption.H"
+    #include "setRootCase.H"
+
+    #include "createTime.H"
+
+    const bool dryrun = args.found("dry-run");
+    const bool verbose = args.found("verbose");
+
+    const word dictName("setExprFieldsDict");
+
+    instantList times = timeSelector::select0(runTime, args);
+
+    if (times.size() < 1)
+    {
+        FatalErrorInFunction
+            << "No times selected." << exit(FatalError);
+    }
+
+    // Disable dimension checking during operation
+    dimensionSet::debug = false;
+
+    #include "createNamedMesh.H"
+
+    autoPtr<surfaceScalarField> dummyPhi;
+
+    autoPtr<IOdictionary> exprDictPtr;
+
+    // Sort out conflicts
+
+    const bool useCommandArgs = args.found("field");
+
+    if (useCommandArgs)
+    {
+        if (args.found("dict"))
+        {
+            FatalErrorInFunction
+                << "Cannot specify both dictionary and command-line arguments"
+                << nl
+                << endl;
+        }
+
+        if (args.found("create") && args.found("keepPatches"))
+        {
+            FatalErrorInFunction
+                << "Cannot specify both 'create' and 'keepPatches'" << nl
+                << endl;
+        }
+    }
+    else
+    {
+        // Carp about inapplicable options
+
+        wordHashSet badOptions
+        ({
+            "create", "keepPatches", "valuePatches",
+            "dimension", "condition", "expression"
+        });
+
+        badOptions.retain(args.options());
+
+        if (!badOptions.empty())
+        {
+            FatalErrorInFunction
+                << "Using a dictionary. Cannot specify command options:" << nl
+                << nl
+                << flatOutput(badOptions.sortedToc()) << nl
+                << endl;
+        }
+
+        #include "setSystemMeshDictionaryIO.H"
+        exprDictPtr.reset(new IOdictionary(dictIO));
+    }
+
+
+    forAll(times, timei)
+    {
+        runTime.setTime(times[timei], timei);
+
+        Info<< "\nTime = " << runTime.timeName() << endl;
+
+        mesh.readUpdate();
+
+        if (args.found("dummy-phi") && !dummyPhi.valid())
+        {
+            Info<< "Adding a dummy phi" << endl;
+            dummyPhi.reset
+            (
+                new surfaceScalarField
+                (
+                    IOobject
+                    (
+                        "phi",
+                        mesh.thisDb().time().constant(),
+                        mesh.thisDb(),
+                        IOobject::NO_READ,
+                        IOobject::NO_WRITE
+                    ),
+                    mesh,
+                    dimensionedScalar(Zero)
+                )
+            );
+        }
+
+        if (args.found("withFunctionObjects"))
+        {
+            runTime.functionObjects().start();
+        }
+
+        if (args.found("field"))
+        {
+            const word fieldName(args.get<word>("field"));
+
+            Info<< "Using command-line options for "
+                << fieldName << nl << endl;
+
+            setExprFieldsControl ctrl;
+
+            ctrl.dryRun = dryrun;
+            ctrl.debugParsing = args.found("debug-parser");
+            ctrl.cacheVariables = !args.found("no-variable-caching");
+
+            ctrl.createNew = args.found("create");
+            ctrl.keepPatches = args.found("keepPatches");
+            ctrl.correctPatches = !args.found("noCorrectPatches");
+            ctrl.correctBCs = args.found("correctResultBoundaryFields");
+            ctrl.useDimension = args.found("dimension");
+
+            expressions::exprString
+                expression
+                (
+                    args.opt("expression"),
+                    dictionary::null
+                );
+
+            expressions::exprString condition;
+            if (args.found("condition"))
+            {
+                args.readIfPresent("condition", condition);
+            }
+
+            dimensionSet dims;
+
+            if (ctrl.useDimension)
+            {
+                ITstream is(args.lookup("dimension"));
+                is >> dims;
+            }
+
+            evaluate
+            (
+                mesh,
+                fieldName,
+                expression,
+                condition,
+                dictionary::null,
+                dims,
+                args.getList<word>("valuePatches", false),
+
+                ctrl
+            );
+        }
+        else if (exprDictPtr.valid())
+        {
+            const dictionary& exprDict = exprDictPtr();
+
+            // Read set construct info from dictionary
+            PtrList<entry> actions(exprDict.lookup("expressions"));
+
+            for (const entry& dEntry : actions)
+            {
+                if (!dEntry.isDict())
+                {
+                    Info<< "Ignore non-dictionary entry: "
+                        << dEntry.keyword() << nl;
+                    continue;
+                }
+
+                const dictionary& dict = dEntry.dict();
+
+                setExprFieldsControl ctrl;
+
+                ctrl.dryRun = dryrun;
+                ctrl.debugParsing = args.found("debug-parser");
+                ctrl.cacheVariables = !args.found("no-variable-caching");
+
+                ctrl.createNew = dict.getOrDefault("create", false);
+                ctrl.keepPatches = dict.getOrDefault("keepPatches", false);
+                ctrl.correctPatches = !args.found("noCorrectPatches");
+                ctrl.correctBCs = args.found("correctResultBoundaryFields");
+
+                if (ctrl.createNew && ctrl.keepPatches)
+                {
+                    FatalIOErrorInFunction(dict)
+                        << "Cannot specify both 'create' and 'keepPatches'"
+                        << nl << endl
+                        << exit(FatalIOError);
+                }
+
+                // Local override
+                dict.readIfPresent
+                (
+                    "correctResultBoundaryFields",
+                    ctrl.correctBCs
+                );
+
+
+                const word fieldName(dict.get<word>("field"));
+
+                expressions::exprString expression
+                (
+                    dict.get<string>("expression"),
+                    dict
+                );
+
+                expressions::exprString condition;
+
+                if (dict.found("condition"))
+                {
+                    condition =
+                        expressions::exprString
+                        (
+                            dict.get<string>("condition"),
+                            dict
+                        );
+                }
+
+                ctrl.useDimension = dict.found("dimension");
+
+                dimensionSet dims;
+                if (ctrl.useDimension)
+                {
+                    dict.lookup("dimension") >> dims;
+                }
+
+                wordList valuePatches;
+                dict.readIfPresent("valuePatches", valuePatches);
+
+                if (verbose && !timei)
+                {
+                    // Report once
+                    Info<< "Processing" << dict << nl;
+                }
+
+                evaluate
+                (
+                    mesh,
+                    fieldName,
+                    expression,
+                    condition,
+                    dict,
+                    dims,
+                    valuePatches,
+
+                    ctrl
+                );
+            }
+        }
+        else if (exprDictPtr.valid())
+        {
+            FatalErrorInFunction
+                << "No command-line or dictionary??" << nl << endl
+                << exit(FatalError);
+        }
+    }
+
+    Info<< "\nEnd\n" << endl;
+
+    return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/setExprFields/setExprFieldsDict b/applications/utilities/preProcessing/setExprFields/setExprFieldsDict
new file mode 100644
index 0000000000000000000000000000000000000000..2e06d9a26e68afb3aa9611eb1ae267a712b10dd1
--- /dev/null
+++ b/applications/utilities/preProcessing/setExprFields/setExprFieldsDict
@@ -0,0 +1,52 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      setExprFieldsDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+expressions
+(
+    T
+    {
+        field       T;
+        dimensions  [0 0 0 1 0 0 0];
+
+        constants
+        {
+            centre (0.21 0 0.01);
+        }
+
+        variables
+        (
+            "radius = 0.1"
+        );
+
+        condition
+        #{
+            // Within the radius
+            (mag(pos() - $[(vector)constants.centre]) < radius)
+
+            // but only +ve y!
+          && pos((pos() - $[(vector)constants.centre]).y()) > 0
+        #};
+
+        expression
+        #{
+            300
+          + 200 * (1 - mag(pos() - $[(vector)constants.centre]) / radius)
+        #};
+    }
+);
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/Make/files b/src/finiteVolume/Make/files
index 18d5b7c57beebdd0c2c9f9706fcef392d6faacf1..8ad81a5a0435b4e9be0c99baf4dd1080d411cfe8 100644
--- a/src/finiteVolume/Make/files
+++ b/src/finiteVolume/Make/files
@@ -250,6 +250,35 @@ $(constraintFvsPatchFields)/wedge/wedgeFvsPatchFields.C
 fields/volFields/volFields.C
 fields/surfaceFields/surfaceFields.C
 
+expr = expressions
+$(expr)/base/exprDriverWriter.C
+
+$(expr)/base/fvExprDriver.C
+$(expr)/base/fvExprDriverFields.C
+$(expr)/base/fvExprDriverIO.C
+$(expr)/base/fvExprDriverNew.C
+
+patchExpr = $(expr)/patch
+$(patchExpr)/patchExpr.C
+$(patchExpr)/patchExprDriver.C
+$(patchExpr)/patchExprDriverFields.C
+$(patchExpr)/patchExprLemonParser.lyy-m4
+$(patchExpr)/patchExprScanner.cc
+
+volumeExpr = $(expr)/volume
+$(volumeExpr)/volumeExpr.C
+$(volumeExpr)/volumeExprDriver.C
+$(volumeExpr)/volumeExprDriverFields.C
+$(volumeExpr)/volumeExprLemonParser.lyy-m4
+$(volumeExpr)/volumeExprScanner.cc
+
+fieldExpr = $(expr)/fields
+$(fieldExpr)/base/patchExprFieldBase.C
+$(fieldExpr)/fvPatchFields/exprFixedValueFvPatchFields.C
+$(fieldExpr)/fvPatchFields/exprMixedFvPatchFields.C
+$(fieldExpr)/pointPatchFields/exprValuePointPatchFields.C
+
+
 fvMatrices/fvMatrices.C
 fvMatrices/fvScalarMatrix/fvScalarMatrix.C
 fvMatrices/solvers/MULES/MULES.C
diff --git a/src/finiteVolume/expressions/base/exprDriverWriter.C b/src/finiteVolume/expressions/base/exprDriverWriter.C
new file mode 100644
index 0000000000000000000000000000000000000000..eb2aa82b82bd59d57626a17e2593dc2bee11341a
--- /dev/null
+++ b/src/finiteVolume/expressions/base/exprDriverWriter.C
@@ -0,0 +1,100 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2010-2018 Bernhard Gschaider <bgschaid@hfd-research.com>
+    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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "exprDriverWriter.H"
+#include "fvExprDriver.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace expressions
+{
+
+    defineTypeName(exprDriverWriter);
+
+} // namespace expressions
+} // namespace Foam
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::expressions::exprDriverWriter::exprDriverWriter
+(
+    const word& name,
+    fvExprDriver& driver
+)
+:
+    regIOobject
+    (
+        IOobject
+        (
+            name,
+            driver.mesh().time().timeName(),
+            "expressions",
+            driver.mesh().time(),
+            IOobject::READ_IF_PRESENT,
+            IOobject::AUTO_WRITE
+        )
+    ),
+    driver_(driver)
+{
+    if (headerOk())
+    {
+        readData(readStream("exprDriverWriter", true));
+    }
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::expressions::exprDriverWriter::readData(Istream& is)
+{
+    dictionary dict(is);
+
+    // driver_.readDict(is);
+
+    driver_.getData(dict);
+
+    return !is.bad();
+}
+
+
+bool Foam::expressions::exprDriverWriter::writeData(Ostream& os) const
+{
+    // driver_.writeDict(os);
+
+    dictionary dict;
+    driver_.prepareData(dict);
+    dict.write(os, false);
+
+    return os.good();
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/base/exprDriverWriter.H b/src/finiteVolume/expressions/base/exprDriverWriter.H
new file mode 100644
index 0000000000000000000000000000000000000000..10fe296e1f3959d9ac50e9738b8f66ddf966916a
--- /dev/null
+++ b/src/finiteVolume/expressions/base/exprDriverWriter.H
@@ -0,0 +1,109 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2011-2018 Bernhard Gschaider <bgschaid@hfd-research.com>
+    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::expressions::exprDriverWriter
+
+Description
+    Registered input/output for an expressions::fvExprDriver
+
+SourceFiles
+    exprDriverWriter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef expressions_exprDriverWriter_H
+#define expressions_exprDriverWriter_H
+
+#include "fvExprDriver.H"
+#include "regIOobject.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace expressions
+{
+
+/*---------------------------------------------------------------------------*\
+                      Class exprDriverWriter Declaration
+\*---------------------------------------------------------------------------*/
+
+class exprDriverWriter
+:
+    public regIOobject
+{
+    // Private Data
+
+        //- The driver to read/write
+        fvExprDriver& driver_;
+
+
+    // Private Member Functions
+
+        //- No null constructor
+        exprDriverWriter() = delete;
+
+        //- No copy construct
+        exprDriverWriter(const exprDriverWriter&) = delete;
+
+        //- No copy assignment
+        void operator=(const exprDriverWriter&) = delete;
+
+
+public:
+
+    //- Runtime type information
+    TypeNameNoDebug("exprDriverWriter");
+
+
+    // Constructors
+
+        //- Construct for named driver
+        exprDriverWriter(const word& name, fvExprDriver& driver);
+
+
+    //- Destructor
+    virtual ~exprDriverWriter() = default;
+
+
+    // Member Functions
+
+        virtual bool readData(Istream& is);
+        virtual bool writeData(Ostream& os) const;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace expressions
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/base/fvExprDriver.C b/src/finiteVolume/expressions/base/fvExprDriver.C
new file mode 100644
index 0000000000000000000000000000000000000000..1e6055daddfb71ce7687f48ade8172821885bb16
--- /dev/null
+++ b/src/finiteVolume/expressions/base/fvExprDriver.C
@@ -0,0 +1,765 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2010-2018 Bernhard Gschaider <bgschaid@hfd-research.com>
+    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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "fvExprDriver.H"
+#include "exprDriverWriter.H"
+#include "expressionEntry.H"
+#include "exprResultGlobals.H"
+
+#include "cellSet.H"
+#include "faceSet.H"
+#include "pointSet.H"
+#include "stringOps.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace expressions
+{
+
+    defineTypeNameAndDebug(fvExprDriver, 0);
+    defineRunTimeSelectionTable(fvExprDriver, dictionary);
+    defineRunTimeSelectionTable(fvExprDriver, idName);
+
+} // End namespace expressions
+} // End namespace Foam
+
+// Currently not working?
+bool Foam::expressions::fvExprDriver::cacheSets_ = true;
+
+const Foam::fvMesh* Foam::expressions::fvExprDriver::defaultMeshPtr_ = nullptr;
+
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+const Foam::fvMesh& Foam::expressions::fvExprDriver::defaultMesh()
+{
+    if (!defaultMeshPtr_)
+    {
+        FatalErrorInFunction
+            << "No default mesh set" << nl
+            << "Try the 'fvExprDriverFunctionObject' as a workaround"
+            << endl
+            << abort(FatalError);
+    }
+
+    return *defaultMeshPtr_;
+}
+
+
+const Foam::fvMesh* Foam::expressions::fvExprDriver::resetDefaultMesh
+(
+    const fvMesh& mesh,
+    const bool force
+)
+{
+    const fvMesh* ptr = defaultMeshPtr_;
+
+    if (force || (ptr != nullptr))
+    {
+        defaultMeshPtr_ = &mesh;
+    }
+
+    return ptr;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::expressions::fvExprDriver::fvExprDriver
+(
+    bool cacheReadFields,
+    bool searchInMemory,
+    bool searchFiles,
+    const dictionary& dict
+)
+:
+    expressions::exprDriver
+    (
+        cacheReadFields,
+        searchInMemory,
+        searchFiles,
+        dict
+    ),
+    globalScopes_(),
+    delayedVariables_(),
+    storedVariables_(),
+    specialVariablesIndex_(-1),
+    otherMeshName_(),
+    libs_(),
+    writer_(nullptr)
+{}
+
+
+Foam::expressions::fvExprDriver::fvExprDriver
+(
+    const fvExprDriver& rhs
+)
+:
+    expressions::exprDriver(rhs),
+    globalScopes_(rhs.globalScopes_),
+    delayedVariables_(rhs.delayedVariables_),
+    storedVariables_(rhs.storedVariables_),
+    specialVariablesIndex_(rhs.specialVariablesIndex_),
+    otherMeshName_(),
+    libs_(),
+    writer_(nullptr)
+{}
+
+
+Foam::expressions::fvExprDriver::fvExprDriver
+(
+    const dictionary& dict
+)
+:
+    fvExprDriver
+    (
+        dict.lookupOrDefault("cacheReadFields", false),
+        dict.lookupOrDefault("searchInMemory", true),
+        dict.lookupOrDefault("searchFiles", false),
+        dict
+    )
+{
+    readDict(dict);
+}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::expressions::fvExprDriver::~fvExprDriver()
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::expressions::fvExprDriver::readDict
+(
+    const dictionary& dict
+)
+{
+    expressions::exprDriver::readDict(dict);
+
+    // wordList plugins;
+    // if (dict.readIfPresent("functionPlugins", plugins))
+    // {
+    //     for (const word& plugin : plugins)
+    //     {
+    //         libs_.open("libswak" + plugin + "FunctionPlugin.so");
+    //     }
+    // }
+
+    dict.readIfPresent("globalScopes", globalScopes_);
+
+    const entry* eptr = nullptr;
+
+    // Special variables
+
+    if
+    (
+        // storedVariables
+        (eptr = dict.findEntry("storedVariables", keyType::LITERAL))
+     != nullptr
+    )
+    {
+        ITstream& is = eptr->stream();
+
+        if (writer_.valid() && storedVariables_.size())
+        {
+            WarningInFunction
+                // << "Context: " << driverContext_ << nl
+                << "The 'storedVariables' was already read."
+                << " No update from " << is
+                << endl;
+        }
+        else
+        {
+            storedVariables_ = List<exprResultStored>(is);
+
+            // Check for excess tokens
+            dict.checkITstream(is, "storedVariables");
+        }
+    }
+
+    if
+    (
+        // delayedVariables
+        (eptr = dict.findEntry("delayedVariables", keyType::LITERAL))
+     != nullptr
+    )
+    {
+        ITstream& is = eptr->stream();
+
+        if (writer_.valid() && delayedVariables_.size())
+        {
+            WarningInFunction
+                // << "Context: " << driverContext_ << nl
+                << "Seems like 'delayedVariables' was already read."
+                << " No update from " << is
+                << endl;
+        }
+        else
+        {
+            List<exprResultDelayed> inputs(is);
+
+            // Check for excess tokens
+            dict.checkITstream(is, "delayedVariables");
+
+            for (auto& var : inputs)
+            {
+                delayedVariables_.insert(var.name(), var);
+            }
+        }
+    }
+
+    return true;
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+const Foam::Time& Foam::expressions::fvExprDriver::runTime() const
+{
+    return this->mesh().time();
+}
+
+
+Foam::word Foam::expressions::fvExprDriver::timeName() const
+{
+    return runTime().timeName();
+}
+
+
+Foam::scalar Foam::expressions::fvExprDriver::timeValue() const
+{
+    return runTime().value();
+}
+
+
+void Foam::expressions::fvExprDriver::updateSpecialVariables(bool force)
+{
+    const bool updated = this->update();
+
+    const label eventIndex = mesh().time().timeIndex();
+    const scalar eventTime = mesh().time().value();
+
+    DebugInfo
+        << "fvExprDriver::updateSpecialVariables(force="
+        << force << ") Updated: " << updated << endl;
+
+    if (specialVariablesIndex_ < 0)
+    {
+        DebugInfo
+            << "First update: " << eventIndex << endl;
+
+        specialVariablesIndex_ = eventIndex;
+
+        for (exprResultStored& v : storedVariables_)
+        {
+            DebugInfo
+                << v.name() << " = " << v.initialValueExpression()
+                << " (has value "
+                << v.hasValue() << ")" << endl;
+
+            if (!v.hasValue())
+            {
+                DebugInfo
+                    << "First value: " << v.initialValueExpression()
+                    << " -> " << v.name() << endl;
+
+                parse(v.initialValueExpression());
+                v = result_;
+                DebugInfo
+                    << "Parser size: " << this->size() << nl
+                    << "Calculated: " << result_ << nl
+                    << "Stored: " << v << nl;
+            }
+        }
+    }
+
+    if (force || specialVariablesIndex_ != eventIndex)
+    {
+        DebugInfo
+            << "Store variables: " << force << ' '
+            << specialVariablesIndex_ << ' '
+            << eventIndex << endl;
+
+        for (exprResultStored& v : storedVariables_)
+        {
+            if (variables_.found(v.name()))
+            {
+                DebugInfo
+                    << "Storing variable: " << v.name() << " "
+                    << variables_[v.name()] << endl;
+
+                v = variables_[v.name()];
+            }
+        }
+        specialVariablesIndex_ = eventIndex;
+    }
+
+    forAllIters(delayedVariables_, iter)
+    {
+        DebugInfo
+            << "Updating delayed variable " << iter().name() << endl;
+
+        if (!iter().updateReadValue(eventTime))
+        {
+            const exprString& expr = iter().startupValueExpression();
+
+            DebugInfo
+                << "Evaluate: " << expr << endl;
+
+            parse(expr);
+            iter().setReadValue(result_);
+
+            DebugInfo
+                << "Value " << iter() << nl
+                << "Type " << iter().valueType() << "("
+                << result_.valueType() << ")" << endl;
+        }
+        else
+        {
+            DebugInfo
+                << iter().name() << " updated without problem" << endl;
+        }
+    }
+}
+
+
+void Foam::expressions::fvExprDriver::clearVariables()
+{
+    DebugInfo
+        << "Clearing variables" << endl;
+
+    const scalar eventTime = mesh().time().value();
+
+    (void)this->update();
+
+    updateSpecialVariables();
+    variables_.clear();
+    for (exprResultStored& v : storedVariables_)
+    {
+        variables_.insert(v.name(), v);
+    }
+
+    addVariables(variableStrings_, false);
+
+    forAllIters(delayedVariables_, iter)
+    {
+        iter().storeValue(eventTime);
+    }
+}
+
+
+void Foam::expressions::fvExprDriver::evaluateVariable
+(
+    const word& varName,
+    const expressions::exprString& expr
+)
+{
+    const regIOobject* objPtr = mesh().findObject<regIOobject>(varName);
+
+    if (!allowShadowing_ && objPtr)
+    {
+        WarningInFunction
+            // << "Context: " << driverContext_ << nl
+            << "Field '" << varName << "' (type " << objPtr->headerClassName()
+            << ") is shadowed by a variable of the same name." << nl
+            << "This may lead to trouble" << nl
+            << "If this is OK set 'allowShadowing'"
+            << " in the relevant parser" << nl
+            << endl;
+    }
+
+    parse(expr);
+    result_.testIfSingleValue();
+
+    DebugInfo
+        << "Evaluating: " << expr << " -> " << varName << endl
+        << result_;
+
+
+    // Assign
+    if (delayedVariables_.found(varName))
+    {
+        // Avoid potential conflicts?
+        variables_.erase(varName);
+
+        DebugInfo
+            << varName << " is delayed" << endl;
+
+        // Copy assignment
+        delayedVariables_[varName] = result_;
+    }
+    else
+    {
+        // Overwrite with a copy
+        variables_.set(varName, exprResult(result_));
+    }
+}
+
+
+void Foam::expressions::fvExprDriver::evaluateVariableRemote
+(
+    string remote,
+    const word& varName,
+    const expressions::exprString& expr
+)
+{
+    DebugInfo
+        << "Evaluating remote " << remote.c_str()
+        << " : " << expr << " -> " << varName << endl;
+
+    word driverType("patch");  // default is patch
+    word identName, regionName;
+
+    const auto slashPos = remote.find('/');
+    if (slashPos != std::string::npos)
+    {
+        regionName = word::validate(remote.substr(slashPos+1));
+        remote.resize(slashPos);
+    }
+
+    const auto quotePos = remote.find('\'');
+    if (quotePos != std::string::npos)
+    {
+        driverType = word::validate(remote.substr(0, quotePos));
+        identName = word::validate(remote.substr(quotePos+1));
+    }
+    else
+    {
+        identName = word::validate(remote);
+    }
+
+    if
+    (
+        driverType == "patch"
+     &&
+        (
+            identName.empty()
+         || identName == "volume"
+         || identName == "internalField"
+        )
+    )
+    {
+        driverType = "internalField";
+    }
+
+    const fvMesh* pRegion = &(this->mesh());
+
+    if (!regionName.empty())
+    {
+        pRegion = pRegion->time().cfindObject<fvMesh>(regionName);
+
+        if (!pRegion)
+        {
+            FatalErrorInFunction
+                << "Cannot resolve mesh region: " << regionName << nl
+                << exit(FatalError);
+        }
+    }
+
+    DebugInfo
+        << "Call other with ("
+        << driverType << ", " << identName << ", " << regionName << ")\n";
+
+    autoPtr<fvExprDriver> otherDriver =
+        fvExprDriver::New(driverType, identName, *pRegion);
+
+    otherDriver->setSearchBehaviour(*this);
+    otherDriver->setGlobalScopes(this->globalScopes_);
+
+    otherDriver->parse(expr);
+
+    exprResult otherResult(this->getRemoteResult(*otherDriver));
+
+    // Check / re-check for uniform. Not normally needed
+    if (!otherResult.isUniform())
+    {
+        otherResult.testIfSingleValue();
+    }
+
+    DebugInfo
+        << "Remote result: " << otherResult << nl;
+
+    // Assign
+    if (delayedVariables_.found(varName))
+    {
+        // Avoid potential conflicts?
+        variables_.erase(varName);
+
+        DebugInfo
+            << varName << " is delayed - setting" << nl;
+
+        // Move assignment
+        delayedVariables_[varName] = std::move(otherResult);
+    }
+    else
+    {
+        // Overwrite with a copy
+        variables_.set(varName, std::move(otherResult));
+    }
+}
+
+
+const Foam::fvMesh&
+Foam::expressions::fvExprDriver::regionMesh
+(
+    const dictionary& dict,
+    const fvMesh& mesh,
+    bool readIfNecessary
+)
+{
+    word regionName;
+
+    if (!dict.readIfPresent("region", regionName))
+    {
+        DebugInFunction << "Using original mesh " << nl;
+        return mesh;
+    }
+
+    DebugInFunction << "Using mesh " << regionName  << endl;
+
+    fvMesh* meshPtr = mesh.time().getObjectPtr<fvMesh>(regionName);
+
+    if (!meshPtr && readIfNecessary)
+    {
+        WarningInFunction
+            << "Region " << regionName
+            << " not in memory. Loading it" << endl;
+
+        meshPtr = new fvMesh
+        (
+            IOobject
+            (
+                regionName,
+                mesh.time().constant(),
+                mesh.time(),
+                IOobject::MUST_READ
+            )
+        );
+
+        meshPtr->polyMesh::store();
+    }
+
+    if (!meshPtr)
+    {
+        FatalErrorInFunction
+            << "No mesh region loaded: " << regionName
+            << endl;
+    }
+
+    return *meshPtr;
+}
+
+
+Foam::word Foam::expressions::fvExprDriver::getTypeOfField
+(
+    const word& fieldName
+) const
+{
+    return getHeaderClassName(this->mesh(), fieldName);
+}
+
+
+Foam::word Foam::expressions::fvExprDriver::getFieldClassName
+(
+    const word& name
+) const
+{
+    if (searchInMemory())
+    {
+        const regIOobject* ioptr = this->mesh().findObject<regIOobject>(name);
+
+        if (ioptr)
+        {
+            return ioptr->type();
+        }
+    }
+
+    if (searchFiles())
+    {
+        return getHeaderClassName(this->mesh(), name);
+    }
+
+    return word::null;
+}
+
+
+Foam::topoSetSource::sourceType
+Foam::expressions::fvExprDriver::topoSetType(const word& setName) const
+{
+    IOobject io(topoSet::findIOobject(mesh(), setName));
+
+    if (cellSet::typeName == io.headerClassName())
+    {
+        return topoSetSource::sourceType::CELLSET_SOURCE;
+    }
+    if (faceSet::typeName == io.headerClassName())
+    {
+        return topoSetSource::sourceType::FACESET_SOURCE;
+    }
+    if (pointSet::typeName == io.headerClassName())
+    {
+        return topoSetSource::sourceType::POINTSET_SOURCE;
+    }
+
+    return topoSetSource::sourceType::UNKNOWN_SOURCE;
+}
+
+
+Foam::topoSetSource::sourceType
+Foam::expressions::fvExprDriver::topoZoneType(const word& setName) const
+{
+    if (mesh().cellZones().findZoneID(setName) >= 0)
+    {
+        return topoSetSource::sourceType::CELLZONE_SOURCE;
+    }
+
+    if (mesh().faceZones().findZoneID(setName) >= 0)
+    {
+        return topoSetSource::sourceType::FACEZONE_SOURCE;
+    }
+
+    if (mesh().pointZones().findZoneID(setName) >= 0)
+    {
+        return topoSetSource::sourceType::POINTZONE_SOURCE;
+    }
+
+    return topoSetSource::sourceType::UNKNOWN_SOURCE;
+}
+
+
+Foam::topoSetSource::sourceType
+Foam::expressions::fvExprDriver::topoSourceType(const word& setName) const
+{
+    auto setType = topoZoneType(setName);
+
+    if (topoSetSource::sourceType::UNKNOWN_SOURCE == setType)
+    {
+        setType = topoSetType(setName);
+    }
+
+    return setType;
+}
+
+
+
+bool Foam::expressions::fvExprDriver::isCellSet(const word& setName) const
+{
+    return
+    (
+        topoSetSource::sourceType::CELLSET_SOURCE
+     == topoSetType(setName)
+    );
+}
+
+
+bool Foam::expressions::fvExprDriver::isFaceSet(const word& setName) const
+{
+    return
+    (
+        topoSetSource::sourceType::FACESET_SOURCE
+     == topoSetType(setName)
+    );
+}
+
+
+bool Foam::expressions::fvExprDriver::isPointSet(const word& setName) const
+{
+    return
+    (
+        topoSetSource::sourceType::POINTSET_SOURCE
+     == topoSetType(setName)
+    );
+}
+
+
+bool Foam::expressions::fvExprDriver::isCellZone(const word& name) const
+{
+    return (mesh().cellZones().findZoneID(name) >= 0);
+}
+
+
+bool Foam::expressions::fvExprDriver::isFaceZone(const word& name) const
+{
+    return (mesh().faceZones().findZoneID(name) >= 0);
+}
+
+
+bool Foam::expressions::fvExprDriver::isPointZone(const word& name) const
+{
+    return (mesh().pointZones().findZoneID(name) >= 0);
+}
+
+
+const Foam::expressions::exprResult&
+Foam::expressions::fvExprDriver::lookupGlobal
+(
+    const word& name
+) const
+{
+    return exprResultGlobals::New(this->mesh()).get(name, globalScopes_);
+}
+
+
+bool Foam::expressions::fvExprDriver::hasDataToWrite() const
+{
+    return (!storedVariables_.empty() || !delayedVariables_.empty());
+}
+
+
+void Foam::expressions::fvExprDriver::getData
+(
+    const dictionary& dict
+)
+{
+    dict.readIfPresent("storedVariables", storedVariables_);
+}
+
+
+void Foam::expressions::fvExprDriver::prepareData
+(
+    dictionary& dict
+) const
+{
+    auto& driver = const_cast<fvExprDriver&>(*this);
+
+    (void)driver.update();
+
+    if (storedVariables_.size())
+    {
+        driver.updateSpecialVariables(true);
+
+        dict.add("storedVariables", storedVariables_);
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/base/fvExprDriver.H b/src/finiteVolume/expressions/base/fvExprDriver.H
new file mode 100644
index 0000000000000000000000000000000000000000..4104d079640eb93b5264bb4f221a4e565c71cdb4
--- /dev/null
+++ b/src/finiteVolume/expressions/base/fvExprDriver.H
@@ -0,0 +1,646 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Original code Copyright (C) 2010-2018 Bernhard Gschaider
+    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::expressions::fvExprDriver
+
+Description
+    Base driver for parsing value expressions associated with an fvMesh.
+
+    Largely based on code and ideas from swak4foam
+
+    Properties
+    \table
+        Property     | Description                          | Required | Default
+        variables    | List of variables for expressions    | no  | ()
+        delayedVariables | List of delayed variables        | no  | ()
+        storedVariables | List of stored variables          | no  | ()
+        globalScopes | Scopes for global variables          | no  | ()
+        allowShadowing | Allow variables to shadow field names | no  | false
+    \endtable
+
+    Debug Properties
+    \table
+        Property     | Description                          | Required | Default
+        debugBaseDriver | Debug level (int) for base driver | no  |
+        debugScanner | Add debug for scanner                | no  | false
+        debugParser  | Add debug for parser                 | no  | false
+    \endtable
+
+SourceFiles
+    fvExprDriverI.H
+    fvExprDriver.C
+    fvExprDriverFields.C
+    fvExprDriverIO.C
+    fvExprDriverNew.C
+    fvExprDriverTemplates.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef expressions_fvExprDriver_H
+#define expressions_fvExprDriver_H
+
+#include "exprDriver.H"
+#include "exprResultDelayed.H"
+#include "exprResultStored.H"
+#include "pointMesh.H"
+#include "volFields.H"
+#include "topoSetSource.H"
+#include "dlLibraryTable.H"
+#include "runTimeSelectionTables.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace expressions
+{
+
+// Forward Declarations
+class exprDriverWriter;
+
+/*---------------------------------------------------------------------------*\
+                         Class fvExprDriver Declaration
+\*---------------------------------------------------------------------------*/
+
+class fvExprDriver
+:
+    public expressions::exprDriver
+{
+    // Static Data
+
+        //- Pointer to the "default" mesh
+        static const fvMesh *defaultMeshPtr_;
+
+        //- Cache cellSets, faceSets insted of reading from disc each time
+        static bool cacheSets_;
+
+
+    // Protected Data
+
+    // Stored Data
+
+        //- The scopes for global variables
+        List<word> globalScopes_;
+
+        //- The (delayed) variables table
+        HashTable<exprResultDelayed> delayedVariables_;
+
+        //- Stored expressions. Read from dictionary and updated as required
+        List<exprResultStored> storedVariables_;
+
+        //- Time index when handling special variables
+        label specialVariablesIndex_;
+
+        //- The name of the other mesh (if it is to be required)
+        word otherMeshName_;
+
+        //- Additional libraries
+        dlLibraryTable libs_;
+
+        //- Writing and restoring
+        autoPtr<exprDriverWriter> writer_;
+
+
+    // Private Member Functions
+
+        //- Read the IOobject and return the headerClassName
+        static word getHeaderClassName
+        (
+            const polyMesh& mesh,
+            const word& name
+        );
+
+        //- Read the set IOobject and return its headerClassName
+        static word getSetClassName
+        (
+            const polyMesh& mesh,
+            const word& name
+        );
+
+
+        //- No copy assignment
+        void operator=(const fvExprDriver&) = delete;
+
+
+protected:
+
+    // Static Member Functions
+
+        //- Determine mesh or region mesh as specified in the dictionary
+        //- with the keyword "region"
+        static const fvMesh& regionMesh
+        (
+            const dictionary& dict,
+            const fvMesh& mesh,
+            bool readIfNecessary
+        );
+
+        //- Default boundary type is calculated
+        template<class T>
+        static inline word defaultBoundaryType(const T&)
+        {
+            return "calculated";
+        }
+
+        //- Default boundary type for volume fields is zeroGradient
+        template<class Type>
+        static inline word defaultBoundaryType
+        (
+            const GeometricField<Type, fvPatchField, volMesh>&
+        )
+        {
+            return "zeroGradient";
+        }
+
+
+        //- Apply correctBoundaryConditions (volume fields only)
+        template<class T>
+        static inline void correctField(T&) {}
+
+        template<class Type>
+        static inline void correctField
+        (
+            GeometricField<Type, fvPatchField, volMesh>& fld
+        )
+        {
+            fld.correctBoundaryConditions();
+        }
+
+
+    // Protected Member Functions
+
+    // Mesh related
+
+        //- The mesh we are attached to
+        virtual const fvMesh& mesh() const = 0;
+
+
+    // Variables
+
+        //- Define scopes for global variables
+        void setGlobalScopes(const wordUList& scopes)
+        {
+            globalScopes_ = scopes;
+        }
+
+        //- Non-const access to the named variable (sub-classes only)
+        inline virtual exprResult& variable(const word& name);
+
+        //- Test existence of a global variable
+        template<class T>
+        bool isGlobalVariable
+        (
+            const word& name,
+            bool isPointVal,
+            label expectedSize = -1
+        ) const;
+
+        //- Return the global variable if available or a null result
+        const exprResult& lookupGlobal(const word& name) const;
+
+
+    // Fields
+
+        //- Test for the existence of a mesh field
+        template<class Type>
+        bool isField
+        (
+            const word& name,
+            bool isPointVal = false,
+            label expectSize = -1  //!< ignored
+        ) const;
+
+
+        //- Retrieve field from memory or disk
+        template<class GeomField>
+        inline tmp<GeomField> getOrReadField
+        (
+            const word& name,
+            bool mandatory = true,
+            bool getOldTime = false
+        );
+
+        //- Retrieve point field from memory or disk
+        template<class GeomField>
+        inline tmp<GeomField> getOrReadPointField
+        (
+            const word& name,
+            bool mandatory = true,
+            bool getOldTime = false
+        );
+
+        //- Retrieve field from memory or disk (implementation)
+        template<class GeomField, class MeshRef>
+        tmp<GeomField> getOrReadFieldImpl
+        (
+            const word& name,
+            const MeshRef& meshRef,
+            bool mandatory = true,
+            bool getOldTime = false
+        );
+
+        //- Helper function for getOrReadField
+        template<class GeomField, class MeshRef>
+        inline tmp<GeomField> readAndRegister
+        (
+            const word& name,
+            const MeshRef& meshRef
+        );
+
+        //- Create a random field
+        //
+        //  \param field the field to populate
+        //  \param seed the seed value. If zero or negative, use as an offset
+        //      to the current timeIndex
+        //  \param gaussian generate a Gaussian distribution
+        void fill_random
+        (
+            scalarField& field,
+            label seed = 0,
+            const bool gaussian = false
+        ) const;
+
+
+    // Sets
+
+        //- The origin of the topoSet
+        enum SetOrigin { INVALID = 0, NEW, FILE, MEMORY, CACHE };
+
+        //- Get topoSet
+        template<class T>
+        autoPtr<T> getTopoSet
+        (
+            const fvMesh& mesh,
+            const word& setName,
+            SetOrigin& origin
+        ) const;
+
+        //- Update topoSet
+        template<class T>
+        inline bool updateSet
+        (
+            autoPtr<T>& setPtr,
+            const word& setName,
+            SetOrigin origin
+        ) const;
+
+
+    // Updating
+
+        //- Examine current variable values and update stored variables
+        virtual void updateSpecialVariables(bool force=false);
+
+        //- Do we need a data file to be written
+        virtual bool hasDataToWrite() const;
+
+        //- Prepare/update special variables and add to dictionary,
+        //- normally via the reader/writer
+        virtual void prepareData(dictionary& dict) const;
+
+        //- Read data from dictionary, normally via the reader/writer
+        virtual void getData(const dictionary& dict);
+
+
+public:
+
+    // Friends
+    friend class exprDriverWriter;
+
+
+    // Static Member Functions
+
+        //- Get the default mesh, if one is defined
+        static const fvMesh& defaultMesh();
+
+        //- Set the default mesh (if not already set)
+        static const fvMesh* resetDefaultMesh
+        (
+            const fvMesh& mesh,
+            const bool force = false  //!< Force reset, even if already set
+        );
+
+
+    //- Runtime type information
+    TypeName("fvExprDriver");
+
+
+    // Run-time selection
+
+        declareRunTimeSelectionTable
+        (
+            autoPtr,
+            fvExprDriver,
+            dictionary,
+            (
+                const dictionary& dict,
+                const fvMesh& mesh
+            ),
+            (dict,mesh)
+        );
+
+        declareRunTimeSelectionTable
+        (
+            autoPtr,
+            fvExprDriver,
+            idName,
+            (
+                const word& ident,
+                const fvMesh& mesh
+            ),
+            (ident, mesh)
+        );
+
+
+    // Constructors
+
+        //- Null constructor, and null construct with search preferences
+        explicit fvExprDriver
+        (
+            bool cacheReadFields = false,
+            bool searchInMemory = true,
+            bool searchFiles = false,
+            const dictionary& dict = dictionary::null
+        );
+
+        //- Copy construct
+        fvExprDriver(const fvExprDriver&);
+
+        //- Construct from a dictionary
+        explicit fvExprDriver(const dictionary& dict);
+
+
+        //- Return a reference to the selected value driver
+        static autoPtr<fvExprDriver> New
+        (
+            const dictionary& dict,
+            const fvMesh& mesh
+        );
+
+        //- Return a reference to the selected value driver
+        static autoPtr<fvExprDriver> New
+        (
+            const dictionary& dict
+        );
+
+        //- Return a reference to the selected value driver
+        static autoPtr<fvExprDriver> New
+        (
+            const word& type,
+            const word& id,
+            const fvMesh& mesh
+        );
+
+        //- Clone
+        virtual autoPtr<fvExprDriver> clone() const = 0;
+
+
+    //- Destructor
+    virtual ~fvExprDriver();
+
+
+    // Public Member Functions
+
+        //- The underlying field size for the expression
+        virtual label size() const = 0;
+
+        //- The underlying point field size for the expression
+        virtual label pointSize() const = 0;
+
+
+    // Mesh Related
+
+        //- The Time associated with the mesh
+        const Time& runTime() const;
+
+        //- The current time name
+        virtual word timeName() const;
+
+        //- The current time value
+        virtual scalar timeValue() const;
+
+
+    // General Controls
+
+        //- Status of cache-sets (static variable)
+        bool cacheSets() const { return cacheSets_; }
+
+
+    // Variables
+
+        //- Clear temporary variables and resets from expression strings
+        virtual void clearVariables();
+
+        //- True if named variable exists
+        inline virtual bool hasVariable(const word& name) const;
+
+        //- Return const-access to the named variable
+        inline virtual const exprResult& variable(const word& name) const;
+
+        //- Test for existence of a local/global variable or a field
+        template<class Type>
+        inline bool isVariableOrField
+        (
+            const word& name,
+            bool isPointVal = false,
+            label expectSize = -1
+        ) const;
+
+        //- Retrieve local/global variable as a tmp field
+        //
+        //  \param name The name of the local/global field
+        //  \param expectSize  The size check on the variable, -1 to ignore
+        //  \param mandatory A missing variable is Fatal, or return
+        //      an invalid tmp
+        template<class Type>
+        tmp<Field<Type>> getVariable
+        (
+            const word& name,
+            label expectSize,
+            const bool mandatory = true
+        ) const;
+
+
+        //- Lookup the field class name (memory or read from disk)
+        //
+        //  Return empty if the name cannot be resolved.
+        word getFieldClassName(const word& name) const;
+
+
+    // Types
+
+        //- Return cell/face/point set type or unknown
+        topoSetSource::sourceType topoSetType(const word& name) const;
+
+        //- Return cell/face/point zone type or unknown
+        topoSetSource::sourceType topoZoneType(const word& name) const;
+
+        //- Return cell/face/point zone/set type or unknown
+        topoSetSource::sourceType topoSourceType(const word& name) const;
+
+        //- Read and return labels associated with the topo set
+        labelList getTopoSetLabels
+        (
+            const word& name,
+            enum topoSetSource::sourceType setType
+        ) const;
+
+        //- Test if name is a known cellZone
+        bool isCellZone(const word& name) const;
+
+        //- Test if name is a known faceZone
+        bool isFaceZone(const word& name) const;
+
+        //- Test if name is a known pointZone
+        bool isPointZone(const word& name) const;
+
+        //- Test if name is a known cellSet
+        bool isCellSet(const word& name) const;
+
+        //- Test if name is a known faceSet
+        bool isFaceSet(const word& name) const;
+
+        //- Test if name is a known pointSet
+        bool isPointSet(const word& name) const;
+
+
+    // Evaluation
+
+        //- Evaluate the expression
+        //- and save as the specified named variable
+        virtual void evaluateVariable
+        (
+            const word& varName,
+            const expressions::exprString& expr
+        );
+
+        //- Evaluate an expression on a remote
+        //- and save as the specified named variable
+        //
+        //  The fully qualified form of the remote is given as follows
+        //  \verbatim
+        //      type'name/region
+        //  \endverbatim
+        //
+        //  If not specified, the default type is "patch", which means the
+        //  following are equivalent
+        //  \verbatim
+        //      patch'name/region
+        //      name/region
+        //  \endverbatim
+        //
+        //  If region is identical to the current region, it can be omitted:
+        //  \verbatim
+        //      patch'name
+        //      name   (default is patch)
+        //  \endverbatim
+        virtual void evaluateVariableRemote
+        (
+            string remote,
+            const word& varName,
+            const expressions::exprString& expr
+        );
+
+
+    // Fields
+
+        //- Test existence of a local/global variable
+        template<class Type>
+        inline bool isVariable
+        (
+            const word& name,
+            bool isPointVal = false,
+            label expectSize = -1
+        ) const;
+
+        //- Test if specified field can be found in memory or disk
+        template<class Type>
+        bool foundField(const word& name) const;
+
+        //- Read the IOobject for fieldName and return its headerClassName
+        //  Empty if the field could not be found.
+        word getTypeOfField(const word& fieldName) const;
+
+
+    // Handling remote data (future)
+
+        // //- Access the other mesh name (future)
+        // const word& otherMeshName() const { return otherMeshName_; }
+        //
+        // //- Access the other mesh name (future)
+        // word& otherMeshName() { return otherMeshName_; }
+
+
+    // Reading
+
+        //- Read variables, tables etc.
+        //  Also usable for objects not constructed from a dictionary.
+        virtual bool readDict(const dictionary& dict);
+
+
+    // Writing
+
+        //- Write "variables", "storedVariables", "delayedVariables",
+        //- "globalScopes" if they are present.
+        Ostream& writeCommon(Ostream& os, bool debug=false) const;
+
+        //- Create a writer for this object
+        void createWriterAndRead(const word& name);
+
+        //- Write data if apropriate
+        //- Should enable exact restarts
+        void tryWrite() const;
+
+
+    // Plugins (future)
+
+        /// //- Tests for a plugin-function
+        /// virtual bool hasPlugin(const word& name) = 0;
+        ///
+        /// //- Return a new plugin-function
+        /// virtual autoPtr<CommonPluginFunction>
+        /// getPlugin(const word& name) = 0;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace expressions
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#include "fvExprDriverI.H"
+
+#ifdef NoRepository
+    #include "fvExprDriverTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/base/fvExprDriverFields.C b/src/finiteVolume/expressions/base/fvExprDriverFields.C
new file mode 100644
index 0000000000000000000000000000000000000000..d2610385a81c7bd8d2ca029a7a4fdae78e5982f0
--- /dev/null
+++ b/src/finiteVolume/expressions/base/fvExprDriverFields.C
@@ -0,0 +1,49 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2010-2018 Bernhard Gschaider <bgschaid@hfd-research.com>
+    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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "fvExprDriver.H"
+#include "Time.H"
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::expressions::fvExprDriver::fill_random
+(
+    scalarField& field,
+    label seed,
+    const bool gaussian
+) const
+{
+    exprDriver::fill_random
+    (
+        field, (seed <= 0 ? (runTime().timeIndex() - seed) : seed),
+        gaussian
+    );
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/base/fvExprDriverI.H b/src/finiteVolume/expressions/base/fvExprDriverI.H
new file mode 100644
index 0000000000000000000000000000000000000000..004871bc5d310cd04c59ff8e6c2b7b9623b04561
--- /dev/null
+++ b/src/finiteVolume/expressions/base/fvExprDriverI.H
@@ -0,0 +1,179 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2010-2018 Bernhard Gschaider <bgschaid@hfd-research.com>
+    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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "Time.H"
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+inline bool Foam::expressions::fvExprDriver::hasVariable
+(
+    const word& name
+) const
+{
+    return delayedVariables_.found(name) || variables_.found(name);
+}
+
+
+inline const Foam::expressions::exprResult&
+Foam::expressions::fvExprDriver::variable
+(
+    const word& name
+) const
+{
+    if (delayedVariables_.found(name))
+    {
+        return delayedVariables_[name];
+    }
+
+    return variables_[name];
+}
+
+
+inline Foam::expressions::exprResult&
+Foam::expressions::fvExprDriver::variable
+(
+    const word& name
+)
+{
+    if (delayedVariables_.found(name))
+    {
+        return delayedVariables_[name];
+    }
+
+    return variables_[name];
+}
+
+
+template<class Type>
+inline bool Foam::expressions::fvExprDriver::isVariable
+(
+    const word& name,
+    bool isPointVal,
+    label expectedSize
+) const
+{
+    return
+    (
+        this->isLocalVariable<Type>(name, isPointVal, expectedSize)
+     || this->isGlobalVariable<Type>(name, isPointVal, expectedSize)
+    );
+}
+
+
+template<class Type>
+inline bool Foam::expressions::fvExprDriver::isVariableOrField
+(
+    const word& name,
+    bool isPointVal,
+    label expectedSize
+)
+const
+{
+    return
+    (
+        this->isVariable<Type>(name, isPointVal, expectedSize)
+     || this->isField<Type>(name, isPointVal)
+    );
+}
+
+
+template<class GeomField>
+inline Foam::tmp<GeomField>
+Foam::expressions::fvExprDriver::getOrReadField
+(
+    const word& name,
+    bool mandatory,
+    bool getOldTime
+)
+{
+    return this->getOrReadFieldImpl<GeomField>
+    (
+        name,
+        this->mesh(),
+        mandatory,
+        getOldTime
+    );
+}
+
+
+template<class GeomField>
+inline Foam::tmp<GeomField>
+Foam::expressions::fvExprDriver::getOrReadPointField
+(
+    const word& name,
+    bool mandatory,
+    bool getOldTime
+)
+{
+    return this->getOrReadFieldImpl<GeomField>
+    (
+        name,
+        pointMesh::New(this->mesh()),
+        mandatory,
+        getOldTime
+    );
+}
+
+
+template<class GeomField, class Mesh>
+inline Foam::tmp<GeomField>
+Foam::expressions::fvExprDriver::readAndRegister
+(
+    const word& name,
+    const Mesh& meshRef
+)
+{
+    GeomField* ptr = new GeomField
+    (
+        IOobject
+        (
+            name,
+            meshRef.thisDb().time().timeName(),
+            meshRef.thisDb(),
+            IOobject::MUST_READ,
+            IOobject::NO_WRITE,
+            false  // Unregistered
+        ),
+        meshRef
+    );
+
+    if (cacheReadFields())
+    {
+        DebugInfo
+            << "Registering a copy of " << name << " with mesh" << nl;
+
+        // This is clunky
+        ptr->checkIn();
+        return tmp<GeomField>(regIOobject::store(ptr));
+    }
+
+    return tmp<GeomField>(ptr);
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/base/fvExprDriverIO.C b/src/finiteVolume/expressions/base/fvExprDriverIO.C
new file mode 100644
index 0000000000000000000000000000000000000000..3a25df5bdb6d4ec33f824fee81699a4141c01f95
--- /dev/null
+++ b/src/finiteVolume/expressions/base/fvExprDriverIO.C
@@ -0,0 +1,280 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2010-2018 Bernhard Gschaider <bgschaid@hfd-research.com>
+    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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "fvExprDriver.H"
+#include "exprDriverWriter.H"
+#include "cellSet.H"
+#include "faceSet.H"
+#include "pointSet.H"
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+Foam::labelList Foam::expressions::fvExprDriver::getTopoSetLabels
+(
+    const word& name,
+    enum topoSetSource::sourceType setType
+) const
+{
+    // Zones first - they are cheap to handle (no IO)
+
+    switch (setType)
+    {
+        case topoSetSource::sourceType::CELLZONE_SOURCE:
+        {
+            const auto& zones = mesh().cellZones();
+            const word& zoneTypeName = cellZone::typeName;
+
+            const label zoneID = zones.findZoneID(name);
+            if (zoneID < 0)
+            {
+                FatalErrorInFunction
+                    << "No " << zoneTypeName << " named "
+                    << name << "found. Has zones: " << zones.names() << endl
+                    << exit(FatalError);
+            }
+
+            return zones[zoneID];
+            break;
+        }
+
+        case topoSetSource::sourceType::FACEZONE_SOURCE:
+        {
+            const auto& zones = mesh().faceZones();
+            const word& zoneTypeName = faceZone::typeName;
+
+            const label zoneID = zones.findZoneID(name);
+            if (zoneID < 0)
+            {
+                FatalErrorInFunction
+                    << "No " << zoneTypeName << " named "
+                    << name << "found. Has zones: " << zones.names() << endl
+                    << exit(FatalError);
+            }
+
+            return zones[zoneID];
+            break;
+        }
+
+        case topoSetSource::sourceType::POINTZONE_SOURCE:
+        {
+            const auto& zones = mesh().pointZones();
+            const word& zoneTypeName = pointZone::typeName;
+
+            const label zoneID = zones.findZoneID(name);
+            if (zoneID < 0)
+            {
+                FatalErrorInFunction
+                    << "No " << zoneTypeName << " named "
+                    << name << "found. Has zones: " << zones.names() << endl
+                    << exit(FatalError);
+            }
+
+            return zones[zoneID];
+            break;
+        }
+
+        default:
+            break;
+    }
+
+
+    IOobject io(topoSet::findIOobject(mesh(), name));
+
+    switch (setType)
+    {
+        case topoSetSource::sourceType::CELLSET_SOURCE:
+        {
+            typedef cellSet classType;
+
+            if (classType::typeName != io.headerClassName())
+            {
+                FatalErrorInFunction
+                    << "Error reading " << classType::typeName
+                    << " <" << name << "> : found "
+                    << io.headerClassName() << nl
+                    << exit(FatalError);
+            }
+
+            classType set(io);
+            return set.sortedToc();
+            break;
+        }
+
+        case topoSetSource::sourceType::FACESET_SOURCE:
+        {
+            typedef faceSet classType;
+
+            if (classType::typeName != io.headerClassName())
+            {
+                FatalErrorInFunction
+                    << "Error reading " << classType::typeName
+                    << " <" << name << "> : found "
+                    << io.headerClassName() << nl
+                    << exit(FatalError);
+            }
+
+            classType set(io);
+            return set.sortedToc();
+            break;
+        }
+
+        case topoSetSource::sourceType::POINTSET_SOURCE:
+        {
+            typedef pointSet classType;
+
+            if (classType::typeName != io.headerClassName())
+            {
+                FatalErrorInFunction
+                    << "Error reading " << classType::typeName
+                    << " <" << name << "> : found "
+                    << io.headerClassName() << nl
+                    << exit(FatalError);
+            }
+
+            classType set(io);
+            return set.sortedToc();
+            break;
+        }
+
+        default:
+        {
+            FatalErrorInFunction
+                << "Unexpected sourceType: " << int(setType) << nl
+                << " for set <" << name << ">" << nl
+                << exit(FatalError);
+            break;
+        }
+    }
+
+    return labelList::null();
+}
+
+
+Foam::word Foam::expressions::fvExprDriver::getHeaderClassName
+(
+    const polyMesh& mesh,
+    const word& name
+)
+{
+    IOobject io
+    (
+        name,
+        mesh.time().timeName(),
+        mesh,
+        IOobject::MUST_READ,
+        IOobject::NO_WRITE
+    );
+    io.typeHeaderOk<IOobject>(false);
+
+    DebugInfo
+        << "Registry: " << mesh.path()
+        << " Name: " << name
+        << " Time: " << mesh.time().timeName()
+        << " Path: " << io.localFilePath(io.headerClassName())
+        << " Class: " << io.headerClassName() << endl;
+
+    return io.headerClassName();
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::Ostream& Foam::expressions::fvExprDriver::writeCommon
+(
+    Ostream& os,
+    bool debug
+) const
+{
+    // Write "variables", even if empty
+    writeVariableStrings(os, "variables");
+
+    if (debug)
+    {
+        os.writeEntry("variableValues", variables_);
+    }
+
+    if (!storedVariables_.empty() || !delayedVariables_.empty())
+    {
+        const_cast<fvExprDriver&>
+        (
+            *this
+        ).updateSpecialVariables(true);
+    }
+
+    if (!storedVariables_.empty())
+    {
+        os.writeEntry("storedVariables", storedVariables_);
+    }
+
+    if (!delayedVariables_.empty())
+    {
+        List<exprResultDelayed> list(delayedVariables_.size());
+
+        auto outIter = list.begin();
+
+        forAllConstIters(delayedVariables_, iter)
+        {
+            *outIter = *iter;
+            ++outIter;
+        }
+
+        os.writeEntry("delayedVariables", list);
+    }
+
+    if (!globalScopes_.empty())
+    {
+        os.writeEntry("globalScopes", globalScopes_);
+    }
+
+    // writeTable(os, "timelines", lines_);
+    // writeTable(os, "lookuptables", lookup_);
+    // writeTable(os, "lookuptables2D", lookup2D_);
+
+    return os;
+}
+
+
+void Foam::expressions::fvExprDriver::createWriterAndRead(const word& name)
+{
+    if (hasDataToWrite() && !writer_.valid())
+    {
+        writer_.set(new exprDriverWriter(name + "_" + this->type(), *this));
+    }
+}
+
+
+void Foam::expressions::fvExprDriver::tryWrite() const
+{
+    if (writer_.valid() && mesh().time().outputTime())
+    {
+        writer_->write();
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/base/fvExprDriverNew.C b/src/finiteVolume/expressions/base/fvExprDriverNew.C
new file mode 100644
index 0000000000000000000000000000000000000000..f2abd79e7be305ec1cc5c32ad93b7729ab86caeb
--- /dev/null
+++ b/src/finiteVolume/expressions/base/fvExprDriverNew.C
@@ -0,0 +1,105 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2010-2018 Bernhard Gschaider <bgschaid@hfd-research.com>
+    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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "fvExprDriver.H"
+
+// * * * * * * * * * * * * * * * * Selectors * * * * * * * * * * * * * * * * //
+
+Foam::autoPtr<Foam::expressions::fvExprDriver>
+Foam::expressions::fvExprDriver::New
+(
+    const dictionary& dict
+)
+{
+    return fvExprDriver::New
+    (
+        dict,
+        defaultMesh()
+    );
+}
+
+
+Foam::autoPtr<Foam::expressions::fvExprDriver>
+Foam::expressions::fvExprDriver::New
+(
+    const dictionary& dict,
+    const fvMesh& mesh
+)
+{
+    const word driverType(dict.get<word>("valueType"));
+
+    auto cstrIter = dictionaryConstructorTablePtr_->cfind(driverType);
+
+    if (!cstrIter.found())
+    {
+        FatalIOErrorInLookup
+        (
+            dict,
+            "valueType",
+            driverType,
+            *dictionaryConstructorTablePtr_
+        ) << exit(FatalIOError);
+    }
+
+    DebugInFunction << "Creating driver of type " << driverType << endl;
+
+    resetDefaultMesh(mesh, false); // lazy
+
+    return autoPtr<fvExprDriver>(cstrIter()(dict, mesh));
+}
+
+
+Foam::autoPtr<Foam::expressions::fvExprDriver>
+Foam::expressions::fvExprDriver::New
+(
+    const word& driverType,
+    const word& id,
+    const fvMesh& mesh
+)
+{
+    auto cstrIter = idNameConstructorTablePtr_->cfind(driverType);
+
+    if (!cstrIter.found())
+    {
+        FatalErrorInLookup
+        (
+            "valueType",
+            driverType,
+            *idNameConstructorTablePtr_
+        ) << exit(FatalError);
+    }
+
+    DebugInFunction << "Creating driver of type " << driverType << endl;
+
+    resetDefaultMesh(mesh, false); // lazy
+
+    return autoPtr<fvExprDriver>(cstrIter()(id, mesh));
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/base/fvExprDriverTemplates.C b/src/finiteVolume/expressions/base/fvExprDriverTemplates.C
new file mode 100644
index 0000000000000000000000000000000000000000..70eda6a08d82f0099279dc00e98f6101d9ffcb24
--- /dev/null
+++ b/src/finiteVolume/expressions/base/fvExprDriverTemplates.C
@@ -0,0 +1,626 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2010-2018 Bernhard Gschaider <bgschaid@hfd-research.com>
+    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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "surfaceMesh.H"
+#include "fvsPatchField.H"
+#include "pointPatchField.H"
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+template<class Type>
+bool Foam::expressions::fvExprDriver::isGlobalVariable
+(
+    const word& name,
+    bool isPointVal,
+    label expectedSize
+) const
+{
+    DebugInfo
+        << "Looking for global" << (isPointVal ? " point" : "")
+        << " field name:" << name;
+
+    const exprResult& result = lookupGlobal(name);
+
+    DebugInfo
+        << " - found (" << result.valueType() << ' ' << result.isPointValue() << ')';
+
+
+    bool good = (result.isType<Type>() && result.isPointValue(isPointVal));
+
+    // Do size checking if requested
+    if (good && expectedSize >= 0)
+    {
+        good = (result.size() == expectedSize);
+        reduce(good, andOp<bool>());
+
+        if (debug && !good)
+        {
+            Info<< " size is";
+        }
+    }
+
+    DebugInfo << (good ? " good" : " bad") << endl;
+
+    return good;
+}
+
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>>
+Foam::expressions::fvExprDriver::getVariable
+(
+    const word& name,
+    label expectedSize,
+    const bool mandatory
+) const
+{
+    tmp<Field<Type>> tresult;
+
+    bool isSingleValue = false;
+
+    if (hasVariable(name) && variable(name).isType<Type>())
+    {
+        isSingleValue = variable(name).isUniform();
+        tresult = variable(name).cref<Type>().clone();
+    }
+    else if (isGlobalVariable<Type>(name, false))
+    {
+        const exprResult& var = lookupGlobal(name);
+
+        isSingleValue = var.isUniform();
+
+        tresult = var.cref<Type>().clone();
+    }
+
+    if (tresult.valid())
+    {
+        if
+        (
+            expectedSize < 0
+         || returnReduce((tresult->size() == expectedSize), andOp<bool>())
+        )
+        {
+            return tresult;
+        }
+
+        if (!isSingleValue)
+        {
+            WarningInFunction
+                << "Variable " << name
+                << " is not a single value and does not fit the size "
+                << expectedSize << ". Using average" << endl;
+        }
+
+        return tmp<Field<Type>>::New(expectedSize, gAverage(tresult()));
+    }
+
+    if (mandatory)
+    {
+        FatalErrorInFunction
+            << "Variable (" << name << ") not found." << nl
+            << exit(FatalError);
+    }
+
+    return nullptr;
+}
+
+
+template<class Type>
+bool Foam::expressions::fvExprDriver::foundField
+(
+    const word& name
+) const
+{
+    if (debug)
+    {
+        Info<< "fvExprDriver::foundField. Name: " << name
+            << " Type: " << Type::typeName
+            << " registry:" << searchInMemory()
+            << " disk:" << searchFiles() << endl;
+    }
+
+    // if (std::is_void<Type>::value) ...
+
+    if (searchInMemory())
+    {
+        const regIOobject* ioptr =
+            this->mesh().findObject<regIOobject>(name);
+
+        if (this->mesh().foundObject<Type>(name))
+        {
+            if (debug)
+            {
+                Info<< "Found registered: " << name << endl;
+            }
+            return true;
+        }
+
+        if (debug)
+        {
+            Info<< "Registered " << name;
+
+            if (ioptr)
+            {
+                Info<< " type:" << ioptr->headerClassName();
+            }
+            Info<< ", not type:" << Type::typeName << nl;
+        }
+    }
+
+
+    if (searchFiles() && getTypeOfField(name) == Type::typeName)
+    {
+        if (debug)
+        {
+            Info<< "Found file: " << name << nl;
+        }
+        return true;
+    }
+    else
+    {
+        if (debug)
+        {
+            Info<< name << " not found" << endl;
+        }
+    }
+
+    return false;
+}
+
+
+template<class Type>
+bool Foam::expressions::fvExprDriver::isField
+(
+    const word& name,
+    bool isPointVal,
+    label
+) const
+{
+    if (debug)
+    {
+        Info<< "fvExprDriver::isField <" << name << '>' << endl;
+    }
+
+    typedef GeometricField<Type, fvPatchField, volMesh> vfieldType;
+    typedef GeometricField<Type, fvsPatchField, surfaceMesh> sfieldType;
+    typedef GeometricField<Type, pointPatchField, pointMesh> pfieldType;
+
+    return
+    (
+        isPointVal
+      ? this->foundField<pfieldType>(name)
+      :
+        (
+            this->foundField<vfieldType>(name)
+         || this->foundField<sfieldType>(name)
+        )
+    );
+}
+
+
+template<class GeomField, class Mesh>
+Foam::tmp<GeomField> Foam::expressions::fvExprDriver::getOrReadFieldImpl
+(
+    const word& name,
+    const Mesh& meshRef,
+    bool mandatory,
+    bool getOldTime
+)
+{
+    typedef typename GeomField::value_type Type;
+
+    if (debug)
+    {
+        Info<< "fvExprDriver::getOrReadField <" << name
+            << "> Type: " << GeomField::typeName << endl;
+    }
+
+    tmp<GeomField> tfield;
+
+    if
+    (
+        (hasVariable(name) && variable(name).isType<Type>())
+     || isGlobalVariable<Type>(name, false)
+    )
+    {
+        if (debug)
+        {
+            Info<< "Getting " << name << " from variables" << endl;
+        }
+
+        if (debug)
+        {
+            Info<< "Creating field " << name << " of type "
+                << GeomField::typeName << nl;
+        }
+
+        tfield.reset
+        (
+            GeomField::New(name, meshRef, dimensioned<Type>(Zero))
+        );
+
+        GeomField& fld = tfield.ref();
+
+        if (debug)
+        {
+            Info<< "New field: " << name << " ownedByRegistry"
+                << fld.ownedByRegistry() << endl;
+        }
+
+        Field<Type> vals;
+
+        if (hasVariable(name) && variable(name).isType<Type>())
+        {
+            vals = variable(name).cref<Type>();
+        }
+        else
+        {
+            vals = lookupGlobal(name).cref<Type>();
+        }
+
+        if (debug)
+        {
+            Pout<< "sizes: " << vals.size() << ' ' << fld.size() << endl;
+        }
+
+        if (returnReduce((vals.size() == fld.size()), andOp<bool>()))
+        {
+            fld.primitiveFieldRef() = vals;
+        }
+        else
+        {
+            Type avg = gAverage(vals);
+
+            bool noWarn = false;
+
+            if (!noWarn)
+            {
+                MinMax<Type> range = gMinMax(vals);
+
+                if (range.mag() > SMALL)
+                {
+                    WarningInFunction
+                        << "The min/max ranges differ " << range
+                        << " - using average " << avg << nl;
+                }
+            }
+
+            fld.primitiveFieldRef() = avg;
+        }
+
+        correctField(fld);
+
+        return tfield;
+    }
+
+
+    const objectRegistry& obr = meshRef.thisDb();
+
+    if (searchInMemory() && obr.foundObject<GeomField>(name))
+    {
+        if (debug)
+        {
+            Info<< "Retrieve registered: " << name << nl;
+        }
+
+        const GeomField& origFld = obr.lookupObject<GeomField>(name);
+
+        // Avoid shadowing the original object
+
+        tfield.reset
+        (
+            GeomField::New(name + "_exprDriverCopy", origFld)
+        );
+
+        if (getOldTime)
+        {
+            if (debug)
+            {
+                Info<< "Getting oldTime of " << name << " has "
+                    << origFld.nOldTimes() << endl;
+            }
+
+            if (!origFld.nOldTimes() && this->prevIterIsOldTime())
+            {
+                if (debug)
+                {
+                    Info<< "No oldTime, using previous iteration" << endl;
+                }
+                tfield.ref().oldTime() = origFld.prevIter();
+            }
+        }
+    }
+    else if (searchFiles() && getTypeOfField(name) == GeomField::typeName)
+    {
+        if (debug)
+        {
+            Info<< "Reading " << name << " from disc" << endl;
+        }
+
+        tfield.reset
+        (
+            this->readAndRegister<GeomField>(name, meshRef)
+        );
+        // oldTime automatically read
+    }
+
+    if (debug)
+    {
+        Info<< "field: valid()=" << tfield.valid() << endl;
+    }
+
+    if (tfield.valid())
+    {
+        GeomField& fld = tfield.ref();
+
+        if (debug)
+        {
+            Info<< "Valid " << name << " found. Removing dimensions" << nl;
+        }
+
+        fld.dimensions().clear();
+
+        if (fld.nOldTimes())
+        {
+            if (debug)
+            {
+                Info<< "Removing dimensions of oldTime of " << name
+                    << " has " << fld.nOldTimes() << nl;
+            }
+
+            // Switch dimension checking off
+            const int oldDebug = dimensionSet::debug;
+            dimensionSet::debug = 0;
+
+            // go through ALL old times
+            GeomField* fp = &(fld);
+
+            while (fp->nOldTimes())
+            {
+                fp = &(fp->oldTime());
+                fp->dimensions().clear();
+            }
+
+            // Restore old value of dimension checking
+            dimensionSet::debug = oldDebug;
+        }
+    }
+    else if (mandatory)
+    {
+        FatalErrorInFunction
+            << "Could not find field " << name
+            << " in registry or on file-system" << nl
+            << exit(FatalError);
+    }
+
+    return tfield;
+}
+
+
+template<class T>
+Foam::autoPtr<T> Foam::expressions::fvExprDriver::getTopoSet
+(
+    const fvMesh& mesh,
+    const word& name,
+    SetOrigin& origin
+) const
+{
+    // Avoid possible name clashes
+    const word regName = name + "RegisteredNameFor" + T::typeName;
+
+    if (debug)
+    {
+        Info<< "Looking for " << T::typeName << " named " << name;
+
+        Info<< " or registered as " << regName << " with mesh "
+            << "Caching:" << cacheSets()
+            << " Found:" << (mesh.foundObject<T>(name))
+            << " Found registered:" << mesh.foundObject<T>(regName)
+            << endl;
+    }
+
+
+    origin = SetOrigin::INVALID;
+    autoPtr<T> setPtr;
+
+    if
+    (
+        !cacheSets()
+     ||
+        (
+            !mesh.thisDb().foundObject<T>(regName)
+         && !mesh.thisDb().foundObject<T>(name)
+        )
+    )
+    {
+        if (debug)
+        {
+            Info<< "Constructing new " << T::typeName << ' ' << name << nl;
+
+            if (debug > 1)
+            {
+                Pout<< mesh.thisDb().names();
+            }
+        }
+
+        origin = SetOrigin::FILE;
+        setPtr.reset(new T(mesh, name, IOobject::MUST_READ));
+
+        if (cacheSets())
+        {
+            if (debug)
+            {
+                Info<< "Registering a copy of " << name << " with mesh" << nl;
+            }
+
+            autoPtr<T> toCache(new T(mesh, regName, *setPtr));
+            toCache->store(toCache);
+        }
+    }
+    else
+    {
+        const T* ptr = mesh.thisDb().findObject<T>(name);
+
+        if (ptr)
+        {
+            if (debug)
+            {
+                Info<< "Getting existing " << name << endl;
+            }
+
+            origin = SetOrigin::MEMORY;
+            setPtr.reset(new T(mesh, name, *ptr));
+        }
+        else
+        {
+            if (debug)
+            {
+                Info<< "Getting existing " << regName << endl;
+            }
+
+            origin = SetOrigin::CACHE;
+            setPtr.reset(new T(mesh, name, mesh.lookupObject<T>(regName)));
+        }
+    }
+
+
+    return setPtr;
+}
+
+
+template<class T>
+bool Foam::expressions::fvExprDriver::updateSet
+(
+    autoPtr<T>& setPtr,
+    const word& name,
+    SetOrigin origin
+) const
+{
+    const label oldSize = setPtr->size();
+
+    bool updated = false;
+    const polyMesh& mesh = dynamic_cast<const polyMesh&>(setPtr->db());
+
+    if (debug)
+    {
+        Info<< "UpdateSet: " << setPtr->name() << " Id: " << name
+            << " Origin: " << int(origin) << endl;
+    }
+
+    switch (origin)
+    {
+        case SetOrigin::FILE:
+        {
+            IOobject header
+            (
+                name,
+                mesh.time().timeName(),
+                polyMesh::meshSubDir/"sets",
+                mesh,
+                IOobject::MUST_READ,
+                IOobject::NO_WRITE
+            );
+
+            if (header.typeHeaderOk<T>())
+            {
+                if (debug)
+                {
+                    Pout<< "Rereading from "
+                        << header.localFilePath(T::typeName) << endl;
+                }
+                setPtr.reset(new T(header));
+                updated = true;
+            }
+            break;
+        }
+
+        case SetOrigin::NEW:
+        case SetOrigin::MEMORY:
+        case SetOrigin::CACHE:
+        {
+            if (origin == SetOrigin::NEW)
+            {
+                WarningInFunction
+                    << "State NEW shouldn't exist"
+                    << endl;
+            }
+
+            word sName = name;
+
+            const T* ptr = mesh.thisDb().findObject<T>(name);
+
+            if (ptr)
+            {
+                if (debug)
+                {
+                    Info<< "Found " << name
+                        << " and rereading it" << endl;
+                }
+
+                setPtr.reset(new T(mesh, name, *ptr));
+            }
+            else
+            {
+                FatalErrorInFunction
+                    << name << " Not found" << endl
+                    << "In registry: " << mesh.thisDb().names() << endl
+                    << exit(FatalError);
+            }
+            updated = true;
+            break;
+        }
+
+        case INVALID:
+        {
+            FatalErrorInFunction
+                << T::typeName << ' ' << name << " is invalid" << endl
+                << exit(FatalError);
+            break;
+        }
+
+        default:
+        {
+            if (debug)
+            {
+                Info<< "Origin " << int(origin) << " not implemented" << endl;
+            }
+            break;
+        }
+    }
+
+    if (debug)
+    {
+        Pout<< name << " old size " << oldSize << " new: "
+            << setPtr->size() << endl;
+    }
+
+    return updated;
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/fields/base/patchExprFieldBase.C b/src/finiteVolume/expressions/fields/base/patchExprFieldBase.C
new file mode 100644
index 0000000000000000000000000000000000000000..a00d9b115ddd053d7ec8a49bd77b819f9535da43
--- /dev/null
+++ b/src/finiteVolume/expressions/fields/base/patchExprFieldBase.C
@@ -0,0 +1,186 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Original code Copyright (C) 2011-2018 Bernhard Gschaider
+    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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "patchExprFieldBase.H"
+#include "facePointPatch.H"
+#include "fvMesh.H"
+#include "fvPatch.H"
+#include "pointMesh.H"
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+const Foam::fvPatch&
+Foam::expressions::patchExprFieldBase::getFvPatch(const facePointPatch& pp)
+{
+    const polyMesh& pmesh = pp.boundaryMesh().mesh().mesh();
+
+    const fvMesh* meshptr = isA<fvMesh>(pmesh);
+
+    if (!meshptr)
+    {
+        FatalErrorInFunction
+            << "Point patch not attached to a base fvMesh, "
+            << "cannot use patch expressions" << nl << endl
+            << exit(FatalError);
+    }
+
+    return meshptr->boundary()[pp.index()];
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::expressions::patchExprFieldBase::patchExprFieldBase()
+:
+    patchExprFieldBase(false)
+{}
+
+
+Foam::expressions::patchExprFieldBase::patchExprFieldBase
+(
+    bool allowGradient
+)
+:
+    debug_(false),
+    allowGradient_(allowGradient),
+    evalOnConstruct_(false),
+    valueExpr_(),
+    gradExpr_(),
+    fracExpr_()
+{}
+
+
+Foam::expressions::patchExprFieldBase::patchExprFieldBase
+(
+    const dictionary& dict,
+    bool allowGradient,
+    bool isPointVal
+)
+:
+    debug_(dict.lookupOrDefault("debug", false)),
+    allowGradient_(allowGradient),
+    evalOnConstruct_(dict.lookupOrDefault<bool>("evalOnConstruct", false)),
+    valueExpr_(),
+    gradExpr_(),
+    fracExpr_()
+{
+    if (debug_)
+    {
+        Info<< "Expression BC with " << dict << nl;
+    }
+
+    string expr;
+
+    if (dict.readIfPresent("valueExpr", expr))
+    {
+        valueExpr_ = expressions::exprString(expr, dict);
+    }
+    else
+    {
+        // No value expression - same as Zero
+        if (debug_)
+        {
+            Info<< "No valueExpr" << nl;
+        }
+    }
+
+    if (allowGradient)
+    {
+        if (dict.readIfPresent("gradientExpr", expr))
+        {
+            gradExpr_ = expressions::exprString(expr, dict);
+        }
+        else
+        {
+            // No gradient expression - same as Zero
+
+            if (debug_)
+            {
+                Info<< "No gradientExpr" << nl;
+            }
+        }
+
+        if (dict.readIfPresent("fractionExpr", expr))
+        {
+            if (!expr.empty() && expr != "0")
+            {
+                if (isPointVal)
+                {
+                    expr = "toPoint(" + expr + ")";
+                }
+
+                fracExpr_ = expressions::exprString(expr, dict);
+            }
+        }
+        else
+        {
+            // No fraction expression - same as 1 (one)
+            // Mixed BC may elect to simply ignore gradient expression
+        }
+    }
+}
+
+
+Foam::expressions::patchExprFieldBase::patchExprFieldBase
+(
+    const patchExprFieldBase& rhs
+)
+:
+    debug_(rhs.debug_),
+    allowGradient_(rhs.allowGradient_),
+    evalOnConstruct_(rhs.evalOnConstruct_),
+    valueExpr_(rhs.valueExpr_),
+    gradExpr_(rhs.gradExpr_),
+    fracExpr_(rhs.fracExpr_)
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::expressions::patchExprFieldBase::write(Ostream& os) const
+{
+    os.writeEntryIfDifferent<bool>("evalOnConstruct", false, evalOnConstruct_);
+
+    // Do not emit debug_ value
+
+    if (!valueExpr_.empty())
+    {
+        os.writeEntry("valueExpr", valueExpr_);
+    }
+    if (!gradExpr_.empty())
+    {
+        os.writeEntry("gradientExpr", gradExpr_);
+    }
+    if (!fracExpr_.empty())
+    {
+        os.writeEntry("fractionExpr", fracExpr_);
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/fields/base/patchExprFieldBase.H b/src/finiteVolume/expressions/fields/base/patchExprFieldBase.H
new file mode 100644
index 0000000000000000000000000000000000000000..5c802b10aa5e13952af01febf009fd4040472680
--- /dev/null
+++ b/src/finiteVolume/expressions/fields/base/patchExprFieldBase.H
@@ -0,0 +1,131 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Original code Copyright (C) 2011-2018 Bernhard Gschaider
+    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::expressions::patchExprFieldBase
+
+Description
+    Base class for managing patches with expressions.
+    The expected input supports values, gradients and mixed conditions
+
+Usage
+    \table
+        Property     | Description                          | Required | Default
+        valueExpr    | expression for fixed value           | no  | 0
+        gradientExpr | expression for patch normal gradient | no  | 0
+        fractionExpr | expression for value fraction weight | no  | 1
+    \endtable
+
+SourceFiles
+    patchExprFieldBase.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef expressions_patchExprFieldBase_H
+#define expressions_patchExprFieldBase_H
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#include "dictionary.H"
+#include "exprString.H"
+
+namespace Foam
+{
+
+// Forward Declarations
+class facePointPatch;
+class fvPatch;
+
+namespace expressions
+{
+
+/*---------------------------------------------------------------------------*\
+                     Class patchExprFieldBase Declaration
+\*---------------------------------------------------------------------------*/
+
+class patchExprFieldBase
+{
+protected:
+
+    // Protected Data
+
+        bool debug_;
+        bool allowGradient_;
+
+        //- Slightly dodgy concept here
+        bool evalOnConstruct_;
+
+        // The expressions
+        expressions::exprString valueExpr_;
+        expressions::exprString gradExpr_;
+        expressions::exprString fracExpr_;
+
+
+public:
+
+    // Static Methods
+
+       //- Find (guess) fvPatch from a pointPatch
+       static const fvPatch& getFvPatch(const facePointPatch& fp);
+
+
+    // Constructors
+
+        //- Null constructor
+        patchExprFieldBase();
+
+        //- Construct with specified gradient handling
+        explicit patchExprFieldBase(bool allowGradient);
+
+        //- Construct from dictionary
+        explicit patchExprFieldBase
+        (
+            const dictionary& dict,
+            bool allowGradient = false,
+            bool isPointVal = false
+        );
+
+        //- Copy constructor
+        patchExprFieldBase(const patchExprFieldBase& rhs);
+
+
+    // Member Functions
+
+        //- Write
+        void write(Ostream& os) const;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace expressions
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/fields/fvPatchFields/exprFixedValueFvPatchField.C b/src/finiteVolume/expressions/fields/fvPatchFields/exprFixedValueFvPatchField.C
new file mode 100644
index 0000000000000000000000000000000000000000..35eab824d214d988887a7c7873365ed12697e255
--- /dev/null
+++ b/src/finiteVolume/expressions/fields/fvPatchFields/exprFixedValueFvPatchField.C
@@ -0,0 +1,215 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Original code Copyright (C) 2009-2018 Bernhard Gschaider
+    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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "exprFixedValueFvPatchField.H"
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+template<class Type>
+void Foam::exprFixedValueFvPatchField<Type>::setDebug()
+{
+    if (expressions::patchExprFieldBase::debug_ && !debug)
+    {
+        debug = 1;
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+template<class Type>
+Foam::exprFixedValueFvPatchField<Type>::exprFixedValueFvPatchField
+(
+    const fvPatch& p,
+    const DimensionedField<Type, volMesh>& iF
+)
+:
+    fixedValueFvPatchField<Type>(p, iF),
+    expressions::patchExprFieldBase(false),
+    driver_(this->patch())
+{}
+
+
+template<class Type>
+Foam::exprFixedValueFvPatchField<Type>::exprFixedValueFvPatchField
+(
+    const exprFixedValueFvPatchField<Type>& ptf,
+    const fvPatch& p,
+    const DimensionedField<Type, volMesh>& iF,
+    const fvPatchFieldMapper& mapper
+)
+:
+    fixedValueFvPatchField<Type>(ptf, p, iF, mapper),
+    expressions::patchExprFieldBase(ptf),
+    driver_(this->patch(), ptf.driver_)
+{
+    setDebug();
+    DebugInFunction << nl;
+}
+
+
+template<class Type>
+Foam::exprFixedValueFvPatchField<Type>::exprFixedValueFvPatchField
+(
+    const fvPatch& p,
+    const DimensionedField<Type, volMesh>& iF,
+    const dictionary& dict,
+    const bool valueRequired
+)
+:
+    fixedValueFvPatchField<Type>(p, iF),
+    expressions::patchExprFieldBase(dict),
+    driver_(this->patch(), dict)
+{
+    setDebug();
+    DebugInFunction << nl;
+
+    // Basic sanity
+    if (this->valueExpr_.empty())
+    {
+        FatalIOErrorInFunction(dict)
+            << "The valueExpr was not defined!" << nl
+            << exit(FatalIOError);
+    }
+
+    driver_.readDict(dict);
+
+    if (dict.found("value"))
+    {
+        fvPatchField<Type>::operator=
+        (
+            Field<Type>("value", dict, p.size())
+        );
+    }
+    else
+    {
+        (*this) == this->patchInternalField();
+
+        WarningInFunction
+            << "No value defined for "
+            << this->internalField().name() << " on "
+            << this->patch().name() << " - setting to internalField value "
+            << nl;
+    }
+
+    if (this->evalOnConstruct_)
+    {
+        // For potentialFoam or other solvers that don't evaluate
+        this->evaluate();
+    }
+}
+
+
+template<class Type>
+Foam::exprFixedValueFvPatchField<Type>::exprFixedValueFvPatchField
+(
+    const exprFixedValueFvPatchField<Type>& ptf
+)
+:
+    fixedValueFvPatchField<Type>(ptf),
+    expressions::patchExprFieldBase(ptf),
+    driver_(this->patch(), ptf.driver_)
+{
+    setDebug();
+    DebugInFunction << nl;
+}
+
+
+template<class Type>
+Foam::exprFixedValueFvPatchField<Type>::exprFixedValueFvPatchField
+(
+    const exprFixedValueFvPatchField<Type>& ptf,
+    const DimensionedField<Type, volMesh>& iF
+)
+:
+    fixedValueFvPatchField<Type>(ptf, iF),
+    expressions::patchExprFieldBase(ptf),
+    driver_(this->patch(), ptf.driver_)
+{
+    setDebug();
+    DebugInFunction << nl;
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+template<class Type>
+void Foam::exprFixedValueFvPatchField<Type>::updateCoeffs()
+{
+    if (debug)
+    {
+        InfoInFunction
+            << "Value: " << this->valueExpr_ << nl
+            << "Variables: ";
+        driver_.writeVariableStrings(Info) << endl;
+    }
+
+    if (this->updated())
+    {
+        return;
+    }
+
+    DebugInFunction
+        << "updating" << nl;
+
+    // Expression evaluation
+    {
+        driver_.clearVariables();
+
+        if (this->valueExpr_.empty())
+        {
+            (*this) == Zero;
+        }
+        else
+        {
+            tmp<Field<Type>> tresult(driver_.evaluate<Type>(this->valueExpr_));
+
+            if (debug)
+            {
+                Info<< "Evaluated: " << tresult();
+            }
+
+            (*this) == tresult;
+        }
+    }
+
+    fixedValueFvPatchField<Type>::updateCoeffs();
+}
+
+
+template<class Type>
+void Foam::exprFixedValueFvPatchField<Type>::write(Ostream& os) const
+{
+    fixedValueFvPatchField<Type>::write(os);
+    expressions::patchExprFieldBase::write(os);
+
+    // driver_.writeCommon(os, this->debug_ || debug);
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/fields/fvPatchFields/exprFixedValueFvPatchField.H b/src/finiteVolume/expressions/fields/fvPatchFields/exprFixedValueFvPatchField.H
new file mode 100644
index 0000000000000000000000000000000000000000..a1282f9542ad98e469868e784d408bee7179f4fb
--- /dev/null
+++ b/src/finiteVolume/expressions/fields/fvPatchFields/exprFixedValueFvPatchField.H
@@ -0,0 +1,175 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Original code Copyright (C) 2009-2018 Bernhard Gschaider
+    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::exprFixedValueFvPatchField
+
+Description
+    A fixed value boundary condition with expressions.
+
+Usage
+    \table
+        Property     | Description                          | Required | Default
+        value        | fixed value                          | yes |
+        valueExpr    | expression for fixed value           | yes |
+    \endtable
+
+SourceFiles
+    exprFixedValueFvPatchField.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef exprFixedValueFvPatchField_H
+#define exprFixedValueFvPatchField_H
+
+#include "fixedValueFvPatchField.H"
+#include "patchExprFieldBase.H"
+#include "patchExprDriver.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                 Class exprFixedValueFvPatchField Declaration
+\*---------------------------------------------------------------------------*/
+
+template<class Type>
+class exprFixedValueFvPatchField
+:
+    public fixedValueFvPatchField<Type>,
+    public expressions::patchExprFieldBase
+{
+protected:
+
+    // Protected Data
+
+        //- The expression driver
+        expressions::patchExpr::parseDriver driver_;
+
+
+    // Protected Member Functions
+
+        //- Set debug ON if "debug" is enabled
+        void setDebug();
+
+
+public:
+
+    //- Runtime type information
+    TypeName("exprFixedValue");
+
+
+    // Constructors
+
+        //- Construct from patch and internal field
+        exprFixedValueFvPatchField
+        (
+            const fvPatch& p,
+            const DimensionedField<Type, volMesh>&
+        );
+
+        //- Construct from patch, internal field and dictionary
+        exprFixedValueFvPatchField
+        (
+            const fvPatch&,
+            const DimensionedField<Type, volMesh>&,
+            const dictionary& dict,
+            const bool valueRequired=true
+        );
+
+        //- Construct by mapping given exprFixedValueFvPatchField
+        //- onto a new patch
+        exprFixedValueFvPatchField
+        (
+            const exprFixedValueFvPatchField<Type>&,
+            const fvPatch&,
+            const DimensionedField<Type, volMesh>&,
+            const fvPatchFieldMapper&
+        );
+
+        //- Construct as copy
+        exprFixedValueFvPatchField
+        (
+            const exprFixedValueFvPatchField<Type>&
+        );
+
+
+        //- Construct and return a clone
+        virtual tmp<fvPatchField<Type>> clone() const
+        {
+            return tmp<fvPatchField<Type>>
+            (
+                new exprFixedValueFvPatchField<Type>(*this)
+            );
+        }
+
+        //- Construct as copy setting internal field reference
+        exprFixedValueFvPatchField
+        (
+            const exprFixedValueFvPatchField<Type>&,
+            const DimensionedField<Type, volMesh>&
+        );
+
+        //- Construct and return a clone setting internal field reference
+        virtual tmp<fvPatchField<Type>> clone
+        (
+            const DimensionedField<Type, volMesh>& iF
+        ) const
+        {
+            return tmp<fvPatchField<Type>>
+            (
+                new exprFixedValueFvPatchField<Type>(*this, iF)
+            );
+        }
+
+
+    // Member Functions
+
+        //- Update the coefficients associated with the patch field
+        virtual void updateCoeffs();
+
+        //- Write
+        virtual void write(Ostream& os) const;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+    #include "exprFixedValueFvPatchField.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/fields/fvPatchFields/exprFixedValueFvPatchFields.C b/src/finiteVolume/expressions/fields/fvPatchFields/exprFixedValueFvPatchFields.C
new file mode 100644
index 0000000000000000000000000000000000000000..5695a23af92259cdd71a90f05bb6c0ddf6d2264d
--- /dev/null
+++ b/src/finiteVolume/expressions/fields/fvPatchFields/exprFixedValueFvPatchFields.C
@@ -0,0 +1,45 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "exprFixedValueFvPatchFields.H"
+#include "volFields.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+makePatchFields(exprFixedValue);
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/fields/fvPatchFields/exprFixedValueFvPatchFields.H b/src/finiteVolume/expressions/fields/fvPatchFields/exprFixedValueFvPatchFields.H
new file mode 100644
index 0000000000000000000000000000000000000000..cab541738ed23f8bbd6e25bd6d90bb2a47c5fbf9
--- /dev/null
+++ b/src/finiteVolume/expressions/fields/fvPatchFields/exprFixedValueFvPatchFields.H
@@ -0,0 +1,51 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef exprFixedValueFvPatchFields_H
+#define exprFixedValueFvPatchFields_H
+
+#include "exprFixedValueFvPatchField.H"
+#include "fieldTypes.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+makePatchTypeFieldTypedefs(exprFixedValue);
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/fields/fvPatchFields/exprFixedValueFvPatchFieldsFwd.H b/src/finiteVolume/expressions/fields/fvPatchFields/exprFixedValueFvPatchFieldsFwd.H
new file mode 100644
index 0000000000000000000000000000000000000000..375adc50be8bbbf94d455c65895f751c6b80f510
--- /dev/null
+++ b/src/finiteVolume/expressions/fields/fvPatchFields/exprFixedValueFvPatchFieldsFwd.H
@@ -0,0 +1,52 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef exprFixedValueFvPatchFieldsFwd_H
+#define exprFixedValueFvPatchFieldsFwd_H
+
+#include "fieldTypes.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+template<class Type> class exprFixedValueFvPatchField;
+
+makePatchTypeFieldTypedefs(exprFixedValue);
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/fields/fvPatchFields/exprMixedFvPatchField.C b/src/finiteVolume/expressions/fields/fvPatchFields/exprMixedFvPatchField.C
new file mode 100644
index 0000000000000000000000000000000000000000..c145744333a94b4c0205dec91235149e10b78f4a
--- /dev/null
+++ b/src/finiteVolume/expressions/fields/fvPatchFields/exprMixedFvPatchField.C
@@ -0,0 +1,309 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Original code Copyright (C) 2009-2018 Bernhard Gschaider
+    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/>.
+\*---------------------------------------------------------------------------*/
+
+#include "exprMixedFvPatchField.H"
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+template<class Type>
+void Foam::exprMixedFvPatchField<Type>::setDebug()
+{
+    if (expressions::patchExprFieldBase::debug_ && !debug)
+    {
+        debug = 1;
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+template<class Type>
+Foam::exprMixedFvPatchField<Type>::exprMixedFvPatchField
+(
+    const fvPatch& p,
+    const DimensionedField<Type, volMesh>& iF
+)
+:
+    mixedFvPatchField<Type>(p, iF),
+    expressions::patchExprFieldBase(true),  // allowGradient
+    driver_(this->patch())
+{
+    this->refValue() = Zero;
+    this->refGrad() = Zero;
+    this->valueFraction() = scalar(1);
+}
+
+
+template<class Type>
+Foam::exprMixedFvPatchField<Type>::exprMixedFvPatchField
+(
+    const exprMixedFvPatchField<Type>& ptf,
+    const fvPatch& p,
+    const DimensionedField<Type, volMesh>& iF,
+    const fvPatchFieldMapper& mapper
+)
+:
+    mixedFvPatchField<Type>(ptf, p, iF, mapper),
+    expressions::patchExprFieldBase(ptf),
+    driver_(this->patch(), ptf.driver_)
+{
+    setDebug();
+    DebugInFunction << nl;
+}
+
+
+template<class Type>
+Foam::exprMixedFvPatchField<Type>::exprMixedFvPatchField
+(
+    const fvPatch& p,
+    const DimensionedField<Type, volMesh>& iF,
+    const dictionary& dict
+)
+:
+    mixedFvPatchField<Type>(p, iF),
+    expressions::patchExprFieldBase(dict, true),
+    driver_(this->patch(), dict)
+{
+    setDebug();
+    DebugInFunction << nl;
+
+    // Basic sanity checks
+    if (this->valueExpr_.empty() && this->gradExpr_.empty())
+    {
+        if (this->valueExpr_.empty())
+        {
+            FatalIOErrorInFunction(dict)
+                << "The valueExpr was not defined!" << nl
+                << exit(FatalIOError);
+        }
+        if (this->gradExpr_.empty())
+        {
+            FatalIOErrorInFunction(dict)
+                << "The gradientExpr was not defined!" << nl
+                << exit(FatalIOError);
+        }
+    }
+
+
+    driver_.readDict(dict);
+
+    // Similar to fvPatchField constructor, which we have bypassed
+    dict.readIfPresent("patchType", this->patchType());
+
+    if (dict.found("refValue"))
+    {
+        this->refValue() = Field<Type>("refValue", dict, p.size());
+    }
+    else
+    {
+        this->refValue() = this->patchInternalField();
+    }
+
+    if (dict.found("value"))
+    {
+        fvPatchField<Type>::operator=
+        (
+            Field<Type>("value", dict, p.size())
+        );
+
+        if (!dict.found("refValue"))
+        {
+            // Ensure refValue has a sensible value for the "update" below
+            this->refValue() = Field<Type>("value", dict, p.size());
+        }
+    }
+    else
+    {
+        fvPatchField<Type>::operator=(this->refValue());
+
+        WarningInFunction
+            << "No value defined for "
+            << this->internalField().name()
+            << " on " << this->patch().name() << " therefore using "
+            << "the internal field next to the patch"
+            << endl;
+    }
+
+
+    if (dict.found("refGradient"))
+    {
+        this->refGrad() = Field<Type>("refGradient", dict, p.size());
+    }
+    else
+    {
+        this->refGrad() = Zero;
+    }
+
+    if (dict.found("valueFraction"))
+    {
+        this->valueFraction() = Field<scalar>("valueFraction", dict, p.size());
+    }
+    else
+    {
+        this->valueFraction() = 1;
+    }
+
+
+    if (this->evalOnConstruct_)
+    {
+        // For potentialFoam or other solvers that don't evaluate
+        this->evaluate();
+    }
+    else
+    {
+        // Emulate mixedFvPatchField<Type>::evaluate,
+        // but avoid our own updateCoeffs
+        if (!this->updated())
+        {
+            this->mixedFvPatchField<Type>::updateCoeffs();
+        }
+
+        Field<Type>::operator=
+        (
+            this->valueFraction()*this->refValue()
+          +
+            (1.0 - this->valueFraction())*
+            (
+                this->patchInternalField()
+              + this->refGrad()/this->patch().deltaCoeffs()
+            )
+        );
+
+        fvPatchField<Type>::evaluate();
+    }
+}
+
+
+template<class Type>
+Foam::exprMixedFvPatchField<Type>::exprMixedFvPatchField
+(
+    const exprMixedFvPatchField<Type>& ptf
+)
+:
+    mixedFvPatchField<Type>(ptf),
+    expressions::patchExprFieldBase(ptf),
+    driver_(this->patch(), ptf.driver_)
+{
+    setDebug();
+    DebugInFunction << nl;
+}
+
+
+template<class Type>
+Foam::exprMixedFvPatchField<Type>::exprMixedFvPatchField
+(
+    const exprMixedFvPatchField<Type>& ptf,
+    const DimensionedField<Type, volMesh>& iF
+)
+:
+    mixedFvPatchField<Type>(ptf, iF),
+    expressions::patchExprFieldBase(ptf),
+    driver_(this->patch(), ptf.driver_)
+{
+    setDebug();
+    DebugInFunction << nl;
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+template<class Type>
+void Foam::exprMixedFvPatchField<Type>::updateCoeffs()
+{
+    if (debug)
+    {
+        InfoInFunction
+            << "Value: " << this->valueExpr_ << nl
+            << "Gradient: " << this->gradExpr_ << nl
+            << "Fraction: " << this->fracExpr_ << nl
+            << "Variables: ";
+
+        driver_.writeVariableStrings(Info) << endl;
+    }
+
+    if (this->updated())
+    {
+        return;
+    }
+
+    DebugInFunction << " - updating" << nl;
+
+
+    // Expression evaluation
+    {
+        driver_.clearVariables();
+
+        if (this->valueExpr_.empty())
+        {
+            this->refValue() = Zero;
+        }
+        else
+        {
+            this->refValue() = driver_.evaluate<Type>(this->valueExpr_);
+        }
+
+        bool evalGrad = !this->gradExpr_.empty();
+
+        if (this->fracExpr_.empty() || this->fracExpr_ == "1")
+        {
+            evalGrad = false;
+            this->valueFraction() = scalar(1);
+        }
+        else if (this->fracExpr_ == "0")
+        {
+            this->valueFraction() = Zero;
+        }
+        else
+        {
+            this->valueFraction() = driver_.evaluate<scalar>(this->fracExpr_);
+        }
+
+        if (evalGrad)
+        {
+            this->refGrad() = driver_.evaluate<Type>(this->gradExpr_);
+        }
+        else
+        {
+            this->refGrad() = Zero;
+        }
+    }
+
+    mixedFvPatchField<Type>::updateCoeffs();
+}
+
+
+template<class Type>
+void Foam::exprMixedFvPatchField<Type>::write(Ostream& os) const
+{
+    mixedFvPatchField<Type>::write(os);
+    expressions::patchExprFieldBase::write(os);
+
+    // driver_.writeCommon(os, this->debug_ || debug);
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/fields/fvPatchFields/exprMixedFvPatchField.H b/src/finiteVolume/expressions/fields/fvPatchFields/exprMixedFvPatchField.H
new file mode 100644
index 0000000000000000000000000000000000000000..53cd1bb75a67afa45d04f67290b2596bcbbef561
--- /dev/null
+++ b/src/finiteVolume/expressions/fields/fvPatchFields/exprMixedFvPatchField.H
@@ -0,0 +1,172 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Original code Copyright (C) 2009-2018 Bernhard Gschaider
+    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::exprMixedFvPatchField
+
+Description
+    A mixed boundary condition with expressions.
+
+Usage
+    \table
+        Property     | Description                          | Required | Default
+        valueExpr    | expression for fixed value           | no  | 0
+        gradientExpr | expression for patch normal gradient | no  | 0
+        fractionExpr | expression for value weighting       | no  | 1
+    \endtable
+
+SourceFiles
+    exprMixedFvPatchField.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef exprMixedFvPatchField_H
+#define exprMixedFvPatchField_H
+
+#include "mixedFvPatchField.H"
+#include "patchExprFieldBase.H"
+#include "patchExprDriver.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                    Class exprMixedFvPatchField Declaration
+\*---------------------------------------------------------------------------*/
+
+template<class Type>
+class exprMixedFvPatchField
+:
+    public mixedFvPatchField<Type>,
+    public expressions::patchExprFieldBase
+{
+protected:
+
+    // Protected Data
+
+        //- The expression driver
+        expressions::patchExpr::parseDriver driver_;
+
+
+    // Protected Member Functions
+
+        //- Set debug ON if "debug" is enabled
+        void setDebug();
+
+
+public:
+
+    //- Runtime type information
+    TypeName("exprMixed");
+
+
+    // Constructors
+
+        //- Construct from patch and internal field
+        exprMixedFvPatchField
+        (
+            const fvPatch& p,
+            const DimensionedField<Type, volMesh>&
+        );
+
+        //- Construct from patch, internal field and dictionary
+        exprMixedFvPatchField
+        (
+            const fvPatch&,
+            const DimensionedField<Type, volMesh>&,
+            const dictionary& dict
+        );
+
+        //- Construct by mapping given exprMixedFvPatchField onto a new patch
+        exprMixedFvPatchField
+        (
+            const exprMixedFvPatchField<Type>&,
+            const fvPatch&,
+            const DimensionedField<Type, volMesh>&,
+            const fvPatchFieldMapper&
+        );
+
+        //- Construct as copy
+        exprMixedFvPatchField
+        (
+            const exprMixedFvPatchField<Type>&
+        );
+
+        //- Construct and return a clone
+        virtual tmp<fvPatchField<Type>> clone() const
+        {
+            return tmp<fvPatchField<Type>>
+            (
+                new exprMixedFvPatchField<Type>(*this)
+            );
+        }
+
+        //- Construct as copy setting internal field reference
+        exprMixedFvPatchField
+        (
+            const exprMixedFvPatchField<Type>&,
+            const DimensionedField<Type, volMesh>&
+        );
+
+        //- Construct and return a clone setting internal field reference
+        virtual tmp<fvPatchField<Type> > clone
+        (
+            const DimensionedField<Type, volMesh>& iF
+        ) const
+        {
+            return tmp<fvPatchField<Type>>
+            (
+                new exprMixedFvPatchField<Type>(*this, iF)
+            );
+        }
+
+
+    // Member Functions
+
+        //- Update the coefficients associated with the patch field
+        virtual void updateCoeffs();
+
+        //- Write
+        virtual void write(Ostream& os) const;
+};
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+    #include "exprMixedFvPatchField.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/fields/fvPatchFields/exprMixedFvPatchFields.C b/src/finiteVolume/expressions/fields/fvPatchFields/exprMixedFvPatchFields.C
new file mode 100644
index 0000000000000000000000000000000000000000..730fee6baa92632203cb6a48f1c7ba7cd8f74491
--- /dev/null
+++ b/src/finiteVolume/expressions/fields/fvPatchFields/exprMixedFvPatchFields.C
@@ -0,0 +1,45 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "exprMixedFvPatchFields.H"
+#include "volFields.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+makePatchFields(exprMixed);
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/fields/fvPatchFields/exprMixedFvPatchFields.H b/src/finiteVolume/expressions/fields/fvPatchFields/exprMixedFvPatchFields.H
new file mode 100644
index 0000000000000000000000000000000000000000..40531f0295a2c90ad287b3c27b62f05eff595c6b
--- /dev/null
+++ b/src/finiteVolume/expressions/fields/fvPatchFields/exprMixedFvPatchFields.H
@@ -0,0 +1,51 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef exprMixedFvPatchFields_H
+#define exprMixedFvPatchFields_H
+
+#include "exprMixedFvPatchField.H"
+#include "fieldTypes.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+makePatchTypeFieldTypedefs(exprMixed);
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/fields/fvPatchFields/exprMixedFvPatchFieldsFwd.H b/src/finiteVolume/expressions/fields/fvPatchFields/exprMixedFvPatchFieldsFwd.H
new file mode 100644
index 0000000000000000000000000000000000000000..2db491b761a34d881d13dcf68b66e48ab37a8d0b
--- /dev/null
+++ b/src/finiteVolume/expressions/fields/fvPatchFields/exprMixedFvPatchFieldsFwd.H
@@ -0,0 +1,52 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef exprMixedFvPatchFieldsFwd_H
+#define exprMixedFvPatchFieldsFwd_H
+
+#include "fieldTypes.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+template<class Type> class exprMixedFvPatchField;
+
+makePatchTypeFieldTypedefs(exprMixed);
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/fields/pointPatchFields/exprValuePointPatchField.C b/src/finiteVolume/expressions/fields/pointPatchFields/exprValuePointPatchField.C
new file mode 100644
index 0000000000000000000000000000000000000000..34585032cfd8a3dedf224154a69ff7892797e747
--- /dev/null
+++ b/src/finiteVolume/expressions/fields/pointPatchFields/exprValuePointPatchField.C
@@ -0,0 +1,219 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Original code Copyright (C) 2010-2018 Bernhard Gschaider
+    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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "exprValuePointPatchField.H"
+#include "pointPatchFieldMapper.H"
+#include "typeInfo.H"
+#include "facePointPatch.H"
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+template<class Type>
+Foam::exprValuePointPatchField<Type>::exprValuePointPatchField
+(
+    const pointPatch& p,
+    const DimensionedField<Type, pointMesh>& iF
+)
+:
+    valuePointPatchField<Type>(p, iF),
+    expressions::patchExprFieldBase(false),
+    driver_
+    (
+        expressions::patchExprFieldBase::getFvPatch
+        (
+            dynamicCast<const facePointPatch>(this->patch())
+        )
+    )
+{}
+
+
+template<class Type>
+Foam::exprValuePointPatchField<Type>::exprValuePointPatchField
+(
+    const exprValuePointPatchField<Type>& ptf,
+    const pointPatch& p,
+    const DimensionedField<Type, pointMesh>& iF,
+    const pointPatchFieldMapper& mapper
+)
+:
+    valuePointPatchField<Type>(ptf, p, iF, mapper),
+    expressions::patchExprFieldBase(ptf),
+    driver_
+    (
+        expressions::patchExprFieldBase::getFvPatch
+        (
+            dynamicCast<const facePointPatch>(this->patch())
+        ),
+        ptf.driver_
+    )
+{}
+
+
+template<class Type>
+Foam::exprValuePointPatchField<Type>::exprValuePointPatchField
+(
+    const pointPatch& p,
+    const DimensionedField<Type, pointMesh>& iF,
+    const dictionary& dict
+)
+:
+    valuePointPatchField<Type>(p, iF),
+    expressions::patchExprFieldBase(dict, false, true),
+    driver_
+    (
+        expressions::patchExprFieldBase::getFvPatch
+        (
+            dynamicCast<const facePointPatch>(this->patch())
+        ),
+        dict
+    )
+{
+    // Basic sanity
+    if (this->valueExpr_.empty())
+    {
+        FatalIOErrorInFunction(dict)
+            << "The valueExpr was not defined!" << nl
+            << exit(FatalIOError);
+    }
+
+    driver_.readDict(dict);
+
+    if (dict.found("value"))
+    {
+        Field<Type>::operator=
+        (
+            Field<Type>("value", dict, p.size())
+        );
+    }
+    else
+    {
+        WarningInFunction
+            << "No value defined for "
+            << this->internalField().name()
+            << " on " << this->patch().name()
+            << endl;
+    }
+
+    if (this->evalOnConstruct_)
+    {
+        // For potentialFoam or other solvers that don't evaluate
+        this->evaluate();
+    }
+}
+
+
+template<class Type>
+Foam::exprValuePointPatchField<Type>::exprValuePointPatchField
+(
+    const exprValuePointPatchField<Type>& ptf,
+    const DimensionedField<Type, pointMesh>& iF
+)
+:
+    valuePointPatchField<Type>(ptf, iF),
+    expressions::patchExprFieldBase(ptf),
+    driver_
+    (
+        expressions::patchExprFieldBase::getFvPatch
+        (
+            dynamicCast<const facePointPatch>(this->patch())
+        ),
+        ptf.driver_
+    )
+{}
+
+
+template<class Type>
+Foam::exprValuePointPatchField<Type>::exprValuePointPatchField
+(
+    const exprValuePointPatchField<Type>& ptf
+)
+:
+    valuePointPatchField<Type>(ptf),
+    expressions::patchExprFieldBase(ptf),
+    driver_
+    (
+        expressions::patchExprFieldBase::getFvPatch
+        (
+            dynamicCast<const facePointPatch>(this->patch())
+        ),
+        ptf.driver_
+    )
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+template<class Type>
+void Foam::exprValuePointPatchField<Type>::updateCoeffs()
+{
+    if (debug)
+    {
+        InfoInFunction
+            << "Value: " << this->valueExpr_ << nl
+            << "Variables: ";
+        driver_.writeVariableStrings(Info)  << endl;
+    }
+
+    if (this->updated())
+    {
+        return;
+    }
+
+    // Expression evaluation
+    {
+        driver_.clearVariables();
+
+        if (this->valueExpr_.empty())
+        {
+            (*this) == Zero;
+        }
+        else
+        {
+            Field<Type>::operator=
+            (
+                driver_.evaluate<Type>(this->valueExpr_, true)
+            );
+        }
+    }
+
+    valuePointPatchField<Type>::updateCoeffs();
+}
+
+
+template<class Type>
+void Foam::exprValuePointPatchField<Type>::write(Ostream& os) const
+{
+    valuePointPatchField<Type>::write(os);
+    expressions::patchExprFieldBase::write(os);
+
+    this->writeEntry("value",os);
+
+    // driver_.writeCommon(os,this->debug_ || debug);
+}
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/fields/pointPatchFields/exprValuePointPatchField.H b/src/finiteVolume/expressions/fields/pointPatchFields/exprValuePointPatchField.H
new file mode 100644
index 0000000000000000000000000000000000000000..3844e0cf54be92930e3821506e7881bf73e7fcfc
--- /dev/null
+++ b/src/finiteVolume/expressions/fields/pointPatchFields/exprValuePointPatchField.H
@@ -0,0 +1,175 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Original code Copyright (C) 2010-2018 Bernhard Gschaider
+    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::exprValuePointPatchField
+
+Description
+    A fixed value point boundary condition with expressions.
+
+Usage
+    \table
+        Property     | Description                          | Required | Default
+        value        | fixed value                          | yes |
+        valueExpr    | expression for fixed value           | yes |
+    \endtable
+
+SourceFiles
+    exprValuePointPatchField.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef exprValuePointPatchField_H
+#define exprValuePointPatchField_H
+
+#include "valuePointPatchField.H"
+#include "patchExprFieldBase.H"
+#include "patchExprDriver.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                  Class exprValuePointPatchField Declaration
+\*---------------------------------------------------------------------------*/
+
+template<class Type>
+class exprValuePointPatchField
+:
+    public valuePointPatchField<Type>,
+    public expressions::patchExprFieldBase
+{
+protected:
+
+    // Protected Data
+
+        //- The expression driver
+        expressions::patchExpr::parseDriver driver_;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("exprValue");
+
+
+    // Constructors
+
+        //- Construct from patch and internal field
+        exprValuePointPatchField
+        (
+            const pointPatch&,
+            const DimensionedField<Type, pointMesh>&
+        );
+
+        //- Construct from patch, internal field and dictionary
+        exprValuePointPatchField
+        (
+            const pointPatch&,
+            const DimensionedField<Type, pointMesh>&,
+            const dictionary&
+        );
+
+        //- Construct by mapping given patchField<Type> onto a new patch
+        exprValuePointPatchField
+        (
+            const exprValuePointPatchField<Type>&,
+            const pointPatch&,
+            const DimensionedField<Type, pointMesh>&,
+            const pointPatchFieldMapper&
+        );
+
+        //- Construct as copy setting internal field reference
+        exprValuePointPatchField
+        (
+            const exprValuePointPatchField<Type>&,
+            const DimensionedField<Type, pointMesh>&
+        );
+
+        //- Construct as copy
+        exprValuePointPatchField
+        (
+            const exprValuePointPatchField<Type>&
+        );
+
+
+        //- Construct and return a clone
+        virtual autoPtr<pointPatchField<Type> > clone() const
+        {
+            return autoPtr<pointPatchField<Type>>
+            (
+                new exprValuePointPatchField<Type>
+                (
+                    *this
+                )
+            );
+        }
+
+
+        //- Construct and return a clone setting internal field reference
+        virtual autoPtr<pointPatchField<Type>> clone
+        (
+            const DimensionedField<Type, pointMesh>& iF
+        ) const
+        {
+            return autoPtr<pointPatchField<Type>>
+            (
+                new exprValuePointPatchField<Type>
+                (
+                    *this,
+                    iF
+                )
+            );
+        }
+
+
+    // Member Functions
+
+        //- Update the patch field
+        virtual void updateCoeffs();
+
+        //- Write
+        virtual void write(Ostream& os) const;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+#    include "exprValuePointPatchField.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/fields/pointPatchFields/exprValuePointPatchFields.C b/src/finiteVolume/expressions/fields/pointPatchFields/exprValuePointPatchFields.C
new file mode 100644
index 0000000000000000000000000000000000000000..e9cca570dd01603e881dfa283d9a1cefcc7c66ea
--- /dev/null
+++ b/src/finiteVolume/expressions/fields/pointPatchFields/exprValuePointPatchFields.C
@@ -0,0 +1,45 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "exprValuePointPatchFields.H"
+#include "pointPatchFields.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+makePointPatchFields(exprValue);
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/fields/pointPatchFields/exprValuePointPatchFields.H b/src/finiteVolume/expressions/fields/pointPatchFields/exprValuePointPatchFields.H
new file mode 100644
index 0000000000000000000000000000000000000000..b0035fcac80d18a491fbef43037df5849c3d439d
--- /dev/null
+++ b/src/finiteVolume/expressions/fields/pointPatchFields/exprValuePointPatchFields.H
@@ -0,0 +1,51 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef exprValuePointPatchFields_H
+#define exprValuePointPatchFields_H
+
+#include "exprValuePointPatchField.H"
+#include "fieldTypes.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+makePointPatchFieldTypedefs(exprValue);
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/patch/createCode b/src/finiteVolume/expressions/patch/createCode
new file mode 100755
index 0000000000000000000000000000000000000000..3aab7655b89ed0cd452a64bb15b3b7e0b0c43f4c
--- /dev/null
+++ b/src/finiteVolume/expressions/patch/createCode
@@ -0,0 +1,13 @@
+#!/bin/sh
+cd ${0%/*} || exit 1                        # Run from this directory
+# Manually create ragel scanner and lemon parser header
+
+prefix=patchExpr
+
+"${WM_PROJECT_DIR:?}/wmake/scripts/makeParser" \
+    -prefix="$prefix" \
+    -scanner=Scanner.rl \
+    -parser=LemonParser.lyy-m4 \
+    "$@"
+
+#------------------------------------------------------------------------------
diff --git a/src/finiteVolume/expressions/patch/patchExpr.C b/src/finiteVolume/expressions/patch/patchExpr.C
new file mode 100644
index 0000000000000000000000000000000000000000..e5a4c54ccc5390dac7ddc5ab63eb8fa1ecfefa99
--- /dev/null
+++ b/src/finiteVolume/expressions/patch/patchExpr.C
@@ -0,0 +1,45 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "patchExprFwd.H"
+#include "defineDebugSwitch.H"
+
+// * * * * * * * * * * * * * * * * Globals * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace expressions
+{
+
+defineDebugSwitchWithName(patchExpr, "patchExpr", 0);
+registerDebugSwitchWithName(patchExpr, patchExpr, "patchExpr");
+
+} // End namespace expressions
+} // End namespace Foam
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/patch/patchExprDriver.C b/src/finiteVolume/expressions/patch/patchExprDriver.C
new file mode 100644
index 0000000000000000000000000000000000000000..5f22efe3e0dec4ec842c3992fb2bda09424560f8
--- /dev/null
+++ b/src/finiteVolume/expressions/patch/patchExprDriver.C
@@ -0,0 +1,188 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "patchExprDriver.H"
+#include "patchExprScanner.H"
+#include "error.H"
+#include "fvPatch.H"
+#include "fvMesh.H"
+#include "className.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace expressions
+{
+namespace patchExpr
+{
+
+defineTypeNameAndDebug(parseDriver, 0);
+
+addNamedToRunTimeSelectionTable
+(
+    fvExprDriver,
+    parseDriver,
+    dictionary,
+    patch
+);
+
+addNamedToRunTimeSelectionTable
+(
+    fvExprDriver,
+    parseDriver,
+    idName,
+    patch
+);
+
+} // End namespace patchExpr
+} // End namespace expressions
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    static label getPatchID(const fvMesh& mesh, const word& patchName)
+    {
+        const auto& bMesh = mesh.boundaryMesh();
+
+        const label patchId = bMesh.findPatchID(patchName);
+
+        if (patchId < 0)
+        {
+            FatalErrorInFunction
+                << "No patch " << patchName << " found in "
+                << flatOutput(bMesh.names()) << nl
+                << exit(FatalError);
+        }
+        return patchId;
+    }
+
+
+    static inline const fvPatch& findFvPatch
+    (
+        const fvMesh& mesh,
+        const word& patchName
+    )
+    {
+        return mesh.boundary()[getPatchID(mesh, patchName)];
+    }
+
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+const Foam::fvPatch& Foam::expressions::patchExpr::parseDriver::getFvPatch
+(
+    const fvMesh& fvm,
+    const dictionary& dict
+)
+{
+    return findFvPatch
+    (
+        regionMesh(dict, fvm, true),
+        dict.get<word>("patch")
+    );
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::expressions::patchExpr::parseDriver::parseDriver(const fvPatch& p)
+:
+    parsing::genericRagelLemonDriver(),
+    expressions::fvExprDriver(),
+    patch_(p)
+{}
+
+
+Foam::expressions::patchExpr::parseDriver::parseDriver
+(
+    const fvPatch& p,
+    const dictionary& dict
+)
+:
+    parsing::genericRagelLemonDriver(),
+    expressions::fvExprDriver(dict),
+    patch_(p)
+{}
+
+
+Foam::expressions::patchExpr::parseDriver::parseDriver
+(
+    const fvPatch& p,
+    const parseDriver& driver_
+)
+:
+    parsing::genericRagelLemonDriver(),
+    expressions::fvExprDriver(driver_),
+    patch_(p)
+{}
+
+
+Foam::expressions::patchExpr::parseDriver::parseDriver
+(
+    const word& patchName,
+    const fvMesh& mesh
+)
+:
+    parseDriver(findFvPatch(mesh, patchName))
+{}
+
+
+Foam::expressions::patchExpr::parseDriver::parseDriver
+(
+    const dictionary& dict,
+    const fvMesh& mesh
+)
+:
+    parseDriver(getFvPatch(mesh, dict), dict)
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+unsigned Foam::expressions::patchExpr::parseDriver::parse
+(
+    const std::string& expr,
+    size_t pos,
+    size_t len
+)
+{
+    scanner scan(this->debugScanner());
+
+    scan.process(expr, pos, len, *this);
+
+    return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/patch/patchExprDriver.H b/src/finiteVolume/expressions/patch/patchExprDriver.H
new file mode 100644
index 0000000000000000000000000000000000000000..294c902cf41ab8e90cb31f3d5c698a2468797697
--- /dev/null
+++ b/src/finiteVolume/expressions/patch/patchExprDriver.H
@@ -0,0 +1,305 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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::expressions::patchExpr::parseDriver
+
+Description
+    Driver for patch expressions
+
+    In addition to the standard mathematical functions, operations and
+    logical and relational operations, the volume expression support the
+    following driver-specific functions:
+
+    Functions
+    \table
+        Function    | Description                      | Number of arguments |
+        vol         | The cell volumes                      | 0 |
+        pos         | The face centres                      | 0 |
+        pts         | The face points                       | 0 |
+        area        | The face area magnitudes              | 0 |
+        weightAverage| Area weighted average                | 1 |
+        weightSum   | Area weighted sum                     | 1 |
+        face        | The face areaNormal vectors           | 0 |
+        point       | A point-field point value             | 1 |
+        faceToPoint | Interpolate face values onto points   | 1 |
+        pointToFace | Interpolate point values onto faces   | 1 |
+        rand        | Random field                          | 0/1 |
+    \endtable
+
+SourceFiles
+    patchExprDriver.C
+    patchExprDriverFields.C
+    patchExprDriverTemplates.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef expressions_patchExprDriver_H
+#define expressions_patchExprDriver_H
+
+#include "patchExprFwd.H"
+#include "fvExprDriver.H"
+#include "Enum.H"
+#include "volFields.H"
+#include "surfaceFields.H"
+#include "pointFields.H"
+#include "genericRagelLemonDriver.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace expressions
+{
+namespace patchExpr
+{
+
+/*---------------------------------------------------------------------------*\
+                         Class parseDriver Declaration
+\*---------------------------------------------------------------------------*/
+
+class parseDriver
+:
+    public parsing::genericRagelLemonDriver,
+    public expressions::fvExprDriver
+{
+    // Private Member Functions
+
+        static const fvPatch& getFvPatch
+        (
+            const fvMesh& fvm,
+            const dictionary& dict
+        );
+
+
+protected:
+
+    // Protected Data
+
+        //- The referenced patch
+        const fvPatch& patch_;
+
+
+    // Protected Member Functions
+
+        // No copy copy construct
+        parseDriver(const parseDriver&) = delete;
+
+        // No copy assignment
+        void operator=(const parseDriver&) = delete;
+
+
+public:
+
+    ClassName("patchExpr::driver");
+
+    // Constructors
+
+        //- Construct for specified patch
+        explicit parseDriver(const fvPatch& p);
+
+        //- Construct for specified patch with given dictionary
+        parseDriver(const fvPatch& p, const dictionary& dict);
+
+        //- Construct for specified patch with copy of driver context
+        parseDriver(const fvPatch& p, const parseDriver& driver_);
+
+        //- Construct with patchName for the given mesh
+        parseDriver(const word& patchName, const fvMesh& mesh);
+
+        //- Construct with "patch" (mandatory) and "region" (optional)
+        //- specified in dictionary
+        parseDriver(const dictionary& dict, const fvMesh& mesh);
+
+        //- Clone
+        virtual autoPtr<expressions::fvExprDriver> clone() const
+        {
+            return autoPtr<expressions::fvExprDriver>
+            (
+                new parseDriver(this->patch_, *this)
+            );
+        }
+
+
+    //- Destructor
+    virtual ~parseDriver() = default;
+
+
+    // Public Member Functions
+
+        //- The mesh we are attached to
+        virtual const fvMesh& mesh() const
+        {
+            return patch_.boundaryMesh().mesh();
+        }
+
+        //- The underlying field size for the expression
+        virtual label size() const
+        {
+            return patch_.patch().size();
+        }
+
+        //- The underlying point field size for the expression
+        virtual label pointSize() const
+        {
+            return patch_.patch().nPoints();
+        }
+
+        //- Field size associated with different geometric field types
+        inline label size(const FieldAssociation geoType) const;
+
+
+    // Evaluation
+
+        //- Perform parsing on (sub) string
+        using genericRagelLemonDriver::content;
+
+        //- Execute the parser
+        virtual unsigned parse
+        (
+            const std::string& expr,
+            size_t pos = 0,
+            size_t len = std::string::npos
+        );
+
+
+    // Field Information
+
+    // Fields
+
+        //- Set result
+        template<class Type>
+        void setResult(Field<Type>* ptr, bool pointVal = false)
+        {
+            result().setResult<Type>(ptr, pointVal);
+        }
+
+
+        //- Retrieve variable as field if possible.
+        //  Test tmp for validity to determine success of the operation.
+        template<class Type>
+        tmp<Field<Type>> getVariableIfAvailable(const word& fldName) const;
+
+        //- Retrieve field (vol field)
+        template<class Type>
+        tmp<Field<Type>>
+        getVolField(const word& fldName);
+
+        //- Retrieve field (surface field)
+        template<class Type>
+        tmp<Field<Type>>
+        getSurfaceField(const word& fldName);
+
+        //- Retrieve field (point field)
+        template<class Type>
+        tmp<Field<Type>>
+        getPointField(const word& fldName);
+
+        //- Return named field
+        template<class Type>
+        tmp<Field<Type>> getField(const word& fldName);
+
+
+    // Field "shape" conversions
+
+        //- Interpolate face to point
+        template<class Type>
+        tmp<Field<Type>> faceToPoint(const Field<Type>& field) const;
+
+        //- Interpolate point to face values
+        template<class Type>
+        tmp<Field<Type>> pointToFace(const Field<Type>& field) const;
+
+
+    // Custom Field Functions
+
+        //- The area-weighted average of a field
+        template<class Type>
+        Type areaAverage(const Field<Type>& fld) const
+        {
+            return weightedAverage(patch_.magSf(), fld);
+        }
+
+        //- The area-weighted sum of a field
+        template<class Type>
+        Type areaSum(const Field<Type>& fld) const
+        {
+            return weightedSum(patch_.magSf(), fld);
+        }
+
+        //- The face area magnitudes [magSf] - (swak = area)
+        tmp<scalarField> field_faceArea() const;
+
+        //- The face centres - (swak = pos)
+        tmp<vectorField> field_faceCentre() const;
+
+        //- The face areas with their vector direction [Sf] - (swak = face)
+        tmp<vectorField> field_areaNormal() const;
+
+        //- The patch point locations - (swak = pts)
+        tmp<vectorField> field_pointField() const;
+
+        //- A uniform random field
+        tmp<scalarField> field_rand(label seed=0, bool gaussian=false) const;
+
+        //- A Gaussian random field
+        tmp<scalarField> field_randGaussian(label seed=0) const
+        {
+            return field_rand(seed, true);
+        }
+};
+
+
+// Template specializations
+
+//- Retrieve field (surface field: bool)
+template<>
+tmp<Field<bool>> parseDriver::getSurfaceField<bool>(const word& fldName);
+
+//- Retrieve field (point field: bool)
+template<>
+tmp<Field<bool>> parseDriver::getPointField<bool>(const word& fldName);
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace patchExpr
+} // End namespace expressions
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#include "patchExprDriverI.H"
+
+#ifdef NoRepository
+    #include "patchExprDriverTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/patch/patchExprDriverFields.C b/src/finiteVolume/expressions/patch/patchExprDriverFields.C
new file mode 100644
index 0000000000000000000000000000000000000000..690a4c2376170a5471a645f7f6ac7a46ad43503f
--- /dev/null
+++ b/src/finiteVolume/expressions/patch/patchExprDriverFields.C
@@ -0,0 +1,100 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "patchExprDriver.H"
+#include "fvPatch.H"
+#include "error.H"
+
+// * * * * * * * * * * * * Template Specializations  * * * * * * * * * * * * //
+
+template<>
+Foam::tmp<Foam::Field<bool>>
+Foam::expressions::patchExpr::parseDriver::getSurfaceField<bool>
+(
+    const word& name
+)
+{
+    return getVariable<bool>(name, this->size());
+}
+
+
+template<>
+Foam::tmp<Foam::Field<bool>>
+Foam::expressions::patchExpr::parseDriver::getPointField<bool>
+(
+    const word& name
+)
+{
+    return getVariable<bool>(name, this->pointSize());
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::tmp<Foam::scalarField>
+Foam::expressions::patchExpr::parseDriver::field_faceArea() const
+{
+    return patch_.magSf();
+}
+
+
+Foam::tmp<Foam::vectorField>
+Foam::expressions::patchExpr::parseDriver::field_faceCentre() const
+{
+    return patch_.Cf();
+}
+
+
+Foam::tmp<Foam::vectorField>
+Foam::expressions::patchExpr::parseDriver::field_areaNormal() const
+{
+    return patch_.Sf();
+}
+
+
+Foam::tmp<Foam::vectorField>
+Foam::expressions::patchExpr::parseDriver::field_pointField() const
+{
+    return patch_.patch().localPoints();
+}
+
+
+Foam::tmp<Foam::scalarField>
+Foam::expressions::patchExpr::parseDriver::field_rand
+(
+    label seed,
+    bool gaussian
+) const
+{
+    auto tresult = tmp<scalarField>::New(this->size());
+    fill_random(tresult.ref(), seed, gaussian);
+
+    return tresult;
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/patch/patchExprDriverI.H b/src/finiteVolume/expressions/patch/patchExprDriverI.H
new file mode 100644
index 0000000000000000000000000000000000000000..b4ae95c4ea0162912e1eea94275b249383d301ce
--- /dev/null
+++ b/src/finiteVolume/expressions/patch/patchExprDriverI.H
@@ -0,0 +1,50 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+inline Foam::label Foam::expressions::patchExpr::parseDriver::size
+(
+    const FieldAssociation geoType
+) const
+{
+    switch (geoType)
+    {
+        case FieldAssociation::POINT_DATA :
+            return patch_.patch().nPoints();
+            break;
+        case FieldAssociation::SURFACE_DATA :
+            return patch_.patch().size();
+            break;
+        default:
+            break;
+    }
+    return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/patch/patchExprDriverTemplates.C b/src/finiteVolume/expressions/patch/patchExprDriverTemplates.C
new file mode 100644
index 0000000000000000000000000000000000000000..7fafffbc50361f3f4da81b646492776c47741e90
--- /dev/null
+++ b/src/finiteVolume/expressions/patch/patchExprDriverTemplates.C
@@ -0,0 +1,228 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "primitivePatchInterpolation.H"
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>>
+Foam::expressions::patchExpr::parseDriver::getVariableIfAvailable
+(
+    const word& name
+) const
+{
+    bool isPointVal = false;
+    bool isUniformVal = false;
+
+    tmp<Field<Type>> tfield;
+
+    if (hasVariable(name) && variable(name).isType<Type>())
+    {
+        const expressions::exprResult& var = variable(name);
+
+        isPointVal = var.isPointValue();
+        isUniformVal = var.isUniform();
+
+        tfield = var.cref<Type>().clone();
+    }
+    else if (isGlobalVariable<Type>(name, false))
+    {
+        const expressions::exprResult& var = lookupGlobal(name);
+
+        isUniformVal = var.isUniform();
+
+        tfield = var.cref<Type>().clone();
+    }
+
+    if (tfield.valid())
+    {
+        const label fldLen = tfield().size();
+        const label len = (isPointVal ? this->pointSize() : this->size());
+
+        if (returnReduce((fldLen == len), andOp<bool>()))
+        {
+            return tfield;
+        }
+
+        if (!isUniformVal)
+        {
+            WarningInFunction
+                << "Variable " << name
+                << " does not fit the size and is not a uniform value." << nl
+                << "Using average value" << endl;
+        }
+
+        return tmp<Field<Type>>::New(this->size(), gAverage(tfield));
+    }
+
+    return tfield;
+}
+
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>>
+Foam::expressions::patchExpr::parseDriver::getVolField(const word& name)
+{
+    return getField<Type>(name);
+}
+
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>>
+Foam::expressions::patchExpr::parseDriver::getSurfaceField(const word& name)
+{
+    return getField<Type>(name);
+}
+
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>>
+Foam::expressions::patchExpr::parseDriver::getPointField(const word& name)
+{
+    return getField<Type>(name);
+}
+
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>>
+Foam::expressions::patchExpr::parseDriver::getField(const word& name)
+{
+    tmp<Field<Type>> tfield = getVariableIfAvailable<Type>(name);
+
+    if (tfield.valid())
+    {
+        return tfield;
+    }
+
+
+    typedef GeometricField<Type, fvPatchField, volMesh> vfieldType;
+    typedef GeometricField<Type, fvsPatchField, surfaceMesh> sfieldType;
+    typedef GeometricField<Type, pointPatchField, pointMesh> pfieldType;
+
+    const objectRegistry& obr = this->mesh().thisDb();
+
+    const vfieldType* vfield = obr.findObject<vfieldType>(name);
+    const sfieldType* sfield = obr.findObject<sfieldType>(name);
+    const pfieldType* pfield = obr.findObject<pfieldType>(name);
+
+    // Local, temporary storage
+    tmp<vfieldType> t_vfield;
+    tmp<sfieldType> t_sfield;
+    tmp<pfieldType> t_pfield;
+
+    if (searchFiles() && !vfield && !sfield && !pfield)
+    {
+        const word fldType = this->getTypeOfField(name);
+
+        if (fldType == vfieldType::typeName)
+        {
+            t_vfield = this->readAndRegister<vfieldType>(name, mesh());
+            vfield = t_vfield.get();
+        }
+        else if (fldType == sfieldType::typeName)
+        {
+            t_sfield = this->readAndRegister<sfieldType>(name, mesh());
+            sfield = t_sfield.get();
+        }
+        else if (fldType == pfieldType::typeName)
+        {
+            t_pfield = this->readAndRegister<pfieldType>
+            (
+                name,
+                pointMesh::New(mesh())
+            );
+            pfield = t_pfield.get();
+        }
+     }
+
+    const label patchIndex = patch_.index();
+
+    if (vfield)
+    {
+        return tmp<Field<Type>>::New
+        (
+            vfield->boundaryField()[patchIndex]
+        );
+    }
+
+    if (sfield)
+    {
+        return tmp<Field<Type>>::New
+        (
+            sfield->boundaryField()[patchIndex]
+        );
+    }
+
+    if (pfield)
+    {
+        return pfield->boundaryField()[patchIndex].patchInternalField();
+    }
+
+
+    FatalErrorInFunction
+        << "No field '" << name << "' of type "
+        << pTraits<Type>::typeName << nl << nl
+        << vfieldType::typeName << " Fields: "
+        << flatOutput(obr.sortedNames<vfieldType>()) << nl
+        << sfieldType::typeName << " Fields: "
+        << flatOutput(obr.sortedNames<sfieldType>()) << nl
+        << pfieldType::typeName << " Fields: "
+        << flatOutput(obr.sortedNames<pfieldType>()) << nl
+        << exit(FatalError);
+
+    return tmp<Field<Type>>::New();
+}
+
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>>
+Foam::expressions::patchExpr::parseDriver::faceToPoint
+(
+    const Field<Type>& field
+) const
+{
+    primitivePatchInterpolation interp(patch_.patch());
+
+    return interp.pointToFaceInterpolate(field);
+}
+
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>>
+Foam::expressions::patchExpr::parseDriver::pointToFace
+(
+    const Field<Type>& field
+) const
+{
+    primitivePatchInterpolation interp(patch_.patch());
+
+    return interp.faceToPointInterpolate(field);
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/patch/patchExprFwd.H b/src/finiteVolume/expressions/patch/patchExprFwd.H
new file mode 100644
index 0000000000000000000000000000000000000000..5682b896fbf6286a68bb8cb68f845ac174d283b1
--- /dev/null
+++ b/src/finiteVolume/expressions/patch/patchExprFwd.H
@@ -0,0 +1,84 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+Namespace
+    Foam::expressions::patchExpr
+
+Description
+    Namespace for patch expressions parsing and evaluation
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef expressions_patchExprFwd_H
+#define expressions_patchExprFwd_H
+
+namespace Foam
+{
+namespace expressions
+{
+namespace patchExpr
+{
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// Forward Declarations
+class parser;
+class scanner;
+class parseDriver;
+union scanToken;
+
+//- Static debugging option
+extern int debug;
+
+
+//- The field association for patch expressions (mutually exclusive)
+enum FieldAssociation : unsigned char
+{
+    NO_DATA = 0,         //!< No data
+    POINT_DATA = 1,      //!< Point data
+    SURFACE_DATA = 2     //!< Surface data
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace patchExpr
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+//- Typedef for patchExpr parseDriver
+typedef patchExpr::parseDriver patchExprDriver;
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+} // End namespace expressions
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/patch/patchExprLemonParser.h b/src/finiteVolume/expressions/patch/patchExprLemonParser.h
new file mode 100644
index 0000000000000000000000000000000000000000..8a84326c4dd20a35dbfb0ef4cbf29d37b1eb8a67
--- /dev/null
+++ b/src/finiteVolume/expressions/patch/patchExprLemonParser.h
@@ -0,0 +1,110 @@
+#define TOK_QUESTION                         1
+#define TOK_COLON                            2
+#define TOK_LOR                              3
+#define TOK_LAND                             4
+#define TOK_BIT_XOR                          5
+#define TOK_BIT_AND                          6
+#define TOK_EQUAL                            7
+#define TOK_NOT_EQUAL                        8
+#define TOK_LESS_EQ                          9
+#define TOK_GREATER_EQ                      10
+#define TOK_LESS                            11
+#define TOK_GREATER                         12
+#define TOK_PLUS                            13
+#define TOK_MINUS                           14
+#define TOK_TIMES                           15
+#define TOK_DIVIDE                          16
+#define TOK_PERCENT                         17
+#define TOK_NEGATE                          18
+#define TOK_NOT                             19
+#define TOK_DOT                             20
+#define TOK_NUMBER                          21
+#define TOK_ZERO                            22
+#define TOK_PI                              23
+#define TOK_LPAREN                          24
+#define TOK_RPAREN                          25
+#define TOK_DEG_TO_RAD                      26
+#define TOK_RAD_TO_DEG                      27
+#define TOK_TIME                            28
+#define TOK_SCALAR_ID                       29
+#define TOK_SSCALAR_ID                      30
+#define TOK_MIN                             31
+#define TOK_COMMA                           32
+#define TOK_MAX                             33
+#define TOK_SUM                             34
+#define TOK_AVERAGE                         35
+#define TOK_EXP                             36
+#define TOK_LOG                             37
+#define TOK_LOG10                           38
+#define TOK_SQR                             39
+#define TOK_SQRT                            40
+#define TOK_CBRT                            41
+#define TOK_SIN                             42
+#define TOK_COS                             43
+#define TOK_TAN                             44
+#define TOK_ASIN                            45
+#define TOK_ACOS                            46
+#define TOK_ATAN                            47
+#define TOK_SINH                            48
+#define TOK_COSH                            49
+#define TOK_TANH                            50
+#define TOK_POW                             51
+#define TOK_ATAN2                           52
+#define TOK_POS                             53
+#define TOK_NEG                             54
+#define TOK_POS0                            55
+#define TOK_NEG0                            56
+#define TOK_SIGN                            57
+#define TOK_FLOOR                           58
+#define TOK_CEIL                            59
+#define TOK_ROUND                           60
+#define TOK_HYPOT                           61
+#define TOK_RAND                            62
+#define TOK_VECTOR_ID                       63
+#define TOK_SVECTOR_ID                      64
+#define TOK_SPH_TENSOR_ID                   65
+#define TOK_SSPH_TENSOR_ID                  66
+#define TOK_SYM_TENSOR_ID                   67
+#define TOK_SSYM_TENSOR_ID                  68
+#define TOK_UNIT_TENSOR                     69
+#define TOK_TENSOR_ID                       70
+#define TOK_STENSOR_ID                      71
+#define TOK_LTRUE                           72
+#define TOK_LFALSE                          73
+#define TOK_BOOL                            74
+#define TOK_SBOOL_ID                        75
+#define TOK_FACE_AREA                       76
+#define TOK_FACE_EXPR                       77
+#define TOK_WEIGHT_AVERAGE                  78
+#define TOK_WEIGHT_SUM                      79
+#define TOK_POINT_EXPR                      80
+#define TOK_PSCALAR_ID                      81
+#define TOK_PVECTOR_ID                      82
+#define TOK_PSPH_TENSOR_ID                  83
+#define TOK_PSYM_TENSOR_ID                  84
+#define TOK_PTENSOR_ID                      85
+#define TOK_PBOOL_ID                        86
+#define TOK_POINTS                          87
+#define TOK_MAG                             88
+#define TOK_MAGSQR                          89
+#define TOK_VECTOR                          90
+#define TOK_TENSOR                          91
+#define TOK_SYM_TENSOR                      92
+#define TOK_SPH_TENSOR                      93
+#define TOK_CMPT_X                          94
+#define TOK_CMPT_Y                          95
+#define TOK_CMPT_Z                          96
+#define TOK_CMPT_XX                         97
+#define TOK_CMPT_XY                         98
+#define TOK_CMPT_XZ                         99
+#define TOK_CMPT_YX                        100
+#define TOK_CMPT_YY                        101
+#define TOK_CMPT_YZ                        102
+#define TOK_CMPT_ZX                        103
+#define TOK_CMPT_ZY                        104
+#define TOK_CMPT_ZZ                        105
+#define TOK_CMPT_II                        106
+#define TOK_TRANSPOSE                      107
+#define TOK_DIAG                           108
+#define TOK_POINT_TO_FACE                  109
+#define TOK_FACE_TO_POINT                  110
diff --git a/src/finiteVolume/expressions/patch/patchExprLemonParser.lyy-m4 b/src/finiteVolume/expressions/patch/patchExprLemonParser.lyy-m4
new file mode 100644
index 0000000000000000000000000000000000000000..477cc7ea463aff567511c1f85e68a014c6d50eba
--- /dev/null
+++ b/src/finiteVolume/expressions/patch/patchExprLemonParser.lyy-m4
@@ -0,0 +1,565 @@
+%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 for patch expressions.
+
+    https://www.sqlite.org/src/doc/trunk/doc/lemon.html
+
+    See detailed notes in the field expression parser.
+
+\*---------------------------------------------------------------------------*/
+} // %include
+
+/*
+ * include[patchExprLemonParserMacros.m4]
+ *include(`patchExprLemonParserMacros.m4')dnl
+ !done in a comment since many editors have issues matching m4 quotes!
+ */
+%include
+{
+#include "patchExprDriver.H"
+#include "patchExprParser.H"
+#include "patchExprScanner.H"
+#include "unitConversion.H"
+#include "error.H"
+#include "volFields.H"
+#include "exprOps.H"
+#include "exprDriverOps.H"
+#include "GeometricFieldOps.H"
+
+// Enable ParseTrace
+#undef NDEBUG
+
+compiler_pragmas()
+
+// Local Functions
+
+tmp_management()
+
+} // %include
+
+// ------------------------------------------------------------------------- //
+
+%namespace      {}
+
+// Use extra argument for the return value
+%extra_context  { Foam::expressions::patchExpr::parseDriver* driver }
+%parse_failure  { driver->reportFatal("Parse failure, giving up..."); }
+%syntax_error   { driver->reportFatal("Syntax error"); }
+
+%token_prefix TOK_
+
+// Terminals
+%token_type    {Foam::expressions::patchExpr::scanToken*}
+// Non-terminals
+%type ivalue   { Foam::label }
+%type svalue   { Foam::scalar }
+%type ident    { Foam::word* }
+
+// Face fields
+declare_field(lfield, Foam::boolField, bool, newField, getSurfaceField)
+declare_field(sfield, Foam::scalarField, Foam::scalar, newField, getField)
+declare_field(vfield, Foam::vectorField, Foam::vector, newField, getField)
+declare_field(hfield, Foam::sphericalTensorField, Foam::sphericalTensor, newField, getField)
+declare_field(yfield, Foam::symmTensorField, Foam::symmTensor, newField, getField)
+declare_field(tfield, Foam::tensorField, Foam::tensor, newField, getField)
+
+// Point fields
+declare_field(plfield, Foam::boolField, bool, newPointField, getPointField)
+declare_field(psfield, Foam::scalarField, Foam::scalar, newPointField, getPointField)
+declare_field(pvfield, Foam::vectorField, Foam::vector, newPointField, getPointField)
+declare_field(phfield, Foam::sphericalTensorField, Foam::sphericalTensor, newPointField, getPointField)
+declare_field(pyfield, Foam::symmTensorField, Foam::symmTensor, newPointField, getPointField)
+declare_field(ptfield, Foam::tensorField, Foam::tensor, newPointField, getPointField)
+
+
+// For each rule action with code, destruction must be done by that code block
+// Lemon does not generate a destructor for that.
+// So do not use Lemon destructors for anything.
+
+operator_precedence()
+
+%start_symbol evaluate
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+/*---------------------------------------------------------------------------*\
+ * Productions (scalar)
+\*---------------------------------------------------------------------------*/
+
+svalue (lhs) ::= NUMBER (a) .       { lhs = (a)->svalue; }  // From scanToken
+svalue (lhs) ::= ZERO .             { lhs = Foam::Zero; }
+svalue (lhs) ::= PI LPAREN RPAREN . { lhs = Foam::constant::mathematical::pi; }
+svalue (lhs) ::= DEG_TO_RAD LPAREN RPAREN . { lhs = Foam::degToRad(); }
+svalue (lhs) ::= RAD_TO_DEG LPAREN RPAREN . { lhs = Foam::radToDeg(); }
+svalue (lhs) ::= TIME LPAREN RPAREN . { lhs = driver->timeValue(); }
+
+
+/* * * * * * * * * * * * * * * * * Face Fields * * * * * * * * * * * * * * * *\
+dnl
+define([_logic_],       [lfield])dnl
+define([_scalar_],      [sfield])dnl
+define([_vector_],      [vfield])dnl
+define([_sphTensor_],   [hfield])dnl
+define([_symTensor_],   [yfield])dnl
+define([_tensor_],      [tfield])dnl
+dnl
+\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*---------------------------------------------------------------------------*\
+ * Productions (scalarField)
+dnl
+define([_target_],      [sfield])dnl
+define([_value_type_],  [Foam::scalar])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_field_from_value(_target_, svalue)
+rule_get_field(_target_, SCALAR_ID)
+rule_get_field(_target_, SSCALAR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_scalar_operations()
+rules_scalar_functions()
+
+// Non-standard but manage via FieldOps::assign
+rule_unary_assign(_target_, _target_, FLOOR, Foam::floorOp<Foam::scalar>())
+rule_unary_assign(_target_, _target_, CEIL, Foam::ceilOp<Foam::scalar>())
+rule_unary_assign(_target_, _target_, ROUND, Foam::roundOp<Foam::scalar>())
+
+// Non-standard but works directly for scalarField
+rule_binary_func(_target_, _target_, _target_, HYPOT, Foam::hypot)
+
+
+// Other functions
+
+_target_ (lhs) ::= RAND LPAREN RPAREN.
+{
+    lhs = driver->field_rand().ptr();
+}
+
+_target_ (lhs) ::= RAND LPAREN NUMBER (seed) RPAREN.
+{
+    // Call with -ve seed to signal use of time index as seed
+    lhs = driver->field_rand(std::round(-(seed)->svalue)).ptr();
+}
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (vectorField)
+dnl
+define([_target_],      [vfield])dnl
+define([_value_type_],  [Foam::vector])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_get_field(_target_, VECTOR_ID)
+rule_get_field(_target_, SVECTOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_vector_operations()
+rules_vector_functions()
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (sphericalTensorField)
+dnl
+define([_target_],      [hfield])dnl
+define([_value_type_],  [Foam::sphericalTensor])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_get_field(_target_, SPH_TENSOR_ID)
+rule_get_field(_target_, SSPH_TENSOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_sphTensor_operations()
+rules_sphTensor_functions()
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (symmTensorField)
+dnl
+define([_target_],      [yfield])dnl
+define([_value_type_],  [Foam::symmTensor])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_get_field(_target_, SYM_TENSOR_ID)
+rule_get_field(_target_, SSYM_TENSOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_symTensor_operations()
+rules_symTensor_functions()
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (tensorField)
+dnl
+define([_target_],      [tfield])dnl
+define([_value_type_],  [Foam::tensor])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+tfield (lhs) ::= UNIT_TENSOR .  { lhs = _new_tfield(Foam::tensor::I); }
+
+rule_get_field(_target_, TENSOR_ID)
+rule_get_field(_target_, STENSOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_tensor_operations()
+rules_tensor_functions()
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+/*---------------------------------------------------------------------------*\
+ * Logic field productions (boolField)
+dnl
+define([_target_],      [lfield])dnl
+define([_value_type_],  [bool])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+_logic_ (lhs) ::= LTRUE .   { lhs = _new_lfield(_logic_true_); }
+_logic_ (lhs) ::= LFALSE .  { lhs = _new_lfield(_logic_false_); }
+
+rule_cast_logical(_target_, _target_)
+rule_cast_logical(_target_, _scalar_, Foam::scalar)
+
+dnl/* Handling of named logic fields not really tested (disable in scanner) */
+rule_get_field(_target_, SBOOL_ID)
+rules_logical_operations(_logic_, _value_type_)
+
+
+/*---------------------------------------------------------------------------*\
+ * General Surface-related productions
+\*---------------------------------------------------------------------------*/
+
+rules_driver_surface_functions()
+
+
+/* * * * * * * * * * * * * * * * Point Fields  * * * * * * * * * * * * * * * *\
+dnl
+define([_logic_],       [plfield])dnl
+define([_scalar_],      [psfield])dnl
+define([_vector_],      [pvfield])dnl
+define([_sphTensor_],   [phfield])dnl
+define([_symTensor_],   [pyfield])dnl
+define([_tensor_],      [ptfield])dnl
+dnl
+\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (point scalarField)
+dnl
+define([_target_],      [psfield])dnl
+define([_value_type_],  [Foam::scalar])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a, true); /* Point */ }
+
+rule_field_from_value(_target_, svalue, POINT_EXPR)
+rule_get_field(_target_, PSCALAR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_scalar_operations()
+rules_scalar_functions()
+
+// Non-standard but manage via FieldOps::assign
+rule_unary_assign(_target_, _target_, FLOOR, Foam::floorOp<Foam::scalar>())
+rule_unary_assign(_target_, _target_, CEIL, Foam::ceilOp<Foam::scalar>())
+rule_unary_assign(_target_, _target_, ROUND, Foam::roundOp<Foam::scalar>())
+
+// Non-standard but works directly for scalarField
+rule_binary_func(_target_, _target_, _target_, HYPOT, Foam::hypot)
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (point vectorField)
+dnl
+define([_target_],      [pvfield])dnl
+define([_value_type_],  [Foam::vector])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a, true); /* Point */ }
+
+rule_get_field(_target_, PVECTOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_vector_operations()
+rules_vector_functions()
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (point sphericalTensorField)
+dnl
+define([_target_],      [phfield])dnl
+define([_value_type_],  [Foam::sphericalTensor])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a, true); /* Point */ }
+
+rule_get_field(_target_, PSPH_TENSOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_sphTensor_operations()
+rules_sphTensor_functions()
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (point symmTensorField)
+dnl
+define([_target_],      [pyfield])dnl
+define([_value_type_],  [Foam::symmTensor])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a, true); /* Point */ }
+
+rule_get_field(_target_, PSYM_TENSOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_symTensor_operations()
+rules_symTensor_functions()
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (point tensorField)
+dnl
+define([_target_],      [ptfield])dnl
+define([_value_type_],  [Foam::tensor])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a, true); /* Point */ }
+
+rule_get_field(_target_, PTENSOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_tensor_operations()
+rules_tensor_functions()
+
+
+/*---------------------------------------------------------------------------*\
+ * Logic field productions (point boolField)
+dnl
+define([_target_],      [plfield])dnl
+define([_value_type_],  [bool])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a, true); /* Point */ }
+_logic_ (lhs) ::= POINT_EXPR LPAREN LTRUE RPAREN .  { lhs = _new_plfield(_logic_true_); }
+_logic_ (lhs) ::= POINT_EXPR LPAREN LFALSE RPAREN . { lhs = _new_plfield(_logic_false_); }
+
+rule_cast_logical(_target_, _target_)
+rule_cast_logical(_target_, _scalar_, Foam::scalar)
+
+dnl/* Handling of named logic fields not really tested (disable in scanner) */
+rule_get_field(_target_, PBOOL_ID)
+rules_logical_operations(_logic_, _value_type_)
+
+
+/*---------------------------------------------------------------------------*\
+ * General Point-related productions
+\*---------------------------------------------------------------------------*/
+
+rules_driver_point_functions()
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+/*---------------------------------------------------------------------------*\
+ * Face field composition
+\*---------------------------------------------------------------------------*/
+
+rule_mag_logical(sfield, lfield)
+rules_mag_functions(sfield, sfield)
+rules_mag_functions(sfield, vfield)
+rules_mag_functions(sfield, tfield)
+rules_mag_functions(sfield, yfield)
+rules_mag_functions(sfield, hfield)
+
+rule_vector_zip(vfield, sfield, VECTOR)
+rule_tensor_zip(tfield, sfield, TENSOR)
+rule_symTensor_zip(yfield, sfield, SYM_TENSOR)
+rule_sphTensor_zip(hfield, sfield, SPH_TENSOR)
+
+rule_vector_components(sfield, vfield)
+rule_tensor_components(sfield, tfield)
+rule_symTensor_components(sfield, yfield)
+rule_sphTensor_components(sfield, hfield)
+
+rule_tensor_transpose(tfield)
+rule_symTensor_transpose(yfield)
+rule_sphTensor_transpose(hfield)
+
+rule_tensor_unzipDiag(vfield, yfield)
+rule_tensor_unzipAll(vfield, tfield)
+
+rule_pointToFace(sfield, psfield)
+rule_pointToFace(vfield, pvfield)
+rule_pointToFace(tfield, ptfield)
+rule_pointToFace(yfield, pyfield)
+rule_pointToFace(hfield, phfield)
+
+
+/*---------------------------------------------------------------------------*\
+ * Point field composition
+\*---------------------------------------------------------------------------*/
+
+rule_mag_logical(psfield, plfield)
+rules_mag_functions(psfield, psfield)
+rules_mag_functions(psfield, pvfield)
+rules_mag_functions(psfield, ptfield)
+rules_mag_functions(psfield, pyfield)
+rules_mag_functions(psfield, phfield)
+
+rule_vector_zip(pvfield, psfield, VECTOR)
+rule_tensor_zip(ptfield, psfield, TENSOR)
+rule_symTensor_zip(pyfield, psfield, SYM_TENSOR)
+rule_sphTensor_zip(phfield, psfield, SPH_TENSOR)
+
+rule_vector_components(psfield, pvfield)
+rule_tensor_components(psfield, ptfield)
+rule_symTensor_components(psfield, pyfield)
+rule_sphTensor_components(psfield, phfield)
+
+rule_tensor_transpose(ptfield)
+rule_symTensor_transpose(pyfield)
+rule_sphTensor_transpose(phfield)
+
+rule_tensor_unzipDiag(pvfield, pyfield)
+rule_tensor_unzipAll(pvfield, ptfield)
+
+rule_faceToPoint(psfield, sfield)
+rule_faceToPoint(pvfield, vfield)
+rule_faceToPoint(ptfield, tfield)
+rule_faceToPoint(pyfield, yfield)
+rule_faceToPoint(phfield, hfield)
+
+
+// ************************************************************************* //
+
+dnl/* Standard m4 quoting
+changequote([`],['])dnl
+dnl*/
+
+%code
+{
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::expressions::patchExpr::parser::stop()
+{
+    if (lemon_)
+    {
+        ParseFree(lemon_, ::operator delete);
+        #ifndef NDEBUG
+        ParseTrace(nullptr, nullptr);
+        #endif
+        lemon_ = nullptr;
+    }
+}
+
+
+void Foam::expressions::patchExpr::parser::start(parseDriver& driver_)
+{
+    this->stop();
+    lemon_ = ParseAlloc(::operator new, &driver_);
+
+    if (debug || driver_.debugParser())
+    {
+        #ifndef NDEBUG
+        ParseTrace(stderr, const_cast<char*>(prompt_));
+        #endif
+    }
+}
+
+
+void Foam::expressions::patchExpr::parser::parse
+(
+    int tokenId,
+    scanToken* tokenVal
+)
+{
+    Parse(lemon_, tokenId, tokenVal);
+}
+
+
+Foam::word Foam::expressions::patchExpr::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
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/patch/patchExprLemonParserMacros.m4 b/src/finiteVolume/expressions/patch/patchExprLemonParserMacros.m4
new file mode 100644
index 0000000000000000000000000000000000000000..aa183dfeb63014d38a4ee3da5ca7664e2ab3a1e7
--- /dev/null
+++ b/src/finiteVolume/expressions/patch/patchExprLemonParserMacros.m4
@@ -0,0 +1,109 @@
+divert(-1)dnl
+#-----------------------------------*- m4 -*-----------------------------------
+#   =========                 |
+#   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+#    \\    /   O peration     |
+#     \\  /    A nd           | www.openfoam.com
+#      \\/     M anipulation  |
+#------------------------------------------------------------------------------
+#     Copyright (C) 2019 OpenCFD Ltd.
+#------------------------------------------------------------------------------
+# License
+#     This file is part of OpenFOAM, licensed under GNU General Public License
+#     <http://www.gnu.org/licenses/>.
+#
+# Description
+#     Driver-specific m4/lemon macros for patch expressions.
+#
+#------------------------------------------------------------------------------
+
+include(`m4/lemon/base-setup.m4')dnl
+include([m4/lemon/operator-precedence.m4])dnl
+dnl
+include([m4/lemon/rules-standard.m4])dnl
+include([m4/lemon/rules-operations.m4])dnl
+include([m4/lemon/rules-functions.m4])dnl
+include([m4/lemon/rules-components.m4])dnl
+include([m4/lemon/rules-fields-components.m4])dnl
+include([m4/lemon/rules-scalar-logic.m4])dnl
+dnl
+divert(-1)dnl
+
+use_bool_logic()dnl     # Use boolField directly
+
+#-------------------------------------------------------------------------------
+# Driver rules
+#-------------------------------------------------------------------------------
+
+define([rules_driver_surface_functions],
+[dnl
+rule_driver_nullary(_scalar_, FACE_AREA, field_faceArea)dnl
+rule_driver_nullary(_vector_, POS, field_faceCentre)dnl  FACE_CENTRE
+rule_driver_nullary(_vector_, FACE_EXPR, field_areaNormal)dnl
+dnl
+rule_driver_inplace_unary(_scalar_, WEIGHT_AVERAGE, areaAverage)dnl
+rule_driver_inplace_unary(_vector_, WEIGHT_AVERAGE, areaAverage)dnl
+rule_driver_inplace_unary(_sphTensor_, WEIGHT_AVERAGE, areaAverage)dnl
+rule_driver_inplace_unary(_symTensor_, WEIGHT_AVERAGE, areaAverage)dnl
+rule_driver_inplace_unary(_tensor_, WEIGHT_AVERAGE, areaAverage)dnl
+dnl
+rule_driver_inplace_unary(_scalar_, WEIGHT_SUM, areaSum)dnl
+rule_driver_inplace_unary(_vector_, WEIGHT_SUM, areaSum)dnl
+rule_driver_inplace_unary(_sphTensor_, WEIGHT_SUM, areaSum)dnl
+rule_driver_inplace_unary(_symTensor_, WEIGHT_SUM, areaSum)dnl
+rule_driver_inplace_unary(_tensor_, WEIGHT_SUM, areaSum)dnl
+dnl
+])
+
+define([rules_driver_point_functions],
+[dnl
+rule_driver_nullary(_vector_, POINTS, field_pointField)dnl
+dnl
+dnl NB use non-driver versions for points - ie, unweighted
+dnl
+rule_inplace_unary(_scalar_, WEIGHT_AVERAGE, Foam::gAverage)dnl
+rule_inplace_unary(_vector_, WEIGHT_AVERAGE, Foam::gAverage)dnl
+rule_inplace_unary(_sphTensor_, WEIGHT_AVERAGE, Foam::gAverage)dnl
+rule_inplace_unary(_symTensor_, WEIGHT_AVERAGE, Foam::gAverage)dnl
+rule_inplace_unary(_tensor_, WEIGHT_AVERAGE, Foam::gAverage)dnl
+dnl
+rule_inplace_unary(_scalar_, WEIGHT_SUM, Foam::gSum)dnl
+rule_inplace_unary(_vector_, WEIGHT_SUM, Foam::gSum)dnl
+rule_inplace_unary(_sphTensor_, WEIGHT_SUM, Foam::gSum)dnl
+rule_inplace_unary(_symTensor_, WEIGHT_SUM, Foam::gSum)dnl
+rule_inplace_unary(_tensor_, WEIGHT_SUM, Foam::gSum)dnl
+dnl
+])
+
+
+#------------------------------------------------------------------------------
+# rule_faceToPoint(out, in)
+# rule_pointToFace(out, in)
+#
+# Description
+#     Production rules for driver faceToPoint, pointToFace,
+#     methods
+#------------------------------------------------------------------------------
+
+define([rule_faceToPoint],
+[rule_driver_unary($1, $2, FACE_TO_POINT, faceToPoint)])
+
+define([rule_pointToFace],
+[rule_driver_unary($1, $2, POINT_TO_FACE, pointToFace)])
+
+
+#------------------------------------------------------------------------------
+# Standard rules for fields: declaration, new/get, driver functions etc.
+
+include([m4/lemon/rules-fields.m4])dnl
+divert(-1)dnl
+
+
+#------------------------------------------------------------------------------
+
+# Additional safety measures
+
+undefine([substr])dnl   # Avoid collision with C/C++ naming
+
+#------------------------------------------------------------------------------
+divert(0)dnl
diff --git a/src/finiteVolume/expressions/patch/patchExprParser.H b/src/finiteVolume/expressions/patch/patchExprParser.H
new file mode 100644
index 0000000000000000000000000000000000000000..a59fcdcca693b4c9f99b2cbe9061dc5cac317460
--- /dev/null
+++ b/src/finiteVolume/expressions/patch/patchExprParser.H
@@ -0,0 +1,106 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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::expressions::patchExpr::parser
+
+Description
+    Lemon parser interface for patch expressions grammar
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef expressions_patchExprParser_H
+#define expressions_patchExprParser_H
+
+#include "patchExprFwd.H"
+
+namespace Foam
+{
+namespace expressions
+{
+namespace patchExpr
+{
+
+/*---------------------------------------------------------------------------*\
+                           Class parser Declaration
+\*---------------------------------------------------------------------------*/
+
+class parser
+{
+    // Private Data
+
+        //- Prompt for parser tracing
+        static constexpr const char* const prompt_ = "patchExpr:";
+
+        //- The lemon parser (demand-driven)
+        void* lemon_;
+
+
+public:
+
+    //- Local object debugging
+    int debug;
+
+
+    // Constructors
+
+        //- Construct null
+        parser() : lemon_(nullptr), debug(patchExpr::debug) {}
+
+
+    //- Destructor, deletes parser backend
+    ~parser()
+    {
+        stop();
+    }
+
+
+    // Member Functions
+
+        //- Start parsing, with the given driver context
+        void start(parseDriver& driver_);
+
+        //- Stop parsing, freeing the allocated parser
+        void stop();
+
+        //- Push token/value to parser
+        void parse(int tokenId, scanToken* tokenVal);
+
+        //- Return the text name corresponding to the tokenId
+        word nameOfToken(int tokenId) const;
+};
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace patchExpr
+} // End namespace expressions
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/patch/patchExprScanner.H b/src/finiteVolume/expressions/patch/patchExprScanner.H
new file mode 100644
index 0000000000000000000000000000000000000000..3003b749bfea5f5ac959cc0d40a53bd22b1d2f5d
--- /dev/null
+++ b/src/finiteVolume/expressions/patch/patchExprScanner.H
@@ -0,0 +1,162 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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::expressions::patchExpr::scanner
+
+Description
+    Ragel lexer/scanner interface for patch expressions.
+
+Note
+    Ragel code generated with the ./createCode script.
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef expressions_patchExprScanner_H
+#define expressions_patchExprScanner_H
+
+#include "patchExprFwd.H"
+#include "scalar.H"
+
+namespace Foam
+{
+namespace expressions
+{
+namespace patchExpr
+{
+
+/*---------------------------------------------------------------------------*\
+                          Class scanToken Declaration
+\*---------------------------------------------------------------------------*/
+
+union scanToken
+{
+    Foam::label  ivalue;
+    Foam::scalar svalue;
+    Foam::word*  name;
+
+    //- Null construct, bit-wise zero for union content
+    scanToken() : ivalue(0) {}
+};
+
+
+/*---------------------------------------------------------------------------*\
+                         Class scanner Declaration
+\*---------------------------------------------------------------------------*/
+
+class scanner
+{
+    // Private Data
+
+        //- Wrapped lemon parser
+        parser* parser_;
+
+        // Ragel code state, action
+        int cs, act;
+
+
+    // Private Member Functions
+
+        //- Dispatch .method to parser (if known) or Fatal
+        bool dispatch_method
+        (
+            const parseDriver& driver_,
+            scanToken& scanTok,
+            word&& ident
+        ) const;
+
+        //- Dispatch identifier to parser (if possible) or Fatal
+        bool dispatch_ident
+        (
+            const parseDriver& driver_,
+            scanToken& scanTok,
+            word&& ident
+        ) const;
+
+
+public:
+
+    //- Local debugging
+    int debug;
+
+
+    // Constructors
+
+        //- Construct null, optionally setting debugging
+        explicit scanner(bool withDebug = false)
+        :
+            parser_(nullptr),
+            debug(patchExpr::debug)
+        {
+            if (withDebug)
+            {
+                debug |= 4;
+            }
+        }
+
+
+    //- 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 patchExpr
+} // End namespace expressions
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/patch/patchExprScanner.cc b/src/finiteVolume/expressions/patch/patchExprScanner.cc
new file mode 100644
index 0000000000000000000000000000000000000000..98fe2225e1150dd27267fe061cc7910b89b24b4a
--- /dev/null
+++ b/src/finiteVolume/expressions/patch/patchExprScanner.cc
@@ -0,0 +1,3836 @@
+
+#line 1 "patchExprScanner.rl"
+/*--------------------------------*- 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 for patch expressions
+
+\*---------------------------------------------------------------------------*/
+
+#include "patchExprScanner.H"
+#include "patchExprDriver.H"
+#include "patchExprLemonParser.h"
+#include "patchExprParser.H"
+#include "Enum.H"
+#include "macros.H"
+
+#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+
+// Debugging to stderr
+#undef  DebugInfo
+#define DebugInfo if (debug) InfoErr
+
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+//- Paste token prefix
+#define TOKEN_OF(T)         TOK_##T
+
+//- An {int, c_str} enum pairing
+#define TOKEN_PAIR(Name,T)  { TOKEN_OF(T), Name }
+
+#undef HAS_LOOKBEHIND_TOKENS
+
+// Special handling of predefined method types. Eg, .x(), .y(), ...
+static const Enum<int> fieldMethodEnums
+({
+    TOKEN_PAIR("x", CMPT_X),
+    TOKEN_PAIR("y", CMPT_Y),
+    TOKEN_PAIR("z", CMPT_Z),
+    TOKEN_PAIR("xx", CMPT_XX),
+    TOKEN_PAIR("xy", CMPT_XY),
+    TOKEN_PAIR("xz", CMPT_XZ),
+    TOKEN_PAIR("yx", CMPT_YX),
+    TOKEN_PAIR("yy", CMPT_YY),
+    TOKEN_PAIR("yz", CMPT_YZ),
+    TOKEN_PAIR("zx", CMPT_ZX),
+    TOKEN_PAIR("zy", CMPT_ZY),
+    TOKEN_PAIR("zz", CMPT_ZZ),
+    TOKEN_PAIR("ii", CMPT_II),
+    TOKEN_PAIR("diag", DIAG),   /* tensors only */
+    TOKEN_PAIR("T", TRANSPOSE), /* tensors only */
+});
+
+// Known field-token types
+static const Enum<int> fieldTokenEnums
+({
+#ifdef TOK_SCALAR_ID
+    TOKEN_PAIR(volScalarField::typeName.c_str(), SCALAR_ID),
+    TOKEN_PAIR(volVectorField::typeName.c_str(), VECTOR_ID),
+    TOKEN_PAIR(volTensorField::typeName.c_str(), TENSOR_ID),
+    TOKEN_PAIR(volSymmTensorField::typeName.c_str(), SYM_TENSOR_ID),
+    TOKEN_PAIR(volSphericalTensorField::typeName.c_str(), SPH_TENSOR_ID),
+#else
+#error TOK_SCALAR_ID not defined
+#endif
+#ifdef TOK_SSCALAR_ID
+    TOKEN_PAIR(surfaceScalarField::typeName.c_str(), SSCALAR_ID),
+    TOKEN_PAIR(surfaceVectorField::typeName.c_str(), SVECTOR_ID),
+    TOKEN_PAIR(surfaceTensorField::typeName.c_str(), STENSOR_ID),
+    TOKEN_PAIR(surfaceSymmTensorField::typeName.c_str(), SSYM_TENSOR_ID),
+    TOKEN_PAIR(surfaceSphericalTensorField::typeName.c_str(), SSPH_TENSOR_ID),
+#else
+#error TOK_SSCALAR_ID not defined
+#endif
+#ifdef TOK_PSCALAR_ID
+    TOKEN_PAIR(pointScalarField::typeName.c_str(), PSCALAR_ID),
+    TOKEN_PAIR(pointVectorField::typeName.c_str(), PVECTOR_ID),
+    TOKEN_PAIR(pointTensorField::typeName.c_str(), PTENSOR_ID),
+    TOKEN_PAIR(pointSymmTensorField::typeName.c_str(), PSYM_TENSOR_ID),
+    TOKEN_PAIR(pointSphericalTensorField::typeName.c_str(), PSPH_TENSOR_ID),
+#else
+#warning TOK_PSCALAR_ID not defined
+#endif
+});
+
+
+// Simple compile-time function name declarations.
+// Useful for handling driver-specific dispatching, or functions that
+// are not universally available.
+static const Enum<int> funcTokenEnums
+({
+#ifdef TOK_FLOOR
+    TOKEN_PAIR("floor", FLOOR),
+    TOKEN_PAIR("ceil", CEIL),
+    TOKEN_PAIR("round", ROUND),
+#endif
+#ifdef TOK_HYPOT  /* Can use hypot? */
+    TOKEN_PAIR("hypot", HYPOT),
+#endif
+
+    // Already parsed as function: TOKEN_PAIR("pos", FACE_CENTRE),
+
+    TOKEN_PAIR("point", POINT_EXPR), // Point value
+    TOKEN_PAIR("face", FACE_EXPR),   // Face areaNormal
+
+    TOKEN_PAIR("faceToPoint", FACE_TO_POINT),
+    TOKEN_PAIR("pointToFace", POINT_TO_FACE),
+
+    TOKEN_PAIR("area", FACE_AREA),
+    TOKEN_PAIR("pts", POINTS),
+});
+
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Classifying token type based on an identifier name is indeed ugly.
+//
+// 1)
+//   Handle special cases (eg, cellSet,...) first that have been tagged
+//   as expected content with the stashed "look-behind" token.
+//   Handle not-found errors here directly.
+//
+// 2)
+//   Fallback to determining which field-type (volScalarField etc) the name
+//   corresponds to.
+//   Handle not-found errors by return -1.
+//
+static int driverTokenType
+(
+    const expressions::patchExpr::parseDriver& driver_,
+    const word& ident
+)
+{
+#if 0
+    // Get stashed "look-behind" to decide what type of identifier we expect
+    const int lookBehind = driver_.resetStashedTokenId();
+
+    if (lookBehind && lookBehindTokenEnums.found(lookBehind))
+    {
+        bool good = false;
+
+        switch (lookBehind)
+        {
+            case TOK_CSET : good = driver_.isCellSet(ident); break;
+            case TOK_FSET : good = driver_.isFaceSet(ident); break;
+            case TOK_PSET : good = driver_.isPointSet(ident); break;
+            case TOK_CZONE : good = driver_.isCellZone(ident); break;
+            case TOK_FZONE : good = driver_.isFaceZone(ident); break;
+            case TOK_PZONE : good = driver_.isPointZone(ident); break;
+        }
+
+        if (good)
+        {
+            return TOK_IDENTIFIER;
+        }
+
+        // Fatal
+        driver_.reportFatal
+        (
+            "Error no " + lookBehindTokenEnums.get(lookBehind) + ": " + ident
+        );
+
+        return -2;  // Extra safety
+    }
+#endif
+
+    // Face variables
+    #ifdef TOK_SSCALAR_ID
+    {
+        #undef checkFieldToken
+        #define checkFieldToken(TokType, Type)                                \
+        if (driver_.isVariable<Type>(ident, false))                           \
+        {                                                                     \
+            return TokType;                                                   \
+        }
+
+        checkFieldToken(TOK_SSCALAR_ID, scalar);
+        checkFieldToken(TOK_SVECTOR_ID, vector);
+        checkFieldToken(TOK_SSYM_TENSOR_ID, symmTensor);
+        checkFieldToken(TOK_SSPH_TENSOR_ID, sphericalTensor);
+        checkFieldToken(TOK_STENSOR_ID, tensor);
+
+        // Not tested: checkFieldToken(TOK_SBOOL_ID, bool);
+    }
+    #endif
+
+    // Point variables
+    #ifdef TOK_PSCALAR_ID
+    {
+        #undef checkFieldToken
+        #define checkFieldToken(TokType, Type)                                \
+        if (driver_.isVariable<Type>(ident, true))                            \
+        {                                                                     \
+            return TokType;                                                   \
+        }
+
+        checkFieldToken(TOK_PSCALAR_ID, scalar);
+        checkFieldToken(TOK_PVECTOR_ID, vector);
+        checkFieldToken(TOK_PTENSOR_ID, tensor);
+        checkFieldToken(TOK_PTENSOR_ID, tensor);
+        checkFieldToken(TOK_PSYM_TENSOR_ID, symmTensor);
+        checkFieldToken(TOK_PSPH_TENSOR_ID, sphericalTensor);
+
+        // Not tested: checkFieldToken(TOK_PBOOL_ID, bool);
+    }
+    #endif
+
+    #undef checkFieldToken
+
+    // Check registered fields and/or disk-files
+    {
+        const word fieldType(driver_.getFieldClassName(ident));
+
+        int tokType = fieldTokenEnums.get(fieldType, -1);
+
+        if (tokType > 0)
+        {
+            return tokType;
+        }
+    }
+
+    return -1;
+}
+
+} // End anonymous namespace
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+// 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
+
+#define EMIT_TOKEN(T)                                                         \
+    driver_.parsePosition() = (ts-buf);                                       \
+    DebugInfo<< STRINGIFY(T) << ": " << driver_.parsePosition() << nl;        \
+    parser_->parse(TOKEN_OF(T), nullptr);                                     \
+    driver_.parsePosition() = (p-buf);
+
+
+
+#line 276 "patchExprScanner.cc"
+static const int patchExpr_start = 11;
+static const int patchExpr_first_final = 11;
+static const int patchExpr_error = 0;
+
+static const int patchExpr_en_main = 11;
+
+
+#line 414 "patchExprScanner.rl"
+
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::expressions::patchExpr::scanner::~scanner()
+{
+    if (parser_)
+    {
+        delete parser_;
+    }
+}
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+bool Foam::expressions::patchExpr::scanner::dispatch_method
+(
+    const parseDriver& driver_,
+    scanToken& scanTok,
+    word&& ident
+) const
+{
+    if (ident[0] == '.')
+    {
+        ident.erase(0, 1);
+    }
+
+    DebugInfo
+        << "Method:" << ident
+        << " at " << driver_.parsePosition() << nl;
+
+    const int methType = fieldMethodEnums.get(ident, -1);
+
+    if (methType > 0)
+    {
+        // Dispatch '.' and "method" separately
+        parser_->parse(TOK_DOT, nullptr);
+        parser_->parse(methType, nullptr);
+
+        return true;
+    }
+
+    driver_.reportFatal("Unknown method: " + ident);
+    return false;
+}
+
+
+bool Foam::expressions::patchExpr::scanner::dispatch_ident
+(
+    const parseDriver& driver_,
+    scanToken& scanTok,
+    word&& ident
+) const
+{
+    int tokType = -1;
+
+    const bool quoted =
+    (
+        (ident.front() == '"' || ident.front() == '\'')
+     && (ident.front() == ident.back())
+    );
+
+    if (quoted)
+    {
+        ident.erase(ident.size()-1);
+        ident.erase(0, 1);
+    }
+    else
+    {
+        // Check for function name
+        tokType = funcTokenEnums.get(ident, -1);
+
+        if (tokType > 0)
+        {
+            DebugInfo
+                << "Emit:" << ident << " function:"
+                << parser_->nameOfToken(tokType) << nl;
+
+            parser_->parse(tokType, nullptr);
+            return true;
+        }
+
+        #ifdef HAS_LOOKBEHIND_TOKENS
+        // Specials such "cset" also reset the look-behind
+        tokType = lookBehindTokenEnums.get(ident, -1);
+
+        if (tokType > 0)
+        {
+            DebugInfo
+                << "Emit:" << ident << " as look-behind:"
+                << parser_->nameOfToken(tokType) << nl;
+
+            driver_.resetStashedTokenId(tokType);
+            parser_->parse(tokType, nullptr);
+            return true;
+        }
+        #endif
+    }
+
+
+    // Can also peek at stashed "look-behind"
+    // const int lookBehind = driver_.stashedTokenId();
+
+    tokType = driverTokenType(driver_, ident);
+
+    if (tokType > 0)
+    {
+        DebugInfo
+            << "Emit:" << ident << " token:"
+            << parser_->nameOfToken(tokType) << nl;
+
+        scanTok.name = new Foam::word(std::move(ident));
+        parser_->parse(tokType, &scanTok);
+
+        return true;
+    }
+
+
+    // Not found? Attempt to strip off '.x' endings etc,
+    // but not when quoted
+
+    const auto dot = ident.rfind('.');
+    const int methType =
+    (
+        quoted || dot == std::string::npos
+      ? -1
+      : fieldMethodEnums.get(ident.substr(dot+1), -1)
+    );
+
+    if
+    (
+        methType > 0
+     && (tokType = driverTokenType(driver_, ident.substr(0, dot))) > 0
+    )
+    {
+        DebugInfo
+            << "Emit:" << ident.substr(0, dot).c_str() << " token:"
+            << parser_->nameOfToken(tokType) << " with "
+            << ident.substr(dot).c_str() << " token:"
+            << parser_->nameOfToken(methType) << nl;
+
+        // The field (before the ".")
+        ident.erase(dot);
+
+        scanTok.name = new Foam::word(std::move(ident));
+        parser_->parse(tokType, &scanTok);
+
+        // Dispatch '.' and "method" separately
+        parser_->parse(TOK_DOT, nullptr);
+        parser_->parse(methType, nullptr);
+
+        return true;
+    }
+
+    driver_.reportFatal
+    (
+        "Object " + ident + " does not exist or wrong type"
+    );
+
+    return false;
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::expressions::patchExpr::scanner::process
+(
+    const std::string& str,
+    size_t strBeg,
+    size_t strLen,
+    parseDriver& driver_
+)
+{
+    // Save debug value
+    const int oldDebug = debug;
+
+    if (driver_.debugScanner())
+    {
+        debug |= 4;
+    }
+
+    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_);
+
+    // Scan token type
+    scanToken scanTok;
+
+    // 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
+    
+#line 511 "patchExprScanner.cc"
+	{
+	cs = patchExpr_start;
+	ts = 0;
+	te = 0;
+	act = 0;
+	}
+
+#line 639 "patchExprScanner.rl"
+   /* ^^^ FSM initialization here ^^^ */;
+
+    
+#line 523 "patchExprScanner.cc"
+	{
+	if ( p == pe )
+		goto _test_eof;
+	switch ( cs )
+	{
+tr2:
+#line 298 "patchExprScanner.rl"
+	{te = p+1;{
+        driver_.parsePosition() = (ts-buf);
+        dispatch_ident(driver_, scanTok, word(ts, te-ts, false));
+        driver_.parsePosition() = (p-buf);
+    }}
+	goto st11;
+tr4:
+#line 298 "patchExprScanner.rl"
+	{te = p+1;{
+        driver_.parsePosition() = (ts-buf);
+        dispatch_ident(driver_, scanTok, word(ts, te-ts, false));
+        driver_.parsePosition() = (p-buf);
+    }}
+	goto st11;
+tr5:
+#line 276 "patchExprScanner.rl"
+	{{p = ((te))-1;}{
+        driver_.parsePosition() = (ts-buf);
+
+        DebugInfo
+            << "Number:" << std::string(ts, te-ts).c_str()
+            << " at " << driver_.parsePosition() << nl;
+
+        if (readScalar(std::string(ts, te-ts), scanTok.svalue))
+        {
+            parser_->parse(TOKEN_OF(NUMBER), &scanTok);
+        }
+        else
+        {
+            driver_.reportFatal
+            (
+                "Error parsing number: " + std::string(ts, te-ts)
+            );
+        }
+
+        driver_.parsePosition() = (p-buf);
+    }}
+	goto st11;
+tr8:
+#line 341 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(EQUAL); }}
+	goto st11;
+tr9:
+#line 395 "patchExprScanner.rl"
+	{{p = ((te))-1;}{ EMIT_TOKEN(TENSOR); }}
+	goto st11;
+tr11:
+#line 403 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(UNIT_TENSOR); }}
+	goto st11;
+tr12:
+#line 344 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(LOR); }}
+	goto st11;
+tr16:
+#line 326 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(PERCENT); }}
+	goto st11;
+tr19:
+#line 327 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(LPAREN); }}
+	goto st11;
+tr20:
+#line 328 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(RPAREN); }}
+	goto st11;
+tr21:
+#line 329 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(TIMES); }}
+	goto st11;
+tr22:
+#line 330 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(PLUS); }}
+	goto st11;
+tr23:
+#line 332 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(COMMA); }}
+	goto st11;
+tr24:
+#line 331 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(MINUS); }}
+	goto st11;
+tr26:
+#line 334 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(DIVIDE); }}
+	goto st11;
+tr28:
+#line 336 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(COLON); }}
+	goto st11;
+tr32:
+#line 335 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(QUESTION); }}
+	goto st11;
+tr35:
+#line 347 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(BIT_XOR); }}
+	goto st11;
+tr52:
+#line 320 "patchExprScanner.rl"
+	{te = p;p--;}
+	goto st11;
+tr53:
+#line 325 "patchExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(NOT); }}
+	goto st11;
+tr54:
+#line 342 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(NOT_EQUAL); }}
+	goto st11;
+tr55:
+#line 345 "patchExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(BIT_AND); }}
+	goto st11;
+tr56:
+#line 343 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(LAND); }}
+	goto st11;
+tr57:
+#line 333 "patchExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(DOT); }}
+	goto st11;
+tr60:
+#line 276 "patchExprScanner.rl"
+	{te = p;p--;{
+        driver_.parsePosition() = (ts-buf);
+
+        DebugInfo
+            << "Number:" << std::string(ts, te-ts).c_str()
+            << " at " << driver_.parsePosition() << nl;
+
+        if (readScalar(std::string(ts, te-ts), scanTok.svalue))
+        {
+            parser_->parse(TOKEN_OF(NUMBER), &scanTok);
+        }
+        else
+        {
+            driver_.reportFatal
+            (
+                "Error parsing number: " + std::string(ts, te-ts)
+            );
+        }
+
+        driver_.parsePosition() = (p-buf);
+    }}
+	goto st11;
+tr62:
+#line 304 "patchExprScanner.rl"
+	{te = p;p--;{
+        // Tokenized ".method" - dispatch '.' and "method" separately
+        driver_.parsePosition() = (ts-buf);
+        dispatch_method(driver_, scanTok, word(ts+1, te-ts-1, false));
+        driver_.parsePosition() = (p-buf);
+    }}
+	goto st11;
+tr63:
+#line 337 "patchExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(LESS); }}
+	goto st11;
+tr64:
+#line 338 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(LESS_EQ); }}
+	goto st11;
+tr65:
+#line 339 "patchExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(GREATER); }}
+	goto st11;
+tr66:
+#line 340 "patchExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(GREATER_EQ); }}
+	goto st11;
+tr67:
+#line 298 "patchExprScanner.rl"
+	{te = p;p--;{
+        driver_.parsePosition() = (ts-buf);
+        dispatch_ident(driver_, scanTok, word(ts, te-ts, false));
+        driver_.parsePosition() = (p-buf);
+    }}
+	goto st11;
+tr69:
+#line 1 "NONE"
+	{	switch( act ) {
+	case 26:
+	{{p = ((te))-1;} EMIT_TOKEN(PI); }
+	break;
+	case 27:
+	{{p = ((te))-1;} EMIT_TOKEN(DEG_TO_RAD); }
+	break;
+	case 28:
+	{{p = ((te))-1;} EMIT_TOKEN(RAD_TO_DEG); }
+	break;
+	case 29:
+	{{p = ((te))-1;} EMIT_TOKEN(EXP); }
+	break;
+	case 31:
+	{{p = ((te))-1;} EMIT_TOKEN(LOG10); }
+	break;
+	case 32:
+	{{p = ((te))-1;} EMIT_TOKEN(POW); }
+	break;
+	case 34:
+	{{p = ((te))-1;} EMIT_TOKEN(SQRT); }
+	break;
+	case 35:
+	{{p = ((te))-1;} EMIT_TOKEN(CBRT); }
+	break;
+	case 39:
+	{{p = ((te))-1;} EMIT_TOKEN(ASIN); }
+	break;
+	case 40:
+	{{p = ((te))-1;} EMIT_TOKEN(ACOS); }
+	break;
+	case 42:
+	{{p = ((te))-1;} EMIT_TOKEN(ATAN2); }
+	break;
+	case 43:
+	{{p = ((te))-1;} EMIT_TOKEN(SINH); }
+	break;
+	case 44:
+	{{p = ((te))-1;} EMIT_TOKEN(COSH); }
+	break;
+	case 45:
+	{{p = ((te))-1;} EMIT_TOKEN(TANH); }
+	break;
+	case 47:
+	{{p = ((te))-1;} EMIT_TOKEN(MAGSQR); }
+	break;
+	case 50:
+	{{p = ((te))-1;} EMIT_TOKEN(POS0); }
+	break;
+	case 51:
+	{{p = ((te))-1;} EMIT_TOKEN(NEG0); }
+	break;
+	case 52:
+	{{p = ((te))-1;} EMIT_TOKEN(SIGN); }
+	break;
+	case 53:
+	{{p = ((te))-1;} EMIT_TOKEN(MIN); }
+	break;
+	case 54:
+	{{p = ((te))-1;} EMIT_TOKEN(MAX); }
+	break;
+	case 55:
+	{{p = ((te))-1;} EMIT_TOKEN(AVERAGE); }
+	break;
+	case 56:
+	{{p = ((te))-1;} EMIT_TOKEN(SUM); }
+	break;
+	case 57:
+	{{p = ((te))-1;} EMIT_TOKEN(WEIGHT_AVERAGE); }
+	break;
+	case 58:
+	{{p = ((te))-1;} EMIT_TOKEN(WEIGHT_SUM); }
+	break;
+	case 59:
+	{{p = ((te))-1;} EMIT_TOKEN(RAND); }
+	break;
+	case 60:
+	{{p = ((te))-1;} EMIT_TOKEN(BOOL); }
+	break;
+	case 61:
+	{{p = ((te))-1;} EMIT_TOKEN(VECTOR); }
+	break;
+	case 63:
+	{{p = ((te))-1;} EMIT_TOKEN(SYM_TENSOR); }
+	break;
+	case 64:
+	{{p = ((te))-1;} EMIT_TOKEN(SPH_TENSOR); }
+	break;
+	case 65:
+	{{p = ((te))-1;} EMIT_TOKEN(ZERO); }
+	break;
+	case 66:
+	{{p = ((te))-1;} EMIT_TOKEN(LTRUE); }
+	break;
+	case 67:
+	{{p = ((te))-1;} EMIT_TOKEN(LFALSE); }
+	break;
+	case 69:
+	{{p = ((te))-1;} EMIT_TOKEN(TIME); }
+	break;
+	case 70:
+	{{p = ((te))-1;}
+        driver_.parsePosition() = (ts-buf);
+        dispatch_ident(driver_, scanTok, word(ts, te-ts, false));
+        driver_.parsePosition() = (p-buf);
+    }
+	break;
+	}
+	}
+	goto st11;
+tr83:
+#line 369 "patchExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(ATAN); }}
+	goto st11;
+tr98:
+#line 365 "patchExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(COS); }}
+	goto st11;
+tr115:
+#line 358 "patchExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(LOG); }}
+	goto st11;
+tr122:
+#line 374 "patchExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(MAG); }}
+	goto st11;
+tr129:
+#line 378 "patchExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(NEG); }}
+	goto st11;
+tr135:
+#line 377 "patchExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(POS); }}
+	goto st11;
+tr154:
+#line 364 "patchExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(SIN); }}
+	goto st11;
+tr170:
+#line 361 "patchExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(SQR); }}
+	goto st11;
+tr186:
+#line 366 "patchExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(TAN); }}
+	goto st11;
+tr192:
+#line 395 "patchExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(TENSOR); }}
+	goto st11;
+st11:
+#line 1 "NONE"
+	{ts = 0;}
+	if ( ++p == pe )
+		goto _test_eof11;
+case 11:
+#line 1 "NONE"
+	{ts = p;}
+#line 870 "patchExprScanner.cc"
+	switch( (*p) ) {
+		case 32: goto st12;
+		case 33: goto st13;
+		case 34: goto st1;
+		case 37: goto tr16;
+		case 38: goto st14;
+		case 39: goto st3;
+		case 40: goto tr19;
+		case 41: goto tr20;
+		case 42: goto tr21;
+		case 43: goto tr22;
+		case 44: goto tr23;
+		case 45: goto tr24;
+		case 46: goto st15;
+		case 47: goto tr26;
+		case 58: goto tr28;
+		case 60: goto st20;
+		case 61: goto st7;
+		case 62: goto st21;
+		case 63: goto tr32;
+		case 90: goto st24;
+		case 94: goto tr35;
+		case 95: goto st22;
+		case 97: goto st27;
+		case 98: goto st40;
+		case 99: goto st43;
+		case 100: goto st48;
+		case 101: goto st55;
+		case 102: goto st57;
+		case 108: goto st61;
+		case 109: goto st65;
+		case 110: goto st71;
+		case 112: goto st74;
+		case 114: goto st77;
+		case 115: goto st85;
+		case 116: goto st113;
+		case 118: goto st125;
+		case 119: goto st130;
+		case 124: goto st10;
+	}
+	if ( (*p) < 48 ) {
+		if ( 9 <= (*p) && (*p) <= 13 )
+			goto st12;
+	} else if ( (*p) > 57 ) {
+		if ( (*p) > 89 ) {
+			if ( 103 <= (*p) && (*p) <= 122 )
+				goto st22;
+		} else if ( (*p) >= 65 )
+			goto st22;
+	} else
+		goto tr27;
+	goto st0;
+st0:
+cs = 0;
+	goto _out;
+st12:
+	if ( ++p == pe )
+		goto _test_eof12;
+case 12:
+	if ( (*p) == 32 )
+		goto st12;
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st12;
+	goto tr52;
+st13:
+	if ( ++p == pe )
+		goto _test_eof13;
+case 13:
+	if ( (*p) == 61 )
+		goto tr54;
+	goto tr53;
+st1:
+	if ( ++p == pe )
+		goto _test_eof1;
+case 1:
+	if ( (*p) == 34 )
+		goto st0;
+	goto st2;
+st2:
+	if ( ++p == pe )
+		goto _test_eof2;
+case 2:
+	if ( (*p) == 34 )
+		goto tr2;
+	goto st2;
+st14:
+	if ( ++p == pe )
+		goto _test_eof14;
+case 14:
+	if ( (*p) == 38 )
+		goto tr56;
+	goto tr55;
+st3:
+	if ( ++p == pe )
+		goto _test_eof3;
+case 3:
+	if ( (*p) == 39 )
+		goto st0;
+	goto st4;
+st4:
+	if ( ++p == pe )
+		goto _test_eof4;
+case 4:
+	if ( (*p) == 39 )
+		goto tr4;
+	goto st4;
+st15:
+	if ( ++p == pe )
+		goto _test_eof15;
+case 15:
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr58;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto st18;
+	} else
+		goto st18;
+	goto tr57;
+tr58:
+#line 1 "NONE"
+	{te = p+1;}
+	goto st16;
+st16:
+	if ( ++p == pe )
+		goto _test_eof16;
+case 16:
+#line 998 "patchExprScanner.cc"
+	switch( (*p) ) {
+		case 69: goto st5;
+		case 101: goto st5;
+	}
+	if ( 48 <= (*p) && (*p) <= 57 )
+		goto tr58;
+	goto tr60;
+st5:
+	if ( ++p == pe )
+		goto _test_eof5;
+case 5:
+	switch( (*p) ) {
+		case 43: goto st6;
+		case 45: goto st6;
+	}
+	if ( 48 <= (*p) && (*p) <= 57 )
+		goto st17;
+	goto tr5;
+st6:
+	if ( ++p == pe )
+		goto _test_eof6;
+case 6:
+	if ( 48 <= (*p) && (*p) <= 57 )
+		goto st17;
+	goto tr5;
+st17:
+	if ( ++p == pe )
+		goto _test_eof17;
+case 17:
+	if ( 48 <= (*p) && (*p) <= 57 )
+		goto st17;
+	goto tr60;
+st18:
+	if ( ++p == pe )
+		goto _test_eof18;
+case 18:
+	if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto st18;
+	} else if ( (*p) >= 65 )
+		goto st18;
+	goto tr62;
+tr27:
+#line 1 "NONE"
+	{te = p+1;}
+	goto st19;
+st19:
+	if ( ++p == pe )
+		goto _test_eof19;
+case 19:
+#line 1049 "patchExprScanner.cc"
+	switch( (*p) ) {
+		case 46: goto tr58;
+		case 69: goto st5;
+		case 101: goto st5;
+	}
+	if ( 48 <= (*p) && (*p) <= 57 )
+		goto tr27;
+	goto tr60;
+st20:
+	if ( ++p == pe )
+		goto _test_eof20;
+case 20:
+	if ( (*p) == 61 )
+		goto tr64;
+	goto tr63;
+st7:
+	if ( ++p == pe )
+		goto _test_eof7;
+case 7:
+	if ( (*p) == 61 )
+		goto tr8;
+	goto st0;
+st21:
+	if ( ++p == pe )
+		goto _test_eof21;
+case 21:
+	if ( (*p) == 61 )
+		goto tr66;
+	goto tr65;
+st22:
+	if ( ++p == pe )
+		goto _test_eof22;
+case 22:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+tr68:
+#line 1 "NONE"
+	{te = p+1;}
+#line 298 "patchExprScanner.rl"
+	{act = 70;}
+	goto st23;
+tr72:
+#line 1 "NONE"
+	{te = p+1;}
+#line 400 "patchExprScanner.rl"
+	{act = 65;}
+	goto st23;
+tr78:
+#line 1 "NONE"
+	{te = p+1;}
+#line 368 "patchExprScanner.rl"
+	{act = 40;}
+	goto st23;
+tr80:
+#line 1 "NONE"
+	{te = p+1;}
+#line 367 "patchExprScanner.rl"
+	{act = 39;}
+	goto st23;
+tr84:
+#line 1 "NONE"
+	{te = p+1;}
+#line 370 "patchExprScanner.rl"
+	{act = 42;}
+	goto st23;
+tr89:
+#line 1 "NONE"
+	{te = p+1;}
+#line 386 "patchExprScanner.rl"
+	{act = 55;}
+	goto st23;
+tr92:
+#line 1 "NONE"
+	{te = p+1;}
+#line 393 "patchExprScanner.rl"
+	{act = 60;}
+	goto st23;
+tr96:
+#line 1 "NONE"
+	{te = p+1;}
+#line 363 "patchExprScanner.rl"
+	{act = 35;}
+	goto st23;
+tr99:
+#line 1 "NONE"
+	{te = p+1;}
+#line 372 "patchExprScanner.rl"
+	{act = 44;}
+	goto st23;
+tr106:
+#line 1 "NONE"
+	{te = p+1;}
+#line 355 "patchExprScanner.rl"
+	{act = 27;}
+	goto st23;
+tr108:
+#line 1 "NONE"
+	{te = p+1;}
+#line 357 "patchExprScanner.rl"
+	{act = 29;}
+	goto st23;
+tr112:
+#line 1 "NONE"
+	{te = p+1;}
+#line 402 "patchExprScanner.rl"
+	{act = 67;}
+	goto st23;
+tr117:
+#line 1 "NONE"
+	{te = p+1;}
+#line 359 "patchExprScanner.rl"
+	{act = 31;}
+	goto st23;
+tr121:
+#line 1 "NONE"
+	{te = p+1;}
+#line 385 "patchExprScanner.rl"
+	{act = 54;}
+	goto st23;
+tr125:
+#line 1 "NONE"
+	{te = p+1;}
+#line 375 "patchExprScanner.rl"
+	{act = 47;}
+	goto st23;
+tr126:
+#line 1 "NONE"
+	{te = p+1;}
+#line 384 "patchExprScanner.rl"
+	{act = 53;}
+	goto st23;
+tr130:
+#line 1 "NONE"
+	{te = p+1;}
+#line 380 "patchExprScanner.rl"
+	{act = 51;}
+	goto st23;
+tr131:
+#line 1 "NONE"
+	{te = p+1;}
+#line 354 "patchExprScanner.rl"
+	{act = 26;}
+	goto st23;
+tr134:
+#line 1 "NONE"
+	{te = p+1;}
+#line 360 "patchExprScanner.rl"
+	{act = 32;}
+	goto st23;
+tr136:
+#line 1 "NONE"
+	{te = p+1;}
+#line 379 "patchExprScanner.rl"
+	{act = 50;}
+	goto st23;
+tr144:
+#line 1 "NONE"
+	{te = p+1;}
+#line 356 "patchExprScanner.rl"
+	{act = 28;}
+	goto st23;
+tr145:
+#line 1 "NONE"
+	{te = p+1;}
+#line 390 "patchExprScanner.rl"
+	{act = 59;}
+	goto st23;
+tr153:
+#line 1 "NONE"
+	{te = p+1;}
+#line 381 "patchExprScanner.rl"
+	{act = 52;}
+	goto st23;
+tr155:
+#line 1 "NONE"
+	{te = p+1;}
+#line 371 "patchExprScanner.rl"
+	{act = 43;}
+	goto st23;
+tr168:
+#line 1 "NONE"
+	{te = p+1;}
+#line 397 "patchExprScanner.rl"
+	{act = 64;}
+	goto st23;
+tr171:
+#line 1 "NONE"
+	{te = p+1;}
+#line 362 "patchExprScanner.rl"
+	{act = 34;}
+	goto st23;
+tr172:
+#line 1 "NONE"
+	{te = p+1;}
+#line 387 "patchExprScanner.rl"
+	{act = 56;}
+	goto st23;
+tr180:
+#line 1 "NONE"
+	{te = p+1;}
+#line 396 "patchExprScanner.rl"
+	{act = 63;}
+	goto st23;
+tr187:
+#line 1 "NONE"
+	{te = p+1;}
+#line 373 "patchExprScanner.rl"
+	{act = 45;}
+	goto st23;
+tr195:
+#line 1 "NONE"
+	{te = p+1;}
+#line 404 "patchExprScanner.rl"
+	{act = 69;}
+	goto st23;
+tr197:
+#line 1 "NONE"
+	{te = p+1;}
+#line 401 "patchExprScanner.rl"
+	{act = 66;}
+	goto st23;
+tr202:
+#line 1 "NONE"
+	{te = p+1;}
+#line 394 "patchExprScanner.rl"
+	{act = 61;}
+	goto st23;
+tr215:
+#line 1 "NONE"
+	{te = p+1;}
+#line 388 "patchExprScanner.rl"
+	{act = 57;}
+	goto st23;
+tr217:
+#line 1 "NONE"
+	{te = p+1;}
+#line 389 "patchExprScanner.rl"
+	{act = 58;}
+	goto st23;
+st23:
+	if ( ++p == pe )
+		goto _test_eof23;
+case 23:
+#line 1304 "patchExprScanner.cc"
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr69;
+st24:
+	if ( ++p == pe )
+		goto _test_eof24;
+case 24:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st25;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st25:
+	if ( ++p == pe )
+		goto _test_eof25;
+case 25:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto st26;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st26:
+	if ( ++p == pe )
+		goto _test_eof26;
+case 26:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto tr72;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st27:
+	if ( ++p == pe )
+		goto _test_eof27;
+case 27:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 99: goto st28;
+		case 115: goto st30;
+		case 116: goto st32;
+		case 118: goto st35;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st28:
+	if ( ++p == pe )
+		goto _test_eof28;
+case 28:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st29;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st29:
+	if ( ++p == pe )
+		goto _test_eof29;
+case 29:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 115: goto tr78;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st30:
+	if ( ++p == pe )
+		goto _test_eof30;
+case 30:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 105: goto st31;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st31:
+	if ( ++p == pe )
+		goto _test_eof31;
+case 31:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 110: goto tr80;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st32:
+	if ( ++p == pe )
+		goto _test_eof32;
+case 32:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st33;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st33:
+	if ( ++p == pe )
+		goto _test_eof33;
+case 33:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 110: goto st34;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st34:
+	if ( ++p == pe )
+		goto _test_eof34;
+case 34:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 50: goto tr84;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr83;
+st35:
+	if ( ++p == pe )
+		goto _test_eof35;
+case 35:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st36;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st36:
+	if ( ++p == pe )
+		goto _test_eof36;
+case 36:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto st37;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st37:
+	if ( ++p == pe )
+		goto _test_eof37;
+case 37:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st38;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st38:
+	if ( ++p == pe )
+		goto _test_eof38;
+case 38:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto st39;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st39:
+	if ( ++p == pe )
+		goto _test_eof39;
+case 39:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto tr89;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st40:
+	if ( ++p == pe )
+		goto _test_eof40;
+case 40:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st41;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st41:
+	if ( ++p == pe )
+		goto _test_eof41;
+case 41:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st42;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st42:
+	if ( ++p == pe )
+		goto _test_eof42;
+case 42:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 108: goto tr92;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st43:
+	if ( ++p == pe )
+		goto _test_eof43;
+case 43:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 98: goto st44;
+		case 111: goto st46;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st44:
+	if ( ++p == pe )
+		goto _test_eof44;
+case 44:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto st45;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st45:
+	if ( ++p == pe )
+		goto _test_eof45;
+case 45:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 116: goto tr96;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st46:
+	if ( ++p == pe )
+		goto _test_eof46;
+case 46:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 115: goto st47;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st47:
+	if ( ++p == pe )
+		goto _test_eof47;
+case 47:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 104: goto tr99;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr98;
+st48:
+	if ( ++p == pe )
+		goto _test_eof48;
+case 48:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st49;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st49:
+	if ( ++p == pe )
+		goto _test_eof49;
+case 49:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto st50;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st50:
+	if ( ++p == pe )
+		goto _test_eof50;
+case 50:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 84: goto st51;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st51:
+	if ( ++p == pe )
+		goto _test_eof51;
+case 51:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st52;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st52:
+	if ( ++p == pe )
+		goto _test_eof52;
+case 52:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 82: goto st53;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st53:
+	if ( ++p == pe )
+		goto _test_eof53;
+case 53:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st54;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st54:
+	if ( ++p == pe )
+		goto _test_eof54;
+case 54:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 100: goto tr106;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st55:
+	if ( ++p == pe )
+		goto _test_eof55;
+case 55:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 120: goto st56;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st56:
+	if ( ++p == pe )
+		goto _test_eof56;
+case 56:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 112: goto tr108;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st57:
+	if ( ++p == pe )
+		goto _test_eof57;
+case 57:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st58;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st58:
+	if ( ++p == pe )
+		goto _test_eof58;
+case 58:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 108: goto st59;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st59:
+	if ( ++p == pe )
+		goto _test_eof59;
+case 59:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 115: goto st60;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st60:
+	if ( ++p == pe )
+		goto _test_eof60;
+case 60:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto tr112;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st61:
+	if ( ++p == pe )
+		goto _test_eof61;
+case 61:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st62;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st62:
+	if ( ++p == pe )
+		goto _test_eof62;
+case 62:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto st63;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st63:
+	if ( ++p == pe )
+		goto _test_eof63;
+case 63:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 49: goto st64;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr115;
+st64:
+	if ( ++p == pe )
+		goto _test_eof64;
+case 64:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 48: goto tr117;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 49 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st65:
+	if ( ++p == pe )
+		goto _test_eof65;
+case 65:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st66;
+		case 105: goto st70;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st66:
+	if ( ++p == pe )
+		goto _test_eof66;
+case 66:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto st67;
+		case 120: goto tr121;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st67:
+	if ( ++p == pe )
+		goto _test_eof67;
+case 67:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 83: goto st68;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr122;
+st68:
+	if ( ++p == pe )
+		goto _test_eof68;
+case 68:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 113: goto st69;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st69:
+	if ( ++p == pe )
+		goto _test_eof69;
+case 69:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto tr125;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st70:
+	if ( ++p == pe )
+		goto _test_eof70;
+case 70:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 110: goto tr126;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st71:
+	if ( ++p == pe )
+		goto _test_eof71;
+case 71:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st72;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st72:
+	if ( ++p == pe )
+		goto _test_eof72;
+case 72:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto st73;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st73:
+	if ( ++p == pe )
+		goto _test_eof73;
+case 73:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 48: goto tr130;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 49 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr129;
+st74:
+	if ( ++p == pe )
+		goto _test_eof74;
+case 74:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 105: goto tr131;
+		case 111: goto st75;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st75:
+	if ( ++p == pe )
+		goto _test_eof75;
+case 75:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 115: goto st76;
+		case 119: goto tr134;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st76:
+	if ( ++p == pe )
+		goto _test_eof76;
+case 76:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 48: goto tr136;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 49 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr135;
+st77:
+	if ( ++p == pe )
+		goto _test_eof77;
+case 77:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st78;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st78:
+	if ( ++p == pe )
+		goto _test_eof78;
+case 78:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 100: goto st79;
+		case 110: goto st84;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st79:
+	if ( ++p == pe )
+		goto _test_eof79;
+case 79:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 84: goto st80;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st80:
+	if ( ++p == pe )
+		goto _test_eof80;
+case 80:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st81;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st81:
+	if ( ++p == pe )
+		goto _test_eof81;
+case 81:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 68: goto st82;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st82:
+	if ( ++p == pe )
+		goto _test_eof82;
+case 82:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st83;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st83:
+	if ( ++p == pe )
+		goto _test_eof83;
+case 83:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto tr144;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st84:
+	if ( ++p == pe )
+		goto _test_eof84;
+case 84:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 100: goto tr145;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st85:
+	if ( ++p == pe )
+		goto _test_eof85;
+case 85:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 105: goto st86;
+		case 112: goto st89;
+		case 113: goto st102;
+		case 117: goto st104;
+		case 121: goto st105;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st86:
+	if ( ++p == pe )
+		goto _test_eof86;
+case 86:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto st87;
+		case 110: goto st88;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st87:
+	if ( ++p == pe )
+		goto _test_eof87;
+case 87:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 110: goto tr153;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st88:
+	if ( ++p == pe )
+		goto _test_eof88;
+case 88:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 104: goto tr155;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr154;
+st89:
+	if ( ++p == pe )
+		goto _test_eof89;
+case 89:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 104: goto st90;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st90:
+	if ( ++p == pe )
+		goto _test_eof90;
+case 90:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st91;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st91:
+	if ( ++p == pe )
+		goto _test_eof91;
+case 91:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto st92;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st92:
+	if ( ++p == pe )
+		goto _test_eof92;
+case 92:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 105: goto st93;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st93:
+	if ( ++p == pe )
+		goto _test_eof93;
+case 93:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 99: goto st94;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st94:
+	if ( ++p == pe )
+		goto _test_eof94;
+case 94:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st95;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st95:
+	if ( ++p == pe )
+		goto _test_eof95;
+case 95:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 108: goto st96;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st96:
+	if ( ++p == pe )
+		goto _test_eof96;
+case 96:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 84: goto st97;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st97:
+	if ( ++p == pe )
+		goto _test_eof97;
+case 97:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st98;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st98:
+	if ( ++p == pe )
+		goto _test_eof98;
+case 98:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 110: goto st99;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st99:
+	if ( ++p == pe )
+		goto _test_eof99;
+case 99:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 115: goto st100;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st100:
+	if ( ++p == pe )
+		goto _test_eof100;
+case 100:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st101;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st101:
+	if ( ++p == pe )
+		goto _test_eof101;
+case 101:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto tr168;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st102:
+	if ( ++p == pe )
+		goto _test_eof102;
+case 102:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto st103;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st103:
+	if ( ++p == pe )
+		goto _test_eof103;
+case 103:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 116: goto tr171;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr170;
+st104:
+	if ( ++p == pe )
+		goto _test_eof104;
+case 104:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 109: goto tr172;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st105:
+	if ( ++p == pe )
+		goto _test_eof105;
+case 105:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 109: goto st106;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st106:
+	if ( ++p == pe )
+		goto _test_eof106;
+case 106:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 109: goto st107;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st107:
+	if ( ++p == pe )
+		goto _test_eof107;
+case 107:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 84: goto st108;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st108:
+	if ( ++p == pe )
+		goto _test_eof108;
+case 108:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st109;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st109:
+	if ( ++p == pe )
+		goto _test_eof109;
+case 109:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 110: goto st110;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st110:
+	if ( ++p == pe )
+		goto _test_eof110;
+case 110:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 115: goto st111;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st111:
+	if ( ++p == pe )
+		goto _test_eof111;
+case 111:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st112;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st112:
+	if ( ++p == pe )
+		goto _test_eof112;
+case 112:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto tr180;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st113:
+	if ( ++p == pe )
+		goto _test_eof113;
+case 113:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st114;
+		case 101: goto st116;
+		case 105: goto st121;
+		case 114: goto st123;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st114:
+	if ( ++p == pe )
+		goto _test_eof114;
+case 114:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 110: goto st115;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st115:
+	if ( ++p == pe )
+		goto _test_eof115;
+case 115:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 104: goto tr187;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr186;
+st116:
+	if ( ++p == pe )
+		goto _test_eof116;
+case 116:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 110: goto st117;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st117:
+	if ( ++p == pe )
+		goto _test_eof117;
+case 117:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 115: goto st118;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st118:
+	if ( ++p == pe )
+		goto _test_eof118;
+case 118:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st119;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st119:
+	if ( ++p == pe )
+		goto _test_eof119;
+case 119:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto tr191;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+tr191:
+#line 1 "NONE"
+	{te = p+1;}
+	goto st120;
+st120:
+	if ( ++p == pe )
+		goto _test_eof120;
+case 120:
+#line 3071 "patchExprScanner.cc"
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 58: goto st8;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr192;
+st8:
+	if ( ++p == pe )
+		goto _test_eof8;
+case 8:
+	if ( (*p) == 58 )
+		goto st9;
+	goto tr9;
+st9:
+	if ( ++p == pe )
+		goto _test_eof9;
+case 9:
+	if ( (*p) == 73 )
+		goto tr11;
+	goto tr9;
+st121:
+	if ( ++p == pe )
+		goto _test_eof121;
+case 121:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 109: goto st122;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st122:
+	if ( ++p == pe )
+		goto _test_eof122;
+case 122:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto tr195;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st123:
+	if ( ++p == pe )
+		goto _test_eof123;
+case 123:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 117: goto st124;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st124:
+	if ( ++p == pe )
+		goto _test_eof124;
+case 124:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto tr197;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st125:
+	if ( ++p == pe )
+		goto _test_eof125;
+case 125:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st126;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st126:
+	if ( ++p == pe )
+		goto _test_eof126;
+case 126:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 99: goto st127;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st127:
+	if ( ++p == pe )
+		goto _test_eof127;
+case 127:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 116: goto st128;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st128:
+	if ( ++p == pe )
+		goto _test_eof128;
+case 128:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st129;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st129:
+	if ( ++p == pe )
+		goto _test_eof129;
+case 129:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto tr202;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st130:
+	if ( ++p == pe )
+		goto _test_eof130;
+case 130:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st131;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st131:
+	if ( ++p == pe )
+		goto _test_eof131;
+case 131:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 105: goto st132;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st132:
+	if ( ++p == pe )
+		goto _test_eof132;
+case 132:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto st133;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st133:
+	if ( ++p == pe )
+		goto _test_eof133;
+case 133:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 104: goto st134;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st134:
+	if ( ++p == pe )
+		goto _test_eof134;
+case 134:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 116: goto st135;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st135:
+	if ( ++p == pe )
+		goto _test_eof135;
+case 135:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 65: goto st136;
+		case 83: goto st142;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 66 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st136:
+	if ( ++p == pe )
+		goto _test_eof136;
+case 136:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 118: goto st137;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st137:
+	if ( ++p == pe )
+		goto _test_eof137;
+case 137:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st138;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st138:
+	if ( ++p == pe )
+		goto _test_eof138;
+case 138:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto st139;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st139:
+	if ( ++p == pe )
+		goto _test_eof139;
+case 139:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st140;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st140:
+	if ( ++p == pe )
+		goto _test_eof140;
+case 140:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto st141;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st141:
+	if ( ++p == pe )
+		goto _test_eof141;
+case 141:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto tr215;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st142:
+	if ( ++p == pe )
+		goto _test_eof142;
+case 142:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 117: goto st143;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st143:
+	if ( ++p == pe )
+		goto _test_eof143;
+case 143:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 109: goto tr217;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st10:
+	if ( ++p == pe )
+		goto _test_eof10;
+case 10:
+	if ( (*p) == 124 )
+		goto tr12;
+	goto st0;
+	}
+	_test_eof11: cs = 11; goto _test_eof; 
+	_test_eof12: cs = 12; goto _test_eof; 
+	_test_eof13: cs = 13; goto _test_eof; 
+	_test_eof1: cs = 1; goto _test_eof; 
+	_test_eof2: cs = 2; goto _test_eof; 
+	_test_eof14: cs = 14; goto _test_eof; 
+	_test_eof3: cs = 3; goto _test_eof; 
+	_test_eof4: cs = 4; goto _test_eof; 
+	_test_eof15: cs = 15; goto _test_eof; 
+	_test_eof16: cs = 16; goto _test_eof; 
+	_test_eof5: cs = 5; goto _test_eof; 
+	_test_eof6: cs = 6; 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_eof7: cs = 7; 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_eof86: cs = 86; goto _test_eof; 
+	_test_eof87: cs = 87; goto _test_eof; 
+	_test_eof88: cs = 88; goto _test_eof; 
+	_test_eof89: cs = 89; goto _test_eof; 
+	_test_eof90: cs = 90; 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_eof94: cs = 94; goto _test_eof; 
+	_test_eof95: cs = 95; goto _test_eof; 
+	_test_eof96: cs = 96; goto _test_eof; 
+	_test_eof97: cs = 97; goto _test_eof; 
+	_test_eof98: cs = 98; goto _test_eof; 
+	_test_eof99: cs = 99; goto _test_eof; 
+	_test_eof100: cs = 100; goto _test_eof; 
+	_test_eof101: cs = 101; goto _test_eof; 
+	_test_eof102: cs = 102; goto _test_eof; 
+	_test_eof103: cs = 103; goto _test_eof; 
+	_test_eof104: cs = 104; goto _test_eof; 
+	_test_eof105: cs = 105; goto _test_eof; 
+	_test_eof106: cs = 106; goto _test_eof; 
+	_test_eof107: cs = 107; goto _test_eof; 
+	_test_eof108: cs = 108; goto _test_eof; 
+	_test_eof109: cs = 109; goto _test_eof; 
+	_test_eof110: cs = 110; goto _test_eof; 
+	_test_eof111: cs = 111; goto _test_eof; 
+	_test_eof112: cs = 112; goto _test_eof; 
+	_test_eof113: cs = 113; goto _test_eof; 
+	_test_eof114: cs = 114; goto _test_eof; 
+	_test_eof115: cs = 115; goto _test_eof; 
+	_test_eof116: cs = 116; goto _test_eof; 
+	_test_eof117: cs = 117; goto _test_eof; 
+	_test_eof118: cs = 118; goto _test_eof; 
+	_test_eof119: cs = 119; goto _test_eof; 
+	_test_eof120: cs = 120; goto _test_eof; 
+	_test_eof8: cs = 8; goto _test_eof; 
+	_test_eof9: cs = 9; goto _test_eof; 
+	_test_eof121: cs = 121; goto _test_eof; 
+	_test_eof122: cs = 122; goto _test_eof; 
+	_test_eof123: cs = 123; goto _test_eof; 
+	_test_eof124: cs = 124; goto _test_eof; 
+	_test_eof125: cs = 125; goto _test_eof; 
+	_test_eof126: cs = 126; goto _test_eof; 
+	_test_eof127: cs = 127; goto _test_eof; 
+	_test_eof128: cs = 128; goto _test_eof; 
+	_test_eof129: cs = 129; goto _test_eof; 
+	_test_eof130: cs = 130; goto _test_eof; 
+	_test_eof131: cs = 131; goto _test_eof; 
+	_test_eof132: cs = 132; goto _test_eof; 
+	_test_eof133: cs = 133; goto _test_eof; 
+	_test_eof134: cs = 134; goto _test_eof; 
+	_test_eof135: cs = 135; goto _test_eof; 
+	_test_eof136: cs = 136; goto _test_eof; 
+	_test_eof137: cs = 137; goto _test_eof; 
+	_test_eof138: cs = 138; goto _test_eof; 
+	_test_eof139: cs = 139; goto _test_eof; 
+	_test_eof140: cs = 140; goto _test_eof; 
+	_test_eof141: cs = 141; goto _test_eof; 
+	_test_eof142: cs = 142; goto _test_eof; 
+	_test_eof143: cs = 143; goto _test_eof; 
+	_test_eof10: cs = 10; goto _test_eof; 
+
+	_test_eof: {}
+	if ( p == eof )
+	{
+	switch ( cs ) {
+	case 12: goto tr52;
+	case 13: goto tr53;
+	case 14: goto tr55;
+	case 15: goto tr57;
+	case 16: goto tr60;
+	case 5: goto tr5;
+	case 6: goto tr5;
+	case 17: goto tr60;
+	case 18: goto tr62;
+	case 19: goto tr60;
+	case 20: goto tr63;
+	case 21: goto tr65;
+	case 22: goto tr67;
+	case 23: goto tr69;
+	case 24: goto tr67;
+	case 25: goto tr67;
+	case 26: goto tr67;
+	case 27: goto tr67;
+	case 28: goto tr67;
+	case 29: goto tr67;
+	case 30: goto tr67;
+	case 31: goto tr67;
+	case 32: goto tr67;
+	case 33: goto tr67;
+	case 34: goto tr83;
+	case 35: goto tr67;
+	case 36: goto tr67;
+	case 37: goto tr67;
+	case 38: goto tr67;
+	case 39: goto tr67;
+	case 40: goto tr67;
+	case 41: goto tr67;
+	case 42: goto tr67;
+	case 43: goto tr67;
+	case 44: goto tr67;
+	case 45: goto tr67;
+	case 46: goto tr67;
+	case 47: goto tr98;
+	case 48: goto tr67;
+	case 49: goto tr67;
+	case 50: goto tr67;
+	case 51: goto tr67;
+	case 52: goto tr67;
+	case 53: goto tr67;
+	case 54: goto tr67;
+	case 55: goto tr67;
+	case 56: goto tr67;
+	case 57: goto tr67;
+	case 58: goto tr67;
+	case 59: goto tr67;
+	case 60: goto tr67;
+	case 61: goto tr67;
+	case 62: goto tr67;
+	case 63: goto tr115;
+	case 64: goto tr67;
+	case 65: goto tr67;
+	case 66: goto tr67;
+	case 67: goto tr122;
+	case 68: goto tr67;
+	case 69: goto tr67;
+	case 70: goto tr67;
+	case 71: goto tr67;
+	case 72: goto tr67;
+	case 73: goto tr129;
+	case 74: goto tr67;
+	case 75: goto tr67;
+	case 76: goto tr135;
+	case 77: goto tr67;
+	case 78: goto tr67;
+	case 79: goto tr67;
+	case 80: goto tr67;
+	case 81: goto tr67;
+	case 82: goto tr67;
+	case 83: goto tr67;
+	case 84: goto tr67;
+	case 85: goto tr67;
+	case 86: goto tr67;
+	case 87: goto tr67;
+	case 88: goto tr154;
+	case 89: goto tr67;
+	case 90: goto tr67;
+	case 91: goto tr67;
+	case 92: goto tr67;
+	case 93: goto tr67;
+	case 94: goto tr67;
+	case 95: goto tr67;
+	case 96: goto tr67;
+	case 97: goto tr67;
+	case 98: goto tr67;
+	case 99: goto tr67;
+	case 100: goto tr67;
+	case 101: goto tr67;
+	case 102: goto tr67;
+	case 103: goto tr170;
+	case 104: goto tr67;
+	case 105: goto tr67;
+	case 106: goto tr67;
+	case 107: goto tr67;
+	case 108: goto tr67;
+	case 109: goto tr67;
+	case 110: goto tr67;
+	case 111: goto tr67;
+	case 112: goto tr67;
+	case 113: goto tr67;
+	case 114: goto tr67;
+	case 115: goto tr186;
+	case 116: goto tr67;
+	case 117: goto tr67;
+	case 118: goto tr67;
+	case 119: goto tr67;
+	case 120: goto tr192;
+	case 8: goto tr9;
+	case 9: goto tr9;
+	case 121: goto tr67;
+	case 122: goto tr67;
+	case 123: goto tr67;
+	case 124: goto tr67;
+	case 125: goto tr67;
+	case 126: goto tr67;
+	case 127: goto tr67;
+	case 128: goto tr67;
+	case 129: goto tr67;
+	case 130: goto tr67;
+	case 131: goto tr67;
+	case 132: goto tr67;
+	case 133: goto tr67;
+	case 134: goto tr67;
+	case 135: goto tr67;
+	case 136: goto tr67;
+	case 137: goto tr67;
+	case 138: goto tr67;
+	case 139: goto tr67;
+	case 140: goto tr67;
+	case 141: goto tr67;
+	case 142: goto tr67;
+	case 143: goto tr67;
+	}
+	}
+
+	_out: {}
+	}
+
+#line 641 "patchExprScanner.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, nullptr);
+    parser_->stop();
+
+    // Restore debug value
+    debug = oldDebug;
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/patch/patchExprScanner.rl b/src/finiteVolume/expressions/patch/patchExprScanner.rl
new file mode 100644
index 0000000000000000000000000000000000000000..e26c621c3f0573e4a8de5cc81d14aac718a48910
--- /dev/null
+++ b/src/finiteVolume/expressions/patch/patchExprScanner.rl
@@ -0,0 +1,664 @@
+/*--------------------------------*- 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 for patch expressions
+
+\*---------------------------------------------------------------------------*/
+
+#include "patchExprScanner.H"
+#include "patchExprDriver.H"
+#include "patchExprLemonParser.h"
+#include "patchExprParser.H"
+#include "Enum.H"
+#include "macros.H"
+
+#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+
+// Debugging to stderr
+#undef  DebugInfo
+#define DebugInfo if (debug) InfoErr
+
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+//- Paste token prefix
+#define TOKEN_OF(T)         TOK_##T
+
+//- An {int, c_str} enum pairing
+#define TOKEN_PAIR(Name,T)  { TOKEN_OF(T), Name }
+
+#undef HAS_LOOKBEHIND_TOKENS
+
+// Special handling of predefined method types. Eg, .x(), .y(), ...
+static const Enum<int> fieldMethodEnums
+({
+    TOKEN_PAIR("x", CMPT_X),
+    TOKEN_PAIR("y", CMPT_Y),
+    TOKEN_PAIR("z", CMPT_Z),
+    TOKEN_PAIR("xx", CMPT_XX),
+    TOKEN_PAIR("xy", CMPT_XY),
+    TOKEN_PAIR("xz", CMPT_XZ),
+    TOKEN_PAIR("yx", CMPT_YX),
+    TOKEN_PAIR("yy", CMPT_YY),
+    TOKEN_PAIR("yz", CMPT_YZ),
+    TOKEN_PAIR("zx", CMPT_ZX),
+    TOKEN_PAIR("zy", CMPT_ZY),
+    TOKEN_PAIR("zz", CMPT_ZZ),
+    TOKEN_PAIR("ii", CMPT_II),
+    TOKEN_PAIR("diag", DIAG),   /* tensors only */
+    TOKEN_PAIR("T", TRANSPOSE), /* tensors only */
+});
+
+// Known field-token types
+static const Enum<int> fieldTokenEnums
+({
+#ifdef TOK_SCALAR_ID
+    TOKEN_PAIR(volScalarField::typeName.c_str(), SCALAR_ID),
+    TOKEN_PAIR(volVectorField::typeName.c_str(), VECTOR_ID),
+    TOKEN_PAIR(volTensorField::typeName.c_str(), TENSOR_ID),
+    TOKEN_PAIR(volSymmTensorField::typeName.c_str(), SYM_TENSOR_ID),
+    TOKEN_PAIR(volSphericalTensorField::typeName.c_str(), SPH_TENSOR_ID),
+#else
+#error TOK_SCALAR_ID not defined
+#endif
+#ifdef TOK_SSCALAR_ID
+    TOKEN_PAIR(surfaceScalarField::typeName.c_str(), SSCALAR_ID),
+    TOKEN_PAIR(surfaceVectorField::typeName.c_str(), SVECTOR_ID),
+    TOKEN_PAIR(surfaceTensorField::typeName.c_str(), STENSOR_ID),
+    TOKEN_PAIR(surfaceSymmTensorField::typeName.c_str(), SSYM_TENSOR_ID),
+    TOKEN_PAIR(surfaceSphericalTensorField::typeName.c_str(), SSPH_TENSOR_ID),
+#else
+#error TOK_SSCALAR_ID not defined
+#endif
+#ifdef TOK_PSCALAR_ID
+    TOKEN_PAIR(pointScalarField::typeName.c_str(), PSCALAR_ID),
+    TOKEN_PAIR(pointVectorField::typeName.c_str(), PVECTOR_ID),
+    TOKEN_PAIR(pointTensorField::typeName.c_str(), PTENSOR_ID),
+    TOKEN_PAIR(pointSymmTensorField::typeName.c_str(), PSYM_TENSOR_ID),
+    TOKEN_PAIR(pointSphericalTensorField::typeName.c_str(), PSPH_TENSOR_ID),
+#else
+#warning TOK_PSCALAR_ID not defined
+#endif
+});
+
+
+// Simple compile-time function name declarations.
+// Useful for handling driver-specific dispatching, or functions that
+// are not universally available.
+static const Enum<int> funcTokenEnums
+({
+#ifdef TOK_FLOOR
+    TOKEN_PAIR("floor", FLOOR),
+    TOKEN_PAIR("ceil", CEIL),
+    TOKEN_PAIR("round", ROUND),
+#endif
+#ifdef TOK_HYPOT  /* Can use hypot? */
+    TOKEN_PAIR("hypot", HYPOT),
+#endif
+
+    // Already parsed as function: TOKEN_PAIR("pos", FACE_CENTRE),
+
+    TOKEN_PAIR("point", POINT_EXPR), // Point value
+    TOKEN_PAIR("face", FACE_EXPR),   // Face areaNormal
+
+    TOKEN_PAIR("faceToPoint", FACE_TO_POINT),
+    TOKEN_PAIR("pointToFace", POINT_TO_FACE),
+
+    TOKEN_PAIR("area", FACE_AREA),
+    TOKEN_PAIR("pts", POINTS),
+});
+
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Classifying token type based on an identifier name is indeed ugly.
+//
+// 1)
+//   Handle special cases (eg, cellSet,...) first that have been tagged
+//   as expected content with the stashed "look-behind" token.
+//   Handle not-found errors here directly.
+//
+// 2)
+//   Fallback to determining which field-type (volScalarField etc) the name
+//   corresponds to.
+//   Handle not-found errors by return -1.
+//
+static int driverTokenType
+(
+    const expressions::patchExpr::parseDriver& driver_,
+    const word& ident
+)
+{
+#if 0
+    // Get stashed "look-behind" to decide what type of identifier we expect
+    const int lookBehind = driver_.resetStashedTokenId();
+
+    if (lookBehind && lookBehindTokenEnums.found(lookBehind))
+    {
+        bool good = false;
+
+        switch (lookBehind)
+        {
+            case TOK_CSET : good = driver_.isCellSet(ident); break;
+            case TOK_FSET : good = driver_.isFaceSet(ident); break;
+            case TOK_PSET : good = driver_.isPointSet(ident); break;
+            case TOK_CZONE : good = driver_.isCellZone(ident); break;
+            case TOK_FZONE : good = driver_.isFaceZone(ident); break;
+            case TOK_PZONE : good = driver_.isPointZone(ident); break;
+        }
+
+        if (good)
+        {
+            return TOK_IDENTIFIER;
+        }
+
+        // Fatal
+        driver_.reportFatal
+        (
+            "Error no " + lookBehindTokenEnums.get(lookBehind) + ": " + ident
+        );
+
+        return -2;  // Extra safety
+    }
+#endif
+
+    // Face variables
+    #ifdef TOK_SSCALAR_ID
+    {
+        #undef checkFieldToken
+        #define checkFieldToken(TokType, Type)                                \
+        if (driver_.isVariable<Type>(ident, false))                           \
+        {                                                                     \
+            return TokType;                                                   \
+        }
+
+        checkFieldToken(TOK_SSCALAR_ID, scalar);
+        checkFieldToken(TOK_SVECTOR_ID, vector);
+        checkFieldToken(TOK_SSYM_TENSOR_ID, symmTensor);
+        checkFieldToken(TOK_SSPH_TENSOR_ID, sphericalTensor);
+        checkFieldToken(TOK_STENSOR_ID, tensor);
+
+        // Not tested: checkFieldToken(TOK_SBOOL_ID, bool);
+    }
+    #endif
+
+    // Point variables
+    #ifdef TOK_PSCALAR_ID
+    {
+        #undef checkFieldToken
+        #define checkFieldToken(TokType, Type)                                \
+        if (driver_.isVariable<Type>(ident, true))                            \
+        {                                                                     \
+            return TokType;                                                   \
+        }
+
+        checkFieldToken(TOK_PSCALAR_ID, scalar);
+        checkFieldToken(TOK_PVECTOR_ID, vector);
+        checkFieldToken(TOK_PTENSOR_ID, tensor);
+        checkFieldToken(TOK_PTENSOR_ID, tensor);
+        checkFieldToken(TOK_PSYM_TENSOR_ID, symmTensor);
+        checkFieldToken(TOK_PSPH_TENSOR_ID, sphericalTensor);
+
+        // Not tested: checkFieldToken(TOK_PBOOL_ID, bool);
+    }
+    #endif
+
+    #undef checkFieldToken
+
+    // Check registered fields and/or disk-files
+    {
+        const word fieldType(driver_.getFieldClassName(ident));
+
+        int tokType = fieldTokenEnums.get(fieldType, -1);
+
+        if (tokType > 0)
+        {
+            return tokType;
+        }
+    }
+
+    return -1;
+}
+
+} // End anonymous namespace
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+// 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
+
+#define EMIT_TOKEN(T)                                                         \
+    driver_.parsePosition() = (ts-buf);                                       \
+    DebugInfo<< STRINGIFY(T) << ": " << driver_.parsePosition() << nl;        \
+    parser_->parse(TOKEN_OF(T), nullptr);                                     \
+    driver_.parsePosition() = (p-buf);
+
+
+%%{
+    machine patchExpr;
+    write  data;
+
+    action emit_number {
+        driver_.parsePosition() = (ts-buf);
+
+        DebugInfo
+            << "Number:" << std::string(ts, te-ts).c_str()
+            << " at " << driver_.parsePosition() << nl;
+
+        if (readScalar(std::string(ts, te-ts), scanTok.svalue))
+        {
+            parser_->parse(TOKEN_OF(NUMBER), &scanTok);
+        }
+        else
+        {
+            driver_.reportFatal
+            (
+                "Error parsing number: " + std::string(ts, te-ts)
+            );
+        }
+
+        driver_.parsePosition() = (p-buf);
+    }
+
+    action emit_ident {
+        driver_.parsePosition() = (ts-buf);
+        dispatch_ident(driver_, scanTok, word(ts, te-ts, false));
+        driver_.parsePosition() = (p-buf);
+    }
+
+    action emit_method {
+        // Tokenized ".method" - dispatch '.' and "method" separately
+        driver_.parsePosition() = (ts-buf);
+        dispatch_method(driver_, scanTok, word(ts+1, te-ts-1, false));
+        driver_.parsePosition() = (p-buf);
+    }
+
+    decimal = ((digit* '.' digit+) | (digit+ '.'?)) ;
+    number  = ((digit+ | decimal) ([Ee][\-+]? digit+)?) ;
+    ident   = ((alpha|'_') . ((alnum|[._])**)) ;
+    dquoted = '"' [^\"]+ '"' ;
+    squoted = "'" [^\']+ "'" ;
+
+
+    ## 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(DOT); };
+    '/'  =>{ EMIT_TOKEN(DIVIDE); };
+    '?'  =>{ 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); };
+    '&'  =>{ EMIT_TOKEN(BIT_AND); };
+## Not needed?  '|'  =>{ EMIT_TOKEN(BIT_OK); };
+    '^'  =>{ EMIT_TOKEN(BIT_XOR); };
+
+    ## Some '.method' - Error if unknown
+    '.' alpha+ => emit_method;
+
+
+    ## 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); };
+    "sinh"      =>{ EMIT_TOKEN(SINH); };
+    "cosh"      =>{ EMIT_TOKEN(COSH); };
+    "tanh"      =>{ EMIT_TOKEN(TANH); };
+    "mag"       =>{ EMIT_TOKEN(MAG); };
+    "magSqr"    =>{ EMIT_TOKEN(MAGSQR); };
+
+    "pos"       =>{ EMIT_TOKEN(POS); };
+    "neg"       =>{ EMIT_TOKEN(NEG); };
+    "pos0"      =>{ EMIT_TOKEN(POS0); };
+    "neg0"      =>{ EMIT_TOKEN(NEG0); };
+    "sign"      =>{ EMIT_TOKEN(SIGN); };
+
+    ## Reductions, or other special functions
+    "min"       =>{ EMIT_TOKEN(MIN); };
+    "max"       =>{ EMIT_TOKEN(MAX); };
+    "average"   =>{ EMIT_TOKEN(AVERAGE); };
+    "sum"       =>{ EMIT_TOKEN(SUM); };
+    "weightAverage" =>{ EMIT_TOKEN(WEIGHT_AVERAGE); };
+    "weightSum" =>{ EMIT_TOKEN(WEIGHT_SUM); };
+    "rand"      =>{ EMIT_TOKEN(RAND); };
+
+    ## Types
+    "bool"      =>{ EMIT_TOKEN(BOOL); };
+    "vector"    =>{ EMIT_TOKEN(VECTOR); };
+    "tensor"    =>{ EMIT_TOKEN(TENSOR); };
+    "symmTensor" =>{ EMIT_TOKEN(SYM_TENSOR); };
+    "sphericalTensor" =>{ EMIT_TOKEN(SPH_TENSOR); };
+
+    ## Single value (constants, etc)
+    "Zero"      =>{ EMIT_TOKEN(ZERO); };
+    "true"      =>{ EMIT_TOKEN(LTRUE); };
+    "false"     =>{ EMIT_TOKEN(LFALSE); };
+    "tensor::I" =>{ EMIT_TOKEN(UNIT_TENSOR); };
+    "time"      =>{ EMIT_TOKEN(TIME); };
+
+    ## Identifier (field, etc - error if unknown)
+    ## Handle 'bare' names and single/double quoted ones
+    ident       => emit_ident;
+    dquoted     => emit_ident;
+    squoted     => emit_ident;
+
+    space*;
+    *|;
+}%%
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::expressions::patchExpr::scanner::~scanner()
+{
+    if (parser_)
+    {
+        delete parser_;
+    }
+}
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+bool Foam::expressions::patchExpr::scanner::dispatch_method
+(
+    const parseDriver& driver_,
+    scanToken& scanTok,
+    word&& ident
+) const
+{
+    if (ident[0] == '.')
+    {
+        ident.erase(0, 1);
+    }
+
+    DebugInfo
+        << "Method:" << ident
+        << " at " << driver_.parsePosition() << nl;
+
+    const int methType = fieldMethodEnums.get(ident, -1);
+
+    if (methType > 0)
+    {
+        // Dispatch '.' and "method" separately
+        parser_->parse(TOK_DOT, nullptr);
+        parser_->parse(methType, nullptr);
+
+        return true;
+    }
+
+    driver_.reportFatal("Unknown method: " + ident);
+    return false;
+}
+
+
+bool Foam::expressions::patchExpr::scanner::dispatch_ident
+(
+    const parseDriver& driver_,
+    scanToken& scanTok,
+    word&& ident
+) const
+{
+    int tokType = -1;
+
+    const bool quoted =
+    (
+        (ident.front() == '"' || ident.front() == '\'')
+     && (ident.front() == ident.back())
+    );
+
+    if (quoted)
+    {
+        ident.erase(ident.size()-1);
+        ident.erase(0, 1);
+    }
+    else
+    {
+        // Check for function name
+        tokType = funcTokenEnums.get(ident, -1);
+
+        if (tokType > 0)
+        {
+            DebugInfo
+                << "Emit:" << ident << " function:"
+                << parser_->nameOfToken(tokType) << nl;
+
+            parser_->parse(tokType, nullptr);
+            return true;
+        }
+
+        #ifdef HAS_LOOKBEHIND_TOKENS
+        // Specials such "cset" also reset the look-behind
+        tokType = lookBehindTokenEnums.get(ident, -1);
+
+        if (tokType > 0)
+        {
+            DebugInfo
+                << "Emit:" << ident << " as look-behind:"
+                << parser_->nameOfToken(tokType) << nl;
+
+            driver_.resetStashedTokenId(tokType);
+            parser_->parse(tokType, nullptr);
+            return true;
+        }
+        #endif
+    }
+
+
+    // Can also peek at stashed "look-behind"
+    // const int lookBehind = driver_.stashedTokenId();
+
+    tokType = driverTokenType(driver_, ident);
+
+    if (tokType > 0)
+    {
+        DebugInfo
+            << "Emit:" << ident << " token:"
+            << parser_->nameOfToken(tokType) << nl;
+
+        scanTok.name = new Foam::word(std::move(ident));
+        parser_->parse(tokType, &scanTok);
+
+        return true;
+    }
+
+
+    // Not found? Attempt to strip off '.x' endings etc,
+    // but not when quoted
+
+    const auto dot = ident.rfind('.');
+    const int methType =
+    (
+        quoted || dot == std::string::npos
+      ? -1
+      : fieldMethodEnums.get(ident.substr(dot+1), -1)
+    );
+
+    if
+    (
+        methType > 0
+     && (tokType = driverTokenType(driver_, ident.substr(0, dot))) > 0
+    )
+    {
+        DebugInfo
+            << "Emit:" << ident.substr(0, dot).c_str() << " token:"
+            << parser_->nameOfToken(tokType) << " with "
+            << ident.substr(dot).c_str() << " token:"
+            << parser_->nameOfToken(methType) << nl;
+
+        // The field (before the ".")
+        ident.erase(dot);
+
+        scanTok.name = new Foam::word(std::move(ident));
+        parser_->parse(tokType, &scanTok);
+
+        // Dispatch '.' and "method" separately
+        parser_->parse(TOK_DOT, nullptr);
+        parser_->parse(methType, nullptr);
+
+        return true;
+    }
+
+    driver_.reportFatal
+    (
+        "Object " + ident + " does not exist or wrong type"
+    );
+
+    return false;
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::expressions::patchExpr::scanner::process
+(
+    const std::string& str,
+    size_t strBeg,
+    size_t strLen,
+    parseDriver& driver_
+)
+{
+    // Save debug value
+    const int oldDebug = debug;
+
+    if (driver_.debugScanner())
+    {
+        debug |= 4;
+    }
+
+    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_);
+
+    // Scan token type
+    scanToken scanTok;
+
+    // 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, nullptr);
+    parser_->stop();
+
+    // Restore debug value
+    debug = oldDebug;
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/volume/createCode b/src/finiteVolume/expressions/volume/createCode
new file mode 100755
index 0000000000000000000000000000000000000000..30d5e5902c024d04cf5ce08c8f46309e3a963659
--- /dev/null
+++ b/src/finiteVolume/expressions/volume/createCode
@@ -0,0 +1,13 @@
+#!/bin/sh
+cd ${0%/*} || exit 1                        # Run from this directory
+# Manually create ragel scanner and lemon parser header
+
+prefix=volumeExpr
+
+"${WM_PROJECT_DIR:?}/wmake/scripts/makeParser" \
+    -prefix="$prefix" \
+    -scanner=Scanner.rl \
+    -parser=LemonParser.lyy-m4 \
+    "$@"
+
+#------------------------------------------------------------------------------
diff --git a/src/finiteVolume/expressions/volume/volumeExpr.C b/src/finiteVolume/expressions/volume/volumeExpr.C
new file mode 100644
index 0000000000000000000000000000000000000000..8859f6deb56a0fcaddebdc8883f99537cac04436
--- /dev/null
+++ b/src/finiteVolume/expressions/volume/volumeExpr.C
@@ -0,0 +1,44 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "volumeExprFwd.H"
+#include "defineDebugSwitch.H"
+
+// * * * * * * * * * * * * * * * * Globals * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace expressions
+{
+defineDebugSwitchWithName(volumeExpr, "volumeExpr", 0);
+registerDebugSwitchWithName(volumeExpr, volumeExpr, "volumeExpr");
+
+} // End namespace expressions
+} // End namespace Foam
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/volume/volumeExprDriver.C b/src/finiteVolume/expressions/volume/volumeExprDriver.C
new file mode 100644
index 0000000000000000000000000000000000000000..8cb2c5002ec94ddaf83bddbe29ba91274501fd2d
--- /dev/null
+++ b/src/finiteVolume/expressions/volume/volumeExprDriver.C
@@ -0,0 +1,226 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "volumeExprDriver.H"
+#include "volumeExprScanner.H"
+#include "error.H"
+#include "fvPatch.H"
+#include "fvMesh.H"
+#include "className.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace expressions
+{
+namespace volumeExpr
+{
+
+defineTypeNameAndDebug(parseDriver, 0);
+
+addNamedToRunTimeSelectionTable
+(
+    fvExprDriver,
+    parseDriver,
+    dictionary,
+    volume
+);
+
+addNamedToRunTimeSelectionTable
+(
+    fvExprDriver,
+    parseDriver,
+    idName,
+    volume
+);
+
+addNamedToRunTimeSelectionTable
+(
+    fvExprDriver,
+    parseDriver,
+    dictionary,
+    internalField
+);
+
+addNamedToRunTimeSelectionTable
+(
+    fvExprDriver,
+    parseDriver,
+    idName,
+    internalField
+);
+
+} // End namespace volumeExpr
+} // End namespace expressions
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    static label getPatchID(const fvMesh& mesh, const word& patchName)
+    {
+        const auto& bMesh = mesh.boundaryMesh();
+
+        const label patchId = bMesh.findPatchID(patchName);
+
+        if (patchId < 0)
+        {
+            FatalErrorInFunction
+                << "No patch " << patchName << " found in "
+                << flatOutput(bMesh.names()) << nl
+                << exit(FatalError);
+        }
+        return patchId;
+    }
+
+
+    static inline const polyPatch& findPolyPatch
+    (
+        const fvMesh& mesh,
+        const word& patchName
+    )
+    {
+        return mesh.boundaryMesh()[getPatchID(mesh, patchName)];
+    }
+
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::expressions::volumeExpr::parseDriver::parseDriver
+(
+    const fvMesh& mesh,
+    bool cacheReadFields
+)
+:
+    parsing::genericRagelLemonDriver(),
+    expressions::fvExprDriver(cacheReadFields),
+    mesh_(mesh),
+    resultType_(),
+    isLogical_(false),
+    fieldGeoType_(NO_DATA),
+    resultDimension_()
+{}
+
+
+Foam::expressions::volumeExpr::parseDriver::parseDriver
+(
+    const fvMesh& mesh,
+    const dictionary& dict
+)
+:
+    parsing::genericRagelLemonDriver(),
+    expressions::fvExprDriver(dict),
+    mesh_(mesh),
+    resultType_(),
+    isLogical_(false),
+    fieldGeoType_(NO_DATA),
+    resultDimension_()
+{}
+
+
+Foam::expressions::volumeExpr::parseDriver::parseDriver
+(
+    const fvMesh& mesh,
+    const parseDriver& driver
+)
+:
+    parsing::genericRagelLemonDriver(),
+    expressions::fvExprDriver(driver),
+    mesh_(mesh),
+    resultType_(),
+    isLogical_(false),
+    fieldGeoType_(NO_DATA),
+    resultDimension_()
+{}
+
+
+Foam::expressions::volumeExpr::parseDriver::parseDriver
+(
+    const word& meshName,
+    const fvMesh& mesh
+)
+:
+    parseDriver(mesh)
+{
+    //?? Info<< "Warn that meshName is ignored?" << nl;
+}
+
+
+Foam::expressions::volumeExpr::parseDriver::parseDriver
+(
+    const dictionary& dict,
+    const fvMesh& mesh
+)
+:
+    parsing::genericRagelLemonDriver(),
+    expressions::fvExprDriver(dict),
+    mesh_(mesh),
+    resultType_(),
+    isLogical_(false),
+    fieldGeoType_(NO_DATA),
+    resultDimension_()
+{}
+
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+bool Foam::expressions::volumeExpr::parseDriver::readDict
+(
+    const dictionary& dict
+)
+{
+    expressions::fvExprDriver::readDict(dict);
+    dict.readIfPresent("dimensions", resultDimension_);
+
+    return true;
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+unsigned Foam::expressions::volumeExpr::parseDriver::parse
+(
+    const std::string& expr,
+    size_t pos,
+    size_t len
+)
+{
+    scanner scan(this->debugScanner());
+
+    scan.process(expr, pos, len, *this);
+
+    return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/volume/volumeExprDriver.H b/src/finiteVolume/expressions/volume/volumeExprDriver.H
new file mode 100644
index 0000000000000000000000000000000000000000..0ddb6a2104e5d056ecd9f0c6e174d20f0954d026
--- /dev/null
+++ b/src/finiteVolume/expressions/volume/volumeExprDriver.H
@@ -0,0 +1,510 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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::expressions::volumeExpr::parseDriver
+
+Description
+    Driver for volume, surface, point field expressions
+
+    Additional Properties
+    \table
+        Property     | Description                          | Required | Default
+        dimensions   | Dimensions for the expression result | no  |
+    \endtable
+
+    In addition to the standard mathematical functions, operations and
+    logical and relational operations, the volume expression support the
+    following driver-specific functions:
+
+    Functions
+    \table
+        Function    | Description                      | Number of arguments |
+        vol         | The cell volumes                      | 0 |
+        pos         | The cell centres                      | 0 |
+        pts         | The cell points                       | 0 |
+        area        | The face area magnitudes              | 0 |
+        fpos        | The face centres                      | 0 |
+        weightAverage| Volume or area weighted average      | 1 |
+        weightSum   | Volume or area weighted sum           | 1 |
+        face        | The face areaNormal vectors           | 0 |
+        face        | A surface-field face value            | 1 |
+        point       | A point-field point value             | 1 |
+        cellToFace  | Interpolate cell values onto faces    | 1 |
+        cellToPoint | Interpolate cell values onto points   | 1 |
+        pointToCell | Interpolate point values onto cells   | 1 |
+        reconstruct | Reconstruct cell vector from surface scalar | 1 |
+        rand        | Random field                          | 0/1 |
+    \endtable
+
+    Selections
+    \table
+        Function| Description                           | Number of arguments |
+        cset    | Logical vol field corresponding to cellSet    | 1 |
+        fset    | Logical surf field corresponding to faceSet   | 1 |
+        pset    | Logical point field corresponding to pointSet | 1 |
+        czone   | Logical vol field corresponding to cellZone   | 1 |
+        fzone   | Logical surf field corresponding to faceZone  | 1 |
+        pzone   | Logical point field corresponding to pointZone| 1 |
+    \endtable
+
+SourceFiles
+    volumeExprDriver.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef expressions_volumeExprDriver_H
+#define expressions_volumeExprDriver_H
+
+#include "volumeExprFwd.H"
+#include "fvExprDriver.H"
+#include "volFields.H"
+#include "surfaceFields.H"
+#include "pointFields.H"
+#include "genericRagelLemonDriver.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace expressions
+{
+namespace volumeExpr
+{
+
+/*---------------------------------------------------------------------------*\
+                         Class parseDriver Declaration
+\*---------------------------------------------------------------------------*/
+
+class parseDriver
+:
+    public parsing::genericRagelLemonDriver,
+    public expressions::fvExprDriver
+{
+protected:
+
+    // Protected Data
+
+        //- The referenced mesh
+        const fvMesh& mesh_;
+
+        //- The results (volume, surface, point)
+        autoPtr<regIOobject> resultField_;
+
+        //- The result type-name.
+        //  Normally volScalarField, surfaceVectorField etc,
+        //  but Scalar is modified for logical as volScalarField etc
+        word resultType_;
+
+        //- A logical (bool-like) field (but actually a scalar)
+        bool isLogical_;
+
+        //- A volume/surface/point field
+        enum FieldAssociation fieldGeoType_;
+
+        //- The result dimensions
+        dimensionSet resultDimension_;
+
+
+    // Protected Member Functions
+
+        //- Deep-copy the internalField as a result.
+        //  Uses the isLogical() and isPointData() values to handle
+        //  additional bookkeeping.
+        //  For isLogical(), renames the resultType_ from '*Scalar*'
+        //  to '*Logical*' (eg, volLogicalField)
+        template<class Type>
+        void setInternalFieldResult(const Field<Type>& fld);
+
+        //- Cell selections (as logical)
+        tmp<volScalarField> field_cellSelection
+        (
+            const word& name,
+            enum topoSetSource::sourceType setType
+        ) const;
+
+        //- Face selections (as logical)
+        tmp<surfaceScalarField> field_faceSelection
+        (
+            const word& name,
+            enum topoSetSource::sourceType setType
+        ) const;
+
+        //- Point selections (as logical)
+        tmp<pointScalarField> field_pointSelection
+        (
+            const word& name,
+            enum topoSetSource::sourceType setType
+        ) const;
+
+
+        // No copy copy construct
+        parseDriver(const parseDriver&) = delete;
+
+        // No copy assignment
+        void operator=(const parseDriver&) = delete;
+
+
+public:
+
+    ClassName("volumeExpr::driver");
+
+    // Constructors
+
+        //- Construct for specified mesh
+        explicit parseDriver
+        (
+            const fvMesh& mesh,
+            bool cacheReadFields = false
+        );
+
+        //- Construct for specified mesh with given dictionary
+        parseDriver(const fvMesh& mesh, const dictionary& dict);
+
+        //- Construct for specified mesh with copy of driver context
+        parseDriver(const fvMesh& mesh, const parseDriver& driver);
+
+        //- Construct with meshName for the given mesh
+        parseDriver(const word& meshName, const fvMesh& mesh);
+
+        //- Construct with patchName and region specified in dictionary
+        parseDriver(const dictionary& dict, const fvMesh& mesh);
+
+        //- Clone
+        virtual autoPtr<expressions::fvExprDriver> clone() const
+        {
+            return autoPtr<expressions::fvExprDriver>
+            (
+                new parseDriver(this->mesh_, *this)
+            );
+        }
+
+
+    //- Destructor
+    virtual ~parseDriver() = default;
+
+
+    // Public Member Functions
+
+        //- The mesh we are attached to
+        virtual const fvMesh& mesh() const
+        {
+            return mesh_;
+        }
+
+        //- The underlying field size for the expression
+        virtual label size() const
+        {
+            return mesh_.nCells();
+        }
+
+        //- The underlying point field size for the expression
+        virtual label pointSize() const
+        {
+            return mesh_.nPoints();
+        }
+
+        //- Field size associated with different geometric field types
+        inline label size(const FieldAssociation geoType) const;
+
+
+    // Reading
+
+        //- Read variables, tables etc.
+        //  Adds support for "dimensions"
+        virtual bool readDict(const dictionary& dict);
+
+
+    // Evaluation
+
+        //- Perform parsing on (sub) string
+        using genericRagelLemonDriver::content;
+
+        //- Execute the parser
+        virtual unsigned parse
+        (
+            const std::string& expr,
+            size_t pos = 0,
+            size_t len = std::string::npos
+        );
+
+
+    // Field Information
+
+        //- The result type-name.
+        //  Normally volScalarField, surfaceVectorField etc,
+        //  but Scalar is modified for logical as volScalarField etc
+        const word& resultType() const
+        {
+            return resultType_;
+        }
+
+        //- The geometric field association
+        FieldAssociation fieldAssociation() const
+        {
+            return fieldGeoType_;
+        }
+
+        //- A logical (bool-like) field. Actually stored as a scalar.
+        bool isLogical() const
+        {
+            return isLogical_;
+        }
+
+        //- A volume field
+        bool isVolumeData() const
+        {
+            return fieldGeoType_ == FieldAssociation::VOLUME_DATA;
+        }
+
+        //- A surface field
+        bool isSurfaceData() const
+        {
+            return fieldGeoType_ == FieldAssociation::SURFACE_DATA;
+        }
+
+        //- A point field
+        bool isPointData() const
+        {
+            return fieldGeoType_ == FieldAssociation::POINT_DATA;
+        }
+
+        //- Test if stored result pointer is the specified type
+        template<class GeoField>
+        const GeoField* isResultType() const;
+
+        //- Test if stored result pointer is the specified type
+        //- and matches the specified logical type
+        template<class GeoField>
+        const GeoField* isResultType(bool logical, bool dieOnNull=false) const;
+
+
+    // Set Fields
+
+        //- Set result (vol field)
+        template<class Type>
+        void setResult
+        (
+            GeometricField<Type, fvPatchField, volMesh>* ptr,
+            bool logical = false
+        );
+
+        //- Set result (surface field)
+        template<class Type>
+        void setResult
+        (
+            GeometricField<Type, fvsPatchField, surfaceMesh>* ptr,
+            bool logical = false
+        );
+
+        //- Set result (point field)
+        template<class Type>
+        void setResult
+        (
+            GeometricField<Type, pointPatchField, pointMesh>* ptr,
+            bool logical = false
+        );
+
+
+    // New Fields
+
+        //- Return a new volume field with the mesh size
+        template<class Type>
+        tmp<GeometricField<Type, fvPatchField, volMesh>>
+        newVolField(const Type& val = pTraits<Type>::zero) const;
+
+        //- Return a new surface field with the mesh nInternalFaces size
+        template<class Type>
+        tmp<GeometricField<Type, fvsPatchField, surfaceMesh>>
+        newSurfaceField(const Type& val = pTraits<Type>::zero) const;
+
+        //- Return a new point field with the mesh nPoints size
+        template<class Type>
+        tmp<GeometricField<Type, pointPatchField, pointMesh>>
+        newPointField(const Type& val = pTraits<Type>::zero) const;
+
+
+        //- Retrieve field (vol field)
+        template<class Type>
+        tmp<GeometricField<Type, fvPatchField, volMesh>>
+        getVolField(const word& fldName, bool getOldTime=false);
+
+        //- Retrieve field (surface field)
+        template<class Type>
+        tmp<GeometricField<Type, fvsPatchField, surfaceMesh>>
+        getSurfaceField(const word& fldName, bool getOldTime=false);
+
+        //- Retrieve field (surface field)
+        template<class Type>
+        tmp<GeometricField<Type, pointPatchField, pointMesh>>
+        getPointField(const word& fldName, bool getOldTime=false);
+
+
+    // Field "shape" conversions
+
+        //- Interpolate cell to face values
+        template<class Type>
+        tmp<GeometricField<Type, fvsPatchField, surfaceMesh>>
+        cellToFace
+        (
+            const GeometricField<Type,fvPatchField,volMesh>& field
+        ) const;
+
+        //- Interpolate cell to point values
+        template<class Type>
+        tmp<GeometricField<Type, pointPatchField, pointMesh>>
+        cellToPoint
+        (
+            const GeometricField<Type, fvPatchField, volMesh>& field
+        ) const;
+
+        //- Interpolate point to cell values
+        template<class Type>
+        tmp<GeometricField<Type, fvPatchField, volMesh>>
+        pointToCell
+        (
+            const GeometricField<Type, pointPatchField, pointMesh>& field
+        ) const;
+
+
+    // Custom Field Functions
+
+        //- The volume-weighted average of a field
+        template<class Type>
+        Type volAverage
+        (
+            GeometricField<Type, fvPatchField, volMesh>& fld
+        ) const
+        {
+            return weightedAverage(fld.mesh().V(), fld.primitiveField());
+        }
+
+        //- The volume-weighted sum of a field
+        template<class Type>
+        Type volSum
+        (
+            GeometricField<Type, fvPatchField, volMesh>& fld
+        ) const
+        {
+            return weightedSum(fld.mesh().V(), fld.primitiveField());
+        }
+
+        //- The area-weighted average of a field
+        template<class Type>
+        Type areaAverage
+        (
+            GeometricField<Type, fvsPatchField, surfaceMesh>& fld
+        ) const
+        {
+            return weightedAverage
+            (
+                fld.mesh().magSf().primitiveField(),
+                fld.primitiveField()
+            );
+        }
+
+        //- The area-weighted sum of a field
+        template<class Type>
+        Type areaSum
+        (
+            GeometricField<Type, fvsPatchField, surfaceMesh>& fld
+        ) const
+        {
+            return weightedSum
+            (
+                fld.mesh().magSf().primitiveField(),
+                fld.primitiveField()
+            );
+        }
+
+
+        //- The cell volumes - (swak = vol)
+        tmp<volScalarField> field_cellVolume() const;
+
+        //- The cell centres - (swak = pos)
+        tmp<volVectorField> field_cellCentre() const;
+
+        //- The face area magnitudes [magSf] - (swak = area)
+        tmp<surfaceScalarField> field_faceArea() const;
+
+        //- The face centres - (swak = fpos)
+        tmp<surfaceVectorField> field_faceCentre() const;
+
+        //- The face areas with their vector direction [Sf] - (swak = face)
+        tmp<surfaceVectorField> field_areaNormal() const;
+
+        //- The mesh point locations - (swak = pts)
+        tmp<pointVectorField> field_pointField() const;
+
+
+        //- Cell selection (set)
+        inline tmp<volScalarField> field_cellSet(const word& name) const;
+
+        //- Cell selection (zone)
+        inline tmp<volScalarField> field_cellZone(const word& name) const;
+
+        //- Face selection (set)
+        inline tmp<surfaceScalarField> field_faceSet(const word& name) const;
+
+        //- Face selection (zone)
+        inline tmp<surfaceScalarField> field_faceZone(const word& name) const;
+
+        //- Point selection (set)
+        inline tmp<pointScalarField> field_pointSet(const word& name) const;
+
+        //- Point selection (zone)
+        inline tmp<pointScalarField> field_pointZone(const word& name) const;
+
+        //- A uniform random field
+        tmp<volScalarField> field_rand(label seed=0, bool gaussian=false) const;
+
+        //- A Gaussian random field
+        tmp<volScalarField> field_randGaussian(label seed=0) const
+        {
+            return field_rand(seed, true);
+        }
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace volumeExpr
+} // End namespace expressions
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#include "volumeExprDriverI.H"
+
+#ifdef NoRepository
+    #include "volumeExprDriverTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/volume/volumeExprDriverFields.C b/src/finiteVolume/expressions/volume/volumeExprDriverFields.C
new file mode 100644
index 0000000000000000000000000000000000000000..efc3457e091d0ea7c75bf12a0e492411251df803
--- /dev/null
+++ b/src/finiteVolume/expressions/volume/volumeExprDriverFields.C
@@ -0,0 +1,283 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "volumeExprDriver.H"
+#include "fvPatch.H"
+#include "error.H"
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::tmp<Foam::volScalarField>
+Foam::expressions::volumeExpr::parseDriver::field_cellSelection
+(
+    const word& name,
+    enum topoSetSource::sourceType setType
+) const
+{
+    auto tresult = volScalarField::New
+    (
+        "selected",
+        mesh(),
+        dimensionedScalar(Zero)
+    );
+
+    labelList selected;
+    switch (setType)
+    {
+        case topoSetSource::sourceType::CELLZONE_SOURCE:
+        case topoSetSource::sourceType::CELLSET_SOURCE:
+        {
+            selected = getTopoSetLabels(name, setType);
+            break;
+        }
+
+        default:
+        {
+            FatalErrorInFunction
+                << "Unexpected sourceType: " << int(setType) << nl
+                << exit(FatalError);
+            break;
+        }
+    }
+
+    auto& fld = tresult.ref().primitiveFieldRef();
+    UIndirectList<scalar>(fld, selected) = scalar(1);
+
+    return tresult;
+}
+
+
+Foam::tmp<Foam::surfaceScalarField>
+Foam::expressions::volumeExpr::parseDriver::field_faceSelection
+(
+    const word& name,
+    enum topoSetSource::sourceType setType
+) const
+{
+    auto tresult = surfaceScalarField::New
+    (
+        "selected",
+        mesh(),
+        dimensionedScalar(Zero)
+    );
+
+    labelList selected;
+    switch (setType)
+    {
+        case topoSetSource::sourceType::FACESET_SOURCE:
+        case topoSetSource::sourceType::FACEZONE_SOURCE:
+        {
+            selected = getTopoSetLabels(name, setType);
+            break;
+        }
+
+        default:
+        {
+            FatalErrorInFunction
+                << "Unexpected sourceType: " << int(setType) << nl
+                << exit(FatalError);
+            break;
+        }
+    }
+
+    const auto& bmesh = mesh().boundaryMesh();
+
+    auto& result = tresult.ref();
+    auto& fld = result.primitiveFieldRef();
+    auto& bfld = result.boundaryFieldRef();
+
+    label nErrors = 0;
+
+    for (const label facei : selected)
+    {
+        if (facei < mesh().nInternalFaces())
+        {
+            fld[facei] = scalar(1);
+        }
+        else
+        {
+            const label patchi = bmesh.whichPatch(facei);
+
+            if (patchi < 0)
+            {
+                ++nErrors;
+            }
+            else
+            {
+                bfld[patchi][facei-bmesh[patchi].start()] = scalar(1);
+            }
+        }
+    }
+
+    if (nErrors)
+    {
+        WarningInFunction
+            << "The faceSet/faceZone " << name << " contained "
+            << nErrors << " faces outside of the addressing range" << nl
+            << nl;
+    }
+
+
+    return tresult;
+}
+
+
+Foam::tmp<Foam::pointScalarField>
+Foam::expressions::volumeExpr::parseDriver::field_pointSelection
+(
+    const word& name,
+    enum topoSetSource::sourceType setType
+) const
+{
+    auto tresult = pointScalarField::New
+    (
+        "selected",
+        pointMesh::New(mesh()),
+        dimensionedScalar(Zero)
+    );
+
+    labelList selected;
+    switch (setType)
+    {
+        case topoSetSource::sourceType::POINTSET_SOURCE:
+        case topoSetSource::sourceType::POINTZONE_SOURCE:
+        {
+            selected = getTopoSetLabels(name, setType);
+            break;
+        }
+
+        default:
+        {
+            FatalErrorInFunction
+                << "Unexpected sourceType: " << int(setType) << nl
+                << exit(FatalError);
+            break;
+        }
+    }
+
+    auto& fld = tresult.ref().primitiveFieldRef();
+    UIndirectList<scalar>(fld, selected) = scalar(1);
+
+    return tresult;
+}
+
+
+
+Foam::tmp<Foam::volScalarField>
+Foam::expressions::volumeExpr::parseDriver::field_cellVolume() const
+{
+    return volScalarField::New
+    (
+        "vol",
+        mesh(),
+        dimVol,
+        mesh().V()
+    );
+}
+
+
+Foam::tmp<Foam::volVectorField>
+Foam::expressions::volumeExpr::parseDriver::field_cellCentre() const
+{
+    return tmp<volVectorField>::New(mesh().C());
+}
+
+
+Foam::tmp<Foam::surfaceScalarField>
+Foam::expressions::volumeExpr::parseDriver::field_faceArea() const
+{
+    return surfaceScalarField::New
+    (
+        "face",
+        mesh(),
+        dimless,
+        mesh().magSf()
+    );
+}
+
+
+Foam::tmp<Foam::surfaceVectorField>
+Foam::expressions::volumeExpr::parseDriver::field_faceCentre() const
+{
+    return surfaceVectorField::New
+    (
+        "fpos",
+        mesh(),
+        dimless,
+        mesh().Cf()
+    );
+}
+
+
+Foam::tmp<Foam::surfaceVectorField>
+Foam::expressions::volumeExpr::parseDriver::field_areaNormal() const
+{
+    return surfaceVectorField::New
+    (
+        "face",
+        mesh(),
+        dimless,
+        mesh().Sf()
+    );
+}
+
+
+Foam::tmp<Foam::pointVectorField>
+Foam::expressions::volumeExpr::parseDriver::field_pointField() const
+{
+    return pointVectorField::New
+    (
+        "pts",
+        pointMesh::New(mesh()),
+        dimless,
+        mesh().points()
+    );
+}
+
+
+Foam::tmp<Foam::volScalarField>
+Foam::expressions::volumeExpr::parseDriver::field_rand
+(
+    label seed,
+    bool gaussian
+) const
+{
+    auto tresult = volScalarField::New
+    (
+        "rand",
+        mesh(),
+        dimless
+    );
+    auto& fld = tresult.ref().primitiveFieldRef();
+
+    fill_random(fld, seed, gaussian);
+
+    return tresult;
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/volume/volumeExprDriverI.H b/src/finiteVolume/expressions/volume/volumeExprDriverI.H
new file mode 100644
index 0000000000000000000000000000000000000000..2863f0ba1e8b900bb5dc0c425a1ace255932231a
--- /dev/null
+++ b/src/finiteVolume/expressions/volume/volumeExprDriverI.H
@@ -0,0 +1,137 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+inline Foam::label Foam::expressions::volumeExpr::parseDriver::size
+(
+    const FieldAssociation geoType
+) const
+{
+    switch (geoType)
+    {
+        case FieldAssociation::POINT_DATA :
+            return mesh_.nPoints();
+            break;
+        case FieldAssociation::SURFACE_DATA :
+            return mesh_.nInternalFaces();
+            break;
+        case FieldAssociation::VOLUME_DATA :
+            return mesh_.nCells();
+            break;
+        default:
+            break;
+    }
+    return 0;
+}
+
+
+inline Foam::tmp<Foam::volScalarField>
+Foam::expressions::volumeExpr::parseDriver::parseDriver::field_cellSet
+(
+    const word& name
+) const
+{
+    return field_cellSelection
+    (
+        name,
+        topoSetSource::sourceType::CELLSET_SOURCE
+    );
+}
+
+
+inline Foam::tmp<Foam::volScalarField>
+Foam::expressions::volumeExpr::parseDriver::field_cellZone
+(
+    const word& name
+) const
+{
+    return field_cellSelection
+    (
+        name,
+        topoSetSource::sourceType::CELLZONE_SOURCE
+    );
+}
+
+
+inline Foam::tmp<Foam::surfaceScalarField>
+Foam::expressions::volumeExpr::parseDriver::field_faceSet
+(
+    const word& name
+) const
+{
+    return field_faceSelection
+    (
+        name,
+        topoSetSource::sourceType::FACESET_SOURCE
+    );
+}
+
+
+inline Foam::tmp<Foam::surfaceScalarField>
+Foam::expressions::volumeExpr::parseDriver::field_faceZone
+(
+    const word& name
+) const
+{
+    return field_faceSelection
+    (
+        name,
+        topoSetSource::sourceType::FACEZONE_SOURCE
+    );
+}
+
+
+inline Foam::tmp<Foam::pointScalarField>
+Foam::expressions::volumeExpr::parseDriver::field_pointSet
+(
+    const word& name
+) const
+{
+    return field_pointSelection
+    (
+        name,
+        topoSetSource::sourceType::POINTSET_SOURCE
+    );
+}
+
+
+inline Foam::tmp<Foam::pointScalarField>
+Foam::expressions::volumeExpr::parseDriver::field_pointZone
+(
+    const word& name
+) const
+{
+    return field_pointSelection
+    (
+        name,
+        topoSetSource::sourceType::POINTZONE_SOURCE
+    );
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/volume/volumeExprDriverTemplates.C b/src/finiteVolume/expressions/volume/volumeExprDriverTemplates.C
new file mode 100644
index 0000000000000000000000000000000000000000..3ffdb5b0b155a5fe24fe1e4de5fe8e53f7b211de
--- /dev/null
+++ b/src/finiteVolume/expressions/volume/volumeExprDriverTemplates.C
@@ -0,0 +1,331 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "exprOps.H"
+#include "FieldOps.H"
+#include "surfaceInterpolate.H"
+#include "volPointInterpolation.H"
+#include "interpolatePointToCell.H"
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+template<class Type>
+void Foam::expressions::volumeExpr::parseDriver::setInternalFieldResult
+(
+    const Field<Type>& fld
+)
+{
+    if (isLogical_)
+    {
+        // Eg, volScalarField -> volLogicalField
+        resultType_.replace("Scalar", "Logical");
+
+        Field<bool> bools(fld.size());
+        FieldOps::assign(bools, fld, expressions::boolOp<Type>());
+
+        this->result().setResult(std::move(bools), this->isPointData());
+    }
+    else
+    {
+        // Deep copy
+        this->result().setResult(fld, this->isPointData());
+    }
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+template<class Type>
+void Foam::expressions::volumeExpr::parseDriver::setResult
+(
+    GeometricField<Type, fvPatchField, volMesh>* ptr,
+    bool logical
+)
+{
+    typedef GeometricField<Type, fvPatchField, volMesh> fieldType;
+
+    resultField_.clear();
+
+    // Characteristics
+    resultType_ = pTraits<fieldType>::typeName;
+    isLogical_ = logical;
+    fieldGeoType_ = VOLUME_DATA;
+
+    // Always strip out dimensions?
+    if (!resultDimension_.dimensionless())
+    {
+        ptr->dimensions().reset(resultDimension_);
+    }
+
+    setInternalFieldResult(ptr->primitiveField());
+
+    // Take ownership
+    resultField_.reset(ptr);
+}
+
+
+template<class Type>
+void Foam::expressions::volumeExpr::parseDriver::setResult
+(
+    GeometricField<Type, fvsPatchField, surfaceMesh>* ptr,
+    bool logical
+)
+{
+    typedef GeometricField<Type, fvsPatchField, surfaceMesh> fieldType;
+
+    resultField_.clear();
+
+    // Characteristics
+    resultType_ = pTraits<fieldType>::typeName;
+    isLogical_ = logical;
+    fieldGeoType_ = SURFACE_DATA;
+
+    // Always strip out dimensions?
+    if (!resultDimension_.dimensionless())
+    {
+        ptr->dimensions().reset(resultDimension_);
+    }
+
+    setInternalFieldResult(ptr->primitiveField());
+
+    // Take ownership
+    resultField_.reset(ptr);
+}
+
+
+template<class Type>
+void Foam::expressions::volumeExpr::parseDriver::setResult
+(
+    GeometricField<Type, pointPatchField, pointMesh>* ptr,
+    bool logical
+)
+{
+    typedef GeometricField<Type, pointPatchField, pointMesh> fieldType;
+
+    resultField_.clear();
+
+    // Characteristics
+    resultType_ = pTraits<fieldType>::typeName;
+    isLogical_ = logical;
+    fieldGeoType_ = POINT_DATA;
+
+    // Always strip out dimensions?
+    if (!resultDimension_.dimensionless())
+    {
+        ptr->dimensions().reset(resultDimension_);
+    }
+
+    setInternalFieldResult(ptr->primitiveField());
+
+    // Take ownership
+    resultField_.reset(ptr);
+}
+
+
+template<class GeomField>
+const GeomField*
+Foam::expressions::volumeExpr::parseDriver::isResultType() const
+{
+    return dynamic_cast<const GeomField*>(resultField_.get());
+}
+
+
+template<class GeomField>
+const GeomField*
+Foam::expressions::volumeExpr::parseDriver::isResultType
+(
+    bool logical,
+    bool dieOnNull
+) const
+{
+    const regIOobject* ptr = resultField_.get();
+
+    if (dieOnNull && ptr != nullptr)
+    {
+        FatalErrorInFunction
+            << "No result available. Requested "
+            << pTraits<GeomField>::typeName << nl
+            << exit(FatalError);
+    }
+
+    if (isLogical_ == logical)
+    {
+        return dynamic_cast<const GeomField*>(ptr);
+    }
+
+    return nullptr;
+}
+
+
+template<class Type>
+Foam::tmp<Foam::GeometricField<Type, Foam::fvPatchField, Foam::volMesh>>
+Foam::expressions::volumeExpr::parseDriver::getVolField
+(
+    const word& fldName,
+    bool getOldTime
+)
+{
+    typedef GeometricField<Type, fvPatchField, volMesh> fieldType;
+
+    return this->getOrReadField<fieldType>
+    (
+        fldName,
+        true,  // mandatory
+        getOldTime
+    );
+}
+
+
+template<class Type>
+Foam::tmp<Foam::GeometricField<Type, Foam::fvsPatchField, Foam::surfaceMesh>>
+Foam::expressions::volumeExpr::parseDriver::getSurfaceField
+(
+    const word& fldName,
+    bool getOldTime
+)
+{
+    typedef GeometricField<Type, fvsPatchField, surfaceMesh> fieldType;
+
+    return this->getOrReadField<fieldType>
+    (
+        fldName,
+        true,  // mandatory
+        getOldTime
+    );
+}
+
+
+template<class Type>
+Foam::tmp<Foam::GeometricField<Type, Foam::pointPatchField, Foam::pointMesh>>
+Foam::expressions::volumeExpr::parseDriver::getPointField
+(
+    const word& fldName,
+    bool getOldTime
+)
+{
+    typedef GeometricField<Type, pointPatchField, pointMesh> fieldType;
+
+    return this->getOrReadPointField<fieldType>
+    (
+        fldName,
+        true,  // mandatory
+        getOldTime
+    );
+}
+
+
+template<class Type>
+Foam::tmp<Foam::GeometricField<Type, Foam::fvPatchField, Foam::volMesh>>
+Foam::expressions::volumeExpr::parseDriver::newVolField
+(
+    const Type& val
+) const
+{
+    return GeometricField<Type,fvPatchField,volMesh>::New
+    (
+        word("constant.") + word(pTraits<Type>::typeName),
+        mesh(),
+        dimensioned<Type>("", dimless, val)
+    );
+}
+
+
+template<class Type>
+Foam::tmp<Foam::GeometricField<Type, Foam::fvsPatchField, Foam::surfaceMesh>>
+Foam::expressions::volumeExpr::parseDriver::newSurfaceField
+(
+    const Type& val
+) const
+{
+    return GeometricField<Type,fvsPatchField,surfaceMesh>::New
+    (
+        word("constant.") + word(pTraits<Type>::typeName),
+        mesh(),
+        dimensioned<Type>("", dimless, val)
+    );
+}
+
+
+template<class Type>
+Foam::tmp<Foam::GeometricField<Type, Foam::pointPatchField, Foam::pointMesh>>
+Foam::expressions::volumeExpr::parseDriver::newPointField
+(
+    const Type& val
+) const
+{
+    return GeometricField<Type,pointPatchField,pointMesh>::New
+    (
+        word("constant.") + word(pTraits<Type>::typeName),
+        pointMesh::New(mesh()),
+        dimensioned<Type>("", dimless, val)
+    );
+}
+
+
+template<class Type>
+Foam::tmp<Foam::GeometricField<Type, Foam::fvsPatchField, Foam::surfaceMesh>>
+Foam::expressions::volumeExpr::parseDriver::cellToFace
+(
+    const GeometricField<Type, fvPatchField, volMesh>& field
+) const
+{
+    return fvc::interpolate(field);
+}
+
+
+template<class Type>
+Foam::tmp<Foam::GeometricField<Type, Foam::pointPatchField, Foam::pointMesh>>
+Foam::expressions::volumeExpr::parseDriver::cellToPoint
+(
+    const GeometricField<Type, fvPatchField, volMesh>& field
+) const
+{
+    volPointInterpolation interp(this->mesh());
+    return interp.interpolate(field);
+}
+
+
+template<class Type>
+Foam::tmp<Foam::GeometricField<Type,Foam::fvPatchField,Foam::volMesh>>
+Foam::expressions::volumeExpr::parseDriver::pointToCell
+(
+    const GeometricField<Type, pointPatchField, pointMesh>& field
+) const
+{
+    auto tresult = newVolField<Type>();
+    auto& result = tresult.ref();
+
+    forAll(result,celli)
+    {
+        result[celli] = interpolatePointToCell(field, celli);
+    }
+
+    return tresult;
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/volume/volumeExprFwd.H b/src/finiteVolume/expressions/volume/volumeExprFwd.H
new file mode 100644
index 0000000000000000000000000000000000000000..1a5df406c4bb7c02c2634980a815796d8cf581a4
--- /dev/null
+++ b/src/finiteVolume/expressions/volume/volumeExprFwd.H
@@ -0,0 +1,84 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+Namespace
+    Foam::expressions::volumeExpr
+
+Description
+    Namespace for volume field expressions parsing and evaluation
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef expressions_volumeExprFwd_H
+#define expressions_volumeExprFwd_H
+
+namespace Foam
+{
+namespace expressions
+{
+namespace volumeExpr
+{
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// Forward Declarations
+class parser;
+class scanner;
+class parseDriver;
+union scanToken;
+
+//- Static debugging option
+extern int debug;
+
+//- The field association for volume expressions (mutually exclusive)
+enum FieldAssociation : unsigned char
+{
+    NO_DATA = 0,         //!< No data
+    POINT_DATA = 1,      //!< Point data
+    SURFACE_DATA = 2,    //!< Surface data
+    VOLUME_DATA = 3      //!< Volume data
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace volumeExpr
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+//- Typedef for volumeExpr parseDriver
+typedef volumeExpr::parseDriver volumeExprDriver;
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace expressions
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/volume/volumeExprLemonParser.h b/src/finiteVolume/expressions/volume/volumeExprLemonParser.h
new file mode 100644
index 0000000000000000000000000000000000000000..2cca6cd8082bb5e0c70b23a4967b85c74db2dd1c
--- /dev/null
+++ b/src/finiteVolume/expressions/volume/volumeExprLemonParser.h
@@ -0,0 +1,119 @@
+#define TOK_QUESTION                         1
+#define TOK_COLON                            2
+#define TOK_LOR                              3
+#define TOK_LAND                             4
+#define TOK_BIT_XOR                          5
+#define TOK_BIT_AND                          6
+#define TOK_EQUAL                            7
+#define TOK_NOT_EQUAL                        8
+#define TOK_LESS_EQ                          9
+#define TOK_GREATER_EQ                      10
+#define TOK_LESS                            11
+#define TOK_GREATER                         12
+#define TOK_PLUS                            13
+#define TOK_MINUS                           14
+#define TOK_TIMES                           15
+#define TOK_DIVIDE                          16
+#define TOK_PERCENT                         17
+#define TOK_NEGATE                          18
+#define TOK_NOT                             19
+#define TOK_DOT                             20
+#define TOK_NUMBER                          21
+#define TOK_ZERO                            22
+#define TOK_PI                              23
+#define TOK_LPAREN                          24
+#define TOK_RPAREN                          25
+#define TOK_DEG_TO_RAD                      26
+#define TOK_RAD_TO_DEG                      27
+#define TOK_TIME                            28
+#define TOK_SCALAR_ID                       29
+#define TOK_MIN                             30
+#define TOK_COMMA                           31
+#define TOK_MAX                             32
+#define TOK_SUM                             33
+#define TOK_AVERAGE                         34
+#define TOK_EXP                             35
+#define TOK_LOG                             36
+#define TOK_LOG10                           37
+#define TOK_SQR                             38
+#define TOK_SQRT                            39
+#define TOK_CBRT                            40
+#define TOK_SIN                             41
+#define TOK_COS                             42
+#define TOK_TAN                             43
+#define TOK_ASIN                            44
+#define TOK_ACOS                            45
+#define TOK_ATAN                            46
+#define TOK_SINH                            47
+#define TOK_COSH                            48
+#define TOK_TANH                            49
+#define TOK_POW                             50
+#define TOK_ATAN2                           51
+#define TOK_POS                             52
+#define TOK_NEG                             53
+#define TOK_POS0                            54
+#define TOK_NEG0                            55
+#define TOK_SIGN                            56
+#define TOK_FLOOR                           57
+#define TOK_CEIL                            58
+#define TOK_ROUND                           59
+#define TOK_HYPOT                           60
+#define TOK_RAND                            61
+#define TOK_VECTOR_ID                       62
+#define TOK_SPH_TENSOR_ID                   63
+#define TOK_SYM_TENSOR_ID                   64
+#define TOK_UNIT_TENSOR                     65
+#define TOK_TENSOR_ID                       66
+#define TOK_LTRUE                           67
+#define TOK_LFALSE                          68
+#define TOK_BOOL                            69
+#define TOK_CSET                            70
+#define TOK_IDENTIFIER                      71
+#define TOK_CZONE                           72
+#define TOK_CELL_VOLUME                     73
+#define TOK_WEIGHT_AVERAGE                  74
+#define TOK_WEIGHT_SUM                      75
+#define TOK_FACE_EXPR                       76
+#define TOK_SSCALAR_ID                      77
+#define TOK_SVECTOR_ID                      78
+#define TOK_SSPH_TENSOR_ID                  79
+#define TOK_SSYM_TENSOR_ID                  80
+#define TOK_STENSOR_ID                      81
+#define TOK_FSET                            82
+#define TOK_FZONE                           83
+#define TOK_FACE_AREA                       84
+#define TOK_FACE_CENTRE                     85
+#define TOK_POINT_EXPR                      86
+#define TOK_PSCALAR_ID                      87
+#define TOK_PVECTOR_ID                      88
+#define TOK_PSPH_TENSOR_ID                  89
+#define TOK_PSYM_TENSOR_ID                  90
+#define TOK_PTENSOR_ID                      91
+#define TOK_PSET                            92
+#define TOK_PZONE                           93
+#define TOK_POINTS                          94
+#define TOK_MAG                             95
+#define TOK_MAGSQR                          96
+#define TOK_VECTOR                          97
+#define TOK_TENSOR                          98
+#define TOK_SYM_TENSOR                      99
+#define TOK_SPH_TENSOR                     100
+#define TOK_CMPT_X                         101
+#define TOK_CMPT_Y                         102
+#define TOK_CMPT_Z                         103
+#define TOK_CMPT_XX                        104
+#define TOK_CMPT_XY                        105
+#define TOK_CMPT_XZ                        106
+#define TOK_CMPT_YX                        107
+#define TOK_CMPT_YY                        108
+#define TOK_CMPT_YZ                        109
+#define TOK_CMPT_ZX                        110
+#define TOK_CMPT_ZY                        111
+#define TOK_CMPT_ZZ                        112
+#define TOK_CMPT_II                        113
+#define TOK_TRANSPOSE                      114
+#define TOK_DIAG                           115
+#define TOK_POINT_TO_CELL                  116
+#define TOK_RECONSTRUCT                    117
+#define TOK_CELL_TO_FACE                   118
+#define TOK_CELL_TO_POINT                  119
diff --git a/src/finiteVolume/expressions/volume/volumeExprLemonParser.lyy-m4 b/src/finiteVolume/expressions/volume/volumeExprLemonParser.lyy-m4
new file mode 100644
index 0000000000000000000000000000000000000000..cc7ae238b5d990c150b05318efeaf3cd08a4d670
--- /dev/null
+++ b/src/finiteVolume/expressions/volume/volumeExprLemonParser.lyy-m4
@@ -0,0 +1,826 @@
+%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 for volume expressions.
+
+    https://www.sqlite.org/src/doc/trunk/doc/lemon.html
+
+    See detailed notes in the field expression parser.
+
+\*---------------------------------------------------------------------------*/
+} // %include
+
+/*
+ * include[volumeExprLemonParserMacros.m4]
+ *include(`volumeExprLemonParserMacros.m4')dnl
+ !done in a comment since many editors have issues matching m4 quotes!
+ */
+%include
+{
+#include "volumeExprDriver.H"
+#include "volumeExprParser.H"
+#include "volumeExprScanner.H"
+#include "unitConversion.H"
+#include "error.H"
+#include "volFields.H"
+#include "surfaceFields.H"
+#include "pointFields.H"
+#include "exprDriverOps.H"
+#include "GeometricFieldOps.H"
+#include "fvcReconstruct.H"
+
+
+// Enable ParseTrace
+#undef NDEBUG
+
+compiler_pragmas()
+
+// Local Functions
+
+tmp_management()
+
+dnl
+dnl #if 0
+dnl namespace Foam
+dnl {
+dnl //- Check field sizes
+dnl template<class T1, class T2>
+dnl static bool checkSizes(const char* what, T1* arg1, T2* arg2)
+dnl {
+dnl     if (arg1 == nullptr || arg2 == nullptr)
+dnl     {
+dnl         FatalError
+dnl             << "Null pointer in " << what << nl
+dnl             << Foam::exit(Foam::FatalError);
+dnl     }
+dnl     if (arg1->size() != arg2->size())
+dnl     {
+dnl         FatalError
+dnl             << "Size mismatch in " << what << ' '
+dnl             << pTraits<typename T1::value_type>::typeName << ", "
+dnl             << pTraits<typename T2::value_type>::typeName << "\n("
+dnl             << arg1->size() << " != " << arg2->size() << ')' << nl
+dnl             << Foam::exit(Foam::FatalError);
+dnl     }
+dnl
+dnl     return true;
+dnl }
+dnl } // End namespace Foam
+dnl #endif
+dnl
+} // %include
+
+// ------------------------------------------------------------------------- //
+
+%namespace      {}
+
+// Use extra argument for the return value
+%extra_context  { Foam::expressions::volumeExpr::parseDriver* driver }
+%parse_failure  { driver->reportFatal("Parse failure, giving up..."); }
+%syntax_error   { driver->reportFatal("Syntax error"); }
+
+%token_prefix TOK_
+
+// Terminals
+%token_type    {Foam::expressions::volumeExpr::scanToken*}
+// Non-terminals
+%type ivalue   { Foam::label }
+%type svalue   { Foam::scalar }
+%type ident    { Foam::word* }
+
+// Volume fields
+declare_field(lfield, Foam::volScalarField, Foam::scalar, newVolField, getVolField)
+declare_field(sfield, Foam::volScalarField, Foam::scalar, newVolField, getVolField)
+declare_field(vfield, Foam::volVectorField, Foam::vector, newVolField, getVolField)
+declare_field(hfield, Foam::volSphericalTensorField, Foam::sphericalTensor, newVolField, getVolField)
+declare_field(yfield, Foam::volSymmTensorField, Foam::symmTensor, newVolField, getVolField)
+declare_field(tfield, Foam::volTensorField, Foam::tensor, newVolField, getVolField)
+
+// Surface fields
+declare_field(slfield, Foam::surfaceScalarField, Foam::scalar, newSurfaceField, getSurfaceField)
+declare_field(ssfield, Foam::surfaceScalarField, Foam::scalar, newSurfaceField, getSurfaceField)
+declare_field(svfield, Foam::surfaceVectorField, Foam::vector, newSurfaceField, getSurfaceField)
+declare_field(shfield, Foam::surfaceSphericalTensorField, Foam::sphericalTensor, newSurfaceField, getSurfaceField)
+declare_field(syfield, Foam::surfaceSymmTensorField, Foam::symmTensor, newSurfaceField, getSurfaceField)
+declare_field(stfield, Foam::surfaceTensorField, Foam::tensor, newSurfaceField, getSurfaceField)
+
+// Point fields
+declare_field(plfield, Foam::pointScalarField, Foam::scalar, newPointField, getPointField)
+declare_field(psfield, Foam::pointScalarField, Foam::scalar, newPointField, getPointField)
+declare_field(pvfield, Foam::pointVectorField, Foam::vector, newPointField, getPointField)
+declare_field(phfield, Foam::pointSphericalTensorField, Foam::sphericalTensor, newPointField, getPointField)
+declare_field(pyfield, Foam::pointSymmTensorField, Foam::symmTensor, newPointField, getPointField)
+declare_field(ptfield, Foam::pointTensorField, Foam::tensor, newPointField, getPointField)
+
+// For each rule action with code, destruction must be done by that code block
+// Lemon does not generate a destructor for that.
+// So do not use Lemon destructors for anything.
+
+operator_precedence()
+
+%start_symbol evaluate
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+/*---------------------------------------------------------------------------*\
+ * Productions (scalar)
+\*---------------------------------------------------------------------------*/
+
+svalue (lhs) ::= NUMBER (a) .       { lhs = (a)->svalue; }  // From scanToken
+svalue (lhs) ::= ZERO .             { lhs = Foam::Zero; }
+svalue (lhs) ::= PI LPAREN RPAREN . { lhs = Foam::constant::mathematical::pi; }
+svalue (lhs) ::= DEG_TO_RAD LPAREN RPAREN . { lhs = Foam::degToRad(); }
+svalue (lhs) ::= RAD_TO_DEG LPAREN RPAREN . { lhs = Foam::radToDeg(); }
+svalue (lhs) ::= TIME LPAREN RPAREN . { lhs = driver->timeValue(); }
+
+
+/* * * * * * * * * * * * * * * * Volume Fields * * * * * * * * * * * * * * * *\
+dnl
+define([_logic_],       [lfield])dnl
+define([_scalar_],      [sfield])dnl
+define([_vector_],      [vfield])dnl
+define([_sphTensor_],   [hfield])dnl
+define([_symTensor_],   [yfield])dnl
+define([_tensor_],      [tfield])dnl
+dnl
+\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*---------------------------------------------------------------------------*\
+ * Productions (volScalarField)
+dnl
+define([_target_],      [sfield])dnl
+define([_value_type_],  [Foam::scalar])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_field_from_value(_target_, svalue)
+rule_get_field(_target_, SCALAR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_scalar_operations()
+rules_scalar_functions()
+
+// Non-standard but manage via FieldOps::assign
+rule_unary_assign(_target_, _target_, FLOOR, Foam::floorOp<Foam::scalar>())
+rule_unary_assign(_target_, _target_, CEIL, Foam::ceilOp<Foam::scalar>())
+rule_unary_assign(_target_, _target_, ROUND, Foam::roundOp<Foam::scalar>())
+
+// Non-standard but manage via FieldOps::assign
+rule_binary_assign(_target_, _target_, _target_, HYPOT, Foam::hypotOp<Foam::scalar>())
+
+
+// Other functions
+
+_target_ (lhs) ::= RAND LPAREN RPAREN .
+{
+    lhs = driver->field_rand().ptr();
+}
+
+_target_ (lhs) ::= RAND LPAREN NUMBER (seed) RPAREN .
+{
+    // Call with -ve seed to signal use of time index as seed
+    lhs = driver->field_rand(std::round(-(seed)->svalue)).ptr();
+}
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (volVectorField)
+dnl
+define([_target_],      [vfield])dnl
+define([_value_type_],  [Foam::vector])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_get_field(_target_, VECTOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_vector_operations()
+rules_vector_functions()
+
+/*---------------------------------------------------------------------------*\
+ * Productions (volSphericalTensorField)
+dnl
+define([_target_],      [hfield])dnl
+define([_value_type_],  [Foam::sphericalTensor])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_get_field(_target_, SPH_TENSOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_sphTensor_operations()
+rules_sphTensor_functions()
+
+/*---------------------------------------------------------------------------*\
+ * Productions (volSymmTensorField)
+dnl
+define([_target_],      [yfield])dnl
+define([_value_type_],  [Foam::symmTensor])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_get_field(_target_, SYM_TENSOR_ID)
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_symTensor_operations()
+rules_symTensor_functions()
+
+/*---------------------------------------------------------------------------*\
+ * Productions (volTensorField)
+dnl
+define([_target_],      [tfield])dnl
+define([_value_type_],  [Foam::tensor])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+tfield (lhs) ::= UNIT_TENSOR .  { lhs = _new_tfield(Foam::tensor::I); }
+
+rule_get_field(_target_, TENSOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_tensor_operations()
+rules_tensor_functions()
+
+/*---------------------------------------------------------------------------*\
+ * Logic field productions (volScalarField)
+dnl
+define([_target_],      [lfield])dnl
+define([_value_type_],  [Foam::scalar])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a, true); /* Logical */ }
+
+_logic_ (lhs) ::= LTRUE .   { lhs = _new_lfield(_logic_true_); }
+_logic_ (lhs) ::= LFALSE .  { lhs = _new_lfield(_logic_false_); }
+
+rule_cast_logical(_target_, _target_)
+rule_cast_logical(_target_, _scalar_, Foam::scalar)
+
+rules_logical_operations(_logic_, _value_type_)
+
+
+/*---------------------------------------------------------------------------*\
+ * General Volume-related productions
+\*---------------------------------------------------------------------------*/
+
+rules_driver_volume_functions()
+
+
+/* * * * * * * * * * * * * * * * Surface Fields  * * * * * * * * * * * * * * *\
+dnl
+define([_logic_],       [slfield])dnl
+define([_scalar_],      [ssfield])dnl
+define([_vector_],      [svfield])dnl
+define([_sphTensor_],   [shfield])dnl
+define([_symTensor_],   [syfield])dnl
+define([_tensor_],      [stfield])dnl
+dnl
+\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+dnl> %ifndef disable_surface_fields
+
+/*---------------------------------------------------------------------------*\
+ * Productions (surfaceScalarField)
+dnl
+define([_target_],      [ssfield])dnl
+define([_value_type_],  [Foam::scalar])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_field_from_value(_target_, svalue, FACE_EXPR)
+rule_get_field(_target_, SSCALAR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_scalar_operations()
+rules_scalar_functions()
+
+// Non-standard but manage via FieldOps::assign
+rule_unary_assign(_target_, _target_, FLOOR, Foam::floorOp<Foam::scalar>())
+rule_unary_assign(_target_, _target_, CEIL, Foam::ceilOp<Foam::scalar>())
+rule_unary_assign(_target_, _target_, ROUND, Foam::roundOp<Foam::scalar>())
+
+// Non-standard but manage via FieldOps::assign
+rule_binary_assign(_target_, _target_, _target_, HYPOT, Foam::hypotOp<Foam::scalar>())
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (surfaceVectorField)
+dnl
+define([_target_],      [svfield])dnl
+define([_value_type_],  [Foam::vector])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_get_field(_target_, SVECTOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_vector_operations()
+rules_vector_functions()
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (surfaceSphericalTensorField)
+dnl
+define([_target_],      [shfield])dnl
+define([_value_type_],  [Foam::sphericalTensor])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_get_field(_target_, SSPH_TENSOR_ID)
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_sphTensor_operations()
+rules_sphTensor_functions()
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (surfaceSymmTensorField)
+dnl
+define([_target_],      [syfield])dnl
+define([_value_type_],  [Foam::symmTensor])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_get_field(_target_, SSYM_TENSOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_symTensor_operations()
+rules_symTensor_functions()
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (surfaceTensorField)
+dnl
+define([_target_],      [stfield])dnl
+define([_value_type_],  [Foam::tensor])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_get_field(_target_, STENSOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_tensor_operations()
+rules_tensor_functions()
+
+
+/*---------------------------------------------------------------------------*\
+ * Logic field productions (surfaceScalarField)
+dnl
+define([_target_],      [slfield])dnl
+define([_value_type_],  [Foam::scalar])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a, true); /* Logical */ }
+
+_logic_ (lhs) ::= FACE_EXPR LPAREN LTRUE RPAREN .  { lhs = _new_slfield(_logic_true_); }
+_logic_ (lhs) ::= FACE_EXPR LPAREN LFALSE RPAREN . { lhs = _new_slfield(_logic_false_); }
+
+rule_cast_logical(_target_, _target_)
+rule_cast_logical(_target_, _scalar_, Foam::scalar)
+
+rules_logical_operations(_logic_, _value_type_)
+
+
+/*---------------------------------------------------------------------------*\
+ * General Surface-related productions
+\*---------------------------------------------------------------------------*/
+
+rules_driver_surface_functions()
+
+dnl> %endif
+// End disable_surface_fields
+
+
+/* * * * * * * * * * * * * * * * Point Fields  * * * * * * * * * * * * * * * *\
+dnl
+define([_logic_],       [plfield])dnl
+define([_scalar_],      [psfield])dnl
+define([_vector_],      [pvfield])dnl
+define([_sphTensor_],   [phfield])dnl
+define([_symTensor_],   [pyfield])dnl
+define([_tensor_],      [ptfield])dnl
+dnl
+dnl Several functions are incomplete for point fields
+dnl
+pushdef([incomplete_rule_unary_func], [])
+pushdef([incomplete_rule_binary_func], [])
+\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+dnl> %ifndef disable_point_fields
+
+/*---------------------------------------------------------------------------*\
+ * Productions (pointScalarField)
+dnl
+define([_target_],      [psfield])dnl
+define([_value_type_],  [Foam::scalar])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_field_from_value(_target_, svalue, POINT_EXPR)
+rule_get_field(_target_, PSCALAR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+
+// Non-standard but manage via FieldOps::assign
+rule_unary_assign(_target_, _target_, FLOOR, Foam::floorOp<Foam::scalar>())
+rule_unary_assign(_target_, _target_, CEIL, Foam::ceilOp<Foam::scalar>())
+rule_unary_assign(_target_, _target_, ROUND, Foam::roundOp<Foam::scalar>())
+
+// Non-standard but manage via FieldOps::assign
+rule_binary_assign(_target_, _target_, _target_, HYPOT, Foam::hypotOp<Foam::scalar>())
+
+
+/*---------------------------------------------------------------------------*\
+ * Some special handling for point fields.
+ * Some operators are incomplete, or ambiguous.
+ * Just calculate directly on the primitiveField, which is not as bad as it
+ * sounds since most of the pointPatchField operators are dummies (no-op)
+ * anyhow.
+dnl
+pushdef([rule_binary_op],
+[$1 (lhs) ::= $2 (a) $4 $3 (b) .
+{
+    lhs = _new_$1();
+    (*lhs).primitiveFieldRef() =
+        (make_obj(a).primitiveField() $5 make_obj(b).primitiveField());
+}]
+)dnl>
+dnl
+pushdef([rule_unary_func],
+[$1 (lhs) ::= $3 LPAREN $2 (a) RPAREN .
+{
+    lhs = _new_$1();
+    (*lhs).primitiveFieldRef() = $4 (make_obj(a).primitiveField());
+}]
+)dnl>
+dnl
+pushdef([rule_binary_func],
+[$1 (lhs) ::= $4 LPAREN $2 (a) COMMA $3 (b) RPAREN .
+{
+    lhs = _new_$1();
+    (*lhs).primitiveFieldRef() =
+        $5(make_obj(a).primitiveField(), make_obj(b).primitiveField());
+}]
+)dnl>
+dnl
+\*---------------------------------------------------------------------------*/
+
+rules_scalar_operations()
+rules_scalar_functions()
+
+dnl/* Restore definitions
+popdef([rule_binary_op])dnl
+popdef([rule_unary_func])dnl
+popdef([rule_binary_func])dnl
+popdef([incomplete_rule_unary_func])dnl
+popdef([incomplete_rule_binary_func])dnl
+dnl*/
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (pointVectorField)
+dnl
+define([_target_],      [pvfield])dnl
+define([_value_type_],  [Foam::vector])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_get_field(_target_, PVECTOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_vector_operations()
+rules_vector_functions()
+
+/*---------------------------------------------------------------------------*\
+ * Productions (pointSphericalTensorField)
+dnl
+define([_target_],      [phfield])dnl
+define([_value_type_],  [Foam::sphericalTensor])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_get_field(_target_, PSPH_TENSOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_sphTensor_operations()
+rules_sphTensor_functions()
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (pointSymmTensorField)
+dnl
+define([_target_],      [pyfield])dnl
+define([_value_type_],  [Foam::symmTensor])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_get_field(_target_, PSYM_TENSOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_symTensor_operations()
+rules_symTensor_functions()
+
+
+/*---------------------------------------------------------------------------*\
+ * Productions (pointTensorField)
+dnl
+define([_target_],      [ptfield])dnl
+define([_value_type_],  [Foam::tensor])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a); }
+
+rule_get_field(_target_, PTENSOR_ID)
+
+rules_standard(_target_, _value_type_, _logic_)
+rules_inplace_gUnary(_target_)
+rules_tensor_operations()
+rules_tensor_functions()
+
+/*---------------------------------------------------------------------------*\
+ * Logic field productions (pointScalarField)
+dnl
+define([_target_],      [plfield])dnl
+define([_value_type_],  [Foam::scalar])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+evaluate ::= _target_ (a) . { driver->setResult(a, true); /* Logical */ }
+
+_logic_ (lhs) ::= POINT_EXPR LPAREN LTRUE RPAREN .  { lhs = _new_plfield(_logic_true_); }
+_logic_ (lhs) ::= POINT_EXPR LPAREN LFALSE RPAREN . { lhs = _new_plfield(_logic_false_); }
+rules_logical_operations(_logic_, _value_type_)
+
+
+/*---------------------------------------------------------------------------*\
+ * General Point-related productions
+\*---------------------------------------------------------------------------*/
+
+rules_driver_point_functions()
+
+dnl> %endif
+// End disable_point_fields
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+/*---------------------------------------------------------------------------*\
+ * Volume field composition
+\*---------------------------------------------------------------------------*/
+
+rule_mag_logical(sfield, lfield)
+rules_mag_functions(sfield, sfield)
+rules_mag_functions(sfield, vfield)
+rules_mag_functions(sfield, tfield)
+rules_mag_functions(sfield, yfield)
+rules_mag_functions(sfield, hfield)
+
+rule_vector_zip(vfield, sfield, VECTOR)
+rule_tensor_zip(tfield, sfield, TENSOR)
+rule_symTensor_zip(yfield, sfield, SYM_TENSOR)
+rule_sphTensor_zip(hfield, sfield, SPH_TENSOR)
+
+rule_vector_components(sfield, vfield)
+rule_tensor_components(sfield, tfield)
+rule_symTensor_components(sfield, yfield)
+rule_sphTensor_components(sfield, hfield)
+
+rule_tensor_transpose(tfield)
+rule_symTensor_transpose(yfield)
+rule_sphTensor_transpose(hfield)
+
+rule_tensor_unzipDiag(vfield, yfield)
+rule_tensor_unzipAll(vfield, tfield)
+
+rule_pointToCell(sfield, psfield)
+rule_pointToCell(vfield, pvfield)
+rule_pointToCell(tfield, ptfield)
+rule_pointToCell(yfield, pyfield)
+rule_pointToCell(hfield, phfield)
+
+vfield (lhs) ::= RECONSTRUCT LPAREN ssfield (a) RPAREN.
+{
+    lhs = Foam::fvc::reconstruct(make_obj(a)).ptr();
+}
+
+
+/*---------------------------------------------------------------------------*\
+ * Surface field composition
+\*---------------------------------------------------------------------------*/
+
+rule_mag_logical(ssfield, slfield)
+rules_mag_functions(ssfield, ssfield)
+rules_mag_functions(ssfield, svfield)
+rules_mag_functions(ssfield, stfield)
+rules_mag_functions(ssfield, syfield)
+rules_mag_functions(ssfield, shfield)
+
+rule_vector_zip(svfield, ssfield, VECTOR)
+rule_tensor_zip(stfield, ssfield, TENSOR)
+rule_symTensor_zip(syfield, ssfield, SYM_TENSOR)
+rule_sphTensor_zip(shfield, ssfield, SPH_TENSOR)
+
+rule_vector_components(ssfield, svfield)
+rule_tensor_components(ssfield, stfield)
+rule_symTensor_components(ssfield, syfield)
+rule_sphTensor_components(ssfield, shfield)
+
+rule_tensor_transpose(stfield)
+rule_symTensor_transpose(syfield)
+rule_sphTensor_transpose(shfield)
+
+rule_tensor_unzipDiag(svfield, syfield)
+rule_tensor_unzipAll(svfield, stfield)
+
+rule_cellToFace(ssfield, sfield)
+rule_cellToFace(svfield, vfield)
+rule_cellToFace(stfield, tfield)
+rule_cellToFace(syfield, yfield)
+rule_cellToFace(shfield, hfield)
+
+
+/*---------------------------------------------------------------------------*\
+ * Point field composition
+ * - Use primitiveField directly
+dnl
+pushdef([field_write_access], [($1).primitiveFieldRef()])dnl
+pushdef([field_read_access], [($1).primitiveField()])dnl
+dnl
+\*---------------------------------------------------------------------------*/
+
+rule_mag_logical(psfield, plfield)
+rules_mag_functions(psfield, psfield)
+rules_mag_functions(psfield, pvfield)
+rules_mag_functions(psfield, ptfield)
+rules_mag_functions(psfield, pyfield)
+rules_mag_functions(psfield, phfield)
+
+rule_vector_zip(pvfield, psfield, VECTOR)
+rule_tensor_zip(ptfield, psfield, TENSOR)
+rule_symTensor_zip(pyfield, psfield, SYM_TENSOR)
+rule_sphTensor_zip(phfield, psfield, SPH_TENSOR)
+
+rule_vector_components(psfield, pvfield)
+rule_tensor_components(psfield, ptfield)
+rule_symTensor_components(psfield, pyfield)
+rule_sphTensor_components(psfield, phfield)
+
+rule_tensor_transpose(ptfield)
+rule_symTensor_transpose(pyfield)
+rule_sphTensor_transpose(phfield)
+
+rule_tensor_unzipDiag(pvfield, pyfield)
+rule_tensor_unzipAll(pvfield, ptfield)
+
+rule_cellToPoint(psfield, sfield)
+rule_cellToPoint(pvfield, vfield)
+rule_cellToPoint(ptfield, tfield)
+rule_cellToPoint(pyfield, yfield)
+rule_cellToPoint(phfield, hfield)
+
+
+dnl/* Restore definitions
+popdef([field_write_access])dnl
+popdef([field_read_access])dnl
+dnl*/
+
+// ************************************************************************* //
+
+dnl/* Standard m4 quoting
+changequote([`],['])dnl
+dnl*/
+
+%code
+{
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::expressions::volumeExpr::parser::stop()
+{
+    if (lemon_)
+    {
+        ParseFree(lemon_, ::operator delete);
+        #ifndef NDEBUG
+        ParseTrace(nullptr, nullptr);
+        #endif
+        lemon_ = nullptr;
+    }
+}
+
+
+void Foam::expressions::volumeExpr::parser::start(parseDriver& driver_)
+{
+    this->stop();
+    lemon_ = ParseAlloc(::operator new, &driver_);
+
+    if (debug || driver_.debugParser())
+    {
+        #ifndef NDEBUG
+        ParseTrace(stderr, const_cast<char*>(prompt_));
+        #endif
+    }
+}
+
+
+void Foam::expressions::volumeExpr::parser::parse
+(
+    int tokenId,
+    scanToken* tokenVal
+)
+{
+    Parse(lemon_, tokenId, tokenVal);
+}
+
+
+Foam::word Foam::expressions::volumeExpr::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
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/volume/volumeExprLemonParserMacros.m4 b/src/finiteVolume/expressions/volume/volumeExprLemonParserMacros.m4
new file mode 100644
index 0000000000000000000000000000000000000000..a0df5c6314cfc6fbaab7bfc8ddc6fc31d642d9ef
--- /dev/null
+++ b/src/finiteVolume/expressions/volume/volumeExprLemonParserMacros.m4
@@ -0,0 +1,139 @@
+divert(-1)dnl
+#-----------------------------------*- m4 -*-----------------------------------
+#   =========                 |
+#   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+#    \\    /   O peration     |
+#     \\  /    A nd           | www.openfoam.com
+#      \\/     M anipulation  |
+#------------------------------------------------------------------------------
+#     Copyright (C) 2019 OpenCFD Ltd.
+#------------------------------------------------------------------------------
+# License
+#     This file is part of OpenFOAM, licensed under GNU General Public License
+#     <http://www.gnu.org/licenses/>.
+#
+# Description
+#     Driver-specific m4/lemon macros for volume expressions.
+#
+#------------------------------------------------------------------------------
+
+include(`m4/lemon/base-setup.m4')dnl
+include([m4/lemon/operator-precedence.m4])dnl
+dnl
+include([m4/lemon/rules-standard.m4])dnl
+include([m4/lemon/rules-operations.m4])dnl
+include([m4/lemon/rules-functions.m4])dnl
+include([m4/lemon/rules-components.m4])dnl
+include([m4/lemon/rules-fields-components.m4])dnl
+include([m4/lemon/rules-scalar-logic.m4])dnl
+dnl
+divert(-1)dnl
+
+#------------------------------------------------------------------------------
+# Driver rules
+#------------------------------------------------------------------------------
+
+define([rules_driver_volume_functions],
+[dnl
+rule_driver_select(_logic_, CSET, field_cellSet)dnl
+rule_driver_select(_logic_, CZONE, field_cellZone)dnl
+dnl
+rule_driver_nullary(_scalar_, CELL_VOLUME, field_cellVolume)dnl
+rule_driver_nullary(_vector_, POS, field_cellCentre)dnl CELL_CENTRE
+dnl
+rule_driver_inplace_unary(_scalar_, WEIGHT_AVERAGE, volAverage)dnl
+rule_driver_inplace_unary(_vector_, WEIGHT_AVERAGE, volAverage)dnl
+rule_driver_inplace_unary(_sphTensor_, WEIGHT_AVERAGE, volAverage)dnl
+rule_driver_inplace_unary(_symTensor_, WEIGHT_AVERAGE, volAverage)dnl
+rule_driver_inplace_unary(_tensor_, WEIGHT_AVERAGE, volAverage)dnl
+dnl
+rule_driver_inplace_unary(_scalar_, WEIGHT_SUM, volSum)dnl
+rule_driver_inplace_unary(_vector_, WEIGHT_SUM, volSum)dnl
+rule_driver_inplace_unary(_sphTensor_, WEIGHT_SUM, volSum)dnl
+rule_driver_inplace_unary(_symTensor_, WEIGHT_SUM, volSum)dnl
+rule_driver_inplace_unary(_tensor_, WEIGHT_SUM, volSum)dnl
+dnl
+])
+
+define([rules_driver_surface_functions],
+[dnl
+rule_driver_select(_logic_, FSET, field_faceSet)dnl
+rule_driver_select(_logic_, FZONE, field_faceZone)dnl
+dnl
+rule_driver_nullary(_scalar_, FACE_AREA, field_faceArea)dnl
+rule_driver_nullary(_vector_, FACE_CENTRE, field_faceCentre)dnl
+rule_driver_nullary(_vector_, FACE_EXPR, field_areaNormal)dnl
+dnl
+rule_driver_inplace_unary(_scalar_, WEIGHT_AVERAGE, areaAverage)dnl
+rule_driver_inplace_unary(_vector_, WEIGHT_AVERAGE, areaAverage)dnl
+rule_driver_inplace_unary(_sphTensor_, WEIGHT_AVERAGE, areaAverage)dnl
+rule_driver_inplace_unary(_symTensor_, WEIGHT_AVERAGE, areaAverage)dnl
+rule_driver_inplace_unary(_tensor_, WEIGHT_AVERAGE, areaAverage)dnl
+dnl
+rule_driver_inplace_unary(_scalar_, WEIGHT_SUM, areaSum)dnl
+rule_driver_inplace_unary(_vector_, WEIGHT_SUM, areaSum)dnl
+rule_driver_inplace_unary(_sphTensor_, WEIGHT_SUM, areaSum)dnl
+rule_driver_inplace_unary(_symTensor_, WEIGHT_SUM, areaSum)dnl
+rule_driver_inplace_unary(_tensor_, WEIGHT_SUM, areaSum)dnl
+dnl
+])
+
+define([rules_driver_point_functions],
+[dnl
+rule_driver_select(_logic_, PSET, field_pointSet)dnl
+rule_driver_select(_logic_, PZONE, field_pointZone)dnl
+dnl
+rule_driver_nullary(_vector_, POINTS, field_pointField)dnl
+dnl
+dnl NB use non-driver versions for points - ie, unweighted
+dnl
+rule_inplace_unary(_scalar_, WEIGHT_AVERAGE, Foam::gAverage)dnl
+rule_inplace_unary(_vector_, WEIGHT_AVERAGE, Foam::gAverage)dnl
+rule_inplace_unary(_sphTensor_, WEIGHT_AVERAGE, Foam::gAverage)dnl
+rule_inplace_unary(_symTensor_, WEIGHT_AVERAGE, Foam::gAverage)dnl
+rule_inplace_unary(_tensor_, WEIGHT_AVERAGE, Foam::gAverage)dnl
+dnl
+rule_inplace_unary(_scalar_, WEIGHT_SUM, Foam::gSum)dnl
+rule_inplace_unary(_vector_, WEIGHT_SUM, Foam::gSum)dnl
+rule_inplace_unary(_sphTensor_, WEIGHT_SUM, Foam::gSum)dnl
+rule_inplace_unary(_symTensor_, WEIGHT_SUM, Foam::gSum)dnl
+rule_inplace_unary(_tensor_, WEIGHT_SUM, Foam::gSum)dnl
+dnl
+])
+
+
+#------------------------------------------------------------------------------
+# rule_cellToFace(out, in)
+# rule_cellToPoint(out, in)
+# rule_pointToCell(out, in)
+#
+# Description
+#     Production rules for driver cellToFace, cellToPoint, pointToCell,
+#     methods
+#------------------------------------------------------------------------------
+
+define([rule_cellToFace],
+[rule_driver_unary($1, $2, CELL_TO_FACE, cellToFace)])
+
+define([rule_cellToPoint],
+[rule_driver_unary($1, $2, CELL_TO_POINT, cellToPoint)])
+
+define([rule_pointToCell],
+[rule_driver_unary($1, $2, POINT_TO_CELL, pointToCell)])
+
+
+#------------------------------------------------------------------------------
+# Standard rules for fields: declaration, new/get, driver functions etc.
+
+include([m4/lemon/rules-fields.m4])dnl
+divert(-1)dnl
+
+
+#------------------------------------------------------------------------------
+
+# Additional safety measures
+
+undefine([substr])dnl   # Avoid collision with C/C++ naming
+
+#------------------------------------------------------------------------------
+divert(0)dnl
diff --git a/src/finiteVolume/expressions/volume/volumeExprParser.H b/src/finiteVolume/expressions/volume/volumeExprParser.H
new file mode 100644
index 0000000000000000000000000000000000000000..7664d10b4e6d89d39006a9561bcc7b6c284cc302
--- /dev/null
+++ b/src/finiteVolume/expressions/volume/volumeExprParser.H
@@ -0,0 +1,106 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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::expressions::volumeExpr::parser
+
+Description
+    Lemon parser interface for volume expressions grammar
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef expressions_volumeExprParser_H
+#define expressions_volumeExprParser_H
+
+#include "volumeExprFwd.H"
+
+namespace Foam
+{
+namespace expressions
+{
+namespace volumeExpr
+{
+
+/*---------------------------------------------------------------------------*\
+                           Class parser Declaration
+\*---------------------------------------------------------------------------*/
+
+class parser
+{
+    // Private Data
+
+        //- Prompt for parser tracing
+        static constexpr const char* const prompt_ = "volExpr:";
+
+        //- The lemon parser (demand-driven)
+        void* lemon_;
+
+
+public:
+
+    //- Local object debugging
+    int debug;
+
+
+    // Constructors
+
+        //- Construct null
+        parser() : lemon_(nullptr), debug(volumeExpr::debug) {}
+
+
+    //- Destructor, deletes parser backend
+    ~parser()
+    {
+        stop();
+    }
+
+
+    // Member Functions
+
+        //- Start parsing, with the given driver context
+        void start(parseDriver& driver_);
+
+        //- Stop parsing, freeing the allocated parser
+        void stop();
+
+        //- Push token/value to parser
+        void parse(int tokenId, scanToken* tokenVal);
+
+        //- Return the text name corresponding to the tokenId
+        word nameOfToken(int tokenId) const;
+};
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace volumeExpr
+} // End namespace expressions
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/volume/volumeExprScanner.H b/src/finiteVolume/expressions/volume/volumeExprScanner.H
new file mode 100644
index 0000000000000000000000000000000000000000..b432a602fbe9860d864b18b57a99a512956b27f6
--- /dev/null
+++ b/src/finiteVolume/expressions/volume/volumeExprScanner.H
@@ -0,0 +1,162 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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::expressions::volumeExpr::scanner
+
+Description
+    Ragel lexer/scanner interface for volume expressions.
+
+Note
+    Ragel code generated with the ./createCode script.
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef expressions_volumeExprScanner_H
+#define expressions_volumeExprScanner_H
+
+#include "volumeExprFwd.H"
+#include "scalar.H"
+
+namespace Foam
+{
+namespace expressions
+{
+namespace volumeExpr
+{
+
+/*---------------------------------------------------------------------------*\
+                          Class scanToken Declaration
+\*---------------------------------------------------------------------------*/
+
+union scanToken
+{
+    Foam::label  ivalue;
+    Foam::scalar svalue;
+    Foam::word*  name;
+
+    //- Null construct, bit-wise zero for union content
+    scanToken() : ivalue(0) {}
+};
+
+
+/*---------------------------------------------------------------------------*\
+                         Class scanner Declaration
+\*---------------------------------------------------------------------------*/
+
+class scanner
+{
+    // Private Data
+
+        //- Wrapped lemon parser
+        parser* parser_;
+
+        // Ragel code state, action
+        int cs, act;
+
+
+    // Private Member Functions
+
+        //- Dispatch .method to parser (if known) or Fatal
+        bool dispatch_method
+        (
+            const parseDriver& driver_,
+            scanToken& scanTok,
+            word&& ident
+        ) const;
+
+        //- Dispatch identifier to parser (if possible) or Fatal
+        bool dispatch_ident
+        (
+            const parseDriver& driver_,
+            scanToken& scanTok,
+            word&& ident
+        ) const;
+
+
+public:
+
+    //- Local debugging
+    int debug;
+
+
+    // Constructors
+
+        //- Construct null, optionally setting debugging
+        explicit scanner(bool withDebug = false)
+        :
+            parser_(nullptr),
+            debug(volumeExpr::debug)
+        {
+            if (withDebug)
+            {
+                debug |= 4;
+            }
+        }
+
+
+    //- 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 volumeExpr
+} // End namespace expressions
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/volume/volumeExprScanner.cc b/src/finiteVolume/expressions/volume/volumeExprScanner.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c83ed2be6303a5bcac2ce8726601083c93ad3cd9
--- /dev/null
+++ b/src/finiteVolume/expressions/volume/volumeExprScanner.cc
@@ -0,0 +1,3866 @@
+
+#line 1 "volumeExprScanner.rl"
+/*--------------------------------*- 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 volume field expressions
+
+\*---------------------------------------------------------------------------*/
+
+#include "volumeExprScanner.H"
+#include "volumeExprDriver.H"
+#include "volumeExprLemonParser.h"
+#include "volumeExprParser.H"
+#include "Enum.H"
+#include "macros.H"
+
+#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+
+// Debugging to stderr
+#undef  DebugInfo
+#define DebugInfo if (debug) InfoErr
+
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+//- Paste token prefix
+#define TOKEN_OF(T)         TOK_##T
+
+//- An {int, c_str} enum pairing
+#define TOKEN_PAIR(Name,T)  { TOKEN_OF(T), Name }
+
+// Special handling for these known (stashed) look-back types
+static const Enum<int> lookBehindTokenEnums
+({
+    TOKEN_PAIR("cellSet", CSET),
+    TOKEN_PAIR("faceSet", FSET),
+    TOKEN_PAIR("pointSet", PSET),
+    TOKEN_PAIR("cellZone", CZONE),
+    TOKEN_PAIR("faceZone", FZONE),
+    TOKEN_PAIR("pointZone", PZONE),
+});
+
+#define HAS_LOOKBEHIND_TOKENS
+
+
+// Special handling of predefined method types. Eg, .x(), .y(), ...
+static const Enum<int> fieldMethodEnums
+({
+    TOKEN_PAIR("x", CMPT_X),
+    TOKEN_PAIR("y", CMPT_Y),
+    TOKEN_PAIR("z", CMPT_Z),
+    TOKEN_PAIR("xx", CMPT_XX),
+    TOKEN_PAIR("xy", CMPT_XY),
+    TOKEN_PAIR("xz", CMPT_XZ),
+    TOKEN_PAIR("yx", CMPT_YX),
+    TOKEN_PAIR("yy", CMPT_YY),
+    TOKEN_PAIR("yz", CMPT_YZ),
+    TOKEN_PAIR("zx", CMPT_ZX),
+    TOKEN_PAIR("zy", CMPT_ZY),
+    TOKEN_PAIR("zz", CMPT_ZZ),
+    TOKEN_PAIR("ii", CMPT_II),
+    TOKEN_PAIR("diag", DIAG),   /* tensors only */
+    TOKEN_PAIR("T", TRANSPOSE), /* tensors only */
+});
+
+
+// Known field-token types
+static const Enum<int> fieldTokenEnums
+({
+#ifdef TOK_SCALAR_ID
+    TOKEN_PAIR(volScalarField::typeName.c_str(), SCALAR_ID),
+    TOKEN_PAIR(volVectorField::typeName.c_str(), VECTOR_ID),
+    TOKEN_PAIR(volTensorField::typeName.c_str(), TENSOR_ID),
+    TOKEN_PAIR(volSymmTensorField::typeName.c_str(), SYM_TENSOR_ID),
+    TOKEN_PAIR(volSphericalTensorField::typeName.c_str(), SPH_TENSOR_ID),
+#else
+#error TOK_SCALAR_ID not defined
+#endif
+#ifdef TOK_SSCALAR_ID
+    TOKEN_PAIR(surfaceScalarField::typeName.c_str(), SSCALAR_ID),
+    TOKEN_PAIR(surfaceVectorField::typeName.c_str(), SVECTOR_ID),
+    TOKEN_PAIR(surfaceTensorField::typeName.c_str(), STENSOR_ID),
+    TOKEN_PAIR(surfaceSymmTensorField::typeName.c_str(), SSYM_TENSOR_ID),
+    TOKEN_PAIR(surfaceSphericalTensorField::typeName.c_str(), SSPH_TENSOR_ID),
+#else
+#warning TOK_SSCALAR_ID not defined
+#endif
+#ifdef TOK_PSCALAR_ID
+    TOKEN_PAIR(pointScalarField::typeName.c_str(), PSCALAR_ID),
+    TOKEN_PAIR(pointVectorField::typeName.c_str(), PVECTOR_ID),
+    TOKEN_PAIR(pointTensorField::typeName.c_str(), PTENSOR_ID),
+    TOKEN_PAIR(pointSymmTensorField::typeName.c_str(), PSYM_TENSOR_ID),
+    TOKEN_PAIR(pointSphericalTensorField::typeName.c_str(), PSPH_TENSOR_ID),
+#else
+#warning TOK_PSCALAR_ID not defined
+#endif
+});
+
+
+// Simple compile-time function name declarations.
+// Useful for handling driver-specific dispatching, or functions that
+// are not universally available.
+static const Enum<int> funcTokenEnums
+({
+#ifdef TOK_FLOOR
+    TOKEN_PAIR("floor", FLOOR),
+    TOKEN_PAIR("ceil", CEIL),
+    TOKEN_PAIR("round", ROUND),
+#endif
+#ifdef TOK_HYPOT  /* Can use hypot? */
+    TOKEN_PAIR("hypot", HYPOT),
+#endif
+
+    // Already parsed as function: TOKEN_PAIR("pos", CELL_CENTRE),
+
+    TOKEN_PAIR("point", POINT_EXPR), // Point value
+    TOKEN_PAIR("face", FACE_EXPR),   // Face value, Face areaNormal
+
+    TOKEN_PAIR("cellToPoint", CELL_TO_POINT),
+    TOKEN_PAIR("cellToFace",  CELL_TO_FACE),
+    TOKEN_PAIR("pointToCell", POINT_TO_CELL),
+
+    TOKEN_PAIR("area", FACE_AREA),
+    TOKEN_PAIR("vol",  CELL_VOLUME),
+
+    TOKEN_PAIR("fpos", FACE_CENTRE),
+    TOKEN_PAIR("pts", POINTS),
+});
+
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Classifying token type based on an identifier name is indeed ugly.
+//
+// 1)
+//   Handle special cases (eg, cellSet,...) first that have been tagged
+//   as expected content with the stashed "look-behind" token.
+//   Handle not-found errors here directly.
+//
+// 2)
+//   Fallback to determining which field-type (volScalarField etc) the name
+//   corresponds to.
+//   Handle not-found errors by return -1.
+//
+static int driverTokenType
+(
+    const expressions::volumeExpr::parseDriver& driver_,
+    const word& ident
+)
+{
+    // Get stashed "look-behind" to decide what type of identifier we expect
+    const int lookBehind = driver_.resetStashedTokenId();
+
+    if (lookBehind && lookBehindTokenEnums.found(lookBehind))
+    {
+        bool good = false;
+
+        switch (lookBehind)
+        {
+            case TOK_CSET : good = driver_.isCellSet(ident); break;
+            case TOK_FSET : good = driver_.isFaceSet(ident); break;
+            case TOK_PSET : good = driver_.isPointSet(ident); break;
+            case TOK_CZONE : good = driver_.isCellZone(ident); break;
+            case TOK_FZONE : good = driver_.isFaceZone(ident); break;
+            case TOK_PZONE : good = driver_.isPointZone(ident); break;
+        }
+
+        if (good)
+        {
+            return TOK_IDENTIFIER;
+        }
+
+        // Fatal
+        driver_.reportFatal
+        (
+            "Error no " + lookBehindTokenEnums.get(lookBehind) + ": " + ident
+        );
+
+        return -2;  // Extra safety
+    }
+
+    // Surface variables - distinguish from volume by size
+    #ifdef TOK_SSCALAR_ID
+    {
+        const label len = driver_.mesh().nInternalFaces();
+
+        #undef checkFieldToken
+        #define checkFieldToken(TokType, Type)                                \
+        if (driver_.isVariable<Type>(ident, false, len))                      \
+        {                                                                     \
+            return TokType;                                                   \
+        }
+
+        checkFieldToken(TOK_SSCALAR_ID, scalar);
+        checkFieldToken(TOK_SVECTOR_ID, vector);
+        checkFieldToken(TOK_SSYM_TENSOR_ID, symmTensor);
+        checkFieldToken(TOK_SSPH_TENSOR_ID, sphericalTensor);
+        checkFieldToken(TOK_STENSOR_ID, tensor);
+    }
+    #endif
+
+    // Point variables
+    #ifdef TOK_PSCALAR_ID
+    {
+        #undef checkFieldToken
+        #define checkFieldToken(TokType, Type)                                \
+        if (driver_.isVariable<Type>(ident, true))                            \
+        {                                                                     \
+            return TokType;                                                   \
+        }
+
+        checkFieldToken(TOK_PSCALAR_ID, scalar);
+        checkFieldToken(TOK_PVECTOR_ID, vector);
+        checkFieldToken(TOK_PSPH_TENSOR_ID, sphericalTensor);
+        checkFieldToken(TOK_PSYM_TENSOR_ID, symmTensor);
+        checkFieldToken(TOK_PTENSOR_ID, tensor);
+    }
+    #endif
+
+    // Volume variables
+    #ifdef TOK_SCALAR_ID
+    {
+        #undef checkFieldToken
+        #define checkFieldToken(TokType, Type)                                \
+        if (driver_.isVariable<Type>(ident, false))                           \
+        {                                                                     \
+            return TokType;                                                   \
+        }
+
+        checkFieldToken(TOK_SCALAR_ID, scalar);
+        checkFieldToken(TOK_VECTOR_ID, vector);
+        checkFieldToken(TOK_SPH_TENSOR_ID, sphericalTensor);
+        checkFieldToken(TOK_SYM_TENSOR_ID, symmTensor);
+        checkFieldToken(TOK_TENSOR_ID, tensor);
+    }
+    #endif
+
+    #undef checkFieldToken
+
+    // Check registered fields and/or disk-files
+    {
+        const word fieldType(driver_.getFieldClassName(ident));
+
+        int tokType = fieldTokenEnums.get(fieldType, -1);
+
+        if (tokType > 0)
+        {
+            return tokType;
+        }
+    }
+
+    return -1;
+}
+
+} // End anonymous namespace
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+// 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
+
+#define EMIT_TOKEN(T)                                                         \
+    driver_.parsePosition() = (ts-buf);                                       \
+    DebugInfo<< STRINGIFY(T) << ": " << driver_.parsePosition() << nl;        \
+    parser_->parse(TOKEN_OF(T), nullptr);                                     \
+    driver_.parsePosition() = (p-buf);
+
+
+
+#line 306 "volumeExprScanner.cc"
+static const int volumeExpr_start = 11;
+static const int volumeExpr_first_final = 11;
+static const int volumeExpr_error = 0;
+
+static const int volumeExpr_en_main = 11;
+
+
+#line 444 "volumeExprScanner.rl"
+
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::expressions::volumeExpr::scanner::~scanner()
+{
+    if (parser_)
+    {
+        delete parser_;
+    }
+}
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+bool Foam::expressions::volumeExpr::scanner::dispatch_method
+(
+    const parseDriver& driver_,
+    scanToken& scanTok,
+    word&& ident
+) const
+{
+    if (ident[0] == '.')
+    {
+        ident.erase(0, 1);
+    }
+
+    DebugInfo
+        << "Method:" << ident
+        << " at " << driver_.parsePosition() << nl;
+
+    const int methType = fieldMethodEnums.get(ident, -1);
+
+    if (methType > 0)
+    {
+        // Dispatch '.' and "method" separately
+        parser_->parse(TOK_DOT, nullptr);
+        parser_->parse(methType, nullptr);
+
+        return true;
+    }
+
+    driver_.reportFatal("Unknown method: " + ident);
+    return false;
+}
+
+
+bool Foam::expressions::volumeExpr::scanner::dispatch_ident
+(
+    const parseDriver& driver_,
+    scanToken& scanTok,
+    word&& ident
+) const
+{
+    int tokType = -1;
+
+    const bool quoted =
+    (
+        (ident.front() == '"' || ident.front() == '\'')
+     && (ident.front() == ident.back())
+    );
+
+    if (quoted)
+    {
+        ident.erase(ident.size()-1);
+        ident.erase(0, 1);
+    }
+    else
+    {
+        // Check for function name
+        tokType = funcTokenEnums.get(ident, -1);
+
+        if (tokType > 0)
+        {
+            DebugInfo
+                << "Emit:" << ident << " function:"
+                << parser_->nameOfToken(tokType) << nl;
+
+            parser_->parse(tokType, nullptr);
+            return true;
+        }
+
+        #ifdef HAS_LOOKBEHIND_TOKENS
+        // Specials such "cset" also reset the look-behind
+        tokType = lookBehindTokenEnums.get(ident, -1);
+
+        if (tokType > 0)
+        {
+            DebugInfo
+                << "Emit:" << ident << " as look-behind:"
+                << parser_->nameOfToken(tokType) << nl;
+
+            driver_.resetStashedTokenId(tokType);
+            parser_->parse(tokType, nullptr);
+            return true;
+        }
+        #endif
+    }
+
+
+    // Can also peek at stashed "look-behind"
+    // const int lookBehind = driver_.stashedTokenId();
+
+    tokType = driverTokenType(driver_, ident);
+
+    if (tokType > 0)
+    {
+        DebugInfo
+            << "Emit:" << ident << " token:"
+            << parser_->nameOfToken(tokType) << nl;
+
+        scanTok.name = new Foam::word(std::move(ident));
+        parser_->parse(tokType, &scanTok);
+
+        return true;
+    }
+
+
+    // Not found? Attempt to strip off '.x' endings etc,
+    // but not when quoted
+
+    const auto dot = ident.rfind('.');
+    const int methType =
+    (
+        quoted || dot == std::string::npos
+      ? -1
+      : fieldMethodEnums.get(ident.substr(dot+1), -1)
+    );
+
+    if
+    (
+        methType > 0
+     && (tokType = driverTokenType(driver_, ident.substr(0, dot))) > 0
+    )
+    {
+        DebugInfo
+            << "Emit:" << ident.substr(0, dot).c_str() << " token:"
+            << parser_->nameOfToken(tokType) << " with "
+            << ident.substr(dot).c_str() << " token:"
+            << parser_->nameOfToken(methType) << nl;
+
+        // The field (before the ".")
+        ident.erase(dot);
+
+        scanTok.name = new Foam::word(std::move(ident));
+        parser_->parse(tokType, &scanTok);
+
+        // Dispatch '.' and "method" separately
+        parser_->parse(TOK_DOT, nullptr);
+        parser_->parse(methType, nullptr);
+
+        return true;
+    }
+
+    driver_.reportFatal
+    (
+        "Object " + ident + " does not exist or wrong type"
+    );
+
+    return false;
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::expressions::volumeExpr::scanner::process
+(
+    const std::string& str,
+    size_t strBeg,
+    size_t strLen,
+    parseDriver& driver_
+)
+{
+    // Save debug value
+    const int oldDebug = debug;
+
+    if (driver_.debugScanner())
+    {
+        debug |= 4;
+    }
+
+    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_);
+
+    // Scan token type
+    scanToken scanTok;
+
+    // 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
+    
+#line 541 "volumeExprScanner.cc"
+	{
+	cs = volumeExpr_start;
+	ts = 0;
+	te = 0;
+	act = 0;
+	}
+
+#line 669 "volumeExprScanner.rl"
+   /* ^^^ FSM initialization here ^^^ */;
+
+    
+#line 553 "volumeExprScanner.cc"
+	{
+	if ( p == pe )
+		goto _test_eof;
+	switch ( cs )
+	{
+tr2:
+#line 328 "volumeExprScanner.rl"
+	{te = p+1;{
+        driver_.parsePosition() = (ts-buf);
+        dispatch_ident(driver_, scanTok, word(ts, te-ts, false));
+        driver_.parsePosition() = (p-buf);
+    }}
+	goto st11;
+tr4:
+#line 328 "volumeExprScanner.rl"
+	{te = p+1;{
+        driver_.parsePosition() = (ts-buf);
+        dispatch_ident(driver_, scanTok, word(ts, te-ts, false));
+        driver_.parsePosition() = (p-buf);
+    }}
+	goto st11;
+tr5:
+#line 306 "volumeExprScanner.rl"
+	{{p = ((te))-1;}{
+        driver_.parsePosition() = (ts-buf);
+
+        DebugInfo
+            << "Number:" << std::string(ts, te-ts).c_str()
+            << " at " << driver_.parsePosition() << nl;
+
+        if (readScalar(std::string(ts, te-ts), scanTok.svalue))
+        {
+            parser_->parse(TOKEN_OF(NUMBER), &scanTok);
+        }
+        else
+        {
+            driver_.reportFatal
+            (
+                "Error parsing number: " + std::string(ts, te-ts)
+            );
+        }
+
+        driver_.parsePosition() = (p-buf);
+    }}
+	goto st11;
+tr8:
+#line 371 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(EQUAL); }}
+	goto st11;
+tr9:
+#line 425 "volumeExprScanner.rl"
+	{{p = ((te))-1;}{ EMIT_TOKEN(TENSOR); }}
+	goto st11;
+tr11:
+#line 433 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(UNIT_TENSOR); }}
+	goto st11;
+tr12:
+#line 374 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(LOR); }}
+	goto st11;
+tr16:
+#line 356 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(PERCENT); }}
+	goto st11;
+tr19:
+#line 357 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(LPAREN); }}
+	goto st11;
+tr20:
+#line 358 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(RPAREN); }}
+	goto st11;
+tr21:
+#line 359 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(TIMES); }}
+	goto st11;
+tr22:
+#line 360 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(PLUS); }}
+	goto st11;
+tr23:
+#line 362 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(COMMA); }}
+	goto st11;
+tr24:
+#line 361 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(MINUS); }}
+	goto st11;
+tr26:
+#line 364 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(DIVIDE); }}
+	goto st11;
+tr28:
+#line 366 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(COLON); }}
+	goto st11;
+tr32:
+#line 365 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(QUESTION); }}
+	goto st11;
+tr35:
+#line 377 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(BIT_XOR); }}
+	goto st11;
+tr52:
+#line 350 "volumeExprScanner.rl"
+	{te = p;p--;}
+	goto st11;
+tr53:
+#line 355 "volumeExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(NOT); }}
+	goto st11;
+tr54:
+#line 372 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(NOT_EQUAL); }}
+	goto st11;
+tr55:
+#line 375 "volumeExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(BIT_AND); }}
+	goto st11;
+tr56:
+#line 373 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(LAND); }}
+	goto st11;
+tr57:
+#line 363 "volumeExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(DOT); }}
+	goto st11;
+tr60:
+#line 306 "volumeExprScanner.rl"
+	{te = p;p--;{
+        driver_.parsePosition() = (ts-buf);
+
+        DebugInfo
+            << "Number:" << std::string(ts, te-ts).c_str()
+            << " at " << driver_.parsePosition() << nl;
+
+        if (readScalar(std::string(ts, te-ts), scanTok.svalue))
+        {
+            parser_->parse(TOKEN_OF(NUMBER), &scanTok);
+        }
+        else
+        {
+            driver_.reportFatal
+            (
+                "Error parsing number: " + std::string(ts, te-ts)
+            );
+        }
+
+        driver_.parsePosition() = (p-buf);
+    }}
+	goto st11;
+tr62:
+#line 334 "volumeExprScanner.rl"
+	{te = p;p--;{
+        // Tokenized ".method" - dispatch '.' and "method" separately
+        driver_.parsePosition() = (ts-buf);
+        dispatch_method(driver_, scanTok, word(ts+1, te-ts-1, false));
+        driver_.parsePosition() = (p-buf);
+    }}
+	goto st11;
+tr63:
+#line 367 "volumeExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(LESS); }}
+	goto st11;
+tr64:
+#line 368 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(LESS_EQ); }}
+	goto st11;
+tr65:
+#line 369 "volumeExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(GREATER); }}
+	goto st11;
+tr66:
+#line 370 "volumeExprScanner.rl"
+	{te = p+1;{ EMIT_TOKEN(GREATER_EQ); }}
+	goto st11;
+tr67:
+#line 328 "volumeExprScanner.rl"
+	{te = p;p--;{
+        driver_.parsePosition() = (ts-buf);
+        dispatch_ident(driver_, scanTok, word(ts, te-ts, false));
+        driver_.parsePosition() = (p-buf);
+    }}
+	goto st11;
+tr69:
+#line 1 "NONE"
+	{	switch( act ) {
+	case 26:
+	{{p = ((te))-1;} EMIT_TOKEN(PI); }
+	break;
+	case 27:
+	{{p = ((te))-1;} EMIT_TOKEN(DEG_TO_RAD); }
+	break;
+	case 28:
+	{{p = ((te))-1;} EMIT_TOKEN(RAD_TO_DEG); }
+	break;
+	case 29:
+	{{p = ((te))-1;} EMIT_TOKEN(EXP); }
+	break;
+	case 31:
+	{{p = ((te))-1;} EMIT_TOKEN(LOG10); }
+	break;
+	case 32:
+	{{p = ((te))-1;} EMIT_TOKEN(POW); }
+	break;
+	case 34:
+	{{p = ((te))-1;} EMIT_TOKEN(SQRT); }
+	break;
+	case 35:
+	{{p = ((te))-1;} EMIT_TOKEN(CBRT); }
+	break;
+	case 39:
+	{{p = ((te))-1;} EMIT_TOKEN(ASIN); }
+	break;
+	case 40:
+	{{p = ((te))-1;} EMIT_TOKEN(ACOS); }
+	break;
+	case 42:
+	{{p = ((te))-1;} EMIT_TOKEN(ATAN2); }
+	break;
+	case 43:
+	{{p = ((te))-1;} EMIT_TOKEN(SINH); }
+	break;
+	case 44:
+	{{p = ((te))-1;} EMIT_TOKEN(COSH); }
+	break;
+	case 45:
+	{{p = ((te))-1;} EMIT_TOKEN(TANH); }
+	break;
+	case 47:
+	{{p = ((te))-1;} EMIT_TOKEN(MAGSQR); }
+	break;
+	case 50:
+	{{p = ((te))-1;} EMIT_TOKEN(POS0); }
+	break;
+	case 51:
+	{{p = ((te))-1;} EMIT_TOKEN(NEG0); }
+	break;
+	case 52:
+	{{p = ((te))-1;} EMIT_TOKEN(SIGN); }
+	break;
+	case 53:
+	{{p = ((te))-1;} EMIT_TOKEN(MIN); }
+	break;
+	case 54:
+	{{p = ((te))-1;} EMIT_TOKEN(MAX); }
+	break;
+	case 55:
+	{{p = ((te))-1;} EMIT_TOKEN(AVERAGE); }
+	break;
+	case 56:
+	{{p = ((te))-1;} EMIT_TOKEN(SUM); }
+	break;
+	case 57:
+	{{p = ((te))-1;} EMIT_TOKEN(WEIGHT_AVERAGE); }
+	break;
+	case 58:
+	{{p = ((te))-1;} EMIT_TOKEN(WEIGHT_SUM); }
+	break;
+	case 59:
+	{{p = ((te))-1;} EMIT_TOKEN(RAND); }
+	break;
+	case 60:
+	{{p = ((te))-1;} EMIT_TOKEN(BOOL); }
+	break;
+	case 61:
+	{{p = ((te))-1;} EMIT_TOKEN(VECTOR); }
+	break;
+	case 63:
+	{{p = ((te))-1;} EMIT_TOKEN(SYM_TENSOR); }
+	break;
+	case 64:
+	{{p = ((te))-1;} EMIT_TOKEN(SPH_TENSOR); }
+	break;
+	case 65:
+	{{p = ((te))-1;} EMIT_TOKEN(ZERO); }
+	break;
+	case 66:
+	{{p = ((te))-1;} EMIT_TOKEN(LTRUE); }
+	break;
+	case 67:
+	{{p = ((te))-1;} EMIT_TOKEN(LFALSE); }
+	break;
+	case 69:
+	{{p = ((te))-1;} EMIT_TOKEN(TIME); }
+	break;
+	case 70:
+	{{p = ((te))-1;}
+        driver_.parsePosition() = (ts-buf);
+        dispatch_ident(driver_, scanTok, word(ts, te-ts, false));
+        driver_.parsePosition() = (p-buf);
+    }
+	break;
+	}
+	}
+	goto st11;
+tr83:
+#line 399 "volumeExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(ATAN); }}
+	goto st11;
+tr98:
+#line 395 "volumeExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(COS); }}
+	goto st11;
+tr115:
+#line 388 "volumeExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(LOG); }}
+	goto st11;
+tr122:
+#line 404 "volumeExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(MAG); }}
+	goto st11;
+tr129:
+#line 408 "volumeExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(NEG); }}
+	goto st11;
+tr135:
+#line 407 "volumeExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(POS); }}
+	goto st11;
+tr154:
+#line 394 "volumeExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(SIN); }}
+	goto st11;
+tr170:
+#line 391 "volumeExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(SQR); }}
+	goto st11;
+tr186:
+#line 396 "volumeExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(TAN); }}
+	goto st11;
+tr192:
+#line 425 "volumeExprScanner.rl"
+	{te = p;p--;{ EMIT_TOKEN(TENSOR); }}
+	goto st11;
+st11:
+#line 1 "NONE"
+	{ts = 0;}
+	if ( ++p == pe )
+		goto _test_eof11;
+case 11:
+#line 1 "NONE"
+	{ts = p;}
+#line 900 "volumeExprScanner.cc"
+	switch( (*p) ) {
+		case 32: goto st12;
+		case 33: goto st13;
+		case 34: goto st1;
+		case 37: goto tr16;
+		case 38: goto st14;
+		case 39: goto st3;
+		case 40: goto tr19;
+		case 41: goto tr20;
+		case 42: goto tr21;
+		case 43: goto tr22;
+		case 44: goto tr23;
+		case 45: goto tr24;
+		case 46: goto st15;
+		case 47: goto tr26;
+		case 58: goto tr28;
+		case 60: goto st20;
+		case 61: goto st7;
+		case 62: goto st21;
+		case 63: goto tr32;
+		case 90: goto st24;
+		case 94: goto tr35;
+		case 95: goto st22;
+		case 97: goto st27;
+		case 98: goto st40;
+		case 99: goto st43;
+		case 100: goto st48;
+		case 101: goto st55;
+		case 102: goto st57;
+		case 108: goto st61;
+		case 109: goto st65;
+		case 110: goto st71;
+		case 112: goto st74;
+		case 114: goto st77;
+		case 115: goto st85;
+		case 116: goto st113;
+		case 118: goto st125;
+		case 119: goto st130;
+		case 124: goto st10;
+	}
+	if ( (*p) < 48 ) {
+		if ( 9 <= (*p) && (*p) <= 13 )
+			goto st12;
+	} else if ( (*p) > 57 ) {
+		if ( (*p) > 89 ) {
+			if ( 103 <= (*p) && (*p) <= 122 )
+				goto st22;
+		} else if ( (*p) >= 65 )
+			goto st22;
+	} else
+		goto tr27;
+	goto st0;
+st0:
+cs = 0;
+	goto _out;
+st12:
+	if ( ++p == pe )
+		goto _test_eof12;
+case 12:
+	if ( (*p) == 32 )
+		goto st12;
+	if ( 9 <= (*p) && (*p) <= 13 )
+		goto st12;
+	goto tr52;
+st13:
+	if ( ++p == pe )
+		goto _test_eof13;
+case 13:
+	if ( (*p) == 61 )
+		goto tr54;
+	goto tr53;
+st1:
+	if ( ++p == pe )
+		goto _test_eof1;
+case 1:
+	if ( (*p) == 34 )
+		goto st0;
+	goto st2;
+st2:
+	if ( ++p == pe )
+		goto _test_eof2;
+case 2:
+	if ( (*p) == 34 )
+		goto tr2;
+	goto st2;
+st14:
+	if ( ++p == pe )
+		goto _test_eof14;
+case 14:
+	if ( (*p) == 38 )
+		goto tr56;
+	goto tr55;
+st3:
+	if ( ++p == pe )
+		goto _test_eof3;
+case 3:
+	if ( (*p) == 39 )
+		goto st0;
+	goto st4;
+st4:
+	if ( ++p == pe )
+		goto _test_eof4;
+case 4:
+	if ( (*p) == 39 )
+		goto tr4;
+	goto st4;
+st15:
+	if ( ++p == pe )
+		goto _test_eof15;
+case 15:
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr58;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto st18;
+	} else
+		goto st18;
+	goto tr57;
+tr58:
+#line 1 "NONE"
+	{te = p+1;}
+	goto st16;
+st16:
+	if ( ++p == pe )
+		goto _test_eof16;
+case 16:
+#line 1028 "volumeExprScanner.cc"
+	switch( (*p) ) {
+		case 69: goto st5;
+		case 101: goto st5;
+	}
+	if ( 48 <= (*p) && (*p) <= 57 )
+		goto tr58;
+	goto tr60;
+st5:
+	if ( ++p == pe )
+		goto _test_eof5;
+case 5:
+	switch( (*p) ) {
+		case 43: goto st6;
+		case 45: goto st6;
+	}
+	if ( 48 <= (*p) && (*p) <= 57 )
+		goto st17;
+	goto tr5;
+st6:
+	if ( ++p == pe )
+		goto _test_eof6;
+case 6:
+	if ( 48 <= (*p) && (*p) <= 57 )
+		goto st17;
+	goto tr5;
+st17:
+	if ( ++p == pe )
+		goto _test_eof17;
+case 17:
+	if ( 48 <= (*p) && (*p) <= 57 )
+		goto st17;
+	goto tr60;
+st18:
+	if ( ++p == pe )
+		goto _test_eof18;
+case 18:
+	if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto st18;
+	} else if ( (*p) >= 65 )
+		goto st18;
+	goto tr62;
+tr27:
+#line 1 "NONE"
+	{te = p+1;}
+	goto st19;
+st19:
+	if ( ++p == pe )
+		goto _test_eof19;
+case 19:
+#line 1079 "volumeExprScanner.cc"
+	switch( (*p) ) {
+		case 46: goto tr58;
+		case 69: goto st5;
+		case 101: goto st5;
+	}
+	if ( 48 <= (*p) && (*p) <= 57 )
+		goto tr27;
+	goto tr60;
+st20:
+	if ( ++p == pe )
+		goto _test_eof20;
+case 20:
+	if ( (*p) == 61 )
+		goto tr64;
+	goto tr63;
+st7:
+	if ( ++p == pe )
+		goto _test_eof7;
+case 7:
+	if ( (*p) == 61 )
+		goto tr8;
+	goto st0;
+st21:
+	if ( ++p == pe )
+		goto _test_eof21;
+case 21:
+	if ( (*p) == 61 )
+		goto tr66;
+	goto tr65;
+st22:
+	if ( ++p == pe )
+		goto _test_eof22;
+case 22:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+tr68:
+#line 1 "NONE"
+	{te = p+1;}
+#line 328 "volumeExprScanner.rl"
+	{act = 70;}
+	goto st23;
+tr72:
+#line 1 "NONE"
+	{te = p+1;}
+#line 430 "volumeExprScanner.rl"
+	{act = 65;}
+	goto st23;
+tr78:
+#line 1 "NONE"
+	{te = p+1;}
+#line 398 "volumeExprScanner.rl"
+	{act = 40;}
+	goto st23;
+tr80:
+#line 1 "NONE"
+	{te = p+1;}
+#line 397 "volumeExprScanner.rl"
+	{act = 39;}
+	goto st23;
+tr84:
+#line 1 "NONE"
+	{te = p+1;}
+#line 400 "volumeExprScanner.rl"
+	{act = 42;}
+	goto st23;
+tr89:
+#line 1 "NONE"
+	{te = p+1;}
+#line 416 "volumeExprScanner.rl"
+	{act = 55;}
+	goto st23;
+tr92:
+#line 1 "NONE"
+	{te = p+1;}
+#line 423 "volumeExprScanner.rl"
+	{act = 60;}
+	goto st23;
+tr96:
+#line 1 "NONE"
+	{te = p+1;}
+#line 393 "volumeExprScanner.rl"
+	{act = 35;}
+	goto st23;
+tr99:
+#line 1 "NONE"
+	{te = p+1;}
+#line 402 "volumeExprScanner.rl"
+	{act = 44;}
+	goto st23;
+tr106:
+#line 1 "NONE"
+	{te = p+1;}
+#line 385 "volumeExprScanner.rl"
+	{act = 27;}
+	goto st23;
+tr108:
+#line 1 "NONE"
+	{te = p+1;}
+#line 387 "volumeExprScanner.rl"
+	{act = 29;}
+	goto st23;
+tr112:
+#line 1 "NONE"
+	{te = p+1;}
+#line 432 "volumeExprScanner.rl"
+	{act = 67;}
+	goto st23;
+tr117:
+#line 1 "NONE"
+	{te = p+1;}
+#line 389 "volumeExprScanner.rl"
+	{act = 31;}
+	goto st23;
+tr121:
+#line 1 "NONE"
+	{te = p+1;}
+#line 415 "volumeExprScanner.rl"
+	{act = 54;}
+	goto st23;
+tr125:
+#line 1 "NONE"
+	{te = p+1;}
+#line 405 "volumeExprScanner.rl"
+	{act = 47;}
+	goto st23;
+tr126:
+#line 1 "NONE"
+	{te = p+1;}
+#line 414 "volumeExprScanner.rl"
+	{act = 53;}
+	goto st23;
+tr130:
+#line 1 "NONE"
+	{te = p+1;}
+#line 410 "volumeExprScanner.rl"
+	{act = 51;}
+	goto st23;
+tr131:
+#line 1 "NONE"
+	{te = p+1;}
+#line 384 "volumeExprScanner.rl"
+	{act = 26;}
+	goto st23;
+tr134:
+#line 1 "NONE"
+	{te = p+1;}
+#line 390 "volumeExprScanner.rl"
+	{act = 32;}
+	goto st23;
+tr136:
+#line 1 "NONE"
+	{te = p+1;}
+#line 409 "volumeExprScanner.rl"
+	{act = 50;}
+	goto st23;
+tr144:
+#line 1 "NONE"
+	{te = p+1;}
+#line 386 "volumeExprScanner.rl"
+	{act = 28;}
+	goto st23;
+tr145:
+#line 1 "NONE"
+	{te = p+1;}
+#line 420 "volumeExprScanner.rl"
+	{act = 59;}
+	goto st23;
+tr153:
+#line 1 "NONE"
+	{te = p+1;}
+#line 411 "volumeExprScanner.rl"
+	{act = 52;}
+	goto st23;
+tr155:
+#line 1 "NONE"
+	{te = p+1;}
+#line 401 "volumeExprScanner.rl"
+	{act = 43;}
+	goto st23;
+tr168:
+#line 1 "NONE"
+	{te = p+1;}
+#line 427 "volumeExprScanner.rl"
+	{act = 64;}
+	goto st23;
+tr171:
+#line 1 "NONE"
+	{te = p+1;}
+#line 392 "volumeExprScanner.rl"
+	{act = 34;}
+	goto st23;
+tr172:
+#line 1 "NONE"
+	{te = p+1;}
+#line 417 "volumeExprScanner.rl"
+	{act = 56;}
+	goto st23;
+tr180:
+#line 1 "NONE"
+	{te = p+1;}
+#line 426 "volumeExprScanner.rl"
+	{act = 63;}
+	goto st23;
+tr187:
+#line 1 "NONE"
+	{te = p+1;}
+#line 403 "volumeExprScanner.rl"
+	{act = 45;}
+	goto st23;
+tr195:
+#line 1 "NONE"
+	{te = p+1;}
+#line 434 "volumeExprScanner.rl"
+	{act = 69;}
+	goto st23;
+tr197:
+#line 1 "NONE"
+	{te = p+1;}
+#line 431 "volumeExprScanner.rl"
+	{act = 66;}
+	goto st23;
+tr202:
+#line 1 "NONE"
+	{te = p+1;}
+#line 424 "volumeExprScanner.rl"
+	{act = 61;}
+	goto st23;
+tr215:
+#line 1 "NONE"
+	{te = p+1;}
+#line 418 "volumeExprScanner.rl"
+	{act = 57;}
+	goto st23;
+tr217:
+#line 1 "NONE"
+	{te = p+1;}
+#line 419 "volumeExprScanner.rl"
+	{act = 58;}
+	goto st23;
+st23:
+	if ( ++p == pe )
+		goto _test_eof23;
+case 23:
+#line 1334 "volumeExprScanner.cc"
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr69;
+st24:
+	if ( ++p == pe )
+		goto _test_eof24;
+case 24:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st25;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st25:
+	if ( ++p == pe )
+		goto _test_eof25;
+case 25:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto st26;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st26:
+	if ( ++p == pe )
+		goto _test_eof26;
+case 26:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto tr72;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st27:
+	if ( ++p == pe )
+		goto _test_eof27;
+case 27:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 99: goto st28;
+		case 115: goto st30;
+		case 116: goto st32;
+		case 118: goto st35;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st28:
+	if ( ++p == pe )
+		goto _test_eof28;
+case 28:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st29;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st29:
+	if ( ++p == pe )
+		goto _test_eof29;
+case 29:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 115: goto tr78;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st30:
+	if ( ++p == pe )
+		goto _test_eof30;
+case 30:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 105: goto st31;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st31:
+	if ( ++p == pe )
+		goto _test_eof31;
+case 31:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 110: goto tr80;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st32:
+	if ( ++p == pe )
+		goto _test_eof32;
+case 32:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st33;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st33:
+	if ( ++p == pe )
+		goto _test_eof33;
+case 33:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 110: goto st34;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st34:
+	if ( ++p == pe )
+		goto _test_eof34;
+case 34:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 50: goto tr84;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr83;
+st35:
+	if ( ++p == pe )
+		goto _test_eof35;
+case 35:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st36;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st36:
+	if ( ++p == pe )
+		goto _test_eof36;
+case 36:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto st37;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st37:
+	if ( ++p == pe )
+		goto _test_eof37;
+case 37:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st38;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st38:
+	if ( ++p == pe )
+		goto _test_eof38;
+case 38:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto st39;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st39:
+	if ( ++p == pe )
+		goto _test_eof39;
+case 39:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto tr89;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st40:
+	if ( ++p == pe )
+		goto _test_eof40;
+case 40:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st41;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st41:
+	if ( ++p == pe )
+		goto _test_eof41;
+case 41:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st42;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st42:
+	if ( ++p == pe )
+		goto _test_eof42;
+case 42:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 108: goto tr92;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st43:
+	if ( ++p == pe )
+		goto _test_eof43;
+case 43:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 98: goto st44;
+		case 111: goto st46;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st44:
+	if ( ++p == pe )
+		goto _test_eof44;
+case 44:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto st45;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st45:
+	if ( ++p == pe )
+		goto _test_eof45;
+case 45:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 116: goto tr96;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st46:
+	if ( ++p == pe )
+		goto _test_eof46;
+case 46:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 115: goto st47;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st47:
+	if ( ++p == pe )
+		goto _test_eof47;
+case 47:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 104: goto tr99;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr98;
+st48:
+	if ( ++p == pe )
+		goto _test_eof48;
+case 48:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st49;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st49:
+	if ( ++p == pe )
+		goto _test_eof49;
+case 49:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto st50;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st50:
+	if ( ++p == pe )
+		goto _test_eof50;
+case 50:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 84: goto st51;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st51:
+	if ( ++p == pe )
+		goto _test_eof51;
+case 51:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st52;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st52:
+	if ( ++p == pe )
+		goto _test_eof52;
+case 52:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 82: goto st53;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st53:
+	if ( ++p == pe )
+		goto _test_eof53;
+case 53:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st54;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st54:
+	if ( ++p == pe )
+		goto _test_eof54;
+case 54:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 100: goto tr106;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st55:
+	if ( ++p == pe )
+		goto _test_eof55;
+case 55:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 120: goto st56;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st56:
+	if ( ++p == pe )
+		goto _test_eof56;
+case 56:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 112: goto tr108;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st57:
+	if ( ++p == pe )
+		goto _test_eof57;
+case 57:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st58;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st58:
+	if ( ++p == pe )
+		goto _test_eof58;
+case 58:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 108: goto st59;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st59:
+	if ( ++p == pe )
+		goto _test_eof59;
+case 59:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 115: goto st60;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st60:
+	if ( ++p == pe )
+		goto _test_eof60;
+case 60:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto tr112;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st61:
+	if ( ++p == pe )
+		goto _test_eof61;
+case 61:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st62;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st62:
+	if ( ++p == pe )
+		goto _test_eof62;
+case 62:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto st63;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st63:
+	if ( ++p == pe )
+		goto _test_eof63;
+case 63:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 49: goto st64;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr115;
+st64:
+	if ( ++p == pe )
+		goto _test_eof64;
+case 64:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 48: goto tr117;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 49 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st65:
+	if ( ++p == pe )
+		goto _test_eof65;
+case 65:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st66;
+		case 105: goto st70;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st66:
+	if ( ++p == pe )
+		goto _test_eof66;
+case 66:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto st67;
+		case 120: goto tr121;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st67:
+	if ( ++p == pe )
+		goto _test_eof67;
+case 67:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 83: goto st68;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr122;
+st68:
+	if ( ++p == pe )
+		goto _test_eof68;
+case 68:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 113: goto st69;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st69:
+	if ( ++p == pe )
+		goto _test_eof69;
+case 69:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto tr125;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st70:
+	if ( ++p == pe )
+		goto _test_eof70;
+case 70:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 110: goto tr126;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st71:
+	if ( ++p == pe )
+		goto _test_eof71;
+case 71:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st72;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st72:
+	if ( ++p == pe )
+		goto _test_eof72;
+case 72:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto st73;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st73:
+	if ( ++p == pe )
+		goto _test_eof73;
+case 73:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 48: goto tr130;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 49 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr129;
+st74:
+	if ( ++p == pe )
+		goto _test_eof74;
+case 74:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 105: goto tr131;
+		case 111: goto st75;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st75:
+	if ( ++p == pe )
+		goto _test_eof75;
+case 75:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 115: goto st76;
+		case 119: goto tr134;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st76:
+	if ( ++p == pe )
+		goto _test_eof76;
+case 76:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 48: goto tr136;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 49 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr135;
+st77:
+	if ( ++p == pe )
+		goto _test_eof77;
+case 77:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st78;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st78:
+	if ( ++p == pe )
+		goto _test_eof78;
+case 78:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 100: goto st79;
+		case 110: goto st84;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st79:
+	if ( ++p == pe )
+		goto _test_eof79;
+case 79:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 84: goto st80;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st80:
+	if ( ++p == pe )
+		goto _test_eof80;
+case 80:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st81;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st81:
+	if ( ++p == pe )
+		goto _test_eof81;
+case 81:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 68: goto st82;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st82:
+	if ( ++p == pe )
+		goto _test_eof82;
+case 82:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st83;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st83:
+	if ( ++p == pe )
+		goto _test_eof83;
+case 83:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto tr144;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st84:
+	if ( ++p == pe )
+		goto _test_eof84;
+case 84:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 100: goto tr145;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st85:
+	if ( ++p == pe )
+		goto _test_eof85;
+case 85:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 105: goto st86;
+		case 112: goto st89;
+		case 113: goto st102;
+		case 117: goto st104;
+		case 121: goto st105;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st86:
+	if ( ++p == pe )
+		goto _test_eof86;
+case 86:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto st87;
+		case 110: goto st88;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st87:
+	if ( ++p == pe )
+		goto _test_eof87;
+case 87:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 110: goto tr153;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st88:
+	if ( ++p == pe )
+		goto _test_eof88;
+case 88:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 104: goto tr155;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr154;
+st89:
+	if ( ++p == pe )
+		goto _test_eof89;
+case 89:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 104: goto st90;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st90:
+	if ( ++p == pe )
+		goto _test_eof90;
+case 90:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st91;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st91:
+	if ( ++p == pe )
+		goto _test_eof91;
+case 91:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto st92;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st92:
+	if ( ++p == pe )
+		goto _test_eof92;
+case 92:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 105: goto st93;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st93:
+	if ( ++p == pe )
+		goto _test_eof93;
+case 93:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 99: goto st94;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st94:
+	if ( ++p == pe )
+		goto _test_eof94;
+case 94:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st95;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st95:
+	if ( ++p == pe )
+		goto _test_eof95;
+case 95:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 108: goto st96;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st96:
+	if ( ++p == pe )
+		goto _test_eof96;
+case 96:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 84: goto st97;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st97:
+	if ( ++p == pe )
+		goto _test_eof97;
+case 97:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st98;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st98:
+	if ( ++p == pe )
+		goto _test_eof98;
+case 98:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 110: goto st99;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st99:
+	if ( ++p == pe )
+		goto _test_eof99;
+case 99:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 115: goto st100;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st100:
+	if ( ++p == pe )
+		goto _test_eof100;
+case 100:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st101;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st101:
+	if ( ++p == pe )
+		goto _test_eof101;
+case 101:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto tr168;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st102:
+	if ( ++p == pe )
+		goto _test_eof102;
+case 102:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto st103;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st103:
+	if ( ++p == pe )
+		goto _test_eof103;
+case 103:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 116: goto tr171;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr170;
+st104:
+	if ( ++p == pe )
+		goto _test_eof104;
+case 104:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 109: goto tr172;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st105:
+	if ( ++p == pe )
+		goto _test_eof105;
+case 105:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 109: goto st106;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st106:
+	if ( ++p == pe )
+		goto _test_eof106;
+case 106:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 109: goto st107;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st107:
+	if ( ++p == pe )
+		goto _test_eof107;
+case 107:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 84: goto st108;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st108:
+	if ( ++p == pe )
+		goto _test_eof108;
+case 108:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st109;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st109:
+	if ( ++p == pe )
+		goto _test_eof109;
+case 109:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 110: goto st110;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st110:
+	if ( ++p == pe )
+		goto _test_eof110;
+case 110:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 115: goto st111;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st111:
+	if ( ++p == pe )
+		goto _test_eof111;
+case 111:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st112;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st112:
+	if ( ++p == pe )
+		goto _test_eof112;
+case 112:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto tr180;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st113:
+	if ( ++p == pe )
+		goto _test_eof113;
+case 113:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st114;
+		case 101: goto st116;
+		case 105: goto st121;
+		case 114: goto st123;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st114:
+	if ( ++p == pe )
+		goto _test_eof114;
+case 114:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 110: goto st115;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st115:
+	if ( ++p == pe )
+		goto _test_eof115;
+case 115:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 104: goto tr187;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr186;
+st116:
+	if ( ++p == pe )
+		goto _test_eof116;
+case 116:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 110: goto st117;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st117:
+	if ( ++p == pe )
+		goto _test_eof117;
+case 117:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 115: goto st118;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st118:
+	if ( ++p == pe )
+		goto _test_eof118;
+case 118:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st119;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st119:
+	if ( ++p == pe )
+		goto _test_eof119;
+case 119:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto tr191;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+tr191:
+#line 1 "NONE"
+	{te = p+1;}
+	goto st120;
+st120:
+	if ( ++p == pe )
+		goto _test_eof120;
+case 120:
+#line 3101 "volumeExprScanner.cc"
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 58: goto st8;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr192;
+st8:
+	if ( ++p == pe )
+		goto _test_eof8;
+case 8:
+	if ( (*p) == 58 )
+		goto st9;
+	goto tr9;
+st9:
+	if ( ++p == pe )
+		goto _test_eof9;
+case 9:
+	if ( (*p) == 73 )
+		goto tr11;
+	goto tr9;
+st121:
+	if ( ++p == pe )
+		goto _test_eof121;
+case 121:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 109: goto st122;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st122:
+	if ( ++p == pe )
+		goto _test_eof122;
+case 122:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto tr195;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st123:
+	if ( ++p == pe )
+		goto _test_eof123;
+case 123:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 117: goto st124;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st124:
+	if ( ++p == pe )
+		goto _test_eof124;
+case 124:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto tr197;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st125:
+	if ( ++p == pe )
+		goto _test_eof125;
+case 125:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st126;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st126:
+	if ( ++p == pe )
+		goto _test_eof126;
+case 126:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 99: goto st127;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st127:
+	if ( ++p == pe )
+		goto _test_eof127;
+case 127:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 116: goto st128;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st128:
+	if ( ++p == pe )
+		goto _test_eof128;
+case 128:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 111: goto st129;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st129:
+	if ( ++p == pe )
+		goto _test_eof129;
+case 129:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto tr202;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st130:
+	if ( ++p == pe )
+		goto _test_eof130;
+case 130:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st131;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st131:
+	if ( ++p == pe )
+		goto _test_eof131;
+case 131:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 105: goto st132;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st132:
+	if ( ++p == pe )
+		goto _test_eof132;
+case 132:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto st133;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st133:
+	if ( ++p == pe )
+		goto _test_eof133;
+case 133:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 104: goto st134;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st134:
+	if ( ++p == pe )
+		goto _test_eof134;
+case 134:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 116: goto st135;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st135:
+	if ( ++p == pe )
+		goto _test_eof135;
+case 135:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 65: goto st136;
+		case 83: goto st142;
+		case 95: goto tr68;
+	}
+	if ( (*p) < 66 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st136:
+	if ( ++p == pe )
+		goto _test_eof136;
+case 136:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 118: goto st137;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st137:
+	if ( ++p == pe )
+		goto _test_eof137;
+case 137:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto st138;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st138:
+	if ( ++p == pe )
+		goto _test_eof138;
+case 138:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 114: goto st139;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st139:
+	if ( ++p == pe )
+		goto _test_eof139;
+case 139:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 97: goto st140;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 98 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st140:
+	if ( ++p == pe )
+		goto _test_eof140;
+case 140:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 103: goto st141;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st141:
+	if ( ++p == pe )
+		goto _test_eof141;
+case 141:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 101: goto tr215;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st142:
+	if ( ++p == pe )
+		goto _test_eof142;
+case 142:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 117: goto st143;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st143:
+	if ( ++p == pe )
+		goto _test_eof143;
+case 143:
+	switch( (*p) ) {
+		case 46: goto tr68;
+		case 95: goto tr68;
+		case 109: goto tr217;
+	}
+	if ( (*p) < 65 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr68;
+	} else if ( (*p) > 90 ) {
+		if ( 97 <= (*p) && (*p) <= 122 )
+			goto tr68;
+	} else
+		goto tr68;
+	goto tr67;
+st10:
+	if ( ++p == pe )
+		goto _test_eof10;
+case 10:
+	if ( (*p) == 124 )
+		goto tr12;
+	goto st0;
+	}
+	_test_eof11: cs = 11; goto _test_eof; 
+	_test_eof12: cs = 12; goto _test_eof; 
+	_test_eof13: cs = 13; goto _test_eof; 
+	_test_eof1: cs = 1; goto _test_eof; 
+	_test_eof2: cs = 2; goto _test_eof; 
+	_test_eof14: cs = 14; goto _test_eof; 
+	_test_eof3: cs = 3; goto _test_eof; 
+	_test_eof4: cs = 4; goto _test_eof; 
+	_test_eof15: cs = 15; goto _test_eof; 
+	_test_eof16: cs = 16; goto _test_eof; 
+	_test_eof5: cs = 5; goto _test_eof; 
+	_test_eof6: cs = 6; 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_eof7: cs = 7; 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_eof86: cs = 86; goto _test_eof; 
+	_test_eof87: cs = 87; goto _test_eof; 
+	_test_eof88: cs = 88; goto _test_eof; 
+	_test_eof89: cs = 89; goto _test_eof; 
+	_test_eof90: cs = 90; 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_eof94: cs = 94; goto _test_eof; 
+	_test_eof95: cs = 95; goto _test_eof; 
+	_test_eof96: cs = 96; goto _test_eof; 
+	_test_eof97: cs = 97; goto _test_eof; 
+	_test_eof98: cs = 98; goto _test_eof; 
+	_test_eof99: cs = 99; goto _test_eof; 
+	_test_eof100: cs = 100; goto _test_eof; 
+	_test_eof101: cs = 101; goto _test_eof; 
+	_test_eof102: cs = 102; goto _test_eof; 
+	_test_eof103: cs = 103; goto _test_eof; 
+	_test_eof104: cs = 104; goto _test_eof; 
+	_test_eof105: cs = 105; goto _test_eof; 
+	_test_eof106: cs = 106; goto _test_eof; 
+	_test_eof107: cs = 107; goto _test_eof; 
+	_test_eof108: cs = 108; goto _test_eof; 
+	_test_eof109: cs = 109; goto _test_eof; 
+	_test_eof110: cs = 110; goto _test_eof; 
+	_test_eof111: cs = 111; goto _test_eof; 
+	_test_eof112: cs = 112; goto _test_eof; 
+	_test_eof113: cs = 113; goto _test_eof; 
+	_test_eof114: cs = 114; goto _test_eof; 
+	_test_eof115: cs = 115; goto _test_eof; 
+	_test_eof116: cs = 116; goto _test_eof; 
+	_test_eof117: cs = 117; goto _test_eof; 
+	_test_eof118: cs = 118; goto _test_eof; 
+	_test_eof119: cs = 119; goto _test_eof; 
+	_test_eof120: cs = 120; goto _test_eof; 
+	_test_eof8: cs = 8; goto _test_eof; 
+	_test_eof9: cs = 9; goto _test_eof; 
+	_test_eof121: cs = 121; goto _test_eof; 
+	_test_eof122: cs = 122; goto _test_eof; 
+	_test_eof123: cs = 123; goto _test_eof; 
+	_test_eof124: cs = 124; goto _test_eof; 
+	_test_eof125: cs = 125; goto _test_eof; 
+	_test_eof126: cs = 126; goto _test_eof; 
+	_test_eof127: cs = 127; goto _test_eof; 
+	_test_eof128: cs = 128; goto _test_eof; 
+	_test_eof129: cs = 129; goto _test_eof; 
+	_test_eof130: cs = 130; goto _test_eof; 
+	_test_eof131: cs = 131; goto _test_eof; 
+	_test_eof132: cs = 132; goto _test_eof; 
+	_test_eof133: cs = 133; goto _test_eof; 
+	_test_eof134: cs = 134; goto _test_eof; 
+	_test_eof135: cs = 135; goto _test_eof; 
+	_test_eof136: cs = 136; goto _test_eof; 
+	_test_eof137: cs = 137; goto _test_eof; 
+	_test_eof138: cs = 138; goto _test_eof; 
+	_test_eof139: cs = 139; goto _test_eof; 
+	_test_eof140: cs = 140; goto _test_eof; 
+	_test_eof141: cs = 141; goto _test_eof; 
+	_test_eof142: cs = 142; goto _test_eof; 
+	_test_eof143: cs = 143; goto _test_eof; 
+	_test_eof10: cs = 10; goto _test_eof; 
+
+	_test_eof: {}
+	if ( p == eof )
+	{
+	switch ( cs ) {
+	case 12: goto tr52;
+	case 13: goto tr53;
+	case 14: goto tr55;
+	case 15: goto tr57;
+	case 16: goto tr60;
+	case 5: goto tr5;
+	case 6: goto tr5;
+	case 17: goto tr60;
+	case 18: goto tr62;
+	case 19: goto tr60;
+	case 20: goto tr63;
+	case 21: goto tr65;
+	case 22: goto tr67;
+	case 23: goto tr69;
+	case 24: goto tr67;
+	case 25: goto tr67;
+	case 26: goto tr67;
+	case 27: goto tr67;
+	case 28: goto tr67;
+	case 29: goto tr67;
+	case 30: goto tr67;
+	case 31: goto tr67;
+	case 32: goto tr67;
+	case 33: goto tr67;
+	case 34: goto tr83;
+	case 35: goto tr67;
+	case 36: goto tr67;
+	case 37: goto tr67;
+	case 38: goto tr67;
+	case 39: goto tr67;
+	case 40: goto tr67;
+	case 41: goto tr67;
+	case 42: goto tr67;
+	case 43: goto tr67;
+	case 44: goto tr67;
+	case 45: goto tr67;
+	case 46: goto tr67;
+	case 47: goto tr98;
+	case 48: goto tr67;
+	case 49: goto tr67;
+	case 50: goto tr67;
+	case 51: goto tr67;
+	case 52: goto tr67;
+	case 53: goto tr67;
+	case 54: goto tr67;
+	case 55: goto tr67;
+	case 56: goto tr67;
+	case 57: goto tr67;
+	case 58: goto tr67;
+	case 59: goto tr67;
+	case 60: goto tr67;
+	case 61: goto tr67;
+	case 62: goto tr67;
+	case 63: goto tr115;
+	case 64: goto tr67;
+	case 65: goto tr67;
+	case 66: goto tr67;
+	case 67: goto tr122;
+	case 68: goto tr67;
+	case 69: goto tr67;
+	case 70: goto tr67;
+	case 71: goto tr67;
+	case 72: goto tr67;
+	case 73: goto tr129;
+	case 74: goto tr67;
+	case 75: goto tr67;
+	case 76: goto tr135;
+	case 77: goto tr67;
+	case 78: goto tr67;
+	case 79: goto tr67;
+	case 80: goto tr67;
+	case 81: goto tr67;
+	case 82: goto tr67;
+	case 83: goto tr67;
+	case 84: goto tr67;
+	case 85: goto tr67;
+	case 86: goto tr67;
+	case 87: goto tr67;
+	case 88: goto tr154;
+	case 89: goto tr67;
+	case 90: goto tr67;
+	case 91: goto tr67;
+	case 92: goto tr67;
+	case 93: goto tr67;
+	case 94: goto tr67;
+	case 95: goto tr67;
+	case 96: goto tr67;
+	case 97: goto tr67;
+	case 98: goto tr67;
+	case 99: goto tr67;
+	case 100: goto tr67;
+	case 101: goto tr67;
+	case 102: goto tr67;
+	case 103: goto tr170;
+	case 104: goto tr67;
+	case 105: goto tr67;
+	case 106: goto tr67;
+	case 107: goto tr67;
+	case 108: goto tr67;
+	case 109: goto tr67;
+	case 110: goto tr67;
+	case 111: goto tr67;
+	case 112: goto tr67;
+	case 113: goto tr67;
+	case 114: goto tr67;
+	case 115: goto tr186;
+	case 116: goto tr67;
+	case 117: goto tr67;
+	case 118: goto tr67;
+	case 119: goto tr67;
+	case 120: goto tr192;
+	case 8: goto tr9;
+	case 9: goto tr9;
+	case 121: goto tr67;
+	case 122: goto tr67;
+	case 123: goto tr67;
+	case 124: goto tr67;
+	case 125: goto tr67;
+	case 126: goto tr67;
+	case 127: goto tr67;
+	case 128: goto tr67;
+	case 129: goto tr67;
+	case 130: goto tr67;
+	case 131: goto tr67;
+	case 132: goto tr67;
+	case 133: goto tr67;
+	case 134: goto tr67;
+	case 135: goto tr67;
+	case 136: goto tr67;
+	case 137: goto tr67;
+	case 138: goto tr67;
+	case 139: goto tr67;
+	case 140: goto tr67;
+	case 141: goto tr67;
+	case 142: goto tr67;
+	case 143: goto tr67;
+	}
+	}
+
+	_out: {}
+	}
+
+#line 671 "volumeExprScanner.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, nullptr);
+    parser_->stop();
+
+    // Restore debug value
+    debug = oldDebug;
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/expressions/volume/volumeExprScanner.rl b/src/finiteVolume/expressions/volume/volumeExprScanner.rl
new file mode 100644
index 0000000000000000000000000000000000000000..0b198b10af6ec960b5bbcdc1006ffc7c9d885c9b
--- /dev/null
+++ b/src/finiteVolume/expressions/volume/volumeExprScanner.rl
@@ -0,0 +1,694 @@
+/*--------------------------------*- 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 volume field expressions
+
+\*---------------------------------------------------------------------------*/
+
+#include "volumeExprScanner.H"
+#include "volumeExprDriver.H"
+#include "volumeExprLemonParser.h"
+#include "volumeExprParser.H"
+#include "Enum.H"
+#include "macros.H"
+
+#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+
+// Debugging to stderr
+#undef  DebugInfo
+#define DebugInfo if (debug) InfoErr
+
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+//- Paste token prefix
+#define TOKEN_OF(T)         TOK_##T
+
+//- An {int, c_str} enum pairing
+#define TOKEN_PAIR(Name,T)  { TOKEN_OF(T), Name }
+
+// Special handling for these known (stashed) look-back types
+static const Enum<int> lookBehindTokenEnums
+({
+    TOKEN_PAIR("cellSet", CSET),
+    TOKEN_PAIR("faceSet", FSET),
+    TOKEN_PAIR("pointSet", PSET),
+    TOKEN_PAIR("cellZone", CZONE),
+    TOKEN_PAIR("faceZone", FZONE),
+    TOKEN_PAIR("pointZone", PZONE),
+});
+
+#define HAS_LOOKBEHIND_TOKENS
+
+
+// Special handling of predefined method types. Eg, .x(), .y(), ...
+static const Enum<int> fieldMethodEnums
+({
+    TOKEN_PAIR("x", CMPT_X),
+    TOKEN_PAIR("y", CMPT_Y),
+    TOKEN_PAIR("z", CMPT_Z),
+    TOKEN_PAIR("xx", CMPT_XX),
+    TOKEN_PAIR("xy", CMPT_XY),
+    TOKEN_PAIR("xz", CMPT_XZ),
+    TOKEN_PAIR("yx", CMPT_YX),
+    TOKEN_PAIR("yy", CMPT_YY),
+    TOKEN_PAIR("yz", CMPT_YZ),
+    TOKEN_PAIR("zx", CMPT_ZX),
+    TOKEN_PAIR("zy", CMPT_ZY),
+    TOKEN_PAIR("zz", CMPT_ZZ),
+    TOKEN_PAIR("ii", CMPT_II),
+    TOKEN_PAIR("diag", DIAG),   /* tensors only */
+    TOKEN_PAIR("T", TRANSPOSE), /* tensors only */
+});
+
+
+// Known field-token types
+static const Enum<int> fieldTokenEnums
+({
+#ifdef TOK_SCALAR_ID
+    TOKEN_PAIR(volScalarField::typeName.c_str(), SCALAR_ID),
+    TOKEN_PAIR(volVectorField::typeName.c_str(), VECTOR_ID),
+    TOKEN_PAIR(volTensorField::typeName.c_str(), TENSOR_ID),
+    TOKEN_PAIR(volSymmTensorField::typeName.c_str(), SYM_TENSOR_ID),
+    TOKEN_PAIR(volSphericalTensorField::typeName.c_str(), SPH_TENSOR_ID),
+#else
+#error TOK_SCALAR_ID not defined
+#endif
+#ifdef TOK_SSCALAR_ID
+    TOKEN_PAIR(surfaceScalarField::typeName.c_str(), SSCALAR_ID),
+    TOKEN_PAIR(surfaceVectorField::typeName.c_str(), SVECTOR_ID),
+    TOKEN_PAIR(surfaceTensorField::typeName.c_str(), STENSOR_ID),
+    TOKEN_PAIR(surfaceSymmTensorField::typeName.c_str(), SSYM_TENSOR_ID),
+    TOKEN_PAIR(surfaceSphericalTensorField::typeName.c_str(), SSPH_TENSOR_ID),
+#else
+#warning TOK_SSCALAR_ID not defined
+#endif
+#ifdef TOK_PSCALAR_ID
+    TOKEN_PAIR(pointScalarField::typeName.c_str(), PSCALAR_ID),
+    TOKEN_PAIR(pointVectorField::typeName.c_str(), PVECTOR_ID),
+    TOKEN_PAIR(pointTensorField::typeName.c_str(), PTENSOR_ID),
+    TOKEN_PAIR(pointSymmTensorField::typeName.c_str(), PSYM_TENSOR_ID),
+    TOKEN_PAIR(pointSphericalTensorField::typeName.c_str(), PSPH_TENSOR_ID),
+#else
+#warning TOK_PSCALAR_ID not defined
+#endif
+});
+
+
+// Simple compile-time function name declarations.
+// Useful for handling driver-specific dispatching, or functions that
+// are not universally available.
+static const Enum<int> funcTokenEnums
+({
+#ifdef TOK_FLOOR
+    TOKEN_PAIR("floor", FLOOR),
+    TOKEN_PAIR("ceil", CEIL),
+    TOKEN_PAIR("round", ROUND),
+#endif
+#ifdef TOK_HYPOT  /* Can use hypot? */
+    TOKEN_PAIR("hypot", HYPOT),
+#endif
+
+    // Already parsed as function: TOKEN_PAIR("pos", CELL_CENTRE),
+
+    TOKEN_PAIR("point", POINT_EXPR), // Point value
+    TOKEN_PAIR("face", FACE_EXPR),   // Face value, Face areaNormal
+
+    TOKEN_PAIR("cellToPoint", CELL_TO_POINT),
+    TOKEN_PAIR("cellToFace",  CELL_TO_FACE),
+    TOKEN_PAIR("pointToCell", POINT_TO_CELL),
+
+    TOKEN_PAIR("area", FACE_AREA),
+    TOKEN_PAIR("vol",  CELL_VOLUME),
+
+    TOKEN_PAIR("fpos", FACE_CENTRE),
+    TOKEN_PAIR("pts", POINTS),
+});
+
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Classifying token type based on an identifier name is indeed ugly.
+//
+// 1)
+//   Handle special cases (eg, cellSet,...) first that have been tagged
+//   as expected content with the stashed "look-behind" token.
+//   Handle not-found errors here directly.
+//
+// 2)
+//   Fallback to determining which field-type (volScalarField etc) the name
+//   corresponds to.
+//   Handle not-found errors by return -1.
+//
+static int driverTokenType
+(
+    const expressions::volumeExpr::parseDriver& driver_,
+    const word& ident
+)
+{
+    // Get stashed "look-behind" to decide what type of identifier we expect
+    const int lookBehind = driver_.resetStashedTokenId();
+
+    if (lookBehind && lookBehindTokenEnums.found(lookBehind))
+    {
+        bool good = false;
+
+        switch (lookBehind)
+        {
+            case TOK_CSET : good = driver_.isCellSet(ident); break;
+            case TOK_FSET : good = driver_.isFaceSet(ident); break;
+            case TOK_PSET : good = driver_.isPointSet(ident); break;
+            case TOK_CZONE : good = driver_.isCellZone(ident); break;
+            case TOK_FZONE : good = driver_.isFaceZone(ident); break;
+            case TOK_PZONE : good = driver_.isPointZone(ident); break;
+        }
+
+        if (good)
+        {
+            return TOK_IDENTIFIER;
+        }
+
+        // Fatal
+        driver_.reportFatal
+        (
+            "Error no " + lookBehindTokenEnums.get(lookBehind) + ": " + ident
+        );
+
+        return -2;  // Extra safety
+    }
+
+    // Surface variables - distinguish from volume by size
+    #ifdef TOK_SSCALAR_ID
+    {
+        const label len = driver_.mesh().nInternalFaces();
+
+        #undef checkFieldToken
+        #define checkFieldToken(TokType, Type)                                \
+        if (driver_.isVariable<Type>(ident, false, len))                      \
+        {                                                                     \
+            return TokType;                                                   \
+        }
+
+        checkFieldToken(TOK_SSCALAR_ID, scalar);
+        checkFieldToken(TOK_SVECTOR_ID, vector);
+        checkFieldToken(TOK_SSYM_TENSOR_ID, symmTensor);
+        checkFieldToken(TOK_SSPH_TENSOR_ID, sphericalTensor);
+        checkFieldToken(TOK_STENSOR_ID, tensor);
+    }
+    #endif
+
+    // Point variables
+    #ifdef TOK_PSCALAR_ID
+    {
+        #undef checkFieldToken
+        #define checkFieldToken(TokType, Type)                                \
+        if (driver_.isVariable<Type>(ident, true))                            \
+        {                                                                     \
+            return TokType;                                                   \
+        }
+
+        checkFieldToken(TOK_PSCALAR_ID, scalar);
+        checkFieldToken(TOK_PVECTOR_ID, vector);
+        checkFieldToken(TOK_PSPH_TENSOR_ID, sphericalTensor);
+        checkFieldToken(TOK_PSYM_TENSOR_ID, symmTensor);
+        checkFieldToken(TOK_PTENSOR_ID, tensor);
+    }
+    #endif
+
+    // Volume variables
+    #ifdef TOK_SCALAR_ID
+    {
+        #undef checkFieldToken
+        #define checkFieldToken(TokType, Type)                                \
+        if (driver_.isVariable<Type>(ident, false))                           \
+        {                                                                     \
+            return TokType;                                                   \
+        }
+
+        checkFieldToken(TOK_SCALAR_ID, scalar);
+        checkFieldToken(TOK_VECTOR_ID, vector);
+        checkFieldToken(TOK_SPH_TENSOR_ID, sphericalTensor);
+        checkFieldToken(TOK_SYM_TENSOR_ID, symmTensor);
+        checkFieldToken(TOK_TENSOR_ID, tensor);
+    }
+    #endif
+
+    #undef checkFieldToken
+
+    // Check registered fields and/or disk-files
+    {
+        const word fieldType(driver_.getFieldClassName(ident));
+
+        int tokType = fieldTokenEnums.get(fieldType, -1);
+
+        if (tokType > 0)
+        {
+            return tokType;
+        }
+    }
+
+    return -1;
+}
+
+} // End anonymous namespace
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+// 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
+
+#define EMIT_TOKEN(T)                                                         \
+    driver_.parsePosition() = (ts-buf);                                       \
+    DebugInfo<< STRINGIFY(T) << ": " << driver_.parsePosition() << nl;        \
+    parser_->parse(TOKEN_OF(T), nullptr);                                     \
+    driver_.parsePosition() = (p-buf);
+
+
+%%{
+    machine volumeExpr;
+    write   data;
+
+    action emit_number {
+        driver_.parsePosition() = (ts-buf);
+
+        DebugInfo
+            << "Number:" << std::string(ts, te-ts).c_str()
+            << " at " << driver_.parsePosition() << nl;
+
+        if (readScalar(std::string(ts, te-ts), scanTok.svalue))
+        {
+            parser_->parse(TOKEN_OF(NUMBER), &scanTok);
+        }
+        else
+        {
+            driver_.reportFatal
+            (
+                "Error parsing number: " + std::string(ts, te-ts)
+            );
+        }
+
+        driver_.parsePosition() = (p-buf);
+    }
+
+    action emit_ident {
+        driver_.parsePosition() = (ts-buf);
+        dispatch_ident(driver_, scanTok, word(ts, te-ts, false));
+        driver_.parsePosition() = (p-buf);
+    }
+
+    action emit_method {
+        // Tokenized ".method" - dispatch '.' and "method" separately
+        driver_.parsePosition() = (ts-buf);
+        dispatch_method(driver_, scanTok, word(ts+1, te-ts-1, false));
+        driver_.parsePosition() = (p-buf);
+    }
+
+    decimal = ((digit* '.' digit+) | (digit+ '.'?)) ;
+    number  = ((digit+ | decimal) ([Ee][\-+]? digit+)?) ;
+    ident   = ((alpha|'_') . ((alnum|[._])**)) ;
+    dquoted = '"' [^\"]+ '"' ;
+    squoted = "'" [^\']+ "'" ;
+
+
+    ## 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(DOT); };
+    '/'  =>{ EMIT_TOKEN(DIVIDE); };
+    '?'  =>{ 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); };
+    '&'  =>{ EMIT_TOKEN(BIT_AND); };
+## Not needed?  '|'  =>{ EMIT_TOKEN(BIT_OK); };
+    '^'  =>{ EMIT_TOKEN(BIT_XOR); };
+
+    ## Some '.method' - Error if unknown
+    '.' alpha+ => emit_method;
+
+
+    ## 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); };
+    "sinh"      =>{ EMIT_TOKEN(SINH); };
+    "cosh"      =>{ EMIT_TOKEN(COSH); };
+    "tanh"      =>{ EMIT_TOKEN(TANH); };
+    "mag"       =>{ EMIT_TOKEN(MAG); };
+    "magSqr"    =>{ EMIT_TOKEN(MAGSQR); };
+
+    "pos"       =>{ EMIT_TOKEN(POS); };
+    "neg"       =>{ EMIT_TOKEN(NEG); };
+    "pos0"      =>{ EMIT_TOKEN(POS0); };
+    "neg0"      =>{ EMIT_TOKEN(NEG0); };
+    "sign"      =>{ EMIT_TOKEN(SIGN); };
+
+    ## Reductions, or other special functions
+    "min"       =>{ EMIT_TOKEN(MIN); };
+    "max"       =>{ EMIT_TOKEN(MAX); };
+    "average"   =>{ EMIT_TOKEN(AVERAGE); };
+    "sum"       =>{ EMIT_TOKEN(SUM); };
+    "weightAverage" =>{ EMIT_TOKEN(WEIGHT_AVERAGE); };
+    "weightSum" =>{ EMIT_TOKEN(WEIGHT_SUM); };
+    "rand"      =>{ EMIT_TOKEN(RAND); };
+
+    ## Types
+    "bool"      =>{ EMIT_TOKEN(BOOL); };
+    "vector"    =>{ EMIT_TOKEN(VECTOR); };
+    "tensor"    =>{ EMIT_TOKEN(TENSOR); };
+    "symmTensor" =>{ EMIT_TOKEN(SYM_TENSOR); };
+    "sphericalTensor" =>{ EMIT_TOKEN(SPH_TENSOR); };
+
+    ## Single value (constants, etc)
+    "Zero"      =>{ EMIT_TOKEN(ZERO); };
+    "true"      =>{ EMIT_TOKEN(LTRUE); };
+    "false"     =>{ EMIT_TOKEN(LFALSE); };
+    "tensor::I" =>{ EMIT_TOKEN(UNIT_TENSOR); };
+    "time"      =>{ EMIT_TOKEN(TIME); };
+
+    ## Identifier (field, etc - error if unknown)
+    ## Handle 'bare' names and single/double quoted ones
+    ident       => emit_ident;
+    dquoted     => emit_ident;
+    squoted     => emit_ident;
+
+    space*;
+    *|;
+}%%
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::expressions::volumeExpr::scanner::~scanner()
+{
+    if (parser_)
+    {
+        delete parser_;
+    }
+}
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+bool Foam::expressions::volumeExpr::scanner::dispatch_method
+(
+    const parseDriver& driver_,
+    scanToken& scanTok,
+    word&& ident
+) const
+{
+    if (ident[0] == '.')
+    {
+        ident.erase(0, 1);
+    }
+
+    DebugInfo
+        << "Method:" << ident
+        << " at " << driver_.parsePosition() << nl;
+
+    const int methType = fieldMethodEnums.get(ident, -1);
+
+    if (methType > 0)
+    {
+        // Dispatch '.' and "method" separately
+        parser_->parse(TOK_DOT, nullptr);
+        parser_->parse(methType, nullptr);
+
+        return true;
+    }
+
+    driver_.reportFatal("Unknown method: " + ident);
+    return false;
+}
+
+
+bool Foam::expressions::volumeExpr::scanner::dispatch_ident
+(
+    const parseDriver& driver_,
+    scanToken& scanTok,
+    word&& ident
+) const
+{
+    int tokType = -1;
+
+    const bool quoted =
+    (
+        (ident.front() == '"' || ident.front() == '\'')
+     && (ident.front() == ident.back())
+    );
+
+    if (quoted)
+    {
+        ident.erase(ident.size()-1);
+        ident.erase(0, 1);
+    }
+    else
+    {
+        // Check for function name
+        tokType = funcTokenEnums.get(ident, -1);
+
+        if (tokType > 0)
+        {
+            DebugInfo
+                << "Emit:" << ident << " function:"
+                << parser_->nameOfToken(tokType) << nl;
+
+            parser_->parse(tokType, nullptr);
+            return true;
+        }
+
+        #ifdef HAS_LOOKBEHIND_TOKENS
+        // Specials such "cset" also reset the look-behind
+        tokType = lookBehindTokenEnums.get(ident, -1);
+
+        if (tokType > 0)
+        {
+            DebugInfo
+                << "Emit:" << ident << " as look-behind:"
+                << parser_->nameOfToken(tokType) << nl;
+
+            driver_.resetStashedTokenId(tokType);
+            parser_->parse(tokType, nullptr);
+            return true;
+        }
+        #endif
+    }
+
+
+    // Can also peek at stashed "look-behind"
+    // const int lookBehind = driver_.stashedTokenId();
+
+    tokType = driverTokenType(driver_, ident);
+
+    if (tokType > 0)
+    {
+        DebugInfo
+            << "Emit:" << ident << " token:"
+            << parser_->nameOfToken(tokType) << nl;
+
+        scanTok.name = new Foam::word(std::move(ident));
+        parser_->parse(tokType, &scanTok);
+
+        return true;
+    }
+
+
+    // Not found? Attempt to strip off '.x' endings etc,
+    // but not when quoted
+
+    const auto dot = ident.rfind('.');
+    const int methType =
+    (
+        quoted || dot == std::string::npos
+      ? -1
+      : fieldMethodEnums.get(ident.substr(dot+1), -1)
+    );
+
+    if
+    (
+        methType > 0
+     && (tokType = driverTokenType(driver_, ident.substr(0, dot))) > 0
+    )
+    {
+        DebugInfo
+            << "Emit:" << ident.substr(0, dot).c_str() << " token:"
+            << parser_->nameOfToken(tokType) << " with "
+            << ident.substr(dot).c_str() << " token:"
+            << parser_->nameOfToken(methType) << nl;
+
+        // The field (before the ".")
+        ident.erase(dot);
+
+        scanTok.name = new Foam::word(std::move(ident));
+        parser_->parse(tokType, &scanTok);
+
+        // Dispatch '.' and "method" separately
+        parser_->parse(TOK_DOT, nullptr);
+        parser_->parse(methType, nullptr);
+
+        return true;
+    }
+
+    driver_.reportFatal
+    (
+        "Object " + ident + " does not exist or wrong type"
+    );
+
+    return false;
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::expressions::volumeExpr::scanner::process
+(
+    const std::string& str,
+    size_t strBeg,
+    size_t strLen,
+    parseDriver& driver_
+)
+{
+    // Save debug value
+    const int oldDebug = debug;
+
+    if (driver_.debugScanner())
+    {
+        debug |= 4;
+    }
+
+    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_);
+
+    // Scan token type
+    scanToken scanTok;
+
+    // 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, nullptr);
+    parser_->stop();
+
+    // Restore debug value
+    debug = oldDebug;
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/T b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/T
new file mode 100644
index 0000000000000000000000000000000000000000..166a338f306e8136b3630a02fe3502e1ff5b4e0e
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/T
@@ -0,0 +1,55 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       volScalarField;
+    location    "0";
+    object      T;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+dimensions      [0 0 0 1 0 0 0];
+
+internalField   uniform 300;
+
+boundaryField
+{
+    inlet
+    {
+        type            outletMappedUniformInletHeatAddition;
+        outletPatch     outlet1;
+        Q               5;     // Heat addition in W
+        TMin            300;
+        TMax            500;
+        value           $internalField;
+    }
+
+    outlet1
+    {
+        type            inletOutlet;
+        inletValue      $internalField;
+        value           $internalField;
+    }
+
+    outlet2
+    {
+        type            inletOutlet;
+        inletValue      $internalField;
+        value           $internalField;
+    }
+
+    defaultFaces
+    {
+        type            zeroGradient;
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/U b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/U
new file mode 100644
index 0000000000000000000000000000000000000000..60b7f0d8f09802e940157c7fce58afd966763be1
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/U
@@ -0,0 +1,50 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       volVectorField;
+    object      U;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+dimensions      [0 1 -1 0 0 0 0];
+
+internalField   uniform (0 0 0);
+
+boundaryField
+{
+    inlet
+    {
+        type            pressureInletOutletVelocity;
+        value           uniform (0 0 0);
+    }
+
+    outlet1
+    {
+        type            inletOutlet;
+        inletValue      uniform (0 0 0);
+        value           uniform (0 0 0);
+    }
+
+    outlet2
+    {
+        type            inletOutlet;
+        inletValue      uniform (0 0 0);
+        value           uniform (0 0 0);
+    }
+
+    defaultFaces
+    {
+        type            fixedValue;
+        value           uniform (0 0 0);
+    }
+}
+
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/alphat b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/alphat
new file mode 100644
index 0000000000000000000000000000000000000000..6471c6900653ead97535beeb8a9b72e712819532
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/alphat
@@ -0,0 +1,50 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       volScalarField;
+    location    "0";
+    object      alphat;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+dimensions      [1 -1 -1 0 0 0 0];
+
+internalField   uniform 1e-3;
+
+boundaryField
+{
+    inlet
+    {
+        type            calculated;
+        value           uniform 0;
+    }
+
+    outlet1
+    {
+        type            calculated;
+        value           uniform 0;
+    }
+
+    outlet2
+    {
+        type            calculated;
+        value           uniform 0;
+    }
+
+    defaultFaces
+    {
+        type            compressible::alphatWallFunction;
+        value           $internalField;
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/epsilon b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/epsilon
new file mode 100644
index 0000000000000000000000000000000000000000..3f577a7999de7891bb56bf8470d29e272e7c3415
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/epsilon
@@ -0,0 +1,53 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       volScalarField;
+    location    "0";
+    object      epsilon;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+dimensions      [0 2 -3 0 0 0 0];
+
+internalField   uniform 200;
+
+boundaryField
+{
+    inlet
+    {
+        type            turbulentMixingLengthDissipationRateInlet;
+        mixingLength    0.01;       // 1cm - half channel height
+        value           $internalField;
+    }
+
+    outlet1
+    {
+        type            inletOutlet;
+        inletValue      $internalField;
+        value           $internalField;
+    }
+
+    outlet2
+    {
+        type            inletOutlet;
+        inletValue      $internalField;
+        value           $internalField;
+    }
+
+    defaultFaces
+    {
+        type            epsilonWallFunction;
+        value           $internalField;
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/k b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/k
new file mode 100644
index 0000000000000000000000000000000000000000..1e041613e9057b1f4ad9a779e57b932db784ff2f
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/k
@@ -0,0 +1,51 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       volScalarField;
+    location    "0";
+    object      k;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+dimensions      [0 2 -2 0 0 0 0];
+
+internalField   uniform 1;
+
+boundaryField
+{
+    inlet
+    {
+        type            turbulentIntensityKineticEnergyInlet;
+        intensity       0.05;       // 5% turbulent intensity
+        value           $internalField;
+    }
+
+    outlet1
+    {
+        type            inletOutlet;
+        inletValue      $internalField;
+    }
+
+    outlet2
+    {
+        type            inletOutlet;
+        inletValue      $internalField;
+    }
+
+    defaultFaces
+    {
+        type            kqRWallFunction;
+        value           $internalField;
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/nuTilda b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/nuTilda
new file mode 100644
index 0000000000000000000000000000000000000000..dc39c62165a512a39cf3d3b12ba3bc6e1896d55d
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/nuTilda
@@ -0,0 +1,44 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       volScalarField;
+    object      nuTilda;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+dimensions      [0 2 -1 0 0 0 0];
+
+internalField   uniform 0;
+
+boundaryField
+{
+    inlet
+    {
+        type            zeroGradient;
+    }
+
+    outlet1
+    {
+        type            zeroGradient;
+    }
+
+    outlet2
+    {
+        type            zeroGradient;
+    }
+
+    defaultFaces
+    {
+        type            zeroGradient;
+    }
+}
+
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/nut b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/nut
new file mode 100644
index 0000000000000000000000000000000000000000..1552d4d85c8c8eab272c513ea53c3e17cf443d40
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/nut
@@ -0,0 +1,50 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       volScalarField;
+    location    "0";
+    object      nut;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+dimensions      [0 2 -1 0 0 0 0];
+
+internalField   uniform 0;
+
+boundaryField
+{
+    inlet
+    {
+        type            calculated;
+        value           uniform 0;
+    }
+
+    outlet1
+    {
+        type            calculated;
+        value           uniform 0;
+    }
+
+    outlet2
+    {
+        type            calculated;
+        value           uniform 0;
+    }
+
+    defaultFaces
+    {
+        type            nutkWallFunction;
+        value           uniform 0;
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/p b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/p
new file mode 100644
index 0000000000000000000000000000000000000000..1e896e6508e17bb1e29aae6b71da545c7e5ae59f
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/0/p
@@ -0,0 +1,71 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       volScalarField;
+    object      p;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+dimensions      [1 -1 -2 0 0 0 0];
+
+internalField   uniform 1e5;
+
+boundaryField
+{
+    inlet
+    {
+        type            uniformTotalPressure;
+        gamma           1.2;
+
+        p0              table
+        (
+            (0 1e5)
+            (1 1.4e5)
+        );
+    }
+
+    outlet1
+    {
+        type            fixedValue;
+        value           $internalField;
+    }
+
+    outlet1
+    {
+        type            exprFixedValue;
+        value           $internalField;
+
+        valueExpr      "0.5*(pInlet + pOutlet2)";
+        variables
+        (
+            "pInlet{inlet} = weightAverage(p)"
+            "pOutlet2{outlet2} = p"
+        );
+
+        // debug           true;
+        // debugScanner    true;
+        // debugParser     true;
+    }
+
+
+    outlet2
+    {
+        type            fixedValue;
+        value           $internalField;
+    }
+
+    defaultFaces
+    {
+        type            zeroGradient;
+    }
+}
+
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/README.txt b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5c812a1e4bd27920bb8d5b4f8c8360d0d9432175
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/README.txt
@@ -0,0 +1,4 @@
+15/8/8 Simple T-junction. Inlet on left, one outlet at bottom, one at top.
+To test multiple outlets.
+
+Enable debugScanner/debugParser for a verbose view of the parsing engine.
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/constant/fvOptions b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/constant/fvOptions
new file mode 100644
index 0000000000000000000000000000000000000000..ce5c49527bb7f8ea02ba26a3e15fc1fe2a4dee06
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/constant/fvOptions
@@ -0,0 +1,22 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    location    "constant";
+    object      fvOptions;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+viscousDissipation
+{
+    type            viscousDissipation;
+    enabled         true;
+}
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/constant/thermophysicalProperties b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/constant/thermophysicalProperties
new file mode 100644
index 0000000000000000000000000000000000000000..356422609d9fa0054e4c4589cade640aa1516b58
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/constant/thermophysicalProperties
@@ -0,0 +1,48 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    location    "constant";
+    object      thermophysicalProperties;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+thermoType
+{
+    type            hePsiThermo;
+    mixture         pureMixture;
+    transport       sutherland;
+    thermo          hConst;
+    equationOfState perfectGas;
+    specie          specie;
+    energy          sensibleEnthalpy;
+}
+
+mixture
+{
+    specie
+    {
+        molWeight   28.9;
+    }
+    thermodynamics
+    {
+        Cp          1007;
+        Hf          0;
+    }
+    transport
+    {
+        As          1.4792e-06;
+        Ts          116;
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/constant/turbulenceProperties b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/constant/turbulenceProperties
new file mode 100644
index 0000000000000000000000000000000000000000..f728d3fb7cd5ed4a5372fd0d20d3a093c28cd6f2
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/constant/turbulenceProperties
@@ -0,0 +1,30 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    location    "constant";
+    object      turbulenceProperties;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+simulationType RAS;
+
+RAS
+{
+    RASModel        kEpsilon;
+
+    turbulence      on;
+
+    printCoeffs     on;
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/blockMeshDict b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/blockMeshDict
new file mode 100644
index 0000000000000000000000000000000000000000..ed28fc6988fa13c5a377dfad67d3f762072c7ffd
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/blockMeshDict
@@ -0,0 +1,127 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      blockMeshDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+//           outlet1
+//             +-+
+//             | |
+//             | |
+//             | |
+//             | |
+// +-----------+ |
+// |inlet        |
+// +-----------+ |
+//             | |
+//             | |
+//             | |
+//             | |
+//             +-+
+//           outlet2
+
+scale   1;
+
+vertices
+(
+    (0.0  -0.01 0)   //0
+    (0.2  -0.01 0)
+    (0.2   0.01 0)   //2
+    (0.0   0.01 0)
+
+    (0.22 -0.01 0)  //4
+    (0.22  0.01 0)
+
+    (0.2  -0.21 0)  //6
+    (0.22 -0.21 0)
+
+    (0.2   0.21 0)  //8
+    (0.22  0.21 0)
+
+    // Z
+    (0.0  -0.01 0.02)   //0
+    (0.2  -0.01 0.02)
+    (0.2   0.01 0.02)   //2
+    (0.0   0.01 0.02)
+
+    (0.22 -0.01 0.02)  //4
+    (0.22  0.01 0.02)
+
+    (0.2  -0.21 0.02)  //6
+    (0.22 -0.21 0.02)
+
+    (0.2   0.21 0.02)  //8
+    (0.22  0.21 0.02)
+
+);
+
+blocks
+(
+    // inlet block
+    hex (0 1 2 3  10 11 12 13) (50 5 5) simpleGrading (1 1 1)
+
+    // central block
+    hex (1 4 5 2  11 14 15 12) (5 5 5) simpleGrading (1 1 1)
+
+    // bottom block
+    hex (6 7 4 1  16 17 14 11) (5 50 5) simpleGrading (1 1 1)
+
+    // top block
+    hex (2 5 9 8  12 15 19 18) (5 50 5) simpleGrading (1 1 1)
+);
+
+edges
+(
+);
+
+boundary
+(
+    inlet
+    {
+        type patch;
+        faces
+        (
+            (0 10 13 3)
+        );
+    }
+
+    outlet1
+    {
+        type patch;
+        faces
+        (
+            (6 7 17 16)
+        );
+    }
+
+    outlet2
+    {
+        type patch;
+        faces
+        (
+            (8 18 19 9)
+        );
+    }
+
+    defaultFaces
+    {
+        type wall;
+        faces ();
+    }
+);
+
+mergePatchPairs
+(
+);
+
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/controlDict b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/controlDict
new file mode 100644
index 0000000000000000000000000000000000000000..02148db3ccfb508ec28eac8443f6ca9fa71f508e
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/controlDict
@@ -0,0 +1,52 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    location    "system";
+    object      controlDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+application     rhoPimpleFoam;
+
+startFrom       startTime;
+
+startTime       0;
+
+stopAt          endTime;
+
+endTime         1.0;
+
+deltaT          0.001;
+
+writeControl    adjustable;
+
+writeInterval   0.1;
+
+purgeWrite      0;
+
+writeFormat     ascii;
+
+writePrecision  6;
+
+writeCompression off;
+
+timeFormat      general;
+
+timePrecision   6;
+
+runTimeModifiable true;
+
+adjustTimeStep  yes;
+
+maxCo           3;
+
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/fvSchemes b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/fvSchemes
new file mode 100644
index 0000000000000000000000000000000000000000..26d7a694892315be3e4389153daaff26eb149027
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/fvSchemes
@@ -0,0 +1,57 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    location    "system";
+    object      fvSchemes;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+ddtSchemes
+{
+    default         Euler;
+}
+
+gradSchemes
+{
+    default         Gauss linear;
+}
+
+divSchemes
+{
+    default         none;
+    div(phi,U)      Gauss limitedLinearV 1;
+    div(phi,k)      Gauss limitedLinear 1;
+    div(phi,epsilon) Gauss limitedLinear 1;
+    div(phi,h)      Gauss limitedLinear 1;
+    div(phi,R)      Gauss limitedLinear 1;
+    div(R)          Gauss linear;
+    div(phi,K)      Gauss linear;
+    div(((rho*nuEff)*dev2(T(grad(U))))) Gauss linear;
+}
+
+laplacianSchemes
+{
+    default         Gauss linear corrected;
+}
+
+interpolationSchemes
+{
+    default         linear;
+}
+
+snGradSchemes
+{
+    default         corrected;
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/fvSolution b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/fvSolution
new file mode 100644
index 0000000000000000000000000000000000000000..82ea2a0025c053581fcf0a712cc57974fd4b42cb
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/fvSolution
@@ -0,0 +1,70 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    location    "system";
+    object      fvSolution;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+solvers
+{
+    p
+    {
+        solver          GAMG;
+        tolerance       1e-08;
+        relTol          0.01;
+        smoother        GaussSeidel;
+    }
+
+    pFinal
+    {
+        solver          GAMG;
+        tolerance       1e-08;
+        relTol          0;
+        smoother        GaussSeidel;
+    }
+
+    "(rho|U|k|epsilon|h)"
+    {
+        solver          smoothSolver;
+        smoother        symGaussSeidel;
+        tolerance       1e-07;
+        relTol          0.05;
+    }
+
+    "(rho|U|k|epsilon|h)Final"
+    {
+        $U;
+        tolerance       1e-07;
+        relTol          0;
+    }
+}
+
+PIMPLE
+{
+    momentumPredictor   no;
+    transonic           no;
+    nOuterCorrectors 1;
+    nCorrectors      5;
+    nNonOrthogonalCorrectors 0;
+}
+
+relaxationFactors
+{
+    equations
+    {
+        ".*"           1;
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/setExprBoundaryFieldsDict b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/setExprBoundaryFieldsDict
new file mode 100644
index 0000000000000000000000000000000000000000..6412e4f64b29a30fd4887b49dc0b4e68119781e2
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/setExprBoundaryFieldsDict
@@ -0,0 +1,32 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      setExprBoundaryFieldsDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+pattern
+{
+    field   T;
+
+    expressions
+    (
+        {
+            patch   outlet2;
+            target  something;
+            expression #{ (pos().x() < 1e-4 ? 60 : 120) #};
+        }
+    );
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/setExprFieldsDict b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/setExprFieldsDict
new file mode 100644
index 0000000000000000000000000000000000000000..1a124966e73b8c5454906613c123b448121d1e85
--- /dev/null
+++ b/tutorials/compressible/rhoPimpleFoam/RAS/TJunctionAverage/system/setExprFieldsDict
@@ -0,0 +1,53 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1912                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      setExprFieldsDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+
+expressions
+(
+    T
+    {
+        field       T;
+        dimensions  [0 0 0 1 0 0 0];
+
+        constants
+        {
+            centre (0.21 0 0.01);
+        }
+
+        variables
+        (
+            "radius = 0.1"
+        );
+
+        condition
+        #{
+            // Within the radius
+            (mag(pos() - $[(vector)constants.centre]) < radius)
+
+            // but only +ve y!
+          && pos((pos() - $[(vector)constants.centre]).y()) > 0
+        #};
+
+        expression
+        #{
+            300
+          + 200 * (1 - mag(pos() - $[(vector)constants.centre]) / radius)
+        #};
+    }
+);
+
+
+// ************************************************************************* //