/*
    File                 : parser.ypp
    Project              : LabPlot
    Description          : Parser for mathematical expressions
    --------------------------------------------------------------------
    SPDX-FileCopyrightText: 2014 Alexander Semke <alexander.semke@web.de>
    SPDX-FileCopyrightText: 2014-2020 Stefan Gerlach <stefan.gerlach@uni.kn>
    SPDX-License-Identifier: GPL-2.0-or-later
*/

%{
#include <cstring>
#include <cctype>
#include <cstdlib>
#include <clocale>
#include <cmath>
#ifdef HAVE_XLOCALE
#include <xlocale.h>
#endif
#include "parser.h"
#include "constants.h"
#include "functions.h"
#if defined(_WIN32)
#define locale_t _locale_t
#define strtod_l _strtod_l
#define freelocale _free_locale
#endif

#include <gsl/gsl_sf_gamma.h>

#ifdef PDEBUG
#include <cstdio>
#define pdebug(...) fprintf(stderr, __VA_ARGS__)
#else
#define pdebug(...) {}
#endif

#define YYERROR_VERBOSE 1

/* params passed to yylex (and yyerror) */
typedef struct param {
	size_t pos;		/* current position in string */
	char* string;		/* the string to parse */
	const char* locale;	/* name of locale to convert numbers */
} param;

int yyerror(param *p, const char *err);
int yylex(param *p);
int _variablesCounter = 0;
bool skipSpecialFunctionEvaluation = false;

int variablesCounter() {
	return _variablesCounter;
}

double res;
char _lastErrorMessage[256] = "";

const char* lastErrorMessage() {
	return _lastErrorMessage;
}

static void wrongArgumentNumberMessage(const char* function_name, int provided, int expected) {
	snprintf(_lastErrorMessage, sizeof(_lastErrorMessage), "Parsing Error: Wrong argument count for %s. Provided: %d, Expected: %d", function_name, provided, expected);
}

static void wrongArgumentInternalErrorMessage(const char* function_name, int expected) {
	snprintf(_lastErrorMessage, sizeof(_lastErrorMessage), "Internal parsing Error: Wrong argument count for %s. Expected: %d , but function does not have this number of arguments", function_name, expected);
}

static void notImplementedError(const char* function_name) {
	snprintf(_lastErrorMessage, sizeof(_lastErrorMessage), "Parsing Error: '%s' not implemented.", function_name);
}

static void yyerrorFunction(const char* function_name, const char* msg) {
	snprintf(_lastErrorMessage, sizeof(_lastErrorMessage), "Parsing Error: In function '%s': %s", function_name, msg);
}

static void yyerror(const char* msg) {
	snprintf(_lastErrorMessage, sizeof(_lastErrorMessage), "Parsing Error: %s", msg);
}



%}

%lex-param {param *p}
%parse-param {param *p}

%union {
double dval;	/* For returning numbers */
symbol *tptr;   /* For returning symbol-table pointers */
}

%token <dval>  NUM 	/* Simple double precision number */
%token <tptr> VAR FNCT SPECFNCT /* VARiable and FuNCTion and Special functions*/
/* https://www.gnu.org/software/bison/manual/html_node/Token-Decl.html */
%token <operator> OR "||"
%token <operator> AND "&&"
%token <operator> LE "<="
%token <operator> GE ">="
%type  <dval>  expr

%right '='
%left '-' '+' OR LE GE '<' '>'
%left '*' '/' '%' AND
%left NEG     /* Negation--unary minus */
%right '^' '!'

%%
input:   /* empty */
	| input line
;

line:	'\n'
	| expr '\n'   { res=$1; }
	| error '\n' { yyerrok; }
;

expr:      NUM       { $$ = $1;                            }
| VAR                { $$ = std::get<double>($1->value); _variablesCounter++;}
| VAR '=' expr       { $$ = std::get<double>($1->value) = $3; _variablesCounter++;       }
| SPECFNCT '(' ')'       {
							const auto& special_function = std::get<special_function_def>($1->value);
							if (!special_function.payload.expired() && !special_function.payload.lock()->constant)
								_variablesCounter++;
							const int argc = special_function.funsptr->argc;
							if (argc != 0) {
								wrongArgumentNumberMessage($1->name, 0, argc);
								yynerrs++;
								return 1;
							}
							try {
								const auto function = std::get<func_tPayload>(special_function.funsptr->fnct);
								if (!skipSpecialFunctionEvaluation) {
									if (function == nullptr) {
										notImplementedError($1->name);
										yynerrs++;
										return 2;
									}
									$$ = function(special_function.payload);
								} else
									$$ = std::nan("0");
							} catch (const std::bad_variant_access& ex) {
								wrongArgumentInternalErrorMessage($1->name, 0);
								yynerrs++;
								return 1;
							}
						}
| SPECFNCT '(' VAR ')'  {
							const auto& special_function = std::get<special_function_def>($1->value);
							if (!special_function.payload.expired() && !special_function.payload.lock()->constant)
								_variablesCounter++;
							const int argc = special_function.funsptr->argc;
							if (argc != 1) {
								wrongArgumentNumberMessage($1->name, 1, argc);
								yynerrs++;
								return 1;
							}
							try {
								const auto function = std::get<func_t1Payload>(special_function.funsptr->fnct);
								if (!skipSpecialFunctionEvaluation) {
									if (function == nullptr) {
										notImplementedError($1->name);
										yynerrs++;
										return 2;
									}
									$$ = function($3->name, special_function.payload);
								} else
									$$ = std::nan("0");
							} catch (const std::bad_variant_access& ex) {
								wrongArgumentInternalErrorMessage($1->name, 1);
								yynerrs++;
								return 1;
							}
						}
| SPECFNCT '(' expr ';' VAR ')'  {
									const auto& special_function = std::get<special_function_def>($1->value);
									if (!special_function.payload.expired() && !special_function.payload.lock()->constant)
										_variablesCounter++;
									const int argc = special_function.funsptr->argc;
									if (argc != 2) {
										wrongArgumentNumberMessage($1->name, 2, argc);
										yynerrs++;
										return 1;
									}
									try {
										const auto function = std::get<func_t2Payload>(special_function.funsptr->fnct);
										if (!skipSpecialFunctionEvaluation) {
											if (function == nullptr) {
												notImplementedError($1->name);
												yynerrs++;
												return 2;
											}
											$$ = function($3 ,$5->name, special_function.payload);
										} else
											$$ = std::nan("0");
									} catch (const std::bad_variant_access& ex) {
										wrongArgumentInternalErrorMessage($1->name, 2);
										yynerrs++;
										return 1;
									}
								  }
| SPECFNCT '(' expr ';' expr ';' VAR ')'  {
											const auto& special_function = std::get<special_function_def>($1->value);
											if (!special_function.payload.expired() && !special_function.payload.lock()->constant)
												_variablesCounter++;
											const int argc = special_function.funsptr->argc;
											if (argc != 3) {
												wrongArgumentNumberMessage($1->name, 3, argc);
												yynerrs++;
												return 1;
											}
											try {
												const auto function = std::get<func_t3Payload>(special_function.funsptr->fnct);
												if (!skipSpecialFunctionEvaluation) {
													if (function == nullptr) {
														notImplementedError($1->name);
														yynerrs++;
														return 2;
													}
													$$ = function($3, $5, $7->name, special_function.payload);
												} else
													$$ = std::nan("0");
											} catch (const std::bad_variant_access& ex) {
												wrongArgumentInternalErrorMessage($1->name, 3);
												yynerrs++;
												return 1;
											}
										  }
| SPECFNCT '(' expr ';' expr ';' expr ';' VAR ')'  {
													const auto& special_function = std::get<special_function_def>($1->value);
													if (!special_function.payload.expired() && !special_function.payload.lock()->constant)
														_variablesCounter++;
													const int argc = special_function.funsptr->argc;
													if (argc != 4) {
														wrongArgumentNumberMessage($1->name, 4, argc);
														yynerrs++;
														return 1;
													}
													try {
														const auto function = std::get<func_t4Payload>(special_function.funsptr->fnct);
														if (!skipSpecialFunctionEvaluation) {
															if (function == nullptr) {
																notImplementedError($1->name);
																yynerrs++;
																return 2;
															}
															$$ = function($3, $5, $7, $9->name, special_function.payload);
														} else
															$$ = std::nan("0");
													} catch (const std::bad_variant_access& ex) {
														wrongArgumentInternalErrorMessage($1->name, 4);
														yynerrs++;
														return 1;
													}


												  }
| SPECFNCT '(' expr ')'  { yynerrs++; yyerrorFunction($1->name, "Argument must be a variable not an expression");}
| SPECFNCT '(' expr ';' expr ')'   { yynerrs++; yyerrorFunction($1->name, "Last argument must be a variable not an expression");}
| SPECFNCT '(' expr ';' expr ';' expr ')'  { yynerrs++; yyerrorFunction($1->name, "Last argument must be a variable not an expression");}
| SPECFNCT '(' expr ';' expr ';' expr ';' expr ')'  { yynerrs++; yyerrorFunction($1->name, "Last argument must be a variable not an expression");}
| FNCT '(' ')'       {
						const funs* function = std::get<funs*>($1->value);
						const int argc = function->argc;
						if (argc != 0) {
							wrongArgumentNumberMessage($1->name, 0, argc);
							yynerrs++;
							return 1;
						} else {
							try {
								const auto fnct_ptr = std::get<func_t>(function->fnct);
								$$ = fnct_ptr();
							} catch (const std::bad_variant_access& ex) {
								wrongArgumentInternalErrorMessage($1->name, 0);
								yynerrs++;
								return 1;
							}
						}
					}
| FNCT '(' expr ')'  {
						const funs* function = std::get<funs*>($1->value);
						const int argc = function->argc;
						if (argc != 1) {
							wrongArgumentNumberMessage($1->name, 1, argc);
							yynerrs++;
							return 1;
						} else {
							try {
								const auto fnct_ptr = std::get<func_t1>(function->fnct);
								$$ = fnct_ptr($3);
							} catch (const std::bad_variant_access& ex) {
								wrongArgumentInternalErrorMessage($1->name, 1);
								yynerrs++;
								return 1;
							}
						}
					}
| FNCT '(' expr ',' expr ')'  {
								const funs* function = std::get<funs*>($1->value);
								const int argc = function->argc;
								if (argc != 2) {
									wrongArgumentNumberMessage($1->name, 2, argc);
									yynerrs++;
									return 1;
								} else {
									try {
										const auto fnct_ptr = std::get<func_t2>(function->fnct);
										$$ = fnct_ptr($3,$5);
									} catch (const std::bad_variant_access& ex) {
										wrongArgumentInternalErrorMessage($1->name, 2);
										yynerrs++;
										return 1;
									}
								}
							}
| FNCT '(' expr ',' expr ',' expr ')'  {
										const funs* function = std::get<funs*>($1->value);
										const int argc = function->argc;
										if (argc != 3) {
											wrongArgumentNumberMessage($1->name, 3, argc);
											yynerrs++;
											return 1;
										} else {
											try {
												const auto fnct_ptr = std::get<func_t3>(function->fnct);
												$$ = fnct_ptr($3,$5,$7);
											} catch (const std::bad_variant_access& ex) {
												wrongArgumentInternalErrorMessage($1->name, 3);
												yynerrs++;
												return 1;
											}
										}
									}
| FNCT '(' expr ',' expr ',' expr ',' expr ')'  {
													const funs* function = std::get<funs*>($1->value);
													const int argc = function->argc;
													if (argc != 4) {
														wrongArgumentNumberMessage($1->name, 4, argc);
														yynerrs++;
														return 1;
													} else {
														try {
															const auto fnct_ptr = std::get<func_t4>(function->fnct);
															$$ = fnct_ptr($3,$5,$7,$9);
														} catch (const std::bad_variant_access& ex) {
															wrongArgumentInternalErrorMessage($1->name, 4);
															yynerrs++;
															return 1;
														}
													}
												}
| FNCT '(' expr ';' expr ')'  {
								const funs* function = std::get<funs*>($1->value);
								const int argc = function->argc;
								if (argc != 2) {
									wrongArgumentNumberMessage($1->name, 2, argc);
									yynerrs++;
									return 1;
								} else {
									try {
										const auto fnct_ptr = std::get<func_t2>(function->fnct);
										$$ = fnct_ptr($3,$5);
									} catch (const std::bad_variant_access& ex) {
										wrongArgumentInternalErrorMessage($1->name, 2);
										yynerrs++;
										return 1;
									}
								}
							}
| FNCT '(' expr ';' expr ';' expr ')'  {
										const funs* function = std::get<funs*>($1->value);
										const int argc = function->argc;
										if (argc != 3) {
											wrongArgumentNumberMessage($1->name, 3, argc);
											yynerrs++;
											return 1;
										} else {
											try {
												const auto fnct_ptr = std::get<func_t3>(function->fnct);
												$$ = fnct_ptr($3,$5,$7);
											} catch (const std::bad_variant_access& ex) {
												wrongArgumentInternalErrorMessage($1->name, 3);
												yynerrs++;
												return 1;
											}
										}
									}
| FNCT '(' expr ';' expr ';' expr ';' expr ')'  {
													const funs* function = std::get<funs*>($1->value);
													const int argc = function->argc;
													if (argc != 4) {
														wrongArgumentNumberMessage($1->name, 4, argc);
														yynerrs++;
														return 1;
													} else {
														try {
															const auto fnct_ptr = std::get<func_t4>(function->fnct);
															$$ = fnct_ptr($3,$5,$7,$9);
														} catch (const std::bad_variant_access& ex) {
															wrongArgumentInternalErrorMessage($1->name, 4);
															yynerrs++;
															return 1;
														}
													}
												}
| FNCT '(' expr ';' expr ';' expr ';' expr ';' expr ')'  {
													const funs* function = std::get<funs*>($1->value);
													const int argc = function->argc;
													if (argc != 5) {
														wrongArgumentNumberMessage($1->name, 5, argc);
														yynerrs++;
														return 1;
													} else {
														try {
															const auto fnct_ptr = std::get<func_t5>(function->fnct);
															$$ = fnct_ptr($3,$5,$7,$9,$11);
														} catch (const std::bad_variant_access& ex) {
															wrongArgumentInternalErrorMessage($1->name, 5);
															yynerrs++;
															return 1;
														}
													}
}
| expr '+' expr      { $$ = $1 + $3;                       }
| expr '-' expr      { $$ = $1 - $3;                       }
| expr OR expr       { $$ = orFunction($1, $3);            }
| expr '*' expr      { $$ = $1 * $3;                       }
| expr '/' expr      { $$ = $1 / $3;                       }
| expr '%' expr      { $$ = (int)($1) % (int)($3);         }
| expr AND expr      { $$ = andFunction($1, $3);           }
| '!' expr           { $$ = notFunction($2);               }
| expr GE expr       { $$ = greaterEqualThan($1, $3);      }
| expr LE expr       { $$ = lessEqualThan($1, $3);         }
| expr '>' expr      { $$ = greaterThan($1, $3);           }
| expr '<' expr      { $$ = lessThan($1, $3);              }
| '-' expr  %prec NEG{ $$ = -$2;                           }
| expr '^' expr      { $$ = std::pow($1, $3);              }
| expr '*' '*' expr  { $$ = std::pow($1, $4);              }
| '(' expr ')'       { $$ = $2;                            }
| '|' expr '|'       { $$ = std::abs($2);                  }
| expr '!'           { $$ = gsl_sf_fact((unsigned int)$1); }
;

%%

/* global symbol table (as linked list) */
symbol *symbol_table = nullptr;

int parse_errors(void) {
	return yynerrs;
}

int yyerror(param *p, const char *s) {
	/* remove trailing newline */
	p->string[strcspn(p->string, "\n")] = 0;
	printf("PARSER ERROR: %s @ position %d of string '%s'\n", s, (int)(p->pos), p->string);

	return 0;
}

/* save symbol in symbol table (at start of linked list) */
symbol* put_symbol(const char *symbol_name, int symbol_type) {
/*	pdebug("PARSER: put_symbol(): symbol_name = '%s'\n", symbol_name); */

	symbol *ptr = new symbol;
	assert(ptr);
	ptr->name = (char *)malloc(strlen(symbol_name) + 1);
	strcpy(ptr->name, symbol_name);
	ptr->type = symbol_type;
	switch (symbol_type) {
	case VAR: ptr->value = 0.; break;
	case FNCT: ptr->value = nullptr; break;
	case SPECFNCT: {
		special_function_def sfp;
		ptr->value = sfp;
		break;
	}
	}

	ptr->next = (symbol *)symbol_table;
	symbol_table = ptr;

/*	pdebug("PARSER: put_symbol() DONE\n"); */
	return ptr;
}

/* remove symbol of name symbol_name from symbol table
   removes only variables of value 0
   returns 0 on success */
int remove_symbol(const char *symbol_name) {
	symbol* ptr = symbol_table;

	/* check if head contains symbol */
	if (ptr && (strcmp(ptr->name, symbol_name) == 0)) {
		if (ptr->type == VAR && std::get<double>(ptr->value) == 0) {
			pdebug("PARSER: REMOVING symbol '%s'\n", symbol_name);
			symbol_table = ptr->next;
			free(ptr->name);
			delete ptr;
		}
		return 0;
	}

	/* search for symbol to be deleted */
	symbol* prev;
	while (ptr && (strcmp(ptr->name, symbol_name) != 0)) {
		prev = ptr;
		ptr = ptr->next;
	}

	/* symbol not found or is not a variable or is not 0 */
	if (!ptr || ptr->type != VAR || std::get<double>(ptr->value) != 0)
		return 1;

	/* remove symbol */
	pdebug("PARSER: REMOVING symbol '%s'\n", symbol_name);
	prev->next = ptr->next;
	free(ptr->name);
	delete ptr;

	return 0;
}

/* get symbol from symbol table
   returns 0 if symbol not found */
symbol* get_symbol(const char *symbol_name) {
	pdebug("PARSER: get_symbol(): symbol_name = '%s'\n", symbol_name);

	symbol *ptr;
	for (ptr = symbol_table; ptr != nullptr; ptr = (symbol *)ptr->next) {
		/* pdebug("%s ", ptr->name); */
		if (strcmp(ptr->name, symbol_name) == 0) {
			pdebug("PARSER:		SYMBOL FOUND\n");
			return ptr;
		}
	}

	pdebug("PARSER:		SYMBOL NOT FOUND\n");
	return nullptr;
}

/* initialize symbol table with all known functions and constants */
void init_table(void) {
	pdebug("PARSER: init_table()\n");

	symbol *ptr = nullptr;
	int i;
	/* add functions */
	for (i = 0; i < _number_functions; i++) {
		ptr = put_symbol(_functions[i].name, FNCT);
		ptr->value = &_functions[i];
	}
	/* add special functions */
	for (i = 0; i < _number_specialfunctions; i++) {
		ptr = put_symbol(_special_functions[i].name, SPECFNCT);

		special_function_def sfd;
		sfd.funsptr = &_special_functions[i];
		ptr->value = sfd;
	}
	/* add constants */
	for (i = 0; i < _number_constants; i++) {
		ptr = put_symbol(_constants[i].name, VAR);
		ptr->value = _constants[i].value;
	}

	pdebug("PARSER: init_table() DONE. sym_table = %p\n", ptr);
}

bool set_specialfunction0(const char* function_name, func_tPayload function, std::weak_ptr<Payload> payload) {
	pdebug("PARSER: set_SpecialFunction0()\n");


	symbol *ptr = get_symbol(function_name);
	if (!ptr) // function name not found
		return false;

	assert(std::get<special_function_def>(ptr->value).funsptr->argc == 0);
	std::get<special_function_def>(ptr->value).funsptr->fnct = function;
	std::get<special_function_def>(ptr->value).funsptr->name = function_name;
	std::get<special_function_def>(ptr->value).payload = payload;
	return true;
}

bool set_specialfunction1(const char* function_name, func_t1Payload function, std::shared_ptr<Payload> payload) {
	pdebug("PARSER: set_SpecialFunction1()\n");


	symbol *ptr = get_symbol(function_name);
	if (!ptr) // function name not found
		return false;
	assert(std::get<special_function_def>(ptr->value).funsptr->argc == 1);
	std::get<special_function_def>(ptr->value).funsptr->fnct = function;
	std::get<special_function_def>(ptr->value).funsptr->name = function_name;
	std::get<special_function_def>(ptr->value).payload = payload;
	return true;
}

bool set_specialfunction2(const char* function_name, func_t2Payload function, std::shared_ptr<Payload> payload) {
	pdebug("PARSER: set_SpecialFunction2()\n");


	symbol *ptr = get_symbol(function_name);
	if (!ptr) // function name not found
		return false;

	assert(std::get<special_function_def>(ptr->value).funsptr->argc == 2);
	std::get<special_function_def>(ptr->value).funsptr->fnct = function;
	std::get<special_function_def>(ptr->value).funsptr->name = function_name;
	std::get<special_function_def>(ptr->value).payload = payload;
	return true;
}

bool set_specialfunction3(const char* function_name, func_t3Payload function, std::shared_ptr<Payload> payload) {
	pdebug("PARSER: set_SpecialFunction3()\n");


	symbol *ptr = get_symbol(function_name);
	if (!ptr) // function name not found
		return false;

	assert(std::get<special_function_def>(ptr->value).funsptr->argc == 3);
	std::get<special_function_def>(ptr->value).funsptr->fnct = function;
	std::get<special_function_def>(ptr->value).funsptr->name = function_name;
	std::get<special_function_def>(ptr->value).payload = payload;
	return true;
}

bool set_specialfunction4(const char* function_name, func_t4Payload function, std::shared_ptr<Payload> payload) {
	pdebug("PARSER: set_SpecialFunction4()\n");


	symbol *ptr = get_symbol(function_name);
	if (!ptr) // function name not found
		return false;

	assert(std::get<special_function_def>(ptr->value).funsptr->argc == 4);
	std::get<special_function_def>(ptr->value).funsptr->fnct = function;
	std::get<special_function_def>(ptr->value).funsptr->name = function_name;
	std::get<special_function_def>(ptr->value).payload = payload;
	return true;
}

void delete_table(void) {
	pdebug("PARSER: delete_table()\n");
	while(symbol_table) {
		symbol *tmp = symbol_table;
		symbol_table = symbol_table->next;
		free(tmp->name);
		free(tmp);
	}
}

/* add new symbol with value or just set value if symbol is a variable */
symbol* assign_symbol(const char* symbol_name, double value) {
	pdebug("PARSER: assign_symbol() : symbol_name = '%s', value = %g\n", symbol_name, value);

	/* be sure that the symbol table has been initialized */
	if (!symbol_table)
		init_table();

	symbol* ptr = get_symbol(symbol_name);
	if (!ptr) {
		pdebug("PARSER: calling putsymbol(): symbol_name = '%s'\n", symbol_name);
		ptr = put_symbol(symbol_name, VAR);
	} else {
		pdebug("PARSER: Symbol already assigned\n");
	}

	/* do not assign value if symbol already exits as function */
	if (ptr->type == VAR)
		ptr->value = value;

	return ptr;
}

static int getcharstr(param *p) {
	pdebug(" getcharstr() pos = %d\n", (int)(p->pos));

	if (p->string[p->pos] == '\0')
		return EOF;
	/* pdebug("PARSER: 	char is %c\n", p->string[p->pos]); */
	return p->string[(p->pos)++];
}

static void ungetcstr(size_t *pos) {
	/* pdebug("PARSER: ungetcstr()\n"); */
	if (*pos > 0)
		(*pos)--;
}

double parse(const char* string, const char* locale) {
	pdebug("\nPARSER: parse('%s') len = %d\n********************************\n", string, (int)strlen(string));

	/* be sure that the symbol table has been initialized */
	if (!symbol_table)
		init_table();

	_variablesCounter = 0;
	_lastErrorMessage[0] = 0;

	param p;
	p.pos = 0;
	p.locale = locale;

	/* leave space to terminate string by "\n\0" */
	const size_t slen = strlen(string) + 2;
	p.string = (char *) malloc(slen * sizeof(char));
	if (p.string == nullptr) {
		printf("PARSER ERROR: Out of memory for parsing string\n");
		return 0.;
	}

	strcpy(p.string, string);
	p.string[strlen(string)] = '\n';	// end for parsing
	p.string[strlen(string)+1] = '\0';	// end of string
	/* pdebug("PARSER: Call yyparse() for \"%s\" (len = %d)\n", p.string, (int)strlen(p.string)); */

	/* parameter for yylex */
	res = NAN;	/* default value */
	yynerrs = 0;	/* reset error count */
	yyparse(&p);

	pdebug("PARSER: parse() DONE (result = %g, errors = %d)\n*******************************\n", res, parse_errors());
	free(p.string);
	p.string = nullptr;

	return res;
}

double parse_with_vars(const char *str, const parser_var *vars, int nvars, const char* locale) {
	pdebug("\nPARSER: parse_with_var(\"%s\") len = %d\n", str, (int)strlen(str));

	int i;
	for(i = 0; i < nvars; i++) {	/*assign vars */
		pdebug("PARSER: Assign '%s' the value %g\n", vars[i].name, vars[i].value);
		assign_symbol(vars[i].name, vars[i].value);
	}

	return parse(str, locale);
}

int yylex(param *p) {
	pdebug("PARSER: YYLEX()");

	/* get char and skip white space */
	int c;
	while ((c = getcharstr(p)) == ' ' || c == '\t');

	/* finish if reached EOF */
	if (c == EOF) {
		pdebug("PARSER: FINISHED\n");
		return 0;
	}
	/* check for non-ASCII chars */
	if (!isascii(c)) {
		pdebug(" non-ASCII character found. Giving up\n");
		yynerrs++;
		return 0;
	}
	if (c == '\n') {
		pdebug("PARSER: Reached EOL\n");
		return c;
	}

	if (c == '&') {
		// Check if the next is also an &, because then it is the AND operator
		if (getcharstr(p) == '&') {
			return AND;
		}
		ungetcstr(&(p->pos));
	} else if (c == '|') {
		if (getcharstr(p) == '|') {
			return OR;
		}
		ungetcstr(&(p->pos));
	} else if (c == '>') {
		if (getcharstr(p) == '=') {	// >=
			return GE;
		}
		ungetcstr(&(p->pos));
	} else if (c == '<') {
		if (getcharstr(p) == '=') {	// <=
			return LE;
		}
		ungetcstr(&(p->pos));
	}

	pdebug("PARSER: PROCESSING character '%c'\n", c);

	/* process numbers */
	if (isdigit(c)) {
		pdebug("PARSER: Found NUMBER (starts with digit)\n");
                ungetcstr(&(p->pos));
                char *s = &(p->string[p->pos]);

		/* convert to double */
		char *remain;
#if defined(_WIN32)
		locale_t locale = _create_locale(LC_NUMERIC, p->locale);
		if (locale == NULL) {
			pdebug("PARSER ERROR in newlocale(%s): %s. Trying system locale.\n", p->locale, strerror(errno));
			locale = _create_locale(LC_NUMERIC, "");
		}
#else
		locale_t locale = newlocale(LC_NUMERIC_MASK, p->locale, (locale_t)nullptr);
		if (locale == (locale_t)nullptr) {
			pdebug("PARSER ERROR in newlocale(%s): %s. Trying system locale.\n", p->locale, strerror(errno));
			locale = newlocale(LC_NUMERIC_MASK, "", (locale_t)nullptr);
			pdebug("PARSER:		Reading: '%s' with system locale\n", s);
		} else {
			pdebug("PARSER:		Reading: '%s' with locale %s\n", s, p->locale);
		}
#endif
		double result;
		if (locale != nullptr) {
#if defined(__OpenBSD__) || defined(__HAIKU__)
			result = strtod(s, &remain);
#else
			result = strtod_l(s, &remain, locale);
#endif
			freelocale(locale);
		} else // use C locale
			result = strtod(s, &remain);

		pdebug("PARSER:		Remain: '%s'\n", remain);

		/* check conversion */
		if(strlen(s) == strlen(remain))
			return 0;

		pdebug("PARSER:		Result = %g\n", result);
		yylval.dval = result;

                p->pos += strlen(s) - strlen(remain);

		return NUM;
	}

	/* process symbol */
	if (isalpha (c) || c == '.') {
		pdebug("PARSER: Found SYMBOL (starts with alpha)\n");
		static char *symbol_name = nullptr;
		static int length = 0;
		int i = 0;

		/* Initially make the buffer long enough for a 10-character symbol name */
		if (length == 0) {
			length = 10;
			symbol_name = (char *) malloc(length + 1);
		}

		do {
			pdebug("PARSER: Reading symbol .. ");
			/* If buffer is full, make it bigger */
			if (i == length) {
				length *= 2;
				symbol_name = (char *) realloc(symbol_name, length + 1);
			}
			symbol_name[i++] = c;
			c = getcharstr(p);
			pdebug("PARSER:		got '%c'\n", c);
		}
		while (c != EOF && (isalnum(c) || c == '_' || c == '.'));
		pdebug("PARSER: Reading SYMBOL DONE\n");

		if (c != EOF)
			ungetcstr(&(p->pos));
		symbol_name[i] = '\0';

		symbol *s = get_symbol(symbol_name);
		if(s == nullptr) {	/* symbol unknown */
			pdebug("PARSER ERROR: Symbol '%s' UNKNOWN\n", symbol_name);
			yynerrs++;
			return 0;
			/* old behavior: add symbol */
			/* s = put_symbol(symbol_name, VAR); */
		}

		yylval.tptr = s;
		return s->type;
	}

	/* else: single operator */
	pdebug("PARSER: Found single operator '%c'\n", c);
	return c;
}
