Last active
September 1, 2019 18:07
-
-
Save cpq/a141bd80a98d479dc8e3c3a5ae8827b4 to your computer and use it in GitHub Desktop.
Simple recursive descent expression parser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) 2019 Sergey Lyubka | |
// All rights reserved | |
#include <stdio.h> | |
#include <string.h> | |
enum { TOK_EOF = '#', TOK_NUM = 'N', TOK_IDENT = 'I' }; | |
enum { ERR_OK, ERR_EXPECTED_NUM, ERR_EXPECTED_PAREN }; | |
//#define DEBUG(fmt, ...) printf("%s:" fmt "\n", __func__, __VA_ARGS__) | |
#define DEBUG(fmt, ...) | |
typedef int val_t; | |
struct js { | |
int pos; | |
int tok; | |
const char *code; | |
val_t symtab; | |
}; | |
static int parse_expr(struct js *p, val_t *v); | |
static int pnext(struct js *js) { | |
int tok = TOK_EOF; | |
if (js->code[js->pos] != '\0') { | |
tok = js->code[js->pos++]; | |
if (tok >= '0' && tok <= '9') tok = TOK_NUM; | |
if (tok >= 'a' && tok <= 'z') tok = TOK_IDENT; | |
} | |
js->tok = tok; | |
DEBUG("%c %d", tok, tok); | |
return tok; | |
} | |
static val_t do_op(val_t a, val_t b, int op) { | |
DEBUG("%d %d %c", a, b, op); | |
switch (op) { | |
case '+': | |
return a + b; | |
case '-': | |
return a - b; | |
case '*': | |
return a * b; | |
case '/': | |
return a / b; | |
case '%': | |
return a % b; | |
} | |
return 0; | |
} | |
typedef int bpf_t(struct js *p, val_t *); | |
static int parse_ltr_binop(struct js *js, val_t *v, bpf_t f1, bpf_t f2, | |
const char *ops) { | |
int res = ERR_OK, op; | |
DEBUG("%s", " f1 before"); | |
if ((res = f1(js, v)) != ERR_OK) return res; | |
op = js->tok; | |
DEBUG(" f1 after, v %d, op %c", *v, op); | |
if (strchr(ops, op) != NULL) { | |
val_t b = 0; | |
DEBUG(" f2 before %c", op); | |
pnext(js); | |
if ((res = f2(js, &b)) != ERR_OK) return res; | |
DEBUG(" f2 after %c, val %d", op, b); | |
*v = do_op(*v, b, op); | |
} | |
return res; | |
} | |
static int parse_rtl_binop(struct js *js, val_t *v, bpf_t f1, bpf_t f2, | |
const char *ops) { | |
int res = ERR_OK, op; | |
DEBUG("%s", " f1 before"); | |
if ((res = f1(js, v)) != ERR_OK) return res; | |
op = js->tok; | |
DEBUG(" f1 after, v %d, op %c", *v, op); | |
if (strchr(ops, op) != NULL) { | |
val_t b = 0; | |
DEBUG(" f2 before %c", op); | |
pnext(js); | |
if ((res = f2(js, &b)) != ERR_OK) return res; | |
DEBUG(" f2 after %c, val %d", op, b); | |
*v = do_op(*v, b, op); | |
} | |
return res; | |
} | |
static int parse_literal(struct js *js, val_t *v) { | |
int res = ERR_OK; | |
DEBUG("%s", ""); | |
if (js->tok == '(') { | |
if ((res = parse_expr(js, v)) != ERR_OK) return ERR_OK; | |
if (pnext(js) != ')') return ERR_EXPECTED_PAREN; | |
} else if (js->tok != TOK_NUM) { | |
return ERR_EXPECTED_NUM; | |
} else { | |
*v = js->code[js->pos - 1] - '0'; | |
} | |
pnext(js); | |
return res; | |
} | |
static int parse_mul_div(struct js *p, val_t *v) { | |
DEBUG("%s", ""); | |
return parse_ltr_binop(p, v, parse_literal, parse_mul_div, "*/%"); | |
} | |
static int parse_plus_minus(struct js *p, val_t *v) { | |
DEBUG("%s", ""); | |
return parse_ltr_binop(p, v, parse_mul_div, parse_plus_minus, "+-"); | |
} | |
static int parse_assignment(struct js *js, val_t *v) { | |
DEBUG("%s", ""); | |
return parse_rtl_binop(js, v, parse_plus_minus, parse_assignment, "="); | |
} | |
static int parse_expr(struct js *p, val_t *v) { | |
DEBUG("%s", ""); | |
return parse_assignment(p, v); | |
} | |
static int parse_stmt(struct js *js, val_t *v) { | |
return parse_expr(js, v); | |
} | |
static int parse_code(struct js *js, val_t *v, int endtok) { | |
int res = ERR_OK; | |
DEBUG("endtok %c", endtok); | |
do { | |
do { | |
pnext(js); | |
} while (js->tok == ';'); | |
res = parse_stmt(js, v); | |
} while (res == ERR_OK && js->tok != endtok && js->tok != TOK_EOF); | |
return res; | |
} | |
int main(int argc, char *argv[]) { | |
if (argc > 1) { | |
struct js js = {0, TOK_EOF, argv[1], 0}; | |
val_t v = 0; | |
int res = parse_code(&js, &v, TOK_EOF); | |
printf("%d (%s, code: %d)\n", v, res == ERR_OK ? "success" : "error", res); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment