Builtins

Now that our evaluation relies on the new function type we need to make sure we can register all of our builtin functions with the environment before we start the interactive prompt. At the moment our builtin functions are not the correct type. We need to change their type signature such that they take in some environment, and where appropriate change them to pass this environment into other calls that require it. I won’t post the code for this, so go ahead and change the type signatures of the buildin functions to take an lenv* as their first argument now. If you are confused you can look at the sample code for this chapter.

As an example we can make use of our builtin_op function to define separate builtins for each of the maths functions our language supports.

  1. lval* builtin_add(lenv* e, lval* a) {
  2. return builtin_op(e, a, "+");
  3. }
  4. lval* builtin_sub(lenv* e, lval* a) {
  5. return builtin_op(e, a, "-");
  6. }
  7. lval* builtin_mul(lenv* e, lval* a) {
  8. return builtin_op(e, a, "*");
  9. }
  10. lval* builtin_div(lenv* e, lval* a) {
  11. return builtin_op(e, a, "/");
  12. }

Once we’ve changed the builtins to the correct type we can create a function that registers all of our builtins into an environment.

For each builtin we want to create a function lval and symbol lval with the given name. We then register these with the environment using lenv_put. The environment always takes or returns copies of a values, so we need to remember to delete these two lval after registration as we won’t need them any more.

If we split this task into two functions we can neatly register all of our builtins with some environment.

  1. void lenv_add_builtin(lenv* e, char* name, lbuiltin func) {
  2. lval* k = lval_sym(name);
  3. lval* v = lval_fun(func);
  4. lenv_put(e, k, v);
  5. lval_del(k); lval_del(v);
  6. }
  7. void lenv_add_builtins(lenv* e) {
  8. /* List Functions */
  9. lenv_add_builtin(e, "list", builtin_list);
  10. lenv_add_builtin(e, "head", builtin_head);
  11. lenv_add_builtin(e, "tail", builtin_tail);
  12. lenv_add_builtin(e, "eval", builtin_eval);
  13. lenv_add_builtin(e, "join", builtin_join);
  14. /* Mathematical Functions */
  15. lenv_add_builtin(e, "+", builtin_add);
  16. lenv_add_builtin(e, "-", builtin_sub);
  17. lenv_add_builtin(e, "*", builtin_mul);
  18. lenv_add_builtin(e, "/", builtin_div);
  19. }

The final step is to call this function before we create the interactive prompt. We also need to remember to delete the environment once we are finished.

  1. lenv* e = lenv_new();
  2. lenv_add_builtins(e);
  3. while (1) {
  4. char* input = readline("lispy> ");
  5. add_history(input);
  6. mpc_result_t r;
  7. if (mpc_parse("<stdin>", input, Lispy, &r)) {
  8. lval* x = lval_eval(e, lval_read(r.output));
  9. lval_println(x);
  10. lval_del(x);
  11. mpc_ast_delete(r.output);
  12. } else {
  13. mpc_err_print(r.error);
  14. mpc_err_delete(r.error);
  15. }
  16. free(input);
  17. }
  18. lenv_del(e);

If everything is working correctly we should have a play around in the prompt and verify that functions are actually a new type of value now, not symbols.

  1. lispy> +
  2. <function>
  3. lispy> eval (head {5 10 11 15})
  4. 5
  5. lispy> eval (head {+ - + - * /})
  6. <function>
  7. lispy> (eval (head {+ - + - * /})) 10 20
  8. 30
  9. lispy> hello
  10. Error: unbound symbol!
  11. lispy>