【发布时间】:2021-08-23 12:13:58
【问题描述】:
我试图弄清楚如何自己解析 C 中的 S 表达式,以便为我自己的基本 Lisp 存储数据和代码(作为学习练习编写,而不是用于生产)。
在解释我的代码和我的推理之前,我应该解释一下,我对 S 表达式的了解只有 Wikipedia 关于它的文章的介绍部分,以及偶尔看一眼 Common Lisp 代码,所以我的结构和变量的命名可能有点偏。
我的实现语言是 C,在定义任何函数之前,我创建了以下结构:
typedef enum {
string,
letter,
integer,
} atom_type;
typedef struct {
void* blob;
atom_type type;
} atom;
typedef struct expr {
atom* current;
struct expr* next;
} expr;
每个原子都存储在一个结构atom 中,其中包含一个枚举实例(?我不确定这方面的正确术语)和一个指向要存储的数据的空指针。每个 S 表达式“节点”由一个指向原子的指针和一个指向下一个 S 表达式节点的指针组成。
我编写了一个基本函数,它接受一个字符串并将其解析为一个原子,如下所示:
atom* parse_term(char* str) {
size_t len = strlen(str);
atom* current = malloc(sizeof(atom));
if(str[0] == '\'') {
current->blob = (char*) &str[1];
current->type = letter;
} else if(str[0] == '\"') {
char temp[256];
int pos = 1;
while(str[pos] != '\"') {
temp[pos] = str[pos];
pos++;
}
current->blob = malloc(256 * sizeof(char));
current->blob = (char*) &temp;
current->type = string;
} else if(isdigit(str[0])){
char temp[256];
int pos = 0;
while(str[pos] != ' ') {
temp[pos] = str[pos];
pos++;
}
int tmp = atoi(temp);
current->blob = (int*) &tmp;
current->type = integer;
}
return current;
}
函数似乎工作正常;至少,当我打印出数据类型时,它会正确显示。但除此之外,我无法弄清楚如何打印出实际的“blob”:我尝试使用 %p 格式化代码以及 switch 语句:
void print_atom(atom* current) {
switch(current->type) {
case string:
printf("atom%s\ttype:%d", current->blob, current->type);
case letter:
printf("atom%c\ttype:%d", current->blob, current->type);
case integer:
printf("atom%c\ttype:%d", current->blob, current->type);
}
}
但这不起作用。在字符串的情况下,它返回乱码文本,而在其他情况下,它只是不打印原子信息应该在的任何内容。
我想这是我在结构中使用 void* 的产物;我该如何补救?我认为我确实正确地转换了(尽管我很可能是错的,请告诉我),我能想到的唯一其他选择是在“原子”结构中为每种支持的数据类型存储一个硬编码变量,但这似乎是浪费资源。
【问题讨论】:
-
基本的 Lisp 数据类型是 cons 单元格,用于表示列表。它包含两个对称字段:car 和 cdr,它们都指向 S 表达式。 Cons 单元用于表示列表,但在最基本的情况下,它们只是点对。 S-expression 可以是 cons 单元格、符号、字符串、数字等。
-
current->blob = malloc(256 * sizeof(char)); current->blob = (char*) &temp;= 即时内存泄漏,最终在temp之后出现 UB 不再可行。事实上,所有将地址存储到自动本地变量(temp、tmp),然后在对象生命周期之外使用这些地址的所有代码行都是未定义行为的秘诀。 -
查看buildyourownlisp.com了解更多详情/提示
-
BuildYourOwnLisp 是我最初对 Lisps 感兴趣的原因——我故意选择不遵循该教程,因为它依赖于解析器库,而解析器是我期待编写的部分。