expression-evaluator.cpp revision 23d10530cfbacb04216e386441b4f64e2f6018d3
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* Original file from libgimpwidgets: gimpeevl.c
* Copyright (C) 2008 Fredrik Alstromer <roe@excu.se>
* Copyright (C) 2008 Martin Nordholts <martinn@svn.gnome.org>
* Modified for Inkscape by Johan Engelen
* Copyright (C) 2011 Johan Engelen
*
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
*/
/** Introducing eevl eva, the evaluator. A straightforward recursive
* descent parser, no fuss, no new dependencies. The lexer is hand
* coded, tedious, not extremely fast but works. It evaluates the
* expression as it goes along, and does not create a parse tree or
* anything, and will not optimize anything. It uses doubles for
* precision, with the given use case, that's enough to combat any
* rounding errors (as opposed to optimizing the evalutation).
*
* It relies on external unit resolving through a callback and does
* elementary dimensionality constraint check (e.g. "2 mm + 3 px * 4
* in" is an error, as L + L^2 is a missmatch). It uses g_strtod() for numeric
* conversions and it's non-destructive in terms of the paramters, and
* it's reentrant.
*
* EBNF:
*
* expression ::= term { ('+' | '-') term }* |
* <empty string> ;
*
* term ::= signed factor { ( '*' | '/' ) signed factor }* ;
*
* signed factor ::= ( '+' | '-' )? factor ;
*
* unit factor ::= factor unit? ;
*
* factor ::= number | '(' expression ')' ;
*
* number ::= ? what g_strtod() consumes ? ;
*
* unit ::= ? what not g_strtod() consumes and not whitespace ? ;
*
* The code should match the EBNF rather closely (except for the
* non-terminal unit factor, which is inlined into factor) for
* maintainability reasons.
*
* It will allow 1++1 and 1+-1 (resulting in 2 and 0, respectively),
* but I figured one might want that, and I don't think it's going to
* throw anyone off.
*/
#include "config.h"
#include "util/expression-evaluator.h"
#include <string.h>
namespace Inkscape {
namespace Util {
enum
{
GIMP_EEVL_TOKEN_NUM = 30000,
GIMP_EEVL_TOKEN_IDENTIFIER = 30001,
GIMP_EEVL_TOKEN_ANY = 40000,
GIMP_EEVL_TOKEN_END = 50000
};
typedef int GimpEevlTokenType;
typedef struct
{
union
{
struct
{
const gchar *c;
};
} value;
typedef struct
{
const gchar *start_of_current_token;
} GimpEevl;
/** Unit Resolver...
*/
{
static UnitTable unit_table;
if (!unit) {
return true;
}else if (!identifier) {
return true;
// Catch the case of zero or negative unit factors (error!)
return false;
}
return true;
} else {
return false;
}
}
const char *msg);
/**
* Evaluates the given arithmetic expression, along with an optional dimension
* analysis, and basic unit conversions.
*
* @param string The NULL-terminated string to be evaluated.
* @param unit_resolver_proc Unit resolver callback.
*
* All units conversions factors are relative to some implicit
* base-unit (which in GIMP is inches). This is also the unit of the
* returned value.
*
* Returns: A #GimpEevlQuantity with a value given in the base unit along with
* the order of the dimension (i.e. if the base unit is inches, a dimension
* order of two menas in^2).
*
* @return Result of evaluation.
* @throws Inkscape::Util::EvaluatorException There was a parse error.
**/
{
}
return gimp_eevl_complete(&eva);
}
static void
{
/* Preload symbol... */
gimp_eevl_lex (eva);
}
static GimpEevlQuantity
{
GimpEevlQuantity result = {0, 0};
/* Empty expression evaluates to 0 */
return result;
/* There should be nothing left to parse by now */
/* Entire expression is dimensionless, apply default unit if
* applicable
*/
{
}
return result;
}
static GimpEevlQuantity
{
/* continue evaluating terms, chained with + or -. */
{
/* If dimensions missmatch, attempt default unit assignent */
{
{
}
else if (evaluated_terms.dimension == 0 &&
{
}
else
{
}
}
}
return evaluated_terms;
}
static GimpEevlQuantity
{
{
if (division)
{
}
else
{
}
}
return evaluated_signed_factors;
}
static GimpEevlQuantity
{
return result;
}
static GimpEevlQuantity
{
GimpEevlQuantity evaluated_factor = { 0, 0 };
if (gimp_eevl_accept (eva,
{
}
{
}
else
{
}
{
&result,
{
}
else
{
}
}
return evaluated_factor;
}
static gboolean
{
{
if (consumed_token)
/* Parse next token */
gimp_eevl_lex (eva);
}
return existed;
}
static void
{
const gchar *s;
eva->start_of_current_token = s;
if (! s || s[0] == '\0')
{
/* We're all done */
}
else if (s[0] == '+' || s[0] == '-')
{
/* Snatch these before the g_strtod() does, othewise they might
* be used in a numeric conversion.
*/
}
else
{
/* Attempt to parse a numeric value */
{
/* A numeric could be parsed, use it */
}
else if (gimp_eevl_unit_identifier_start (s[0]))
{
/* Unit identifier */
}
else
{
/* Everything else is a single character token */
}
}
}
static void
{
}
static void
{
}
static void
{
return;
}
static gboolean
{
return (g_unichar_isalpha (c) ||
c == (gunichar) '%' ||
c == (gunichar) '\'');
}
static gboolean
{
return (gimp_eevl_unit_identifier_start (c) ||
g_unichar_isdigit (c));
}
/**
* gimp_eevl_unit_identifier_size:
* @s:
* @start:
*
* Returns: Size of identifier in bytes (not including NULL
* terminator).
**/
static gint
{
gunichar c = g_utf8_get_char (s);
if (gimp_eevl_unit_identifier_start (c))
{
s = g_utf8_next_char (s);
c = g_utf8_get_char (s);
length++;
while (gimp_eevl_unit_identifier_continue (c))
{
s = g_utf8_next_char (s);
c = g_utf8_get_char (s);
length++;
}
}
}
static void
{
}
static void
const char *msg)
{
}
} // namespace Util
} // namespace Inkscape