- 编程语言
- 编译器
- 云原生
- 语言运行时
【免费下载链接】wing
A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡
Wing 是一门面向云计算的编程语言,其for..in循环是处理数组、集合(Set)与数值区间(range)的统一迭代语法,在 preflight(基础设施定义阶段)与 inflight(运行时阶段)中均可使用。本文以仓库中的 for_loop.test.w 编译快照 为骨架,结合 tests/valid/for_loop.test.w 原始用例、语言参考文档 以及wingc编译器的类型检查与代码生成源码,完整还原 Wing 循环语法的语义、边界行为,并逐段解读其面向 AWS(tf-aws)的 Terraform 部署产物与运行时 JavaScript 代码,帮助你彻底理解"写一段循环,最终会变成什么"。
一、快照文件是什么:hangar 测试框架的编译产物记录
tools/hangar/__snapshots__/test_corpus/valid/目录下的快照文件,是 Wing 官方测试框架hangar在编译/运行合法用例后自动生成的"黄金文件"(golden snapshot)。快照文件名遵循{用例名}.test.w_{编译目标}.md的约定:
for_loop.test.w_compile_tf-aws.md:记录用例在tf-aws(Terraform + AWS)目标下的完整编译产物;- 同目录下的
for_loop.test.w_test_sim.md:记录同一用例在sim(本地模拟器)目标下的运行输出。
以for_loop.test.w_compile_tf-aws.md为例,其正文依次包含四个章节:
## inflight.$Closure1-1.cjs—— 闭包$Closure1(即new cloud.Function(inflight () => {...})的处理器)的 inflight JavaScript 代码;## inflight.Foo-1.cjs—— 类Foo中inflight hello()方法的 inflight JavaScript 代码;## main.tf.json—— 生成的 Terraform JSON 清单,描述 AWS 上要创建的全部基础设施资源;## preflight.cjs—— preflight 阶段的完整 JavaScript 代码(循环逻辑逐条翻译为$helpers.range(...)与for...of)。
这个快照是理解"Wing 源码 → 编译产物"映射关系的第一手材料,也是本文所有分析的起点。
二、Wing for..in 语言语义速览
Wing 语言参考在 「2.6 for」 一节明确了循环的核心语义:
for..in语句用于遍历数组(array)、集合(set)或数值区间(range);- 区间默认是"包含起始值、不包含结束值"(inclusive of the start, exclusive of the end),即
0..5遍历 0、1、2、3、4; - 循环变量(loop invariant)隐式可重新赋值(
var),无需(也不能)额外声明; - 使用
..=语法表示闭区间(含结束值)。
官方 by-example 教程 给出了最直观的入门示例:
// a standard for loop(左闭右开区间) for item in 1..3 { log(item); // 输出 1、2 } // for-in with arrays let arr = [1, 2, 3]; for item in arr { log(item); // 输出 1、2、3 } // break a loop let items = Set<num>[1, 2, 3]; for item in items { if item == 1 { break; } log(item); } // continue the next iteration of the loop for item in 1..10 { if item % 2 == 0 { continue; } log(item); // 输出 1、3、5、7、9 }三、从测试用例反推完整语法语义
for_loop.test.w 是本文对应快照的源程序,它系统性地覆盖了for..in的绝大多数场景。逐段拆解如下。
3.1 数组与集合的嵌套遍历
let words = ["wing", "lang", "dang"]; let uniqueNumbers = Set<num>[ 1, 2, 3 ]; for word in words { for number in uniqueNumbers { assert(number > 0); log("{word}: {number}"); } }要点:
words是数组,uniqueNumbers是Set<num>,两者都可用for..in直接遍历;- 支持多层嵌套循环,内层循环可引用外层循环变量(如字符串插值
"{word}: {number}"); assert(number > 0)与log(...)可以出现在循环体内任意位置。
3.2 break:提前终止内层循环
let var i = 0; for word in words { i = i + 1; let var preBreakHits = 0; let var postBreakHits = 0; for number in uniqueNumbers { assert(number > 0); log("{word}: {number}"); preBreakHits = preBreakHits + 1; if (number == 2) { break; } postBreakHits = postBreakHits + 1; } assert(preBreakHits == 2); assert(postBreakHits == 1); } assert(i == 3);这段用例精确刻画了break的行为:uniqueNumbers包含 1、2、3 三个元素,当number == 2时中断,因此每个内层循环体实际执行 2 次(preBreakHits == 2),而break之后的语句只执行 1 次(postBreakHits == 1);外层循环完整执行 3 轮(i == 3)。同时注意let var声明:Wing 默认变量不可重新赋值,循环内需要计数的变量必须用var修饰。
3.3 continue:跳过本轮剩余语句
let var j = 0; for word in words { j = j + 1; let var preContinueHits = 0; let var postContinueHits = 0; for number in uniqueNumbers { assert(number > 0); log("{word}: {number}"); preContinueHits = preContinueHits + 1; if (number > 0) { continue; } postContinueHits = postContinueHits + 1; } assert(preContinueHits == 3); assert(postContinueHits == 0); } assert(j == 3);条件number > 0恒为真,所以每一轮都continue,continue之后的语句一次都不会执行(postContinueHits == 0),但循环体整体仍完整执行 3 次(preContinueHits == 3)。
3.4 区间(range)的完整边界矩阵
测试用例如同一张"真值表",覆盖了区间在正向、反向、零长度、含端/不含端、负边界、变量与表达式边界下的全部行为:
| Wing 写法 | 语义 | 实际迭代序列(来自 sim 快照 stdout) |
|---|---|---|
0..0 | 左闭右开、起点=终点 | 无迭代("there's no value to iterate") |
0..=0 | 闭区间、起点=终点 | 0 |
0..2 | 左闭右开 | 0, 1 |
0..=2 | 闭区间 | 0, 1, 2 |
2..0 | 反向、开区间 | 2, 1 |
2..=0 | 反向、闭区间 | 2, 1, 0 |
0..-2 | 反向负边界、开区间 | 0, -1 |
0..=-2 | 反向负边界、闭区间 | 0, -1, -2 |
-2..0 | 正向负起点 | -2, -1 |
-2..=0 | 正向负起点、闭区间 | -2, -1, 0 |
0..z(z=2) | 变量做边界 | 0, 1 |
0..=z | 变量做边界、闭区间 | 0, 1, 2 |
z..0 | 变量做起始边界、反向 | 2, 1 |
0..(z*2) | 表达式做边界 | 0, 1, 2, 3 |
0..=(z*2) | 表达式做边界、闭区间 | 0, 1, 2, 3, 4 |
(z*2)..0 | 表达式做起始边界、反向 | 4, 3, 2, 1 |
这些断言揭示了两个关键结论:
- Wing 的区间天然支持反向迭代——
2..0会从 2 递减到 1,而不是像多数语言那样产生空区间; - 区间的起止值可以是任意数值表达式,包括负数、变量(
z)与算术表达式(z*2),且左右边界均支持..=闭区间变体。
对应的运行期证据可查看 for_loop.test.w 的 sim 运行快照,其stdout.log逐行印证了上表全部序列。
3.5 inflight 场景与类方法内的循环
用例末尾把循环放进了云资源与类方法中:
bring cloud; new cloud.Function(inflight () => { for x in 0..10 { assert(x <= 0); assert(x > 10); log("{x}"); } }); // for loop in a class class Foo { inflight hello() { for p in ["hello"] { log(p); } } }说明for..in在inflight 闭包(作为cloud.Function的处理器)和inflight 类方法内部同样一等公民,这也是快照中会出现inflight.$Closure1-1.cjs与inflight.Foo-1.cjs两份独立产物的原因。注意第 2 行"死循环"(assert(x <= 0)恒假)是刻意为之,用于验证区间在 inflight 上下文中的编译与断言机制。
四、编译器如何实现 for..in:wingc 源码级拆解
Wing 编译器(wingc)用 Rust 实现,for..in从源码到 JavaScript 的旅程可分为三个环节。
4.1 语法解析:区分..与..=
在 parser.rs 的build_loop_range_expression中,解析器通过语法树节点是否携带inclusive字段决定区间开闭:
let inclusive = if expression_node.child_by_field_name("inclusive").is_some() { Some(true) } else { Some(false) }; Ok(Expr::new( ExprKind::Range { start: Box::new(self.build_expression(...)?), inclusive: inclusive, end: Box::new(self.build_expression(...)?), }, ... ))即:0..2的inclusive为false,0..=2的inclusive为true,起止点则作为普通表达式递归解析,这解释了为何变量与算术表达式都能直接充当边界。AST 定义见 ast.rs 的ExprKind::Range { start, inclusive: Option<bool>, end }。
4.2 类型检查:只有可迭代类型能进入循环
在 type_check.rs 的type_check_for_loop中:
- 先对
iterable做类型检查,若类型不可迭代则报错Unable to iterate over "{type}"; - 遍历元素类型按 iterable 的具体类型推导:数组/集合取元素类型(
Type::Array(t) => *t等),Anything保留原类型; - 循环变量以自由变量(
make_free_variable)形式定义在新建的作用域环境中,因此它在循环体内天然可读写(呼应"loop invariant 隐式为 var"的语言参考描述)。
对应地,仓库中的 tests/invalid/for_loop.test.w 演示了错误路径:对cloud.Bucket执行for test in bucket,编译器在 invalid.ts.snap 快照 中同时给出两条诊断:
error: Reserved word --> for_loop.test.w:5:5 error: Unable to iterate over "Bucket" --> for_loop.test.w:5:13(第一条 "Reserved word" 由 parser.rs 的check_reserved_symbol触发——test是 Wing 保留字;第二条则由上文类型检查产生。)这组 invalid 快照恰好构成语义的"反例证据"。
4.3 代码生成:for..of + $helpers.range
在 jsify.rs 中,StmtKind::ForLoop被翻译为标准的for...of:
"for (const ", jsify_symbol(&iterator), " of ", self.jsify_expression(iterable, ctx), ") {"而区间表达式(ExprKind::Range)则被翻译为对运行时助手函数的调用(jsify.rs):
format!("{HELPERS_VAR}.range("), start, ",", end, ",", inclusive, ")"运行时助手位于 sdk 的 helpers.ts:
export function range(start: number, end: number, inclusive: boolean) { function* iterator() { let i = start; let limit = inclusive ? (end < start ? end - 1 : end + 1) : end; while (i < limit) yield i++; while (i > limit) yield i--; } return iterator(); }range是一个生成器函数,其实现正是"反向区间可行"的根源:
inclusive == false时limit = end,只按一个方向推进;inclusive == true时,根据end < start决定把limit向"多走一步"的方向调整(正向end + 1,反向end - 1),从而让闭区间同时覆盖正向与反向两种迭代方向;- 两个
while分别处理i < limit(递增)与i > limit(递减)两种情况,互斥生效。
assert助手同样定义在同文件(helpers.ts),断言失败时抛出assertion failed: <message>异常。
五、逐文件解读 tf-aws 编译产物
回到快照正文,逐一对照编译产物中每个文件与源程序的对应关系。
5.1 preflight.cjs:循环逻辑的"逐行直译"
preflight.cjs 是整体骨架,其关键映射如下:
- 数组与 Set 遍历直接翻译为
for (const word of words)/for (const number of uniqueNumbers); log("{word}: {number}")的字符串插值被编译为console.log(String.raw({ raw: ["", ": ", ""] }, word, number))——String.raw拆分静态片段与表达式,正是 jsify.rs 中插值字符串的处理;- 区间循环全部翻译为
for (const x of $helpers.range(0,2,false))形式,第三参即inclusive布尔值;表达式边界(z*2)与负边界(-2)原样保留为表达式参数; break/continue直译为 JavaScript 同名关键字,因此计数断言(preBreakHits == 2等)在产物中表现为$helpers.eq(...)调用;- 文件末尾通过
$PlatformManager.createApp(...)创建应用并调用$APP.synth(),触发 Terraform 合成。
5.2 inflight 文件:云函数与类方法的运行时闭包
inflight.$Closure1-1.cjs对应cloud.Function的处理器:async handle()内是for (const x of $helpers.range(0,10,false))的循环,印证"区间在 inflight 闭包内直接可用";inflight.Foo-1.cjs对应class Foo的inflight hello():for (const p of ["hello"])遍历字面量数组;- 两个闭包都通过
require("@winglang/sdk/lib/helpers")获取$helpers,说明运行时依赖由 SDK 提供;_liftMap(见 preflight.cjs 中get _liftMap())声明该闭包无需捕获任何外部变量,因此两个 inflight 文件的module.exports = function({ })参数列表为空。
5.3 main.tf.json:循环引发的 AWS 资源清单
main.tf.json 展示了一次new cloud.Function(...)在 tf-aws 目标下生成的完整资源集合:
| 资源类型 | 资源 ID | 作用 |
|---|---|---|
aws_s3_bucket | Code | 存放函数代码的 S3 桶(bucket_prefix: "code-c84a50b1-") |
aws_s3_object | Function_S3Object_C62A0C2D | 代码包对象(source/key为占位符<ASSET_SOURCE>/<ASSET_KEY>) |
aws_lambda_function | Function | Lambda 函数本体,runtime: "nodejs20.x"、architectures: ["arm64"]、memory_size: 1024、timeout: 60 |
aws_cloudwatch_log_group | Function_CloudwatchLogGroup_ABDCF4C4 | 日志组/aws/lambda/Function-c852aba6,retention_in_days: 30 |
aws_iam_role | Function_IamRole_678BE84C | 假定lambda.amazonaws.com的执行角色 |
aws_iam_role_policy | Function_IamRolePolicy_E3B26607 | 内联策略(此处为占位none:null) |
aws_iam_role_policy_attachment | Function_IamRolePolicyAttachment_CACE1358 | 附加AWSLambdaBasicExecutionRole托管策略 |
每个资源都带有//.metadata标注:path(构造树中的逻辑路径,如root/Default/Default/Function)与uniqueId(稳定唯一 ID)。函数环境变量注入WING_TARGET: "tf-aws"与WING_FUNCTION_NAME,handler: "index.handler"、publish: true,说明 Wing 的cloud.Function在 AWS 上以"代码入 S3 + Lambda 引用"的标准模式落地。注意:循环本身是语言级结构,不会在 Terraform 清单中产生任何额外资源——它只存在于函数/闭包的 JavaScript 实现中。
六、本地运行与验证方式
快照对应的运行结果可通过 Wing 自带的模拟器目标(sim)直接验证。参考 for_loop.test.w 的 sim 快照,其stdout.log从上到下依次输出:嵌套遍历的 9 行word: number、break 变体(每轮 2 行)与 continue 变体(每轮 3 行)的日志、随后是 16 段---分隔的区间迭代日志,最终以pass ─ for_loop.test.wsim (no tests)结束。
若想在自己的机器上复现,可创建main.w并写入本文 第 2 节 的示例,然后在仓库根目录执行:
# 本地模拟器运行(wing it 为 console 本地运行命令) wing it # 编译到 tf-aws 目标并查看生成的 Terraform 清单 wing compile -t tf-aws main.w要重新生成或校验官方快照,可运行 hangar 测试框架(tools/hangar),其测试入口位于 generated_test_targets.ts,test_corpus快照即由 hangar 对 tests/valid 下全部用例编译/测试后落盘而成。
七、小结:一张图理解 for..in 的全链路
从语言语义到云资源,for..in的完整链路可以概括为:
- 编写:对数组、Set 或区间(
..开 /..=闭)使用for..in,循环变量隐式为var,支持break/continue、嵌套与反向区间; - 编译:parser.rs 解析出
ExprKind::Range→ type_check.rs 校验 iterable 可迭代并推导元素类型 → jsify.rs 生成for...of与$helpers.range(start, end, inclusive); - 运行:helpers.ts 的生成器
range根据inclusive与方向决定迭代序列,assert负责运行时断言; - 部署:若循环出现在
cloud.Function处理器中,则连同函数一起编译为 Lambda 闭包,并在 main.tf.json 中生成完整的 AWS 资源清单。
整个过程的每一步都有仓库快照与源码作为证据:想要深入调试或贡献,可以从 tests/valid/for_loop.test.w 与 tools/hangar/snapshots/test_corpus/valid/ 这对"源码—快照"组合入手,对照 wingc 与 sdk helpers 逐行验证。
- 编程语言
- 编译器
- 云原生
- 语言运行时
【免费下载链接】wing
A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡
相关推荐
Wing 语言 `util.sleep` 延迟执行解析:从 inflight 语义到 tf-aws 编译产物快照验证
Wing 语言 util.sleep 延迟执行解析:从 inflight 语义到 tf aws 编译产物快照验证 util.sleep 是 Wing 标准库中唯
编程语言编译器云原生语言运行时深入解析 Wing math.sign() 与 Hangar 快照测试:从 SDK 源码、tf-aws 编译产物到模拟器验证
深入解析 Wing math.sign 与 Hangar 快照测试:从 SDK 源码、tf aws 编译产物到模拟器验证 本篇技术指南以 Wing 仓库中 si
编程语言编译器云原生语言运行时深入解读 Wing 语言 `math.toRadix` 的 tf-aws 编译快照:从 API 用法到 Hangar 快照测试机制
深入解读 Wing 语言 math.toRadix 的 tf aws 编译快照:从 API 用法到 Hangar 快照测试机制 本文以仓库快照文档 toradi
编程语言编译器云原生语言运行时
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考