目录
前言
通过开发一门类 Lisp 的编程语言来理解编程语言的设计思想,本实践来自著名的《Build Your Own Lisp》。
- 代码实现:https://github.com/JmilkFan/Lispy
前文列表
《用 C 语言开发一门编程语言 — 交互式解析器l》
《用 C 语言开发一门编程语言 — 语法解析器运行原理》
《用 C 语言开发一门编程语言 — 波兰表达式解析器》
《用 C 语言开发一门编程语言 — 表达式存储器》
符号表达式
符号表达式(Symbolic Expression,S-Expression)是一种通过 Symbol(符号)来表示数学表达式的方法,以灵活性著称,常用于实现人工智能领域的代数运算系统。例如:Python SymPy 库等。我们接下来将会使用 S-Expression 来支撑 Lispy 的函数式编程范式。
值得注意的是,区别于波兰表达式(算术表达式)会对 Number(操作数)直接进行处理,S-Expression 并不用于 Numbers 的存储和运算,而是更专注于 Symbols 的存储。例如:算术表达式关注 “3 + 4” 的值是 7,而 S-Expression 关注的是 “x + y” 这一条代数式的存储,而不是计算它们的值。
基于这样的特性,S-Expression 可以用于表示任何一组数学公式,而不仅仅是特定的数值。
S-Expression 的语法规则定义
S-Expression 的语法规则非常简单:
- 使用小括号包括一条 S-Expression。
- 一条 S-Expression 由若干个 Symbols 或 Other S-Expression 组成。
所以 S-Expression 和波兰表达式一样,本质是一种适用于计算机迭代处理的树结构。
符号表达式解析器
实现 S-Expression 解析器,需要对 “表达式存储“ 和 “运算求值“ 这 2 个核心部分进行解构,从原来简单的 “输入 => 运算 => 输出” 流程,拆分为:
- 先存储:读取并存储输入。
- 再求值:对输入进行求值。
所以,S-Expression 解析器的实现分为 3 大部分:
- 首先,Lispy 读取到了用户的输入后,S-Expression 语法解析器首先会将输入的字符序列解析为 S-Expression AST(抽象语法树);
- 然后,Lispy 需要递归 AST 的每个节点,根据节点的 tag 和 contents 成员,将相应的数据储存到 Lispy Values(数值存储器);
- 最后,Lispy 才可能通过对 Lispy Values 进行遍历求值而来得到最终的运行结果。
1、读取并存储输入
S-Expression 语法解析实现
我们将原有的波兰表达式解析器改造为 S-Expression 解析器。
为了方便后续的演进,我们还把 Operator(操作符)规则重定义为了 Symbol(符号)规则,使其可以表示更多的含义,例如:关键字、变量、操作符、函数等。
mpc_parser_t *Number = mpc_new("number");
mpc_parser_t *Symbol = mpc_new("symbol");
mpc_parser_t *Sexpr = mpc_new("sexpr");
mpc_parser_t *Expr = mpc_new("expr");
mpc_parser_t *Lispy = mpc_new("lispy");
mpca_lang(MPCA_LANG_DEFAULT,
" \
number : /-?[0-9]+/ ; \
symbol : '+' | '-' | '*' | '/' ; \
sexpr : '(' <expr>* ')' ; \
expr : <number> | <symbol> | <sexpr> ; \
lispy : /^/ <expr>* /$/ ; \
",
Number, Symbol, Sexpr, Expr, Lispy);
mpc_cleanup(5, Number, Symbol, Sexpr, Expr, Lispy);
- Symbol(符号):定义为一种特殊的数值类型,可以用来表示 Key Word(关键字)、Variable(变量)、Operator(操作符)、Function(函数)等元素。
- Expression(表达式):是由 Symbol 和 Other Expressions 组成的一个列表,使用 ‘()’ 括号包围。
S-Expression 存储器实现
存储器结构定义
S-Expression 由 2 个核心部分组成:
所以,首先添加 2 个新的 Value Types,分别表示 Symbols 和 S-Expression。
enum { LVAL_ERR, LVAL_NUM, LVAL_SYM, LVAL_SEXPR };
- 1
- LVAL_SYM:表示输入的符号。
- LVAL_SEXPR:表示 S-Expression。
然后,正如上文所说,S-Expression 本质是一颗递归遍历的树,我们将 lval(Lispy Values)结构体改造为树形数据结构,使其 “可变长、且可递归“。
typedef struct lval {
int type;
long num;
char *err; // Error and Symbol types have some string data
char *sym;
int count; // Count and Pointer to a list of "lval*"
struct lval **cell;
} lval;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
-
改造 err 成员,为 String 类型,可直接存储具体的 Error Message,而不只是一个 Error code。
-
添加 sym 成员,为 String 类型,存储 Symbols。
-
添加 count 成员,为 int 类型,存储递归嵌套的 Expression 个数。
-
添加 cell 成员,为指针数据类型,可变长,指向下一个 Expression,可递归。
最终实现了一个具有父子节点结构的树状数据结构。
存储器构造函数实现
为了避免 C 语言值传递的性能损耗,我们将构造函数的返回值都定义为指针类型。所以,我们改造以往的 lval 结构体类型变量的构造函数,让其返回 lval 结构体类型指针,而不再是整个变量。
/* Construct a pointer to a new Number lval */
lval* lval_num(long x) {
lval* v = malloc(sizeof(lval));
v->type = LVAL_NUM;
v->num = x;
return v;
}
/* Construct a pointer to a new Error lval */
lval* lval_err(char* m) {
lval* v = malloc(sizeof(lval));
v->type = LVAL_ERR;
v->err = malloc(strlen(m) + 1);
strcpy(v->err, m);
return v;
}
/* Construct a pointer to a new Symbol lval */
lval* lval_sym(char* s) {
lval* v = malloc(sizeof(lval));
v->type = LVAL_SYM;
v->sym = malloc(strlen(s) + 1);
strcpy(v->sym, s);
return v;
}
/* A pointer to a new empty Sexpr lval */
lval* lval_sexpr(void) {
lval* v = malloc(sizeof(lval));
v->type = LVAL_SEXPR;
v->count = 0;
v->cell = NULL;
return v;
}
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
注意,在 C 语言中,String 的本质是一个以 “\0"(null)空字符做为终止标记的字符数组,字符串的最后一个字符必须是 \0
。而 strlen 函数返回的是字符串的实际长度,并不包含 “\0",所以为了保证有足够的空间存储所有字符,我们需要在额外 +1。
存储器析构函数实现
既然我们在构造函数中大量使用了 malloc 内存分配,那么在析构函数中就要完成内存空间的回收。
当某个 lval 变量完成使命之后,我们不仅需要删除它本身所指向的堆内存,还要轮询着依次删除它的指针成员所指向的堆内存。这样就不会导致内存的泄露了。
void lval_del(lval* v) {
switch (v->type) {
/* Do nothing special for number type */
case LVAL_NUM: break;
/* For Err or Sym free the string data */
case LVAL_ERR: free(v->err); break;
case LVAL_SYM: free(v->sym); break;
/* If Sexpr then delete all elements inside */
case LVAL_SEXPR:
for (int i = 0; i < v->count; i++) {
lval_del(v->cell[i]);
}
/* Also free the memory allocated to contain the pointers */
free(v->cell);
break;
}
/* Free the memory allocated for the "lval" struct itself */
free(v);
}
- 18
- 19
- 20
- 21
- 22
- 23
值得注意的是,释放内存的时候要讲究顺序,先释放干净结构体变量指针成员指向的堆内存,最后再释放结构体变量自身指向的堆内存,否则同样会出现诸如 double free or corruption 此类的内存崩溃。
读取并存储 S-Expression
定义好表达式存储器之后,我们就可以将经过语法解析器处理的数据储存到存储器中了。
结合以往解析波兰表达式抽象语法树的经验,我们对 S-Expression AST 进行描述:
- 如果给定节点的被标记为 number 或 symbol,则我们可以调用对应的构造函数直接返回一个
lval *
。 - 如果给定的节点被标记为 root 或 sexpr,则我们应该构造一个空的 S-Expression 类型的
lval *
,并逐一将它的子节点加入。
lval* lval_read_num(mpc_ast_t* t) {
errno = 0;
long x = strtol(t->contents, NULL, 10);
return errno != ERANGE ?
lval_num(x) : lval_err("invalid number");
}
lval* lval_read(mpc_ast_t* t) {
/* If Symbol or Number return conversion to that type */
if (strstr(t->tag, "number")) { return lval_read_num(t); }
if (strstr(t->tag, "symbol")) { return lval_sym(t->contents); }
/* If root (>) or sexpr then create empty list */
lval* x = NULL;
if (strcmp(t->tag, ">") == 0) { x = lval_sexpr(); }
if (strstr(t->tag, "sexpr")) { x = lval_sexpr(); }
/* Fill this list with any valid expression contained within */
for (int i = 0; i < t->children_num; i++) {
if (strcmp(t->children[i]->contents, "(") == 0) { continue; }
if (strcmp(t->children[i]->contents, ")") == 0) { continue; }
if (strcmp(t->children[i]->tag, "regex") == 0) { continue; }
x = lval_add(x, lval_read(t->children[i]));
}
return x;
}
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
为了更加方便的向一个 S-Expression 中添加元素,我们实现一个 lval_add 函数,这个函数做了两件事情:
- 将 S-Expression 子表达式的数量加 1。
- 使用 realloc 函数为 cell 成员的长度重新扩大申请内存。
lval* lval_add(lval* v, lval* x) {
v->count++;
v->cell = realloc(v->cell, sizeof(lval*) * v->count);
v->cell[v->count-1] = x;
return v;
}
- 1
- 2
- 3
- 4
- 5
- 6
2、对输入进行求值
通过以上的过程,我们就完成了接受输入并存储的步骤,接下来就是进行运算求值。
S-Expression 的运算和波兰表达式的运算并无本质区别,都是对 AST 的遍历。但代码实现上肯定存在着差别:
- 首先取列表第一个元素为操作符。
- 然后遍历所有剩下的元素,将它们作为操作数。
lval* lval_eval_sexpr(lval* v);
lval* lval_pop(lval* v, int i);
lval* lval_take(lval* v, int i);
lval* builtin_op(lval* a, char* op);
lval* lval_eval(lval* v) {
/* Evaluate Sexpressions */
if (v->type == LVAL_SEXPR) { return lval_eval_sexpr(v); }
/* All other lval types remain the same */
return v;
}
lval* lval_eval_sexpr(lval* v) {
/* Evaluate Children */
for (int i = 0; i < v->count; i++) {
v->cell[i] = lval_eval(v->cell[i]);
}
/* Error Checking */
for (int i = 0; i < v->count; i++) {
/* 如果是 Error 类型,就直接将其取走。*/
if (v->cell[i]->type == LVAL_ERR) { return lval_take(v, i); }
}
if (v->count == 0) { return v; } // 没有子节点,空表达式 `{}`,原封不动的将其返回。
if (v->count == 1) { return lval_take(v, 0); } // 只有一个子节点,直接将其取走。
/**
* 后续为合法的表达式,并且有个多于一个的子节点。
* 使用 lval_pop 函数将第一个元素从表达式中分离开来。
*/
lval* f = lval_pop(v, 0);
if (f->type != LVAL_SYM) {
lval_del(f);
lval_del(v);
return lval_err("S-expression Does not start with symbol!");
}
/* 开始最终对 Number 操作数的运算处理。*/
lval* result = builtin_op(v, f->sym);
lval_del(f);
return result;
}
/**
* lval_pop 弹出函数
* 将所操作的 S-Expression 的第 i 个元素取出,然后将取出的子节点返回,并将其后面的子节点向前移动填补空缺,使得这个 S-Expression 不再包含这个子节点。
* 需要注意的是,这个函数并不会将这个 S-Expression 本身删除,它只是从中取出某个子节点,剩下的子节点依旧保持原样。这意味着这两个部分都需要在某个地方通过 lval_del 函数来删除干净。
*/
lval* lval_pop(lval* v, int i) {
/* Find the item at "i" */
lval* x = v->cell[i];
/* Shift memory after the item at "i" over the top */
memmove(&v->cell[i], &v->cell[i+1], sizeof(lval*) * (v->count-i-1));
/* Decrease the count of items in the list */
v->count--;
/* Reallocate the memory used */
v->cell = realloc(v->cell, sizeof(lval*) * v->count);
return x;
}
/**
* lval_take 去除函数
* 和 lval_pop 函数类似,不过它在取出了目标子节点之后,就将 S-Expression 本身以及剩下的子节点都删除掉。
* 它利用了 lval_pop 函数并做了一点小小的改变,却使得我们的代码可读性更高了一些。
* 所以,与 lval_pop 相反,我们只需要保证被取出的子节点最终被释放掉即可。
*/
lval* lval_take(lval* v, int i) {
lval* x = lval_pop(v, i);
lval_del(v);
return x;
}
/**
* builtin_op 求值函数
*/
lval* builtin_op(lval* a, char* op) {
/* 该函数对参数应该做更加严格的检查,对任何不是 LVAL_NUM 类型的 lval 结构体变量,都应该返回一个错误。*/
for (int i = 0; i < a->count; i++) {
if (a->cell[i]->type != LVAL_NUM) {
lval_del(a);
return lval_err("Cannot operate on non-number!");
}
}
/* 将第一个数字弹出开始计算。*/
lval* x = lval_pop(a, 0);
/* 后如果后面没有其它的子节点,并且操作符为减号时,它会对第一个数字进行取反操作。*/
if ((strcmp(op, "-") == 0) && a->count == 0) {
x->num = -x->num;
}
/* 如果还有更多的节点,它就不断地从 cell 数组中取出,遍历进行数学运算。*/
while (a->count > 0) {
lval* y = lval_pop(a, 0);
if (strcmp(op, "+") == 0) { x->num += y->num; }
if (strcmp(op, "-") == 0) { x->num -= y->num; }
if (strcmp(op, "*") == 0) { x->num *= y->num; }
if (strcmp(op, "/") == 0) {
/* 如果做除法时遇到被除数为零的情况,就将临时变量 x 和 y 以及参数列表删除,并返回一个错误。*/
if (y->num == 0) {
lval_del(x); lval_del(y);
x = lval_err("Division By Zero!"); break;
}
x->num /= y->num;
}
lval_del(y);
}
/* 如果没有错误,参数列表最终会被删除,并返回一个新的表达式。*/
lval_del(a);
return x;
}
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
3、打印运算结果
至此,Lisp 的 S-Expression 运算过程就基本结束了,最后我们还需要将运算结果打印出来。
为了打印 S-Expression,我们创建一个新函数,遍历所有的子表达式,并将它们单独打印出来,以空格隔开,正如输入的时候一样。
void lval_print(lval* v);
void lval_expr_print(lval* v, char open, char close) {
putchar(open);
for (int i = 0; i < v->count; i++) {
/* Print Value contained within */
lval_print(v->cell[i]);
/* Don't print trailing space if last element */
if (i != (v->count-1)) {
putchar(' ');
}
}
putchar(close);
}
void lval_print(lval* v) {
switch (v->type) {
case LVAL_NUM: printf("%li", v->num); break;
case LVAL_ERR: printf("Error: %s", v->err); break;
case LVAL_SYM: printf("%s", v->sym); break;
case LVAL_SEXPR: lval_expr_print(v, '(', ')'); break;
}
}
void lval_println(lval* v) { lval_print(v); putchar('\n'); }
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
最后,次更新一下 main 函数,在其打印表达式之前,先将输入经由求值函数处理即可:
lval* x = lval_eval(lval_read(r.output));
lval_println(x);
lval_del(x);
- 1
- 2
- 3
源代码
#include <stdio.h>
#include <stdlib.h>
#include "mpc.h"
#ifdef _WIN32
#include <string.h>
static char buffer[2048];
char *readline(char *prompt) {
fputs(prompt, stdout);
fgets(buffer, 2048, stdin);
char *cpy = malloc(strlen(buffer) + 1);
strcpy(cpy, buffer);
cpy[strlen(cpy) - 1] = '\0';
return cpy;
}
void add_history(char *unused) {}
#else
#ifdef __linux__
#include <readline/readline.h>
#include <readline/history.h>
#endif
#ifdef __MACH__
#include <readline/readline.h>
#endif
#endif
/* Create Enumeration of Possible lval Types */
enum {
LVAL_NUM,
LVAL_ERR,
LVAL_SYM,
LVAL_SEXPR
};
/* Declare New lval Struct */
typedef struct lval {
int type;
long num;
/* Count and Pointer to a list of "lval*" */
struct lval** cell;
int count;
/* Error and Symbol types have some string data */
char *err;
char *sym;
} lval;
/* Construct a pointer to a new Number lval */
lval *lval_num(long x) {
lval *v = malloc(sizeof(lval));
v->type = LVAL_NUM;
v->num = x;
return v;
}
/* Construct a pointer to a new Error lval */
lval *lval_err(char *msg) {
lval *v = malloc(sizeof(lval));
v->type = LVAL_ERR;
v->err = malloc(strlen(msg) + 1);
strcpy(v->err, msg);
return v;
}
/* Construct a pointer to a new Symbol lval */
lval *lval_sym(char *sym) {
lval *v = malloc(sizeof(lval));
v->type = LVAL_SYM;
v->sym = malloc(strlen(sym) + 1);
strcpy(v->sym, sym);
return v;
}
/* A pointer to a new empty Sexpr lval */
lval *lval_sexpr(void) {
lval *v = malloc(sizeof(lval));
v->type = LVAL_SEXPR;
v->count = 0;
v->cell = NULL;
return v;
}
void lval_del(lval *v) {
switch (v->type) {
/* Do nothing special for number type */
case LVAL_NUM:
break;
/* For Err or Sym free the string data */
case LVAL_ERR:
free(v->err);
break;
case LVAL_SYM:
free(v->sym);
break;
/* If Sexpr then delete all elements inside */
case LVAL_SEXPR:
for (int i = 0; i < v->count; i++) {
lval_del(v->cell[i]);
}
/* Also free the memory allocated to contain the pointers */
free(v->cell);
break;
}
/* Free the memory allocated for the "lval" struct itself */
free(v);
}
lval *lval_add(lval *v, lval *x) {
v->count++;
v->cell = realloc(v->cell, sizeof(lval*) * v->count);
v->cell[v->count-1] = x;
return v;
}
lval *lval_read_num(mpc_ast_t *t) {
errno = 0;
long x = strtol(t->contents, NULL, 10);
return errno != ERANGE
? lval_num(x)
: lval_err("invalid number");
}
lval *lval_read(mpc_ast_t *t) {
/* If Symbol or Number return conversion to that type */
if (strstr(t->tag, "number")) {
return lval_read_num(t);
}
if (strstr(t->tag, "symbol")) {
return lval_sym(t->contents);
}
/* If root (>) or sexpr then create empty list */
lval *x = NULL;
if (strcmp(t->tag, ">") == 0) {
x = lval_sexpr();
}
if (strstr(t->tag, "sexpr")) {
x = lval_sexpr();
}
/* Fill this list with any valid expression contained within */
for (int i = 0; i < t->children_num; i++) {
if (strcmp(t->children[i]->contents, "(") == 0) { continue; }
if (strcmp(t->children[i]->contents, ")") == 0) { continue; }
if (strcmp(t->children[i]->tag, "regex") == 0) { continue; }
x = lval_add(x, lval_read(t->children[i]));
}
return x;
}
void lval_print(lval *v);
void lval_expr_print(lval *v, char open, char close) {
putchar(open);
for (int i = 0; i < v->count; i++) {
/* Print Value contained within */
lval_print(v->cell[i]);
/* Don't print trailing space if last element */
if (i != (v->count-1)) {
putchar(' ');
}
}
putchar(close);
}
/* Print an "lval*" */
void lval_print(lval *v) {
switch (v->type) {
case LVAL_NUM: printf("%li", v->num); break;
case LVAL_ERR: printf("Error: %s", v->err); break;
case LVAL_SYM: printf("%s", v->sym); break;
case LVAL_SEXPR: lval_expr_print(v, '(', ')'); break;
}
}
/* Print an "lval" followed by a newline */
void lval_println(lval *v) {
lval_print(v);
putchar('\n');
}
lval *lval_pop(lval *v, int i) {
/* Find the item at "i" */
lval *x = v->cell[i];
/* Shift memory after the item at "i" over the top */
memmove(&v->cell[i], &v->cell[i+1],
sizeof(lval*) * (v->count-i-1));
/* Decrease the count of items in the list */
v->count--;
/* Reallocate the memory used */
v->cell = realloc(v->cell, sizeof(lval*) * v->count);
return x;
}
lval *lval_take(lval *v, int i) {
lval *x = lval_pop(v, i);
lval_del(v);
return x;
}
lval *builtin_op(lval *a, char *op) {
/* Ensure all arguments are numbers */
for (int i = 0; i < a->count; i++) {
if (a->cell[i]->type != LVAL_NUM) {
lval_del(a);
return lval_err("Cannot operate on non-number!");
}
}
/* Pop the first element */
lval *x = lval_pop(a, 0);
/* If no arguments and sub then perform unary negation */
if ((strcmp(op, "-") == 0) && a->count == 0) {
x->num = -x->num;
}
/* While there are still elements remaining */
while (a->count > 0) {
/* Pop the next element */
lval *y = lval_pop(a, 0);
if (strcmp(op, "+") == 0) { x->num += y->num; }
if (strcmp(op, "-") == 0) { x->num -= y->num; }
if (strcmp(op, "*") == 0) { x->num *= y->num; }
if (strcmp(op, "/") == 0) {
if (y->num == 0) {
lval_del(x);
lval_del(y);
x = lval_err("Division By Zero!");
break;
}
x->num /= y->num;
}
lval_del(y);
}
lval_del(a);
return x;
}
lval *lval_eval(lval *v);
lval *lval_eval_sexpr(lval *v) {
/* Evaluate Children */
for (int i = 0; i < v->count; i++) {
v->cell[i] = lval_eval(v->cell[i]);
}
/* Error Checking */
for (int i = 0; i < v->count; i++) {
if (v->cell[i]->type == LVAL_ERR) {
return lval_take(v, i);
}
}
/* Empty Expression */
if (v->count == 0) { return v; }
/* Single Expression */
if (v->count == 1) { return lval_take(v, 0); }
/* Ensure First Element is Symbol */
lval *f = lval_pop(v, 0);
if (f->type != LVAL_SYM) {
lval_del(f);
lval_del(v);
return lval_err("S-expression Does not start with symbol!");
}
/* Call builtin with operator */
lval *result = builtin_op(v, f->sym);
lval_del(f);
return result;
}
lval *lval_eval(lval *v) {
/* Evaluate Sexpressions */
if (v->type == LVAL_SEXPR) {
return lval_eval_sexpr(v);
}
/* All other lval types remain the same */
return v;
}
int main(int argc, char *argv[]) {
/* Create Some Parsers */
mpc_parser_t *Number = mpc_new("number");
mpc_parser_t* Symbol = mpc_new("symbol");
mpc_parser_t* Sexpr = mpc_new("sexpr");
mpc_parser_t *Expr = mpc_new("expr");
mpc_parser_t *Lispy = mpc_new("lispy");
/* Define them with the following Language */
mpca_lang(MPCA_LANG_DEFAULT,
" \
number : /-?[0-9]+/ ; \
symbol : '+' | '-' | '*' | '/' ; \
sexpr : '(' <expr>* ')' ; \
expr : <number> | <symbol> | <sexpr> ; \
lispy : /^/ <expr>* /$/ ; \
",
Number, Symbol, Sexpr, Expr, Lispy);
puts("Lispy Version 0.1");
puts("Press Ctrl+c to Exit\n");
while(1) {
char *input = NULL;
input = readline("lispy> ");
add_history(input);
/* Attempt to parse the user input */
mpc_result_t r;
if (mpc_parse("<stdin>", input, Lispy, &r)) {
/* On success print and delete the AST */
lval *x = lval_eval(lval_read(r.output));
lval_println(x);
lval_del(x);
mpc_ast_delete(r.output);
} else {
/* Otherwise print and delete the Error */
mpc_err_print(r.error);
mpc_err_delete(r.error);
}
free(input);
}
/* Undefine and delete our parsers */
mpc_cleanup(4, Number, Symbol, Sexpr, Expr, Lispy);
return 0;
}
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
编译:
gcc -g -std=c99 -Wall main.c mpc.c -lreadline -lm -o main
- 1
运行:
Lispy Version 0.1
Press Ctrl+c to Exit
lispy> + 1 (* 7 5) 3
39
lispy> (- 100)
-100
lispy>
()
lispy> /
/
lispy> (/ ())
Error: Cannot operate on non-number!
lispy> ^C
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14