C宏元编程:编译期LISP解释器(三)A级原语

目录⇣
(一)总体思路
(二)列表操作
(三)A级原语
外部链接⇣
这是一个超级神奇的项目CSP Git Repo
纯粹用C宏写的LISP解释器!
(目前还没有完成,最重要的lambda已经实现了,cond暂时还有问题嵌套会出错x)
(想拉一些小伙伴一起玩一起烧脑呀!可惜似乎人类玩家直接看源码大概率大脑爆栈,于是尝试写了一些文章之类。。原始wiki可以戳这里
CSP Wiki
//最近有些忙没时间来管这个项目结果开发进度完全停滞了呢QAQ。。
//Contributor! Contributor! Contributor!

这次我们来看一些复杂一点的Interpreter A原语实现。

ATOM原语

首先贴相关代码:

#define _DESTROY_E(...) __VA_ARGS__
#define _DESTROY_N(...)
#define _DESTROY_BOTTOM _DESTROY_E(_DESTROY_N,_DESTROY_N)
#define _ODESTROY(car,k,...) k(_n((car)) ODESTROYY)
#define _ODESTROYY(car,k,...) k(_n((car)) ODESTROY)
#define ODESTROY(...) _ODESTROY( __VA_ARGS__,_DESTROY_E)
#define ODESTROYY(...) _ODESTROYY( __VA_ARGS__,_DESTROY_E)

#define _bsem )
#define _T(x) T
#define __ATOM(x) ODESTROY x (_DESTROY_BOTTOM)
#define RODESTROY T _n()(
#define KATOMT(...) (T)
#define KATOMR () _n _n()(
#define ATOM(x) _e(CAT(KATOM,_e(CAT(R,__ATOM(x)) _bsem)))

前面这一大堆东西目的很明显(_DESTROY_E _DESTROY_N _DESTROY_BOTTOM _ODESTROYY _ODESTROYY ODESTROY ODESTROYY)(还不明白的同学可以复习一下之前发的文档)。_ODESTROYY _ODESTROYY目的就是交替展开出零宏来吃掉形如(a)(b)(c)...的一连串列表/原子,而ODESTROY ODESTROYY利用零点构造技术,这样只需要采用ODESTROY (a)(b)(c)... (_DESTROY_BOTTOM)就可以消灭掉“尾巴”,干净地吃掉整个列表。至于为什么这里又定义了一遍单位宏和零宏,主要是为了避免命名空间污染。因为CPP对于两个不同名的单位宏/零宏处理行为是不一样的(例如e1(e2(...))能在一次扫描中展开,而e1(e1(...))则可能需要2次,特别是内层e1是在前一次扫描过程中展开出来的情况下),为了防止系统大起来之后各种weird,干脆能采用不同名单位宏/零宏的都约定重新命名好了。

后面的就是实现ATOM的主要部分。这里利用了ODESTROY的这个性质:它能吃掉(a)(b)(c)..这样一组带括号的东西,但却对a这样不带括号的项没有任何作用。

所以思路很明确了:把输入的括号“脱掉”再用ODESTROY作用,原子/列表的行为就会出现差异。

然后就是老套路:粘贴宏CAT邪术。

__ATOM(a) //=>ODESTROY a (_DESTROY_BOTTOM)
__ATOM((a)) //=>ODESTROY (a) (_DESTROY_BOTTOM)=> 没有啦!
CAT(KATOM,_e(CAT(R,__ATOM(a)) _bsem)) 
//=>CAT(KATOM,RODESTROY a (_DESTROY_BOTTOM) _bsem)
//=>CAT(KATOM,T (a (_DESTROY_BOTTOM)))
//=>KATOMT(a (_DESTROY_BOTTOM)) => (T) 胜利!
CAT(KATOM,_e(CAT(R,__ATOM((a))) _bsem)) 
//=>CAT(KATOM,R _bsem)
//=>KATOMR _bsem
//=>() _n() =>() 胜利!

这样ATOM原语就实现辣!

AND函数

现在来分析一下Interpreter A中实现的这个非原语宏:AND函数。

(Root of LISP中提到了这个函数所以我就实现了。。不过在Interpreter B eval中没有用到,感兴趣的小伙伴可以把它作为Interpreter B函数来整合一下)

#define _AND(a) INTERNAL_EVAL((COND((NOT(a)(COND_EAT))((T)(_AND_Y)))))
#define _AND_Y(a) INTERNAL_EVAL((COND((NOT(a)(COND_EAT))((T)(_AND)))))

#define andtransCOND_EAT ()
#define andtransCOND_EATY ()
#define andtrans_AND_Y (T)
#define andtrans_AND (T)

#define $and(a) CAT(andtrans,_AND a)

这里用到了COND原语,它的实现思路和ATOM类似,但可能复杂一些,感兴趣的可以自行阅读源码(误

另外COND乃万恶之源,它不会跳过假分支的求值过程。。所以AND对于非法输入也不会非常稳定,需要保证其子句都是安全的。

COND的用法:

COND((()(expr1))((T)(expr2))(()(expr3))...) => (expr1) (即展开成第一个条件为真的子句)

这里的INTERNAL_EVAL是用于执行Interpreter A上类列表写法的一个工具宏。

$and工作原理:

$and((T)()(T)) // => CAT(andtrans, _AND(T)()(T))
// 如果 _AND 后是(T) 那么它会展开出_AND_Y,以此进行交替迭代,直到碰到非真的原子为止。
//=>CAT(andtrans, _AND_Y()(T) )
// _AND_Y碰到“假”了!现在它展开出 COND_EAT, 用你熟悉的方式吃掉后面的所有项
// 吃完后剩下 COND_EAT 或 COND_EATY尾巴
//=>CAT(andtrans,COND_EATY) => () CAT宏邪术!
// 如果从头到尾都是真元素,则会剩下 _AND 或 _AND_Y 尾巴
//最后留下andtrans_AND或andtrans_AND_Y => (T)

P.S.

和已有wiki同构的部分已经写完了。。接下来就得两边一起写了。。然后解释器核心还没开发完呢。所以%滑稽%

文章被以下专栏收录