PC_graph/lex.c
2024-12-18 15:52:01 +01:00

231 lines
6.3 KiB
C

#include "lex.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#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;
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 "<eof>";
case TOK_ERROR: return "<error>";
case TOK_NUMBER: return "<number>";
case TOK_PLUS: return "'+'";
case TOK_MINUS: return "'-'";
case TOK_MULTIPLY: return "'*'";
case TOK_DIVIDE: return "'/'";
case TOK_POWER: return "'^'";
case TOK_VARIABLE: return "<variable>";
case TOK_FUNCTION: return "<function>";
case TOK_LEFT_PAREN: return "'('";
case TOK_RIGHT_PAREN: return "')'";
case TOK_COMMA: return "','";
default: return "<unknown token>";
}
}
#ifdef LEX_DEBUG
void lex_debug_print_token(const struct token *tok) {
printf("%-20s ", lex_token_str(tok->type));
if (tok->type == TOK_NUMBER)
printf("%.2f\n", tok->val.num);
else if (tok->type == TOK_FUNCTION) {
printf("%s\n", fns_get()[tok->val.fn_idx].name);
}
else
printf("\n");
}
#endif /* LEX_DEBUG */