Skip to content

Instantly share code, notes, and snippets.

@rctay
Created September 16, 2011 12:51
Show Gist options
  • Save rctay/1222056 to your computer and use it in GitHub Desktop.
Save rctay/1222056 to your computer and use it in GitHub Desktop.
CS2104 interpreter
#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