← Back to team overview

kicad-developers team mailing list archive

[PATCH] Numeric evaluator tests + unary "+"

 

Hi,

Here are some patches for the numeric evaluator.

There are three parts:
* Add tests and test current behaviour of the numeric evaluator.
Remove broken and unused/unbuildable old tests.
* Add a CMake target (libeval_grammar) to allow to rebuild the grammar
* Modify and rebuild the grammar to allow unary + in expressions. Not
a major change, but consistent with most other numeric evaluations,
and symmetric with the minus notation.

The CMake target is run manually and, as currently, the generated C
source checked in. The target will only be available when lemon is
installed.

Having this available as a CMake target makes it much more accessible
to other developers to maintain the grammar. Tests allow to ensure
behaviour is not changed when not expected.

Cheers,

John
From 7feb9a01589a71d0a26bff6f58e221988b7aae35 Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Wed, 28 Nov 2018 13:46:42 +0000
Subject: [PATCH 2/3] Build: Add a CMake target libeval lemon grammar

The libeval grammar can now be rebuilt with "make libeval_grammar".
This is only possible when lemon is installed.

Also a couple of modifications to the lemon grammar to
account for external changes since the grammer was last generated.

Also move the grammar defines into grammar.h - this is how
Lemon produces them, and by manually copying into the target .cpp,
it make automated regeneration impossible.
---
 common/CMakeLists.txt                |  2 ++
 common/libeval/CMakeLists.txt        | 43 ++++++++++++++++++++++++++++
 common/libeval/grammar.h             | 12 ++++++++
 common/libeval/grammar.lemon         |  4 +--
 common/libeval/numeric_evaluator.cpp | 15 +---------
 5 files changed, 60 insertions(+), 16 deletions(-)
 create mode 100644 common/libeval/CMakeLists.txt
 create mode 100644 common/libeval/grammar.h

diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 7c1db83df..569543a87 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -3,6 +3,8 @@ if( COMPILER_SUPPORTS_WSHADOW )
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WSHADOW_FLAGS}")
 endif()
 
+add_subdirectory( libeval )
+
 include_directories( BEFORE ${INC_BEFORE} )
 include_directories(
     ./dialogs
diff --git a/common/libeval/CMakeLists.txt b/common/libeval/CMakeLists.txt
new file mode 100644
index 000000000..44ec1fbd2
--- /dev/null
+++ b/common/libeval/CMakeLists.txt
@@ -0,0 +1,43 @@
+#
+#  This program source code file is part of KICAD, a free EDA CAD application.
+#
+#  Copyright (C) 2018 Kicad Developers, see AUTHORS.txt for contributors.
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, you may find one here:
+#  http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+#  or you may search the http://www.gnu.org website for the version 2 license,
+#  or you may write to the Free Software Foundation, Inc.,
+#  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+find_program(LEMON lemon)
+
+if( LEMON )
+
+    macro( generate_lemon_grammar GRAMMAR_LEMON GRAMMAR_C )
+        add_custom_command(
+            DEPENDS ${GRAMMAR_LEMON}
+            OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${GRAMMAR_C}
+            COMMAND ${LEMON} -q ${GRAMMAR_LEMON}
+            COMMENT "Running Lemon on ${GRAMMAR_LEMON} -> ${GRAMMAR_C}"
+            WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+        )
+    endmacro()
+
+    generate_lemon_grammar( grammar.lemon grammar.c )
+
+    add_custom_target(libeval_grammar
+        DEPENDS grammar.c
+    )
+
+endif()
\ No newline at end of file
diff --git a/common/libeval/grammar.h b/common/libeval/grammar.h
new file mode 100644
index 000000000..593f3cdd4
--- /dev/null
+++ b/common/libeval/grammar.h
@@ -0,0 +1,12 @@
+#define VAR                             1
+#define ASSIGN                          2
+#define SEMCOL                          3
+#define PLUS                            4
+#define MINUS                           5
+#define UNIT                            6
+#define DIVIDE                          7
+#define MULT                            8
+#define ENDS                            9
+#define VALUE                          10
+#define PARENL                         11
+#define PARENR                         12
diff --git a/common/libeval/grammar.lemon b/common/libeval/grammar.lemon
index ffe627e9f..b5a7a22fb 100644
--- a/common/libeval/grammar.lemon
+++ b/common/libeval/grammar.lemon
@@ -18,7 +18,7 @@
 */
 
 %token_type { numEval::TokenType }
-%extra_argument { NumericEvaluator* pEval }
+%extra_argument { NUMERIC_EVALUATOR* pEval }
 
 %nonassoc VAR ASSIGN SEMCOL.
 %left PLUS MINUS.
@@ -27,7 +27,7 @@
 
 %include {
 #include <assert.h>
-#include "numeric_evaluator.h"
+#include <libeval/numeric_evaluator.h>
 }
 
 %syntax_error {
diff --git a/common/libeval/numeric_evaluator.cpp b/common/libeval/numeric_evaluator.cpp
index a268253ed..8f7be2ca8 100644
--- a/common/libeval/numeric_evaluator.cpp
+++ b/common/libeval/numeric_evaluator.cpp
@@ -34,6 +34,7 @@ namespace numEval
 #endif
 
 #include "grammar.c"
+#include "grammar.h"
 
 #ifdef __GNUC__
 #pragma GCC diagnostic pop
@@ -42,20 +43,6 @@ namespace numEval
 } /* namespace numEval */
 
 
-#define VAR                              1
-#define ASSIGN                           2
-#define SEMCOL                           3
-#define PLUS                             4
-#define MINUS                            5
-#define UNIT                             6
-#define DIVIDE                           7
-#define MULT                             8
-#define ENDS                             9
-#define VALUE                           10
-#define PARENL                          11
-#define PARENR                          12
-
-
 NUMERIC_EVALUATOR::NUMERIC_EVALUATOR( EDA_UNITS_T aUnits, bool aUseMils )
 {
     struct lconv* lc = localeconv();
-- 
2.19.2

From 07a3378a6ba751a5be8ddc22196ab2fa55d956a4 Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Wed, 28 Nov 2018 12:48:45 +0000
Subject: [PATCH 1/3] QA: Add numeric evaluator tests

Remove obsolete and unbuildable common/libeval/main.cpp
test program.
---
 common/libeval/main.cpp                      |  52 -----
 qa/common/CMakeLists.txt                     |   2 +
 qa/common/libeval/test_numeric_evaluator.cpp | 231 +++++++++++++++++++
 3 files changed, 233 insertions(+), 52 deletions(-)
 delete mode 100644 common/libeval/main.cpp
 create mode 100644 qa/common/libeval/test_numeric_evaluator.cpp

diff --git a/common/libeval/main.cpp b/common/libeval/main.cpp
deleted file mode 100644
index 7e174971d..000000000
--- a/common/libeval/main.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-    This file is part of libeval, a simple math expression evaluator
-
-    Copyright (C) 2017 Michael Geselbracht, mgeselbracht3@xxxxxxxxx
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <https://www.gnu.org/licenses/>.
-*/
-
-#include <stdio.h>
-#include <assert.h>
-
-#include "numeric_evaluator.h"
-
-int main()
-{
-   NumericEvaluator eval;
-
-   eval.process("2.54mm+50mil");
-   if (eval.isValid()) printf("%s\n", eval.result());
-
-   eval.process("x=1; y=5;");
-   if (eval.isValid()) printf("%s\n", eval.result());
-   eval.process("x+y");
-   if (eval.isValid()) printf("%s\n", eval.result());
-
-   eval.setVar("posx", -3.14152);
-   bool retval = eval.process("posx");
-   assert(retval == eval.isValid());
-   if (eval.isValid()) printf("%s\n", eval.result());
-
-   eval.process("x=1; y=2");
-   eval.setVar("z", 3);
-   eval.process("x+y+z");
-   printf("x+y+z=%s\n", eval.result());
-
-   eval.process("1\"");
-   printf("1\" = %s\n", eval.result());
-   eval.process("12.7 - 0.1\" - 50mil");
-   printf("12.7 - 0.1\" - 50mil = %s\n", eval.result());
-}
-
diff --git a/qa/common/CMakeLists.txt b/qa/common/CMakeLists.txt
index 20979fb46..7ea7084f5 100644
--- a/qa/common/CMakeLists.txt
+++ b/qa/common/CMakeLists.txt
@@ -40,6 +40,8 @@ add_executable( qa_common
     test_title_block.cpp
     test_utf8.cpp
 
+    libeval/test_numeric_evaluator.cpp
+
     geometry/test_fillet.cpp
 
     view/test_zoom_controller.cpp
diff --git a/qa/common/libeval/test_numeric_evaluator.cpp b/qa/common/libeval/test_numeric_evaluator.cpp
new file mode 100644
index 000000000..f6edec29b
--- /dev/null
+++ b/qa/common/libeval/test_numeric_evaluator.cpp
@@ -0,0 +1,231 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2018 KiCad Developers, see CHANGELOG.TXT for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+/**
+ * @file test_numeric_evaluator.cpp
+ * Test suite for #NUMERIC_EVALUATOR
+ */
+
+#include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
+
+#include <libeval/numeric_evaluator.h>
+
+struct NUM_EVAL_FIXTURE
+{
+    NUM_EVAL_FIXTURE() : m_eval( EDA_UNITS_T::MILLIMETRES, false )
+    {
+    }
+
+    NUMERIC_EVALUATOR m_eval;
+};
+
+
+/**
+ * Declares the struct as the Boost test fixture.
+ */
+BOOST_FIXTURE_TEST_SUITE( NumericEvaluator, NUM_EVAL_FIXTURE )
+
+
+/**
+ * Struct representing a test case for #NUMERIC_EVALUATOR
+ */
+struct EVAL_CASE
+{
+    wxString input;
+    wxString exp_result;
+};
+
+
+/**
+ * Basic class ops: set one up, trivial input, tear it down
+ */
+BOOST_AUTO_TEST_CASE( Basic )
+{
+    m_eval.Process( "1" );
+    BOOST_CHECK_EQUAL( m_eval.Result(), "1" );
+}
+
+/**
+ * Check that getting/setting vars works
+ */
+BOOST_AUTO_TEST_CASE( SetVar )
+{
+    m_eval.SetVar( "MoL", 42 );
+
+    m_eval.Process( "1 + MoL" );
+    BOOST_CHECK_EQUAL( m_eval.GetVar( "MoL" ), 42 );
+    BOOST_CHECK_EQUAL( m_eval.Result(), "43" );
+
+    m_eval.SetVar( "MoL", 422 );
+
+    // have to process again to re-evaluate
+    m_eval.Process( "1 + MoL" );
+    BOOST_CHECK_EQUAL( m_eval.Result(), "423" );
+
+    // Can remove one var
+    m_eval.SetVar( "pi", 3.14 );
+    BOOST_CHECK_EQUAL( m_eval.GetVar( "pi" ), 3.14 );
+    m_eval.RemoveVar( "pi" );
+    BOOST_CHECK_EQUAL( m_eval.GetVar( "pi" ), 0.0 );
+
+    // Other is still there
+    BOOST_CHECK_EQUAL( m_eval.GetVar( "MoL" ), 422 );
+
+    // Add another one back
+    m_eval.SetVar( "piish", 3.1 );
+
+    // String clear doesn't clear vars
+    m_eval.Clear();
+    m_eval.Process( "1 + MoL + piish" );
+    BOOST_CHECK_EQUAL( m_eval.Result(), "426.1" );
+
+    // Clear both
+    m_eval.ClearVar();
+    BOOST_CHECK_EQUAL( m_eval.GetVar( "MoL" ), 0.0 );
+    BOOST_CHECK_EQUAL( m_eval.GetVar( "piish" ), 0.0 );
+}
+
+/**
+ * A list of valid test strings and the expected results
+ */
+static const std::vector<EVAL_CASE> eval_cases_valid = {
+    // Empty case
+    { "", "0" },
+    // Trivial eval
+    { "1", "1" },
+    // Decimal separators
+    { "1.5", "1.5" },
+    { "1,5", "1.5" },
+    // Semicolon is valid, but the result is NaN
+    { "1;", "nan" },
+    // With own unit
+    { "1mm", "1" },
+    // Unit that's not the evaluator's unit
+    { "1in", "25.4" },
+    // Unit with white-space
+    { "1 in", "25.4" },
+    // Unit-less arithmetic
+    { "1+2", "3" },
+    // Multiple units
+    { "1 + 10mm + 1\" + 1.5in + 500mil", "87.2" },
+    // Any White-space is OK
+    { "   1 +     2    ", "3" },
+    // Decimals are OK in expressions
+    { "1.5 + 0.2 + .1", "1.8" },
+    // Negatives are OK
+    { "3 - 10", "-7" },
+    // Lots of operands
+    { "1 + 2 + 10 + 1000.05", "1013.05" },
+    // Operator precedence
+    { "1 + 2 - 4 * 20 / 2", "-37" },
+    // Parens
+    { "(1)", "1" },
+    // Parens affect precedence
+    { "-(1 + (2 - 4)) * 20.8 / 2", "10.4" },
+    // Unary addition, not a leading operator
+    //{ "+2 - 1", "1" } // TODO: This does not come out as valid
+    // Unknown vars are 0.0
+    { "1 + unknown", "1" },
+    // Set var in-string
+    { "x = 1; 1 + x", "2" },
+    // Multiple set vars
+    { "x = 1; y = 2; 10 + x - y", "9" },
+};
+
+
+/**
+ * Run through a set of test strings, clearing in between
+ */
+BOOST_AUTO_TEST_CASE( Results )
+{
+    for( const auto& c : eval_cases_valid )
+    {
+        // Clear for new string input
+        m_eval.Clear();
+
+        m_eval.Process( c.input );
+
+        // These are all valid
+        BOOST_CHECK_EQUAL( m_eval.IsValid(), true );
+        BOOST_CHECK_EQUAL( m_eval.Result(), c.exp_result );
+
+        // Does original text still match?
+        BOOST_CHECK_EQUAL( m_eval.OriginalText(), c.input );
+    }
+}
+
+struct EVAL_INVALID_CASE
+{
+    wxString input;
+};
+
+/**
+ * A list of invalid test strings
+ */
+static const std::vector<EVAL_INVALID_CASE> eval_cases_invalid = {
+    // Trailing operator
+    { "1+" },
+    // Leading operator
+    { "+2 + 1" },
+    { "*2 + 1" },
+    // No operator
+    { "1 2" },
+    { "(1)(2)" },
+    // Unknown operator
+    { "1 $ 2" },
+    // Mismatched parens
+    { "(1 + 2" },
+    { "1 + 2)" },
+    // random text
+    { "sdfsdf sdfsd" },
+    // Div by 0
+    { "1 / 0" },
+    { "1 / unknown" },
+    // Semicolons can't be empty or redundant
+    { ";" },
+    { ";1" },
+    { ";1;" },
+};
+
+/**
+ * Run through a set of invalid test strings, clearing in between
+ */
+BOOST_AUTO_TEST_CASE( ResultsInvalid )
+{
+    for( const auto& c : eval_cases_invalid )
+    {
+        // Clear for new string input
+        m_eval.Clear();
+
+        m_eval.Process( c.input );
+
+        // These are all valid
+        BOOST_CHECK_EQUAL( m_eval.IsValid(), false );
+
+        // Does original text still match?
+        BOOST_CHECK_EQUAL( m_eval.OriginalText(), c.input );
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
\ No newline at end of file
-- 
2.19.2

From 7213336d26db8be89b69a00a38576150d6c299ae Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Wed, 28 Nov 2018 14:40:33 +0000
Subject: [PATCH 3/3] Libeval: leading + is a sign, not an operator

Due to (presumably) a different lemon version,
there are some formatting changes in the grammar,
and some manual debug left in has been removed.
---
 common/libeval/grammar.c                     | 530 ++++++++-----------
 common/libeval/grammar.lemon                 |   1 +
 qa/common/libeval/test_numeric_evaluator.cpp |   5 +-
 3 files changed, 234 insertions(+), 302 deletions(-)

diff --git a/common/libeval/grammar.c b/common/libeval/grammar.c
index 4551f2d38..47c649a14 100644
--- a/common/libeval/grammar.c
+++ b/common/libeval/grammar.c
@@ -1,92 +1,63 @@
-/*
-** 2000-05-29
-**
-** The author disclaims copyright to this source code.  In place of
-** a legal notice, here is a blessing:
-**
-**    May you do good and not evil.
-**    May you find forgiveness for yourself and forgive others.
-**    May you share freely, never taking more than you give.
-**
-*************************************************************************
-** Driver template for the LEMON parser generator.
-**
-** The "lemon" program processes an LALR(1) input grammar file, then uses
-** this template to construct a parser.  The "lemon" program inserts text
-** at each "%%" line.  Also, any "P-a-r-s-e" identifer prefix (without the
-** interstitial "-" characters) contained in this template is changed into
-** the value of the %name directive from the grammar.  Otherwise, the content
-** of this template is copied straight through into the generate parser
-** source file.
-**
-** The following is the concatenation of all %include directives from the
-** input grammar file:
+/* Driver template for the LEMON parser generator.
+** The author disclaims copyright to this source code.
 */
+/* First off, code is included that follows the "include" declaration
+** in the input grammar file. */
 #include <stdio.h>
-/************ Begin %include sections from the grammar ************************/
 #line 28 "grammar.lemon"
 
 #include <assert.h>
 #include <libeval/numeric_evaluator.h>
-#line 32 "grammar.c"
-/**************** End of %include directives **********************************/
-/* These constants specify the various numeric values for terminal symbols
-** in a format understandable to "makeheaders".  This section is blank unless
-** "lemon" is run with the "-m" command-line option.
-***************** Begin makeheaders token definitions *************************/
-/**************** End makeheaders token definitions ***************************/
-
-/* The next sections is a series of control #defines.
+#line 12 "grammar.c"
+/* Next is all token values, in a form suitable for use by makeheaders.
+** This section will be null unless lemon is run with the -m switch.
+*/
+/* 
+** These constants (all generated automatically by the parser generator)
+** specify the various kinds of tokens (terminals) that the parser
+** understands. 
+**
+** Each symbol here is a terminal symbol in the grammar.
+*/
+/* Make sure the INTERFACE macro is defined.
+*/
+#ifndef INTERFACE
+# define INTERFACE 1
+#endif
+/* The next thing included is series of defines which control
 ** various aspects of the generated parser.
-**    YYCODETYPE         is the data type used to store the integer codes
-**                       that represent terminal and non-terminal symbols.
-**                       "unsigned char" is used if there are fewer than
-**                       256 symbols.  Larger types otherwise.
-**    YYNOCODE           is a number of type YYCODETYPE that is not used for
-**                       any terminal or nonterminal symbol.
+**    YYCODETYPE         is the data type used for storing terminal
+**                       and nonterminal numbers.  "unsigned char" is
+**                       used if there are fewer than 250 terminals
+**                       and nonterminals.  "int" is used otherwise.
+**    YYNOCODE           is a number of type YYCODETYPE which corresponds
+**                       to no legal terminal or nonterminal number.  This
+**                       number is used to fill in empty slots of the hash 
+**                       table.
 **    YYFALLBACK         If defined, this indicates that one or more tokens
-**                       (also known as: "terminal symbols") have fall-back
-**                       values which should be used if the original symbol
-**                       would not parse.  This permits keywords to sometimes
-**                       be used as identifiers, for example.
-**    YYACTIONTYPE       is the data type used for "action codes" - numbers
-**                       that indicate what to do in response to the next
-**                       token.
-**    ParseTOKENTYPE     is the data type used for minor type for terminal
-**                       symbols.  Background: A "minor type" is a semantic
-**                       value associated with a terminal or non-terminal
-**                       symbols.  For example, for an "ID" terminal symbol,
-**                       the minor type might be the name of the identifier.
-**                       Each non-terminal can have a different minor type.
-**                       Terminal symbols all have the same minor type, though.
-**                       This macros defines the minor type for terminal 
-**                       symbols.
-**    YYMINORTYPE        is the data type used for all minor types.
+**                       have fall-back values which should be used if the
+**                       original value of the token will not parse.
+**    YYACTIONTYPE       is the data type used for storing terminal
+**                       and nonterminal numbers.  "unsigned char" is
+**                       used if there are fewer than 250 rules and
+**                       states combined.  "int" is used otherwise.
+**    ParseTOKENTYPE     is the data type used for minor tokens given 
+**                       directly to the parser from the tokenizer.
+**    YYMINORTYPE        is the data type used for all minor tokens.
 **                       This is typically a union of many types, one of
 **                       which is ParseTOKENTYPE.  The entry in the union
-**                       for terminal symbols is called "yy0".
+**                       for base tokens is called "yy0".
 **    YYSTACKDEPTH       is the maximum depth of the parser's stack.  If
 **                       zero the stack is dynamically sized using realloc()
 **    ParseARG_SDECL     A static variable declaration for the %extra_argument
 **    ParseARG_PDECL     A parameter declaration for the %extra_argument
 **    ParseARG_STORE     Code to store %extra_argument into yypParser
 **    ParseARG_FETCH     Code to extract %extra_argument from yypParser
-**    YYERRORSYMBOL      is the code number of the error symbol.  If not
-**                       defined, then do no error processing.
 **    YYNSTATE           the combined number of states.
 **    YYNRULE            the number of rules in the grammar
-**    YY_MAX_SHIFT       Maximum value for shift actions
-**    YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
-**    YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
-**    YY_MIN_REDUCE      Maximum value for reduce actions
-**    YY_ERROR_ACTION    The yy_action[] code for syntax error
-**    YY_ACCEPT_ACTION   The yy_action[] code for accept
-**    YY_NO_ACTION       The yy_action[] code for no-op
+**    YYERRORSYMBOL      is the code number of the error symbol.  If not
+**                       defined, then do no error processing.
 */
-#ifndef INTERFACE
-# define INTERFACE 1
-#endif
-/************* Begin control #defines *****************************************/
 #define YYCODETYPE unsigned char
 #define YYNOCODE 19
 #define YYACTIONTYPE unsigned char
@@ -99,20 +70,14 @@ typedef union {
 #define YYSTACKDEPTH 100
 #endif
 #define ParseARG_SDECL  NUMERIC_EVALUATOR* pEval ;
-#define ParseARG_PDECL , NUMERIC_EVALUATOR* pEval
-#define ParseARG_FETCH  NUMERIC_EVALUATOR* pEval  = yypParser->pEval
+#define ParseARG_PDECL , NUMERIC_EVALUATOR* pEval 
+#define ParseARG_FETCH  NUMERIC_EVALUATOR* pEval  = yypParser->pEval 
 #define ParseARG_STORE yypParser->pEval  = pEval 
-#define YYNSTATE             16
-#define YYNRULE              16
-#define YY_MAX_SHIFT         15
-#define YY_MIN_SHIFTREDUCE   26
-#define YY_MAX_SHIFTREDUCE   41
-#define YY_MIN_REDUCE        42
-#define YY_MAX_REDUCE        57
-#define YY_ERROR_ACTION      58
-#define YY_ACCEPT_ACTION     59
-#define YY_NO_ACTION         60
-/************* End control #defines *******************************************/
+#define YYNSTATE 28
+#define YYNRULE 17
+#define YY_NO_ACTION      (YYNSTATE+YYNRULE+2)
+#define YY_ACCEPT_ACTION  (YYNSTATE+YYNRULE+1)
+#define YY_ERROR_ACTION   (YYNSTATE+YYNRULE)
 
 /* The yyzerominor constant is used to initialize instances of
 ** YYMINORTYPE objects to zero. */
@@ -139,20 +104,16 @@ static const YYMINORTYPE yyzerominor = { 0 };
 ** Suppose the action integer is N.  Then the action is determined as
 ** follows
 **
-**   0 <= N <= YY_MAX_SHIFT             Shift N.  That is, push the lookahead
+**   0 <= N < YYNSTATE                  Shift N.  That is, push the lookahead
 **                                      token onto the stack and goto state N.
 **
-**   N between YY_MIN_SHIFTREDUCE       Shift to an arbitrary state then
-**     and YY_MAX_SHIFTREDUCE           reduce by rule N-YY_MIN_SHIFTREDUCE.
+**   YYNSTATE <= N < YYNSTATE+YYNRULE   Reduce by rule N-YYNSTATE.
 **
-**   N between YY_MIN_REDUCE            Reduce by rule N-YY_MIN_REDUCE
-**     and YY_MAX_REDUCE
-
-**   N == YY_ERROR_ACTION               A syntax error has occurred.
+**   N == YYNSTATE+YYNRULE              A syntax error has occurred.
 **
-**   N == YY_ACCEPT_ACTION              The parser accepts its input.
+**   N == YYNSTATE+YYNRULE+1            The parser accepts its input.
 **
-**   N == YY_NO_ACTION                  No such action.  Denotes unused
+**   N == YYNSTATE+YYNRULE+2            No such action.  Denotes unused
 **                                      slots in the yy_action[] table.
 **
 ** The action table is constructed as a single large table named yy_action[].
@@ -181,48 +142,47 @@ static const YYMINORTYPE yyzerominor = { 0 };
 **  yy_reduce_ofst[]   For each state, the offset into yy_action for
 **                     shifting non-terminals after a reduce.
 **  yy_default[]       Default action for each state.
-**
-*********** Begin parsing tables **********************************************/
-#define YY_ACTTAB_COUNT (51)
+*/
+#define YY_ACTTAB_COUNT (58)
 static const YYACTIONTYPE yy_action[] = {
- /*     0 */    31,    8,    7,   33,    5,    6,   30,   42,   15,   10,
- /*    10 */    15,   11,    4,   12,    4,   40,   29,   32,    2,   32,
- /*    20 */     2,    8,    7,   33,    5,    6,   15,   28,    9,   41,
- /*    30 */     4,   39,   13,   14,   29,   32,    2,   44,    3,    8,
- /*    40 */     7,   33,    5,    6,   59,    1,   27,    9,   33,    5,
- /*    50 */     6,
+ /*     0 */    24,    9,    8,   23,    6,    7,   25,   28,   17,   16,
+ /*    10 */    17,    4,    5,    4,    5,    3,   26,   22,    2,   22,
+ /*    20 */     2,    9,    8,   23,    6,    7,   47,   17,   15,   19,
+ /*    30 */     4,    5,   21,   20,   47,   26,   22,    2,   27,   10,
+ /*    40 */     9,    8,   23,    6,    7,   46,    1,   18,   10,   23,
+ /*    50 */     6,    7,   47,   47,   14,   13,   12,   11,
 };
 static const YYCODETYPE yy_lookahead[] = {
  /*     0 */     3,    4,    5,    6,    7,    8,    9,    0,    1,   17,
- /*    10 */     1,   17,    5,   17,    5,   17,    9,   10,   11,   10,
- /*    20 */    11,    4,    5,    6,    7,    8,    1,   16,   17,   12,
- /*    30 */     5,   17,   17,   17,    9,   10,   11,   18,    2,    4,
- /*    40 */     5,    6,    7,    8,   14,   15,   16,   17,    6,    7,
- /*    50 */     8,
+ /*    10 */     1,    4,    5,    4,    5,    2,    9,   10,   11,   10,
+ /*    20 */    11,    4,    5,    6,    7,    8,   18,    1,   17,   12,
+ /*    30 */     4,    5,   17,   17,   18,    9,   10,   11,   16,   17,
+ /*    40 */     4,    5,    6,    7,    8,   14,   15,   16,   17,    6,
+ /*    50 */     7,    8,   18,   18,   17,   17,   17,   17,
 };
 #define YY_SHIFT_USE_DFLT (-4)
-#define YY_SHIFT_COUNT (15)
+#define YY_SHIFT_COUNT (17)
 #define YY_SHIFT_MIN   (-3)
-#define YY_SHIFT_MAX   (42)
+#define YY_SHIFT_MAX   (43)
 static const signed char yy_shift_ofst[] = {
- /*     0 */    25,    7,    9,    9,    9,    9,    9,    9,    9,   -3,
- /*    10 */    17,   35,   42,   42,   42,   36,
+ /*     0 */    26,    7,    9,    9,    9,    9,    9,    9,    9,    9,
+ /*    10 */    -3,   17,   36,   43,   43,   43,   43,   13,
 };
 #define YY_REDUCE_USE_DFLT (-9)
-#define YY_REDUCE_COUNT (8)
+#define YY_REDUCE_COUNT (9)
 #define YY_REDUCE_MIN   (-8)
-#define YY_REDUCE_MAX   (30)
+#define YY_REDUCE_MAX   (40)
 static const signed char yy_reduce_ofst[] = {
- /*     0 */    30,   11,   -8,   -6,   -4,   -2,   14,   15,   16,
+ /*     0 */    31,   22,   40,   39,   38,   37,   16,   15,   11,   -8,
 };
 static const YYACTIONTYPE yy_default[] = {
- /*     0 */    58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
- /*    10 */    58,   52,   50,   54,   53,   51,
+ /*     0 */    45,   45,   45,   45,   45,   45,   45,   45,   45,   45,
+ /*    10 */    45,   45,   39,   37,   36,   41,   40,   38,   29,   44,
+ /*    20 */    43,   42,   34,   35,   33,   32,   31,   30,
 };
-/********** End of lemon-generated parsing tables *****************************/
 
-/* The next table maps tokens (terminal symbols) into fallback tokens.  
-** If a construct like the following:
+/* The next table maps tokens into fallback tokens.  If a construct
+** like the following:
 ** 
 **      %fallback ID X Y Z.
 **
@@ -230,10 +190,6 @@ static const YYACTIONTYPE yy_default[] = {
 ** and Z.  Whenever one of the tokens X, Y, or Z is input to the parser
 ** but it does not parse, the type of the token is changed to ID and
 ** the parse is retried before an error is thrown.
-**
-** This feature can be used, for example, to cause some keywords in a language
-** to revert to identifiers if they keyword does not apply in the context where
-** it appears.
 */
 #ifdef YYFALLBACK
 static const YYCODETYPE yyFallback[] = {
@@ -251,13 +207,9 @@ static const YYCODETYPE yyFallback[] = {
 **   +  The semantic value stored at this level of the stack.  This is
 **      the information used by the action routines in the grammar.
 **      It is sometimes called the "minor" token.
-**
-** After the "shift" half of a SHIFTREDUCE action, the stateno field
-** actually contains the reduce action for the second half of the
-** SHIFTREDUCE.
 */
 struct yyStackEntry {
-  YYACTIONTYPE stateno;  /* The state-number, or reduce action in SHIFTREDUCE */
+  YYACTIONTYPE stateno;  /* The state-number */
   YYCODETYPE major;      /* The major token value.  This is the code
                          ** number for the token at this stack level */
   YYMINORTYPE minor;     /* The user-supplied minor token value.  This
@@ -340,13 +292,14 @@ static const char *const yyRuleName[] = {
  /*   6 */ "expr ::= VALUE",
  /*   7 */ "expr ::= expr UNIT",
  /*   8 */ "expr ::= MINUS expr",
- /*   9 */ "expr ::= VAR",
- /*  10 */ "expr ::= VAR ASSIGN expr",
- /*  11 */ "expr ::= expr PLUS expr",
- /*  12 */ "expr ::= expr MINUS expr",
- /*  13 */ "expr ::= expr MULT expr",
- /*  14 */ "expr ::= expr DIVIDE expr",
- /*  15 */ "expr ::= PARENL expr PARENR",
+ /*   9 */ "expr ::= PLUS expr",
+ /*  10 */ "expr ::= VAR",
+ /*  11 */ "expr ::= VAR ASSIGN expr",
+ /*  12 */ "expr ::= expr PLUS expr",
+ /*  13 */ "expr ::= expr MINUS expr",
+ /*  14 */ "expr ::= expr MULT expr",
+ /*  15 */ "expr ::= expr DIVIDE expr",
+ /*  16 */ "expr ::= PARENL expr PARENR",
 };
 #endif /* NDEBUG */
 
@@ -374,15 +327,6 @@ static void yyGrowStack(yyParser *p){
 }
 #endif
 
-/* Datatype of the argument to the memory allocated passed as the
-** second argument to ParseAlloc() below.  This can be changed by
-** putting an appropriate #define in the %include section of the input
-** grammar.
-*/
-#ifndef YYMALLOCARGTYPE
-# define YYMALLOCARGTYPE size_t
-#endif
-
 /* 
 ** This function allocates a new parser.
 ** The only argument is a pointer to a function which works like
@@ -395,9 +339,9 @@ static void yyGrowStack(yyParser *p){
 ** A pointer to a parser.  This pointer is used in subsequent calls
 ** to Parse and ParseFree.
 */
-void *ParseAlloc(void *(*mallocProc)(YYMALLOCARGTYPE)){
+void *ParseAlloc(void *(*mallocProc)(size_t)){
   yyParser *pParser;
-  pParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) );
+  pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) );
   if( pParser ){
     pParser->yyidx = -1;
 #ifdef YYTRACKMAXSTACKDEPTH
@@ -412,12 +356,10 @@ void *ParseAlloc(void *(*mallocProc)(YYMALLOCARGTYPE)){
   return pParser;
 }
 
-/* The following function deletes the "minor type" or semantic value
-** associated with a symbol.  The symbol can be either a terminal
-** or nonterminal. "yymajor" is the symbol code, and "yypminor" is
-** a pointer to the value to be deleted.  The code used to do the 
-** deletions is derived from the %destructor and/or %token_destructor
-** directives of the input grammar.
+/* The following function deletes the value associated with a
+** symbol.  The symbol can be either a terminal or nonterminal.
+** "yymajor" is the symbol code, and "yypminor" is a pointer to
+** the value.
 */
 static void yy_destructor(
   yyParser *yypParser,    /* The parser */
@@ -433,11 +375,9 @@ static void yy_destructor(
     ** being destroyed before it is finished parsing.
     **
     ** Note: during a reduce, the only symbols destroyed are those
-    ** which appear on the RHS of the rule, but which are *not* used
+    ** which appear on the RHS of the rule, but which are not used
     ** inside the C code.
     */
-/********* Begin destructor definitions ***************************************/
-/********* End destructor definitions *****************************************/
     default:  break;   /* If no destructor action specified: do nothing */
   }
 }
@@ -447,37 +387,45 @@ static void yy_destructor(
 **
 ** If there is a destructor routine associated with the token which
 ** is popped from the stack, then call it.
+**
+** Return the major token number for the symbol popped.
 */
-static void yy_pop_parser_stack(yyParser *pParser){
-  yyStackEntry *yytos;
-  assert( pParser->yyidx>=0 );
-  yytos = &pParser->yystack[pParser->yyidx--];
+static int yy_pop_parser_stack(yyParser *pParser){
+  YYCODETYPE yymajor;
+  yyStackEntry *yytos = &pParser->yystack[pParser->yyidx];
+
+  if( pParser->yyidx<0 ) return 0;
 #ifndef NDEBUG
-  if( yyTraceFILE ){
+  if( yyTraceFILE && pParser->yyidx>=0 ){
     fprintf(yyTraceFILE,"%sPopping %s\n",
       yyTracePrompt,
       yyTokenName[yytos->major]);
   }
 #endif
-  yy_destructor(pParser, yytos->major, &yytos->minor);
+  yymajor = yytos->major;
+  yy_destructor(pParser, yymajor, &yytos->minor);
+  pParser->yyidx--;
+  return yymajor;
 }
 
 /* 
-** Deallocate and destroy a parser.  Destructors are called for
+** Deallocate and destroy a parser.  Destructors are all called for
 ** all stack elements before shutting the parser down.
 **
-** If the YYPARSEFREENEVERNULL macro exists (for example because it
-** is defined in a %include section of the input grammar) then it is
-** assumed that the input pointer is never NULL.
+** Inputs:
+** <ul>
+** <li>  A pointer to the parser.  This should be a pointer
+**       obtained from ParseAlloc.
+** <li>  A pointer to a function used to reclaim memory obtained
+**       from malloc.
+** </ul>
 */
 void ParseFree(
   void *p,                    /* The parser to be deleted */
   void (*freeProc)(void*)     /* Function used to reclaim memory */
 ){
   yyParser *pParser = (yyParser*)p;
-#ifndef YYPARSEFREENEVERNULL
   if( pParser==0 ) return;
-#endif
   while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser);
 #if YYSTACKDEPTH<=0
   free(pParser->yystack);
@@ -498,6 +446,10 @@ int ParseStackPeak(void *p){
 /*
 ** Find the appropriate action for a parser given the terminal
 ** look-ahead token iLookAhead.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead.  If it is, return the action, otherwise
+** return YY_NO_ACTION.
 */
 static int yy_find_shift_action(
   yyParser *pParser,        /* The parser */
@@ -506,64 +458,63 @@ static int yy_find_shift_action(
   int i;
   int stateno = pParser->yystack[pParser->yyidx].stateno;
  
-  if( stateno>=YY_MIN_REDUCE ) return stateno;
-  assert( stateno <= YY_SHIFT_COUNT );
-  do{
-    i = yy_shift_ofst[stateno];
-    if( i==YY_SHIFT_USE_DFLT ) return yy_default[stateno];
-    assert( iLookAhead!=YYNOCODE );
-    i += iLookAhead;
-    if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
-      if( iLookAhead>0 ){
+  if( stateno>YY_SHIFT_COUNT
+   || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){
+    return yy_default[stateno];
+  }
+  assert( iLookAhead!=YYNOCODE );
+  i += iLookAhead;
+  if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
+    if( iLookAhead>0 ){
 #ifdef YYFALLBACK
-        YYCODETYPE iFallback;            /* Fallback token */
-        if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
-               && (iFallback = yyFallback[iLookAhead])!=0 ){
+      YYCODETYPE iFallback;            /* Fallback token */
+      if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
+             && (iFallback = yyFallback[iLookAhead])!=0 ){
 #ifndef NDEBUG
-          if( yyTraceFILE ){
-            fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
-               yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
-          }
-#endif
-          assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
-          iLookAhead = iFallback;
-          continue;
+        if( yyTraceFILE ){
+          fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
+             yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
         }
+#endif
+        return yy_find_shift_action(pParser, iFallback);
+      }
 #endif
 #ifdef YYWILDCARD
-        {
-          int j = i - iLookAhead + YYWILDCARD;
-          if( 
+      {
+        int j = i - iLookAhead + YYWILDCARD;
+        if( 
 #if YY_SHIFT_MIN+YYWILDCARD<0
-            j>=0 &&
+          j>=0 &&
 #endif
 #if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT
-            j<YY_ACTTAB_COUNT &&
+          j<YY_ACTTAB_COUNT &&
 #endif
-            yy_lookahead[j]==YYWILDCARD
-          ){
+          yy_lookahead[j]==YYWILDCARD
+        ){
 #ifndef NDEBUG
-            if( yyTraceFILE ){
-              fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
-                 yyTracePrompt, yyTokenName[iLookAhead],
-                 yyTokenName[YYWILDCARD]);
-            }
-#endif /* NDEBUG */
-            return yy_action[j];
+          if( yyTraceFILE ){
+            fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
+               yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]);
           }
+#endif /* NDEBUG */
+          return yy_action[j];
         }
-#endif /* YYWILDCARD */
       }
-      return yy_default[stateno];
-    }else{
-      return yy_action[i];
+#endif /* YYWILDCARD */
     }
-  }while(1);
+    return yy_default[stateno];
+  }else{
+    return yy_action[i];
+  }
 }
 
 /*
 ** Find the appropriate action for a parser given the non-terminal
 ** look-ahead token iLookAhead.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead.  If it is, return the action, otherwise
+** return YY_NO_ACTION.
 */
 static int yy_find_reduce_action(
   int stateno,              /* Current state number */
@@ -606,31 +557,9 @@ static void yyStackOverflow(yyParser *yypParser, YYMINORTYPE *yypMinor){
    while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
    /* Here code is inserted which will execute if the parser
    ** stack every overflows */
-/******** Begin %stack_overflow code ******************************************/
-/******** End %stack_overflow code ********************************************/
    ParseARG_STORE; /* Suppress warning about unused %extra_argument var */
 }
 
-/*
-** Print tracing information for a SHIFT action
-*/
-#ifndef NDEBUG
-static void yyTraceShift(yyParser *yypParser, int yyNewState){
-  if( yyTraceFILE ){
-    if( yyNewState<YYNSTATE ){
-      fprintf(yyTraceFILE,"%sShift '%s', go to state %d\n",
-         yyTracePrompt,yyTokenName[yypParser->yystack[yypParser->yyidx].major],
-         yyNewState);
-    }else{
-      fprintf(yyTraceFILE,"%sShift '%s'\n",
-         yyTracePrompt,yyTokenName[yypParser->yystack[yypParser->yyidx].major]);
-    }
-  }
-}
-#else
-# define yyTraceShift(X,Y)
-#endif
-
 /*
 ** Perform a shift action.
 */
@@ -665,7 +594,16 @@ static void yy_shift(
   yytos->stateno = (YYACTIONTYPE)yyNewState;
   yytos->major = (YYCODETYPE)yyMajor;
   yytos->minor = *yypMinor;
-  yyTraceShift(yypParser, yyNewState);
+#ifndef NDEBUG
+  if( yyTraceFILE && yypParser->yyidx>0 ){
+    int i;
+    fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
+    fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
+    for(i=1; i<=yypParser->yyidx; i++)
+      fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]);
+    fprintf(yyTraceFILE,"\n");
+  }
+#endif
 }
 
 /* The following table contains information about every rule that
@@ -684,6 +622,7 @@ static const struct {
   { 17, 1 },
   { 17, 2 },
   { 17, 2 },
+  { 17, 2 },
   { 17, 1 },
   { 17, 3 },
   { 17, 3 },
@@ -713,13 +652,29 @@ static void yy_reduce(
 #ifndef NDEBUG
   if( yyTraceFILE && yyruleno>=0 
         && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
-    yysize = yyRuleInfo[yyruleno].nrhs;
-    fprintf(yyTraceFILE, "%sReduce [%s], go to state %d.\n", yyTracePrompt,
-      yyRuleName[yyruleno], yymsp[-yysize].stateno);
+    fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt,
+      yyRuleName[yyruleno]);
   }
 #endif /* NDEBUG */
+
+  /* Silence complaints from purify about yygotominor being uninitialized
+  ** in some cases when it is copied into the stack after the following
+  ** switch.  yygotominor is uninitialized when a rule reduces that does
+  ** not set the value of its left-hand side nonterminal.  Leaving the
+  ** value of the nonterminal uninitialized is utterly harmless as long
+  ** as the value is never used.  So really the only thing this code
+  ** accomplishes is to quieten purify.  
+  **
+  ** 2007-01-16:  The wireshark project (www.wireshark.org) reports that
+  ** without this code, their parser segfaults.  I'm not sure what there
+  ** parser is doing to make this happen.  This is the second bug report
+  ** from wireshark this week.  Clearly they are stressing Lemon in ways
+  ** that it has not been previously stressed...  (SQLite ticket #2172)
+  */
+  /*memset(&yygotominor, 0, sizeof(yygotominor));*/
   yygotominor = yyzerominor;
 
+
   switch( yyruleno ){
   /* Beginning here are the reduction cases.  A typical example
   ** follows:
@@ -729,59 +684,63 @@ static void yy_reduce(
   **  #line <lineno> <thisfile>
   **     break;
   */
-/********** Begin reduce actions **********************************************/
       case 4: /* stmt ::= expr ENDS */
 #line 49 "grammar.lemon"
 { pEval->parseSetResult(yymsp[-1].minor.yy0.valid ? yymsp[-1].minor.yy0.dValue : NAN); }
-#line 737 "grammar.c"
+#line 691 "grammar.c"
         break;
       case 5: /* stmt ::= expr SEMCOL */
 #line 50 "grammar.lemon"
 { pEval->parseSetResult(NAN); }
-#line 742 "grammar.c"
+#line 696 "grammar.c"
         break;
       case 6: /* expr ::= VALUE */
 #line 52 "grammar.lemon"
 { yygotominor.yy0.dValue = yymsp[0].minor.yy0.dValue; yygotominor.yy0.valid=true; }
-#line 747 "grammar.c"
+#line 701 "grammar.c"
         break;
       case 7: /* expr ::= expr UNIT */
 #line 53 "grammar.lemon"
 { yygotominor.yy0.dValue = yymsp[-1].minor.yy0.dValue * yymsp[0].minor.yy0.dValue; yygotominor.yy0.valid=yymsp[-1].minor.yy0.valid; }
-#line 752 "grammar.c"
+#line 706 "grammar.c"
         break;
       case 8: /* expr ::= MINUS expr */
 #line 54 "grammar.lemon"
 { yygotominor.yy0.dValue = -yymsp[0].minor.yy0.dValue; yygotominor.yy0.valid=yymsp[0].minor.yy0.valid; }
-#line 757 "grammar.c"
+#line 711 "grammar.c"
         break;
-      case 9: /* expr ::= VAR */
+      case 9: /* expr ::= PLUS expr */
 #line 55 "grammar.lemon"
-{ yygotominor.yy0.dValue = pEval->GetVar(yymsp[0].minor.yy0.text); yygotominor.yy0.valid=true; }
-#line 762 "grammar.c"
+{ yygotominor.yy0.dValue = yymsp[0].minor.yy0.dValue; yygotominor.yy0.valid=yymsp[0].minor.yy0.valid; }
+#line 716 "grammar.c"
         break;
-      case 10: /* expr ::= VAR ASSIGN expr */
+      case 10: /* expr ::= VAR */
 #line 56 "grammar.lemon"
-{ pEval->SetVar(yymsp[-2].minor.yy0.text, yymsp[0].minor.yy0.dValue); yygotominor.yy0.dValue = yymsp[0].minor.yy0.dValue; yygotominor.yy0.valid=false; }
-#line 767 "grammar.c"
+{ yygotominor.yy0.dValue = pEval->GetVar(yymsp[0].minor.yy0.text); yygotominor.yy0.valid=true; }
+#line 721 "grammar.c"
         break;
-      case 11: /* expr ::= expr PLUS expr */
+      case 11: /* expr ::= VAR ASSIGN expr */
 #line 57 "grammar.lemon"
-{ yygotominor.yy0.dValue = yymsp[-2].minor.yy0.dValue + yymsp[0].minor.yy0.dValue; yygotominor.yy0.valid=yymsp[0].minor.yy0.valid; }
-#line 772 "grammar.c"
+{ pEval->SetVar(yymsp[-2].minor.yy0.text, yymsp[0].minor.yy0.dValue); yygotominor.yy0.dValue = yymsp[0].minor.yy0.dValue; yygotominor.yy0.valid=false; }
+#line 726 "grammar.c"
         break;
-      case 12: /* expr ::= expr MINUS expr */
+      case 12: /* expr ::= expr PLUS expr */
 #line 58 "grammar.lemon"
-{ yygotominor.yy0.dValue = yymsp[-2].minor.yy0.dValue - yymsp[0].minor.yy0.dValue; yygotominor.yy0.valid=yymsp[0].minor.yy0.valid; }
-#line 777 "grammar.c"
+{ yygotominor.yy0.dValue = yymsp[-2].minor.yy0.dValue + yymsp[0].minor.yy0.dValue; yygotominor.yy0.valid=yymsp[0].minor.yy0.valid; }
+#line 731 "grammar.c"
         break;
-      case 13: /* expr ::= expr MULT expr */
+      case 13: /* expr ::= expr MINUS expr */
 #line 59 "grammar.lemon"
-{ yygotominor.yy0.dValue = yymsp[-2].minor.yy0.dValue * yymsp[0].minor.yy0.dValue; yygotominor.yy0.valid=yymsp[0].minor.yy0.valid; }
-#line 782 "grammar.c"
+{ yygotominor.yy0.dValue = yymsp[-2].minor.yy0.dValue - yymsp[0].minor.yy0.dValue; yygotominor.yy0.valid=yymsp[0].minor.yy0.valid; }
+#line 736 "grammar.c"
         break;
-      case 14: /* expr ::= expr DIVIDE expr */
+      case 14: /* expr ::= expr MULT expr */
 #line 60 "grammar.lemon"
+{ yygotominor.yy0.dValue = yymsp[-2].minor.yy0.dValue * yymsp[0].minor.yy0.dValue; yygotominor.yy0.valid=yymsp[0].minor.yy0.valid; }
+#line 741 "grammar.c"
+        break;
+      case 15: /* expr ::= expr DIVIDE expr */
+#line 61 "grammar.lemon"
 {
    if (yymsp[0].minor.yy0.dValue != 0.0) {
       yygotominor.yy0.dValue = yymsp[-2].minor.yy0.dValue / yymsp[0].minor.yy0.dValue;
@@ -789,12 +748,12 @@ static void yy_reduce(
    else pEval->parseError("Div by zero");
    yygotominor.yy0.valid=yymsp[0].minor.yy0.valid; 
 }
-#line 793 "grammar.c"
+#line 752 "grammar.c"
         break;
-      case 15: /* expr ::= PARENL expr PARENR */
-#line 67 "grammar.lemon"
+      case 16: /* expr ::= PARENL expr PARENR */
+#line 68 "grammar.lemon"
 { yygotominor.yy0.dValue = yymsp[-1].minor.yy0.dValue; yygotominor.yy0.valid=yymsp[-1].minor.yy0.valid; }
-#line 798 "grammar.c"
+#line 757 "grammar.c"
         break;
       default:
       /* (0) main ::= in */ yytestcase(yyruleno==0);
@@ -802,16 +761,14 @@ static void yy_reduce(
       /* (2) in ::= in stmt */ yytestcase(yyruleno==2);
       /* (3) stmt ::= ENDS */ yytestcase(yyruleno==3);
         break;
-/********** End reduce actions ************************************************/
   };
-  assert( yyruleno>=0 && yyruleno<sizeof(yyRuleInfo)/sizeof(yyRuleInfo[0]) );
   yygoto = yyRuleInfo[yyruleno].lhs;
   yysize = yyRuleInfo[yyruleno].nrhs;
   yypParser->yyidx -= yysize;
   yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto);
-  if( yyact <= YY_MAX_SHIFTREDUCE ){
-    if( yyact>YY_MAX_SHIFT ) yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
-    /* If the reduce action popped at least
+  if( yyact < YYNSTATE ){
+#ifdef NDEBUG
+    /* If we are not debugging and the reduce action popped at least
     ** one element off the stack, then we can push the new element back
     ** onto the stack here, and skip the stack overflow test in yy_shift().
     ** That gives a significant speed improvement. */
@@ -821,12 +778,13 @@ static void yy_reduce(
       yymsp->stateno = (YYACTIONTYPE)yyact;
       yymsp->major = (YYCODETYPE)yygoto;
       yymsp->minor = yygotominor;
-      yyTraceShift(yypParser, yyact);
-    }else{
+    }else
+#endif
+    {
       yy_shift(yypParser,yyact,yygoto,&yygotominor);
     }
   }else{
-    assert( yyact == YY_ACCEPT_ACTION );
+    assert( yyact == YYNSTATE + YYNRULE + 1 );
     yy_accept(yypParser);
   }
 }
@@ -847,8 +805,6 @@ static void yy_parse_failed(
   while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
   /* Here code is inserted which will be executed whenever the
   ** parser fails */
-/************ Begin %parse_failure code ***************************************/
-/************ End %parse_failure code *****************************************/
   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
 }
 #endif /* YYNOERRORRECOVERY */
@@ -863,12 +819,10 @@ static void yy_syntax_error(
 ){
   ParseARG_FETCH;
 #define TOKEN (yyminor.yy0)
-/************ Begin %syntax_error code ****************************************/
 #line 33 "grammar.lemon"
 
   pEval->parseError("Syntax error");
-#line 871 "grammar.c"
-/************ End %syntax_error code ******************************************/
+#line 826 "grammar.c"
   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
 }
 
@@ -887,12 +841,10 @@ static void yy_accept(
   while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
   /* Here code is inserted which will be executed whenever the
   ** parser accepts */
-/*********** Begin %parse_accept code *****************************************/
 #line 37 "grammar.lemon"
 
   pEval->parseOk();
-#line 895 "grammar.c"
-/*********** End %parse_accept code *******************************************/
+#line 848 "grammar.c"
   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
 }
 
@@ -923,9 +875,7 @@ void Parse(
 ){
   YYMINORTYPE yyminorunion;
   int yyact;            /* The parser action. */
-#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
   int yyendofinput;     /* True if we are at the end of input */
-#endif
 #ifdef YYERRORSYMBOL
   int yyerrorhit = 0;   /* True if yymajor has invoked an error */
 #endif
@@ -946,34 +896,26 @@ void Parse(
     yypParser->yyerrcnt = -1;
     yypParser->yystack[0].stateno = 0;
     yypParser->yystack[0].major = 0;
-#ifndef NDEBUG
-    if( yyTraceFILE ){
-      fprintf(yyTraceFILE,"%sInitialize. Empty stack. State 0\n",
-              yyTracePrompt);
-    }
-#endif
   }
   yyminorunion.yy0 = yyminor;
-#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
   yyendofinput = (yymajor==0);
-#endif
   ParseARG_STORE;
 
 #ifndef NDEBUG
   if( yyTraceFILE ){
-    fprintf(yyTraceFILE,"%sInput '%s'\n",yyTracePrompt,yyTokenName[yymajor]);
+    fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]);
   }
 #endif
 
   do{
     yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor);
-    if( yyact <= YY_MAX_SHIFTREDUCE ){
-      if( yyact > YY_MAX_SHIFT ) yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
+    if( yyact<YYNSTATE ){
+      assert( !yyendofinput );  /* Impossible to shift the $ token */
       yy_shift(yypParser,yyact,yymajor,&yyminorunion);
       yypParser->yyerrcnt--;
       yymajor = YYNOCODE;
-    }else if( yyact <= YY_MAX_REDUCE ){
-      yy_reduce(yypParser,yyact-YY_MIN_REDUCE);
+    }else if( yyact < YYNSTATE + YYNRULE ){
+      yy_reduce(yypParser,yyact-YYNSTATE);
     }else{
       assert( yyact == YY_ERROR_ACTION );
 #ifdef YYERRORSYMBOL
@@ -1023,7 +965,7 @@ void Parse(
           yymx != YYERRORSYMBOL &&
           (yyact = yy_find_reduce_action(
                         yypParser->yystack[yypParser->yyidx].stateno,
-                        YYERRORSYMBOL)) >= YY_MIN_REDUCE
+                        YYERRORSYMBOL)) >= YYNSTATE
         ){
           yy_pop_parser_stack(yypParser);
         }
@@ -1073,15 +1015,5 @@ void Parse(
 #endif
     }
   }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 );
-#ifndef NDEBUG
-  if( yyTraceFILE ){
-    int i;
-    fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt);
-    for(i=1; i<=yypParser->yyidx; i++)
-      fprintf(yyTraceFILE,"%c%s", i==1 ? '[' : ' ', 
-              yyTokenName[yypParser->yystack[i].major]);
-    fprintf(yyTraceFILE,"]\n");
-  }
-#endif
   return;
 }
diff --git a/common/libeval/grammar.lemon b/common/libeval/grammar.lemon
index b5a7a22fb..44516005c 100644
--- a/common/libeval/grammar.lemon
+++ b/common/libeval/grammar.lemon
@@ -52,6 +52,7 @@ stmt ::= expr SEMCOL.                     { pEval->parseSetResult(NAN); }
 expr(A) ::= VALUE(B).                     { A.dValue = B.dValue; A.valid=true; }
 expr(A) ::= expr(B) UNIT(C).              { A.dValue = B.dValue * C.dValue; A.valid=B.valid; }
 expr(A) ::= MINUS expr(B).                { A.dValue = -B.dValue; A.valid=B.valid; }
+expr(A) ::= PLUS expr(B).                 { A.dValue = B.dValue; A.valid=B.valid; }
 expr(A) ::= VAR(B).                       { A.dValue = pEval->GetVar(B.text); A.valid=true; }
 expr(A) ::= VAR(B) ASSIGN expr(C).        { pEval->SetVar(B.text, C.dValue); A.dValue = C.dValue; A.valid=false; }
 expr(A) ::= expr(B) PLUS expr(C).         { A.dValue = B.dValue + C.dValue; A.valid=C.valid; }
diff --git a/qa/common/libeval/test_numeric_evaluator.cpp b/qa/common/libeval/test_numeric_evaluator.cpp
index f6edec29b..b550e6c1c 100644
--- a/qa/common/libeval/test_numeric_evaluator.cpp
+++ b/qa/common/libeval/test_numeric_evaluator.cpp
@@ -143,8 +143,8 @@ static const std::vector<EVAL_CASE> eval_cases_valid = {
     { "(1)", "1" },
     // Parens affect precedence
     { "-(1 + (2 - 4)) * 20.8 / 2", "10.4" },
-    // Unary addition, not a leading operator
-    //{ "+2 - 1", "1" } // TODO: This does not come out as valid
+    // Unary addition is a sign, not a leading operator
+    { "+2 - 1", "1" },
     // Unknown vars are 0.0
     { "1 + unknown", "1" },
     // Set var in-string
@@ -187,7 +187,6 @@ static const std::vector<EVAL_INVALID_CASE> eval_cases_invalid = {
     // Trailing operator
     { "1+" },
     // Leading operator
-    { "+2 + 1" },
     { "*2 + 1" },
     // No operator
     { "1 2" },
-- 
2.19.2


Follow ups