ESC
输入关键词搜索文章
目录

字符串

Build Your Own Lisp · 第 14 章
为 Lisp 添加字符串支持
Chapter 14 · 字符串
字符串操作

章节信息

原书章节:第 14 章 Strings

中文翻译KSCO (GitHub)

原书地址buildyourownlisp.com

字符串类型

到目前为止,我们的 Lisp 已经有了数字、符号、函数和列表。现在是时候添加字符串支持了。字符串是一系列字符的组合,用双引号包围。

我们需要添加一个新的 lval 类型来表示字符串:

enum { LVAL_ERR, LVAL_NUM,   LVAL_SYM,
       LVAL_FUN, LVAL_STR,   LVAL_SEXPR,
       LVAL_QEXPR };

字符串的构造函数和析构函数如下:

lval* lval_str(char* s) {
  lval* v = malloc(sizeof(lval));
  v->type = LVAL_STR;
  v->str = malloc(strlen(s) + 1);
  strcpy(v->str, s);
  return v;
}

在打印字符串时,我们需要用双引号包围:

case LVAL_STR:   printf("\"%s\"", v->str); break;
字符串操作

我们将实现以下字符串操作:

字符串内置函数

  • str:将值转换为字符串
  • join:连接多个字符串
  • len:获取字符串长度
  • head:获取字符串第一个字符
  • tail:获取字符串除第一个字符外的部分
  • init:获取字符串除最后一个字符外的部分
  • last:获取字符串最后一个字符
  • rest:获取字符串除第一个字符外的部分

实现字符串长度:

lval* builtin_len(lenv* e, lval* a) {
  LASSERT_NUM("len", a, 1);

  lval* x;
  if (a->cell[0]->type == LVAL_STR) {
    x = lval_num(strlen(a->cell[0]->str));
  } else if (a->cell[0]->type == LVAL_QEXPR) {
    x = lval_num(a->cell[0]->count);
  } else {
    lval_del(a);
    return lval_err("Function 'len' passed incorrect type!");
  }

  lval_del(a);
  return x;
}

实现字符串连接:

lval* builtin_join(lenv* e, lval* a) {

  for (int i = 0; i < a->count; i++) {
    LASSERT(a, a->cell[i]->type == LVAL_STR,
      "Function 'join' passed incorrect type.");
  }

  char* str = calloc(1, sizeof(char));
  str[0] = '';

  for (int i = 0; i < a->count; i++) {
    char* old = str;
    int len = strlen(old) + strlen(a->cell[i]->str) + 1;
    str = malloc(len);
    strcpy(str, old);
    strcat(str, a->cell[i]->str);
    free(old);
  }

  lval_del(a);
  return lval_str(str);
}
字符串与符号

字符串和符号看起来很相似,但它们有不同的用途:

字符串 vs 符号

  • 符号:代表一个名字,用于查找变量
  • 字符串:代表一系列字符,用于文本处理

符号 hello 会被求值为变量 hello 的值,而字符串 "hello" 就是字符串本身。

在解析字符串时,我们需要处理转义字符,如 \n(换行)、\t(制表符)等。

复习速查

  • 字符串:用双引号包围的字符序列
  • str:将值转换为字符串
  • join:连接多个字符串
  • len:获取字符串长度
  • head/tail:获取字符串的首字符/剩余部分
  • 转义字符\n\t\\"