Lisp1 eval

-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*/

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值