跳到主要内容

2 篇博文 含有标签「Yuescript」

查看所有标签

给 Moonscript 重写编译器的故事

· 阅读需 13 分钟
李瑾
Dora SSR 开发者。

tokyo moon

Moonscript 是一门极为小众的编程语言

  Moonscript 是一门编译成为 Lua 代码并在 Lua 虚拟机运行的编程语言。主要语法和特性借鉴于 Coffeescript。这门语言的优势在于语言简练、具有较强表达力的同时能保留尽可能高的可读性,在表达力和可读性之间取得一个比较好的平衡点。有较为克制不那么 corner case 的语法糖。用来写一些经常变化的业务逻辑非常省力,实践下来编写相同的游戏开发类的业务逻辑,用 Moonscript 比写原生的 Lua 能缩减到 1/2,甚至到 1/3 的代码量,更少的代码对减少 Bug 的产生或是问题排查也有很多帮助。另外这门语言还有一个重要特点,据 Discord 群里的老哥说,全世界范围内的活跃用户可能只有 20 多人。还有一个更重要的特点就是这是一门 Sailor Moon Themed 的编程语言。

logo里暗藏情怀

logo里暗藏情怀

开源和免费难以为继

  Moonscript 的作者因使用这门语言开发了一些商业网站,如销售独立游戏的 itch.io,以及分享绘画作品的网站 streak.club。说为了保持这门语言的稳定性,从 2017 年开始暂缓了项目的维护,不再增加新特性,甚至 issue fix 也不积极了。当然生活不易,作者还开了 github sponsor 希望他开发的开源软件能获得更多支持。我们也没理由要求别人一直免费给大家做贡献。

不爽就自己重写

  当然,作为 Moonscript 粉丝的我对这样的状况是不能够接受的。原版 Moonscript 编译器是用 Moonscript 写的,核心是用 C 语言实现的 PEG 文法解析库解析 Moonscript 代码生成 AST 结构传到 Lua 环境中,再由 Moonscript 编译生成的 Lua 代码操作 AST 结构把Moonscript 代码翻译成 Lua 代码。这个方案还是挺浪费资源,C 语言实现的 parser 很高效,但是后续回到 Lua 环境创建大量 Lua 的数据结构,增加资源消耗和 Lua GC 时间其实并无必要,在数千行 Moonscript 代码的项目中,如果不做预编译,在运行时才动态加载 Moonscript 代码,会明显感觉到程序的长时间卡顿。另外用动态类型的语言来操作需要严格检查数据类型的 AST 结构,完全是动态语言开发的弱项。

  当然说得再多不如拿出代码有意义,所以我没有继承已有的 code base,而是直接用第二喜欢的编程语言 C++ 进行了完全的重写(第一喜欢的就是 Moonscript)。并在重写的同时顺便修复了各类作者未解决的问题,并引入一些缺失了几年的其它语言都已经用烂的编程特性。

  详见项目:Yuescript

Transpilers For Lua 和 PEG 文法

  不过说到编译生成另一门编程语言的编译器,现在更准确的叫法是叫做转译器(transpiler)。Lua 语言因为语言设计的简洁,实现了只用做一次遍历的递归下降解析器,本身的编译时间极快。又因为大家各自编程喜好的不同,很多人就打起了开发其它编程语言转译成 Lua 的转译器,扩展 Lua 语言的开发能力的想法。除了 Moonscript 外现在已有各类从 Javascript、Typescript、Lisp、C、Python、Go 和C# 等等各种语言转译成 Lua 的实现。另外也有各种给 Lua 语言加上静态类型检查的想法。

  说到底还是因为大家的审美和个性化的需求的日益增长,以及硬件的发展解放了算力,让大家都不再纠结于程序文法复杂度以及程序编译期间各种开销的问题,解放了大家研发新编程语言的生产力。就如 Python 之父曾因为历史原因,在三十年前为了确保 parser 的执行效率,降低文本解析阶段的内存消耗,实现了 LL(1) 的文法,只要一个 token 的 look ahead 就足够完成文法解析。后来算力和内存提供的条件已经大大超过以前,便开始考虑采用对程序开发更加友好的PEG文法,通过使用足够多的缓存支持无限多次的文法匹配回溯(backtrace),提升解析器开发的灵活性,以增强未来 Python 语言演化的能力。

  原版 Moonscript 也是用 PEG 文法实现的。一般实现 PEG 支持的程序库都是提供通过 parser combinator 的形式编写解析器程序。我在 C++ 中先尝试了使用 meta programming 实现的在编译期构建 parser 的黑魔法库 PEGTL,结果未获得任何开发上的增益,调试困难就不用说了,如果文法有复杂度太高,或者左递归,直接编译期提示生成函数嵌套超过最大值,左递归报错是应该,正常的嵌套太深就只能尝试调大编译参数看能不能过编译了。好不容易调好了 parser 生成一看好几个M的 binary size,才发现这个库比起应用更多的只是炫技。最终我找到了 parserlib(https://github.com/axilmar/parserlib )。运行时生成 parser,带有 AST 生成还提供一定程度的左递归文法自动解决功能,看了代码关于如何在 parse 的过程中创建 AST 的部分很精妙,就决定是它了。

用 C++ 编写 Transpiler 的优势

  有的人形容 Moonscript 是 Lua 上的一套宏系统,的确没错,很多 Moon 的语法其实就是加了能简写代码的 Lua 语法糖。Moon 转译到 Lua 只要做三步操作,第一步是解析代码生成 Moon AST,第二步是把 Moon AST 转换成对应的 Lua AST,最后一步把 Lua AST 转换成代码文本。用 C++ 操作 AST 结构的优势就是可以在编译期以及运行时以比较小的代价完成对 AST 结构的类型检查。

  并且到 C++17 版本 C++ 语言增加了很多新的编程特性,编程的表达力和抽象能力也已经变得更加强大。原版用 Moonscript 编写的 Moonscript 编译器用了近 5K 行代码,现在用 C++17 实现相同的业务功能也只用 5K 行多一点的代码量。Discord 群里另一位老哥也说他在 C++98 的年代写相同规模的项目预估代码量是不低于上万行的,C++17 已经带来了他没想到的语言进步。当然表达力、抽象力是增强了,用了一些黑魔法特性以后,生成的 binary size 也增大了很多。

  通过 C++ 的 meta programing 的能力,我们可以放心地写这样的代码:

// 检查ast节点是Exp或ExpList
if (item.is<Exp_t, ExpList_t>()) {
...
}

// 检查某节点开始是否匹配某个ast结构分支
// 并获取最后一个匹配的节点
if (auto variable =
node->getByPath<ChainValue_t, Callable_t, Variable_t>()) {
auto varName = toString(variable->name);
...
}

// 用switch语句分别处理不同的ast结构
// id作为编译期常量由编译器自动生成,无需人手工编号
switch (node->getId()) {
case id<While_t>(): {
auto while_ = static_cast<While_t*>(node);
...
break;
}
case id<For_t>(): {
auto for_ = static_cast<For_t*>(node);
...
break;
}
...
}

  通过利用模板泛型参数的功能,可以将一些参数类型的检查放到编译期。如:

node->getByPath<ChainValue_t, Callable_t>()

  就要比类似

node->getByPath("ChainValue", "Callable")

  这样的写法少很多潜在的风险,同时进行了编译期参数检查,运行时类型匹配的两重功能,动态类型的语言是很难取代这样的优势的。在这些设施的帮助下,不用额外设计特别复杂的检查机制,错误地操作 AST 结构就会产生明确的编译报错或是运行时报错,让 C++ 写 transpiler 无比爽快和省心。

方言中的方言——Yuescript 语言的生产应用

  Yuescript 在创作之初其实有一直绑定了一个开源的游戏引擎项目 Dora SSR (https://dora-ssr.net ),可以说 Yuescript 的一个重要的创作目标,就是为了让支持 Lua 语言的 Dora SSR 开源游戏引擎用上升级版的 Moonscript 语言。结合 Dora SSR 的 Web IDE,我们还给 Yuescript 语言稍微增加了一点点代码编辑器上的类型推导和代码补全的辅助能力。

  我特别喜欢在参加一些 Game Jam 活动的时候,和策划伙伴一顿头脑风暴,然后掏出 Dora SSR 引擎和 Yuescript 就是一阵不考虑太多编程设计且“不计后果”的糊玩法编码。当然编程设计也不能说是完全没有,结合 Dora SSR 游戏引擎的消息系统机制 + Yuescript 函数式风格编程的写法。Game Jam 里埋头花几个钟头写 1k 行代码左右,在一个函数内把游戏 demo 写完也是没有问题的。在 Dora SSR 的仓库 里也可以看到我们过往糊的各种 Game Jam 小游戏的 Yuescript 源码。

  所以对 Lua 的方言 Moonscript 的方言 Yuescript 语言的可用性,至少也是在 Dora SSR 项目中有过不少代码有在做验证啦。

从编译器、游戏引擎到游戏掌机——我是这样做独立游戏的

· 阅读需 11 分钟
李瑾
Dora SSR 开发者。

引言

  自己开发制作游戏是一个儿时起就有的梦,特别是长时间接触魔兽争霸3世界编辑器后,我对游戏引擎和开发工具也有着特别的兴趣。学生时代接触编程以后,梦的外延开始扩散,不满足于使用各式编程语言做开发,开始维护一门自己喜欢的写游戏业务逻辑的编程语言 Yuescript,因为学习图形学和作为学习项目重写 Cocos2d-x 有了 Dora SSR 游戏引擎。工作后因为对游戏掌机的喜爱,开始与伙伴合作研发自由开放的可编程游戏掌机设备——吉祥机,实现自己游戏梦终极的 Digital Freedom。

游戏脚本语言的乐趣与挑战

编程语言游乐场!

编程语言游乐场!

  各式新的编程语言的学习是充满乐趣,对不同语言工具的接触也会带来不同的编程理念和程序设计思想。对于复杂多变的游戏玩法的脚本编程(Scripting)我也形成了自己编程偏好,即使用一门尽可能简洁和表达力强的编程语言来编写容易变化的业务逻辑,可以转译为 Lua 语言执行的 Yuescript 就是满足这个需求的产物。后来随着使用自己的 Dora SSR 游戏引擎项目有了更多的游戏开发体验,又为 Dora SSR 游戏引擎引入了 Teal(为 Lua 语言添加静态类型检查能力的语言),Typescript(进一步增强代码编辑器提示和代码检查的语言),JSX 和 XML(提供描述性代码进行组件化开发的语言)等等。每一种脚本语言都能在特定的游戏开发场景发挥优势,并通过转译到最终运行的同样的Lua语言进行无缝的互通调用。不只是基于 Lua 语言的扩展,Dora SSR 游戏引擎还在尝试通过 WASM 虚拟机来支持更加多样的可以用做游戏脚本编程的语言,如 Rust 和准备支持的 C++ 和 Go 等,兼顾性能与引擎的运行时扩展能力。

游戏引擎的创新之路

随时随地用任何设备制作游戏!

随时随地用任何设备制作游戏!

  说到游戏引擎大家总是想到高性能高质量的图形渲染,搭建复杂的游戏场景。实际上作为独立游戏开发者,或是游戏制作的爱好者,并不是人人都有条件追求3A游戏的制作(钞能力)。我认为很多 2D 游戏或是 2D 混合 3D 效果的游戏也能表达展现十分有创意和独特的游戏作品。而且能运行自己制作游戏的终端最好是不受限制的,再进一步,也许能用于开发游戏的终端也可以是不受限制的。所以就有了 Dora SSR 游戏引擎的项目目标,在尽可能多的设备上为游戏开发爱好者提供便捷易用的环境甚至是游戏开发 IDE。一直以来游戏开发这件也成为了我的个人生活体验的一部分。哪怕只有碎片化的时间和手边随机可用作游戏开发和运行的设备,我也想有空就利用起来碎片化地写两行游戏代码,或是调试一个游戏功能,并把它变成了一种比较随性惬意的休闲活动。

  所以 Dora SSR 搭建了通过游戏引擎运行时内置用于游戏开发的 Web IDE 服务器,可以通过其它方便做输入的设备通过 Web 浏览器做访问,并实现直接在任意的终端运行设备上直接编写运行和调试游戏开发的代码。同时获得代码编辑器可视提示服务、以及使用其它游戏开发和资源管理的可视化工具。目前 Dora SSR 在努力之下已具备了在 Windows、macOS、iOS、Android、多个 Linux 发行版上进行游戏开发的能力。

向着自由开放的游戏掌机梦想迈进

开源开放?软件和硬件全都要!

开源开放?软件和硬件全都要!

  到此我觉得对游戏开发能力的自由和开放体验的追求还远不到尽头。作为喜好各式掌机的老玩家,在体验了诸多国产开源掌机的商业产品后,我感觉深深的不满足。用掌上游戏机玩游戏目前还是在卷硬件参数和外观设计来提供体验的差别,而我期待的掌机并不只是玩游戏上的体验,还应该是一个可以用来自由的开发、运行甚至发行自制游戏的设备。很多掌机厂商都有自己的商业化模式和获得盈利的闭环,所以不会允许硬件设备获得太多可编程定制的能力。于是和同样对硬件发烧的伙伴一起研究构建完全自由开放的掌机设备。并尽可能提供包括机器的计算核心、外设和外观均可进行模块化的定制和更换的能力(使科技不再以换壳为本)于是又有了“吉祥机”的项目。

吉祥机 + Dora SSR 游戏引擎

吉祥机 + Dora SSR 游戏引擎

回到制作游戏的初心

社区在做的开源独立游戏项目《灵数奇缘》

社区在做的开源独立游戏项目《灵数奇缘》

  所以折腾了半天我的游戏到底做出来了没有呢?答案当然是做了,但没完全做出来啦。在生成式 AI 大模型进入彻底火爆前夕的2020年我们就想象了一个关于未来的 AI 的游戏故事,人的物质需求已经得到完全满足,生下来的目的只剩下了通过进行游戏娱乐,并通过采集过程数据给AI生产有创造力和展现智能的训练数据。人生的价值都是由未来的银行通过评估人的游戏活动所创造的智能数据的质量和价值,来进行货币分配而评定的。人类贡献的数据训练出的 AI 会帮助人完成一切的物质生产、到人类个体的养育和社会管理的工作。在这样的背景下,人还会有什么样的故事。最后游戏输出的价值观就是人生来就应该是改造世界的主体,而不是只会适应一切现状的被改造的客体。也呼应了我和我的伙伴们一直在追寻的东西,想要不被与生而来的一切所定义,就去靠自己的主动创造去重新定义一切。

  如果对我们在做的编程语言、游戏引擎、游戏掌机或是游戏项目感兴趣,欢迎 Star 我们的仓库或是进入我们的 Q 群聊聊。目前的项目都还在比较早起的阶段,但是会互相整合和验证迭代,关注我们也可以及时看到项目是怎么做的以及我们的进展。

  最后我们诚挚邀请每一位对游戏开发有热情的朋友加入我们,无论是贡献代码、提供意见还是分享我们的项目,您的每一份努力都能帮助我们共同实现游戏开发自由的梦想。

项目地址