Skip to content

Instantly share code, notes, and snippets.

@cpq
Last active September 1, 2019 18:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cpq/a141bd80a98d479dc8e3c3a5ae8827b4 to your computer and use it in GitHub Desktop.
Save cpq/a141bd80a98d479dc8e3c3a5ae8827b4 to your computer and use it in GitHub Desktop.
Simple recursive descent expression parser
// 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