柚子/实现自解释源码

Created Sun, 10 Nov 2024 00:00:00 +0000 Modified Tue, 17 Dec 2024 05:03:42 +0000
By Yoyo 496 Words 2 min Edit

什么是自解释源码

我给它的定义是能够像 Bash 脚本、Python 脚本那样,可以直接执行的源码。我不喜欢解释型和松散类型的语言,更喜欢像 C/C++/Rust 这种编译型和强类型的语言。但是又羡慕 Python 那样可以直接运行的便利性。

原理

无论是 C/C++还是 Rust,它们都是编译型语言,总归要编译成机器码才能运行,我也没有能够手搓一套解释器的能力。所以我们需要 滥用 C、C++和 Rust 的预处理器,在源码的前面部分 注入 一些 shell 脚本,在执行时先运行 shell 脚本,再编译并运行 C/C++/Rust 的源码。

实现

C、C++

# 在 C、C++中并不是有效注释,但所幸我们可以通过 #if 0注释 掉开头的 shell 脚本。而恰好 #在shell脚本中是有效的注释符 。

#if 0
# 获取文件名
file=`basename $0`
# 获取文件名(不带后缀)
exec=`echo $file | cut -d . -f 1`
out=/tmp/tmp/$exec

# 编译并运行
gcc -o $out $file && ./$out
#endif

#include <stdio.h>
int main()
{
    printf("Hello, World!\n");
    return 0;
}

Rust

Rust 的预处理器是 rustc,它会将 #!/ 开头的行视为注释,所以我们可以在代码开头加入 #!/bin/sh 来告诉 shell 这是一个 shell 脚本。
#![allow(unused_attributes)] 是一个编译器属性,用于在 Rust 编译过程中禁止发出某些警告。这里的作用是它刚好是 # 开头的,在 shell 中被视为注释,而后面的 /* */ 在 rust 中被视为多行注释,所以不会报错。

#!/bin/sh
#![allow(unused_attributes)] /*
# 输出文件目录
out=/tmp/tmp
# 编译
rustc "$0" -o ${out}
# 运行
exec ${out} $@ || exit $?
#*/

fn main() {
    println!("Hello, World!");
}