/*
This file is part of darktable,
copyright (c) 2013 tobias ellinghaus.
darktable 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.
darktable 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 darktable. If not, see .
*/
#include
#include
#include
#include
typedef enum token_types_t
{
T_NUMBER, // everything will be treated as floats
T_OPERATOR
} token_types_t;
typedef enum operators_t
{
O_PLUS,
O_INC,
O_MINUS,
O_DEC,
O_MULTIPLY,
O_DIVISION,
O_MODULO,
O_POWER,
O_LEFTROUND,
O_RIGHTROUND,
} operators_t;
typedef union token_data_t
{
float number;
operators_t operator;
} token_data_t;
typedef struct token_t
{
token_types_t type;
token_data_t data;
} token_t;
typedef struct parser_state_t
{
char *p;
float x;
token_t *token;
} parser_state_t;
/** the scanner **/
static float read_number(parser_state_t *self)
{
return g_ascii_strtod(self->p, &self->p);
}
static token_t *get_token(parser_state_t *self)
{
if(!self->p) return NULL;
token_t *token = (token_t *)malloc(sizeof(token_t));
for(; *self->p; self->p++)
{
switch(*self->p)
{
case ' ':
case '\t':
continue;
case '+':
if(self->p[1] == '+')
{
self->p += 2;
token->data.operator= O_INC;
}
else
{
self->p++;
token->data.operator= O_PLUS;
}
token->type = T_OPERATOR;
return token;
case '-':
if(self->p[1] == '-')
{
self->p += 2;
token->data.operator= O_DEC;
}
else
{
self->p++;
token->data.operator= O_MINUS;
}
token->type = T_OPERATOR;
return token;
case '*':
self->p++;
token->type = T_OPERATOR;
token->data.operator= O_MULTIPLY;
return token;
case '/':
self->p++;
token->type = T_OPERATOR;
token->data.operator= O_DIVISION;
return token;
case '%':
self->p++;
token->type = T_OPERATOR;
token->data.operator= O_MODULO;
return token;
case '^':
self->p++;
token->type = T_OPERATOR;
token->data.operator= O_POWER;
return token;
case '(':
self->p++;
token->type = T_OPERATOR;
token->data.operator= O_LEFTROUND;
return token;
case ')':
self->p++;
token->type = T_OPERATOR;
token->data.operator= O_RIGHTROUND;
return token;
case 'x':
case 'X':
self->p++;
token->type = T_NUMBER;
token->data.number = self->x;
return token;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
case ',':
{
token->data.number = read_number(self);
token->type = T_NUMBER;
return token;
}
default:
// people complained about the messages when "TRUE" was fed to the calculator
// printf("error: %c\n", *self->p);
break;
}
}
free(token);
return NULL;
}
/** the parser **/
static float parse_expression(parser_state_t *self);
static float parse_additive_expression(parser_state_t *self);
static float parse_multiplicative_expression(parser_state_t *self);
static float parse_power_expression(parser_state_t *self);
static float parse_unary_expression(parser_state_t *self);
static float parse_primary_expression(parser_state_t *self);
static float parse_expression(parser_state_t *self)
{
return parse_additive_expression(self);
}
static float parse_additive_expression(parser_state_t *self)
{
float left, right;
if(!self->token) return NAN;
left = parse_multiplicative_expression(self);
while(self->token && self->token->type == T_OPERATOR)
{
operators_t operator= self->token->data.operator;
if(operator!= O_PLUS &&operator!= O_MINUS) return left;
free(self->token);
self->token = get_token(self);
right = parse_multiplicative_expression(self);
if(operator== O_PLUS)
left += right;
else if(operator== O_MINUS)
left -= right;
}
return left;
}
static float parse_multiplicative_expression(parser_state_t *self)
{
float left, right;
if(!self->token) return NAN;
left = parse_power_expression(self);
while(self->token && self->token->type == T_OPERATOR)
{
operators_t operator= self->token->data.operator;
if(operator!= O_MULTIPLY &&operator!= O_DIVISION &&operator!= O_MODULO) return left;
free(self->token);
self->token = get_token(self);
right = parse_power_expression(self);
if(operator== O_MULTIPLY)
left *= right;
else if(operator== O_DIVISION)
left /= right;
else if(operator== O_MODULO)
left = fmodf(left, right);
}
return left;
}
static float parse_power_expression(parser_state_t *self)
{
float left, right;
if(!self->token) return NAN;
left = parse_unary_expression(self);
while(self->token && self->token->type == T_OPERATOR)
{
if(self->token->data.operator!= O_POWER) return left;
free(self->token);
self->token = get_token(self);
right = parse_unary_expression(self);
left = powf(left, right);
}
return left;
}
static float parse_unary_expression(parser_state_t *self)
{
if(!self->token) return NAN;
if(self->token->type == T_OPERATOR)
{
if(self->token->data.operator== O_MINUS)
{
free(self->token);
self->token = get_token(self);
return -1.0 * parse_unary_expression(self);
}
if(self->token->data.operator== O_PLUS)
{
free(self->token);
self->token = get_token(self);
return parse_unary_expression(self);
}
}
return parse_primary_expression(self);
}
static float parse_primary_expression(parser_state_t *self)
{
if(!self->token) return NAN;
if(self->token->type == T_NUMBER)
{
float result = self->token->data.number;
free(self->token);
self->token = get_token(self);
return result;
}
if(self->token->type == T_OPERATOR && self->token->data.operator== O_LEFTROUND)
{
float result;
free(self->token);
self->token = get_token(self);
result = parse_expression(self);
if(!self->token || self->token->type != T_OPERATOR || self->token->data.operator!= O_RIGHTROUND)
return NAN;
free(self->token);
self->token = get_token(self);
return result;
}
return NAN;
}
/** the public interface **/
float dt_calculator_solve(float x, const char *formula)
{
if(formula == NULL || *formula == '\0') return NAN;
float result;
gchar *dotformula = g_strdup(formula);
parser_state_t *self = (parser_state_t *)malloc(sizeof(parser_state_t));
self->p = g_strdelimit(dotformula, ",", '.');
self->x = x;
self->token = get_token(self);
// operators_t operator = -1;
if(self->token && self->token->type == T_OPERATOR)
{
switch(self->token->data.operator)
{
case O_INC:
result = x + 1.0;
goto end;
case O_DEC:
result = x - 1.0;
goto end;
// case O_PLUS:
// case O_MINUS:
// case O_MULTIPLY:
// case O_DIVISION:
// case O_MODULO:
// case O_POWER:
// operator = self->token->data.operator;
// free(self->token);
// self->token = get_token(self);
// break;
default:
break;
}
}
result = parse_expression(self);
// switch(operator)
// {
// case O_PLUS: result = x + res; break;
// case O_MINUS: result = x - res; break;
// case O_MULTIPLY: result = x * res; break;
// case O_DIVISION: result = x / res; break;
// case O_MODULO: result = fmodf(x, res); break;
// case O_POWER: result = powf(x, res); break;
// default: break;
// }
if(self->token) result = NAN;
end:
free(self->token);
free(self);
g_free(dotformula);
return result;
}
// int main()
// {
// const char *input = "5/0";
// float x = 3;
//
// printf("%s\n", input);
//
// float res = dt_calculator_solve(x, input);
//
// printf("%f\n", res);
//
// return 0;
// }
// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
// vim: shiftwidth=2 expandtab tabstop=2 cindent
// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-space on;