[units] Be more strict in the grammar we are parsing

Current parsing of units has a number of shortcomings:
  * a number followed by trailing space (without any unit specified) was
    not recognized,
  * "5 emeralds" was parsed as 5em,
  * the way we parse the digits after the separator makes us lose
    precision for no good reason (5.0 is parsed as 5.00010014...f which
    makes g_assert_cmpfloat() fail)

Let's define a stricter grammar we can recognize and try to do so. The
description is in EBNF form, removing the optional <> which is a pain
when having to write DocBook, and using '' for the terminal symbols.

Last step, add more ClutterUnits unit test to get a better coverage of
the grammar we want to parse.

Reviewed-by: Emmanuele Bassi <ebassi@linux.intel.com>
This commit is contained in:
Damien Lespiau 2009-10-06 12:02:15 +01:00
parent 4d481e03f3
commit 96859959bd
2 changed files with 70 additions and 28 deletions

View File

@ -362,10 +362,14 @@ clutter_units_to_pixels (ClutterUnits *units)
* A #ClutterUnits expressed in string should match:
*
* |[
* number: [0-9]
* unit_value: &lt;number&gt;+
* unit_name: px|pt|mm|em
* units: &lt;unit_value&gt; &lt;unit_name&gt;
* units: wsp* unit-value wsp* unit-name? wsp*
* unit-value: number
* unit-name: 'px' | 'pt' | 'mm' | 'em'
* number: digit+
* | digit* sep digit+
* sep: '.' | ','
* digit: '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
* wsp: (#0x20 | #0x9 | #0xA | #0xB | #0xC | #0xD)+
* ]|
*
* For instance, these are valid strings:
@ -375,6 +379,7 @@ clutter_units_to_pixels (ClutterUnits *units)
* 5.1 em
* 24 pt
* 12.6 mm
* .3 cm
* ]|
*
* While these are not:
@ -401,51 +406,66 @@ clutter_units_from_string (ClutterUnits *units,
g_return_val_if_fail (units != NULL, FALSE);
g_return_val_if_fail (str != NULL, FALSE);
/* Ensure that the first character is a digit */
/* strip leading space */
while (g_ascii_isspace (*str))
str++;
if (*str == '\0')
return FALSE;
if (!g_ascii_isdigit (*str))
return FALSE;
/* integer part */
value = (gfloat) strtoul (str, (char **) &str, 10);
if (*str == '.' || *str == ',')
{
glong frac = 100000;
gfloat divisor = 0.1;
while (g_ascii_isdigit (*++str))
/* 5.cm is not a valid number */
if (!g_ascii_isdigit (*++str))
return FALSE;
while (g_ascii_isdigit (*str))
{
frac += (*str - '0') * frac;
frac /= 10;
value += (*str - '0') * divisor;
divisor *= 0.1;
str++;
}
value += (1.0f / (gfloat) frac);
}
while (g_ascii_isspace (*str))
str++;
/* assume pixels by default, if no unit is specified */
if (*str == '\0')
unit_type = CLUTTER_UNIT_PIXEL;
else
else if (strncmp (str, "em", 2) == 0)
{
while (g_ascii_isspace (*str))
str++;
if (strncmp (str, "em", 2) == 0)
unit_type = CLUTTER_UNIT_EM;
else if (strncmp (str, "mm", 2) == 0)
unit_type = CLUTTER_UNIT_MM;
else if (strncmp (str, "pt", 2) == 0)
unit_type = CLUTTER_UNIT_POINT;
else if (strncmp (str, "px", 2) == 0)
unit_type = CLUTTER_UNIT_PIXEL;
else
return FALSE;
unit_type = CLUTTER_UNIT_EM;
str += 2;
}
else if (strncmp (str, "mm", 2) == 0)
{
unit_type = CLUTTER_UNIT_MM;
str += 2;
}
else if (strncmp (str, "pt", 2) == 0)
{
unit_type = CLUTTER_UNIT_POINT;
str += 2;
}
else if (strncmp (str, "px", 2) == 0)
{
unit_type = CLUTTER_UNIT_PIXEL;
str += 2;
}
else
return FALSE;
/* ensure the unit is only followed by white space */
while (g_ascii_isspace (*str))
str++;
if (*str != '\0')
return FALSE;
units->unit_type = unit_type;
units->value = value;

View File

@ -31,14 +31,36 @@ test_units_string (TestConformSimpleFixture *fixture,
g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_PIXEL);
g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 10);
g_assert (clutter_units_from_string (&units, "10 ") == TRUE);
g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_PIXEL);
g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 10);
g_assert (clutter_units_from_string (&units, "5 em") == TRUE);
g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_EM);
g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 5);
g_assert (clutter_units_from_string (&units, "5 emeralds") == FALSE);
g_assert (clutter_units_from_string (&units, " 16 mm") == TRUE);
g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_MM);
g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 16);
g_assert (clutter_units_from_string (&units, " 24 pt ") == TRUE);
g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_POINT);
g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 24);
g_assert (clutter_units_from_string (&units, " 32 em garbage") == FALSE);
g_assert (clutter_units_from_string (&units, "5.1mm") == TRUE);
g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_MM);
g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 5.1f);
g_assert (clutter_units_from_string (&units, "5,mm") == FALSE);
g_assert (clutter_units_from_string (&units, ".5pt") == TRUE);
g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_POINT);
g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 0.5f);
g_assert (clutter_units_from_string (&units, "1 pony") == FALSE);
clutter_units_from_pt (&units, 24.0);