Compare commits

...

112 Commits
v0.6.0 ... main

Author SHA1 Message Date
5b3d807bbc
!67 release: 合并 v0.8.0 版本至 main 分支
Merge pull request !67 from Luke/release/v0.8.0
2025-08-27 07:44:23 +00:00
44b2ebb64a chore: 优化多平台打包 2025-08-27 15:24:58 +08:00
eb7e2e7e93 chore: 更新项目版本至 0.8.0
- 修改 .env 文件中的 SNOW_VERSION 从 0.7.0 到 0.8.0
- 更新 Gitee 问题模板中的软件版本选项
- 修改 pom.xml 中的项目版本号
- 更新 README.md 中的项目版本徽章链接
2025-08-27 14:52:30 +08:00
77eddb83f4
!65 enhancement: 支持字符串转义与 Unicode
Merge pull request !65 from Luke/feature/add-escape-characters
2025-08-27 03:17:00 +00:00
4181ff8ae2 refactor: 移除 StringEscape 类中未使用的 escape 方法
- 删除了 StringEscape 类中的 escape 方法,该方法用于将普通字符串编码为可安全存储的转义字符串
- 保留了unescape方法,并简化了其文档注释
2025-08-27 10:44:44 +08:00
49b5e61e8e feat: 增加字符串字面量的转义符和 Unicode 转义解析
- 引入 StringEscape 工具类用于字符串转义
- 在解析字符串字面量时,去除首尾引号
- 对字符串内部内容进行转义符和 Unicode 转义解析
2025-08-27 10:43:34 +08:00
e75a60026d test: 新增 Demo26 演示程序 2025-08-27 10:42:11 +08:00
83eb29fa3c chore: 更新测试配置文件
- 在测试配置文件中添加了 Demo21、Demo22、Demo23、Demo24 和 Demo25 应用
2025-08-27 10:41:34 +08:00
bc512fd02d feat: 添加字符串转义/反转义工具类
- 新增 StringEscape 类,提供编译期和运行期的字符串转义/反转义功能
- 支持常见控制字符、特殊字符和 Unicode 字符的转义/反转义
- 适用于 .water指令文件的保存和虚拟机执行时的字符串还原
2025-08-27 10:41:19 +08:00
0bbd82e9ce chore: 删除 Run 配置文件 2025-08-27 10:39:28 +08:00
2fc2a93414 feat: 增强引用推送指令功能
- 支持字符串字面量和数组字面量的解析
- 添加对不同数据类型(如浮点数、长整型等)的解析支持
- 优化了代码结构,提高了可读性和可维护性
- 增加了对 Unicode 逃逸序列的支持
- 添加了详细的类和方法注释,提高了文档质量
2025-08-27 10:39:21 +08:00
9bdc6245bf fix: 修复字符串常量在 VM 代码中的表示问题
- 在 LoadConstGenerator 中添加字符串转义功能,确保字符串常量在 VM 代码中正确表示
- 优化 formatConst 方法,对字符串常量进行转义处理
- 新增 escape 方法,用于字符串转义,包括控制字符和非 ASCII 字符的处理
2025-08-27 10:35:23 +08:00
16ba11c391
!64 feature: 实现私有函数(下划线开头仅限本模块访问)语义检查
Merge pull request !64 from Luke/feature/add-function-access-control
2025-08-26 10:12:30 +00:00
19e473e93c chore: 添加 Demo25运行配置 2025-08-26 18:09:15 +08:00
5537a448e8 test: 添加 Demo25 示例项目 2025-08-26 18:09:01 +08:00
caa62b0bfb feat: 增加模块私有函数访问控制
- 新增规则:以"_"开头的函数名只允许在本模块访问,禁止跨模块访问
- 在 CallExpressionAnalyzer 中实现该规则的检查逻辑
2025-08-26 18:08:46 +08:00
3595631e2c
!63 feat: 增强语义分析与全局常量处理
Merge pull request !63 from Luke/feature/add-constant
2025-08-26 09:31:01 +00:00
b033d53d28 test: Demo24 增加更多测试用例 2025-08-26 17:30:15 +08:00
8eda4fcbce chore: 修改 Demo24 配置文件 2025-08-26 17:29:51 +08:00
b02af0728a test: 修改 Demo24 2025-08-26 16:59:25 +08:00
b058ee4526 refactor: 优化全局变量处理和常量折叠逻辑
- 过滤与函数参数重名的全局声明,避免命名冲突
- 优化常量折叠逻辑,支持跨模块常量优化- 修复字符串字面量和布尔字面量的处理
- 优化代码结构,提高可读性和可维护性
2025-08-26 16:29:29 +08:00
71cbb3a737 test: 增加 Demo 24 2025-08-26 16:22:53 +08:00
ac0d3b9785 chore: 添加 Demo23 和 Demo24 的运行配置文件 2025-08-26 16:22:28 +08:00
f69dd85365 test: 增加 Demo23 2025-08-26 14:40:45 +08:00
c412d1194b feat: 增加全局符号表支持
- 在 ModuleInfo 类中添加 SymbolTable 类型的 globals 字段
- 实现 getGlobals 和 setGlobals 方法,用于获取和设置全局符号表
2025-08-26 14:33:45 +08:00
f39aece9cf feat: 支持模块成员访问的语义分析
- 实现了跨模块常量和全局变量访问的语义分析- 能根据目标模块的全局符号表返回准确的类型信息- 完全支持跨模块类型推断
- 对非模块成员访问的情况增加了错误处理
2025-08-26 14:32:58 +08:00
439304c86a feat: 增强全局常量处理与字面量解析
- 支持整数、浮点数、字符串和布尔字面量的解析
- 添加类型后缀支持(b/s/l/f/d),并进行相应的类型转换
- 优化数字字面量的解析逻辑,支持科学计数法和下划线分隔符
- 改进字面量提取工具的文档注释,明确支持的类型和转换规则
2025-08-26 14:32:13 +08:00
6e31185519 feat: 重构 FunctionChecker 采用两遍扫描策略
- 设计核心:采用“两遍扫描”方案,彻底解决跨模块全局变量/常量类型推断和引用依赖问题
  - 第一遍:为所有模块预先构建并注册全局符号表(globals)
  - 第二遍:在全局符号表全部就绪后,依次分析所有模块的函数体
-功能职责:
  - 遍历所有模块,先建立 globals,再遍历并检查所有函数体语句
  - 为每个函数体构建完整符号表,并注册参数变量
  - 分发每条语句到对应 StatementAnalyzer进行类型检查和错误校验
  - 自动检查非 void 函数 return 完备性
  - 记录所有语义错误,便于前端高亮和诊断
2025-08-26 14:29:37 +08:00
1c86c1dce7 feat: 改进 IR 指令生成逻辑并支持浮点数
- 新增对 long、float 和 double 类型常量加载的支持
- 优化二元运算指令生成,支持不同类型的操作数
- 实现基于变量类型的 move 指令,提高类型兼容性
- 简化控制流指令的生成逻辑
- 优化代码结构,提高可读性和可维护性
2025-08-26 14:01:19 +08:00
1f8176d15e test: 修改 Demo22 2025-08-26 11:55:25 +08:00
f8a331809f chore: 移除 Demo22.run.xml 中的调试参数 2025-08-26 11:55:10 +08:00
0d6c6cef5a feat: 添加成员表达式语义分析器
- 实现了 MemberExpressionAnalyzer 类,用于分析成员访问表达式
- 目前仅支持 ModuleName.constOrVar 形式的跨模块常量/全局变量访问
- 不支持对象成员访问,如 a.b.c- 类型降级为 int,后续由 IR 阶段进行常量折叠
2025-08-26 11:52:47 +08:00
06bb647c83 feat: 实现全局常量预加载及跨模块常量折叠
- 新增 preloadGlobals 方法,扫描所有模块节点,将常量注册到全局常量表- 支持整数、浮点数、字符串和布尔字面量的直接评估
- 优化了 IRProgram 的构建过程,增加了对全局常量的处理
2025-08-26 11:44:04 +08:00
cc66eee75f feat: 支持跨模块全局常量查找
- 新增 externalConsts 字典用于存放跨模块导入的全局常量
- 修改 getConstValue 方法以支持查找外部常量
- 添加 importExternalConst 方法用于导入外部常量
2025-08-26 11:37:05 +08:00
1e2b0e4e0d feat: 新增全局常量表支持跨模块常量管理
- 添加 GlobalConstTable 类,用于编译期间跨模块常量查询和折叠
- 提供注册常量、查询常量和获取所有常量的功能
- 保证线程安全,支持并发操作- 常量的 key采用“模块名.常量名”的格式,确保唯一性
2025-08-26 11:35:08 +08:00
1a0c3a38cb feat: 更新 FunctionBuilder构建流程与文档
- 在全局函数表中注册函数名与返回类型
- 初始化 IRFunction 实例与 IRContext 上下文对象
- 自动导入全局常量(包括跨模块 const 变量)到当前作用域
- 根据函数返回类型设置表达式推断的默认字面量类型后缀
- 遍历声明形参,每个参数分配虚拟寄存器,并注册到作用域
- 依次转换函数体中的每条语句为 IR指令
- 函数体转换完成后,清理默认类型后缀
2025-08-26 11:32:48 +08:00
fcdc3a49f8 feat: 支持成员访问表达式
- 添加对成员访问表达式(如 ModuleA.a)的支持
- 实现 buildMember 方法来处理成员表达式节点
- 增加对全局常量和作用域内常量的处理逻辑
2025-08-26 11:30:20 +08:00
139c42a90e feat: 实现成员访问表达式分析器
- 替换 UnsupportedExpressionAnalyzer 为 MemberExpressionAnalyzer
2025-08-26 11:29:03 +08:00
2dfcc4c95e chore: 更新项目构建配置 2025-08-26 09:52:14 +08:00
91aef32a42 feat: 添加常量符号类型并完善文档注释
- 在 SymbolKind 枚举中添加 CONSTANT 类型,用于表示只读、不可变的公开常量- 完善了各符号类型的文档注释,明确其用途和特点
- 优化了代码格式和段落结构,提高了代码可读性
2025-08-26 01:45:00 +08:00
18c9cbad7b feat: 增加常量声明支持并优化变量声明节点
- 在 DeclarationNode 类中添加 isConst 字段,用于表示是否为常量声明
-增加 isConst() 方法以判断声明是否为常量
- 优化类注释,明确变量和方法的作用
- 更新构造函数以支持常量声明
2025-08-26 01:44:16 +08:00
310e909359 test: 重构 Demo22 模块导入和文件布局
- 修改 Main.snow,移除未使用的 ModuleA 导入
- 将 ModuleA.snow 文件内容合并到 Main.snow 中
2025-08-26 01:43:27 +08:00
d10b7336c1 test: 重构示例代码 Demo22 2025-08-26 01:19:05 +08:00
3b39e9059b feat: 添加 const 关键字支持
- 在 TokenFactory 类中的 KEYWORDS 集合中添加了 "const" 关键字
- 此修改为编译器增加了对常量声明的支持,扩展了语言的功能
2025-08-26 01:18:24 +08:00
c44f8a4630 refactor: 优化 declare 语句的标识符处理逻辑
- 允许 declare 后跟随 const 关键字
-调整错误提示信息,使其更加清晰
- 优化代码结构,提高可读性
2025-08-26 01:18:07 +08:00
c4e9e541fb refactor: 优化全局变量声明的错误信息
-根据变量是否为常量,动态设置符号种类
-针对常量和变量分别定制重复声明的错误信息
2025-08-26 01:17:47 +08:00
da7d7bbcaa feat: 增加常量声明支持并优化声明语句解析
- 增加对常量声明的支持,通过匹配 "const" 关键字
-优化代码结构,使用 var tokens变量引用词法 token 流
- 简化代码逻辑,提高可读性和可维护性
2025-08-26 01:17:13 +08:00
d0c34ce1c2 feat: 优化声明语句语义分析
- 重构代码,优化分析流程,增加对常量声明的处理
-改进错误处理和日志输出,提高可读性和调试友好性
- 修复了一些潜在的语义分析问题,如常量未初始化的检查
2025-08-26 01:16:40 +08:00
1872221666 refactor: 优化赋值语句语义分析逻辑 2025-08-26 01:16:26 +08:00
0a35289ad0 chore: 添加 Demo22运行配置 2025-08-25 18:01:02 +08:00
5f42beaf2a test: 添加 Demo22 演示项目 2025-08-25 18:00:54 +08:00
b454d65962 chore: 新增 Demo14 的运行配置 2025-08-25 18:00:40 +08:00
d536bd17ce
!62 feat: 构建与发布脚本优化
Merge pull request !62 from Luke/feature/add-build-docker
2025-08-25 07:33:23 +00:00
3b3478ee30 style: 删除无用注释 2025-08-25 15:21:18 +08:00
926e87888c build: 优化打包脚本并移除并行构建总结 2025-08-25 15:20:36 +08:00
f7db4fcce8 chore: 添加 build-release-all.ps1 运行配置 2025-08-25 13:47:20 +08:00
9afc1caac7 build: 移除构建失败时的冗余错误提示 2025-08-25 13:47:08 +08:00
2a9d2b7f5f chore: 修改脚本名词 2025-08-25 13:43:38 +08:00
f11303f0e8 build: 添加 Linux 和 Windows 平台的发布脚本配置 2025-08-25 13:41:40 +08:00
7dd273759a build: 实现 Windows 和 Linux并行构建脚本
- 新增 build-parallel.ps1脚本,用于同时执行 Windows 和 Linux 构建任务
- 实现了日志实时输出和错误处理功能
- 添加了构建结果摘要和错误代码处理
2025-08-25 13:41:26 +08:00
e626553a9c build: 降级 SNOW_VERSION 至 0.7.0 2025-08-25 13:18:01 +08:00
b2c0b986c4 build: 更新版本号并重构发布脚本
-重构 release-linux.ps1 脚本,添加版本号读取和输出路径定义
- 新增 release-windows.ps1 脚本,实现 Windows 版本的构建和打包
- 添加 dotenv.ps1工具脚本,用于统一解析 .env 文件
2025-08-25 13:17:51 +08:00
a16f0ac059 chore: 添加 Linux 版本导出脚本和相关配置
- 新增 .env 文件和生成脚本
- 更新 docker-compose.yml 和 Dockerfile 以支持 Linux 版本导出
- 添加 PowerShell 脚本生成 .env 文件
- 实现 Linux 版本导出的完整流程
2025-08-25 12:32:10 +08:00
d3646c86ec chore: 增加docker自动构建 2025-08-24 22:02:54 +08:00
39dff9efc5
!61 release: 合并 v0.7.0 版本至 main 分支
Merge pull request !61 from Luke/release/v0.7.0
2025-08-24 04:15:25 +00:00
80efd7c357 feat: 添加 os.snow系统库模块 2025-08-24 12:13:23 +08:00
99345ddd19 chore: 更新项目版本至0.7.0 2025-08-24 12:05:30 +08:00
df07531edc chore: 更新 bug issue 模板中的软件版本选项
将 v0.6.0 版本替换为 v0.7.0版本,以反映软件的最新版本。
2025-08-24 12:05:15 +08:00
5511978ed8
!60 refactor: 移除词法分析器中的调试代码
Merge pull request !60 from Luke/feature/del-tokens
2025-08-22 09:37:19 +00:00
3406e1a2dd refactor: 移除词法分析器中的调试代码 2025-08-22 17:34:53 +08:00
00f73ecb47
!56 refactor: 重构关键字
Merge pull request !56 from zhangxun/feature/refactor-keyword
2025-08-04 09:23:38 +00:00
e51ba01962
!58 fix: 省略模块函数调用前缀,语义分析依然判定函数存在
Merge pull request !58 from zhangxun/bugfix/module-function-call-prefix
2025-08-04 09:00:21 +00:00
zhangxun
a9b4a6682a fix: 省略模块函数调用前缀,语义分析依然判定函数存在 2025-08-04 16:37:04 +08:00
zhangxun
6c5bafb3e9
!57 docs: 移除废弃的字面量后缀 i、d
Merge pull request !57 from zhangxun/bugfix/abandoned-numerical-suffixes
2025-08-04 07:56:50 +00:00
zhangxun
8d04397f32 docs: 移除废弃的字面量后缀 i、d 2025-08-04 15:26:00 +08:00
zhangxun
7898505717 docs: update README.md 2025-08-04 15:20:30 +08:00
5fdb86cd1b
!55 feature: 增强错误定位
Merge pull request !55 from zhangxun/feature/enhancement-error-location
2025-08-04 07:15:07 +00:00
zhangxun
60adecf808 Merge remote-tracking branch 'origin/feature/refactor-keyword' into feature/refactor-keyword 2025-08-04 15:08:50 +08:00
zhangxun
4ae77434dc docs: 更新文档 2025-08-04 15:08:29 +08:00
zhangxun
582ba2ce27 test: 重构 Demo 2025-08-04 15:08:29 +08:00
zhangxun
359c65228d test: 重构 BugFarm 2025-08-04 15:08:28 +08:00
zhangxun
3e7e5f2dad feat: 支持标准 URL 格式 2025-08-04 15:01:11 +08:00
zhangxun
9a61798d81 docs: 更新文档 2025-08-04 14:52:24 +08:00
zhangxun
9f3b3ad860 refactor: 重构 Demo 2025-08-04 14:52:13 +08:00
zhangxun
62f58cd83a refactor: 重构 BugFarm 2025-08-04 14:52:02 +08:00
zhangxun
a8cf93671e refactor: 重构标准库 2025-08-04 14:51:24 +08:00
zhangxun
d11822f4ba refactor: 重构模板代码 2025-08-04 14:50:10 +08:00
zhangxun
87560db20d refactor: 重构关键字 2025-08-04 14:49:39 +08:00
zhangxun
3a2a1363e6 feat: 增强错误定位 2025-08-04 13:45:23 +08:00
d54a2c59b6
!54 feat: 禁止全局变量重复声明
Merge pull request !54 from Luke/bugfix/fix-global-redeclare-error
2025-08-03 15:25:48 +00:00
4eb1aff553 Merge branch 'dev' into bugfix/fix-global-redeclare-error 2025-08-03 00:49:18 +08:00
80ee450b41
!53 feat: 支持一维及多维数组类型和字面量,重构调用指令生成逻辑
Merge pull request !53 from Luke/feature/add-support-for-array-syntax
2025-08-02 16:48:02 +00:00
f876f66414 chore: 添加 Bug6 运行配置文件 2025-08-03 00:39:18 +08:00
2188171b63 test: 添加 Bug6 2025-08-03 00:39:12 +08:00
7fab3cc662 feat: 检查全局变量重复声明
- 在 FunctionChecker 类中增加了对全局变量重复声明的检查
- 如果全局变量已经存在,则添加语义错误信息
2025-08-03 00:36:34 +08:00
62f2baab03 chore: 添加 Demo21运行配置 2025-08-03 00:16:35 +08:00
60388f6846 test: 添加 Demo21 项目 2025-08-03 00:16:17 +08:00
7c242d998f test: 更新 Demo20 2025-08-03 00:08:56 +08:00
d3a85a24bf feat: 支持数组元素赋值操作
- 新增 __setindex_x 系列内置函数,用于数组元素赋值
- 实现了对 byte、short、int、long、float、double、boolean 和引用类型数组的支持
- 修改了 ExpressionBuilder 和 StatementBuilder以支持数组赋值语法
- 更新了 VirtualMachineEngine 和 SyscallCommand 以支持新的 ARR_SET系统调用
2025-08-03 00:08:28 +08:00
477591303a chore: 添加 Demo20运行配置文件 2025-08-02 19:53:55 +08:00
f4a269540a chore: 更新测试配置文件 2025-08-02 12:33:27 +08:00
cedc77cbc6 test: 修改 Demo20 2025-08-02 12:33:14 +08:00
f61cfa5638 fix: 修复常量类型在 VM 中的表示问题
- 为浮点数添加 f 后缀,避免在 VM 中被错误解释为双精度浮点数
- 为长整数添加 L 后缀,保留类型信息
- 双精度浮点数和整数使用默认格式输出
- 支持数组常量的递归序列化,保留类型信息
- 优化了常量生成逻辑,提高了代码可读性和维护性
2025-08-02 12:32:28 +08:00
f6c877a7a1 refactor: 调整代码格式和注释 2025-08-02 10:51:14 +08:00
6083c0b024 test: 将 print 函数替换为 os.print 2025-08-02 10:50:22 +08:00
b093f8db72 feat: 支持数组下标访问类型分派
- 扩展 __index_i 函数支持 byte、short、int、long、float、double、boolean等类型
- 新增 __index_b、__index_s、__index_l、__index_f、__index_d、__index_r 函数- 优化数组元素访问的 IR 生成逻辑,根据类型选择合适的函数
- 更新 VM 层的 ARR_GET 子命令处理逻辑,支持多种数据类型
2025-08-02 10:49:54 +08:00
4a84f37b20 test: 更新 Demo19 示例 2025-08-01 23:34:57 +08:00
2a6fa8034c chore: Demo19 添加调试参数 2025-08-01 23:34:40 +08:00
102e84bc01 feat: 重构并扩展调用指令生成逻辑
- 优化了 syscall、数组下标访问和普通函数调用的处理逻辑
- 新增对多维数组下标访问的支持
- 改进了字符串常量和寄存器的绑定机制
- 统一了参数加载和错误处理的代码
2025-08-01 23:33:31 +08:00
e5f23b77bd chore: 添加 Demo19运行配置文件 2025-08-01 18:36:15 +08:00
028561fc4f test: 添加 Demo19 项目 2025-08-01 18:36:06 +08:00
82f4ba1a6e feat: 支持数组类型及数组字面量
- 新增 ArrayLiteralNode 表示数组字面量表达式节点
- 实现 ArrayLiteralAnalyzer 进行数组字面量的语义分析
- 添加 ArrayType 表示数组类型,并支持多维数组
- 修改 Context 类,增加对数组类型的支持
- 更新 DeclarationStatementParser,支持多维数组类型的声明
- 在 CallGenerator 中添加对特殊函数 __index_i 的处理,用于数组索引操作
2025-08-01 18:34:03 +08:00
135 changed files with 4430 additions and 1048 deletions

2
.env Normal file
View File

@ -0,0 +1,2 @@
# Auto-generated by build\tools\generate-dotenv.ps1
SNOW_VERSION=0.8.0

View File

@ -69,7 +69,7 @@ body:
attributes: attributes:
label: 软件版本/分支 label: 软件版本/分支
options: options:
- v0.6.0 - v0.8.0
- main - main
- dev - dev
- 其他 - 其他

10
.run/Bug6.run.xml Normal file
View File

@ -0,0 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Bug6" type="Application" factoryName="Application" folderName="BugFarm">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/BugFarm/Bug6 -o target/Bug6 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@ -7,4 +7,12 @@
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
</configuration> </configuration>
<configuration default="false" name="Demo14" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo14 -o target/Demo14" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component> </component>

10
.run/Demo19.run.xml Normal file
View File

@ -0,0 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo19" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo19 -o target/Demo19 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

10
.run/Demo20.run.xml Normal file
View File

@ -0,0 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo20" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo20 -o target/Demo20 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

10
.run/Demo21.run.xml Normal file
View File

@ -0,0 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo21" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo21 -o target/Demo21 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

10
.run/Demo22.run.xml Normal file
View File

@ -0,0 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo22" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo22 -o target/Demo22" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

10
.run/Demo23.run.xml Normal file
View File

@ -0,0 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo23" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo23 -o target/Demo23 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

10
.run/Demo24.run.xml Normal file
View File

@ -0,0 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo24" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo24 -o target/Demo24" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

10
.run/Demo25.run.xml Normal file
View File

@ -0,0 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo25" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo25 -o target/Demo25" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@ -1,7 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run" type="CompoundRunConfigurationType">
<toRun name="build_project2tar.ps1" type="PowerShellRunType" />
<toRun name="Demo1" type="Application" />
<method v="2" />
</configuration>
</component>

View File

@ -0,0 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="build-project2tar.ps1" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$/build/build-project2tar.ps1" executablePath="C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe">
<envs />
<method v="2" />
</configuration>
</component>

View File

@ -1,9 +1,9 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="build_project2tar.ps1" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$/build/build_project2tar.ps1" executablePath="C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe"> <configuration default="false" name="build-release-all.ps1" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$/build/build-release-all.ps1" executablePath="C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe">
<envs /> <envs />
<method v="2" /> <method v="2" />
</configuration> </configuration>
<configuration default="false" name="build_project2tar.ps1" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$/build/build_project2tar.ps1" executablePath="C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe"> <configuration default="false" name="build-release-all.ps1" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$/build/build-release-all.ps1" executablePath="C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe">
<envs /> <envs />
<method v="2" /> <method v="2" />
</configuration> </configuration>

View File

@ -0,0 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="release-linux.ps1" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$/build/release-linux.ps1" executablePath="C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe">
<envs />
<method v="2" />
</configuration>
</component>

View File

@ -0,0 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="release-windows.ps1" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$/build/release-windows.ps1" executablePath="C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe">
<envs />
<method v="2" />
</configuration>
</component>

View File

@ -9,7 +9,15 @@
<toRun name="Demo15" type="Application" /> <toRun name="Demo15" type="Application" />
<toRun name="Demo16" type="Application" /> <toRun name="Demo16" type="Application" />
<toRun name="Demo17" type="Application" /> <toRun name="Demo17" type="Application" />
<toRun name="Demo18" type="Application" />
<toRun name="Demo19" type="Application" />
<toRun name="Demo2" type="Application" /> <toRun name="Demo2" type="Application" />
<toRun name="Demo20" type="Application" />
<toRun name="Demo21" type="Application" />
<toRun name="Demo22" type="Application" />
<toRun name="Demo23" type="Application" />
<toRun name="Demo24" type="Application" />
<toRun name="Demo25" type="Application" />
<toRun name="Demo3" type="Application" /> <toRun name="Demo3" type="Application" />
<toRun name="Demo4" type="Application" /> <toRun name="Demo4" type="Application" />
<toRun name="Demo6" type="Application" /> <toRun name="Demo6" type="Application" />

57
Dockerfile Normal file
View File

@ -0,0 +1,57 @@
# Stage 1: 官方 GraalVM 社区版(已含 native-image
FROM ghcr.io/graalvm/native-image-community:24.0.2 AS builder
RUN microdnf install -y \
gcc gcc-c++ make git wget tar gzip which findutils maven \
&& microdnf clean all
# ---------- 构建 musl ----------
ARG MUSL_VER=1.2.5
WORKDIR /tmp
RUN wget -q https://musl.libc.org/releases/musl-${MUSL_VER}.tar.gz \
&& tar -xzf musl-${MUSL_VER}.tar.gz \
&& cd musl-${MUSL_VER} \
&& ./configure --prefix=/opt/musl-${MUSL_VER} --disable-shared \
&& make -j"$(nproc)" \
&& make install \
&& ln -s /opt/musl-${MUSL_VER} /opt/musl \
&& cd / && rm -rf /tmp/musl-${MUSL_VER}*
RUN ln -s /opt/musl/bin/musl-gcc /usr/local/bin/x86_64-linux-musl-gcc \
&& ln -s /opt/musl/bin/musl-gcc /usr/local/bin/x86_64-linux-musl-cc
ENV PATH="/opt/musl/bin:${PATH}"
ENV CC="musl-gcc"
ENV C_INCLUDE_PATH="/opt/musl/include"
ENV LIBRARY_PATH="/opt/musl/lib"
# ---------- 静态 zlib ----------
ARG ZLIB_VERSION=1.3.1
WORKDIR /tmp
RUN wget -q https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz \
&& tar -xzf zlib-${ZLIB_VERSION}.tar.gz \
&& cd zlib-${ZLIB_VERSION} \
&& CC=musl-gcc ./configure --static --prefix=/opt/musl \
&& make -j"$(nproc)" \
&& make install \
&& cd / && rm -rf /tmp/zlib-${ZLIB_VERSION}*
# ---------- Maven 缓存优化 ----------
WORKDIR /app
COPY pom.xml ./
# 先拉依赖并缓存
RUN mvn -B -P native-linux dependency:go-offline
# ---------- 复制源码 ----------
COPY . /app
# ---------- 编译 native image ----------
RUN mvn -P native-linux -DskipTests clean package
# ------------------------------------------------------------
# Stage 2: 输出产物镜像(可以直接 cp 出二进制)
# ------------------------------------------------------------
FROM busybox AS export
WORKDIR /export
COPY --from=builder /app/org.jcnc.snow.cli.SnowCLI /export/Snow

View File

@ -11,8 +11,8 @@
<a href="https://gitee.com/jcnc-org/snow/blob/main/LICENSE"> <a href="https://gitee.com/jcnc-org/snow/blob/main/LICENSE">
<img src="https://img.shields.io/badge/%20license-Apache--2.0%20-blue" alt=""> <img src="https://img.shields.io/badge/%20license-Apache--2.0%20-blue" alt="">
</a> </a>
<a href="https://gitee.com/jcnc-org/snow/tree/v0.6.0/"> <a href="https://gitee.com/jcnc-org/snow/tree/v0.8.0/">
<img src="https://img.shields.io/badge/version-v0.6.0-blue" alt=""> <img src="https://img.shields.io/badge/version-v0.8.0-blue" alt="">
</a> </a>
</p> </p>
@ -44,7 +44,7 @@ SnowVM) 的完整编译-执行链路。
Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的语法和严格的类型系统,以帮助 LLM 更好地理解程序。 Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的语法和严格的类型系统,以帮助 LLM 更好地理解程序。
语言使用显式的 `module` 声明来组织代码,用 `function`,`parameter`,`return_type`,`body` 等关键字分隔不同代码块,语法结构固定且易读。此外,Snow 语言使用显式的 `module` 声明来组织代码,用 `function`,`params`,`returns`,`body` 等关键字分隔不同代码块,语法结构固定且易读。此外,Snow
实现了语义分析来检查变量作用域和类型一致性,在编译阶段捕获错误并确保生成的中间代码正确无误。这种自上而下的编译流程,使得代码设计和生成更加模块化,可解释,也有利于调试和优化。 实现了语义分析来检查变量作用域和类型一致性,在编译阶段捕获错误并确保生成的中间代码正确无误。这种自上而下的编译流程,使得代码设计和生成更加模块化,可解释,也有利于调试和优化。
相关背景: [心路历程](docs/Snow-Lang-Journey/Snow-Lang-Journey.md) 相关背景: [心路历程](docs/Snow-Lang-Journey/Snow-Lang-Journey.md)
@ -111,7 +111,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
module: Main module: Main
import:Math import:Math
function: main function: main
return_type: int returns: int
body: body:
Math.add(6,1) Math.add(6,1)
return 0 return 0
@ -135,7 +135,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
3 15 IDENTIFIER main 3 15 IDENTIFIER main
3 19 NEWLINE \n 3 19 NEWLINE \n
4 9 KEYWORD return_type 4 9 KEYWORD returns
4 20 COLON : 4 20 COLON :
4 22 TYPE int 4 22 TYPE int
4 25 NEWLINE \n 4 25 NEWLINE \n
@ -174,10 +174,10 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
#### Math.snow #### Math.snow
module: Math module: Math
function: add function: add
parameter: params:
declare n1: int declare n1: int
declare n2: int declare n2: int
return_type: int returns: int
body: body:
return n1 + n2 return n1 + n2
end body end body
@ -195,7 +195,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
2 15 IDENTIFIER add 2 15 IDENTIFIER add
2 18 NEWLINE \n 2 18 NEWLINE \n
3 9 KEYWORD parameter 3 9 KEYWORD params
3 18 COLON : 3 18 COLON :
3 19 NEWLINE \n 3 19 NEWLINE \n
@ -211,7 +211,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
5 25 TYPE int 5 25 TYPE int
5 28 NEWLINE \n 5 28 NEWLINE \n
6 9 KEYWORD return_type 6 9 KEYWORD returns
6 20 COLON : 6 20 COLON :
6 22 TYPE int 6 22 TYPE int
6 25 NEWLINE \n 6 25 NEWLINE \n
@ -494,7 +494,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
```snow ```snow
module: Math module: Math
function: main function: main
return_type: int returns: int
body: body:
Math.factorial(6) Math.factorial(6)
return 0 return 0
@ -502,9 +502,9 @@ module: Math
end function end function
function: factorial function: factorial
parameter: params:
declare n:int declare n:int
return_type: int returns: int
body: body:
declare num1:int = 1 declare num1:int = 1
loop: loop:

View File

@ -0,0 +1,48 @@
# Set the tar package name
$tarName = "Snow.tar"
# Get the script's current directory (build folder)
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
# Get the parent directory (the project root)
$parentDir = Split-Path -Parent $scriptDir
# Set the full path to the tar package
$tarPath = Join-Path $parentDir $tarName
# Output message: starting to create tar package
Write-Output "Starting to create tar package: $tarName in $parentDir ..."
# Remove old tar package if it exists
if (Test-Path $tarPath) {
Write-Output "Found an old $tarName, removing it..."
Remove-Item $tarPath -Force
}
# Make sure the tar command is available
$tarCommand = "tar"
if (-not (Get-Command $tarCommand -ErrorAction SilentlyContinue)) {
Write-Error "❌ 'tar' command is not available. Please make sure 'tar' is installed and can be run from PowerShell."
exit 1
}
# Execute tar: change to org\jcnc directory and compress the snow folder
try {
# Build the command and run it
$tarCommandArgs = "-cf", $tarPath, "-C", "$scriptDir\..\src\main\java\org\jcnc", "snow"
Write-Output "Running tar command: tar $tarCommandArgs"
& $tarCommand @tarCommandArgs
} catch {
Write-Error "❌ Failed to create tar package. Error: $_"
exit 1
}
# Check if tar package was created successfully
if (Test-Path $tarPath) {
Write-Output "✅ Successfully created $tarName"
exit 0
} else {
Write-Error "❌ Creation failed. Please check the tar command and paths."
exit 1
}

129
build/build-release-all.ps1 Normal file
View File

@ -0,0 +1,129 @@
param(
[string]$LogDir = (Join-Path $PSScriptRoot 'target\parallel-logs')
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$winScript = Join-Path $PSScriptRoot 'release-windows.ps1'
$linScript = Join-Path $PSScriptRoot 'release-linux.ps1'
if (-not (Test-Path $winScript)) { throw "File not found: $winScript" }
if (-not (Test-Path $linScript)) { throw "File not found: $linScript" }
$winLogOut = [System.IO.Path]::GetTempFileName()
$winLogErr = [System.IO.Path]::GetTempFileName()
$linLogOut = [System.IO.Path]::GetTempFileName()
$linLogErr = [System.IO.Path]::GetTempFileName()
$winProc = Start-Process powershell.exe -ArgumentList @('-NoProfile','-ExecutionPolicy','Bypass','-File',"`"$winScript`"") `
-RedirectStandardOutput $winLogOut -RedirectStandardError $winLogErr -NoNewWindow -PassThru
$linProc = Start-Process powershell.exe -ArgumentList @('-NoProfile','-ExecutionPolicy','Bypass','-File',"`"$linScript`"") `
-RedirectStandardOutput $linLogOut -RedirectStandardError $linLogErr -NoNewWindow -PassThru
$winPosOut = 0
$winPosErr = 0
$linPosOut = 0
$linPosErr = 0
Write-Host "===== Build Started ====="
while (-not $winProc.HasExited -or -not $linProc.HasExited) {
# windows-release stdout
if (Test-Path $winLogOut) {
$size = (Get-Item $winLogOut).Length
if ($size -gt $winPosOut) {
$fs = [System.IO.File]::Open($winLogOut, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
$fs.Position = $winPosOut
$sr = New-Object System.IO.StreamReader($fs)
while (!$sr.EndOfStream) {
$line = $sr.ReadLine()
if ($line) { Write-Host "[windows-release][OUT] $line" }
}
$winPosOut = $fs.Position
$sr.Close()
$fs.Close()
}
}
# windows-release stderr
if (Test-Path $winLogErr) {
$size = (Get-Item $winLogErr).Length
if ($size -gt $winPosErr) {
$fs = [System.IO.File]::Open($winLogErr, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
$fs.Position = $winPosErr
$sr = New-Object System.IO.StreamReader($fs)
while (!$sr.EndOfStream) {
$line = $sr.ReadLine()
if ($line) { Write-Host "[windows-release][ERR] $line" -ForegroundColor Red }
}
$winPosErr = $fs.Position
$sr.Close()
$fs.Close()
}
}
# linux-release stdout
if (Test-Path $linLogOut) {
$size = (Get-Item $linLogOut).Length
if ($size -gt $linPosOut) {
$fs = [System.IO.File]::Open($linLogOut, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
$fs.Position = $linPosOut
$sr = New-Object System.IO.StreamReader($fs)
while (!$sr.EndOfStream) {
$line = $sr.ReadLine()
if ($line) { Write-Host "[linux-release][OUT] $line" }
}
$linPosOut = $fs.Position
$sr.Close()
$fs.Close()
}
}
# linux-release stderr
if (Test-Path $linLogErr) {
$size = (Get-Item $linLogErr).Length
if ($size -gt $linPosErr) {
$fs = [System.IO.File]::Open($linLogErr, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
$fs.Position = $linPosErr
$sr = New-Object System.IO.StreamReader($fs)
while (!$sr.EndOfStream) {
$line = $sr.ReadLine()
if ($line) { Write-Host "[linux-release][ERR] $line" -ForegroundColor Red }
}
$linPosErr = $fs.Position
$sr.Close()
$fs.Close()
}
}
Start-Sleep -Milliseconds 200
}
# After processes exit, print any remaining output
$tasks = @(
@{proc=$winProc; log=$winLogOut; tag='windows-release'; type='OUT'; skip=$winPosOut},
@{proc=$winProc; log=$winLogErr; tag='windows-release'; type='ERR'; skip=$winPosErr},
@{proc=$linProc; log=$linLogOut; tag='linux-release'; type='OUT'; skip=$linPosOut},
@{proc=$linProc; log=$linLogErr; tag='linux-release'; type='ERR'; skip=$linPosErr}
)
foreach ($item in $tasks) {
if (Test-Path $item.log) {
$fs = [System.IO.File]::Open($item.log, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
$fs.Position = $item.skip
$sr = New-Object System.IO.StreamReader($fs)
while (!$sr.EndOfStream) {
$line = $sr.ReadLine()
if ($line) {
if ($item.type -eq 'ERR') {
Write-Host "[$($item.tag)][ERR] $line" -ForegroundColor Red
} else {
Write-Host "[$($item.tag)][OUT] $line"
}
}
}
$sr.Close()
$fs.Close()
}
}
Write-Host ""
Write-Host "All tasks completed successfully." -ForegroundColor Green
Remove-Item $winLogOut, $winLogErr, $linLogOut, $linLogErr -Force
exit 0

View File

@ -1,47 +0,0 @@
# 设定 tar 包的名称
$tarName = "Snow.tar"
# 获取脚本当前目录build文件夹
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
# 获取上一级目录snow 根目录)
$parentDir = Split-Path -Parent $scriptDir
# 设置 tar 包的完整路径
$tarPath = Join-Path $parentDir $tarName
# 输出开始创建 tar 包的消息
Write-Output "开始创建 tar 包: $tarName$parentDir ..."
# 如果存在旧 tar 包,先删除它
if (Test-Path $tarPath) {
Write-Output "发现旧的 $tarName,正在删除..."
Remove-Item $tarPath -Force
}
# 确保 tar 命令可用
$tarCommand = "tar"
if (-not (Get-Command $tarCommand -ErrorAction SilentlyContinue)) {
Write-Error "❌ tar 命令不可用。请确保 tar 已安装并可在 PowerShell 中执行。"
exit 1
}
# 执行打包操作: 切换到 org\jcnc 目录下再压缩 snow 文件夹
try {
# 构建命令并执行
$tarCommandArgs = "-cf", $tarPath, "-C", "$scriptDir\..\src\main\java\org\jcnc", "snow"
Write-Output "执行 tar 命令: tar $tarCommandArgs"
& $tarCommand @tarCommandArgs
} catch {
Write-Error "❌ 创建 tar 包失败。错误信息: $_"
exit 1
}
# 检查 tar 包是否创建成功
if (Test-Path $tarPath) {
Write-Output "✅ 成功创建 $tarName"
} else {
Write-Error "❌ 创建失败,请检查 tar 命令和路径是否正确。"
exit 1
}

136
build/release-linux.ps1 Normal file
View File

@ -0,0 +1,136 @@
# run-linux-snow-export.ps1
# Build and package linux-snow-export, version read from SNOW_VERSION in .env
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
# Import shared dotenv parser function
. "$PSScriptRoot\tools\dotenv.ps1"
Write-Host "Step 0: Generate .env..."
try {
& "$PSScriptRoot\tools\generate-dotenv.ps1" -ErrorAction Stop
} catch {
Write-Error "Failed to generate .env: $( $_.Exception.Message )"
exit 1
}
Write-Host "Step 1: Build and run linux-snow-export..."
docker compose run --build --rm linux-snow-export
if ($LASTEXITCODE -ne 0) {
Write-Error "Build & Run failed, exiting script."
exit $LASTEXITCODE
}
Write-Host "Step 2: Run linux-snow-export without rebuild..."
docker compose run --rm linux-snow-export
if ($LASTEXITCODE -ne 0) {
Write-Error "Run without rebuild failed, exiting script."
exit $LASTEXITCODE
}
# ===== Step 3: Read version from .env =====
$projectRoot = Resolve-Path (Join-Path $PSScriptRoot "..")
$dotenvPath = Join-Path $projectRoot ".env"
if (-not (Test-Path -LiteralPath $dotenvPath)) {
Write-Error ".env not found at: $dotenvPath"
exit 1
}
$version = Read-DotEnvValue -FilePath $dotenvPath -Key 'SNOW_VERSION'
if (-not $version) {
Write-Error "SNOW_VERSION not found in .env"
exit 1
}
# ===== Step 4: Define output paths =====
$targetDir = Join-Path $projectRoot "target\release"
$outDir = Join-Path $targetDir "Snow-v$version-linux-x64"
$tgzPath = Join-Path $targetDir "Snow-v$version-linux-x64.tgz"
# ===== Step 5: Package to .tgz (no extra top-level dir, max compression) =====
Write-Host "Step 5: Package to .tgz..."
if (-not (Test-Path -LiteralPath $outDir)) {
Write-Error "Output directory not found: $outDir"
exit 1
}
# Ensure target directory exists
if (-not (Test-Path -LiteralPath $targetDir)) {
New-Item -ItemType Directory -Force -Path $targetDir | Out-Null
}
# Remove old package if exists
if (Test-Path -LiteralPath $tgzPath) {
Write-Host "→ Removing existing tgz: $tgzPath"
Remove-Item -LiteralPath $tgzPath -Force
}
function Invoke-TarGz {
param(
[Parameter(Mandatory = $true)][string]$SourceDir,
[Parameter(Mandatory = $true)][string]$DestTgz
)
$tarExe = "tar"
$isWindows = $env:OS -eq 'Windows_NT'
if ($isWindows) {
$psi = @{
FilePath = $tarExe
ArgumentList= @("-C", $SourceDir, "-czf", $DestTgz, ".")
NoNewWindow = $true
Wait = $true
}
try {
$p = Start-Process @psi -PassThru -ErrorAction Stop
$p.WaitForExit()
if ($p.ExitCode -ne 0) {
throw "tar exited with code $($p.ExitCode)"
}
} catch {
throw "Packaging failed (Windows tar): $($_.Exception.Message)"
}
} else {
try {
$psi = @{
FilePath = $tarExe
ArgumentList= @("-C", $SourceDir, "-c", "-f", $DestTgz, "-I", "gzip -9", ".")
NoNewWindow = $true
Wait = $true
}
$p = Start-Process @psi -PassThru -ErrorAction Stop
$p.WaitForExit()
if ($p.ExitCode -eq 0) { return }
} catch { }
try {
$psi = @{
FilePath = $tarExe
ArgumentList= @("-C", $SourceDir, "-c", "-z", "-f", $DestTgz, ".")
NoNewWindow = $true
Wait = $true
}
$p = Start-Process @psi -PassThru -ErrorAction Stop
$p.WaitForExit()
if ($p.ExitCode -ne 0) {
throw "tar exited with code $($p.ExitCode)"
}
} catch {
throw "Packaging failed (Linux tar): $($_.Exception.Message)"
}
}
}
try {
Invoke-TarGz -SourceDir $outDir -DestTgz $tgzPath
} catch {
Write-Error $_.Exception.Message
exit 1
}
Write-Host ">>> Package ready!" -ForegroundColor Green
Write-Host "Version : $version"
Write-Host "Output Dir : $outDir"
Write-Host "Tgz File : $tgzPath"

117
build/release-windows.ps1 Normal file
View File

@ -0,0 +1,117 @@
# release-windows.ps1
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
Set-StrictMode -Version Latest
# Import shared dotenv parser function
. "$PSScriptRoot\tools\dotenv.ps1"
# ===== Utility Functions =====
function Find-PomUpwards([string]$startDir) {
$dir = Resolve-Path $startDir
while ($true) {
$pom = Join-Path $dir "pom.xml"
if (Test-Path $pom) { return $pom }
$parent = Split-Path $dir -Parent
if ($parent -eq $dir -or [string]::IsNullOrEmpty($parent)) { return $null }
$dir = $parent
}
}
# ===== Step 0: Generate .env =====
Write-Host "Step 0: Generate .env..."
try {
& "$PSScriptRoot\tools\generate-dotenv.ps1" -ErrorAction Stop
} catch {
Write-Error "Failed to generate .env: $($_.Exception.Message)"
exit 1
}
# ===== Step 1: Locate project root & build =====
Write-Host "Step 1: Locate project root and build..."
$pom = Find-PomUpwards -startDir $PSScriptRoot
if (-not $pom) {
Write-Error "pom.xml not found. Please run this script within the project."
exit 1
}
$projectRoot = Split-Path $pom -Parent
Push-Location $projectRoot
try {
Write-Host "→ Running: mvn clean package"
mvn clean package
if ($LASTEXITCODE -ne 0) {
Write-Error "Maven build failed, exiting script."
exit $LASTEXITCODE
}
# ===== Step 2: Read SNOW_VERSION =====
Write-Host "Step 2: Read SNOW_VERSION from .env..."
$dotenvPath = Join-Path $projectRoot ".env"
$snowVersion = Read-DotEnvValue -FilePath $dotenvPath -Key "SNOW_VERSION"
if (-not $snowVersion) {
Write-Host "SNOW_VERSION not found in .env, using placeholder 0.0.0." -ForegroundColor Yellow
$snowVersion = "0.0.0"
}
Write-Host "SNOW_VERSION = $snowVersion"
# ===== Step 3: Prepare release directory structure =====
Write-Host "Step 3: Prepare release directory structure..."
$targetDir = Join-Path $projectRoot "target"
$exePath = Join-Path $targetDir "Snow.exe"
if (-not (Test-Path $exePath)) {
Write-Error "Expected build artifact not found: $exePath"
exit 1
}
$verName = "Snow-v${snowVersion}-windows-x64"
$releaseRoot = Join-Path $targetDir "release"
$outDir = Join-Path $releaseRoot $verName
$binDir = Join-Path $outDir "bin"
$libDir = Join-Path $outDir "lib"
# Clean old directory
if (Test-Path $outDir) {
Write-Host "→ Cleaning previous output directory..."
Remove-Item $outDir -Recurse -Force
}
New-Item -ItemType Directory -Force -Path $binDir | Out-Null
Copy-Item -Path $exePath -Destination (Join-Path $binDir "Snow.exe") -Force
Write-Host ">>> Collected Snow.exe"
# Optional lib
$projectLib = Join-Path $projectRoot "lib"
if (Test-Path $projectLib) {
New-Item -ItemType Directory -Force -Path $libDir | Out-Null
Copy-Item -Path (Join-Path $projectLib "*") -Destination $libDir -Recurse -Force
Write-Host ">>> Copied lib directory"
} else {
Write-Host ">>> lib directory not found, skipping." -ForegroundColor Yellow
}
# ===== Step 4: Create release zip =====
Write-Host "Step 4: Create release zip..."
New-Item -ItemType Directory -Force -Path $releaseRoot | Out-Null
$zipPath = Join-Path $releaseRoot ("{0}.zip" -f $verName)
if (Test-Path $zipPath) {
Write-Host "→ Removing existing zip: $zipPath"
Remove-Item $zipPath -Force
}
try {
Compress-Archive -Path (Join-Path $outDir '*') -DestinationPath $zipPath -CompressionLevel Optimal -Force
} catch {
Write-Error "Failed to create zip: $($_.Exception.Message)"
exit 1
}
Write-Host ">>> Package ready!" -ForegroundColor Green
Write-Host "Version : $snowVersion"
Write-Host "Output Dir : $outDir"
Write-Host "Zip File : $zipPath"
}
finally {
Pop-Location
}

53
build/tools/dotenv.ps1 Normal file
View File

@ -0,0 +1,53 @@
# tools/dotenv.ps1
# Unified .env reader function:
# - Supports `KEY=VAL` and `export KEY=VAL`
# - Skips blank lines and comments
# - Handles quoted values (single or double quotes)
# - Allows inline comments at the end of a line (space + #)
# - If the same KEY is defined multiple times, the last one takes precedence
Set-StrictMode -Version Latest
function Read-DotEnvValue {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)][string]$FilePath,
[Parameter(Mandatory=$true)][string]$Key
)
if (-not (Test-Path -LiteralPath $FilePath)) { return $null }
# Match the target key (escaped), allowing optional "export" prefix
$pattern = '^(?:\s*export\s+)?(?<k>' + [regex]::Escape($Key) + ')\s*=\s*(?<v>.*)$'
$value = $null
# Read line by line for large file compatibility
Get-Content -LiteralPath $FilePath | ForEach-Object {
$line = $_
# Skip blank lines and full-line comments
if ($line -match '^\s*$') { return }
if ($line -match '^\s*#') { return }
if ($line -match $pattern) {
$v = $matches['v']
# Remove surrounding quotes if present
$trimmed = $v.Trim()
if ($trimmed -match '^\s*"(.*)"\s*$') {
$v = $matches[1]
} elseif ($trimmed -match "^\s*'(.*)'\s*$") {
$v = $matches[1]
} else {
# Strip inline comments (space + # …), ignoring escaped \#
if ($v -match '^(.*?)(?<!\\)\s+#.*$') {
$v = $matches[1]
}
}
$value = $v.Trim()
}
}
return $value
}

View File

@ -0,0 +1,23 @@
# build\tools\generate-dotenv.ps1
# Repository root: go up two levels from build\tools\
$repoRoot = (Get-Item $PSScriptRoot).Parent.Parent.FullName
$envPath = Join-Path $repoRoot ".env"
$pomPath = Join-Path $repoRoot "pom.xml"
if (-not (Test-Path $pomPath -PathType Leaf)) {
throw "pom.xml not found: $pomPath"
}
[xml]$pom = Get-Content $pomPath -Encoding UTF8
$version = $pom.project.version
$lines = @(
"# Auto-generated by build\tools\generate-dotenv.ps1"
"SNOW_VERSION=$version"
)
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
[System.IO.File]::WriteAllLines($envPath, $lines, $utf8NoBom)
Write-Host "Generated/overwritten $envPath (version: $version)"
return

24
docker-compose.yml Normal file
View File

@ -0,0 +1,24 @@
services:
# Run with: docker compose run --rm linux-snow-export
linux-snow-export:
build:
context: .
target: export
command:
- /bin/sh
- -c
- |
set -e
ver="Snow-v${SNOW_VERSION}-linux-x64"
mkdir -p "/output/release/$$ver/bin"
cp /export/Snow "/output/release/$$ver/bin/"
if [ -d /export/lib ]; then
mkdir -p "/output/release/$$ver/lib"
cp -a /export/lib/. "/output/release/$$ver/lib/"
fi
tar -C /output/release -czf "/output/release/$$ver.tgz" "$$ver"
volumes:
- ./target:/output
- ./lib:/export/lib:ro
env_file:
- .env

View File

@ -76,8 +76,8 @@
module: Main module: Main
import:Math import:Math
function: main function: main
parameter: params:
return_type: int returns: int
body: body:
Math.factorial(6L,1L) Math.factorial(6L,1L)
@ -90,10 +90,10 @@ end module
## 源代码 (test.snow) ## 源代码 (test.snow)
module: Math module: Math
function: factorial function: factorial
parameter: params:
declare n1: long declare n1: long
declare n2: long declare n2: long
return_type: long returns: long
body: body:
return n1+n2 return n1+n2
end body end body

View File

@ -7,7 +7,7 @@
```snow ```snow
module: Main module: Main
function: main function: main
return_type: int returns: int
body: body:
return 1 + 1 return 1 + 1
@ -57,10 +57,8 @@ bool 类型:
|---------|----| |---------|----|
| b、B | 7b | | b、B | 7b |
| s、S | 7s | | s、S | 7s |
| i、I | 7i |
| l、L | 7l | | l、L | 7l |
| f、F | 7f | | f、F | 7f |
| d、D | 7d |
### 变量 ### 变量
@ -116,7 +114,7 @@ cond 可以是表达式(结果为 bool 类型)或者 bool 字面量
```snow ```snow
module: Main module: Main
function: main function: main
return_type: int returns: int
body: body:
if 5 > 7 then if 5 > 7 then
return 5 return 5
@ -154,7 +152,7 @@ end loop
```snow ```snow
module: Main module: Main
function: main function: main
return_type: int returns: int
body: body:
declare sum: int = 0 declare sum: int = 0
loop: loop:
@ -179,17 +177,17 @@ end module
函数的形式如下: 函数的形式如下:
```snow ```snow
function: add function: add
parameter: params:
declare a: int declare a: int
declare b: int declare b: int
return_type: int returns: int
body: body:
return a + b return a + b
end body end body
end function end function
``` ```
其中 add 是函数名parameter 下面是参数列表(可省略),与变量的定义类似,但是不允许赋初值, 其中 add 是函数名params 下面是参数列表(可省略),与变量的定义类似,但是不允许赋初值,
接着 return_type 设置返回值类型,最后的 body 为函数体。 接着 returns 设置返回值类型,最后的 body 为函数体。
## 模块 ## 模块
@ -202,7 +200,7 @@ snow 会自动将同名模块的函数合并。
```snow ```snow
module: Main module: Main
function: main function: main
return_type: int returns: int
body: body:
return 1 + 1 return 1 + 1
@ -218,10 +216,10 @@ end module
// Math.snow // Math.snow
module: Math module: Math
function: add function: add
parameter: params:
declare a: int declare a: int
declare b: int declare b: int
return_type: int returns: int
body: body:
return a + b return a + b
end body end body
@ -235,7 +233,7 @@ end module
module: Main module: Main
import: Math import: Math
function: main function: main
return_type: int returns: int
body: body:
return Math.add(5, 7) return Math.add(5, 7)
@ -250,7 +248,7 @@ end module
module: Main module: Main
import: Math, Time import: Math, Time
function: main function: main
return_type: int returns: int
body: body:
return Math.add(5, 7) return Math.add(5, 7)

View File

@ -1,7 +1,7 @@
module: Math module: Math
function: main function: main
parameter: params:
return_type: int returns: int
body: body:
Math.factorial(6) Math.factorial(6)
return 0 return 0
@ -9,9 +9,9 @@ module: Math
end function end function
function: factorial function: factorial
parameter: params:
declare n:int declare n:int
return_type: int returns: int
body: body:
declare num1:int = 1 declare num1:int = 1
loop: loop:

View File

@ -1,9 +1,9 @@
module: os module: os
import: os import: os
function: print function: print
parameter: params:
declare i1: int declare i1: int
return_type: void returns: void
body: body:
syscall("PRINT",i1) syscall("PRINT",i1)
end body end body

View File

@ -1,6 +1,6 @@
function: main function: main
parameter: params:
return_type: int returns: int
body: body:
loop: loop:
init: init:

View File

@ -1,6 +1,6 @@
module: Main module: Main
function: main function: main
return_type: void returns: void
body: body:
declare abc:int =1 declare abc:int =1
end body end body

View File

@ -1,7 +1,7 @@
module: Main module: Main
import: os import: os
function: main function: main
return_type: void returns: void
body: body:
loop: loop:
init: init:

View File

@ -1,9 +1,9 @@
module: os module: os
import: os import: os
function: print function: print
parameter: params:
declare i1: int declare i1: int
return_type: void returns: void
body: body:
syscall("PRINT",i1) syscall("PRINT",i1)
end body end body

View File

@ -1,7 +1,7 @@
module: Main module: Main
import: os import: os
function: main function: main
return_type: void returns: void
body: body:
// 合法 // 合法
declare b1: byte = 127b declare b1: byte = 127b

View File

@ -1,9 +1,9 @@
module: os module: os
import: os import: os
function: print function: print
parameter: params:
declare i1: int declare i1: int
return_type: void returns: void
body: body:
syscall("PRINT",i1) syscall("PRINT",i1)
end body end body

View File

@ -1,7 +1,7 @@
module: Main module: Main
import: ModuleB import: ModuleB
function: main function: main
return_type: int returns: int
body: body:
return ModuleB.fun() return ModuleB.fun()
end body end body

View File

@ -1,6 +1,6 @@
module: ModuleA module: ModuleA
function: fun function: fun
return_type: int returns: int
body: body:
return 123 return 123
end body end body

View File

@ -1,7 +1,7 @@
module: ModuleB module: ModuleB
import: ModuleA import: ModuleA
function: fun function: fun
return_type: int returns: int
body: body:
return ModuleA.fun() + ModuleA.fun() return ModuleA.fun() + ModuleA.fun()
end body end body

View File

@ -3,8 +3,8 @@ module: Main
globals: globals:
declare sum: int = 0 declare sum: int = 0
function: main function: main
parameter: params:
return_type: int returns: int
body: body:
sum = 20 sum = 20

View File

@ -1,9 +1,9 @@
module: os module: os
import: os import: os
function: print function: print
parameter: params:
declare i1: int declare i1: int
return_type: void returns: void
body: body:
syscall("PRINT",i1) syscall("PRINT",i1)
end body end body

View File

@ -0,0 +1,13 @@
module: Main
import: os
globals:
declare sum: int = 123
function: main
params:
returns: int
body:
os.print(sum)
return 0
end body
end function
end module

View File

@ -0,0 +1,11 @@
module: os
import: os
function: print
params:
declare i1: int
returns: void
body:
syscall("PRINT",i1)
end body
end function
end module

View File

@ -1,7 +1,7 @@
module: Main module: Main
import:Math import:Math
function: main function: main
return_type: int returns: int
body: body:
Math.add(6,1) Math.add(6,1)
return 0 return 0

View File

@ -1,9 +1,9 @@
module: Math module: Math
function: add function: add
parameter: params:
declare n1: int declare n1: int
declare n2: int declare n2: int
return_type: int returns: int
body: body:
return n1 + n2 return n1 + n2
end body end body

View File

@ -1,5 +1,5 @@
function: main function: main
return_type: int returns: int
body: body:
declare res: boolean = 8L > 7L declare res: boolean = 8L > 7L
if res then if res then

View File

@ -1,5 +1,5 @@
function: main function: main
return_type: int returns: int
body: body:
return 65537 return 65537
end body end body

View File

@ -1,6 +1,6 @@
module: Main module: Main
function: main function: main
return_type: int returns: int
body: body:
foo() foo()
@ -9,7 +9,7 @@ module: Main
end function end function
function: foo function: foo
return_type: int returns: int
body: body:
if false then if false then
return 1 return 1

View File

@ -1,6 +1,6 @@
module: Main module: Main
function: main function: main
return_type: int returns: int
body: body:
5 == 7 5 == 7
5 == 7s 5 == 7s

View File

@ -1,7 +1,7 @@
module: Main module: Main
import: os import: os
function: main function: main
return_type: void returns: void
body: body:
os.print(222) os.print(222)
end body end body

View File

@ -1,9 +1,9 @@
module: os module: os
import: os import: os
function: print function: print
parameter: params:
declare i1: int declare i1: int
return_type: void returns: void
body: body:
syscall("PRINT",i1) syscall("PRINT",i1)
end body end body

View File

@ -3,7 +3,7 @@ module: Main
globals: globals:
declare num2:int=10 declare num2:int=10
function: main function: main
return_type: void returns: void
body: body:
declare num1:int=11 declare num1:int=11
os.print(num1+num2+abc()) os.print(num1+num2+abc())
@ -11,7 +11,7 @@ module: Main
end function end function
function: abc function: abc
return_type: int returns: int
body: body:
return 1 return 1
end body end body

View File

@ -1,9 +1,9 @@
module: os module: os
import: os import: os
function: print function: print
parameter: params:
declare i1: int declare i1: int
return_type: void returns: void
body: body:
syscall("PRINT",i1) syscall("PRINT",i1)
end body end body

View File

@ -1,7 +1,7 @@
module: Main module: Main
import: os import: os
function: main function: main
return_type: int returns: int
body: body:
loop: loop:
init: init:

View File

@ -1,9 +1,9 @@
module: os module: os
import: os import: os
function: print function: print
parameter: params:
declare i1: int declare i1: int
return_type: void returns: void
body: body:
syscall("PRINT",i1) syscall("PRINT",i1)
end body end body

View File

@ -1,7 +1,7 @@
module: Main module: Main
import: os import: os
function: main function: main
return_type: int returns: int
body: body:
loop: loop:
init: init:

View File

@ -1,9 +1,9 @@
module: os module: os
import: os import: os
function: print function: print
parameter: params:
declare i1: int declare i1: int
return_type: void returns: void
body: body:
syscall("PRINT",i1) syscall("PRINT",i1)
end body end body

View File

@ -1,7 +1,7 @@
module: Main module: Main
import: os import: os
function: main function: main
return_type: int returns: int
body: body:
loop: loop:
init: init:
@ -31,8 +31,8 @@ module: Main
if j % 2 == 0 then if j % 2 == 0 then
continue continue
end if end if
print(i) os.print(i)
print(j) os.print(j)
end body end body
end loop end loop
end body end body

View File

@ -1,9 +1,9 @@
module: os module: os
import: os import: os
function: print function: print
parameter: params:
declare i1: int declare i1: int
return_type: void returns: void
body: body:
syscall("PRINT",i1) syscall("PRINT",i1)
end body end body

View File

@ -0,0 +1,26 @@
module: Main
import: os
function: main
returns: void
body:
declare n: int[][][][] = [
[
[ [17, 18], [19, 20] ],
[ [21, 22], [23, 24] ]
],
[
[ [25, 26], [27, 28] ],
[ [29, 30], [31, 32] ]
]
]
declare i: int = 0
declare j: int = 1
declare k: int = 0
declare l: int = 0
declare x: int = n[i][j][k][l]
os.print(x)
end body
end function
end module

View File

@ -0,0 +1,11 @@
module: os
import: os
function: print
params:
declare i1: int
returns: void
body:
syscall("PRINT",i1)
end body
end function
end module

View File

@ -1,7 +1,7 @@
module: Main module: Main
function: main function: main
parameter: params:
return_type: int returns: int
body: body:
return (1+2) / 3 * 4 + 2 *2 return (1+2) / 3 * 4 + 2 *2
end body end body

View File

@ -0,0 +1,12 @@
module: Main
import:os
function: main
returns: void
body:
declare arr: int[] = [1, 2, 3]
arr[0] = 5
os.print(arr[0])
end body
end function
end module

View File

@ -0,0 +1,11 @@
module: os
import: os
function: print
params:
declare i1: int
returns: void
body:
syscall("PRINT",i1)
end body
end function
end module

View File

@ -0,0 +1,35 @@
module: Main
import: os
globals:
declare sum: int = 123
function: main
params:
returns: int
body:
declare arr: int[][][][][][][][][][][][][][][][][][][][][][] = [[[[[[[[[[[[[[[[[[[[[[1], [2], [3]]]]]]]]]]]]]]]]]]]]]]
loop:
init:
declare i: int = 0
cond:
i < 3
step:
i = i + 1
body:
arr[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][i][0] = arr[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][i][0] + 1
end body
end loop
loop:
init:
declare i: int = 0
cond:
i < 3
step:
i = i + 1
body:
os.print(arr[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][i][0])
end body
end loop
return 0
end body
end function
end module

View File

@ -0,0 +1,11 @@
module: os
import: os
function: print
params:
declare i1: int
returns: void
body:
syscall("PRINT",i1)
end body
end function
end module

View File

@ -0,0 +1,15 @@
module: Main
import: ModuleA,os
function: main
returns: void
body:
declare sum: int = ModuleA.a+2
os.print(sum+1)
end body
end function
end module
module: ModuleA
globals:
declare const a: int =10
end module

View File

@ -0,0 +1,11 @@
module: os
import: os
function: print
params:
declare i1: int
returns: void
body:
syscall("PRINT",i1)
end body
end function
end module

View File

@ -0,0 +1,14 @@
module: Main
import: ModuleA
function: main
returns: void
body:
declare sum: byte = ModuleA.a
end body
end function
end module
module: ModuleA
globals:
declare const a: byte = 2b
end module

View File

@ -0,0 +1,26 @@
module: Main
import: ModuleA,os
globals:
declare const c: int = 10
function: main
returns: void
body:
declare a: int = ModuleA.sum(c,2)
os.print(a)
end body
end function
end module
module: ModuleA
globals:
declare const a: int = 10
function: sum
params:
declare a: int
declare b: int
returns: int
body:
return a + b
end body
end function
end module

View File

@ -0,0 +1,11 @@
module: os
import: os
function: print
params:
declare i1: int
returns: void
body:
syscall("PRINT",i1)
end body
end function
end module

View File

@ -0,0 +1,23 @@
module: Main
import: os,ModuleA
globals:
declare c: int = 10
function: main
returns: void
body:
c = ModuleA.getA()
os.print(c)
end body
end function
end module
module: ModuleA
globals:
declare a: int = 2
function: getA
returns: int
body:
return a
end body
end function
end module

View File

@ -0,0 +1,11 @@
module: os
import: os
function: print
params:
declare i1: int
returns: void
body:
syscall("PRINT",i1)
end body
end function
end module

View File

@ -0,0 +1,20 @@
module: Main
function: main
returns: void
body:
// 1. 常见转义符
declare sNewline : string = "换行示例:\n第二行"
declare sTab : string = "制表符示例:\t列二"
declare sBackslash: string = "反斜杠示例: C:\\Snow"
declare sDQuote : string = "双引号示例: \"Snow\""
declare sSQuote : string = "单引号示例: \'Snow\'"
declare sCarriage : string = "回车示例:\rCarriage"
declare sBackspace: string = "退格示例: ABC\bD"
declare sFormFeed : string = "换页示例:\fPage-2"
// 2. Unicode 转义
declare sUnicode : string = "𪚥𠮷: \u4F60\u597D, Snow!"
end body
end function
end module

View File

@ -1,7 +1,7 @@
module: Main module: Main
function: main function: main
parameter: params:
return_type: int returns: int
body: body:
declare n1: int =1 declare n1: int =1
declare n2: int =2 declare n2: int =2

View File

@ -1,7 +1,7 @@
module: Main module: Main
function: main function: main
parameter: params:
return_type: boolean returns: boolean
body: body:
declare b1: boolean =true declare b1: boolean =true

View File

@ -1,7 +1,7 @@
module: Main module: Main
function: main function: main
parameter: params:
return_type: int returns: int
body: body:
declare b1: boolean = true declare b1: boolean = true
loop: loop:

View File

@ -1,7 +1,7 @@
module: Main module: Main
function: main function: main
parameter: params:
return_type: int returns: int
body: body:
declare b1 :int = -1 declare b1 :int = -1
return b1 return b1

View File

@ -1,7 +1,7 @@
module: Main module: Main
function: main function: main
parameter: params:
return_type: boolean returns: boolean
body: body:
declare b1 :boolean = true declare b1 :boolean = true
return !b1 return !b1

View File

@ -1,7 +1,7 @@
module: Main module: Main
function: main function: main
parameter: params:
return_type: long returns: long
body: body:
declare n: long declare n: long
n = 2147483647 n = 2147483647

View File

@ -1,7 +1,7 @@
module: Math module: Math
function: main function: main
parameter: params:
return_type: int returns: int
body: body:
Math.factorial(6) Math.factorial(6)
return 0 return 0
@ -9,9 +9,9 @@ module: Math
end function end function
function: factorial function: factorial
parameter: params:
declare n:int declare n:int
return_type: int returns: int
body: body:
declare num1:int = 1 declare num1:int = 1
loop: loop:

48
pom.xml
View File

@ -7,7 +7,7 @@
<groupId>org.jcnc.snow</groupId> <groupId>org.jcnc.snow</groupId>
<artifactId>Snow</artifactId> <artifactId>Snow</artifactId>
<version>0.6.0</version> <version>0.8.0</version>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@ -70,11 +70,7 @@
</build> </build>
<profiles> <profiles>
<!-- <!-- 原生镜像构建: Linux 平台(使用 Docker builder 中的 musl 工具链) -->
原生镜像构建: Linux 平台
- 使用 GraalVM 的 native-image 工具,生成静态链接的可执行文件
- 依赖 musl libc需提前安装并配置 musl-gcc 工具链
-->
<profile> <profile>
<id>native-linux</id> <id>native-linux</id>
<activation> <activation>
@ -82,25 +78,39 @@
<family>unix</family> <family>unix</family>
</os> </os>
</activation> </activation>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.graalvm.buildtools</groupId> <groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId> <artifactId>native-maven-plugin</artifactId>
<version>${native.maven.plugin.version}</version> <version>${native.maven.plugin.version}</version>
<!-- 启用插件扩展,允许在 build 生命周期中无须额外配置 -->
<extensions>true</extensions> <extensions>true</extensions>
<configuration>
<mainClass>org.jcnc.snow.cli.SnowCLI</mainClass>
<imageName>Snow</imageName>
<outputDirectory>${project.build.directory}</outputDirectory>
<buildArgs>
<buildArg>--static</buildArg>
<buildArg>--libc=musl</buildArg>
<buildArg>--emit=build-report</buildArg>
<buildArg>-O2</buildArg>
<buildArg>-H:Class=org.jcnc.snow.cli.SnowCLI</buildArg>
<buildArg>-H:CCompilerPath=/opt/musl/bin/musl-gcc</buildArg>
<buildArg>-H:CLibraryPath=/opt/musl/lib</buildArg>
</buildArgs>
</configuration>
<executions> <executions>
<!-- 打包阶段生成原生可执行文件 -->
<execution> <execution>
<id>build-native</id> <id>build-native</id>
<goals> <goals>
<!-- compile-no-fork 在当前 JVM 进程中执行 native-image -->
<goal>compile-no-fork</goal> <goal>compile-no-fork</goal>
</goals> </goals>
<phase>package</phase> <phase>package</phase>
</execution> </execution>
<!-- 测试阶段运行原生镜像的测试 -->
<execution> <execution>
<id>test-native</id> <id>test-native</id>
<goals> <goals>
@ -109,24 +119,6 @@
<phase>test</phase> <phase>test</phase>
</execution> </execution>
</executions> </executions>
<configuration>
<buildArgs>
<!-- 静态链接 -->
<buildArg>--static</buildArg>
<!-- 指定 musl libc -->
<buildArg>--libc=musl</buildArg>
<!-- 输出构建报告 -->
<buildArg>--emit build-report</buildArg>
<!-- 优化级别 O2 -->
<buildArg>-O2</buildArg>
</buildArgs>
<environment>
<!-- 指定使用 musl 工具链 -->
<PATH>/opt/musl-1.2.5/bin:${env.PATH}</PATH>
<C_INCLUDE_PATH>/opt/musl-1.2.5/include</C_INCLUDE_PATH>
<LIBRARY_PATH>/opt/musl-1.2.5/lib</LIBRARY_PATH>
</environment>
</configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>

View File

@ -0,0 +1,82 @@
package org.jcnc.snow.common;
/**
* <p>
* 字符串转义/反转义工具类主要用于
* <ul>
* <li><b>编译期</b>将运行时的字符串安全地编码为单行形式用于 .water 指令文件的保存</li>
* <li><b>运行期</b>在虚拟机VM执行相关指令时将转义后的字符串还原成真实字符</li>
* </ul>
* <br>
* 转义规则兼容 Java 字符串转义包括 \n, \t, \r 等常见控制字符同时对于不可见或非 ASCII 字符会编码为 Unicode 形式 <code>uXXXX</code>
* </p>
*/
public final class StringEscape {
/**
* 工具类私有构造方法禁止实例化
*/
private StringEscape() {
}
/**
* <b>运行期方法</b>
* <p>将转义序列还原为实际字符</p>
*
* <ul>
* <li>支持常见的转义字符序列</li>
* <li>支持 uXXXX 形式的 Unicode 字符反转义</li>
* <li>对于无法识别的转义按原样输出</li>
* </ul>
*
* @param src 含有转义序列的字符串
* @return 反转义后的字符串原样还原
*/
public static String unescape(String src) {
StringBuilder out = new StringBuilder();
for (int i = 0; i < src.length(); i++) {
char c = src.charAt(i);
if (c != '\\') { // 非转义字符直接输出
out.append(c);
continue;
}
// 如果是最后一个字符为反斜杠则原样输出
if (i == src.length() - 1) {
out.append('\\');
break;
}
char n = src.charAt(++i); // 下一个字符
switch (n) {
case 'n' -> out.append('\n'); // 换行
case 't' -> out.append('\t'); // 制表符
case 'r' -> out.append('\r'); // 回车
case 'b' -> out.append('\b'); // 退格
case 'f' -> out.append('\f'); // 换页
case '\\' -> out.append('\\'); // 反斜杠
case '"' -> out.append('"'); // 双引号
case '\'' -> out.append('\''); // 单引号
case 'u' -> {
// Unicode 转义需读取接下来的 4 位十六进制数字
if (i + 4 <= src.length() - 1) {
String hex = src.substring(i + 1, i + 5);
try {
out.append((char) Integer.parseInt(hex, 16));
i += 4;
} catch (NumberFormatException ignore) {
// 非法 hex原样输出
out.append("\\u").append(hex);
i += 4;
}
} else {
// 字符串末尾长度不足原样输出
out.append("\\u");
}
}
default -> out.append(n); // 其他未定义的转义序列原样输出
}
}
return out.toString();
}
}

View File

@ -10,45 +10,46 @@ import org.jcnc.snow.compiler.ir.value.IRConstant;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
import org.jcnc.snow.vm.engine.VMOpCode; import org.jcnc.snow.vm.engine.VMOpCode;
import java.util.*; import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* <b>CallGenerator - IR {@code CallInstruction} 生成 VM 指令</b> * {@code CallGenerator} 负责将 IR 层的 {@link CallInstruction} 生成对应的 VM 层函数调用指令
*
* <p> * <p>
* 本类负责将中间表示IR中的函数调用指令 {@link CallInstruction} 转换为虚拟机VM指令 * 支持 syscall普通函数调用一维/多维数组下标访问以及字符串常量池绑定等功能
* 支持普通函数调用和特殊的 syscall 指令转换
* </p> * </p>
* *
* <p>
* <b>能力说明</b>
* <ul> * <ul>
* <li>支持识别 {@code IRConstant} 直接字面量或已绑定到虚拟寄存器的字符串常量直接降级为 {@code SYSCALL &lt;SUBCMD&gt;} 指令</li> * <li>syscall: 支持字符串常量与寄存器到子命令的绑定与解析</li>
* <li>对普通函数完成参数加载调用返回值保存等指令生成</li> * <li>数组下标访问: 支持所有主流基础类型 __index_b/s/i/l/f/d/r</li>
* <li>普通函数: 支持自动推断返回值类型与槽位类型</li>
* </ul> * </ul>
* </p>
*/ */
public class CallGenerator implements InstructionGenerator<CallInstruction> { public class CallGenerator implements InstructionGenerator<CallInstruction> {
/** /**
* <虚拟寄存器 id, 对应的字符串常量> * 字符串常量池用于绑定虚拟寄存器 id 到字符串值 syscall 子命令使用
* <p>用于记录虚拟寄存器与其绑定字符串常量的映射 {@link LoadConstGenerator} 在编译期间填充</p> * <br>
* 使用 ConcurrentHashMap 保证并发安全所有 registerStringConst 的注册和读取都是线程安全的
*/ */
private static final Map<Integer, String> STRING_CONST_POOL = new HashMap<>(); private static final Map<Integer, String> STRING_CONST_POOL = new ConcurrentHashMap<>();
/** /**
* 注册字符串常量到虚拟寄存器 * 注册一个字符串常量绑定到虚拟寄存器 id
* <p> {@link LoadConstGenerator} 在加载字符串常量时调用</p>
* *
* @param regId 虚拟寄存器 id * @param regId 虚拟寄存器 id
* @param value 字符串常量 * @param value 字符串常量
*/ */
public static void registerStringConst(int regId, String value) { public static void registerStringConst(int regId, String value) {
STRING_CONST_POOL.put(regId, value); STRING_CONST_POOL.put(regId, value);
} }
/** /**
* 返回本生成器支持的 IR 指令类型CallInstruction * 返回本生成器支持的 IR 指令类型
*
* @return {@link CallInstruction} 类型的 Class 对象
*/ */
@Override @Override
public Class<CallInstruction> supportedClass() { public Class<CallInstruction> supportedClass() {
@ -56,11 +57,11 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
} }
/** /**
* 生成 VM 指令的主逻辑 * 生成指定的调用指令包括 syscall数组下标普通函数
* *
* @param ins 当前 IR 指令函数调用 * @param ins IR 层的调用指令
* @param out 指令输出构建器 * @param out VM 程序构建器
* @param slotMap IR 虚拟寄存器与物理槽位映射 * @param slotMap 寄存器到槽位的映射表
* @param currentFn 当前函数名 * @param currentFn 当前函数名
*/ */
@Override @Override
@ -68,79 +69,357 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
VMProgramBuilder out, VMProgramBuilder out,
Map<IRVirtualRegister, Integer> slotMap, Map<IRVirtualRegister, Integer> slotMap,
String currentFn) { String currentFn) {
String fn = ins.getFunctionName();
/* ========== 特殊处理 syscall 调用 ========== */ // 特殊处理 syscall 调用
if ("syscall".equals(ins.getFunctionName()) || if ("syscall".equals(fn) || fn.endsWith(".syscall")) {
ins.getFunctionName().endsWith(".syscall")) { generateSyscall(ins, out, slotMap, fn);
List<IRValue> args = ins.getArguments();
if (args.isEmpty()) {
throw new IllegalStateException("syscall 需要子命令参数");
}
// ---------- 0. 解析 syscall 子命令 ----------
// 子命令支持 IRConstant直接字面量或虚拟寄存器需已绑定字符串
String subcmd;
IRValue first = args.getFirst();
if (first instanceof IRConstant(Object value)) { // 直接字面量
if (!(value instanceof String s))
throw new IllegalStateException("syscall 第一个参数必须是字符串常量");
subcmd = s.toUpperCase(Locale.ROOT);
} else if (first instanceof IRVirtualRegister vr) { // 虚拟寄存器
// 从常量池中查找是否已绑定字符串
subcmd = Optional.ofNullable(STRING_CONST_POOL.get(vr.id()))
.orElseThrow(() ->
new IllegalStateException("未找到 syscall 字符串常量绑定: " + vr));
subcmd = subcmd.toUpperCase(Locale.ROOT);
} else {
throw new IllegalStateException("syscall 第一个参数必须是字符串常量");
}
// ---------- 1. 压栈其余 syscall 参数index 1 开始 ----------
for (int i = 1; i < args.size(); i++) {
IRVirtualRegister vr = (IRVirtualRegister) args.get(i);
int slotId = slotMap.get(vr);
char t = out.getSlotType(slotId);
if (t == '\0') t = 'I'; // 默认整型
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId);
}
// ---------- 2. 生成 SYSCALL 指令 ----------
out.emit(VMOpCode.SYSCALL + " " + subcmd);
return; // syscall 无返回值直接返回
}
/* ========== 普通函数调用 ========== */
// ---------- 1. 推断返回值类型 void 返回时用 ----------
char retType = 'I'; // 默认为整型
if (!ins.getArguments().isEmpty()) {
int firstSlot = slotMap.get((IRVirtualRegister) ins.getArguments().getFirst());
retType = out.getSlotType(firstSlot);
if (retType == '\0') retType = 'I';
}
// ---------- 2. 加载全部实参 ----------
for (var arg : ins.getArguments()) {
int slotId = slotMap.get((IRVirtualRegister) arg);
char t = out.getSlotType(slotId);
if (t == '\0') t = 'I'; // 默认整型
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId);
}
// ---------- 3. 发出 CALL 指令 ----------
out.emitCall(ins.getFunctionName(), ins.getArguments().size());
// ---------- 3.5 如果为 void 返回直接结束 ----------
if ("void".equals(GlobalFunctionTable.getReturnType(ins.getFunctionName()))) {
return; return;
} }
// ---------- 4. 保存返回值 ---------- // 各种一维数组类型byte/short/int/long/float/double/boolean读取
int destSlot = slotMap.get(ins.getDest()); switch (fn) {
case "__index_b" -> {
generateIndexInstruction(ins, out, slotMap, 'B');
return;
}
case "__index_s" -> {
generateIndexInstruction(ins, out, slotMap, 'S');
return;
}
case "__index_i" -> {
generateIndexInstruction(ins, out, slotMap, 'I');
return;
}
case "__index_l" -> {
generateIndexInstruction(ins, out, slotMap, 'L');
return;
}
case "__index_f" -> {
generateIndexInstruction(ins, out, slotMap, 'F');
return;
}
case "__index_d" -> {
generateIndexInstruction(ins, out, slotMap, 'D');
return;
}
case "__index_r" -> {
generateIndexInstruction(ins, out, slotMap, 'R');
return;
}
case "__setindex_b" -> {
generateSetIndexInstruction(ins, out, slotMap, 'B');
return;
}
case "__setindex_s" -> {
generateSetIndexInstruction(ins, out, slotMap, 'S');
return;
}
case "__setindex_i" -> {
generateSetIndexInstruction(ins, out, slotMap, 'I');
return;
}
case "__setindex_l" -> {
generateSetIndexInstruction(ins, out, slotMap, 'L');
return;
}
case "__setindex_f" -> {
generateSetIndexInstruction(ins, out, slotMap, 'F');
return;
}
case "__setindex_d" -> {
generateSetIndexInstruction(ins, out, slotMap, 'D');
return;
}
case "__setindex_r" -> {
generateSetIndexInstruction(ins, out, slotMap, 'R');
return;
}
}
// 普通函数调用
generateNormalCall(ins, out, slotMap, fn);
}
// ========== 私有辅助方法 ==========
/**
* 生成数组元素赋值指令arr[idx] = value无返回值
* <p>
* 调用栈压栈顺序为arr (引用类型, R)idx (整型, I)value (元素类型, T)
* 执行 SYSCALL ARR_SET 指令以完成数组赋值操作
* </p>
*
* @param ins 当前的调用指令包含参数信息
* @param out VM 指令生成器用于输出 VM 指令
* @param slotMap 虚拟寄存器与槽位的映射表
* @param valType 待写入的 value 的类型标识'B': byte, 'S': short, 'I': int, 'L': long, 'F': float, 'D': double, 其余为引用类型'R'
* @throws IllegalStateException 如果参数数量不为3则抛出异常
*/
private void generateSetIndexInstruction(CallInstruction ins,
VMProgramBuilder out,
Map<IRVirtualRegister, Integer> slotMap,
char valType) {
List<IRValue> args = ins.getArguments();
if (args.size() != 3) {
// 参数数量错误抛出异常并输出实际参数列表
throw new IllegalStateException(
"[CallGenerator] __setindex_* 需要三个参数(arr, idx, value),实际: " + args);
}
// 第一个参数为数组对象压入引用类型寄存器'R' arr
loadArgument(out, slotMap, args.get(0), 'R', ins.getFunctionName());
// 第二个参数为索引值压入整型寄存器'I' idx
loadArgument(out, slotMap, args.get(1), 'I', ins.getFunctionName());
// 第三个参数为待赋值元素根据元素类型压入相应类型寄存器
// 支持类型'B'(byte), 'S'(short), 'I'(int), 'L'(long), 'F'(float), 'D'(double)
// 其他情况如引用类型'R'处理
switch (valType) {
case 'B' -> loadArgument(out, slotMap, args.get(2), 'B', ins.getFunctionName());
case 'S' -> loadArgument(out, slotMap, args.get(2), 'S', ins.getFunctionName());
case 'I' -> loadArgument(out, slotMap, args.get(2), 'I', ins.getFunctionName());
case 'L' -> loadArgument(out, slotMap, args.get(2), 'L', ins.getFunctionName());
case 'F' -> loadArgument(out, slotMap, args.get(2), 'F', ins.getFunctionName());
case 'D' -> loadArgument(out, slotMap, args.get(2), 'D', ins.getFunctionName());
default -> loadArgument(out, slotMap, args.get(2), 'R', ins.getFunctionName());
}
// 输出 VM 指令SYSCALL ARR_SET完成数组元素写入操作
out.emit(VMOpCode.SYSCALL + " " + "ARR_SET");
}
/**
* 解析 syscall 子命令第一个参数支持字符串常量与已绑定字符串的虚拟寄存器
*
* @param arg 子命令参数应为字符串常量或寄存器
* @param fn 当前函数名仅用于报错信息
* @return 子命令大写字符串
* @throws IllegalStateException 如果参数不是字符串常量或已绑定寄存器
*/
private String resolveSyscallSubcmd(IRValue arg, String fn) {
switch (arg) {
case IRConstant(String s) -> {
return s.toUpperCase(Locale.ROOT);
}
case IRConstant(Object value) -> throw new IllegalStateException(
"[CallGenerator] syscall 第一个参数必须是字符串常量 (function: %s, value: %s)"
.formatted(fn, value)
);
case IRVirtualRegister(int id) -> {
String s = STRING_CONST_POOL.get(id);
if (s == null) {
throw new IllegalStateException(
"[CallGenerator] 未找到 syscall 字符串常量绑定 (function: %s, regId: %d)"
.formatted(fn, id)
);
}
return s.toUpperCase(Locale.ROOT);
}
case null, default -> throw new IllegalStateException(
"[CallGenerator] syscall 第一个参数必须是字符串常量或已绑定字符串的寄存器 (function: %s, arg: %s)"
.formatted(fn, arg)
);
}
}
/**
* 加载一个参数到栈支持指定默认类型如果未设置类型则采用默认类型
*
* @param out VM 程序构建器
* @param slotMap 寄存器到槽位的映射
* @param arg 参数值应为虚拟寄存器
* @param defaultType 默认类型
* @param fn 当前函数名用于错误提示
* @throws IllegalStateException 如果参数不是虚拟寄存器或槽位未找到
*/
private void loadArgument(VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, IRValue arg, char defaultType, String fn) {
if (!(arg instanceof IRVirtualRegister vr)) {
throw new IllegalStateException(
"[CallGenerator] 参数必须为虚拟寄存器 (function: %s, arg: %s)".formatted(fn, arg));
}
Integer slot = slotMap.get(vr);
if (slot == null) {
throw new IllegalStateException(
"[CallGenerator] 未找到虚拟寄存器的槽位映射 (function: %s, reg: %s)".formatted(fn, vr));
}
char t = out.getSlotType(slot);
if (t == '\0') t = defaultType;
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slot);
}
/**
* 生成一维/多维数组的下标访问指令支持类型分派
* <p>
* 本方法用于将 IR 层的数组下标访问表达式 arr[idx]生成对应的 VM 指令序列
* 支持 byte/short/int/long/float/double/reference 等所有基础类型的数组元素访问
* </p>
*
* <ul>
* <li>1. 依次加载数组参数arr和下标参数idx到操作数栈</li>
* <li>2. 发出 ARR_GET 系统调用指令SYSCALL ARR_GET通过 VM 访问对应元素</li>
* <li>3. 根据元素声明类型将结果写入目标槽位支持类型分派B/S/I/L/F/D/R</li>
* <li>4. 若参数或返回寄存器缺失则抛出异常提示</li>
* </ul>
*
* @param ins 下标访问对应的 IR 调用指令函数名通常为 __index_x
* @param out VM 程序构建器用于发出 VM 指令
* @param slotMap IR 虚拟寄存器到 VM 槽位的映射表
* @param retType 元素类型标识'B' byte, 'S' short, 'I' int, 'L' long, 'F' float, 'D' double, 'R' ref/obj
* @throws IllegalStateException 参数个数不符缺少目标寄存器未找到槽位等情况
*/
private void generateIndexInstruction(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, char retType) {
String fn = ins.getFunctionName();
List<IRValue> args = ins.getArguments();
if (args.size() != 2) {
throw new IllegalStateException(
"[CallGenerator] %s 需要两个参数(arr, idx),实际: %s".formatted(fn, args));
}
// 加载数组参数寄存器类型按 R/ref 处理默认对象槽位
loadArgument(out, slotMap, args.get(0), 'R', fn);
// 加载下标参数寄存器类型按 I/int 处理
loadArgument(out, slotMap, args.get(1), 'I', fn);
// 发出 ARR_GET 系统调用元素访问由 VM 完成类型分派
out.emit(VMOpCode.SYSCALL + " " + "ARR_GET");
// 保存返回值到目标寄存器
IRVirtualRegister dest = ins.getDest();
if (dest == null) {
throw new IllegalStateException(
"[CallGenerator] %s 需要有目标寄存器用于保存返回值".formatted(fn));
}
Integer destSlot = slotMap.get(dest);
if (destSlot == null) {
throw new IllegalStateException(
"[CallGenerator] %s 未找到目标寄存器的槽位映射 (dest: %s)".formatted(fn, dest));
}
// 按元素类型分派写入 VM 槽位
switch (retType) {
case 'B' -> {
out.emit(OpHelper.opcode("B_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'B');
}
case 'S' -> {
out.emit(OpHelper.opcode("S_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'S');
}
case 'I' -> {
out.emit(OpHelper.opcode("I_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'I');
}
case 'L' -> {
out.emit(OpHelper.opcode("L_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'L');
}
case 'F' -> {
out.emit(OpHelper.opcode("F_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'F');
}
case 'D' -> {
out.emit(OpHelper.opcode("D_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'D');
}
default -> {
out.emit(OpHelper.opcode("R_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'R');
}
}
}
/**
* 生成 syscall 指令分支逻辑
* <ol>
* <li>解析 syscall 子命令</li>
* <li>压栈剩余参数</li>
* <li>发出 SYSCALL 指令</li>
* <li>若有返回值则保存至目标槽位</li>
* </ol>
*
* @param ins 调用指令
* @param out VM 程序构建器
* @param slotMap 寄存器到槽位映射
* @param fn 当前函数名
*/
private void generateSyscall(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, String fn) {
List<IRValue> args = ins.getArguments();
if (args.isEmpty()) {
throw new IllegalStateException(
"[CallGenerator] syscall 需要子命令参数 (function: %s)".formatted(fn));
}
// 0. 解析 syscall 子命令第一个参数
String subcmd = resolveSyscallSubcmd(args.getFirst(), fn);
// 1. 压栈其余 syscall 参数 index 1 开始
for (int i = 1; i < args.size(); i++) {
loadArgument(out, slotMap, args.get(i), 'R', fn);
}
// 2. 生成 SYSCALL 指令
out.emit(VMOpCode.SYSCALL + " " + subcmd);
// 3. 有返回值则保存到目标槽位
IRVirtualRegister dest = ins.getDest();
if (dest != null) {
Integer destSlot = slotMap.get(dest);
if (destSlot == null) {
throw new IllegalStateException(
"[CallGenerator] syscall 未找到目标寄存器的槽位映射 (function: %s, dest: %s)"
.formatted(fn, dest));
}
out.emit(OpHelper.opcode("I_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'I');
}
}
/**
* 生成普通函数调用指令
* <ol>
* <li>推断返回值类型</li>
* <li>压栈所有参数</li>
* <li>生成 CALL 指令</li>
* <li>保存返回值若非 void</li>
* </ol>
*
* @param ins 调用指令
* @param out VM 程序构建器
* @param slotMap 寄存器到槽位映射
* @param fn 当前函数名
*/
private void generateNormalCall(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, String fn) {
// 1. 推断返回值类型首字母大写缺省为 'I'
String retTypeName = GlobalFunctionTable.getReturnType(fn);
char retType = (retTypeName != null && !retTypeName.isEmpty()) ? Character.toUpperCase(retTypeName.charAt(0)) : 'I';
// 2. 压栈所有参数
for (IRValue arg : ins.getArguments()) {
loadArgument(out, slotMap, arg, 'R', fn);
}
// 3. 发出 CALL 指令
out.emitCall(fn, ins.getArguments().size());
// 3.5 void 返回直接结束
if ("void".equals(retTypeName)) {
return;
}
// 4. 保存返回值到目标寄存器
IRVirtualRegister dest = ins.getDest();
if (dest == null) {
throw new IllegalStateException(
"[CallGenerator] 普通函数调用未找到目标寄存器 (function: %s)".formatted(fn));
}
Integer destSlot = slotMap.get(dest);
if (destSlot == null) {
throw new IllegalStateException(
"[CallGenerator] 普通函数调用未找到目标寄存器的槽位映射 (function: %s, dest: %s)".formatted(fn, dest));
}
out.emit(OpHelper.opcode(retType + "_STORE") + " " + destSlot); out.emit(OpHelper.opcode(retType + "_STORE") + " " + destSlot);
out.setSlotType(destSlot, retType); out.setSlotType(destSlot, retType);
} }

View File

@ -7,34 +7,136 @@ import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction;
import org.jcnc.snow.compiler.ir.value.IRConstant; import org.jcnc.snow.compiler.ir.value.IRConstant;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
/** /**
* <b>LoadConstGenerator - IR {@code LoadConstInstruction} 生成 VM 指令</b> * <b>LoadConstGenerator</b>
* *
* <p> * <p>
* 本类负责将 IR 层的常量加载指令 {@link LoadConstInstruction} 转换为对应的虚拟机指令 * This generator converts an IR-level {@link LoadConstInstruction} into corresponding VM instructions.
* 额外支持如果常量类型为 {@code String}会同步登记到 * If the constant is a {@code String}, it will also be registered in the
* {@link CallGenerator} 的字符串常量池方便 syscall 降级场景使用 * {@link CallGenerator} string constant pool for later use.
* </p>
*
* <p>
* Key implementation notes:
* <ul>
* <li>When the constant is an array (List), type information is preserved in the R_PUSH payload:</li>
* <li>Float values get an <code>f</code> suffix (e.g., 0.1f)</li>
* <li>Long values get an <code>L</code> suffix (e.g., 123L)</li>
* <li>Double and Integer values use their default string format (e.g., 1.0, 42)</li>
* <li>Nested arrays are recursively serialized with correct type suffixes.</li>
* </ul>
* This prevents type confusion on the VM side (e.g., float being misread as double)
* and avoids cast exceptions during store operations.
* </p> * </p>
*/ */
public class LoadConstGenerator implements InstructionGenerator<LoadConstInstruction> { public class LoadConstGenerator implements InstructionGenerator<LoadConstInstruction> {
/** /**
* 指定本生成器支持的 IR 指令类型LoadConstInstruction * Formats a constant value for use as a VM instruction payload.
* For lists, recursively formats each element with type suffixes where appropriate.
*
* @param v The constant value.
* @return The formatted string payload for VM code.
*/ */
private static String formatConst(Object v) {
return formatConst(v, false);
}
/**
* Recursively formats constant values (including nested arrays), preserving
* type suffixes and escaping strings. Used internally for array/list handling.
*
* @param v The constant value.
* @param insideArray Whether this value is inside an array context (controls type suffixing).
* @return The formatted string for VM code.
*/
private static String formatConst(Object v, boolean insideArray) {
if (v instanceof List<?> list) {
// Recursively process each element in the list
return "[" + list.stream()
.map(x -> formatConst(x, true))
.collect(Collectors.joining(", ")) + "]";
}
if (v instanceof String s) {
// Escape and wrap the string in double quotes, to avoid line breaks or control chars breaking VM code
return "\"" + escape(s) + "\"";
}
if (v instanceof Float f) {
float fv = f;
String s = (fv == (long) fv) ? String.format("%.1f", fv) : f.toString();
return insideArray ? (s + "f") : s;
}
if (v instanceof Long l) {
return insideArray ? (l + "L") : l.toString();
}
if (v instanceof Double d) {
double dv = d;
return (dv == (long) dv) ? String.format("%.1f", dv) : Double.toString(dv);
}
if (v instanceof Short s) {
return insideArray ? (s + "s") : Short.toString(s);
}
if (v instanceof Byte b) {
return insideArray ? (b + "b") : Byte.toString(b);
}
if (v instanceof Boolean b) {
return b ? "1" : "0";
}
return String.valueOf(v);
}
/**
* Escapes a string for use in VM code: replaces control characters and all non-ASCII characters
* with their corresponding escape sequences, so the .water file remains single-line and parseable.
* Supported escapes: \n, \r, \t, \f, \b, \", \', \\, and Unicode escapes like "uXXXX" for non-ASCII.
*
* @param s The input string.
* @return The escaped string.
*/
private static String escape(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); ++i) {
char ch = s.charAt(i);
switch (ch) {
case '\n' -> sb.append("\\n");
case '\r' -> sb.append("\\r");
case '\t' -> sb.append("\\t");
case '\f' -> sb.append("\\f");
case '\b' -> sb.append("\\b");
case '\"' -> sb.append("\\\"");
case '\'' -> sb.append("\\'");
case '\\' -> sb.append("\\\\");
default -> {
// Escape non-ASCII and control characters using uXXXX
if (ch < 0x20 || ch > 0x7E) {
sb.append(String.format("\\u%04X", (int) ch));
} else {
sb.append(ch);
}
}
}
}
return sb.toString();
}
@Override @Override
public Class<LoadConstInstruction> supportedClass() { public Class<LoadConstInstruction> supportedClass() {
return LoadConstInstruction.class; return LoadConstInstruction.class;
} }
/** /**
* 生成 VM 指令主流程 * Generates VM code for a LoadConstInstruction.
* Produces PUSH and STORE instructions, sets the slot type,
* and registers string constants if necessary.
* *
* @param ins 当前常量加载指令 * @param ins The IR instruction to generate.
* @param out 指令输出构建器 * @param out The output program builder.
* @param slotMap 虚拟寄存器与物理槽位映射 * @param slotMap The mapping from IR virtual register to physical slot.
* @param currentFn 当前函数名 * @param currentFn The current function name.
*/ */
@Override @Override
public void generate(LoadConstInstruction ins, public void generate(LoadConstInstruction ins,
@ -42,34 +144,35 @@ public class LoadConstGenerator implements InstructionGenerator<LoadConstInstruc
Map<IRVirtualRegister, Integer> slotMap, Map<IRVirtualRegister, Integer> slotMap,
String currentFn) { String currentFn) {
/* 1. 获取常量值 */ // 1. Retrieve the constant value from the instruction
IRConstant constant = (IRConstant) ins.operands().getFirst(); IRConstant constant = (IRConstant) ins.operands().getFirst();
Object value = constant.value(); Object value = constant.value();
/* 2. 生成 PUSH 指令,将常量值入栈 */ // 2. Format and emit the PUSH instruction (arrays will use type-aware formatting)
out.emit(OpHelper.pushOpcodeFor(value) + " " + value); String payload = formatConst(value);
out.emit(OpHelper.pushOpcodeFor(value) + " " + payload);
/* 3. STORE 到目标槽位 */ // 3. Emit STORE to the destination slot
int slot = slotMap.get(ins.dest()); int slot = slotMap.get(ins.dest());
out.emit(OpHelper.storeOpcodeFor(value) + " " + slot); out.emit(OpHelper.storeOpcodeFor(value) + " " + slot);
/* 4. 标记槽位数据类型(用于后续类型推断和 LOAD/STORE 指令选择) */ // 4. Mark the slot's data type for later use (type inference, instruction selection, etc.)
char prefix = switch (value) { char prefix = switch (value) {
case Integer _ -> 'I'; // 整型 case Integer _ -> 'I'; // Integer
case Long _ -> 'L'; // 长整型 case Long _ -> 'L'; // Long
case Short _ -> 'S'; // 短整型 case Short _ -> 'S'; // Short
case Byte _ -> 'B'; // 字节型 case Byte _ -> 'B'; // Byte
case Double _ -> 'D'; // 双精度 case Double _ -> 'D'; // Double
case Float _ -> 'F'; // 单精度 case Float _ -> 'F'; // Float
case Boolean _ -> 'I'; // 布尔类型用 I 处理 case Boolean _ -> 'I'; // Booleans are treated as integers (1/0)
case String _ -> 'R'; // 字符串常量 case String _ -> 'R'; // Reference type for strings
case null, default -> // 其它类型异常 case java.util.List<?> _ -> 'R'; // Reference type for arrays/lists
throw new IllegalStateException("未知的常量类型: " case null, default -> throw new IllegalStateException("Unknown constant type: "
+ (value != null ? value.getClass() : null)); + (value != null ? value.getClass() : null));
}; };
out.setSlotType(slot, prefix); out.setSlotType(slot, prefix);
/* 5. 如果是字符串常量,则登记到 CallGenerator 的常量池,便于 syscall 字符串降级使用 */ // 5. Register the string constant for the string constant pool if needed
if (value instanceof String s) { if (value instanceof String s) {
CallGenerator.registerStringConst(ins.dest().id(), s); CallGenerator.registerStringConst(ins.dest().id(), s);
} }

View File

@ -234,6 +234,7 @@ public final class OpHelper {
if (v instanceof Double) return "D"; if (v instanceof Double) return "D";
if (v instanceof Float) return "F"; if (v instanceof Float) return "F";
if (v instanceof String) return "R"; //引用类型 if (v instanceof String) return "R"; //引用类型
if (v instanceof java.util.List) return "R"; // 引用类型数组等
throw new IllegalStateException("Unknown const type: " + v.getClass()); throw new IllegalStateException("Unknown const type: " + v.getClass());
} }

View File

@ -1,6 +1,8 @@
package org.jcnc.snow.compiler.ir.builder; package org.jcnc.snow.compiler.ir.builder;
import org.jcnc.snow.compiler.ir.common.GlobalConstTable;
import org.jcnc.snow.compiler.ir.core.IROpCode; import org.jcnc.snow.compiler.ir.core.IROpCode;
import org.jcnc.snow.compiler.ir.core.IRValue;
import org.jcnc.snow.compiler.ir.instruction.CallInstruction; import org.jcnc.snow.compiler.ir.instruction.CallInstruction;
import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction; import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction;
import org.jcnc.snow.compiler.ir.instruction.UnaryOperationInstruction; import org.jcnc.snow.compiler.ir.instruction.UnaryOperationInstruction;
@ -11,40 +13,26 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
import org.jcnc.snow.compiler.parser.ast.*; import org.jcnc.snow.compiler.parser.ast.*;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import java.util.*; import java.util.ArrayList;
import java.util.List;
/** /**
* <b>ExpressionBuilder - 表达式 IR 构建器</b> * {@code ExpressionBuilder} 表达式 IR 构建器
*
* <p> * <p>
* 负责将 AST 表达式节点递归转换为 IR 虚拟寄存器操作并生成对应的 IR 指令序列 * 负责将 AST 表达式节点递归转换为 IR 虚拟寄存器操作并生成对应的 IR 指令序列
* 支持字面量标识符二元表达式一元表达式函数调用等多种类型表达式 * 支持字面量标识符二元表达式一元表达式函数调用数组下标等多种类型表达式
* </p>
*
* <p>
* 主要功能
* <ul> * <ul>
* <li>将表达式节点映射为虚拟寄存器</li> * <li>将表达式节点映射为虚拟寄存器</li>
* <li>为每种表达式类型生成对应 IR 指令</li> * <li>为每种表达式类型生成对应 IR 指令</li>
* <li>支持表达式嵌套的递归构建</li> * <li>支持表达式嵌套的递归构建</li>
* <li>支持写入指定目标寄存器避免冗余的 move 指令</li> * <li>支持写入指定目标寄存器避免冗余的 move 指令</li>
* <li>支持 IndexExpressionNode 的编译期折叠arr[2]并在运行时降级为 __index_i/__index_r</li>
* </ul> * </ul>
* </p>
*/ */
public record ExpressionBuilder(IRContext ctx) { public record ExpressionBuilder(IRContext ctx) {
/* ───────────────── 顶层入口 ───────────────── */
/** /**
* 构建任意 AST 表达式节点自动为其分配一个新的虚拟寄存器并返回该寄存器 * 构建表达式返回结果寄存器
*
* <p>
* 这是表达式 IR 生成的核心入口它会根据不同的表达式类型进行分派递归构建 IR 指令
* </p>
*
* @param expr 任意 AST 表达式节点
* @return 存储该表达式结果的虚拟寄存器
* @throws IllegalStateException 遇到不支持的表达式类型或未定义标识符
*/ */
public IRVirtualRegister build(ExpressionNode expr) { public IRVirtualRegister build(ExpressionNode expr) {
return switch (expr) { return switch (expr) {
@ -58,53 +46,96 @@ public record ExpressionBuilder(IRContext ctx) {
case IdentifierNode id -> { case IdentifierNode id -> {
// 查找当前作用域中的变量寄存器 // 查找当前作用域中的变量寄存器
IRVirtualRegister reg = ctx.getScope().lookup(id.name()); IRVirtualRegister reg = ctx.getScope().lookup(id.name());
if (reg == null) if (reg == null) throw new IllegalStateException("未定义标识符: " + id.name());
throw new IllegalStateException("未定义标识符: " + id.name());
yield reg; yield reg;
} }
// 模块常量 / 全局变量 ModuleA.a
case MemberExpressionNode mem -> buildMember(mem);
// 二元表达式 a+b, x==y // 二元表达式 a+b, x==y
case BinaryExpressionNode bin -> buildBinary(bin); case BinaryExpressionNode bin -> buildBinary(bin);
// 函数/方法调用表达式 // 函数/方法调用表达式
case CallExpressionNode call -> buildCall(call); case CallExpressionNode call -> buildCall(call);
// 一元表达式 -a, !a // 一元表达式 -a, !a
case UnaryExpressionNode un -> buildUnary(un); case UnaryExpressionNode un -> buildUnary(un);
case IndexExpressionNode idx -> buildIndex(idx);
case ArrayLiteralNode arr -> buildArrayLiteral(arr);
// 默认分支遇到未知表达式类型则直接抛异常 // 默认分支遇到未知表达式类型则直接抛异常
default -> throw new IllegalStateException( default -> throw new IllegalStateException(
"不支持的表达式类型: " + expr.getClass().getSimpleName()); "不支持的表达式类型: " + expr.getClass().getSimpleName());
}; };
} }
/**
* 成员访问表达式构建
*
* @param mem 成员表达式节点
* @return 存储结果的虚拟寄存器
*/
private IRVirtualRegister buildMember(MemberExpressionNode mem) {
if (!(mem.object() instanceof IdentifierNode id)) {
throw new IllegalStateException("不支持的成员访问对象类型: "
+ mem.object().getClass().getSimpleName());
}
String qualified = id.name() + "." + mem.member();
/* 1) 尝试直接获取已有寄存器绑定 */
IRVirtualRegister reg = ctx.getScope().lookup(qualified);
if (reg != null) {
return reg;
}
/* 2) 折叠为编译期常量:先查作用域,再查全局常量表 */
Object v = ctx.getScope().getConstValue(qualified);
if (v == null) {
v = GlobalConstTable.get(qualified);
}
if (v != null) {
IRVirtualRegister r = ctx.newRegister();
ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(v)));
return r;
}
throw new IllegalStateException("未定义的常量: " + qualified);
}
/* ───────────────── 写入指定寄存器 ───────────────── */ /* ───────────────── 写入指定寄存器 ───────────────── */
/** /**
* 生成表达式并将其结果直接写入目标寄存器避免冗余的 move 操作 * 将表达式节点 {@link ExpressionNode} 的结果写入指定的虚拟寄存器 {@code dest}
*
* <p> * <p>
* 某些简单表达式如字面量变量名可以直接写入目标寄存器复杂表达式则会先 build 到新寄存器 move 到目标寄存器 * 按表达式类型分派处理包括
* <ul>
* <li>字面量数字字符串布尔数组生成 loadConst 指令直接写入目标寄存器</li>
* <li>变量标识符查表获取源寄存器 move 到目标寄存器</li>
* <li>二元表达式下标调用表达式递归生成子表达式结果并写入目标寄存器</li>
* <li>其它类型统一先 build 到临时寄存器 move 到目标寄存器</li>
* </ul>
* </p> * </p>
* *
* @param node 要生成的表达式节点 * @param node 要求值的表达式节点
* @param dest 目标虚拟寄存器用于存储结果 * @param dest 结果目标虚拟寄存器
* @throws IllegalStateException 若标识符未定义如变量未声明时引用
*/ */
public void buildInto(ExpressionNode node, IRVirtualRegister dest) { public void buildInto(ExpressionNode node, IRVirtualRegister dest) {
switch (node) { switch (node) {
// 数字字面量生成 loadConst 指令写入目标寄存器 // 数字字面量生成 loadConst 指令将数值常量写入目标寄存器
case NumberLiteralNode n -> case NumberLiteralNode n -> InstructionFactory.loadConstInto(
InstructionFactory.loadConstInto(
ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value())); ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value()));
// 字符串字面量生成 loadConst 指令写入目标寄存器 // 字符串字面量生成 loadConst 指令将字符串常量写入目标寄存器
case StringLiteralNode s -> case StringLiteralNode s -> InstructionFactory.loadConstInto(
InstructionFactory.loadConstInto(
ctx, dest, new IRConstant(s.value())); ctx, dest, new IRConstant(s.value()));
// 布尔字面量转换为 int 1/0生成 loadConst // 布尔字面量转换为 int 1/0生成 loadConst 指令写入目标寄存器
case BoolLiteralNode b -> case BoolLiteralNode b -> InstructionFactory.loadConstInto(
InstructionFactory.loadConstInto(
ctx, dest, new IRConstant(b.getValue() ? 1 : 0)); ctx, dest, new IRConstant(b.getValue() ? 1 : 0));
// 标识符查表获取原寄存器然后 move 到目标寄存器 // 数组字面量生成数组常量并写入目标寄存器
case ArrayLiteralNode arr -> InstructionFactory.loadConstInto(
ctx, dest, buildArrayConstant(arr));
// 变量标识符查表获得源寄存器move 到目标寄存器
case IdentifierNode id -> { case IdentifierNode id -> {
IRVirtualRegister src = ctx.getScope().lookup(id.name()); IRVirtualRegister src = ctx.getScope().lookup(id.name());
if (src == null) if (src == null)
@ -112,10 +143,22 @@ public record ExpressionBuilder(IRContext ctx) {
InstructionFactory.move(ctx, src, dest); InstructionFactory.move(ctx, src, dest);
} }
// 二元表达式递归生成并写入目标寄存器 // 二元表达式递归生成左右子表达式将结果写入目标寄存器
case BinaryExpressionNode bin -> buildBinaryInto(bin, dest); case BinaryExpressionNode bin -> buildBinaryInto(bin, dest);
// 其它复杂情况 build 到新寄存器 move 到目标寄存器 // 下标表达式递归生成索引结果move 到目标寄存器
case IndexExpressionNode idx -> {
IRVirtualRegister tmp = buildIndex(idx);
InstructionFactory.move(ctx, tmp, dest);
}
// 调用表达式递归生成调用结果move 到目标寄存器
case CallExpressionNode call -> {
IRVirtualRegister tmp = buildCall(call);
InstructionFactory.move(ctx, tmp, dest);
}
// 其它类型统一先 build 到临时寄存器 move 到目标寄存器
default -> { default -> {
IRVirtualRegister tmp = build(node); IRVirtualRegister tmp = build(node);
InstructionFactory.move(ctx, tmp, dest); InstructionFactory.move(ctx, tmp, dest);
@ -123,7 +166,201 @@ public record ExpressionBuilder(IRContext ctx) {
} }
} }
/* ───────────────── 具体表达式类型 ───────────────── */
/**
* 构建下标访问表达式IndexExpressionNode
* <ul>
* <li>若数组和下标均为编译期常量则直接进行常量折叠直接返回目标常量寄存器</li>
* <li>否则
* <ul>
* <li>若数组表达式本身是下一个下标的中间值即多维数组链式下标则先用 __index_r 获取引用</li>
* <li>最后一级用 __index_b/s/i/l/f/d/r按声明类型智能分派</li>
* </ul>
* </li>
* </ul>
*
* @param node 下标访问表达式节点
* @return 存放结果的虚拟寄存器
*/
private IRVirtualRegister buildIndex(IndexExpressionNode node) {
// 1. 常量折叠如果 array index 都是编译期常量直接取值
Object arrConst = tryFoldConst(node.array());
Object idxConst = tryFoldConst(node.index());
if (arrConst instanceof java.util.List<?> list && idxConst instanceof Number num) {
int i = num.intValue();
// 越界检查
if (i < 0 || i >= list.size())
throw new IllegalStateException("数组下标越界: " + i + " (长度 " + list.size() + ")");
Object elem = list.get(i);
IRVirtualRegister r = ctx.newRegister();
// 加载常量元素到新寄存器
InstructionFactory.loadConstInto(ctx, r, new IRConstant(elem));
return r;
}
// 2. 处理多级下标 arr[1][2]中间层用 __index_r 返回引用
IRVirtualRegister arrReg = (node.array() instanceof IndexExpressionNode inner)
? buildIndexRef(inner) // 递归获取引用
: build(node.array()); // 否则直接生成 array 的值
// 3. 生成下标值
IRVirtualRegister idxReg = build(node.index());
// 4. 创建目标寄存器
IRVirtualRegister dest = ctx.newRegister();
// 5. 准备参数
List<IRValue> argv = new ArrayList<>();
argv.add(arrReg);
argv.add(idxReg);
// 6. 选择调用指令
if (node.array() instanceof IndexExpressionNode) {
// 非最末层下标取引用
ctx.addInstruction(new CallInstruction(dest, "__index_r", argv));
} else {
// 最末层下标取实际元素值按声明类型分派
String func = "__index_i"; // 默认整型
if (node.array() instanceof IdentifierNode id) {
String declType = ctx.getScope().lookupType(id.name()); // "double[]""int[]"
if (declType != null) {
String base = declType.toLowerCase();
int p = base.indexOf('[');
if (p > 0) base = base.substring(0, p); // 基本类型
switch (base) {
case "byte" -> func = "__index_b";
case "short" -> func = "__index_s";
case "int" -> func = "__index_i";
case "long" -> func = "__index_l";
case "float" -> func = "__index_f";
case "double" -> func = "__index_d";
case "boolean" -> func = "__index_i"; // 布尔型用 int 通道返回 1/0
case "string" -> func = "__index_r"; // 字符串/其它未识别类型均走引用
default -> func = "__index_r";
}
}
}
ctx.addInstruction(new CallInstruction(dest, func, argv));
}
return dest;
}
/**
* 构建中间层下标访问表达式返回引用
* <p>
* 用于多维数组的链式下标访问 arr[1][2]保证中间结果是可被再次下标的引用
* <ul>
* <li>若数组和下标均为编译期常量则直接常量折叠返回目标常量寄存器</li>
* <li>否则递归处理 array生成引用指令__index_r</li>
* </ul>
* </p>
*
* @param node 下标访问表达式节点
* @return 存放引用结果的虚拟寄存器
*/
public IRVirtualRegister buildIndexRef(IndexExpressionNode node) {
// 1. 常量折叠如果 array index 都是编译期常量直接取值
Object arrConst = tryFoldConst(node.array());
Object idxConst = tryFoldConst(node.index());
if (arrConst instanceof java.util.List<?> list && idxConst instanceof Number num) {
int i = num.intValue();
// 越界检查
if (i < 0 || i >= list.size())
throw new IllegalStateException("数组下标越界: " + i + " (长度 " + list.size() + ")");
Object elem = list.get(i);
IRVirtualRegister r = ctx.newRegister();
// 加载常量元素到新寄存器
InstructionFactory.loadConstInto(ctx, r, new IRConstant(elem));
return r;
}
// 2. 递归生成 array 引用用于支持链式多级下标
IRVirtualRegister arrReg = (node.array() instanceof IndexExpressionNode inner)
? buildIndexRef(inner) // 递归向下返回引用
: build(node.array()); // 基础数组直接 build
// 3. 生成下标值
IRVirtualRegister idxReg = build(node.index());
// 4. 创建目标寄存器
IRVirtualRegister dest = ctx.newRegister();
// 5. 组织参数列表
List<IRValue> argv = new ArrayList<>();
argv.add(arrReg);
argv.add(idxReg);
// 6. 生成 __index_r 调用指令总是返回引用
ctx.addInstruction(new CallInstruction(dest, "__index_r", argv));
return dest;
}
/**
* 常量折叠工具支持嵌套数组
* <p>
* 尝试将表达式节点 {@code expr} 折叠为常量值用于编译期计算/优化
* <ul>
* <li>数字字面量返回 int double</li>
* <li>字符串字面量返回字符串</li>
* <li>布尔字面量返回 1true 0false</li>
* <li>数组字面量递归折叠所有元素为 List如果有一项不能折叠则返回 null</li>
* <li>标识符尝试从作用域查找编译期常量值</li>
* <li>其它类型无法折叠返回 null</li>
* </ul>
* </p>
*
* @param expr 需要折叠的表达式节点
* @return 编译期常量值支持 intdoubleStringList否则返回 null
*/
private Object tryFoldConst(ExpressionNode expr) {
if (expr == null) return null;
// 数字字面量尝试解析为 int double
if (expr instanceof NumberLiteralNode n) {
String s = n.value();
try {
if (s.contains(".") || s.contains("e") || s.contains("E"))
return Double.parseDouble(s); // 带小数或科学计数法为 double
return Integer.parseInt(s); // 否则为 int
} catch (NumberFormatException e) {
return null; // 无法解析为数字
}
}
// 字符串字面量直接返回字符串
if (expr instanceof StringLiteralNode s) return s.value();
// 布尔字面量true 返回 1false 返回 0
if (expr instanceof BoolLiteralNode b) return b.getValue() ? 1 : 0;
// 数组字面量递归折叠所有元素
if (expr instanceof ArrayLiteralNode arr) {
List<Object> list = new ArrayList<>();
for (ExpressionNode e : arr.elements()) {
Object v = tryFoldConst(e);
if (v == null) return null; // 有一项无法折叠则整体失败
list.add(v);
}
return List.copyOf(list);
}
// 标识符尝试查找作用域中的常量值
if (expr instanceof IdentifierNode id) {
Object v = null;
try {
v = ctx.getScope().getConstValue(id.name());
} catch (Throwable ignored) {
// 查不到常量或异常都视为无法折叠
}
return v;
}
// 其它类型不支持折叠返回 null
return null;
}
/** /**
* 一元表达式构建 * 一元表达式构建
@ -140,59 +377,67 @@ public record ExpressionBuilder(IRContext ctx) {
IRVirtualRegister src = build(un.operand()); IRVirtualRegister src = build(un.operand());
// 分配目标寄存器 // 分配目标寄存器
IRVirtualRegister dest = ctx.newRegister(); IRVirtualRegister dest = ctx.newRegister();
switch (un.operator()) { switch (un.operator()) {
// 算术负号生成取负指令 // 算术负号生成取负指令例如-a
case "-" -> ctx.addInstruction( case "-" -> ctx.addInstruction(
new UnaryOperationInstruction(ExpressionUtils.negOp(un.operand()), dest, src)); new UnaryOperationInstruction(ExpressionUtils.negOp(un.operand()), dest, src));
// 逻辑非等价于 a == 0生成比较指令
// 逻辑非等价于 a == 0生成整数等于比较指令!a
case "!" -> { case "!" -> {
// 生成常量0的寄存器
IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0); IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
// 比较 src 是否等于0等价于逻辑非
return InstructionFactory.binOp(ctx, IROpCode.CMP_IEQ, src, zero); return InstructionFactory.binOp(ctx, IROpCode.CMP_IEQ, src, zero);
} }
// 其它一元运算符不支持异常 // 其它一元运算符不支持异常
default -> throw new IllegalStateException("未知一元运算符: " + un.operator()); default -> throw new IllegalStateException("未知一元运算符: " + un.operator());
} }
return dest; return dest;
} }
/** /**
* 构建函数或方法调用表达式模块内未限定调用会自动补全当前模块名 * 构建函数或方法调用表达式
* <p>
* 支持普通函数调用foo(a, b)与成员方法调用obj.method(a, b)
* <ul>
* <li>首先递归生成所有参数的虚拟寄存器列表</li>
* <li>根据 callee 类型区分成员访问或直接标识符调用并规范化方法名如加前缀</li>
* <li>为返回值分配新寄存器生成 Call 指令</li>
* </ul>
* </p>
* *
* @param call AST 调用表达式节点 * @param call 函数/方法调用表达式节点
* @return 存储调用结果的虚拟寄存器 * @return 调用结果的虚拟寄存器
*/ */
private IRVirtualRegister buildCall(CallExpressionNode call) { private IRVirtualRegister buildCall(CallExpressionNode call) {
// 递归生成所有参数实参对应的寄存器 // 1. 递归生成所有参数的寄存器
List<IRVirtualRegister> argv = call.arguments().stream().map(this::build).toList(); List<IRVirtualRegister> argv = call.arguments().stream().map(this::build).toList();
// 解析被调用目标名支持普通函数/成员方法 // 2. 规范化被调用方法名区分成员方法与普通函数
String callee = switch (call.callee()) { String callee = switch (call.callee()) {
// 成员方法调用例如 obj.foo() // 成员方法调用 obj.method()
case MemberExpressionNode m when m.object() instanceof IdentifierNode id case MemberExpressionNode m when m.object() instanceof IdentifierNode id -> id.name() + "." + m.member();
-> id.name() + "." + m.member(); // 普通函数调用或处理命名空间前缀如当前方法名为 namespace.func
// 普通函数调用如果未指定模块自动补全当前模块名
case IdentifierNode id -> { case IdentifierNode id -> {
String current = ctx.getFunction().name(); String current = ctx.getFunction().name();
int dot = current.lastIndexOf('.'); int dot = current.lastIndexOf('.');
if (dot > 0) { if (dot > 0)
// 当前处于模块内函数Module.func补全为同模块下的全限定名 yield current.substring(0, dot) + "." + id.name(); // 同命名空间内调用
yield current.substring(0, dot) + "." + id.name(); yield id.name(); // 全局函数调用
} else {
// 顶层/脚本函数等不含模块前缀保持原样
yield id.name();
} }
} // 其它类型不支持
// 其它情况暂不支持 default -> throw new IllegalStateException(
default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName()); "不支持的调用目标: " + call.callee().getClass().getSimpleName());
}; };
// 为返回值分配新寄存器生成 Call 指令 // 3. 分配用于存放返回值的新寄存器生成 Call 指令
IRVirtualRegister dest = ctx.newRegister(); IRVirtualRegister dest = ctx.newRegister();
ctx.addInstruction(new CallInstruction(dest, callee, new ArrayList<>(argv))); ctx.addInstruction(new CallInstruction(dest, callee, new ArrayList<>(argv)));
return dest; return dest;
} }
/** /**
* 二元表达式构建结果存储到新寄存器 * 二元表达式构建结果存储到新寄存器
* <br> * <br>
@ -207,18 +452,21 @@ public record ExpressionBuilder(IRContext ctx) {
IRVirtualRegister b = build(bin.right()); IRVirtualRegister b = build(bin.right());
String op = bin.operator(); String op = bin.operator();
// 比较运算符==!=>< 需要生成条件跳转或布尔值寄存器 // 判断是否为比较运算符==!=><
if (ComparisonUtils.isComparisonOperator(op)) { if (ComparisonUtils.isComparisonOperator(op)) {
// 生成比较操作返回布尔值寄存器
return InstructionFactory.binOp( return InstructionFactory.binOp(
ctx, ctx,
// 通过比较工具获得合适的 IR 操作码 // 根据运算符和操作数类型获得合适的 IR 操作码
ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()), ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()),
a, b); a, b);
} }
// 其它算术/位运算 // 其它二元运算算术位运算等
IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right()); IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());
if (code == null) throw new IllegalStateException("不支持的运算符: " + op); if (code == null)
throw new IllegalStateException("不支持的运算符: " + op);
// 生成二元操作指令
return InstructionFactory.binOp(ctx, code, a, b); return InstructionFactory.binOp(ctx, code, a, b);
} }
@ -229,22 +477,29 @@ public record ExpressionBuilder(IRContext ctx) {
* @param dest 目标虚拟寄存器 * @param dest 目标虚拟寄存器
*/ */
private void buildBinaryInto(BinaryExpressionNode bin, IRVirtualRegister dest) { private void buildBinaryInto(BinaryExpressionNode bin, IRVirtualRegister dest) {
// 递归生成左右操作数的寄存器
IRVirtualRegister a = build(bin.left()); IRVirtualRegister a = build(bin.left());
IRVirtualRegister b = build(bin.right()); IRVirtualRegister b = build(bin.right());
String op = bin.operator(); String op = bin.operator();
// 处理比较运算符==!=><
if (ComparisonUtils.isComparisonOperator(op)) { if (ComparisonUtils.isComparisonOperator(op)) {
InstructionFactory.binOpInto( InstructionFactory.binOpInto(
ctx, ctx,
// 选择对应类型和符号的比较操作码
ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()), ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()),
a, b, dest); a, b, dest);
} else { } else {
// 算术或位运算符
IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right()); IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());
if (code == null) throw new IllegalStateException("不支持的运算符: " + op); if (code == null)
throw new IllegalStateException("不支持的运算符: " + op);
// 生成二元操作指令写入目标寄存器
InstructionFactory.binOpInto(ctx, code, a, b, dest); InstructionFactory.binOpInto(ctx, code, a, b, dest);
} }
} }
/* ───────────────── 字面量辅助方法 ───────────────── */ /* ───────────────── 字面量辅助方法 ───────────────── */
/** /**
@ -254,8 +509,11 @@ public record ExpressionBuilder(IRContext ctx) {
* @return 存储该字面量的寄存器 * @return 存储该字面量的寄存器
*/ */
private IRVirtualRegister buildNumberLiteral(String value) { private IRVirtualRegister buildNumberLiteral(String value) {
// 解析数字常量
IRConstant c = ExpressionUtils.buildNumberConstant(ctx, value); IRConstant c = ExpressionUtils.buildNumberConstant(ctx, value);
// 分配新寄存器
IRVirtualRegister r = ctx.newRegister(); IRVirtualRegister r = ctx.newRegister();
// 生成 LoadConst 指令
ctx.addInstruction(new LoadConstInstruction(r, c)); ctx.addInstruction(new LoadConstInstruction(r, c));
return r; return r;
} }
@ -267,8 +525,11 @@ public record ExpressionBuilder(IRContext ctx) {
* @return 存储该字符串的寄存器 * @return 存储该字符串的寄存器
*/ */
private IRVirtualRegister buildStringLiteral(String value) { private IRVirtualRegister buildStringLiteral(String value) {
// 构建字符串常量
IRConstant c = new IRConstant(value); IRConstant c = new IRConstant(value);
// 分配新寄存器
IRVirtualRegister r = ctx.newRegister(); IRVirtualRegister r = ctx.newRegister();
// 生成 LoadConst 指令
ctx.addInstruction(new LoadConstInstruction(r, c)); ctx.addInstruction(new LoadConstInstruction(r, c));
return r; return r;
} }
@ -280,9 +541,74 @@ public record ExpressionBuilder(IRContext ctx) {
* @return 存储 1/0 的寄存器 * @return 存储 1/0 的寄存器
*/ */
private IRVirtualRegister buildBoolLiteral(boolean v) { private IRVirtualRegister buildBoolLiteral(boolean v) {
// 转换为 1 0 的常量
IRConstant c = new IRConstant(v ? 1 : 0); IRConstant c = new IRConstant(v ? 1 : 0);
// 分配新寄存器
IRVirtualRegister r = ctx.newRegister(); IRVirtualRegister r = ctx.newRegister();
// 生成 LoadConst 指令
ctx.addInstruction(new LoadConstInstruction(r, c)); ctx.addInstruction(new LoadConstInstruction(r, c));
return r; return r;
} }
/**
* 构建数组字面量表达式元素均为常量时
*
* @param arr 数组字面量节点
* @return 存储该数组的寄存器
*/
private IRVirtualRegister buildArrayLiteral(ArrayLiteralNode arr) {
// 递归生成支持嵌套的数组常量
IRConstant c = buildArrayConstant(arr);
// 分配新寄存器
IRVirtualRegister r = ctx.newRegister();
// 生成 LoadConst 指令
ctx.addInstruction(new LoadConstInstruction(r, c));
return r;
}
/**
* 构建支持嵌套的数组常量表达式
* <p>
* 遍历并递归处理数组字面量的所有元素
* <ul>
* <li>数字字面量根据内容生成 int double 常量</li>
* <li>字符串字面量直接存储字符串内容</li>
* <li>布尔字面量转换为 1true 0false存储</li>
* <li>数组字面量递归构建允许多层嵌套最终生成嵌套的 List</li>
* </ul>
* 若包含非常量元素则抛出异常
* </p>
*
* @param arr 数组字面量节点
* @return 封装所有常量元素支持嵌套 List {@link IRConstant}
* @throws IllegalStateException 如果数组中存在非常量元素
*/
private IRConstant buildArrayConstant(ArrayLiteralNode arr) {
List<Object> list = new ArrayList<>();
for (ExpressionNode e : arr.elements()) {
switch (e) {
// 数字字面量解析并加入
case NumberLiteralNode n -> {
IRConstant num = ExpressionUtils.buildNumberConstant(ctx, n.value());
list.add(num.value());
}
// 字符串字面量直接加入
case StringLiteralNode s -> list.add(s.value());
// 布尔字面量转成 1/0
case BoolLiteralNode b -> list.add(b.getValue() ? 1 : 0);
// 嵌套数组递归生成并加入
case ArrayLiteralNode inner -> {
IRConstant innerConst = buildArrayConstant(inner);
list.add(innerConst.value());
}
// 其它类型暂不支持
default -> throw new IllegalStateException(
"暂不支持含非常量元素的数组字面量: " + e.getClass().getSimpleName());
}
}
// 返回不可变的 List 封装为 IRConstant
return new IRConstant(List.copyOf(list));
}
} }

View File

@ -1,5 +1,6 @@
package org.jcnc.snow.compiler.ir.builder; package org.jcnc.snow.compiler.ir.builder;
import org.jcnc.snow.compiler.ir.common.GlobalConstTable;
import org.jcnc.snow.compiler.ir.common.GlobalFunctionTable; import org.jcnc.snow.compiler.ir.common.GlobalFunctionTable;
import org.jcnc.snow.compiler.ir.core.IRFunction; import org.jcnc.snow.compiler.ir.core.IRFunction;
import org.jcnc.snow.compiler.ir.utils.ExpressionUtils; import org.jcnc.snow.compiler.ir.utils.ExpressionUtils;
@ -13,6 +14,13 @@ import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
* <p> * <p>
* 负责将语法树中的 FunctionNode 节点转化为可执行的 IRFunction * 负责将语法树中的 FunctionNode 节点转化为可执行的 IRFunction
* 包含参数声明返回类型推断函数体语句转换等步骤 * 包含参数声明返回类型推断函数体语句转换等步骤
*
* <ul>
* <li>支持自动导入全局/跨模块常量使跨模块常量引用 ModuleA.a IR 阶段可用</li>
* <li>将函数形参声明为虚拟寄存器并注册到作用域便于后续指令生成</li>
* <li>根据返回类型设置表达式默认字面量类型保证 IR 层类型一致性</li>
* <li>遍历并转换函数体语句为 IR 指令</li>
* </ul>
*/ */
public class FunctionBuilder { public class FunctionBuilder {
@ -21,11 +29,15 @@ public class FunctionBuilder {
* <p> * <p>
* 构建过程包括: * 构建过程包括:
* <ol> * <ol>
* <li>初始化 IRFunction 实例和上下文</li> * <li>在全局函数表注册函数签名便于后续模块/语义分析阶段查询</li>
* <li>根据函数返回类型设置默认类型后缀便于表达式推断</li> * <li>初始化 IRFunction 实例和 IRContext 上下文对象包含作用域与寄存器信息</li>
* <li>声明参数到作用域并为每个参数分配虚拟寄存器</li> * <li>自动导入全局常量包括跨模块 const 变量到当前作用域
* <li>遍历并转换函数体内的每条语句为 IR 指令</li> * 使成员访问如 ModuleA.a 可直接折叠为常量</li>
* <li>函数构建完成后清理默认类型后缀防止影响其他函数</li> * <li>根据函数返回类型设置表达式推断的默认字面量类型后缀
* doubled, floatf避免类型不一致</li>
* <li>遍历声明形参每个参数分配虚拟寄存器并注册到作用域</li>
* <li>依次转换函数体中的每条语句为 IR 指令</li>
* <li>函数体转换完成后清理默认类型后缀防止影响后续函数构建</li>
* </ol> * </ol>
* *
* @param functionNode 表示函数定义的语法树节点 * @param functionNode 表示函数定义的语法树节点
@ -33,44 +45,56 @@ public class FunctionBuilder {
*/ */
public IRFunction build(FunctionNode functionNode) { public IRFunction build(FunctionNode functionNode) {
// 在全局函数表中注册函数信息 // 1. 在全局函数表注册函数名与返回类型
// 方便其他阶段/模块调用类型检查
GlobalFunctionTable.register(functionNode.name(), functionNode.returnType()); GlobalFunctionTable.register(functionNode.name(), functionNode.returnType());
// 2. 初始化 IRFunction 实例与上下文对象
// 0) 基本初始化: 创建 IRFunction 实例与对应上下文 // IRFunction: 表示该函数的中间代码容器
// IRContext: 负责作用域寄存器分配等编译上下文管理
IRFunction irFunction = new IRFunction(functionNode.name()); IRFunction irFunction = new IRFunction(functionNode.name());
IRContext irContext = new IRContext(irFunction); IRContext irContext = new IRContext(irFunction);
// 1) 把函数返回类型注入为默认类型后缀供表达式类型推断 // 3. 自动导入所有全局/跨模块常量到当前作用域
// 支持如 ModuleA.a 这样的常量访问/折叠参见 ExpressionBuilder
GlobalConstTable.all().forEach((k, v) ->
irContext.getScope().importExternalConst(k, v));
// 4. 根据函数返回类型设置默认类型后缀
// 例如返回类型为 double , 字面量表达式自动用 d 后缀
char _returnSuffix = switch (functionNode.returnType().toLowerCase()) { char _returnSuffix = switch (functionNode.returnType().toLowerCase()) {
case "double" -> 'd'; case "double" -> 'd';
case "float" -> 'f'; case "float" -> 'f';
case "long" -> 'l'; case "long" -> 'l';
case "short" -> 's'; case "short" -> 's';
case "byte" -> 'b'; case "byte" -> 'b';
default -> '\0'; default -> '\0'; // 其它类型不设默认后缀
}; };
ExpressionUtils.setDefaultSuffix(_returnSuffix); ExpressionUtils.setDefaultSuffix(_returnSuffix);
try { try {
// 2) 声明形参: 为每个参数分配虚拟寄存器并声明到作用域 // 5. 遍历函数参数列表
// - 为每个参数分配一个新的虚拟寄存器
// - 注册参数名类型寄存器到当前作用域
// - 添加参数寄存器到 IRFunction用于后续调用与指令生成
for (ParameterNode p : functionNode.parameters()) { for (ParameterNode p : functionNode.parameters()) {
IRVirtualRegister reg = irFunction.newRegister(); // 新寄存器 IRVirtualRegister reg = irFunction.newRegister();
irContext.getScope().declare(p.name(), p.type(), reg); // 变量名寄存器绑定 irContext.getScope().declare(p.name(), p.type(), reg);
irFunction.addParameter(reg); // 添加到函数参数列表 irFunction.addParameter(reg);
} }
// 3) 生成函数体 IR: 遍历每条语句逐一转化 // 6. 遍历函数体语句节点转换为 IR 指令
// StatementBuilder 负责将每条语句递归转换为 IR
StatementBuilder stmtBuilder = new StatementBuilder(irContext); StatementBuilder stmtBuilder = new StatementBuilder(irContext);
for (StatementNode stmt : functionNode.body()) { for (StatementNode stmt : functionNode.body()) {
stmtBuilder.build(stmt); stmtBuilder.build(stmt);
} }
} finally { } finally {
// 4) 清除默认后缀避免影响后续函数的推断 // 7. 清理默认类型后缀防止影响后续其他函数的类型推断
ExpressionUtils.clearDefaultSuffix(); ExpressionUtils.clearDefaultSuffix();
} }
// 返回构建好 IRFunction // 8. 返回构建完成 IRFunction
return irFunction; return irFunction;
} }
} }

View File

@ -7,109 +7,155 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* IRBuilderScope 用于管理单个函数内变量名与虚拟寄存器的映射关系 * {@code IRBuilderScope} 用于管理单个函数作用域变量名与虚拟寄存器的映射关系
* * <p>
* <p>主要功能包括: * 主要职责
* <ul> * <ul>
* <li>维护在当前作用域中已声明变量的寄存器分配信息</li> * <li>维护当前作用域内所有已声明变量的寄存器和类型信息</li>
* <li>支持将已有虚拟寄存器与变量名重新绑定</li> * <li>支持变量与虚拟寄存器的重新绑定与查找</li>
* <li>根据变量名查找对应的虚拟寄存器实例或类型</li> * <li>支持变量的类型信息记录与查询</li>
* <li>支持变量的编译期常量值记录与查询便于常量折叠等优化</li>
* <li>支持跨模块全局常量 ModuleA.a查找</li>
* </ul> * </ul>
*/ */
final class IRBuilderScope { final class IRBuilderScope {
/** /** 变量名到虚拟寄存器的映射表(本地变量) */
* 存储变量名到对应 IRVirtualRegister 的映射
* 变量名为键虚拟寄存器对象为值用于查找和更新
*/
private final Map<String, IRVirtualRegister> vars = new HashMap<>(); private final Map<String, IRVirtualRegister> vars = new HashMap<>();
/** 变量名到类型字符串的映射表 */
/**
* 存储变量名到对应类型的映射
* <br>
* 变量名为键变量类型为值用于变量类型提升
*/
private final Map<String, String> varTypes = new HashMap<>(); private final Map<String, String> varTypes = new HashMap<>();
/** 变量名到编译期常量值的映射表(本作用域) */
private final Map<String, Object> varConstValues = new HashMap<>();
/** /**
* 当前作用域所绑定的 IRFunction 对象用于申请新的虚拟寄存器 * 额外存放跨模块导入的全局常量
* key 形如 "ModuleA.a" value 为其常量值
*/ */
private final Map<String, Object> externalConsts = new HashMap<>();
/** 当前作用域所绑定的 IRFunction 实例 */
private IRFunction fn; private IRFunction fn;
/** /**
* 将指定的 IRFunction 关联到当前作用域以便后续声明变量时能够 * 绑定当前作用域到指定 IRFunction
* 调用该函数的 newRegister() 方法生成新的寄存器
* *
* @param fn 要绑定到本作用域的 IRFunction 实例 * @param fn 目标 IRFunction
*/ */
void attachFunction(IRFunction fn) { void attachFunction(IRFunction fn) {
this.fn = fn; this.fn = fn;
} }
/** /**
* 在当前作用域中声明一个新变量并为其分配一个新的虚拟寄存器 * 声明一个新变量并分配新的虚拟寄存器
* 调用绑定的 IRFunction.newRegister() 生成寄存器后保存到映射表中
* *
* @param name 变量名称作为映射键使用 * @param name 变量名称
* @param type 变量类型 * @param type 变量类型
*/ */
void declare(String name, String type) { void declare(String name, String type) {
IRVirtualRegister reg = fn.newRegister(); IRVirtualRegister reg = fn.newRegister();
vars.put(name, reg); vars.put(name, reg);
varTypes.put(name, type); varTypes.put(name, type);
varConstValues.remove(name);
} }
/** /**
* 在当前作用域中声明或导入一个已有的虚拟寄存器并将其与指定变量名绑定 * 声明新变量并绑定到指定的寄存器
* 该方法可用于将外部或前一作用域的寄存器导入到本作用域
* *
* @param name 变量名称作为映射键使用 * @param name 变量名称
* @param type 变量类型 * @param type 变量类型
* @param reg 要绑定到该名称的 IRVirtualRegister 实例 * @param reg 绑定的虚拟寄存器
*/ */
void declare(String name, String type, IRVirtualRegister reg) { void declare(String name, String type, IRVirtualRegister reg) {
vars.put(name, reg); vars.put(name, reg);
varTypes.put(name, type); varTypes.put(name, type);
varConstValues.remove(name);
} }
/** /**
* 更新已存在变量的虚拟寄存器绑定关系若变量已声明则替换其对应的寄存器 * 更新变量的虚拟寄存器绑定如变量已存在则覆盖否则等同于新声明
* 若尚未声明则等同于声明新变量
* *
* @param name 变量名称作为映射键使用 * @param name 变量名称
* @param reg 新的 IRVirtualRegister 实例用于替换旧绑定 * @param reg 新的虚拟寄存器
*/ */
void put(String name, IRVirtualRegister reg) { void put(String name, IRVirtualRegister reg) {
vars.put(name, reg); vars.put(name, reg);
} }
/** /**
* 根据变量名称在当前作用域中查找对应的虚拟寄存器 * 查找变量名对应的虚拟寄存器
* *
* @param name 需要查询的变量名称 * @param name 变量名
* @return 如果该名称已绑定寄存器则返回对应的 IRVirtualRegister * @return 已绑定的虚拟寄存器若未声明则返回 null
* 如果未声明则返回 null
*/ */
IRVirtualRegister lookup(String name) { IRVirtualRegister lookup(String name) {
return vars.get(name); return vars.get(name);
} }
/** /**
* 根据变量名称在当前作用域中查找对应的类型 * 查找变量名对应的类型名
* *
* @param name 需要查询的变量名称 * @param name 变量名
* @return 如果该名称已声明则返回对应的类型 * @return 已声明类型字符串若未声明则返回 null
* 如果未声明则返回 null
*/ */
String lookupType(String name) { String lookupType(String name) {
return varTypes.get(name); return varTypes.get(name);
} }
/** /**
* 获取 变量->类型的映射 的不可变副本 * 获取变量名到类型名映射的不可变副本
* @return 变量->类型的映射 的不可变副本 *
* @return 变量名类型名映射的只读视图
*/ */
Map<String, String> getVarTypes() { Map<String, String> getVarTypes() {
return Map.copyOf(varTypes); return Map.copyOf(varTypes);
} }
// ---------------- 编译期常量相关接口 ----------------
/**
* 设置变量的编译期常量值本地变量
*
* @param name 变量名称
* @param value 常量值null 表示清除
*/
void setConstValue(String name, Object value) {
if (value == null) varConstValues.remove(name);
else varConstValues.put(name, value);
}
/**
* 获取变量的编译期常量值本地变量或导入的外部常量
* <br>
* 优先查找本地常量未命中再查外部 "ModuleA.a"
*
* @param name 变量名称或"模块名.常量名"
* @return 编译期常量值 null
*/
Object getConstValue(String name) {
Object v = varConstValues.get(name);
if (v != null) return v;
// 支持跨模块常量/全局变量
return externalConsts.get(name);
}
/**
* 清除变量的编译期常量值绑定本地
*
* @param name 变量名称
*/
void clearConstValue(String name) {
varConstValues.remove(name);
}
// ---------------- 跨模块常量导入支持 ----------------
/**
* 导入外部其他模块的全局常量/变量
*
* @param qualifiedName 形如 "ModuleA.a"
* @param value 其常量值
*/
void importExternalConst(String qualifiedName, Object value) {
externalConsts.put(qualifiedName, value);
}
} }

View File

@ -1,24 +1,26 @@
package org.jcnc.snow.compiler.ir.builder; package org.jcnc.snow.compiler.ir.builder;
import org.jcnc.snow.compiler.ir.common.GlobalConstTable;
import org.jcnc.snow.compiler.ir.core.IRFunction; import org.jcnc.snow.compiler.ir.core.IRFunction;
import org.jcnc.snow.compiler.ir.core.IRProgram; import org.jcnc.snow.compiler.ir.core.IRProgram;
import org.jcnc.snow.compiler.parser.ast.FunctionNode; import org.jcnc.snow.compiler.parser.ast.*;
import org.jcnc.snow.compiler.parser.ast.ModuleNode; import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.Node; import org.jcnc.snow.compiler.parser.ast.base.Node;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext; import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode; import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* IRProgramBuilder 负责将 AST 根节点(如模块函数顶层语句)转换为可执行的 IRProgram 实例 * IRProgramBuilder 负责将 AST 顶层节点转换为可执行的 {@link IRProgram}
* <p> *
* 主要职责:
* <ul> * <ul>
* <li>遍历 AST 根节点根据类型分别处理(模块函数顶层语句)</li> * <li>预扫描所有模块 <code>declare const</code> 常量登记到全局常量表支持跨模块常量折叠</li>
* <li>对模块内的函数添加全限定名并在函数体前注入全局变量声明</li> * <li>对模块内的函数加上模块前缀保证命名唯一并将本模块全局声明注入到函数体前部</li>
* <li>单独的顶层语句封装为特殊的 "_start" 函数</li> * <li>独立顶层语句自动包装为特殊的 "_start" 函数脚本模式支持</li>
* </ul> * </ul>
*/ */
public final class IRProgramBuilder { public final class IRProgramBuilder {
@ -31,20 +33,23 @@ public final class IRProgramBuilder {
* @throws IllegalStateException 遇到不支持的顶层节点类型时抛出 * @throws IllegalStateException 遇到不支持的顶层节点类型时抛出
*/ */
public IRProgram buildProgram(List<Node> roots) { public IRProgram buildProgram(List<Node> roots) {
// 预先收集并登记全部模块常量到全局常量表
preloadGlobals(roots);
IRProgram irProgram = new IRProgram(); IRProgram irProgram = new IRProgram();
for (Node node : roots) { for (Node node : roots) {
switch (node) { switch (node) {
case ModuleNode moduleNode -> { case ModuleNode moduleNode -> {
// 处理模块节点:遍历其中所有函数统一用模块名.函数名作为全限定名 // 处理模块节点遍历其中所有函数统一用模块名.函数名作为全限定名避免命名冲突
for (FunctionNode f : moduleNode.functions()) { for (FunctionNode f : moduleNode.functions()) {
irProgram.add(buildFunctionWithGlobals(moduleNode, f)); irProgram.add(buildFunctionWithGlobals(moduleNode, f));
} }
} }
case FunctionNode functionNode -> case FunctionNode functionNode ->
// 处理顶层函数节点:直接构建为 IRFunction 并加入 // 处理顶层函数节点直接构建为 IRFunction 并加入
irProgram.add(buildFunction(functionNode)); irProgram.add(buildFunction(functionNode));
case StatementNode statementNode -> case StatementNode statementNode ->
// 处理脚本式顶层语句:封装成 "_start" 函数后构建并添加 // 处理脚本式顶层语句封装成 "_start" 函数后构建并添加
irProgram.add(buildFunction(wrapTopLevel(statementNode))); irProgram.add(buildFunction(wrapTopLevel(statementNode)));
default -> default ->
// 遇到未知类型节点抛出异常 // 遇到未知类型节点抛出异常
@ -54,25 +59,111 @@ public final class IRProgramBuilder {
return irProgram; return irProgram;
} }
// ===================== 全局常量收集 =====================
/**
* 扫描所有模块节点将其中声明的 const 全局变量compile-time 常量
* "模块名.常量名" 形式注册到全局常量表
* 支持跨模块常量折叠用于后续 IR 生成过程中的常量折叠优化
*
* @param roots AST 顶层节点列表
*/
private void preloadGlobals(List<Node> roots) {
for (Node n : roots) {
if (n instanceof ModuleNode mod) {
String moduleName = mod.name();
if (mod.globals() == null) continue;
for (DeclarationNode decl : mod.globals()) {
// 只处理 compile-time const 常量并要求有初始值
if (!decl.isConst() || decl.getInitializer().isEmpty()) continue;
ExpressionNode init = decl.getInitializer().get();
Object value = evalLiteral(init);
if (value != null) {
GlobalConstTable.register(moduleName + "." + decl.getName(), value);
}
}
}
}
}
/**
* 字面量提取与类型折叠工具
* 用于将表达式节点还原为 Java 原生类型intlongdoubleString等仅支持直接字面量
*
* @param expr 要计算的表达式节点
* @return 提取到的原生常量值不支持的情况返回 null
*/
private Object evalLiteral(ExpressionNode expr) {
return switch (expr) {
case NumberLiteralNode num -> {
String raw = num.value();
String s = raw.replace("_", "");
char last = Character.toLowerCase(s.charAt(s.length() - 1));
String core = switch (last) {
case 'b', 's', 'l', 'f', 'd' -> s.substring(0, s.length() - 1);
default -> s;
};
try {
if (core.contains(".") || core.contains("e") || core.contains("E")) {
// 浮点数处理
yield Double.parseDouble(core);
}
long lv = Long.parseLong(core);
yield switch (last) {
case 'b' -> (byte) lv;
case 's' -> (short) lv;
case 'l' -> lv;
default -> (int) lv;
};
} catch (NumberFormatException ignore) {
yield null;
}
}
case StringLiteralNode str -> str.value();
case BoolLiteralNode b -> b.getValue() ? 1 : 0;
default -> null;
};
}
// ===================== IRFunction 构建辅助 =====================
/** /**
* 构建带有模块全局声明注入的函数并将函数名加上模块前缀保证模块内函数名唯一 * 构建带有模块全局声明注入的函数并将函数名加上模块前缀保证模块内函数名唯一
* <p> * 如果模块有全局声明则这些声明会被插入到函数体前部**会过滤掉与参数同名的全局声明**
* 如果模块有全局声明则这些声明会被插入到函数体前部
* *
* @param moduleNode 当前模块节点 * @param moduleNode 所属模块节点
* @param functionNode 模块中的函数节点 * @param functionNode 待构建的函数节点
* @return 包含全局声明已加前缀函数名的 IRFunction * @return 包含全局声明 IRFunction
*/ */
private IRFunction buildFunctionWithGlobals(ModuleNode moduleNode, FunctionNode functionNode) { private IRFunction buildFunctionWithGlobals(ModuleNode moduleNode, FunctionNode functionNode) {
// 拼接模块名和函数名生成全限定名 // 拼接模块名和函数名生成全限定名
String qualifiedName = moduleNode.name() + "." + functionNode.name(); String qualifiedName = moduleNode.name() + "." + functionNode.name();
// 若无全局声明仅重命名后直接构建
if (moduleNode.globals() == null || moduleNode.globals().isEmpty()) { if (moduleNode.globals() == null || moduleNode.globals().isEmpty()) {
// 无全局声明直接重命名构建
return buildFunction(renameFunction(functionNode, qualifiedName)); return buildFunction(renameFunction(functionNode, qualifiedName));
} }
// 若有全局声明插入到函数体最前面
List<StatementNode> newBody = new ArrayList<>(moduleNode.globals().size() + functionNode.body().size()); // ------- 过滤与参数重名的全局声明 -------
newBody.addAll(moduleNode.globals()); Set<String> paramNames = new HashSet<>();
for (ParameterNode p : functionNode.parameters()) {
paramNames.add(p.name());
}
List<StatementNode> filteredGlobals = new ArrayList<>();
for (DeclarationNode g : moduleNode.globals()) {
// 避免全局声明和参数重名优先参数
if (!paramNames.contains(g.getName())) {
filteredGlobals.add(g);
}
}
if (filteredGlobals.isEmpty()) {
// 过滤后已无可插入的全局声明
return buildFunction(renameFunction(functionNode, qualifiedName));
}
// 合并全局声明与函数体前插全局声明
List<StatementNode> newBody = new ArrayList<>(filteredGlobals.size() + functionNode.body().size());
newBody.addAll(filteredGlobals);
newBody.addAll(functionNode.body()); newBody.addAll(functionNode.body());
FunctionNode wrapped = new FunctionNode( FunctionNode wrapped = new FunctionNode(
qualifiedName, qualifiedName,
@ -85,11 +176,11 @@ public final class IRProgramBuilder {
} }
/** /**
* 生成一个重命名的 FunctionNode(只修改函数名其他属性保持不变) * 生成一个重命名的 FunctionNode只修改函数名其他属性保持不变
* *
* @param fn 原始函数节点 * @param fn 原始函数节点
* @param newName 新的函数名(通常为全限定名) * @param newName 新的函数名全限定名
* @return 重命名后的 FunctionNode * @return 重命名后的函数节点
*/ */
private FunctionNode renameFunction(FunctionNode fn, String newName) { private FunctionNode renameFunction(FunctionNode fn, String newName) {
return new FunctionNode( return new FunctionNode(
@ -104,8 +195,8 @@ public final class IRProgramBuilder {
/** /**
* 构建 IRFunction * 构建 IRFunction
* *
* @param functionNode 要转换的函数节点 * @param functionNode 待构建的 FunctionNode
* @return 转换结果 IRFunction * @return 构建后的 IRFunction
*/ */
private IRFunction buildFunction(FunctionNode functionNode) { private IRFunction buildFunction(FunctionNode functionNode) {
return new FunctionBuilder().build(functionNode); return new FunctionBuilder().build(functionNode);
@ -113,11 +204,10 @@ public final class IRProgramBuilder {
/** /**
* 将顶层语句节点封装成特殊的 "_start" 函数 * 将顶层语句节点封装成特殊的 "_start" 函数
* <p> * 主要用于脚本模式支持使得顶层语句也可以被 IR 执行引擎统一处理
* 这对于脚本式文件支持至关重要(即文件最外层直接写语句)
* *
* @param stmt 要封装的顶层语句 * @param stmt 顶层语句节点
* @return 包装成 FunctionNode "_start" 函数 * @return 封装后的 FunctionNode
*/ */
private FunctionNode wrapTopLevel(StatementNode stmt) { private FunctionNode wrapTopLevel(StatementNode stmt) {
return new FunctionNode( return new FunctionNode(

View File

@ -6,23 +6,16 @@ import org.jcnc.snow.compiler.ir.value.IRConstant;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
/** /**
* InstructionFactory 统一生成并注册 IR 指令的工厂类 * IR 指令统一生成工厂类负责封装常量加载二元运算赋值控制流等指令生成逻辑
* <p> * 提高 IR 生成阶段的可维护性与复用性
* 该类封装了常见的 IR 指令生成方式包括常量加载二元运算赋值控制流等
* 统一简化指令插入和寄存器分配逻辑提升 IR 生成阶段的代码可维护性和复用性
* </p>
*/ */
public class InstructionFactory { public class InstructionFactory {
/* ====================================================================== */
/* 常量 / 通用二元运算(新寄存器) */
/* ====================================================================== */
/** /**
* 加载整数常量将其写入一个新分配的虚拟寄存器并返回该寄存器 * 加载整数常量将其写入一个新分配的虚拟寄存器并返回该寄存器
* *
* @param ctx 当前 IR 上下文用于分配寄存器与添加指令 * @param ctx 当前 IR 上下文
* @param value 要加载的整数常量值 * @param value 整数常量值
* @return 存储该常量的新虚拟寄存器 * @return 存储该常量的新虚拟寄存器
*/ */
public static IRVirtualRegister loadConst(IRContext ctx, int value) { public static IRVirtualRegister loadConst(IRContext ctx, int value) {
@ -31,88 +24,150 @@ public class InstructionFactory {
return r; return r;
} }
/**
* 加载 long 类型常量到新寄存器
*
* @param ctx 当前 IR 上下文
* @param value long 类型常量值
* @return 存储该常量的新虚拟寄存器
*/
public static IRVirtualRegister loadConst(IRContext ctx, long value) {
IRVirtualRegister r = ctx.newRegister();
ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(value)));
return r;
}
/**
* 加载 float 类型常量到新寄存器
*
* @param ctx 当前 IR 上下文
* @param value float 类型常量值
* @return 存储该常量的新虚拟寄存器
*/
public static IRVirtualRegister loadConst(IRContext ctx, float value) {
IRVirtualRegister r = ctx.newRegister();
ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(value)));
return r;
}
/**
* 加载 double 类型常量到新寄存器
*
* @param ctx 当前 IR 上下文
* @param value double 类型常量值
* @return 存储该常量的新虚拟寄存器
*/
public static IRVirtualRegister loadConst(IRContext ctx, double value) {
IRVirtualRegister r = ctx.newRegister();
ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(value)));
return r;
}
/** /**
* 执行二元运算如加法减法等结果写入新分配的虚拟寄存器并返回该寄存器 * 执行二元运算如加法减法等结果写入新分配的虚拟寄存器并返回该寄存器
* *
* @param ctx 当前 IR 上下文 * @param ctx 当前 IR 上下文
* @param op 运算类型IROpCode 枚举 ADD_I32 * @param op 二元运算操作码
* @param a 第一个操作数寄存器 * @param a 操作数寄存器
* @param b 第二个操作数寄存器 * @param b 操作数寄存器
* @return 保存运算结果的新虚拟寄存器 * @return 存储结果的新虚拟寄存器
*/ */
public static IRVirtualRegister binOp(IRContext ctx, IROpCode op, public static IRVirtualRegister binOp(IRContext ctx, IROpCode op, IRVirtualRegister a, IRVirtualRegister b) {
IRVirtualRegister a, IRVirtualRegister b) {
IRVirtualRegister dest = ctx.newRegister(); IRVirtualRegister dest = ctx.newRegister();
ctx.addInstruction(new BinaryOperationInstruction(op, dest, a, b)); ctx.addInstruction(new BinaryOperationInstruction(op, dest, a, b));
return dest; return dest;
} }
/* ====================================================================== */
/* 直接写入指定寄存器 */
/* ====================================================================== */
/** /**
* 加载整数常量到指定虚拟寄存器 * 加载常量到指定寄存器
* *
* @param ctx 当前 IR 上下文 * @param ctx 当前 IR 上下文
* @param dest 目标寄存器 * @param dest 目标虚拟寄存器
* @param value 要加载的整数常量 * @param value IR 常量值
*/ */
public static void loadConstInto(IRContext ctx, IRVirtualRegister dest, IRConstant value) { public static void loadConstInto(IRContext ctx, IRVirtualRegister dest, IRConstant value) {
ctx.addInstruction(new LoadConstInstruction(dest, value)); ctx.addInstruction(new LoadConstInstruction(dest, value));
} }
/** /**
* 对两个寄存器执行二元运算将结果写入指定目标寄存器 * 执行二元运算将结果写入指定寄存器
* *
* @param ctx 当前 IR 上下文 * @param ctx 当前 IR 上下文
* @param op 运算类型IROpCode 枚举 * @param op 二元运算操作码
* @param a 第一个操作数寄存器 * @param a 操作数寄存器
* @param b 第二个操作数寄存器 * @param b 操作数寄存器
* @param dest 运算结果目标寄存器 * @param dest 目标虚拟寄存器
*/ */
public static void binOpInto(IRContext ctx, IROpCode op, public static void binOpInto(IRContext ctx, IROpCode op, IRVirtualRegister a, IRVirtualRegister b, IRVirtualRegister dest) {
IRVirtualRegister a, IRVirtualRegister b,
IRVirtualRegister dest) {
ctx.addInstruction(new BinaryOperationInstruction(op, dest, a, b)); ctx.addInstruction(new BinaryOperationInstruction(op, dest, a, b));
} }
/** /**
* Move 指令src dest若寄存器相同也安全 * 生成值拷贝语义src dest
* <p> * 若类型无法推断默认采用 int 方案ADD_I32, src+0
* 实现方式: dest = src + 0即加上常量 0
* </p>
* *
* @param ctx 当前 IR 上下文 * @param ctx 当前 IR 上下文
* @param src 源寄存器 * @param src 源寄存器
* @param dest 目标寄存器 * @param dest 目标寄存器
*/ */
public static void move(IRContext ctx, IRVirtualRegister src, IRVirtualRegister dest) { public static void move(IRContext ctx, IRVirtualRegister src, IRVirtualRegister dest) {
// 自赋值无需任何操作避免生成多余的常量 0 寄存器
if (src == dest) { if (src == dest) {
return; return;
} }
// 回退实现: dest = src + 0 String varType = ctx.getVarType(); // 需要 IRContext 提供
IRVirtualRegister zero = loadConst(ctx, 0); char suffix = '\0';
ctx.addInstruction(new BinaryOperationInstruction(IROpCode.ADD_I32, dest, src, zero)); if (varType != null) {
switch (varType) {
case "byte" -> suffix = 'b';
case "short" -> suffix = 's';
case "int" -> suffix = 'i';
case "long" -> suffix = 'l';
case "float" -> suffix = 'f';
case "double" -> suffix = 'd';
}
}
IRVirtualRegister zero;
IROpCode op = switch (suffix) {
case 'd' -> {
zero = loadConst(ctx, 0.0);
yield IROpCode.ADD_D64;
}
case 'f' -> {
zero = loadConst(ctx, 0.0f);
yield IROpCode.ADD_F32;
}
case 'l' -> {
zero = loadConst(ctx, 0L);
yield IROpCode.ADD_L64;
}
case 's' -> {
zero = loadConst(ctx, 0);
yield IROpCode.ADD_S16;
}
case 'b' -> {
zero = loadConst(ctx, 0);
yield IROpCode.ADD_B8;
}
default -> {
zero = loadConst(ctx, 0);
yield IROpCode.ADD_I32;
}
};
ctx.addInstruction(new BinaryOperationInstruction(op, dest, src, zero));
} }
/* ====================================================================== */
/* 控制流指令 */
/* ====================================================================== */
/** /**
* 生成无条件跳转JMP指令跳转到指定标签 * 生成无条件跳转指令
* *
* @param ctx 当前 IR 上下文 * @param ctx 当前 IR 上下文
* @param label 目标标签名 * @param label 跳转目标标签
*/ */
public static void jmp(IRContext ctx, String label) { public static void jmp(IRContext ctx, String label) {
ctx.addInstruction(new IRJumpInstruction(label)); ctx.addInstruction(new IRJumpInstruction(label));
} }
/** /**
* IR 中插入一个标签Label * IR 中插入标签
* *
* @param ctx 当前 IR 上下文 * @param ctx 当前 IR 上下文
* @param label 标签名 * @param label 标签名
@ -122,22 +177,18 @@ public class InstructionFactory {
} }
/** /**
* 比较跳转 if a < b goto label根据条件跳转到目标标签 * 比较两个寄存器并根据结果跳转到指定标签
* *
* @param ctx 当前 IR 上下文 * @param ctx 当前 IR 上下文
* @param cmp 比较操作码 IROpCode.LT_I32 * @param cmp 比较操作码
* @param a 第一个操作数寄存器 * @param a 操作数寄存器
* @param b 第二个操作数寄存器 * @param b 操作数寄存器
* @param targetLabel 跳转目标标签 * @param targetLabel 跳转目标标签
*/ */
public static void cmpJump(IRContext ctx, IROpCode cmp, public static void cmpJump(IRContext ctx, IROpCode cmp, IRVirtualRegister a, IRVirtualRegister b, String targetLabel) {
IRVirtualRegister a, IRVirtualRegister b,
String targetLabel) {
ctx.addInstruction(new IRCompareJumpInstruction(cmp, a, b, targetLabel)); ctx.addInstruction(new IRCompareJumpInstruction(cmp, a, b, targetLabel));
} }
/* ---------------- 返回 ---------------- */
/** /**
* 生成返回指令带返回值 * 生成返回指令带返回值
* *
@ -149,7 +200,7 @@ public class InstructionFactory {
} }
/** /**
* 生成无返回值 return 指令 void 函数 * 生成无返回值void返回指令
* *
* @param ctx 当前 IR 上下文 * @param ctx 当前 IR 上下文
*/ */

View File

@ -10,13 +10,23 @@ import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode; import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Locale;
/** /**
* StatementBuilder AST 语句节点 ({@link StatementNode}) 转换为 IR 指令序列的构建器 * <b>StatementBuilder</b> AST 语句节点 ({@link StatementNode}) IR 指令序列的构建器
* <p> * <p>
* 负责将各种语句节点循环分支表达式赋值声明返回等生成对应的 IR 指令并管理作用域和控制流标签 * 负责将各类语句如循环分支表达式赋值声明返回breakcontinue
* 转换为对应的 IR 指令并自动管理作用域虚拟寄存器分配以及控制流标签 break/continue 目标
* </p> * </p>
*
* <ul>
* <li>支持多种语句类型的分发与转换</li>
* <li> {@link ExpressionBuilder} 协作完成表达式相关 IR 生成</li>
* <li>负责控制流跳转分支循环的标签分配与维护</li>
* <li>在变量赋值和声明时自动常量折叠和登记</li>
* </ul>
*
* @author [你的名字]
* @since 1.0
*/ */
public class StatementBuilder { public class StatementBuilder {
@ -24,79 +34,126 @@ public class StatementBuilder {
* 当前 IR 上下文包含作用域指令序列等信息 * 当前 IR 上下文包含作用域指令序列等信息
*/ */
private final IRContext ctx; private final IRContext ctx;
/** /**
* 表达式 IR 构建器用于将表达式节点转为 IR 指令 * 表达式 IR 构建器用于将表达式节点转为 IR 指令
*/ */
private final ExpressionBuilder expr; private final ExpressionBuilder expr;
/** /**
* break 目标标签栈保存每层循环的结束标签 * break 目标标签栈保存每层循环的结束标签用于 break 跳转
*/ */
private final ArrayDeque<String> breakTargets = new ArrayDeque<>(); private final ArrayDeque<String> breakTargets = new ArrayDeque<>();
/** /**
* continue 目标标签栈保存每层循环的 step 起始标签 * continue 目标标签栈保存每层循环的 step 起始标签用于 continue 跳转
*/ */
private final ArrayDeque<String> continueTargets = new ArrayDeque<>(); private final ArrayDeque<String> continueTargets = new ArrayDeque<>();
/** /**
* 构造方法 * 构造方法初始化 StatementBuilder
* *
* @param ctx IR 编译上下文环境 * @param ctx IR 编译上下文环境包含作用域标签指令等信息
*/ */
public StatementBuilder(IRContext ctx) { public StatementBuilder(IRContext ctx) {
this.ctx = ctx; this.ctx = ctx;
this.expr = new ExpressionBuilder(ctx); this.expr = new ExpressionBuilder(ctx);
} }
private static char typeSuffixFromType(String type) {
if (type == null) return '\0';
return switch (type.toLowerCase(Locale.ROOT)) {
case "byte" -> 'b';
case "short" -> 's';
case "long" -> 'l';
case "float" -> 'f';
case "double" -> 'd';
default -> '\0'; // 其余默认按 32-bit 整型处理
};
}
/** /**
* 将一个 AST 语句节点转为 IR 指令序列 * 将一个 AST 语句节点转为 IR 指令序列
* <p>
* 根据节点类型分发到对应的处理方法 * 根据节点类型分发到对应的处理方法
* 支持循环分支表达式语句赋值声明返回breakcontinue
* 不支持的语句类型会抛出异常
* </p>
* *
* @param stmt 待转换的语句节点 * @param stmt 待转换的语句节点不能为空
* @throws IllegalStateException 若遇到不支持的语句类型 break/continue 不在循环体中
*/ */
public void build(StatementNode stmt) { public void build(StatementNode stmt) {
if (stmt instanceof LoopNode loop) { if (stmt instanceof LoopNode loop) {
// 循环语句
buildLoop(loop); buildLoop(loop);
return; return;
} }
if (stmt instanceof IfNode ifNode) { if (stmt instanceof IfNode ifNode) {
// 分支if-else语句
buildIf(ifNode); buildIf(ifNode);
return; return;
} }
if (stmt instanceof ExpressionStatementNode(ExpressionNode exp, NodeContext _)) { if (stmt instanceof ExpressionStatementNode(ExpressionNode exp, NodeContext _)) {
// 纯表达式语句 foo();
expr.build(exp); expr.build(exp);
return; return;
} }
if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs, NodeContext _)) { if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs, NodeContext _)) {
// 赋值语句 a = b + 1;
final String type = ctx.getScope().lookupType(var); final String type = ctx.getScope().lookupType(var);
// 1. 设置声明变量的类型
ctx.setVarType(type); ctx.setVarType(type);
IRVirtualRegister target = getOrDeclareRegister(var, type); IRVirtualRegister target = getOrDeclareRegister(var, type);
expr.buildInto(rhs, target); expr.buildInto(rhs, target);
// 2. 清除变量声明 // 赋值时尝试记录/清除常量
try {
Object constVal = tryFoldConst(rhs);
if (constVal != null)
ctx.getScope().setConstValue(var, constVal);
else
ctx.getScope().clearConstValue(var);
} catch (Throwable ignored) {
}
ctx.clearVarType(); ctx.clearVarType();
return;
}
// ==== 支持数组下标赋值 arr[idx] = value ====
if (stmt instanceof IndexAssignmentNode idxAssign) {
IndexExpressionNode target = idxAssign.target();
// 1. 目标数组寄存器多维时用 buildIndexRef 拿引用
IRVirtualRegister arrReg = (target.array() instanceof IndexExpressionNode inner)
? expr.buildIndexRef(inner)
: expr.build(target.array());
// 2. 下标与右值
IRVirtualRegister idxReg = expr.build(target.index());
IRVirtualRegister valReg = expr.build(idxAssign.value());
// 3. 选择内置函数名 __setindex_x根据元素类型分派
String func = "__setindex_r";
org.jcnc.snow.compiler.parser.ast.base.ExpressionNode base = target.array();
while (base instanceof IndexExpressionNode innerIdx) base = innerIdx.array();
if (base instanceof IdentifierNode id) {
String arrType = ctx.getScope().lookupType(id.name());
if (arrType != null) {
String elemType = arrType.endsWith("[]") ? arrType.substring(0, arrType.length() - 2) : arrType;
switch (elemType) {
case "byte" -> func = "__setindex_b";
case "short" -> func = "__setindex_s";
case "int" -> func = "__setindex_i";
case "long" -> func = "__setindex_l";
case "float" -> func = "__setindex_f";
case "double" -> func = "__setindex_d";
case "boolean" -> func = "__setindex_i";
case "string" -> func = "__setindex_r";
default -> func = "__setindex_r";
}
}
}
// 4. 生成 CALL 指令
java.util.List<org.jcnc.snow.compiler.ir.core.IRValue> argv =
java.util.List.of(arrReg, idxReg, valReg);
ctx.addInstruction(new org.jcnc.snow.compiler.ir.instruction.CallInstruction(null, func, argv));
// 5. 赋值后清理常量绑定
try {
if (base instanceof IdentifierNode id2) {
ctx.getScope().clearConstValue(id2.name());
}
} catch (Throwable ignored) {}
return; return;
} }
if (stmt instanceof DeclarationNode decl) { if (stmt instanceof DeclarationNode decl) {
// 变量声明语句 int a = 1; // 变量声明语句 int a = 1;
if (decl.getInitializer().isPresent()) { if (decl.getInitializer().isPresent()) {
@ -113,16 +170,21 @@ public class StatementBuilder {
// 即使初始值是某个已存在变量 outer_i这里是值的拷贝 // 即使初始值是某个已存在变量 outer_i这里是值的拷贝
expr.buildInto(decl.getInitializer().get(), dest); expr.buildInto(decl.getInitializer().get(), dest);
// 4. 清理类型设置防止影响后续变量声明 // 声明赋初值时登记常量
ctx.clearVarType(); try {
Object constVal = tryFoldConst(decl.getInitializer().get());
if (constVal != null)
ctx.getScope().setConstValue(decl.getName(), constVal);
else
ctx.getScope().clearConstValue(decl.getName());
} catch (Throwable ignored) {
}
// 5. 在作用域内将变量名与新分配的寄存器进行绑定 ctx.clearVarType();
// 这样后续对该变量的任何操作都只会影响 dest不会反向影响初值表达式中的源变量
ctx.getScope().declare(decl.getName(), decl.getType(), dest); ctx.getScope().declare(decl.getName(), decl.getType(), dest);
} else { } else {
// 仅声明变量无初值 int a;
// 在作用域内声明并分配新寄存器但不进行初始化
ctx.getScope().declare(decl.getName(), decl.getType()); ctx.getScope().declare(decl.getName(), decl.getType());
ctx.getScope().clearConstValue(decl.getName());
} }
return; return;
} }
@ -159,10 +221,11 @@ public class StatementBuilder {
} }
/** /**
* 获取变量名对应的寄存器不存在则声明一个新的 * 获取变量名对应的寄存器如果尚未声明则新声明一个并返回
* *
* @param name 变量名 * @param name 变量名不能为空
* @return 变量对应的虚拟寄存器 * @param type 变量类型不能为空
* @return 变量对应的虚拟寄存器 {@link IRVirtualRegister}
*/ */
private IRVirtualRegister getOrDeclareRegister(String name, String type) { private IRVirtualRegister getOrDeclareRegister(String name, String type) {
IRVirtualRegister reg = ctx.getScope().lookup(name); IRVirtualRegister reg = ctx.getScope().lookup(name);
@ -174,19 +237,21 @@ public class StatementBuilder {
} }
/** /**
* 批量构建一组语句节点顺序处理每个语句 * 批量构建一组语句节点顺序依次处理
* *
* @param stmts 语句节点集合 * @param stmts 语句节点集合不可为 null
*/ */
private void buildStatements(Iterable<StatementNode> stmts) { private void buildStatements(Iterable<StatementNode> stmts) {
for (StatementNode s : stmts) build(s); for (StatementNode s : stmts) build(s);
} }
/** /**
* 构建循环语句for/while * 构建循环语句for/while包括初始语句条件判断循环体更新语句跳回判断等 IR 指令
* 处理流程: 初始语句 条件判断 循环体 更新语句 跳回条件 * <p>
* 自动维护 break/continue 的目标标签
* </p>
* *
* @param loop 循环节点 * @param loop 循环节点不能为空
*/ */
private void buildLoop(LoopNode loop) { private void buildLoop(LoopNode loop) {
if (loop.init() != null) build(loop.init()); if (loop.init() != null) build(loop.init());
@ -222,9 +287,11 @@ public class StatementBuilder {
/** /**
* 构建分支语句if/else * 构建分支语句if/else
* 处理流程: 条件判断 then 分支 else 分支可选 * <p>
* 包括条件判断then 分支else 分支可选结束标签等
* </p>
* *
* @param ifNode if 语句节点 * @param ifNode if 语句节点不能为空
*/ */
private void buildIf(IfNode ifNode) { private void buildIf(IfNode ifNode) {
String lblElse = ctx.newLabel(); String lblElse = ctx.newLabel();
@ -245,11 +312,14 @@ public class StatementBuilder {
} }
/** /**
* 条件跳转指令的生成 * 发射条件跳转指令如果 cond 不成立则跳转到 falseLabel
* 如果是二元比较表达式直接使用对应比较操作码否则等价于与 0 比较 * <p>
* 对于二元比较表达式会选择恰当的比较指令
* 其他类型表达式等价于 (cond == 0) 时跳转
* </p>
* *
* @param cond 条件表达式 * @param cond 条件表达式节点不可为 null
* @param falseLabel 条件不成立时跳转到的标签 * @param falseLabel 条件不成立时跳转的标签不可为 null
*/ */
private void emitConditionalJump(ExpressionNode cond, String falseLabel) { private void emitConditionalJump(ExpressionNode cond, String falseLabel) {
if (cond instanceof BinaryExpressionNode( if (cond instanceof BinaryExpressionNode(
@ -271,7 +341,79 @@ public class StatementBuilder {
} else { } else {
IRVirtualRegister condReg = expr.build(cond); IRVirtualRegister condReg = expr.build(cond);
IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0); IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
InstructionFactory.cmpJump(ctx, IROpCode.CMP_IEQ, condReg, zero, falseLabel); InstructionFactory.cmpJump(ctx, org.jcnc.snow.compiler.ir.core.IROpCode.CMP_IEQ, condReg, zero, falseLabel);
} }
} }
/**
* 递归尝试对表达式做常量折叠constant folding
* <p>
* 该方法会根据表达式类型进行常量求值
* <ul>
* <li>如果是数字字面量解析为 Integer Double</li>
* <li>如果是字符串字面量直接返回字符串内容</li>
* <li>如果是布尔字面量返回 1true 0false</li>
* <li>如果是数组字面量会递归所有元素全部是常量时返回包含常量元素的 List</li>
* <li>如果是标识符节点尝试从作用域查找是否登记为常量如果找到则返回</li>
* <li>其余类型或无法确定常量时返回 null</li>
* </ul>
* 用于全局/局部常量传播优化与类型推断
*
* @param expr 待折叠的表达式节点允许为 null
* @return 如果可折叠则返回其常量值 IntegerDoubleStringList否则返回 null
*/
private Object tryFoldConst(ExpressionNode expr) {
// 1. 空节点直接返回 null
if (expr == null) return null;
// 2. 数字字面量尝试解析为 Integer Double
if (expr instanceof NumberLiteralNode n) {
String s = n.value(); // 获取文本内容
try {
// 判断是否为浮点型包含 . e/E 科学计数法
if (s.contains(".") || s.contains("e") || s.contains("E")) {
return Double.parseDouble(s); // 解析为 Double
}
return Integer.parseInt(s); // 否则解析为 Integer
} catch (NumberFormatException e) {
// 解析失败返回 null
return null;
}
}
// 3. 字符串字面量直接返回字符串内容
if (expr instanceof StringLiteralNode s) {
return s.value();
}
// 4. 布尔字面量true 返回 1false 返回 0
if (expr instanceof BoolLiteralNode b) {
return b.getValue() ? 1 : 0;
}
// 5. 数组字面量递归所有元素做常量折叠只有全为常量时才返回 List
if (expr instanceof ArrayLiteralNode arr) {
java.util.List<Object> list = new java.util.ArrayList<>();
for (ExpressionNode e : arr.elements()) {
Object v = tryFoldConst(e); // 递归折叠每个元素
if (v == null) return null; // 只要有一个不是常量则整个数组不是常量
list.add(v);
}
// 所有元素均为常量返回只读 List
return java.util.List.copyOf(list);
}
// 6. 标识符尝试查找该变量在当前作用域是否登记为常量
if (expr instanceof IdentifierNode id) {
try {
Object v = ctx.getScope().getConstValue(id.name());
if (v != null) return v; // 查到常量则返回
} catch (Throwable ignored) {
}
}
// 7. 其他情况均视为不可折叠返回 null
return null;
}
} }

View File

@ -0,0 +1,70 @@
package org.jcnc.snow.compiler.ir.common;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 全局常量表用于跨模块编译期常量查询和折叠
*
* <p>
* 主要功能
* <ul>
* <li> IRProgramBuilder 预扫描阶段将所有模块级 <code>const</code> 常量
* ModuleA.a注册到全局常量表支持跨模块访问</li>
* <li>后续任何阶段均可通过 {@link #get(String)} 查询已注册常量实现编译期常量折叠</li>
* <li>保证线程安全支持并发注册和访问</li>
* </ul>
* <p>
* 常量的 key 格式为模块名.常量名 "ModuleA.a"以便唯一标识
*
* <p>
* 典型用法
* <pre>
* GlobalConstTable.register("ModuleA.a", 10); // 注册常量
* Object val = GlobalConstTable.get("ModuleA.a"); // 查询常量
* </pre>
*/
public final class GlobalConstTable {
/** 存储全局常量: “ModuleName.constName” → 常量值。线程安全。 */
private static final Map<String, Object> CONSTS = new ConcurrentHashMap<>();
/**
* 工具类构造器防止实例化
*/
private GlobalConstTable() { /* utility class */ }
/**
* 注册一个全局常量到表中只在首次注册时生效避免被覆盖
*
* @param qualifiedName 常量的全限定名 "ModuleA.a"
* @param value 常量的字面值 10字符串布尔等
* @throws IllegalArgumentException 名称为 null 或空串时抛出
*/
public static void register(String qualifiedName, Object value) {
if (qualifiedName == null || qualifiedName.isBlank()) {
throw new IllegalArgumentException("常量名不能为空");
}
CONSTS.putIfAbsent(qualifiedName, value);
}
/**
* 获取指定全局常量的值
*
* @param qualifiedName 常量的全限定名 "ModuleA.a"
* @return 查到的常量值如果未注册则返回 null
*/
public static Object get(String qualifiedName) {
return CONSTS.get(qualifiedName);
}
/**
* 返回全部已注册常量的不可变视图快照
* <p>注意只读不可修改</p>
*
* @return key=常量名value=常量值的不可变 Map
*/
public static Map<String, Object> all() {
return Map.copyOf(CONSTS);
}
}

View File

@ -1,11 +1,9 @@
package org.jcnc.snow.compiler.lexer.core; package org.jcnc.snow.compiler.lexer.core;
import org.jcnc.snow.common.SnowConfig;
import org.jcnc.snow.compiler.lexer.base.TokenScanner; import org.jcnc.snow.compiler.lexer.base.TokenScanner;
import org.jcnc.snow.compiler.lexer.scanners.*; import org.jcnc.snow.compiler.lexer.scanners.*;
import org.jcnc.snow.compiler.lexer.token.Token; import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.lexer.token.TokenType; import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.lexer.utils.TokenPrinter;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
@ -37,7 +35,7 @@ public class LexerEngine {
* @param sourceName 文件名诊断用 * @param sourceName 文件名诊断用
*/ */
public LexerEngine(String source, String sourceName) { public LexerEngine(String source, String sourceName) {
this.absPath = new File(sourceName).getAbsolutePath(); this.absPath = new File(sourceName).getAbsolutePath().replace('\\', '/');
this.context = new LexerContext(source); this.context = new LexerContext(source);
this.scanners = List.of( this.scanners = List.of(
new WhitespaceTokenScanner(), new WhitespaceTokenScanner(),
@ -56,9 +54,9 @@ public class LexerEngine {
/* 2. 后置整体校验 */ /* 2. 后置整体校验 */
validateTokens(); validateTokens();
/* 3. 打印 token */ /* 3. 打印 token */
if (SnowConfig.isDebug()) { // if (SnowConfig.isDebug()) {
TokenPrinter.print(tokens); // TokenPrinter.print(tokens);
} // }
/* 4. 统一报告错误 */ /* 4. 统一报告错误 */
report(errors); report(errors);
@ -127,8 +125,8 @@ public class LexerEngine {
/** /**
* 目前包含2条规则: <br> * 目前包含2条规则: <br>
* 1. Declare-Ident declare 后必须紧跟合法标识符并且只能一个<br> * 1. Declare-Ident declare 后必须紧跟合法标识符 const + 标识符并且只能一个<br>
* 2. Double-Ident declare 后若出现第二个 IDENTIFIER 视为多余<br> * 2. Double-Ident declare 后若出现第二个多余的 IDENTIFIER<br>
* <p>发现问题仅写入 {@link #errors}不抛异常</p> * <p>发现问题仅写入 {@link #errors}不抛异常</p>
*/ */
private void validateTokens() { private void validateTokens() {
@ -139,17 +137,28 @@ public class LexerEngine {
if (tok.getType() == TokenType.KEYWORD if (tok.getType() == TokenType.KEYWORD
&& "declare".equalsIgnoreCase(tok.getLexeme())) { && "declare".equalsIgnoreCase(tok.getLexeme())) {
// 第一个非 NEWLINE token // declare 后第一个非 NEWLINE token
Token id1 = findNextNonNewline(i); Token t1 = findNextNonNewline(i);
// 如果有 const允许
boolean hasConst = t1 != null
&& t1.getType() == TokenType.KEYWORD
&& "const".equalsIgnoreCase(t1.getLexeme());
int identStartIdx = hasConst ? tokens.indexOf(t1) : i;
// 找下一个非 NEWLINE token如果有 const就找下一个
Token id1 = findNextNonNewline(identStartIdx);
// id1 必须是 IDENTIFIER
if (id1 == null || id1.getType() != TokenType.IDENTIFIER) { if (id1 == null || id1.getType() != TokenType.IDENTIFIER) {
errors.add(err( errors.add(err(
(id1 == null ? tok : id1), (id1 == null ? (hasConst ? t1 : tok) : id1),
"declare 后必须跟合法标识符 (以字母或 '_' 开头)" "declare 后必须跟合法标识符 (可选 const 关键字)"
)); ));
continue; // 若首标识符就错后续检查可略 continue; // 若首标识符就错后续检查可略
} }
// 检查是否有第二个 IDENTIFIER // 检查是否有第二个多余的 IDENTIFIER
Token id2 = findNextNonNewline(tokens.indexOf(id1)); Token id2 = findNextNonNewline(tokens.indexOf(id1));
if (id2 != null && id2.getType() == TokenType.IDENTIFIER) { if (id2 != null && id2.getType() == TokenType.IDENTIFIER) {
errors.add(err(id2, "declare 声明中出现多余的标识符")); errors.add(err(id2, "declare 声明中出现多余的标识符"));

View File

@ -50,6 +50,6 @@ public class LexicalError {
*/ */
@Override @Override
public String toString() { public String toString() {
return file + ":" + line + ", 列 " + column + ": " + message; return "file:///" + file + ":" + line + ":" + column + ": " + message;
} }
} }

View File

@ -28,7 +28,7 @@ public class SymbolTokenScanner extends AbstractTokenScanner {
*/ */
@Override @Override
public boolean canHandle(char c, LexerContext ctx) { public boolean canHandle(char c, LexerContext ctx) {
return ":,().+-*/".indexOf(c) >= 0; return ":,().+-*/[]".indexOf(c) >= 0;
} }
/** /**
@ -53,6 +53,8 @@ public class SymbolTokenScanner extends AbstractTokenScanner {
case '/' -> TokenType.DIVIDE; case '/' -> TokenType.DIVIDE;
case '(' -> TokenType.LPAREN; case '(' -> TokenType.LPAREN;
case ')' -> TokenType.RPAREN; case ')' -> TokenType.RPAREN;
case '[' -> TokenType.LBRACKET;
case ']' -> TokenType.RBRACKET;
default -> TokenType.UNKNOWN; default -> TokenType.UNKNOWN;
}; };
return new Token(type, String.valueOf(c), line, col); return new Token(type, String.valueOf(c), line, col);

View File

@ -26,9 +26,9 @@ public class TokenFactory {
* 语言的保留关键字集合 * 语言的保留关键字集合
*/ */
private static final Set<String> KEYWORDS = Set.of private static final Set<String> KEYWORDS = Set.of
("module", "function", "parameter", "return_type", "body", "end", ("module", "function", "params", "returns", "body", "end",
"if", "then", "else", "loop", "declare", "return", "import", "init", "if", "then", "else", "loop", "declare", "return", "import", "init",
"cond", "step", "globals", "break", "continue"); "cond", "step", "globals", "break", "continue", "const");
/** /**
* 内置类型名称集合 intstring * 内置类型名称集合 intstring

View File

@ -14,10 +14,10 @@ public enum TokenType {
/** 普通标识符,如变量名、函数名等 */ /** 普通标识符,如变量名、函数名等 */
IDENTIFIER, IDENTIFIER,
/** 语言保留关键字(如 if、return、module 等) */ /** 关键字declare、if、else、loop、break、continue、return 等) */
KEYWORD, KEYWORD,
/** 内置类型名称(如 int、string、bool 等) */ /** 内置类型名byte、short、int、long、float、double、string、boolean、void 等) */
TYPE, TYPE,
/* ---------- 字面量 ---------- */ /* ---------- 字面量 ---------- */
@ -67,6 +67,12 @@ public enum TokenType {
/** 右括号 ')' */ /** 右括号 ')' */
RPAREN, RPAREN,
/** 左中括号 '[' */
LBRACKET,
/** 右中括号 ']' */
RBRACKET,
/** 相等比较符号 '==' */ /** 相等比较符号 '==' */
DOUBLE_EQUALS, DOUBLE_EQUALS,

View File

@ -0,0 +1,40 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import java.util.List;
/**
* {@code ArrayLiteralNode} 表示数组字面量表达式节点
* <p>
* 例如[1, 2, 3] [[1, 2], [3, 4]] 这样的语法结构均对应本节点类型
* </p>
*
* <ul>
* <li>{@link #elements()} 保存所有元素表达式节点</li>
* <li>{@link #context()} 表示该节点在源代码中的上下文信息如位置父节点等</li>
* </ul>
*/
public record ArrayLiteralNode(
/*
数组字面量中的所有元素表达式按顺序
*/
List<ExpressionNode> elements,
/*
节点的上下文信息如源码位置等
*/
NodeContext context
) implements ExpressionNode {
/**
* 返回字符串形式 {@code Array[1, 2, 3]}
*
* @return 表示该数组字面量节点的字符串
*/
@Override
public String toString() {
return "Array" + elements;
}
}

Some files were not shown because too many files have changed in this diff Show More