当前位置: 首页 > 后端技术 > Java

用java写一个lisp解释器(6if是用宏实现的)

时间:2023-04-01 16:04:36 Java

先看看自定义函数ifbefore((defineif(lambda(pthen_velse_v)((or(andpcar)cdr)(consthen_velse_v))))(if(quote(>12))(3)(cons24)))看起来没问题,但是因为它会在涉及输入参数绑定值时评估传入的s-expression值,if涉及到递归,就会有问题,所以我们需要加引号,但是这样也会带来新的问题如果我们要延迟对传入的s表达式的求值,就需要修改每个表达式,无疑增加了很多脑力负担,如何减轻负担?我们的期望我们的表达式变成了这样:(if(>12)3(cons24))首先,我们想到的是将上面的表达式展开后的预期效果是什么?((or(and(>12)car)cdr)(cons3(cons24)))这时候我们可以使用apply函数来实现。在开始之前,我们首先转换if函数then_velse_v)))))这时候我们可以使用apply函数来调用,apply函数的作用是函数调用时,当最后一个参数是列表或者引用时会被压平如下(applyif(`((>12)3(cons24))))=>((or(and(>12)car)cdr)(cons3(cons24)))等同于(if(`(>12))(`3)(`(cons24)))=>((or(and(>12)car)cdr)(cons3(cons24)))他们都会输出我们期望的化合物列表。这个时候我们只需要再调用一次,比如(apply(applyif(`((>12)3(cons24)))))细心的你已经发现我们在定义if函数的时候用了很多list函数和quote函数。好像很麻烦。这里我们使用一种简化的写法(defineif(lambda(pthen_velse_v)(`((or(and,pcar)cdr)(cons,then,else)))))如果引用类型中有,符号的下一个元素将被评估,否则将被忽略核心是两个应用。首先,执行第二个apply会得到表达式。第一个将获取表达式并调用它以获得最终值。(apply(applyif(`((>12)3(cons24)))))核心就是把这段的pattern写成这样(apply(applyexp(`exp)))然后我们封装代码如下:privatestaticObjectdefineMacro(ApplyArgsapplyArgs){Conscdr=applyArgs.getExp();validateTrue(applyArgs.getEnv().noContains(cdr.carSymbols()),"不要重复定义"+cdr.carSymbols());FunctionapplyFun=(applyArgs1)->{Conscons=markList(Symbols.of("apply"),cdr.cdr().car(),markQuote(applyArgs1.getExp().list().toArray()));返回applyArgs1.eval(markList(Symbols.of("apply"),applyArgs1.eval(cons)));};applyArgs.getEnv().setEnv(cdr.carSymbols(),applyFun);returnnull;}然后注册reg("define-macro",FunManager::defineMacro);这时候我们的if函数就可以通过宏来实现了。宏可以理解为生成列表的工具,里面用到了apply。引用模板渲染等技术。(define-macroif(lambda(pthen.else)(`(((or(and,pcar)cdr)(cons,then,else)))))这时候我们if的pthen_velse_v可以不再用引号修饰。(if(>12)3(cons24))现在,我们回到了起点。总结一下,实现了两个版本的宏,apply函数已经改变了4-5次,引用改了不下3次,前后大概用了三天时间,抽象封装一层就够了(不过这里不止一层)。