-Lisp
- 通过正则表达式和mpc文件构建语法树
- 构建终端缓冲区,提供命令行输入
- 对各文件进行拆分
- 通过自定义运算数据结构体lval对用户命令进行解析,得出运算结果,返回并打印结果
#include "mpc.h"
#ifdef _WIN32
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
#include <readline/readline.h>
#include <readline/history.h>
#endif
typedef struct {
int type;
long num;
int err;
} lval;
typedef struct mpc_ast_t{
char* tag;//regex{exp,number,op,lipsy}
char* contents;//
mpc_state_t state;
int children_num;
struct mpc_ast_t** children;//array of children pointer
//mpc_ast_t* -> children[i] ->contents(0:children_num)
} mpc_ast_t;
enum { LVAL_NUM, LVAL_ERR };
enum { LERR_DIV_ZERO, LERR_BAD_OP, LERR_BAD_NUM };
lval lval_num(long x){
lval v;
v.type=LVAL_NUM;
v.num=x;
return v;
}
lval lval_err(int err){
lval v;
v.type=LVAL_ERR;
v.err=err;
return v;
}
//print lval
void lval_print(lval x){
switch(v.type){
case LVAL_NUM:printf("%li",v.num);break;
case LVAL_ERR:
//check type of error
if(v.err==LERR_DIV_ZERO){
printf("error:Divided by zero");
}
if(v.err==LERR_BAD_OP)
printf("error:Invalid Operator");
if(v.err==LERR_BAD_NUM){
printf("error:Invalid Number");
}
break;
}
}
void lval_println(lval v,(void*)lval_print(lval x)){
lval_print(v);
putchar('\n');
}
//strtol 函数进行字符串到数字的转换
lval eval_op(lval x,char *op,lval y){
//num is invalid and return it
if(x.type==LVAL_ERR)
return x;
if(y.type==LVAL_ERR)
return y;
//do evaluation
switch(op){
case "+":return lval_num(x.num+y.num);
case "-":return lval_num(x.num-y.num);
case "*":return lval_num(x.num*y.num);
case "/"return (y.num==0)?lval_err(LERR_DIV_ZERO):lval_num(x.num/y.num);
default:return lval_err(LERR_BAD_OP);
}
}
/* atoi 将一个 char* 转为 long 型
strcmp 输入两个字符串,如果相等返回0
strstr 输入两个字符串,如果第二个是第一个的子串,
就返回首次出现的下标,如果第二个没有出现在第一字符串内就输出0。 */
/*
t->content:number(char *)
t->children[1]:op(char *)
t->children[2]:number(char *)
t->children[3]:exp(char *)
t->children[k]:number(char *)
*/
lval eval(mpc_ast_t *t){
if(strstr(t->tag,"number")){
long errno=0;
long x=strtol(t->content,NULL,10);//???
return errno==ERANGE?lval_err(LERR_BAD_NUM):lval_num(x);//????
}
char *op=t->children[1];
lval x=eval(t->children[2]);
int i=3;
while(strstr(t->children[3]->tag,"expr"){
x=eval_op(lval(x),op,eval(t->children[i]));//
i++;
}
return x;
}
int main(int argc, char** argv) {
mpc_parser_t* Number = mpc_new("number");
mpc_parser_t* Operator = mpc_new("operator");
mpc_parser_t* Expr = mpc_new("expr");
mpc_parser_t* Lispy = mpc_new("lispy");
mpca_lang(MPCA_LANG_DEFAULT,
" \
number : /-?[0-9]+/ ; \
operator : '+' | '-' | '*' | '/' ; \
expr : <number> | '(' <operator> <expr>+ ')' ; \
lispy : /^/ <operator> <expr>+ /$/ ; \
",
Number, Operator, Expr, Lispy);
puts("Lispy Version 0.3");
puts("Press Ctrl+c to Exit\n");
while (1) {
char* input = readline("lispy> ");
add_history(input);
mpc_result_t r;
if (mpc_parse("<stdin>", input, Lispy, &r)) {
long result = eval(r.output);
printf("%li\n", result);
mpc_ast_delete(r.output);
} else {
mpc_err_print(r.error);
mpc_err_delete(r.error);
}
free(input);
}
mpc_cleanup(4, Number, Operator, Expr, Lispy);
return 0;
}
//%li 表示 long 类型
/* 在C语言中,一旦声明为寄存器变量,由于可能会保存到寄存器中,编译器是不允许对其取地址(&)的。
volatile 的作用 是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
*/
/*
char * fgets ( char * str, int num, FILE * stream );
从stream里读num-1个字符作为字符串str的内容,直至读到换行符,但换行符会被读入字符串中
stream可以是stdin
char * gets ( char * str )
从stdin,也就是终端标准输入读入一个字符串,直至读到换行符,换行符不会读入字符串中
readline 和 fgets 一样,从命令行读取一行输入,并且允许用户使用左右箭头进行编辑
add_history 可以纪录下我们之前输入过的命令,并使用上下箭头来获取。
*/
/* 预处理器也是一个程序,它在编译之前运行。
它有很多作用。而我们之前就已经悄悄地用过预处理器了。
任何以井号 # 开头的语句都是一个预处理命令。
预处理的另一个作用是检测当前的代码在哪个操作系统中运行,从而来产生平台相关的代码。
*/
/* atoi(const char*) trans char* to long
strstr(const char *s1,const char *s2) KMP*/