字符串
Build Your Own Lisp · 第 14 章
为 Lisp 添加字符串支持
字符串类型
到目前为止,我们的 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、\\"等