191 lines
4.4 KiB
C
191 lines
4.4 KiB
C
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include "lex.h"
|
|
|
|
struct lexer {
|
|
const char* start;
|
|
const char* prev_p;
|
|
const char* p;
|
|
struct token tok;
|
|
};
|
|
|
|
struct lexer *lex_create(const char *str) {
|
|
struct lexer *lex = malloc(sizeof(struct lexer));
|
|
|
|
if (!lex)
|
|
return NULL;
|
|
|
|
lex->start = str;
|
|
lex->prev_p = str;
|
|
lex->p = str;
|
|
|
|
lex_next(lex);
|
|
|
|
return lex;
|
|
}
|
|
|
|
void lex_free(struct lexer *lex) {
|
|
free(lex);
|
|
}
|
|
|
|
static int is_whitespace(char p) {
|
|
return p == ' ' || p == '\t' || p == '\n' || p == '\r';
|
|
}
|
|
|
|
static void skip_whitespace(struct lexer *lex) {
|
|
while (is_whitespace(*lex->p))
|
|
++lex->p;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int try_fn(struct lexer *lex, const char *tok_str, enum math_fn fn) {
|
|
if (try_advance(lex, tok_str)) {
|
|
lex->tok.type = TOK_FUNCTION;
|
|
lex->tok.val.fn = fn;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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, "x", TOK_X)) return;
|
|
|
|
if (try_token(lex, "(", TOK_LEFT_PAREN)) return;
|
|
if (try_token(lex, ")", TOK_RIGHT_PAREN)) return;
|
|
|
|
if (try_fn(lex, "abs", FN_ABS)) return;
|
|
if (try_fn(lex, "exp", FN_EXP)) return;
|
|
if (try_fn(lex, "ln", FN_LN)) return;
|
|
if (try_fn(lex, "log", FN_LOG)) return;
|
|
if (try_fn(lex, "sinh", FN_SINH)) return;
|
|
if (try_fn(lex, "sin", FN_SIN)) return;
|
|
if (try_fn(lex, "cosh", FN_COSH)) return;
|
|
if (try_fn(lex, "cos", FN_COS)) return;
|
|
if (try_fn(lex, "tanh", FN_TANH)) return;
|
|
if (try_fn(lex, "tan", FN_TAN)) return;
|
|
if (try_fn(lex, "asin", FN_ASIN)) return;
|
|
if (try_fn(lex, "acos", FN_ACOS)) return;
|
|
if (try_fn(lex, "atan", FN_ATAN)) return;
|
|
|
|
if (try_number(lex)) return;
|
|
|
|
fprintf(stderr, "Lexer error - unrecognized sequence \"%s\"\n", lex->p);
|
|
lex_print_position(lex);
|
|
|
|
lex->tok.type = TOK_ERROR;
|
|
return;
|
|
}
|
|
|
|
struct token *lex_token(struct lexer *lex) {
|
|
return &lex->tok;
|
|
}
|
|
|
|
void lex_print_position(struct lexer *lex) {
|
|
int i;
|
|
int pos = lex->prev_p - lex->start;
|
|
|
|
fprintf(stderr, "At character %d\n", pos);
|
|
fprintf(stderr, " %s\n", lex->start);
|
|
fprintf(stderr, " ");
|
|
for (i = 0; i < pos; ++i)
|
|
fprintf(stderr, " ");
|
|
|
|
fprintf(stderr, "^\n----------------------------------------------------\n");
|
|
}
|
|
|
|
|
|
#ifdef LEX_DEBUG
|
|
|
|
void lex_debug_print_token(struct token *tok) {
|
|
static const char *token_str[] = {
|
|
"TOK_EOF",
|
|
"TOK_ERROR",
|
|
"TOK_NUMBER",
|
|
"TOK_PLUS",
|
|
"TOK_MINUS",
|
|
"TOK_MULTIPLY",
|
|
"TOK_DIVIDE",
|
|
"TOK_POWER",
|
|
"TOK_X",
|
|
"TOK_FUNCTION",
|
|
"TOK_LEFT_PAREN",
|
|
"TOK_RIGHT_PAREN"
|
|
};
|
|
|
|
static const char *fn_str[] = {
|
|
"FN_ABS",
|
|
"FN_EXP",
|
|
"FN_LN",
|
|
"FN_LOG",
|
|
"FN_SIN",
|
|
"FN_COS",
|
|
"FN_TAN",
|
|
"FN_ASIN",
|
|
"FN_ACOS",
|
|
"FN_ATAN",
|
|
"FN_SINH",
|
|
"FN_COSH",
|
|
"FN_TANH"
|
|
};
|
|
|
|
printf("%-20s ", token_str[tok->type]);
|
|
|
|
if (tok->type == TOK_NUMBER)
|
|
printf("%.2f\n", tok->val.num);
|
|
else if (tok->type == TOK_FUNCTION)
|
|
printf("%s\n", fn_str[tok->val.fn]);
|
|
else
|
|
printf("\n");
|
|
}
|
|
|
|
#endif /* LEX_DEBUG */ |