/* 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 cna 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 int get_exponent(s_token *tokens, int i, int *token_count) { *token_count = 0; // valide : // - '*x^2' -> exponent 2 // - '*x²' -> exponent 2 // - 'x^2' -> exponent 2 // - '*x' -> exponent 1 // - 'x' -> exponent 1 // - '' -> exponent 0 // first reach VARIABLE : 'x' or '*x' if (tokens[i].type == TOKEN_VARIABLE) { // token is 'x' i++; *token_count = 1; } else if (tokens[i].type == TOKEN_FACTOR_MULT) { // token is '*' i++; if (tokens[i].type == TOKEN_VARIABLE) { // tokens are '*x' i++; *token_count = 2; } else { stop_errors("at exponent place, after a '*' we should have a 'var', but instead got : '%s' (token number %i)", token_type_to_str(tokens[i].type), i); } } else { // if token are neither 'x' or '*x', then exponent is 0 return 0; } // then get power sign '^' or directly superscript power like '²' if (tokens[i].type == TOKEN_POWER) { i++; *token_count += 1; } else if (tokens[i].type == TOKEN_NUMBER_INT_SUPER) { *token_count += 1; // return exponent directly return tokens[i].value_int; } else { // if token is 'x' not followed by '^' -> it's an exponent 1 return 1; } // then get exponent if (tokens[i].type == TOKEN_NUMBER_INT) { *token_count += 1; } else { stop_errors("at exponent place, we should have an int, but instead got : '%s' (token number %i)", token_type_to_str(tokens[i].type), i); } return tokens[i].value_int; } 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)); } }