-
-
Save rctay/1222056 to your computer and use it in GitHub Desktop.
CS2104 interpreter
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
#include <ctype.h> | |
#include <malloc.h> | |
#include <stdarg.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
/* utilities - shamelessly lifted from Git */ | |
void vreportf(const char *prefix, const char *err, va_list params) | |
{ | |
char msg[4096]; | |
vsnprintf(msg, sizeof(msg), err, params); | |
fprintf(stderr, "%s%s\n", prefix, msg); | |
} | |
void die(const char *err, ...) | |
__attribute__((noreturn)) | |
__attribute__((format (printf, 1, 2))); | |
void die(const char *err, ...) | |
{ | |
va_list params; | |
va_start(params, err); | |
vreportf("fatal: ", err, params); | |
exit(128); | |
va_end(params); | |
} | |
int error(const char *err, ...) | |
{ | |
va_list params; | |
va_start(params, err); | |
vreportf("error: ", err, params); | |
va_end(params); | |
return -1; | |
} | |
/* | |
* strbuf implementation - shamelessly lifted from Git | |
*/ | |
struct strbuf { | |
size_t alloc; | |
size_t len; | |
char *buf; | |
}; | |
#define STRBUF_INIT {0, 0, NULL} | |
void strbuf_init(struct strbuf *sb) { | |
sb->alloc = sb->len = 0; | |
if (!(sb->buf = (char *) malloc(sizeof(char)))) | |
die("malloc failed"); | |
sb->buf[0] = '\0'; | |
} | |
void strbuf_grow(struct strbuf *sb, size_t size) { | |
char *newbuf; | |
if (sb->alloc >= size) | |
return; | |
if (!(newbuf = (char *) realloc(sb->buf, size * sizeof(char)))) | |
die("realloc failed"); | |
sb->buf = newbuf; | |
} | |
void strbuf_addch(struct strbuf *sb, char *ch) { | |
size_t new_sz; | |
if (sb->len + 1 > sb->alloc) { | |
new_sz = 1 + sb->alloc * 2; | |
strbuf_grow(sb, new_sz); | |
sb->alloc = new_sz; | |
} | |
sb->buf[sb->len++] = *ch; | |
sb->buf[sb->len] = '\0'; | |
} | |
char *strbuf_detach(struct strbuf *sb) { | |
char *res = sb->alloc ? sb->buf : NULL; | |
sb->alloc = sb->len = 0; | |
sb->buf = NULL; | |
return res; | |
} | |
/* operators */ | |
typedef int op_t; | |
const op_t OP_NUL = 0; | |
const op_t OP_ADD = 1; | |
const op_t OP_SUB = 2; | |
const op_t OP_MUL = 3; | |
const op_t OP_DIV = 4; | |
struct expression { | |
struct strbuf buf; | |
char *var_name; | |
op_t op; | |
char *val; | |
}; | |
struct pvar { | |
char *name; | |
int val; | |
struct pvar *next; | |
}; | |
struct context { | |
struct pvar *vars; | |
}; | |
/* interpreter */ | |
struct pvar *get_var_named(struct context *ctxt, char *name) { | |
struct pvar *curr; | |
if (!ctxt->vars) | |
return NULL; | |
for (curr = ctxt->vars; curr; curr = curr->next) { | |
if (!strcmp(curr->name, name)) | |
return curr; | |
} | |
return NULL; | |
} | |
int get_val(struct context *ctxt, struct expression *expr) { | |
char *ch; | |
struct pvar *var; | |
int val = 0; | |
var = get_var_named(ctxt, expr->val); | |
if (var) | |
return var->val; | |
ch = expr->val; | |
while (*ch != '\0') { | |
if (!isdigit(*ch)) | |
die("unrecognized value or var: %s", expr->val); | |
val = val * 10 + atoi(ch); | |
ch++; | |
} | |
return val; | |
} | |
int run_expr(struct context *ctxt, struct expression *expr) { | |
int ret = 0; | |
int val; | |
struct pvar *var; | |
var = get_var_named(ctxt, expr->var_name); | |
if (!var) { | |
if (!(var = (struct pvar *) malloc(sizeof(struct pvar)))) | |
die("malloc failed"); | |
memset(var, 0, sizeof(struct pvar)); | |
var->name = expr->var_name; | |
if (ctxt->vars) | |
ctxt->vars->next = var; | |
else | |
ctxt->vars = var; | |
expr->var_name = NULL; | |
} | |
val = get_val(ctxt, expr); | |
if (expr->op == OP_ADD) | |
var->val += val; | |
else if (expr->op == OP_SUB) | |
var->val -= val; | |
else if (expr->op == OP_MUL) | |
var->val *= val; | |
else if (expr->op == OP_DIV) | |
var->val /= val; | |
free(expr->var_name); | |
free(expr->val); | |
return ret; | |
} | |
/* state transitions */ | |
#define STATE_START 0 | |
#define STATE_VAR 1 | |
#define STATE_OP 2 | |
#define STATE_EQ 3 | |
#define STATE_VAL 4 | |
#define STATE_END 5 | |
typedef int state_handler(struct expression *, char); | |
typedef int state_transit_out(struct expression *); | |
struct transition { | |
int before; | |
int after; | |
state_handler *handler; | |
state_transit_out *transit_out; | |
}; | |
/* var state */ | |
struct transition *state_transitions[5][256]; | |
int _name_handler(struct expression *expr, char ch) { | |
strbuf_addch(&expr->buf, &ch); | |
return 0; | |
} | |
int var_transit_out(struct expression *expr) { | |
expr->var_name = strbuf_detach(&expr->buf); | |
return 0; | |
} | |
struct transition start_to_var = { STATE_START, STATE_VAR, _name_handler, var_transit_out }; | |
struct transition var_to_var = { STATE_VAR, STATE_VAR, _name_handler, var_transit_out }; | |
/* op states */ | |
int add_op_handler(struct expression *expr, char ch) { | |
expr->op = OP_ADD; | |
return 0; | |
} | |
struct transition var_to_add_op = { STATE_VAR, STATE_OP, add_op_handler, NULL }; | |
int sub_op_handler(struct expression *expr, char ch) { | |
expr->op = OP_SUB; | |
return 0; | |
} | |
struct transition var_to_sub_op = { STATE_VAR, STATE_OP, sub_op_handler, NULL }; | |
int mul_op_handler(struct expression *expr, char ch) { | |
expr->op = OP_MUL; | |
return 0; | |
} | |
struct transition var_to_mul_op = { STATE_VAR, STATE_OP, mul_op_handler, NULL }; | |
int div_op_handler(struct expression *expr, char ch) { | |
expr->op = OP_DIV; | |
return 0; | |
} | |
struct transition var_to_div_op = { STATE_VAR, STATE_OP, div_op_handler, NULL }; | |
/* eq state */ | |
struct transition op_to_eq = { STATE_OP, STATE_EQ, NULL, NULL }; | |
/* val state */ | |
int val_transit_out(struct expression *expr) { | |
expr->val = strbuf_detach(&expr->buf); | |
return 0; | |
} | |
struct transition eq_to_val = { STATE_EQ, STATE_VAL, _name_handler, val_transit_out }; | |
struct transition val_to_val = { STATE_VAL, STATE_VAL, _name_handler, val_transit_out }; | |
/* end state */ | |
struct transition val_to_end = { STATE_VAL, STATE_END, NULL, NULL }; | |
/* | |
* helpers to fill the transition map | |
*/ | |
void fill_one(int index, struct transition *t) { | |
state_transitions[t->before][index] = t; | |
} | |
void fill_range(int from, int to, struct transition *t) { | |
struct transition **sts = state_transitions[t->before]; | |
for (; from <= to; from++) | |
sts[from] = t; | |
} | |
void setup_states() { | |
memset(&state_transitions, 0, sizeof(state_transitions)); | |
fill_one('_', &start_to_var); | |
fill_range('a', 'z', &start_to_var); | |
fill_range('A', 'Z', &start_to_var); | |
fill_one('_', &var_to_var); | |
fill_range('a', 'z', &var_to_var); | |
fill_range('A', 'Z', &var_to_var); | |
fill_range('0', '9', &var_to_var); | |
fill_one('+', &var_to_add_op); | |
fill_one('-', &var_to_sub_op); | |
fill_one('*', &var_to_mul_op); | |
fill_one('/', &var_to_div_op); | |
fill_one('=', &op_to_eq); | |
fill_one('_', &eq_to_val); | |
fill_range('a', 'z', &eq_to_val); | |
fill_range('A', 'Z', &eq_to_val); | |
fill_range('0', '9', &eq_to_val); | |
fill_one('_', &val_to_val); | |
fill_range('a', 'z', &val_to_val); | |
fill_range('A', 'Z', &val_to_val); | |
fill_range('0', '9', &val_to_val); | |
fill_one(';', &val_to_end); | |
} | |
int main() { | |
int ret; | |
char ch; | |
int state = STATE_START; | |
struct transition *t, *prev = NULL; | |
struct expression expr = { STRBUF_INIT, NULL, OP_NUL, NULL }; | |
struct context ctxt = { NULL }; | |
struct pvar *curr; | |
setup_states(); | |
while (scanf("%c", &ch) == 1) { | |
t = state_transitions[state][(int) ch]; | |
if (!t) | |
return error("bad char '%c' for state %d\n", ch, state); | |
/* let the previous state clean up before calling the new state's handler */ | |
if (t->after != state | |
&& prev | |
&& prev->transit_out | |
&& (ret = prev->transit_out(&expr))) | |
/* assume prev->transit_out() has reported the error */ | |
return ret; | |
if (t->handler && (ret = t->handler(&expr, ch))) | |
/* assume prev->handler() has reported the error */ | |
return ret; | |
state = t->after; | |
prev = t; | |
if (state == STATE_END) { | |
if ((ret = run_expr(&ctxt, &expr))) | |
/* assume run_expr() has reported the error */ | |
return ret; | |
state = STATE_START; | |
prev = NULL; | |
} | |
} | |
if (state != STATE_START) | |
return error("incomplete expression near token %d\n", state); | |
curr = ctxt.vars; | |
while (curr) { | |
printf("%s = %d%s", | |
curr->name, | |
curr->val, | |
curr->next ? "; " : "\n"); | |
curr = curr->next; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment