Example #1
0
/*
 * NAME:	optimize->expr()
 * DESCRIPTION:	optimize an expression
 */
static Uint opt_expr(node **m, bool pop)
{
    Uint d1, d2, i;
    node *n;
    node **oldside, *side;
    Uint olddepth;

    n = *m;
    switch (n->type) {
    case N_FLOAT:
    case N_GLOBAL:
    case N_INT:
    case N_LOCAL:
    case N_STR:
    case N_NIL:
	return !pop;

    case N_TOINT:
    case N_CAST:
	return opt_expr(&n->l.left, FALSE);

    case N_NEG:
    case N_UMIN:
	return max2(opt_expr(&n->l.left, FALSE), 2);

    case N_CATCH:
	oldside = side_start(&side, &olddepth);
	d1 = opt_expr(&n->l.left, TRUE);
	if (d1 == 0) {
	    n->l.left = (node *) NULL;
	}
	d1 = max2(d1, side_end(&n->l.left, side, oldside, olddepth));
	if (d1 == 0) {
	    *m = node_nil();
	    (*m)->line = n->line;
	    return !pop;
	}
	return d1;

    case N_TOFLOAT:
	if (n->l.left->mod != T_INT) {
	    return opt_expr(&n->l.left, FALSE);
	}
	/* fall through */
    case N_NOT:
    case N_TST:
	if (pop) {
	    *m = n->l.left;
	    return opt_expr(m, TRUE);
	}
	return opt_expr(&n->l.left, FALSE);

    case N_TOSTRING:
	if (pop && (n->l.left->mod == T_INT || n->l.left->mod == T_FLOAT)) {
	    *m = n->l.left;
	    return opt_expr(m, TRUE);
	}
	return opt_expr(&n->l.left, FALSE);

    case N_LVALUE:
	return opt_lvalue(n->l.left);

    case N_ADD_EQ_1:
    case N_ADD_EQ_1_INT:
    case N_ADD_EQ_1_FLOAT:
    case N_SUB_EQ_1:
    case N_SUB_EQ_1_INT:
    case N_SUB_EQ_1_FLOAT:
	return opt_lvalue(n->l.left) + 1;

    case N_MIN_MIN:
	if (pop) {
	    n->type = N_SUB_EQ_1;
	}
	return opt_lvalue(n->l.left) + 1;

    case N_MIN_MIN_INT:
	if (pop) {
	    n->type = N_SUB_EQ_1_INT;
	}
	return opt_lvalue(n->l.left) + 1;

    case N_MIN_MIN_FLOAT:
	if (pop) {
	    n->type = N_SUB_EQ_1_FLOAT;
	}
	return opt_lvalue(n->l.left) + 1;

    case N_PLUS_PLUS:
	if (pop) {
	    n->type = N_ADD_EQ_1;
	}
	return opt_lvalue(n->l.left) + 1;

    case N_PLUS_PLUS_INT:
	if (pop) {
	    n->type = N_ADD_EQ_1_INT;
	}
	return opt_lvalue(n->l.left) + 1;

    case N_PLUS_PLUS_FLOAT:
	if (pop) {
	    n->type = N_ADD_EQ_1_FLOAT;
	}
	return opt_lvalue(n->l.left) + 1;

    case N_FUNC:
	m = &n->l.left->r.right;
	n = *m;
	if (n == (node *) NULL) {
	    return 1;
	}

	d1 = 0;
	for (i = 0; n->type == N_PAIR; ) {
	    oldside = side_start(&side, &olddepth);
	    d2 = opt_expr(&n->l.left, FALSE);
	    d1 = max3(d1, i + d2,
		      i + side_end(&n->l.left, side, oldside, olddepth));
	    m = &n->r.right;
	    n = n->l.left;
	    i += (n->type == N_LVALUE ||
		  (n->type == N_COMMA && n->r.right->type == N_LVALUE)) ? 6 : 1;
	    n = *m;
	}
	if (n->type == N_SPREAD) {
	    m = &n->l.left;
	}
	oldside = side_start(&side, &olddepth);
	d2 = opt_expr(m, FALSE);
	d1 = max3(d1, i + d2, i + side_end(m, side, oldside, olddepth));
	n = *m;
	if (n->type == N_LVALUE ||
	    (n->type == N_COMMA && n->r.right->type == N_LVALUE)) {
	    d1 += 2;
	}
	return d1;

    case N_INSTANCEOF:
	return opt_expr(&n->l.left, FALSE) + 1;

    case N_GE:
    case N_GT:
    case N_LE:
    case N_LT:
	if (n->l.left->mod != n->r.right->mod) {
	    return max2(opt_expr(&n->l.left, FALSE),
			opt_expr(&n->r.right, FALSE) + 1);
	}
	/* fall through */
    case N_EQ:
    case N_NE:
	if (pop) {
	    d1 = opt_expr(&n->l.left, TRUE);
	    if (d1 == 0) {
		*m = n->r.right;
		return opt_expr(m, TRUE);
	    }
	    d2 = opt_expr(&n->r.right, TRUE);
	    if (d2 == 0) {
		*m = n->l.left;
		return d1;
	    }
	    n->type = N_COMMA;
	    side_add(m, d1);
	    return d2;
	}
	return opt_binop(m);

    case N_DIV_INT:
    case N_MOD_INT:
	if (n->r.right->type == N_INT && n->r.right->l.number == 0) {
	    d1 = opt_binop(m);
	    return (d1 == 1) ? !pop : d1;
	}
	/* fall through */
    case N_ADD_INT:
    case N_ADD_FLOAT:
    case N_AND_INT:
    case N_DIV_FLOAT:
    case N_EQ_INT:
    case N_EQ_FLOAT:
    case N_GE_INT:
    case N_GE_FLOAT:
    case N_GT_INT:
    case N_GT_FLOAT:
    case N_LE_INT:
    case N_LE_FLOAT:
    case N_LSHIFT_INT:
    case N_LT_INT:
    case N_LT_FLOAT:
    case N_MULT_INT:
    case N_MULT_FLOAT:
    case N_NE_INT:
    case N_NE_FLOAT:
    case N_OR_INT:
    case N_RSHIFT_INT:
    case N_SUB_INT:
    case N_SUB_FLOAT:
    case N_XOR_INT:
	if (pop) {
	    d1 = opt_expr(&n->l.left, TRUE);
	    if (d1 == 0) {
		*m = n->r.right;
		return opt_expr(m, TRUE);
	    }
	    d2 = opt_expr(&n->r.right, TRUE);
	    if (d2 == 0) {
		*m = n->l.left;
		return d1;
	    }
	    n->type = N_COMMA;
	    side_add(m, d1);
	    return d2;
	}
	/* fall through */
    case N_ADD:
    case N_AND:
    case N_DIV:
    case N_LSHIFT:
    case N_MOD:
    case N_MULT:
    case N_OR:
    case N_RSHIFT:
    case N_SUB:
    case N_SUM:
    case N_XOR:
	d1 = opt_binop(m);
	return (d1 == 1) ? !pop : d1;

    case N_INDEX:
	if (n->l.left->type == N_STR && n->r.right->type == N_INT) {
	    if (n->r.right->l.number < 0 ||
		n->r.right->l.number >= (long) n->l.left->l.string->len) {
		return 2;
	    }
	    node_toint(n, (Int) str_index(n->l.left->l.string,
					  (long) n->r.right->l.number));
	    return !pop;
	}
	if (n->l.left->type == N_FUNC && n->r.right->mod == T_INT) {
	    if (n->l.left->r.number == kd_status) {
		n->type = N_FUNC;
		if (n->l.left->l.left->r.right != (node *) NULL) {
		    /* status(obj)[i] */
		    n = n->l.left;
		    n->type = N_STR;
		    n->r.right = n->l.left;
		    n->l.string = n->l.left->l.string;
		    n = n->r.right;
		    n->type = N_PAIR;
		    n->l.left = n->r.right;
		    n->r.right = (*m)->r.right;
		    (*m)->r.number = ((long) KFCALL << 24) | KF_STATUSO_IDX;
		} else {
		    /* status()[i] */
		    n->l.left = n->l.left->l.left;
		    n->l.left->r.right = n->r.right;
		    n->r.number = ((long) KFCALL << 24) | KF_STATUS_IDX;
		}
		return opt_expr(m, pop);
	    }
	    if (n->l.left->r.number == kd_call_trace) {
		/* call_trace()[i] */
		n->type = N_FUNC;
		n->l.left = n->l.left->l.left;
		n->l.left->r.right = n->r.right;
		n->r.number = ((long) KFCALL << 24) | KF_CALLTR_IDX;
		return opt_expr(m, pop);
	    }
	}
	return max3(opt_expr(&n->l.left, FALSE),
		    opt_expr(&n->r.right, FALSE) + 1, 3);

    case N_ADD_EQ:
    case N_ADD_EQ_INT:
    case N_ADD_EQ_FLOAT:
    case N_AND_EQ:
    case N_AND_EQ_INT:
    case N_DIV_EQ:
    case N_DIV_EQ_INT:
    case N_DIV_EQ_FLOAT:
    case N_LSHIFT_EQ:
    case N_LSHIFT_EQ_INT:
    case N_MOD_EQ:
    case N_MOD_EQ_INT:
    case N_MULT_EQ:
    case N_MULT_EQ_INT:
    case N_MULT_EQ_FLOAT:
    case N_OR_EQ:
    case N_OR_EQ_INT:
    case N_RSHIFT_EQ:
    case N_RSHIFT_EQ_INT:
    case N_SUB_EQ:
    case N_SUB_EQ_INT:
    case N_SUB_EQ_FLOAT:
    case N_SUM_EQ:
    case N_XOR_EQ:
    case N_XOR_EQ_INT:
	return opt_asgnexp(m, pop);

    case N_ASSIGN:
	if (n->l.left->type == N_AGGR) {
	    d2 = 0;
	    for (n = n->l.left->l.left; n->type == N_PAIR; n = n->r.right) {
		d1 = opt_lvalue(n->l.left);
		d2 += 2 + ((d1 < 4) ? d1 : 4);
	    }
	    d1 = opt_lvalue(n);
	    d2 += 2 + ((d1 < 4) ? d1 : 4);
	    return d2 + max2(2, opt_expr(&(*m)->r.right, FALSE));
	} else {
	    d1 = opt_lvalue(n->l.left);
	    return max2(d1, ((d1 < 4) ? d1 : 4) + opt_expr(&n->r.right, FALSE));
	}

    case N_COMMA:
	side_add(m, opt_expr(&n->l.left, TRUE));
	return opt_expr(m, pop);

    case N_LAND:
	d1 = opt_cond(&n->l.left, FALSE);
	if (n->l.left->flags & F_CONST) {
	    if (!opt_ctest(n->l.left)) {
		/* false && x */
		*m = n->l.left;
		return !pop;
	    }
	    /* true && x */
	    n->type = N_TST;
	    n->l.left = n->r.right;
	    return opt_expr(m, pop);
	}

	oldside = side_start(&side, &olddepth);
	d2 = opt_cond(&n->r.right, pop);
	if (d2 == 0) {
	    n->r.right = (node *) NULL;
	}
	d2 = max2(d2, side_end(&n->r.right, side, oldside, olddepth));
	if (d2 == 0) {
	    *m = n->l.left;
	    return opt_expr(m, TRUE);
	}
	if (n->r.right->flags & F_CONST) {
	    if (pop) {
		*m = n->l.left;
		return opt_expr(m, TRUE);
	    }
	    if (!opt_ctest(n->r.right)) {
		/* x && false */
		n->type = N_COMMA;
		return opt_expr(m, FALSE);
	    }
	    /* x && true */
	    n->type = N_TST;
	    return d1;
	}
	if (n->r.right->type == N_COMMA) {
	    n = n->r.right;
	    if ((n->r.right->flags & F_CONST) && !opt_ctest(n->r.right)) {
		/* x && (y, false) --> (x && y, false) */
		(*m)->r.right = n->l.left;
		n->l.left = *m;
		*m = n;
		return opt_expr(m, pop);
	    }
	}
	return max2(d1, d2);

    case N_LOR:
	d1 = opt_cond(&n->l.left, FALSE);
	if (n->l.left->flags & F_CONST) {
	    if (opt_ctest(n->l.left)) {
		/* true || x */
		*m = n->l.left;
		return !pop;
	    }
	    /* false || x */
	    n->type = N_TST;
	    n->l.left = n->r.right;
	    return opt_expr(m, pop);
	}

	oldside = side_start(&side, &olddepth);
	d2 = opt_cond(&n->r.right, pop);
	if (d2 == 0) {
	    n->r.right = (node *) NULL;
	}
	d2 = max2(d2, side_end(&n->r.right, side, oldside, olddepth));
	if (d2 == 0) {
	    *m = n->l.left;
	    return opt_expr(m, TRUE);
	}
	if (n->r.right->flags & F_CONST) {
	    if (pop) {
		*m = n->l.left;
		return opt_expr(m, TRUE);
	    }
	    if (opt_ctest(n->r.right)) {
		/* x || true */
		n->type = N_COMMA;
		return opt_expr(m, FALSE);
	    }
	    /* x || false */
	    n->type = N_TST;
	    return d1;
	}
	if (n->r.right->type == N_COMMA) {
	    n = n->r.right;
	    if ((n->r.right->flags & F_CONST) && opt_ctest(n->r.right)) {
		/* x || (y, true) --> (x || y, true) */
		(*m)->r.right = n->l.left;
		n->l.left = *m;
		*m = n;
		return opt_expr(m, pop);
	    }
	}
	return max2(d1, d2);

    case N_QUEST:
	i = opt_cond(&n->l.left, FALSE);
	if (n->l.left->flags & F_CONST) {
	    if (opt_ctest(n->l.left)) {
		*m = n->r.right->l.left;
	    } else {
		*m = n->r.right->r.right;
	    }
	    return opt_expr(m, pop);
	}
	if (n->l.left->type == N_COMMA && (n->l.left->r.right->flags & F_CONST))
	{
	    side_add(&n->l.left, i);
	    if (opt_ctest(n->l.left)) {
		*m = n->r.right->l.left;
	    } else {
		*m = n->r.right->r.right;
	    }
	    return opt_expr(m, pop);
	}

	n = n->r.right;
	oldside = side_start(&side, &olddepth);
	d1 = opt_expr(&n->l.left, pop);
	if (d1 == 0) {
	    n->l.left = (node *) NULL;
	}
	d1 = max2(d1, side_end(&n->l.left, side, oldside, olddepth));
	if (d1 == 0) {
	    n->l.left = (node *) NULL;
	}
	oldside = side_start(&side, &olddepth);
	d2 = opt_expr(&n->r.right, pop);
	if (d2 == 0) {
	    n->r.right = (node *) NULL;
	}
	d2 = max2(d2, side_end(&n->r.right, side, oldside, olddepth));
	if (d2 == 0) {
	    n->r.right = (node *) NULL;
	}
	return max3(i, d1, d2);

    case N_RANGE:
	d1 = opt_expr(&n->l.left, FALSE);
	d2 = 1;
	if (n->r.right->l.left != (node *) NULL) {
	    d2 = opt_expr(&n->r.right->l.left, FALSE);
	    if ((n->l.left->mod == T_STRING || (n->l.left->mod & T_REF) != 0) &&
		n->r.right->l.left->type == N_INT &&
		n->r.right->l.left->l.number == 0) {
		/*
		 * str[0 .. x] or arr[0 .. x]
		 */
		n->r.right->l.left = (node *) NULL;
		d2 = 1;
	    } else {
		d1 = max2(d1, d2 + 1);
		d2 = 2;
	    }
	}
	if (n->r.right->r.right != (node *) NULL) {
	    d1 = max2(d1, d2 + opt_expr(&n->r.right->r.right, FALSE));
	}
	if (n->l.left->type == N_STR) {
	    long from, to;

	    if (n->r.right->l.left == (node *) NULL) {
		from = 0;
	    } else {
		if (n->r.right->l.left->type != N_INT) {
		    return d1;
		}
		from = n->r.right->l.left->l.number;
	    }
	    if (n->r.right->r.right == (node *) NULL) {
		to = n->l.left->l.string->len - 1;
	    } else {
		if (n->r.right->r.right->type != N_INT) {
		    return d1;
		}
		to = n->r.right->r.right->l.number;
	    }
	    if (from >= 0 && from <= to + 1 &&
		to < (long) n->l.left->l.string->len) {
		node_tostr(n, str_range(n->l.left->l.string, from, to));
		return !pop;
	    }
	    return d1;
	}
	return max2(d1, 3);

    case N_AGGR:
	if (n->mod == T_MAPPING) {
	    n = n->l.left;
	    if (n == (node *) NULL) {
		return 1;
	    }

	    d1 = 0;
	    for (i = 0; n->type == N_PAIR; i += 2) {
		oldside = side_start(&side, &olddepth);
		d2 = opt_expr(&n->l.left->l.left, FALSE);
		d1 = max3(d1, i + d2, i + side_end(&n->l.left->l.left,
						   side, oldside, olddepth));
		oldside = side_start(&side, &olddepth);
		d2 = opt_expr(&n->l.left->r.right, FALSE);
		d1 = max3(d1, i + 1 + d2,
			  i + 1 + side_end(&n->l.left->r.right, side, oldside,
					   olddepth));
		n = n->r.right;
	    }
	    oldside = side_start(&side, &olddepth);
	    d2 = opt_expr(&n->l.left, FALSE);
	    d1 = max3(d1, i + d2,
		      i + side_end(&n->l.left, side, oldside, olddepth));
	    oldside = side_start(&side, &olddepth);
	    d2 = opt_expr(&n->r.right, FALSE);
	    return max3(d1, i + 1 + d2,
			i + 1 + side_end(&n->r.right, side, oldside, olddepth));
	} else {
	    m = &n->l.left;
	    n = *m;
	    if (n == (node *) NULL) {
		return 1;
	    }

	    d1 = 0;
	    for (i = 0; n->type == N_PAIR; i++) {
		oldside = side_start(&side, &olddepth);
		d2 = opt_expr(&n->l.left, FALSE);
		d1 = max3(d1, i + d2,
			  i + side_end(&n->l.left, side, oldside, olddepth));
		m = &n->r.right;
		n = *m;
	    }
	    oldside = side_start(&side, &olddepth);
	    d2 = opt_expr(m, FALSE);
	    return max3(d1, i + d2, i + side_end(m, side, oldside, olddepth));
	}
    }

# ifdef DEBUG
    fatal("unknown expression type %d", n->type);
# endif
    return 0;
}
Example #2
0
File: test.c Project: perlawk/horns
void test() {
	test_code("VERSION", node_str(VERSION));
	test_code("PLATFORM", node_str(PLATFORM));
	test_code("ARCH", node_str(ARCH));
	test_code("COPYRIGHT", node_str(COPYRIGHT));

	test_code("\'()", node_empty_list());
	test_code("(quote)", node_empty_list());

	test_code("(+ 1 2)", node_num(3));
	test_code("(- 9 5)", node_num(4));
	test_code("(* 1 5)", node_num(5));
	test_code("(/ 12 2)", node_num(6));
	test_code("(% 7 8)", node_num(7));
	test_code("(+ 1.5 1.5)", node_num(3));

	test_code("1e+300", node_num(1e+300));
	test_code("(string 1e+300)", node_str("1e+300"));

	test_code("(** 2 3 2)", node_num(64));
	test_code("(sqrt 4)", node_num(2));
	test_code("(sin (/ pi 2))", node_num(1));
	test_code("(cos pi)", node_num(-1));
	test_code("(ln e)", node_num(1));
	test_code("(log 100)", node_num(2));

	test_code("(not true)", node_nil());
	test_code("(not nil)", node_true());
	test_code("(and true true)", node_true());
	test_code("(and true nil)", node_nil());
	test_code("(and true true true nil)", node_nil());
	test_code("(and nil true)", node_nil());
	test_code("(and nil nil)", node_nil());
	test_code("(or true nil)", node_true());
	test_code("(or true true)", node_true());
	test_code("(or nil true)", node_true());
	test_code("(or nil nil)", node_nil());
	test_code("(or nil nil nil true)", node_true());

	node *l=node_empty_list();
	l=node_append(l, node_num(1));
	l=node_append(l, node_num(2));
	l=node_append(l, node_num(3));
	test_code("(push \'(2 3) 1)", l);
	test_code("(push (quote 2 3) 1)", l);
	test_code("(push \"at\" \"c\")", node_str("cat"));
	test_code("(set \'a \'(2 3)) (push a 1) a", l);
	test_code("(set \'b (pop a)) b", node_num(1));

	l=node_empty_list();
	l=node_append(l, node_num(2));
	l=node_append(l, node_num(3));
	test_code("a", l);

	test_code("(exists? x)", node_nil());
	test_code("(set \'x 5) (exists? x)", node_true());

	test_code("(string nil)", node_str("nil"));
	test_code("(string 5)", node_str("5"));
	test_code("(string 3.14)", node_str("3.14"));
	test_code("(string \'(1 2 3))", node_str("(1 2 3)"));
	test_code("(string \"hi\")", node_str("hi"));

	test_code("(chomp \"Herb\\n\")", node_str("Herb"));
	test_code("(chomp \"Herb\\r\")", node_str("Herb"));
	test_code("(chomp \"Herb\\r\\n\")", node_str("Herb"));
	test_code("(chomp \"Herb\")", node_str("Herb"));
	test_code("(chop \"Herb\\n\")", node_str("Herb"));
	test_code("(chop \"Herb\\r\")", node_str("Herb"));
	test_code("(chop \"Herb\")", node_str("Her"));

	test_code("(atom? 1)", node_true());
	test_code("(length 1)", node_num(0));
	test_code("(compare 1 2)", node_num(-1));
	test_code("(compare nil nil)", node_num(0));
	test_code("(compare \'(1 2) \'(1 2))", node_num(0));
	test_code("(< -1 0)", node_true());
	test_code("(< 1 2)", node_true());
	test_code("(> 2 1)", node_true());
	test_code("(< 2 1)", node_nil());
	test_code("(<= 1 1)", node_true());
	test_code("(>= 2 2)", node_true());
	test_code("(= 1 1)", node_true());
	test_code("(= 1 2)", node_nil());
	test_code("(= (copy \'(1 2 3)) \'(1 2 3))", node_true());
	test_code("(= nil true)", node_nil());
	test_code("(!= nil true)", node_true());

	test_code("(= (copy +) +)", node_true());

	l=node_empty_list();
	l=node_append(l, node_num(1));
	l=node_append(l, node_num(2));
	l=node_append(l, node_num(3));
	test_code("(append \'(1 2) 3)", l);
	test_code("(prepend \'(2 3) 1)", l);

	test_code("(eval)", node_nil());
	test_code("(eval 1)", node_num(1));
	test_code("(eval \'(1 2 3))", node_nil());
	test_code("(eval \'(+ 1 1))", node_num(2));
	test_code("(eval \'(eval \'(+ 1 1)))", node_num(2));

	node *calculation=node_empty_list();
	calculation=node_append(calculation, node_id("+"));
	calculation=node_append(calculation, node_num(1));
	calculation=node_append(calculation, node_num(1));

	test_code("\'(+ 1 1)", calculation);

	test_code("(cat \'(1) \'(2) \'(3))", l);
	test_code("(cat \'() \'(1 2) \'(3))", l);
	test_code("(cat \'(1 2) \'(3) \'())", l);

	test_code("(cat \"c\" \"a\" \"t\")", node_str("cat"));
	test_code("(cat \"\" \"ca\" \"t\")", node_str("cat"));
	test_code("(cat \"ca\" \"t\" \"\")", node_str("cat"));

	test_code("(length \"hi\")", node_num(2));

	l=node_empty_list();
	l=node_append(l, node_num(1));
	l=node_append(l, node_num(2));
	test_code("\'(1 2)", l);

	test_code("(atom? \'(1 2))", node_nil());
	test_code("(length \'(1 2))", node_num(2));
	test_code("(empty? nil)", node_true());
	test_code("(empty? \'())", node_true());
	test_code("(empty? \'(1))", node_nil());

	test_code("(first \'(1 2 3))", node_num(1));
	test_code("(first \"abc\")", node_str("a"));

	test_code("(number \"-1\")", node_num(-1));
	test_code("(set \'a 1) (+ a 1)", node_num(2));
	test_code("z", node_nil());
	test_code("(needs-closing-paren", node_nil());

	test_code("(- (* 2 2 2 2 2 2 2 2 2 2) 1022)", node_num(2));

	test_code("(> pi 3)", node_true());
	test_code("(= pi 3)", node_nil());
	test_code("(< e 3)", node_true());
	test_code("(= e 3)", node_nil());

	test_code("(> (time) 0)", node_true());

	l=node_empty_list();
	l=node_append(l, node_nil());
	l=node_append(l, node_true());
	test_code("\'(nil true)", l);

	test_code("(set \'a \'(1 2 3)) (first a)", node_num(1));

	l=node_empty_list();
	l=node_append(l, node_num(2));
	l=node_append(l, node_num(3));
	test_code("(set \'a \'(1 2 3)) (rest a)", l);
	test_code("(set \'a \'(1 2 3)) (last a)", node_num(3));
	test_code("(list? a)", node_true());
	test_code("(atom? a)", node_nil());

	test_code("(set \'3 2)", node_nil());
	test_code("(* 3 3)", node_num(9));

	test_code("(index \"robert\" \"bert\")", node_num(2));
	test_code("(index \'(1 2 3) 1)", node_num(0));
	test_code("(index \'(1 2 3) 4)", node_num(-1));
	test_code("(index \"a b c\" \"\")", node_num(-1));
	test_code("(in? \'(1 2 3) 1)", node_true());
	test_code("(in? \'(1 2 3) 4)", node_nil());
	test_code("(at \'(1 2 3) 0)", node_num(1));
	test_code("(at \'(1 2 3) 1)", node_num(2));
	test_code("(at \'(1 2 3) 2)", node_num(3));
	test_code("(at \'(1 2 3) 3)", node_nil());
	test_code("(count \'(1 2 3) 1)", node_num(1));
	test_code("(count \'(1 1 1) 1)", node_num(3));
	test_code("(count \'(1 2 3) 4)", node_num(0));

	test_code("(count \'() 1)", node_num(0));

	l=node_empty_list();
	l=node_append(l, node_str("a"));
	l=node_append(l, node_str("b"));
	l=node_append(l, node_str("c"));
	test_code("(split \"a b c\" \" \")", l);

	l=node_empty_list();
	l=node_append(l, node_str("a b c"));
	test_code("(split \"a b c\" \"!\")", l);
	test_code("(split \"a b c\" \"\")", l);
	test_code("(join \'(\"a\" \"b\" \"c\") \" \")", node_str("a b c"));
	test_code("(join \'(\"a\" \"b\" \"c\") \", \")", node_str("a, b, c"));
	test_code("(join \'(\"a\" \"b\" \"c\") \"\")", node_str("abc"));
	test_code("(join \'(\"a\") \" \")", node_str("a"));
	test_code("(join \'() \" \")", node_str(""));

	node *k=node_empty_list();
	k=node_append(k, node_str("a"));
	k=node_append(k, node_str("b"));
	k=node_append(k, node_str("c"));

	node *v=node_empty_list();
	v=node_append(v, node_num(1));
	v=node_append(v, node_num(2));
	v=node_append(v, node_num(3));

	node *h=node_hash(k, v);
	test_code("(set \'a (hash \'(\"a\" \"b\" \"c\") \'(1 2 3)))", h);

	test_code("(hash? a)", node_true());
	test_code("(hash? \'(1 2))", node_nil());

	test_code("(hash-keys a)", k);
	test_code("(hash-values a)", v);

	test_code("(set \'a (hash \'() \'())) (hash-set a \"guy\" \"robert\") (hash-get a \"guy\")", node_str("robert"));

	test_code("(hash? (env))", node_true());

	test_code("(env \"COOLNAME\" \"Bobby\")", node_str("Bobby"));
	test_code("(env \"COOLNAME\")", node_str("Bobby"));
	test_code("(env \"COOLNAME\" \"red=blue\")", node_str("red=blue"));
	test_code("(env \"COOLNAME\")", node_str("red=blue"));
	test_code("(hash? (env))", node_true());

	test_code("(if (< 1 2) 1 2)", node_num(1));
	test_code("(if (< 1 2) 1)", node_num(1));
	test_code("(if (> 1 2) 1 2)", node_num(2));
	test_code("(if (> 1 2) 1)", node_nil());

	test_code("(unless (< 1 2) 1 2)", node_num(2));
	test_code("(unless (< 1 2) 1)", node_nil());
	test_code("(unless (> 1 2) 1 2)", node_num(1));
	test_code("(unless (> 1 2) 1)", node_num(1));

	test_code("(set \'i 0)", node_num(0));
	test_code("(while (< i 5) (set \'i (+ i 1)))", node_num(5));

	test_code("(for (j 0 5) j)", node_num(4));

	test_code("(block (+ 2 2) (- 4 1) (* 1 2) (/ 5 5))", node_num(1));
	test_code("(let (\'y 3) (+ y 2))", node_num(5));
	test_code("y", node_nil());
	test_code("(set \'f (lambda (x) (+ x 1))) (f 4)", node_num(5));

	node *vars=node_empty_list();
	vars=node_append(vars, node_id("x"));
	vars=node_append(vars, node_id("y"));

	node *exps=node_empty_list();
	exps=node_append(exps, node_id("*"));
	exps=node_append(exps, node_id("x"));
	exps=node_append(exps, node_id("y"));

	node *temp=node_empty_list();
	temp=node_append(temp, exps);

	node *lambda=node_lambda(vars, temp);
	test_code("(lambda (x y) (* x y))", lambda);
	test_code("((lambda (x y) (* x y)) 2 3)", node_num(6));
	test_code("(set \'multiply (lambda (x y) (* x y))) (multiply 5)", node_nil());
	test_code("(atom? multiply)", node_true());

	test_code("(set \'a (lambda () 5)) (a)", node_num(5));
	test_code("(set \'a (lambda (x y) (+ x y) (- x y) (* x y) (/ x y))) (a 1 2)", node_num(0.5));

	vars=node_empty_list();
	vars=node_append(vars, node_id("x"));

	exps=node_empty_list();
	exps=node_append(exps, node_id("*"));
	exps=node_append(exps, node_id("x"));
	exps=node_append(exps, node_id("x"));

	temp=node_empty_list();
	temp=node_append(temp, exps);

	node *def=node_lambda(vars, temp);

	test_code("(def (square x) (* x x))", def);
	test_code("(square)", node_nil());
	test_code("(square 4)", node_num(16));

	test_code("(set \'plus +)", node_builtin("+", node_add));
	test_code("(set \'+ -)", node_builtin("-", node_sub));
	test_code("(+ 1 1)", node_num(0));
	test_code("(set '+ plus)", node_builtin("+", node_add));
	test_code("(+ 1 1)", node_num(2));

	if (FAILURES<1) printf("ALL %d TESTS PASS\n", SUCCESSES);
	else printf("%d TESTS FAIL\n", FAILURES);
}