目录

前文列表

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

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

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

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

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

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

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

用 C 语言开发一门编程语言 — 变量元素设计

用 C 语言开发一门编程语言 — 基于 Lambda 表达式的函数设计

条件分支

本文中,我们将介绍几种在 C 中经常用到的和条件判断有关的函数。包括:排序,等式,if 函数,递归函数等。

排序函数

我们将复用之前的 Number 数据类型来表示比较结果。 制定一个类似于 C 语言的条件分支规则:0 即 False,非 0 即 True。

因此,我们的排序函数有点像算术函数的简化版本。 排序函数仅仅能够处理数字。此外,我们希望函数只能适用于两个参数的情形。根据两个 lval 输入之间的相等比较返回一个数字为 0 或 1 的 lval。使用 C 语言的比较运算符可以做到这一点。与之前的算术函数一样,我们将使用单个函数来完成所有比较。

  1. 首先我们检查错误情形。
  2. 然后我们比较参数中的数字获得结果。
  3. 最后,我们将结果作为数字返回。
lval* builtin_gt(lenv* e, lval* a) {
return builtin_ord(e, a, ">");
} lval* builtin_lt(lenv* e, lval* a) {
return builtin_ord(e, a, "<");
} lval* builtin_ge(lenv* e, lval* a) {
return builtin_ord(e, a, ">=");
} lval* builtin_le(lenv* e, lval* a) {
return builtin_ord(e, a, "<=");
} lval* builtin_ord(lenv* e, lval* a, char* op) {
LASSERT_NUM(op, a, 2);
LASSERT_TYPE(op, a, 0, LVAL_NUM);
LASSERT_TYPE(op, a, 1, LVAL_NUM); int r;
if (strcmp(op, ">") == 0) {
r = (a->cell[0]->num > a->cell[1]->num);
}
if (strcmp(op, "<") == 0) {
r = (a->cell[0]->num < a->cell[1]->num);
}
if (strcmp(op, ">=") == 0) {
r = (a->cell[0]->num >= a->cell[1]->num);
}
if (strcmp(op, "<=") == 0) {
r = (a->cell[0]->num <= a->cell[1]->num);
}
lval_del(a);
return lval_num(r);
}

等于函数

等于函数与排序函数有所不同,因为我们希望它不仅仅适用于数字类型。它还将用于判断输入是否为空列表,或者判断两个函数是否相同。因此我们需要定义一个用来检验两个不同类型 lval 变量是否相等的函数。

该函数实质上检查两个构成 lval 类型数据的所有字段是否相等。 若所有字段都相等,则两者被认为是相等的。 否则,若存在任何差异,则两者被认为是不相等的。

int lval_eq(lval* x, lval* y) {

  /* Different Types are always unequal */
if (x->type != y->type) { return 0; } /* Compare Based upon type */
switch (x->type) {
/* Compare Number Value */
case LVAL_NUM: return (x->num == y->num); /* Compare String Values */
case LVAL_ERR: return (strcmp(x->err, y->err) == 0);
case LVAL_SYM: return (strcmp(x->sym, y->sym) == 0); /* If builtin compare, otherwise compare formals and body */
case LVAL_FUN:
if (x->builtin || y->builtin) {
return x->builtin == y->builtin;
} else {
return lval_eq(x->formals, y->formals)
&& lval_eq(x->body, y->body);
} /* If list compare every individual element */
case LVAL_QEXPR:
case LVAL_SEXPR:
if (x->count != y->count) { return 0; }
for (int i = 0; i < x->count; i++) {
/* If any element not equal then whole list not equal */
if (!lval_eq(x->cell[i], y->cell[i])) { return 0; }
}
/* Otherwise lists must be equal */
return 1;
break;
}
return 0;
}

使用该函数,可以非常简单地新增用于比较是否相等的内置函数。 我们仅需要保证输入为两个参数,检验它们是否相等,然后我们将比较结果存储到一个新的 lval 中并返回它。

lval* builtin_cmp(lenv* e, lval* a, char* op) {
LASSERT_NUM(op, a, 2);
int r;
if (strcmp(op, "==") == 0) {
r = lval_eq(a->cell[0], a->cell[1]);
}
if (strcmp(op, "!=") == 0) {
r = !lval_eq(a->cell[0], a->cell[1]);
}
lval_del(a);
return lval_num(r);
} lval* builtin_eq(lenv* e, lval* a) {
return builtin_cmp(e, a, "==");
} lval* builtin_ne(lenv* e, lval* a) {
return builtin_cmp(e, a, "!=");
}

if 函数

为了更好的使用比较运算符,我们需要新增 if 函数。 该函数有点像 C 语言中的三元运算。在条件为真时,它对一段代码求值,如果条件为假,则对另一段代码求值。

我们再次使用 Q-Expression 来对输入进行编码。 首先,我们让用户传入比较结果,然后我们让用户传入两个 Q-Expression,分别表示在条件结果为 True 或 False 时进行求值的代码。

lval* builtin_if(lenv* e, lval* a) {
LASSERT_NUM("if", a, 3);
LASSERT_TYPE("if", a, 0, LVAL_NUM);
LASSERT_TYPE("if", a, 1, LVAL_QEXPR);
LASSERT_TYPE("if", a, 2, LVAL_QEXPR); /* Mark Both Expressions as evaluable */
lval* x;
a->cell[1]->type = LVAL_SEXPR;
a->cell[2]->type = LVAL_SEXPR; if (a->cell[0]->num) {
/* If condition is true evaluate first expression */
x = lval_eval(e, lval_pop(a, 1));
} else {
/* Otherwise evaluate second expression */
x = lval_eval(e, lval_pop(a, 2));
} /* Delete argument list and return */
lval_del(a);
return x;
}

剩下的就是让我们注册这些新增内置函数。

/* Comparison Functions */
lenv_add_builtin(e, "if", builtin_if);
lenv_add_builtin(e, "==", builtin_eq);
lenv_add_builtin(e, "!=", builtin_ne);
lenv_add_builtin(e, ">", builtin_gt);
lenv_add_builtin(e, "<", builtin_lt);
lenv_add_builtin(e, ">=", builtin_ge);
lenv_add_builtin(e, "<=", builtin_le);

递归函数

引入条件分支,实际上让我们的语言变得更加强大。 因为条件分支可以有效地让我们实现递归函数。递归函数是那些自己调用自己的函数。 我们已经在 C 中使用这些来执行表达式的读取和求值。 我们需要条件分支的原因是因为它们让我们检验我们想要终止递归的情况。

例如,我们可以使用条件分支来实现一个函数 len,该函数返回列表中元素个数。 如果我们遇到空列表,我们只返回 0。 否则我们返回输入列表的 tail 的长度加上 1。 它重复调用 len 函数,直到遇到空列表。 此时它返回 0 并将所有其他部分结果加在一起。

(fun {len l} {
if (== l {})
{0}
{+ 1 (len (tail l))}
})

就像在 C 中一样,递归函数具有令人愉悦的对称性。

首先,我们对空列表(基线条件)做一些处理。 然后,如果我们得到更大的列表,我们就会取出一个部分,例如列表的头部元素,并对它做一些处理,然后再将它与已经调用了该函数的其余部分进行组合。

这是另一个递归函数 —— 反转列表。和之前一样,函数检查列表是否空列表,但该函数中基线条件的处理是返回空列表。这是合理的,因为空列表的反转形式同样也是空列表。但如果是更大的列表,函数会使尾部反转,并将其放在头部前面。

(fun {reverse l} {
if (== l {})
{{}}
{join (reverse (tail l)) (head l)}
})

我们将使用递归来构建许多函数。 因为这将成为在我们的语言中实现循环的主要方式。

源代码

#include <stdio.h>
#include <stdlib.h>
#include "mpc.h" #define LASSERT(args, cond, fmt, ...) \
if (!(cond)) { lval* err = lval_err(fmt, ##__VA_ARGS__); lval_del(args); return err; } #define LASSERT_TYPE(func, args, index, expect) \
LASSERT(args, args->cell[index]->type == expect, \
"Function '%s' passed incorrect type for argument %i. Got %s, Expected %s.", \
func, index, ltype_name(args->cell[index]->type), ltype_name(expect)) #define LASSERT_NUM(func, args, num) \
LASSERT(args, args->count == num, \
"Function '%s' passed incorrect number of arguments. Got %i, Expected %i.", \
func, args->count, num) #define LASSERT_NOT_EMPTY(func, args, index) \
LASSERT(args, args->cell[index]->count != 0, \
"Function '%s' passed {} for argument %i.", func, index); #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 /* Forward Declarations */
struct lval;
struct lenv;
typedef struct lval lval;
typedef struct lenv lenv; /* Lisp Value Type Enumeration */
enum {
LVAL_NUM,
LVAL_ERR,
LVAL_SYM,
LVAL_FUN,
LVAL_SEXPR,
LVAL_QEXPR
}; typedef lval *(*lbuiltin)(lenv*, lval*); /* Declare lisp lval Struct */
struct lval {
int type; /* Basic */
long num;
char *err;
char *sym; /* Function */
lbuiltin builtin;
lenv *env;
lval *formals;
lval *body; /* Expression */
int count;
struct lval **cell;
}; /* 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;
} char *ltype_name(int t) {
switch(t) {
case LVAL_FUN: return "Function";
case LVAL_NUM: return "Number";
case LVAL_ERR: return "Error";
case LVAL_SYM: return "Symbol";
case LVAL_SEXPR: return "S-Expression";
case LVAL_QEXPR: return "Q-Expression";
default: return "Unknown";
}
} /* Construct a pointer to a new Error lval */
lval *lval_err(char *fmt, ...) {
lval *v = malloc(sizeof(lval));
v->type = LVAL_ERR;
/* Create a va list and initialize it */
va_list va;
va_start(va, fmt); /* Allocate 512 bytes of space */
v->err = malloc(512); /* printf the error string with a maximum of 511 characters */
vsnprintf(v->err, 511, fmt, va); /* Reallocate to number of bytes actually used */
v->err = realloc(v->err, strlen(v->err)+1); /* Cleanup our va list */
va_end(va); 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;
} /* A pointer to a new empty Qexpr lval */
lval *lval_qexpr(void) {
lval *v = malloc(sizeof(lval));
v->type = LVAL_QEXPR;
v->count = 0;
v->cell = NULL;
return v;
} lval* lval_builtin(lbuiltin func) {
lval *v = malloc(sizeof(lval));
v->type = LVAL_FUN;
v->builtin = func;
return v;
} struct lenv {
lenv *par; int count;
char **syms;
lval **vals;
}; lenv *lenv_new(void) {
lenv *e = malloc(sizeof(lenv));
e->par = NULL;
e->count = 0;
e->syms = NULL;
e->vals = NULL;
return e;
} lval *lval_lambda(lval *formals, lval *body) {
lval *v = malloc(sizeof(lval));
v->type = LVAL_FUN; /* Set Builtin to Null */
v->builtin = NULL; /* Build new environment */
v->env = lenv_new(); /* Set Formals and Body */
v->formals = formals;
v->body = body;
return v;
} void lenv_del(lenv *e); 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; case LVAL_FUN:
if (!v->builtin) {
lenv_del(v->env);
lval_del(v->formals);
lval_del(v->body);
}
break; /* If Qexpr or Sexpr then delete all elements inside */
case LVAL_QEXPR:
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);
} void lenv_del(lenv *e) {
for (int i = 0; i < e->count; i++) {
free(e->syms[i]);
lval_del(e->vals[i]);
}
free(e->syms);
free(e->vals);
free(e);
} lval *lval_copy(lval *v); lenv *lenv_copy(lenv *e) {
lenv *n = malloc(sizeof(lenv));
n->par = e->par;
n->count = e->count;
n->syms = malloc(sizeof(char*) * n->count);
n->vals = malloc(sizeof(lval*) * n->count);
for (int i = 0; i < e->count; i++) {
n->syms[i] = malloc(strlen(e->syms[i]) + 1);
strcpy(n->syms[i], e->syms[i]);
n->vals[i] = lval_copy(e->vals[i]);
}
return n;
} lval *lval_copy(lval *v) {
lval *x = malloc(sizeof(lval));
x->type = v->type; switch (v->type) {
/* Copy Functions and Numbers Directly */
case LVAL_FUN:
if (v->builtin) {
x->builtin = v->builtin;
} else {
x->builtin = NULL;
x->env = lenv_copy(v->env);
x->formals = lval_copy(v->formals);
x->body = lval_copy(v->body);
}
break;
case LVAL_NUM: x->num = v->num; break; /* Copy Strings using malloc and strcpy */
case LVAL_ERR:
x->err = malloc(strlen(v->err) + 1);
strcpy(x->err, v->err);
break; case LVAL_SYM:
x->sym = malloc(strlen(v->sym) + 1);
strcpy(x->sym, v->sym);
break; /* Copy Lists by copying each sub-expression */
case LVAL_SEXPR:
case LVAL_QEXPR:
x->count = v->count;
x->cell = malloc(sizeof(lval*) * x->count);
for (int i = 0; i < x->count; i++) {
x->cell[i] = lval_copy(v->cell[i]);
}
break;
}
return x;
} lval *lenv_get(lenv *e, lval *k) {
/* Iterate over all items in environment */
for (int i = 0; i < e->count; i++) {
/* Check if the stored string matches the symbol string */
/* If it does, return a copy of the value */
if (strcmp(e->syms[i], k->sym) == 0) {
return lval_copy(e->vals[i]);
}
} /* If no symbol check in parent otherwise error */
if (e->par) {
return lenv_get(e->par, k);
} else {
return lval_err("Unbound Symbol '%s'", k->sym);
}
} void lenv_put(lenv *e, lval *k, lval *v) {
/* Iterate over all items in environment */
/* This is to see if variable already exists */
for (int i = 0; i < e->count; i++) {
/* If variable is found delete item at that position */
/* And replace with variable supplied by user */
if (strcmp(e->syms[i], k->sym) == 0) {
lval_del(e->vals[i]);
e->vals[i] = lval_copy(v);
return;
}
} /* If no existing entry found allocate space for new entry */
e->count++;
e->vals = realloc(e->vals, sizeof(lval*) * e->count);
e->syms = realloc(e->syms, sizeof(char*) * e->count); /* Copy contents of lval and symbol string into new location */
e->vals[e->count-1] = lval_copy(v);
e->syms[e->count-1] = malloc(strlen(k->sym)+1);
strcpy(e->syms[e->count-1], k->sym);
} void lenv_def(lenv *e, lval *k, lval *v) {
/* Iterate till e has no parent */
while (e->par) {
e = e->par;
}
/* Put value in e */
lenv_put(e, k, 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();
}
if (strstr(t->tag, "qexpr")) {
x = lval_qexpr();
} /* 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]->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;
case LVAL_QEXPR: lval_expr_print(v, '{', '}'); break;
case LVAL_FUN:
if (v->builtin) {
printf("<builtin>");
} else {
printf("(\\ "); lval_print(v->formals);
putchar(' ');
lval_print(v->body);
putchar(')');
}
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_eval(lenv* e, lval *a);
lval *builtin_list(lenv *e, lval *a); lval *lval_call(lenv *e, lval *f, lval *a) {
/* If Builtin then simply apply that */
if (f->builtin) {
return f->builtin(e, a);
} /* Record Argument Counts */
int given = a->count;
int total = f->formals->count; /* While arguments still remain to be processed */
while (a->count) {
/* If we've ran out of formal arguments to bind */
if (f->formals->count == 0) {
lval_del(a);
return lval_err("Function passed too many arguments. "
"Got %i, Expected %i.", given, total);
} /* Pop the first symbol from the formals */
lval *sym = lval_pop(f->formals, 0); /* Special Case to deal with '&' */
if (strcmp(sym->sym, "&") == 0) {
/* Ensure '&' is followed by another symbol */
if (f->formals->count != 1) {
lval_del(a);
return lval_err("Function format invalid. "
"Symbol '&' not followed by single symbol.");
} /* Next formal should be bound to remaining arguments */
lval *nsym = lval_pop(f->formals, 0);
lenv_put(f->env, nsym, builtin_list(e, a));
lval_del(sym); lval_del(nsym);
break;
} /* Pop the next argument from the list */
lval* val = lval_pop(a, 0); /* Bind a copy into the function's environment */
lenv_put(f->env, sym, val); /* Delete symbol and value */
lval_del(sym); lval_del(val);
} /* Argument list is now bound so can be cleaned up */
lval_del(a); /* If '&' remains in formal list bind to empty list */
if (f->formals->count > 0 && strcmp(f->formals->cell[0]->sym, "&") == 0) {
/* Check to ensure that & is not passed invalidly. */
if (f->formals->count != 2) {
return lval_err("Function format invalid. "
"Symbol '&' not followed by single symbol.");
} /* Pop and delete '&' symbol */
lval_del(lval_pop(f->formals, 0)); /* Pop next symbol and create empty list */
lval* sym = lval_pop(f->formals, 0);
lval* val = lval_qexpr(); /* Bind to environment and delete */
lenv_put(f->env, sym, val);
lval_del(sym); lval_del(val);
} /* If all formals have been bound evaluate */
if (f->formals->count == 0) { /* Set environment parent to evaluation environment */
f->env->par = e; /* Evaluate and return */
return builtin_eval(f->env, lval_add(lval_sexpr(),
lval_copy(f->body)));
} else {
/* Otherwise return partially evaluated function */
return lval_copy(f);
}
} lval *lval_eval(lenv *e, lval *v);
lval *builtin(lval* a, char* func); lval *lval_eval_sexpr(lenv *e, lval *v) {
/* Evaluate Children */
for (int i = 0; i < v->count; i++) {
v->cell[i] = lval_eval(e, 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 a function after evaluation */
lval *f = lval_pop(v, 0);
if (f->type != LVAL_FUN) {
lval *err = lval_err("S-Expression starts with incorrect type. "
"Got %s, Expected %s.",
ltype_name(f->type), ltype_name(LVAL_FUN));
lval_del(f);
lval_del(v);
return err;
} lval *result = lval_call(e, f, v);
lval_del(f);
return result;
} lval *lval_eval(lenv *e, lval *v) {
if (v->type == LVAL_SYM) {
lval *x = lenv_get(e, v);
lval_del(v);
return x;
} /* Evaluate Sexpressions */
if (v->type == LVAL_SEXPR) {
return lval_eval_sexpr(e, v);
} /* All other lval types remain the same */
return v;
} lval *builtin_op(lenv* e, lval *a, char *op) { /* Ensure all arguments are numbers */
for (int i = 0; i < a->count; i++) {
LASSERT_TYPE(op, a, i, LVAL_NUM);
} /* 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 *builtin_head(lenv* e, lval *a) {
LASSERT_NUM("head", a, 1);
LASSERT_TYPE("head", a, 0, LVAL_QEXPR);
LASSERT_NOT_EMPTY("head", a, 0); /* Otherwise take first argument */
lval *v = lval_take(a, 0); /* Delete all elements that are not head and return */
while (v->count > 1) {
lval_del(lval_pop(v, 1));
} return v;
} lval *builtin_tail(lenv *e, lval *a) {
LASSERT_NUM("tail", a, 1);
LASSERT_TYPE("tail", a, 0, LVAL_QEXPR);
LASSERT_NOT_EMPTY("tail", a, 0); /* Take first argument */
lval *v = lval_take(a, 0); /* Delete first element and return */
lval_del(lval_pop(v, 0)); return v; } lval *builtin_list(lenv *e, lval *a) {
a->type = LVAL_QEXPR;
return a;
} lval *builtin_eval(lenv* e, lval *a) {
LASSERT_NUM("eval", a, 1);
LASSERT_TYPE("eval", a, 0, LVAL_QEXPR); lval *x = lval_take(a, 0);
x->type = LVAL_SEXPR;
return lval_eval(e, x);
} lval *lval_join(lval *x, lval *y) { /* For each cell in 'y' add it to 'x' */
while (y->count) {
x = lval_add(x, lval_pop(y, 0));
} /* Delete the empty 'y' and return 'x' */
lval_del(y);
return x;
} lval *builtin_join(lenv *e, lval *a) {
for (int i = 0; i < a->count; i++) {
LASSERT_TYPE("join", a, i, LVAL_QEXPR);
} lval *x = lval_pop(a, 0); while (a->count) {
x = lval_join(x, lval_pop(a, 0));
} lval_del(a);
return x;
} lval *builtin_add(lenv *e, lval *a) {
return builtin_op(e, a, "+");
} lval *builtin_sub(lenv *e, lval *a) {
return builtin_op(e, a, "-");
} lval *builtin_mul(lenv *e, lval *a) {
return builtin_op(e, a, "*");
} lval *builtin_div(lenv *e, lval *a) {
return builtin_op(e, a, "/");
} void lenv_add_builtin(lenv *e, char *name, lbuiltin func) {
lval *k = lval_sym(name);
lval* v = lval_builtin(func);
lenv_put(e, k, v);
lval_del(k); lval_del(v);
} lval *builtin_var(lenv *e, lval *a, char *func) {
LASSERT_TYPE(func, a, 0, LVAL_QEXPR); lval* syms = a->cell[0];
for (int i = 0; i < syms->count; i++) {
LASSERT(a, (syms->cell[i]->type == LVAL_SYM),
"Function '%s' cannot define non-symbol. "
"Got %s, Expected %s.", func,
ltype_name(syms->cell[i]->type),
ltype_name(LVAL_SYM));
} LASSERT(a, (syms->count == a->count-1),
"Function '%s' passed too many arguments for symbols. "
"Got %i, Expected %i.", func, syms->count, a->count-1); for (int i = 0; i < syms->count; i++) {
/* If 'def' define in globally. If 'put' define in locally */
if (strcmp(func, "def") == 0) {
lenv_def(e, syms->cell[i], a->cell[i+1]);
} if (strcmp(func, "=") == 0) {
lenv_put(e, syms->cell[i], a->cell[i+1]);
}
} lval_del(a);
return lval_sexpr();
} lval *builtin_def(lenv *e, lval *a) {
return builtin_var(e, a, "def");
} lval *builtin_put(lenv *e, lval *a) {
return builtin_var(e, a, "=");
} lval *builtin_lambda(lenv *e, lval *a) {
/* Check Two arguments, each of which are Q-Expressions */
LASSERT_NUM("\\", a, 2);
LASSERT_TYPE("\\", a, 0, LVAL_QEXPR);
LASSERT_TYPE("\\", a, 1, LVAL_QEXPR); /* Check first Q-Expression contains only Symbols */
for (int i = 0; i < a->cell[0]->count; i++) {
LASSERT(a, (a->cell[0]->cell[i]->type == LVAL_SYM),
"Cannot define non-symbol. Got %s, Expected %s.",
ltype_name(a->cell[0]->cell[i]->type),ltype_name(LVAL_SYM));
} /* Pop first two arguments and pass them to lval_lambda */
lval *formals = lval_pop(a, 0);
lval *body = lval_pop(a, 0);
lval_del(a); return lval_lambda(formals, body);
} lval *builtin_ord(lenv *e, lval *a, char *op); lval *builtin_gt(lenv *e, lval *a) {
return builtin_ord(e, a, ">");
} lval *builtin_lt(lenv *e, lval *a) {
return builtin_ord(e, a, "<");
} lval *builtin_ge(lenv *e, lval *a) {
return builtin_ord(e, a, ">=");
} lval *builtin_le(lenv *e, lval *a) {
return builtin_ord(e, a, "<=");
} lval *builtin_ord(lenv *e, lval *a, char *op) {
LASSERT_NUM(op, a, 2);
LASSERT_TYPE(op, a, 0, LVAL_NUM);
LASSERT_TYPE(op, a, 1, LVAL_NUM); int r;
if (strcmp(op, ">") == 0) {
r = (a->cell[0]->num > a->cell[1]->num);
}
if (strcmp(op, "<") == 0) {
r = (a->cell[0]->num < a->cell[1]->num);
}
if (strcmp(op, ">=") == 0) {
r = (a->cell[0]->num >= a->cell[1]->num);
}
if (strcmp(op, "<=") == 0) {
r = (a->cell[0]->num <= a->cell[1]->num);
}
lval_del(a);
return lval_num(r);
} int lval_eq(lval *x, lval *y) {
/* Different Types are always unequal */
if (x->type != y->type) { return 0; } /* Compare Based upon type */
switch (x->type) {
/* Compare Number Value */
case LVAL_NUM: return (x->num == y->num); /* Compare String Values */
case LVAL_ERR: return (strcmp(x->err, y->err) == 0);
case LVAL_SYM: return (strcmp(x->sym, y->sym) == 0); /* If builtin compare, otherwise compare formals and body */
case LVAL_FUN:
if (x->builtin || y->builtin) {
return x->builtin == y->builtin;
} else {
return lval_eq(x->formals, y->formals) && lval_eq(x->body, y->body);
} /* If list compare every individual element */
case LVAL_QEXPR:
case LVAL_SEXPR:
if (x->count != y->count) { return 0; }
for (int i = 0; i < x->count; i++) {
/* If any element not equal then whole list not equal */
if (!lval_eq(x->cell[i], y->cell[i])) { return 0; }
}
/* Otherwise lists must be equal */
return 1;
break;
}
return 0;
} lval *builtin_cmp(lenv *e, lval *a, char *op) {
LASSERT_NUM(op, a, 2);
int r;
if (strcmp(op, "==") == 0) {
r = lval_eq(a->cell[0], a->cell[1]);
}
if (strcmp(op, "!=") == 0) {
r = !lval_eq(a->cell[0], a->cell[1]);
}
lval_del(a);
return lval_num(r);
} lval *builtin_eq(lenv *e, lval *a) {
return builtin_cmp(e, a, "==");
} lval *builtin_ne(lenv *e, lval *a) {
return builtin_cmp(e, a, "!=");
} lval *builtin_if(lenv *e, lval *a) {
LASSERT_NUM("if", a, 3);
LASSERT_TYPE("if", a, 0, LVAL_NUM);
LASSERT_TYPE("if", a, 1, LVAL_QEXPR);
LASSERT_TYPE("if", a, 2, LVAL_QEXPR); /* Mark Both Expressions as evaluable */
lval *x;
a->cell[1]->type = LVAL_SEXPR;
a->cell[2]->type = LVAL_SEXPR; if (a->cell[0]->num) {
/* If condition is true evaluate first expression */
x = lval_eval(e, lval_pop(a, 1));
} else {
/* Otherwise evaluate second expression */
x = lval_eval(e, lval_pop(a, 2));
} /* Delete argument list and return */
lval_del(a);
return x;
} void lenv_add_builtins(lenv *e) {
/* Variable Functions */
lenv_add_builtin(e, "def", builtin_def);
lenv_add_builtin(e, "\\", builtin_lambda);
lenv_add_builtin(e, "=", builtin_put); /* List Functions */
lenv_add_builtin(e, "list", builtin_list);
lenv_add_builtin(e, "head", builtin_head);
lenv_add_builtin(e, "tail", builtin_tail);
lenv_add_builtin(e, "eval", builtin_eval);
lenv_add_builtin(e, "join", builtin_join); /* Mathematical Functions */
lenv_add_builtin(e, "+", builtin_add);
lenv_add_builtin(e, "-", builtin_sub);
lenv_add_builtin(e, "*", builtin_mul);
lenv_add_builtin(e, "/", builtin_div); /* Comparison Functions */
lenv_add_builtin(e, "if", builtin_if);
lenv_add_builtin(e, "==", builtin_eq);
lenv_add_builtin(e, "!=", builtin_ne);
lenv_add_builtin(e, ">", builtin_gt);
lenv_add_builtin(e, "<", builtin_lt);
lenv_add_builtin(e, ">=", builtin_ge);
lenv_add_builtin(e, "<=", builtin_le);
} 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 *Qexpr = mpc_new("qexpr");
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 : /[a-zA-Z0-9_+\\-*\\/\\\\=<>!&]+/ ; \
sexpr : '(' <expr>* ')' ; \
qexpr : '{' <expr>* '}' ; \
expr : <number> | <symbol> | <sexpr> | <qexpr> ; \
lispy : /^/ <expr>* /$/ ; \
",
Number, Symbol, Sexpr, Qexpr, Expr, Lispy); puts("Lispy Version 0.1");
puts("Press Ctrl+c to Exit\n"); lenv *e = lenv_new();
lenv_add_builtins(e); while(1) { char *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(e, 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); } lenv_del(e); /* Undefine and delete our parsers */
mpc_cleanup(6, Number, Symbol, Sexpr, Qexpr, Expr, Lispy); return 0;
}

编译:

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

运行:

$ ./parsing
Lispy Version 0.1
Press Ctrl+c to Exit lispy> > 10 5
1
lispy> <= 88 5
0
lispy> == 5 6
0
lispy> == 5 {}
0
lispy> == 1 1
1
lispy> != {} 56
1
lispy> == {1 2 3 {5 6}} {1 2 3 {5 6}}
1
lispy> def {x y} 100 200
()
lispy> if (== x y) {+ x y} {- x y}
-100

用 C 语言开发一门编程语言 — 条件分支的更多相关文章

  1. Python 编程语言要掌握的技能之一:编写条件分支代码的技巧

    Python 里的分支代码 Python 支持最为常见的 if/else 条件分支语句,不过它缺少在其他编程语言中常见的 switch/case 语句. 除此之外,Python 还为 for/whil ...

  2. C++条件分支结构

    一.对于近期学习知识点的摘要: 1. 从第一个.cpp文件谈起, #include<iostream> //头文件 using namespace std; //使用命名空间,namesp ...

  3. Java开发的基础条件:

    ------------Java开发的基础条件:Java相关的基础+对编程的自己的理解+调试代码+自己的坚持 一定要谦逊,不人云亦云,不去妄言某一门语言或技术好或坏!不是哪门技术有问题,而是(不会用才 ...

  4. Python 工匠:编写条件分支代码的技巧

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由鹅厂优文发表于云+社区专栏 作者:朱雷 | 腾讯IEG高级工程师 『Python 工匠』是什么? 我一直觉得编程某种意义是一门『手艺』 ...

  5. Python :编写条件分支代码的技巧

    『Python 工匠』是什么? 我一直觉得编程某种意义是一门『手艺』,因为优雅而高效的代码,就如同完美的手工艺品一样让人赏心悦目. 在雕琢代码的过程中,有大工程:比如应该用什么架构.哪种设计模式.也有 ...

  6. Python教程(2.7)——条件分支

    像其它语言一样,Python也有条件分支. 例如,输入用户年龄,可能需要判断是否成年,并做出不同反应.这就需要用到条件分支. if条件分支 if条件分支的一般格式如下: if condition: s ...

  7. 用Hyperledger Fabric(超级账本)来构建Java语言开发区块链的环境

    面向 Java 开发人员的链代码简介 您或许听说过区块链,但可能不确定它对 Java™ 开发人员有何用.本教程将帮助大家解惑.我将分步展示如何使用 Hyperledger Fabric v0.6 来构 ...

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

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

  9. C#语言开发规范-ching版

    拙劣之处请大家斧正,愚某虚心接受,如有雷同,不胜荣幸 C#语言开发规范 作者ching 1.  命名规范 a) 类 [规则1-1]使用Pascal规则命名类名,即首字母要大写. eg: Class T ...

  10. C语言开发中常见报错的解决方案

    C语言开发中常见报错的解决方案 整理来源于网络,侵权请通知删除.*禁止转载 ---- fatal error C1003: error count exceeds number; stopping c ...

随机推荐

  1. 知识汇总:查看linux服务器系统命令

    要查看Linux服务器的系统信息,你可以使用多种命令来获取不同类型的信息.以下是一些常 用的命令和它们的用途: uname - 显示基本的系统信息 uname -a:显示所有的系统信息,包括内核名称. ...

  2. Matplotlib绘图设置---坐标轴刻度和标签设置

    每个axes对象都有xaxis和yaxis属性,且xaxis和yaxis的每一个坐标轴都有主要刻度线/标签和次要刻度线/标签组成,标签位置通过一个Locator对象设置,标签格式通过一个Formatt ...

  3. mysql系列之杂谈(一)

    从刚开始工作到现在,除了实习的时候在国企用过oracle,毕业之后陪伴我的数据库一直都是mysql,而由于mysql的开源特性,也让成为无数公司的宠儿,越走越远. 我们在刚开始使用mysql时,会发现 ...

  4. C# Dictionary(数据字典)的基本用法

    C# Dictionary(数据字典)的基本用法 - Mr.Cat~ - 博客园 (cnblogs.com)   通常情况下,我们可以通过 int 类型的索引来从数组或者 List 集合中查询所需的数 ...

  5. Nacos 无法注册服务

    情况描述: Nacos服务搭建完成. 引入了Nacos的依赖. <!-- SpringCloud Alibaba 服务管理 --> <dependency> <group ...

  6. Android与STM32通信中巧妙解决中文乱码问题

    前言 网上一大堆都是要多种格式相互转换,并且要很大的字库文件,对于小应用工程,小容量的STM32芯片,额外多出这些开销会感到蛮不舒服的,而且绝大部分的乱码问题时发生在STM32这边,所以本文是从And ...

  7. 重新点亮shell————管道和重定向[二]

    前言 简单介绍一下管道和重定向. 正文 管道和信号一样,是进程通信的方式之一 管道符是"|",将前一个命令执行的结果传递给后面的命令. 比如说: ls -l | more 就是第一 ...

  8. redis 简单整理——redis 的有序集合基本结构和命令[六]

    前言 简单介绍一下redis的有序集合的基本结构和命令. 正文 有序集合相对于哈希.列表.集合来说会有一点点陌生,但既然叫有序 集合,那么它和集合必然有着联系,它保留了集合不能有重复成员的特性, 但不 ...

  9. 【Azure Developer】.Net 简单示例 "文字动图显示" Typing to SVG

    问题描述 看见一个有趣的页面,可以把输入的文字信息,直接输出SVG图片,还可以实现动图模式. 示例URL:  https://readme-typing-svg.demolab.com/?font=F ...

  10. SQL SERVER 数据库性能优化与管理从零基础到走两步系列(一)——性能计数器

    前辈大佬资料: 使用性能监视器找出SQLServer硬件瓶颈 在网络上苦苦流浪了近十几个小时,从百度到谷歌,从CSDN到博客园,从知乎到微信读书,看了无数本滥竽充数的书,读了无数篇夹生的技术文章,快下 ...