Sexpr* args = b->value.b.args;
        Sexpr* val = eval(car(args), env);
        Sexpr* key = car(cdr(args));
-       printf("val: %s\n", sprint_sexpr(val));
-       printf("key: %s\n", sprint_sexpr(key));
        append_to_dict(env, key, val);
        return val;
 }
 
 Sexpr* apply_builtin(Sexpr* func, Sexpr* arg, Sexpr* env);
 
 Sexpr* eval(Sexpr* s, Sexpr* dict) {
-       printf("s: %s\n", sprint_sexpr(s));
+       //printf("s: %s\n", sprint_sexpr(s));
        // non-null s
        // generally assumes that a sexpr passed to this is well-formed (ie no inapt NULLS)
        // question: does a completed builtin get evaluated here?
        if(s->type != CONS) {
                return s;
        } // from now on: type is cons
-       //printf("idk\n");
        Sexpr* curr = s;
        while(curr->type == CONS) {
                if(cdr(curr)->type == NIL)
 
 }
 
 
+bool balance_checker(Sexpr* tokens) {
+       int balance = 0;
+       Sexpr* curr = tokens;
+       while(curr->type != NIL) {
+               Sexpr* h = car(curr);
+               if(h->type == SYM) {
+                       if(strcmp(h->value.s, "(")==0)
+                               balance++;
+                       if(strcmp(h->value.s, ")")==0)
+                               balance--;
+                       if(balance < 0) return false;
+               }
+               curr = cdr(curr);
+       }
+       //printf("b: %d\n", balance);
+       return balance == 0;
+}
+
 Sexpr* cons_parse(Sexpr* tokens) {
        Sexpr* reversed = reverse(tokens);
        // takes results from previous parsing ops, aka the forward-facing?
        //printf("t: %s\n", sprint_sexpr(*tokens));
        Sexpr* vals = vals_parse(tokens);
        //printf("v: %s\n", sprint_sexpr(vals));
+       if(!balance_checker(vals)) {
+               printf("unbalanced parenthesis\n");
+               return NULL;
+       }
        Sexpr* done = cons_parse(vals);
        //printf("c: %s\n", sprint_sexpr(*done));
        return done;
 
                if(input == NULL)
                        return 0;
                linenoiseHistoryAdd(input);
-               printf("asdf1\n");
                Sexpr* in = parse(input);
-               printf("asdf2\n");
                if(in == NULL) {
                        printf("bad input\n");
                }
-               printf("- -%s\n", sprint_sexpr(in));
-               Sexpr* out = eval(car(in), env);
-               printf(" - %s\n", sprint_sexpr(out));
+               else {
+                       //printf("- -%s\n", sprint_sexpr(in));
+                       Sexpr* out = eval(car(in), env);
+                       printf(" - %s\n", sprint_sexpr(out));
+               }
                linenoiseFree(input);
        }
        return 0;
 
 }
 
 char* sprint_sexpr(Sexpr* s) {
-       if(s == NULL) printf("SHIT IT'S NULL\n");
+       if(s == NULL) {
+               printf("UH OH IT'S NULL\n");
+               return NULL;
+       }
        // assumes not null
        size_t nbytes;
        char* out;
 
 void test_parser() {
        printf("parsing again...\n");
 
-       printf("_: %s\n", sprint_sexpr(parse("457")));
+       printf("_: %s\n", sprint_sexpr(parse("456")));
        printf("_: %s\n", sprint_sexpr(parse("a b c d e")));
        printf("_: %s\n", sprint_sexpr(parse("(a b (3 2 (5) c) d (e f) g)")));
-       //printf("_: %s\n", sprint_sexpr(parse("(457")));
+       printf("_: %s\n", sprint_sexpr(parse("(457")));
 }
 
 void test_eq() {