~brenns10/funlisp

adf1bdffb90c65f4052b2a3ac0a84ffa41b63b7f — Stephen Brennan 1 year, 10 months ago 242b21a
Implement the let builtin
2 files changed, 84 insertions(+), 0 deletions(-)

A scripts/tests/let.lisp
M src/builtins.c
A scripts/tests/let.lisp => scripts/tests/let.lisp +40 -0
@@ 0,0 1,40 @@
; test that we can bind values
(let ((x 5)) (assert (equal? x 5)))

; test that we can bind many values
(let ((x 5) (y 5)) (assert (equal? 10 (+ x y))))

; test that we can execute multiple lines of code in let
(let ((x 5) (y 5))
  (assert (equal? 5 x))
  (assert (equal? 5 y)))

; test that we can refer to prior bindings
(let ((x 1)
      (y (+ 1 x)))
  (assert (equal? y 2)))

; test that we can even refer to future bindings later on :)
(let ((return2 (lambda () (+ 1 (return1))))
      (return1 (lambda () 1)))
  (assert (equal? (return2) 2))
  (assert (equal? (return1) 1)))

; it should even work fine without any bindings...
(assert (equal? (let () 5) 5))

; errors
(assert-error 'LE_2FEW
  (let))
(assert-error 'LE_2FEW
  (let ()))
(assert-error 'LE_TYPE
  (let (not-a-binding-list-haha) 5))
(assert-error 'LE_TYPE
  (let ((1 'that-is-not-a-symbol??)) 5))
(assert-error 'LE_2FEW
  (let ((i-am-too-few-to-be-a-binding)) 5))
(assert-error 'LE_2MANY
  (let ((name 'which-value 'should-i-choose?)) 5))

; OUTPUT(0)

M src/builtins.c => src/builtins.c +44 -0
@@ 698,6 698,49 @@ static lisp_value *lisp_builtin_list(
	return (lisp_value*)arglist;
}

static lisp_value *lisp_builtin_let(
		lisp_runtime *rt, lisp_scope *scope, lisp_list *arglist, void *user)
{
	/*
	 * args are NOT evaluated.
	 * (let ((symbol binding)
	 *       (symbol binding) ...)
	 *   expression ...)
	 *
	 * This would be roughly equivalent to letrec* from, say, racket.
	 */
	lisp_list *binding_list, *expressions;
	lisp_list *it;
	lisp_symbol *sym;
	lisp_value *binding;
	lisp_scope *new_scope;

	(void) user;

	if (!lisp_get_args(rt, arglist, "lR", &binding_list, &expressions))
		return NULL;

	/*
	 * It feels dirty to just create a scope and never clean it up, but rest
	 * assured it will be garbage collected.
	 */
	new_scope = lisp_new_empty_scope(rt);
	new_scope->up = scope;

	it = binding_list;
	lisp_for_each(it) {
		if (!lisp_is(it->left, type_list))
			return lisp_error(rt, LE_TYPE, "expected binding list");
		if (!lisp_get_args(rt, (lisp_list*)it->left, "s*", &sym, &binding))
			return NULL;
		binding = lisp_eval(rt, new_scope, binding);
		if (!binding)
			return NULL;
		lisp_scope_bind(new_scope, sym, binding);
	}

	return lisp_progn(rt, new_scope, expressions);
}

void lisp_scope_populate_builtins(lisp_runtime *rt, lisp_scope *scope)
{


@@ 735,4 778,5 @@ void lisp_scope_populate_builtins(lisp_runtime *rt, lisp_scope *scope)
	lisp_scope_add_builtin(rt, scope, "assert-error", lisp_builtin_assert_error, NULL, 0);
	lisp_scope_add_builtin(rt, scope, "cond", lisp_builtin_cond, NULL, 0);
	lisp_scope_add_builtin(rt, scope, "list", lisp_builtin_list, NULL, 1);
	lisp_scope_add_builtin(rt, scope, "let", lisp_builtin_let, NULL, 0);
}