☰
深入解析 Wing 的 for..in 循环:从语言语义到 tf-aws 编译产物与 hangar 快照验证
2026/10/15 3:13:34 网站建设 项目流程
  • 编程语言
  • 编译器
  • 云原生
  • 语言运行时

【免费下载链接】wing

A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡

项目地址:https://gitcode.com/gh_mirrors/wi/wing
点击查看免费下载

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为例,其正文依次包含四个章节:

  1. ## inflight.$Closure1-1.cjs—— 闭包$Closure1(即new cloud.Function(inflight () => {...})的处理器)的 inflight JavaScript 代码;
  2. ## inflight.Foo-1.cjs—— 类Foo中inflight hello()方法的 inflight JavaScript 代码;
  3. ## main.tf.json—— 生成的 Terraform JSON 清单,描述 AWS 上要创建的全部基础设施资源;
  4. ## 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

这些断言揭示了两个关键结论:

  1. Wing 的区间天然支持反向迭代——2..0会从 2 递减到 1,而不是像多数语言那样产生空区间;
  2. 区间的起止值可以是任意数值表达式,包括负数、变量(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_bucketCode存放函数代码的 S3 桶(bucket_prefix: "code-c84a50b1-")
aws_s3_objectFunction_S3Object_C62A0C2D代码包对象(source/key为占位符<ASSET_SOURCE>/<ASSET_KEY>)
aws_lambda_functionFunctionLambda 函数本体,runtime: "nodejs20.x"、architectures: ["arm64"]、memory_size: 1024、timeout: 60
aws_cloudwatch_log_groupFunction_CloudwatchLogGroup_ABDCF4C4日志组/aws/lambda/Function-c852aba6,retention_in_days: 30
aws_iam_roleFunction_IamRole_678BE84C假定lambda.amazonaws.com的执行角色
aws_iam_role_policyFunction_IamRolePolicy_E3B26607内联策略(此处为占位none:null)
aws_iam_role_policy_attachmentFunction_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的完整链路可以概括为:

  1. 编写:对数组、Set 或区间(..开 /..=闭)使用for..in,循环变量隐式为var,支持break/continue、嵌套与反向区间;
  2. 编译:parser.rs 解析出ExprKind::Range→ type_check.rs 校验 iterable 可迭代并推导元素类型 → jsify.rs 生成for...of与$helpers.range(start, end, inclusive);
  3. 运行:helpers.ts 的生成器range根据inclusive与方向决定迭代序列,assert负责运行时断言;
  4. 部署:若循环出现在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 ⚡

项目地址:https://gitcode.com/gh_mirrors/wi/wing
点击查看免费下载

相关推荐

上一篇:终极D2DX补丁指南:三步让你的暗黑破坏神2在现代PC上完美重生
下一篇:英雄联盟Akari助手:免费开源游戏效率工具完整使用指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询