#include "lex.h" #include #include #include #include "math_functions.h" #define CONST_PI 3.141592653589793 #define CONST_E 2.718281828459045 #define CONST_S (1.0 / 9.0) void lex_init(struct lexer *lex, const char *str, const char *variable_name) { error_buffer_init(&lex->eb); lex->start = str; lex->prev_p = str; lex->p = str; lex->variable_name = variable_name; lex_next(lex); } /* Vrátí 1, pokud je c "bílý znak" */ static int is_whitespace(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; } /* Přeskočí bílé znaky v řetězci */ static void skip_whitespace(struct lexer *lex) { while (is_whitespace(*lex->p)) ++lex->p; } /* Posune ukazatel, pokud se v řetězci na aktuální pozici nachází řetězec str a vrátí 1, jinak ukazatel nezmění a vrátí 0 */ static int try_advance(struct lexer *lex, const char *str) { const char *temp_p = lex->p; while (1) { if (!*str) { lex->p = temp_p; return 1; } if (*str != *temp_p) { return 0; } ++temp_p; ++str; } } /* Vrátí 1, pokud se c smí vyskytovat v identifikátorech */ static int is_identifier_char(char c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } /* Stejné jako try_advance, ale přidává podmínku, že následující znak není znakem s povoleným výskytem v identifikátorech. Např. "sin" bude rozpoznán v "sin(...)", ale v "sinh" ne. */ static int try_advance_identifier(struct lexer *lex, const char *str) { const char *temp_p = lex->p; if (!try_advance(lex, str)) return 0; /* ověřit konec identifikátoru */ if (is_identifier_char(*lex->p)) { lex->p = temp_p; return 0; } return 1; } /* Vrátí 1, pokud je na začátku řetězce nalezen daný token a nastaví příslušně aktuální token. */ static int try_token(struct lexer *lex, const char *tok_str, enum token_type type) { if (try_advance(lex, tok_str)) { lex->tok.type = type; return 1; } return 0; } /* Vrátí 1, pokud je na začátku řetězce nalezena daná funkce a nastaví příslušně aktuální token. */ static int try_fns(struct lexer *lex) { const struct math_function *const fns = fns_get(); size_t i; for (i = 0; fns[i].name; ++i) { if (try_advance_identifier(lex, fns[i].name)) { lex->tok.type = TOK_FUNCTION; lex->tok.val.fn_idx = i; return 1; } } return 0; } /* Vrátí 1, pokud je na začátku řetězce nalezena číselná konstanta a nastaví příslušně aktuální token. */ static int try_number(struct lexer *lex) { char *end; double val; val = strtod(lex->p, &end); if (lex->p == end) return 0; lex->tok.type = TOK_NUMBER; lex->tok.val.num = val; lex->p = end; return 1; } /* Vrátí 1, pokud je na začátku řetězce nalezena speciální číselná konstanta a nastaví příslušně aktuální token. */ static int try_constant(struct lexer *lex, const char *name, double val) { if (!try_advance_identifier(lex, name)) return 0; lex->tok.type = TOK_NUMBER; lex->tok.val.num = val; return 1; } void lex_next(struct lexer *lex) { skip_whitespace(lex); lex->prev_p = lex->p; if (!*lex->p) { lex->tok.type = TOK_EOF; return; } if (try_token(lex, "+", TOK_PLUS)) return; if (try_token(lex, "-", TOK_MINUS)) return; if (try_token(lex, "*", TOK_MULTIPLY)) return; if (try_token(lex, "/", TOK_DIVIDE)) return; if (try_token(lex, "^", TOK_POWER)) return; if (try_token(lex, "(", TOK_LEFT_PAREN)) return; if (try_token(lex, ")", TOK_RIGHT_PAREN)) return; if (try_token(lex, ",", TOK_COMMA)) return; if (try_constant(lex, "pi", CONST_PI)) return; if (try_constant(lex, "e", CONST_E)) return; if (try_constant(lex, "skibidi", CONST_S)) return; /* easter egg */ if (try_advance_identifier(lex, lex->variable_name)) { lex->tok.type = TOK_VARIABLE; return; } if (try_fns(lex)) return; if (try_number(lex)) return; error_set(&lex->eb, ERR_INVALID_FUNCTION); error_printf(&lex->eb, "Unrecognized sequence \"%s\"\n", lex->p); lex_print_position(lex, &lex->eb); lex->tok.type = TOK_ERROR; return; } struct token *lex_token(struct lexer *lex) { return &lex->tok; } void lex_print_position(const struct lexer *lex, struct error_buffer *eb) { size_t pos = lex->prev_p - lex->start; size_t i; size_t input_len = strlen(lex->start); error_printf(eb, "At character %d\n", pos); error_printf(eb, " %s\n", lex->start); error_printf(eb, " "); for (i = 0; i < input_len; ++i) { char c = '~'; if (i == pos) c = '^'; error_printf(eb, "%c", c); } error_printf(eb, "\n"); } enum error_code lex_get_error(const struct lexer *lex) { return error_get(&lex->eb); } const char *lex_get_error_text(const struct lexer *lex) { return error_get_text(&lex->eb); } const char *lex_token_str(enum token_type token) { switch (token) { case TOK_EOF: return ""; case TOK_ERROR: return ""; case TOK_NUMBER: return ""; case TOK_PLUS: return "'+'"; case TOK_MINUS: return "'-'"; case TOK_MULTIPLY: return "'*'"; case TOK_DIVIDE: return "'/'"; case TOK_POWER: return "'^'"; case TOK_VARIABLE: return ""; case TOK_FUNCTION: return ""; case TOK_LEFT_PAREN: return "'('"; case TOK_RIGHT_PAREN: return "')'"; case TOK_COMMA: return "','"; default: return ""; } }