Files
42_EXT_05_computorv1/src/parser.c

298 lines
8.6 KiB
C

/* parser.c */
#include "computorv1.h"
/**
1. VAR | NUMBER_I | NUMBER_D | ! POW | SIGN_P | SIGN_M | ! FACTOR_MUL | ! FACTOR_DIV | ! EQUAL | END
NT | NUMBER | NT | SIGN | ! FACTOR | NT | NT
2. VAR | NUMBER_I | NUMBER_D | ! POW | ! SIGN_P | ! SIGN_M | ! FACTOR_MUL | ! FACTOR_DIV | ! EQUAL | END
NT | NUMBER | NT | ! SIGN | ! FACTOR | NT | NT
3. VAR | ! NUMBER_I | ! NUMBER_D | ! POW | ! SIGN_P | ! SIGN_M | FACTOR_MUL | ! FACTOR_DIV | ! EQUAL | END
NT | ! NUMBER | NT | ! SIGN | FACTOR | NT | NT
*/
static e_term_sign get_sign(s_token *tokens, int i, int *token_count)
{
*token_count = 0;
int j;
e_term_sign ret_sign;
// default to '+'
ret_sign = TERM_PLUS;
if (tokens[i].tag == TOKEN_SIGN)
{
// we can have two signs in a row, like "3 - -2" or "3 - +2"
j = 0;
while (j < 2)
{
if (tokens[i + j].tag != TOKEN_SIGN)
break;
if (tokens[i + j].type == TOKEN_SIGN_MINUS)
ret_sign = (ret_sign == TERM_PLUS) ? TERM_MINUS : TERM_PLUS;
*token_count += 1;
j++;
}
}
else if (i == 0)
{
// if most left term, the sign can be ommited for a '+' sign in front of a number or variable
*token_count = 0;
ret_sign = TERM_PLUS;
}
else if (tokens[i - 1].type == TOKEN_EQUAL)
{
// if first token after 'equal', the sign can be ommited for a '+' sign in front of a number or variable
*token_count = 0;
ret_sign = TERM_PLUS;
}
else
{
stop_errors("at begining of term, we should have a token 'sign', not '%s' (token[%i])", token_type_to_str(tokens[i].type), i);
}
return ret_sign;
}
static double get_double_value(s_token token)
{
if (token.tag != TOKEN_NUMBER)
{
stop_errors("this was suppose to be a number, instead got a %s", token_type_to_str(token.type));
}
if (token.type == TOKEN_NUMBER_DOUBLE)
{
return token.value_double;
}
// else it's an int
return token.value_int;
}
static double get_coefficient(s_token *tokens, int i, int *token_count)
{
double coefficient;
coefficient = 1.0;
*token_count = 0;
// if not coefficient token
if (tokens[i].type == TOKEN_VARIABLE)
{
*token_count = 0;
return coefficient;
}
// if coefficient tokens
if (tokens[i].tag == TOKEN_NUMBER)
{
*token_count = 1;
coefficient = get_double_value(tokens[i]);
}
// detect more coefficients, like "3 * 2 / 5" etc
i++;
while (tokens[i].tag == TOKEN_FACTOR)
{
i++; // to check if token after factor is number
if (tokens[i].tag != TOKEN_NUMBER)
{
return coefficient;
}
*token_count += 2;
if (tokens[i - 1].type == TOKEN_FACTOR_MULT)
{
coefficient *= get_double_value(tokens[i]);
}
else if (tokens[i - 1].type == TOKEN_FACTOR_DIV)
{
coefficient /= get_double_value(tokens[i]);
}
i++; // to check if next token is a factor
}
return coefficient;
}
static bool token_sequence(s_token *tokens, e_token_type *types, int len)
{
int i;
i = 0;
while (i < len)
{
if (tokens[i].type != types[i])
return false;
i++;
}
return true;
}
static int get_exponent(s_token *tokens, int i, int *token_count)
{
*token_count = 0;
int ret_exponent;
// valide :
// - [*] [x] [^] [2] -> exponent 2
// - [*] [x] [²] -> exponent 2
// - [*] [x] -> exponent 1
// - [x] [^] [2] -> exponent 2
// - [x] [²] -> exponent 2
// - [x] -> exponent 1
// - '' -> exponent 0
// invalid first token :
// - '2' -> number
if (tokens[i].tag == TOKEN_NUMBER)
{
// exponent term cannot begin with a number
stop_errors("at exponent place, we should have an exponent expression, but instead got : '%s' (token number %i)", token_type_to_str(tokens[i].type), i);
}
else if (token_sequence(&tokens[i], (e_token_type[]){TOKEN_FACTOR_MULT, TOKEN_VARIABLE, TOKEN_POWER, TOKEN_NUMBER_INT}, 4))
{
// ex: [*] [x] [^] [2]
*token_count = 4;
ret_exponent = tokens[i + 3].value_int;
}
else if (token_sequence(&tokens[i], (e_token_type[]){TOKEN_FACTOR_MULT, TOKEN_VARIABLE, TOKEN_NUMBER_INT_SUPER}, 3))
{
// ex: [*] [x] [²]
*token_count = 3;
ret_exponent = tokens[i + 2].value_int;
}
else if (token_sequence(&tokens[i], (e_token_type[]){TOKEN_FACTOR_MULT, TOKEN_VARIABLE}, 2))
{
// ex: [*] [x] -> exponent 1
*token_count = 2;
ret_exponent = 1;
}
else if (token_sequence(&tokens[i], (e_token_type[]){TOKEN_VARIABLE, TOKEN_POWER, TOKEN_NUMBER_INT}, 3))
{
// ex: [x] [^] [2]
*token_count = 3;
ret_exponent = tokens[i + 2].value_int;
}
else if (token_sequence(&tokens[i], (e_token_type[]){TOKEN_VARIABLE, TOKEN_NUMBER_INT_SUPER}, 2))
{
// ex: [x] [²]
*token_count = 2;
ret_exponent = tokens[i + 1].value_int;
}
else if (token_sequence(&tokens[i], (e_token_type[]){TOKEN_VARIABLE}, 1))
{
// ex: [x] -> exponent 1
*token_count = 1;
ret_exponent = 1;
}
else
{
// no variable, so no exponent -> exponent 0
*token_count = 0;
ret_exponent = 0;
}
// check if exponent is not too big, to avoid overflow when calculating power
if (ret_exponent > MAX_EXPONENT)
{
stop_errors("exponent is too big (max supported exponent is %d), got : %d\n", MAX_EXPONENT, ret_exponent);
}
return ret_exponent;
}
static void check_variables(s_token *tokens)
{
int i;
char var;
i = 0;
var = 0;
while (tokens[i].type != TOKEN_END)
{
// variable -> all variables must be the same
if (tokens[i].type == TOKEN_VARIABLE)
{
if (!var)
{
var = tokens[i].value_char;
}
else if (var != tokens[i].value_char)
{
stop_errors("old var : '%c' - new var : '%c' (token number %i)", var, tokens[i].value_char, i);
}
}
i++;
}
}
void parse(s_token *tokens, s_term *terms, int terms_count_max)
{
int i;
int terms_count;
int token_count;
int sign;
e_term_position term_position;
check_variables(tokens);
print_debug("PARSER STEPS :\n");
terms_count = 0;
token_count = 0;
i = 0;
term_position = TERM_LEFT;
while (tokens[i].type != TOKEN_END && terms_count < terms_count_max)
{
print_debug("- token[%i]\n", i);
// equal
if (tokens[i].type == TOKEN_EQUAL)
{
term_position = TERM_RIGHT;
i++;
continue;
}
// position
terms[terms_count].position = term_position;
// sign
sign = 1;
e_term_sign ret_sign = get_sign(tokens, i, &token_count);
terms[terms_count].sign = ret_sign;
if (ret_sign == TERM_MINUS)
sign = -1;
i += token_count;
print_debug("term[%i] get_sign: (%i)[%s], token_count: [%d]\n", terms_count, ret_sign, term_sign_to_str(ret_sign), token_count);
// coefficient
double ret_coefficient = get_coefficient(tokens, i, &token_count);
terms[terms_count].coefficient = ret_coefficient * sign;
i += token_count;
print_debug("term[%i] get_coefficient: [%g], token_count: [%d]\n", terms_count, ret_coefficient, token_count);
// exponent
int ret_exponent = get_exponent(tokens, i, &token_count);
terms[terms_count].exponent = ret_exponent;
i += token_count;
print_debug("term[%i] get_exponent: [%i], token_count: [%d]\n", terms_count, ret_exponent, token_count);
terms_count++;
}
// last token is TOKEN_END, and terms[] should have at least one more spot for the END term
if (tokens[i].type == TOKEN_END && terms_count < terms_count_max)
{
terms[terms_count].position = TERM_POS_END;
terms[terms_count].sign = TERM_SIGN_END;
terms[terms_count].coefficient = 0;
terms[terms_count].exponent = 0;
}
else
{
stop_errors("terms_count: %i, terms_count_max: %i, tokens[%i].type: %s", terms_count, terms_count_max, i, token_type_to_str(tokens[i].type));
}
}