#define _GNU_SOURCE //asprintf #include #include #include #include #include "inter_builtins.h" #include "interpreter.h" #include "util.h" extern InterRet interpret(InterState *is,const AST *ast); InterRet builtin_do(InterState *is,int nargs,AST **args){ assert(args); InterRet ir; for(int i=0;itype){ case AST_LIST: cond=args[0]->li.len!=0; break; case AST_LAMBDAARG: assert(false); case AST_WORD: assert(false); case AST_NUMBER: cond=args[0]->nu.num!=0; break; case AST_STRING: cond=args[0]->st.len!=0; break; default: return ir_err_c("Invalid node type in condition of builtin 'if'"); } if(cond)return interpret(is,args[1]); else if(nargs==3)return interpret(is,args[2]); else return ir_ast(ast_list(0,malloc(1,AST*))); } InterRet builtin_print(InterState *is,int nargs,AST **args){ (void)is; assert(args); for(int i=0;i0)putchar(' '); if(args[i]->type==AST_STRING){ fwrite(args[i]->st.str,1,args[i]->st.len,stdout); } else { char *s=ast_stringify(args[i]); printf("%s",s); free(s); } } putchar('\n'); return ir_ast(ast_list(0,malloc(1,AST*))); } #define BUILTIN_LOGICAL_OP(op,name) \ InterRet builtin_##name(InterState *is,int nargs,AST **args){ \ (void)is; \ assert(args); \ if(nargs!=2||args[0]->type!=AST_NUMBER||args[1]->type!=AST_NUMBER){ \ return ir_err_c("Logical operator '" #op "' expects two number arguments"); \ } \ return ir_ast(ast_number(args[0]->nu.num op args[1]->nu.num)); \ } BUILTIN_LOGICAL_OP(==,equals); BUILTIN_LOGICAL_OP(>,greater); BUILTIN_LOGICAL_OP(>=,greaterequals); BUILTIN_LOGICAL_OP(<,less); BUILTIN_LOGICAL_OP(<=,lessequals); #undef BUILTIN_LOGICAL_OP #define BUILTIN_ARITH_OP(op,name,defval,expr) \ InterRet builtin_##name(InterState *is,int nargs,AST **args){ \ (void)is; \ assert(args); \ if(nargs==0)return ir_ast(ast_number(defval)); \ for(int i=0;itype!=AST_NUMBER){ \ return ir_err_c("Non-number argument passed to builtin '" #op "'"); \ } \ } \ double res=args[0]->nu.num; \ for(int i=1;inu.num; \ res=expr; \ } \ return ir_ast(ast_number(res)); \ } BUILTIN_ARITH_OP(+,sum,0,res+n); BUILTIN_ARITH_OP(-,difference,0,res-n); BUILTIN_ARITH_OP(*,product,1,res*n); BUILTIN_ARITH_OP(/,quotient,1,res/n); BUILTIN_ARITH_OP(%,remainder,1,floatmod(res,n)); #undef BUILTIN_ARITH_OP static void convert_arguments(AST *ast,int nargs,const char **args){ switch(ast->type){ case AST_LIST: for(int i=0;ili.len;i++)convert_arguments(ast->li.nodes[i],nargs,args); break; case AST_LAMBDA: break; case AST_WORD:{ int i; for(i=0;iwo.word,args[i])==0)break; } if(i==nargs)break; free(ast->wo.word); ast->type=AST_LAMBDAARG; ast->ar.idx=i; break; } case AST_QUOTED: convert_arguments(ast->qu.ast,nargs,args); break; case AST_LAMBDAARG: case AST_NUMBER: case AST_STRING: case AST_SYMBOL: break; default: assert(false); } } InterRet builtin_define(InterState *is,int nargs,AST **args){ if(nargs!=3)return ir_err_c("Invalid number of arguments to builtin 'define'"); if(args[0]->type!=AST_SYMBOL){ return ir_err_c("First argument to builtin 'define' should be symbol"); } if(args[1]->type!=AST_LIST){ return ir_err_c("Second argument to builtin 'define' should be quoted list"); } const AST *arglist=args[1]; for(int i=0;ili.len;i++){ if(arglist->li.nodes[i]->type!=AST_WORD){ return ir_err_c("Second argument to builtin 'define' should contain words"); } } const char *funcname=args[0]->sy.name; AST *body=args[2]; const char *argnames[arglist->li.len]; for(int i=0;ili.len;i++)argnames[i]=arglist->li.nodes[i]->wo.word; convert_arguments(body,arglist->li.len,argnames); inter_assign(is,funcname,ast_lambda(NULL,ast_copy(body))); return ir_ast(ast_list(0,malloc(1,AST*))); }