Bug 144464 - Equation parser doesn't like missing leading zeros
Summary: Equation parser doesn't like missing leading zeros
Status: RESOLVED FIXED
Alias: None
Product: kst
Classification: Applications
Component: general (show other bugs)
Version: 1.x
Platform: Compiled Sources Linux
: NOR normal
Target Milestone: ---
Assignee: kst
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2007-04-20 22:37 UTC by D. V. Wiebe
Modified: 2007-05-08 20:38 UTC (History)
0 users

See Also:
Latest Commit:
Version Fixed In:


Attachments
proposed patch for Barth's number format (61.59 KB, patch)
2007-05-03 20:38 UTC, Eli Fidler
Details
fixed patch (64.09 KB, patch)
2007-05-03 22:31 UTC, Eli Fidler
Details

Note You need to log in before you can comment on or make changes to this bug.
Description D. V. Wiebe 2007-04-20 22:37:34 UTC
Version:           1.4.0 (using KDE KDE 3.5.6)
Installed from:    Compiled From Sources
Compiler:          gcc (GCC) 4.1.2 
OS:                Linux

The equation parser doesn't like it when I omit leading zeros on numbers smaller than one.  ie. the equation:

  x + .1

produces the syntax error error:

  Unknown character '.'.

The equation parser should treat this the same as:

  x + 0.1
Comment 1 Matthew Truch 2007-04-21 00:06:47 UTC
I find this particularly annoying in the 'Range' tab of a plot.  When I want the range to go from .01 to .1, I don't want to have to type the leading zero, and the error message only says "There is a problem with the Y range expression" is a little odd.  

Also, if you enter a value like .1 into the range and then click OK, it pops up the error dialog, which only gives you the button OK, and when you click that, the edit plot dialog disappears (since you hit OK), but since there was an error, it should not dissappear, it should stay up so you can correct the error.  
Comment 2 Eli Fidler 2007-04-24 20:47:13 UTC
fixed in 1.5 branch (657676)
Comment 3 Andrew Walker 2007-04-24 21:13:49 UTC
The only problem with this change is that the syntax now allows values
with leading zeroes where it didn't before.

e.g. 021, 005, etc.
Comment 4 Eli Fidler 2007-04-24 21:18:37 UTC
I could stop that, but those look like valid numbers to me.
Comment 5 Andrew Walker 2007-04-24 21:20:33 UTC
I agree that they should probably be considered valid. It might be a good idea to make this explicit by adding appropriate test cases.
Comment 6 Netterfield 2007-04-26 16:54:12 UTC
The only worry about 051 is that some people might want this to mean octal 51.  Not a big deal.

Does the number get reformatted when you come back to edit it?
Comment 7 Andrew Walker 2007-04-30 20:21:59 UTC
Given the octal argument I think we probably should use a more complex regular expression to exclude the leading zeroes.
Comment 8 D. V. Wiebe 2007-04-30 22:52:55 UTC
I disagree.  Unless kst is going to explicitly consider 051 an octal number---an unreasonable thing to do, in my opinion---I see no reason for disallowing leading zeros.  kst should behave like, say, kcalc which takes 051 to mean decimal 51.
Comment 9 George Staikos 2007-05-01 05:13:09 UTC
On 26-Apr-07, at 10:54 AM, netterfield@astro.utoronto.ca wrote:

> The only worry about 051 is that some people might want this to  
> mean octal 51.  Not a big deal.


   Agreed, allowing 051 is ugly for this reason and also because it  
could actually be a typo (501 vs 051, 15+05 vs 150+5) which would  
originally have thrown an error but now does not.  That's why the  
regexp used to explicitly forbid it.  I'm not a fan of this change.

> Does the number get reformatted when you come back to edit it?


   No

--
George Staikos
KDE Developer				http://www.kde.org/
Staikos Computing Services Inc.		http://www.staikos.net/
Comment 10 Andrew Walker 2007-05-02 01:40:09 UTC
While we're worrying about leading zeroes its probably also worth noting that the regexp accepts 2e6 and 2.0e6 but not 2.e6

Comment 11 Netterfield 2007-05-02 05:28:10 UTC
So: all plausible numbers should be accepted as decimal numbers as you would 
with a calculator: so:
054.0
54.
54.e-3
54.e-03
54.0e-3

etc...

On Tuesday 01 May 2007 7:40:10 pm Andrew Walker wrote:
[bugs.kde.org quoted mail]
Comment 12 Eli Fidler 2007-05-03 20:38:01 UTC
Created attachment 20472 [details]
proposed patch for Barth's number format
Comment 13 Andrew Walker 2007-05-03 20:55:17 UTC
The second test case you added was:

test("0.10", 0.0, 1.0);

while you meant:

test("0.10", 0.0, 0.1);

Andrew

On Thursday 03 May 2007 11:38, Eli Fidler wrote:
[bugs.kde.org quoted mail]
Comment 14 Eli Fidler 2007-05-03 22:31:31 UTC
Created attachment 20473 [details]
fixed patch
Comment 15 Eli Fidler 2007-05-03 22:31:55 UTC
Andrew, you're right. I added some better tests for E+5 and E-5, which actually do parse.
Comment 16 Andrew Walker 2007-05-03 23:20:48 UTC
Looks good to me, Eli. Please apply it to 1.5 branch. Thanks.
Comment 17 George Staikos 2007-05-04 16:39:44 UTC
Looks fine for trunk.  Please apply.

--
George Staikos
KDE Developer				http://www.kde.org/
Staikos Computing Services Inc.		http://www.staikos.net/
Comment 18 Andrew Walker 2007-05-08 20:34:29 UTC
SVN commit 662614 by arwalker:

CCBUG:144464 Check in change to definition of number regexp for 1.5 branch

 M  +58 -59    escan.c  
 M  +1 -2      escan.l  


--- branches/work/kst/1.5/kst/src/libkstmath/escan.c #662613:662614
@@ -386,7 +386,7 @@
 	flex_int32_t yy_verify;
 	flex_int32_t yy_nxt;
 	};
-static yyconst flex_int16_t yy_acclist[79] =
+static yyconst flex_int16_t yy_acclist[80] =
     {   0,
        30,   28,   29,   27,   28,   29,   29,    6,   28,   29,
        22,   28,   29,    7,   28,   29,   24,   28,   29,   25,
@@ -395,7 +395,7 @@
        28,   29,   11,   28,   29,   15,   28,   29,   13,   28,
        29,    2,   28,   29,    4,   28,   29,    5,   28,   29,
        23,   28,   29,    8,   28,   29,    3,   29,   17,    9,
-        1,    1,   12,   16,   14,    2,   10,    1
+        1,    1,    1,   12,   16,   14,    2,   10,    1
     } ;
 
 static yyconst flex_int16_t yy_accept[45] =
@@ -403,8 +403,8 @@
         1,    1,    1,    1,    1,    2,    4,    7,    8,   11,
        14,   17,   20,   23,   26,   29,   32,   35,   37,   40,
        43,   46,   49,   52,   55,   58,   61,   64,   67,   69,
-       70,   71,   72,   72,   73,   73,   74,   75,   76,   77,
-       78,   78,   79,   79
+       70,   71,   72,   73,   74,   74,   75,   76,   77,   78,
+       79,   79,   80,   80
     } ;
 
 static yyconst flex_int32_t yy_ec[256] =
@@ -448,11 +448,11 @@
 
 static yyconst flex_int16_t yy_base[45] =
     {   0,
-        0,    0,   50,   49,   51,   54,   54,   54,   33,   54,
-       43,   54,   54,   54,   54,   54,   54,   33,   54,   12,
-       30,   29,   28,    9,   54,   54,   54,   20,   54,   54,
-       54,   11,   28,   20,   24,   54,   54,   54,   18,   54,
-       27,   26,   54,   29
+        0,    0,   51,   50,   52,   55,   55,   55,   34,   55,
+       44,   55,   55,   55,   55,   55,   55,   34,   55,   12,
+       31,   30,   29,    9,   55,   55,   55,   21,   55,   55,
+       55,   11,   15,   21,   27,   55,   55,   55,   24,   55,
+       25,   23,   55,   32
     } ;
 
 static yyconst flex_int16_t yy_def[45] =
@@ -464,28 +464,28 @@
        43,   43,    0,   43
     } ;
 
-static yyconst flex_int16_t yy_nxt[79] =
+static yyconst flex_int16_t yy_nxt[80] =
     {   0,
         6,    7,    8,    9,   10,   11,   12,   13,   14,   15,
        16,   17,   18,   19,   20,   21,   22,   23,   24,   24,
-       25,   26,   27,   28,   33,   32,   34,   39,   39,   29,
-       35,   35,   33,   41,   34,   41,   39,   39,   42,   35,
-       42,   42,   32,   40,   38,   37,   36,   32,   31,   30,
-       43,    8,    8,    5,   43,   43,   43,   43,   43,   43,
+       25,   26,   27,   28,   33,   32,   34,   39,   39,   32,
+       35,   35,   29,   33,   35,   34,   41,   42,   41,   42,
+       35,   42,   39,   39,   40,   38,   37,   36,   32,   31,
+       30,   43,    8,    8,    5,   43,   43,   43,   43,   43,
        43,   43,   43,   43,   43,   43,   43,   43,   43,   43,
-       43,   43,   43,   43,   43,   43,   43,   43
+       43,   43,   43,   43,   43,   43,   43,   43,   43
     } ;
 
-static yyconst flex_int16_t yy_chk[79] =
+static yyconst flex_int16_t yy_chk[80] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
-        1,    1,    1,    1,   20,   32,   20,   24,   24,   44,
-       32,   20,   34,   35,   34,   35,   39,   39,   35,   34,
-       42,   41,   33,   28,   23,   22,   21,   18,   11,    9,
-        5,    4,    3,   43,   43,   43,   43,   43,   43,   43,
+        1,    1,    1,    1,   20,   32,   20,   24,   24,   33,
+       32,   20,   44,   34,   33,   34,   35,   42,   35,   41,
+       34,   35,   39,   39,   28,   23,   22,   21,   18,   11,
+        9,    5,    4,    3,   43,   43,   43,   43,   43,   43,
        43,   43,   43,   43,   43,   43,   43,   43,   43,   43,
-       43,   43,   43,   43,   43,   43,   43,   43
+       43,   43,   43,   43,   43,   43,   43,   43,   43
     } ;
 
 /* Table of booleans, true if rule could match eol. */
@@ -537,8 +537,7 @@
 char *dataSpecPtr;
 void yyerror(const char *s);
 
-/*Number	(0|[1-9][0-9]*)([\.][0-9]+)?([eE][\+\-][0-9]+)?*/
-#line 542 "<stdout>"
+#line 541 "<stdout>"
 
 #define INITIAL 0
 #define DATA 1
@@ -688,10 +687,10 @@
 	register char *yy_cp, *yy_bp;
 	register int yy_act;
     
-#line 20 "escan.l"
+#line 19 "escan.l"
 
 
-#line 695 "<stdout>"
+#line 694 "<stdout>"
 
 	if ( (yy_init) )
 		{
@@ -754,7 +753,7 @@
 			*(yy_state_ptr)++ = yy_current_state;
 			++yy_cp;
 			}
-		while ( yy_base[yy_current_state] != 54 );
+		while ( yy_base[yy_current_state] != 55 );
 
 yy_find_action:
 		yy_current_state = *--(yy_state_ptr);
@@ -793,7 +792,7 @@
 	{ /* beginning of action switch */
 case 1:
 YY_RULE_SETUP
-#line 22 "escan.l"
+#line 21 "escan.l"
 {
 			yylval.number = atof(yytext);
 			/*printf("Found a number %.15f\n", yylval.number);*/
@@ -802,7 +801,7 @@
 	YY_BREAK
 case 2:
 YY_RULE_SETUP
-#line 29 "escan.l"
+#line 28 "escan.l"
 {
 			yylval.data = strdup(yytext);
 			/*printf("Found an ID [%s]\n", yylval.data);*/
@@ -811,7 +810,7 @@
 	YY_BREAK
 case 3:
 YY_RULE_SETUP
-#line 35 "escan.l"
+#line 34 "escan.l"
 {
 			switch (*yytext) {
 				case '[':
@@ -843,7 +842,7 @@
 		}
 	YY_BREAK
 case YY_STATE_EOF(DATA):
-#line 65 "escan.l"
+#line 64 "escan.l"
 {
 			yyerror("Invalid data reference.");
 			yy_flush_buffer(YY_CURRENT_BUFFER);
@@ -853,7 +852,7 @@
 	YY_BREAK
 case 4:
 YY_RULE_SETUP
-#line 72 "escan.l"
+#line 71 "escan.l"
 {
 			bracketCount = 1;
 			dataSpecPtr = dataSpec;
@@ -863,7 +862,7 @@
 	YY_BREAK
 case 5:
 YY_RULE_SETUP
-#line 79 "escan.l"
+#line 78 "escan.l"
 {
 			yyerror("Unmatched ']'.");
 			yy_flush_buffer(YY_CURRENT_BUFFER);
@@ -872,117 +871,117 @@
 	YY_BREAK
 case 6:
 YY_RULE_SETUP
-#line 85 "escan.l"
+#line 84 "escan.l"
 return T_NOT;
 	YY_BREAK
 case 7:
 YY_RULE_SETUP
-#line 87 "escan.l"
+#line 86 "escan.l"
 return T_AND;
 	YY_BREAK
 case 8:
 YY_RULE_SETUP
-#line 89 "escan.l"
+#line 88 "escan.l"
 return T_OR;
 	YY_BREAK
 case 9:
 YY_RULE_SETUP
-#line 91 "escan.l"
+#line 90 "escan.l"
 return T_LAND;
 	YY_BREAK
 case 10:
 YY_RULE_SETUP
-#line 93 "escan.l"
+#line 92 "escan.l"
 return T_LOR;
 	YY_BREAK
 case 11:
 YY_RULE_SETUP
-#line 95 "escan.l"
+#line 94 "escan.l"
 return T_LT;
 	YY_BREAK
 case 12:
 YY_RULE_SETUP
-#line 97 "escan.l"
+#line 96 "escan.l"
 return T_LE;
 	YY_BREAK
 case 13:
 YY_RULE_SETUP
-#line 99 "escan.l"
+#line 98 "escan.l"
 return T_GT;
 	YY_BREAK
 case 14:
 YY_RULE_SETUP
-#line 101 "escan.l"
+#line 100 "escan.l"
 return T_GE;
 	YY_BREAK
 case 15:
 YY_RULE_SETUP
-#line 103 "escan.l"
+#line 102 "escan.l"
 return T_EQ;
 	YY_BREAK
 case 16:
 YY_RULE_SETUP
-#line 105 "escan.l"
+#line 104 "escan.l"
 return T_EQ;
 	YY_BREAK
 case 17:
 YY_RULE_SETUP
-#line 107 "escan.l"
+#line 106 "escan.l"
 return T_NE;
 	YY_BREAK
 case 18:
 YY_RULE_SETUP
-#line 109 "escan.l"
+#line 108 "escan.l"
 return T_ADD;
 	YY_BREAK
 case 19:
 YY_RULE_SETUP
-#line 111 "escan.l"
+#line 110 "escan.l"
 return T_SUBTRACT;
 	YY_BREAK
 case 20:
 YY_RULE_SETUP
-#line 113 "escan.l"
+#line 112 "escan.l"
 return T_MULTIPLY;
 	YY_BREAK
 case 21:
 YY_RULE_SETUP
-#line 115 "escan.l"
+#line 114 "escan.l"
 return T_DIVIDE;
 	YY_BREAK
 case 22:
 YY_RULE_SETUP
-#line 117 "escan.l"
+#line 116 "escan.l"
 return T_MOD;
 	YY_BREAK
 case 23:
 YY_RULE_SETUP
-#line 119 "escan.l"
+#line 118 "escan.l"
 return T_EXP;
 	YY_BREAK
 case 24:
 YY_RULE_SETUP
-#line 121 "escan.l"
+#line 120 "escan.l"
 return T_OPENPAR;
 	YY_BREAK
 case 25:
 YY_RULE_SETUP
-#line 123 "escan.l"
+#line 122 "escan.l"
 return T_CLOSEPAR;
 	YY_BREAK
 case 26:
 YY_RULE_SETUP
-#line 125 "escan.l"
+#line 124 "escan.l"
 return T_COMMA;
 	YY_BREAK
 case 27:
 YY_RULE_SETUP
-#line 127 "escan.l"
+#line 126 "escan.l"
 {}
 	YY_BREAK
 case 28:
 YY_RULE_SETUP
-#line 129 "escan.l"
+#line 128 "escan.l"
 {
 			/*printf("Found char '%c'\n", yytext[0]);*/
 			yylval.character = yytext[0];
@@ -990,7 +989,7 @@
 		}
 	YY_BREAK
 case YY_STATE_EOF(INITIAL):
-#line 136 "escan.l"
+#line 135 "escan.l"
 {
 			/*printf("yyterminate\n");*/
 			yyterminate();
@@ -998,10 +997,10 @@
 	YY_BREAK
 case 29:
 YY_RULE_SETUP
-#line 141 "escan.l"
+#line 140 "escan.l"
 ECHO;
 	YY_BREAK
-#line 1005 "<stdout>"
+#line 1004 "<stdout>"
 
 	case YY_END_OF_BUFFER:
 		{
@@ -1930,7 +1929,7 @@
 
 #define YYTABLES_NAME "yytables"
 
-#line 141 "escan.l"
+#line 140 "escan.l"
 
 
 
--- branches/work/kst/1.5/kst/src/libkstmath/escan.l #662613:662614
@@ -13,8 +13,7 @@
 
 %}
 
- /*Number	(0|[1-9][0-9]*)([\.][0-9]+)?([eE][\+\-][0-9]+)?*/
-Number	[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)? 
+Number	([0-9]*\.?[0-9]+|[0-9]+\.)([eE][-+]?[0-9]+)?
 Id	[A-Za-z]+
 
 %%
Comment 19 Andrew Walker 2007-05-08 20:38:32 UTC
SVN commit 662616 by arwalker:

BUG:144464 Checkin Eli's fix for number regexp to 1.5 branch

 M  +83 -2     testeqparser.cpp  


--- branches/work/kst/1.5/kst/tests/testeqparser.cpp #662615:662616
@@ -109,6 +109,58 @@
 }
 
 
+bool doTestNotEqual(const char *equation, double x, double result, const double tol = 0.00000000001) {
+  yy_scan_string(equation);
+  int rc = yyparse();
+  if (rc == 0) {
+    vectorsUsed.clear();
+    Equation::Node *eq = static_cast<Equation::Node*>(ParsedEquation);
+    assert(eq);
+    ParsedEquation = 0L;
+    Equation::Context ctx;
+    ctx.sampleCount = 2;
+    ctx.noPoint = NOPOINT;
+    ctx.x = x;
+    ctx.xVector = xVector;
+    if (xVector) {
+      ctx.sampleCount = xVector->length();
+    }
+    Equation::FoldVisitor vis(&ctx, &eq);
+    if (eq->isConst() && !dynamic_cast<Equation::Number*>(eq)) {
+      if (!optimizerFailed) {
+        optimizerFailed = true;
+        ::rc--;
+        printf("Optimizer bug: found an unoptimized const equation.  Optimizing for coverage purposes.\n");
+      }
+      double v = eq->value(&ctx);
+      delete eq;
+      eq = new Equation::Number(v);
+    }
+    KstScalarMap scm;
+    KstStringMap stm;
+    eq->collectObjects(vectorsUsed, scm, stm);
+    eq->update(-1, &ctx);
+    double v = eq->value(&ctx);
+    delete eq;
+    if (fabs(v - result) < tol || (result != result && v != v) || (result == INF && v == INF) || (result == -INF && v == -INF)) {
+      printf("Result: %.16f\n", v);
+      return false;
+    } else {
+      return true;
+    }
+  } else {
+    // Parse error
+    printf("Failures on [%s] -------------------------\n", equation);
+    for (QStringList::ConstIterator i = Equation::errorStack.constBegin(); i != Equation::errorStack.constEnd(); ++i) {
+      printf("%s\n", (*i).latin1());
+    }
+    printf("------------------------------------------\n");
+    delete (Equation::Node*)ParsedEquation;
+    ParsedEquation = 0L;
+    return false;
+  }
+}
+
 void test(const char *equation, double x, double result, const double tol = 0.00000000001) {
   if (!doTest(equation, x, result, tol)) {
     rc--;
@@ -117,6 +169,14 @@
 }
 
 
+void testNotEqual(const char *equation, double x, double result, const double tol = 0.00000000001) {
+  if (!doTestNotEqual(equation, x, result, tol)) {
+    rc--;
+    printf("Test of (%s)[%.16f] != %.16f failed.\n", equation, x, result);
+  }
+}
+
+
 void testParseFail(const char *equation) {
   yy_scan_string(equation);
   if (0 == yyparse()) {
@@ -165,6 +225,29 @@
   test("1E+1", 0.0, 1E+1);
   test("1E-1", 0.0, 1E-1);
   test("0.2e1", 0.0, 0.2e1);
+  test("01", 0.0, 1);
+  test("0.10", 0.0, 0.1);
+  test("01.", 0.0, 1);
+  test("1.", 0.0, 1.0);
+  test("1.0E5", 0.0, 1.0E5);
+  test("1.0E+5", 0.0, 1.0E+5);
+  test("1.0E-5", 0.0, 1.0E-5);
+  test("1.E5", 0.0, 1.0E5);
+  test("1.E+5", 0.0, 1.0E+5);
+  test("1.E-5", 0.0, 1.0E-5);
+  test("0.1000", 0.0, 0.1000);
+  test(".1000", 0.0, .1000);
+  testNotEqual("E+5", 0.0, 0e5);  // this will actually be 2.7128182846 + 5
+  testNotEqual("E+5", 0.0, 1e5);  // this will actually be 2.7182882846 + 5
+  testNotEqual("E-5", 0.0, 0e-5); // this will actually be 2.7182882846 - 5
+  testNotEqual("E-5", 0.0, 1e-5); // this will actually be 2.7182882846 - 5
+  testParseFail(".");
+  testParseFail("E5");
+  testParseFail(".E5");
+  testParseFail("1E5.0");
+  testParseFail("1E+5.0");
+  testParseFail("1E5.");
+  testParseFail("1E+5.");
 
   // Basics
   test("x", -1.0, -1.0);
@@ -486,8 +569,6 @@
   testParseFail("2<=");
   testParseFail("2<=<=2");
   testParseFail("2<==2");
-  testParseFail(".");
-  testParseFail("2.");
   testParseFail(",");
   testParseFail(",2");
   testParseFail("2,"); // Doesn't give a specific error - how to catch this?