⚠️ 别在家尝试:今天为你的 JS 应用添加 Brainf**k 支持!
Source: Dev.to
亲爱的 dev.to 管理员们,请耐心看完这篇。
这篇文章会出现一些脏话,正如标题所暗示的那样。不过这些脏话都是包名和实际的技术术语,并无冒犯之意。我会在合适的地方对大多数进行马赛克处理。
又来了。这些东西大概不该在生产环境中使用,因为它们要么完全毫无用处,要么会让维护它的人抓狂,或者让所有人都大喊“不要”“请别这么做”。
这次是关于深奥的编程语言
具体来说,是 Brainf**k,一种 Ook! 的方言,可惜它根本无法被猩猩理解。而且,不,我拒绝相信其实是相反的情况。
Brainf**k 因其难以阅读而臭名昭著。主要原因是它的指令都是单字符,能够将栈指针移动 1 位,或将指针指向的值增加或减少 1,并且只要指针当前指向的栈元素大于 0 就会循环。
我最近写了一篇关于 使用和创建 unplugins 的文章,在文中提到了加入 Brainf**k 支持,并自言自语地想到了:
于是我们来了。如果你曾经想把一半的应用程序用一种繁琐、曲折、难以阅读的方式来构建,现在就是时机!而且你甚至可以把它开源,让其他人也这么做!
创建 Unplugin 模板
很直接。我们还需要另一个依赖叫 “hirnf**k”,它是一个把 Brainfk 代码转译为 JS 的库。对于不懂德语的朋友,这个包名的含义是 “brainfk”,就酱。
# Oops – typo!
$ px degit unplugin/unplugin-starter unplugin-bf
bash: px: command not found
哎呀,打错字了。是的,这已经开局不佳。
$ npx degit unplugin/unplugin-starter unplugin-bf
$ cd unplugin-bf/ && npm i -s hirnfick
好多了。
这会创建一个 unplugin 并安装我们的源到源编译器。我们将在 unplugin 的 src/index.ts 文件中实现编译。
查看 hirnf**k 的文档
它包含了一个可以稍作修改的 ESM 示例:
import hirnfick from "https://jspm.dev/hirnfick";
const helloWorldBF =
"++++++++[>++++[>++>+++>+++>++>+>->>+[>.>---." +
"+++++++..+++.>>.>+.>++.";
try {
const helloJs = hirnfick.compileToJsWeb(helloWorldBF);
const runHello = new Function(`${helloJs} return main().output.trim();`);
console.log(runHello());
} catch (err) {
console.error(`Error: ${err.message}`);
}
完美吧?它创建了一个可执行函数,甚至已经包含了一个 Brainf**k 的 “Hello World”。
实现全部功能
这出乎意料地简单。幸运的是,unplugin starter 已经为我们提供了大部分样板代码;我们只需要填补空白即可。
Starter 代码
import type { UnpluginFactory } from "unplugin";
import type { Options } from "./types";
import { createUnplugin } from "unplugin";
export const unpluginFactory: UnpluginFactory = (options) => ({
name: "unplugin-starter",
transformInclude(id) {
return id.endsWith("main.ts");
},
transform(code) {
return code.replace("__UNPLUGIN__", `Hello Unplugin! ${options}`);
},
});
export const unplugin = /* #__PURE__ */ createUnplugin(unpluginFactory);
export default unplugin;
调整样板代码
- 更改文件过滤器 – 我们想要匹配
.bf文件,而不是main.ts。 - 重命名 unplugin – 改为
unplugin-bf。 - 移除未使用的选项。
// ...
export const unpluginFactory: UnpluginFactory = () => ({
name: "unplugin-bf",
transformInclude(id) {
return id.endsWith(".bf");
},
transform(code) {
return code;
},
});
添加实际的编译逻辑
现在我们把 hirnf**k 的示例复制粘贴到 transform 函数中,并稍作调整:
import type { UnpluginFactory } from "unplugin";
import type { Options } from "./types";
import hirnfick from "hirnfick";
import { createUnplugin } from "unplugin";
export const unpluginFactory: UnpluginFactory = () => ({
name: "unplugin-bf",
transformInclude(id) {
return id.endsWith(".bf");
},
transform(code) {
const transformed = hirnfick.compileToJsWeb(code);
const wrapped = `export default new Function(\`${transformed} return main().output.trim();\`)`;
return wrapped;
},
});
export const unplugin = /* #__PURE__ */ createUnplugin(unpluginFactory);
export default unplugin;
信不信由你,我们已经完成了。现在我们的 JS 应用通过这个可爱的 unplugin 已经支持 Brainf**k 了。
工作原理
当 Vite/Rollup(或任何支持 unplugins 的打包工具)处理文件时,它会:
- 使用
transformInclude检查文件名。只有以.bf结尾的文件会被传递进去。 - 将文件内容(
code)传递给transform函数。 - 使用
hirnfick.compileToJsWeb将 Brainfk 源码编译成 JavaScript**。 - 将生成的 JS 包装在
new Function中,该函数返回 Brainf**k 程序的去除首尾空白后的输出。 - 将该函数作为模块的默认导出,因此你可以像使用其他 JS 模块一样导入并运行它。
就这么简单——一个小巧、独立的桥梁,把晦涩的语言连接到现代 JavaScript。祝使用愉快!
Brainf**k → JavaScript 编译工作原理
unplugin 获取 .bf 文件的内容(一个字符串),并将其包装在一个导出默认函数的 JavaScript 模块中。
该函数运行编译后的 Brainf**k 代码,并返回 . 命令本应打印的内容,所有空白字符都会被去除。
最小示例
Brainf**k 源码:
+-.
编译后的 JavaScript 如下:
export default new Function(`let position = 0;
const cells = [0];
let output = '';
function putchar() {
output += String.fromCharCode(cells[position]);
}
function main() {
if (cells[position] 0) {
cells[position] -= 1;
}
putchar(String.fromCharCode(cells[position]));
return { cells, output };
}
return main().output.trim();`)
你可以看到每条 Brainf**k 指令是如何映射到 JavaScript 的:
| Brainf**k | 对应的 JavaScript |
|---|---|
+ | cells[position] += 1; |
- | cells[position] -= 1; |
. | putchar(String.fromCharCode(cells[position])); |
尝试一下
unplugin‑starter 仓库已经包含了一个 playground,帮你把一切都准备好。
-
创建一个 Brainfk 文件**
hello-world.bf++++++++[>++++[>++>+++>+++>++>+>->>+[>.>---.+++++++..+++.>>.>+.>++. -
在
main.ts中导入并运行它import HelloWorld from './hello-world.bf' document.body.innerHTML = HelloWorld()VSCode 会报错,因为它不知道 .bf 文件会导出 JavaScript,但 unplugin 会在构建时处理它。(未来的 VSCode 插件可以消除这些警告。)
-
启动 playground
cd playground npm i && npm run dev这会启动一个开发服务器(通常在
http://localhost:3000)。在浏览器中打开它会看到输出:
成功了!
⚠️ 警告: 请 不要 在生产代码中使用 Brainf**k。它仅仅是一个有趣的实验。
扩展编译器
- Input support via the
,command. - Source maps (good luck!).
- Ook! language support.
感谢!
我希望你和我写这篇文章时一样享受。如果你喜欢,请留下一个 ❤️。
我在业余时间写技术文章,爱喝咖啡。如果你想支持我的工作,你可以:
你也可以在 Bluesky 🦋 关注我。

