/* Static checking for E. 
    (excluding '-' and if0-then-else)
   Verifies that no undeclared variable is used. 
*/
#include <stdio.h>
#include "ast.h"

/* Environments describing sets of identifiers ------------------------ */

/* Use a linked list to represent the set. */

typedef struct envS *Env;

struct envS {
  id i;
  Env next;
};

static Env mk_Env(id i, Env next) {
  Env env = checked_malloc(sizeof(*env));
  env->i = i;
  env->next = next;
  return env;
}

static Env empty_env = NULL;

static Env extend_env(Env env,id i) {
    return mk_Env(i,env);
}

static int lookup_env(Env env,id i) {
  while (env != NULL && (strcmp(env->i,i) != 0))
    env = env->next;
  return (env != NULL);
}

/* Checking routines -------------------------------------------------- */

static void check_var(Env env, id i) {
  if (!lookup_env(env,i)) {
    fprintf(stderr,"Variable \"%s\" used but not declared\n", i);
    exit(1);
  }
}

static void check_Exp(Env env,Exp e) {
  switch (e->kind) {
  case VarExp: 
    check_var(env,e->u.varexp.i);
    break;
  case ConstExp:
    break;
  case PlusExp: 
    check_Exp(env,e->u.plusexp.l);
    check_Exp(env,e->u.plusexp.r);
    break;
  case AsgnExp:
    check_var(env,e->u.asgnexp.i);
    check_Exp(env,e->u.asgnexp.e);
    break;
  case BlockExp: {
    ids is;
    Exps es;
    for (is = e->u.blockexp.is; is; is = is->next) 
      env = extend_env(env,is->i);
    for (es = e->u.blockexp.es; es; es = es->next)
      check_Exp(env,es->e);
    break; }
  }
}

/* Top-level checking routine ----------------------------------------- */

void check(Exp e) {
  check_Exp(empty_env,e);
}

