写点什么

函数 到 AST

作者:Miracle
  • 2025-09-24
    四川
  • 本文字数:1968 字

    阅读完需:约 6 分钟

函数 到 AST

大家可以看看到。我们已经成功的把一个合法的或者叫做一个看起来像那么回事儿的。没有类型的代码。我这边特意强调一下是没有类型的代码。可以编译成 AST 的形式,也就是语句的列表。那么这个接下来会有两个方向。一个是我们把这个这个语句数组 把它编译成直接送给机器解释执行。这就是我们所谓的脚本语言。解释执行的虚拟机,当然了。直接解释执行是很慢的,因为 statement 是一个很复杂的表达式。机器会花很大的时间去识别和理解每一条语句。包括这个语句的类型,从里面找到合适的变量去处理。还有分配合适的寄存器,存储位置 装载变量等


另外一种直接把它编译到目标代码,比如说我们的 x86 体系。但 x86 和 arm 没意思,因为这个事情已经无数的编译器都干了,我们真的想挑战的是我们编译成一个很少有能做到的脚本语言,基本上没有作为 target 的一个语言,比如说我们把它编译成 GPU 上。可以运行的 Shader。这个就很牛了。我们用 mandblot 集合来举例子吧。大家知道 madlebrot 集合是数学上就 分形的一个概念。他的基本算法是在一个复平面上。计算一个所谓的叫逃逸系数的一个东西。他的算法很简单,我下面把他的那个伪代码贴在下面。

fn escape(x, y, max_iter) {    let iter = 0;    let zx = 0.0;    let zy = 0.0;    while iter < max_iter {        let zx2 = zx * zx;        let zy2 = zy * zy;        if zx2 + zy2 > 4.0 {            break;        }        let tmp = zx2 - zy2 + x; // 实部        zy = 2.0 * zx * zy + y; // 虚部        zx = tmp;        iter += 1;    }    return iter;}
复制代码


大家可以看到。这一段是纯算法的说明。要注意的是我特地把这个带往里面所有的类型都去掉了。也就是说我们这是一个纯动态类型的算法。基本上我我们刚才那个语法分析的一个 parser 已经已经完全可以解析这段代码,但是要想解析这个,刚才的 parser 缺少了一个东西就是函数的定义。所以我们要在那个刚才的代码里把这个函数的定义加上去 Fn { name: SmolStr, args: Vec<SmolStr>, body: Vec<Stmt> },上面就是这个函数在刚才那个语句枚举里面的定义。

    Fn { name: SmolStr, args: Vec<SmolStr>, body: Vec<Stmt> },
复制代码


我们用下面的代码来解析函数的定义。


        let args = super::ident_parser().padded_by(rust_ws()).clone().separated_by(just(',').padded_by(rust_ws()))            .allow_trailing().collect::<Vec<SmolStr>>().delimited_by(                just('(').padded_by(rust_ws()),                just(')').padded_by(rust_ws())            ).labelled("parameter list");
let fn_decl = just("fn").padded().ignore_then(super::ident_parser().padded_by(rust_ws()) .then(args.or_not().map(|opt| opt.unwrap_or_default()))) .then(block.clone()).map_with(|((name, args), body), _| { if let Stmt::Block(body_stmts) = body { Stmt::Fn { name, args, body: body_stmts, } } else { unreachable!("Block should always be Stmt::Block") } }).labelled("function declaration").boxed();
复制代码


下面是测试输出的代码

fn main() -> Result<()> {    println!("sizeof {}", std::mem::size_of::<Dynamic>());    let ast = parser::program_parser().parse(MANDELBROT);    if ast.has_errors() {        for e in ast.errors() {            println!("{:?}", e);            for exp in e.expected() {                println!("  expected: {}", exp); // 这里会输出 "identifier"            }        }    }    if let Some(mut stmts) = ast.into_output() {        if let Stmt::Fn{name, args, body} = stmts.pop().unwrap() {            println!("{:?}", name);            println!("{:?}", args);            for st in body {                println!("{:?}", st);            }        }    }    Ok(())}
复制代码


输出的结果是这样的



我们可以直接的执行这些语句了,大家可以看到。这个 statement 其实是直接对这个 pattern 赋值,这个 pattern 是个简单的标识符,比如说 x y z。然后我们把它赋值到这个符号之后就可以执行语句了,按照这个语义。他是个 pattern 的 condition。然后根据这个如果条件满足就执行他里面的 statement 列表里面的一条条 语句执行下去。然后根据结果,然后再循环。里面如果有 break 就 break 到 循环的外面。对吧?然后这里面还有各种 赋值,然后就是理论上来说这个这个已经是这种抽象代码 是 AST 抽象语法树已经是可以直包括也可以直接翻译成为这个对应的字节码或者是 编译成为 LLVM 中间代码 形成一个完整的语言工具链

用户头像

Miracle

关注

三十年资深码农 2019-10-25 加入

还未添加个人简介

评论

发布
暂无评论
函数 到 AST_Miracle_InfoQ写作社区