目录

前文列表

用 C 语言开发一门编程语言 — 交互式解析器l

用 C 语言开发一门编程语言 — 跨平台的可移植性

用 C 语言开发一门编程语言 — 语法解析器

用 C 语言开发一门编程语言 — 抽象语法树

用 C 语言开发一门编程语言 — 异常处理

用 C 语言开发一门编程语言 — S-表达式

Q-表达式

Q-表达式(Quoted Expression,Q-Expression)跟 S-Expression 一样,也是 Lisp 表达式的一种。但 Q-Expression 不受到 Lisp 的求值机制的作用,这是通过应用 C 语言的宏特性来实现的。宏看起来类似于普通的函数,但不会对参数进行求值。有一个叫做 “引用” 的宏(`)可以用来禁止几乎所有表达式的求值,这个宏也是 Q-Expression 的灵感来源。

也就是说,当受到函数的作用时,Q-Expression 不会被求值,而是保持原样。这一特性让 Q-Expression 有着广泛的应用。Lisp 程序员经常使用 Q-Expression 来存储和管理其他的 Lisp 数据类型,例如:数字、符号或 S-Expression 等等。

读取并存储输入

实现 Q-Expression 语法解析器

Q-Expression 的语法和 S-Expression 非常相似,唯一的不同是 Q-Expression 包裹在大括号 {} 中,而 S-Expression 包裹在小括号 () 中,Q-Expression 的语法规则如下:

  1. mpc_parser_t* Number = mpc_new("number");
  2. mpc_parser_t* Symbol = mpc_new("symbol");
  3. mpc_parser_t* Sexpr = mpc_new("sexpr");
  4. mpc_parser_t* Qexpr = mpc_new("qexpr");
  5. mpc_parser_t* Expr = mpc_new("expr");
  6. mpc_parser_t* Lispy = mpc_new("lispy");
  7. mpca_lang(MPCA_LANG_DEFAULT,
  8. " \
  9. number : /-?[0-9]+/ ; \
  10. symbol : '+' | '-' | '*' | '/' ; \
  11. sexpr : '(' <expr>* ')' ; \
  12. qexpr : '{' <expr>* '}' ; \
  13. expr : <number> | <symbol> | <sexpr> | <qexpr> ; \
  14. lispy : /^/ <expr>* /$/ ; \
  15. ",
  16. Number, Symbol, Sexpr, Qexpr, Expr, Lispy);
  17. mpc_cleanup(6, Number, Symbol, Sexpr, Qexpr, Expr, Lispy);

读取 Q-Expression

由于 Q-Expression 和 S-Expression 的形式基本一致,所以它们内部实现也大致是相同的。我们考虑重用 S-Expression 的数据结构来表示 Q-Expression。

同样的,首先在 lval 枚举类型中添加一个标识 Q-Expression 的类型:

  1. enum { LVAL_ERR, LVAL_NUM, LVAL_SYM, LVAL_SEXPR, LVAL_QEXPR };

另外,还需为其编写一个构造函数:

  1. /* A pointer to a new empty Qexpr lval */
  2. lval* lval_qexpr(void) {
  3. lval* v = malloc(sizeof(lval));
  4. v->type = LVAL_QEXPR;
  5. v->count = 0;
  6. v->cell = NULL;
  7. return v;
  8. }

Q-Expression 的打印和删除逻辑也和 S-Expression 别无二致,我们只需照葫芦画瓢即可:

  1. void lval_print(lval* v) {
  2. switch (v->type) {
  3. case LVAL_NUM: printf("%li", v->num); break;
  4. case LVAL_ERR: printf("Error: %s", v->err); break;
  5. case LVAL_SYM: printf("%s", v->sym); break;
  6. case LVAL_SEXPR: lval_expr_print(v, '(', ')'); break;
  7. case LVAL_QEXPR: lval_expr_print(v, '{', '}'); break;
  8. }
  9. }
  10. void lval_del(lval* v) {
  11. switch (v->type) {
  12. case LVAL_NUM: break;
  13. case LVAL_ERR: free(v->err); break;
  14. case LVAL_SYM: free(v->sym); break;
  15. /* If Qexpr or Sexpr then delete all elements inside */
  16. case LVAL_QEXPR:
  17. case LVAL_SEXPR:
  18. for (int i = 0; i < v->count; i++) {
  19. lval_del(v->cell[i]);
  20. }
  21. /* Also free the memory allocated to contain the pointers */
  22. free(v->cell);
  23. break;
  24. }
  25. free(v);
  26. }

最后,再更新一下读取函数 lval_read,使其可以正确读取 Q-Expression:

  1. if (strstr(t->tag, "qexpr")) { x = lval_qexpr(); }

因为 Q-Expression 重用了所有 S-Expression 的数据类型,所以我们也自然可以重用所有 S-Expression 的函数,例如 lval_add。在 lval_read 中添加一下代码识别花括号::

  1. if (strcmp(t->children[i]->contents, "(") == 0) { continue; }
  2. if (strcmp(t->children[i]->contents, ")") == 0) { continue; }
  3. if (strcmp(t->children[i]->contents, "}") == 0) { continue; }
  4. if (strcmp(t->children[i]->contents, "{") == 0) { continue; }

注意,因为 Q-Expression 没有任何求值方式,所以无需改动任何已有的求值函数。

实现 Q-Expression 的函数

在添加 Q-Expression 之后,我们还需要定义一系列的操作来管理它。类似于数学操作,这些操作定义了 Q-Expression 具体的行为:

  • list 函数:接收一个或者多个参数,返回一个包含所有参数的 Q-Expression。
  • head 函数:接受一个 Q-Expression,返回一个包含其第一个元素的 Q-Expression。
  • tail 函数:接受一个 Q-Expression,返回一个除首元素外的 Q-Expression。
  • join 函数:接受一个或者多个 Q-Expression,返回一个将其连在一起的 Q-Expression。
  • eval 函数:接受一个 Q-Expression,将其看做一个 S-Expression,并运行。

如同我们前面加的数学运算符一样,这些新的操作符也需要加入到 symbol 语法规则中:

  1. mpca_lang(MPCA_LANG_DEFAULT,
  2. " \
  3. number : /-?[0-9]+/ ; \
  4. symbol : \"list\" | \"head\" | \"tail\" \
  5. | \"join\" | \"eval\" | '+' | '-' | '*' | '/' ; \
  6. sexpr : '(' <expr>* ')' ; \
  7. qexpr : '{' <expr>* '}' ; \
  8. expr : <number> | <symbol> | <sexpr> | <qexpr> ; \
  9. lispy : /^/ <expr>* /$/ ; \
  10. ",
  11. Number, Symbol, Sexpr, Qexpr, Expr, Lispy)

Head & Tail

注意,head 和 tail 函数在某些条件下是不能执行的。首先要保证输入的参数只有一个,并且类型为 Q-Expression。其次这个输入的 Q-Expression 不能为空。

  • head 函数:接受一个 Q-Expression,返回一个包含其第一个元素的 Q-Expression。可以重复执行 pop 并 delete 在第二个数组元素上,直到数组为空。
  • tail 函数:接受一个 Q-Expression,返回一个除首元素外的 Q-Expression。只需要 pop 并 delete 第一个数组元素,剩余元素组成的数组则为我们所需要的。
  1. lval* builtin_head(lval* a) {
  2. /* Check Error Conditions */
  3. if (a->count != 1) {
  4. lval_del(a);
  5. return lval_err("Function 'head' passed too many arguments!");
  6. }
  7. if (a->cell[0]->type != LVAL_QEXPR) {
  8. lval_del(a);
  9. return lval_err("Function 'head' passed incorrect types!");
  10. }
  11. if (a->cell[0]->count == 0) {
  12. lval_del(a);
  13. return lval_err("Function 'head' passed {}!");
  14. }
  15. /* Otherwise take first argument */
  16. lval* v = lval_take(a, 0);
  17. /* Delete all elements that are not head and return */
  18. while (v->count > 1) { lval_del(lval_pop(v, 1)); }
  19. return v;
  20. }
  21. lval* builtin_tail(lval* a) {
  22. /* Check Error Conditions */
  23. if (a->count != 1) {
  24. lval_del(a);
  25. return lval_err("Function 'tail' passed too many arguments!");
  26. }
  27. if (a->cell[0]->type != LVAL_QEXPR) {
  28. lval_del(a);
  29. return lval_err("Function 'tail' passed incorrect types!");
  30. }
  31. if (a->cell[0]->count == 0) {
  32. lval_del(a);
  33. return lval_err("Function 'tail' passed {}!");
  34. }
  35. /* Take first argument */
  36. lval* v = lval_take(a, 0);
  37. /* Delete first element and return */
  38. lval_del(lval_pop(v, 0));
  39. return v;
  40. }

使用 C 语言的宏特性对上述代码进行优化

虽然上述实现的 head 和 tail 函数能够实现我们所需要的功能,但是代码难懂且长。有大段的代码是进行错误处理,使得真正逻辑的实现部分不那么明显。要解决这个问题,我们可以使用 C 语言的宏。

这里我们定义一个宏名为 LASSERT 的宏来帮助处理异常。注意,通常宏名都是全大写,这样能够和 C 函数名区分开来。LASSERT 宏有三个参数:args,cond 和 err。宏名定义如下:

  1. #define LASSERT(args, cond, err) \
  2. if (!(cond)) { lval_del(args); return lval_err(err); }

如此的,我们就可以通过定义这三个参数来生成代码了。

对 head 和 tail 函数进行优化:

  1. lval* builtin_head(lval* a) {
  2. LASSERT(a, a->count == 1,
  3. "Function 'head' passed too many arguments!");
  4. LASSERT(a, a->cell[0]->type == LVAL_QEXPR,
  5. "Function 'head' passed incorrect type!");
  6. LASSERT(a, a->cell[0]->count != 0,
  7. "Function 'head' passed {}!");
  8. lval* v = lval_take(a, 0);
  9. while (v->count > 1) { lval_del(lval_pop(v, 1)); }
  10. return v;
  11. }
  12. lval* builtin_tail(lval* a) {
  13. LASSERT(a, a->count == 1,
  14. "Function 'tail' passed too many arguments!");
  15. LASSERT(a, a->cell[0]->type == LVAL_QEXPR,
  16. "Function 'tail' passed incorrect type!");
  17. LASSERT(a, a->cell[0]->count != 0,
  18. "Function 'tail' passed {}!");
  19. lval* v = lval_take(a, 0);
  20. lval_del(lval_pop(v, 0));
  21. return v;
  22. }

List & Eval

  • list 函数比较简单。它只需将输入的一个或多个 S-Expression 转化为一个 Q-Expression。
  • eval 函数更像是转化。它将一个 Q-Expression 转化为 S-Expression,然后使用 lval_eval 运行。
  1. lval* builtin_list(lval* a) {
  2. a->type = LVAL_QEXPR;
  3. return a;
  4. }
  5. lval* builtin_eval(lval* a) {
  6. LASSERT(a, a->count == 1,
  7. "Function 'eval' passed too many arguments!");
  8. LASSERT(a, a->cell[0]->type == LVAL_QEXPR,
  9. "Function 'eval' passed incorrect type!");
  10. lval* x = lval_take(a, 0);
  11. x->type = LVAL_SEXPR;
  12. return lval_eval(x);
  13. }

Join

join 函数需要多个参数,其结构看起来更像先前定义的 builtin_op 函数。首先确保所有的参数都是 Q-Expression,然后将它们拼接起来。我们定义了 lval_join 函数,它将 y 中元素依次弹出并添加进 x 中,然后将 y 删除,返回 x。

  1. lval* builtin_join(lval* a) {
  2. for (int i = 0; i < a->count; i++) {
  3. LASSERT(a, a->cell[i]->type == LVAL_QEXPR,
  4. "Function 'join' passed incorrect type.");
  5. }
  6. lval* x = lval_pop(a, 0);
  7. while (a->count) {
  8. x = lval_join(x, lval_pop(a, 0));
  9. }
  10. lval_del(a);
  11. return x;
  12. }
  13. lval* lval_join(lval* x, lval* y) {
  14. /* For each cell in 'y' add it to 'x' */
  15. while (y->count) {
  16. x = lval_add(x, lval_pop(y, 0));
  17. }
  18. /* Delete the empty 'y' and return 'x' */
  19. lval_del(y);
  20. return x;
  21. }

函数索引

最后,还需要一个函数,根据提供的 Symbol 来调用相应的方法。这里我们可以用 strcmp 和 strstr 函数来实现。

  1. lval* builtin(lval* a, char* func) {
  2. if (strcmp("list", func) == 0) { return builtin_list(a); }
  3. if (strcmp("head", func) == 0) { return builtin_head(a); }
  4. if (strcmp("tail", func) == 0) { return builtin_tail(a); }
  5. if (strcmp("join", func) == 0) { return builtin_join(a); }
  6. if (strcmp("eval", func) == 0) { return builtin_eval(a); }
  7. if (strstr("+-/*", func)) { return builtin_op(a, func); }
  8. lval_del(a);
  9. return lval_err("Unknown Function!");
  10. }

同时修改早先 lval_eval_sexpr 函数来调用新的 buildin:

  1. /* Call builtin with operator */
  2. lval* result = builtin(v, f->sym);
  3. lval_del(f);
  4. return result;

源代码

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include "mpc.h"
  4. #define LASSERT(args, cond, err) \
  5. if (!(cond)) { lval_del(args); return lval_err(err); }
  6. #ifdef _WIN32
  7. #include <string.h>
  8. static char buffer[2048];
  9. char *readline(char *prompt) {
  10. fputs(prompt, stdout);
  11. fgets(buffer, 2048, stdin);
  12. char *cpy = malloc(strlen(buffer) + 1);
  13. strcpy(cpy, buffer);
  14. cpy[strlen(cpy) - 1] = '\0';
  15. return cpy;
  16. }
  17. void add_history(char *unused) {}
  18. #else
  19. #ifdef __linux__
  20. #include <readline/readline.h>
  21. #include <readline/history.h>
  22. #endif
  23. #ifdef __MACH__
  24. #include <readline/readline.h>
  25. #endif
  26. #endif
  27. /* Create Enumeration of Possible lval Types */
  28. enum {
  29. LVAL_NUM,
  30. LVAL_ERR,
  31. LVAL_SYM,
  32. LVAL_SEXPR,
  33. LVAL_QEXPR
  34. };
  35. /* Declare New lval Struct */
  36. typedef struct lval {
  37. int type;
  38. long num;
  39. /* Count and Pointer to a list of "lval*" */
  40. struct lval** cell;
  41. int count;
  42. /* Error and Symbol types have some string data */
  43. char *err;
  44. char *sym;
  45. } lval;
  46. /* Construct a pointer to a new Number lval */
  47. lval *lval_num(long x) {
  48. lval *v = malloc(sizeof(lval));
  49. v->type = LVAL_NUM;
  50. v->num = x;
  51. return v;
  52. }
  53. /* Construct a pointer to a new Error lval */
  54. lval *lval_err(char *msg) {
  55. lval *v = malloc(sizeof(lval));
  56. v->type = LVAL_ERR;
  57. v->err = malloc(strlen(msg) + 1);
  58. strcpy(v->err, msg);
  59. return v;
  60. }
  61. /* Construct a pointer to a new Symbol lval */
  62. lval *lval_sym(char *sym) {
  63. lval *v = malloc(sizeof(lval));
  64. v->type = LVAL_SYM;
  65. v->sym = malloc(strlen(sym) + 1);
  66. strcpy(v->sym, sym);
  67. return v;
  68. }
  69. /* A pointer to a new empty Sexpr lval */
  70. lval *lval_sexpr(void) {
  71. lval *v = malloc(sizeof(lval));
  72. v->type = LVAL_SEXPR;
  73. v->count = 0;
  74. v->cell = NULL;
  75. return v;
  76. }
  77. /* A pointer to a new empty Qexpr lval */
  78. lval *lval_qexpr(void) {
  79. lval *v = malloc(sizeof(lval));
  80. v->type = LVAL_QEXPR;
  81. v->count = 0;
  82. v->cell = NULL;
  83. return v;
  84. }
  85. void lval_del(lval *v) {
  86. switch (v->type) {
  87. /* Do nothing special for number type */
  88. case LVAL_NUM:
  89. break;
  90. /* For Err or Sym free the string data */
  91. case LVAL_ERR:
  92. free(v->err);
  93. break;
  94. case LVAL_SYM:
  95. free(v->sym);
  96. break;
  97. /* If Qexpr or Sexpr then delete all elements inside */
  98. case LVAL_QEXPR:
  99. case LVAL_SEXPR:
  100. for (int i = 0; i < v->count; i++) {
  101. lval_del(v->cell[i]);
  102. }
  103. /* Also free the memory allocated to contain the pointers */
  104. free(v->cell);
  105. break;
  106. }
  107. /* Free the memory allocated for the "lval" struct itself */
  108. free(v);
  109. }
  110. lval *lval_add(lval *v, lval *x) {
  111. v->count++;
  112. v->cell = realloc(v->cell, sizeof(lval*) * v->count);
  113. v->cell[v->count-1] = x;
  114. return v;
  115. }
  116. lval *lval_read_num(mpc_ast_t *t) {
  117. errno = 0;
  118. long x = strtol(t->contents, NULL, 10);
  119. return errno != ERANGE
  120. ? lval_num(x)
  121. : lval_err("invalid number");
  122. }
  123. lval *lval_read(mpc_ast_t *t) {
  124. /* If Symbol or Number return conversion to that type */
  125. if (strstr(t->tag, "number")) {
  126. return lval_read_num(t);
  127. }
  128. if (strstr(t->tag, "symbol")) {
  129. return lval_sym(t->contents);
  130. }
  131. /* If root (>) or sexpr then create empty list */
  132. lval *x = NULL;
  133. if (strcmp(t->tag, ">") == 0) {
  134. x = lval_sexpr();
  135. }
  136. if (strstr(t->tag, "sexpr")) {
  137. x = lval_sexpr();
  138. }
  139. if (strstr(t->tag, "qexpr")) {
  140. x = lval_qexpr();
  141. }
  142. /* Fill this list with any valid expression contained within */
  143. for (int i = 0; i < t->children_num; i++) {
  144. if (strcmp(t->children[i]->contents, "(") == 0) { continue; }
  145. if (strcmp(t->children[i]->contents, ")") == 0) { continue; }
  146. if (strcmp(t->children[i]->contents, "}") == 0) { continue; }
  147. if (strcmp(t->children[i]->contents, "{") == 0) { continue; }
  148. if (strcmp(t->children[i]->tag, "regex") == 0) { continue; }
  149. x = lval_add(x, lval_read(t->children[i]));
  150. }
  151. return x;
  152. }
  153. void lval_print(lval *v);
  154. void lval_expr_print(lval *v, char open, char close) {
  155. putchar(open);
  156. for (int i = 0; i < v->count; i++) {
  157. /* Print Value contained within */
  158. lval_print(v->cell[i]);
  159. /* Don't print trailing space if last element */
  160. if (i != (v->count-1)) {
  161. putchar(' ');
  162. }
  163. }
  164. putchar(close);
  165. }
  166. /* Print an "lval*" */
  167. void lval_print(lval *v) {
  168. switch (v->type) {
  169. case LVAL_NUM: printf("%li", v->num); break;
  170. case LVAL_ERR: printf("Error: %s", v->err); break;
  171. case LVAL_SYM: printf("%s", v->sym); break;
  172. case LVAL_SEXPR: lval_expr_print(v, '(', ')'); break;
  173. case LVAL_QEXPR: lval_expr_print(v, '{', '}'); break;
  174. }
  175. }
  176. /* Print an "lval" followed by a newline */
  177. void lval_println(lval *v) {
  178. lval_print(v);
  179. putchar('\n');
  180. }
  181. lval *lval_pop(lval *v, int i) {
  182. /* Find the item at "i" */
  183. lval *x = v->cell[i];
  184. /* Shift memory after the item at "i" over the top */
  185. memmove(&v->cell[i], &v->cell[i+1],
  186. sizeof(lval*) * (v->count-i-1));
  187. /* Decrease the count of items in the list */
  188. v->count--;
  189. /* Reallocate the memory used */
  190. v->cell = realloc(v->cell, sizeof(lval*) * v->count);
  191. return x;
  192. }
  193. lval *lval_take(lval *v, int i) {
  194. lval *x = lval_pop(v, i);
  195. lval_del(v);
  196. return x;
  197. }
  198. lval *builtin_op(lval *a, char *op) {
  199. /* Ensure all arguments are numbers */
  200. for (int i = 0; i < a->count; i++) {
  201. if (a->cell[i]->type != LVAL_NUM) {
  202. lval_del(a);
  203. return lval_err("Cannot operate on non-number!");
  204. }
  205. }
  206. /* Pop the first element */
  207. lval *x = lval_pop(a, 0);
  208. /* If no arguments and sub then perform unary negation */
  209. if ((strcmp(op, "-") == 0) && a->count == 0) {
  210. x->num = -x->num;
  211. }
  212. /* While there are still elements remaining */
  213. while (a->count > 0) {
  214. /* Pop the next element */
  215. lval *y = lval_pop(a, 0);
  216. if (strcmp(op, "+") == 0) { x->num += y->num; }
  217. if (strcmp(op, "-") == 0) { x->num -= y->num; }
  218. if (strcmp(op, "*") == 0) { x->num *= y->num; }
  219. if (strcmp(op, "/") == 0) {
  220. if (y->num == 0) {
  221. lval_del(x);
  222. lval_del(y);
  223. x = lval_err("Division By Zero!");
  224. break;
  225. }
  226. x->num /= y->num;
  227. }
  228. lval_del(y);
  229. }
  230. lval_del(a);
  231. return x;
  232. }
  233. lval *lval_eval(lval *v);
  234. lval *builtin(lval* a, char* func);
  235. lval *lval_eval_sexpr(lval *v) {
  236. /* Evaluate Children */
  237. for (int i = 0; i < v->count; i++) {
  238. v->cell[i] = lval_eval(v->cell[i]);
  239. }
  240. /* Error Checking */
  241. for (int i = 0; i < v->count; i++) {
  242. if (v->cell[i]->type == LVAL_ERR) {
  243. return lval_take(v, i);
  244. }
  245. }
  246. /* Empty Expression */
  247. if (v->count == 0) { return v; }
  248. /* Single Expression */
  249. if (v->count == 1) { return lval_take(v, 0); }
  250. /* Ensure First Element is Symbol */
  251. lval *f = lval_pop(v, 0);
  252. if (f->type != LVAL_SYM) {
  253. lval_del(f);
  254. lval_del(v);
  255. return lval_err("S-expression Does not start with symbol!");
  256. }
  257. /* Call builtin with operator */
  258. lval *result = builtin(v, f->sym);
  259. lval_del(f);
  260. return result;
  261. }
  262. lval *lval_eval(lval *v) {
  263. /* Evaluate Sexpressions */
  264. if (v->type == LVAL_SEXPR) {
  265. return lval_eval_sexpr(v);
  266. }
  267. /* All other lval types remain the same */
  268. return v;
  269. }
  270. lval *builtin_head(lval *a) {
  271. LASSERT(a, a->count == 1,
  272. "Function 'head' passed too many arguments!");
  273. LASSERT(a, a->cell[0]->type == LVAL_QEXPR,
  274. "Function 'head' passed incorrect type!");
  275. LASSERT(a, a->cell[0]->count != 0,
  276. "Function 'head' passed {}!");
  277. /* Otherwise take first argument */
  278. lval *v = lval_take(a, 0);
  279. /* Delete all elements that are not head and return */
  280. while (v->count > 1) {
  281. lval_del(lval_pop(v, 1));
  282. }
  283. return v;
  284. }
  285. lval *builtin_tail(lval *a) {
  286. LASSERT(a, a->count == 1,
  287. "Function 'tail' passed too many arguments!");
  288. LASSERT(a, a->cell[0]->type == LVAL_QEXPR,
  289. "Function 'tail' passed incorrect type!");
  290. LASSERT(a, a->cell[0]->count != 0,
  291. "Function 'tail' passed {}!");
  292. /* Take first argument */
  293. lval *v = lval_take(a, 0);
  294. /* Delete first element and return */
  295. lval_del(lval_pop(v, 0));
  296. return v;
  297. }
  298. lval *builtin_list(lval *a) {
  299. a->type = LVAL_QEXPR;
  300. return a;
  301. }
  302. lval *builtin_eval(lval *a) {
  303. LASSERT(a, a->count == 1,
  304. "Function 'eval' passed too many arguments!");
  305. LASSERT(a, a->cell[0]->type == LVAL_QEXPR,
  306. "Function 'eval' passed incorrect type!");
  307. lval *x = lval_take(a, 0);
  308. x->type = LVAL_SEXPR;
  309. return lval_eval(x);
  310. }
  311. lval *lval_join(lval *x, lval *y) {
  312. /* For each cell in 'y' add it to 'x' */
  313. while (y->count) {
  314. x = lval_add(x, lval_pop(y, 0));
  315. }
  316. /* Delete the empty 'y' and return 'x' */
  317. lval_del(y);
  318. return x;
  319. }
  320. lval *builtin_join(lval *a) {
  321. for (int i = 0; i < a->count; i++) {
  322. LASSERT(a, a->cell[i]->type == LVAL_QEXPR,
  323. "Function 'join' passed incorrect type.");
  324. }
  325. lval *x = lval_pop(a, 0);
  326. while (a->count) {
  327. x = lval_join(x, lval_pop(a, 0));
  328. }
  329. lval_del(a);
  330. return x;
  331. }
  332. lval *builtin(lval* a, char* func) {
  333. if (strcmp("list", func) == 0) { return builtin_list(a); }
  334. if (strcmp("head", func) == 0) { return builtin_head(a); }
  335. if (strcmp("tail", func) == 0) { return builtin_tail(a); }
  336. if (strcmp("join", func) == 0) { return builtin_join(a); }
  337. if (strcmp("eval", func) == 0) { return builtin_eval(a); }
  338. if (strstr("+-/*", func)) { return builtin_op(a, func); }
  339. lval_del(a);
  340. return lval_err("Unknown Function!");
  341. }
  342. int main(int argc, char *argv[]) {
  343. /* Create Some Parsers */
  344. mpc_parser_t *Number = mpc_new("number");
  345. mpc_parser_t* Symbol = mpc_new("symbol");
  346. mpc_parser_t* Sexpr = mpc_new("sexpr");
  347. mpc_parser_t *Qexpr = mpc_new("qexpr");
  348. mpc_parser_t *Expr = mpc_new("expr");
  349. mpc_parser_t *Lispy = mpc_new("lispy");
  350. /* Define them with the following Language */
  351. mpca_lang(MPCA_LANG_DEFAULT,
  352. " \
  353. number : /-?[0-9]+/ ; \
  354. symbol : \"list\" | \"head\" | \"tail\" \
  355. | \"join\" | \"eval\" \
  356. | '+' | '-' | '*' | '/' ; \
  357. sexpr : '(' <expr>* ')' ; \
  358. qexpr : '{' <expr>* '}' ; \
  359. expr : <number> | <symbol> | <sexpr> | <qexpr> ; \
  360. lispy : /^/ <expr>* /$/ ; \
  361. ",
  362. Number, Symbol, Sexpr, Qexpr, Expr, Lispy);
  363. puts("Lispy Version 0.1");
  364. puts("Press Ctrl+c to Exit\n");
  365. while(1) {
  366. char *input = NULL;
  367. input = readline("lispy> ");
  368. add_history(input);
  369. /* Attempt to parse the user input */
  370. mpc_result_t r;
  371. if (mpc_parse("<stdin>", input, Lispy, &r)) {
  372. /* On success print and delete the AST */
  373. lval *x = lval_eval(lval_read(r.output));
  374. lval_println(x);
  375. lval_del(x);
  376. mpc_ast_delete(r.output);
  377. } else {
  378. /* Otherwise print and delete the Error */
  379. mpc_err_print(r.error);
  380. mpc_err_delete(r.error);
  381. }
  382. free(input);
  383. }
  384. /* Undefine and delete our parsers */
  385. mpc_cleanup(6, Number, Symbol, Sexpr, Qexpr, Expr, Lispy);
  386. return 0;
  387. }

编译

  1. gcc -g -std=c99 -Wall parsing.c mpc.c -lreadline -lm -o parsing

运行:

  1. $ ./parsing
  2. Lispy Version 0.1
  3. Press Ctrl+c to Exit
  4. lispy> list 1 2 3 4
  5. {1 2 3 4}
  6. lispy> {head (list 1 2 3 4)}
  7. {head (list 1 2 3 4)}
  8. lispy> eval {head (list 1 2 3 4)}
  9. {1}
  10. lispy> tail {tail tail tail}
  11. {tail tail}
  12. lispy> eval (tail {tail tail {5 6 7}})
  13. {6 7}
  14. lispy> eval (head {(+ 1 2) (+ 10 20)})
  15. 3
  16. lispy> ^C

用 C 语言开发一门编程语言 — Q-表达式的更多相关文章

  1. Mac OSX下Go语言开发环境的搭建与配置--使用InteliJ IDEA 13

    折腾了一上午终于把go语言的ide配置好了. 其实GO语言的语法和特性早在去年的时候就学习了一遍.结果后来一直没机会进行开发,结果还是个GO小白.感叹一下,要学好一门编程语言唯一的途径就是多写代码.. ...

  2. Go语言开发

    Go语言圣经(中文版)     Go编程语言规范 搭建Go开发及调试环境(LiteIDE + GoClipse) -- Windows篇           Go开发工具 Go命令行操作命令详细介绍 ...

  3. Go语言开发第一个Hello,World

    在网上看到go语言的各种评价,也是闻名已久,但是没有自己实践过,也不知道它的好,它的坏,今天就来试试第一个小程序 第一步.如何下载 1)下载go安装程序 下载地址:https://golang.org ...

  4. Go语言开发环境配置

    一.我为什么要学习go语言 当今已经是移动和云计算时代,Go出现在了工业向云计算转型的时刻,简单.高效.内 置并发原语和现代的标准库让Go语言尤其适合云端软件开发(毕竟它就是为此而设计的).到2014 ...

  5. windows下用eclipse+goclipse插件+gdb搭建go语言开发调试环境

    windows下用eclipse+goclipse插件+gdb搭建go语言开发调试环境   http://rongmayisheng.com/post/windows%E4%B8%8B%E7%94%A ...

  6. (转载)Go语言开发环境配置

    一.我为什么要学习go语言 当今已经是移动和云计算时代,Go出现在了工业向云计算转型的时刻,简单.高效.内 置并发原语和现代的标准库让Go语言尤其适合云端软件开发(毕竟它就是为此而设计的).到2014 ...

  7. Go语言开发环境安装

    Go是Google开发的一种编译型,並發型,并具有垃圾回收功能的编程语言. 去http://golang.org/doc/install#download 下载相应的版本. 1.安装go语言:2.将g ...

  8. 收MUD巫师学徒,MUD开发,LPC语言开发

    收MUD巫师学徒,MUD开发,LPC语言开发 对这个有兴趣的联系我,签订协议  Q 184377367

  9. Mac电脑C语言开发的入门帖

    本文是写给Mac电脑开发新手的入门帖,诸神请退散. C语言 C语言可说是电脑环境中的"镇国神器",从发明至今,虽然C语言的使用者缓慢的减少,但从当前市场应用情况看,尚无一台电脑能够 ...

  10. 第一行代码:以太坊(2)-使用Solidity语言开发和测试智能合约

    智能合约是以太坊的核心之一,用户可以利用智能合约实现更灵活的代币以及其他DApp.不过在深入讲解如何开发智能合约之前,需要先介绍一下以太坊中用于开发智能合约的Solidity语言,以及相关的开发和测试 ...

随机推荐

  1. 关于pwn题的栈平衡中ret的作用

    以nssctf里的where_is_my_shell为例 题目提供了一个system函数,和一个buf数组.数组的栈空间如图所示,这里不讨论怎么解题,只说明payload里的ret的作用. 假设没有r ...

  2. ubuntu环境下安装perf工具

    检查当前环境内核的版本,执行如下命令: uname -a 输出信息如下: Linux jackie-ubuntu 5.4.0-26-generic #30-Ubuntu SMP Mon Apr 20 ...

  3. Kafka原理剖析之「位点提交」

    一.背景 Kafka的位点提交一直是Consumer端非常重要的一部分,业务上我们经常遇到的消息丢失.消息重复也与其息息相关.位点提交说简单也简单,说复杂也确实复杂,没有人能用一段简短的话将其说清楚, ...

  4. HMS Core电商解决方案之商品3D商品展示

    传统电商商品展示采用图文结合的形式,文案介绍产品的相关参数,搭配精美图片去吸引客户眼球.但图文商品展示由于色差.尺寸不符等原因,会让消费者产生图片和实物不一致的疑虑,且消费者需要消耗大量精力阅读和比较 ...

  5. 开发案例:使用canvas实现图表系列之折线图

      一.功能结构 实现一个公共组件的时候,首先分析一下大概的实现结构以及开发思路,方便我们少走弯路,也可以使组件更加容易拓展,维护性更强.然后我会把功能逐个拆开来讲,这样大家才能学习到更详细的内容.下 ...

  6. Vue3 + TypeScript 开发指南

    0x00 概述 阅读以下内容需要具备一定的 Vue2 基础 代码采用规范为:TypeScript + 组合式 API + setup 语法糖 (1)Vue3 简介 Vue3 第一个正式版发布于 202 ...

  7. CentOS 防火墙配置实战精要

    防火墙是服务器最重要的安全屏障,正确的操作对应用也是非常非常关键,本文参考了一些实战经验并经过实验验证,集众家之长做了简单的归纳整理,希望能帮助你更好地操作防火墙. 设置CentOS防火墙开放端口 在 ...

  8. nginx重新整理——————热部署和日志切割[三]

    前言 简单演示热部署和日志切割. 正文 什么是热部署了,我们前文也说过了一个编译后的nginx 二进制. 热部署就是无需停止现有的nginx,替换正在运行的nginx. 步骤: 复制nginx 二进制 ...

  9. mysql8在Win10下安装教程

    一.准备工作 下载mysql8安装包,下载URL地址:https://mirrors.tuna.tsinghua.edu.cn/mysql/downloads/MySQL-8.0/ 二.管理员权限执行 ...

  10. HL7传输协议

    HL7消息通过各种TCP/IP传输发送,其中一些包括: 下层协议(LLP) 文件传输协议(FTP) 简单对象访问协议(SOAP) 简单邮件传输协议(SMTP) 尽管HL7可以使用多种传输协议进行数据传 ...