读取字符串

现在我们需要添加对字符串解析的支持。像往常一样,Lispy需要添加名称为string的新语法规则到Lispy语法分析器中。

我们将要使用的字符串表示规则与C相同。这意味着字符串实质上是两个引号"之间的一系列转义字符或普通字符组成。我们将下式指定为Lispy语法中字符串正则表达式。

  1. string : /\"(\\\\.|[^\"])*\"/ ;

这个正则表达式看起来很复杂,但在某些方面更有助于理解。这个式子是这样理解的。该字符串是从"字符开始,后面跟着零个或多个跟着任意字符.的反斜杠\\,或者非"的任意字符[^\\"]。最后,以"收尾。

我们也需要在lval_read函数中添加一个case来处理字符串读取。

  1. if (strstr(t->tag, "string")) { return lval_read_str(t); }

因为字符串是以转义形式输入的,所以我们需要创建lval_read_str函数来解决这个问题。 这个功能有点棘手,因为它必须解决以下问题。 首先,它必须剥离字符串两侧"字符。然后必须对转义字符串进行解码,将一系列转义字符(如\n)转换成实际编码字符。最后必须创建一个新的lval 并清理函数中使用过的内存。

  1. lval* lval_read_str(mpc_ast_t* t) {
  2. /* Cut off the final quote character */
  3. t->contents[strlen(t->contents)-1] = '\0';
  4. /* Copy the string missing out the first quote character */
  5. char* unescaped = malloc(strlen(t->contents+1)+1);
  6. strcpy(unescaped, t->contents+1);
  7. /* Pass through the unescape function */
  8. unescaped = mpcf_unescape(unescaped);
  9. /* Construct a new lval using the string */
  10. lval* str = lval_str(unescaped);
  11. /* Free the string and return */
  12. free(unescaped);
  13. return str;
  14. }

如果这一切都没有问题,我们应该能够在REPL中使用字符串。 接下来我们在函数中使用一下字符串。

  1. lispy> "hello"
  2. "hello"
  3. lispy> "hello\n"
  4. "hello\n"
  5. lispy> "hello\""
  6. "hello\""
  7. lispy> head {"hello" "world"}
  8. {"hello"}
  9. lispy> eval (head {"hello" "world"})
  10. "hello"
  11. lispy>