lisp 获取横断面数据_【Lisp系列】手动递归

69d786031aad748f0daa65a1ececdd36.png

目前,cliblisp已经实现了Y-combinator,如题图所示。

Y-combinator的具体细节和推导,在Y Combinator - bajdcc - 博客园中有介绍。

实施手动递归的原因

继前一篇完成lisp的大体功能之后,计划将其作为一种胶水语言,并向模拟多线程方向前进,这就势必要将lisp的运行设置成低粒度的、可控的。

原计划将其的eval部分设计成指令式,不过指令的设计有点复杂,因此就退一步,将其改造为半指令式,也就是手动实现栈式调用,实际上这不是以指令为最小单位,而是以方法为最小单位。

    cval *cvm::run(ast_node *root) {
        mem.save_stack();
        auto val = conv(root, global_env); // 将AST转换为cval树
        cval *ret = nullptr;
        call(eval, val, global_env, &ret); // eval(val, global_env)
        // 自己实现调用栈
        while (!eval_stack.empty()) {
            auto frame = eval_stack.back();
            auto r = frame->fun(this, frame); // 以方法为最小单位
            if (r == s_ret) {
                eval_mem.free(frame);
                eval_stack.pop_back();
            }
        }
        assert(ret);
        eval_stack.clear(); // 清空方法栈
        eval_mem.clear(); // 清空栈帧
        eval_tmp.clear(); // 清空变量
        return ret;
    }

原先的代码只需做一些微小的改进,改造成状态机模式。

手动递归的数据结构

每次调用压入一个栈帧。

栈帧:

    struct cframe {
        csub fun; // 调用的函数接口
        cval *val, *env, **ret; // cval树,当前调用环境,返回值地址
        void *arg; // 可选参数
    };

将所有的函数接口选用统一的签名。

通用函数接口:

    enum status_t {
        s_ret,
        s_call,
        s_error,
    };
 
using csub_t = status_t (*)(cvm *vm, cframe *frame);

返回值说明函数是否是返回还是递归调用,以后会使用s_error来实现异常处理。

参数为cvm和cframe,即this指针和栈帧。

例:手动调归后的eval函数

status_t builtins::call_eval(cvm *vm, cframe *frame) {
        auto &val = frame->val;//从栈帧获取参数
        auto &env = frame->env;
        if (val->val._v.count > 2)
            vm->error("eval not support more than one args");
        auto op = VM_OP(val);
        struct tmp_bag {
            bool qexp;
            cval *ret;
        }; // 栈上保存的临时变量
        if (frame->arg == nullptr) {
            auto tmp = vm->eval_tmp.alloc<tmp_bag>();//申请临时变量
            memset(tmp, 0, sizeof(tmp_bag));
            tmp->qexp = op->type == ast_qexpr;
            frame->arg = tmp;
            if (tmp->qexp)
                op->type = ast_sexpr;
            return vm->call(cvm::eval, op, env, &tmp->ret);//压栈
        } else {
            auto tmp = (tmp_bag *) frame->arg;
            if (tmp->qexp)
                op->type = ast_qexpr;
            auto ret = tmp->ret;
            vm->eval_tmp.free(tmp);//删除变量
            VM_RET(ret);//返回结果
        }
    }

后续计划

手动递归将eval方法变成了可控的,但条件是每个方法必须不能是阻塞的。

后续将clib2d加入cliblisp做控制台功能,就可以在OpenGL的idle方法中调用cliblisp函数了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值