加载函数

我们想构建一个函数,当传入文件名称时加载并对文件中表达式求值。为了实现这个函数,我们需要用到语法解析器,因为我们需要其来读取文件内容、解析表达式并求值。加载函数将依赖于名为Lispympc_parser*

因此,就像函数一样,我们需要前向声明解析器指针,并将其放置于文件的顶端。

  1. mpc_parser_t* Number;
  2. mpc_parser_t* Symbol;
  3. mpc_parser_t* String;
  4. mpc_parser_t* Comment;
  5. mpc_parser_t* Sexpr;
  6. mpc_parser_t* Qexpr;
  7. mpc_parser_t* Expr;
  8. mpc_parser_t* Lispy;

我们的load函数就像任何其他内置函数一样。其首先需要检查输入参数是否为单个字符串。然后我们调用mpc_parse_contents函数通过语法解析器读入文件的内容。就像mpc_parse一样,它将文件内容解析为一些其中包含抽象语法树或错误的mpc_result对象。

与命令提示符略有不同,在成功解析文件时,我们不应将其视为一个表达式。在输入文件时,我们让用户列出多个表达式并对所有表达式单独求值。为了实现这个需求,我们需要遍历文件内容中的每个表达式并逐个进行求值。如果出现任何错误,我们应该打印错误信息并继续。

若解析出错,我们将提取错误信息并返回一个error型lval。若解析正确,则此内置函数的返回值为一个空表达式。完整代码如下。

  1. lval* builtin_load(lenv* e, lval* a) {
  2. LASSERT_NUM("load", a, 1);
  3. LASSERT_TYPE("load", a, 0, LVAL_STR);
  4. /* Parse File given by string name */
  5. mpc_result_t r;
  6. if (mpc_parse_contents(a->cell[0]->str, Lispy, &r)) {
  7. /* Read contents */
  8. lval* expr = lval_read(r.output);
  9. mpc_ast_delete(r.output);
  10. /* Evaluate each Expression */
  11. while (expr->count) {
  12. lval* x = lval_eval(e, lval_pop(expr, 0));
  13. /* If Evaluation leads to error print it */
  14. if (x->type == LVAL_ERR) { lval_println(x); }
  15. lval_del(x);
  16. }
  17. /* Delete expressions and arguments */
  18. lval_del(expr);
  19. lval_del(a);
  20. /* Return empty list */
  21. return lval_sexpr();
  22. } else {
  23. /* Get Parse Error as String */
  24. char* err_msg = mpc_err_string(r.error);
  25. mpc_err_delete(r.error);
  26. /* Create new error message using it */
  27. lval* err = lval_err("Could not load Library %s", err_msg);
  28. free(err_msg);
  29. lval_del(a);
  30. /* Cleanup and return error */
  31. return err;
  32. }
  33. }