
编程语言语言运行时编译器【免费下载链接】wrenThe Wren Programming Language. Wren is a small, fast, class-based concurrent scripting language.项目地址https://gitcode.com/gh_mirrors/wr/wren点击查看免费下载Wren 是一门小型、快速、基于类的并发脚本语言其错误处理机制别具一格它没有传统语言的 try/catch 异常体系而是将错误划分为语法错误、运行时错误与失败failure三个层次并借助轻量级协程 Fiber 完成运行时错误的捕获与传播。本文以官方文档 doc/site/error-handling.markdown 为骨架结合 src/vm/wren_core.wren、src/vm/wren_vm.c 的底层实现与 test/core/fiber 下的大量测试用例完整讲解 Wren 中每一类错误的含义、表现与处理方式。读完本文你将掌握 Wren 的报错格式、Fiber.try()/Fiber.abort()/fiber.error的完整用法以及用返回值表达失败的 Wren 惯用设计。错误的三种层次Wren 官方文档将错误明确划分为三个层次理解这个分层是掌握 Wren 错误处理的前提层次触发时机典型来源处理手段语法错误代码被读取/编译时不符合语法、作用域冲突无法被程序捕获编译期直接报错运行时错误代码执行时方法不存在、参数类型错误等中止当前 Fiber可被try()捕获失败failure代码执行时解析失败等可预见场景通过返回值如null表达其中失败并非语言层面的报错而是一种约定俗成的设计模式本文最后一节会详细展开。语法错误编译期即时报错语法错误是开发者最先遇到的错误类型包括代码不符合语言文法的硬语法错误例如1 * 2Wren 在尝试读取代码时立即检测到这类问题并给出友好的错误信息[main line 1] Error on *: Unexpected token for expression.错误信息采用统一格式[模块名 行号] Error on 出错位置: 具体原因让开发者能直接定位到出错的文件、行号与 token。作用域级别的语义错误一些更具语义色彩的编译期错误也归入此类比如使用未定义的变量或在同一作用域内重复声明同名变量。以下代码var a once var a twiceWren 会报告[main line 2] Error on a: Top-level variable is already defined.值得强调的是Wren 会在执行任何代码之前完成全部编译期检查。也就是说只要程序开始运行你就可以确定代码中不存在语法或作用域层面的错误。与某些脚本语言边执行边发现的做法不同Wren 倾向于在尽可能早的阶段暴露问题。从实现角度看这类检查发生在编译器src/vm/wren_compiler.c的变量解析阶段源码先被完整编译为字节码编译过程中同步进行作用域与符号检查只有编译成功才会进入解释执行阶段。这也解释了为何 Wren 敢于承诺运行即无语法错误。运行时错误VM 无法执行的操作即使消除了所有编译期错误程序仍可能在运行时遭遇无法静态检测的问题这类错误统称为运行时错误。它们大多由 VM 自身抛出源于代码尝试执行 VM 无法完成的操作。最常见的两种运行时错误方法不存在method not found。当你对某个对象调用一个方法而该对象的类及其全部父类都没有定义此方法时Wren 无能为力class Foo { construct new() {} } var foo Foo.new() foo.someRandomMethod运行结果Foo does not implement method someRandomMethod.参数类型错误wrong argument type。例如列表的索引必须是数字传入其他类型就是错误var list [a, b, c] list[1]程序随即退出并输出Subscript must be a number or a range. [main line 2] in (script)第二行便是调用栈追踪它指出错误发生在哪一行、位于哪个方法调用链中。除了上述两种运行时错误还包括列表越界、调用函数时参数个数错误等。错误即中止不带病运行与某些语言在运行时错误后继续执行不同Wren 一旦发生运行时错误就停止执行当前代码。因为运行时错误意味着代码存在 bugWren 希望立即引起你的注意为此它会打印完整的调用栈标明错误发生位置以及所有引出该错误的调用链。设计哲学是宁可立即中止也不要让错误悄悄污染后续逻辑。底层机制runtimeError 沿 Fiber 链传播在 VM 实现中运行时错误的传播路径非常清晰。当解释器遇到错误时会调用 src/vm/wren_vm.c 中的runtimeError()函数。该函数从当前 Fiber 出发沿着caller指针逐级向上遍历整个 Fiber 调用链沿途每一个 Fiber 都会被赋予同一个错误current-error error如果某个 Fiber 是被try方式调用的状态为FIBER_TRY则停止传播让try方法返回错误消息否则继续向上解链current-caller NULL若一直走到链头都无人接住错误则调用wrenDebugPrintStackTrace(vm)打印调用栈并停止整个 VM。这段代码同时印证了本文后续要讲的错误会沿着 Fiber 调用链寻找try这一行为。用 Fiber 处理运行时错误try() 与 error为了保持语言简洁Wren 没有异常处理机制exception handling而是利用 Fiber 来承担错误处理职责Fiber 的基础概念见 doc/site/concurrency.markdown。当一个运行时错误发生时当前 Fiber 被中止正常情况下Wren 会沿着调用链逐级中止所有调用过它的 Fiber一路追溯到主 Fiber然后退出 VM。try()把错误接住但如果你用try方法运行一个 Fiber情况就不同了如果被调用的 Fiber 内部发生运行时错误错误会被捕获并由try方法以字符串形式返回。var fiber Fiber.new { 123.badMethod } var error fiber.try() System.print(Caught error: error)输出Caught error: Num does not implement method badMethod.注意此处123的类在 Wren 中显示为Num这与 src/vm/wren_core.wren 中class Num {}的类名定义一致。被中止的 Fiber 之后不能再被使用但其他 Fiber 可以照常运行。try方法还支持传入参数用于给首次启动的 Fiber 函数传值函数可带 0 或 1 个参数var fiber Fiber.new {|value| value.badMethod } var error fiber.try(just a string) System.print(Caught error: error)关于try的边界行为test/core/fiber 中的测试给出了精确约定fiber.try()成功捕获错误后再次try()同一个 Fiber会报Cannot try an aborted fiber.见 try_error.wren对已经正常运行完毕的 Fiber 调用try()会报Cannot try a finished fiber.见 try_done.wren被try捕获错误后再对它call()会报Cannot call an aborted fiber.见 call_error.wren。error 属性从 Fiber 对象上读取错误当一个 Fiber 因运行时错误被中止后你还可以通过该 Fiber 的error属性取得错误消息。延续上面的例子System.print(fiber.error)同样输出Num does not implement method badMethod.在 test/core/fiber/error.wren 中可以看到error属性的完整语义未出错前fiber.error为nulltry()捕获错误之后fiber.error才被填充为错误消息。也就是说error是中止后才有效的信息来源与try()的返回值互补。沿调用链传播try 可以隔层接住错误如果你有一整条 Fiber 调用链且某处发生运行时错误错误会沿链逐级向上寻找try调用。因此即使错误产生于被try调用的 Fiber 所间接调用的更深层 Fiber 中try同样能捕获它。test/core/fiber/try_through_call.wren 用一个五层 Fiber 链完整演示了这一行为var fiber1 Fiber.new { Fiber.abort(Abort!) } var fiber2 Fiber.new { fiber1.call() } var fiber3 Fiber.new { fiber2.call() } var fiber4 Fiber.new { fiber3.try() } var fiber5 Fiber.new { fiber4.call() } fiber5.call()测试断言显示fiber1、fiber2、fiber3都被同一错误中止fiber.error均为Abort!而fiber4执行try的 Fiber和fiber5的error为null程序在try处成功接住错误后继续执行。这正是 src/vm/wren_vm.c 中沿途所有 Fiber 都被中止直到命中FIBER_TRY状态这一逻辑的运行时体现。主动制造运行时错误Fiber.abort()大多数运行时错误由 VM 内部产生但你也可以主动制造运行时错误——调用Fiber类上的静态方法abort()Fiber.abort(Something bad happened)必须传入错误消息且必须是字符串当传入的消息为null时不会引发任何运行时错误。两个边界情况都有对应的测试佐证test/core/fiber/abort.wrenFiber.abort(Error message.)后fiber.try()返回Error message.fiber.isDone为truefiber.error也填充为同一消息——可见abort引发的错误与 VM 原生运行时错误走的是同一条捕获通路test/core/fiber/abort_null.wrenFiber.abort(null)不会中止 Fiber后续代码继续执行打印get hereFiber 甚至可以照常yieldfiber.isDone为false、fiber.error为null。有趣的是测试 abort_not_string.wren 显示Fiber.abort(123)传入非字符串时错误消息会原样保留为数字123并被try()返回。虽然官方文档明确要求必须传入字符串但从实现与测试看传入非字符串并不会触发类型错误而是把该值当作错误消息传递。abort()在核心库中被大量用作参数校验手段。查看 src/vm/wren_core.wren 可以看到典型模式skip(count) { if (!(count is Num) || !count.isInteger || count 0) { Fiber.abort(Count must be a non-negative integer.) } ... }类似的参数校验散落在String.splitDelimiter must be a non-empty string.、List.sortComparer must be a function.、Sequence.reduceCant reduce an empty sequence.等核心方法中可选模块 src/optional/wren_opt_random.wren如 Sequence cannot be empty.与 src/optional/wren_opt_meta.wren如 Module name must be a string.也沿用了同一约定。这为自定义库实现参数校验提供了标准范式。失败Failure用返回值表达可预期的错误最后一个层次是最高层的失败。以上所有错误都指向bug——代码本身写错了但有些错误意味着代码只是因为不可预见的原因没能完成任务这类情况本文称之为 failure失败。考虑一个从用户输入解析数字的程序很多字符串并非合法数字解析必然可能失败。程序唯一能阻止这种失败的方式是在解析前先校验字符串但校验一个字符串是不是数字与解析它几乎是同一件事——你无法在不实现解析逻辑的情况下预先判断成败。对于这种失败必然会发生、且程序确实想要处理它的场景Fiber 与try()过于粗粒度并不适用。约定成功返回结果失败返回 nullWren 的答案是这些操作通过返回某种错误指示来表达失败。例如一个解析数字的方法可以在成功时返回数字、在解析失败时返回null。由于 Wren 是动态类型语言一个方法返回不同类型值的做法既简单又自然。这一模式在核心库中有最直接的官方范例——Num.fromString()。查看测试 test/core/number/from_string.wrenSystem.print(Num.fromString(123) 123) // expect: true System.print(Num.fromString( 12 ) 12) // expect: true // Test some non-number literals and ensure they return null. System.print(Num.fromString(test1) null) // expect: true System.print(Num.fromString() null) // expect: true System.print(Num.fromString(prefix1.2) null) // expect: true System.print(Num.fromString(1.2suffix) null) // expect: true成功时返回数字、失败时返回null——调用方用一行判断即可区分两种结果无需依赖异常机制。这也解释了为什么Num.fromString对非法输入不抛错它属于失败语义而非bug语义。相比之下如果传入的参数本身类型不对如Num.fromString(1)那属于 bug会触发运行时错误Argument must be a string.见 from_string_not_string.wren如果数字字面量超出范围则报Number literal is too large.见 from_string_too_large.wren。三者对比可以清晰看出 Wren 对失败与错误的边界划分。与 Fiber 捕获的取舍在 doc/site/modularity.markdown 所倡导的模块化实践中这一模式同样适用跨模块传递可能失败的结果时返回null或其他哨兵值比抛错更轻量、更易组合。而try()/abort()更适合处理异常情况下才发生的真正错误。实践总结Wren 错误处理的四条规则编译期错误语法、作用域不可捕获Wren 在执行前完成全部静态检查运行即无语法错误运行时错误即中止不带病运行用 Fiber 中止 调用栈追踪暴露 bug捕获用try()查询用errorfiber.try()返回错误消息字符串被中止的 Fiber 不可复用fiber.error在出错后填充错误会沿 Fiber 链传播直到命中try主动报错用Fiber.abort(message)消息须为字符串null表示不报错可预期的失败用返回值表达如Num.fromString成功返回数字、失败返回null不要滥用 Fiber 异常机制。完整的 Fiber API 细节call、yield、transfer、transferError等可进一步参阅 doc/site/modules/core/fiber.markdown其测试覆盖集中在 test/core/fiber官方指南中的姊妹篇 并发与 Fiber 与 模块化 也值得一并阅读。赞分享编程语言语言运行时编译器【免费下载链接】wrenThe Wren Programming Language. Wren is a small, fast, class-based concurrent scripting language.项目地址https://gitcode.com/gh_mirrors/wr/wren点击查看免费下载相关推荐掌握cel-go错误处理从语法错误到运行时异常的完整指南掌握cel go错误处理从语法错误到运行时异常的完整指南 cel go作为一款快速、可移植且非图灵完备的表达式评估引擎在实际应用中难免会遇到各种错误。本文将JavaQuestPlayer终极指南5分钟开启你的QSP游戏开发之旅JavaQuestPlayer终极指南5分钟开启你的QSP游戏开发之旅 想象一下你是一名游戏创作者脑海中构思着精彩的文字冒险游戏但面对复杂的开发工具和编游戏开发桌面应用Lepton AI日志收集与分析使用ELK栈监控服务运行状态Lepton AI日志收集与分析使用ELK栈监控服务运行状态 Lepton AI作为一款Pythonic的AI服务构建框架提供了强大的日志系统帮助开发者监控上一篇【亲测免费】 Picocrypt小巧而强大的文件加密工具下一篇luci-app-mosdns支持哪些DNS协议一文读懂DoT/DoH/DoQ全解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考