commit 0c2cb0357d91ffc7eb600db5857e1d2c6af88edb Author: Luke Date: Fri Jun 6 15:53:19 2025 +0800 feat: Initialize project skeleton, including basic directories and configuration files diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f7e7803 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +.idea + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store +/.idea/ +/Snow.tar +/src/main/java/org/jcnc/snow/compiler/ir.tar diff --git a/.run/Run.run.xml b/.run/Run.run.xml new file mode 100644 index 0000000..421bd23 --- /dev/null +++ b/.run/Run.run.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.run/SnowCompiler.run.xml b/.run/SnowCompiler.run.xml new file mode 100644 index 0000000..4b0cdab --- /dev/null +++ b/.run/SnowCompiler.run.xml @@ -0,0 +1,33 @@ + + + + + + \ No newline at end of file diff --git a/.run/build_project2tar.ps1.run.xml b/.run/build_project2tar.ps1.run.xml new file mode 100644 index 0000000..19c0a66 --- /dev/null +++ b/.run/build_project2tar.ps1.run.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/build/build_project2tar.ps1 b/build/build_project2tar.ps1 new file mode 100644 index 0000000..2587e79 --- /dev/null +++ b/build/build_project2tar.ps1 @@ -0,0 +1,26 @@ +$tarName = "Snow.tar" + +# 脚本当前目录(build文件夹) +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition + +# 上一级目录(snow根目录) +$parentDir = Split-Path -Parent $scriptDir + +# tar包的完整路径 +$tarPath = Join-Path $parentDir $tarName + +# 如果存在旧tar包,删除 +if (Test-Path $tarPath) { + Remove-Item $tarPath +} + +Write-Output "正在创建新的 $tarName 到 $parentDir ..." + +# 打包:先切换到 org\jcnc 目录下再压缩 snow 文件夹 +& tar -cf $tarPath -C "$scriptDir\..\src\main\java\org\jcnc" snow + +if (Test-Path $tarPath) { + Write-Output "✅ 成功创建 $tarName" +} else { + Write-Output "❌ 创建失败,请检查 tar 命令" +} diff --git a/build/readme.md b/build/readme.md new file mode 100644 index 0000000..8224329 --- /dev/null +++ b/build/readme.md @@ -0,0 +1,5 @@ +使用 build——project2tar.ps1 需要在管理员权限下的 PowerShell 输入下面的内容 + +Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned + +Tip:RemoteSigned 表示:本地创建的脚本可以运行,从互联网下载的脚本必须有签名。 \ No newline at end of file diff --git a/lib/math/factorial/factorial.snow b/lib/math/factorial/factorial.snow new file mode 100644 index 0000000..e4e84db --- /dev/null +++ b/lib/math/factorial/factorial.snow @@ -0,0 +1,34 @@ +module: Math + function: main + parameter: + return_type: int + body: + Math.factorial(6) + + return 0 + end body + end function + + + function: factorial + parameter: + declare n:int + return_type: int + body: + declare num1:int = 1 + loop: + initializer: + declare counter:int = 1 + condition: + counter <= n + update: + counter = counter + 1 + body: + num1 = num1 * counter + end body + end loop + return num1 + end body + end function + +end module diff --git a/lib/test/Dead_loop.snow b/lib/test/Dead_loop.snow new file mode 100644 index 0000000..5f73bfb --- /dev/null +++ b/lib/test/Dead_loop.snow @@ -0,0 +1,18 @@ +function: main + parameter: + return_type: int + body: + loop: + initializer: + declare i:int = 0 + condition: + 1 == 1 + update: + i = i + 1 + body: + + end body + end loop + return 0 + end body +end function \ No newline at end of file diff --git a/lib/test/opcode.snow b/lib/test/opcode.snow new file mode 100644 index 0000000..3a1ffe6 --- /dev/null +++ b/lib/test/opcode.snow @@ -0,0 +1,22 @@ +module: Math + function: factorial + parameter: + declare n:int + return_type: int + body: + declare num1:int = 1 + loop: + initializer: + declare counter:int = 1 + condition: + counter <= n + update: + counter = counter + 1 + body: + num1 = num1 * counter + end body + end loop + return num1 + end body + end function +end module diff --git a/playground/main.snow b/playground/main.snow new file mode 100644 index 0000000..e2beb69 --- /dev/null +++ b/playground/main.snow @@ -0,0 +1,13 @@ +module: Main + import:Math + function: main + parameter: + return_type: int + body: + Math.factorial(6L,1L) + + return 0 + end body + end function + +end module diff --git a/playground/test.snow b/playground/test.snow new file mode 100644 index 0000000..161b7b2 --- /dev/null +++ b/playground/test.snow @@ -0,0 +1,11 @@ +module: Math + function: factorial + parameter: + declare n1: long + declare n2: long + return_type: long + body: + return n1+n2 + end body + end function +end module diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..b935411 --- /dev/null +++ b/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + org.jcnc.snow + Snow + 1.0-SNAPSHOT + + + 23 + 23 + 0.10.5 + + + + + native + + + + + org.graalvm.buildtools + native-maven-plugin + ${native.maven.plugin.version} + true + + + build-native + + compile-no-fork + + package + + + test-native + + test + + test + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.12.1 + + true + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + org.jcnc.snow.compiler.cli.SnowCompiler + true + + + + + + + + + + + \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..8efd8f0 --- /dev/null +++ b/readme.md @@ -0,0 +1 @@ +## Snow \ No newline at end of file diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 0000000..4759b93 --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,6 @@ +module org.jcnc.snow.compiler { + requires java.desktop; + requires java.logging; + exports org.jcnc.snow.compiler.ir.core; + exports org.jcnc.snow.compiler.ir.instruction; +} diff --git a/src/main/java/org/jcnc/snow/compiler/backend/alloc/RegisterAllocator.java b/src/main/java/org/jcnc/snow/compiler/backend/alloc/RegisterAllocator.java new file mode 100644 index 0000000..10e50a9 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/backend/alloc/RegisterAllocator.java @@ -0,0 +1,77 @@ +package org.jcnc.snow.compiler.backend.alloc; + +import org.jcnc.snow.compiler.ir.core.IRFunction; +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.core.IRValue; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.HashMap; +import java.util.Map; + +/** + * 线性扫描寄存器分配器 + *

+ * 本类为 IR(中间表示)中的虚拟寄存器分配物理槽号,通常用于后端生成目标代码时确定 + * 各虚拟寄存器实际对应的物理寄存器或栈槽号。采用简单的线性扫描分配策略。 + *

+ *

+ * 分配过程如下: + *

    + *
  1. 优先为函数参数分配槽号,从 0 开始,按参数顺序递增。
  2. + *
  3. 遍历函数体的每条指令,为尚未分配的目标寄存器及其操作数分配新的槽号。
  4. + *
  5. 保证每个虚拟寄存器在函数作用域内分配唯一且连续的槽号。
  6. + *
+ *

+ */ +public final class RegisterAllocator { + + /** + * 虚拟寄存器到槽号的分配映射表。 + *

+ * 键:虚拟寄存器 {@link IRVirtualRegister}; + * 值:对应分配的槽编号 {@link Integer}。 + *

+ */ + private final Map map = new HashMap<>(); + + /** + * 为指定 IR 函数分配所有虚拟寄存器的槽号。 + *

+ * 分配顺序说明: + *

    + *
  1. 首先为所有参数分配槽号。
  2. + *
  3. 然后线性遍历函数体,为每个指令涉及的虚拟寄存器(目标或操作数)分配槽号, + * 遇到未分配的寄存器立即分配下一个可用槽号。
  4. + *
+ * 返回的映射不可变,防止外部修改。 + *

+ * + * @param fn 需要进行寄存器分配的 IR 函数对象 + * @return 一个不可变映射,记录所有虚拟寄存器到槽号的分配关系 + */ + public Map allocate(IRFunction fn) { + int next = 0; // 下一个可分配的槽编号 + + // 1. 优先为所有参数分配槽号(顺序与参数列表一致) + for (IRVirtualRegister param : fn.parameters()) { + map.put(param, next++); + } + + // 2. 线性扫描整个函数体,依次为指令涉及的虚拟寄存器分配槽号 + for (IRInstruction inst : fn.body()) { + // 2.1 若指令有目标寄存器且尚未分配,则分配新槽号 + if (inst.dest() != null && !map.containsKey(inst.dest())) { + map.put(inst.dest(), next++); + } + // 2.2 遍历所有操作数,若为虚拟寄存器且尚未分配,则分配新槽号 + for (IRValue operand : inst.operands()) { + if (operand instanceof IRVirtualRegister vr && !map.containsKey(vr)) { + map.put(vr, next++); + } + } + } + + // 3. 返回不可变映射,防止外部代码误修改分配结果 + return Map.copyOf(map); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/backend/builder/VMCodeGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/builder/VMCodeGenerator.java new file mode 100644 index 0000000..4bc2e11 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/backend/builder/VMCodeGenerator.java @@ -0,0 +1,91 @@ +package org.jcnc.snow.compiler.backend.builder; + +import org.jcnc.snow.compiler.backend.core.InstructionGenerator; +import org.jcnc.snow.compiler.ir.core.IRFunction; +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 虚拟机代码生成器(VMCodeGenerator) + *

+ * 本类作为指令生成器调度中心,不负责任何具体 IR 指令到 VM 指令的转换实现, + * 仅负责根据指令类型分发到对应的 {@link InstructionGenerator} 子类完成实际生成。 + *

+ *

+ * 工作流程简述: + *

    + *
  1. 接收一组已注册的 IR 指令生成器,并建立类型到生成器的映射表。
  2. + *
  3. 遍历 IR 函数体的每条指令,根据类型找到对应的生成器,调用其 generate 方法生成 VM 指令。
  4. + *
  5. 生成流程以函数为单位(beginFunction/endFunction)。
  6. + *
+ */ +public final class VMCodeGenerator { + + /** + * 指令类型到生成器的注册表(调度表)。 + *

+ * 键:IR 指令类型(Class对象), + * 值:对应的指令生成器实例。 + *

+ */ + private final Map, InstructionGenerator> registry; + + /** + * 虚拟寄存器到槽号的映射表,由 RegisterAllocator 负责生成。 + */ + private final Map slotMap; + + /** + * 虚拟机程序构建器,用于输出 VM 指令。 + */ + private final VMProgramBuilder out; + + /** + * 当前处理的函数名,用于部分指令生成逻辑(如主函数判断等)。 + */ + private String currentFn; + + /** + * 构造方法 + * + * @param slotMap 虚拟寄存器到槽号的映射 + * @param out 虚拟机程序构建器 + * @param generators 各类 IR 指令生成器集合,需预先构建 + */ + public VMCodeGenerator(Map slotMap, + VMProgramBuilder out, + List> generators) { + this.slotMap = slotMap; + this.out = out; + // 按类型注册各 IR 指令生成器,建立不可变类型-生成器映射表 + this.registry = generators.stream() + .collect(Collectors.toUnmodifiableMap(InstructionGenerator::supportedClass, g -> g)); + } + + /** + * 为一个 IR 函数生成虚拟机指令 + * + * @param fn 待生成的 IR 函数对象 + * @throws IllegalStateException 若遇到不支持的 IR 指令类型 + */ + public void generate(IRFunction fn) { + this.currentFn = fn.name(); + out.beginFunction(currentFn); // 输出函数起始 + for (IRInstruction ins : fn.body()) { + @SuppressWarnings("unchecked") + // 取得与当前 IR 指令类型匹配的生成器(泛型强转消除类型警告) + InstructionGenerator gen = + (InstructionGenerator) registry.get(ins.getClass()); + if (gen == null) { + throw new IllegalStateException("Unsupported IR: " + ins); + } + // 通过多态分发到实际生成器 + gen.generate(ins, out, slotMap, currentFn); + } + out.endFunction(); // 输出函数结束 + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/backend/builder/VMProgramBuilder.java b/src/main/java/org/jcnc/snow/compiler/backend/builder/VMProgramBuilder.java new file mode 100644 index 0000000..c3b9be3 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/backend/builder/VMProgramBuilder.java @@ -0,0 +1,195 @@ +package org.jcnc.snow.compiler.backend.builder; + +import org.jcnc.snow.vm.engine.VMOpCode; + +import java.util.*; + +/** + * VMProgramBuilder:构建线性 VM 程序(即按顺序存放所有 VM 指令)。 + *

+ * 本类用于编译器后端,将所有生成的 VM 指令(包括分支和调用指令)统一存储、管理。 + * 支持符号(如函数入口、标签地址)的延迟解析与回填(fix-up)机制, + * 可在目标尚未定义时提前生成分支或调用指令,定义后自动修正。 + *

+ *

+ * 常用于处理跨函数、跨标签的 CALL/JUMP 等复杂控制流,确保最终生成的 VM 指令地址一致正确。 + *

+ */ +public final class VMProgramBuilder { + /** 未解析目标的 CALL 指令信息(待修补) */ + private record CallFix(int index, String target, int nArgs) {} + + /** 未解析目标的分支指令(JUMP/IC_* 等待修补) */ + private record BranchFix(int index, String label) {} + + /** 占位符:用于表示尚未确定的符号地址 */ + private static final String PLACEHOLDER = "-1"; + + /** 按顺序存放的 VM 指令文本 */ + private final List code = new ArrayList<>(); + + // 虚拟机槽位编号到数据类型前缀的映射(如 0 -> 'I', 1 -> 'D' 等) + private final Map slotType = new HashMap<>(); + + /** 符号(如函数名、标签名)到其首地址(即指令序号/偏移量)的映射表 + * 主要用于跳转和调用,定位具体的代码位置 */ + private final Map addr = new HashMap<>(); + + /** 所有待回填(fix-up)的 CALL 调用指令记录 + * 由于被调用目标地址在编译时可能尚未确定,需要先记录,最终统一回填 */ + private final List callFixes = new ArrayList<>(); + + /** 所有待回填(fix-up)的分支跳转指令记录 + * 与 CALL 类似,分支指令的目标地址也可能需要编译后期再补充 */ + private final List branchFixes = new ArrayList<>(); + + /** 程序计数器(Program Counter),表示下一个生成指令将插入的位置 */ + private int pc = 0; + + /** + * 设置某个槽位对应的数据类型前缀 + * @param slot 槽位编号 + * @param prefix 类型前缀(如 'I' 表示 int,'D' 表示 double 等) + */ + public void setSlotType(int slot, char prefix) { + slotType.put(slot, prefix); + } + + /** + * 获取某个槽位对应的数据类型前缀 + * 若未指定则返回默认类型 'I'(int) + * @param slot 槽位编号 + * @return 类型前缀(如 'I', 'D' 等) + */ + public char getSlotType(int slot) { + return slotType.getOrDefault(slot, 'I'); + } + + + /** + * 标记函数入口或标签,并尝试修补所有等候该符号的指令。 + * @param name 符号名(函数名/标签名) + */ + public void beginFunction(String name) { + addr.put(name, pc); // 记录当前地址为入口 + patchCallFixes(name); // 修补所有待该符号的 CALL + patchBranchFixes(name); // 修补所有待该符号的分支 + } + + /** + * 结束函数(当前实现为空,方便 API 统一)。 + */ + public void endFunction() { /* no-op */ } + + /** + * 写入一条 VM 指令或标签。 + *
    + *
  • 如果以冒号结尾,视为标签,仅登记其地址,不写入指令流,也不递增 pc。
  • + *
  • 否则写入实际指令,并自增 pc。
  • + *
+ * @param line 一行 VM 指令文本或标签名(结尾有冒号) + */ + public void emit(String line) { + if (line.endsWith(":")) { // 是标签定义行 + String label = line.substring(0, line.length() - 1); + addr.put(label, pc); // 记录标签地址 + patchBranchFixes(label); // 修补所有以该标签为目标的分支指令 + return; // 标签行不写入 code,不递增 pc + } + code.add(line); // 普通指令写入 code + pc++; + } + + /** + * 生成 CALL 指令。 + * 支持延迟修补:若目标已知,直接写入地址;否则写入占位并登记 fix-up。 + * @param target 目标函数名 + * @param nArgs 参数个数 + */ + public void emitCall(String target, int nArgs) { + Integer a = resolve(target); + if (a != null) { + emit(VMOpCode.CALL + " " + a + " " + nArgs); + } else { + emit(VMOpCode.CALL + " " + PLACEHOLDER + " " + nArgs); + callFixes.add(new CallFix(pc - 1, target, nArgs)); + } + } + + /** + * 生成分支(JUMP 或 IC_*)指令。 + * 支持延迟修补机制。 + * @param opcode 指令名 + * @param label 目标标签名 + */ + public void emitBranch(String opcode, String label) { + Integer a = resolve(label); + if (a != null) { + emit(opcode + ' ' + a); + } else { + emit(opcode + ' ' + PLACEHOLDER); + branchFixes.add(new BranchFix(pc - 1, label)); + } + } + + /** + * 构建最终 VM 代码文本列表。 + *
    + *
  • 若存在未解析符号(CALL 或分支),则抛出异常。
  • + *
  • 否则返回不可变指令流。
  • + *
+ * @return 完整 VM 指令流 + * @throws IllegalStateException 若有未修补的符号引用 + */ + public List build() { + if (!callFixes.isEmpty() || !branchFixes.isEmpty()) { + throw new IllegalStateException( + "Unresolved symbols — CALL: " + callFixes + + ", BRANCH: " + branchFixes); + } + return List.copyOf(code); + } + + /** + * 解析符号地址。若全限定名找不到则降级尝试简单名。 + * @param sym 符号名 + * @return 地址或 null(未定义) + */ + private Integer resolve(String sym) { + Integer a = addr.get(sym); + if (a == null && sym.contains(".")) { + a = addr.get(sym.substring(sym.lastIndexOf('.') + 1)); + } + return a; + } + + /** + * 修补所有以 name 为目标的 CALL 占位符。 + * @param name 新定义的函数名 + */ + private void patchCallFixes(String name) { + for (Iterator it = callFixes.iterator(); it.hasNext();) { + CallFix f = it.next(); + // 目标函数名完全匹配或后缀匹配(兼容全限定名) + if (f.target.equals(name) || f.target.endsWith("." + name)) { + code.set(f.index, VMOpCode.CALL + " " + addr.get(name) + " " + f.nArgs); + it.remove(); + } + } + } + + /** + * 修补所有以 label 为目标的分支占位符。 + * @param label 新定义的标签名 + */ + private void patchBranchFixes(String label) { + for (Iterator it = branchFixes.iterator(); it.hasNext();) { + BranchFix f = it.next(); + if (f.label.equals(label)) { + String patched = code.get(f.index).replace(PLACEHOLDER, addr.get(label).toString()); + code.set(f.index, patched); + it.remove(); + } + } + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/backend/core/InstructionGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/core/InstructionGenerator.java new file mode 100644 index 0000000..f7acfd1 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/backend/core/InstructionGenerator.java @@ -0,0 +1,34 @@ +package org.jcnc.snow.compiler.backend.core; + +import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder; +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.Map; + +/** + * 通用指令生成器接口 + *

+ * 本接口规定了所有 IR 指令生成器(翻译器)必须实现的方法,负责将特定类型的 IR 指令 + * 翻译为虚拟机(VM)指令。每个具体的指令生成器都需要指定其支持的 IR 指令类型,并实现翻译生成的方法。 + * + * @param 指令生成器所支持的 IRInstruction 子类型 + */ +public interface InstructionGenerator { + /** + * 获取当前生成器支持的 IR 指令类型。 + * + * @return 当前生成器支持的 IRInstruction 类对象 + */ + Class supportedClass(); + + /** + * 将一条 IR 指令翻译为对应的 VM 指令序列。 + * + * @param ins 当前待翻译的 IR 指令 + * @param out 虚拟机程序构建器,用于输出 VM 指令 + * @param slotMap 虚拟寄存器与实际槽号的映射关系 + * @param currentFn 当前函数名称(用于作用域或调试等) + */ + void generate(T ins, VMProgramBuilder out, Map slotMap, String currentFn); +} diff --git a/src/main/java/org/jcnc/snow/compiler/backend/doc/README.md b/src/main/java/org/jcnc/snow/compiler/backend/doc/README.md new file mode 100644 index 0000000..b24d5be --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/backend/doc/README.md @@ -0,0 +1,56 @@ +# Snow Compiler - 后端模块 + +> Snow 编译器的后端模块 —— 负责将中间表示(IR)翻译、优化并生成虚拟机指令序列(VM) + +## 项目简介 + +**后端模块(Backend)** 是 [Snow 编译器]() 的核心组成部分之一,承接中间表示(IR)与运行时虚拟机(VM)之间的桥梁。 +它主要完成以下工作: + +1. **寄存器分配**:将 IR 中的虚拟寄存器映射为物理槽号或栈槽; +2. **指令生成与调度**:为每条 IR 指令分配对应的 VM 指令生成器,并负责调用它们输出指令文本; +3. **程序构建**:管理函数入口、标签定义、延迟修补(fix-up)CALL/JUMP 等控制流指令地址; +4. **操作码与常量映射**:提供 IR 操作码到 VM 操作码的映射工具,以及根据常量类型选择 PUSH/STORE 指令。 + +后端模块设计注重可扩展性:各类 IR → VM 的转换逻辑被拆分到不同的 `*Generator` 类中,新增指令只需注册新的生成器;寄存器分配和程序构建也各司其职,职责清晰,便于维护和测试。 + +## 核心功能 + +* **线性扫描寄存器分配**:`RegisterAllocator` 按参数优先、指令顺序为 IR 虚拟寄存器分配连续槽号。 +* **指令生成器体系**:`InstructionGenerator` 接口 + 多个 `*Generator` 实现,支持二元运算、跳转、调用、标签、常量加载、一元运算、返回等指令。 +* **生成器调度**:`VMCodeGenerator` 建立类型到生成器的映射,遍历 IR 函数体并分发调用,实现多态化指令生成。 +* **程序构建与延迟修补**:`VMProgramBuilder` 维护指令流、符号地址表及待修补列表,实现对 `CALL`/`JUMP` 等指令的延迟地址填充。 +* **操作码与常量映射**:`IROpCodeMapper` 提供 IR 操作码到 VM 指令名的静态映射;`OpHelper` 基于反射获取 VMOpCode 常量,并根据值类型(I/L/F…)选择合适的 PUSH/STORE 操作码。 + +## 模块结构 + +``` +backend/ + ├── core/ + │ └── InstructionGenerator.java // 通用指令生成器接口 + ├── generator/ + │ ├── BinaryOpGenerator.java // 二元运算 + │ ├── UnaryOpGenerator.java // 一元运算 + │ ├── LoadConstGenerator.java // 常量加载 + │ ├── CallGenerator.java // 函数调用 + │ ├── ReturnGenerator.java // 函数返回 + │ ├── CmpJumpGenerator.java // 条件跳转 + │ ├── JumpGenerator.java // 无条件跳转 + │ └── LabelGenerator.java // 标签定义 + ├── alloc/ + │ └── RegisterAllocator.java // 线性扫描寄存器分配 + ├── builder/ + │ ├── VMCodeGenerator.java // 指令生成调度 + │ └── VMProgramBuilder.java // VM 程序构建与 fix-up + └── util/ + ├── IROpCodeMapper.java // IR → VM 操作码映射 + └── OpHelper.java // opcode 反射 & 常量指令辅助 +``` + +## 开发环境 + +* JDK 23 或更高版本 +* Maven 构建管理 +* 推荐 IDE:IntelliJ IDEA + +--- diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/BinaryOpGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/BinaryOpGenerator.java new file mode 100644 index 0000000..de907fb --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/BinaryOpGenerator.java @@ -0,0 +1,133 @@ +package org.jcnc.snow.compiler.backend.generator; + +import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder; +import org.jcnc.snow.compiler.backend.core.InstructionGenerator; +import org.jcnc.snow.compiler.backend.util.OpHelper; +import org.jcnc.snow.compiler.ir.instruction.BinaryOperationInstruction; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.Map; + +/** + * 二元运算指令生成器 + * 支持二元运算指令的自动类型提升。 + *

类型提升优先级为:D > F > L > I > S > B

+ */ +public class BinaryOpGenerator implements InstructionGenerator { + + /* ---------- 类型优先级工具 ---------- */ + + /** + * 返回类型前缀的优先级数值。数值越大,类型“越宽”。 + * D: 6, F: 5, L: 4, I: 3, S: 2, B: 1 + */ + private static int rank(char p) { + return switch (p) { + case 'D' -> 6; + case 'F' -> 5; + case 'L' -> 4; + case 'I' -> 3; + case 'S' -> 2; + case 'B' -> 1; + default -> 0; + }; + } + + /** + * 返回a和b中优先级更高的类型前缀(即类型提升结果)。 + */ + private static char promote(char a, char b) { + return rank(a) >= rank(b) ? a : b; + } + + /** + * 类型前缀转字符串,方便拼接。 + */ + private static String str(char p) { + return String.valueOf(p); + } + + /* ---------- 类型转换指令工具 ---------- */ + + /** + * 根据源类型和目标类型前缀,返回相应的类型转换指令助记符。 + * + * @param from 源类型前缀 + * @param to 目标类型前缀 + * @return 转换指令字符串,如 "I2L" 或 "F2D",若无需转换则返回null + */ + private static String convert(char from, char to) { + if (from == to) return null; // 类型一致,无需转换 + return switch ("" + from + to) { + case "IL" -> "I2L"; + case "ID" -> "I2D"; + case "IF" -> "I2F"; + case "LI" -> "L2I"; + case "LD" -> "L2D"; + case "LF" -> "L2F"; + case "FI" -> "F2I"; + case "FL" -> "F2L"; + case "FD" -> "F2D"; + case "DI" -> "D2I"; + case "DL" -> "D2L"; + case "DF" -> "D2F"; + case "SI" -> "S2I"; + case "BI" -> "B2I"; + default -> null; // 其它组合暂未用到 + }; + } + + /** + * 返回本生成器支持的指令类型,即 BinaryOperationInstruction。 + */ + @Override + public Class supportedClass() { + return BinaryOperationInstruction.class; + } + + /** + * 生成二元运算的虚拟机指令,实现自动类型提升和必要的类型转换。 + * + * @param ins 当前二元运算IR指令 + * @param out 虚拟机程序构建器 + * @param slotMap IR虚拟寄存器到实际槽位编号的映射 + * @param currentFn 当前函数名 + */ + @Override + public void generate(BinaryOperationInstruction ins, + VMProgramBuilder out, + Map slotMap, + String currentFn) { + + /* ------- 1. 获取左右操作数的槽位编号和类型 ------- */ + int lSlot = slotMap.get((IRVirtualRegister) ins.operands().get(0)); // 左操作数槽位 + int rSlot = slotMap.get((IRVirtualRegister) ins.operands().get(1)); // 右操作数槽位 + int dSlot = slotMap.get(ins.dest()); // 目标槽位 + char lType = out.getSlotType(lSlot); // 左操作数类型前缀 + char rType = out.getSlotType(rSlot); // 右操作数类型前缀 + + // 类型提升,确定本次二元运算的目标类型(优先级较高的那一个) + char tType = promote(lType, rType); + String tPref = str(tType); // 用于拼接指令字符串 + + /* ------- 2. 加载左操作数,并自动进行类型转换(如有必要) ------- */ + out.emit(OpHelper.opcode(str(lType) + "_LOAD") + " " + lSlot); // LOAD指令 + String cvt = convert(lType, tType); // 如需类型提升 + if (cvt != null) out.emit(OpHelper.opcode(cvt)); // 插入类型转换指令 + + /* ------- 3. 加载右操作数,并自动进行类型转换(如有必要) ------- */ + out.emit(OpHelper.opcode(str(rType) + "_LOAD") + " " + rSlot); // LOAD指令 + cvt = convert(rType, tType); // 如需类型提升 + if (cvt != null) out.emit(OpHelper.opcode(cvt)); // 插入类型转换指令 + + /* ------- 4. 生成具体的二元运算指令 ------- */ + // 获取IR指令中的操作名(如ADD、SUB、MUL等,去掉结尾的"_"后缀) + String opName = ins.op().name().split("_")[0]; + // 例如生成 "I_ADD", "D_MUL" 等虚拟机指令 + out.emit(OpHelper.opcode(tPref + "_" + opName)); + + /* ------- 5. 结果存入目标槽位,并更新槽位类型 ------- */ + out.emit(OpHelper.opcode(tPref + "_STORE") + " " + dSlot); + out.setSlotType(dSlot, tType); // 记录运算结果的类型前缀,便于后续指令正确处理 + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java new file mode 100644 index 0000000..ff6961b --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java @@ -0,0 +1,76 @@ +package org.jcnc.snow.compiler.backend.generator; + +import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder; +import org.jcnc.snow.compiler.backend.core.InstructionGenerator; +import org.jcnc.snow.compiler.backend.util.OpHelper; +import org.jcnc.snow.compiler.ir.instruction.CallInstruction; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.Map; + +/** + * 函数调用指令生成器。 + *

+ * 该类实现了函数调用(CallInstruction)的指令翻译逻辑, + * 负责将 IR 层的函数调用转换为虚拟机可执行的低级指令。 + */ +public class CallGenerator implements InstructionGenerator { + + /** + * 返回本指令生成器支持的 IR 指令类型(CallInstruction)。 + * + * @return 指令类型的 Class 对象 + */ + @Override + public Class supportedClass() { + return CallInstruction.class; + } + + /** + * 生成函数调用相关的虚拟机指令。 + *

+ * 步骤如下: + *

    + *
  1. 预测返回值类型(采用首个实参槽的类型作为近似)
  2. + *
  3. 为每个参数根据实际类型发出加载指令
  4. + *
  5. 生成 CALL 调用指令
  6. + *
  7. 将返回值存储到目标槽,并记录类型信息
  8. + *
+ * + * @param ins 待翻译的 CallInstruction 指令对象 + * @param out 指令输出与类型槽管理器 + * @param slotMap IR 寄存器到槽号的映射 + * @param currentFn 当前函数名(未用,可用于递归/闭包等复杂场景) + */ + @Override + public void generate(CallInstruction ins, + VMProgramBuilder out, + Map slotMap, + String currentFn) { + + // —— 1. 预测返回值类型(用首个实参槽类型作为近似推断) —— + 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'; // 类型未知时默认整型 + // 生成类型相关的加载指令,如 I_LOAD、F_LOAD 等 + out.emit(OpHelper.opcode(String.valueOf(t) + "_LOAD") + " " + slotId); + } + + // —— 3. 生成 CALL 调用指令 —— + out.emitCall(ins.getFunctionName(), ins.getArguments().size()); + + // —— 4. 将返回值存入目标槽,并记录槽的类型 —— + int destSlot = slotMap.get(ins.getDest()); // 目标寄存器槽 + out.emit(OpHelper.opcode(String.valueOf(retType) + "_STORE") + " " + destSlot); + out.setSlotType(destSlot, retType); // 标记返回值类型 + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/CmpJumpGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/CmpJumpGenerator.java new file mode 100644 index 0000000..5f15051 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/CmpJumpGenerator.java @@ -0,0 +1,57 @@ +package org.jcnc.snow.compiler.backend.generator; + +import org.jcnc.snow.compiler.backend.util.IROpCodeMapper; +import org.jcnc.snow.compiler.backend.util.OpHelper; +import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder; +import org.jcnc.snow.compiler.backend.core.InstructionGenerator; +import org.jcnc.snow.compiler.ir.instruction.IRCompareJumpInstruction; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.Map; + +/** + * 条件比较跳转指令生成器 + *

+ * 该类实现了 {@link InstructionGenerator} 接口,用于将 IR 中的条件比较跳转指令 + * 转换为虚拟机可执行的指令序列。主要流程是先将比较操作数加载到虚拟机栈中,生成比较操作码, + * 并发出跳转到目标标签的指令。 + */ +public class CmpJumpGenerator implements InstructionGenerator { + + /** + * 返回该生成器所支持的指令类型。 + * + * @return {@link IRCompareJumpInstruction} 的类对象 + */ + @Override + public Class supportedClass() { + return IRCompareJumpInstruction.class; + } + + /** + * 生成条件比较跳转相关的虚拟机指令。 + * + * @param ins 需要生成的条件比较跳转中间指令(IR) + * @param out 虚拟机程序构建器,用于输出生成的指令 + * @param slotMap 虚拟寄存器到实际槽(slot)编号的映射表 + * @param currentFn 当前处理的函数名(可用于调试或作用域标识) + */ + @Override + public void generate(IRCompareJumpInstruction ins, + VMProgramBuilder out, + Map slotMap, + String currentFn) { + // 获取左操作数所在的寄存器槽编号 + int leftSlot = slotMap.get(ins.left()); + // 获取右操作数所在的寄存器槽编号 + int rightSlot = slotMap.get(ins.right()); + // 加载左操作数到虚拟机栈 + out.emit(OpHelper.opcode("I_LOAD") + " " + leftSlot); + // 加载右操作数到虚拟机栈 + out.emit(OpHelper.opcode("I_LOAD") + " " + rightSlot); + // 获取与当前比较操作对应的虚拟机操作码 + String cmpOp = IROpCodeMapper.toVMOp(ins.op()); + // 生成分支跳转指令,如果比较成立则跳转到目标标签 + out.emitBranch(OpHelper.opcode(cmpOp), ins.label()); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/JumpGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/JumpGenerator.java new file mode 100644 index 0000000..85d2907 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/JumpGenerator.java @@ -0,0 +1,46 @@ +package org.jcnc.snow.compiler.backend.generator; + +import org.jcnc.snow.compiler.backend.util.OpHelper; +import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder; +import org.jcnc.snow.compiler.backend.core.InstructionGenerator; +import org.jcnc.snow.compiler.ir.instruction.IRJumpInstruction; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.Map; + +/** + * 无条件跳转指令生成器 + *

+ * 该类实现了 {@link InstructionGenerator} 接口,用于将 IR 中的无条件跳转指令 + * (即跳转到指定标签)翻译为虚拟机指令。 + *

+ */ +public class JumpGenerator implements InstructionGenerator { + + /** + * 返回本生成器所支持的 IR 指令类型。 + * + * @return {@link IRJumpInstruction} 的类对象 + */ + @Override + public Class supportedClass() { + return IRJumpInstruction.class; + } + + /** + * 生成对应的虚拟机跳转指令。 + * + * @param ins 当前 IR 跳转指令 + * @param out 虚拟机程序构建器,用于输出指令 + * @param slotMap 虚拟寄存器与槽号的映射表(本跳转指令未用到) + * @param currentFn 当前函数名称(便于调试或作用域标识) + */ + @Override + public void generate(IRJumpInstruction ins, + VMProgramBuilder out, + Map slotMap, + String currentFn) { + // 生成无条件跳转到指定标签的虚拟机指令 + out.emitBranch(OpHelper.opcode("JUMP"), ins.label()); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/LabelGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/LabelGenerator.java new file mode 100644 index 0000000..55844ce --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/LabelGenerator.java @@ -0,0 +1,45 @@ +package org.jcnc.snow.compiler.backend.generator; + +import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder; +import org.jcnc.snow.compiler.backend.core.InstructionGenerator; +import org.jcnc.snow.compiler.ir.instruction.IRLabelInstruction; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.Map; + +/** + * 标签指令生成器 + *

+ * 本类实现了 {@link InstructionGenerator} 接口,用于将 IR 层的标签指令翻译为 + * 虚拟机(VM)中的标签标记。标签一般用于跳转或分支目的地的定位。 + *

+ */ +public class LabelGenerator implements InstructionGenerator { + + /** + * 返回本生成器所支持的 IR 指令类型。 + * + * @return {@link IRLabelInstruction} 的类对象 + */ + @Override + public Class supportedClass() { + return IRLabelInstruction.class; + } + + /** + * 生成对应的虚拟机标签指令。 + * + * @param ins 当前 IR 标签指令 + * @param out 虚拟机程序构建器,用于输出指令 + * @param slotMap 虚拟寄存器与槽号的映射表(标签指令未使用此参数) + * @param currentFn 当前函数名称(用于调试或作用域标识,可选) + */ + @Override + public void generate(IRLabelInstruction ins, + VMProgramBuilder out, + Map slotMap, + String currentFn) { + // 生成标签(如 "label_name:"),用于虚拟机指令流中的位置标记 + out.emit(ins.name() + ":"); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java new file mode 100644 index 0000000..8c8ee32 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java @@ -0,0 +1,74 @@ +package org.jcnc.snow.compiler.backend.generator; + +import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder; +import org.jcnc.snow.compiler.backend.core.InstructionGenerator; +import org.jcnc.snow.compiler.backend.util.OpHelper; +import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction; +import org.jcnc.snow.compiler.ir.value.IRConstant; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.Map; + +/** + * 常量加载指令生成器 + * 该类用于生成将常量加载到虚拟机寄存器的指令,包括 PUSH 常量值和 STORE 到指定槽位, + * 并为每个槽位设置正确的类型前缀(如 'I', 'L', 'F' 等)。 + */ +public class LoadConstGenerator implements InstructionGenerator { + + /** + * 返回本生成器支持的指令类型,即 LoadConstInstruction。 + * + * @return 支持的指令类型的 Class 对象 + */ + @Override + public Class supportedClass() { + return LoadConstInstruction.class; + } + + /** + * 生成一条常量加载指令的目标虚拟机代码。 + * + * @param ins 当前要生成的 LoadConstInstruction 指令 + * @param out VMProgramBuilder,用于输出生成的虚拟机指令 + * @param slotMap IR 虚拟寄存器到实际槽位编号的映射表 + * @param currentFn 当前函数名(如有需要可使用) + */ + @Override + public void generate(LoadConstInstruction ins, + VMProgramBuilder out, + Map slotMap, + String currentFn) { + // 1. 获取常量值(第一个操作数必为常量) + IRConstant constant = (IRConstant) ins.operands().getFirst(); + Object value = constant.value(); + + // 2. 生成 PUSH 指令,将常量值推入操作数栈 + // 通过 OpHelper 辅助方法获取合适的数据类型前缀 + String pushOp = OpHelper.pushOpcodeFor(value); + out.emit(pushOp + " " + value); + + // 3. 生成 STORE 指令,将栈顶的值存入对应槽位(寄存器) + // 同样通过 OpHelper 获取对应类型的 STORE 指令 + String storeOp = OpHelper.storeOpcodeFor(value); + // 获取目标虚拟寄存器对应的槽位编号 + int slot = slotMap.get(ins.dest()); + out.emit(storeOp + " " + slot); + + // 4. 根据常量的 Java 类型,为槽位设置正确的前缀字符 + // 这样在后续类型检查/运行时可用,常见前缀如 'I', 'L', 'F', 'D', 'S', 'B' + char prefix = switch (value) { + case Integer _ -> 'I'; // 整型 + case Long _ -> 'L'; // 长整型 + case Short _ -> 'S'; // 短整型 + case Byte _ -> 'B'; // 字节型 + case Double _ -> 'D'; // 双精度浮点型 + case Float _ -> 'F'; // 单精度浮点型 + case null, default -> + throw new IllegalStateException("Unknown const type: " + (value != null ? value.getClass() : null)); + }; + + // 写入槽位类型映射表 + out.setSlotType(slot, prefix); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/ReturnGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/ReturnGenerator.java new file mode 100644 index 0000000..a58af50 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/ReturnGenerator.java @@ -0,0 +1,55 @@ +package org.jcnc.snow.compiler.backend.generator; + +import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder; +import org.jcnc.snow.compiler.backend.core.InstructionGenerator; +import org.jcnc.snow.compiler.backend.util.OpHelper; +import org.jcnc.snow.compiler.ir.instruction.ReturnInstruction; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.Map; + +/** + * 返回指令生成器 + *

+ * 本类实现了 {@link InstructionGenerator} 接口,用于将 IR 中的函数返回指令翻译为 + * 虚拟机可执行的返回(RET/HALT)相关指令。支持有返回值和无返回值两种情况。 + *

+ */ +public class ReturnGenerator implements InstructionGenerator { + + /** + * 返回该生成器支持的 IR 指令类型。 + * + * @return {@link ReturnInstruction} 的类对象 + */ + @Override + public Class supportedClass() { + return ReturnInstruction.class; + } + + /** + * 生成对应的虚拟机返回指令。 + * + * @param ins 当前 IR 返回指令 + * @param out 虚拟机程序构建器,用于输出 VM 指令 + * @param slotMap 虚拟寄存器到槽号的映射表 + * @param currentFn 当前函数名称(用于判断是否为主函数 main) + */ + @Override + public void generate(ReturnInstruction ins, + VMProgramBuilder out, + Map slotMap, + String currentFn) { + // 若存在返回值,先将返回值加载到虚拟机栈顶 + if (ins.value() != null) { + int slotId = slotMap.get(ins.value()); + // 根据之前记录的槽类型前缀(I/L/S/B/D/F)选 LOAD + char prefix = out.getSlotType(slotId); + String loadOp = prefix + "_LOAD"; + out.emit(OpHelper.opcode(loadOp) + " " + slotId); + } + // 主函数返回使用 HALT,普通函数返回使用 RET + String code = "main".equals(currentFn) ? "HALT" : "RET"; + out.emit(OpHelper.opcode(code)); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/UnaryOpGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/UnaryOpGenerator.java new file mode 100644 index 0000000..f1c5db2 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/UnaryOpGenerator.java @@ -0,0 +1,53 @@ +package org.jcnc.snow.compiler.backend.generator; + +import org.jcnc.snow.compiler.backend.util.IROpCodeMapper; +import org.jcnc.snow.compiler.backend.util.OpHelper; +import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder; +import org.jcnc.snow.compiler.backend.core.InstructionGenerator; +import org.jcnc.snow.compiler.ir.instruction.UnaryOperationInstruction; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.Map; + +/** + * 一元运算指令生成器 + *

+ * 本类实现了 {@link InstructionGenerator} 接口,用于将 IR 中的一元运算指令 + * (如取负等)翻译为虚拟机(VM)可执行的指令序列。 + *

+ */ +public class UnaryOpGenerator implements InstructionGenerator { + + /** + * 返回本生成器所支持的 IR 指令类型。 + * + * @return {@link UnaryOperationInstruction} 的类对象 + */ + @Override + public Class supportedClass() { + return UnaryOperationInstruction.class; + } + + /** + * 生成一元运算相关的虚拟机指令。 + * + * @param ins 当前 IR 一元运算指令 + * @param out 虚拟机程序构建器,用于输出指令 + * @param slotMap 虚拟寄存器与槽号的映射表 + * @param currentFn 当前函数名称(用于作用域或调试,可选) + */ + @Override + public void generate(UnaryOperationInstruction ins, + VMProgramBuilder out, + Map slotMap, + String currentFn) { + // 获取操作数所在槽号 + int slotId = slotMap.get((IRVirtualRegister) ins.operands().getFirst()); + // 加载操作数到虚拟机栈顶 + out.emit(OpHelper.opcode("I_LOAD") + " " + slotId); + // 生成对应的一元运算操作码(如取负等) + out.emit(OpHelper.opcode(IROpCodeMapper.toVMOp(ins.op()))); + // 将结果存储到目标寄存器槽 + out.emit(OpHelper.opcode("I_STORE") + " " + slotMap.get(ins.dest())); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/backend/util/IROpCodeMapper.java b/src/main/java/org/jcnc/snow/compiler/backend/util/IROpCodeMapper.java new file mode 100644 index 0000000..4ff674d --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/backend/util/IROpCodeMapper.java @@ -0,0 +1,127 @@ +package org.jcnc.snow.compiler.backend.util; + +import org.jcnc.snow.compiler.ir.core.IROpCode; + +import java.util.EnumMap; +import java.util.Map; + +/** + * IR 操作码与虚拟机指令名映射工具类。 + *

+ * 本类用于将 IR 层的操作码({@link IROpCode})映射为目标虚拟机的指令名({@code String})。 + * 该工具类在编译器后端阶段提供指令名转换功能,不处理参数或操作数,仅负责纯映射。 + *

+ *

+ * 若需扩展新的操作码或 VM 指令,应在本类中统一维护映射关系。 + *

+ */ +public final class IROpCodeMapper { + + /** + * IR 操作码到 VM 指令名的静态映射表。 + *
    + *
  • 键:IR 操作码({@link IROpCode} 枚举项)
  • + *
  • 值:对应虚拟机指令名(字符串)
  • + *
+ * 使用 {@link EnumMap},查找和存储高效。 + */ + private static final Map opcodeMap = new EnumMap<>(IROpCode.class); + + // 静态代码块,初始化所有 IR 操作码到 VM 指令名的映射关系 + static { + opcodeMap.put(IROpCode.CONV_I32_TO_F32, "I2F"); + opcodeMap.put(IROpCode.CONV_I32_TO_D64, "I2D"); + + opcodeMap.put(IROpCode.CONV_F32_TO_I32, "F2I"); + opcodeMap.put(IROpCode.CONV_D64_TO_I32, "D2I"); + + opcodeMap.put(IROpCode.CONV_F32_TO_D64, "F2D"); + opcodeMap.put(IROpCode.CONV_D64_TO_F32, "D2F"); + + // 整形8位算术运算映射 + opcodeMap.put(IROpCode.ADD_B8, "B_ADD"); + opcodeMap.put(IROpCode.SUB_B8, "B_SUB"); + opcodeMap.put(IROpCode.MUL_B8, "B_MUL"); + opcodeMap.put(IROpCode.DIV_B8, "B_DIV"); + opcodeMap.put(IROpCode.NEG_B8, "B_NEG"); + + // 整形16位算术运算映射 + opcodeMap.put(IROpCode.ADD_S16, "S_ADD"); + opcodeMap.put(IROpCode.SUB_S16, "S_SUB"); + opcodeMap.put(IROpCode.MUL_S16, "S_MUL"); + opcodeMap.put(IROpCode.DIV_S16, "S_DIV"); + opcodeMap.put(IROpCode.NEG_S16, "S_NEG"); + + // 整形32位算术运算映射 + opcodeMap.put(IROpCode.ADD_I32, "I_ADD"); + opcodeMap.put(IROpCode.SUB_I32, "I_SUB"); + opcodeMap.put(IROpCode.MUL_I32, "I_MUL"); + opcodeMap.put(IROpCode.DIV_I32, "I_DIV"); + opcodeMap.put(IROpCode.NEG_I32, "I_NEG"); + + // 整形64位算术运算映射 + opcodeMap.put(IROpCode.ADD_L64, "L_ADD"); + opcodeMap.put(IROpCode.SUB_L64, "L_SUB"); + opcodeMap.put(IROpCode.MUL_L64, "L_MUL"); + opcodeMap.put(IROpCode.DIV_L64, "L_DIV"); + opcodeMap.put(IROpCode.NEG_L64, "L_NEG"); + + // --- 32-bit floating point --- + + opcodeMap.put(IROpCode.ADD_F32, "F_ADD"); + opcodeMap.put(IROpCode.SUB_F32, "F_SUB"); + opcodeMap.put(IROpCode.MUL_F32, "F_MUL"); + opcodeMap.put(IROpCode.DIV_F32, "F_DIV"); + opcodeMap.put(IROpCode.NEG_F32, "F_NEG"); + + // --- 64-bit floating point --- + opcodeMap.put(IROpCode.ADD_D64, "D_ADD"); + opcodeMap.put(IROpCode.SUB_D64, "D_SUB"); + opcodeMap.put(IROpCode.MUL_D64, "D_MUL"); + opcodeMap.put(IROpCode.DIV_D64, "D_DIV"); + opcodeMap.put(IROpCode.NEG_D64, "D_NEG"); + + // 比较运算映射 + opcodeMap.put(IROpCode.CMP_EQ, "IC_EQ"); // 相等 + opcodeMap.put(IROpCode.CMP_NE, "IC_NE"); // 不等 + opcodeMap.put(IROpCode.CMP_LT, "IC_L"); // 小于 + opcodeMap.put(IROpCode.CMP_GT, "IC_G"); // 大于 + opcodeMap.put(IROpCode.CMP_LE, "IC_LE"); // 小于等于 + opcodeMap.put(IROpCode.CMP_GE, "IC_GE"); // 大于等于 + + // 加载与存储 + opcodeMap.put(IROpCode.LOAD, "I_LOAD"); // 加载 + opcodeMap.put(IROpCode.STORE, "I_STORE"); // 存储 + opcodeMap.put(IROpCode.CONST, "I_PUSH"); // 常量入栈 + + // 跳转与标签 + opcodeMap.put(IROpCode.JUMP, "JMP"); // 无条件跳转 + opcodeMap.put(IROpCode.LABEL, "LABEL"); // 标签 + + // 函数相关 + opcodeMap.put(IROpCode.CALL, "CALL"); // 调用 + opcodeMap.put(IROpCode.RET, "RET"); // 返回 + } + + /** + * 工具类私有构造,禁止实例化。 + */ + private IROpCodeMapper() { + // 禁止实例化 + } + + /** + * 根据指定 IR 操作码获取对应的虚拟机指令名。 + * + * @param irOp 需转换的 IR 操作码({@link IROpCode} 枚举值) + * @return 对应的虚拟机指令名(字符串) + * @throws IllegalArgumentException 若 {@code irOp} 未定义映射关系,抛出异常 + */ + public static String toVMOp(IROpCode irOp) { + String vmCode = opcodeMap.get(irOp); + if (vmCode == null) { + throw new IllegalArgumentException("未映射的 IR 操作码: " + irOp); + } + return vmCode; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/backend/util/OpHelper.java b/src/main/java/org/jcnc/snow/compiler/backend/util/OpHelper.java new file mode 100644 index 0000000..bc632cf --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/backend/util/OpHelper.java @@ -0,0 +1,168 @@ +package org.jcnc.snow.compiler.backend.util; + +import org.jcnc.snow.vm.engine.VMOpCode; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Opcode 帮助类 + *

+ * 通过 静态不可变 Map 保存指令名到 opcode(以字符串表示)的映射表。 + *

+ * 本文件由脚本根据 {@link VMOpCode} 中实际存在的 public static 字段自动生成, + * 保证与指令集保持同步,避免手写出错。 + *

+ */ +public final class OpHelper { + + /** + * 指令名 → opcode 字符串 的静态映射表 + */ + private static final Map OPCODE_MAP; + + static { + Map map = new HashMap<>(); + map.put("I_ADD", Integer.toString(VMOpCode.I_ADD)); + map.put("I_SUB", Integer.toString(VMOpCode.I_SUB)); + map.put("I_MUL", Integer.toString(VMOpCode.I_MUL)); + map.put("I_DIV", Integer.toString(VMOpCode.I_DIV)); + map.put("I_MOD", Integer.toString(VMOpCode.I_MOD)); + map.put("I_INC", Integer.toString(VMOpCode.I_INC)); + map.put("I_NEG", Integer.toString(VMOpCode.I_NEG)); + map.put("L_ADD", Integer.toString(VMOpCode.L_ADD)); + map.put("L_SUB", Integer.toString(VMOpCode.L_SUB)); + map.put("L_MUL", Integer.toString(VMOpCode.L_MUL)); + map.put("L_DIV", Integer.toString(VMOpCode.L_DIV)); + map.put("L_MOD", Integer.toString(VMOpCode.L_MOD)); + map.put("L_INC", Integer.toString(VMOpCode.L_INC)); + map.put("L_NEG", Integer.toString(VMOpCode.L_NEG)); + map.put("S_ADD", Integer.toString(VMOpCode.S_ADD)); + map.put("S_SUB", Integer.toString(VMOpCode.S_SUB)); + map.put("S_MUL", Integer.toString(VMOpCode.S_MUL)); + map.put("S_DIV", Integer.toString(VMOpCode.S_DIV)); + map.put("S_MOD", Integer.toString(VMOpCode.S_MOD)); + map.put("S_INC", Integer.toString(VMOpCode.S_INC)); + map.put("S_NEG", Integer.toString(VMOpCode.S_NEG)); + map.put("B_ADD", Integer.toString(VMOpCode.B_ADD)); + map.put("B_SUB", Integer.toString(VMOpCode.B_SUB)); + map.put("B_MUL", Integer.toString(VMOpCode.B_MUL)); + map.put("B_DIV", Integer.toString(VMOpCode.B_DIV)); + map.put("B_MOD", Integer.toString(VMOpCode.B_MOD)); + map.put("B_INC", Integer.toString(VMOpCode.B_INC)); + map.put("B_NEG", Integer.toString(VMOpCode.B_NEG)); + map.put("D_ADD", Integer.toString(VMOpCode.D_ADD)); + map.put("D_SUB", Integer.toString(VMOpCode.D_SUB)); + map.put("D_MUL", Integer.toString(VMOpCode.D_MUL)); + map.put("D_DIV", Integer.toString(VMOpCode.D_DIV)); + map.put("D_MOD", Integer.toString(VMOpCode.D_MOD)); + map.put("D_NEG", Integer.toString(VMOpCode.D_NEG)); + map.put("F_ADD", Integer.toString(VMOpCode.F_ADD)); + map.put("F_SUB", Integer.toString(VMOpCode.F_SUB)); + map.put("F_MUL", Integer.toString(VMOpCode.F_MUL)); + map.put("F_DIV", Integer.toString(VMOpCode.F_DIV)); + map.put("F_MOD", Integer.toString(VMOpCode.F_MOD)); + map.put("F_NEG", Integer.toString(VMOpCode.F_NEG)); + map.put("D_INC", Integer.toString(VMOpCode.D_INC)); + map.put("F_INC", Integer.toString(VMOpCode.F_INC)); + map.put("I2L", Integer.toString(VMOpCode.I2L)); + map.put("I2S", Integer.toString(VMOpCode.I2S)); + map.put("I2B", Integer.toString(VMOpCode.I2B)); + map.put("I2D", Integer.toString(VMOpCode.I2D)); + map.put("I2F", Integer.toString(VMOpCode.I2F)); + map.put("L2I", Integer.toString(VMOpCode.L2I)); + map.put("L2D", Integer.toString(VMOpCode.L2D)); + map.put("L2F", Integer.toString(VMOpCode.L2F)); + map.put("F2I", Integer.toString(VMOpCode.F2I)); + map.put("F2L", Integer.toString(VMOpCode.F2L)); + map.put("F2D", Integer.toString(VMOpCode.F2D)); + map.put("D2I", Integer.toString(VMOpCode.D2I)); + map.put("D2L", Integer.toString(VMOpCode.D2L)); + map.put("D2F", Integer.toString(VMOpCode.D2F)); + map.put("S2I", Integer.toString(VMOpCode.S2I)); + map.put("B2I", Integer.toString(VMOpCode.B2I)); + map.put("I_AND", Integer.toString(VMOpCode.I_AND)); + map.put("I_OR", Integer.toString(VMOpCode.I_OR)); + map.put("I_XOR", Integer.toString(VMOpCode.I_XOR)); + map.put("L_AND", Integer.toString(VMOpCode.L_AND)); + map.put("L_OR", Integer.toString(VMOpCode.L_OR)); + map.put("L_XOR", Integer.toString(VMOpCode.L_XOR)); + map.put("JUMP", Integer.toString(VMOpCode.JUMP)); + map.put("IC_E", Integer.toString(VMOpCode.IC_E)); + map.put("IC_NE", Integer.toString(VMOpCode.IC_NE)); + map.put("IC_G", Integer.toString(VMOpCode.IC_G)); + map.put("IC_GE", Integer.toString(VMOpCode.IC_GE)); + map.put("IC_L", Integer.toString(VMOpCode.IC_L)); + map.put("IC_LE", Integer.toString(VMOpCode.IC_LE)); + map.put("I_PUSH", Integer.toString(VMOpCode.I_PUSH)); + map.put("L_PUSH", Integer.toString(VMOpCode.L_PUSH)); + map.put("S_PUSH", Integer.toString(VMOpCode.S_PUSH)); + map.put("B_PUSH", Integer.toString(VMOpCode.B_PUSH)); + map.put("D_PUSH", Integer.toString(VMOpCode.D_PUSH)); + map.put("F_PUSH", Integer.toString(VMOpCode.F_PUSH)); + map.put("POP", Integer.toString(VMOpCode.POP)); + map.put("DUP", Integer.toString(VMOpCode.DUP)); + map.put("SWAP", Integer.toString(VMOpCode.SWAP)); + map.put("I_STORE", Integer.toString(VMOpCode.I_STORE)); + map.put("L_STORE", Integer.toString(VMOpCode.L_STORE)); + map.put("S_STORE", Integer.toString(VMOpCode.S_STORE)); + map.put("B_STORE", Integer.toString(VMOpCode.B_STORE)); + map.put("D_STORE", Integer.toString(VMOpCode.D_STORE)); + map.put("F_STORE", Integer.toString(VMOpCode.F_STORE)); + map.put("I_LOAD", Integer.toString(VMOpCode.I_LOAD)); + map.put("L_LOAD", Integer.toString(VMOpCode.L_LOAD)); + map.put("S_LOAD", Integer.toString(VMOpCode.S_LOAD)); + map.put("B_LOAD", Integer.toString(VMOpCode.B_LOAD)); + map.put("D_LOAD", Integer.toString(VMOpCode.D_LOAD)); + map.put("F_LOAD", Integer.toString(VMOpCode.F_LOAD)); + map.put("MOV", Integer.toString(VMOpCode.MOV)); + map.put("CALL", Integer.toString(VMOpCode.CALL)); + map.put("RET", Integer.toString(VMOpCode.RET)); + map.put("HALT", Integer.toString(VMOpCode.HALT)); + OPCODE_MAP = Collections.unmodifiableMap(map); + } + + /** + * 私有构造器,禁止实例化 + */ + private OpHelper() { + } + + /** + * 根据指令名获取 opcode 字符串。 + * + * @param name 指令名(如 "I_LOAD"、"I_PUSH") + * @return opcode 字符串 + * @throws IllegalStateException 若名称未知 + */ + public static String opcode(String name) { + String code = OPCODE_MAP.get(name); + if (code == null) { + throw new IllegalStateException("Unknown opcode: " + name); + } + return code; + } + + // region 辅助方法 – 根据常量类型推导 PUSH / STORE 指令名 + + public static String pushOpcodeFor(Object v) { + return opcode(typePrefix(v) + "_PUSH"); + } + + public static String storeOpcodeFor(Object v) { + return opcode(typePrefix(v) + "_STORE"); + } + + private static String typePrefix(Object v) { + if (v instanceof Integer) return "I"; + if (v instanceof Long) return "L"; + if (v instanceof Short) return "S"; + if (v instanceof Byte) return "B"; + if (v instanceof Double) return "D"; + if (v instanceof Float) return "F"; + throw new IllegalStateException("Unknown const type: " + v.getClass()); + } + + // endregion +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/cli/SnowCompiler.java b/src/main/java/org/jcnc/snow/compiler/cli/SnowCompiler.java new file mode 100644 index 0000000..6c2b335 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/cli/SnowCompiler.java @@ -0,0 +1,149 @@ +package org.jcnc.snow.compiler.cli; + +import org.jcnc.snow.compiler.backend.alloc.RegisterAllocator; +import org.jcnc.snow.compiler.backend.builder.VMCodeGenerator; +import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder; +import org.jcnc.snow.compiler.backend.core.InstructionGenerator; +import org.jcnc.snow.compiler.backend.generator.*; +import org.jcnc.snow.compiler.ir.builder.IRProgramBuilder; +import org.jcnc.snow.compiler.ir.core.IRFunction; +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.core.IRProgram; +import org.jcnc.snow.compiler.lexer.core.LexerEngine; +import org.jcnc.snow.compiler.parser.ast.base.Node; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.core.ParserEngine; +import org.jcnc.snow.compiler.parser.function.ASTPrinter; +import org.jcnc.snow.compiler.semantic.core.SemanticAnalyzerRunner; +import org.jcnc.snow.vm.engine.VMMode; +import org.jcnc.snow.vm.engine.VirtualMachineEngine; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.*; +import java.util.*; + +/** + * SnowCompiler CLI —— 多文件 / 单文件 / 目录 模式。 + */ +public class SnowCompiler { + + public static void main(String[] args) throws IOException { + if (args.length == 0) { + System.err.println(""" + Usage: + snow [file2.snow …] + snow -d (compile all *.snow recursively) + """); + return; + } + + /* ---------- 1. 收集所有待编译源码 ---------- */ + List sources = collectSources(args); + if (sources.isEmpty()) { + System.err.println("No .snow source files found."); + return; + } + + /* ---------- 2. 逐个词法+语法分析,合并 AST ---------- */ + List allAst = new ArrayList<>(); + for (Path p : sources) { + if (!Files.exists(p)) { + System.err.println("File not found: " + p); + return; + } + String code = Files.readString(p, StandardCharsets.UTF_8); + + // 保持原有“## 源代码”打印,但标注文件名,兼容旧脚本 + System.out.println("## 源代码 (" + p.getFileName() + ")"); + System.out.println(code); + + LexerEngine lexer = new LexerEngine(code, p.toString()); + ParserContext ctx = new ParserContext(lexer.getAllTokens()); + allAst.addAll(new ParserEngine(ctx).parse()); + } + + /* ---------- 3. 语义分析 ---------- */ + SemanticAnalyzerRunner.runSemanticAnalysis(allAst, false); + + /* ---------- 4. AST → IR ---------- */ + IRProgram program = new IRProgramBuilder().buildProgram(allAst); + program = reorderForEntry(program); // 保证入口 main 在首位 + + System.out.println("## 编译器输出"); + System.out.println("### AST"); + ASTPrinter.printJson(allAst); + System.out.println("### IR"); + System.out.println(program); + + /* ---------- 5. IR → VM 指令 ---------- */ + VMProgramBuilder builder = new VMProgramBuilder(); + List> generators = Arrays.asList( + new LoadConstGenerator(), + new BinaryOpGenerator(), + new UnaryOpGenerator(), + new CallGenerator(), + new ReturnGenerator(), + new LabelGenerator(), + new JumpGenerator(), + new CmpJumpGenerator() + ); + + for (IRFunction fn : program.functions()) { + Map slotMap = + new RegisterAllocator().allocate(fn); + new VMCodeGenerator(slotMap, builder, generators).generate(fn); + } + List finalCode = builder.build(); + + System.out.println("### VM code"); + finalCode.forEach(System.out::println); + + /* ---------- 6. 运行虚拟机 ---------- */ + VirtualMachineEngine vm = new VirtualMachineEngine(VMMode.RUN); + vm.execute(finalCode); + vm.printLocalVariables(); + } + + + /** + * 根据参数收集待编译文件: + * - snow file1 file2 … ← 多文件 / 单文件 + * - snow -d srcDir ← 目录递归所有 *.snow + */ + private static List collectSources(String[] args) throws IOException { + if (args.length == 2 && "-d".equals(args[0])) { + Path dir = Path.of(args[1]); + if (!Files.isDirectory(dir)) { + System.err.println("Not a directory: " + dir); + return List.of(); + } + try (var stream = Files.walk(dir)) { + return stream.filter(p -> p.toString().endsWith(".snow")) + .sorted() // 稳定顺序,方便比对输出 + .toList(); + } + } + // 普通文件参数 + return Arrays.stream(args).map(Path::of).toList(); + } + + /** + * 把 main 函数放到 Program.functions()[0],保证 PC=0 即入口; + * 如果用户未写 main,则保持原顺序(语义分析会报错)。 + */ + private static IRProgram reorderForEntry(IRProgram in) { + List ordered = new ArrayList<>(in.functions()); + int idx = -1; + for (int i = 0; i < ordered.size(); i++) { + if ("main".equals(ordered.get(i).name())) { + idx = i; break; + } + } + if (idx > 0) Collections.swap(ordered, 0, idx); + + IRProgram out = new IRProgram(); + ordered.forEach(out::add); + return out; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java new file mode 100644 index 0000000..a68120f --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java @@ -0,0 +1,182 @@ +package org.jcnc.snow.compiler.ir.builder; + +import org.jcnc.snow.compiler.ir.core.IROpCode; +import org.jcnc.snow.compiler.ir.instruction.CallInstruction; +import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction; +import org.jcnc.snow.compiler.ir.value.IRConstant; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; +import org.jcnc.snow.compiler.ir.utils.ExpressionUtils; +import org.jcnc.snow.compiler.parser.ast.*; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; + +import java.util.*; + +/** + * 表达式构建器 + *

+ * 该类负责将抽象语法树(AST)的表达式节点转换为中间表示(IR)指令和虚拟寄存器, + * 是编译器IR生成阶段的核心工具。 + *
+ * 主要职责包括: + *

    + *
  • 将数字字面量、标识符、二元表达式、函数调用等AST表达式节点,翻译为对应的IR指令序列
  • + *
  • 管理并分配虚拟寄存器,保证IR操作的数据流正确
  • + *
+ *

+ */ +public record ExpressionBuilder(IRContext ctx) { + + /** + * 构建并返回某个表达式节点对应的虚拟寄存器。 + * + *

会根据节点的实际类型分别处理: + *

    + *
  • 数字字面量:新建常量寄存器
  • + *
  • 标识符:查找当前作用域中的寄存器
  • + *
  • 二元表达式:递归处理子表达式并进行相应运算
  • + *
  • 函数调用:生成对应的Call指令
  • + *
  • 其它类型不支持,抛出异常
  • + *
+ * + * @param expr 要转换的表达式AST节点 + * @return 该表达式的计算结果寄存器 + * @throws IllegalStateException 如果遇到未定义的标识符或不支持的表达式类型 + */ + public IRVirtualRegister build(ExpressionNode expr) { + return switch (expr) { + // 数字字面量,如 "123", "1.0f" + case NumberLiteralNode n -> buildNumberLiteral(n.value()); + // 标识符,如变量x,直接查作用域 + case IdentifierNode id -> { + IRVirtualRegister reg = ctx.getScope().lookup(id.name()); + if (reg == null) throw new IllegalStateException("未定义标识符: " + id.name()); + yield reg; + } + // 二元表达式,如 "a + b" + case BinaryExpressionNode bin -> buildBinary(bin); + // 函数调用,如 foo(a, b) + case CallExpressionNode call -> buildCall(call); + // 其它不支持 + default -> throw new IllegalStateException("不支持的表达式类型: " + expr.getClass().getSimpleName()); + }; + } + + /** + * 直接将表达式计算结果写入指定的目标寄存器(dest)。 + *

+ * 与{@link #build(ExpressionNode)}类似,但支持目标寄存器复用(避免不必要的move)。 + * + * @param node 表达式AST节点 + * @param dest 目标寄存器 + * @throws IllegalStateException 未定义标识符/不支持的表达式类型时报错 + */ + public void buildInto(ExpressionNode node, IRVirtualRegister dest) { + switch (node) { + // 数字字面量,直接加载到目标寄存器 + case NumberLiteralNode n -> + InstructionFactory.loadConstInto(ctx, dest, ExpressionUtils.parseIntSafely(n.value())); + // 标识符,查找并move到目标寄存器 + case IdentifierNode id -> { + IRVirtualRegister src = ctx.getScope().lookup(id.name()); + if (src == null) throw new IllegalStateException("未定义标识符: " + id.name()); + InstructionFactory.move(ctx, src, dest); + } + // 二元表达式,直接写入目标寄存器 + case BinaryExpressionNode bin -> buildBinaryInto(bin, dest); + // 其他表达式,先递归生成寄存器,再move到目标寄存器 + default -> { + IRVirtualRegister tmp = build(node); + InstructionFactory.move(ctx, tmp, dest); + } + } + } + + // ===================== 内部私有方法 ===================== + + /** + * 构建二元表达式的IR,生成新寄存器存储结果。 + *

+ * 先递归构建左右操作数,之后根据操作符类别(算术或比较)决定生成的IR操作码, + * 并生成对应的二元运算指令。 + * + * @param bin 二元表达式节点 + * @return 存放结果的虚拟寄存器 + */ + private IRVirtualRegister buildBinary(BinaryExpressionNode bin) { + String op = bin.operator(); + IRVirtualRegister left = build(bin.left()); + IRVirtualRegister right = build(bin.right()); + // 处理比较操作符 + if (ExpressionUtils.isComparisonOperator(op)) { + return InstructionFactory.binOp(ctx, ExpressionUtils.cmpOp(op), left, right); + } + // 处理算术运算符 + IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right()); + if (code == null) throw new IllegalStateException("不支持的运算符: " + op); + return InstructionFactory.binOp(ctx, code, left, right); + } + + /** + * 将二元表达式的结果直接写入指定寄存器dest。 + *

+ * 结构与{@link #buildBinary(BinaryExpressionNode)}类似,但不会新分配寄存器。 + * + * @param bin 二元表达式节点 + * @param dest 目标寄存器 + */ + private void buildBinaryInto(BinaryExpressionNode bin, IRVirtualRegister dest) { + IRVirtualRegister a = build(bin.left()); + IRVirtualRegister b = build(bin.right()); + String op = bin.operator(); + if (ExpressionUtils.isComparisonOperator(op)) { + InstructionFactory.binOpInto(ctx, ExpressionUtils.cmpOp(op), a, b, dest); + } else { + IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right()); + if (code == null) throw new IllegalStateException("不支持的运算符: " + op); + InstructionFactory.binOpInto(ctx, code, a, b, dest); + } + } + + /** + * 处理函数调用表达式,生成对应的Call指令和目标寄存器。 + *

+ * 支持普通标识符调用和成员调用(如 mod.func),会为每个参数依次生成子表达式的寄存器。 + * + * @param call 调用表达式AST节点 + * @return 返回结果存放的寄存器 + */ + private IRVirtualRegister buildCall(CallExpressionNode call) { + // 递归构建所有参数的寄存器 + List argv = call.arguments().stream() + .map(this::build) + .toList(); + // 获取完整调用目标名称(支持成员/模块调用和普通调用) + String fullName = switch (call.callee()) { + case MemberExpressionNode member when member.object() instanceof IdentifierNode mod -> + ((IdentifierNode)member.object()).name() + "." + member.member(); + case IdentifierNode id -> id.name(); + default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName()); + }; + // 申请目标寄存器 + IRVirtualRegister dest = ctx.newRegister(); + // 添加Call指令到IR上下文 + ctx.addInstruction(new CallInstruction(dest, fullName, new ArrayList<>(argv))); + return dest; + } + + /** + * 处理数字字面量,生成常量寄存器和加载指令。 + *

+ * 会将字符串型字面量(如 "123", "1.0f")解析为具体的IRConstant, + * 并分配一个新的虚拟寄存器来存放该常量。 + * + * @param value 字面量字符串 + * @return 存放该常量的寄存器 + */ + private IRVirtualRegister buildNumberLiteral(String value) { + IRConstant constant = ExpressionUtils.buildNumberConstant(value); + IRVirtualRegister reg = ctx.newRegister(); + ctx.addInstruction(new LoadConstInstruction(reg, constant)); + return reg; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/FunctionBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/FunctionBuilder.java new file mode 100644 index 0000000..cc14557 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/FunctionBuilder.java @@ -0,0 +1,71 @@ +package org.jcnc.snow.compiler.ir.builder; + +import org.jcnc.snow.compiler.ir.core.IRFunction; +import org.jcnc.snow.compiler.ir.utils.ExpressionUtils; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.parser.ast.ParameterNode; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; + +/** + * IR 函数构建器。 + *

+ * 负责将语法树中的 FunctionNode 节点转化为可执行的 IRFunction, + * 包含参数声明、返回类型推断、函数体语句转换等步骤。 + */ +public class FunctionBuilder { + + /** + * 将 AST 中的 FunctionNode 构建为可执行的 IRFunction。 + *

+ * 构建过程包括: + *

    + *
  1. 初始化 IRFunction 实例和上下文
  2. + *
  3. 根据函数返回类型,设置默认类型后缀,便于表达式推断
  4. + *
  5. 声明参数到作用域,并为每个参数分配虚拟寄存器
  6. + *
  7. 遍历并转换函数体内的每条语句为 IR 指令
  8. + *
  9. 函数构建完成后,清理默认类型后缀,防止影响其他函数
  10. + *
+ * + * @param functionNode 表示函数定义的语法树节点 + * @return 构建得到的 IRFunction 对象 + */ + public IRFunction build(FunctionNode functionNode) { + + // 0) 基本初始化:创建 IRFunction 实例与对应上下文 + IRFunction irFunction = new IRFunction(functionNode.name()); + IRContext irContext = new IRContext(irFunction); + + // 1) 把函数返回类型注入为默认类型后缀(供表达式类型推断) + char _returnSuffix = switch (functionNode.returnType().toLowerCase()) { + case "double" -> 'd'; + case "float" -> 'f'; + case "long" -> 'l'; + case "short" -> 's'; + case "byte" -> 'b'; + default -> '\0'; + }; + ExpressionUtils.setDefaultSuffix(_returnSuffix); + + try { + // 2) 声明形参:为每个参数分配虚拟寄存器并声明到作用域 + for (ParameterNode p : functionNode.parameters()) { + IRVirtualRegister reg = irFunction.newRegister(); // 新寄存器 + irContext.getScope().declare(p.name(), reg); // 变量名→寄存器绑定 + irFunction.addParameter(reg); // 添加到函数参数列表 + } + + // 3) 生成函数体 IR:遍历每条语句,逐一转化 + StatementBuilder stmtBuilder = new StatementBuilder(irContext); + for (StatementNode stmt : functionNode.body()) { + stmtBuilder.build(stmt); + } + } finally { + // 4) 清除默认后缀,避免影响后续函数的推断 + ExpressionUtils.clearDefaultSuffix(); + } + + // 返回构建好的 IRFunction + return irFunction; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/IRBuilderScope.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRBuilderScope.java new file mode 100644 index 0000000..168eac2 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRBuilderScope.java @@ -0,0 +1,85 @@ +package org.jcnc.snow.compiler.ir.builder; + +import org.jcnc.snow.compiler.ir.core.IRFunction; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.HashMap; +import java.util.Map; + +/** + * IRBuilderScope 用于管理单个函数内变量名与虚拟寄存器的映射关系。 + * + *

主要功能包括: + *

    + *
  • 维护在当前作用域中已声明变量的寄存器分配信息;
  • + *
  • 支持将已有虚拟寄存器与变量名重新绑定;
  • + *
  • 根据变量名查找对应的虚拟寄存器实例。
  • + *
+ */ +final class IRBuilderScope { + + /** + * 存储变量名到对应 IRVirtualRegister 的映射。 + * 变量名为键,虚拟寄存器对象为值,用于查找和更新。 + */ + private final Map vars = new HashMap<>(); + + /** + * 当前作用域所绑定的 IRFunction 对象,用于申请新的虚拟寄存器。 + */ + private IRFunction fn; + + /** + * 将指定的 IRFunction 关联到当前作用域,以便后续声明变量时能够 + * 调用该函数的 newRegister() 方法生成新的寄存器。 + * + * @param fn 要绑定到本作用域的 IRFunction 实例 + */ + void attachFunction(IRFunction fn) { + this.fn = fn; + } + + /** + * 在当前作用域中声明一个新变量,并为其分配一个新的虚拟寄存器。 + * 调用绑定的 IRFunction.newRegister() 生成寄存器后保存到映射表中。 + * + * @param name 变量名称,作为映射键使用 + */ + void declare(String name) { + IRVirtualRegister reg = fn.newRegister(); + vars.put(name, reg); + } + + /** + * 在当前作用域中声明或导入一个已有的虚拟寄存器,并将其与指定变量名绑定。 + * 该方法可用于将外部或前一作用域的寄存器导入到本作用域。 + * + * @param name 变量名称,作为映射键使用 + * @param reg 要绑定到该名称的 IRVirtualRegister 实例 + */ + void declare(String name, IRVirtualRegister reg) { + vars.put(name, reg); + } + + /** + * 更新已存在变量的虚拟寄存器绑定关系。若变量已声明,则替换其对应的寄存器; + * 若尚未声明,则等同于声明新变量。 + * + * @param name 变量名称,作为映射键使用 + * @param reg 新的 IRVirtualRegister 实例,用于替换旧绑定 + */ + void put(String name, IRVirtualRegister reg) { + vars.put(name, reg); + } + + /** + * 根据变量名称在当前作用域中查找对应的虚拟寄存器。 + * + * @param name 需要查询的变量名称 + * @return 如果该名称已绑定寄存器,则返回对应的 IRVirtualRegister; + * 如果未声明,则返回 null + */ + IRVirtualRegister lookup(String name) { + return vars.get(name); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/IRContext.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRContext.java new file mode 100644 index 0000000..7b7232d --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRContext.java @@ -0,0 +1,88 @@ +package org.jcnc.snow.compiler.ir.builder; + +import org.jcnc.snow.compiler.ir.core.IRFunction; +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +/** + * IRContext 类负责封装当前正在构建的 IRFunction 实例 + * 以及与之配套的作用域管理(IRBuilderScope), + * 并简化虚拟寄存器分配与 IR 指令添加操作。 + * + *

本类提供以下核心功能: + *

    + *
  • 持有并操作当前 IRFunction 对象;
  • + *
  • 管理变量名与虚拟寄存器的映射关系;
  • + *
  • 分配新的虚拟寄存器实例;
  • + *
  • 将生成的 IRInstruction 自动添加到 IRFunction 中;
  • + *
+ */ +public class IRContext { + + /* ➡ 新增:生成唯一标签用 */ + private int labelCounter = 0; + /** + * 当前正在构建的 IRFunction 对象,所有指令将添加至此 + */ + private final IRFunction function; + + /** + * 用于管理当前函数作用域内变量与虚拟寄存器的映射 + */ + private final IRBuilderScope scope; + + /** + * 构造一个新的 IRContext,并将指定的 IRFunction 与作用域关联。 + * + * @param function 要构建的 IRFunction 实例 + */ + public IRContext(IRFunction function) { + this.function = function; + this.scope = new IRBuilderScope(); + // 关联作用域与 IRFunction,以便在声明变量时申请寄存器 + this.scope.attachFunction(function); + } + + /** + * 获取当前正在构建的 IRFunction 对象。 + * + * @return 当前 IRFunction 实例 + */ + public IRFunction getFunction() { + return function; + } + + /** + * 获取当前函数的变量与寄存器映射作用域。 + * + *

包内可见:仅限 builder 包内部使用。 + * + * @return IRBuilderScope 实例 + */ + IRBuilderScope getScope() { + return scope; + } + + /** + * 为当前函数分配一个新的虚拟寄存器。 + * + * @return 分配到的 IRVirtualRegister 对象 + */ + public IRVirtualRegister newRegister() { + return function.newRegister(); + } + + /** + * 将指定的 IRInstruction 添加到当前 IRFunction 的指令列表中。 + * + * @param instr 要添加的 IRInstruction 实例 + */ + public void addInstruction(IRInstruction instr) { + function.add(instr); + } + + /** 生成一个形如 L0 / L1 ... 的唯一标签名 */ + public String newLabel() { + return "L" + (labelCounter++); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/IRProgramBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRProgramBuilder.java new file mode 100644 index 0000000..a1168ec --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRProgramBuilder.java @@ -0,0 +1,81 @@ +package org.jcnc.snow.compiler.ir.builder; + +import org.jcnc.snow.compiler.ir.core.IRFunction; +import org.jcnc.snow.compiler.ir.core.IRProgram; +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.parser.ast.ModuleNode; +import org.jcnc.snow.compiler.parser.ast.base.Node; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; + +import java.util.List; + +/** + * 本类负责将解析生成的 AST 根节点列表转换为可执行的 IRProgram。 + * + *

主要职责: + *

    + *
  • 遍历输入的顶层节点,识别 ModuleNode、FunctionNode 及脚本式顶层 StatementNode;
  • + *
  • 对 ModuleNode 中的所有函数节点调用 FunctionBuilder 构建 IRFunction 并添加至 IRProgram;
  • + *
  • 对单独的 FunctionNode 节点直接构建并纳入 IRProgram;
  • + *
  • 对顶层脚本式 StatementNode 自动封装为名称固定的“_start”函数,再行构建并纳入 IRProgram;
  • + *
  • 对不支持的节点类型抛出 IllegalStateException,以确保编译流程严谨。
  • + *
+ */ +public final class IRProgramBuilder { + + /** + * 构建完整的 IRProgram 实例。 + * + * @param roots 含 ModuleNode、FunctionNode 或 StatementNode 的顶层 AST 根节点列表 + * @return 包含所有转换后 IRFunction 的 IRProgram 对象 + * @throws IllegalStateException 遇到不支持的顶层节点类型时抛出 + */ + public IRProgram buildProgram(List roots) { + IRProgram irProgram = new IRProgram(); + for (Node node : roots) { + switch (node) { + case ModuleNode moduleNode -> + // 模块节点:批量构建并添加模块内所有函数 + moduleNode.functions().forEach(f -> irProgram.add(buildFunction(f))); + case FunctionNode functionNode -> + // 顶层函数节点:直接构建并添加 + irProgram.add(buildFunction(functionNode)); + case StatementNode statementNode -> + // 脚本式顶层语句:封装为“_start”函数后构建并添加 + irProgram.add(buildFunction(wrapTopLevel(statementNode))); + default -> + // 严格校验节点类型,遇不支持者立即失败 + throw new IllegalStateException("Unsupported top-level node: " + node); + } + } + return irProgram; + } + + /** + * 利用 FunctionBuilder 将 FunctionNode 转换为 IRFunction。 + * + * @param functionNode 待构建的 AST FunctionNode + * @return 构建完成的 IRFunction 实例 + */ + private IRFunction buildFunction(FunctionNode functionNode) { + return new FunctionBuilder().build(functionNode); + } + + /** + * 将单个脚本式顶层 StatementNode 封装为名称固定的“_start”函数节点。 + * + *

封装规则: + *

    + *
  • 函数名固定为“_start”;
  • + *
  • 返回类型设为 null,由后续流程处理;
  • + *
  • 参数列表为空;
  • + *
  • 函数主体仅包含传入的单条语句。
  • + *
+ * + * @param stmt 待封装的顶层脚本语句节点 + * @return 生成的 FunctionNode,用于后续 IRFunction 构建 + */ + private FunctionNode wrapTopLevel(StatementNode stmt) { + return new FunctionNode("_start", null, String.valueOf(List.of()), List.of(stmt)); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/InstructionFactory.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/InstructionFactory.java new file mode 100644 index 0000000..b0a9993 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/InstructionFactory.java @@ -0,0 +1,155 @@ +package org.jcnc.snow.compiler.ir.builder; + +import org.jcnc.snow.compiler.ir.core.IROpCode; +import org.jcnc.snow.compiler.ir.instruction.*; +import org.jcnc.snow.compiler.ir.value.IRConstant; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +/** + * InstructionFactory —— 统一生成并注册 IR 指令的工厂类。 + *

+ * 该类封装了常见的 IR 指令生成方式,包括常量加载、二元运算、赋值、控制流等, + * 统一简化指令插入和寄存器分配逻辑,提升 IR 生成阶段的代码可维护性和复用性。 + *

+ */ +public class InstructionFactory { + + /* ====================================================================== */ + /* 常量 / 通用二元运算(新寄存器) */ + /* ====================================================================== */ + + /** + * 加载整数常量,将其写入一个新分配的虚拟寄存器,并返回该寄存器。 + * + * @param ctx 当前 IR 上下文(用于分配寄存器与添加指令) + * @param value 要加载的整数常量值 + * @return 存储该常量的新虚拟寄存器 + */ + public static IRVirtualRegister loadConst(IRContext ctx, int value) { + IRVirtualRegister r = ctx.newRegister(); + ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(value))); + return r; + } + + /** + * 执行二元运算(如加法、减法等),结果写入新分配的虚拟寄存器并返回该寄存器。 + * + * @param ctx 当前 IR 上下文 + * @param op 运算类型(IROpCode 枚举,如 ADD_I32 等) + * @param a 第一个操作数寄存器 + * @param b 第二个操作数寄存器 + * @return 保存运算结果的新虚拟寄存器 + */ + public static IRVirtualRegister binOp(IRContext ctx, IROpCode op, + IRVirtualRegister a, IRVirtualRegister b) { + IRVirtualRegister dest = ctx.newRegister(); + ctx.addInstruction(new BinaryOperationInstruction(op, dest, a, b)); + return dest; + } + + /* ====================================================================== */ + /* 直接写入指定寄存器 */ + /* ====================================================================== */ + + /** + * 加载整数常量到指定虚拟寄存器。 + * + * @param ctx 当前 IR 上下文 + * @param dest 目标寄存器 + * @param value 要加载的整数常量 + */ + public static void loadConstInto(IRContext ctx, IRVirtualRegister dest, int value) { + ctx.addInstruction(new LoadConstInstruction(dest, new IRConstant(value))); + } + + /** + * 对两个寄存器执行二元运算,将结果写入指定目标寄存器。 + * + * @param ctx 当前 IR 上下文 + * @param op 运算类型(IROpCode 枚举) + * @param a 第一个操作数寄存器 + * @param b 第二个操作数寄存器 + * @param dest 运算结果目标寄存器 + */ + public static void binOpInto(IRContext ctx, IROpCode op, + IRVirtualRegister a, IRVirtualRegister b, + IRVirtualRegister dest) { + ctx.addInstruction(new BinaryOperationInstruction(op, dest, a, b)); + } + + /** + * 简易 Move 指令(src → dest)。若寄存器相同也安全。 + *

+ * 实现方式:dest = src + 0(即加上常量 0)。 + *

+ * + * @param ctx 当前 IR 上下文 + * @param src 源寄存器 + * @param dest 目标寄存器 + */ + public static void move(IRContext ctx, IRVirtualRegister src, IRVirtualRegister dest) { + /* 采用 “dest = src + 0” 的最简实现 */ + IRVirtualRegister zero = loadConst(ctx, 0); + ctx.addInstruction(new BinaryOperationInstruction(IROpCode.ADD_I32, dest, src, zero)); + } + + /* ====================================================================== */ + /* 控制流指令 */ + /* ====================================================================== */ + + /** + * 生成无条件跳转(JMP)指令,跳转到指定标签。 + * + * @param ctx 当前 IR 上下文 + * @param label 目标标签名 + */ + public static void jmp(IRContext ctx, String label) { + ctx.addInstruction(new IRJumpInstruction(label)); + } + + /** + * 在 IR 中插入一个标签(Label)。 + * + * @param ctx 当前 IR 上下文 + * @param label 标签名 + */ + public static void label(IRContext ctx, String label) { + ctx.addInstruction(new IRLabelInstruction(label)); + } + + /** + * 比较跳转(如 if a < b goto label),根据条件跳转到目标标签。 + * + * @param ctx 当前 IR 上下文 + * @param cmp 比较操作码(如 IROpCode.LT_I32 等) + * @param a 第一个操作数寄存器 + * @param b 第二个操作数寄存器 + * @param targetLabel 跳转目标标签 + */ + public static void cmpJump(IRContext ctx, IROpCode cmp, + IRVirtualRegister a, IRVirtualRegister b, + String targetLabel) { + ctx.addInstruction(new IRCompareJumpInstruction(cmp, a, b, targetLabel)); + } + + /* ---------------- 返回 ---------------- */ + + /** + * 生成返回指令(带返回值)。 + * + * @param ctx 当前 IR 上下文 + * @param value 返回值寄存器 + */ + public static void ret(IRContext ctx, IRVirtualRegister value) { + ctx.addInstruction(new ReturnInstruction(value)); + } + + /** + * 生成无返回值的 return 指令(如 void 函数)。 + * + * @param ctx 当前 IR 上下文 + */ + public static void retVoid(IRContext ctx) { + ctx.addInstruction(new ReturnInstruction(null)); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java new file mode 100644 index 0000000..18f750b --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java @@ -0,0 +1,180 @@ +package org.jcnc.snow.compiler.ir.builder; + +import org.jcnc.snow.compiler.ir.core.IROpCode; +import org.jcnc.snow.compiler.ir.utils.ExpressionUtils; +import org.jcnc.snow.compiler.ir.utils.IROpCodeUtils; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; +import org.jcnc.snow.compiler.parser.ast.*; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; + +/** + * StatementBuilder —— 将 AST 语句节点 ({@link StatementNode}) 转换为 IR 指令序列的构建器。 + *

+ * 负责将各种语句节点(循环、分支、表达式、赋值、声明、返回等)生成对应的 IR 指令,并管理作用域和控制流标签。 + *

+ */ +public class StatementBuilder { + + /** 当前 IR 上下文,包含作用域、指令序列等信息。 */ + private final IRContext ctx; + /** 表达式 IR 构建器,用于将表达式节点转为 IR 指令。 */ + private final ExpressionBuilder expr; + + /** + * 构造方法。 + * @param ctx IR 编译上下文环境 + */ + public StatementBuilder(IRContext ctx) { + this.ctx = ctx; + this.expr = new ExpressionBuilder(ctx); + } + + /** + * 将一个 AST 语句节点转为 IR 指令序列。 + * 根据节点类型分发到对应的处理方法。 + * @param stmt 待转换的语句节点 + */ + public void build(StatementNode stmt) { + if (stmt instanceof LoopNode loop) { + // 循环语句 + buildLoop(loop); + return; + } + if (stmt instanceof IfNode ifNode) { + // 分支(if-else)语句 + buildIf(ifNode); + return; + } + if (stmt instanceof ExpressionStatementNode(ExpressionNode exp)) { + // 纯表达式语句,如 foo(); + expr.build(exp); + return; + } + if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs)) { + // 赋值语句,如 a = b + 1; + IRVirtualRegister target = getOrDeclareRegister(var); + expr.buildInto(rhs, target); + return; + } + if (stmt instanceof DeclarationNode decl) { + // 变量声明,如 int a = 1; + if (decl.getInitializer().isPresent()) { + // 声明同时有初值 + IRVirtualRegister r = expr.build(decl.getInitializer().get()); + ctx.getScope().declare(decl.getName(), r); + } else { + // 仅声明,无初值 + ctx.getScope().declare(decl.getName()); + } + return; + } + if (stmt instanceof ReturnNode ret) { + // return 语句 + if (ret.getExpression().isPresent()) { + // return 带返回值 + IRVirtualRegister r = expr.build(ret.getExpression().get()); + InstructionFactory.ret(ctx, r); + } else { + // return 无返回值 + InstructionFactory.retVoid(ctx); + } + return; + } + // 不支持的语句类型 + throw new IllegalStateException("Unsupported statement: " + stmt.getClass().getSimpleName() + ": " + stmt); + } + + /** + * 获取变量名对应的寄存器,不存在则声明一个新的。 + * @param name 变量名 + * @return 变量对应的虚拟寄存器 + */ + private IRVirtualRegister getOrDeclareRegister(String name) { + IRVirtualRegister reg = ctx.getScope().lookup(name); + if (reg == null) { + reg = ctx.newRegister(); + ctx.getScope().declare(name, reg); + } + return reg; + } + + /** + * 批量构建一组语句节点,顺序处理每个语句。 + * @param stmts 语句节点集合 + */ + private void buildStatements(Iterable stmts) { + for (StatementNode s : stmts) build(s); + } + + /** + * 构建循环语句(for/while)。 + * 处理流程:初始语句 → 条件判断 → 循环体 → 更新语句 → 跳回条件。 + * @param loop 循环节点 + */ + private void buildLoop(LoopNode loop) { + if (loop.initializer() != null) build(loop.initializer()); + String lblStart = ctx.newLabel(); + String lblEnd = ctx.newLabel(); + // 循环开始标签 + InstructionFactory.label(ctx, lblStart); + + // 条件不满足则跳出循环 + emitConditionalJump(loop.condition(), lblEnd); + // 构建循环体 + buildStatements(loop.body()); + // 更新部分(如 for 的 i++) + if (loop.update() != null) build(loop.update()); + + // 跳回循环起点 + InstructionFactory.jmp(ctx, lblStart); + // 循环结束标签 + InstructionFactory.label(ctx, lblEnd); + } + + /** + * 构建分支语句(if/else)。 + * 处理流程:条件判断 → then 分支 → else 分支(可选)。 + * @param ifNode if 语句节点 + */ + private void buildIf(IfNode ifNode) { + String lblElse = ctx.newLabel(); + String lblEnd = ctx.newLabel(); + // 条件不成立则跳转到 else + emitConditionalJump(ifNode.condition(), lblElse); + + // then 分支 + buildStatements(ifNode.thenBranch()); + // then 分支执行完后直接跳到结束 + InstructionFactory.jmp(ctx, lblEnd); + + // else 分支(可能为空) + InstructionFactory.label(ctx, lblElse); + if (ifNode.elseBranch() != null) buildStatements(ifNode.elseBranch()); + // 结束标签 + InstructionFactory.label(ctx, lblEnd); + } + + /** + * 条件跳转指令的生成。 + * 如果是二元比较表达式,直接使用对应比较操作码;否则等价于与 0 比较。 + * @param cond 条件表达式 + * @param falseLabel 条件不成立时跳转到的标签 + */ + private void emitConditionalJump(ExpressionNode cond, String falseLabel) { + if (cond instanceof BinaryExpressionNode(ExpressionNode left, String operator, ExpressionNode right) + && ExpressionUtils.isComparisonOperator(operator)) { + // 如果是比较操作(如 ==, >, <),直接生成对应的条件跳转 + IRVirtualRegister a = expr.build(left); + IRVirtualRegister b = expr.build(right); + // 获取反向比较操作码 + IROpCode falseOp = IROpCodeUtils.invert(ExpressionUtils.cmpOp(operator)); + InstructionFactory.cmpJump(ctx, falseOp, a, b, falseLabel); + } else { + // 否则将 cond 与 0 比较,相等则跳转 + IRVirtualRegister condReg = expr.build(cond); + IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0); + InstructionFactory.cmpJump(ctx, IROpCode.CMP_EQ, condReg, zero, falseLabel); + } + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/core/IRFunction.java b/src/main/java/org/jcnc/snow/compiler/ir/core/IRFunction.java new file mode 100644 index 0000000..74cbd08 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/core/IRFunction.java @@ -0,0 +1,144 @@ +package org.jcnc.snow.compiler.ir.core; + +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.ArrayList; +import java.util.List; + +/** + * 表示单个函数的中间表示(IR)。 + *

+ * IRFunction 跟踪代码生成和优化所需的所有信息, + * 包括函数标识符、IR 指令序列、 + * 声明参数列表以及生成唯一虚拟寄存器的机制。 + *

+ */ +public class IRFunction { + + /** + * 函数名,对应源级函数的标识。 + */ + private final String name; + + /** + * IR 指令列表,组成函数体。 + */ + private final List body = new ArrayList<>(); + + /** + * 用于生成新的虚拟寄存器编号的计数器。 + */ + private int regCounter = 0; + + /** + * 正式参数所对应的虚拟寄存器列表,按声明顺序排列。 + */ + private final List parameters = new ArrayList<>(); + + /** + * 构造一个具有指定名称的 IRFunction 实例。 + * + * @param name 要关联的函数名称 + */ + public IRFunction(String name) { + this.name = name; + } + + /** + * 分配一个新的虚拟寄存器。 + * 每次调用会生成一个带有唯一编号的 IRVirtualRegister。 + * + * @return 新分配的虚拟寄存器 + */ + public IRVirtualRegister newRegister() { + return new IRVirtualRegister(regCounter++); + } + + /** + * 将一个虚拟寄存器添加到函数的正式参数列表中。 + *

+ * 应按源函数签名中参数的声明顺序逐一调用此方法。 + *

+ * + * @param vr 表示函数某个参数的虚拟寄存器 + */ + public void addParameter(IRVirtualRegister vr) { + parameters.add(vr); + } + + /** + * 获取函数正式参数的只读列表。 + * + * @return 按声明顺序排列的虚拟寄存器列表 + */ + public List parameters() { + return List.copyOf(parameters); + } + + /** + * 向函数体末尾追加一条 IR 指令。 + * + * @param inst 要追加的 IRInstruction 实例 + */ + public void add(IRInstruction inst) { + body.add(inst); + } + + /** + * 获取函数体中所有指令的只读列表。 + * + * @return 表示函数体的 IRInstruction 列表 + */ + public List body() { + return List.copyOf(body); + } + + /** + * 获取函数的源级名称。 + * + * @return 函数名称 + */ + public String name() { + return name; + } + + /** + * 获取已分配的虚拟寄存器总数。 + * + * @return 虚拟寄存器计数 + */ + public int registerCount() { + return regCounter; + } + + /** + * 以IR代码表示,示例: + *
+     * func 名称(%0, %1, ...) {
+     *   指令0
+     *   指令1
+     *   ...
+     * }
+     * 
+ * + * @return 函数的 IR 文本表示 + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder("func ") + .append(name) + .append('('); + for (int i = 0; i < parameters.size(); i++) { + sb.append(parameters.get(i)); + if (i < parameters.size() - 1) { + sb.append(", "); + } + } + sb.append(") {\n"); + for (IRInstruction inst : body) { + sb.append(" ").append(inst).append('\n'); + } + sb.append('}'); + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/ir/core/IRInstruction.java b/src/main/java/org/jcnc/snow/compiler/ir/core/IRInstruction.java new file mode 100644 index 0000000..2821d58 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/core/IRInstruction.java @@ -0,0 +1,62 @@ +package org.jcnc.snow.compiler.ir.core; + +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; +import java.util.List; + +/** + * IRInstruction —— 所有 IR(中间表示)指令的抽象基类。 + *

+ * 本类定义了编译器中间表示系统中所有指令的基本结构和行为。 + * 具体指令通过继承此类并实现各自的操作码(Opcode)和访问者方法, + * 以支持统一的指令处理和访问模式。 + *

+ */ +public abstract class IRInstruction { + + /** + * 获取该指令的操作码(Opcode)。 + *

+ * 每个具体指令子类必须实现此方法,返回对应的 IROpCode 枚举值。 + *

+ * + * @return 表示指令类型的 IROpCode 实例 + */ + public abstract IROpCode op(); + + /** + * 获取指令的目标虚拟寄存器(destination register)。 + *

+ * 默认实现返回 null;只有具有目标寄存器的指令(如赋值、算术运算) + * 应重写此方法以返回相应的 IRVirtualRegister。 + *

+ * + * @return 目标虚拟寄存器,若无目标寄存器则返回 null + */ + public IRVirtualRegister dest() { + return null; + } + + /** + * 获取指令的操作数列表。 + *

+ * 默认实现返回空列表;具体指令子类应根据需要重写此方法, + * 提供所有参与运算或调用的 IRValue 操作数集合。 + *

+ * + * @return 包含本指令所有操作数的列表 + */ + public List operands() { + return List.of(); + } + + /** + * 接受一个 IRVisitor 实例,实现访问者模式的入口。 + *

+ * 具体指令子类必须实现此方法,以便 IRVisitor 根据指令类型 + * 调用相应的访问逻辑。 + *

+ * + * @param visitor 实现 IRVisitor 接口的访问者对象 + */ + public abstract void accept(IRVisitor visitor); +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/ir/core/IROpCode.java b/src/main/java/org/jcnc/snow/compiler/ir/core/IROpCode.java new file mode 100644 index 0000000..24e86e3 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/core/IROpCode.java @@ -0,0 +1,89 @@ +package org.jcnc.snow.compiler.ir.core; + +/** + * {@code IROpCode} 枚举类型定义了中间表示(IR)层支持的全部操作码。 + *

+ * 每个操作码代表一种低层次、语义明确的中间指令,用于构建目标函数的中间表示。 + * 这些操作涵盖了不同数据位宽的整数与浮点数算术运算、逻辑与比较操作、 + * 数据加载与存储指令、控制流指令(如跳转、条件跳转、标签)、 + * 以及函数调用与返回等功能。 + *

+ * 本枚举用于 {@link IRInstruction} 体系结构中,是 IR 指令识别和转换的核心部分, + *

+ */ +public enum IROpCode { + + // 整型 ↔ 单精度 + CONV_I32_TO_F32, + CONV_F32_TO_I32, + // 整型 ↔ 双精度 + CONV_I32_TO_D64, + CONV_D64_TO_I32, + // 单精度 ↔ 双精度 + CONV_F32_TO_D64, + CONV_D64_TO_F32, + + + /* ───── 算术运算(8位整数:byte)───── */ + ADD_B8, // 8位整型加法:a = b + c + SUB_B8, // 8位整型减法:a = b - c + MUL_B8, // 8位整型乘法:a = b * c + DIV_B8, // 8位整型除法:a = b / c + NEG_B8, // 8位整型取负:a = -b + + /* ───── 算术运算(16位整数:short)───── */ + ADD_S16, // 16位整型加法 + SUB_S16, // 16位整型减法 + MUL_S16, // 16位整型乘法 + DIV_S16, // 16位整型除法 + NEG_S16, // 16位整型取负 + + /* ───── 算术运算(32位整数:int)───── */ + ADD_I32, // 32位整型加法 + SUB_I32, // 32位整型减法 + MUL_I32, // 32位整型乘法 + DIV_I32, // 32位整型除法 + NEG_I32, // 32位整型取负 + + /* ───── 算术运算(64位整数:long)───── */ + ADD_L64, // 64位整型加法 + SUB_L64, // 64位整型减法 + MUL_L64, // 64位整型乘法 + DIV_L64, // 64位整型除法 + NEG_L64, // 64位整型取负 + + /* ───── 算术运算(32位浮点数:float)───── */ + ADD_F32, // 32位浮点加法 + SUB_F32, // 32位浮点减法 + MUL_F32, // 32位浮点乘法 + DIV_F32, // 32位浮点除法 + NEG_F32, // 32位浮点取负 + + /* ───── 算术运算(64位浮点数:double)───── */ + ADD_D64, // 64位浮点加法 + SUB_D64, // 64位浮点减法 + MUL_D64, // 64位浮点乘法 + DIV_D64, // 64位浮点除法 + NEG_D64, // 64位浮点取负 + + /* ───── 逻辑与比较运算指令 ───── */ + CMP_EQ, // 相等比较:a == b + CMP_NE, // 不等比较:a != b + CMP_LT, // 小于比较:a < b + CMP_GT, // 大于比较:a > b + CMP_LE, // 小于等于:a <= b + CMP_GE, // 大于等于:a >= b + + /* ───── 数据访问与常量操作 ───── */ + LOAD, // 从内存加载数据至寄存器 + STORE, // 将寄存器数据写回内存 + CONST, // 将常量写入目标寄存器 + + /* ───── 控制流指令 ───── */ + JUMP, // 无条件跳转至标签 + LABEL, // 标签定义 + + /* ───── 函数调用与返回 ───── */ + CALL, // 函数调用 + RET // 函数返回 +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/ir/core/IROpCodeMappings.java b/src/main/java/org/jcnc/snow/compiler/ir/core/IROpCodeMappings.java new file mode 100644 index 0000000..69e985e --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/core/IROpCodeMappings.java @@ -0,0 +1,50 @@ +package org.jcnc.snow.compiler.ir.core; + +import java.util.Map; + +/** + * 操作符与IR操作码映射表,统一管理所有类型的算术和比较操作映射。 + */ +public final class IROpCodeMappings { + private IROpCodeMappings() {} // 禁止实例化 + + // 8位整型运算符映射 + public static final Map OP_I8 = Map.of( + "+", IROpCode.ADD_B8, "-", IROpCode.SUB_B8, + "*", IROpCode.MUL_B8, "/", IROpCode.DIV_B8 + ); + // 16位整型 + public static final Map OP_I16 = Map.of( + "+", IROpCode.ADD_S16, "-", IROpCode.SUB_S16, + "*", IROpCode.MUL_S16, "/", IROpCode.DIV_S16 + ); + // 32位整型 + public static final Map OP_I32 = Map.of( + "+", IROpCode.ADD_I32, "-", IROpCode.SUB_I32, + "*", IROpCode.MUL_I32, "/", IROpCode.DIV_I32 + ); + // 64位长整型 + public static final Map OP_L64 = Map.of( + "+", IROpCode.ADD_L64, "-", IROpCode.SUB_L64, + "*", IROpCode.MUL_L64, "/", IROpCode.DIV_L64 + ); + // 32位浮点型 + public static final Map OP_F32 = Map.of( + "+", IROpCode.ADD_F32, "-", IROpCode.SUB_F32, + "*", IROpCode.MUL_F32, "/", IROpCode.DIV_F32 + ); + // 64位双精度浮点型 + public static final Map OP_D64 = Map.of( + "+", IROpCode.ADD_D64, "-", IROpCode.SUB_D64, + "*", IROpCode.MUL_D64, "/", IROpCode.DIV_D64 + ); + // 比较操作符映射 + public static final Map CMP = Map.of( + "==", IROpCode.CMP_EQ, + "!=", IROpCode.CMP_NE, + "<", IROpCode.CMP_LT, + ">", IROpCode.CMP_GT, + "<=", IROpCode.CMP_LE, + ">=", IROpCode.CMP_GE + ); +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/core/IRPrinter.java b/src/main/java/org/jcnc/snow/compiler/ir/core/IRPrinter.java new file mode 100644 index 0000000..03cdc70 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/core/IRPrinter.java @@ -0,0 +1,59 @@ +package org.jcnc.snow.compiler.ir.core; + +import org.jcnc.snow.compiler.ir.instruction.IRAddInstruction; +import org.jcnc.snow.compiler.ir.instruction.IRJumpInstruction; +import org.jcnc.snow.compiler.ir.instruction.IRReturnInstruction; + +/** + * {@code IRPrinter} 是一个用于打印 IR 指令的访问者实现。 + *

+ * 本类实现 {@link IRVisitor} 接口,通过覆盖各类指令的访问方法, + * 提供对不同类型 IR 指令的格式化输出,通常用于调试或测试。 + * 默认行为是在控制台(System.out)输出指令的基本信息。 + *

+ *

+ * 可通过继承该类进一步扩展对更多指令类型的支持,或重写输出格式以适配不同的前端/后端需求。 + *

+ */ +public abstract class IRPrinter implements IRVisitor { + + /** + * 访问 {@link IRAddInstruction} 加法指令。 + *

+ * 默认输出形式为 "Add: ",其中 为指令对象的字符串表示。 + *

+ * + * @param inst 加法 IR 指令实例 + */ + @Override + public void visit(IRAddInstruction inst) { + System.out.println("Add: " + inst); + } + + /** + * 访问 {@link IRJumpInstruction} 跳转指令。 + *

+ * 默认输出形式为 "Jump: "。 + *

+ * + * @param inst 跳转 IR 指令实例 + */ + @Override + public void visit(IRJumpInstruction inst) { + System.out.println("Jump: " + inst); + } + + /** + * 访问 {@link IRReturnInstruction} 返回指令。 + *

+ * 默认输出形式为 "Return: "。 + *

+ * + * @param inst 返回 IR 指令实例 + */ + @Override + public void visit(IRReturnInstruction inst) { + System.out.println("Return: " + inst); + } + +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/ir/core/IRProgram.java b/src/main/java/org/jcnc/snow/compiler/ir/core/IRProgram.java new file mode 100644 index 0000000..03ff60a --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/core/IRProgram.java @@ -0,0 +1,62 @@ +package org.jcnc.snow.compiler.ir.core; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * {@code IRProgram} 表示一份完整的中间表示(Intermediate Representation, IR)程序。 + *

+ * 它作为编译器后端处理阶段的核心结构,承载所有由源代码翻译得到的 {@link IRFunction} 实例, + * 形成整体性的中间表示单元,便于进行后续的优化、目标代码生成或静态分析。 + *

+ */ +public final class IRProgram { + + /** + * 存储程序中所有函数的有序集合。 + */ + private final List functions = new ArrayList<>(); + + /** + * 将一个 {@link IRFunction} 添加到程序中。 + *

+ * 函数会按添加顺序保留在内部集合中。 + *

+ * + * @param irFunction 要加入的 IR 函数对象 + */ + public void add(IRFunction irFunction) { + functions.add(irFunction); + } + + /** + * 获取程序中全部函数的只读视图。 + *

+ * 外部调用者无法通过返回的列表修改函数集合,从而确保封装性与结构完整性。 + *

+ * + * @return 不可变的函数列表 + */ + public List functions() { + return Collections.unmodifiableList(functions); + } + + /** + * 返回该 IR 程序的字符串形式。 + *

+ * 每个函数按其 {@code toString()} 表示输出,换行分隔。 + * 通常用于调试与日志输出。 + *

+ * + * @return 表示整个 IR 程序的格式化字符串 + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (IRFunction f : functions) { + sb.append(f).append('\n'); + } + return sb.toString(); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/core/IRValue.java b/src/main/java/org/jcnc/snow/compiler/ir/core/IRValue.java new file mode 100644 index 0000000..ab520f6 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/core/IRValue.java @@ -0,0 +1,29 @@ +package org.jcnc.snow.compiler.ir.core; + +import org.jcnc.snow.compiler.ir.value.IRConstant; +import org.jcnc.snow.compiler.ir.value.IRLabel; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +/** + * {@code IRValue} 表示 IR 指令系统中可被操作的值类型。 + *

+ * 它定义了所有 IR 指令在使用操作数、参数、结果或跳转目标时的统一抽象。 + * 实现该接口的类型可以作为 {@link IRInstruction} 中的操作数出现。 + *

+ * + *

当前支持的 IR 值类型包括:

+ *
    + *
  • {@link IRVirtualRegister}:虚拟寄存器,表示计算结果或中间变量
  • + *
  • {@link IRConstant}:常量值,表示不可变的字面量或数值
  • + *
  • {@link IRLabel}:标签,表示跳转指令的目标地址
  • + *
+ * + *

+ * 该接口声明为 {@code sealed interface},限制只能被上述类型实现。 + * 这种设计允许编译器对 {@code IRValue} 的使用进行静态穷尽性检查, + * 有助于提升类型安全性与维护性。 + *

+ */ +public sealed interface IRValue + permits IRVirtualRegister, IRConstant, IRLabel { +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/core/IRVisitor.java b/src/main/java/org/jcnc/snow/compiler/ir/core/IRVisitor.java new file mode 100644 index 0000000..82231f9 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/core/IRVisitor.java @@ -0,0 +1,80 @@ +package org.jcnc.snow.compiler.ir.core; + +import org.jcnc.snow.compiler.ir.instruction.*; + +/** + * {@code IRVisitor} 是中间表示(IR)指令体系的访问者接口。 + *

+ * 它定义了访问者模式的核心机制,通过对每种 {@link IRInstruction} 子类 + * 提供独立的 {@code visit} 方法,实现对指令的分发与处理。 + * 不同的访问者实现可用于执行不同任务,例如: + *

+ *
    + *
  • {@code IRPrinter}:打印指令内容
  • + *
  • {@code IROptimizer}:分析与重写 IR 以优化性能
  • + *
  • {@code IRCodeGenerator}:生成平台相关的机器码或汇编代码
  • + *
+ * + *

+ * 每当添加新的 {@code IRInstruction} 子类,应同步扩展该接口, + * 以确保访问行为的一致性与完整性。 + *

+ */ +public interface IRVisitor { + + /** + * 访问加法指令(示例实现)。 + * + * @param inst 加法指令实例 + */ + void visit(IRAddInstruction inst); + + /** + * 访问跳转指令。 + * + * @param inst 跳转指令实例 + */ + void visit(IRJumpInstruction inst); + + /** + * 访问返回指令(无返回值)。 + * + * @param inst 返回指令实例 + */ + void visit(IRReturnInstruction inst); + + /** + * 访问二元运算指令(如加减乘除等)。 + * + * @param inst 二元运算 IR 指令实例 + */ + void visit(BinaryOperationInstruction inst); + + /** + * 访问加载常量的指令。 + * + * @param inst 常量加载指令实例 + */ + void visit(LoadConstInstruction inst); + + /** + * 访问返回指令(支持返回值)。 + * + * @param inst 通用返回指令实例 + */ + void visit(ReturnInstruction inst); + + /** + * 访问一元运算指令(如取负等)。 + * + * @param inst 一元运算指令实例 + */ + void visit(UnaryOperationInstruction inst); + + /** + * 访问函数调用指令。 + * + * @param instruction 函数调用指令实例 + */ + void visit(CallInstruction instruction); +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/doc/README.md b/src/main/java/org/jcnc/snow/compiler/ir/doc/README.md new file mode 100644 index 0000000..3d482c0 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/doc/README.md @@ -0,0 +1,37 @@ +# Snow Compiler - IR 模块 + +> Snow 编译器的中间表示模块 —— 负责构建、组织和输出中间表示指令(IR) + +## 项目简介 + +**IR(Intermediate Representation)** 是 [Snow 编译器]() 项目的核心模块,承担中间表示的构建、组织与管理任务。 +它用于在前端语法分析与后端目标代码生成之间,提供结构清晰、便于优化和转换的抽象表示形式。 + +IR 模块以类 SSA(Static Single Assignment)形式设计,通过统一的指令体系、虚拟寄存器模型和构建器架构,实现了良好的表达力与可扩展性,为后续优化和代码生成阶段打下基础。 + +## 核心功能 + +* **统一的中间表示模型**:表达控制流与数据流,支持函数、指令、值等核心结构 +* **IR 构建器体系**:模块化构建函数、表达式与语句 IR,简化前端对接 +* **灵活的指令层级结构**:支持二元操作、跳转、返回等多种基本指令 +* **寄存器与常量模型**:统一管理虚拟寄存器、常量、标签等值类型 +* **IR 打印与调试支持**:辅助输出 IR 文本格式,支持可视化与调试 + +## 模块结构 + +``` +ir/ + ├── builder/ // 构建器模块:负责构造表达式、函数与语句的 IR + ├── core/ // 核心定义:IR 基础结构,如函数、指令、程序、访问器等 + ├── instruction/ // 指令实现:具体的 IR 指令类型(如加法、跳转、返回等) + ├── utils/ // 工具模块:提供 IR 操作相关的辅助函数(如表达式工具、操作码工具等) + └── value/ // 值模型:常量、标签、虚拟寄存器等 +``` + +## 开发环境 + +* JDK 23 或更高版本 +* Maven 构建管理 +* 推荐 IDE:IntelliJ IDEA + +--- diff --git a/src/main/java/org/jcnc/snow/compiler/ir/instruction/BinaryOperationInstruction.java b/src/main/java/org/jcnc/snow/compiler/ir/instruction/BinaryOperationInstruction.java new file mode 100644 index 0000000..e42df97 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/instruction/BinaryOperationInstruction.java @@ -0,0 +1,97 @@ +package org.jcnc.snow.compiler.ir.instruction; + +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.core.IROpCode; +import org.jcnc.snow.compiler.ir.core.IRValue; +import org.jcnc.snow.compiler.ir.core.IRVisitor; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.List; + +/** + * BinaryOperationInstruction —— 表示一个二元运算指令,格式为:dest = lhs OP rhs + *

+ * 该类用于描述形如 a = b + c 或 a = x * y 的二元运算指令。 + * 运算类型(OP)由 {@link IROpCode} 指定,包括加法、减法、乘法、除法等。 + * 左右操作数为 IRValue 类型,结果保存在目标虚拟寄存器 dest 中。 + */ +public final class BinaryOperationInstruction extends IRInstruction { + + /** 指令操作符,如 ADD_I32、SUB_I32 等,取自 IROpCode 枚举 */ + private final IROpCode op; + + /** 运算结果将写入的目标虚拟寄存器 */ + private final IRVirtualRegister dest; + + /** 运算的左操作数 */ + private final IRValue lhs; + + /** 运算的右操作数 */ + private final IRValue rhs; + + /** + * 构造函数,创建一个完整的二元运算指令。 + * + * @param op 运算类型(加、减、乘、除等) + * @param dest 运算结果的目标寄存器 + * @param lhs 左操作数 + * @param rhs 右操作数 + */ + public BinaryOperationInstruction(IROpCode op, IRVirtualRegister dest, IRValue lhs, IRValue rhs) { + this.op = op; + this.dest = dest; + this.lhs = lhs; + this.rhs = rhs; + } + + /** + * 获取该指令的操作符。 + * + * @return 运算类型(IROpCode) + */ + @Override + public IROpCode op() { + return op; + } + + /** + * 获取该指令的目标寄存器。 + * + * @return 运算结果将写入的虚拟寄存器 + */ + @Override + public IRVirtualRegister dest() { + return dest; + } + + /** + * 获取该指令使用的操作数。 + * + * @return 一个包含左、右操作数的列表 + */ + @Override + public List operands() { + return List.of(lhs, rhs); + } + + /** + * 转换为字符串格式,便于调试与打印。 + * 例:v1 = ADD_I32 v2, v3 + * + * @return 指令的字符串表示形式 + */ + @Override + public String toString() { + return dest + " = " + op + " " + lhs + ", " + rhs; + } + + /** + * 接受访问者对象,实现访问者模式分发逻辑。 + * + * @param visitor 实现 IRVisitor 的访问者对象 + */ + @Override + public void accept(IRVisitor visitor) { + visitor.visit(this); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/instruction/CallInstruction.java b/src/main/java/org/jcnc/snow/compiler/ir/instruction/CallInstruction.java new file mode 100644 index 0000000..7e9f152 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/instruction/CallInstruction.java @@ -0,0 +1,64 @@ +package org.jcnc.snow.compiler.ir.instruction; + +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.core.IROpCode; +import org.jcnc.snow.compiler.ir.core.IRVisitor; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; +import org.jcnc.snow.compiler.ir.core.IRValue; + +import java.util.ArrayList; +import java.util.List; + +/** + * CallInstruction —— 表示一次函数调用,格式:dest = CALL functionName, arg1, arg2, ... + */ +public class CallInstruction extends IRInstruction { + private final IRVirtualRegister dest; + private final String functionName; + private final List arguments; + + public CallInstruction(IRVirtualRegister dest, String functionName, List args) { + this.dest = dest; + this.functionName = functionName; + this.arguments = List.copyOf(args); + } + + @Override + public IROpCode op() { + return IROpCode.CALL; + } + + @Override + public List operands() { + List ops = new ArrayList<>(); + ops.add(dest); + ops.addAll(arguments); + return ops; + } + + public IRVirtualRegister getDest() { + return dest; + } + + public String getFunctionName() { + return functionName; + } + + public List getArguments() { + return arguments; + } + + @Override + public void accept(IRVisitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(dest + " = CALL " + functionName); + for (IRValue arg : arguments) { + sb.append(", ").append(arg); + } + return sb.toString(); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/instruction/IRAddInstruction.java b/src/main/java/org/jcnc/snow/compiler/ir/instruction/IRAddInstruction.java new file mode 100644 index 0000000..f35cfa3 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/instruction/IRAddInstruction.java @@ -0,0 +1,92 @@ +package org.jcnc.snow.compiler.ir.instruction; + +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.core.IROpCode; +import org.jcnc.snow.compiler.ir.core.IRValue; +import org.jcnc.snow.compiler.ir.core.IRVisitor; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.List; + +/** + * IRAddInstruction —— 表示一个加法指令,形如:dest = lhs + rhs + *

+ * 本类是一个具体的 IRInstruction 子类,表示将两个值相加,并将结果写入目标寄存器的操作。 + * 虽然功能与通用的 {@link BinaryOperationInstruction} 类似,但它作为更简化明确的指令实现, + * 通常用于测试或示例用途,也可为特殊优化保留独立形式。 + */ +public class IRAddInstruction extends IRInstruction { + + /** 运算结果存放的目标虚拟寄存器 */ + private final IRVirtualRegister dest; + + /** 左操作数 */ + private final IRValue lhs; + + /** 右操作数 */ + private final IRValue rhs; + + /** + * 构造函数,创建加法指令实例。 + * + * @param dest 运算结果的存储位置 + * @param lhs 加法左操作数 + * @param rhs 加法右操作数 + */ + public IRAddInstruction(IRVirtualRegister dest, IRValue lhs, IRValue rhs) { + this.dest = dest; + this.lhs = lhs; + this.rhs = rhs; + } + + /** + * 返回该指令的操作码:ADD_I32。 + * + * @return 加法操作码 + */ + @Override + public IROpCode op() { + return IROpCode.ADD_I32; + } + + /** + * 获取指令的目标寄存器。 + * + * @return 运算结果存放的虚拟寄存器 + */ + @Override + public IRVirtualRegister dest() { + return dest; + } + + /** + * 获取加法指令的两个操作数。 + * + * @return 包含左、右操作数的列表 + */ + @Override + public List operands() { + return List.of(lhs, rhs); + } + + /** + * 使用访问者处理当前加法指令。 + * + * @param visitor 实现 IRVisitor 的访问者对象 + */ + @Override + public void accept(IRVisitor visitor) { + visitor.visit(this); + } + + /** + * 返回指令的字符串形式,方便调试。 + * 例如:v1 = v2 + v3 + * + * @return 字符串表示形式 + */ + @Override + public String toString() { + return dest + " = " + lhs + " + " + rhs; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/instruction/IRCompareJumpInstruction.java b/src/main/java/org/jcnc/snow/compiler/ir/instruction/IRCompareJumpInstruction.java new file mode 100644 index 0000000..9c34401 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/instruction/IRCompareJumpInstruction.java @@ -0,0 +1,47 @@ +package org.jcnc.snow.compiler.ir.instruction; + +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.core.IROpCode; +import org.jcnc.snow.compiler.ir.core.IRVisitor; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +/** + * “比较 + 条件跳转” 复合指令: + * if ( left right ) jump targetLabel; + *

+ * 其中 cmpOp 只能是 IROpCode.CMP_* 六种比较操作码。 + */ +public final class IRCompareJumpInstruction extends IRInstruction { + + private final IROpCode cmpOp; // CMP_EQ / CMP_NE / CMP_LT / ... + private final IRVirtualRegister left, right; // 两个比较操作数 + private final String targetLabel; // 跳转目标 + + public IRCompareJumpInstruction(IROpCode cmpOp, + IRVirtualRegister left, + IRVirtualRegister right, + String targetLabel) { + this.cmpOp = cmpOp; + this.left = left; + this.right = right; + this.targetLabel = targetLabel; + } + + @Override + public IROpCode op() { + return cmpOp; + } + + public IRVirtualRegister left() { return left; } + public IRVirtualRegister right() { return right; } + public String label() { return targetLabel; } + + @Override + public String toString() { + return cmpOp + " " + left + ", " + right + " -> " + targetLabel; + } + + /** 暂无访问者实现,留空 */ + @Override + public void accept(IRVisitor visitor) { /* no-op */ } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/instruction/IRJumpInstruction.java b/src/main/java/org/jcnc/snow/compiler/ir/instruction/IRJumpInstruction.java new file mode 100644 index 0000000..51d5c9a --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/instruction/IRJumpInstruction.java @@ -0,0 +1,66 @@ +package org.jcnc.snow.compiler.ir.instruction; + +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.core.IROpCode; +import org.jcnc.snow.compiler.ir.core.IRVisitor; + +/** + * IRJumpInstruction —— 表示一个无条件跳转(jump)的 IR 指令。 + *

+ * 该指令用于控制流结构中,实现无条件跳转到指定标签(label)。 + * 是 if-else、循环、函数跳转等高级语言结构翻译到中间表示的重要组成部分。 + */ +public class IRJumpInstruction extends IRInstruction { + + /** 跳转目标的标签名 */ + private final String label; + + /** + * 构造函数,创建跳转指令。 + * + * @param label 跳转目标标签的名称 + */ + public IRJumpInstruction(String label) { + this.label = label; + } + + /** + * 获取该指令对应的操作码:JUMP。 + * + * @return IROpCode.JUMP + */ + @Override + public IROpCode op() { + return IROpCode.JUMP; + } + + /** + * 获取跳转目标标签名。 + * + * @return 标签名称字符串 + */ + public String label() { + return label; + } + + /** + * 接受访问者,用于访问者模式处理。 + * + * @param visitor 实现 IRVisitor 的访问者实例 + */ + @Override + public void accept(IRVisitor visitor) { + visitor.visit(this); + } + + /** + * 将指令转为字符串形式,便于打印与调试。 + * 例如:jump L1 + * + * @return 指令的字符串表示 + */ + @Override + public String toString() { + return "jump " + label; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/instruction/IRLabelInstruction.java b/src/main/java/org/jcnc/snow/compiler/ir/instruction/IRLabelInstruction.java new file mode 100644 index 0000000..8198b44 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/instruction/IRLabelInstruction.java @@ -0,0 +1,39 @@ +package org.jcnc.snow.compiler.ir.instruction; + +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.core.IROpCode; +import org.jcnc.snow.compiler.ir.core.IRVisitor; + +/** + * 在 IR 中标记一个代码位置,用于 Jump / 条件跳转目标。 + *

+ * 生成到 VM 时并不会真正发出可执行指令, + * 仅在 {@code VMCodeGenerator} 内部被用来计算真实地址。 + */ +public final class IRLabelInstruction extends IRInstruction { + private final String name; + + public IRLabelInstruction(String name) { + this.name = name; + } + + @Override + public IROpCode op() { + return IROpCode.LABEL; + } + + public String name() { + return name; + } + + @Override + public String toString() { + return name + ":"; + } + + /** 目前尚未对 Label 做访问者处理,空实现即可 */ + @Override + public void accept(IRVisitor visitor) { + /* no-op */ + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/instruction/IRReturnInstruction.java b/src/main/java/org/jcnc/snow/compiler/ir/instruction/IRReturnInstruction.java new file mode 100644 index 0000000..edf5034 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/instruction/IRReturnInstruction.java @@ -0,0 +1,71 @@ +package org.jcnc.snow.compiler.ir.instruction; + +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.core.IROpCode; +import org.jcnc.snow.compiler.ir.core.IRValue; +import org.jcnc.snow.compiler.ir.core.IRVisitor; + +import java.util.List; + +/** + * IRReturnInstruction —— 表示一个带返回值的返回(ret)指令。 + *

+ * 此指令用于函数结束时将某个值作为返回结果返回给调用者。 + * 返回值可以是常量、寄存器或表达式的结果。 + * 若不返回值,也可以扩展为 null 代表 void 函数。 + */ +public class IRReturnInstruction extends IRInstruction { + + /** 要返回的值,可以是常量、虚拟寄存器等 */ + private final IRValue returnValue; + + /** + * 构造函数,创建返回指令。 + * + * @param returnValue 函数的返回值 + */ + public IRReturnInstruction(IRValue returnValue) { + this.returnValue = returnValue; + } + + /** + * 获取该指令的操作码:RET。 + * + * @return IROpCode.RET,表示返回操作 + */ + @Override + public IROpCode op() { + return IROpCode.RET; + } + + /** + * 返回该指令的操作数列表(仅包含返回值)。 + * + * @return 含一个元素的列表,即返回值 + */ + @Override + public List operands() { + return List.of(returnValue); + } + + /** + * 接受访问者处理该指令,适用于访问者模式。 + * + * @param visitor 实现了 IRVisitor 的访问者对象 + */ + @Override + public void accept(IRVisitor visitor) { + visitor.visit(this); + } + + /** + * 转换为字符串形式,便于调试与打印。 + * 示例:ret v1 + * + * @return 字符串形式的返回指令 + */ + @Override + public String toString() { + return "ret " + returnValue; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/instruction/LoadConstInstruction.java b/src/main/java/org/jcnc/snow/compiler/ir/instruction/LoadConstInstruction.java new file mode 100644 index 0000000..0b9a73e --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/instruction/LoadConstInstruction.java @@ -0,0 +1,87 @@ +package org.jcnc.snow.compiler.ir.instruction; + +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.core.IROpCode; +import org.jcnc.snow.compiler.ir.core.IRValue; +import org.jcnc.snow.compiler.ir.core.IRVisitor; +import org.jcnc.snow.compiler.ir.value.IRConstant; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.List; + +/** + * LoadConstInstruction —— 表示一个常量加载指令,格式为:dest = CONST k + *

+ * 该指令的功能是将一个常量(字面量或编译期已知值)加载到一个虚拟寄存器中, + * 供后续指令使用。例如,在表达式计算、参数传递、初始化等场景中常用。 + */ +public final class LoadConstInstruction extends IRInstruction { + + /** 要加载的常量值,类型为 IRConstant */ + private final IRConstant k; + + /** 存放常量结果的目标虚拟寄存器 */ + private final IRVirtualRegister dest; + + /** + * 构造函数,创建常量加载指令。 + * + * @param dest 存放常量的目标虚拟寄存器 + * @param k 要加载的常量值 + */ + public LoadConstInstruction(IRVirtualRegister dest, IRConstant k) { + this.dest = dest; + this.k = k; + } + + /** + * 获取该指令的操作码,固定为 CONST。 + * + * @return IROpCode.CONST + */ + @Override + public IROpCode op() { + return IROpCode.CONST; + } + + /** + * 获取指令的目标虚拟寄存器。 + * + * @return 用于存放常量的寄存器 + */ + @Override + public IRVirtualRegister dest() { + return dest; + } + + /** + * 获取该指令的操作数(仅包含要加载的常量)。 + * + * @return 含一个元素(k)的操作数列表 + */ + @Override + public List operands() { + return List.of(k); + } + + /** + * 返回该指令的字符串形式,便于调试或打印。 + * 例如:v1 = CONST 42 + * + * @return 指令的字符串表示 + */ + @Override + public String toString() { + return dest + " = CONST " + k; + } + + /** + * 接受访问者模式处理。 + * + * @param visitor 实现 IRVisitor 的访问者对象 + */ + @Override + public void accept(IRVisitor visitor) { + visitor.visit(this); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/instruction/ReturnInstruction.java b/src/main/java/org/jcnc/snow/compiler/ir/instruction/ReturnInstruction.java new file mode 100644 index 0000000..759c3ff --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/instruction/ReturnInstruction.java @@ -0,0 +1,90 @@ +package org.jcnc.snow.compiler.ir.instruction; + +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.core.IROpCode; +import org.jcnc.snow.compiler.ir.core.IRValue; +import org.jcnc.snow.compiler.ir.core.IRVisitor; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.List; + +/** + * ReturnInstruction —— 表示函数返回指令,格式:RET 或 RET + *

+ * 此类用于描述函数执行完毕后的返回操作。支持两种返回形式: + * - 无返回值(void):生成无参的 RET 指令 + * - 有返回值:将指定虚拟寄存器中的值返回给调用者 + *

+ * 与 {@link IRReturnInstruction} 类似,但更通用,适配多种函数返回风格。 + */ +public final class ReturnInstruction extends IRInstruction { + + /** + * 返回值所在的虚拟寄存器。 + * 如果为 null,则代表函数无返回值(即 void)。 + */ + private final IRVirtualRegister value; + + /** + * 构造函数,创建返回指令实例。 + * + * @param value 若函数有返回值,传入对应虚拟寄存器; + * 若为 void 函数,则传 null。 + */ + public ReturnInstruction(IRVirtualRegister value) { + this.value = value; + } + + /** + * 返回该指令的操作码类型:RET。 + * + * @return IROpCode.RET + */ + @Override + public IROpCode op() { + return IROpCode.RET; + } + + /** + * 获取该指令的操作数。 + * 如果为 void 返回,则返回空列表; + * 否则返回一个仅包含返回寄存器的列表。 + * + * @return 操作数列表 + */ + @Override + public List operands() { + return value == null ? List.of() : List.of(value); + } + + /** + * 获取返回值所在的虚拟寄存器(如有)。 + * + * @return 返回值寄存器,或 null(表示 void) + */ + public IRVirtualRegister value() { + return value; + } + + /** + * 转换为字符串形式,便于调试与输出。 + * - 无返回值:RET + * - 有返回值:RET v1 + * + * @return 字符串表示的返回指令 + */ + @Override + public String toString() { + return value == null ? "RET" : "RET " + value; + } + + /** + * 接受访问者对象,实现访问者模式分发。 + * + * @param visitor 实现 IRVisitor 的访问者 + */ + @Override + public void accept(IRVisitor visitor) { + visitor.visit(this); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/instruction/UnaryOperationInstruction.java b/src/main/java/org/jcnc/snow/compiler/ir/instruction/UnaryOperationInstruction.java new file mode 100644 index 0000000..25b9e96 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/instruction/UnaryOperationInstruction.java @@ -0,0 +1,97 @@ +package org.jcnc.snow.compiler.ir.instruction; + +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.core.IROpCode; +import org.jcnc.snow.compiler.ir.core.IRValue; +import org.jcnc.snow.compiler.ir.core.IRVisitor; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; + +import java.util.List; + +/** + * UnaryOperationInstruction —— 表示一个一元运算指令,格式:dest = OP val + *

+ * 用于对单个操作数 val 执行指定的一元运算 OP(例如取负 NEG), + * 并将结果写入目标虚拟寄存器 dest。 + *

+ * 支持的操作由 {@link IROpCode} 定义,目前常见的一元操作包括: + *

    + *
  • NEG_I32 —— 整数取负:dest = -val
  • + *
  • (可扩展)逻辑非、按位非等
  • + *
+ */ +public final class UnaryOperationInstruction extends IRInstruction { + + /** 一元运算操作符(如 NEG_I32) */ + private final IROpCode op; + + /** 运算结果写入的目标虚拟寄存器 */ + private final IRVirtualRegister dest; + + /** 被操作的值(唯一操作数) */ + private final IRValue val; + + /** + * 构造函数,创建一元运算指令。 + * + * @param op 一元运算操作符 + * @param dest 运算结果目标寄存器 + * @param val 参与运算的操作数 + */ + public UnaryOperationInstruction(IROpCode op, IRVirtualRegister dest, IRValue val) { + this.op = op; + this.dest = dest; + this.val = val; + } + + /** + * 获取该指令的操作码。 + * + * @return 一元运算的操作码(如 NEG_I32) + */ + @Override + public IROpCode op() { + return op; + } + + /** + * 获取该指令的目标寄存器。 + * + * @return 运算结果的目标寄存器 + */ + @Override + public IRVirtualRegister dest() { + return dest; + } + + /** + * 获取指令的操作数(仅一个)。 + * + * @return 单元素列表,仅包含 val + */ + @Override + public List operands() { + return List.of(val); + } + + /** + * 将该指令格式化为字符串,便于打印与调试。 + * 形式:dest = OP val,例如:v1 = NEG v2 + * + * @return 字符串形式的指令 + */ + @Override + public String toString() { + return dest + " = " + op + " " + val; + } + + /** + * 接受访问者访问该指令,实现访问者模式。 + * + * @param visitor 访问者实例 + */ + @Override + public void accept(IRVisitor visitor) { + visitor.visit(this); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/utils/ExpressionUtils.java b/src/main/java/org/jcnc/snow/compiler/ir/utils/ExpressionUtils.java new file mode 100644 index 0000000..cc1e426 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/utils/ExpressionUtils.java @@ -0,0 +1,202 @@ +package org.jcnc.snow.compiler.ir.utils; + +import org.jcnc.snow.compiler.ir.core.IROpCode; +import org.jcnc.snow.compiler.ir.core.IROpCodeMappings; +import org.jcnc.snow.compiler.ir.value.IRConstant; +import org.jcnc.snow.compiler.parser.ast.BinaryExpressionNode; +import org.jcnc.snow.compiler.parser.ast.NumberLiteralNode; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; + +import java.util.Map; + +/** + * 表达式分析与运算符辅助工具类。 + *

+ * 主要功能包括: + *

    + *
  • 字面量常量的解析与类型推断
  • + *
  • 自动匹配操作码
  • + *
  • 表达式类型合并与判定
  • + *
+ */ +public class ExpressionUtils { + + /** 用于存储默认的类型后缀(如函数返回类型),线程隔离。 */ + private static final ThreadLocal DEFAULT_SUFFIX = + ThreadLocal.withInitial(() -> '\0'); + + /** + * 在进入函数 IR 构建前设置默认的类型后缀(比如函数返回类型)。 + * @param suffix 默认后缀字符,如 'i', 'l', 'f', 'd' 等 + */ + public static void setDefaultSuffix(char suffix) { + DEFAULT_SUFFIX.set(suffix); + } + + /** + * 在函数 IR 构建结束后清除默认后缀,避免影响后续分析。 + */ + public static void clearDefaultSuffix() { + DEFAULT_SUFFIX.set('\0'); + } + + /** + * 解析整数字面量字符串,自动去除类型后缀(b/s/l/f/d/B/S/L/F/D),并转换为 int。 + * + * @param literal 字面量字符串,如 "123", "123l", "42B" + * @return 解析得到的 int 整数 + */ + public static int parseIntSafely(String literal) { + // 去掉类型后缀,只保留数字部分 + String digits = literal.replaceAll("[bslfdBSDLF]$", ""); + return Integer.parseInt(digits); + } + + /** + * 根据数字字面量字符串自动判断类型,生成对应类型的 IRConstant。 + * 支持 b/s/l/f/d 类型后缀与浮点格式,自动分配合适类型。 + * + * @param value 字面量字符串(如 "1", "2l", "3.14f", "5D") + * @return 生成的 IRConstant 对象,包含正确类型 + */ + public static IRConstant buildNumberConstant(String value) { + char suffix = value.isEmpty() ? '\0' : Character.toLowerCase(value.charAt(value.length() - 1)); + String digits = switch (suffix) { + case 'b','s','l','f','d' -> value.substring(0, value.length() - 1); + default -> value; + }; + // 根据类型后缀或数值格式创建常量 + return switch (suffix) { + case 'b' -> new IRConstant(Byte.parseByte(digits)); + case 's' -> new IRConstant(Short.parseShort(digits)); + case 'l' -> new IRConstant(Long.parseLong(digits)); + case 'f' -> new IRConstant(Float.parseFloat(digits)); + case 'd' -> new IRConstant(Double.parseDouble(digits)); + default -> looksLikeFloat(digits) + ? new IRConstant(Double.parseDouble(digits)) + : new IRConstant(Integer.parseInt(digits)); + }; + } + + /* =================== 类型推断与操作符匹配 =================== */ + + /** + * 递归推断单个表达式节点的类型后缀(b/s/l/f/d)。 + * 对于二元表达式,将左右两侧的类型自动提升合并,遵循优先级顺序:d > f > l > s > b > '\0'。 + * + * @param node 表达式节点 + * @return 类型后缀字符,b/s/l/f/d 或 '\0' + */ + private static char typeChar(ExpressionNode node) { + // 字面量节点,直接判断最后一位 + if (node instanceof NumberLiteralNode(String value)) { + char last = Character.toLowerCase(value.charAt(value.length() - 1)); + return switch (last) { + case 'b', 's', 'l', 'f', 'd' -> last; + default -> looksLikeFloat(value) ? 'd' : '\0'; + }; + } + // 二元表达式,递归判断左右子节点 + if (node instanceof BinaryExpressionNode bin) { + char l = typeChar(bin.left()); + char r = typeChar(bin.right()); + return maxTypeChar(l, r); + } + // 其他情况(如变量节点),暂不处理,默认返回 '\0' + return '\0'; + } + + /** + * 推断两个表达式节点合并后的类型后缀。 + * 返回优先级更高的类型后缀字符。 + * + * @param left 左表达式节点 + * @param right 右表达式节点 + * @return 合并后类型的后缀字符 + */ + public static char resolveSuffix(ExpressionNode left, ExpressionNode right) { + return maxTypeChar(typeChar(left), typeChar(right)); + } + + /** + * 在两个类型后缀中选取精度更高的一个。 + * 优先级依次为:d > f > l > s > b > '\0' + * + * @param l 左类型后缀 + * @param r 右类型后缀 + * @return 更高优先级的类型后缀字符 + */ + private static char maxTypeChar(char l, char r) { + if (l == 'd' || r == 'd') return 'd'; + if (l == 'f' || r == 'f') return 'f'; + if (l == 'l' || r == 'l') return 'l'; + if (l == 's' || r == 's') return 's'; + if (l == 'b' || r == 'b') return 'b'; + return '\0'; + } + + /** + * 判断给定字符串是否为比较运算符(如 >, <, == 等)。 + * + * @param op 操作符字符串 + * @return 如果是比较操作符返回 true,否则返回 false + */ + public static boolean isComparisonOperator(String op) { + return IROpCodeMappings.CMP.containsKey(op); + } + + /** + * 获取比较操作符对应的中间表示操作码(IROpCode)。 + * + * @param op 比较操作符字符串 + * @return 对应的 IROpCode,如果不存在则返回 null + */ + public static IROpCode cmpOp(String op) { + return IROpCodeMappings.CMP.get(op); + } + + /** + * 根据操作符和两侧表达式自动选择正确的 IROpCode。 + * 首先根据参与表达式类型推断后缀,若无法推断则回退到函数默认类型, + * 还无法推断则默认使用 i32(32位整型)。 + * + * @param op 操作符字符串,如 "+" + * @param left 左侧表达式节点 + * @param right 右侧表达式节点 + * @return 匹配的 IROpCode,如果不存在则返回 null + */ + public static IROpCode resolveOpCode(String op, + ExpressionNode left, + ExpressionNode right) { + + /* 1) 尝试从参与者常量字面量推断 */ + char suffix = resolveSuffix(left, right); + + /* 2) 若无法推断,退回到函数返回类型(DEFAULT_SUFFIX) */ + if (suffix == '\0') { + suffix = DEFAULT_SUFFIX.get(); + } + + /* 3) 再次失败则默认为 i32 */ + Map table = switch (suffix) { + case 'b' -> IROpCodeMappings.OP_I8; + case 's' -> IROpCodeMappings.OP_I16; + case 'l' -> IROpCodeMappings.OP_L64; + case 'f' -> IROpCodeMappings.OP_F32; + case 'd' -> IROpCodeMappings.OP_D64; + default -> IROpCodeMappings.OP_I32; + }; + + return table.get(op); + } + + /** + * 判断字符串是否为浮点数形式(即包含小数点或科学计数法 e/E)。 + * + * @param digits 数字字符串 + * @return 如果看起来像浮点数则返回 true,否则返回 false + */ + private static boolean looksLikeFloat(String digits) { + return digits.indexOf('.') >= 0 || digits.indexOf('e') >= 0 || digits.indexOf('E') >= 0; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/utils/IROpCodeUtils.java b/src/main/java/org/jcnc/snow/compiler/ir/utils/IROpCodeUtils.java new file mode 100644 index 0000000..dee9733 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/utils/IROpCodeUtils.java @@ -0,0 +1,26 @@ +package org.jcnc.snow.compiler.ir.utils; + +import org.jcnc.snow.compiler.ir.core.IROpCode; + +import java.util.Map; + +/** + * IR 操作码辅助工具。 + */ +public class IROpCodeUtils { + private static final Map INVERT = Map.of( + IROpCode.CMP_EQ, IROpCode.CMP_NE, + IROpCode.CMP_NE, IROpCode.CMP_EQ, + IROpCode.CMP_LT, IROpCode.CMP_GE, + IROpCode.CMP_GE, IROpCode.CMP_LT, + IROpCode.CMP_GT, IROpCode.CMP_LE, + IROpCode.CMP_LE, IROpCode.CMP_GT + ); + + /** + * 获取给定比较操作的“相反操作码”。 + */ + public static IROpCode invert(IROpCode op) { + return INVERT.get(op); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/value/IRConstant.java b/src/main/java/org/jcnc/snow/compiler/ir/value/IRConstant.java new file mode 100644 index 0000000..1af295c --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/value/IRConstant.java @@ -0,0 +1,31 @@ +package org.jcnc.snow.compiler.ir.value; + +import org.jcnc.snow.compiler.ir.core.IRValue; + +/** + * IRConstant —— 表示中间表示(IR)系统中的常量值。 + *

+ * 常量用于表示在编译期间已知的不可变值,例如字面整数、浮点数、布尔值或字符串。 + * 与 {@link IRVirtualRegister} 不同,常量不需要通过寄存器存储, + * 可直接作为 IR 指令的操作数使用。 + *

+ * 典型应用: + * - 加载常量指令:v1 = CONST 42 + * - 计算表达式:v2 = ADD v1, 100 + */ +public record IRConstant(Object value) implements IRValue { + + /** + * 将常量值转换为字符串,用于打印 IR 指令或调试输出。 + *

+ * 例如: + * - 整数常量:42 + * - 字符串常量:"hello" + * + * @return 常量的字符串表示 + */ + @Override + public String toString() { + return value.toString(); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/value/IRLabel.java b/src/main/java/org/jcnc/snow/compiler/ir/value/IRLabel.java new file mode 100644 index 0000000..f8ad112 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/value/IRLabel.java @@ -0,0 +1,29 @@ +package org.jcnc.snow.compiler.ir.value; + +import org.jcnc.snow.compiler.ir.core.IRValue; + +/** + * IRLabel —— 表示中间表示(IR)系统中的跳转目标标签。 + *

+ * 标签用于控制流指令(如 JUMP等)中, + * 作为程序执行跳转的目的地,是 IR 控制流图(CFG)中的基本构建块。 + *

+ * 每个标签由一个唯一的名称(String name)标识, + * 可用于生成目标代码中的符号标签或跳转地址。 + *

+ * 该类实现了 {@link IRValue} 接口,因此也可被视为指令操作数, + * 在某些 IRInstruction 中以参数形式出现(如条件跳转目标)。 + */ +public record IRLabel(String name) implements IRValue { + + /** + * 返回标签的字符串形式,便于打印或调试。 + * 通常表示为带冒号的形式,例如 "L1:"。 + * + * @return 格式化后的标签字符串 + */ + @Override + public String toString() { + return name + ":"; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/ir/value/IRVirtualRegister.java b/src/main/java/org/jcnc/snow/compiler/ir/value/IRVirtualRegister.java new file mode 100644 index 0000000..f4bdaa3 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/value/IRVirtualRegister.java @@ -0,0 +1,34 @@ +package org.jcnc.snow.compiler.ir.value; + +import org.jcnc.snow.compiler.ir.core.IRValue; + +/** + * IRVirtualRegister —— 表示一个静态单赋值(SSA)形式的虚拟寄存器。 + *

+ * 在 IR 系统中,虚拟寄存器用于存储每个中间计算结果,是 SSA(Static Single Assignment)形式的核心。 + * 每个虚拟寄存器在程序中只被赋值一次,其值来源于一条明确的指令输出。 + *

+ * 特点: + *

    + *
  • 每个寄存器有唯一编号 {@code id},由 {@code IRFunction.newRegister()} 自动生成
  • + *
  • 实现 {@link IRValue} 接口,可作为 IRInstruction 的操作数
  • + *
  • 具备良好的打印与调试格式:%id
  • + *
+ * + * 适用于表达式求值、参数传递、函数返回值、临时变量等所有中间值场景。 + * + * @param id 寄存器的唯一编号,通常从 0 开始递增 + */ +public record IRVirtualRegister(int id) implements IRValue { + + /** + * 将虚拟寄存器转换为字符串格式,方便输出和调试。 + * 格式为:%,例如 %3 表示编号为 3 的虚拟寄存器。 + * + * @return 格式化的字符串表示 + */ + @Override + public String toString() { + return "%" + id; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/base/TokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/base/TokenScanner.java new file mode 100644 index 0000000..a99fd85 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/base/TokenScanner.java @@ -0,0 +1,46 @@ +package org.jcnc.snow.compiler.lexer.base; + +import org.jcnc.snow.compiler.lexer.core.LexerContext; +import org.jcnc.snow.compiler.lexer.token.Token; + +import java.util.List; + +/** + * {@code TokenScanner} 接口定义了所有词法扫描器的统一行为规范。 + *

+ * 编译器前端中的词法分析阶段将源代码字符流解析为语义上有意义的记号(token), + * 每种类型的记号(如标识符、数字、字符串、符号等)应有对应的 {@code TokenScanner} 实现类。 + * 词法分析器根据当前输入字符判断并分派给能处理该字符的扫描器进行处理。 + *

+ *

+ * 实现类通常会结合 {@link LexerContext} 提供的流访问与状态接口, + * 完成一个完整 Token 的提取,并将其添加到结果集中。 + *

+ */ +public interface TokenScanner { + + /** + * 判断当前字符是否可以由该扫描器处理。 + *

+ * 词法分析器会按顺序查询已注册的 {@code TokenScanner} 实例, + * 使用该方法决定当前字符是否可由某个扫描器识别与处理。 + *

+ * + * @param c 当前读取的字符 + * @param ctx 当前词法分析上下文,提供字符流和辅助状态 + * @return 若该扫描器可处理当前字符,则返回 {@code true},否则返回 {@code false} + */ + boolean canHandle(char c, LexerContext ctx); + + /** + * 处理以当前字符为起始的 token,并将扫描结果添加至 tokens 列表中。 + *

+ * 扫描器需消费一定数量的字符,构建合法的 {@link Token} 实例, + * 并调用 {@code tokens.add(...)} 添加至结果集中。 + *

+ * + * @param ctx 当前词法上下文 + * @param tokens 存储扫描结果的 token 列表 + */ + void handle(LexerContext ctx, List tokens); +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerContext.java b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerContext.java new file mode 100644 index 0000000..dbfe62f --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerContext.java @@ -0,0 +1,125 @@ +package org.jcnc.snow.compiler.lexer.core; + +import org.jcnc.snow.compiler.lexer.base.TokenScanner; + +/** + * {@code LexerContext} 是词法分析阶段的上下文状态管理器。 + *

+ * 该类提供对源代码字符流的读取访问,追踪当前行号与列号, + * 并支持字符匹配、回看与指针推进等操作,是 {@link TokenScanner} 实现进行词法识别的重要支撑工具。 + *

+ *

+ * 所有源代码输入在构造时统一将 Windows 风格的换行符(\r\n)转换为 Unix 风格(\n), + * 保证换行行为一致性。 + *

+ */ +public class LexerContext { + + /** 源代码字符串,换行符已标准化为 \n */ + private final String source; + + /** 当前扫描位置(从 0 开始的偏移) */ + private int pos = 0; + + /** 当前行号,从 1 开始 */ + private int line = 1; + + /** 当前列号,从 1 开始 */ + private int col = 1; + + /** 上一个字符对应的列号(用于位置精确记录) */ + private int lastCol = 1; + + /** + * 构造一个新的 {@code LexerContext} 实例,并标准化换行符。 + * + * @param source 原始源代码字符串 + */ + public LexerContext(String source) { + this.source = source.replace("\r\n", "\n"); + } + + /** + * 判断是否已读取到源代码末尾。 + * + * @return 若已结束,返回 {@code true};否则返回 {@code false} + */ + public boolean isAtEnd() { + return pos >= source.length(); + } + + /** + * 消费当前字符并前进一个位置,自动更新行列信息。 + * + * @return 当前字符,若已结束则返回空字符('\0') + */ + public char advance() { + if (isAtEnd()) return '\0'; + char c = source.charAt(pos++); + lastCol = col; + if (c == '\n') { + line++; + col = 1; + } else { + col++; + } + return c; + } + + /** + * 查看当前位置的字符,但不前进。 + * + * @return 当前字符,若结束则返回空字符 + */ + public char peek() { + return isAtEnd() ? '\0' : source.charAt(pos); + } + + /** + * 查看下一个字符,但不改变位置。 + * + * @return 下一个字符,若结束则返回空字符 + */ + public char peekNext() { + return pos + 1 >= source.length() ? '\0' : source.charAt(pos + 1); + } + + /** + * 若当前字符与期望字符相同,则前进并返回 {@code true},否则不动并返回 {@code false}。 + * + * @param expected 期待匹配的字符 + * @return 是否匹配成功并消费 + */ + public boolean match(char expected) { + if (isAtEnd() || source.charAt(pos) != expected) return false; + advance(); + return true; + } + + /** + * 获取当前位置的行号。 + * + * @return 当前行号(从 1 开始) + */ + public int getLine() { + return line; + } + + /** + * 获取当前位置的列号。 + * + * @return 当前列号(从 1 开始) + */ + public int getCol() { + return col; + } + + /** + * 获取上一个字符所在的列号。 + * + * @return 上一个字符对应的列位置 + */ + public int getLastCol() { + return lastCol; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java new file mode 100644 index 0000000..819fa07 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java @@ -0,0 +1,111 @@ +package org.jcnc.snow.compiler.lexer.core; + +import org.jcnc.snow.compiler.lexer.base.TokenScanner; +import org.jcnc.snow.compiler.lexer.scanners.*; +import org.jcnc.snow.compiler.lexer.token.Token; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@code LexerEngine} 是编译器前端的词法分析器核心实现。 + *

+ * 负责将源代码字符串按顺序扫描并转换为一系列 {@link Token} 实例, + * 每个 Token 表示语法上可识别的最小单位(如标识符、关键字、常量、运算符等)。 + *

+ * 分析流程通过注册多个 {@link TokenScanner} 扫描器实现类型识别, + * 并由 {@link LexerContext} 提供字符流与位置信息支持。 + * 支持文件名传递,遇到非法字符时会以“文件名:行:列:错误信息”输出简洁诊断。 + *

+ */ +public class LexerEngine { + /** + * 扫描生成的 Token 序列(包含文件结束符 EOF) + */ + private final List tokens = new ArrayList<>(); + + /** + * 词法上下文,提供字符流读取与位置信息 + */ + private final LexerContext context; + + /** + * Token 扫描器集合,按优先级顺序组织,用于识别不同类别的 Token + */ + private final List scanners; + + /** + * 构造词法分析器(假定输入源自标准输入,文件名默认为 ) + * + * @param source 源代码文本 + */ + public LexerEngine(String source) { + this(source, ""); + } + + /** + * 构造词法分析器,并指定源文件名(用于诊断信息)。 + * 构造时立即进行全量扫描。 + * + * @param source 源代码文本 + * @param sourceName 文件名或来源描述(如"main.snow") + */ + public LexerEngine(String source, String sourceName) { + this.context = new LexerContext(source); + this.scanners = List.of( + new WhitespaceTokenScanner(), // 跳过空格、制表符等 + new NewlineTokenScanner(), // 处理换行符,生成 NEWLINE Token + new CommentTokenScanner(), // 处理单行/多行注释 + new NumberTokenScanner(), // 识别整数与浮点数字面量 + new IdentifierTokenScanner(), // 识别标识符和关键字 + new StringTokenScanner(), // 处理字符串常量 + new OperatorTokenScanner(), // 识别运算符 + new SymbolTokenScanner(), // 识别括号、分号等符号 + new UnknownTokenScanner() // 捕捉无法识别的字符,最后兜底 + ); + + // 主扫描流程,遇到非法字符立即输出错误并终止进程 + try { + scanAllTokens(); + } catch (LexicalException le) { + // 输出:文件名:行:列: 错误信息,简洁明了 + System.err.printf( + "%s:%d:%d: %s%n", + sourceName, + le.getLine(), // 获取出错行号 + le.getColumn(), // 获取出错列号 + le.getMessage() // 错误描述 + ); + System.exit(65); // 65 = EX_DATAERR,标准数据错误退出码 + } + } + + /** + * 主扫描循环,将源代码转为 Token 序列 + * 依次尝试每个扫描器,直到找到可处理当前字符的扫描器为止 + * 扫描到结尾后补充 EOF Token + */ + private void scanAllTokens() { + while (!context.isAtEnd()) { + char currentChar = context.peek(); + // 依次查找能处理当前字符的扫描器 + for (TokenScanner scanner : scanners) { + if (scanner.canHandle(currentChar, context)) { + scanner.handle(context, tokens); + break; // 已处理,跳到下一个字符 + } + } + } + // 末尾补一个 EOF 标记 + tokens.add(Token.eof(context.getLine())); + } + + /** + * 获取全部 Token(包含 EOF),返回只读列表 + * + * @return 词法分析结果 Token 列表 + */ + public List getAllTokens() { + return List.copyOf(tokens); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexicalException.java b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexicalException.java new file mode 100644 index 0000000..96cfc0c --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexicalException.java @@ -0,0 +1,54 @@ +package org.jcnc.snow.compiler.lexer.core; + +/** + * 词法异常(LexicalException)。 + *

+ * 当 {@link LexerEngine} 在扫描过程中遇到 + * 非法或无法识别的字符序列时抛出该异常。 + *

    + *
  • 异常消息仅包含一行简明错误信息(包含行号与列号);
  • + *
  • 完全禁止 Java 堆栈信息输出,使命令行输出保持整洁。
  • + *
+ *
+ * 例:
+ *     main.s:2:19: Lexical error: Illegal character sequence '@' at 2:19
+ * 
+ */ +public class LexicalException extends RuntimeException { + /** 错误发生的行号(从1开始) */ + private final int line; + /** 错误发生的列号(从1开始) */ + private final int column; + + /** + * 构造词法异常 + * @param reason 错误原因(如:非法字符描述) + * @param line 出错行号 + * @param column 出错列号 + */ + public LexicalException(String reason, int line, int column) { + // 构造出错消息,并禁止异常堆栈打印 + super(String.format("Lexical error: %s at %d:%d", reason, line, column), + null, false, false); + this.line = line; + this.column = column; + } + + /** + * 屏蔽异常堆栈填充(始终不打印堆栈信息) + */ + @Override + public synchronized Throwable fillInStackTrace() { return this; } + + /** + * 获取出错的行号 + * @return 行号 + */ + public int getLine() { return line; } + + /** + * 获取出错的列号 + * @return 列号 + */ + public int getColumn() { return column; } +} diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/doc/README.md b/src/main/java/org/jcnc/snow/compiler/lexer/doc/README.md new file mode 100644 index 0000000..a1c0a54 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/doc/README.md @@ -0,0 +1,38 @@ +# Snow Compiler - Lexer 模块 + +> Snow 编译器的前端模块 —— 负责将源代码扫描并生成 Token 流 + +## 项目简介 + +**Lexer** 是 [Snow 编译器]() 项目的子模块,承担编译器前端的词法分析阶段任务。 +它负责将源代码输入,根据 Snow 语言的语法规则,识别并划分为有意义的 Token 流,为后续语法解析(Parser)提供标准输入。 + +该模块采用模块化设计,每类 Token 类型(如标识符、运算符、数字、字符串等)由独立的扫描器(Scanner)负责识别,提高了扩展性和可维护性。 + +## 核心功能 + +- **词法分析引擎**:从源代码提取标准化 Token 流 +- **模块化扫描器体系**:按需扩展不同类型的 Token 识别 +- **灵活的上下文管理**:跟踪源代码位置,支持错误处理 +- **统一的 Token 工厂**:集中创建 Token 实例 +- **工具支持**:Token 打印与调试辅助 + +## 模块结构 + +``` +lexer/ + ├── base/ // 公共基础接口 + ├── core/ // 词法分析核心引擎与上下文 + ├── scanners/ // 各类 Token 扫描器 + ├── token/ // Token 定义、工厂与类型 + ├── utils/ // 辅助工具(如 Token 打印) + └── doc/ // 项目文档 +``` + +## 开发环境 + +* JDK 23 或更高版本 +* Maven 构建管理 +* 推荐 IDE:IntelliJ IDEA + +--- diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/AbstractTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/AbstractTokenScanner.java new file mode 100644 index 0000000..917b2c1 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/AbstractTokenScanner.java @@ -0,0 +1,65 @@ +package org.jcnc.snow.compiler.lexer.scanners; + +import org.jcnc.snow.compiler.lexer.core.LexerContext; +import org.jcnc.snow.compiler.lexer.base.TokenScanner; +import org.jcnc.snow.compiler.lexer.token.Token; + +import java.util.List; +import java.util.function.Predicate; + +/** + * {@code AbstractTokenScanner} 是 {@link TokenScanner} 的抽象实现, + * 封装了常用的扫描行为与模板逻辑,简化子类的实现负担。 + *

+ * 子类只需实现 {@link #scanToken(LexerContext, int, int)} 方法, + * 专注于处理具体的 Token 构造逻辑, + * 而位置信息提取、Token 添加等通用操作由本类统一完成。 + *

+ */ +public abstract class AbstractTokenScanner implements TokenScanner { + + /** + * 处理当前字符起始的 Token,附带行列信息并加入 Token 列表。 + * + * @param ctx 当前词法分析上下文 + * @param tokens 存储扫描结果的 Token 列表 + */ + @Override + public void handle(LexerContext ctx, List tokens) { + int line = ctx.getLine(); + int col = ctx.getCol(); + Token token = scanToken(ctx, line, col); + if (token != null) { + tokens.add(token); + } + } + + /** + * 抽象方法:由子类实现具体的扫描逻辑。 + *

+ * 实现应消费一定字符并根据规则构造 Token。 + * 若无需生成 Token,可返回 null。 + *

+ * + * @param ctx 当前扫描上下文 + * @param line 当前行号 + * @param col 当前列号 + * @return 构造的 Token 或 null + */ + protected abstract Token scanToken(LexerContext ctx, int line, int col); + + /** + * 工具方法:连续读取字符直到遇到不满足指定条件的字符。 + * + * @param ctx 当前词法上下文 + * @param predicate 字符匹配条件 + * @return 满足条件的字符组成的字符串 + */ + protected String readWhile(LexerContext ctx, Predicate predicate) { + StringBuilder sb = new StringBuilder(); + while (!ctx.isAtEnd() && predicate.test(ctx.peek())) { + sb.append(ctx.advance()); + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/CommentTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/CommentTokenScanner.java new file mode 100644 index 0000000..a8a3313 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/CommentTokenScanner.java @@ -0,0 +1,74 @@ +package org.jcnc.snow.compiler.lexer.scanners; + +import org.jcnc.snow.compiler.lexer.core.LexerContext; +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.lexer.token.TokenType; + +/** + * 注释扫描器:处理源代码中的注释部分,包括: + *
    + *
  • 单行注释(以 "//" 开头,直到行尾)
  • + *
  • 多行注释(以 "/*" 开头,以 "*/" 结尾)
  • + *
+ *

+ * 本扫描器会识别注释并生成 {@code TokenType.COMMENT} 类型的 Token, + * 不会丢弃注释内容,而是将完整注释文本保留在 Token 中,便于后续分析(如文档提取、保留注释等场景)。 + *

+ */ +public class CommentTokenScanner extends AbstractTokenScanner { + + /** + * 判断是否可以处理当前位置的字符。 + *

当当前位置字符为 '/' 且下一个字符为 '/' 或 '*' 时,表示可能是注释的起始。

+ * + * @param c 当前字符 + * @param ctx 当前词法上下文 + * @return 如果是注释的起始符,则返回 true + */ + @Override + public boolean canHandle(char c, LexerContext ctx) { + return c == '/' && (ctx.peekNext() == '/' || ctx.peekNext() == '*'); + } + + /** + * 实现注释的扫描逻辑。 + *

支持两种注释格式:

+ *
    + *
  • 单行注释: 以 "//" 开头,直到遇到换行符
  • + *
  • 多行注释: 以 "/*" 开头,直到遇到 "*/" 结束
  • + *
+ * + * @param ctx 词法上下文 + * @param line 当前行号(用于 Token 位置信息) + * @param col 当前列号(用于 Token 位置信息) + * @return 包含完整注释内容的 COMMENT 类型 Token + */ + @Override + protected Token scanToken(LexerContext ctx, int line, int col) { + // 消费第一个 '/' 字符 + ctx.advance(); + StringBuilder sb = new StringBuilder("/"); + + // 处理单行注释 // + if (ctx.match('/')) { + sb.append('/'); + while (!ctx.isAtEnd() && ctx.peek() != '\n') { + sb.append(ctx.advance()); + } + } + // 处理多行注释 /* ... */ + else if (ctx.match('*')) { + sb.append('*'); + while (!ctx.isAtEnd()) { + char ch = ctx.advance(); + sb.append(ch); + if (ch == '*' && ctx.peek() == '/') { + sb.append(ctx.advance()); // 消费 '/' + break; + } + } + } + + return new Token(TokenType.COMMENT, sb.toString(), line, col); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/IdentifierTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/IdentifierTokenScanner.java new file mode 100644 index 0000000..633d834 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/IdentifierTokenScanner.java @@ -0,0 +1,48 @@ +package org.jcnc.snow.compiler.lexer.scanners; + +import org.jcnc.snow.compiler.lexer.core.LexerContext; +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.lexer.token.TokenFactory; + +/** + * 标识符扫描器:处理标识符的识别,如变量名、函数名等。 + *

+ * 识别规则如下: + *

    + *
  • 必须以字母或下划线(_)开头
  • + *
  • 后续字符可以是字母、数字或下划线
  • + *
+ *

+ * 扫描完成后会调用 {@link TokenFactory} 自动判断是否为关键字, + * 并返回对应类型的 {@link Token}。 + */ +public class IdentifierTokenScanner extends AbstractTokenScanner { + + /** + * 判断是否可以处理当前位置的字符。 + *

如果字符为字母或下划线,则认为是标识符的起始。

+ * + * @param c 当前字符 + * @param ctx 当前词法上下文 + * @return 如果是标识符起始字符,则返回 true + */ + @Override + public boolean canHandle(char c, LexerContext ctx) { + return Character.isLetter(c) || c == '_'; + } + + /** + * 执行标识符的扫描逻辑。 + *

连续读取满足标识符规则的字符序列,交由 {@code TokenFactory} 创建对应的 Token。

+ * + * @param ctx 词法上下文 + * @param line 当前行号 + * @param col 当前列号 + * @return 标识符或关键字类型的 Token + */ + @Override + protected Token scanToken(LexerContext ctx, int line, int col) { + String lexeme = readWhile(ctx, ch -> Character.isLetterOrDigit(ch) || ch == '_'); + return TokenFactory.create(lexeme, line, col); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NewlineTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NewlineTokenScanner.java new file mode 100644 index 0000000..daea57c --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NewlineTokenScanner.java @@ -0,0 +1,41 @@ +package org.jcnc.snow.compiler.lexer.scanners; + +import org.jcnc.snow.compiler.lexer.core.LexerContext; +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.lexer.token.TokenType; + +/** + * 换行符扫描器:将源代码中的换行符(\n)识别为 {@code NEWLINE} 类型的 Token。 + *

+ * 通常用于记录行的分界,辅助语法分析阶段进行行敏感的判断或保持结构清晰。 + */ +public class NewlineTokenScanner extends AbstractTokenScanner { + + /** + * 判断是否可以处理当前位置的字符。 + *

当字符为换行符(\n)时返回 true。

+ * + * @param c 当前字符 + * @param ctx 当前词法上下文 + * @return 如果为换行符,则返回 true + */ + @Override + public boolean canHandle(char c, LexerContext ctx) { + return c == '\n'; + } + + /** + * 执行换行符的扫描逻辑。 + *

读取一个换行符并生成对应的 {@code NEWLINE} 类型 Token。

+ * + * @param ctx 词法上下文 + * @param line 当前行号 + * @param col 当前列号 + * @return 表示换行的 Token + */ + @Override + protected Token scanToken(LexerContext ctx, int line, int col) { + ctx.advance(); + return new Token(TokenType.NEWLINE, "\n", line, col); + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java new file mode 100644 index 0000000..517509f --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java @@ -0,0 +1,92 @@ +package org.jcnc.snow.compiler.lexer.scanners; + +import org.jcnc.snow.compiler.lexer.core.LexerContext; +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.lexer.token.TokenType; + +/** + * 数字扫描器:识别整数、小数以及带有类型后缀的数字字面量。 + *

+ * 支持的格式示例: + *

    + *
  • 整数:123、0、45678
  • + *
  • 小数:3.14、0.5、12.0
  • + *
  • 带类型后缀:2.0f、42L、7s、255B
  • + *
+ *

+ * 语法允许在数字 (整数或小数) 末尾添加以下单字符后缀来显式指定常量类型: + *

b | s | l | f | d   // 分别对应 byte、short、long、float、double
+ * B | S | L | F | D   // 同上,大小写皆可
+ * 生成的 Token 类型始终为 {@code NUMBER_LITERAL},词法单元将携带完整的文本(含后缀,若存在)。 + */ +public class NumberTokenScanner extends AbstractTokenScanner { + + /** + * 可选类型后缀字符集合 (大小写均可)。 + * 与 {@code ExpressionBuilder} 内的后缀解析逻辑保持一致。 + */ + private static final String SUFFIX_CHARS = "bslfdBSLFD"; + + /** + * 判断是否可以处理当前位置的字符。 + *

当字符为数字时,表示可能是数字字面量的起始。

+ * + * @param c 当前字符 + * @param ctx 当前词法上下文 + * @return 如果为数字字符,则返回 true + */ + @Override + public boolean canHandle(char c, LexerContext ctx) { + return Character.isDigit(c); + } + + /** + * 执行数字扫描逻辑。 + *
    + *
  1. 连续读取数字字符,允许出现一个小数点,用于识别整数或小数。
  2. + *
  3. 读取完主体后,一次性检查下一个字符,若属于合法类型后缀则吸收。
  4. + *
+ * 这样可以保证诸如 {@code 2.0f} 被视为一个整体的 {@code NUMBER_LITERAL}, + * 而不是拆分成 "2.0" 与 "f" 两个 Token。 + * + * @param ctx 词法上下文 + * @param line 当前行号 + * @param col 当前列号 + * @return 表示数字字面量的 Token + */ + @Override + protected Token scanToken(LexerContext ctx, int line, int col) { + StringBuilder sb = new StringBuilder(); + boolean hasDot = false; // 标识是否已经遇到过小数点 + + /* + * 1️⃣ 扫描整数或小数主体 + * 允许出现一个小数点,其余必须是数字。 + */ + while (!ctx.isAtEnd()) { + char c = ctx.peek(); + if (c == '.' && !hasDot) { + hasDot = true; + sb.append(ctx.advance()); + } else if (Character.isDigit(c)) { + sb.append(ctx.advance()); + } else { + break; // 遇到非数字/第二个点 => 主体结束 + } + } + + /* + * 2️⃣ 可选类型后缀 + * 如果下一字符是合法后缀字母,则一起纳入当前 Token。 + */ + if (!ctx.isAtEnd()) { + char suffix = ctx.peek(); + if (SUFFIX_CHARS.indexOf(suffix) >= 0) { + sb.append(ctx.advance()); + } + } + + // 构造并返回 NUMBER_LITERAL Token,文本内容形如 "123", "3.14f" 等。 + return new Token(TokenType.NUMBER_LITERAL, sb.toString(), line, col); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java new file mode 100644 index 0000000..6b8dde9 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java @@ -0,0 +1,116 @@ +package org.jcnc.snow.compiler.lexer.scanners; + +import org.jcnc.snow.compiler.lexer.core.LexerContext; +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.lexer.token.TokenType; + +/** + * 运算符扫描器:识别逻辑与比较运算符,包括单字符和双字符组合。 + *

+ * 支持的运算符包括: + *

    + *
  • 赋值与比较:=、==、!=
  • + *
  • 关系运算符:>、>=、<、<=
  • + *
  • 逻辑运算符:&&、||
  • + *
+ *

+ * 不符合上述组合的字符会返回 {@code UNKNOWN} 类型的 Token。 + */ +public class OperatorTokenScanner extends AbstractTokenScanner { + + /** + * 判断是否可以处理当前位置的字符。 + *

运算符扫描器关注的起始字符包括:=、!、<、>、|、&

+ * + * @param c 当前字符 + * @param ctx 当前词法上下文 + * @return 如果是潜在的运算符起始字符,则返回 true + */ + @Override + public boolean canHandle(char c, LexerContext ctx) { + return "=!<>|&%".indexOf(c) >= 0; + } + + /** + * 扫描并识别运算符 Token。 + *

支持组合运算符判断,如 ==、!=、>= 等, + * 若无法匹配组合形式则退回单字符形式。

+ * + * @param ctx 词法上下文 + * @param line 当前行号 + * @param col 当前列号 + * @return 对应的运算符 Token,无法识别的运算符返回 {@code UNKNOWN} + */ + @Override + protected Token scanToken(LexerContext ctx, int line, int col) { + char c = ctx.advance(); + String lexeme; + TokenType type; + + switch (c) { + case '=': + if (ctx.match('=')) { + lexeme = "=="; + type = TokenType.DOUBLE_EQUALS; + } else { + lexeme = "="; + type = TokenType.EQUALS; + } + break; + case '!': + if (ctx.match('=')) { + lexeme = "!="; + type = TokenType.NOT_EQUALS; + } else { + lexeme = "!"; + type = TokenType.UNKNOWN; + } + break; + case '>': + if (ctx.match('=')) { + lexeme = ">="; + type = TokenType.GREATER_EQUAL; + } else { + lexeme = ">"; + type = TokenType.GREATER_THAN; + } + break; + case '<': + if (ctx.match('=')) { + lexeme = "<="; + type = TokenType.LESS_EQUAL; + } else { + lexeme = "<"; + type = TokenType.LESS_THAN; + } + break; + case '%': + lexeme = "%"; + type = TokenType.MODULO; + break; + case '&': + if (ctx.match('&')) { + lexeme = "&&"; + type = TokenType.AND; + } else { + lexeme = "&"; + type = TokenType.UNKNOWN; + } + break; + case '|': + if (ctx.match('|')) { + lexeme = "||"; + type = TokenType.OR; + } else { + lexeme = "|"; + type = TokenType.UNKNOWN; + } + break; + default: + lexeme = String.valueOf(c); + type = TokenType.UNKNOWN; + } + + return new Token(type, lexeme, line, col); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/StringTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/StringTokenScanner.java new file mode 100644 index 0000000..a8643e4 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/StringTokenScanner.java @@ -0,0 +1,63 @@ +package org.jcnc.snow.compiler.lexer.scanners; + +import org.jcnc.snow.compiler.lexer.core.LexerContext; +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.lexer.token.TokenType; + +/** + * 字符串扫描器:处理双引号包裹的字符串字面量,支持基本的转义字符。 + *

+ * 支持格式示例: + *

    + *
  • "hello"
  • + *
  • "line\\nbreak"
  • + *
  • "escaped \\\" quote"
  • + *
+ *

+ * 扫描器会保留原始字符串的形式(包含双引号和转义符), + * 并生成 {@code STRING_LITERAL} 类型的 Token。 + */ +public class StringTokenScanner extends AbstractTokenScanner { + + /** + * 判断是否可以处理当前位置的字符。 + *

当字符为双引号(")时,认为是字符串字面量的开始。

+ * + * @param c 当前字符 + * @param ctx 当前词法上下文 + * @return 如果为字符串起始符,则返回 true + */ + @Override + public boolean canHandle(char c, LexerContext ctx) { + return c == '"'; + } + + /** + * 执行字符串的扫描逻辑。 + *

从当前位置开始,读取直到匹配结束的双引号。 + * 支持转义字符(如 \"、\\n 等),不会中断字符串扫描。

+ * + * @param ctx 词法上下文 + * @param line 当前行号 + * @param col 当前列号 + * @return 字符串字面量类型的 Token + */ + @Override + protected Token scanToken(LexerContext ctx, int line, int col) { + StringBuilder sb = new StringBuilder(); + sb.append(ctx.advance()); // 起始双引号 + + while (!ctx.isAtEnd()) { + char c = ctx.advance(); + sb.append(c); + + if (c == '\\') { + sb.append(ctx.advance()); // 添加转义字符后的实际字符 + } else if (c == '"') { + break; + } + } + + return new Token(TokenType.STRING_LITERAL, sb.toString(), line, col); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/SymbolTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/SymbolTokenScanner.java new file mode 100644 index 0000000..8e4eedd --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/SymbolTokenScanner.java @@ -0,0 +1,60 @@ +package org.jcnc.snow.compiler.lexer.scanners; + +import org.jcnc.snow.compiler.lexer.core.LexerContext; +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.lexer.token.TokenType; + +/** + * 符号扫描器:识别常见的单字符符号,如冒号、逗号、括号和算术符号。 + *

+ * 支持的符号包括: + *

    + *
  • 标点符号:: , .
  • + *
  • 括号:( )
  • + *
  • 算术运算符:+ - *
  • + *
+ *

+ * 生成的 Token 类型根据字符分别对应 {@link TokenType} 枚举中的定义。 + */ +public class SymbolTokenScanner extends AbstractTokenScanner { + + /** + * 判断是否可以处理当前位置的字符。 + *

本扫描器处理的符号包括 : , ( ) . + - *

+ * + * @param c 当前字符 + * @param ctx 当前词法上下文 + * @return 如果是支持的符号字符,则返回 true + */ + @Override + public boolean canHandle(char c, LexerContext ctx) { + return ":,().+-*/".indexOf(c) >= 0; + } + + /** + * 执行符号的扫描逻辑。 + *

根据字符匹配对应的 {@code TokenType} 类型,构造并返回 Token。

+ * + * @param ctx 词法上下文 + * @param line 当前行号 + * @param col 当前列号 + * @return 表示符号的 Token + */ + @Override + protected Token scanToken(LexerContext ctx, int line, int col) { + char c = ctx.advance(); + TokenType type = switch (c) { + case ':' -> TokenType.COLON; + case ',' -> TokenType.COMMA; + case '.' -> TokenType.DOT; + case '+' -> TokenType.PLUS; + case '-' -> TokenType.MINUS; + case '*' -> TokenType.MULTIPLY; + case '/' -> TokenType.DIVIDE; + case '(' -> TokenType.LPAREN; + case ')' -> TokenType.RPAREN; + default -> TokenType.UNKNOWN; + }; + return new Token(type, String.valueOf(c), line, col); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/UnknownTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/UnknownTokenScanner.java new file mode 100644 index 0000000..8f8a940 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/UnknownTokenScanner.java @@ -0,0 +1,55 @@ +package org.jcnc.snow.compiler.lexer.scanners; + +import org.jcnc.snow.compiler.lexer.core.LexerContext; +import org.jcnc.snow.compiler.lexer.core.LexicalException; +import org.jcnc.snow.compiler.lexer.token.Token; + +/** + * 未知 Token 扫描器(UnknownTokenScanner)。 + *

+ * 作为所有扫描器的兜底处理器。当前字符若不被任何其他扫描器识别, + * 由本类处理并抛出 {@link LexicalException},终止词法分析流程。 + *

+ *

+ * 主要作用:保证所有非法、不可识别的字符(如@、$等)不会被静默跳过或误当作合法 Token, + * 而是在词法阶段立刻定位并报错,有助于尽早发现源代码问题。 + *

+ */ +public class UnknownTokenScanner extends AbstractTokenScanner { + + /** + * 判断是否可以处理当前字符。 + * 对于 UnknownTokenScanner,始终返回 true(兜底扫描器,必须排在扫描器链末尾)。 + * @param c 当前待处理字符 + * @param ctx 词法上下文 + * @return 是否处理该字符(始终为 true) + */ + @Override + public boolean canHandle(char c, LexerContext ctx) { + return true; + } + + /** + * 实际处理非法字符序列的方法。 + * 连续读取所有无法被其他扫描器识别的字符,组成错误片段并抛出异常。 + * @param ctx 词法上下文 + * @param line 错误发生行号 + * @param col 错误发生列号 + * @return 不会返回Token(始终抛异常) + * @throws LexicalException 非法字符导致的词法错误 + */ + @Override + protected Token scanToken(LexerContext ctx, int line, int col) { + // 读取一段非法字符(既不是字母数字、也不是常见符号) + String lexeme = readWhile(ctx, ch -> + !Character.isLetterOrDigit(ch) && + !Character.isWhitespace(ch) && + ":,().+-*{};\"".indexOf(ch) < 0 + ); + // 如果没读到任何字符,则把当前字符单独作为非法片段 + if (lexeme.isEmpty()) + lexeme = String.valueOf(ctx.advance()); + // 抛出词法异常,并带上错误片段与具体位置 + throw new LexicalException("Illegal character sequence '" + lexeme + "'", line, col); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/WhitespaceTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/WhitespaceTokenScanner.java new file mode 100644 index 0000000..36c9768 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/WhitespaceTokenScanner.java @@ -0,0 +1,42 @@ +package org.jcnc.snow.compiler.lexer.scanners; + +import org.jcnc.snow.compiler.lexer.core.LexerContext; +import org.jcnc.snow.compiler.lexer.token.Token; + +/** + * 空白符扫描器:跳过非换行的空白字符,不生成任何 Token。 + *

+ * 支持的空白字符包括空格、制表符(Tab)等,但不包括换行符(由 {@link NewlineTokenScanner} 处理)。 + *

+ * 此扫描器仅用于忽略多余的空白内容,保持 Token 流的紧凑性。 + */ +public class WhitespaceTokenScanner extends AbstractTokenScanner { + + /** + * 判断是否可以处理当前位置的字符。 + *

当字符为空白符但不是换行符(\n)时返回 true。

+ * + * @param c 当前字符 + * @param ctx 当前词法上下文 + * @return 如果为非换行的空白字符,则返回 true + */ + @Override + public boolean canHandle(char c, LexerContext ctx) { + return Character.isWhitespace(c) && c != '\n'; + } + + /** + * 跳过空白字符,不生成 Token。 + *

直接推进上下文位置,返回 null。

+ * + * @param ctx 词法上下文 + * @param line 当前行号 + * @param col 当前列号 + * @return 始终返回 null(表示无 Token 产生) + */ + @Override + protected Token scanToken(LexerContext ctx, int line, int col) { + ctx.advance(); + return null; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/token/Token.java b/src/main/java/org/jcnc/snow/compiler/lexer/token/Token.java new file mode 100644 index 0000000..8dcdc65 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/token/Token.java @@ -0,0 +1,113 @@ +package org.jcnc.snow.compiler.lexer.token; + +/** + * {@code Token} 表示词法分析过程中生成的最小语法单元, + * 包含类型信息、词素内容、源代码中对应的原始文本片段以及精确的位置信息。 + *

+ * 一个 Token 通常对应源代码中一个具有语义意义的片段,如关键字、标识符、常量、运算符等。 + * 区分 lexeme(清洗后的词素)与 raw(原始片段)是为了支持如带引号的字符串、注释等需要保留原始形式的元素。 + *

+ */ +public class Token { + + /** Token 的类型,如 KEYWORD、IDENTIFIER、TYPE 等。 */ + private final TokenType type; + + /** 清洗后的词素内容,例如去掉引号的字符串正文或注释正文。 */ + private final String lexeme; + + /** 源代码中对应的原始片段,可能包含引号、注释符号等。 */ + private final String raw; + + /** Token 在源文件中的行号,从 1 开始计数。 */ + private final int line; + + /** Token 在源文件行中的列号,从 1 开始计数。 */ + private final int col; + + /** + * 构造一个完整信息的 Token 实例。 + * + * @param type Token 类型 + * @param lexeme 清洗后的词素内容 + * @param raw 源代码中的原始片段 + * @param line 所在源文件的行号(从 1 开始) + * @param col 所在源文件行的列号(从 1 开始) + */ + public Token(TokenType type, String lexeme, String raw, int line, int col) { + this.type = type; + this.lexeme = lexeme; + this.raw = raw; + this.line = line; + this.col = col; + } + + /** + * 构造一个简化形式的 Token,词素与原始片段一致。 + *

+ * 适用于标识符、关键字、符号等不需要区分原始与清洗内容的 Token。 + *

+ * + * @param type Token 类型 + * @param lexeme Token 内容(同时作为原始片段) + * @param line 行号 + * @param col 列号 + */ + public Token(TokenType type, String lexeme, int line, int col) { + this(type, lexeme, lexeme, line, col); + } + + /** + * 构造并返回一个表示文件结束(EOF)的 Token 实例。 + *

+ * 用于表示扫描结束的特殊符号。 + *

+ * + * @param line 文件结束所在行号 + * @return EOF 类型的 Token + */ + public static Token eof(int line) { + return new Token(TokenType.EOF, "", "", line, 1); + } + + /** @return 此 Token 的类型 */ + public TokenType getType() { + return type; + } + + /** @return 清洗后的词素内容(lexeme) */ + public String getLexeme() { + return lexeme; + } + + /** @return 源代码中的原始片段 */ + public String getRaw() { + return raw; + } + + /** @return Token 所在的源文件行号(从 1 开始) */ + public int getLine() { + return line; + } + + /** @return Token 所在行的列号(从 1 开始) */ + public int getCol() { + return col; + } + + /** + * 返回该 Token 的字符串表示,包含类型、词素、行列信息。 + *

+ * 通常用于日志打印或调试目的。 + *

+ * + * @return Token 的描述性字符串 + */ + @Override + public String toString() { + return String.format( + "Token(type=%s, lexeme='%s', line=%d, col=%d)", + type, lexeme, line, col + ); + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenFactory.java b/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenFactory.java new file mode 100644 index 0000000..ab7b9e3 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenFactory.java @@ -0,0 +1,112 @@ +package org.jcnc.snow.compiler.lexer.token; + +import java.util.Set; + +/** + * {@code TokenFactory} 是一个用于词法分析的静态工厂类。 + *

+ * 该类提供静态方法,根据输入的原始词素(字符串), + * 自动识别其对应的词法类型,并生成相应的 {@link Token} 实例。 + * 支持自动区分语言关键字、内置类型、合法标识符等, + * 简化了词法扫描器(Lexer)的核心处理流程。 + *

+ * + *

+ * 主要功能与特性: + *

    + *
  • 统一管理语言关键字和类型名集合,便于扩展与维护。
  • + *
  • 自动推断 Token 类型,无需外部干预。
  • + *
  • 对不合法的词素自动标记为 UNKNOWN 类型。
  • + *
+ *

+ * + * @author 你的名字 + * @version 1.0 + */ +public class TokenFactory { + + /** + * 语言的保留关键字集合。 + */ + private static final Set KEYWORDS = Set.of( + "module", "function", "parameter", "return_type", + "body", "end", "if", "then", "else", "loop", + "declare", "return", "import", + "initializer", "condition", "update" + ); + + /** + * 内置类型名称集合,如 int、string 等。 + */ + private static final Set TYPES = Set.of( + "int", "string", "float", "bool", "void", "double", "long", "short", "byte" + ); + + /** + * 创建一个根据内容自动推断类型的 {@link Token} 实例。 + *

+ * 优先级顺序为:类型(TYPE) > 关键字(KEYWORD) > 标识符(IDENTIFIER) > 未知(UNKNOWN)。 + * 若原始字符串同时属于多类,则按优先级最高者处理。 + *

+ * + * @param raw 原始词法单元文本 + * @param line Token 所在行号(从1开始) + * @param col Token 所在列号(从1开始) + * @return 构造好的 {@link Token} 实例 + */ + public static Token create(String raw, int line, int col) { + TokenType type = determineTokenType(raw); + return new Token(type, raw, line, col); + } + + /** + * 判断并推断给定字符串的 {@link TokenType} 类型。 + *

+ * 优先级依次为:TYPE > KEYWORD > IDENTIFIER > UNKNOWN。 + *

+ * + * @param raw 原始词素字符串 + * @return 推断出的 {@link TokenType} 类型 + */ + private static TokenType determineTokenType(String raw) { + if (isType(raw)) return TokenType.TYPE; + if (isKeyword(raw)) return TokenType.KEYWORD; + if (isIdentifier(raw)) return TokenType.IDENTIFIER; + return TokenType.UNKNOWN; + } + + /** + * 判断指定字符串是否为内置类型标识。 + * + * @param raw 输入的字符串 + * @return 若为类型名则返回 {@code true},否则返回 {@code false} + */ + private static boolean isType(String raw) { + return TYPES.contains(raw); + } + + /** + * 判断指定字符串是否为语言保留关键字。 + * + * @param raw 输入的字符串 + * @return 若为关键字则返回 {@code true},否则返回 {@code false} + */ + private static boolean isKeyword(String raw) { + return KEYWORDS.contains(raw); + } + + /** + * 判断指定字符串是否为合法的标识符(Identifier)。 + *

+ * 合法标识符需以字母(a-z/A-Z)或下划线(_)开头, + * 后续可包含字母、数字或下划线。 + * 例如:_abc, a1b2, name_123 均为合法标识符。 + *

+ * + * @param raw 输入的字符串 + * @return 若为合法标识符则返回 {@code true},否则返回 {@code false} + */ + private static boolean isIdentifier(String raw) { + return raw.matches("[a-zA-Z_][a-zA-Z0-9_]*"); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenType.java b/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenType.java new file mode 100644 index 0000000..db7f087 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenType.java @@ -0,0 +1,95 @@ +package org.jcnc.snow.compiler.lexer.token; + +/** + * {@code TokenType} 枚举定义了 Snow 编程语言词法分析阶段可识别的所有词法单元类型。 + *

+ * 每个枚举值代表一种语义类别, + * 用于描述源代码中出现的关键字、标识符、字面量、运算符、控制符号等语言构件。 + * 在语法分析及后续处理阶段,依赖该枚举对 Token 进行分类、判断与分支处理。 + *

+ */ +public enum TokenType { + + /** 普通标识符,如变量名、函数名等 */ + IDENTIFIER, + + /** 语言保留关键字(如 if、return、module 等) */ + KEYWORD, + + /** 内置类型名称(如 int、string、bool 等) */ + TYPE, + + /** 字符串字面量(如 "hello") */ + STRING_LITERAL, + + /** 数字字面量(整数或浮点数) */ + NUMBER_LITERAL, + + /** 冒号 ':' */ + COLON, + + /** 逗号 ',' */ + COMMA, + + /** 点号 '.' */ + DOT, + + /** 赋值符号 '=' */ + EQUALS, + + /** 取模运算符 '%' */ + MODULO, + + /** 加号 '+' */ + PLUS, + + /** 乘号 '*' */ + MULTIPLY, + /**除号 '/' */ + DIVIDE, + + /** 减号 '-' */ + MINUS, + + /** 左括号 '(' */ + LPAREN, + + /** 右括号 ')' */ + RPAREN, + + /** 相等比较符号 '==' */ + DOUBLE_EQUALS, + + /** 不等比较符号 '!=' */ + NOT_EQUALS, + + /** 大于符号 '>' */ + GREATER_THAN, + + /** 大于等于符号 '>=' */ + GREATER_EQUAL, + + /** 小于符号 '<' */ + LESS_THAN, + + /** 小于等于符号 '<=' */ + LESS_EQUAL, + + /** 逻辑与符号 '&&' */ + AND, + + /** 逻辑或符号 '||' */ + OR, + + /** 换行符,标识逻辑换行 */ + NEWLINE, + + /** 单行或多行注释内容 */ + COMMENT, + + /** 文件结束符(End of File) */ + EOF, + + /** 无法识别的非法或未知符号 */ + UNKNOWN +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/utils/TokenPrinter.java b/src/main/java/org/jcnc/snow/compiler/lexer/utils/TokenPrinter.java new file mode 100644 index 0000000..e9e4e59 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/lexer/utils/TokenPrinter.java @@ -0,0 +1,63 @@ +package org.jcnc.snow.compiler.lexer.utils; + +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.lexer.token.TokenType; + +import java.util.List; + +/** + * TokenPrinter 用于以表格形式将词法分析生成的 Token 列表输出到控制台。 + *

+ * 输出包含每个 Token 的行号、列号、类型以及词素(lexeme), + * 并对换行、制表符等特殊字符进行转义显示。 + *

+ */ +public class TokenPrinter { + + /** + * 将给定的 Token 列表打印到标准输出(控制台)。 + *

+ * 输出格式: + *

+     * line   col    type             lexeme
+     * ----------------------------------------------------
+     * 1      1      KEYWORD          module
+     * 1      7      IDENTIFIER       MyModule
+     * ...
+     * 
+ * 并且对 lexeme 中的换行符、制表符、回车符等进行转义(\n、\t、\r), + * 以便在表格中保持排版整齐。如果遇到类型为 {@link TokenType#NEWLINE} 的 Token, + * 会在该行后额外插入一个空行以增强可读性。 + * + * @param tokens 要打印的 Token 列表,不应为 null;列表中的每个元素 + * 都应包含有效的行号、列号、类型和词素信息 + */ + public static void print(List tokens) { + // 打印表头:列名对齐,宽度分别为 6、6、16 + System.out.printf("%-6s %-6s %-16s %s%n", "line", "col", "type", "lexeme"); + System.out.println("----------------------------------------------------"); + + // 逐个 Token 输出对应信息 + for (Token token : tokens) { + // 对 lexeme 中的特殊字符进行转义,避免表格错位 + String lexeme = token.getLexeme() + .replace("\n", "\\n") + .replace("\t", "\\t") + .replace("\r", "\\r"); + + // 按照固定格式输出:行号、列号、类型、词素 + System.out.printf("%-6d %-6d %-16s %s%n", + token.getLine(), + token.getCol(), + token.getType(), + lexeme + ); + + // 如果当前 Token 是换行符类型,则额外打印一行空白行 + if (token.getType() == TokenType.NEWLINE) { + System.out.println(); + } + } + } + +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/AssignmentNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/AssignmentNode.java new file mode 100644 index 0000000..69781b9 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/AssignmentNode.java @@ -0,0 +1,34 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; + +/** + * {@code AssignmentNode} 表示抽象语法树(AST)中的赋值语句节点。 + *

+ * 赋值语句用于将右侧表达式的值存储到左侧指定的变量中, + * 通常形式为 {@code x = expression},其中 {@code x} 是目标变量, + * {@code expression} 是用于计算赋值结果的表达式。 + *

+ *

+ * 该节点作为语句节点的一种实现,适用于语义分析、类型检查、IR 构建等多个阶段。 + *

+ * + * @param variable 左值变量名(即赋值目标) + * @param value 表达式右值(即赋值来源) + */ +public record AssignmentNode(String variable, ExpressionNode value) implements StatementNode { + + /** + * 返回赋值语句的字符串形式,便于调试与日志输出。 + *

+ * 典型格式形如 {@code x = y + 1}。 + *

+ * + * @return 表示赋值语句的字符串形式 + */ + @Override + public String toString() { + return variable + " = " + value; + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/BinaryExpressionNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/BinaryExpressionNode.java new file mode 100644 index 0000000..6e49e63 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/BinaryExpressionNode.java @@ -0,0 +1,32 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; + +/** + * {@code BinaryExpressionNode} 表示抽象语法树(AST)中的二元运算表达式节点。 + *

+ * 二元表达式通常由两个操作数和一个中间操作符构成,例如 {@code a + b}。 + * 此结构广泛用于数学计算、逻辑判断、字符串拼接等语法结构中。 + *

+ * + * @param left 左操作数(子表达式) + * @param operator 运算符字符串(如 "+", "-", "*", "/" 等) + * @param right 右操作数(子表达式) + */ +public record BinaryExpressionNode(ExpressionNode left, String operator, + ExpressionNode right) implements ExpressionNode { + + /** + * 返回该二元运算表达式的字符串表示形式。 + *

+ * 输出格式为:{@code left + " " + operator + " " + right}, + * 适用于调试或打印语法树结构。 + *

+ * + * @return 表示该二元表达式的字符串 + */ + @Override + public String toString() { + return left + " " + operator + " " + right; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/CallExpressionNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/CallExpressionNode.java new file mode 100644 index 0000000..c9109a1 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/CallExpressionNode.java @@ -0,0 +1,39 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; + +import java.util.List; + +/** + * {@code CallExpressionNode} 表示抽象语法树(AST)中的函数调用表达式节点。 + *

+ * 函数调用表达式用于表示函数或过程的调用操作, + * 包括被调用对象(callee)以及一组参数表达式(arguments)。 + *

+ * + * @param callee 被调用的表达式节点,通常为函数标识符或成员访问表达式。 + * @param arguments 参数表达式列表,依照调用顺序排列。 + */ +public record CallExpressionNode(ExpressionNode callee, List arguments) implements ExpressionNode { + + /** + * 返回函数调用表达式的字符串形式。 + *

+ * 该格式将输出为类似 {@code foo(a, b, c)} 的形式, + * 便于调试与语法树可视化。 + *

+ * + * @return 表示函数调用的字符串表示 + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(callee).append("("); + for (int i = 0; i < arguments.size(); i++) { + sb.append(arguments.get(i)); + if (i + 1 < arguments.size()) sb.append(", "); + } + sb.append(")"); + return sb.toString(); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/DeclarationNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/DeclarationNode.java new file mode 100644 index 0000000..1819d1f --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/DeclarationNode.java @@ -0,0 +1,65 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; + +import java.util.Optional; + +/** + * {@code DeclarationNode} 表示抽象语法树(AST)中的变量声明语句节点。 + *

+ * 变量声明用于在语法层引入新的标识符及其类型信息, + * 通常格式为 {@code type name = initializer;},其中初始化表达式可省略。 + *

+ */ +public class DeclarationNode implements StatementNode { + + /** 声明的变量名称 */ + private final String name; + + /** 变量的数据类型(如 "int", "string") */ + private final String type; + + /** 可选的初始化表达式 */ + private final Optional initializer; + + /** + * 构造一个 {@code DeclarationNode} 实例。 + * + * @param name 变量名称 + * @param type 变量类型字符串(如 "int"、"string") + * @param initializer 可选初始化表达式,若为 {@code null} 表示未初始化 + */ + public DeclarationNode(String name, String type, ExpressionNode initializer) { + this.name = name; + this.type = type; + this.initializer = Optional.ofNullable(initializer); + } + + /** + * 获取变量名称。 + * + * @return 变量名字符串 + */ + public String getName() { + return name; + } + + /** + * 获取变量类型字符串。 + * + * @return 类型名称(如 "int") + */ + public String getType() { + return type; + } + + /** + * 获取可选的初始化表达式。 + * + * @return 一个 Optional 包装的初始化表达式对象,可能为空 + */ + public Optional getInitializer() { + return initializer; + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/ExpressionStatementNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/ExpressionStatementNode.java new file mode 100644 index 0000000..cf62fb1 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/ExpressionStatementNode.java @@ -0,0 +1,16 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; + +/** + * {@code ExpressionStatementNode} 表示抽象语法树(AST)中的表达式语句节点。 + *

+ * 表达式语句通常由一个单独的表达式组成,并以语句形式出现。 + * 例如:{@code foo();}、{@code x = 1;}、{@code print("hello");} 等。 + *

+ * + * @param expression 表达式主体,通常为函数调用、赋值、方法链式调用等可求值表达式。 + */ +public record ExpressionStatementNode(ExpressionNode expression) implements StatementNode { +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/FunctionNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/FunctionNode.java new file mode 100644 index 0000000..7ab55dd --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/FunctionNode.java @@ -0,0 +1,23 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.Node; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; + +import java.util.List; + +/** + * {@code FunctionNode} 表示抽象语法树(AST)中的函数定义结构。 + *

+ * 函数定义通常包含函数名、形参列表、返回类型以及函数体, + * 在语义分析、类型检查与代码生成等阶段具有核心地位。 + * 示例:{@code int add(int a, int b) { return a + b; }} + *

+ * + * @param name 函数名称标识符 + * @param parameters 参数列表,每项为 {@link ParameterNode} 表示一个形参定义 + * @param returnType 函数的返回类型(如 "int"、"void" 等) + * @param body 函数体语句块,由一组 {@link StatementNode} 构成 + */ +public record FunctionNode(String name, List parameters, String returnType, + List body) implements Node { +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/IdentifierNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/IdentifierNode.java new file mode 100644 index 0000000..cfce3d4 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/IdentifierNode.java @@ -0,0 +1,25 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; + +/** + * {@code IdentifierNode} 表示抽象语法树(AST)中的标识符表达式节点。 + *

+ * 该节点用于表示变量名、函数名、字段名等符号引用。 + * 在语义分析中,通常需要将此类节点绑定到其声明位置或符号表项。 + *

+ * + * @param name 标识符的文本名称(如变量名 "x",函数名 "foo") + */ +public record IdentifierNode(String name) implements ExpressionNode { + + /** + * 返回标识符节点的字符串形式,通常为其名称本身。 + * + * @return 标识符名称字符串 + */ + @Override + public String toString() { + return name; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/IfNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/IfNode.java new file mode 100644 index 0000000..9ac9371 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/IfNode.java @@ -0,0 +1,38 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; + +import java.util.List; + +/** + * {@code IfNode} 表示抽象语法树(AST)中的条件语句结构(if-else)。 + *

+ * 该节点包含一个条件表达式(condition)、一个 then 分支语句列表, + * 以及一个可选的 else 分支语句列表。 + *

+ *

+ * 条件表达式为布尔类型,决定是否执行 then 分支。 + * 若 condition 为假,则执行 else 分支(如果提供)。 + *

+ *

+ * 示例语法结构: + *

+ *
{@code
+ * if (x > 0) {
+ *     print("Positive");
+ * } else {
+ *     print("Negative");
+ * }
+ * }
+ * + * @param condition 控制分支执行的条件表达式 + * @param thenBranch 条件为 true 时执行的语句块 + * @param elseBranch 条件为 false 时执行的语句块(可为空) + */ +public record IfNode( + ExpressionNode condition, + List thenBranch, + List elseBranch +) implements StatementNode { +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/ImportNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/ImportNode.java new file mode 100644 index 0000000..f551fd9 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/ImportNode.java @@ -0,0 +1,19 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.Node; + +/** + * {@code ImportNode} 表示抽象语法树(AST)中的 import 语句节点。 + *

+ * import 语句用于引入外部模块或库文件,其语法形式一般为: + * {@code import my.module;} + *

+ *

+ * 本节点仅存储导入目标模块的名称,不包含路径解析或绑定逻辑, + * 这些通常由语义分析器或模块加载器处理。 + *

+ * + * @param moduleName 被导入的模块名称,通常为点分层次结构(如 "core.utils") + */ +public record ImportNode(String moduleName) implements Node { +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/LoopNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/LoopNode.java new file mode 100644 index 0000000..7821ae4 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/LoopNode.java @@ -0,0 +1,23 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; + +import java.util.List; + +/** + * {@code LoopNode} 表示抽象语法树(AST)中的循环语句结构。 + *

+ * 该节点建模了类似传统 {@code for} 循环的控制结构, + * 包含初始化语句、循环条件、更新语句及循环体。 + * 每一部分均对应为 AST 中的子节点,便于进一步语义分析与代码生成。 + *

+ * + * @param initializer 在循环开始前执行的初始化语句 + * @param condition 每次迭代前评估的条件表达式,控制循环是否继续 + * @param update 每轮迭代完成后执行的更新语句 + * @param body 循环体语句列表,表示循环主体执行逻辑 + */ +public record LoopNode(StatementNode initializer, ExpressionNode condition, StatementNode update, + List body) implements StatementNode { +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/MemberExpressionNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/MemberExpressionNode.java new file mode 100644 index 0000000..7b49db3 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/MemberExpressionNode.java @@ -0,0 +1,29 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; + +/** + * {@code MemberExpressionNode} 表示抽象语法树(AST)中的成员访问表达式节点。 + *

+ * 用于表示对象字段或方法的访问操作,语法形式如 {@code object.member}。 + * 成员访问常见于结构体、模块、对象导入等上下文中,是表达式链中常见的构件之一。 + *

+ * + * @param object 左侧对象表达式,表示成员所属的作用域或容器 + * @param member 要访问的成员名称(字段名或方法名) + */ +public record MemberExpressionNode(ExpressionNode object, String member) implements ExpressionNode { + + /** + * 返回成员访问表达式的字符串形式。 + *

+ * 输出格式为 {@code object.member},用于调试或语法树可视化。 + *

+ * + * @return 成员访问表达式的字符串形式 + */ + @Override + public String toString() { + return object + "." + member; + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/ModuleNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/ModuleNode.java new file mode 100644 index 0000000..564402a --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/ModuleNode.java @@ -0,0 +1,38 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.Node; + +import java.util.List; +import java.util.StringJoiner; + +/** + * 表示模块定义的 AST 节点。 + * 一个模块通常由模块名、导入语句列表和函数定义列表组成。 + * } + * + * @param name 模块名称。 + * @param imports 模块导入列表,每个导入是一个 {@link ImportNode}。 + * @param functions 模块中的函数列表,每个函数是一个 {@link FunctionNode}。 + */ +public record ModuleNode(String name, List imports, List functions) implements Node { + + /** + * 返回模块节点的字符串表示形式,包含模块名、导入模块列表和函数列表。 + * + * @return 模块的简洁字符串表示,用于调试和日志输出。 + */ + @Override + public String toString() { + StringJoiner importJoiner = new StringJoiner(", "); + for (ImportNode imp : imports) { + importJoiner.add(imp.moduleName()); + } + StringJoiner funcJoiner = new StringJoiner(", "); + for (FunctionNode fn : functions) { + funcJoiner.add(fn.name()); + } + return "Module(name=" + name + + ", imports=[" + importJoiner + "]" + + ", functions=[" + funcJoiner + "])"; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/NumberLiteralNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/NumberLiteralNode.java new file mode 100644 index 0000000..f30f526 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/NumberLiteralNode.java @@ -0,0 +1,26 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; + +/** + * {@code NumberLiteralNode} 表示抽象语法树(AST)中的数字字面量表达式节点。 + *

+ * 用于表示源代码中的数值常量,如整数 {@code 42} 或浮点数 {@code 3.14}。 + * 为了兼容不同数值格式,本节点以字符串形式存储原始值, + * 在语义分析或类型推导阶段再行解析为具体数值类型。 + *

+ * + * @param value 数字字面量的原始字符串表示 + */ +public record NumberLiteralNode(String value) implements ExpressionNode { + + /** + * 返回数字字面量的字符串形式。 + * + * @return 字面量原始字符串值(例如 "42" 或 "3.14") + */ + @Override + public String toString() { + return value; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/ParameterNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/ParameterNode.java new file mode 100644 index 0000000..91e93aa --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/ParameterNode.java @@ -0,0 +1,29 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.Node; + +/** + * {@code ParameterNode} 表示抽象语法树(AST)中的函数参数定义节点。 + *

+ * 每个参数节点包含参数的名称和类型信息, + * 用于构成函数签名并参与类型检查与函数调用匹配。 + *

+ * + * @param name 参数名称标识符 + * @param type 参数类型字符串(如 "int"、"string") + */ +public record ParameterNode(String name, String type) implements Node { + + /** + * 返回参数的字符串形式,格式为 {@code name:type}。 + *

+ * 用于调试输出或构建函数签名描述。 + *

+ * + * @return 参数的字符串形式(如 {@code count:int}) + */ + @Override + public String toString() { + return name + ":" + type; + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/ReturnNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/ReturnNode.java new file mode 100644 index 0000000..28143dd --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/ReturnNode.java @@ -0,0 +1,43 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; + +import java.util.Optional; + +/** + * {@code ReturnNode} 表示抽象语法树(AST)中的 return 语句节点。 + *

+ * return 语句用于从当前函数中返回控制权,并可携带一个可选的返回值表达式。 + *

+ *

+ * 示例: + *

    + *
  • {@code return;}
  • + *
  • {@code return x + 1;}
  • + *
+ *

+ */ +public class ReturnNode implements StatementNode { + + /** 可选的返回值表达式 */ + private final Optional expression; + + /** + * 构造一个 {@code ReturnNode} 实例。 + * + * @param expression 返回值表达式,如果无返回值则可为 {@code null} + */ + public ReturnNode(ExpressionNode expression) { + this.expression = Optional.ofNullable(expression); + } + + /** + * 获取可选的返回值表达式。 + * + * @return 如果有返回值则返回 {@code Optional.of(expression)},否则返回 {@code Optional.empty()} + */ + public Optional getExpression() { + return expression; + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/StringLiteralNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/StringLiteralNode.java new file mode 100644 index 0000000..b04d561 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/StringLiteralNode.java @@ -0,0 +1,28 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; + +/** + * {@code StringLiteralNode} 表示抽象语法树(AST)中的字符串字面量表达式节点。 + *

+ * 用于表示源代码中出现的字符串常量,如 {@code "hello"}、{@code "abc123"} 等。 + * 节点内部仅保存不带引号的字符串内容,便于后续语义处理或编码。 + *

+ * + * @param value 字符串常量的内容,原始值中不包含双引号 + */ +public record StringLiteralNode(String value) implements ExpressionNode { + + /** + * 返回字符串字面量的带引号表示,适用于语法树调试或文本输出。 + *

+ * 例如,当 {@code value = Result:} 时,返回 {@code "Result:"}。 + *

+ * + * @return 字符串字面量的完整表示形式(带双引号) + */ + @Override + public String toString() { + return "\"" + value + "\""; + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/base/ExpressionNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/base/ExpressionNode.java new file mode 100644 index 0000000..e8db645 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/base/ExpressionNode.java @@ -0,0 +1,14 @@ +package org.jcnc.snow.compiler.parser.ast.base; + +/** + * {@code ExpressionNode} 表示抽象语法树(AST)中所有表达式类型节点的统一接口。 + *

+ * 作为标记接口(Marker Interface),该接口用于区分表达式与其他语法结构, + * 不定义具体方法,其子类型通常表示可参与求值运算的结构, + * 如常量表达式、变量引用、函数调用、算术运算等。 + *

+ *

+ * 所有实现此接口的节点可参与表达式求值、语义分析、类型检查与中间代码生成等处理流程。 + *

+ */ +public interface ExpressionNode extends Node {} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/base/Node.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/base/Node.java new file mode 100644 index 0000000..8c7a8b9 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/base/Node.java @@ -0,0 +1,18 @@ +package org.jcnc.snow.compiler.parser.ast.base; + +/** + * {@code Node} 是抽象语法树(AST)中所有语法节点的统一根接口。 + *

+ * 作为标记接口(Marker Interface),该接口不定义任何方法, + * 主要用于统一标识并组织 AST 体系中的各种语法构件节点,包括: + *

+ *
    + *
  • {@link ExpressionNode}:表达式节点,如常量、变量引用、函数调用等
  • + *
  • {@link StatementNode}:语句节点,如声明、赋值、条件控制、循环、返回语句等
  • + *
  • 模块、函数、参数等高层结构节点
  • + *
+ *

+ * 所有 AST 处理逻辑(如遍历、分析、代码生成)均可基于该接口实现统一调度和类型判定。 + *

+ */ +public interface Node {} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/base/StatementNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/base/StatementNode.java new file mode 100644 index 0000000..b4db2cb --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/base/StatementNode.java @@ -0,0 +1,11 @@ +package org.jcnc.snow.compiler.parser.ast.base; + +/** + * {@code StatementNode} 表示抽象语法树(AST)中所有语句结构的统一接口。 + *

+ * 该接口为标记接口(Marker Interface),用于识别和区分语句类节点, + * 包括但不限于变量声明、赋值语句、控制结构(如 if、loop)、返回语句等。 + * 实现此接口的类应表示程序在运行时执行的具体语法行为。 + *

+ */ +public interface StatementNode extends Node {} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/base/TopLevelParser.java b/src/main/java/org/jcnc/snow/compiler/parser/base/TopLevelParser.java new file mode 100644 index 0000000..2e07785 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/base/TopLevelParser.java @@ -0,0 +1,31 @@ +package org.jcnc.snow.compiler.parser.base; + +import org.jcnc.snow.compiler.parser.ast.base.Node; +import org.jcnc.snow.compiler.parser.context.ParserContext; + +/** + * {@code TopLevelParser} 是顶层语法结构的解析器接口。 + *

+ * 用于解析模块级别的构造,例如 {@code module}、{@code import}、{@code function} 等。 + * 所有顶层语法解析器应实现该接口,并从 {@link ParserContext} 提供的 TokenStream 中提取并构建 AST 节点。 + *

+ *

+ * 本接口由 {@link org.jcnc.snow.compiler.parser.factory.TopLevelParserFactory} 负责根据关键字进行动态分派, + * 以便支持扩展性与模块化解析策略。 + *

+ */ +public interface TopLevelParser { + + /** + * 从解析上下文中解析一个顶层语法结构节点。 + *

+ * 每个实现类应根据自身语法规则消费 TokenStream 中的相应 token, + * 构造对应的 AST 子树结构。 + *

+ * + * @param ctx 当前解析上下文,包含 Token 流与中间状态 + * @return 构建完成的 AST 节点,不应为 {@code null} + * @throws IllegalStateException 若解析过程遇到非法结构或上下文状态异常 + */ + Node parse(ParserContext ctx); +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/ParseException.java b/src/main/java/org/jcnc/snow/compiler/parser/context/ParseException.java new file mode 100644 index 0000000..0313d7a --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/context/ParseException.java @@ -0,0 +1,24 @@ +package org.jcnc.snow.compiler.parser.context; + +/** + * {@code ParseException} 表示语法分析阶段发生的错误。 + *

+ * 当语法分析器遇到非法的语法结构或无法继续处理的标记序列时, + * 应抛出该异常以中断当前解析流程,并向调用方报告错误信息。 + *

+ *

+ * 该异常通常由 {@code ParserContext} 或各类语法规则处理器主动抛出, + * 用于提示编译器前端或 IDE 系统进行错误提示与恢复。 + *

+ */ +public class ParseException extends RuntimeException { + + /** + * 构造一个带有错误描述信息的解析异常实例。 + * + * @param message 错误描述文本,用于指明具体的语法错误原因 + */ + public ParseException(String message) { + super(message); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/ParserContext.java b/src/main/java/org/jcnc/snow/compiler/parser/context/ParserContext.java new file mode 100644 index 0000000..bbf2b2e --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/context/ParserContext.java @@ -0,0 +1,36 @@ +package org.jcnc.snow.compiler.parser.context; + +import org.jcnc.snow.compiler.lexer.token.Token; +import java.util.List; + +/** + * {@code ParserContext} 表示语法分析阶段的共享上下文容器。 + *

+ * 封装了词法单元流(TokenStream),供各级语法解析器读取与回退。 + * 后续还可扩展为包含错误收集、符号表管理、作用域追踪等功能模块, + * 以支持完整的编译前端功能。 + *

+ */ +public class ParserContext { + + /** 当前语法分析所使用的 Token 流 */ + private final TokenStream tokens; + + /** + * 使用词法分析得到的 Token 列表构造上下文。 + * + * @param tokens 词法分析器生成的 Token 集合 + */ + public ParserContext(List tokens) { + this.tokens = new TokenStream(tokens); + } + + /** + * 获取封装的 Token 流,用于驱动语法分析过程。 + * + * @return 当前使用的 {@link TokenStream} 实例 + */ + public TokenStream getTokens() { + return tokens; + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java b/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java new file mode 100644 index 0000000..5841b7b --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java @@ -0,0 +1,127 @@ +package org.jcnc.snow.compiler.parser.context; + +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.lexer.token.TokenType; + +import java.util.List; + +/** + * {@code TokenStream} 封装了一个 Token 列表并维护当前解析位置, + * 是语法分析器读取词法单元的核心工具类。 + *

+ * 提供前瞻(peek)、消费(next)、匹配(match)、断言(expect)等常用操作, + * 支持前向查看和异常处理,适用于递归下降解析等常见语法构建策略。 + *

+ */ +public class TokenStream { + + /** 源 Token 列表 */ + private final List tokens; + + /** 当前解析位置索引 */ + private int pos = 0; + + /** + * 使用 Token 列表构造 TokenStream。 + * + * @param tokens 由词法分析器产生的 Token 集合 + */ + public TokenStream(List tokens) { + this.tokens = tokens; + } + + /** + * 向前查看指定偏移量处的 Token(不移动位置)。 + * + * @param offset 相对当前位置的偏移量(0 表示当前) + * @return 指定位置的 Token;若越界则返回自动构造的 EOF Token + */ + public Token peek(int offset) { + int idx = pos + offset; + if (idx >= tokens.size()) { + return Token.eof(tokens.size() + 1); + } + return tokens.get(idx); + } + + /** + * 查看当前位置的 Token,等效于 {@code peek(0)}。 + * + * @return 当前 Token + */ + public Token peek() { + return peek(0); + } + + /** + * 消费当前位置的 Token 并返回,位置前移。 + * + * @return 当前 Token + */ + public Token next() { + Token t = peek(); + pos++; + return t; + } + + /** + * 匹配当前 Token 的词素与指定字符串,若匹配则消费。 + * + * @param lexeme 待匹配词素 + * @return 若成功匹配则返回 true + */ + public boolean match(String lexeme) { + if (peek().getLexeme().equals(lexeme)) { + next(); + return true; + } + return false; + } + + /** + * 断言当前 Token 的词素与指定值相符,否则抛出 {@link ParseException}。 + * + * @param lexeme 期望的词素值 + * @return 匹配成功的 Token + * @throws ParseException 若词素不符 + */ + public Token expect(String lexeme) { + Token t = peek(); + if (!t.getLexeme().equals(lexeme)) { + throw new ParseException( + "Expected lexeme '" + lexeme + "' but got '" + t.getLexeme() + + "' at " + t.getLine() + ":" + t.getCol() + ); + } + return next(); + } + + /** + * 断言当前 Token 类型为指定类型,否则抛出 {@link ParseException}。 + * + * @param type 期望的 Token 类型 + * @return 匹配成功的 Token + * @throws ParseException 若类型不匹配 + */ + public Token expectType(TokenType type) { + Token t = peek(); + if (t.getType() != type) { + throw new ParseException( + "Expected token type " + type + " but got " + t.getType() + + " ('" + t.getLexeme() + "') at " + t.getLine() + ":" + t.getCol() + ); + } + return next(); + } + + /** + * 判断是否“已经”到达 EOF。 + * + * @return 若当前位置 Token 为 EOF,则返回 true,否则 false + */ + public boolean isAtEnd() { + return peek().getType() != TokenType.EOF; + } + + +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java b/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java new file mode 100644 index 0000000..03810b5 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java @@ -0,0 +1,64 @@ +package org.jcnc.snow.compiler.parser.core; + +import org.jcnc.snow.compiler.lexer.token.TokenType; +import org.jcnc.snow.compiler.parser.base.TopLevelParser; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.context.TokenStream; +import org.jcnc.snow.compiler.parser.factory.TopLevelParserFactory; +import org.jcnc.snow.compiler.parser.ast.base.Node; + +import java.util.ArrayList; +import java.util.List; + +public record ParserEngine(ParserContext ctx) { + + public List parse() { + List nodes = new ArrayList<>(); + List errs = new ArrayList<>(); + TokenStream ts = ctx.getTokens(); + + while (ts.isAtEnd()) { // ← 取反 + // 跳过空行 + if (ts.peek().getType() == TokenType.NEWLINE) { + ts.next(); + continue; + } + + TopLevelParser parser = TopLevelParserFactory.get(ts.peek().getLexeme()); + + try { + nodes.add(parser.parse(ctx)); + } catch (Exception ex) { + errs.add(ex.getMessage()); + synchronize(ts); // 错误恢复 + } + } + + if (!errs.isEmpty()) { + throw new IllegalStateException("解析过程中检测到 " + + errs.size() + " 处错误:\n - " + + String.join("\n - ", errs)); + } + return nodes; + } + + /** + * 错误同步:跳到下一行或下一个已注册顶层关键字 + */ + private void synchronize(TokenStream ts) { + while (ts.isAtEnd()) { + if (ts.peek().getType() == TokenType.NEWLINE) { + ts.next(); + break; + } + if (TopLevelParserFactory.get(ts.peek().getLexeme()) != null) { + break; + } + ts.next(); + } + // 连续空行全部吃掉 + while (ts.isAtEnd() && ts.peek().getType() == TokenType.NEWLINE) { + ts.next(); + } + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/doc/README.md b/src/main/java/org/jcnc/snow/compiler/parser/doc/README.md new file mode 100644 index 0000000..2615ad0 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/doc/README.md @@ -0,0 +1,45 @@ +# Snow Compiler - Parser 模块 + +> Snow 编译器的前端模块 —— 负责将 Token 流解析为抽象语法树(AST) + +## 项目简介 + +**Parser** 是 [Snow 编译器]() 项目的子模块,承担编译器前端的解析阶段任务。 +它负责将词法分析得到的 Token 流,按照 Snow 语言的语法规则,解析为标准的抽象语法树(AST)。 + +框架使用了 Pratt 解析算法处理表达式,同时采用模块化设计,支持声明、函数、条件语句、循环语句等各类结构的解析。 + +## 核心功能 + +- **抽象语法树(AST)生成**:定义丰富的 AST 节点类型 +- **表达式解析器**:基于 Pratt 解析,支持优先级、调用、成员访问等 +- **语句解析器**:支持 Snow 语言中的声明、控制流等 +- **模块解析器**:处理 `import` 导入声明 +- **函数解析器**:支持函数定义与调用 +- **灵活的解析上下文管理**:错误处理与流式 Token 管理 +- **工具支持**:AST JSON 序列化,便于调试与前后端通信 + +## 模块结构 + +``` +parser/ + ├── ast/ // AST 节点定义 + ├── base/ // 顶层解析器接口 + ├── context/ // 解析上下文与异常处理 + ├── core/ // 核心解析引擎 + ├── expression/ // 表达式解析模块 + ├── factory/ // 解析器工厂 + ├── function/ // 函数解析及 AST 打印 + ├── module/ // 模块导入解析 + ├── statement/ // 各类语句解析器 + └── utils/ // JSON 序列化、辅助方法 +``` + +## 开发环境 + +* JDK 23 或更高版本 +* Maven 构建管理 +* 推荐 IDE:IntelliJ IDEA + +--- + diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/BinaryOperatorParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/BinaryOperatorParselet.java new file mode 100644 index 0000000..da86f01 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/BinaryOperatorParselet.java @@ -0,0 +1,61 @@ +package org.jcnc.snow.compiler.parser.expression; + +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.parser.ast.BinaryExpressionNode; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.expression.base.InfixParselet; + +/** + * {@code BinaryOperatorParselet} 表示用于解析二元中缀表达式的解析器。 + *

+ * 该解析器支持二元运算符表达式(如 {@code a + b}、{@code x * y}), + * 可配置操作符优先级与结合性(左结合或右结合)。 + * 适用于 Pratt 解析器架构中中缀阶段的语法处理。 + *

+ * + * @param precedence 当前运算符的优先级 + * @param leftAssoc 是否为左结合运算符 + */ +public record BinaryOperatorParselet(Precedence precedence, boolean leftAssoc) implements InfixParselet { + + /** + * 构造一个中缀二元运算符的解析器。 + * + * @param precedence 运算符的优先级 + * @param leftAssoc 是否左结合(true 表示左结合,false 表示右结合) + */ + public BinaryOperatorParselet { + } + + /** + * 解析当前二元中缀表达式。 + * + * @param ctx 当前解析上下文 + * @param left 当前已解析的左表达式 + * @return 构建完成的 {@link BinaryExpressionNode} AST 节点 + */ + @Override + public ExpressionNode parse(ParserContext ctx, ExpressionNode left) { + Token op = ctx.getTokens().next(); + int prec = precedence.ordinal(); + + // 右侧表达式根据结合性确定优先级绑定 + ExpressionNode right = new PrattExpressionParser().parseExpression( + ctx, + leftAssoc ? Precedence.values()[prec] : Precedence.values()[prec - 1] + ); + + return new BinaryExpressionNode(left, op.getLexeme(), right); + } + + /** + * 获取当前运算符的优先级。 + * + * @return 运算符优先级枚举 + */ + @Override + public Precedence precedence() { + return precedence; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/CallParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/CallParselet.java new file mode 100644 index 0000000..4e8d3a3 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/CallParselet.java @@ -0,0 +1,53 @@ +package org.jcnc.snow.compiler.parser.expression; + +import org.jcnc.snow.compiler.parser.ast.CallExpressionNode; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.expression.base.InfixParselet; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@code CallParselet} 表示函数调用语法的中缀解析器。 + *

+ * 用于处理形如 {@code foo(arg1, arg2)} 的函数调用结构。 + * 在 Pratt 解析器架构中,该解析器在函数名之后接收括号开始的调用参数, + * 构建 {@link CallExpressionNode} 抽象语法树节点。 + *

+ */ +public class CallParselet implements InfixParselet { + + /** + * 解析函数调用表达式,格式为 {@code callee(args...)}。 + * + * @param ctx 当前解析上下文 + * @param left 函数名或调用目标(即 callee 表达式) + * @return 构建完成的函数调用 AST 节点 + */ + @Override + public ExpressionNode parse(ParserContext ctx, ExpressionNode left) { + ctx.getTokens().next(); // 消费 "(" + + List args = new ArrayList<>(); + + if (!ctx.getTokens().peek().getLexeme().equals(")")) { + do { + args.add(new PrattExpressionParser().parse(ctx)); + } while (ctx.getTokens().match(",")); + } + + ctx.getTokens().expect(")"); // 消费并验证 ")" + return new CallExpressionNode(left, args); + } + + /** + * 获取函数调用操作的优先级。 + * + * @return 表达式优先级 {@link Precedence#CALL} + */ + @Override + public Precedence precedence() { + return Precedence.CALL; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/GroupingParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/GroupingParselet.java new file mode 100644 index 0000000..8f79f11 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/GroupingParselet.java @@ -0,0 +1,34 @@ +package org.jcnc.snow.compiler.parser.expression; + +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet; + +/** + * {@code GroupingParselet} 解析圆括号括起的子表达式。 + *

+ * 用于处理形如 {@code (a + b)} 的表达式结构, + * 通常用于提升括号内表达式的优先级,以控制运算顺序。 + *

+ */ +public class GroupingParselet implements PrefixParselet { + + /** + * 解析括号表达式。 + *

+ * 该方法假定当前 token 为左括号 "(",将解析其内部表达式, + * 并断言后续 token 为右括号 ")"。 + *

+ * + * @param ctx 当前解析上下文 + * @param token 当前起始 token,应为 "(" + * @return 被括号包裹的子表达式节点 + */ + @Override + public ExpressionNode parse(ParserContext ctx, Token token) { + ExpressionNode expr = new PrattExpressionParser().parse(ctx); + ctx.getTokens().expect(")"); + return expr; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/IdentifierParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/IdentifierParselet.java new file mode 100644 index 0000000..057c425 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/IdentifierParselet.java @@ -0,0 +1,29 @@ +package org.jcnc.snow.compiler.parser.expression; + +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.parser.ast.IdentifierNode; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet; + +/** + * {@code IdentifierParselet} 是用于解析标识符表达式的前缀解析器。 + *

+ * 适用于解析如 {@code x}、{@code count}、{@code isValid} 等变量名或函数名, + * 通常出现在表达式的开头部分,是 AST 中的基础表达式节点之一。 + *

+ */ +public class IdentifierParselet implements PrefixParselet { + + /** + * 解析标识符表达式。 + * + * @param ctx 当前语法解析上下文(本实现未使用) + * @param token 当前标识符 Token + * @return 构建的 {@link IdentifierNode} 表达式节点 + */ + @Override + public ExpressionNode parse(ParserContext ctx, Token token) { + return new IdentifierNode(token.getLexeme()); + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/MemberParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/MemberParselet.java new file mode 100644 index 0000000..b0f1e5f --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/MemberParselet.java @@ -0,0 +1,44 @@ +package org.jcnc.snow.compiler.parser.expression; + +import org.jcnc.snow.compiler.lexer.token.TokenType; +import org.jcnc.snow.compiler.parser.ast.MemberExpressionNode; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.context.TokenStream; +import org.jcnc.snow.compiler.parser.expression.base.InfixParselet; + +/** + * {@code MemberParselet} 是用于解析成员访问表达式的中缀解析器。 + *

+ * 解析形如 {@code object.property} 的语法结构,常用于访问对象字段或方法, + * 是一种紧绑定操作,优先级通常与函数调用相同。 + *

+ */ +public class MemberParselet implements InfixParselet { + + /** + * 解析成员访问表达式。 + * + * @param ctx 当前解析上下文 + * @param left 已解析的对象表达式(左侧) + * @return {@link MemberExpressionNode} 表示成员访问的表达式节点 + */ + @Override + public ExpressionNode parse(ParserContext ctx, ExpressionNode left) { + TokenStream ts = ctx.getTokens(); + ts.expect("."); // 消费点号 + + String member = ts.expectType(TokenType.IDENTIFIER).getLexeme(); + return new MemberExpressionNode(left, member); + } + + /** + * 获取成员访问操作的优先级。 + * + * @return 表达式优先级 {@link Precedence#CALL} + */ + @Override + public Precedence precedence() { + return Precedence.CALL; + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/NumberLiteralParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/NumberLiteralParselet.java new file mode 100644 index 0000000..6ea186f --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/NumberLiteralParselet.java @@ -0,0 +1,29 @@ +package org.jcnc.snow.compiler.parser.expression; + +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.parser.ast.NumberLiteralNode; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet; + +/** + * {@code NumberLiteralParselet} 是用于解析数字字面量的前缀解析器。 + *

+ * 适用于处理如 {@code 42}、{@code 3.14} 等整型或浮点型常量表达式, + * 通常出现在表达式的起始位置或子表达式内部。 + *

+ */ +public class NumberLiteralParselet implements PrefixParselet { + + /** + * 将当前的数字 Token 转换为 {@link NumberLiteralNode} 节点。 + * + * @param ctx 当前语法解析上下文(此实现未使用) + * @param token 当前的数字字面量 Token + * @return 构建完成的数字表达式节点 + */ + @Override + public ExpressionNode parse(ParserContext ctx, Token token) { + return new NumberLiteralNode(token.getLexeme()); + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java new file mode 100644 index 0000000..5edcf69 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java @@ -0,0 +1,110 @@ +package org.jcnc.snow.compiler.parser.expression; + +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.lexer.token.TokenType; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.expression.base.ExpressionParser; +import org.jcnc.snow.compiler.parser.expression.base.InfixParselet; +import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet; + +import java.util.HashMap; +import java.util.Map; + +/** + * {@code PrattExpressionParser} 是基于 Pratt 算法实现的表达式解析器。 + *

+ * 它支持灵活的运算符优先级控制,结合前缀(PrefixParselet)和中缀(InfixParselet)解析器, + * 可高效解析复杂表达式结构,包括: + *

    + *
  • 字面量(数字、字符串)
  • + *
  • 标识符
  • + *
  • 函数调用、成员访问
  • + *
  • 带括号的表达式、二元运算符
  • + *
+ * 本类提供统一注册机制和递归表达式解析入口。 + *

+ */ +public class PrattExpressionParser implements ExpressionParser { + + /** + * 前缀解析器注册表:按 Token 类型映射 + */ + private static final Map prefixes = new HashMap<>(); + + /** + * 中缀解析器注册表:按运算符词素映射 + */ + private static final Map infixes = new HashMap<>(); + + static { + // 注册前缀解析器 + prefixes.put(TokenType.NUMBER_LITERAL.name(), new NumberLiteralParselet()); + prefixes.put(TokenType.IDENTIFIER.name(), new IdentifierParselet()); + prefixes.put(TokenType.LPAREN.name(), new GroupingParselet()); + prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet()); + + // 注册中缀解析器 + infixes.put("+", new BinaryOperatorParselet(Precedence.SUM, true)); + infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true)); + infixes.put("*", new BinaryOperatorParselet(Precedence.PRODUCT, true)); + infixes.put("/", new BinaryOperatorParselet(Precedence.PRODUCT, true)); + infixes.put("%", new BinaryOperatorParselet(Precedence.PRODUCT, true)); + infixes.put(">", new BinaryOperatorParselet(Precedence.SUM, true)); + infixes.put("<", new BinaryOperatorParselet(Precedence.SUM, true)); + infixes.put("==", new BinaryOperatorParselet(Precedence.SUM, true)); + infixes.put("!=", new BinaryOperatorParselet(Precedence.SUM, true)); + infixes.put(">=", new BinaryOperatorParselet(Precedence.SUM, true)); + infixes.put("<=", new BinaryOperatorParselet(Precedence.SUM, true)); + infixes.put("(", new CallParselet()); + infixes.put(".", new MemberParselet()); + } + + /** + * 表达式解析入口,使用最低优先级启动递归解析。 + * + * @param ctx 当前语法解析上下文 + * @return 表达式抽象语法树节点 + */ + @Override + public ExpressionNode parse(ParserContext ctx) { + return parseExpression(ctx, Precedence.LOWEST); + } + + /** + * 根据指定优先级解析表达式。 + * + * @param ctx 当前上下文 + * @param prec 当前优先级阈值 + * @return 构建完成的表达式节点 + */ + ExpressionNode parseExpression(ParserContext ctx, Precedence prec) { + Token token = ctx.getTokens().next(); + PrefixParselet prefix = prefixes.get(token.getType().name()); + if (prefix == null) { + throw new IllegalStateException("没有为该 Token 类型注册前缀解析器: " + token.getType()); + } + + ExpressionNode left = prefix.parse(ctx, token); + + while (ctx.getTokens().isAtEnd() + && prec.ordinal() < nextPrecedence(ctx)) { + String lex = ctx.getTokens().peek().getLexeme(); + InfixParselet infix = infixes.get(lex); + if (infix == null) break; + left = infix.parse(ctx, left); + } + return left; + } + + /** + * 获取下一个中缀解析器的优先级,用于判断是否继续解析。 + * + * @param ctx 当前上下文 + * @return 优先级枚举 ordinal 值;若无解析器则为 -1 + */ + private int nextPrecedence(ParserContext ctx) { + InfixParselet infix = infixes.get(ctx.getTokens().peek().getLexeme()); + return infix != null ? infix.precedence().ordinal() : -1; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/Precedence.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/Precedence.java new file mode 100644 index 0000000..5eb7efe --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/Precedence.java @@ -0,0 +1,31 @@ +package org.jcnc.snow.compiler.parser.expression; + +/** + * {@code Precedence} 表示表达式中各类运算符的优先级枚举。 + *

+ * 该优先级枚举用于 Pratt 解析器判断运算符的结合顺序。 + * 枚举顺序即优先级高低,数值越大绑定越紧密。 + *

+ */ +public enum Precedence { + + /** + * 最低优先级,通常用于整个表达式解析的起始入口。 + */ + LOWEST, + + /** + * 加法和减法的优先级(例如 +、-)。 + */ + SUM, + + /** + * 乘法、除法、取模等更高优先级的二元运算符(例如 *、/、%)。 + */ + PRODUCT, + + /** + * 函数调用、成员访问等最强绑定(例如 foo()、obj.prop)。 + */ + CALL +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/StringLiteralParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/StringLiteralParselet.java new file mode 100644 index 0000000..b73603d --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/StringLiteralParselet.java @@ -0,0 +1,32 @@ +package org.jcnc.snow.compiler.parser.expression; + +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.ast.StringLiteralNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet; + +/** + * {@code StringLiteralParselet} 是用于解析字符串字面量的前缀解析器。 + *

+ * 该解析器会将原始字符串 Token(带引号)转换为 {@link StringLiteralNode}, + * 并自动去除词素前后的双引号。 + * 例如,输入 Token 为 {@code "\"hello\""},将被解析为 {@code StringLiteralNode("hello")}。 + *

+ */ +public class StringLiteralParselet implements PrefixParselet { + + /** + * 解析字符串字面量 Token。 + * + * @param ctx 当前语法解析上下文(未使用) + * @param token 当前字符串 Token(包含引号) + * @return {@link StringLiteralNode} 表达式节点 + */ + @Override + public ExpressionNode parse(ParserContext ctx, Token token) { + String raw = token.getRaw(); + String content = raw.substring(1, raw.length() - 1); + return new StringLiteralNode(content); + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/base/ExpressionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/base/ExpressionParser.java new file mode 100644 index 0000000..bbf15ed --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/base/ExpressionParser.java @@ -0,0 +1,31 @@ +package org.jcnc.snow.compiler.parser.expression.base; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; + +/** + * {@code ExpressionParser} 是用于解析表达式的通用接口。 + *

+ * 实现该接口的解析器应根据 {@link ParserContext} 中提供的 Token 流, + * 构建一个有效的 {@link ExpressionNode} 抽象语法树结构。 + *

+ *

+ * 不同的实现可以采用不同的解析技术: + *

    + *
  • 递归下降(Recursive Descent)
  • + *
  • Pratt Parser(前缀/中缀优先级驱动)
  • + *
  • 操作符优先表等其他手段
  • + *
+ * 通常用于函数体、表达式语句、条件判断等上下文中的子树构建。 + *

+ */ +public interface ExpressionParser { + + /** + * 从解析上下文中解析并返回一个表达式节点。 + * + * @param ctx 当前语法解析上下文,提供 Token 流与辅助状态 + * @return 构建完成的 {@link ExpressionNode} 表达式 AST 子节点 + */ + ExpressionNode parse(ParserContext ctx); +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/base/InfixParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/base/InfixParselet.java new file mode 100644 index 0000000..c886a83 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/base/InfixParselet.java @@ -0,0 +1,41 @@ +package org.jcnc.snow.compiler.parser.expression.base; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.expression.Precedence; + +/** + * {@code InfixParselet} 表示中缀表达式的解析器接口。 + *

+ * 用于构建如 {@code a + b}、{@code x * y}、{@code f(x)} 或 {@code obj.prop} 等结构, + * 是 Pratt 解析器架构中处理中缀操作的关键组件。 + *

+ *

+ * 每个中缀解析器负责: + *

    + *
  • 根据左侧已解析的表达式,结合当前运算符继续解析右侧部分
  • + *
  • 提供运算符优先级,用于判断是否继续嵌套解析
  • + *
+ *

+ */ +public interface InfixParselet { + + /** + * 根据已解析的左侧表达式与上下文,解析出完整的中缀表达式节点。 + * + * @param ctx 当前解析上下文,包含 Token 流状态 + * @param left 当前已解析的左表达式节点 + * @return 组合完成的中缀表达式 AST 节点 + */ + ExpressionNode parse(ParserContext ctx, ExpressionNode left); + + /** + * 获取当前中缀表达式的解析优先级。 + *

+ * 用于决定当前操作符是否绑定左右子表达式,影响解析树结构。 + *

+ * + * @return 表达式优先级枚举值 + */ + Precedence precedence(); +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/base/PrefixParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/base/PrefixParselet.java new file mode 100644 index 0000000..426a62f --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/base/PrefixParselet.java @@ -0,0 +1,33 @@ +package org.jcnc.snow.compiler.parser.expression.base; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.lexer.token.Token; + +/** + * {@code PrefixParselet} 是用于解析前缀表达式的通用接口。 + *

+ * 前缀表达式是以某个词法单元(Token)作为起始的表达式结构, + * 常见类型包括: + *

    + *
  • 数字字面量(如 {@code 42})
  • + *
  • 标识符(如 {@code foo})
  • + *
  • 括号包裹的子表达式(如 {@code (a + b)})
  • + *
  • 前缀一元运算(如 {@code -x}、{@code !flag})
  • + *
+ *

+ *

+ * 本接口通常用于 Pratt 解析器架构中,负责识别语法的起点。 + *

+ */ +public interface PrefixParselet { + + /** + * 解析一个以当前 Token 开头的前缀表达式节点。 + * + * @param ctx 当前解析上下文,包含 Token 流状态 + * @param token 当前读取到的前缀 Token + * @return 构建完成的 {@link ExpressionNode} 表达式节点 + */ + ExpressionNode parse(ParserContext ctx, Token token); +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/factory/StatementParserFactory.java b/src/main/java/org/jcnc/snow/compiler/parser/factory/StatementParserFactory.java new file mode 100644 index 0000000..d3b4578 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/factory/StatementParserFactory.java @@ -0,0 +1,44 @@ +package org.jcnc.snow.compiler.parser.factory; + +import org.jcnc.snow.compiler.parser.statement.*; + +import java.util.Map; +import java.util.HashMap; + +/** + * {@code StatementParserFactory} 是一个语句解析器工厂类, + * 用于根据关键字动态选择适当的 {@link StatementParser} 实现。 + *

+ * 本类通过静态注册的方式将各类语句解析器绑定到对应的关键字上, + * 在语法分析阶段根据当前语句起始词快速获取对应解析逻辑。 + *

+ *

+ * 若传入的关键字未被显式注册,将回退使用默认的表达式语句解析器 {@link ExpressionStatementParser}。 + *

+ */ +public class StatementParserFactory { + + /** 注册表:语句关键字 -> 对应语句解析器 */ + private static final Map registry = new HashMap<>(); + + static { + // 注册各类语句解析器 + registry.put("declare", new DeclarationStatementParser()); + registry.put("if", new IfStatementParser()); + registry.put("loop", new LoopStatementParser()); + registry.put("return", new ReturnStatementParser()); + + // 默认处理器:表达式语句 + registry.put("", new ExpressionStatementParser()); + } + + /** + * 根据关键字查找对应的语句解析器。 + * + * @param keyword 当前语句起始关键字(如 "if"、"loop" 等) + * @return 匹配的 {@link StatementParser} 实例;若无匹配则返回默认解析器 + */ + public static StatementParser get(String keyword) { + return registry.getOrDefault(keyword, registry.get("")); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/factory/TopLevelParserFactory.java b/src/main/java/org/jcnc/snow/compiler/parser/factory/TopLevelParserFactory.java new file mode 100644 index 0000000..7fa779c --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/factory/TopLevelParserFactory.java @@ -0,0 +1,29 @@ +package org.jcnc.snow.compiler.parser.factory; + +import org.jcnc.snow.compiler.parser.base.TopLevelParser; +import org.jcnc.snow.compiler.parser.module.ModuleParser; +import org.jcnc.snow.compiler.parser.function.FunctionParser; +import org.jcnc.snow.compiler.parser.top.ScriptTopLevelParser; + +import java.util.Map; +import java.util.HashMap; + +public class TopLevelParserFactory { + + private static final Map registry = new HashMap<>(); + private static final TopLevelParser DEFAULT = new ScriptTopLevelParser(); // ← 默认解析器 + + static { + // 顶层结构解析器 + registry.put("module", new ModuleParser()); + registry.put("function", new FunctionParser()); + // 也可按需继续注册其它关键字 + } + + /** + * 根据关键字获取解析器;若未注册,回退到脚本语句解析。 + */ + public static TopLevelParser get(String keyword) { + return registry.getOrDefault(keyword, DEFAULT); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/function/ASTPrinter.java b/src/main/java/org/jcnc/snow/compiler/parser/function/ASTPrinter.java new file mode 100644 index 0000000..65bf0c0 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/function/ASTPrinter.java @@ -0,0 +1,136 @@ +package org.jcnc.snow.compiler.parser.function; + +import org.jcnc.snow.compiler.parser.ast.*; +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.StatementNode; +import org.jcnc.snow.compiler.parser.utils.ASTJsonSerializer; +import org.jcnc.snow.compiler.parser.utils.JsonFormatter; + +import java.util.List; + +/** + * {@code ASTPrinter} 是一个抽象语法树(AST)打印工具类, + * 提供用于调试和可视化的格式化文本输出与 JSON 序列化能力。 + * + *

+ * 该类支持以缩进方式层级打印任意 AST 节点,包括模块、函数、控制结构等, + * 适用于开发阶段调试语法树结构或输出结构化语法分析结果。 + *

+ * + *

+ * 同时支持将 AST 序列化为 JSON 字符串,并使用 {@link JsonFormatter} 美化输出, + * 便于与外部工具对接或做进一步分析。 + *

+ */ +public class ASTPrinter { + + /** + * 打印整个抽象语法树的节点集合。 + * + *

+ * 每个节点及其子节点将以多行文本形式打印,展示语法树的层级结构。 + * 该方法通常用于打印完整模块、函数集等顶层结构。 + *

+ * + * @param nodes 要打印的 AST 节点列表,通常为模块或顶层语句列表。 + */ + public static void print(List nodes) { + for (Node n : nodes) { + print(n, 0); + } + } + + /** + * 递归打印单个 AST 节点及其子节点,带有格式化缩进。 + * + *

+ * 支持的节点类型包括模块、函数、声明、赋值、条件语句、循环、返回语句、表达式语句等。 + * 若节点类型未显式支持,则退回使用 {@code toString()} 方法进行输出。 + *

+ * + * @param n 要打印的 AST 节点。 + * @param indent 当前缩进层级,每级对应两个空格。 + */ + private static void print(Node n, int indent) { + String pad = " ".repeat(indent); + + switch (n) { + case ModuleNode m -> { + System.out.println(pad + "module " + m.name()); + for (ImportNode imp : m.imports()) { + System.out.println(pad + " import " + imp.moduleName()); + } + for (FunctionNode fn : m.functions()) { + print(fn, indent + 1); + } + } + case FunctionNode( + String name, List parameters, String returnType, List body + ) -> { + System.out.println(pad + "function " + name + + "(params=" + parameters + ", return=" + returnType + ")"); + for (StatementNode stmt : body) { + print(stmt, indent + 1); + } + } + case DeclarationNode d -> { + String init = d.getInitializer() + .map(Object::toString) + .map(s -> " = " + s) + .orElse(""); + System.out.println(pad + "declare " + d.getName() + ":" + d.getType() + init); + } + case AssignmentNode(String variable, ExpressionNode value) -> + System.out.println(pad + variable + " = " + value); + case IfNode(ExpressionNode condition, List thenBranch, List elseBranch) -> { + System.out.println(pad + "if " + condition); + for (StatementNode stmt : thenBranch) { + print(stmt, indent + 1); + } + if (!elseBranch.isEmpty()) { + System.out.println(pad + "else"); + for (StatementNode stmt : elseBranch) { + print(stmt, indent + 1); + } + } + } + case LoopNode( + StatementNode initializer, ExpressionNode condition, StatementNode update, List body + ) -> { + System.out.println(pad + "loop {"); + print(initializer, indent + 1); + System.out.println(pad + " condition: " + condition); + System.out.println(pad + " update: " + update); + System.out.println(pad + " body {"); + for (StatementNode stmt : body) { + print(stmt, indent + 2); + } + System.out.println(pad + " }"); + System.out.println(pad + "}"); + } + case ReturnNode r -> System.out.println(pad + "return" + + r.getExpression().map(e -> " " + e).orElse("")); + case ExpressionStatementNode(ExpressionNode expression) -> + System.out.println(pad + expression); + case null, default -> + System.out.println(pad + n); // 回退处理 + } + } + + /** + * 打印 AST 的 JSON 表示形式。 + * + *

+ * 本方法将语法树序列化为 JSON 字符串,并通过 {@link JsonFormatter#prettyPrint(String)} 方法进行格式化。 + * 适用于将 AST 结构导出至文件或供其他系统消费。 + *

+ * + * @param nodes 要序列化并打印的 AST 节点列表。 + */ + public static void printJson(List nodes) { + String rawJson = ASTJsonSerializer.toJsonString(nodes); + String pretty = JsonFormatter.prettyPrint(rawJson); + System.out.println(pretty); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/function/FunctionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/function/FunctionParser.java new file mode 100644 index 0000000..372387c --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/function/FunctionParser.java @@ -0,0 +1,270 @@ +package org.jcnc.snow.compiler.parser.function; + +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.lexer.token.TokenType; +import org.jcnc.snow.compiler.parser.base.TopLevelParser; +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.parser.ast.ParameterNode; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.context.TokenStream; +import org.jcnc.snow.compiler.parser.factory.StatementParserFactory; +import org.jcnc.snow.compiler.parser.utils.FlexibleSectionParser; +import org.jcnc.snow.compiler.parser.utils.FlexibleSectionParser.SectionDefinition; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * {@code FunctionParser} 是顶层函数定义的语法解析器, + * 实现 {@link TopLevelParser} 接口,用于将源代码中的函数块解析为抽象语法树(AST)中的 {@link FunctionNode}。 + * + *

+ * 本类使用 {@link FlexibleSectionParser} 机制,按照语义区块结构对函数进行模块化解析,支持以下部分: + *

+ * + *
    + *
  • 函数头(关键字 {@code function:} 与函数名)
  • + *
  • 参数列表(parameter 区块)
  • + *
  • 返回类型(return_type 区块)
  • + *
  • 函数体(body 区块)
  • + *
  • 函数结束(关键字 {@code end function})
  • + *
+ * + *

+ * 各区块允许包含注释(类型 {@code COMMENT})与空行(类型 {@code NEWLINE}),解析器将自动跳过无效 token 保持语法连续性。 + *

+ * + *

+ * 最终将函数结构封装为 {@link FunctionNode} 并返回,供后续编译阶段使用。 + *

+ */ +public class FunctionParser implements TopLevelParser { + + /** + * 顶层语法解析入口。 + * + *

+ * 该方法负责完整解析函数定义,包括其所有组成部分,并构建对应的 {@link FunctionNode}。 + *

+ * + * @param ctx 当前解析上下文,包含 {@link TokenStream} 和符号表等作用域信息。 + * @return 构建完成的 {@link FunctionNode} 抽象语法树节点。 + */ + @Override + public FunctionNode parse(ParserContext ctx) { + TokenStream ts = ctx.getTokens(); + + parseFunctionHeader(ts); + String functionName = parseFunctionName(ts); + + List parameters = new ArrayList<>(); + String[] returnType = new String[1]; + List body = new ArrayList<>(); + + Map sections = getSectionDefinitions(parameters, returnType, body); + FlexibleSectionParser.parse(ctx, ts, sections); + + parseFunctionFooter(ts); + + return new FunctionNode(functionName, parameters, returnType[0], body); + } + + /** + * 构造函数定义中各区块的解析规则(parameter、return_type、body)。 + * + *

+ * 每个 {@link SectionDefinition} 包含两个部分:区块起始判断器(基于关键字)与具体的解析逻辑。 + *

+ * + * @param params 参数节点收集容器,解析结果将存入此列表。 + * @param returnType 返回类型容器,以单元素数组方式模拟引用传递。 + * @param body 函数体语句节点列表容器。 + * @return 区块关键字到解析定义的映射表。 + */ + private Map getSectionDefinitions( + List params, + String[] returnType, + List body) { + Map map = new HashMap<>(); + + map.put("parameter", new SectionDefinition( + (TokenStream stream) -> stream.peek().getLexeme().equals("parameter"), + (ParserContext context, TokenStream stream) -> params.addAll(parseParameters(stream)) + )); + + map.put("return_type", new SectionDefinition( + (TokenStream stream) -> stream.peek().getLexeme().equals("return_type"), + (ParserContext context, TokenStream stream) -> returnType[0] = parseReturnType(stream) + )); + + map.put("body", new SectionDefinition( + (TokenStream stream) -> stream.peek().getLexeme().equals("body"), + (ParserContext context, TokenStream stream) -> body.addAll(parseFunctionBody(context, stream)) + )); + + return map; + } + + /** + * 解析函数头部标识符 {@code function:},并跳过其后多余注释与空行。 + * + * @param ts 当前使用的 {@link TokenStream}。 + */ + private void parseFunctionHeader(TokenStream ts) { + ts.expect("function"); + ts.expect(":"); + skipComments(ts); + skipNewlines(ts); + } + + /** + * 解析函数名称(标识符)并跳过换行。 + * + * @param ts 当前使用的 {@link TokenStream}。 + * @return 函数名字符串。 + */ + private String parseFunctionName(TokenStream ts) { + String name = ts.expectType(TokenType.IDENTIFIER).getLexeme(); + ts.expectType(TokenType.NEWLINE); + return name; + } + + /** + * 解析函数结束标记 {@code end function}。 + * + * @param ts 当前使用的 {@link TokenStream}。 + */ + private void parseFunctionFooter(TokenStream ts) { + ts.expect("end"); + ts.expect("function"); + ts.expectType(TokenType.NEWLINE); + } + + /** + * 解析函数参数列表。 + * + *

+ * 支持声明后附加注释,格式示例: + *

+     * parameter:
+     *   declare x: int   // 说明文字
+     *   declare y: float
+     * 
+ *

+ * + * @param ts 当前使用的 {@link TokenStream}。 + * @return 所有参数节点的列表。 + */ + private List parseParameters(TokenStream ts) { + ts.expect("parameter"); + ts.expect(":"); + skipComments(ts); + ts.expectType(TokenType.NEWLINE); + skipNewlines(ts); + + List list = new ArrayList<>(); + while (true) { + skipComments(ts); + if (ts.peek().getType() == TokenType.NEWLINE) { + ts.next(); + continue; + } + String lex = ts.peek().getLexeme(); + if (lex.equals("return_type") || lex.equals("body") || lex.equals("end")) { + break; + } + ts.expect("declare"); + String pname = ts.expectType(TokenType.IDENTIFIER).getLexeme(); + ts.expect(":"); + String ptype = ts.expectType(TokenType.TYPE).getLexeme(); + skipComments(ts); + ts.expectType(TokenType.NEWLINE); + list.add(new ParameterNode(pname, ptype)); + } + return list; + } + + /** + * 解析返回类型声明。 + * + *

+ * 格式为 {@code return_type: TYPE},支持前置或行尾注释。 + *

+ * + * @param ts 当前使用的 {@link TokenStream}。 + * @return 返回类型名称字符串。 + */ + private String parseReturnType(TokenStream ts) { + ts.expect("return_type"); + ts.expect(":"); + skipComments(ts); + Token typeToken = ts.expectType(TokenType.TYPE); + String rtype = typeToken.getLexeme(); + skipComments(ts); + ts.expectType(TokenType.NEWLINE); + skipNewlines(ts); + return rtype; + } + + /** + * 解析函数体区块,直到遇到 {@code end body}。 + * + *

+ * 每一行由对应的语句解析器处理,可嵌套控制结构、返回语句、表达式等。 + *

+ * + * @param ctx 当前解析上下文。 + * @param ts 当前使用的 {@link TokenStream}。 + * @return 所有函数体语句节点的列表。 + */ + private List parseFunctionBody(ParserContext ctx, TokenStream ts) { + ts.expect("body"); + ts.expect(":"); + skipComments(ts); + ts.expectType(TokenType.NEWLINE); + skipNewlines(ts); + + List stmts = new ArrayList<>(); + while (true) { + skipComments(ts); + if (ts.peek().getType() == TokenType.NEWLINE) { + ts.next(); + continue; + } + if ("end".equals(ts.peek().getLexeme())) { + break; + } + stmts.add(StatementParserFactory.get(ts.peek().getLexeme()).parse(ctx)); + } + ts.expect("end"); + ts.expect("body"); + ts.expectType(TokenType.NEWLINE); + skipNewlines(ts); + return stmts; + } + + /** + * 跳过连续的注释 token(类型 {@code COMMENT})。 + * + * @param ts 当前使用的 {@link TokenStream}。 + */ + private void skipComments(TokenStream ts) { + while (ts.peek().getType() == TokenType.COMMENT) { + ts.next(); + } + } + + /** + * 跳过连续的空行 token(类型 {@code NEWLINE})。 + * + * @param ts 当前使用的 {@link TokenStream}。 + */ + private void skipNewlines(TokenStream ts) { + while (ts.peek().getType() == TokenType.NEWLINE) { + ts.next(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/module/ImportParser.java b/src/main/java/org/jcnc/snow/compiler/parser/module/ImportParser.java new file mode 100644 index 0000000..38dd788 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/module/ImportParser.java @@ -0,0 +1,64 @@ +package org.jcnc.snow.compiler.parser.module; + +import org.jcnc.snow.compiler.lexer.token.TokenType; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.ast.ImportNode; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@code ImportParser} 类用于解析源码中的 import 导入语句。 + *

+ * 支持以下格式的语法: + *

+ * import: module1, module2, module3
+ * 
+ * 每个模块名称必须为合法的标识符,多个模块之间使用英文逗号(,)分隔,语句末尾必须以换行符结尾。 + * 本类的职责是识别并提取所有导入模块的名称,并将其封装为 {@link ImportNode} 节点,供后续语法树构建或语义分析阶段使用。 + */ +public class ImportParser { + + /** + * 解析 import 语句,并返回表示被导入模块的语法树节点列表。 + *

+ * 该方法会依次执行以下操作: + *

    + *
  1. 确认当前语句以关键字 {@code import} 开头。
  2. + *
  3. 确认后跟一个冒号 {@code :}。
  4. + *
  5. 解析至少一个模块名称(标识符),多个模块使用逗号分隔。
  6. + *
  7. 确认语句以换行符 {@code NEWLINE} 结束。
  8. + *
+ * 若语法不符合上述规则,将在解析过程中抛出异常。 + * + * @param ctx 表示当前解析器所处的上下文环境,包含词法流及语法状态信息。 + * @return 返回一个包含所有被导入模块的 {@link ImportNode} 实例列表。 + */ + public List parse(ParserContext ctx) { + // 期望第一个 token 是 "import" 关键字 + ctx.getTokens().expect("import"); + + // 紧接其后必须是冒号 ":" + ctx.getTokens().expect(":"); + + // 用于存储解析得到的 ImportNode 对象 + List imports = new ArrayList<>(); + + // 解析一个或多个模块名(标识符),允许使用逗号分隔多个模块 + do { + // 获取当前标识符类型的词法单元,并提取其原始词素 + String mod = ctx.getTokens() + .expectType(TokenType.IDENTIFIER) + .getLexeme(); + + // 创建 ImportNode 节点并加入列表 + imports.add(new ImportNode(mod)); + } while (ctx.getTokens().match(",")); // 如果匹配到逗号,继续解析下一个模块名 + + // 最后必须匹配换行符,标志 import 语句的结束 + ctx.getTokens().expectType(TokenType.NEWLINE); + + // 返回完整的 ImportNode 列表 + return imports; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java b/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java new file mode 100644 index 0000000..02ada73 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java @@ -0,0 +1,96 @@ +package org.jcnc.snow.compiler.parser.module; + +import org.jcnc.snow.compiler.lexer.token.TokenType; +import org.jcnc.snow.compiler.parser.base.TopLevelParser; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.context.TokenStream; +import org.jcnc.snow.compiler.parser.ast.ImportNode; +import org.jcnc.snow.compiler.parser.ast.ModuleNode; +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.parser.function.FunctionParser; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@code ModuleParser} 类负责解析源码中的模块定义结构,属于顶层结构解析器的一种。 + *

+ * 模块中可包含多个导入语句和函数定义,导入语句可在模块中任意位置出现, + * 同时支持空行,空行将被自动忽略,不影响语法结构的正确性。 + */ +public class ModuleParser implements TopLevelParser { + + /** + * 解析一个模块定义块,返回构建好的 {@link ModuleNode} 对象。 + *

+ * 本方法的语法流程包括: + *

    + *
  1. 匹配模块声明开头 {@code module: IDENTIFIER}。
  2. + *
  3. 收集模块体中的 import 语句与 function 定义,允许穿插空行。
  4. + *
  5. 模块结尾必须为 {@code end module},且后接换行符。
  6. + *
+ * 所有语法错误将在解析过程中抛出异常,以便准确反馈问题位置和原因。 + * + * @param ctx 当前解析器上下文,包含词法流、状态信息等。 + * @return 返回一个 {@link ModuleNode} 实例,表示完整模块的语法结构。 + * @throws IllegalStateException 当模块体中出现未识别的语句时抛出。 + */ + @Override + public ModuleNode parse(ParserContext ctx) { + // 获取当前上下文中提供的词法流 + TokenStream ts = ctx.getTokens(); + + // 期望模块声明以关键字 "module:" 开始 + ts.expect("module"); + ts.expect(":"); + + // 读取模块名称(要求为标识符类型的词法单元) + String name = ts.expectType(TokenType.IDENTIFIER).getLexeme(); + + // 模块声明必须以换行符结束 + ts.expectType(TokenType.NEWLINE); + + // 初始化模块的导入节点列表与函数节点列表 + List imports = new ArrayList<>(); + List functions = new ArrayList<>(); + + // 创建 import 与 function 的子解析器 + ImportParser importParser = new ImportParser(); + FunctionParser funcParser = new FunctionParser(); + + // 进入模块主体内容解析循环 + while (true) { + // 跳过所有空行(即连续的 NEWLINE) + if (ts.peek().getType() == TokenType.NEWLINE) { + ts.next(); + continue; + } + + // 若遇到 "end",则表明模块定义结束 + if ("end".equals(ts.peek().getLexeme())) { + break; + } + + // 根据当前行首关键字决定解析器的选择 + String lex = ts.peek().getLexeme(); + if ("import".equals(lex)) { + // 调用导入语句解析器,解析多个模块导入节点 + imports.addAll(importParser.parse(ctx)); + } else if ("function".equals(lex)) { + // 调用函数定义解析器,解析单个函数结构 + functions.add(funcParser.parse(ctx)); + } else { + // 遇到无法识别的语句开头,抛出异常并提供详细提示 + throw new IllegalStateException("Unexpected token in module: " + lex); + } + } + + // 确保模块体以 "end module" 结束 + ts.expect("end"); + ts.expect("module"); + ts.expectType(TokenType.NEWLINE); + + // 构建并返回完整的模块语法树节点 + return new ModuleNode(name, imports, functions); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java new file mode 100644 index 0000000..b99d09c --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java @@ -0,0 +1,74 @@ +package org.jcnc.snow.compiler.parser.statement; + +import org.jcnc.snow.compiler.lexer.token.TokenType; +import org.jcnc.snow.compiler.parser.ast.DeclarationNode; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser; + +/** + * {@code DeclarationStatementParser} 类负责解析变量声明语句,是语句级解析器的一部分。 + *

+ * 本解析器支持以下两种形式的声明语法: + *

{@code
+ * declare myVar:Integer
+ * declare myVar:Integer = 42 + 3
+ * }
+ * 其中: + *
    + *
  • {@code myVar} 为变量名(必须为标识符类型);
  • + *
  • {@code Integer} 为类型标注(必须为类型标记);
  • + *
  • 可选的初始化表达式由 {@link PrattExpressionParser} 解析;
  • + *
  • 每条声明语句必须以换行符({@code NEWLINE})结束。
  • + *
+ * 若语法不满足上述结构,将在解析过程中抛出异常。 + */ +public class DeclarationStatementParser implements StatementParser { + + /** + * 解析一条 {@code declare} 声明语句,并返回对应的抽象语法树节点 {@link DeclarationNode}。 + *

+ * 解析流程如下: + *

    + *
  1. 匹配关键字 {@code declare};
  2. + *
  3. 读取变量名称(标识符类型);
  4. + *
  5. 读取类型标注(在冒号后,要求为 {@code TYPE} 类型);
  6. + *
  7. 若存在 {@code =},则继续解析其后的表达式作为初始化值;
  8. + *
  9. 最终必须匹配 {@code NEWLINE} 表示语句结束。
  10. + *
+ * 若遇到非法语法结构,将触发异常并中断解析过程。 + * + * @param ctx 当前语法解析上下文,包含词法流、错误信息等。 + * @return 返回一个 {@link DeclarationNode} 节点,表示解析完成的声明语法结构。 + */ + @Override + public DeclarationNode parse(ParserContext ctx) { + // 声明语句必须以 "declare" 开头 + ctx.getTokens().expect("declare"); + + // 获取变量名称(标识符) + String name = ctx.getTokens() + .expectType(TokenType.IDENTIFIER) + .getLexeme(); + + // 类型标注的冒号分隔符 + ctx.getTokens().expect(":"); + + // 获取变量类型(类型标识符) + String type = ctx.getTokens() + .expectType(TokenType.TYPE) + .getLexeme(); + + // 可选的初始化表达式,若存在 "=",则解析等号右侧表达式 + ExpressionNode init = null; + if (ctx.getTokens().match("=")) { + init = new PrattExpressionParser().parse(ctx); + } + + // 声明语句必须以换行符结尾 + ctx.getTokens().expectType(TokenType.NEWLINE); + + // 返回构建好的声明语法树节点 + return new DeclarationNode(name, type, init); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java new file mode 100644 index 0000000..dd9df94 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java @@ -0,0 +1,70 @@ +package org.jcnc.snow.compiler.parser.statement; + +import org.jcnc.snow.compiler.lexer.token.TokenType; +import org.jcnc.snow.compiler.parser.ast.AssignmentNode; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.ast.ExpressionStatementNode; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.context.TokenStream; +import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser; + +/** + * {@code ExpressionStatementParser} 负责解析通用表达式语句,包括赋值语句和单一表达式语句。 + *

+ * 支持的语法结构如下: + *

{@code
+ * x = 1 + 2        // 赋值语句
+ * doSomething()    // 函数调用等普通表达式语句
+ * }
+ *
    + *
  • 若以标识符开头,且后接等号 {@code =},则视为赋值语句,解析为 {@link AssignmentNode}。
  • + *
  • 否则视为普通表达式,解析为 {@link ExpressionStatementNode}。
  • + *
  • 所有表达式语句必须以换行符 {@code NEWLINE} 结束。
  • + *
+ * 不允许以关键字或空行作为表达式的起始,若遇到非法开头,将抛出解析异常。 + */ +public class ExpressionStatementParser implements StatementParser { + + /** + * 解析一个表达式语句,根据上下文决定其为赋值或一般表达式。 + *

+ * 具体逻辑如下: + *

    + *
  1. 若当前行为标识符后接等号,则作为赋值处理。
  2. + *
  3. 否则解析整个表达式作为单独语句。
  4. + *
  5. 所有语句都必须以换行符结束。
  6. + *
  7. 若表达式以关键字或空行开头,将立即抛出异常,避免非法解析。
  8. + *
+ * + * @param ctx 当前解析上下文,提供词法流与状态信息。 + * @return 返回 {@link AssignmentNode} 或 {@link ExpressionStatementNode} 表示的语法节点。 + * @throws IllegalStateException 若表达式起始为关键字或语法非法。 + */ + @Override + public StatementNode parse(ParserContext ctx) { + TokenStream ts = ctx.getTokens(); + + // 快速检查:若遇空行或关键字开头,不可作为表达式语句 + if (ts.peek().getType() == TokenType.NEWLINE || ts.peek().getType() == TokenType.KEYWORD) { + throw new IllegalStateException("Cannot parse expression starting with keyword: " + ts.peek().getLexeme()); + } + + // 处理赋值语句:格式为 identifier = expression + if (ts.peek().getType() == TokenType.IDENTIFIER + && ts.peek(1).getLexeme().equals("=")) { + + String varName = ts.next().getLexeme(); // 消耗标识符 + ts.expect("="); // 消耗等号 + ExpressionNode value = new PrattExpressionParser().parse(ctx); // 解析表达式 + ts.expectType(TokenType.NEWLINE); // 语句必须以换行符结束 + return new AssignmentNode(varName, value); // 返回赋值节点 + } + + // 处理普通表达式语句,如函数调用、字面量、运算表达式等 + ExpressionNode expr = new PrattExpressionParser().parse(ctx); + ts.expectType(TokenType.NEWLINE); // 语句必须以换行符结束 + return new ExpressionStatementNode(expr); // 返回表达式语句节点 + } + +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/IfStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/IfStatementParser.java new file mode 100644 index 0000000..f0e2498 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/IfStatementParser.java @@ -0,0 +1,125 @@ +package org.jcnc.snow.compiler.parser.statement; + +import org.jcnc.snow.compiler.lexer.token.Token; +import org.jcnc.snow.compiler.lexer.token.TokenType; +import org.jcnc.snow.compiler.parser.ast.IfNode; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser; +import org.jcnc.snow.compiler.parser.factory.StatementParserFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@code IfStatementParser} 类负责解析 if 条件语句,是语句级解析器中的条件分支处理器。 + *

+ * 本解析器支持以下结构的条件语法: + *

{@code
+ * if  then
+ *     
+ * [else
+ *     ]
+ * end if
+ * }
+ * 其中: + *
    + *
  • {@code } 为任意可解析的布尔或数值表达式,使用 {@link PrattExpressionParser} 解析;
  • + *
  • {@code } 与 {@code } 可包含多条语句,自动跳过空行;
  • + *
  • {@code else} 分支为可选,若存在,必须紧跟换行与语句;
  • + *
  • {@code end if} 为终止标识,表示整个 if 语句块的结束。
  • + *
+ * 所有语句的实际解析由 {@link StatementParserFactory} 根据关键词动态分派处理。 + */ +public class IfStatementParser implements StatementParser { + + /** + * 解析一条完整的 if 条件语句,返回语法树中对应的 {@link IfNode} 节点。 + *

+ * 本方法支持 then 分支和可选的 else 分支,并确保以 {@code end if} 正确结尾。 + * 在解析过程中自动跳过空行;遇到未知关键字或不符合预期的 token 时会抛出异常。 + * + * @param ctx 当前的语法解析上下文,包含 token 流和语义环境。 + * @return 构造完成的 {@link IfNode},包含条件表达式、then 分支和 else 分支语句列表。 + * @throws IllegalStateException 若语法结构不完整或存在非法 token。 + */ + @Override + public IfNode parse(ParserContext ctx) { + var ts = ctx.getTokens(); // 获取 token 流引用 + + // 消耗起始关键字 "if" + ts.expect("if"); + + // 使用 Pratt 算法解析 if 条件表达式 + var condition = new PrattExpressionParser().parse(ctx); + + // 条件表达式后必须紧跟 "then" 和换行 + ts.expect("then"); + ts.expectType(TokenType.NEWLINE); + + // 初始化 then 和 else 分支语句列表 + List thenBranch = new ArrayList<>(); + List elseBranch = new ArrayList<>(); + + // ------------------------- + // 解析 THEN 分支语句块 + // ------------------------- + while (true) { + Token peek = ts.peek(); + + // 跳过空行 + if (peek.getType() == TokenType.NEWLINE) { + ts.next(); + continue; + } + + // 遇到 else 或 end 表示 then 分支结束 + if (peek.getType() == TokenType.KEYWORD && + (peek.getLexeme().equals("else") || peek.getLexeme().equals("end"))) { + break; + } + + // 获取当前语句的关键字,调用工厂获取对应解析器 + String keyword = peek.getType() == TokenType.KEYWORD ? peek.getLexeme() : ""; + StatementNode stmt = StatementParserFactory.get(keyword).parse(ctx); + thenBranch.add(stmt); + } + + // ------------------------- + // 解析 ELSE 分支语句块(可选) + // ------------------------- + if (ts.peek().getLexeme().equals("else")) { + ts.next(); // 消耗 "else" + ts.expectType(TokenType.NEWLINE); // 消耗换行符 + + while (true) { + Token peek = ts.peek(); + + // 跳过空行 + if (peek.getType() == TokenType.NEWLINE) { + ts.next(); + continue; + } + + // "end" 表示 else 分支结束 + if (peek.getType() == TokenType.KEYWORD && peek.getLexeme().equals("end")) { + break; + } + + String keyword = peek.getType() == TokenType.KEYWORD ? peek.getLexeme() : ""; + StatementNode stmt = StatementParserFactory.get(keyword).parse(ctx); + elseBranch.add(stmt); + } + } + + // ------------------------- + // 统一结束处理:end if + // ------------------------- + ts.expect("end"); + ts.expect("if"); + ts.expectType(TokenType.NEWLINE); + + // 构建并返回 IfNode,包含条件、then 分支和 else 分支 + return new IfNode(condition, thenBranch, elseBranch); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/LoopStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/LoopStatementParser.java new file mode 100644 index 0000000..583b5c9 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/LoopStatementParser.java @@ -0,0 +1,145 @@ +package org.jcnc.snow.compiler.parser.statement; + +import org.jcnc.snow.compiler.lexer.token.TokenType; +import org.jcnc.snow.compiler.parser.ast.AssignmentNode; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.ast.LoopNode; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.context.TokenStream; +import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser; +import org.jcnc.snow.compiler.parser.factory.StatementParserFactory; +import org.jcnc.snow.compiler.parser.utils.FlexibleSectionParser; +import org.jcnc.snow.compiler.parser.utils.ParserUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * {@code LoopStatementParser} 类负责解析自定义结构化的 {@code loop} 语句块。 + *

+ * 该语法结构参考了传统的 for-loop,并将其拆解为命名的语义区块: + *

{@code
+ * loop:
+ *   initializer:
+ *     declare i:int = 0
+ *   condition:
+ *     i < 10
+ *   update:
+ *     i = i + 1
+ *   body:
+ *     print(i)
+ * end body
+ * end loop
+ * }
+ * + * 各区块说明: + *
    + *
  • {@code initializer}:初始化语句,通常为变量声明。
  • + *
  • {@code condition}:循环判断条件,必须为布尔或数值表达式。
  • + *
  • {@code update}:每轮执行后更新逻辑,通常为赋值语句。
  • + *
  • {@code body}:主执行语句块,支持任意多条语句。
  • + *
+ * 本类依赖 {@link FlexibleSectionParser} 实现各区块的统一处理,确保结构明确、可扩展。 + */ +public class LoopStatementParser implements StatementParser { + + /** + * 解析 {@code loop} 语句块,构建出对应的 {@link LoopNode} 抽象语法树节点。 + *

+ * 本方法会按顺序检查各个命名区块(可乱序书写),并分别绑定其对应语义解析器: + *

    + *
  • 通过 {@link ParserUtils#matchHeader} 匹配区块开头;
  • + *
  • 通过 {@link FlexibleSectionParser} 派发区块逻辑;
  • + *
  • 通过 {@link StatementParserFactory} 调用实际语句解析;
  • + *
  • 最后以 {@code end loop} 表示结构终止。
  • + *
+ * + * @param ctx 当前解析上下文。 + * @return {@link LoopNode},包含初始化、条件、更新与循环体等信息。 + */ + @Override + public LoopNode parse(ParserContext ctx) { + TokenStream ts = ctx.getTokens(); + + // 匹配 loop: 起始语法 + ParserUtils.matchHeader(ts, "loop"); + + // 使用数组模拟引用以便在 lambda 中写入(Java 不支持闭包内修改局部变量) + final StatementNode[] initializer = new StatementNode[1]; + final ExpressionNode[] condition = new ExpressionNode[1]; + final AssignmentNode[] update = new AssignmentNode[1]; + final List body = new ArrayList<>(); + + // 定义各命名区块的识别与处理逻辑 + Map sections = new HashMap<>(); + + // initializer 区块:仅支持一条语句,通常为 declare + sections.put("initializer", new FlexibleSectionParser.SectionDefinition( + ts1 -> ts1.peek().getLexeme().equals("initializer"), + (ctx1, ts1) -> { + ParserUtils.matchHeader(ts1, "initializer"); + initializer[0] = StatementParserFactory.get(ts1.peek().getLexeme()).parse(ctx1); + ParserUtils.skipNewlines(ts1); + } + )); + + // condition 区块:支持任意可解析为布尔的表达式 + sections.put("condition", new FlexibleSectionParser.SectionDefinition( + ts1 -> ts1.peek().getLexeme().equals("condition"), + (ctx1, ts1) -> { + ParserUtils.matchHeader(ts1, "condition"); + condition[0] = new PrattExpressionParser().parse(ctx1); + ts1.expectType(TokenType.NEWLINE); + ParserUtils.skipNewlines(ts1); + } + )); + + // update 区块:目前仅支持单一变量赋值语句 + sections.put("update", new FlexibleSectionParser.SectionDefinition( + ts1 -> ts1.peek().getLexeme().equals("update"), + (ctx1, ts1) -> { + ParserUtils.matchHeader(ts1, "update"); + String varName = ts1.expectType(TokenType.IDENTIFIER).getLexeme(); + ts1.expect("="); + ExpressionNode expr = new PrattExpressionParser().parse(ctx1); + ts1.expectType(TokenType.NEWLINE); + update[0] = new AssignmentNode(varName, expr); + ParserUtils.skipNewlines(ts1); + } + )); + + // body 区块:支持多条语句,直到遇到 end body + sections.put("body", new FlexibleSectionParser.SectionDefinition( + ts1 -> ts1.peek().getLexeme().equals("body"), + (ctx1, ts1) -> { + ParserUtils.matchHeader(ts1, "body"); + + while (!(ts1.peek().getLexeme().equals("end") && + ts1.peek(1).getLexeme().equals("body"))) { + String keyword = ts1.peek().getType() == TokenType.KEYWORD + ? ts1.peek().getLexeme() + : ""; + body.add(StatementParserFactory.get(keyword).parse(ctx1)); + ParserUtils.skipNewlines(ts1); + } + + ts1.expect("end"); + ts1.expect("body"); + ts1.expectType(TokenType.NEWLINE); + ParserUtils.skipNewlines(ts1); + } + )); + + // 使用通用区块解析器处理各命名结构块 + FlexibleSectionParser.parse(ctx, ts, sections); + + // 解析结尾的 end loop 标记 + ParserUtils.matchFooter(ts, "loop"); + + // 返回构造完成的 LoopNode + return new LoopNode(initializer[0], condition[0], update[0], body); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/ReturnStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/ReturnStatementParser.java new file mode 100644 index 0000000..8a6a6a2 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/ReturnStatementParser.java @@ -0,0 +1,54 @@ +package org.jcnc.snow.compiler.parser.statement; + +import org.jcnc.snow.compiler.lexer.token.TokenType; +import org.jcnc.snow.compiler.parser.ast.ReturnNode; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser; + +/** + * {@code ReturnStatementParser} 负责解析 return 语句,是语句级解析器的一部分。 + *

+ * 支持以下两种 return 语句形式: + *

{@code
+ * return             // 无返回值
+ * return expression  // 带返回值
+ * }
+ * 所有 return 语句都必须以换行符({@code NEWLINE})结束,返回值表达式(若存在)由 {@link PrattExpressionParser} 负责解析。 + * 若语法结构不满足要求,将在解析过程中抛出异常。 + */ +public class ReturnStatementParser implements StatementParser { + + /** + * 解析一条 return 语句,并返回对应的 {@link ReturnNode} 抽象语法树节点。 + *

+ * 解析逻辑如下: + *

    + *
  1. 匹配起始关键字 {@code return}。
  2. + *
  3. 判断其后是否为 {@code NEWLINE},若否则表示存在返回值表达式。
  4. + *
  5. 使用 {@link PrattExpressionParser} 解析返回值表达式(若存在)。
  6. + *
  7. 最后匹配换行符,标志语句结束。
  8. + *
+ * + * @param ctx 当前解析上下文,包含词法流与语法状态。 + * @return 构造完成的 {@link ReturnNode},表示 return 语句的语法树节点。 + */ + @Override + public ReturnNode parse(ParserContext ctx) { + // 消耗 "return" 关键字 + ctx.getTokens().expect("return"); + + ExpressionNode expr = null; + + // 如果下一 token 不是换行符,说明存在返回值表达式 + if (ctx.getTokens().peek().getType() != TokenType.NEWLINE) { + expr = new PrattExpressionParser().parse(ctx); + } + + // return 语句必须以换行符结束 + ctx.getTokens().expectType(TokenType.NEWLINE); + + // 构建并返回 ReturnNode(可能为空表达式) + return new ReturnNode(expr); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/StatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/StatementParser.java new file mode 100644 index 0000000..edcc075 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/StatementParser.java @@ -0,0 +1,26 @@ +package org.jcnc.snow.compiler.parser.statement; + +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; + +/** + * {@code StatementParser} 是所有语句解析器的通用接口。 + *

+ * 其职责是从给定的 {@link ParserContext} 中读取并分析当前语句,构造并返回相应的抽象语法树节点。 + * 所有语句类型(如变量声明、赋值语句、控制结构、函数返回等)应提供对应的实现类。 + * + *

+ * 通常,此接口的实现由 {@code StatementParserFactory} 根据当前关键字动态派发,用于解析模块体、 + * 条件分支、循环体或其他语句块中的单条语句。 + */ +public interface StatementParser { + + /** + * 解析一条语句,将其从词法表示转换为结构化语法树节点。 + * + * @param ctx 当前的解析上下文,提供 token 流、状态与符号环境等。 + * @return 表示该语句的 AST 节点,类型为 {@link StatementNode} 或其子类。 + * @throws IllegalStateException 若语法非法或结构不完整。 + */ + StatementNode parse(ParserContext ctx); +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/top/ScriptTopLevelParser.java b/src/main/java/org/jcnc/snow/compiler/parser/top/ScriptTopLevelParser.java new file mode 100644 index 0000000..f669c8e --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/top/ScriptTopLevelParser.java @@ -0,0 +1,25 @@ +package org.jcnc.snow.compiler.parser.top; + +import org.jcnc.snow.compiler.parser.base.TopLevelParser; +import org.jcnc.snow.compiler.parser.ast.base.Node; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.factory.StatementParserFactory; +import org.jcnc.snow.compiler.parser.statement.StatementParser; + +/** + * {@code ScriptTopLevelParser} 允许在无 module 包裹的情况下 + * 直接解析单条顶层语句(脚本模式)。 + *

+ * 解析得到的 {@link StatementNode} 将在 IR 阶段被封装成 _start 函数。 + */ +public class ScriptTopLevelParser implements TopLevelParser { + + @Override + public Node parse(ParserContext ctx) { + String first = ctx.getTokens().peek().getLexeme(); + StatementParser sp = StatementParserFactory.get(first); + StatementNode stmt = sp.parse(ctx); + return stmt; // StatementNode 亦是 Node + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java new file mode 100644 index 0000000..df36075 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java @@ -0,0 +1,198 @@ +package org.jcnc.snow.compiler.parser.utils; + +import org.jcnc.snow.compiler.parser.ast.*; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.ast.base.Node; + +import java.util.*; + +/** + * {@code ASTJsonSerializer} 是抽象语法树(AST)序列化工具类。 + *

+ * 该工具可将编译器内部构建的 AST 节点对象转换为通用的 {@code Map} 和 {@code List} 结构, + * 并可借助 {@code JSONParser.toJson(Object)} 方法将其序列化为 JSON 字符串,用于调试、 + * 可视化或跨语言数据传输。 + *

+ * 支持的节点类型包括: + *

    + *
  • {@link ModuleNode}
  • + *
  • {@link FunctionNode}
  • + *
  • {@link DeclarationNode}
  • + *
  • {@link AssignmentNode}
  • + *
  • {@link IfNode}
  • + *
  • {@link LoopNode}
  • + *
  • {@link ReturnNode}
  • + *
  • {@link ExpressionStatementNode}
  • + *
  • 各类 {@link ExpressionNode} 子类型,如 {@code BinaryExpressionNode}, {@code IdentifierNode} 等
  • + *
+ */ +public class ASTJsonSerializer { + + /** + * 创建包含 {@code type} 字段的节点 Map,用于标识节点类型。 + * + * @param type 节点类型字符串。 + * @return 一个初始化后的 Map 实例。 + */ + private static Map newNodeMap(String type) { + Map m = new LinkedHashMap<>(); + m.put("type", type); + return m; + } + + /** + * 构建表达式节点的 Map 表示,支持动态键值对传参。 + * + * @param type 表达式类型。 + * @param kv 可变参数(key-value 键值对)。 + * @return 表示表达式节点的 Map。 + */ + private static Map exprMap(String type, Object... kv) { + Map m = new LinkedHashMap<>(); + m.put("type", type); + for (int i = 0; i < kv.length; i += 2) { + m.put((String) kv[i], kv[i + 1]); + } + return m; + } + + /** + * 将 AST 根节点列表转换为 JSON 字符串。 + * + * @param ast 表示顶层语法树结构的节点列表。 + * @return 对应的 JSON 字符串表示形式。 + */ + public static String toJsonString(List ast) { + List list = new ArrayList<>(ast.size()); + for (Node n : ast) { + list.add(nodeToMap(n)); + } + return JSONParser.toJson(list); + } + + /** + * 将任意 AST 节点递归转换为 Map/List 结构。 + * + * @param n 要转换的 AST 节点。 + * @return 以 Map/List 表示的结构化数据。 + */ + private static Object nodeToMap(Node n) { + return switch (n) { + // 模块节点 + case ModuleNode(String name, List imports, List functions) -> { + Map map = newNodeMap("Module"); + map.put("name", name); + List imps = new ArrayList<>(imports.size()); + for (ImportNode imp : imports) { + imps.add(Map.of("type", "Import", "module", imp.moduleName())); + } + map.put("imports", imps); + List funcs = new ArrayList<>(functions.size()); + for (FunctionNode f : functions) { + funcs.add(nodeToMap(f)); + } + map.put("functions", funcs); + yield map; + } + // 函数定义节点 + case FunctionNode f -> { + Map map = newNodeMap("Function"); + map.put("name", f.name()); + List params = new ArrayList<>(f.parameters().size()); + for (var p : f.parameters()) { + params.add(Map.of("name", p.name(), "type", p.type())); + } + map.put("parameters", params); + map.put("returnType", f.returnType()); + List body = new ArrayList<>(f.body().size()); + for (Node stmt : f.body()) { + body.add(nodeToMap(stmt)); + } + map.put("body", body); + yield map; + } + // 变量声明节点 + case DeclarationNode d -> { + Map map = newNodeMap("Declaration"); + map.put("name", d.getName()); + map.put("varType", d.getType()); + map.put("initializer", d.getInitializer().map(ASTJsonSerializer::exprToMap).orElse(null)); + yield map; + } + // 赋值语句节点 + case AssignmentNode a -> exprMap("Assignment", + "variable", a.variable(), + "value", exprToMap(a.value()) + ); + // 条件语句节点 + case IfNode i -> { + Map map = newNodeMap("If"); + map.put("condition", exprToMap(i.condition())); + List thenList = new ArrayList<>(i.thenBranch().size()); + for (Node stmt : i.thenBranch()) thenList.add(nodeToMap(stmt)); + map.put("then", thenList); + if (!i.elseBranch().isEmpty()) { + List elseList = new ArrayList<>(i.elseBranch().size()); + for (Node stmt : i.elseBranch()) elseList.add(nodeToMap(stmt)); + map.put("else", elseList); + } + yield map; + } + // 循环语句节点 + case LoopNode l -> { + Map map = newNodeMap("Loop"); + map.put("initializer", l.initializer() != null ? nodeToMap(l.initializer()) : null); + map.put("condition", l.condition() != null ? exprToMap(l.condition()) : null); + map.put("update", l.update() != null ? nodeToMap(l.update()) : null); + List body = new ArrayList<>(l.body().size()); + for (Node stmt : l.body()) body.add(nodeToMap(stmt)); + map.put("body", body); + yield map; + } + // return 语句节点 + case ReturnNode r -> { + Map map = newNodeMap("Return"); + r.getExpression().ifPresent(expr -> map.put("value", exprToMap(expr))); + yield map; + } + // 表达式语句节点 + case ExpressionStatementNode e -> exprMap("ExpressionStatement", + "expression", exprToMap(e.expression()) + ); + // 通用表达式节点 + case ExpressionNode expressionNode -> exprToMap(expressionNode); + // 其他类型(兜底处理) + default -> Map.of("type", n.getClass().getSimpleName()); + }; + } + + /** + * 将表达式类型节点转换为 Map 表示形式。 + * + * @param expr 表达式 AST 节点。 + * @return 表示该表达式的 Map。 + */ + private static Object exprToMap(ExpressionNode expr) { + return switch (expr) { + case BinaryExpressionNode(ExpressionNode left, String operator, ExpressionNode right) -> exprMap("BinaryExpression", + "left", exprToMap(left), + "operator", operator, + "right", exprToMap(right) + ); + case IdentifierNode(String name) -> exprMap("Identifier", "name", name); + case NumberLiteralNode(String value) -> exprMap("NumberLiteral", "value", value); + case StringLiteralNode(String value) -> exprMap("StringLiteral", "value", value); + case CallExpressionNode(ExpressionNode callee, List arguments) -> { + List args = new ArrayList<>(arguments.size()); + for (ExpressionNode arg : arguments) args.add(exprToMap(arg)); + yield exprMap("CallExpression", "callee", exprToMap(callee), "arguments", args); + } + case MemberExpressionNode(ExpressionNode object, String member) -> exprMap("MemberExpression", + "object", exprToMap(object), + "member", member + ); + // 默认兜底处理:只写类型 + default -> Map.of("type", expr.getClass().getSimpleName()); + }; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/FlexibleSectionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/FlexibleSectionParser.java new file mode 100644 index 0000000..c319d4f --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/FlexibleSectionParser.java @@ -0,0 +1,121 @@ +package org.jcnc.snow.compiler.parser.utils; + +import org.jcnc.snow.compiler.lexer.token.TokenType; +import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.context.TokenStream; + +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Predicate; + +/** + * {@code FlexibleSectionParser} 是一个通用的语法块解析工具。 + *

+ * 该工具支持解析由关键字标识的多段结构化区块内容,常用于解析函数、类、模块、循环等语法单元中的命名子结构。 + * 相比传统硬编码方式,提供更灵活、可组合的解析能力,允许解析器模块动态注册处理逻辑,而非将所有逻辑写死在主流程中。 + * + *

典型应用包括: + *

    + *
  • 函数体解析中的 {@code params}、{@code returns}、{@code body} 等部分
  • + *
  • 模块定义中的 {@code imports}、{@code functions} 等部分
  • + *
  • 用户自定义 DSL 的可扩展语法结构
  • + *
+ * + *

该工具具备以下能力: + *

    + *
  • 自动跳过注释与空行
  • + *
  • 根据区块名称调用外部提供的解析器
  • + *
  • 支持终止标志(如 {@code end})来退出解析流程
  • + *
+ */ +public class FlexibleSectionParser { + + /** + * 启动结构化区块的统一解析流程。 + *

+ * 每次调用会: + *

    + *
  1. 从 token 流中跳过空行与注释
  2. + *
  3. 依照当前 token 判断是否匹配某个区块
  4. + *
  5. 调用对应 {@link SectionDefinition} 执行区块解析逻辑
  6. + *
  7. 若遇到 {@code end} 关键字,则终止解析过程
  8. + *
  9. 若当前 token 不匹配任何已注册区块,抛出异常
  10. + *
+ * + * @param ctx 当前解析上下文,提供语法环境与作用域信息 + * @param tokens 当前 token 流 + * @param sectionDefinitions 各个区块的定义映射(key 为关键字,value 为判断 + 解析逻辑组合) + * @throws RuntimeException 若出现无法识别的关键字或未满足的匹配条件 + */ + public static void parse(ParserContext ctx, + TokenStream tokens, + Map sectionDefinitions) { + + // 跳过开头的注释或空行 + skipCommentsAndNewlines(tokens); + + while (true) { + // 跳过当前区块之间的空白与注释 + skipCommentsAndNewlines(tokens); + + String keyword = tokens.peek().getLexeme(); + + // 结束关键字表示解析流程终止 + if ("end".equals(keyword)) { + break; + } + + // 查找匹配的区块定义 + SectionDefinition definition = sectionDefinitions.get(keyword); + if (definition != null && definition.condition().test(tokens)) { + definition.parser().accept(ctx, tokens); // 执行解析逻辑 + } else { + throw new RuntimeException("未识别的关键字或条件不满足: " + keyword); + } + } + } + + /** + * 跳过连续出现的注释行或空行(NEWLINE)。 + *

+ * 该方法用于在区块之间清理无效 token,避免影响结构判断。 + * + * @param tokens 当前 token 流 + */ + private static void skipCommentsAndNewlines(TokenStream tokens) { + while (true) { + TokenType type = tokens.peek().getType(); + if (type == TokenType.COMMENT || type == TokenType.NEWLINE) { + tokens.next(); // 跳过注释或换行 + continue; + } + break; + } + } + + /** + * 表示一个结构区块的定义,包含匹配条件与解析器。 + *

+ * 每个区块由两部分组成: + *

    + *
  • {@code condition}:用于判断当前 token 是否应进入该区块
  • + *
  • {@code parser}:该区块对应的实际解析逻辑
  • + *
+ * 可实现懒加载、多语言支持或 DSL 的结构化扩展。 + * + * @param condition 判断是否触发该区块的谓词函数 + * @param parser 区块解析逻辑(消费语法上下文与 token 流) + */ + public record SectionDefinition(Predicate condition, + BiConsumer parser) { + @Override + public Predicate condition() { + return condition; + } + + @Override + public BiConsumer parser() { + return parser; + } + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/JSONParser.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/JSONParser.java new file mode 100644 index 0000000..ce30193 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/JSONParser.java @@ -0,0 +1,370 @@ +package org.jcnc.snow.compiler.parser.utils; + +import java.util.*; +import java.util.Map.Entry; + +/** + * JSON 工具类,提供线程安全、可重用的解析与序列化功能 + *

+ * - 解析:将合法的 JSON 文本转换为 Java 原生对象(Map、List、String、Number、Boolean 或 null) + * - 序列化:将 Java 原生对象转换为符合 JSON 标准的字符串 + *

+ * 设计要点: + * 1. 使用静态方法作为唯一入口,避免状态共享导致的线程安全问题 + * 2. 解析器内部使用 char[] 缓冲区,提高访问性能 + * 3. 维护行列号信息,抛出异常时能精确定位错误位置 + * 4. 序列化器基于 StringBuilder,预分配容量,减少中间字符串创建 + */ +public class JSONParser { + + private JSONParser() {} + + /** + * 将 JSON 文本解析为对应的 Java 对象 + * @param input JSON 格式字符串 + * @return 对应的 Java 原生对象: + * - JSON 对象 -> Map + * - JSON 数组 -> List + * - JSON 字符串 -> String + * - JSON 数值 -> Long 或 Double + * - JSON 布尔 -> Boolean + * - JSON null -> null + * @throws RuntimeException 如果遇到语法错误或多余字符,异常消息中包含行列信息 + */ + public static Object parse(String input) { + return new Parser(input).parseInternal(); + } + + /** + * 将 Java 原生对象序列化为 JSON 字符串 + * @param obj 支持的类型:Map、Collection、String、Number、Boolean 或 null + * @return 符合 JSON 规范的字符串 + */ + public static String toJson(Object obj) { + return Writer.write(obj); + } + + // ======= 内部解析器 ======= + /** + * 负责将 char[] 缓冲区中的 JSON 文本解析为 Java 对象 + */ + private static class Parser { + /** 输入缓冲区 */ + private final char[] buf; + /** 当前解析到的位置索引 */ + private int pos; + /** 当前字符所在行号,从 1 开始 */ + private int line; + /** 当前字符所在列号,从 1 开始 */ + private int col; + + /** + * 构造解析器,初始化缓冲区和行列信息 + * @param input 待解析的 JSON 文本 + */ + Parser(String input) { + this.buf = input.toCharArray(); + this.pos = 0; + this.line = 1; + this.col = 1; + } + + /** + * 入口方法,跳过空白后调用 parseValue,再校验尾部无多余字符 + */ + Object parseInternal() { + skipWhitespace(); + Object value = parseValue(); + skipWhitespace(); + if (pos < buf.length) { + error("存在无法识别的多余字符"); + } + return value; + } + + /** + * 根据下一个字符决定解析哪种 JSON 值 + */ + private Object parseValue() { + skipWhitespace(); + if (match("null")) return null; + if (match("true")) return Boolean.TRUE; + if (match("false")) return Boolean.FALSE; + char c = currentChar(); + if (c == '"') return parseString(); + if (c == '{') return parseObject(); + if (c == '[') return parseArray(); + if (c == '-' || isDigit(c)) return parseNumber(); + error("遇到意外字符 '" + c + "'"); + return null; // 永不到达 + } + + /** + * 解析 JSON 对象,返回 Map + */ + private Map parseObject() { + expect('{'); // 跳过 '{' + skipWhitespace(); + Map map = new LinkedHashMap<>(); + // 空对象 {} + if (currentChar() == '}') { + advance(); // 跳过 '}' + return map; + } + // 多成员对象解析 + while (true) { + skipWhitespace(); + String key = parseString(); // 解析键 + skipWhitespace(); expect(':'); skipWhitespace(); + Object val = parseValue(); // 解析值 + map.put(key, val); + skipWhitespace(); + if (currentChar() == '}') { + advance(); // 跳过 '}' + break; + } + expect(','); skipWhitespace(); + } + return map; + } + + /** + * 解析 JSON 数组,返回 List + */ + private List parseArray() { + expect('['); + skipWhitespace(); + List list = new ArrayList<>(); + // 空数组 [] + if (currentChar() == ']') { + advance(); // 跳过 ']' + return list; + } + // 多元素数组解析 + while (true) { + skipWhitespace(); + list.add(parseValue()); + skipWhitespace(); + if (currentChar() == ']') { + advance(); + break; + } + expect(','); skipWhitespace(); + } + return list; + } + + /** + * 解析 JSON 字符串文字,处理转义字符 + */ + private String parseString() { + expect('"'); // 跳过开头 '"' + StringBuilder sb = new StringBuilder(); + while (true) { + char c = currentChar(); + if (c == '"') { + advance(); // 跳过结束 '"' + break; + } + if (c == '\\') { + advance(); // 跳过 '\' + c = currentChar(); + switch (c) { + case '"': sb.append('"'); break; + case '\\': sb.append('\\'); break; + case '/': sb.append('/'); break; + case 'b': sb.append('\b'); break; + case 'f': sb.append('\f'); break; + case 'n': sb.append('\n'); break; + case 'r': sb.append('\r'); break; + case 't': sb.append('\t'); break; + case 'u': // 解析 Unicode 转义 + String hex = new String(buf, pos+1, 4); + sb.append((char) Integer.parseInt(hex, 16)); + pos += 4; col += 4; + break; + default: + error("无效转义字符 '\\" + c + "'"); + } + advance(); + } else { + sb.append(c); + advance(); + } + } + return sb.toString(); + } + + /** + * 解析 JSON 数值,支持整数、浮点及科学计数法 + */ + private Number parseNumber() { + int start = pos; + if (currentChar() == '-') advance(); + while (isDigit(currentChar())) advance(); + if (currentChar() == '.') { + do advance(); + while (isDigit(currentChar())); + } + if (currentChar() == 'e' || currentChar() == 'E') { + advance(); + if (currentChar() == '+' || currentChar() == '-') advance(); + while (isDigit(currentChar())) advance(); + } + String num = new String(buf, start, pos - start); + // 判断返回 Long 还是 Double + if (num.indexOf('.') >= 0 || num.indexOf('e') >= 0 || num.indexOf('E') >= 0) { + return Double.parseDouble(num); + } + try { + return Long.parseLong(num); + } catch (NumberFormatException e) { + return Double.parseDouble(num); + } + } + + /** + * 跳过所有空白字符,支持空格、制表符、回车、换行 + */ + private void skipWhitespace() { + while (pos < buf.length) { + char c = buf[pos]; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { + advance(); + } else { + break; + } + } + } + + /** + * 获取当前位置字符,超出范围返回 '\0' + */ + private char currentChar() { + return pos < buf.length ? buf[pos] : '\0'; + } + + /** + * 推进到下一个字符,并更新行列信息 + */ + private void advance() { + if (pos < buf.length) { + if (buf[pos] == '\n') { + line++; col = 1; + } else { + col++; + } + pos++; + } + } + + /** + * 验证当前位置字符等于预期字符,否则抛出错误 + */ + private void expect(char c) { + if (currentChar() != c) { + error("期望 '" + c + "',但遇到 '" + currentChar() + "'"); + } + advance(); + } + + /** + * 尝试匹配给定字符串,匹配成功则移动位置并返回 true + */ + private boolean match(String s) { + int len = s.length(); + if (pos + len > buf.length) return false; + for (int i = 0; i < len; i++) { + if (buf[pos + i] != s.charAt(i)) return false; + } + for (int i = 0; i < len; i++) advance(); + return true; + } + + /** + * 判断字符是否为数字 + */ + private boolean isDigit(char c) { + return c >= '0' && c <= '9'; + } + + /** + * 抛出带行列定位的解析错误 + */ + private void error(String msg) { + throw new RuntimeException("Error at line " + line + ", column " + col + ": " + msg); + } + } + + // ======= 内部序列化器 ======= + /** + * 负责高效地将 Java 对象写为 JSON 文本 + */ + private static class Writer { + /** 默认 StringBuilder 初始容量,避免频繁扩容 */ + private static final int DEFAULT_CAPACITY = 1024; + + /** + * 入口方法,根据 obj 类型分派写入逻辑 + */ + static String write(Object obj) { + StringBuilder sb = new StringBuilder(DEFAULT_CAPACITY); + writeValue(obj, sb); + return sb.toString(); + } + + /** + * 根据对象类型选择合适的写入方式 + */ + private static void writeValue(Object obj, StringBuilder sb) { + if (obj == null) { + sb.append("null"); + } else if (obj instanceof String) { + quote((String) obj, sb); + } else if (obj instanceof Number || obj instanceof Boolean) { + sb.append(obj); + } else if (obj instanceof Map) { + sb.append('{'); + boolean first = true; + for (Entry e : ((Map) obj).entrySet()) { + if (!first) sb.append(','); + first = false; + quote(e.getKey().toString(), sb); + sb.append(':'); + writeValue(e.getValue(), sb); + } + sb.append('}'); + } else if (obj instanceof Collection) { + sb.append('['); + boolean first = true; + for (Object item : (Collection) obj) { + if (!first) sb.append(','); + first = false; + writeValue(item, sb); + } + sb.append(']'); + } else { + // 其他类型,使用 toString 并加引号 + quote(obj.toString(), sb); + } + } + + /** + * 为字符串添加双引号并转义必须的字符 + */ + private static void quote(String s, StringBuilder sb) { + sb.append('"'); + for (char c : s.toCharArray()) { + switch (c) { + case '\\': sb.append("\\\\"); break; + case '"': sb.append("\\\""); break; + case '\n': sb.append("\\n"); break; + case '\r': sb.append("\\r"); break; + case '\t': sb.append("\\t"); break; + default: sb.append(c); + } + } + sb.append('"'); + } + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/JsonFormatter.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/JsonFormatter.java new file mode 100644 index 0000000..9ee8744 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/JsonFormatter.java @@ -0,0 +1,77 @@ +package org.jcnc.snow.compiler.parser.utils; + +/** + * JSON 格式化工具类。 + * 提供将紧凑 JSON 字符串美化为带缩进和换行的易读格式的方法。 + */ +public class JsonFormatter { + + /** + * 对一个紧凑的 JSON 字符串进行缩进美化。 + * 例如: + *
{@code
+     * {"a":1,"b":[2,3]} →
+     * {
+     *   "a": 1,
+     *   "b": [
+     *     2,
+     *     3
+     *   ]
+     * }
+     * }
+ * + * @param json 紧凑的 JSON 字符串。 + * @return 格式化后的 JSON 字符串,带有缩进与换行。 + */ + public static String prettyPrint(String json) { + StringBuilder sb = new StringBuilder(); + int indent = 0; + boolean inQuotes = false; + + for (int i = 0; i < json.length(); i++) { + char c = json.charAt(i); + + // 检查是否进入或退出字符串(忽略转义的引号) + if (c == '"' && (i == 0 || json.charAt(i - 1) != '\\')) { + inQuotes = !inQuotes; + sb.append(c); + } else if (!inQuotes) { + switch (c) { + case '{', '[' -> { + sb.append(c).append('\n'); + indent++; + appendIndent(sb, indent); + } + case '}', ']' -> { + sb.append('\n'); + indent--; + appendIndent(sb, indent); + sb.append(c); + } + case ',' -> sb.append(c).append('\n').append(" ".repeat(indent)); + case ':' -> sb.append(c).append(' '); + default -> { + if (!Character.isWhitespace(c)) { + sb.append(c); + } + } + } + } else { + // 字符串内部原样输出 + sb.append(c); + } + } + + return sb.toString(); + } + + /** + * 向字符串构建器追加指定层级的缩进。 + * + * @param sb 输出目标。 + * @param indent 缩进层级(每层为两个空格)。 + */ + private static void appendIndent(StringBuilder sb, int indent) { + sb.append(" ".repeat(Math.max(0, indent))); + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/ParserUtils.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/ParserUtils.java new file mode 100644 index 0000000..f20b7c6 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/ParserUtils.java @@ -0,0 +1,64 @@ +package org.jcnc.snow.compiler.parser.utils; + +import org.jcnc.snow.compiler.lexer.token.TokenType; +import org.jcnc.snow.compiler.parser.context.TokenStream; + +/** + * {@code ParserUtils} 是语法结构解析过程中的通用辅助工具类。 + *

+ * 提供一系列静态方法用于标准语法结构(如结构头、结构尾)的匹配校验,以及常用的容错处理操作。 + * 这些方法可在函数定义、模块定义、循环、条件语句等语法块中复用,有效减少冗余代码,提高解析器稳定性。 + * + *

主要功能包括: + *

    + *
  • 匹配结构性语法起始标记(如 {@code loop:}、{@code function:})
  • + *
  • 匹配结构性语法结尾标记(如 {@code end loop}、{@code end function})
  • + *
  • 跳过多余换行符,增强语法容错性
  • + *
+ */ +public class ParserUtils { + + /** + * 匹配结构语法的标准起始格式 {@code keyword:},并跳过其后的换行符。 + *

+ * 该方法适用于需要标识结构起点的语法元素,如 {@code loop:}、{@code function:} 等。 + * 若格式不匹配,将抛出语法异常。 + * + * @param ts 当前的 token 流 + * @param keyword 结构起始关键字(如 "loop", "function", "initializer" 等) + */ + public static void matchHeader(TokenStream ts, String keyword) { + ts.expect(keyword); // 匹配关键字 + ts.expect(":"); // 匹配冒号 + ts.expectType(TokenType.NEWLINE); // 匹配行尾换行 + skipNewlines(ts); // 跳过多余空行 + } + + /** + * 匹配结构语法的标准结尾格式 {@code end keyword}。 + *

+ * 该方法用于验证结构块的结束,例如 {@code end loop}、{@code end if} 等。 + * 若格式不正确,将抛出异常。 + * + * @param ts 当前的 token 流 + * @param keyword 对应的结构关键字(必须与开始标记一致) + */ + public static void matchFooter(TokenStream ts, String keyword) { + ts.expect("end"); // 匹配 'end' + ts.expect(keyword); // 匹配结构名 + ts.expectType(TokenType.NEWLINE); // 匹配行尾 + } + + /** + * 跳过连续的换行符({@code NEWLINE})。 + *

+ * 通常用于解析器之间的过渡阶段,以消除格式干扰,提升容错性。 + * + * @param ts 当前的 token 流 + */ + public static void skipNewlines(TokenStream ts) { + while (ts.peek().getType() == TokenType.NEWLINE) { + ts.next(); // 连续消费换行符 + } + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/AnalyzerRegistry.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/AnalyzerRegistry.java new file mode 100644 index 0000000..46aad81 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/AnalyzerRegistry.java @@ -0,0 +1,97 @@ +package org.jcnc.snow.compiler.semantic.analyzers; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; +import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer; +import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer; +import org.jcnc.snow.compiler.semantic.analyzers.expression.UnsupportedExpressionAnalyzer; + +import java.util.HashMap; +import java.util.Map; + +/** + * {@code AnalyzerRegistry} 是语义分析器的注册与分发中心。 + *

+ * 它负责根据 AST 节点的类型,查找并返回相应的 {@link StatementAnalyzer} 或 {@link ExpressionAnalyzer} 实例。 + * 同时支持注册自定义分析器,并在未找到对应表达式分析器时提供默认兜底处理器。 + *

+ * 主要职责: + *

    + *
  • 支持注册语句和表达式节点类型对应的分析器;
  • + *
  • 在语义分析阶段,根据 AST 节点动态查找对应的分析器;
  • + *
  • 为未注册的表达式类型提供默认处理器 {@link UnsupportedExpressionAnalyzer};
  • + *
  • 不为语句提供默认兜底分析器,未注册类型将返回 {@code null}。
  • + *
+ */ +public class AnalyzerRegistry { + /** Statement 节点类型 → 对应语义分析器映射表 */ + private final Map, StatementAnalyzer> stmtAnalyzers = new HashMap<>(); + + /** Expression 节点类型 → 对应语义分析器映射表 */ + private final Map, ExpressionAnalyzer> exprAnalyzers = new HashMap<>(); + + /** 默认兜底表达式分析器,用于处理未注册的表达式类型 */ + private final ExpressionAnalyzer defaultUnsupported = + new UnsupportedExpressionAnalyzer<>(); + + // ========================= 注册方法 ========================= + + /** + * 注册一个 {@link StatementAnalyzer} 实例,用于处理指定类型的语句节点。 + * + * @param cls 要注册的语句节点类型(Class 对象) + * @param analyzer 与该类型匹配的分析器实例 + * @param {@link StatementNode} 的具体子类 + */ + public void registerStatementAnalyzer( + Class cls, + StatementAnalyzer analyzer + ) { + stmtAnalyzers.put(cls, analyzer); + } + + /** + * 注册一个 {@link ExpressionAnalyzer} 实例,用于处理指定类型的表达式节点。 + * + * @param cls 要注册的表达式节点类型(Class 对象) + * @param analyzer 与该类型匹配的分析器实例 + * @param {@link ExpressionNode} 的具体子类 + */ + public void registerExpressionAnalyzer( + Class cls, + ExpressionAnalyzer analyzer + ) { + exprAnalyzers.put(cls, analyzer); + } + + // ========================= 获取方法 ========================= + + /** + * 根据语句节点的实际类型查找对应的 {@link StatementAnalyzer}。 + *

+ * 若节点类型未注册,返回 {@code null}。 + * + * @param stmt 要分析的语句节点实例 + * @param 语句类型(推断自参数) + * @return 与该节点类型对应的分析器,若未注册则为 {@code null} + */ + @SuppressWarnings("unchecked") + public StatementAnalyzer getStatementAnalyzer(S stmt) { + return (StatementAnalyzer) stmtAnalyzers.get(stmt.getClass()); + } + + /** + * 根据表达式节点的实际类型查找对应的 {@link ExpressionAnalyzer}。 + *

+ * 若节点类型未注册,返回默认兜底分析器 {@link UnsupportedExpressionAnalyzer}。 + * + * @param expr 要分析的表达式节点实例 + * @param 表达式类型(推断自参数) + * @return 与该节点类型对应的分析器,或默认兜底分析器 + */ + @SuppressWarnings("unchecked") + public ExpressionAnalyzer getExpressionAnalyzer(E expr) { + return (ExpressionAnalyzer) + exprAnalyzers.getOrDefault(expr.getClass(), defaultUnsupported); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/base/ExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/base/ExpressionAnalyzer.java new file mode 100644 index 0000000..df49049 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/base/ExpressionAnalyzer.java @@ -0,0 +1,44 @@ +package org.jcnc.snow.compiler.semantic.analyzers.base; + +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.semantic.core.Context; +import org.jcnc.snow.compiler.semantic.core.ModuleInfo; +import org.jcnc.snow.compiler.semantic.symbol.SymbolTable; +import org.jcnc.snow.compiler.semantic.type.Type; + +/** + * 表达式分析器接口:定义了对 AST 中表达式节点进行语义分析的通用契约。 + *

+ * 各种具体的表达式分析器(如调用、二元运算、标识符、字面量等)需实现此接口, + * 在 {@link #analyze(Context, ModuleInfo, FunctionNode, SymbolTable, ExpressionNode)} + * 方法中完成类型推导、语义检查,并将发现的错误记录到上下文中。 + * + * @param 要分析的具体表达式节点类型,必须是 {@link ExpressionNode} 的子类型 + */ +public interface ExpressionAnalyzer { + + /** + * 对给定的表达式节点进行语义分析,并返回推导出的类型。 + *

+ * 实现者应在分析过程中根据节点语义: + *

    + *
  • 校验子表达式类型并递归调用对应的分析器;
  • + *
  • 检查函数调用、运算符合法性;
  • + *
  • 必要时向 {@link Context#getErrors()} 添加 {@link org.jcnc.snow.compiler.semantic.error.SemanticError};
  • + *
  • 返回最终推导出的 {@link Type},以供上层表达式或语句分析使用。
  • + *
+ * + * @param ctx 全局上下文,提供模块注册表、错误收集、日志输出及分析器注册表等 + * @param mi 当前模块信息,用于跨模块调用和函数签名查找 + * @param fn 当前函数节点,可用于返回类型校验或其他函数级上下文 + * @param locals 当前作用域的符号表,包含已声明的变量及其类型 + * @param expr 待分析的表达式节点 + * @return 表达式的推导类型,用于后续类型兼容性检查和类型传播 + */ + Type analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + E expr); +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/base/StatementAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/base/StatementAnalyzer.java new file mode 100644 index 0000000..7e9d1ca --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/base/StatementAnalyzer.java @@ -0,0 +1,40 @@ +package org.jcnc.snow.compiler.semantic.analyzers.base; + +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; +import org.jcnc.snow.compiler.semantic.core.Context; +import org.jcnc.snow.compiler.semantic.core.ModuleInfo; +import org.jcnc.snow.compiler.semantic.symbol.SymbolTable; + +/** + * 语句分析器接口:定义如何对 AST 中的语句节点执行语义检查。 + *

+ * 各具体的语句分析器(如声明、赋值、分支、循环、返回等)需实现此接口, + * 在 {@link #analyze(Context, ModuleInfo, FunctionNode, SymbolTable, StatementNode)} + * 方法中完成: + *

    + *
  • 对自身语义结构进行校验(变量声明、类型匹配、作用域检查等);
  • + *
  • 递归调用其他已注册的语句或表达式分析器;
  • + *
  • 在发现错误时,通过 {@link Context#getErrors()} 记录 {@link org.jcnc.snow.compiler.semantic.error.SemanticError};
  • + *
  • 在上下文需要时记录日志以辅助调试。
  • + *
+ * + * @param 要分析的具体语句节点类型,必须是 {@link StatementNode} 的子类型 + */ +public interface StatementAnalyzer { + + /** + * 对给定的语句节点执行语义分析。 + * + * @param ctx 全局上下文,提供模块注册表、错误收集、日志输出及分析器注册表等 + * @param mi 当前模块信息,用于检查模块导入和函数签名等上下文 + * @param fn 当前函数节点,可用于检查返回类型或函数级别作用域 + * @param locals 当前作用域的符号表,包含已声明的变量及其类型 + * @param stmt 待分析的语句节点实例 + */ + void analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + S stmt); +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/BinaryExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/BinaryExpressionAnalyzer.java new file mode 100644 index 0000000..a9caa0c --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/BinaryExpressionAnalyzer.java @@ -0,0 +1,89 @@ +package org.jcnc.snow.compiler.semantic.analyzers.expression; + +import org.jcnc.snow.compiler.parser.ast.BinaryExpressionNode; +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer; +import org.jcnc.snow.compiler.semantic.core.Context; +import org.jcnc.snow.compiler.semantic.core.ModuleInfo; +import org.jcnc.snow.compiler.semantic.error.SemanticError; +import org.jcnc.snow.compiler.semantic.symbol.SymbolTable; +import org.jcnc.snow.compiler.semantic.type.BuiltinType; +import org.jcnc.snow.compiler.semantic.type.Type; + +/** + * {@code BinaryExpressionAnalyzer} 是一个用于分析二元表达式的语义分析器。 + *

+ * 支持的特性包括: + *

    + *
  • 字符串拼接(当运算符为加号 "+" 且任一操作数为字符串类型时)
  • + *
  • 数值类型的自动宽化转换(如 int 与 float 运算将转换为 float)
  • + *
  • 基本的数值运算符(如 +, -, *, /, %)
  • + *
  • 关系运算符和比较运算符(如 <, <=, >, >=, ==, !=)
  • + *
+ * 对于不支持的运算符或不兼容的类型组合,将记录语义错误,并默认返回 {@code BuiltinType.INT} 以保持分析过程的连续性。 + *

+ * 实现类遵循 {@code ExpressionAnalyzer} 接口规范。 + */ +public class BinaryExpressionAnalyzer implements ExpressionAnalyzer { + + /** + * 分析给定的二元表达式节点,返回其表达式类型。 + * + * @param ctx 当前语义分析上下文,用于访问日志记录、错误收集、注册表等服务。 + * @param mi 当前模块信息,包含模块级别的符号与类型定义。 + * @param fn 当前正在分析的函数节点。 + * @param locals 当前函数作用域内的符号表,用于变量查找。 + * @param bin 要分析的二元表达式节点。 + * @return 分析后推断出的表达式类型。 + */ + @Override + public Type analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + BinaryExpressionNode bin) { + ctx.log("检查二元表达式: " + bin.operator()); + + // 获取左侧表达式类型 + Type left = ctx.getRegistry().getExpressionAnalyzer(bin.left()) + .analyze(ctx, mi, fn, locals, bin.left()); + + // 获取右侧表达式类型 + Type right = ctx.getRegistry().getExpressionAnalyzer(bin.right()) + .analyze(ctx, mi, fn, locals, bin.right()); + + String op = bin.operator(); + + // 情况 1:字符串拼接(+ 操作符且任一操作数为字符串类型) + if (op.equals("+") && + (left == BuiltinType.STRING || right == BuiltinType.STRING)) { + return BuiltinType.STRING; + } + + // 情况 2:数值类型运算或比较 + if ("+-*/%".contains(op) || ("<<=>>===!=").contains(op)) { + if (left.isNumeric() && right.isNumeric()) { + // 自动宽化到更宽的数值类型(如 int + float => float) + Type wide = Type.widen(left, right); + if (wide == null) wide = BuiltinType.INT; // 容错降级为 int + + // 若为比较运算符,统一返回 int 类型作为布尔值表示 + if ("< <= > >= == !=".contains(op)) { + return BuiltinType.INT; + } + + return wide; + } + } + + // 情况 3:不支持的类型组合,记录语义错误 + ctx.getErrors().add(new SemanticError( + bin, + String.format("运算符 '%s' 不支持类型: %s 和 %s", op, left, right) + )); + ctx.log("错误: 运算符 '" + op + "' 不支持类型: " + left + ", " + right); + + // 错误情况下默认返回 int 类型,以保证语义分析不中断 + return BuiltinType.INT; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java new file mode 100644 index 0000000..b82d78f --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java @@ -0,0 +1,138 @@ +package org.jcnc.snow.compiler.semantic.analyzers.expression; + +import org.jcnc.snow.compiler.parser.ast.*; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer; +import org.jcnc.snow.compiler.semantic.core.Context; +import org.jcnc.snow.compiler.semantic.core.ModuleInfo; +import org.jcnc.snow.compiler.semantic.error.SemanticError; +import org.jcnc.snow.compiler.semantic.symbol.SymbolTable; +import org.jcnc.snow.compiler.semantic.type.BuiltinType; +import org.jcnc.snow.compiler.semantic.type.FunctionType; +import org.jcnc.snow.compiler.semantic.type.Type; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@code CallExpressionAnalyzer} 是函数调用表达式的语义分析器。 + *

+ * 它负责处理类似 {@code callee(arg1, arg2, ...)} 形式的调用表达式,执行如下操作: + *

    + *
  • 识别调用目标(支持模块成员函数调用和当前模块函数调用);
  • + *
  • 根据被调用函数的参数签名检查实参数量和类型的兼容性;
  • + *
  • 支持数值参数的宽化转换(如 int → double);
  • + *
  • 支持数值到字符串的隐式转换(自动视为调用 {@code to_string});
  • + *
  • 在发生类型不匹配、未导入模块或函数未定义等情况下记录语义错误。
  • + *
+ */ +public class CallExpressionAnalyzer implements ExpressionAnalyzer { + + /** + * 分析函数调用表达式并推断其类型。 + * + * @param ctx 当前语义分析上下文,提供日志、错误记录、模块访问等功能。 + * @param mi 当前模块信息,用于函数查找及模块依赖判断。 + * @param fn 当前分析的函数节点。 + * @param locals 局部符号表,用于变量查找。 + * @param call 待分析的函数调用表达式节点。 + * @return 表达式的返回类型。如果存在语义错误,默认返回 {@code BuiltinType.INT}。 + */ + @Override + public Type analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + CallExpressionNode call) { + ctx.log("检查函数调用: " + call.callee()); + + ModuleInfo target = mi; // 默认目标模块为当前模块 + String functionName; + ExpressionNode callee = call.callee(); + + // 支持模块调用形式:ModuleName.FunctionName(...) + if (callee instanceof MemberExpressionNode(var obj, String member) + && obj instanceof IdentifierNode(String mod)) { + // 验证模块是否存在并已导入 + if (!ctx.getModules().containsKey(mod) + || (!mi.getImports().contains(mod) && !mi.getName().equals(mod))) { + ctx.getErrors().add(new SemanticError(callee, + "未知或未导入模块: " + mod)); + ctx.log("错误: 未导入模块 " + mod); + return BuiltinType.INT; + } + target = ctx.getModules().get(mod); + functionName = member; + + // 简单函数名形式:func(...) + } else if (callee instanceof IdentifierNode(String name)) { + functionName = name; + + // 不支持的 callee 形式 + } else { + ctx.getErrors().add(new SemanticError(callee, + "不支持的调用方式: " + callee)); + ctx.log("错误: 不支持的调用方式 " + callee); + return BuiltinType.INT; + } + + // 查找目标函数签名 + FunctionType ft = target.getFunctions().get(functionName); + if (ft == null) { + ctx.getErrors().add(new SemanticError(callee, + "函数未定义: " + functionName)); + ctx.log("错误: 函数未定义 " + functionName); + return BuiltinType.INT; + } + + // 分析所有实参并获取类型 + List args = new ArrayList<>(); + for (ExpressionNode arg : call.arguments()) { + args.add(ctx.getRegistry().getExpressionAnalyzer(arg) + .analyze(ctx, mi, fn, locals, arg)); + } + + // 参数数量检查 + if (args.size() != ft.paramTypes().size()) { + ctx.getErrors().add(new SemanticError(call, + "参数数量不匹配: 期望 " + ft.paramTypes().size() + + " 个, 实际 " + args.size() + " 个")); + ctx.log("错误: 参数数量不匹配: 期望 " + + ft.paramTypes().size() + ", 实际 " + args.size()); + + } else { + // 参数类型检查与转换支持 + for (int i = 0; i < args.size(); i++) { + Type expected = ft.paramTypes().get(i); + Type actual = args.get(i); + + // 完全兼容或数值宽化转换 + boolean ok = expected.isCompatible(actual) + || (expected.isNumeric() && actual.isNumeric() + && Type.widen(actual, expected) == expected); + + // 支持将数值自动转换为字符串 + if (!ok && expected == BuiltinType.STRING && actual.isNumeric()) { + ctx.log(String.format( + "隐式将参数 %d 的数值类型 %s 转换为 string (to_string)", + i, actual + )); + ok = true; + } + + // 类型不匹配,记录语义错误 + if (!ok) { + ctx.getErrors().add(new SemanticError(call, + String.format("参数类型不匹配 (位置 %d): 期望 %s, 实际 %s", + i, expected, actual))); + ctx.log("错误: 参数类型不匹配 (位置 " + i + "): 期望 " + + expected + ", 实际 " + actual); + } + } + } + + // 返回函数的返回类型作为整个调用表达式的类型 + ctx.log("函数调用类型: 返回 " + ft.returnType()); + return ft.returnType(); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/IdentifierAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/IdentifierAnalyzer.java new file mode 100644 index 0000000..5d05336 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/IdentifierAnalyzer.java @@ -0,0 +1,59 @@ +package org.jcnc.snow.compiler.semantic.analyzers.expression; + +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.parser.ast.IdentifierNode; +import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer; +import org.jcnc.snow.compiler.semantic.core.Context; +import org.jcnc.snow.compiler.semantic.core.ModuleInfo; +import org.jcnc.snow.compiler.semantic.error.SemanticError; +import org.jcnc.snow.compiler.semantic.symbol.Symbol; +import org.jcnc.snow.compiler.semantic.symbol.SymbolTable; +import org.jcnc.snow.compiler.semantic.type.BuiltinType; +import org.jcnc.snow.compiler.semantic.type.Type; + +/** + * {@code IdentifierAnalyzer} 是用于分析标识符表达式(如变量名、常量名)的语义分析器。 + *

+ * 它的主要职责是在给定的局部作用域中查找标识符对应的符号定义,并返回其类型信息。 + * 如果标识符未在当前作用域内声明,则: + *

    + *
  • 向语义错误列表中添加一条 {@link SemanticError} 记录;
  • + *
  • 为保证分析过程的连续性,默认返回 {@link BuiltinType#INT} 类型作为降级处理。
  • + *
+ *

+ * 该分析器通常用于处理表达式中的变量引用或常量引用场景。 + */ +public class IdentifierAnalyzer implements ExpressionAnalyzer { + + /** + * 对标识符表达式进行语义分析,判断其是否在当前作用域中已声明,并返回其类型。 + * + * @param ctx 当前语义分析上下文对象,提供模块信息、错误收集、日志记录等服务。 + * @param mi 当前模块信息(此实现中未使用,但保留用于扩展)。 + * @param fn 当前分析的函数节点(此实现中未使用,但保留用于扩展)。 + * @param locals 当前函数或代码块的符号表,记录已声明的变量及其类型信息。 + * @param id 表达式中出现的 {@link IdentifierNode} 实例,表示待解析的标识符名称。 + * @return 若标识符已在符号表中声明,则返回对应的 {@link Type}; + * 否则返回 {@link BuiltinType#INT} 作为错误降级类型。 + */ + @Override + public Type analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + IdentifierNode id) { + + // 在当前作用域中查找符号(变量或常量) + Symbol sym = locals.resolve(id.name()); + if (sym == null) { + // 未声明标识符:记录语义错误,返回降级类型 + ctx.getErrors().add(new SemanticError(id, + "未声明的标识符: " + id.name())); + ctx.log("错误: 未声明的标识符 " + id.name()); + return BuiltinType.INT; + } + + // 返回符号的类型信息 + return sym.type(); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/NumberLiteralAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/NumberLiteralAnalyzer.java new file mode 100644 index 0000000..a59fadd --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/NumberLiteralAnalyzer.java @@ -0,0 +1,97 @@ +package org.jcnc.snow.compiler.semantic.analyzers.expression; + +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.parser.ast.NumberLiteralNode; +import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer; +import org.jcnc.snow.compiler.semantic.core.Context; +import org.jcnc.snow.compiler.semantic.core.ModuleInfo; +import org.jcnc.snow.compiler.semantic.symbol.SymbolTable; +import org.jcnc.snow.compiler.semantic.type.BuiltinType; +import org.jcnc.snow.compiler.semantic.type.Type; + +/** + * {@code NumberLiteralAnalyzer} 用于对数字字面量表达式进行语义分析并推断其精确类型。 + *

+ * 类型判定逻辑如下: + *

    + *
  1. 首先检查字面量末尾是否带有类型后缀(不区分大小写): + *
      + *
    • {@code b}:byte 类型({@link BuiltinType#BYTE})
    • + *
    • {@code s}:short 类型({@link BuiltinType#SHORT})
    • + *
    • {@code l}:long 类型({@link BuiltinType#LONG})
    • + *
    • {@code f}:float 类型({@link BuiltinType#FLOAT})
    • + *
    • {@code d}:double 类型({@link BuiltinType#DOUBLE})
    • + *
    + *
  2. + *
  3. 若无后缀,则: + *
      + *
    • 只要文本中包含 {@code '.'} 或 {@code e/E},即判为 double 类型
    • + *
    • 否则默认判为 int 类型
    • + *
    + *
  4. + *
+ * 本分析器不处理溢出、非法格式等诊断,仅做类型推断。 + */ +public class NumberLiteralAnalyzer implements ExpressionAnalyzer { + + /** + * 对数字字面量进行语义分析,推断其类型。 + *

+ * 分析流程: + *

    + *
  1. 若字面量以后缀结尾,直接按后缀映射类型。
  2. + *
  3. 否则,若含有小数点或科学计数法标记,则为 double,否则为 int。
  4. + *
+ * + * @param ctx 当前语义分析上下文(可用于记录诊断信息等,当前未使用) + * @param mi 当前模块信息(未使用) + * @param fn 当前函数节点(未使用) + * @param locals 当前作用域的符号表(未使用) + * @param expr 数字字面量表达式节点 + * @return 对应的 {@link BuiltinType},如 INT、DOUBLE 等 + */ + @Override + public Type analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + NumberLiteralNode expr) { + + // 获取字面量原始文本(如 "123", "3.14", "2f" 等) + String raw = expr.value(); + if (raw == null || raw.isEmpty()) { + // 理论上不应为空,兜底返回 int 类型 + return BuiltinType.INT; + } + + // 获取最后一个字符,判断是否为类型后缀(b/s/l/f/d,忽略大小写) + char lastChar = raw.charAt(raw.length() - 1); + char suffix = Character.toLowerCase(lastChar); + boolean hasSuffix = switch (suffix) { + case 'b', 's', 'l', 'f', 'd' -> true; + default -> false; + }; + + // 若有后缀,则 digits 为去除后缀的数字部分,否则为原文本 + String digits = hasSuffix ? raw.substring(0, raw.length() - 1) : raw; + + // 1. 若有后缀,直接返回对应类型 + if (hasSuffix) { + return switch (suffix) { + case 'b' -> BuiltinType.BYTE; + case 's' -> BuiltinType.SHORT; + case 'l' -> BuiltinType.LONG; + case 'f' -> BuiltinType.FLOAT; + case 'd' -> BuiltinType.DOUBLE; + default -> BuiltinType.INT; // 理论上不会到这里 + }; + } + + // 2. 无后缀,根据文本是否含小数点或科学计数法(e/E)判断类型 + if (digits.indexOf('.') >= 0 || digits.indexOf('e') >= 0 || digits.indexOf('E') >= 0) { + return BuiltinType.DOUBLE; // 有小数/科学计数,默认 double 类型 + } + + return BuiltinType.INT; // 否则为纯整数,默认 int 类型 + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/StringLiteralAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/StringLiteralAnalyzer.java new file mode 100644 index 0000000..518fa93 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/StringLiteralAnalyzer.java @@ -0,0 +1,45 @@ +package org.jcnc.snow.compiler.semantic.analyzers.expression; + +import org.jcnc.snow.compiler.parser.ast.StringLiteralNode; +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer; +import org.jcnc.snow.compiler.semantic.core.Context; +import org.jcnc.snow.compiler.semantic.core.ModuleInfo; +import org.jcnc.snow.compiler.semantic.symbol.SymbolTable; +import org.jcnc.snow.compiler.semantic.type.BuiltinType; +import org.jcnc.snow.compiler.semantic.type.Type; + +/** + * {@code StringLiteralAnalyzer} 是字符串字面量表达式的语义分析器。 + *

+ * 负责分析源代码中的字符串字面量(例如 {@code "hello"}、{@code ""} 等), + * 并确定其类型。根据语言规范,所有字符串字面量默认视为 {@link BuiltinType#STRING} 类型。 + *

+ * 特点如下: + *

    + *
  • 不依赖符号表、函数上下文或模块信息,属于上下文无关表达式分析器;
  • + *
  • 恒定返回 {@code STRING} 类型;
  • + *
  • 不产生任何语义错误,具备语义稳定性。
  • + *
+ */ +public class StringLiteralAnalyzer implements ExpressionAnalyzer { + + /** + * 分析字符串字面量表达式并返回其类型。 + * + * @param ctx 当前语义分析上下文对象(本分析器不使用该参数,保留用于接口一致性) + * @param mi 当前模块信息(未使用,因字面量无模块依赖) + * @param fn 当前所在的函数节点(未使用,因字面量无函数依赖) + * @param locals 当前作用域的符号表(未使用,因字面量不引用符号) + * @param expr 要分析的 {@link StringLiteralNode} 节点 + * @return 始终返回 {@link BuiltinType#STRING} 类型,表示字符串字面量 + */ + @Override + public Type analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + StringLiteralNode expr) { + return BuiltinType.STRING; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnsupportedExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnsupportedExpressionAnalyzer.java new file mode 100644 index 0000000..01e3332 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnsupportedExpressionAnalyzer.java @@ -0,0 +1,60 @@ +package org.jcnc.snow.compiler.semantic.analyzers.expression; + +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; +import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer; +import org.jcnc.snow.compiler.semantic.core.Context; +import org.jcnc.snow.compiler.semantic.core.ModuleInfo; +import org.jcnc.snow.compiler.semantic.error.SemanticError; +import org.jcnc.snow.compiler.semantic.symbol.SymbolTable; +import org.jcnc.snow.compiler.semantic.type.BuiltinType; +import org.jcnc.snow.compiler.semantic.type.Type; + +/** + * {@code UnsupportedExpressionAnalyzer} 是一个通用兜底表达式分析器, + * 用于处理所有未显式注册的 {@link ExpressionNode} 子类型。 + *

+ * 在语义分析阶段,当分析器注册表中找不到对应节点类型的处理器时, + * 将回退使用本类进行统一处理,以确保编译流程不中断。 + *

+ * 特性说明: + *

    + *
  • 适用于所有未知或暂未实现的表达式类型;
  • + *
  • 自动记录语义错误并打印日志,方便定位与扩展;
  • + *
  • 返回统一的降级类型 {@link BuiltinType#INT},以避免类型缺失造成后续分析失败。
  • + *
+ * + * @param 任意 {@link ExpressionNode} 的子类,支持泛型兜底匹配 + */ +public class UnsupportedExpressionAnalyzer + implements ExpressionAnalyzer { + + /** + * 对不支持或未实现的表达式节点执行兜底处理。 + * + * @param ctx 当前语义分析上下文对象,用于错误记录与日志输出 + * @param mi 当前模块信息(此方法中未使用,保留用于接口一致性) + * @param fn 当前函数节点(此方法中未使用,保留用于接口一致性) + * @param locals 当前局部作用域符号表(此方法中未使用,因不解析具体含义) + * @param expr 不支持的表达式节点 + * @return 固定返回 {@link BuiltinType#INT} 类型,作为占位降级类型 + */ + @Override + public Type analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + E expr) { + // 记录语义错误 + ctx.getErrors().add(new SemanticError( + expr, + "不支持的表达式类型: " + expr + )); + + // 输出错误日志以便调试或后续支持扩展 + ctx.log("错误: 不支持的表达式类型 " + expr); + + // 返回默认类型以避免连锁报错 + return BuiltinType.INT; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/AssignmentAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/AssignmentAnalyzer.java new file mode 100644 index 0000000..91056b5 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/AssignmentAnalyzer.java @@ -0,0 +1,69 @@ +package org.jcnc.snow.compiler.semantic.analyzers.statement; + +import org.jcnc.snow.compiler.parser.ast.AssignmentNode; +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer; +import org.jcnc.snow.compiler.semantic.core.Context; +import org.jcnc.snow.compiler.semantic.core.ModuleInfo; +import org.jcnc.snow.compiler.semantic.error.SemanticError; +import org.jcnc.snow.compiler.semantic.symbol.*; +import org.jcnc.snow.compiler.semantic.type.Type; + +/** + * {@code AssignmentAnalyzer} 是赋值语句的语义分析器。 + *

+ * 负责分析和验证赋值语句的合法性,包括: + *

    + *
  • 变量是否已声明且可赋值(必须为 {@link SymbolKind#VARIABLE} 类型);
  • + *
  • 赋值右值的类型是否与变量类型兼容;
  • + *
  • 是否允许进行数值类型的自动宽化转换(如 {@code int → float})。
  • + *
+ * 若类型不兼容且无法自动宽化,则将记录语义错误并输出日志信息。 + */ +public class AssignmentAnalyzer implements StatementAnalyzer { + + /** + * 分析赋值语句的语义有效性,包括左值合法性与类型匹配性。 + * + * @param ctx 当前语义分析上下文,包含模块表、错误收集、日志输出等服务。 + * @param mi 当前模块信息,用于支持跨模块语义校验(当前未涉及)。 + * @param fn 当前分析的函数节点,提供局部作用域上下文。 + * @param locals 当前函数或代码块的符号表,用于变量解析与类型信息获取。 + * @param asg 要分析的赋值语句节点 {@link AssignmentNode}。 + */ + @Override + public void analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + AssignmentNode asg) { + + // 获取赋值左值变量名并进行符号解析 + ctx.log("赋值检查: " + asg.variable()); + Symbol sym = locals.resolve(asg.variable()); + + // 检查变量是否已声明且为可赋值的变量类型 + if (sym == null || sym.kind() != SymbolKind.VARIABLE) { + ctx.getErrors().add(new SemanticError(asg, + "未声明的变量: " + asg.variable())); + ctx.log("错误: 未声明的变量 " + asg.variable()); + return; + } + + // 分析右值表达式类型 + Type valType = ctx.getRegistry().getExpressionAnalyzer(asg.value()) + .analyze(ctx, mi, fn, locals, asg.value()); + + // 类型检查:若类型不兼容,则尝试判断是否允许宽化转换 + if (!sym.type().isCompatible(valType)) { + // 数值类型允许自动宽化转换(如 int → double) + if (!(sym.type().isNumeric() && valType.isNumeric() + && Type.widen(valType, sym.type()) == sym.type())) { + ctx.getErrors().add(new SemanticError(asg, + "赋值类型不匹配: 期望 " + sym.type() + + ", 实际 " + valType)); + ctx.log("错误: 赋值类型不匹配 " + asg.variable()); + } + } + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/DeclarationAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/DeclarationAnalyzer.java new file mode 100644 index 0000000..f20b55f --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/DeclarationAnalyzer.java @@ -0,0 +1,85 @@ +package org.jcnc.snow.compiler.semantic.analyzers.statement; + +import org.jcnc.snow.compiler.parser.ast.DeclarationNode; +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer; +import org.jcnc.snow.compiler.semantic.core.Context; +import org.jcnc.snow.compiler.semantic.core.ModuleInfo; +import org.jcnc.snow.compiler.semantic.error.SemanticError; +import org.jcnc.snow.compiler.semantic.symbol.*; +import org.jcnc.snow.compiler.semantic.type.BuiltinType; +import org.jcnc.snow.compiler.semantic.type.Type; + +/** + * {@code DeclarationAnalyzer} 是变量声明语句的语义分析器。 + *

+ * 它负责处理类似 {@code int x = 10;} 的声明语句,具体分析内容包括: + *

    + *
  • 类型解析:将声明中的类型字符串转换为语义层的 {@link Type} 对象;
  • + *
  • 符号定义:将变量注册到当前作用域的 {@link SymbolTable} 中;
  • + *
  • 重复定义检查:防止同一作用域下的变量名冲突;
  • + *
  • 初始化表达式类型校验:检查类型兼容性,支持数值类型宽化(如 int → float)。
  • + *
+ * 若出现类型未识别、重复声明或类型不兼容等问题,将向语义错误列表添加对应错误信息。 + */ +public class DeclarationAnalyzer implements StatementAnalyzer { + + /** + * 对变量声明语句执行语义分析。 + * + * @param ctx 当前语义分析上下文对象,提供类型解析、错误记录、日志输出等功能。 + * @param mi 当前模块信息,支持跨模块引用检查(本分析器未直接使用)。 + * @param fn 当前函数节点,表示当前所在作用域的函数(用于初始化分析上下文)。 + * @param locals 当前作用域的符号表,用于注册和查找变量。 + * @param decl 要分析的变量声明节点 {@link DeclarationNode}。 + */ + @Override + public void analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + DeclarationNode decl) { + + // 1. 解析声明类型 + Type varType = ctx.parseType(decl.getType()); + if (varType == null) { + ctx.getErrors().add(new SemanticError(decl, + "未知类型: " + decl.getType())); + ctx.log("错误: 未知类型 " + decl.getType() + + " 在声明 " + decl.getName()); + varType = BuiltinType.INT; // 容错处理:默认降级为 int + } + ctx.log("声明变量: " + decl.getName() + + " 类型: " + varType); + + // 2. 将变量注册到当前作用域符号表中,检查重复定义 + if (!locals.define(new Symbol( + decl.getName(), varType, SymbolKind.VARIABLE + ))) { + ctx.getErrors().add(new SemanticError(decl, + "变量重复声明: " + decl.getName())); + ctx.log("错误: 变量重复声明 " + decl.getName()); + } + + // 3. 检查初始化表达式(如果存在) + Type finalVarType = varType; // 用于 lambda 捕获 + decl.getInitializer().ifPresent(init -> { + Type initType = ctx.getRegistry().getExpressionAnalyzer(init) + .analyze(ctx, mi, fn, locals, init); + + // 检查类型是否兼容,或是否允许数值宽化转换 + if (!finalVarType.isCompatible(initType)) { + boolean canWiden = finalVarType.isNumeric() + && initType.isNumeric() + && Type.widen(initType, finalVarType) == finalVarType; + if (!canWiden) { + ctx.getErrors().add(new SemanticError(decl, + "初始化类型不匹配: 期望 " + finalVarType + + ", 实际 " + initType)); + ctx.log("错误: 初始化类型不匹配 " + + decl.getName()); + } + } + }); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/IfAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/IfAnalyzer.java new file mode 100644 index 0000000..3483702 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/IfAnalyzer.java @@ -0,0 +1,88 @@ +package org.jcnc.snow.compiler.semantic.analyzers.statement; + +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.parser.ast.IfNode; +import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer; +import org.jcnc.snow.compiler.semantic.core.Context; +import org.jcnc.snow.compiler.semantic.core.ModuleInfo; +import org.jcnc.snow.compiler.semantic.error.SemanticError; +import org.jcnc.snow.compiler.semantic.symbol.SymbolTable; +import org.jcnc.snow.compiler.semantic.type.BuiltinType; +import org.jcnc.snow.compiler.semantic.type.Type; + +/** + * {@code IfAnalyzer} 用于分析 if 语句的语义正确性。 + *

+ * 主要职责如下: + *

    + *
  • 条件表达式类型检查:确认 if 的条件表达式类型为 int(用于真假判断),否则记录语义错误。
  • + *
  • 块级作用域:分别为 then 分支和 else 分支创建独立的符号表(SymbolTable), + * 支持分支内变量的块级作用域,防止分支内声明的变量污染外部或互相干扰,允许分支内变量同名遮蔽。
  • + *
  • 分支递归分析:对 then 和 else 分支的每条语句递归调用对应的语义分析器,进行语义检查。
  • + *
  • 错误记录:若遇到条件类型不符、不支持的语句类型或分支内部其他语义问题,均通过 {@link SemanticError} 记录详细错误信息,并附带代码位置信息。
  • + *
  • 健壮性:不会因一处错误立即终止,而是尽量分析全部分支,收集所有能发现的错误,一次性输出。
  • + *
+ *

+ * 该分析器提升了语言的健壮性与可维护性,是支持 SCompiler 块级作用域及全局错误收集能力的关键一环。 + */ +public class IfAnalyzer implements StatementAnalyzer { + + /** + * 分析 if 语句的语义合法性,包括条件表达式类型、分支作用域及分支语句检查。 + * + * @param ctx 语义分析上下文(记录全局符号表和错误) + * @param mi 当前模块信息 + * @param fn 当前所在函数 + * @param locals 当前作用域符号表 + * @param ifn if 语句 AST 节点 + */ + @Override + public void analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + IfNode ifn) { + + // 1. 检查 if 条件表达式类型 + // 获取对应条件表达式的表达式分析器 + var exprAnalyzer = ctx.getRegistry().getExpressionAnalyzer(ifn.condition()); + // 对条件表达式执行类型分析 + Type condType = exprAnalyzer.analyze(ctx, mi, fn, locals, ifn.condition()); + // 判断条件类型是否为 int(SCompiler 约定 int 表示真假),否则报错 + if (condType != BuiltinType.INT) { + ctx.getErrors().add(new SemanticError(ifn, "if 条件必须为 int 类型(表示真假)")); + } + + // 2. 分析 then 分支 + // 创建 then 分支的块级作用域(以当前 locals 为父作用域) + SymbolTable thenScope = new SymbolTable(locals); + // 遍历 then 分支下的每一条语句 + for (var stmt : ifn.thenBranch()) { + // 获取对应语句类型的分析器 + var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt); + if (stAnalyzer != null) { + // 对当前语句执行语义分析(作用域为 thenScope) + stAnalyzer.analyze(ctx, mi, fn, thenScope, stmt); + } else { + // 若找不到对应的分析器,记录错误 + ctx.getErrors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt)); + } + } + + // 3. 分析 else 分支(可选) + if (!ifn.elseBranch().isEmpty()) { + // 创建 else 分支的块级作用域(同样以 locals 为父作用域) + SymbolTable elseScope = new SymbolTable(locals); + // 遍历 else 分支下的每一条语句 + for (var stmt : ifn.elseBranch()) { + var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt); + if (stAnalyzer != null) { + // 对当前语句执行语义分析(作用域为 elseScope) + stAnalyzer.analyze(ctx, mi, fn, elseScope, stmt); + } else { + ctx.getErrors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt)); + } + } + } + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/LoopAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/LoopAnalyzer.java new file mode 100644 index 0000000..7b67005 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/LoopAnalyzer.java @@ -0,0 +1,79 @@ +package org.jcnc.snow.compiler.semantic.analyzers.statement; + +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.parser.ast.LoopNode; +import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer; +import org.jcnc.snow.compiler.semantic.core.Context; +import org.jcnc.snow.compiler.semantic.core.ModuleInfo; +import org.jcnc.snow.compiler.semantic.error.SemanticError; +import org.jcnc.snow.compiler.semantic.symbol.SymbolTable; +import org.jcnc.snow.compiler.semantic.type.BuiltinType; +import org.jcnc.snow.compiler.semantic.type.Type; + +/** + * {@code LoopAnalyzer} 用于分析 for/while 等循环结构的语义正确性。 + *

+ * 主要职责如下: + *

    + *
  • 为整个循环体(包括初始化、条件、更新、循环体本身)创建独立的块级符号表(作用域),保证循环内变量与外部隔离。
  • + *
  • 依次分析初始化语句、条件表达式、更新语句和循环体各语句,并递归检查嵌套的语法结构。
  • + *
  • 检查条件表达式的类型必须为 int(布尔条件),否则记录语义错误。
  • + *
  • 支持所有错误的收集而不中断流程,便于一次性输出全部问题。
  • + *
+ * 该分析器实现了 SCompiler 语言的块级作用域循环与类型健壮性,是健全语义分析的基础部分。 + */ +public class LoopAnalyzer implements StatementAnalyzer { + + /** + * 分析循环结构(如 for、while)的语义合法性。 + * + * @param ctx 语义分析上下文(错误收集等) + * @param mi 当前模块信息 + * @param fn 当前所在函数 + * @param locals 外部传入的符号表(本地作用域) + * @param ln 当前循环节点 + */ + @Override + public void analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + LoopNode ln) { + + // 1. 创建整个循环结构的块级作用域 + // 新建 loopScope,以支持循环内部变量声明与外部隔离 + SymbolTable loopScope = new SymbolTable(locals); + + // 2. 分析初始化语句(如 for(i=0)),使用 loopScope 作为作用域 + var initAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.initializer()); + if (initAnalyzer != null) { + initAnalyzer.analyze(ctx, mi, fn, loopScope, ln.initializer()); + } + + // 3. 分析条件表达式(如 for(...; cond; ...) 或 while(cond)) + var condAnalyzer = ctx.getRegistry().getExpressionAnalyzer(ln.condition()); + Type condType = condAnalyzer.analyze(ctx, mi, fn, loopScope, ln.condition()); + // 条件类型必须为 int(即 bool),否则记录错误 + if (condType != BuiltinType.INT) { + ctx.getErrors().add(new SemanticError(ln, "loop 条件必须为 int 类型(表示真假)")); + } + + // 4. 分析更新语句(如 for(...; ...; update)) + var updateAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.update()); + if (updateAnalyzer != null) { + updateAnalyzer.analyze(ctx, mi, fn, loopScope, ln.update()); + } + + // 5. 分析循环体内的每一条语句 + for (var stmt : ln.body()) { + var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt); + if (stAnalyzer != null) { + // 递归分析循环体语句,作用域同样为 loopScope + stAnalyzer.analyze(ctx, mi, fn, loopScope, stmt); + } else { + // 不支持的语句类型,记录错误 + ctx.getErrors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt)); + } + } + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/ReturnAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/ReturnAnalyzer.java new file mode 100644 index 0000000..3908909 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/ReturnAnalyzer.java @@ -0,0 +1,78 @@ +package org.jcnc.snow.compiler.semantic.analyzers.statement; + +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.parser.ast.ReturnNode; +import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer; +import org.jcnc.snow.compiler.semantic.core.Context; +import org.jcnc.snow.compiler.semantic.core.ModuleInfo; +import org.jcnc.snow.compiler.semantic.error.SemanticError; +import org.jcnc.snow.compiler.semantic.symbol.SymbolTable; +import org.jcnc.snow.compiler.semantic.type.BuiltinType; +import org.jcnc.snow.compiler.semantic.type.FunctionType; +import org.jcnc.snow.compiler.semantic.type.Type; + +/** + * {@code ReturnAnalyzer} 是用于分析 {@link ReturnNode} 返回语句的语义分析器。 + *

+ * 它负责检查函数中的 return 语句是否与函数定义的返回类型匹配。分析流程包括: + *

    + *
  • 获取当前函数的返回类型 {@link FunctionType#returnType()};
  • + *
  • 若 return 语句包含返回值表达式,检查其类型与函数定义是否兼容;
  • + *
  • 若 return 语句未指定返回值,而函数返回类型非 {@link BuiltinType#VOID},则视为错误;
  • + *
  • 所有不兼容情况将记录为 {@link SemanticError} 并写入分析日志。
  • + *
+ */ +public class ReturnAnalyzer implements StatementAnalyzer { + + /** + * 分析 return 语句的语义合法性。 + * + * @param ctx 当前语义分析上下文对象,提供模块访问、错误记录与分析器调度功能。 + * @param mi 当前模块信息,用于定位当前函数定义。 + * @param fn 当前所在的函数节点,包含函数名及参数定义。 + * @param locals 当前作用域的符号表(return 不依赖变量声明,此参数未使用)。 + * @param ret {@link ReturnNode} 语法节点,表示函数中的 return 语句,可能包含返回值表达式。 + */ + @Override + public void analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + ReturnNode ret) { + + ctx.log("检查 return"); + + // 获取当前函数的定义信息 + FunctionType expected = ctx.getModules() + .get(mi.getName()) + .getFunctions() + .get(fn.name()); + + // 情况 1:存在返回表达式,需进行类型检查 + ret.getExpression().ifPresentOrElse(exp -> { + var exprAnalyzer = ctx.getRegistry().getExpressionAnalyzer(exp); + Type actual = exprAnalyzer.analyze(ctx, mi, fn, locals, exp); + + if (!expected.returnType().isCompatible(actual)) { + ctx.getErrors().add(new SemanticError( + ret, + "return 类型不匹配: 期望 " + + expected.returnType() + + ", 实际 " + + actual + )); + ctx.log("错误: return 类型不匹配"); + } + + // 情况 2:无返回表达式,但函数定义了非 void 返回类型 + }, () -> { + if (expected.returnType() != BuiltinType.VOID) { + ctx.getErrors().add(new SemanticError( + ret, + "非 void 函数必须返回值" + )); + ctx.log("错误: 非 void 函数缺少返回值"); + } + }); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java new file mode 100644 index 0000000..d91d08c --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java @@ -0,0 +1,61 @@ +package org.jcnc.snow.compiler.semantic.core; + +import org.jcnc.snow.compiler.parser.ast.*; +import org.jcnc.snow.compiler.semantic.analyzers.AnalyzerRegistry; +import org.jcnc.snow.compiler.semantic.analyzers.expression.*; +import org.jcnc.snow.compiler.semantic.analyzers.statement.*; + +/** + * {@code AnalyzerRegistrar} 负责将所有语句与表达式的语义分析器 + * 统一注册到 {@link AnalyzerRegistry} 中。 + *

+ * 本类为静态工具类,不可实例化,其唯一公开方法 {@link #registerAll(AnalyzerRegistry)} + * 应在语义分析初始化阶段调用一次,确保所有节点类型都能正确分发到对应分析器。 + *

+ * 注册内容包括: + *

    + *
  • 所有标准语句节点(如变量声明、赋值、条件、循环、返回等)的分析器;
  • + *
  • 所有标准表达式节点(如字面量、标识符、函数调用、二元表达式等)的分析器;
  • + *
  • 对不支持或未实现的表达式节点提供兜底分析器 {@link UnsupportedExpressionAnalyzer}。
  • + *
+ */ +public final class AnalyzerRegistrar { + + /** + * 私有构造函数禁止实例化。 + */ + private AnalyzerRegistrar() { + } + + /** + * 向指定 {@link AnalyzerRegistry} 注册所有语法分析器实例。 + * + * @param registry 待注册的分析器注册表实例 + */ + public static void registerAll(AnalyzerRegistry registry) { + // ---------- 注册语句分析器 ---------- + registry.registerStatementAnalyzer(DeclarationNode.class, new DeclarationAnalyzer()); + registry.registerStatementAnalyzer(AssignmentNode.class, new AssignmentAnalyzer()); + registry.registerStatementAnalyzer(IfNode.class, new IfAnalyzer()); + registry.registerStatementAnalyzer(LoopNode.class, new LoopAnalyzer()); + registry.registerStatementAnalyzer(ReturnNode.class, new ReturnAnalyzer()); + + // 特殊处理:表达式语句(如 "foo();")作为语句包装表达式 + registry.registerStatementAnalyzer(ExpressionStatementNode.class, + (ctx, mi, fn, locals, stmt) -> + registry.getExpressionAnalyzer(stmt.expression()) + .analyze(ctx, mi, fn, locals, stmt.expression()) + ); + + // ---------- 注册表达式分析器 ---------- + registry.registerExpressionAnalyzer(NumberLiteralNode.class, new NumberLiteralAnalyzer()); + registry.registerExpressionAnalyzer(StringLiteralNode.class, new StringLiteralAnalyzer()); + registry.registerExpressionAnalyzer(IdentifierNode.class, new IdentifierAnalyzer()); + registry.registerExpressionAnalyzer(CallExpressionNode.class, new CallExpressionAnalyzer()); + registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer()); + + // 对尚未实现的表达式类型使用兜底处理器(如 MemberExpression) + registry.registerExpressionAnalyzer(MemberExpressionNode.class, + new UnsupportedExpressionAnalyzer<>()); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java new file mode 100644 index 0000000..876411e --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java @@ -0,0 +1,53 @@ +package org.jcnc.snow.compiler.semantic.core; + +import org.jcnc.snow.compiler.semantic.type.*; + +import java.util.Map; + +/** + * {@code BuiltinTypeRegistry} 是内置类型和内置模块的集中注册中心。 + *

+ * 本类主要负责: + *

    + *
  • 定义语言中所有可识别的基础类型(如 int、float、string 等);
  • + *
  • 在语义分析初始化时,将内置模块(如 {@code BuiltinUtils})注册到上下文中;
  • + *
  • 提供对内置类型的快速查找支持。
  • + *
+ * 该类为纯工具类,所有成员均为静态,不可实例化。 + */ +public final class BuiltinTypeRegistry { + + /** + * 内置类型映射表:将类型名称字符串映射到对应的 {@link Type} 实例。 + *

+ * 用于类型解析过程(如解析变量声明或函数返回类型)中, + * 将用户源码中的类型字符串转换为语义类型对象。 + */ + public static final Map BUILTIN_TYPES = Map.of( + "int", BuiltinType.INT, + "long", BuiltinType.LONG, + "short", BuiltinType.SHORT, + "byte", BuiltinType.BYTE, + "float", BuiltinType.FLOAT, + "double", BuiltinType.DOUBLE, + "string", BuiltinType.STRING, + "void", BuiltinType.VOID + ); + + /** + * 私有构造函数,禁止实例化。 + */ + private BuiltinTypeRegistry() { } + + /** + * 初始化语义上下文中与内置模块相关的内容。 + *

+ * 当前实现将内置模块 {@code BuiltinUtils} 注册至上下文模块表中, + * 使其在用户代码中可被访问(如 {@code BuiltinUtils.to_string(...)})。 + * + * @param ctx 当前语义分析上下文 + */ + public static void init(Context ctx) { + + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java new file mode 100644 index 0000000..2d051cb --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java @@ -0,0 +1,127 @@ +package org.jcnc.snow.compiler.semantic.core; + +import org.jcnc.snow.compiler.semantic.analyzers.AnalyzerRegistry; +import org.jcnc.snow.compiler.semantic.error.SemanticError; +import org.jcnc.snow.compiler.semantic.type.Type; + +import java.util.List; +import java.util.Map; + +/** + * {@code Context} 表示语义分析阶段的共享上下文环境。 + *

+ * 它贯穿整个语义分析流程,用于维护并提供以下核心服务: + *

    + *
  • 模块信息管理:包含所有已加载模块(源模块与内置模块);
  • + *
  • 错误收集:集中存储语义分析期间产生的 {@link SemanticError};
  • + *
  • 日志控制:支持按需输出详细调试日志;
  • + *
  • 分析器调度:通过 {@link AnalyzerRegistry} 管理语句/表达式的分析器分发。
  • + *
+ */ +public class Context { + /** 模块表:模块名 → {@link ModuleInfo},用于模块查找与跨模块引用 */ + private final Map modules; + + /** 错误列表:语义分析过程中收集的所有 {@link SemanticError} */ + private final List errors; + + /** 日志开关:若为 true,将启用 {@link #log(String)} 输出日志信息 */ + private final boolean verbose; + + /** 语义分析器注册表:用于按节点类型动态调度分析器 */ + private final AnalyzerRegistry registry; + + /** + * 构造语义分析上下文对象。 + * + * @param modules 已注册的模块信息集合 + * @param errors 错误收集器,分析器将所有语义错误写入此列表 + * @param verbose 是否启用调试日志输出 + * @param registry 分析器注册表,提供类型到分析器的映射与调度能力 + */ + public Context(Map modules, + List errors, + boolean verbose, + AnalyzerRegistry registry) { + this.modules = modules; + this.errors = errors; + this.verbose = verbose; + this.registry = registry; + } + + // ------------------ 模块信息 ------------------ + + /** + * 获取所有模块信息映射表。 + * + * @return 模块名 → 模块信息 {@link ModuleInfo} 的映射 + */ + public Map getModules() { + return modules; + } + + /** @return 模块信息(快捷方式) */ + public Map modules() { + return modules; + } + + // ------------------ 错误收集 ------------------ + + /** + * 获取语义分析过程中记录的所有错误。 + * + * @return 错误列表 + */ + public List getErrors() { + return errors; + } + + /** @return 错误列表(快捷方式) */ + public List errors() { + return errors; + } + + // ------------------ 分析器注册表 ------------------ + + /** + * 获取分析器注册表,用于分发语句与表达式分析器。 + * + * @return {@link AnalyzerRegistry} 实例 + */ + public AnalyzerRegistry getRegistry() { + return registry; + } + + /** @return 注册表(快捷方式) */ + public AnalyzerRegistry registry() { + return registry; + } + + // ------------------ 日志输出 ------------------ + + /** + * 打印日志信息,仅当 {@code verbose} 为 true 时生效。 + * + * @param msg 日志内容 + */ + public void log(String msg) { + if (verbose) { + System.out.println("[SemanticAnalyzer] " + msg); + } + } + + // ------------------ 工具函数 ------------------ + + /** + * 将类型名称字符串解析为对应的内置 {@link Type} 实例。 + *

+ * 若类型在 {@link BuiltinTypeRegistry#BUILTIN_TYPES} 中存在,则返回对应类型; + * 否则返回 {@code null},调用方可据此决定是否降级处理。 + * + * @param name 类型名称(如 "int", "float", "void", "string" 等) + * @return 匹配的 {@link Type},若无匹配项则返回 {@code null} + */ + public Type parseType(String name) { + return BuiltinTypeRegistry.BUILTIN_TYPES.get(name); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/FunctionChecker.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/FunctionChecker.java new file mode 100644 index 0000000..e6d69c3 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/FunctionChecker.java @@ -0,0 +1,85 @@ +package org.jcnc.snow.compiler.semantic.core; + +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.parser.ast.ModuleNode; +import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer; +import org.jcnc.snow.compiler.semantic.error.SemanticError; +import org.jcnc.snow.compiler.semantic.symbol.Symbol; +import org.jcnc.snow.compiler.semantic.symbol.SymbolKind; +import org.jcnc.snow.compiler.semantic.symbol.SymbolTable; + +/** + * {@code FunctionChecker} 是语义分析阶段中用于检查函数体语句合法性的调度器。 + *

+ * 它逐个遍历所有模块中的函数定义,并对函数体中的每一条语句调用对应的语义分析器, + * 执行类型检查、作用域验证、错误记录等任务。 + *

+ * 核心职责包括: + *

    + *
  • 为每个函数构建局部符号表并注册函数参数为变量;
  • + *
  • 分发函数体语句至相应的 {@link StatementAnalyzer};
  • + *
  • 记录未支持语句类型为语义错误;
  • + *
  • 依赖上下文 {@link Context} 提供模块信息、类型解析、错误收集等服务。
  • + *
+ * + * @param ctx 全局语义分析上下文,提供模块信息、注册表、错误记录等支持 + */ +public record FunctionChecker(Context ctx) { + + /** + * 构造函数体检查器。 + * + * @param ctx 当前语义分析上下文 + */ + public FunctionChecker { + } + + /** + * 执行函数体检查流程。 + *

+ * 对所有模块中的所有函数依次进行处理: + *

    + *
  1. 查找模块对应的 {@link ModuleInfo};
  2. + *
  3. 创建函数局部符号表 {@link SymbolTable},并注册所有参数变量;
  4. + *
  5. 对函数体中的每一条语句分发到已注册的分析器进行语义分析;
  6. + *
  7. 若某条语句无可用分析器,则记录为 {@link SemanticError}。
  8. + *
+ * + * @param mods 所有模块的 AST 根节点集合 + */ + public void check(Iterable mods) { + for (ModuleNode mod : mods) { + // 获取当前模块对应的语义信息 + ModuleInfo mi = ctx.modules().get(mod.name()); + + // 遍历模块中所有函数定义 + for (FunctionNode fn : mod.functions()) { + + // 构建函数局部作用域符号表,设置父作用域为 null + SymbolTable locals = new SymbolTable(null); + + // 将函数参数注册为局部变量 + fn.parameters().forEach(p -> + locals.define(new Symbol( + p.name(), + ctx.parseType(p.type()), + SymbolKind.VARIABLE + )) + ); + + // 遍历并分析函数体内的每条语句 + for (var stmt : fn.body()) { + var analyzer = ctx.getRegistry().getStatementAnalyzer(stmt); + if (analyzer != null) { + analyzer.analyze(ctx, mi, fn, locals, stmt); + } else { + ctx.errors().add(new SemanticError( + stmt, + "不支持的语句类型: " + stmt + )); + } + } + } + } + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleInfo.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleInfo.java new file mode 100644 index 0000000..074e8fa --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleInfo.java @@ -0,0 +1,72 @@ +package org.jcnc.snow.compiler.semantic.core; + +import org.jcnc.snow.compiler.semantic.type.FunctionType; + +import java.util.*; + +/** + * {@code ModuleInfo} 表示单个模块在语义分析阶段的元信息封装。 + *

+ * 用于在分析期间管理模块间依赖、函数签名查找等关键任务。 + * 每个模块对应一个唯一的 {@code ModuleInfo} 实例。 + *

+ * 包含信息包括: + *

    + *
  • 模块名称(唯一标识);
  • + *
  • 该模块导入的其他模块名集合;
  • + *
  • 该模块中定义的所有函数签名 {@code Map}。
  • + *
+ */ +public class ModuleInfo { + + /** 模块名称,作为全局唯一标识 */ + private final String name; + + /** 该模块显式导入的模块名集合(用于跨模块访问符号) */ + private final Set imports = new HashSet<>(); + + /** 该模块中定义的函数名 → 函数类型映射 */ + private final Map functions = new HashMap<>(); + + /** + * 构造模块信息对象。 + * + * @param name 模块名称,必须唯一且不可为空 + */ + public ModuleInfo(String name) { + this.name = name; + } + + /** + * 获取模块名称。 + * + * @return 当前模块的唯一名称 + */ + public String getName() { + return name; + } + + /** + * 获取该模块导入的模块名称集合。 + *

+ * 返回集合为内部数据的直接引用,调用方可通过 {@code add/remove} 方法动态维护导入信息。 + * + * @return 可变集合,包含所有导入模块名 + */ + public Set getImports() { + return imports; + } + + /** + * 获取模块中已声明的函数签名表。 + *

+ * 映射键为函数名,值为对应的 {@link FunctionType}。 + * 返回对象为内部引用,可用于添加、修改或删除函数定义。 + * + * @return 模块内函数定义映射表 + */ + public Map getFunctions() { + return functions; + } + +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleRegistry.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleRegistry.java new file mode 100644 index 0000000..adf7c8b --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleRegistry.java @@ -0,0 +1,40 @@ +package org.jcnc.snow.compiler.semantic.core; + +import org.jcnc.snow.compiler.parser.ast.ModuleNode; + +/** + * {@code ModuleRegistry} 负责将用户源码中声明的模块名称注册至全局语义上下文。 + *

+ * 它会遍历语法树中的所有模块节点 {@link ModuleNode},并将其模块名填入 + * {@link Context#modules()} 映射中,为后续语义分析阶段中的模块查找、 + * 导入验证和跨模块调用提供支持。 + *

+ * 注册结果为 {@code Map},键为模块名,值为新建的 {@link ModuleInfo}。 + * 若某模块名已存在(如内置模块),则不会重复注册。 + * + * @param ctx 当前语义分析上下文,用于访问模块表 + */ +public record ModuleRegistry(Context ctx) { + + /** + * 构造模块注册器。 + * + * @param ctx 当前语义分析上下文 + */ + public ModuleRegistry { + } + + /** + * 遍历并注册所有用户定义模块。 + *

+ * 对于每个模块节点,将其名称注册到 {@code ctx.modules()} 中作为键, + * 若模块已存在(例如内置模块),则不会覆盖。 + * + * @param mods 所有模块节点的集合 + */ + public void registerUserModules(Iterable mods) { + for (ModuleNode mod : mods) { + ctx.modules().putIfAbsent(mod.name(), new ModuleInfo(mod.name())); + } + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/SemanticAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/SemanticAnalyzer.java new file mode 100644 index 0000000..c3a1493 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/SemanticAnalyzer.java @@ -0,0 +1,80 @@ +package org.jcnc.snow.compiler.semantic.core; + +import org.jcnc.snow.compiler.parser.ast.ModuleNode; +import org.jcnc.snow.compiler.semantic.analyzers.AnalyzerRegistry; +import org.jcnc.snow.compiler.semantic.error.SemanticError; + +import java.util.*; + +/** + * {@code SemanticAnalyzer} 是编译器语义分析阶段的顶层调度器。 + *

+ * 它负责统一协调模块注册、函数签名登记和函数体语义检查等子任务,构建并维护语义上下文 {@link Context}, + * 并最终输出所有收集到的语义错误列表 {@link SemanticError}。 + *

+ * 语义分析流程分为三个阶段: + *

    + *
  1. 模块注册:将所有用户模块的名称添加至全局模块表中,供后续导入检查与引用;
  2. + *
  3. 函数签名注册:提取函数定义的签名(名称与类型),填入每个模块对应的 {@link ModuleInfo};
  4. + *
  5. 函数体检查:遍历每个函数体,对所有语句与表达式执行类型检查和语义验证。
  6. + *
+ *

+ * 内部使用组件: + *

    + *
  • {@link ModuleRegistry}:注册用户模块;
  • + *
  • {@link SignatureRegistrar}:提取函数签名;
  • + *
  • {@link FunctionChecker}:分析函数体内语句;
  • + *
  • {@link BuiltinTypeRegistry}:初始化内置模块和类型;
  • + *
  • {@link AnalyzerRegistrar}:注册语句和表达式分析器。
  • + *
+ */ +public class SemanticAnalyzer { + + /** 全局语义分析上下文,包含模块信息、错误记录、分析器注册表等 */ + private final Context ctx; + + /** 分析器注册表,管理语法节点与分析器之间的映射关系 */ + private final AnalyzerRegistry registry = new AnalyzerRegistry(); + + // 分析流程中用到的核心子组件 + private final ModuleRegistry moduleRegistry; + private final SignatureRegistrar signatureRegistrar; + private final FunctionChecker functionChecker; + + /** + * 构造语义分析器并完成初始配置。 + * + * @param verbose 是否启用日志输出 + */ + public SemanticAnalyzer(boolean verbose) { + this.ctx = new Context(new HashMap<>(), new ArrayList<>(), verbose, registry); + + // 初始化内置模块及分析器注册表 + BuiltinTypeRegistry.init(ctx); + AnalyzerRegistrar.registerAll(registry); + + // 构造核心组件 + this.moduleRegistry = new ModuleRegistry(ctx); + this.signatureRegistrar = new SignatureRegistrar(ctx); + this.functionChecker = new FunctionChecker(ctx); + } + + /** + * 执行完整语义分析流程。 + *

+ * 输入为用户的模块语法树集合,输出为分析阶段产生的语义错误列表。 + * + * @param modules 所有用户模块(语法树) + * @return 所有语义错误的列表,若分析无误则为空 + */ + public List analyze(List modules) { + ctx.log("开始语义分析"); + + moduleRegistry.registerUserModules(modules); // 注册模块名 + signatureRegistrar.register(modules); // 提取函数签名 + functionChecker.check(modules); // 分析函数体 + + ctx.log("分析完成,错误总数: " + ctx.errors().size()); + return ctx.errors(); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/SemanticAnalyzerRunner.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/SemanticAnalyzerRunner.java new file mode 100644 index 0000000..0998114 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/SemanticAnalyzerRunner.java @@ -0,0 +1,49 @@ +package org.jcnc.snow.compiler.semantic.core; + +import org.jcnc.snow.compiler.parser.ast.ModuleNode; +import org.jcnc.snow.compiler.parser.ast.base.Node; +import org.jcnc.snow.compiler.semantic.error.SemanticError; +import org.jcnc.snow.compiler.semantic.utils.SemanticAnalysisReporter; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * {@code SemanticAnalyzerRunner} 是语义分析阶段的统一入口与调度器。 + *

+ * 功能职责: + *

    + *
  • 从原始 AST 列表中过滤并收集所有 {@link ModuleNode} 节点,作为模块分析的起点;
  • + *
  • 调用 {@link SemanticAnalyzer} 对所有模块节点执行完整语义分析流程;
  • + *
  • 汇总并报告所有 {@link SemanticError};如有语义错误,自动中止编译流程,防止后续崩溃。
  • + *
+ *

+ * 推荐使用方式: + *

+ *     SemanticAnalyzerRunner.runSemanticAnalysis(ast, true);
+ * 
+ *

+ * 该类是实现 SCompiler “所有错误一次性输出,且错误即终止” 语义分析约束的关键。 + */ +public class SemanticAnalyzerRunner { + + /** + * 对输入的语法树执行语义分析并自动报告。 + * + * @param ast 根节点列表(应包含一个或多个 {@link ModuleNode}) + * @param verbose 是否启用详细日志(将控制内部 {@link Context#log(String)} 的行为) + */ + public static void runSemanticAnalysis(List ast, boolean verbose) { + // 1. 从 AST 列表中过滤所有模块节点 ModuleNode + List modules = ast.stream() + .filter(ModuleNode.class::isInstance) // 保留类型为 ModuleNode 的节点 + .map(ModuleNode.class::cast) // 转换为 ModuleNode + .collect(Collectors.toList()); // 收集为 List + + // 2. 调用语义分析器,对所有模块进行全流程语义分析,返回错误列表 + List errors = new SemanticAnalyzer(verbose).analyze(modules); + + // 3. 统一报告全部语义错误;如有错误则自动终止编译(System.exit) + SemanticAnalysisReporter.reportAndExitIfNecessary(errors); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/SignatureRegistrar.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/SignatureRegistrar.java new file mode 100644 index 0000000..1b6fc50 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/SignatureRegistrar.java @@ -0,0 +1,86 @@ +package org.jcnc.snow.compiler.semantic.core; + +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.parser.ast.ImportNode; +import org.jcnc.snow.compiler.parser.ast.ModuleNode; +import org.jcnc.snow.compiler.parser.ast.ParameterNode; +import org.jcnc.snow.compiler.semantic.error.SemanticError; +import org.jcnc.snow.compiler.semantic.type.BuiltinType; +import org.jcnc.snow.compiler.semantic.type.FunctionType; +import org.jcnc.snow.compiler.semantic.type.Type; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * {@code SignatureRegistrar} 负责函数签名登记与导入语义检查。 + *

+ * 在语义分析初期阶段,它遍历每个模块,完成以下任务: + *

    + *
  • 验证所有 {@link ImportNode} 导入的模块是否存在于全局模块表 {@link Context#modules()} 中;
  • + *
  • 将每个 {@link FunctionNode} 的函数签名(参数类型和返回类型)注册到对应 {@link ModuleInfo} 中;
  • + *
  • 在参数或返回类型无法识别时,记录 {@link SemanticError},并进行容错降级。
  • + *
+ * 本组件作为语义分析的准备阶段,为后续函数体检查提供函数类型上下文。 + * + * @param ctx 全局语义分析上下文,提供模块、类型、错误管理等功能 + */ +public record SignatureRegistrar(Context ctx) { + + /** + * 构造函数签名注册器。 + * + * @param ctx 当前语义分析上下文 + */ + public SignatureRegistrar { + } + + /** + * 遍历模块并注册函数签名,同时校验导入模块的合法性。 + * + * @param mods 所有模块的语法树节点集合 + */ + public void register(Iterable mods) { + for (ModuleNode mod : mods) { + ModuleInfo mi = ctx.modules().get(mod.name()); + + // ---------- 1. 模块导入检查 ---------- + for (ImportNode imp : mod.imports()) { + if (!ctx.modules().containsKey(imp.moduleName())) { + ctx.errors().add(new SemanticError( + imp, + "未知模块: " + imp.moduleName() + )); + } else { + mi.getImports().add(imp.moduleName()); + } + } + + // ---------- 2. 函数签名注册 ---------- + for (FunctionNode fn : mod.functions()) { + List params = new ArrayList<>(); + + // 参数类型解析 + for (ParameterNode p : fn.parameters()) { + Type t = Optional.ofNullable(ctx.parseType(p.type())) + .orElseGet(() -> { + ctx.errors().add(new SemanticError( + p, + "未知类型: " + p.type() + )); + return BuiltinType.INT; // 容错降级 + }); + params.add(t); + } + + // 返回类型解析(默认降级为 void) + Type ret = Optional.ofNullable(ctx.parseType(fn.returnType())) + .orElse(BuiltinType.VOID); + + // 注册函数签名 + mi.getFunctions().put(fn.name(), new FunctionType(params, ret)); + } + } + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/doc/README.md b/src/main/java/org/jcnc/snow/compiler/semantic/doc/README.md new file mode 100644 index 0000000..6d921d2 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/doc/README.md @@ -0,0 +1,41 @@ +# Snow Compiler - Semantic 模块 + +> Snow 编译器的前端模块 —— 负责语义分析与符号表管理 + +## 项目简介 + +**Semantic** 是 [Snow 编译器]() 项目的子模块,承担编译器前端的语义分析阶段任务。 +它负责在抽象语法树(AST)基础上,进行符号绑定、类型检查、作用域管理等操作,确保程序符合语义规则,并为后续中间代码生成(IR)阶段提供准备。 + +该模块采用模块化设计,针对不同类型的表达式与语句提供独立的分析器,同时内置完整的符号表和类型系统支持。 + +## 核心功能 + +- **表达式与语句语义分析**:根据 Snow 语言规范校验各类 AST 节点 +- **符号表管理**:支持作用域嵌套、符号绑定与查找 +- **类型系统**:内建基础类型与函数类型推导 +- **语义错误报告**:标准化错误收集与输出 +- **可扩展的分析器体系**:按节点类型组织,便于维护与扩展 + +## 模块结构 + +``` +semantic/ + ├── analyzers/ // 各类表达式和语句分析器 + │ ├── base/ // 基础分析器接口 + │ ├── expression/ // 表达式分析器 + │ └── statement/ // 语句分析器 + ├── core/ // 核心分析引擎与上下文管理 + ├── error/ // 语义错误定义 + ├── symbol/ // 符号表系统 + ├── type/ // 类型系统 + └── utils/ // 辅助工具 +``` + +## 开发环境 + +* JDK 23 或更高版本 +* Maven 构建管理 +* 推荐 IDE:IntelliJ IDEA + +--- \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/error/SemanticError.java b/src/main/java/org/jcnc/snow/compiler/semantic/error/SemanticError.java new file mode 100644 index 0000000..121d0eb --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/error/SemanticError.java @@ -0,0 +1,32 @@ +package org.jcnc.snow.compiler.semantic.error; + + +import org.jcnc.snow.compiler.parser.ast.base.Node; + +/** + * 表示一次语义错误。
+ *
    + *
  • 记录对应 {@link Node} 及出错信息;
  • + *
  • 重写 {@link #toString()},以 行 X, 列 Y: message 格式输出,
  • + *
  • 避免默认的 Node@hash 形式。
  • + *
+ */ +public record SemanticError(Node node, String message) { + + @Override + public String toString() { + // Node 假定提供 line() / column() 方法;如无则返回 -1 + int line = -1; + int col = -1; + if (node != null) { + try { + line = (int) node.getClass().getMethod("line").invoke(node); + col = (int) node.getClass().getMethod("column").invoke(node); + } catch (ReflectiveOperationException ignored) { + // 若 Node 未提供 line/column 方法则保持 -1 + } + } + String pos = (line >= 0 && col >= 0) ? ("行 " + line + ", 列 " + col) : "未知位置"; + return pos + ": " + message; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/symbol/Symbol.java b/src/main/java/org/jcnc/snow/compiler/semantic/symbol/Symbol.java new file mode 100644 index 0000000..f6cc9dc --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/symbol/Symbol.java @@ -0,0 +1,24 @@ +package org.jcnc.snow.compiler.semantic.symbol; + +import org.jcnc.snow.compiler.semantic.type.Type; + +/** + * {@code Symbol} 表示符号表中的一条符号记录,描述语言中命名实体的语义信息。 + *

+ * 符号是语义分析中的基础单元,通常用于表示变量、函数、参数等具名元素。 + * 每个符号具备以下三个核心属性: + *

    + *
  • name:符号的名称,例如变量名或函数名;
  • + *
  • type:符号的类型信息,通常为 {@link Type} 的子类实例;
  • + *
  • kind:符号的种类,由 {@link SymbolKind} 枚举表示(例如 VARIABLE、FUNCTION 等)。
  • + *
      + *
    • 构造器 {@code Symbol(String, Type, SymbolKind)};
    • + *
    • 访问器方法 {@code name()}, {@code type()}, {@code kind()};
    • + *
    • 标准的 {@code equals()}、{@code hashCode()} 和 {@code toString()} 实现。
    • + *
    + * + * @param name 符号名称,必须唯一且非空 + * @param type 符号所代表的类型,可为基础类型、函数类型等 + * @param kind 符号种类,决定其语义用途(例如变量、函数、参数等) + */ +public record Symbol(String name, Type type, SymbolKind kind) { } diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/symbol/SymbolKind.java b/src/main/java/org/jcnc/snow/compiler/semantic/symbol/SymbolKind.java new file mode 100644 index 0000000..625b2f4 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/symbol/SymbolKind.java @@ -0,0 +1,40 @@ +package org.jcnc.snow.compiler.semantic.symbol; + +/** + * {@code SymbolKind} 枚举用于标识符号表中不同类型的命名实体。 + *

    + * 在语义分析过程中,编译器需要根据符号的种类(Kind)采用不同的处理策略: + * 例如变量参与类型推导、函数用于调用匹配、模块用于跨作用域引用等。 + *

    + * 当前支持的符号种类包括: + *

      + *
    • {@link #VARIABLE}:变量符号(局部变量、全局变量、成员变量等);
    • + *
    • {@link #FUNCTION}:函数符号(自由函数、方法、构造函数等);
    • + *
    • {@link #MODULE}:模块符号(代表命名空间、库或逻辑模块);
    • + *
    + */ +public enum SymbolKind { + + /** + * 变量符号,表示在作用域中声明的可赋值实体。 + *

    + * 包括函数参数、局部变量、全局变量、常量等, + * 分析器会基于其类型参与表达式类型校验和赋值检查。 + */ + VARIABLE, + + /** + * 函数符号,表示可调用的过程实体。 + *

    + * 包括普通函数、方法、构造器等。 + * 用于函数签名注册、函数调用检查及返回值推导。 + */ + FUNCTION, + + /** + * 模块符号,表示一个命名空间或模块单元。 + *

    + * 在跨模块调用、导入语句校验、作用域隔离中发挥作用。 + */ + MODULE +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/symbol/SymbolTable.java b/src/main/java/org/jcnc/snow/compiler/semantic/symbol/SymbolTable.java new file mode 100644 index 0000000..29f81ab --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/symbol/SymbolTable.java @@ -0,0 +1,73 @@ +package org.jcnc.snow.compiler.semantic.symbol; + +import java.util.HashMap; +import java.util.Map; + +/** + * {@code SymbolTable} 表示一个语义作用域,用于管理命名实体(如变量、函数、模块等)的符号信息。 + *

    + * 本类支持嵌套作用域结构(Nested Scope),适用于块级作用域、函数作用域、模块作用域等语义环境建模。 + * 每个符号表可挂接一个“父作用域”,以支持多层级作用域链上的符号解析。 + * + *

    核心特性包括: + *

      + *
    • 符号定义(局部作用域内唯一);
    • + *
    • 符号查找(向上查找父作用域);
    • + *
    • 嵌套作用域支持(通过 parent 引用);
    • + *
    • 可用于语义分析、类型检查、作用域验证等场景。
    • + *
    + */ +public class SymbolTable { + + /** 父作用域引用,若为 {@code null} 则表示为最外层作用域(全局或根作用域) */ + private final SymbolTable parent; + + /** 当前作用域内定义的符号映射表(符号名 → {@link Symbol}) */ + private final Map symbols = new HashMap<>(); + + /** + * 创建一个新的符号表实例。 + * + * @param parent 父作用域符号表,若无父作用域则传入 {@code null} + */ + public SymbolTable(SymbolTable parent) { + this.parent = parent; + } + + /** + * 在当前作用域中定义一个新的符号。 + *

    + * 若符号名称在当前作用域中已存在,则定义失败并返回 {@code false}; + * 否则将该符号添加至当前符号映射中并返回 {@code true}。 + * + * @param symbol 待定义的符号实体 + * @return 若定义成功则返回 {@code true};否则返回 {@code false} + */ + public boolean define(Symbol symbol) { + if (symbols.containsKey(symbol.name())) { + return false; + } + symbols.put(symbol.name(), symbol); + return true; + } + + /** + * 根据名称解析符号,支持作用域链向上递归查找。 + *

    查找策略: + *

      + *
    1. 优先在当前作用域中查找该符号名称;
    2. + *
    3. 若未找到且存在父作用域,则递归向上查找;
    4. + *
    5. 若所有作用域中均未找到,则返回 {@code null}。
    6. + *
    + * + * @param name 要解析的符号名称 + * @return 对应的 {@link Symbol} 实例,若未找到则返回 {@code null} + */ + public Symbol resolve(String name) { + Symbol sym = symbols.get(name); + if (sym != null) { + return sym; + } + return (parent != null) ? parent.resolve(name) : null; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/type/BuiltinType.java b/src/main/java/org/jcnc/snow/compiler/semantic/type/BuiltinType.java new file mode 100644 index 0000000..0602510 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/type/BuiltinType.java @@ -0,0 +1,90 @@ +package org.jcnc.snow.compiler.semantic.type; + +/** + * {@code BuiltinType} 枚举定义了本语言支持的所有内置基础类型。 + *

    + * 类型涵盖整数、浮点、字符串及 void 类型,广泛应用于变量声明、 + * 表达式类型推导、函数签名、类型检查等语义分析环节。 + * + *

    支持的类型包括: + *

      + *
    • {@link #BYTE} - 8 位整数
    • + *
    • {@link #SHORT} - 16 位整数
    • + *
    • {@link #INT} - 32 位整数
    • + *
    • {@link #LONG} - 64 位整数
    • + *
    • {@link #FLOAT} - 单精度浮点数
    • + *
    • {@link #DOUBLE} - 双精度浮点数
    • + *
    • {@link #STRING} - 字符串类型
    • + *
    • {@link #VOID} - 空类型,用于表示无返回值的函数
    • + *
    + * + *

    每个枚举实例实现了 {@link Type} 接口,提供以下语义特性: + *

      + *
    • 数值类型判断 {@link #isNumeric()};
    • + *
    • 类型兼容性判断 {@link #isCompatible(Type)};
    • + *
    • 自动数值宽化支持(通过 {@link Type#widen(Type, Type)} 实现)。
    • + *
    + */ +public enum BuiltinType implements Type { + + BYTE, // 8 位有符号整数 + SHORT, // 16 位有符号整数 + INT, // 32 位有符号整数(默认整数类型) + LONG, // 64 位有符号整数 + FLOAT, // 单精度浮点数 + DOUBLE, // 双精度浮点数 + STRING, // 字符串类型 + VOID; // 空类型,用于表示函数无返回值 + + /** + * 判断当前类型是否与指定类型兼容。 + *

    + * 兼容判断规则: + *

      + *
    • 类型完全相同,视为兼容;
    • + *
    • 对于数值类型,若目标类型为宽类型(如 int → double),视为兼容;
    • + *
    • 其他情况视为不兼容。
    • + *
    + * + * @param other 要比较的类型 + * @return 若兼容返回 {@code true},否则返回 {@code false} + */ + @Override + public boolean isCompatible(Type other) { + if (this == other) return true; + // 数值类型间允许自动宽化 + if (this.isNumeric() && other.isNumeric()) { + return Type.widen(other, this) == this; + } + return false; + } + + /** + * 判断当前类型是否为数值类型。 + *

    + * 数值类型包括: + * {@link #BYTE}、{@link #SHORT}、{@link #INT}、{@link #LONG}、 + * {@link #FLOAT}、{@link #DOUBLE}。 + * + * @return 若为数值类型返回 {@code true},否则返回 {@code false} + */ + @Override + public boolean isNumeric() { + return switch (this) { + case BYTE, SHORT, INT, LONG, FLOAT, DOUBLE -> true; + default -> false; + }; + } + + /** + * 获取当前类型的名称(小写形式)。 + *

    + * 用于日志输出、错误提示等语义描述场景。 + * + * @return 当前类型的名称字符串(如 "int", "string", "void" 等) + */ + @Override + public String toString() { + return name().toLowerCase(); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/type/FunctionType.java b/src/main/java/org/jcnc/snow/compiler/semantic/type/FunctionType.java new file mode 100644 index 0000000..3eecaa4 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/type/FunctionType.java @@ -0,0 +1,106 @@ +package org.jcnc.snow.compiler.semantic.type; + +import java.util.List; +import java.util.Objects; + +/** + * {@code FunctionType} 表示函数的类型信息,由参数类型列表返回类型组成。 + *

    + * 适用于函数声明、函数调用、类型检查等语义分析场景。 + *

    + * 例如,一个函数接受两个 {@code int} 参数并返回 {@code string},其函数类型为: + *

    + *   (int, int) -> string
    + * 
    + * + *

    该类使用 Java 16+ {@code record} 语法定义,自动提供: + *

      + *
    • 构造方法;
    • + *
    • 访问器 {@code paramTypes()} 和 {@code returnType()};
    • + *
    • {@code equals()}, {@code hashCode()}, {@code toString()} 方法;
    • + *
    + * + *

    实现接口:{@link Type} + * + * @param paramTypes 参数类型列表(顺序敏感,不可为 null) + * @param returnType 返回类型(不可为 null) + */ +public record FunctionType(List paramTypes, Type returnType) implements Type { + + /** + * 构造函数类型对象。 + *

    + * 参数类型列表将被包装为不可变,以确保函数类型不可修改。 + * + * @param paramTypes 参数类型列表 + * @param returnType 返回类型 + */ + public FunctionType(List paramTypes, Type returnType) { + this.paramTypes = List.copyOf(paramTypes); // 确保不可变 + this.returnType = returnType; + } + + /** + * 判断当前函数类型是否与另一个类型兼容。 + *

    + * 兼容条件: + *

      + *
    • 对方也是 {@code FunctionType};
    • + *
    • 返回类型兼容(可宽化或完全匹配);
    • + *
    • 参数列表完全相等(类型与顺序严格一致)。
    • + *
    + * + * @param other 要检查的另一个类型 + * @return 若兼容返回 {@code true},否则 {@code false} + */ + @Override + public boolean isCompatible(Type other) { + if (!(other instanceof FunctionType(List types, Type type))) return false; + return returnType.isCompatible(type) && paramTypes.equals(types); + } + + /** + * 返回函数类型的标准字符串表示形式。 + *

    + * 格式为: + *

    +     *   (param1, param2, ...) -> returnType
    +     * 
    + * 例如 {@code (int, string) -> void} + * + * @return 函数类型的可读性描述 + */ + @Override + public String toString() { + return "(" + paramTypes + ") -> " + returnType; + } + + /** + * 判断两个函数类型是否相等。 + *

    + * 相等条件为: + *

      + *
    • 引用相同;
    • + *
    • 或参数类型列表完全相等,且返回类型也相等。
    • + *
    + * + * @param obj 待比较的对象 + * @return 若相等返回 {@code true},否则 {@code false} + */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof FunctionType(List types, Type type))) return false; + return returnType.equals(type) && paramTypes.equals(types); + } + + /** + * 计算哈希码,确保与 {@link #equals(Object)} 保持一致性。 + * + * @return 哈希值 + */ + @Override + public int hashCode() { + return Objects.hash(paramTypes, returnType); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java b/src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java new file mode 100644 index 0000000..deecc05 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java @@ -0,0 +1,50 @@ +package org.jcnc.snow.compiler.semantic.type; + +/** + * 类型接口:所有类型(包括内置类型、函数类型等)均需实现此接口, + * 用于在语义分析中进行类型兼容性检查和统一表示。 + */ +public interface Type { + /** + * 判断当前类型是否与另一个类型兼容。 + * + * @param other 要检查兼容性的另一个类型 + * @return 如果兼容则返回 true,否则返回 false + */ + boolean isCompatible(Type other); + + /** + * 判断当前类型是否为数值类型(byte/short/int/long/float/double)。 + *

    + * 默认实现返回 false,BuiltinType 会覆盖此方法。 + * + * @return 如果是数值类型则返回 true,否则返回 false + */ + default boolean isNumeric() { + return false; + } + + /** + * 对两个数值类型执行宽化转换,返回“更宽”的那个类型。 + *

    + * 若 a 和 b 都是数值类型,则按 byte→short→int→long→float→double 顺序选更宽的类型; + * 否则返回 null。 + * + * @param a 第一个类型 + * @param b 第二个类型 + * @return 两者中更宽的数值类型,或 null + */ + static Type widen(Type a, Type b) { + if (!(a.isNumeric() && b.isNumeric())) return null; + var order = java.util.List.of( + BuiltinType.BYTE, + BuiltinType.SHORT, + BuiltinType.INT, + BuiltinType.LONG, + BuiltinType.FLOAT, + BuiltinType.DOUBLE + ); + int ia = order.indexOf(a), ib = order.indexOf(b); + return order.get(Math.max(ia, ib)); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/utils/SemanticAnalysisReporter.java b/src/main/java/org/jcnc/snow/compiler/semantic/utils/SemanticAnalysisReporter.java new file mode 100644 index 0000000..dc2b336 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/utils/SemanticAnalysisReporter.java @@ -0,0 +1,51 @@ +package org.jcnc.snow.compiler.semantic.utils; + +import org.jcnc.snow.compiler.semantic.error.SemanticError; + +import java.util.List; + +/** + * {@code SemanticAnalysisReporter} 用于在语义分析结束后汇总并打印所有收集到的 + * {@link SemanticError}。为了同时满足“完整错误收集”与“按需快速失败”两种使用场景, + * 现在提供两个公共 API: + *

      + *
    • {@link #report(List)} ‑ 仅打印,不终止;
    • + *
    • {@link #reportAndExitIfNecessary(List)} ‑ 若存在错误则 打印并退出
    • + *
    + * 调用方可根据需求选择合适方法。 + */ +public final class SemanticAnalysisReporter { + + private SemanticAnalysisReporter() { } + + /** + * 打印语义分析结果;不会退出进程。 + * + * @param errors 语义分析阶段收集到的错误列表(允许为 {@code null}) + */ + public static void report(List errors) { + if (hasErrors(errors)) { + System.err.println("语义分析发现 " + errors.size() + " 个错误:"); + errors.forEach(err -> System.err.println(" " + err)); + } else { +// System.out.println("## 语义分析通过,没有发现错误\n"); + } + } + + /** + * 打印语义分析结果;如有错误立即以状态码 1 结束进程。 + * 适用于 CLI 工具需要立即中止后续编译阶段的场景。 + * + * @param errors 语义分析阶段收集到的错误列表(允许为 {@code null}) + */ + public static void reportAndExitIfNecessary(List errors) { + report(errors); + if (hasErrors(errors)) { + System.exit(1); + } + } + + private static boolean hasErrors(List errors) { + return errors != null && !errors.isEmpty(); + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/vm/VMInitializer.java b/src/main/java/org/jcnc/snow/vm/VMInitializer.java new file mode 100644 index 0000000..fc1ce08 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/VMInitializer.java @@ -0,0 +1,64 @@ +package org.jcnc.snow.vm; + +import org.jcnc.snow.vm.execution.CommandLoader; +import org.jcnc.snow.vm.engine.VMCommandExecutor; +import org.jcnc.snow.vm.engine.VMMode; +import org.jcnc.snow.vm.engine.VirtualMachineEngine; +import org.jcnc.snow.vm.io.FilePathResolver; +import org.jcnc.snow.vm.utils.VMStateLogger; + +import java.util.List; + +/** + * Virtual Machine Initialization Class, responsible for the VM startup process, including: + *
      + *
    • Retrieving and validating the file path
    • + *
    • Loading the instruction set
    • + *
    • Executing the instructions
    • + *
    • Printing the virtual machine's state
    • + *
    + *

    + * This class contains the main method `main(String[] args)`, which initiates the virtual machine execution flow. + *

    + *

    The main process:

    + *
      + *
    1. Retrieve and parse the file path from command-line arguments
    2. + *
    3. Load the instructions from the specified file path
    4. + *
    5. Execute the loaded instructions using the virtual machine engine
    6. + *
    7. Print the virtual machine's current state
    8. + *
    + */ +public class VMInitializer { + /** + * Default constructor for creating an instance of VMInitializer. + * This constructor is empty as no specific initialization is required. + */ + public VMInitializer() { + // Empty constructor + } + + /** + * Initializes the virtual machine by processing the file path, loading instructions, executing them, + * and printing the virtual machine's state. + * + * @param args Command-line arguments containing the file path of the virtual machine instructions + * @param vmMode The mode in which the virtual machine should operate. + * This can be used to specify different operational modes (e.g., debug mode, normal mode). + */ + public static void initializeAndRunVM(String[] args, VMMode vmMode) { + // Retrieve and validate file path + String filePath = FilePathResolver.getFilePath(args); + if (filePath == null) return; + + // Load commands from the file + List commands = CommandLoader.loadInstructions(filePath); + if (commands.isEmpty()) return; + + // Execute the commands using the virtual machine engine + VirtualMachineEngine virtualMachineEngine = new VirtualMachineEngine(vmMode); + VMCommandExecutor.executeInstructions(virtualMachineEngine, commands); + + // Print the virtual machine's state + VMStateLogger.printVMState(virtualMachineEngine); + } +} diff --git a/src/main/java/org/jcnc/snow/vm/VMLauncher.java b/src/main/java/org/jcnc/snow/vm/VMLauncher.java new file mode 100644 index 0000000..bbcfca8 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/VMLauncher.java @@ -0,0 +1,49 @@ +package org.jcnc.snow.vm; + +import org.jcnc.snow.vm.engine.VMMode; + +import static org.jcnc.snow.vm.VMInitializer.initializeAndRunVM; + +/** + * The {@code VMLauncher} class is responsible for initiating the virtual machine (VM) execution process. + * It processes command-line arguments, loads the instruction set, and invokes the virtual machine engine to + * execute the instructions. It then prints the virtual machine's current state, allowing users to observe + * the VM's progress and results. + * + *

    This class provides the entry point to launch the virtual machine. The main method retrieves the file path + * of the VM instructions from the command-line arguments, initializes the VM engine, and runs the VM in the + * specified mode (e.g., {@link VMMode#DEBUG}).

    + */ +public class VMLauncher { + + /** + * Default constructor for creating an instance of {@code VMLauncher}. + *

    This constructor is empty as no specific initialization is required + * for the {@code VMLauncher} class.

    + */ + public VMLauncher() { + // Empty constructor + } + + /** + * The main method serves as the entry point to start the virtual machine execution process. + * It processes the command-line arguments to retrieve the file path for the virtual machine's + * instruction set, loads the instructions from the specified file, and executes them using + * the virtual machine engine. + * + *

    The sequence of operations in this method is as follows:

    + *
      + *
    1. Retrieve and validate the file path from the command-line arguments.
    2. + *
    3. Load the instruction set from the specified file.
    4. + *
    5. Execute the instructions using the virtual machine engine.
    6. + *
    7. Output the current state of the virtual machine.
    8. + *
    + * + * @param args Command-line arguments passed to the program. The primary argument expected + * is the file path pointing to the virtual machine's instruction set. + */ + public static void main(String[] args) { + // Call the method that initializes and runs the VM in DEBUG mode + initializeAndRunVM(args, VMMode.DEBUG); + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BAddCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BAddCommand.java new file mode 100644 index 0000000..b05f809 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BAddCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.byte8; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * BAddCommand Opcode: Represents the byte8 addition operation in the virtual machine. + *

    This opcode is implemented by the {@link BAddCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two byte8 values from the operand stack.
    2. + *
    3. Performs the addition of the two values (i.e., a + b).
    4. + *
    5. Pushes the sum back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to add two byte8 values together, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class BAddCommand implements Command { + + /** + * Default constructor for creating an instance of BAddCommand. + * This constructor is empty as no specific initialization is required. + */ + public BAddCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for adding two byte8 values. + * + *

    This method retrieves the two byte8 values from the operand stack, adds them together, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + byte b = (byte) operandStack.pop(); + byte a = (byte) operandStack.pop(); + + // Perform the addition and push the result back onto the stack + operandStack.push(a + b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BDivCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BDivCommand.java new file mode 100644 index 0000000..ad24912 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BDivCommand.java @@ -0,0 +1,64 @@ +package org.jcnc.snow.vm.commands.arithmetic.byte8; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * BDivCommand Opcode: Represents the byte8 division operation in the virtual machine. + *

    This opcode is implemented by the {@link BDivCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two byte8 values from the operand stack.
    2. + *
    3. Performs the division operation (i.e., a / b).
    4. + *
    5. Checks for division by zero to prevent errors.
    6. + *
    7. Pushes the result of the division back onto the operand stack for subsequent instructions to use.
    8. + *
    + * + *

    This opcode is typically used to divide one byte8 value by another, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class BDivCommand implements Command { + + /** + * Default constructor for creating an instance of BDivCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public BDivCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for dividing two byte8 values. + * + *

    This method retrieves the two byte8 values from the operand stack, checks for division by zero, performs the division, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + byte b = (byte) operandStack.pop(); + byte a = (byte) operandStack.pop(); + + // Check for division by zero + if (b == 0) { + throw new ArithmeticException("Division by zero is not allowed."); + } + + // Perform the division and push the result back onto the stack + operandStack.push(a / b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BIncCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BIncCommand.java new file mode 100644 index 0000000..8d2dd72 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BIncCommand.java @@ -0,0 +1,70 @@ +package org.jcnc.snow.vm.commands.arithmetic.byte8; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * BIncCommand Opcode: Represents the byte8 increment operation for a local variable in the virtual machine. + *

    This opcode is implemented by the {@link BIncCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Extracts the index of the local variable and the increment value from the instruction parameters.
    2. + *
    3. Retrieves the current value of the local variable at the given index.
    4. + *
    5. Increments the value of the local variable by the specified increment value (i.e., localVariables[index] += increment).
    6. + *
    7. Updates the local variable with the new incremented value.
    8. + *
    9. Returns the updated program counter (PC) value, which typically increments by 1 unless control flow is modified.
    10. + *
    + * + *

    This opcode is useful for optimizing the modification of local variables, especially in tight loops or when managing counters.

    + */ +public class BIncCommand implements Command { + + /** + * Default constructor for creating an instance of BIncCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public BIncCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for incrementing a local variable. + * + *

    This method retrieves the necessary data (index of the local variable and the increment value) from the instruction parameters, + * performs the increment operation on the specified local variable, and returns the updated program counter (PC) value.

    + * + * @param parts The array of instruction parameters, which includes the index of the local variable and + * the increment value (passed directly as bytecode parameters). + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, including the one being incremented. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Retrieve the index of the local variable and the increment value from the parameters + + int localVariableIndex = Integer.parseInt(parts[1]); // Index of the local variable to be incremented + + byte incrementValue = Byte.parseByte(parts[2]); // The value by which to increment the local variable + + // Get the current value of the local variable at the specified index + byte currentValue = (byte) callStack.peekFrame().getLocalVariableStore().getVariable(localVariableIndex); + + // Increment the local variable value by the specified increment + byte newValue = (byte) (currentValue + incrementValue); + + // Update the local variable with the new incremented value + callStack.peekFrame().getLocalVariableStore().setVariable(localVariableIndex, newValue); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BModCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BModCommand.java new file mode 100644 index 0000000..88b12bd --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BModCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.byte8; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * BModCommand Opcode: Represents the byte8 modulus operation in the virtual machine. + *

    This opcode is implemented by the {@link BModCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two byte8 values from the operand stack.
    2. + *
    3. Performs the modulus operation to calculate the remainder (i.e., a % b).
    4. + *
    5. Pushes the result of the modulus operation back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to calculate the remainder of the division of two byte8 values, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class BModCommand implements Command { + + /** + * Default constructor for creating an instance of BModCommand. + * This constructor is empty as no specific initialization is required. + */ + public BModCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for performing a modulus operation between two byte8 values. + * + *

    This method retrieves the two byte8 values from the operand stack, performs the modulus operation, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + byte b = (byte) operandStack.pop(); + byte a = (byte) operandStack.pop(); + + // Perform the modulus operation and push the result back onto the stack + operandStack.push(a % b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BMulCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BMulCommand.java new file mode 100644 index 0000000..e5de63a --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BMulCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.byte8; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * BMulCommand Opcode: Represents the byte8 multiplication operation in the virtual machine. + *

    This opcode is implemented by the {@link BMulCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two byte8 values from the operand stack.
    2. + *
    3. Performs the multiplication of the two values (i.e., a * b).
    4. + *
    5. Pushes the result back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to multiply two byte8 values together, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class BMulCommand implements Command { + + /** + * Default constructor for creating an instance of BMulCommand. + * This constructor is empty as no specific initialization is required. + */ + public BMulCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for multiplying two byte8 values. + * + *

    This method retrieves the two byte8 values from the operand stack, multiplies them together, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + byte b = (byte) operandStack.pop(); + byte a = (byte) operandStack.pop(); + + // Perform the multiplication and push the result back onto the stack + operandStack.push(a * b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BNegCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BNegCommand.java new file mode 100644 index 0000000..d6b67e2 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BNegCommand.java @@ -0,0 +1,60 @@ +package org.jcnc.snow.vm.commands.arithmetic.byte8; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * BNegCommand Opcode: Represents the byte8 negation operation in the virtual machine. + *

    This opcode is implemented by the {@link BNegCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top byte8 value from the operand stack.
    2. + *
    3. Performs the negation of the popped value (i.e., -value).
    4. + *
    5. Pushes the negated result back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to negate a byte8 value, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class BNegCommand implements Command { + + /** + * Default constructor for creating an instance of BNegCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public BNegCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for negating a byte8 value. + * + *

    This method retrieves the byte8 value from the operand stack, negates it, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top byte8 value from the operand stack + byte value = (byte) operandStack.pop(); + + // Perform the negation of the value + byte negatedValue = (byte) -value; + + // Push the negated result back onto the operand stack + operandStack.push(negatedValue); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BSubCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BSubCommand.java new file mode 100644 index 0000000..091cafd --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/byte8/BSubCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.byte8; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * BSubCommand Opcode: Represents the byte8 subtraction operation in the virtual machine. + *

    This opcode is implemented by the {@link BSubCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two byte8 values from the operand stack.
    2. + *
    3. Performs the subtraction of the second popped value from the first (i.e., a - b).
    4. + *
    5. Pushes the result of the subtraction back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to subtract one byte8 value from another, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class BSubCommand implements Command { + + /** + * Default constructor for creating an instance of BSubCommand. + * This constructor is empty as no specific initialization is required. + */ + public BSubCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for subtracting two byte8 values. + * + *

    This method retrieves the two byte8 values from the operand stack, subtracts the second value from the first, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + byte b = (byte) operandStack.pop(); + byte a = (byte) operandStack.pop(); + + // Perform the subtraction and push the result back onto the stack + operandStack.push(a - b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/B2ICommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/B2ICommand.java new file mode 100644 index 0000000..7abd324 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/B2ICommand.java @@ -0,0 +1,47 @@ +package org.jcnc.snow.vm.commands.arithmetic.conversion; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * B2ICommand Opcode: Represents the type conversion operation from byte8 to int32 in the virtual machine. + *

    This opcode is implemented by the {@link B2ICommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top byte8 value from the operand stack.
    2. + *
    3. Convert the byte8 value to an int32 value.
    4. + *
    5. Push the converted int32 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to widen a byte8 value to an int32 type to ensure compatibility with integer-based operations.

    + */ +public class B2ICommand implements Command { + + /** + * Default constructor for creating an instance of B2ICommand. + */ + public B2ICommand() { + // Empty constructor + } + + /** + * Executes the byte8 to int32 conversion operation. + * + * @param parts The array of instruction parameters, which is not used in this operation. + * @param currentPC The current program counter, representing the instruction address. + * @param operandStack The operand stack of the virtual machine. + * @param localVariableStore The local variable store for managing method-local variables. + * @param callStack The call stack of the virtual machine. + * @return The updated program counter after execution. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, + LocalVariableStore localVariableStore, CallStack callStack) { + int convertedValue = (byte) operandStack.pop(); + operandStack.push(convertedValue); + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/D2FCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/D2FCommand.java new file mode 100644 index 0000000..3ed687f --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/D2FCommand.java @@ -0,0 +1,48 @@ +package org.jcnc.snow.vm.commands.arithmetic.conversion; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * D2FCommand Opcode: Represents the type conversion operation from double64 to float32 in the virtual machine. + *

    This opcode is implemented by the {@link D2FCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top double64 value from the operand stack.
    2. + *
    3. Convert the double64 value to a float32 value.
    4. + *
    5. Push the converted float32 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to narrow a double64 value to a float32 type when lower precision floating-point arithmetic is acceptable.

    + */ +public class D2FCommand implements Command { + + /** + * Default constructor for creating an instance of D2FCommand. + */ + public D2FCommand() { + // Empty constructor + } + + /** + * Executes the double64 to float32 conversion operation. + * + * @param parts The array of instruction parameters, which is not used in this operation. + * @param currentPC The current program counter, representing the instruction address. + * @param operandStack The operand stack of the virtual machine. + * @param localVariableStore The local variable store for managing method-local variables. + * @param callStack The call stack of the virtual machine. + * @return The updated program counter after execution. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, + LocalVariableStore localVariableStore, CallStack callStack) { + double value = (double) operandStack.pop(); + float convertedValue = (float) value; + operandStack.push(convertedValue); + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/D2ICommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/D2ICommand.java new file mode 100644 index 0000000..b0c706c --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/D2ICommand.java @@ -0,0 +1,48 @@ +package org.jcnc.snow.vm.commands.arithmetic.conversion; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * D2ICommand Opcode: Represents the type conversion operation from double64 to int32 in the virtual machine. + *

    This opcode is implemented by the {@link D2ICommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top double64 value from the operand stack.
    2. + *
    3. Convert the double64 value to an int32 value (this may involve truncation).
    4. + *
    5. Push the converted int32 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to narrow a double64 value to an int32 type for further integer-based operations.

    + */ +public class D2ICommand implements Command { + + /** + * Default constructor for creating an instance of D2ICommand. + */ + public D2ICommand() { + // Empty constructor + } + + /** + * Executes the double64 to int32 conversion operation. + * + * @param parts The array of instruction parameters, which is not used in this operation. + * @param currentPC The current program counter, representing the instruction address. + * @param operandStack The operand stack of the virtual machine. + * @param localVariableStore The local variable store for managing method-local variables. + * @param callStack The call stack of the virtual machine. + * @return The updated program counter after execution. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, + LocalVariableStore localVariableStore, CallStack callStack) { + double value = (double) operandStack.pop(); + int convertedValue = (int) value; + operandStack.push(convertedValue); + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/D2LCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/D2LCommand.java new file mode 100644 index 0000000..eb38865 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/D2LCommand.java @@ -0,0 +1,48 @@ +package org.jcnc.snow.vm.commands.arithmetic.conversion; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * D2LCommand Opcode: Represents the type conversion operation from double64 to long64 in the virtual machine. + *

    This opcode is implemented by the {@link D2LCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top double64 value from the operand stack.
    2. + *
    3. Convert the double64 value to a long64 value (this may involve truncation).
    4. + *
    5. Push the converted long64 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to narrow a double64 value to a long64 type, which can then be used for integer operations.

    + */ +public class D2LCommand implements Command { + + /** + * Default constructor for creating an instance of D2LCommand. + */ + public D2LCommand() { + // Empty constructor + } + + /** + * Executes the double64 to long64 conversion operation. + * + * @param parts The array of instruction parameters, which is not used in this operation. + * @param currentPC The current program counter, representing the instruction address. + * @param operandStack The operand stack of the virtual machine. + * @param localVariableStore The local variable store for managing method-local variables. + * @param callStack The call stack of the virtual machine. + * @return The updated program counter after execution. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, + LocalVariableStore localVariableStore, CallStack callStack) { + double value = (double) operandStack.pop(); + long convertedValue = (long) value; + operandStack.push(convertedValue); + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/F2DCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/F2DCommand.java new file mode 100644 index 0000000..e997502 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/F2DCommand.java @@ -0,0 +1,47 @@ +package org.jcnc.snow.vm.commands.arithmetic.conversion; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * F2DCommand Opcode: Represents the type conversion operation from float32 to double64 in the virtual machine. + *

    This opcode is implemented by the {@link F2DCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top float32 value from the operand stack.
    2. + *
    3. Convert the float32 value to a double64 value.
    4. + *
    5. Push the converted double64 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to promote a float32 value to a double64 type, thereby increasing precision for floating-point computations.

    + */ +public class F2DCommand implements Command { + + /** + * Default constructor for creating an instance of F2DCommand. + */ + public F2DCommand() { + // Empty constructor + } + + /** + * Executes the float32 to double64 conversion operation. + * + * @param parts The array of instruction parameters, which is not used in this operation. + * @param currentPC The current program counter, representing the instruction address. + * @param operandStack The operand stack of the virtual machine. + * @param localVariableStore The local variable store for managing method-local variables. + * @param callStack The call stack of the virtual machine. + * @return The updated program counter after execution. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, + LocalVariableStore localVariableStore, CallStack callStack) { + double convertedValue = (float) operandStack.pop(); + operandStack.push(convertedValue); + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/F2ICommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/F2ICommand.java new file mode 100644 index 0000000..26cf949 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/F2ICommand.java @@ -0,0 +1,48 @@ +package org.jcnc.snow.vm.commands.arithmetic.conversion; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * F2ICommand Opcode: Represents the type conversion operation from float32 to int32 in the virtual machine. + *

    This opcode is implemented by the {@link F2ICommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top float32 value from the operand stack.
    2. + *
    3. Convert the float32 value to an int32 value (this may involve truncation).
    4. + *
    5. Push the converted int32 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to convert a float32 value to an int32 type for further integer operations or comparisons.

    + */ +public class F2ICommand implements Command { + + /** + * Default constructor for creating an instance of F2ICommand. + */ + public F2ICommand() { + // Empty constructor + } + + /** + * Executes the float32 to int32 conversion operation. + * + * @param parts The array of instruction parameters, which is not used in this operation. + * @param currentPC The current program counter, representing the instruction address. + * @param operandStack The operand stack of the virtual machine. + * @param localVariableStore The local variable store for managing method-local variables. + * @param callStack The call stack of the virtual machine. + * @return The updated program counter after execution. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, + LocalVariableStore localVariableStore, CallStack callStack) { + float value = (float) operandStack.pop(); + int convertedValue = (int) value; + operandStack.push(convertedValue); + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/F2LCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/F2LCommand.java new file mode 100644 index 0000000..55abcbc --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/F2LCommand.java @@ -0,0 +1,48 @@ +package org.jcnc.snow.vm.commands.arithmetic.conversion; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * F2LCommand Opcode: Represents the type conversion operation from float32 to long64 in the virtual machine. + *

    This opcode is implemented by the {@link F2LCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top float32 value from the operand stack.
    2. + *
    3. Convert the float32 value to a long64 value.
    4. + *
    5. Push the converted long64 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to widen a float32 value to a long64 type for operations requiring a larger numeric range.

    + */ +public class F2LCommand implements Command { + + /** + * Default constructor for creating an instance of F2LCommand. + */ + public F2LCommand() { + // Empty constructor + } + + /** + * Executes the float32 to long64 conversion operation. + * + * @param parts The array of instruction parameters, which is not used in this operation. + * @param currentPC The current program counter, representing the instruction address. + * @param operandStack The operand stack of the virtual machine. + * @param localVariableStore The local variable store for managing method-local variables. + * @param callStack The call stack of the virtual machine. + * @return The updated program counter after execution. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, + LocalVariableStore localVariableStore, CallStack callStack) { + float value = (float) operandStack.pop(); + long convertedValue = (long) value; + operandStack.push(convertedValue); + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/I2BCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/I2BCommand.java new file mode 100644 index 0000000..84e2e92 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/I2BCommand.java @@ -0,0 +1,48 @@ +package org.jcnc.snow.vm.commands.arithmetic.conversion; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * I2BCommand Opcode: Represents the type conversion operation from int32 to byte8 in the virtual machine. + *

    This opcode is implemented by the {@link I2BCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top int32 value from the operand stack.
    2. + *
    3. Convert the int32 value to a byte8 value (this may involve truncation).
    4. + *
    5. Push the converted byte8 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to narrow an int32 value to a byte8 type when a smaller numeric representation is required.

    + */ +public class I2BCommand implements Command { + + /** + * Default constructor for creating an instance of I2BCommand. + */ + public I2BCommand() { + // Empty constructor + } + + /** + * Executes the int32 to byte8 conversion operation. + * + * @param parts The array of instruction parameters, which is not used in this operation. + * @param currentPC The current program counter, representing the instruction address. + * @param operandStack The operand stack of the virtual machine. + * @param localVariableStore The local variable store for managing method-local variables. + * @param callStack The call stack of the virtual machine. + * @return The updated program counter after execution. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, + LocalVariableStore localVariableStore, CallStack callStack) { + int value = (int) operandStack.pop(); + byte convertedValue = (byte) value; + operandStack.push(convertedValue); + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/I2DCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/I2DCommand.java new file mode 100644 index 0000000..3653a36 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/I2DCommand.java @@ -0,0 +1,47 @@ +package org.jcnc.snow.vm.commands.arithmetic.conversion; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * I2DCommand Opcode: Represents the type conversion operation from int32 to double64 in the virtual machine. + *

    This opcode is implemented by the {@link I2DCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top int32 value from the operand stack.
    2. + *
    3. Convert the int32 value to a double64 value.
    4. + *
    5. Push the converted double64 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to widen an int32 value to a double64 type, providing high-precision floating-point calculations.

    + */ +public class I2DCommand implements Command { + + /** + * Default constructor for creating an instance of I2DCommand. + */ + public I2DCommand() { + // Empty constructor + } + + /** + * Executes the int32 to double64 conversion operation. + * + * @param parts The array of instruction parameters, which is not used in this operation. + * @param currentPC The current program counter, representing the instruction address. + * @param operandStack The operand stack of the virtual machine. + * @param localVariableStore The local variable store for managing method-local variables. + * @param callStack The call stack of the virtual machine. + * @return The updated program counter after execution. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, + LocalVariableStore localVariableStore, CallStack callStack) { + double convertedValue = (int) operandStack.pop(); + operandStack.push(convertedValue); + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/I2FCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/I2FCommand.java new file mode 100644 index 0000000..f21e379 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/I2FCommand.java @@ -0,0 +1,48 @@ +package org.jcnc.snow.vm.commands.arithmetic.conversion; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * I2FCommand Opcode: Represents the type conversion operation from int32 to float32 in the virtual machine. + *

    This opcode is implemented by the {@link I2FCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top int32 value from the operand stack.
    2. + *
    3. Convert the int32 value to a float32 value.
    4. + *
    5. Push the converted float32 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to convert an int32 value to a float32 type when floating-point arithmetic is required.

    + */ +public class I2FCommand implements Command { + + /** + * Default constructor for creating an instance of I2FCommand. + */ + public I2FCommand() { + // Empty constructor + } + + /** + * Executes the int32 to float32 conversion operation. + * + * @param parts The array of instruction parameters, which is not used in this operation. + * @param currentPC The current program counter, representing the instruction address. + * @param operandStack The operand stack of the virtual machine. + * @param localVariableStore The local variable store for managing method-local variables. + * @param callStack The call stack of the virtual machine. + * @return The updated program counter after execution. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, + LocalVariableStore localVariableStore, CallStack callStack) { + int value = (int) operandStack.pop(); + float convertedValue = (float) value; + operandStack.push(convertedValue); + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/I2LCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/I2LCommand.java new file mode 100644 index 0000000..9198cfa --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/I2LCommand.java @@ -0,0 +1,47 @@ +package org.jcnc.snow.vm.commands.arithmetic.conversion; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * I2LCommand Opcode: Represents the type conversion operation from int32 to long64 in the virtual machine. + *

    This opcode is implemented by the {@link I2LCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top int32 value from the operand stack.
    2. + *
    3. Convert the int32 value to a long64 value.
    4. + *
    5. Push the converted long64 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is commonly used to widen an int32 value to a long64 type to accommodate larger numeric ranges.

    + */ +public class I2LCommand implements Command { + + /** + * Default constructor for creating an instance of I2LCommand. + */ + public I2LCommand() { + // Empty constructor + } + + /** + * Executes the int32 to long64 conversion operation. + * + * @param parts The array of instruction parameters, which is not used in this operation. + * @param currentPC The current program counter, representing the instruction address. + * @param operandStack The operand stack of the virtual machine. + * @param localVariableStore The local variable store for managing method-local variables. + * @param callStack The call stack of the virtual machine. + * @return The updated program counter after execution. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, + LocalVariableStore localVariableStore, CallStack callStack) { + long convertedValue = (int) operandStack.pop(); + operandStack.push(convertedValue); + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/I2SCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/I2SCommand.java new file mode 100644 index 0000000..f11f119 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/I2SCommand.java @@ -0,0 +1,48 @@ +package org.jcnc.snow.vm.commands.arithmetic.conversion; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * I2SCommand Opcode: Represents the type conversion operation from int32 to short16 in the virtual machine. + *

    This opcode is implemented by the {@link I2SCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top int32 value from the operand stack.
    2. + *
    3. Convert the int32 value to a short16 value (this may involve truncation).
    4. + *
    5. Push the converted short16 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is typically used to narrow an int32 value to a short16 type when a smaller data representation is required.

    + */ +public class I2SCommand implements Command { + + /** + * Default constructor for creating an instance of I2SCommand. + */ + public I2SCommand() { + // Empty constructor + } + + /** + * Executes the int32 to short16 conversion operation. + * + * @param parts The array of instruction parameters, which is not used in this operation. + * @param currentPC The current program counter, representing the instruction address. + * @param operandStack The operand stack of the virtual machine. + * @param localVariableStore The local variable store for managing method-local variables. + * @param callStack The call stack of the virtual machine. + * @return The updated program counter after execution. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, + LocalVariableStore localVariableStore, CallStack callStack) { + int value = (int) operandStack.pop(); + short convertedValue = (short) value; + operandStack.push(convertedValue); + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/L2DCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/L2DCommand.java new file mode 100644 index 0000000..7c551db --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/L2DCommand.java @@ -0,0 +1,48 @@ +package org.jcnc.snow.vm.commands.arithmetic.conversion; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * L2DCommand Opcode: Represents the type conversion operation from long64 to double64 in the virtual machine. + *

    This opcode is implemented by the {@link L2DCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top long64 value from the operand stack.
    2. + *
    3. Convert the long64 value to a double64 value.
    4. + *
    5. Push the converted double64 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to widen a long64 value to a double64 type for high-precision floating-point computations.

    + */ +public class L2DCommand implements Command { + + /** + * Default constructor for creating an instance of L2DCommand. + */ + public L2DCommand() { + // Empty constructor + } + + /** + * Executes the long64 to double64 conversion operation. + * + * @param parts The array of instruction parameters, which is not used in this operation. + * @param currentPC The current program counter, representing the instruction address. + * @param operandStack The operand stack of the virtual machine. + * @param localVariableStore The local variable store for managing method-local variables. + * @param callStack The call stack of the virtual machine. + * @return The updated program counter after execution. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, + LocalVariableStore localVariableStore, CallStack callStack) { + long value = (long) operandStack.pop(); + double convertedValue = (double) value; + operandStack.push(convertedValue); + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/L2FCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/L2FCommand.java new file mode 100644 index 0000000..f2cbfc2 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/L2FCommand.java @@ -0,0 +1,48 @@ +package org.jcnc.snow.vm.commands.arithmetic.conversion; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * L2FCommand Opcode: Represents the type conversion operation from long64 to float32 in the virtual machine. + *

    This opcode is implemented by the {@link L2FCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top long64 value from the operand stack.
    2. + *
    3. Convert the long64 value to a float32 value.
    4. + *
    5. Push the converted float32 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to convert a long64 value to a float32 type, typically for floating-point arithmetic involving long values.

    + */ +public class L2FCommand implements Command { + + /** + * Default constructor for creating an instance of L2FCommand. + */ + public L2FCommand() { + // Empty constructor + } + + /** + * Executes the long64 to float32 conversion operation. + * + * @param parts The array of instruction parameters, which is not used in this operation. + * @param currentPC The current program counter, representing the instruction address. + * @param operandStack The operand stack of the virtual machine. + * @param localVariableStore The local variable store for managing method-local variables. + * @param callStack The call stack of the virtual machine. + * @return The updated program counter after execution. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, + LocalVariableStore localVariableStore, CallStack callStack) { + long value = (long) operandStack.pop(); + float convertedValue = (float) value; + operandStack.push(convertedValue); + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/L2ICommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/L2ICommand.java new file mode 100644 index 0000000..ee9f494 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/L2ICommand.java @@ -0,0 +1,48 @@ +package org.jcnc.snow.vm.commands.arithmetic.conversion; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * L2ICommand Opcode: Represents the type conversion operation from long64 to int32 in the virtual machine. + *

    This opcode is implemented by the {@link L2ICommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top long64 value from the operand stack.
    2. + *
    3. Convert the long64 value to an int32 value (this may involve truncation).
    4. + *
    5. Push the converted int32 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is typically used to narrow a long64 value to an int32 type for further integer operations.

    + */ +public class L2ICommand implements Command { + + /** + * Default constructor for creating an instance of L2ICommand. + */ + public L2ICommand() { + // Empty constructor + } + + /** + * Executes the long64 to int32 conversion operation. + * + * @param parts The array of instruction parameters, which is not used in this operation. + * @param currentPC The current program counter, representing the instruction address. + * @param operandStack The operand stack of the virtual machine. + * @param localVariableStore The local variable store for managing method-local variables. + * @param callStack The call stack of the virtual machine. + * @return The updated program counter after execution. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, + LocalVariableStore localVariableStore, CallStack callStack) { + long value = (long) operandStack.pop(); + int convertedValue = (int) value; + operandStack.push(convertedValue); + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/S2ICommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/S2ICommand.java new file mode 100644 index 0000000..375f0ea --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/conversion/S2ICommand.java @@ -0,0 +1,47 @@ +package org.jcnc.snow.vm.commands.arithmetic.conversion; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * S2ICommand Opcode: Represents the type conversion operation from short16 to int32 in the virtual machine. + *

    This opcode is implemented by the {@link S2ICommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top short16 value from the operand stack.
    2. + *
    3. Convert the short16 value to an int32 value.
    4. + *
    5. Push the converted int32 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to widen a short16 value to an int32 type, facilitating subsequent integer arithmetic or comparison operations.

    + */ +public class S2ICommand implements Command { + + /** + * Default constructor for creating an instance of S2ICommand. + */ + public S2ICommand() { + // Empty constructor + } + + /** + * Executes the short16 to int32 conversion operation. + * + * @param parts The array of instruction parameters, which is not used in this operation. + * @param currentPC The current program counter, representing the instruction address. + * @param operandStack The operand stack of the virtual machine. + * @param localVariableStore The local variable store for managing method-local variables. + * @param callStack The call stack of the virtual machine. + * @return The updated program counter after execution. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, + LocalVariableStore localVariableStore, CallStack callStack) { + int convertedValue = (short) operandStack.pop(); + operandStack.push(convertedValue); + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DAddCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DAddCommand.java new file mode 100644 index 0000000..76d8b69 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DAddCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.double64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * DAddCommand Opcode: Represents the double64 addition operation in the virtual machine. + *

    This opcode is implemented by the {@link DAddCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two double64 values from the operand stack.
    2. + *
    3. Performs the addition of the two values (i.e., a + b).
    4. + *
    5. Pushes the sum back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to add two double64 values together, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class DAddCommand implements Command { + + /** + * Default constructor for creating an instance of DAddCommand. + * This constructor is empty as no specific initialization is required. + */ + public DAddCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for adding two double64 values. + * + *

    This method retrieves the two double64 values from the operand stack, adds them together, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + double b = (double) operandStack.pop(); + double a = (double) operandStack.pop(); + + // Perform the addition and push the result back onto the stack + operandStack.push(a + b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DDivCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DDivCommand.java new file mode 100644 index 0000000..1a70f9a --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DDivCommand.java @@ -0,0 +1,64 @@ +package org.jcnc.snow.vm.commands.arithmetic.double64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * DDivCommand Opcode: Represents the double64 division operation in the virtual machine. + *

    This opcode is implemented by the {@link DDivCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two double64 values from the operand stack.
    2. + *
    3. Performs the division operation (i.e., a / b).
    4. + *
    5. Checks for division by zero to prevent errors.
    6. + *
    7. Pushes the result of the division back onto the operand stack for subsequent instructions to use.
    8. + *
    + * + *

    This opcode is typically used to divide one double64 value by another, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class DDivCommand implements Command { + + /** + * Default constructor for creating an instance of DDivCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public DDivCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for dividing two double64 values. + * + *

    This method retrieves the two double64 values from the operand stack, checks for division by zero, performs the division, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + double b = (double) operandStack.pop(); + double a = (double) operandStack.pop(); + + // Check for division by zero + if (b == 0) { + throw new ArithmeticException("Division by zero is not allowed."); + } + + // Perform the division and push the result back onto the stack + operandStack.push(a / b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DIncCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DIncCommand.java new file mode 100644 index 0000000..3f9c672 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DIncCommand.java @@ -0,0 +1,70 @@ +package org.jcnc.snow.vm.commands.arithmetic.double64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * DIncCommand Opcode: Represents the double64 increment operation for a local variable in the virtual machine. + *

    This opcode is implemented by the {@link DIncCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Extracts the index of the local variable and the increment value from the instruction parameters.
    2. + *
    3. Retrieves the current value of the local variable at the given index.
    4. + *
    5. Increments the value of the local variable by the specified increment value (i.e., localVariables[index] += increment).
    6. + *
    7. Updates the local variable with the new incremented value.
    8. + *
    9. Returns the updated program counter (PC) value, which typically increments by 1 unless control flow is modified.
    10. + *
    + * + *

    This opcode is useful for optimizing the modification of local variables, especially in tight loops or when managing counters.

    + */ +public class DIncCommand implements Command { + + /** + * Default constructor for creating an instance of DIncCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public DIncCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for incrementing a local variable. + * + *

    This method retrieves the necessary data (index of the local variable and the increment value) from the instruction parameters, + * performs the increment operation on the specified local variable, and returns the updated program counter (PC) value.

    + * + * @param parts The array of instruction parameters, which includes the index of the local variable and + * the increment value (passed directly as bytecode parameters). + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, including the one being incremented. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Retrieve the index of the local variable and the increment value from the parameters + + int localVariableIndex = Integer.parseInt(parts[1]); // Index of the local variable to be incremented + + double incrementValue = Double.parseDouble(parts[2]); // The value by which to increment the local variable + + // Get the current value of the local variable at the specified index + double currentValue = (double) callStack.peekFrame().getLocalVariableStore().getVariable(localVariableIndex); + + // Increment the local variable value by the specified increment + double newValue = currentValue + incrementValue; + + // Update the local variable with the new incremented value + callStack.peekFrame().getLocalVariableStore().setVariable(localVariableIndex, newValue); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DModCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DModCommand.java new file mode 100644 index 0000000..df8cbd7 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DModCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.double64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * DModCommand Opcode: Represents the double64 modulus operation in the virtual machine. + *

    This opcode is implemented by the {@link DModCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two double64 values from the operand stack.
    2. + *
    3. Performs the modulus operation to calculate the remainder (i.e., a % b).
    4. + *
    5. Pushes the result of the modulus operation back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to calculate the remainder of the division of two double64 values, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class DModCommand implements Command { + + /** + * Default constructor for creating an instance of DModCommand. + * This constructor is empty as no specific initialization is required. + */ + public DModCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for performing a modulus operation between two double64 values. + * + *

    This method retrieves the two double64 values from the operand stack, performs the modulus operation, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + double b = (double) operandStack.pop(); + double a = (double) operandStack.pop(); + + // Perform the modulus operation and push the result back onto the stack + operandStack.push(a % b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DMulCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DMulCommand.java new file mode 100644 index 0000000..d5adb01 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DMulCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.double64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * DMulCommand Opcode: Represents the double64 multiplication operation in the virtual machine. + *

    This opcode is implemented by the {@link DMulCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two double64 values from the operand stack.
    2. + *
    3. Performs the multiplication of the two values (i.e., a * b).
    4. + *
    5. Pushes the result back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to multiply two double64 values together, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class DMulCommand implements Command { + + /** + * Default constructor for creating an instance of DMulCommand. + * This constructor is empty as no specific initialization is required. + */ + public DMulCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for multiplying two double64 values. + * + *

    This method retrieves the two double64 values from the operand stack, multiplies them together, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + double b = (double) operandStack.pop(); + double a = (double) operandStack.pop(); + + // Perform the multiplication and push the result back onto the stack + operandStack.push(a * b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DNegCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DNegCommand.java new file mode 100644 index 0000000..1c36841 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DNegCommand.java @@ -0,0 +1,60 @@ +package org.jcnc.snow.vm.commands.arithmetic.double64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * DNegCommand Opcode: Represents the double64 negation operation in the virtual machine. + *

    This opcode is implemented by the {@link DNegCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top double64 value from the operand stack.
    2. + *
    3. Performs the negation of the popped value (i.e., -value).
    4. + *
    5. Pushes the negated result back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to negate a double64 value, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class DNegCommand implements Command { + + /** + * Default constructor for creating an instance of DNegCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public DNegCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for negating a double64 value. + * + *

    This method retrieves the double64 value from the operand stack, negates it, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top double64 value from the operand stack + double value = (double) operandStack.pop(); + + // Perform the negation of the value + double negatedValue = -value; + + // Push the negated result back onto the operand stack + operandStack.push(negatedValue); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DSubCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DSubCommand.java new file mode 100644 index 0000000..72d2bda --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/double64/DSubCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.double64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * DSubCommand Opcode: Represents the double64 subtraction operation in the virtual machine. + *

    This opcode is implemented by the {@link DSubCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two double64 values from the operand stack.
    2. + *
    3. Performs the subtraction of the second popped value from the first (i.e., a - b).
    4. + *
    5. Pushes the result of the subtraction back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to subtract one double64 value from another, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class DSubCommand implements Command { + + /** + * Default constructor for creating an instance of DSubCommand. + * This constructor is empty as no specific initialization is required. + */ + public DSubCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for subtracting two double64 values. + * + *

    This method retrieves the two double64 values from the operand stack, subtracts the second value from the first, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + double b = (double) operandStack.pop(); + double a = (double) operandStack.pop(); + + // Perform the subtraction and push the result back onto the stack + operandStack.push(a - b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FAddCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FAddCommand.java new file mode 100644 index 0000000..3c6e3cb --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FAddCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.float32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * FAddCommand Opcode: Represents the float32 addition operation in the virtual machine. + *

    This opcode is implemented by the {@link FAddCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two float32 values from the operand stack.
    2. + *
    3. Performs the addition of the two values (i.e., a + b).
    4. + *
    5. Pushes the sum back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to add two float32 values together, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class FAddCommand implements Command { + + /** + * Default constructor for creating an instance of FAddCommand. + * This constructor is empty as no specific initialization is required. + */ + public FAddCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for adding two float32 values. + * + *

    This method retrieves the two float32 values from the operand stack, adds them together, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + float b = (float) operandStack.pop(); + float a = (float) operandStack.pop(); + + // Perform the addition and push the result back onto the stack + operandStack.push(a + b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FDivCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FDivCommand.java new file mode 100644 index 0000000..5c3fbda --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FDivCommand.java @@ -0,0 +1,64 @@ +package org.jcnc.snow.vm.commands.arithmetic.float32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * FDivCommand Opcode: Represents the float32 division operation in the virtual machine. + *

    This opcode is implemented by the {@link FDivCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two float32 values from the operand stack.
    2. + *
    3. Performs the division operation (i.e., a / b).
    4. + *
    5. Checks for division by zero to prevent errors.
    6. + *
    7. Pushes the result of the division back onto the operand stack for subsequent instructions to use.
    8. + *
    + * + *

    This opcode is typically used to divide one float32 value by another, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class FDivCommand implements Command { + + /** + * Default constructor for creating an instance of FDivCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public FDivCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for dividing two float32 values. + * + *

    This method retrieves the two float32 values from the operand stack, checks for division by zero, performs the division, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + float b = (float) operandStack.pop(); + float a = (float) operandStack.pop(); + + // Check for division by zero + if (b == 0) { + throw new ArithmeticException("Division by zero is not allowed."); + } + + // Perform the division and push the result back onto the stack + operandStack.push(a / b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FIncCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FIncCommand.java new file mode 100644 index 0000000..4061ebd --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FIncCommand.java @@ -0,0 +1,68 @@ +package org.jcnc.snow.vm.commands.arithmetic.float32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * FIncCommand Opcode: Represents the float32 increment operation for a local variable in the virtual machine. + *

    This opcode is implemented by the {@link FIncCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Extracts the index of the local variable and the increment value from the instruction parameters.
    2. + *
    3. Retrieves the current value of the local variable at the given index.
    4. + *
    5. Increments the value of the local variable by the specified increment value (i.e., localVariables[index] += increment).
    6. + *
    7. Updates the local variable with the new incremented value.
    8. + *
    9. Returns the updated program counter (PC) value, which typically increments by 1 unless control flow is modified.
    10. + *
    + * + *

    This opcode is useful for optimizing the modification of local variables, especially in tight loops or when managing counters.

    + */ +public class FIncCommand implements Command { + + /** + * Default constructor for creating an instance of FIncCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public FIncCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for incrementing a local variable. + * + *

    This method retrieves the necessary data (index of the local variable and the increment value) from the instruction parameters, + * performs the increment operation on the specified local variable, and returns the updated program counter (PC) value.

    + * + * @param parts The array of instruction parameters, which includes the index of the local variable and + * the increment value (passed directly as bytecode parameters). + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, including the one being incremented. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Retrieve the index of the local variable and the increment value from the parameters + int localVariableIndex = Integer.parseInt(parts[1]); // Index of the local variable to be incremented + float incrementValue = Float.parseFloat(parts[2]); // The value by which to increment the local variable + + // Get the current value of the local variable at the specified index + float currentValue = (float) callStack.peekFrame().getLocalVariableStore().getVariable(localVariableIndex); + + // Increment the local variable value by the specified increment + float newValue = currentValue + incrementValue; + + // Update the local variable with the new incremented value + callStack.peekFrame().getLocalVariableStore().setVariable(localVariableIndex, newValue); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FModCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FModCommand.java new file mode 100644 index 0000000..fc0c537 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FModCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.float32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * FModCommand Opcode: Represents the float32 modulus operation in the virtual machine. + *

    This opcode is implemented by the {@link FModCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two float32 values from the operand stack.
    2. + *
    3. Performs the modulus operation to calculate the remainder (i.e., a % b).
    4. + *
    5. Pushes the result of the modulus operation back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to calculate the remainder of the division of two float32 values, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class FModCommand implements Command { + + /** + * Default constructor for creating an instance of FModCommand. + * This constructor is empty as no specific initialization is required. + */ + public FModCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for performing a modulus operation between two float32 values. + * + *

    This method retrieves the two float32 values from the operand stack, performs the modulus operation, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + float b = (float) operandStack.pop(); + float a = (float) operandStack.pop(); + + // Perform the modulus operation and push the result back onto the stack + operandStack.push(a % b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FMulCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FMulCommand.java new file mode 100644 index 0000000..34dfd8b --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FMulCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.float32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * FMulCommand Opcode: Represents the float32 multiplication operation in the virtual machine. + *

    This opcode is implemented by the {@link FMulCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two float32 values from the operand stack.
    2. + *
    3. Performs the multiplication of the two values (i.e., a * b).
    4. + *
    5. Pushes the result back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to multiply two float32 values together, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class FMulCommand implements Command { + + /** + * Default constructor for creating an instance of FMulCommand. + * This constructor is empty as no specific initialization is required. + */ + public FMulCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for multiplying two float32 values. + * + *

    This method retrieves the two float32 values from the operand stack, multiplies them together, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + float b = (float) operandStack.pop(); + float a = (float) operandStack.pop(); + + // Perform the multiplication and push the result back onto the stack + operandStack.push(a * b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FNegCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FNegCommand.java new file mode 100644 index 0000000..9b28674 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FNegCommand.java @@ -0,0 +1,60 @@ +package org.jcnc.snow.vm.commands.arithmetic.float32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * FNegCommand Opcode: Represents the float32 negation operation in the virtual machine. + *

    This opcode is implemented by the {@link FNegCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top float32 value from the operand stack.
    2. + *
    3. Performs the negation of the popped value (i.e., -value).
    4. + *
    5. Pushes the negated result back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to negate a float32 value, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class FNegCommand implements Command { + + /** + * Default constructor for creating an instance of FNegCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public FNegCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for negating a float32 value. + * + *

    This method retrieves the float32 value from the operand stack, negates it, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top float32 value from the operand stack + float value = (float) operandStack.pop(); + + // Perform the negation of the value + float negatedValue = -value; + + // Push the negated result back onto the operand stack + operandStack.push(negatedValue); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FSubCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FSubCommand.java new file mode 100644 index 0000000..dd73714 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/float32/FSubCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.float32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * FSubCommand Opcode: Represents the float32 subtraction operation in the virtual machine. + *

    This opcode is implemented by the {@link FSubCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two float32 values from the operand stack.
    2. + *
    3. Performs the subtraction of the second popped value from the first (i.e., a - b).
    4. + *
    5. Pushes the result of the subtraction back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to subtract one float32 value from another, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class FSubCommand implements Command { + + /** + * Default constructor for creating an instance of FSubCommand. + * This constructor is empty as no specific initialization is required. + */ + public FSubCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for subtracting two float32 values. + * + *

    This method retrieves the two float32 values from the operand stack, subtracts the second value from the first, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + float b = (float) operandStack.pop(); + float a = (float) operandStack.pop(); + + // Perform the subtraction and push the result back onto the stack + operandStack.push(a - b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/IAddCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/IAddCommand.java new file mode 100644 index 0000000..36375e3 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/IAddCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * IAddCommand Opcode: Represents the int32 addition operation in the virtual machine. + *

    This opcode is implemented by the {@link IAddCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two int32 values from the operand stack.
    2. + *
    3. Performs the addition of the two values (i.e., a + b).
    4. + *
    5. Pushes the sum back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to add two int32 values together, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class IAddCommand implements Command { + + /** + * Default constructor for creating an instance of IAddCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public IAddCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for adding two int32 values. + * + *

    This method retrieves the two int32 values from the operand stack, adds them together, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + int b = (int) operandStack.pop(); + int a = (int) operandStack.pop(); + + // Perform the addition and push the result back onto the stack + operandStack.push(a + b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/IDivCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/IDivCommand.java new file mode 100644 index 0000000..ea5c9cf --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/IDivCommand.java @@ -0,0 +1,64 @@ +package org.jcnc.snow.vm.commands.arithmetic.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * IDivCommand Opcode: Represents the int32 division operation in the virtual machine. + *

    This opcode is implemented by the {@link IDivCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two int32 values from the operand stack.
    2. + *
    3. Performs the division operation (i.e., a / b).
    4. + *
    5. Checks for division by zero to prevent errors.
    6. + *
    7. Pushes the result of the division back onto the operand stack for subsequent instructions to use.
    8. + *
    + * + *

    This opcode is typically used to divide one int32 value by another, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class IDivCommand implements Command { + + /** + * Default constructor for creating an instance of IDivCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public IDivCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for dividing two int32 values. + * + *

    This method retrieves the two int32 values from the operand stack, checks for division by zero, performs the division, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + int b = (int) operandStack.pop(); + int a = (int) operandStack.pop(); + + // Check for division by zero + if (b == 0) { + throw new ArithmeticException("Division by zero is not allowed."); + } + + // Perform the division and push the result back onto the stack + operandStack.push(a / b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/IIncCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/IIncCommand.java new file mode 100644 index 0000000..ac1c150 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/IIncCommand.java @@ -0,0 +1,68 @@ +package org.jcnc.snow.vm.commands.arithmetic.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * IIncCommand Opcode: Represents the int32 increment operation for a local variable in the virtual machine. + *

    This opcode is implemented by the {@link IIncCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Extracts the index of the local variable and the increment value from the instruction parameters.
    2. + *
    3. Retrieves the current value of the local variable at the given index.
    4. + *
    5. Increments the value of the local variable by the specified increment value (i.e., localVariables[index] += increment).
    6. + *
    7. Updates the local variable with the new incremented value.
    8. + *
    9. Returns the updated program counter (PC) value, which typically increments by 1 unless control flow is modified.
    10. + *
    + * + *

    This opcode is useful for optimizing the modification of local variables, especially in tight loops or when managing counters.

    + */ +public class IIncCommand implements Command { + + /** + * Default constructor for creating an instance of IIncCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public IIncCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for incrementing a local variable. + * + *

    This method retrieves the necessary data (index of the local variable and the increment value) from the instruction parameters, + * performs the increment operation on the specified local variable, and returns the updated program counter (PC) value.

    + * + * @param parts The array of instruction parameters, which includes the index of the local variable and + * the increment value (passed directly as bytecode parameters). + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, including the one being incremented. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Retrieve the index of the local variable and the increment value from the parameters + int localVariableIndex = Integer.parseInt(parts[1]); // Index of the local variable to be incremented + int incrementValue = Integer.parseInt(parts[2]); // The value by which to increment the local variable + + // Get the current value of the local variable at the specified index + int currentValue = (int) callStack.peekFrame().getLocalVariableStore().getVariable(localVariableIndex); + + // Increment the local variable value by the specified increment + int newValue = currentValue + incrementValue; + + // Update the local variable with the new incremented value + callStack.peekFrame().getLocalVariableStore().setVariable(localVariableIndex, newValue); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/IModCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/IModCommand.java new file mode 100644 index 0000000..0cb9bd0 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/IModCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * IModCommand Opcode: Represents the int32 modulus operation in the virtual machine. + *

    This opcode is implemented by the {@link IModCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two int32 values from the operand stack.
    2. + *
    3. Performs the modulus operation to calculate the remainder (i.e., a % b).
    4. + *
    5. Pushes the result of the modulus operation back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to calculate the remainder of the division of two int32 values, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class IModCommand implements Command { + + /** + * Default constructor for creating an instance of IModCommand. + * This constructor is empty as no specific initialization is required. + */ + public IModCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for performing a modulus operation between two int32 values. + * + *

    This method retrieves the two int32 values from the operand stack, performs the modulus operation, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + int b = (int) operandStack.pop(); + int a = (int) operandStack.pop(); + + // Perform the modulus operation and push the result back onto the stack + operandStack.push(a % b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/IMulCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/IMulCommand.java new file mode 100644 index 0000000..a559759 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/IMulCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * IMulCommand Opcode: Represents the int32 multiplication operation in the virtual machine. + *

    This opcode is implemented by the {@link IMulCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two int32 values from the operand stack.
    2. + *
    3. Performs the multiplication of the two values (i.e., a * b).
    4. + *
    5. Pushes the result back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to multiply two int32 values together, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class IMulCommand implements Command { + + /** + * Default constructor for creating an instance of IMulCommand. + * This constructor is empty as no specific initialization is required. + */ + public IMulCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for multiplying two int32 values. + * + *

    This method retrieves the two int32 values from the operand stack, multiplies them together, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + int b = (int) operandStack.pop(); + int a = (int) operandStack.pop(); + + // Perform the multiplication and push the result back onto the stack + operandStack.push(a * b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/INegCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/INegCommand.java new file mode 100644 index 0000000..2746adb --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/INegCommand.java @@ -0,0 +1,60 @@ +package org.jcnc.snow.vm.commands.arithmetic.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * INegCommand Opcode: Represents the int32 negation operation in the virtual machine. + *

    This opcode is implemented by the {@link INegCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top int32 value from the operand stack.
    2. + *
    3. Performs the negation of the popped value (i.e., -value).
    4. + *
    5. Pushes the negated result back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to negate an int32 value, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class INegCommand implements Command { + + /** + * Default constructor for creating an instance of INegCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public INegCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for negating an int32 value. + * + *

    This method retrieves the int32 value from the operand stack, negates it, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top int32 value from the operand stack + int value = (int) operandStack.pop(); + + // Perform the negation of the value + int negatedValue = -value; + + // Push the negated result back onto the operand stack + operandStack.push(negatedValue); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/ISubCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/ISubCommand.java new file mode 100644 index 0000000..f910e2b --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/int32/ISubCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * ISubCommand Opcode: Represents the int32 subtraction operation in the virtual machine. + *

    This opcode is implemented by the {@link ISubCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two int32 values from the operand stack.
    2. + *
    3. Performs the subtraction of the second popped value from the first (i.e., a - b).
    4. + *
    5. Pushes the result of the subtraction back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to subtract one int32 value from another, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class ISubCommand implements Command { + + /** + * Default constructor for creating an instance of ISubCommand. + * This constructor is empty as no specific initialization is required. + */ + public ISubCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for subtracting two int32 values. + * + *

    This method retrieves the two int32 values from the operand stack, subtracts the second value from the first, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + int b = (int) operandStack.pop(); + int a = (int) operandStack.pop(); + + // Perform the subtraction and push the result back onto the stack + operandStack.push(a - b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LAddCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LAddCommand.java new file mode 100644 index 0000000..c97a6a1 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LAddCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.long64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * LAddCommand Opcode: Represents the long64 addition operation in the virtual machine. + *

    This opcode is implemented by the {@link LAddCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two long64 values from the operand stack.
    2. + *
    3. Performs the addition of the two values (i.e., a + b).
    4. + *
    5. Pushes the sum back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to add two long64 values together, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class LAddCommand implements Command { + + /** + * Default constructor for creating an instance of LAddCommand. + * This constructor is empty as no specific initialization is required. + */ + public LAddCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for adding two long64 values. + * + *

    This method retrieves the two long64 values from the operand stack, adds them together, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + long b = (long) operandStack.pop(); + long a = (long) operandStack.pop(); + + // Perform the addition and push the result back onto the stack + operandStack.push(a + b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LDivCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LDivCommand.java new file mode 100644 index 0000000..7d66218 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LDivCommand.java @@ -0,0 +1,64 @@ +package org.jcnc.snow.vm.commands.arithmetic.long64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * LDivCommand Opcode: Represents the long64 division operation in the virtual machine. + *

    This opcode is implemented by the {@link LDivCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two long64 values from the operand stack.
    2. + *
    3. Performs the division operation (i.e., a / b).
    4. + *
    5. Checks for division by zero to prevent errors.
    6. + *
    7. Pushes the result of the division back onto the operand stack for subsequent instructions to use.
    8. + *
    + * + *

    This opcode is typically used to divide one long64 value by another, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class LDivCommand implements Command { + + /** + * Default constructor for creating an instance of LDivCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public LDivCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for dividing two long64 values. + * + *

    This method retrieves the two long64 values from the operand stack, checks for division by zero, performs the division, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + long b = (long) operandStack.pop(); + long a = (long) operandStack.pop(); + + // Check for division by zero + if (b == 0) { + throw new ArithmeticException("Division by zero is not allowed."); + } + + // Perform the division and push the result back onto the stack + operandStack.push(a / b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LIncCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LIncCommand.java new file mode 100644 index 0000000..18715d3 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LIncCommand.java @@ -0,0 +1,68 @@ +package org.jcnc.snow.vm.commands.arithmetic.long64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * LIncCommand Opcode: Represents the long64 increment operation for a local variable in the virtual machine. + *

    This opcode is implemented by the {@link LIncCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Extracts the index of the local variable and the increment value from the instruction parameters.
    2. + *
    3. Retrieves the current value of the local variable at the given index.
    4. + *
    5. Increments the value of the local variable by the specified increment value (i.e., localVariables[index] += increment).
    6. + *
    7. Updates the local variable with the new incremented value.
    8. + *
    9. Returns the updated program counter (PC) value, which typically increments by 1 unless control flow is modified.
    10. + *
    + * + *

    This opcode is useful for optimizing the modification of local variables, especially in tight loops or when managing counters.

    + */ +public class LIncCommand implements Command { + + /** + * Default constructor for creating an instance of LIncCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public LIncCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for incrementing a local variable. + * + *

    This method retrieves the necessary data (index of the local variable and the increment value) from the instruction parameters, + * performs the increment operation on the specified local variable, and returns the updated program counter (PC) value.

    + * + * @param parts The array of instruction parameters, which includes the index of the local variable and + * the increment value (passed directly as bytecode parameters). + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, including the one being incremented. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Retrieve the index of the local variable and the increment value from the parameters + int localVariableIndex = Integer.parseInt(parts[1]); // Index of the local variable to be incremented + long incrementValue = Long.parseLong(parts[2]); // The value by which to increment the local variable + + // Get the current value of the local variable at the specified index + long currentValue = (long) callStack.peekFrame().getLocalVariableStore().getVariable(localVariableIndex); + + // Increment the local variable value by the specified increment + long newValue = currentValue + incrementValue; + + // Update the local variable with the new incremented value + callStack.peekFrame().getLocalVariableStore().setVariable(localVariableIndex, newValue); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LModCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LModCommand.java new file mode 100644 index 0000000..345c2e5 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LModCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.long64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * LModCommand Opcode: Represents the long64 modulus operation in the virtual machine. + *

    This opcode is implemented by the {@link LModCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two long64 values from the operand stack.
    2. + *
    3. Performs the modulus operation to calculate the remainder (i.e., a % b).
    4. + *
    5. Pushes the result of the modulus operation back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to calculate the remainder of the division of two long64 values, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class LModCommand implements Command { + + /** + * Default constructor for creating an instance of LModCommand. + * This constructor is empty as no specific initialization is required. + */ + public LModCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for performing a modulus operation between two long64 values. + * + *

    This method retrieves the two long64 values from the operand stack, performs the modulus operation, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + long b = (long) operandStack.pop(); + long a = (long) operandStack.pop(); + + // Perform the modulus operation and push the result back onto the stack + operandStack.push(a % b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LMulCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LMulCommand.java new file mode 100644 index 0000000..b5e68aa --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LMulCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.long64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * LMulCommand Opcode: Represents the long64 multiplication operation in the virtual machine. + *

    This opcode is implemented by the {@link LMulCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two long64 values from the operand stack.
    2. + *
    3. Performs the multiplication of the two values (i.e., a * b).
    4. + *
    5. Pushes the result back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to multiply two long64 values together, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class LMulCommand implements Command { + + /** + * Default constructor for creating an instance of LMulCommand. + * This constructor is empty as no specific initialization is required. + */ + public LMulCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for multiplying two long64 values. + * + *

    This method retrieves the two long64 values from the operand stack, multiplies them together, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + long b = (long) operandStack.pop(); + long a = (long) operandStack.pop(); + + // Perform the multiplication and push the result back onto the stack + operandStack.push(a * b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LNegCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LNegCommand.java new file mode 100644 index 0000000..e93fd1d --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LNegCommand.java @@ -0,0 +1,60 @@ +package org.jcnc.snow.vm.commands.arithmetic.long64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * LNegCommand Opcode: Represents the long64 negation operation in the virtual machine. + *

    This opcode is implemented by the {@link LNegCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top long64 value from the operand stack.
    2. + *
    3. Performs the negation of the popped value (i.e., -value).
    4. + *
    5. Pushes the negated result back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to negate a long64 value, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class LNegCommand implements Command { + + /** + * Default constructor for creating an instance of LNegCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public LNegCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for negating a long64 value. + * + *

    This method retrieves the long64 value from the operand stack, negates it, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top long64 value from the operand stack + long value = (long) operandStack.pop(); + + // Perform the negation of the value + long negatedValue = -value; + + // Push the negated result back onto the operand stack + operandStack.push(negatedValue); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LSubCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LSubCommand.java new file mode 100644 index 0000000..f710b10 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/long64/LSubCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.long64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * LSubCommand Opcode: Represents the long64 subtraction operation in the virtual machine. + *

    This opcode is implemented by the {@link LSubCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two long64 values from the operand stack.
    2. + *
    3. Performs the subtraction of the second popped value from the first (i.e., a - b).
    4. + *
    5. Pushes the result of the subtraction back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to subtract one long64 value from another, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class LSubCommand implements Command { + + /** + * Default constructor for creating an instance of LSubCommand. + * This constructor is empty as no specific initialization is required. + */ + public LSubCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for subtracting two long64 values. + * + *

    This method retrieves the two long64 values from the operand stack, subtracts the second value from the first, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + long b = (long) operandStack.pop(); + long a = (long) operandStack.pop(); + + // Perform the subtraction and push the result back onto the stack + operandStack.push(a - b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SAddCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SAddCommand.java new file mode 100644 index 0000000..4137a01 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SAddCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.short16; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * SAddCommand Opcode: Represents the short16 addition operation in the virtual machine. + *

    This opcode is implemented by the {@link SAddCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two short16 values from the operand stack.
    2. + *
    3. Performs the addition of the two values (i.e., a + b).
    4. + *
    5. Pushes the sum back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to add two short16 values together, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class SAddCommand implements Command { + + /** + * Default constructor for creating an instance of SAddCommand. + * This constructor is empty as no specific initialization is required. + */ + public SAddCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for adding two short16 values. + * + *

    This method retrieves the two short16 values from the operand stack, adds them together, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + Short b = (Short) operandStack.pop(); + Short a = (Short) operandStack.pop(); + + // Perform the addition and push the result back onto the stack + operandStack.push(a + b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SDivCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SDivCommand.java new file mode 100644 index 0000000..f14cefa --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SDivCommand.java @@ -0,0 +1,64 @@ +package org.jcnc.snow.vm.commands.arithmetic.short16; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * SDivCommand Opcode: Represents the short16 division operation in the virtual machine. + *

    This opcode is implemented by the {@link SDivCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two short16 values from the operand stack.
    2. + *
    3. Performs the division operation (i.e., a / b).
    4. + *
    5. Checks for division by zero to prevent errors.
    6. + *
    7. Pushes the result of the division back onto the operand stack for subsequent instructions to use.
    8. + *
    + * + *

    This opcode is typically used to divide one short16 value by another, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class SDivCommand implements Command { + + /** + * Default constructor for creating an instance of SDivCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public SDivCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for dividing two short16 values. + * + *

    This method retrieves the two short16 values from the operand stack, checks for division by zero, performs the division, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + short b = (short) operandStack.pop(); + short a = (short) operandStack.pop(); + + // Check for division by zero + if (b == 0) { + throw new ArithmeticException("Division by zero is not allowed."); + } + + // Perform the division and push the result back onto the stack + operandStack.push(a / b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SIncCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SIncCommand.java new file mode 100644 index 0000000..8da3133 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SIncCommand.java @@ -0,0 +1,68 @@ +package org.jcnc.snow.vm.commands.arithmetic.short16; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * SIncCommand Opcode: Represents the short16 increment operation for a local variable in the virtual machine. + *

    This opcode is implemented by the {@link SIncCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Extracts the index of the local variable and the increment value from the instruction parameters.
    2. + *
    3. Retrieves the current value of the local variable at the given index.
    4. + *
    5. Increments the value of the local variable by the specified increment value (i.e., localVariables[index] += increment).
    6. + *
    7. Updates the local variable with the new incremented value.
    8. + *
    9. Returns the updated program counter (PC) value, which typically increments by 1 unless control flow is modified.
    10. + *
    + * + *

    This opcode is useful for optimizing the modification of local variables, especially in tight loops or when managing counters.

    + */ +public class SIncCommand implements Command { + + /** + * Default constructor for creating an instance of SIncCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public SIncCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for incrementing a local variable. + * + *

    This method retrieves the necessary data (index of the local variable and the increment value) from the instruction parameters, + * performs the increment operation on the specified local variable, and returns the updated program counter (PC) value.

    + * + * @param parts The array of instruction parameters, which includes the index of the local variable and + * the increment value (passed directly as bytecode parameters). + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, including the one being incremented. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Retrieve the index of the local variable and the increment value from the parameters + int localVariableIndex = Integer.parseInt(parts[1]); // Index of the local variable to be incremented + short incrementValue = Short.parseShort(parts[2]); // The value by which to increment the local variable + + // Get the current value of the local variable at the specified index + short currentValue = (short) callStack.peekFrame().getLocalVariableStore().getVariable(localVariableIndex); + + // Increment the local variable value by the specified increment + short newValue = (short) (currentValue + incrementValue); + + // Update the local variable with the new incremented value + callStack.peekFrame().getLocalVariableStore().setVariable(localVariableIndex, newValue); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SModCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SModCommand.java new file mode 100644 index 0000000..0047449 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SModCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.short16; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * SModCommand Opcode: Represents the short16 modulus operation in the virtual machine. + *

    This opcode is implemented by the {@link SModCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two short16 values from the operand stack.
    2. + *
    3. Performs the modulus operation to calculate the remainder (i.e., a % b).
    4. + *
    5. Pushes the result of the modulus operation back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to calculate the remainder of the division of two short16 values, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class SModCommand implements Command { + + /** + * Default constructor for creating an instance of SModCommand. + * This constructor is empty as no specific initialization is required. + */ + public SModCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for performing a modulus operation between two short16 values. + * + *

    This method retrieves the two short16 values from the operand stack, performs the modulus operation, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + short b = (short) operandStack.pop(); + short a = (short) operandStack.pop(); + + // Perform the modulus operation and push the result back onto the stack + operandStack.push(a % b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SMulCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SMulCommand.java new file mode 100644 index 0000000..1a2ac4e --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SMulCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.short16; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * SMulCommand Opcode: Represents the short16 multiplication operation in the virtual machine. + *

    This opcode is implemented by the {@link SMulCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two short16 values from the operand stack.
    2. + *
    3. Performs the multiplication of the two values (i.e., a * b).
    4. + *
    5. Pushes the result back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to multiply two short16 values together, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class SMulCommand implements Command { + + /** + * Default constructor for creating an instance of SMulCommand. + * This constructor is empty as no specific initialization is required. + */ + public SMulCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for multiplying two short16 values. + * + *

    This method retrieves the two short16 values from the operand stack, multiplies them together, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + short b = (short) operandStack.pop(); + short a = (short) operandStack.pop(); + + // Perform the multiplication and push the result back onto the stack + operandStack.push(a * b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SNegCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SNegCommand.java new file mode 100644 index 0000000..1474c50 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SNegCommand.java @@ -0,0 +1,60 @@ +package org.jcnc.snow.vm.commands.arithmetic.short16; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * SNegCommand Opcode: Represents the short16 negation operation in the virtual machine. + *

    This opcode is implemented by the {@link SNegCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top short16 value from the operand stack.
    2. + *
    3. Performs the negation of the popped value (i.e., -value).
    4. + *
    5. Pushes the negated result back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to negate a short16 value, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class SNegCommand implements Command { + + /** + * Default constructor for creating an instance of SNegCommand. + * This constructor does not perform any specific initialization, as the command's parameters are passed during execution. + */ + public SNegCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for negating a short16 value. + * + *

    This method retrieves the short16 value from the operand stack, negates it, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top short16 value from the operand stack + short value = (short) operandStack.pop(); + + // Perform the negation of the value + short negatedValue = (short) -value; + + // Push the negated result back onto the operand stack + operandStack.push(negatedValue); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SSubCommand.java b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SSubCommand.java new file mode 100644 index 0000000..66c3295 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/arithmetic/short16/SSubCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.arithmetic.short16; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * SSubCommand Opcode: Represents the short16 subtraction operation in the virtual machine. + *

    This opcode is implemented by the {@link SSubCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two short16 values from the operand stack.
    2. + *
    3. Performs the subtraction of the second popped value from the first (i.e., a - b).
    4. + *
    5. Pushes the result of the subtraction back onto the operand stack for subsequent instructions to use.
    6. + *
    + * + *

    This opcode is typically used to subtract one short16 value from another, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ +public class SSubCommand implements Command { + + /** + * Default constructor for creating an instance of SSubCommand. + * This constructor is empty as no specific initialization is required. + */ + public SSubCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation for subtracting two short16 values. + * + *

    This method retrieves the two short16 values from the operand stack, subtracts the second value from the first, and pushes the result back onto the operand stack.

    + * + * @param parts The array of instruction parameters, which is not used in this case since no additional arguments are needed for this operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically incremented after execution to point to the next instruction. + * @param operandStack The operand stack manager of the virtual machine, responsible for operations such as push, pop, and peek. + * @param localVariableStore The local variable store, used to manage method-local variables during execution. + * It allows access to and modification of local variables, but is not used in this particular operation. + * @param callStack The virtual machine's call stack, keeping track of the method invocation hierarchy. + * Used by instructions that involve method calls or returns (e.g., `CALL` and `RETURN`). + * @return The updated program counter-value, which is typically the current PC incremented by 1, unless control flow is altered. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop the top two operands from the stack + short b = (short) operandStack.pop(); + short a = (short) operandStack.pop(); + + // Perform the subtraction and push the result back onto the stack + operandStack.push(a - b); + + // Return the updated program counter (next instruction) + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/bitwise/int32/IAndCommand.java b/src/main/java/org/jcnc/snow/vm/commands/bitwise/int32/IAndCommand.java new file mode 100644 index 0000000..f43b8c6 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/bitwise/int32/IAndCommand.java @@ -0,0 +1,56 @@ +package org.jcnc.snow.vm.commands.bitwise.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The IAndCommand class implements the {@link Command} interface and represents the int32 bitwise AND (`&`) operation command. + * This class performs an int32 bitwise AND operation in the virtual machine by popping the top two values from the stack, + * computing their int32 bitwise AND operation, and then pushing the result back onto the stack. It is a basic operation command within the virtual machine. + * + *

    Specific behavior:

    + *
      + *
    • Pops two operands from the virtual machine stack.
    • + *
    • Performs the int32 bitwise AND (`&`) operation.
    • + *
    • Pushes the result back onto the virtual machine stack.
    • + *
    + */ +public class IAndCommand implements Command { + /** + * Default constructor for creating an instance of {@code IAndCommand}. + * This constructor is empty as no specific initialization is required. + */ + public IAndCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * @param operandStack The virtual machine's operand stack manager, responsible for pushing and popping values. + * @param localVariableStore The local variable store, typically used to manage method-local variables. + * @param callStack The virtual machine's call stack, used for managing method calls and returns. + * @return The updated program counter-value, typically `currentPC + 1` unless a control flow instruction is executed. + * @throws IllegalStateException if there are not enough operands on the stack to perform the operation. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Ensure the stack has at least two operands + if (operandStack.size() < 2) { + throw new IllegalStateException("Not enough operands on the stack for IAND operation."); + } + + // Pop the top two operands from the stack + final int b = (int) operandStack.pop(); + final int a = (int) operandStack.pop(); + + // Perform the int32 bitwise AND operation and push the result back onto the stack + operandStack.push(a & b); + + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/bitwise/int32/IOrCommand.java b/src/main/java/org/jcnc/snow/vm/commands/bitwise/int32/IOrCommand.java new file mode 100644 index 0000000..2fe93e3 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/bitwise/int32/IOrCommand.java @@ -0,0 +1,56 @@ +package org.jcnc.snow.vm.commands.bitwise.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The {@code IOrCommand} class implements the {@link Command} interface and represents the int32 bitwise OR (`|`) operation command. + * This class performs an int32 bitwise OR operation in the virtual machine by popping the top two values from the stack, + * computing their OR, and then pushing the result back onto the stack. It is a basic operation command within the virtual machine. + * + *

    Operation details:

    + *
      + *
    • Pops two operands from the virtual machine stack.
    • + *
    • Performs the int32 bitwise OR (`|`) operation.
    • + *
    • Pushes the result back onto the virtual machine stack.
    • + *
    + */ +public class IOrCommand implements Command { + /** + * Default constructor for creating an instance of {@code IOrCommand}. + * This constructor is empty as no specific initialization is required. + */ + public IOrCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * @param operandStack The virtual machine's operand stack manager, responsible for performing stack operations. + * @param localVariableStore The local variable store, typically used to manage method-local variables. + * @param callStack The virtual machine's call stack, which keeps track of method invocations. + * @return The updated program counter-value, typically {@code currentPC + 1}, unless a control flow instruction is executed. + * @throws IllegalStateException if there are not enough operands on the stack to perform the operation. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Ensure the stack has at least two operands + if (operandStack.size() < 2) { + throw new IllegalStateException("Not enough operands on the stack for IOR operation."); + } + + // Pop the top two operands from the stack + final int b = (int) operandStack.pop(); + final int a = (int) operandStack.pop(); + + // Perform the int32 bitwise OR operation and push the result back onto the stack + operandStack.push(a | b); + + return currentPC + 1; + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/vm/commands/bitwise/int32/IXorCommand.java b/src/main/java/org/jcnc/snow/vm/commands/bitwise/int32/IXorCommand.java new file mode 100644 index 0000000..0502e0a --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/bitwise/int32/IXorCommand.java @@ -0,0 +1,56 @@ +package org.jcnc.snow.vm.commands.bitwise.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The {@code IXorCommand} class implements the {@link Command} interface and represents the int32 bitwise XOR (`^`) operation command. + * This class performs an int32 bitwise XOR operation in the virtual machine by popping the top two values from the stack, + * computing their XOR, and then pushing the result back onto the stack. It is a basic operation command within the virtual machine. + * + *

    Operation details:

    + *
      + *
    • Pops two operands from the virtual machine stack.
    • + *
    • Performs the int32 bitwise XOR (`^`) operation.
    • + *
    • Pushes the result back onto the virtual machine stack.
    • + *
    + */ +public class IXorCommand implements Command { + /** + * Default constructor for creating an instance of {@code IXorCommand}. + * This constructor is empty as no specific initialization is required. + */ + public IXorCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * @param operandStack The virtual machine's operand stack manager, responsible for performing stack operations. + * @param localVariableStore The local variable store, typically used to manage method-local variables. + * @param callStack The virtual machine's call stack, which keeps track of method invocations. + * @return The updated program counter-value, typically {@code currentPC + 1}, unless a control flow instruction is executed. + * @throws IllegalStateException if there are not enough operands on the stack to perform the operation. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Ensure the stack has at least two operands + if (operandStack.size() < 2) { + throw new IllegalStateException("Not enough operands on the stack for IXOR operation."); + } + + // Pop the top two operands from the stack + final int b = (int) operandStack.pop(); + final int a = (int) operandStack.pop(); + + // Perform the int32 bitwise XOR operation and push the result back onto the stack + operandStack.push(a ^ b); + + return currentPC + 1; + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/vm/commands/bitwise/long64/LAndCommand.java b/src/main/java/org/jcnc/snow/vm/commands/bitwise/long64/LAndCommand.java new file mode 100644 index 0000000..dd79d10 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/bitwise/long64/LAndCommand.java @@ -0,0 +1,56 @@ +package org.jcnc.snow.vm.commands.bitwise.long64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The LAndCommand class implements the {@link Command} interface and represents the long64 bitwise AND (`&`) operation command. + * This class performs a long64 bitwise AND operation in the virtual machine by popping the top two values from the stack, + * computing their long64 bitwise AND operation, and then pushing the result back onto the stack. It is a basic operation command within the virtual machine. + * + *

    Specific behavior:

    + *
      + *
    • Pops two operands from the virtual machine stack.
    • + *
    • Performs the long64 bitwise AND (`&`) operation.
    • + *
    • Pushes the result back onto the virtual machine stack.
    • + *
    + */ +public class LAndCommand implements Command { + /** + * Default constructor for creating an instance of {@code LAndCommand}. + * This constructor is empty as no specific initialization is required. + */ + public LAndCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * @param operandStack The virtual machine's operand stack manager, responsible for pushing and popping values. + * @param localVariableStore The local variable store, typically used to manage method-local variables. + * @param callStack The virtual machine's call stack, used for managing method calls and returns. + * @return The updated program counter-value, typically `currentPC + 1` unless a control flow instruction is executed. + * @throws IllegalStateException if there are not enough operands on the stack to perform the operation. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Ensure the stack has at least two operands + if (operandStack.size() < 2) { + throw new IllegalStateException("Not enough operands on the stack for LAND operation."); + } + + // Pop the top two operands from the stack + final long b = (long) operandStack.pop(); + final long a = (long) operandStack.pop(); + + // Perform the long64 bitwise AND operation and push the result back onto the stack + operandStack.push(a & b); + + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/bitwise/long64/LOrCommand.java b/src/main/java/org/jcnc/snow/vm/commands/bitwise/long64/LOrCommand.java new file mode 100644 index 0000000..33a9bf9 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/bitwise/long64/LOrCommand.java @@ -0,0 +1,56 @@ +package org.jcnc.snow.vm.commands.bitwise.long64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The {@code LOrCommand} class implements the {@link Command} interface and represents the long64 bitwise OR (`|`) operation command. + * This class performs a long64 bitwise OR operation in the virtual machine by popping the top two values from the stack, + * computing their OR, and then pushing the result back onto the stack. It is a basic operation command within the virtual machine. + * + *

    Operation details:

    + *
      + *
    • Pops two operands from the virtual machine stack.
    • + *
    • Performs the long64 bitwise OR (`|`) operation.
    • + *
    • Pushes the result back onto the virtual machine stack.
    • + *
    + */ +public class LOrCommand implements Command { + /** + * Default constructor for creating an instance of {@code LOrCommand}. + * This constructor is empty as no specific initialization is required. + */ + public LOrCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * @param operandStack The virtual machine's operand stack manager, responsible for performing stack operations. + * @param localVariableStore The local variable store, typically used to manage method-local variables. + * @param callStack The virtual machine's call stack, which keeps track of method invocations. + * @return The updated program counter-value, typically {@code currentPC + 1}, unless a control flow instruction is executed. + * @throws IllegalStateException if there are not enough operands on the stack to perform the operation. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Ensure the stack has at least two operands + if (operandStack.size() < 2) { + throw new IllegalStateException("Not enough operands on the stack for LOR operation."); + } + + // Pop the top two operands from the stack + final long b = (long) operandStack.pop(); + final long a = (long) operandStack.pop(); + + // Perform the long64 bitwise OR operation and push the result back onto the stack + operandStack.push(a | b); + + return currentPC + 1; + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/vm/commands/bitwise/long64/LXorCommand.java b/src/main/java/org/jcnc/snow/vm/commands/bitwise/long64/LXorCommand.java new file mode 100644 index 0000000..2a668dd --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/bitwise/long64/LXorCommand.java @@ -0,0 +1,59 @@ +package org.jcnc.snow.vm.commands.bitwise.long64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The {@code LXorCommand} class implements the {@link Command} interface and represents the long64 bitwise XOR + * (`^`) operation command. + * This class performs a long64 bitwise XOR operation in the virtual machine + * by popping the top two values from the stack, + * computing their XOR, and then pushing the result back onto the stack. + * It is a basic operation command within the virtual machine. + * + *

    Operation details:

    + *
      + *
    • Pops two operands from the virtual machine stack.
    • + *
    • Performs the long64 bitwise XOR (`^`) operation.
    • + *
    • Pushes the result back onto the virtual machine stack.
    • + *
    + */ +public class LXorCommand implements Command { + /** + * Default constructor for creating an instance of {@code LXorCommand}. + * This constructor is empty as no specific initialization is required. + */ + public LXorCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * @param operandStack The virtual machine's operand stack manager, responsible for performing stack operations. + * @param localVariableStore The local variable store, typically used to manage method-local variables. + * @param callStack The virtual machine's call stack, which keeps track of method invocations. + * @return The updated program counter-value, typically {@code currentPC + 1}, unless a control flow instruction is executed. + * @throws IllegalStateException if there are not enough operands on the stack to perform the operation. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Ensure the stack has at least two operands + if (operandStack.size() < 2) { + throw new IllegalStateException("Not enough operands on the stack for LXOR operation."); + } + + // Pop the top two operands from the stack + final int b = (int) operandStack.pop(); + final int a = (int) operandStack.pop(); + + // Perform the long64 bitwise XOR operation and push the result back onto the stack + operandStack.push(a ^ b); + + return currentPC + 1; + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/vm/commands/control/all/JumpCommand.java b/src/main/java/org/jcnc/snow/vm/commands/control/all/JumpCommand.java new file mode 100644 index 0000000..63418da --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/control/all/JumpCommand.java @@ -0,0 +1,73 @@ +package org.jcnc.snow.vm.commands.control.all; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.utils.LoggingUtils; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The JumpCommand class implements the {@link Command} interface and represents an unconditional jump instruction in the virtual machine. + * This class performs a jump to the specified target instruction address. + * + *

    Specific behavior:

    + *
      + *
    • Parses the target instruction address and performs validation.
    • + *
    • If the target address is valid (greater than or equal to 0), it jumps to the specified address.
    • + *
    • If the target address is invalid (less than 0), it outputs an error message and halts execution.
    • + *
    + */ +public class JumpCommand implements Command { + /** + * Default constructor for creating an instance of JumpCommand. + * This constructor is empty as no specific initialization is required. + */ + public JumpCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter-value, typically the current program counter-value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the target instruction address + int target = Integer.parseInt(parts[1]); + + // Check if the target address is valid + if (target >= 0) { + LoggingUtils.logInfo("Jumping to instruction", String.valueOf(target)); + return target; + } else { + LoggingUtils.logError("Invalid jump target"); + return -1; // Return -1 to indicate invalid jump + } + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICECommand.java b/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICECommand.java new file mode 100644 index 0000000..6e923af --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICECommand.java @@ -0,0 +1,76 @@ +package org.jcnc.snow.vm.commands.control.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.utils.LoggingUtils; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The ICECommand class implements the {@link Command} interface and represents a conditional jump command in the virtual machine. + * This class compares two values from the stack, and if they are equal, it jumps to the specified target command. + * + *

    Specific behavior:

    + *
      + *
    • Pops two integers from the virtual machine stack.
    • + *
    • If the two integers are equal, jumps to the target command.
    • + *
    • Otherwise, the program continues with the next command.
    • + *
    + */ +public class ICECommand implements Command { + /** + * Default constructor for creating an instance of ICECommand. + * This constructor is empty as no specific initialization is required. + */ + public ICECommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter-value, typically the current program counter-value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the target command address + int target = Integer.parseInt(parts[1]); + + // Pop the two operands from the stack + int b = (int) operandStack.pop(); + int a = (int) operandStack.pop(); + + // If the operands are equal, jump to the target command + if (a == b) { + LoggingUtils.logInfo("Jumping to command", String.valueOf(target)); + return target; + } + + return currentPC + 1; + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICGCommand.java b/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICGCommand.java new file mode 100644 index 0000000..a339313 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICGCommand.java @@ -0,0 +1,76 @@ +package org.jcnc.snow.vm.commands.control.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.utils.LoggingUtils; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The ICGCommand class implements the {@link Command} interface and represents a conditional jump command in the virtual machine. + * This class compares two values from the stack, and if the first value is greater than the second, it jumps to the specified target command. + * + *

    Specific behavior:

    + *
      + *
    • Pops two integers from the virtual machine stack.
    • + *
    • If the first integer is greater than the second, jumps to the target command.
    • + *
    • Otherwise, the program continues with the next command.
    • + *
    + */ +public class ICGCommand implements Command { + /** + * Default constructor for creating an instance of ICGCommand. + * This constructor is empty as no specific initialization is required. + */ + public ICGCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter-value, typically the current program counter-value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the target command address + int target = Integer.parseInt(parts[1]); + + // Pop the two operands from the stack + int b = (int) operandStack.pop(); + int a = (int) operandStack.pop(); + + // If the first operand is greater than the second, jump to the target command + if (a > b) { + LoggingUtils.logInfo("Jumping to command", String.valueOf(target)); + return target; + } + + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICGECommand.java b/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICGECommand.java new file mode 100644 index 0000000..3cfe02d --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICGECommand.java @@ -0,0 +1,76 @@ +package org.jcnc.snow.vm.commands.control.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.utils.LoggingUtils; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The ICGECommand class implements the {@link Command} interface and represents a conditional jump command in the virtual machine. + * This class compares two values from the stack, and if the first value is greater than or equal to the second, it jumps to the specified target command. + * + *

    Specific behavior:

    + *
      + *
    • Pops two integers from the virtual machine stack.
    • + *
    • If the first integer is greater than or equal to the second, jumps to the target command.
    • + *
    • Otherwise, the program continues with the next command.
    • + *
    + */ +public class ICGECommand implements Command { + /** + * Default constructor for creating an instance of ICGECommand. + * This constructor is empty as no specific initialization is required. + */ + public ICGECommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter-value, typically the current program counter-value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the target command address + int target = Integer.parseInt(parts[1]); + + // Pop the two operands from the stack + int b = (int) operandStack.pop(); + int a = (int) operandStack.pop(); + + // If the first operand is greater than or equal to the second, jump to the target command + if (a >= b) { + LoggingUtils.logInfo("Jumping to command", String.valueOf(target)); + return target; + } + + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICLCommand.java b/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICLCommand.java new file mode 100644 index 0000000..ec3d413 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICLCommand.java @@ -0,0 +1,76 @@ +package org.jcnc.snow.vm.commands.control.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.utils.LoggingUtils; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The ICLCommand class implements the {@link Command} interface and represents a conditional jump command in the virtual machine. + * This class compares two values from the stack, and if the first value is less than the second, it jumps to the specified target command. + * + *

    Specific behavior:

    + *
      + *
    • Pops two integers from the virtual machine stack.
    • + *
    • If the first integer is less than the second, jumps to the target command.
    • + *
    • Otherwise, the program continues with the next command.
    • + *
    + */ +public class ICLCommand implements Command { + /** + * Default constructor for creating an instance of ICLCommand. + * This constructor is empty as no specific initialization is required. + */ + public ICLCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter-value, typically the current program counter-value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the target command address + int target = Integer.parseInt(parts[1]); + + // Pop the two operands from the stack + int b = (int) operandStack.pop(); + int a = (int) operandStack.pop(); + + // If the first operand is less than the second, jump to the target command + if (a < b) { + LoggingUtils.logInfo("Jumping to command", String.valueOf(target)); + return target; + } + + return currentPC + 1; + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICLECommand.java b/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICLECommand.java new file mode 100644 index 0000000..4e6c007 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICLECommand.java @@ -0,0 +1,76 @@ +package org.jcnc.snow.vm.commands.control.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.utils.LoggingUtils; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The ICLECommand class implements the {@link Command} interface and represents a conditional jump command in the virtual machine. + * This class compares two values from the stack, and if the first value is less than or equal to the second, it jumps to the specified target command. + * + *

    Specific behavior:

    + *
      + *
    • Pops two integers from the virtual machine stack.
    • + *
    • If the first integer is less than or equal to the second, jumps to the target command.
    • + *
    • Otherwise, the program continues with the next command.
    • + *
    + */ +public class ICLECommand implements Command { + /** + * Default constructor for creating an instance of ICLECommand. + * This constructor is empty as no specific initialization is required. + */ + public ICLECommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter-value, typically the current program counter-value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the target command address + int target = Integer.parseInt(parts[1]); + + // Pop the two operands from the stack + int b = (int) operandStack.pop(); + int a = (int) operandStack.pop(); + + // If the first operand is less than or equal to the second, jump to the target command + if (a <= b) { + LoggingUtils.logInfo("Jumping to command", String.valueOf(target)); + return target; + } + + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICNECommand.java b/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICNECommand.java new file mode 100644 index 0000000..08a46a9 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/control/int32/ICNECommand.java @@ -0,0 +1,76 @@ +package org.jcnc.snow.vm.commands.control.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.utils.LoggingUtils; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The ICNECommand class implements the {@link Command} interface and represents a conditional jump command + * in the virtual machine that triggers if two values are not equal. + * + *

    Specific behavior:

    + *
      + *
    • Pops two integers from the virtual machine stack.
    • + *
    • If the two integers are not equal, jumps to the target command.
    • + *
    • Otherwise, the program continues with the next command.
    • + *
    + */ +public class ICNECommand implements Command { + /** + * Default constructor for creating an instance of ICNECommand. + * This constructor is empty as no specific initialization is required. + */ + public ICNECommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter-value, typically the current program counter-value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the target command address + int target = Integer.parseInt(parts[1]); + + // Pop the two operands from the stack + int b = (int) operandStack.pop(); + int a = (int) operandStack.pop(); + + // If the operands are not equal, jump to the target command + if (a != b) { + LoggingUtils.logInfo("Jumping to command", String.valueOf(target)); + return target; + } + + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/function/CallCommand.java b/src/main/java/org/jcnc/snow/vm/commands/function/CallCommand.java new file mode 100644 index 0000000..a384570 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/function/CallCommand.java @@ -0,0 +1,50 @@ +package org.jcnc.snow.vm.commands.function; + +import org.jcnc.snow.vm.engine.VMMode; +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.*; + +/** + * CALL addr nArgs — pushes a new stack-frame, transfers the specified + * argument count from the operand stack into the callee’s local slots + * 0‥n-1 (left-to-right), then jumps to {@code addr}. + */ +public class CallCommand implements Command { + + @Override + public int execute(String[] parts, + int currentPC, + OperandStack operandStack, + LocalVariableStore callerLVS, + CallStack callStack) { + + if (parts.length < 3) + throw new IllegalArgumentException("CALL: need "); + + int targetAddr; + int nArgs; + try { + targetAddr = Integer.parseInt(parts[1]); + nArgs = Integer.parseInt(parts[2]); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("CALL: malformed operands", e); + } + + /* build new frame & local table for callee */ + LocalVariableStore calleeLVS = new LocalVariableStore(VMMode.RUN); + + /* transfer arguments: operand stack top is last arg */ + for (int slot = nArgs - 1; slot >= 0; slot--) { + if (operandStack.isEmpty()) + throw new IllegalStateException("CALL: operand stack underflow"); + calleeLVS.setVariable(slot, operandStack.pop()); + } + + StackFrame newFrame = new StackFrame(currentPC + 1, calleeLVS, + new MethodContext("subroutine@" + targetAddr, null)); + callStack.pushFrame(newFrame); + + System.out.println("Calling function at address: " + targetAddr); + return targetAddr; // jump + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/function/RetCommand.java b/src/main/java/org/jcnc/snow/vm/commands/function/RetCommand.java new file mode 100644 index 0000000..af37032 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/function/RetCommand.java @@ -0,0 +1,48 @@ +package org.jcnc.snow.vm.commands.function; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.*; + +/** + * Implements the {@code RET} instruction (method return). + * + *

    Root-frame protection: The command first inspects the + * top frame without popping it. If the frame’s return address is {@code 0} + * (the root frame) the VM signals normal termination by returning + * {@link #PROGRAM_END}. The frame is not removed and its locals are + * kept intact. All other frames are popped and their locals cleared.

    + */ +public class RetCommand implements Command { + + /** Sentinel value that tells the VM loop to terminate gracefully. */ + private static final int PROGRAM_END = Integer.MAX_VALUE; + + @Override + public int execute(String[] parts, + int currentPC, + OperandStack operandStack, + LocalVariableStore localVariableStore, + CallStack callStack) { + + /* ----- Guard: must have at least one frame ----- */ + if (callStack.isEmpty()) { + throw new IllegalStateException("RET: call stack is empty."); + } + + StackFrame topFrame = callStack.peekFrame(); + + /* ----- Root frame: do NOT pop, just end program ----- */ + if (topFrame.getReturnAddress() == 0) { + System.out.println("Return 0"); + return PROGRAM_END; // VM main loop should break + } + + /* ----- Normal frame: pop & clean locals, then resume caller ----- */ + StackFrame finished = callStack.popFrame(); + finished.getLocalVariableStore().clearVariables(); + + int returnAddr = finished.getReturnAddress(); + System.out.println("Return " + returnAddr); + return returnAddr; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/memory/all/MovCommand.java b/src/main/java/org/jcnc/snow/vm/commands/memory/all/MovCommand.java new file mode 100644 index 0000000..dd0b11b --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/memory/all/MovCommand.java @@ -0,0 +1,67 @@ +package org.jcnc.snow.vm.commands.memory.all; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The MovCommand class implements the {@link Command} interface and represents a MOV instruction in the virtual machine. + * This class is used to transfer a value from one memory location to another within the local variable store of the current frame. + * + *

    Specific behavior:

    + *
      + *
    • Retrieves a value from the specified source index in the local variable store of the current method frame.
    • + *
    • Stores the retrieved value into the specified destination index within the same local variable store.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class MovCommand implements Command { + /** + * Default constructor for creating an instance of MovCommand. + * This constructor is empty as no specific initialization is required. + */ + public MovCommand() { + // Empty constructor + } + + /** + * Executes the MOV instruction to transfer a value from one local variable to another within the same method frame. + * + *

    This method performs the following operations:

    + *
      + *
    • Parses the source and destination indices from the instruction parameters.
    • + *
    • Retrieves the value from the local variable store at the source index.
    • + *
    • Stores the retrieved value into the destination index of the local variable store.
    • + *
    • Increments the program counter to point to the next instruction in the program sequence.
    • + *
    + * + *

    After execution, the program counter (PC) is updated to continue with the next instruction, unless control flow is modified.

    + * + * @param parts The array of instruction parameters, which includes the source and destination indices + * of the local variables involved in the move operation. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * Typically, this is incremented after each instruction execution. + * @param operandStack The virtual machine's operand stack manager, responsible for pushing, popping, and peeking values. + * @param localVariableStore The local variable store, used to retrieve and store values between different local variables. + * This store is accessed by the current method frame. + * @param callStack The virtual machine's call stack, used to track method calls and manage method frames. It is needed to + * retrieve the correct method frame's local variable store in this command. + * @return The updated program counter-value, typically incremented by 1, unless modified by control flow instructions. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the source and destination indices from the instruction parameters + int sourceIndex = Integer.parseInt(parts[1]); + int destinationIndex = Integer.parseInt(parts[2]); + + // Retrieve the value from the local variable store at the source index + Object value = callStack.peekFrame().getLocalVariableStore().getVariable(sourceIndex); + + // Store the retrieved value into the destination index within the local variable store + callStack.peekFrame().getLocalVariableStore().setVariable(destinationIndex, value); + + // Return the updated program counter to continue to the next instruction + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/memory/byte8/BLoadCommand.java b/src/main/java/org/jcnc/snow/vm/commands/memory/byte8/BLoadCommand.java new file mode 100644 index 0000000..c42e645 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/memory/byte8/BLoadCommand.java @@ -0,0 +1,66 @@ +package org.jcnc.snow.vm.commands.memory.byte8; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The BLoadCommand class implements the {@link Command} interface and represents a load instruction in the virtual machine. + * This class is used to load a value from the local variable store of the current method frame and push it onto the virtual machine stack. + * + *

    Specific behavior:

    + *
      + *
    • Retrieves a value from the specified index in the local variable store of the current frame.
    • + *
    • Pushes that value onto the virtual machine operand stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class BLoadCommand implements Command { + /** + * Default constructor for creating an instance of BLoadCommand. + * This constructor is empty as no specific initialization is required. + */ + public BLoadCommand() { + // Empty constructor + } + + /** + * Executes the load instruction to retrieve a value from the local variable store and push it onto the operand stack. + * + *

    This method performs the following operations:

    + *
      + *
    • Parses the index of the local variable to load from the instruction parameters.
    • + *
    • Retrieves the corresponding value from the local variable store of the current method frame.
    • + *
    • Pushes the retrieved value onto the operand stack for subsequent operations.
    • + *
    • Increments the program counter to point to the next instruction to be executed.
    • + *
    + * + *

    After execution, the program counter (PC) is updated to continue with the next instruction in the sequence, unless control flow changes.

    + * + * @param parts The array of instruction parameters, which typically includes the operation type and the index + * of the local variable to be loaded. The structure may vary based on the specific instruction. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after execution to point to the next instruction. + * @param operandStack The virtual machine's operand stack manager, responsible for pushing, popping, and peeking values. + * @param localVariableStore The local variable store, from which values are loaded based on the provided index. + * This store manages method-local variables. + * @param callStack The virtual machine's call stack, which tracks method calls and returns. It is used to access + * the correct frame's local variable store in this case. + * @return The updated program counter-value, which is typically incremented by 1 unless control flow is modified by other instructions. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the index of the local variable to be loaded + int index = Integer.parseInt(parts[1]); + + // Retrieve the value from the local variable store of the current method frame + byte value = (byte) callStack.peekFrame().getLocalVariableStore().getVariable(index); + + // Push the loaded value onto the operand stack for subsequent operations + operandStack.push(value); + + // Return the updated program counter to continue to the next instruction + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/memory/byte8/BStoreCommand.java b/src/main/java/org/jcnc/snow/vm/commands/memory/byte8/BStoreCommand.java new file mode 100644 index 0000000..47211d1 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/memory/byte8/BStoreCommand.java @@ -0,0 +1,66 @@ +package org.jcnc.snow.vm.commands.memory.byte8; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The BStoreCommand class implements the {@link Command} interface and represents a store instruction in the virtual machine. + * This class is used to store the value from the top of the virtual machine stack into the local variable store, typically used to save computed results into local variables. + * + *

    Specific behavior:

    + *
      + *
    • Pops a value from the virtual machine operand stack.
    • + *
    • Stores that value into the specified index location in the local variable store of the current frame on the call stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class BStoreCommand implements Command { + /** + * Default constructor for creating an instance of BStoreCommand. + * This constructor is empty as no specific initialization is required. + */ + public BStoreCommand() { + // Empty constructor + } + + /** + * Executes the store instruction. + * + *

    This method retrieves the value from the operand stack, pops the value, and stores it in the local variable store + * of the current method frame, based on the specified index. The program counter (PC) is then updated to point to the next instruction.

    + * + *

    The method performs the following actions:

    + *
      + *
    • Parses the index of the local variable where the value will be stored.
    • + *
    • Pops the top value from the operand stack.
    • + *
    • Stores the popped value into the specified local variable index in the current method frame's local variable store.
    • + *
    • Returns the next program counter-value to continue execution of the following instruction.
    • + *
    + * + * @param parts The array of instruction parameters, typically consisting of the operation type and related arguments, + * such as the index for the local variable. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack, used to store and manipulate values during instruction execution. + * @param localVariableStore The local variable store, used for managing method-local variables. + * @param callStack The virtual machine's call stack, which tracks the method invocation hierarchy. + * Used in conjunction with local variable store for method-local variable management. + * @return The updated program counter-value, typically the current PC value incremented by 1, unless modified by control flow instructions. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the index for the local variable where the value will be stored + int index = Integer.parseInt(parts[1]); + + // Pop the value from the operand stack + byte value = (byte) operandStack.pop(); + + // Store the value into the local variable store of the current method frame + callStack.peekFrame().getLocalVariableStore().setVariable(index, value); + + // Return the updated program counter, which moves to the next instruction + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/memory/double64/DLoadCommand.java b/src/main/java/org/jcnc/snow/vm/commands/memory/double64/DLoadCommand.java new file mode 100644 index 0000000..9d34e21 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/memory/double64/DLoadCommand.java @@ -0,0 +1,65 @@ +package org.jcnc.snow.vm.commands.memory.double64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The DLoadCommand class implements the {@link Command} interface and represents a load instruction for 64-bit double values in the virtual machine. + * This class is used to load a double value from the local variable store of the current method frame and push it onto the virtual machine operand stack. + * + *

    Specific behavior:

    + *
      + *
    • Retrieves a double value from the specified index in the local variable store of the current frame.
    • + *
    • Pushes that value onto the virtual machine operand stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class DLoadCommand implements Command { + /** + * Default constructor for creating an instance of DLoadCommand. + * This constructor is empty as no specific initialization is required. + */ + public DLoadCommand() { + // Empty constructor + } + + /** + * Executes the load instruction to retrieve a double value from the local variable store and push it onto the operand stack. + * + *

    This method performs the following operations:

    + *
      + *
    • Parses the index of the local variable to load from the instruction parameters.
    • + *
    • Retrieves the corresponding double value from the local variable store of the current method frame.
    • + *
    • Pushes the retrieved value onto the operand stack for subsequent operations.
    • + *
    • Increments the program counter to point to the next instruction to be executed.
    • + *
    + * + *

    After execution, the program counter (PC) is updated to continue with the next instruction in the sequence, unless control flow changes.

    + * + * @param parts The array of instruction parameters, which typically includes the operation type and the index + * of the local variable to be loaded. The structure may vary based on the specific instruction. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after execution to point to the next instruction. + * @param operandStack The virtual machine's operand stack manager, responsible for pushing, popping, and peeking values. + * @param localVariableStore The local variable store, from which values are loaded based on the provided index. + * This store manages method-local variables. + * @param callStack The virtual machine's call stack, which tracks method calls and returns. It is included for interface consistency but is not used in this instruction. + * @return The updated program counter-value, which is typically incremented by 1 unless control flow is modified by other instructions. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the index of the local variable to be loaded + int index = Integer.parseInt(parts[1]); + + // Retrieve the double value from the local variable store of the current method frame + double value = (double) localVariableStore.getVariable(index); + + // Push the loaded value onto the operand stack for subsequent operations + operandStack.push(value); + + // Return the updated program counter to continue to the next instruction + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/memory/double64/DStoreCommand.java b/src/main/java/org/jcnc/snow/vm/commands/memory/double64/DStoreCommand.java new file mode 100644 index 0000000..7affd8f --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/memory/double64/DStoreCommand.java @@ -0,0 +1,66 @@ +package org.jcnc.snow.vm.commands.memory.double64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The DStoreCommand class implements the {@link Command} interface and represents a store instruction in the virtual machine. + * This class is used to store the value from the top of the virtual machine stack into the local variable store, typically used to save computed results into local variables. + * + *

    Specific behavior:

    + *
      + *
    • Pops a value from the virtual machine operand stack.
    • + *
    • Stores that value into the specified index location in the local variable store of the current frame on the call stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class DStoreCommand implements Command { + /** + * Default constructor for creating an instance of DStoreCommand. + * This constructor is empty as no specific initialization is required. + */ + public DStoreCommand() { + // Empty constructor + } + + /** + * Executes the store instruction. + * + *

    This method retrieves the value from the operand stack, pops the value, and stores it in the local variable store + * of the current method frame, based on the specified index. The program counter (PC) is then updated to point to the next instruction.

    + * + *

    The method performs the following actions:

    + *
      + *
    • Parses the index of the local variable where the value will be stored.
    • + *
    • Pops the top value from the operand stack.
    • + *
    • Stores the popped value into the specified local variable index in the current method frame's local variable store.
    • + *
    • Returns the next program counter-value to continue execution of the following instruction.
    • + *
    + * + * @param parts The array of instruction parameters, typically consisting of the operation type and related arguments, + * such as the index for the local variable. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack, used to store and manipulate values during instruction execution. + * @param localVariableStore The local variable store, used for managing method-local variables. + * @param callStack The virtual machine's call stack, which tracks the method invocation hierarchy. + * Used in conjunction with local variable store for method-local variable management. + * @return The updated program counter-value, typically the current PC value incremented by 1, unless modified by control flow instructions. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the index for the local variable where the value will be stored + int index = Integer.parseInt(parts[1]); + + // Pop the value from the operand stack + double value = (double) operandStack.pop(); + + // Store the value into the local variable store of the current method frame + callStack.peekFrame().getLocalVariableStore().setVariable(index, value); + + // Return the updated program counter, which moves to the next instruction + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/memory/float32/FLoadCommand.java b/src/main/java/org/jcnc/snow/vm/commands/memory/float32/FLoadCommand.java new file mode 100644 index 0000000..7edcdb6 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/memory/float32/FLoadCommand.java @@ -0,0 +1,66 @@ +package org.jcnc.snow.vm.commands.memory.float32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The FLoadCommand class implements the {@link Command} interface and represents a load instruction in the virtual machine. + * This class is used to load a value from the local variable store of the current method frame and push it onto the virtual machine stack. + * + *

    Specific behavior:

    + *
      + *
    • Retrieves a value from the specified index in the local variable store of the current frame.
    • + *
    • Pushes that value onto the virtual machine operand stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class FLoadCommand implements Command { + /** + * Default constructor for creating an instance of FLoadCommand. + * This constructor is empty as no specific initialization is required. + */ + public FLoadCommand() { + // Empty constructor + } + + /** + * Executes the load instruction to retrieve a value from the local variable store and push it onto the operand stack. + * + *

    This method performs the following operations:

    + *
      + *
    • Parses the index of the local variable to load from the instruction parameters.
    • + *
    • Retrieves the corresponding value from the local variable store of the current method frame.
    • + *
    • Pushes the retrieved value onto the operand stack for subsequent operations.
    • + *
    • Increments the program counter to point to the next instruction to be executed.
    • + *
    + * + *

    After execution, the program counter (PC) is updated to continue with the next instruction in the sequence, unless control flow changes.

    + * + * @param parts The array of instruction parameters, which typically includes the operation type and the index + * of the local variable to be loaded. The structure may vary based on the specific instruction. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after execution to point to the next instruction. + * @param operandStack The virtual machine's operand stack manager, responsible for pushing, popping, and peeking values. + * @param localVariableStore The local variable store, from which values are loaded based on the provided index. + * This store manages method-local variables. + * @param callStack The virtual machine's call stack, which tracks method calls and returns. It is used to access + * the correct frame's local variable store in this case. + * @return The updated program counter-value, which is typically incremented by 1 unless control flow is modified by other instructions. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the index of the local variable to be loaded + int index = Integer.parseInt(parts[1]); + + // Retrieve the value from the local variable store of the current method frame + float value = (float) callStack.peekFrame().getLocalVariableStore().getVariable(index); + + // Push the loaded value onto the operand stack for subsequent operations + operandStack.push(value); + + // Return the updated program counter to continue to the next instruction + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/memory/float32/FStoreCommand.java b/src/main/java/org/jcnc/snow/vm/commands/memory/float32/FStoreCommand.java new file mode 100644 index 0000000..4a1ae60 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/memory/float32/FStoreCommand.java @@ -0,0 +1,66 @@ +package org.jcnc.snow.vm.commands.memory.float32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The FStoreCommand class implements the {@link Command} interface and represents a store instruction in the virtual machine. + * This class is used to store the value from the top of the virtual machine stack into the local variable store, typically used to save computed results into local variables. + * + *

    Specific behavior:

    + *
      + *
    • Pops a value from the virtual machine operand stack.
    • + *
    • Stores that value into the specified index location in the local variable store of the current frame on the call stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class FStoreCommand implements Command { + /** + * Default constructor for creating an instance of FStoreCommand. + * This constructor is empty as no specific initialization is required. + */ + public FStoreCommand() { + // Empty constructor + } + + /** + * Executes the store instruction. + * + *

    This method retrieves the value from the operand stack, pops the value, and stores it in the local variable store + * of the current method frame, based on the specified index. The program counter (PC) is then updated to point to the next instruction.

    + * + *

    The method performs the following actions:

    + *
      + *
    • Parses the index of the local variable where the value will be stored.
    • + *
    • Pops the top value from the operand stack.
    • + *
    • Stores the popped value into the specified local variable index in the current method frame's local variable store.
    • + *
    • Returns the next program counter-value to continue execution of the following instruction.
    • + *
    + * + * @param parts The array of instruction parameters, typically consisting of the operation type and related arguments, + * such as the index for the local variable. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack, used to store and manipulate values during instruction execution. + * @param localVariableStore The local variable store, used for managing method-local variables. + * @param callStack The virtual machine's call stack, which tracks the method invocation hierarchy. + * Used in conjunction with local variable store for method-local variable management. + * @return The updated program counter-value, typically the current PC value incremented by 1, unless modified by control flow instructions. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the index for the local variable where the value will be stored + int index = Integer.parseInt(parts[1]); + + // Pop the value from the operand stack + float value = (float) operandStack.pop(); + + // Store the value into the local variable store of the current method frame + callStack.peekFrame().getLocalVariableStore().setVariable(index, value); + + // Return the updated program counter, which moves to the next instruction + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/memory/int32/ILoadCommand.java b/src/main/java/org/jcnc/snow/vm/commands/memory/int32/ILoadCommand.java new file mode 100644 index 0000000..02a55fa --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/memory/int32/ILoadCommand.java @@ -0,0 +1,66 @@ +package org.jcnc.snow.vm.commands.memory.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The ILoadCommand class implements the {@link Command} interface and represents a load instruction in the virtual machine. + * This class is used to load a value from the local variable store of the current method frame and push it onto the virtual machine stack. + * + *

    Specific behavior:

    + *
      + *
    • Retrieves a value from the specified index in the local variable store of the current frame.
    • + *
    • Pushes that value onto the virtual machine operand stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class ILoadCommand implements Command { + /** + * Default constructor for creating an instance of ILoadCommand. + * This constructor is empty as no specific initialization is required. + */ + public ILoadCommand() { + // Empty constructor + } + + /** + * Executes the load instruction to retrieve a value from the local variable store and push it onto the operand stack. + * + *

    This method performs the following operations:

    + *
      + *
    • Parses the index of the local variable to load from the instruction parameters.
    • + *
    • Retrieves the corresponding value from the local variable store of the current method frame.
    • + *
    • Pushes the retrieved value onto the operand stack for subsequent operations.
    • + *
    • Increments the program counter to point to the next instruction to be executed.
    • + *
    + * + *

    After execution, the program counter (PC) is updated to continue with the next instruction in the sequence, unless control flow changes.

    + * + * @param parts The array of instruction parameters, which typically includes the operation type and the index + * of the local variable to be loaded. The structure may vary based on the specific instruction. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after execution to point to the next instruction. + * @param operandStack The virtual machine's operand stack manager, responsible for pushing, popping, and peeking values. + * @param localVariableStore The local variable store, from which values are loaded based on the provided index. + * This store manages method-local variables. + * @param callStack The virtual machine's call stack, which tracks method calls and returns. It is used to access + * the correct frame's local variable store in this case. + * @return The updated program counter-value, which is typically incremented by 1 unless control flow is modified by other instructions. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the index of the local variable to be loaded + int index = Integer.parseInt(parts[1]); + + // Retrieve the value from the local variable store of the current method frame + int value = (int) callStack.peekFrame().getLocalVariableStore().getVariable(index); + + // Push the loaded value onto the operand stack for subsequent operations + operandStack.push(value); + + // Return the updated program counter to continue to the next instruction + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/memory/int32/IStoreCommand.java b/src/main/java/org/jcnc/snow/vm/commands/memory/int32/IStoreCommand.java new file mode 100644 index 0000000..31dfb26 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/memory/int32/IStoreCommand.java @@ -0,0 +1,66 @@ +package org.jcnc.snow.vm.commands.memory.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The IStoreCommand class implements the {@link Command} interface and represents a store instruction in the virtual machine. + * This class is used to store the value from the top of the virtual machine stack into the local variable store, typically used to save computed results into local variables. + * + *

    Specific behavior:

    + *
      + *
    • Pops a value from the virtual machine operand stack.
    • + *
    • Stores that value into the specified index location in the local variable store of the current frame on the call stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class IStoreCommand implements Command { + /** + * Default constructor for creating an instance of IStoreCommand. + * This constructor is empty as no specific initialization is required. + */ + public IStoreCommand() { + // Empty constructor + } + + /** + * Executes the store instruction. + * + *

    This method retrieves the value from the operand stack, pops the value, and stores it in the local variable store + * of the current method frame, based on the specified index. The program counter (PC) is then updated to point to the next instruction.

    + * + *

    The method performs the following actions:

    + *
      + *
    • Parses the index of the local variable where the value will be stored.
    • + *
    • Pops the top value from the operand stack.
    • + *
    • Stores the popped value into the specified local variable index in the current method frame's local variable store.
    • + *
    • Returns the next program counter-value to continue execution of the following instruction.
    • + *
    + * + * @param parts The array of instruction parameters, typically consisting of the operation type and related arguments, + * such as the index for the local variable. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack, used to store and manipulate values during instruction execution. + * @param localVariableStore The local variable store, used for managing method-local variables. + * @param callStack The virtual machine's call stack, which tracks the method invocation hierarchy. + * Used in conjunction with local variable store for method-local variable management. + * @return The updated program counter-value, typically the current PC value incremented by 1, unless modified by control flow instructions. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the index for the local variable where the value will be stored + int index = Integer.parseInt(parts[1]); + + // Pop the value from the operand stack + int value = (int) operandStack.pop(); + + // Store the value into the local variable store of the current method frame + callStack.peekFrame().getLocalVariableStore().setVariable(index, value); + + // Return the updated program counter, which moves to the next instruction + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/memory/long64/LLoadCommand.java b/src/main/java/org/jcnc/snow/vm/commands/memory/long64/LLoadCommand.java new file mode 100644 index 0000000..411d766 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/memory/long64/LLoadCommand.java @@ -0,0 +1,66 @@ +package org.jcnc.snow.vm.commands.memory.long64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The LLoadCommand class implements the {@link Command} interface and represents a load instruction in the virtual machine. + * This class is used to load a value from the local variable store of the current method frame and push it onto the virtual machine stack. + * + *

    Specific behavior:

    + *
      + *
    • Retrieves a value from the specified index in the local variable store of the current frame.
    • + *
    • Pushes that value onto the virtual machine operand stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class LLoadCommand implements Command { + /** + * Default constructor for creating an instance of LLoadCommand. + * This constructor is empty as no specific initialization is required. + */ + public LLoadCommand() { + // Empty constructor + } + + /** + * Executes the load instruction to retrieve a value from the local variable store and push it onto the operand stack. + * + *

    This method performs the following operations:

    + *
      + *
    • Parses the index of the local variable to load from the instruction parameters.
    • + *
    • Retrieves the corresponding value from the local variable store of the current method frame.
    • + *
    • Pushes the retrieved value onto the operand stack for subsequent operations.
    • + *
    • Increments the program counter to point to the next instruction to be executed.
    • + *
    + * + *

    After execution, the program counter (PC) is updated to continue with the next instruction in the sequence, unless control flow changes.

    + * + * @param parts The array of instruction parameters, which typically includes the operation type and the index + * of the local variable to be loaded. The structure may vary based on the specific instruction. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after execution to point to the next instruction. + * @param operandStack The virtual machine's operand stack manager, responsible for pushing, popping, and peeking values. + * @param localVariableStore The local variable store, from which values are loaded based on the provided index. + * This store manages method-local variables. + * @param callStack The virtual machine's call stack, which tracks method calls and returns. It is used to access + * the correct frame's local variable store in this case. + * @return The updated program counter-value, which is typically incremented by 1 unless control flow is modified by other instructions. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the index of the local variable to be loaded + int index = Integer.parseInt(parts[1]); + + // Retrieve the value from the local variable store of the current method frame + long value = (long) callStack.peekFrame().getLocalVariableStore().getVariable(index); + + // Push the loaded value onto the operand stack for subsequent operations + operandStack.push(value); + + // Return the updated program counter to continue to the next instruction + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/memory/long64/LStoreCommand.java b/src/main/java/org/jcnc/snow/vm/commands/memory/long64/LStoreCommand.java new file mode 100644 index 0000000..f1b5e3f --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/memory/long64/LStoreCommand.java @@ -0,0 +1,66 @@ +package org.jcnc.snow.vm.commands.memory.long64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The LStoreCommand class implements the {@link Command} interface and represents a store instruction in the virtual machine. + * This class is used to store the value from the top of the virtual machine stack into the local variable store, typically used to save computed results into local variables. + * + *

    Specific behavior:

    + *
      + *
    • Pops a value from the virtual machine operand stack.
    • + *
    • Stores that value into the specified index location in the local variable store of the current frame on the call stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class LStoreCommand implements Command { + /** + * Default constructor for creating an instance of LStoreCommand. + * This constructor is empty as no specific initialization is required. + */ + public LStoreCommand() { + // Empty constructor + } + + /** + * Executes the store instruction. + * + *

    This method retrieves the value from the operand stack, pops the value, and stores it in the local variable store + * of the current method frame, based on the specified index. The program counter (PC) is then updated to point to the next instruction.

    + * + *

    The method performs the following actions:

    + *
      + *
    • Parses the index of the local variable where the value will be stored.
    • + *
    • Pops the top value from the operand stack.
    • + *
    • Stores the popped value into the specified local variable index in the current method frame's local variable store.
    • + *
    • Returns the next program counter-value to continue execution of the following instruction.
    • + *
    + * + * @param parts The array of instruction parameters, typically consisting of the operation type and related arguments, + * such as the index for the local variable. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack, used to store and manipulate values during instruction execution. + * @param localVariableStore The local variable store, used for managing method-local variables. + * @param callStack The virtual machine's call stack, which tracks the method invocation hierarchy. + * Used in conjunction with local variable store for method-local variable management. + * @return The updated program counter-value, typically the current PC value incremented by 1, unless modified by control flow instructions. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the index for the local variable where the value will be stored + int index = Integer.parseInt(parts[1]); + + // Pop the value from the operand stack + long value = (long) operandStack.pop(); + + // Store the value into the local variable store of the current method frame + callStack.peekFrame().getLocalVariableStore().setVariable(index, value); + + // Return the updated program counter, which moves to the next instruction + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/memory/short16/SLoadCommand.java b/src/main/java/org/jcnc/snow/vm/commands/memory/short16/SLoadCommand.java new file mode 100644 index 0000000..6379837 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/memory/short16/SLoadCommand.java @@ -0,0 +1,66 @@ +package org.jcnc.snow.vm.commands.memory.short16; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The SLoadCommand class implements the {@link Command} interface and represents a load instruction in the virtual machine. + * This class is used to load a value from the local variable store of the current method frame and push it onto the virtual machine stack. + * + *

    Specific behavior:

    + *
      + *
    • Retrieves a value from the specified index in the local variable store of the current frame.
    • + *
    • Pushes that value onto the virtual machine operand stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class SLoadCommand implements Command { + /** + * Default constructor for creating an instance of SLoadCommand. + * This constructor is empty as no specific initialization is required. + */ + public SLoadCommand() { + // Empty constructor + } + + /** + * Executes the load instruction to retrieve a value from the local variable store and push it onto the operand stack. + * + *

    This method performs the following operations:

    + *
      + *
    • Parses the index of the local variable to load from the instruction parameters.
    • + *
    • Retrieves the corresponding value from the local variable store of the current method frame.
    • + *
    • Pushes the retrieved value onto the operand stack for subsequent operations.
    • + *
    • Increments the program counter to point to the next instruction to be executed.
    • + *
    + * + *

    After execution, the program counter (PC) is updated to continue with the next instruction in the sequence, unless control flow changes.

    + * + * @param parts The array of instruction parameters, which typically includes the operation type and the index + * of the local variable to be loaded. The structure may vary based on the specific instruction. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after execution to point to the next instruction. + * @param operandStack The virtual machine's operand stack manager, responsible for pushing, popping, and peeking values. + * @param localVariableStore The local variable store, from which values are loaded based on the provided index. + * This store manages method-local variables. + * @param callStack The virtual machine's call stack, which tracks method calls and returns. It is used to access + * the correct frame's local variable store in this case. + * @return The updated program counter-value, which is typically incremented by 1 unless control flow is modified by other instructions. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the index of the local variable to be loaded + int index = Integer.parseInt(parts[1]); + + // Retrieve the value from the local variable store of the current method frame + short value = (short) callStack.peekFrame().getLocalVariableStore().getVariable(index); + + // Push the loaded value onto the operand stack for subsequent operations + operandStack.push(value); + + // Return the updated program counter to continue to the next instruction + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/memory/short16/SStoreCommand.java b/src/main/java/org/jcnc/snow/vm/commands/memory/short16/SStoreCommand.java new file mode 100644 index 0000000..fb5d79c --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/memory/short16/SStoreCommand.java @@ -0,0 +1,66 @@ +package org.jcnc.snow.vm.commands.memory.short16; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The SStoreCommand class implements the {@link Command} interface and represents a store instruction in the virtual machine. + * This class is used to store the value from the top of the virtual machine stack into the local variable store, typically used to save computed results into local variables. + * + *

    Specific behavior:

    + *
      + *
    • Pops a value from the virtual machine operand stack.
    • + *
    • Stores that value into the specified index location in the local variable store of the current frame on the call stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class SStoreCommand implements Command { + /** + * Default constructor for creating an instance of SStoreCommand. + * This constructor is empty as no specific initialization is required. + */ + public SStoreCommand() { + // Empty constructor + } + + /** + * Executes the store instruction. + * + *

    This method retrieves the value from the operand stack, pops the value, and stores it in the local variable store + * of the current method frame, based on the specified index. The program counter (PC) is then updated to point to the next instruction.

    + * + *

    The method performs the following actions:

    + *
      + *
    • Parses the index of the local variable where the value will be stored.
    • + *
    • Pops the top value from the operand stack.
    • + *
    • Stores the popped value into the specified local variable index in the current method frame's local variable store.
    • + *
    • Returns the next program counter-value to continue execution of the following instruction.
    • + *
    + * + * @param parts The array of instruction parameters, typically consisting of the operation type and related arguments, + * such as the index for the local variable. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack, used to store and manipulate values during instruction execution. + * @param localVariableStore The local variable store, used for managing method-local variables. + * @param callStack The virtual machine's call stack, which tracks the method invocation hierarchy. + * Used in conjunction with local variable store for method-local variable management. + * @return The updated program counter-value, typically the current PC value incremented by 1, unless modified by control flow instructions. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Parse the index for the local variable where the value will be stored + int index = Integer.parseInt(parts[1]); + + // Pop the value from the operand stack + short value = (short) operandStack.pop(); + + // Store the value into the local variable store of the current method frame + callStack.peekFrame().getLocalVariableStore().setVariable(index, value); + + // Return the updated program counter, which moves to the next instruction + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/stack/all/DupCommand.java b/src/main/java/org/jcnc/snow/vm/commands/stack/all/DupCommand.java new file mode 100644 index 0000000..8d0d1f8 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/stack/all/DupCommand.java @@ -0,0 +1,67 @@ +package org.jcnc.snow.vm.commands.stack.all; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The DupCommand class implements the {@link Command} interface and represents the DUP instruction in the virtual machine. + * This class is used to duplicate the top value of the stack, copying it and pushing it back onto the stack. + * + *

    Specific behavior:

    + *
      + *
    • Duplicates the top value of the stack and pushes it back onto the stack.
    • + *
    • Returns the updated program counter value, indicating the continuation of the next instruction.
    • + *
    + */ +public class DupCommand implements Command { + /** + * Default constructor for creating an instance of DupCommand. + * This constructor is empty as no specific initialization is required. + */ + public DupCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter value, typically the current program counter value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Retrieve the top value from the stack + Object topValue = operandStack.peek(); + + // Push the duplicated value back onto the stack + operandStack.push(topValue); + + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/stack/all/PopCommand.java b/src/main/java/org/jcnc/snow/vm/commands/stack/all/PopCommand.java new file mode 100644 index 0000000..fe00f9d --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/stack/all/PopCommand.java @@ -0,0 +1,64 @@ +package org.jcnc.snow.vm.commands.stack.all; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The PopCommand class implements the {@link Command} interface and represents a stack POP instruction in the virtual machine. + * This class is used to pop an element from the virtual machine stack, typically removing the topmost data. + * + *

    Specific behavior:

    + *
      + *
    • Pops an element from the virtual machine stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class PopCommand implements Command { + /** + * Default constructor for creating an instance of PopCommand. + * This constructor is empty as no specific initialization is required. + */ + public PopCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter-value, typically the current program counter-value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Pop an element from the stack + operandStack.pop(); + + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/stack/all/SwapCommand.java b/src/main/java/org/jcnc/snow/vm/commands/stack/all/SwapCommand.java new file mode 100644 index 0000000..1f27318 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/stack/all/SwapCommand.java @@ -0,0 +1,74 @@ +package org.jcnc.snow.vm.commands.stack.all; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The SwapCommand class implements the {@link Command} interface and represents the SWAP instruction in the virtual machine. + * This class is used to swap the top two values on the stack, rearranging the two most recent values. + * + *

    Specific behavior:

    + *
      + *
    • Swaps the two top values of the stack.
    • + *
    • Returns the updated program counter value, indicating the continuation of the next instruction.
    • + *
    + */ +public class SwapCommand implements Command { + /** + * Default constructor for creating an instance of SwapCommand. + * This constructor is empty as no specific initialization is required. + */ + public SwapCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter value, typically the current program counter value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Ensure that the stack has at least two elements to swap + if (operandStack.size() < 2) { + throw new IllegalStateException("Insufficient elements on stack for SWAP operation"); + } + + // Pop the two topmost values from the stack + Object firstValue = operandStack.pop(); + Object secondValue = operandStack.pop(); + + // Push them back in reversed order + operandStack.push(firstValue); + operandStack.push(secondValue); + + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/stack/byte8/BPushCommand.java b/src/main/java/org/jcnc/snow/vm/commands/stack/byte8/BPushCommand.java new file mode 100644 index 0000000..6953ff8 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/stack/byte8/BPushCommand.java @@ -0,0 +1,67 @@ +package org.jcnc.snow.vm.commands.stack.byte8; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The BPushCommand class implements the {@link Command} interface and represents a stack PUSH instruction in the virtual machine. + * This class is used to push a byte8 value onto the virtual machine stack, typically for later operations that require this data. + * + *

    Specific behavior:

    + *
      + *
    • Pushes the provided byte8 value onto the virtual machine stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class BPushCommand implements Command { + /** + * Default constructor for creating an instance of BPushCommand. + * This constructor is empty as no specific initialization is required. + */ + public BPushCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter-value, typically the current program counter-value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Retrieve the byte8 value from the command parameters to be pushed onto the stack + byte value = Byte.parseByte(parts[1]); + + // Push the byte8 value onto the stack + operandStack.push(value); + + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/stack/double64/DPushCommand.java b/src/main/java/org/jcnc/snow/vm/commands/stack/double64/DPushCommand.java new file mode 100644 index 0000000..5245d08 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/stack/double64/DPushCommand.java @@ -0,0 +1,67 @@ +package org.jcnc.snow.vm.commands.stack.double64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The DPushCommand class implements the {@link Command} interface and represents a stack PUSH instruction in the virtual machine. + * This class is used to push a double64 value onto the virtual machine stack, typically for subsequent operations that require this data. + * + *

    Specific behavior:

    + *
      + *
    • Pushes the provided double64 value onto the virtual machine stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class DPushCommand implements Command { + /** + * Default constructor for creating an instance of DPushCommand. + * This constructor is empty as no specific initialization is required. + */ + public DPushCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter-value, typically the current program counter-value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Retrieve the double64 value from the command parameters to be pushed onto the stack + double value = Double.parseDouble(parts[1]); + + // Push the double64 value onto the stack + operandStack.push(value); + + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/stack/float32/FPushCommand.java b/src/main/java/org/jcnc/snow/vm/commands/stack/float32/FPushCommand.java new file mode 100644 index 0000000..da6da33 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/stack/float32/FPushCommand.java @@ -0,0 +1,67 @@ +package org.jcnc.snow.vm.commands.stack.float32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The FPushCommand class implements the {@link Command} interface and represents a stack PUSH instruction in the virtual machine. + * This class is used to push a float32 value onto the virtual machine stack, typically for subsequent operations that require this data. + * + *

    Specific behavior:

    + *
      + *
    • Pushes the provided float32 value onto the virtual machine stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class FPushCommand implements Command { + /** + * Default constructor for creating an instance of FPushCommand. + * This constructor is empty as no specific initialization is required. + */ + public FPushCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter-value, typically the current program counter-value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Retrieve the float32 value from the command parameters to be pushed onto the stack + float value = Float.parseFloat(parts[1]); + + // Push the float32 value onto the stack + operandStack.push(value); + + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/stack/int32/IPushCommand.java b/src/main/java/org/jcnc/snow/vm/commands/stack/int32/IPushCommand.java new file mode 100644 index 0000000..5515e1f --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/stack/int32/IPushCommand.java @@ -0,0 +1,67 @@ +package org.jcnc.snow.vm.commands.stack.int32; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The IPushCommand class implements the {@link Command} interface and represents a stack PUSH instruction in the virtual machine. + * This class is used to push an int32 value onto the virtual machine stack, typically for later operations that require this data. + * + *

    Specific behavior:

    + *
      + *
    • Pushes the provided int32 value onto the virtual machine stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class IPushCommand implements Command { + /** + * Default constructor for creating an instance of IPushCommand. + * This constructor is empty as no specific initialization is required. + */ + public IPushCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter-value, typically the current program counter-value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Retrieve the int32 value from the command parameters to be pushed onto the stack + int value = Integer.parseInt(parts[1]); + + // Push the int32 value onto the stack + operandStack.push(value); + + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/stack/long64/LPushCommand.java b/src/main/java/org/jcnc/snow/vm/commands/stack/long64/LPushCommand.java new file mode 100644 index 0000000..d00b9c7 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/stack/long64/LPushCommand.java @@ -0,0 +1,67 @@ +package org.jcnc.snow.vm.commands.stack.long64; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The LPushCommand class implements the {@link Command} interface and represents a stack PUSH instruction in the virtual machine. + * This class is used to push a long64 value onto the virtual machine stack, typically for later operations that require this data. + * + *

    Specific behavior:

    + *
      + *
    • Pushes the provided long64 value onto the virtual machine stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class LPushCommand implements Command { + /** + * Default constructor for creating an instance of LPushCommand. + * This constructor is empty as no specific initialization is required. + */ + public LPushCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter-value, typically the current program counter-value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Retrieve the long64 value from the command parameters to be pushed onto the stack + long value = Long.parseLong(parts[1]); + + // Push the long64 value onto the stack + operandStack.push(value); + + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/stack/short16/SPushCommand.java b/src/main/java/org/jcnc/snow/vm/commands/stack/short16/SPushCommand.java new file mode 100644 index 0000000..b61fbe2 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/stack/short16/SPushCommand.java @@ -0,0 +1,67 @@ +package org.jcnc.snow.vm.commands.stack.short16; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The SPushCommand class implements the {@link Command} interface and represents a stack PUSH instruction in the virtual machine. + * This class is used to push a short16 value onto the virtual machine stack, typically for later operations that require this data. + * + *

    Specific behavior:

    + *
      + *
    • Pushes the provided short16 value onto the virtual machine stack.
    • + *
    • Returns the updated program counter-value, indicating the continuation of the next instruction.
    • + *
    + */ +public class SPushCommand implements Command { + /** + * Default constructor for creating an instance of SPushCommand. + * This constructor is empty as no specific initialization is required. + */ + public SPushCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter-value, typically the current program counter-value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Retrieve the short16 value from the command parameters to be pushed onto the stack + short value = Short.parseShort(parts[1]); + + // Push the short16 value onto the stack + operandStack.push(value); + + return currentPC + 1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/commands/vm/HaltCommand.java b/src/main/java/org/jcnc/snow/vm/commands/vm/HaltCommand.java new file mode 100644 index 0000000..19e4d5d --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/vm/HaltCommand.java @@ -0,0 +1,67 @@ +package org.jcnc.snow.vm.commands.vm; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.utils.LoggingUtils; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The HaltCommand class implements the {@link Command} interface and represents the stop instruction in the virtual machine. + * This class is used to terminate the execution of the virtual machine. When this command is executed, the virtual machine + * outputs a message and stops executing subsequent instructions. + * + *

    Specific behavior:

    + *
      + *
    • Outputs the message "Process has ended," indicating that the program execution has been terminated.
    • + *
    • Returns -1 to signal the virtual machine to stop execution.
    • + *
    + */ +public class HaltCommand implements Command { + /** + * Default constructor for creating an instance of HaltCommand. + * This constructor is empty as no specific initialization is required. + */ + public HaltCommand() { + // Empty constructor + } + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter-value, typically the current program counter-value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + @Override + public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { + // Output the termination message + LoggingUtils.logInfo("Process has ended", "\n"); + + // Return -1 to indicate the program termination, and the virtual machine will not continue executing subsequent instructions + return -1; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/engine/VMCommandExecutor.java b/src/main/java/org/jcnc/snow/vm/engine/VMCommandExecutor.java new file mode 100644 index 0000000..3c3a830 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/engine/VMCommandExecutor.java @@ -0,0 +1,44 @@ +package org.jcnc.snow.vm.engine; + + +import org.jcnc.snow.vm.utils.LoggingUtils; + +import java.util.List; + +/** + * Virtual Machine Command Executor (VMCommandExecutor) + *

    + * This class is responsible for executing a set of instructions for the virtual machine. It calls the `execute` method of + * {@link VirtualMachineEngine} to execute the provided list of instructions. If any errors occur during execution, the + * error messages are logged using {@link LoggingUtils}. + *

    + */ +public class VMCommandExecutor { + /** + * Default constructor for creating an instance of VMCommandExecutor. + * This constructor is empty as no specific initialization is required. + */ + public VMCommandExecutor() { + // Empty constructor + } + + /** + * Executes the virtual machine instructions. + *

    + * This method takes a virtual machine instance and a list of instructions, and executes them while controlling the + * virtual machine's operation. If any exceptions occur during execution, they are caught and logged via {@link LoggingUtils}. + *

    + * + * @param virtualMachineEngine The virtual machine instance used to execute the instructions. + * @param instructions The list of instructions to be executed. + */ + public static void executeInstructions(VirtualMachineEngine virtualMachineEngine, List instructions) { + try { + // Call the virtual machine's execute method to run the instruction list + virtualMachineEngine.execute(instructions); + } catch (Exception e) { + // If an exception occurs, log the error message + LoggingUtils.logError("Error while executing instructions: " + e.getMessage()); + } + } +} diff --git a/src/main/java/org/jcnc/snow/vm/engine/VMMode.java b/src/main/java/org/jcnc/snow/vm/engine/VMMode.java new file mode 100644 index 0000000..a378ef7 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/engine/VMMode.java @@ -0,0 +1,19 @@ +package org.jcnc.snow.vm.engine; + +/** + * The VMMode enum defines the different operational modes of the virtual machine. + *

    This class is used to distinguish the behavior of the virtual machine in different states, with each mode corresponding to a different operational logic.

    + */ +public enum VMMode { + /** + * Run Mode: The virtual machine executes instructions in the normal execution flow. + *

    In this mode, the virtual machine processes instructions and performs related calculations.

    + */ + RUN, + + /** + * Debug Mode: The virtual machine outputs debug information during execution. + *

    This mode is used for debugging the virtual machine, allowing developers to view detailed information such as the execution state, local variables, and more.

    + */ + DEBUG, +} diff --git a/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java b/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java new file mode 100644 index 0000000..b3a6680 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java @@ -0,0 +1,1770 @@ +package org.jcnc.snow.vm.engine; + +import org.jcnc.snow.vm.commands.arithmetic.byte8.*; +import org.jcnc.snow.vm.commands.arithmetic.conversion.*; +import org.jcnc.snow.vm.commands.arithmetic.double64.*; +import org.jcnc.snow.vm.commands.arithmetic.float32.*; +import org.jcnc.snow.vm.commands.arithmetic.int32.*; +import org.jcnc.snow.vm.commands.arithmetic.long64.*; +import org.jcnc.snow.vm.commands.arithmetic.short16.*; +import org.jcnc.snow.vm.commands.bitwise.int32.IAndCommand; +import org.jcnc.snow.vm.commands.bitwise.int32.IOrCommand; +import org.jcnc.snow.vm.commands.bitwise.int32.IXorCommand; +import org.jcnc.snow.vm.commands.bitwise.long64.LAndCommand; +import org.jcnc.snow.vm.commands.bitwise.long64.LOrCommand; +import org.jcnc.snow.vm.commands.bitwise.long64.LXorCommand; +import org.jcnc.snow.vm.commands.control.all.JumpCommand; +import org.jcnc.snow.vm.commands.control.int32.*; +import org.jcnc.snow.vm.commands.function.CallCommand; +import org.jcnc.snow.vm.commands.function.RetCommand; +import org.jcnc.snow.vm.commands.memory.all.MovCommand; +import org.jcnc.snow.vm.commands.memory.byte8.BLoadCommand; +import org.jcnc.snow.vm.commands.memory.byte8.BStoreCommand; +import org.jcnc.snow.vm.commands.memory.double64.DLoadCommand; +import org.jcnc.snow.vm.commands.memory.double64.DStoreCommand; +import org.jcnc.snow.vm.commands.memory.float32.FLoadCommand; +import org.jcnc.snow.vm.commands.memory.float32.FStoreCommand; +import org.jcnc.snow.vm.commands.memory.int32.ILoadCommand; +import org.jcnc.snow.vm.commands.memory.int32.IStoreCommand; +import org.jcnc.snow.vm.commands.memory.long64.LLoadCommand; +import org.jcnc.snow.vm.commands.memory.long64.LStoreCommand; +import org.jcnc.snow.vm.commands.memory.short16.SLoadCommand; +import org.jcnc.snow.vm.commands.memory.short16.SStoreCommand; +import org.jcnc.snow.vm.commands.stack.all.DupCommand; +import org.jcnc.snow.vm.commands.stack.all.PopCommand; +import org.jcnc.snow.vm.commands.stack.all.SwapCommand; +import org.jcnc.snow.vm.commands.stack.byte8.BPushCommand; +import org.jcnc.snow.vm.commands.stack.double64.DPushCommand; +import org.jcnc.snow.vm.commands.stack.float32.FPushCommand; +import org.jcnc.snow.vm.commands.stack.int32.IPushCommand; +import org.jcnc.snow.vm.commands.stack.long64.LPushCommand; +import org.jcnc.snow.vm.commands.stack.short16.SPushCommand; +import org.jcnc.snow.vm.commands.vm.HaltCommand; +import org.jcnc.snow.vm.module.LocalVariableStore; + +/** + * VMOpCode defines the compact instruction set for the virtual machine, organized into logical categories. + *

    Each opcode represents a specific operation executed by the virtual machine.

    + */ +public class VMOpCode { + // 1 Arithmetic Operations (1–80) + // 1.1 int32 (1-10) + /** + * I_ADD Opcode: Represents the int32 addition operation in the virtual machine. + *

    This opcode is implemented by the {@link IAddCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two int32 values from the operand stack (the first value popped is the second operand, and the second value popped is the first operand).
    2. + *
    3. Performs the addition operation on these two int32s.
    4. + *
    5. Pushes the result of the addition back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic int32 addition tasks.

    + */ + public static final int I_ADD = 1; + /** + * I_SUB Opcode: Represents the int32 subtraction operation in the virtual machine. + *

    This opcode is implemented by the {@link ISubCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two int32 values from the operand stack (the first value popped is the subtrahend, and the second value popped is the minuend).
    2. + *
    3. Performs the subtraction operation by subtracting the subtrahend from the minuend (i.e., minuend - subtrahend).
    4. + *
    5. Pushes the result of the subtraction back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic int32 subtraction tasks.

    + */ + public static final int I_SUB = 2; + /** + * I_MUL Opcode: Represents the int32 multiplication operation in the virtual machine. + *

    This opcode is implemented by the {@link IMulCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two int32 values from the operand stack (the first value popped is the second operand, and the second value popped is the first operand).
    2. + *
    3. Performs the multiplication operation on these two int32s (i.e., firstOperand * secondOperand).
    4. + *
    5. Pushes the result of the multiplication back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic int32 multiplication tasks.

    + */ + public static final int I_MUL = 3; + /** + * I_DIV Opcode: Represents the int32 division operation in the virtual machine. + *

    This opcode is implemented by the {@link IDivCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two int32 values from the operand stack (the first value popped is the divisor, and the second value popped is the dividend).
    2. + *
    3. Performs the division operation by dividing the dividend by the divisor (i.e., dividend / divisor).
    4. + *
    5. Checks for division by zero and throws an {@link ArithmeticException} if the divisor is zero, as this operation is undefined.
    6. + *
    7. Pushes the result of the division back onto the operand stack for later instructions to use.
    8. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic int32 division tasks.

    + */ + public static final int I_DIV = 4; + /** + * I_MOD Opcode: Represents the int32 modulus (remainder) operation in the virtual machine. + *

    This opcode is implemented by the {@link IModCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two int32 values from the operand stack (the first value popped is the divisor, and the second value popped is the dividend).
    2. + *
    3. Performs the modulus operation by calculating the remainder of the division of the dividend by the divisor (i.e., dividend % divisor).
    4. + *
    5. Checks for division by zero and throws an {@link ArithmeticException} if the divisor is zero, as this operation is undefined.
    6. + *
    7. Pushes the result of the modulus operation back onto the operand stack for later instructions to use.
    8. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic int32 modulus (remainder) tasks.

    + */ + public static final int I_MOD = 5; + /** + * I_INC Opcode: Represents the int32 increment operation for a local variable in the virtual machine. + *

    This opcode is implemented by the {@link IIncCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index of the local variable and the increment value from the instruction parameters.
    2. + *
    3. Gets the current value of the local variable at the specified index from the local variable store.
    4. + *
    5. Increments the local variable's value by the specified increment (i.e., + * localVariables[index] += increment).
    6. + *
    7. Updates the local variable store with the new incremented value.
    8. + *
    9. Returns the updated program counter (PC) value, typically incremented by 1, unless control flow changes.
    10. + *
    + * + *

    This opcode is particularly useful for optimizing scenarios where a local variable, such as a counter or loop index, is frequently incremented.

    + */ + public static final int I_INC = 6; + + /** + * I_NEG Opcode: Represents the int32 negation operation in the virtual machine. + *

    This opcode is implemented by the {@link INegCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top int32 value from the operand stack.
    2. + *
    3. Performs the negation of the popped value (i.e., -value).
    4. + *
    5. Pushes the negated result back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is typically used to negate an int32 value, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ + public static final int I_NEG = 7; + // 1.2 long64 (11-20) + /** + * L_ADD Opcode: Represents the long64 addition operation in the virtual machine. + *

    This opcode is implemented by the {@link LAddCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two long64 values from the operand stack (the first value popped is the second operand, and the second value popped is the first operand).
    2. + *
    3. Performs the addition operation on these two long64s.
    4. + *
    5. Pushes the result of the addition back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic long64 addition tasks.

    + */ + public static final int L_ADD = 11; + + /** + * L_SUB Opcode: Represents the long64 subtraction operation in the virtual machine. + *

    This opcode is implemented by the {@link LSubCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two long64 values from the operand stack (the first value popped is the subtrahend, and the second value popped is the minuend).
    2. + *
    3. Performs the subtraction operation by subtracting the subtrahend from the minuend (i.e., minuend - subtrahend).
    4. + *
    5. Pushes the result of the subtraction back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic long64 subtraction tasks.

    + */ + public static final int L_SUB = 12; + + /** + * L_MUL Opcode: Represents the long64 multiplication operation in the virtual machine. + *

    This opcode is implemented by the {@link LMulCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two long64 values from the operand stack (the first value popped is the second operand, and the second value popped is the first operand).
    2. + *
    3. Performs the multiplication operation on these two long64s (i.e., firstOperand * secondOperand).
    4. + *
    5. Pushes the result of the multiplication back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic long64 multiplication tasks.

    + */ + public static final int L_MUL = 13; + + /** + * L_DIV Opcode: Represents the long64 division operation in the virtual machine. + *

    This opcode is implemented by the {@link LDivCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two long64 values from the operand stack (the first value popped is the divisor, and the second value popped is the dividend).
    2. + *
    3. Performs the division operation by dividing the dividend by the divisor (i.e., dividend / divisor).
    4. + *
    5. Checks for division by zero and throws an {@link ArithmeticException} if the divisor is zero, as this operation is undefined.
    6. + *
    7. Pushes the result of the division back onto the operand stack for later instructions to use.
    8. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic long64 division tasks.

    + */ + public static final int L_DIV = 14; + + /** + * L_MOD Opcode: Represents the long64 modulus (remainder) operation in the virtual machine. + *

    This opcode is implemented by the {@link LModCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two long64 values from the operand stack (the first value popped is the divisor, and the second value popped is the dividend).
    2. + *
    3. Performs the modulus operation by calculating the remainder of the division of the dividend by the divisor (i.e., dividend % divisor).
    4. + *
    5. Checks for division by zero and throws an {@link ArithmeticException} if the divisor is zero, as this operation is undefined.
    6. + *
    7. Pushes the result of the modulus operation back onto the operand stack for later instructions to use.
    8. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic long64 modulus (remainder) tasks.

    + */ + public static final int L_MOD = 15; + + /** + * L_INC Opcode: Represents the long64 increment operation for a local variable in the virtual machine. + *

    This opcode is implemented by the {@link LIncCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index of the local variable and the increment value from the instruction parameters.
    2. + *
    3. Gets the current value of the local variable at the specified index from the local variable store.
    4. + *
    5. Increments the local variable's value by the specified increment (i.e., + * localVariables[index] += increment).
    6. + *
    7. Updates the local variable store with the new incremented value.
    8. + *
    9. Returns the updated program counter (PC) value, typically incremented by 1, unless control flow changes.
    10. + *
    + * + *

    This opcode is particularly useful for optimizing scenarios where a local `long64` variable, such as a counter or loop index, is frequently incremented.

    + */ + public static final int L_INC = 16; + + + /** + * L_NEG Opcode: Represents the long64 negation operation in the virtual machine. + *

    This opcode is implemented by the {@link LNegCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top long64 value from the operand stack.
    2. + *
    3. Performs the negation of the popped value (i.e., -value).
    4. + *
    5. Pushes the negated result back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is typically used to negate a long64 value, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ + public static final int L_NEG = 17; + + // 1.3 short16 (21-30) + /** + * S_ADD Opcode: Represents the short16 addition operation in the virtual machine. + *

    This opcode is implemented by the {@link SAddCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two short16 values from the operand stack (the first value popped is the second operand, and the second value popped is the first operand).
    2. + *
    3. Performs the addition operation on these two short16 values.
    4. + *
    5. Pushes the result of the addition back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic short16 addition tasks.

    + */ + public static final int S_ADD = 21; + + /** + * S_SUB Opcode: Represents the short16 subtraction operation in the virtual machine. + *

    This opcode is implemented by the {@link SSubCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two short16 values from the operand stack (the first value popped is the subtrahend, and the second value popped is the minuend).
    2. + *
    3. Performs the subtraction operation by subtracting the subtrahend from the minuend (i.e., minuend - subtrahend).
    4. + *
    5. Pushes the result of the subtraction back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic short16 subtraction tasks.

    + */ + public static final int S_SUB = 22; + + /** + * S_MUL Opcode: Represents the short16 multiplication operation in the virtual machine. + *

    This opcode is implemented by the {@link SMulCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two short16 values from the operand stack (the first value popped is the second operand, and the second value popped is the first operand).
    2. + *
    3. Performs the multiplication operation on these two short16 values (i.e., firstOperand * secondOperand).
    4. + *
    5. Pushes the result of the multiplication back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic short16 multiplication tasks.

    + */ + public static final int S_MUL = 23; + + /** + * S_DIV Opcode: Represents the short16 division operation in the virtual machine. + *

    This opcode is implemented by the {@link SDivCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two short16 values from the operand stack (the first value popped is the divisor, and the second value popped is the dividend).
    2. + *
    3. Performs the division operation by dividing the dividend by the divisor (i.e., dividend / divisor).
    4. + *
    5. Checks for division by zero and throws an {@link ArithmeticException} if the divisor is zero, as this operation is undefined.
    6. + *
    7. Pushes the result of the division back onto the operand stack for later instructions to use.
    8. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic short16 division tasks.

    + */ + public static final int S_DIV = 24; + + /** + * S_MOD Opcode: Represents the short16 modulus (remainder) operation in the virtual machine. + *

    This opcode is implemented by the {@link SModCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two short16 values from the operand stack (the first value popped is the divisor, and the second value popped is the dividend).
    2. + *
    3. Performs the modulus operation by calculating the remainder of the division of the dividend by the divisor (i.e., dividend % divisor).
    4. + *
    5. Checks for division by zero and throws an {@link ArithmeticException} if the divisor is zero, as this operation is undefined.
    6. + *
    7. Pushes the result of the modulus operation back onto the operand stack for later instructions to use.
    8. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic short16 modulus (remainder) tasks.

    + */ + public static final int S_MOD = 25; + + /** + * S_INC Opcode: Represents the short16 increment operation in the virtual machine. + *

    This opcode is implemented by the {@link SIncCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two values from the operand stack: the first value is the index of the short16 local variable to be incremented, + * and the second value is the increment value to be added to the local variable.
    2. + *
    3. Increments the value of the local variable at the given index by the specified increment value (i.e., + * localVariables[index] += increment).
    4. + *
    5. Does not affect the operand stack as the increment operation directly modifies the local variable's value.
    6. + *
    7. Executes quickly as the operation is an efficient increment of a local variable.
    8. + *
    + * + *

    This opcode is useful for scenarios where a local variable needs to be incremented, such as counters within loops, + * or for optimizing the modification of variables in tight loops.

    + */ + public static final int S_INC = 26; + + /** + * S_NEG Opcode: Represents the short16 negation operation in the virtual machine. + *

    This opcode is implemented by the {@link SNegCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top short16 value from the operand stack.
    2. + *
    3. Performs the negation of the popped value (i.e., -value).
    4. + *
    5. Pushes the negated result back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is typically used to negate a short16 value, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ + public static final int S_NEG = 27; + + // 1.4 byte8 (31-40) + /** + * B_ADD Opcode: Represents the byte8 addition operation in the virtual machine. + *

    This opcode is implemented by the {@link BAddCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two byte8 values from the operand stack (the first value popped is the second operand, and the second value popped is the first operand).
    2. + *
    3. Performs the addition operation on these two byte8 values.
    4. + *
    5. Pushes the result of the addition back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic byte8 addition tasks.

    + */ + public static final int B_ADD = 31; + + /** + * B_SUB Opcode: Represents the byte8 subtraction operation in the virtual machine. + *

    This opcode is implemented by the {@link BSubCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two byte8 values from the operand stack (the first value popped is the subtrahend, and the second value popped is the minuend).
    2. + *
    3. Performs the subtraction operation by subtracting the subtrahend from the minuend (i.e., minuend - subtrahend).
    4. + *
    5. Pushes the result of the subtraction back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic byte8 subtraction tasks.

    + */ + public static final int B_SUB = 32; + + /** + * B_MUL Opcode: Represents the byte8 multiplication operation in the virtual machine. + *

    This opcode is implemented by the {@link BMulCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two byte8 values from the operand stack (the first value popped is the second operand, and the second value popped is the first operand).
    2. + *
    3. Performs the multiplication operation on these two byte8 values (i.e., firstOperand * secondOperand).
    4. + *
    5. Pushes the result of the multiplication back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic byte8 multiplication tasks.

    + */ + public static final int B_MUL = 33; + + /** + * B_DIV Opcode: Represents the byte8 division operation in the virtual machine. + *

    This opcode is implemented by the {@link BDivCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two byte8 values from the operand stack (the first value popped is the divisor, and the second value popped is the dividend).
    2. + *
    3. Performs the division operation by dividing the dividend by the divisor (i.e., dividend / divisor).
    4. + *
    5. Checks for division by zero and throws an {@link ArithmeticException} if the divisor is zero, as this operation is undefined.
    6. + *
    7. Pushes the result of the division back onto the operand stack for later instructions to use.
    8. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic byte8 division tasks.

    + */ + public static final int B_DIV = 34; + + /** + * B_MOD Opcode: Represents the byte8 modulus (remainder) operation in the virtual machine. + *

    This opcode is implemented by the {@link BModCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two byte8 values from the operand stack (the first value popped is the divisor, and the second value popped is the dividend).
    2. + *
    3. Performs the modulus operation by calculating the remainder of the division of the dividend by the divisor (i.e., dividend % divisor).
    4. + *
    5. Checks for division by zero and throws an {@link ArithmeticException} if the divisor is zero, as this operation is undefined.
    6. + *
    7. Pushes the result of the modulus operation back onto the operand stack for later instructions to use.
    8. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic byte8 modulus (remainder) tasks.

    + */ + public static final int B_MOD = 35; + + /** + * B_INC Opcode: Represents the byte8 increment operation for a local variable in the virtual machine. + *

    This opcode is implemented by the {@link BIncCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index of the local variable and the increment value from the instruction parameters.
    2. + *
    3. Gets the current value of the local variable at the specified index from the local variable store.
    4. + *
    5. Increments the local variable's value by the specified increment (i.e., + * localVariables[index] += increment).
    6. + *
    7. Updates the local variable store with the new incremented value.
    8. + *
    9. Returns the updated program counter (PC) value, typically incremented by 1, unless control flow changes.
    10. + *
    + * + *

    This opcode is particularly useful for optimizing scenarios where a local `byte8` variable, such as a counter or loop index, is frequently incremented.

    + */ + public static final int B_INC = 36; + + + /** + * B_NEG Opcode: Represents the byte8 negation operation in the virtual machine. + *

    This opcode is implemented by the {@link BNegCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top byte8 value from the operand stack.
    2. + *
    3. Performs the negation of the popped value (i.e., -value).
    4. + *
    5. Pushes the negated result back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is typically used to negate a byte8 value, making it a fundamental operation for arithmetic logic within the virtual machine.

    + */ + public static final int B_NEG = 37; + + // 1.5 double64 (41-50) + /** + * D_ADD Opcode: Represents the double64 precision floating-point addition operation in the virtual machine. + *

    This opcode is implemented by the {@link DAddCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two double64 values from the operand stack (the first value popped is the second operand, and the second value popped is the first operand).
    2. + *
    3. Performs the double64 precision floating-point addition operation on these two operands.
    4. + *
    5. Pushes the result of the addition back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic double64 precision floating-point addition tasks.

    + */ + public static final int D_ADD = 41; + + /** + * D_SUB Opcode: Represents the double64 precision floating-point subtraction operation in the virtual machine. + *

    This opcode is implemented by the {@link DSubCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two double64 values from the operand stack (the first value popped is the subtrahend, and the second value popped is the minuend).
    2. + *
    3. Performs the double64 precision floating-point subtraction operation by subtracting the subtrahend from the minuend (i.e., minuend - subtrahend).
    4. + *
    5. Pushes the result of the subtraction back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic double64 precision floating-point subtraction tasks.

    + */ + public static final int D_SUB = 42; + + /** + * D_MUL Opcode: Represents the double64 precision floating-point multiplication operation in the virtual machine. + *

    This opcode is implemented by the {@link DMulCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two double64 values from the operand stack (the first value popped is the second operand, and the second value popped is the first operand).
    2. + *
    3. Performs the double64 precision floating-point multiplication operation on these two operands (i.e., firstOperand * secondOperand).
    4. + *
    5. Pushes the result of the multiplication back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic double64 precision floating-point multiplication tasks.

    + */ + public static final int D_MUL = 43; + + /** + * D_DIV Opcode: Represents the double64 precision floating-point division operation in the virtual machine. + *

    This opcode is implemented by the {@link DDivCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two double64 values from the operand stack (the first value popped is the divisor, and the second value popped is the dividend).
    2. + *
    3. Performs the double64 precision floating-point division operation by dividing the dividend by the divisor (i.e., dividend / divisor).
    4. + *
    5. Checks for division by zero and throws an {@link ArithmeticException} if the divisor is zero, as this operation is undefined.
    6. + *
    7. Pushes the result of the division back onto the operand stack for later instructions to use.
    8. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic double64 precision floating-point division tasks.

    + */ + public static final int D_DIV = 44; + + /** + * D_MOD Opcode: Represents the double64 precision floating-point modulus (remainder) operation in the virtual machine. + *

    This opcode is implemented by the {@link DModCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two double64 values from the operand stack (the first value popped is the divisor, and the second value popped is the dividend).
    2. + *
    3. Performs the modulus operation by calculating the remainder of the division of the dividend by the divisor (i.e., dividend % divisor).
    4. + *
    5. Checks for division by zero and throws an {@link ArithmeticException} if the divisor is zero, as this operation is undefined.
    6. + *
    7. Pushes the result of the modulus operation back onto the operand stack for later instructions to use.
    8. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic double64 precision floating-point modulus (remainder) tasks.

    + */ + public static final int D_MOD = 45; + + /** + * D_INC Opcode: Represents the double64 increment operation for a local variable in the virtual machine. + *

    This opcode is implemented by the {@link DIncCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index of the local variable and the increment value from the instruction parameters.
    2. + *
    3. Gets the current value of the local variable at the specified index from the local variable store.
    4. + *
    5. Increments the local variable's value by the specified increment (i.e., + * localVariables[index] += increment).
    6. + *
    7. Updates the local variable store with the new incremented value.
    8. + *
    9. Returns the updated program counter (PC) value, typically incremented by 1, unless control flow changes.
    10. + *
    + * + *

    This opcode is particularly useful for optimizing scenarios where a local `double64` variable, such as a counter or loop index, is frequently incremented.

    + */ + public static final int D_INC = 46; + + /** + * D_NEG Opcode: Represents the double64 precision floating-point negation operation in the virtual machine. + *

    This opcode is implemented by the {@link DNegCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top double64 value from the operand stack.
    2. + *
    3. Performs the negation of the popped value (i.e., -value).
    4. + *
    5. Pushes the negated result back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is typically used to negate a double64 precision floating-point value, making it a fundamental operation for double64 precision arithmetic logic within the virtual machine.

    + */ + public static final int D_NEG = 47; + + // 1.6 float32 (51-60) + /** + * F_ADD Opcode: Represents the float32 addition operation in the virtual machine. + *

    This opcode is implemented by the {@link FAddCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two float32 values from the operand stack (the first value popped is the second operand, and the second value popped is the first operand).
    2. + *
    3. Performs the float32 addition operation on these two operands.
    4. + *
    5. Pushes the result of the addition back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic float32 addition tasks.

    + */ + public static final int F_ADD = 51; + /** + * F_SUB Opcode: Represents the float32 subtraction operation in the virtual machine. + *

    This opcode is implemented by the {@link FSubCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two float32 values from the operand stack (the first value popped is the subtrahend, and the second value popped is the minuend).
    2. + *
    3. Performs the float32 subtraction operation by subtracting the subtrahend from the minuend (i.e., minuend - subtrahend).
    4. + *
    5. Pushes the result of the subtraction back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic float32 subtraction tasks.

    + */ + public static final int F_SUB = 52; + /** + * F_MUL Opcode: Represents the float32 multiplication operation in the virtual machine. + *

    This opcode is implemented by the {@link FMulCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two float32 values from the operand stack (the first value popped is the second operand, and the second value popped is the first operand).
    2. + *
    3. Performs the float32 multiplication operation on these two operands (i.e., firstOperand * secondOperand).
    4. + *
    5. Pushes the result of the multiplication back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic float32 multiplication tasks.

    + */ + public static final int F_MUL = 53; + /** + * F_DIV Opcode: Represents the float32 division operation in the virtual machine. + *

    This opcode is implemented by the {@link FDivCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two float32 values from the operand stack (the first value popped is the divisor, and the second value popped is the dividend).
    2. + *
    3. Performs the float32 division operation by dividing the dividend by the divisor (i.e., dividend / divisor).
    4. + *
    5. Checks for division by zero and throws an {@link ArithmeticException} if the divisor is zero, as this operation is undefined.
    6. + *
    7. Pushes the result of the division back onto the operand stack for later instructions to use.
    8. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic float32 division tasks.

    + */ + public static final int F_DIV = 54; + /** + * F_MOD Opcode: Represents the float32 modulus (remainder) operation in the virtual machine. + *

    This opcode is implemented by the {@link FModCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two float32 values from the operand stack (the first value popped is the divisor, and the second value popped is the dividend).
    2. + *
    3. Performs the modulus operation by calculating the remainder of the division of the dividend by the divisor (i.e., dividend % divisor).
    4. + *
    5. Checks for division by zero and throws an {@link ArithmeticException} if the divisor is zero, as this operation is undefined.
    6. + *
    7. Pushes the result of the modulus operation back onto the operand stack for later instructions to use.
    8. + *
    + * + *

    This opcode is a fundamental arithmetic operation within the virtual machine's instruction set, + * primarily used to handle basic float32 modulus (remainder) tasks.

    + */ + public static final int F_MOD = 55; + /** + * F_INC Opcode: Represents the float32 increment operation for a local variable in the virtual machine. + *

    This opcode is implemented by the {@link FIncCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index of the local variable and the increment value from the instruction parameters.
    2. + *
    3. Gets the current value of the local variable at the specified index from the local variable store.
    4. + *
    5. Increments the local variable's value by the specified increment (i.e., + * localVariables[index] += increment).
    6. + *
    7. Updates the local variable store with the new incremented value.
    8. + *
    9. Returns the updated program counter (PC) value, typically incremented by 1, unless control flow changes.
    10. + *
    + * + *

    This opcode is particularly useful for optimizing scenarios where a local `float32` variable, such as a counter or loop index, is frequently incremented.

    + */ + public static final int F_INC = 56; + + /** + * F_NEG Opcode: Represents the float32 negation operation in the virtual machine. + *

    This opcode is implemented by the {@link FNegCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top float32 value from the operand stack.
    2. + *
    3. Performs the negation of the popped value (i.e., -value).
    4. + *
    5. Pushes the negated result back onto the operand stack for later instructions to use.
    6. + *
    + * + *

    This opcode is typically used to negate a float32 value, making it a fundamental operation for float32 arithmetic logic within the virtual machine.

    + */ + public static final int F_NEG = 57; + + /** + * I2L Opcode: Represents the type conversion operation from int32 to long64 in the virtual machine. + *

    This opcode is implemented by the {@link I2LCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top int32 value from the operand stack.
    2. + *
    3. Convert the int32 value to a long64 value.
    4. + *
    5. Push the converted long64 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is commonly used to widen an int32 value to a long64 type to accommodate larger numeric ranges.

    + */ + public static final int I2L = 61; + + /** + * I2S Opcode: Represents the type conversion operation from int32 to short16 in the virtual machine. + *

    This opcode is implemented by the {@link I2SCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top int32 value from the operand stack.
    2. + *
    3. Convert the int32 value to a short16 value (this may involve truncation).
    4. + *
    5. Push the converted short16 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is typically used to narrow an int32 value to a short16 type when a smaller data representation is needed.

    + */ + public static final int I2S = 62; + + /** + * I2B Opcode: Represents the type conversion operation from int32 to byte8 in the virtual machine. + *

    This opcode is implemented by the {@link I2BCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top int32 value from the operand stack.
    2. + *
    3. Convert the int32 value to a byte8 value (this may involve truncation).
    4. + *
    5. Push the converted byte8 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to narrow an int32 value to a byte8 type, suitable when a smaller numeric type is required.

    + */ + public static final int I2B = 63; + + /** + * I2D Opcode: Represents the type conversion operation from int32 to double64 in the virtual machine. + *

    This opcode is implemented by the {@link I2DCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top int32 value from the operand stack.
    2. + *
    3. Convert the int32 value to a double64 value.
    4. + *
    5. Push the converted double64 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to widen an int32 value to a double64 type, providing high-precision floating-point calculations.

    + */ + public static final int I2D = 64; + + /** + * I2F Opcode: Represents the type conversion operation from int32 to float32 in the virtual machine. + *

    This opcode is implemented by the {@link I2FCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top int32 value from the operand stack.
    2. + *
    3. Convert the int32 value to a float32 value.
    4. + *
    5. Push the converted float32 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to convert an int32 value to a float32 type when floating-point arithmetic is required.

    + */ + public static final int I2F = 65; + + /** + * L2I Opcode: Represents the type conversion operation from long64 to int32 in the virtual machine. + *

    This opcode is implemented by the {@link L2ICommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top long64 value from the operand stack.
    2. + *
    3. Convert the long64 value to an int32 value (this may involve truncation).
    4. + *
    5. Push the converted int32 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is typically used to narrow a long64 value to an int32 type for further integer operations.

    + */ + public static final int L2I = 66; + + /** + * L2D Opcode: Represents the type conversion operation from long64 to double64 in the virtual machine. + *

    This opcode is implemented by the {@link L2DCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top long64 value from the operand stack.
    2. + *
    3. Convert the long64 value to a double64 value.
    4. + *
    5. Push the converted double64 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to widen a long64 value to a double64 type for high-precision floating-point computations.

    + */ + public static final int L2D = 67; + + /** + * L2F Opcode: Represents the type conversion operation from long64 to float32 in the virtual machine. + *

    This opcode is implemented by the {@link L2FCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top long64 value from the operand stack.
    2. + *
    3. Convert the long64 value to a float32 value.
    4. + *
    5. Push the converted float32 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to convert a long64 value to a float32 type, typically for floating-point arithmetic involving long values.

    + */ + public static final int L2F = 68; + + /** + * F2I Opcode: Represents the type conversion operation from float32 to int32 in the virtual machine. + *

    This opcode is implemented by the {@link F2ICommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top float32 value from the operand stack.
    2. + *
    3. Convert the float32 value to an int32 value (this may involve truncation).
    4. + *
    5. Push the converted int32 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to convert a float32 value to an int32 type for further integer-based operations or comparisons.

    + */ + public static final int F2I = 69; + + /** + * F2L Opcode: Represents the type conversion operation from float32 to long64 in the virtual machine. + *

    This opcode is implemented by the {@link F2LCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top float32 value from the operand stack.
    2. + *
    3. Convert the float32 value to a long64 value.
    4. + *
    5. Push the converted long64 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to widen a float32 value to a long64 type, which is useful when operations require a larger numeric range.

    + */ + public static final int F2L = 70; + + /** + * F2D Opcode: Represents the type conversion operation from float32 to double64 in the virtual machine. + *

    This opcode is implemented by the {@link F2DCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top float32 value from the operand stack.
    2. + *
    3. Convert the float32 value to a double64 value.
    4. + *
    5. Push the converted double64 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to promote a float32 value to a double64 type, thereby increasing precision for floating-point computations.

    + */ + public static final int F2D = 71; + + /** + * D2I Opcode: Represents the type conversion operation from double64 to int32 in the virtual machine. + *

    This opcode is implemented by the {@link D2ICommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top double64 value from the operand stack.
    2. + *
    3. Convert the double64 value to an int32 value (this may involve truncation).
    4. + *
    5. Push the converted int32 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to narrow a double64 value to an int32 type for further integer-based processing.

    + */ + public static final int D2I = 72; + + /** + * D2L Opcode: Represents the type conversion operation from double64 to long64 in the virtual machine. + *

    This opcode is implemented by the {@link D2LCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top double64 value from the operand stack.
    2. + *
    3. Convert the double64 value to a long64 value (this may involve truncation).
    4. + *
    5. Push the converted long64 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to narrow a double64 value to a long64 type, which can then be used for integer operations.

    + */ + public static final int D2L = 73; + + /** + * D2F Opcode: Represents the type conversion operation from double64 to float32 in the virtual machine. + *

    This opcode is implemented by the {@link D2FCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top double64 value from the operand stack.
    2. + *
    3. Convert the double64 value to a float32 value.
    4. + *
    5. Push the converted float32 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to narrow a double64 value to a float32 type when lower precision floating-point arithmetic is acceptable.

    + */ + public static final int D2F = 74; + + /** + * S2I Opcode: Represents the type conversion operation from short16 to int32 in the virtual machine. + *

    This opcode is implemented by the {@link S2ICommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top short16 value from the operand stack.
    2. + *
    3. Convert the short16 value to an int32 value.
    4. + *
    5. Push the converted int32 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to widen a short16 value to an int32 type, facilitating subsequent integer arithmetic or comparison operations.

    + */ + public static final int S2I = 75; + + /** + * B2I Opcode: Represents the type conversion operation from byte8 to int32 in the virtual machine. + *

    This opcode is implemented by the {@link B2ICommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pop the top byte8 value from the operand stack.
    2. + *
    3. Convert the byte8 value to an int32 value.
    4. + *
    5. Push the converted int32 value back onto the operand stack for subsequent operations.
    6. + *
    + * + *

    This opcode is used to widen a byte8 value to an int32 type to ensure compatibility with integer-based operations.

    + */ + public static final int B2I = 76; + + + + // 2. Bitwise Operations (81–90) + // 2.1 int32 (81-85) + /** + * I_AND Opcode: Represents the int32 bitwise AND operation in the virtual machine. + *

    This opcode is implemented by the {@link IAndCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two int32 values from the operand stack. These values are treated as 32-bit binary representations.
    2. + *
    3. Performs the int32 bitwise AND operation on the two popped operands. The operation compares corresponding bits of both operands: + *
        + *
      • Each bit in the result is set to 1 only if the corresponding bits in both operands are also 1.
      • + *
      • If either of the corresponding bits is 0, the resulting bit is 0.
      • + *
      + *
    4. + *
    5. Pushes the result of the int32 bitwise AND operation back onto the operand stack for further processing.
    6. + *
    + * + *

    This opcode is essential for low-level bit manipulation tasks.

    + */ + public static final int I_AND = 81; + /** + * I_OR Opcode: Represents the int32 bitwise OR operation in the virtual machine. + *

    This opcode is implemented by the {@link IOrCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two int32 values from the operand stack. These values are treated as 32-bit binary representations.
    2. + *
    3. Performs the int32 bitwise OR operation on the two popped operands. The operation compares corresponding bits of both operands: + *
        + *
      • Each bit in the result is set to 1 if at least one of the corresponding bits in either operand is 1.
      • + *
      • If both corresponding bits are 0, the resulting bit is 0.
      • + *
      + *
    4. + *
    5. Pushes the result of the int32 bitwise OR operation back onto the operand stack for further processing.
    6. + *
    + * + *

    This opcode is essential for low-level bit manipulation tasks.

    + */ + public static final int I_OR = 82; + + /** + * I_XOR Opcode: Represents the int32 bitwise XOR operation in the virtual machine. + *

    This opcode is implemented by the {@link IXorCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two int32 values from the operand stack. These values are treated as 32-bit binary representations.
    2. + *
    3. Performs the int32 bitwise XOR (exclusive OR) operation on the two operands: + *
        + *
      • If the corresponding bits are different, the result is 1.
      • + *
      • If the corresponding bits are the same, the result is 0.
      • + *
      + *
    4. + *
    5. Pushes the result of the int32 bitwise XOR operation back onto the operand stack for further processing.
    6. + *
    + * + *

    This opcode is essential for low-level bit manipulation tasks.

    + */ + public static final int I_XOR = 83; + + // 2.2 Long64 (86-90) + /** + * L_AND Opcode: Represents the long64 bitwise AND operation in the virtual machine. + *

    This opcode is implemented by the {@link LAndCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two long64 values from the operand stack. These values are treated as 32-bit binary representations.
    2. + *
    3. Performs the long64 bitwise AND operation on the two popped operands. The operation compares corresponding bits of both operands: + *
        + *
      • Each bit in the result is set to 1 only if the corresponding bits in both operands are also 1.
      • + *
      • If either of the corresponding bits is 0, the resulting bit is 0.
      • + *
      + *
    4. + *
    5. Pushes the result of the long64 bitwise AND operation back onto the operand stack for further processing.
    6. + *
    + * + *

    This opcode is essential for low-level bit manipulation tasks.

    + */ + public static final int L_AND = 86; + /** + * L_OR Opcode: Represents the long64 bitwise OR operation in the virtual machine. + *

    This opcode is implemented by the {@link LOrCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two long64 values from the operand stack. These values are treated as 32-bit binary representations.
    2. + *
    3. Performs the long64 bitwise OR operation on the two popped operands. The operation compares corresponding bits of both operands: + *
        + *
      • Each bit in the result is set to 1 if at least one of the corresponding bits in either operand is 1.
      • + *
      • If both corresponding bits are 0, the resulting bit is 0.
      • + *
      + *
    4. + *
    5. Pushes the result of the long64 bitwise OR operation back onto the operand stack for further processing.
    6. + *
    + * + *

    This opcode is essential for low-level bit manipulation tasks.

    + */ + public static final int L_OR = 87; + + /** + * L_XOR Opcode: Represents the long64 bitwise XOR operation in the virtual machine. + *

    This opcode is implemented by the {@link LXorCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Pops the top two long64 values from the operand stack. These values are treated as 32-bit binary representations.
    2. + *
    3. Performs the long64 bitwise XOR (exclusive OR) operation on the two operands: + *
        + *
      • If the corresponding bits are different, the result is 1.
      • + *
      • If the corresponding bits are the same, the result is 0.
      • + *
      + *
    4. + *
    5. Pushes the result of the long64 bitwise XOR operation back onto the operand stack for further processing.
    6. + *
    + * + *

    This opcode is essential for low-level bit manipulation tasks.

    + */ + public static final int L_XOR = 88; + + + // 3. Control Flow Operations (91–110) + /** + * JUMP Opcode: Represents an unconditional jump to a target instruction address. + *

    This opcode is implemented by the {@link JumpCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Parses the target instruction address from the instruction parameters.
    2. + *
    3. Validates the target address to ensure it is a non-negative int32.
    4. + *
    5. If the target address is valid (greater than or equal to 0), updates the program counter (PC) to the specified target address, + * effectively skipping all intermediate instructions.
    6. + *
    7. If the target address is invalid (less than 0), logs an error message and halts execution by returning an invalid value (typically -1).
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Control flow management in virtual machine execution.
    • + *
    + */ + public static final int JUMP = 91; + /** + * IC_E Opcode: Represents a conditional jump based on int32 equality. + *

    This opcode is implemented by the {@link ICECommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Parses the target instruction address from the instruction parameters.
    2. + *
    3. Pops two int32 values from the operand stack.
    4. + *
    5. Compares the two int32s for equality.
    6. + *
    7. If the int32s are equal, updates the program counter (PC) to the specified target address, + * effectively jumping to the target instruction.
    8. + *
    9. If the int32s are not equal, increments the program counter to proceed with the next sequential instruction.
    10. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Conditional branching in virtual machine execution based on int32 comparison.
    • + *
    • Implementing control flow structures such as if-statements and loops.
    • + *
    + */ + public static final int IC_E = 92; + /** + * IC_NE Opcode: Represents a conditional jump based on int32 inequality. + *

    This opcode is implemented by the {@link ICNECommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Parses the target instruction address from the instruction parameters.
    2. + *
    3. Pops two int32 values from the operand stack.
    4. + *
    5. Compares the two int32s for inequality.
    6. + *
    7. If the int32s are not equal, updates the program counter (PC) to the specified target address, + * effectively jumping to the target instruction.
    8. + *
    9. If the int32s are equal, increments the program counter to proceed with the next sequential instruction.
    10. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Conditional branching in virtual machine execution based on int32 comparison.
    • + *
    • Implementing control flow structures such as conditional loops and if-else statements.
    • + *
    + */ + public static final int IC_NE = 93; + /** + * IC_G Opcode: Represents a conditional jump based on int32 comparison (greater than). + *

    This opcode is implemented by the {@link ICGCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Parses the target instruction address from the instruction parameters.
    2. + *
    3. Pops two int32 values from the operand stack.
    4. + *
    5. Compares the first int32 with the second to determine if it is greater.
    6. + *
    7. If the first int32 is greater than the second, updates the program counter (PC) to the specified target address, + * effectively jumping to the target instruction.
    8. + *
    9. If the first int32 is not greater than the second, increments the program counter to proceed with the next sequential instruction.
    10. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Conditional branching in virtual machine execution based on int32 comparison.
    • + *
    • Implementing control flow structures such as greater-than conditions in loops and conditional statements.
    • + *
    + */ + public static final int IC_G = 94; + /** + * IC_GE Opcode: Represents a conditional jump based on int32 comparison (greater than or equal to). + *

    This opcode is implemented by the {@link ICGECommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Parses the target instruction address from the instruction parameters.
    2. + *
    3. Pops two int32 values from the operand stack.
    4. + *
    5. Compares the first int32 with the second to determine if it is greater than or equal to the second int32.
    6. + *
    7. If the first int32 is greater than or equal to the second, updates the program counter (PC) to the specified target address, + * effectively jumping to the target instruction.
    8. + *
    9. If the first int32 is less than the second, increments the program counter to proceed with the next sequential instruction.
    10. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Conditional branching in virtual machine execution based on int32 comparison.
    • + *
    • Implementing control flow structures such as loops, conditional statements, and range checks.
    • + *
    + */ + public static final int IC_GE = 95; + /** + * IC_L Opcode: Represents a conditional jump based on int32 comparison (less than). + *

    This opcode is implemented by the {@link ICLCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Parses the target instruction address from the instruction parameters.
    2. + *
    3. Pops two int32 values from the operand stack.
    4. + *
    5. Compares the first int32 with the second to determine if it is less than the second int32.
    6. + *
    7. If the first int32 is less than the second, updates the program counter (PC) to the specified target address, + * effectively jumping to the target instruction.
    8. + *
    9. If the first int32 is greater than or equal to the second, increments the program counter to proceed with the next sequential instruction.
    10. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Conditional branching in virtual machine execution based on int32 comparison.
    • + *
    • Implementing control flow structures such as loops, conditional statements, and range validations.
    • + *
    + */ + public static final int IC_L = 96; + /** + * IC_LE Opcode: Represents a conditional jump based on int32 comparison (less than or equal). + *

    This opcode is implemented by the {@link ICLECommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Parses the target instruction address from the instruction parameters.
    2. + *
    3. Pops two int32 values from the operand stack.
    4. + *
    5. Compares the first int32 with the second to determine if it is less than or equal to the second int32.
    6. + *
    7. If the first int32 is less than or equal to the second, updates the program counter (PC) to the specified target address, + * effectively jumping to the target instruction.
    8. + *
    9. If the first int32 is greater than the second, increments the program counter to proceed with the next sequential instruction.
    10. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Conditional branching in virtual machine execution based on int32 comparison.
    • + *
    • Implementing control flow structures such as loops, conditional statements, and boundary checks.
    • + *
    + */ + public static final int IC_LE = 97; + + // 4. Stack Operations (111–150) + // 4.1 PUSH (111-120) + /** + * I_PUSH Opcode: Represents a stack operation that pushes an int32 value onto the operand stack. + *

    This opcode is implemented by the {@link IPushCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Parses the int32 value from the instruction parameters.
    2. + *
    3. Pushes the parsed int32 value onto the operand stack.
    4. + *
    5. Increments the program counter (PC) to proceed with the next sequential instruction.
    6. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Loading constant values into the operand stack for later operations.
    • + *
    • Preparing operands for arithmetic, logical, or comparison instructions.
    • + *
    • Facilitating method calls or control flow by managing stack-based data.
    • + *
    + */ + public static final int I_PUSH = 111; + /** + * L_PUSH Opcode: Represents a stack operation that pushes a long64 value onto the operand stack. + *

    This opcode is implemented by the {@link LPushCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Parses the long64 value from the instruction parameters.
    2. + *
    3. Pushes the parsed long64 value onto the operand stack.
    4. + *
    5. Increments the program counter (PC) to proceed with the next sequential instruction.
    6. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Loading constant values into the operand stack for later operations.
    • + *
    • Preparing operands for arithmetic, logical, or comparison instructions.
    • + *
    • Facilitating method calls or control flow by managing stack-based data.
    • + *
    + */ + public static final int L_PUSH = 112; + /** + * S_PUSH Opcode: Represents a stack operation that pushes a short16 value onto the operand stack. + *

    This opcode is implemented by the {@link SPushCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Parses the short16 value from the instruction parameters.
    2. + *
    3. Pushes the parsed short16 value onto the operand stack.
    4. + *
    5. Increments the program counter (PC) to proceed with the next sequential instruction.
    6. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Loading constant values into the operand stack for later operations.
    • + *
    • Preparing operands for arithmetic, logical, or comparison instructions.
    • + *
    • Facilitating method calls or control flow by managing stack-based data.
    • + *
    + */ + public static final int S_PUSH = 113; + /** + * I_PUSH Opcode: Represents a stack operation that pushes a byte8 value onto the operand stack. + *

    This opcode is implemented by the {@link BPushCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Parses the byte8 value from the instruction parameters.
    2. + *
    3. Pushes the parsed byte8 value onto the operand stack.
    4. + *
    5. Increments the program counter (PC) to proceed with the next sequential instruction.
    6. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Loading constant values into the operand stack for later operations.
    • + *
    • Preparing operands for arithmetic, logical, or comparison instructions.
    • + *
    • Facilitating method calls or control flow by managing stack-based data.
    • + *
    + */ + public static final int B_PUSH = 114; + /** + * I_PUSH Opcode: Represents a stack operation that pushes a double64 value onto the operand stack. + *

    This opcode is implemented by the {@link DPushCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Parses the double64 value from the instruction parameters.
    2. + *
    3. Pushes the parsed double64 value onto the operand stack.
    4. + *
    5. Increments the program counter (PC) to proceed with the next sequential instruction.
    6. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Loading constant values into the operand stack for later operations.
    • + *
    • Preparing operands for arithmetic, logical, or comparison instructions.
    • + *
    • Facilitating method calls or control flow by managing stack-based data.
    • + *
    + */ + public static final int D_PUSH = 115; + /** + * F_PUSH Opcode: Represents a stack operation that pushes a float32 value onto the operand stack. + *

    This opcode is implemented by the {@link FPushCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Parses the float32 value from the instruction parameters.
    2. + *
    3. Pushes the parsed float32 value onto the operand stack.
    4. + *
    5. Increments the program counter (PC) to proceed with the next sequential instruction.
    6. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Loading constant values into the operand stack for later operations.
    • + *
    • Preparing operands for arithmetic, logical, or comparison instructions.
    • + *
    • Facilitating method calls or control flow by managing stack-based data.
    • + *
    + */ + public static final int F_PUSH = 116; + /** + * I_POP Opcode: Represents a stack operation that removes the top element from the operand stack. + *

    This opcode is implemented by the {@link PopCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Removes (pops) the top element from the operand stack.
    2. + *
    3. Discards the popped value, as it is not stored or used further.
    4. + *
    5. Increments the program counter (PC) to proceed with the next sequential instruction.
    6. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Clearing temporary or unnecessary data from the operand stack.
    • + *
    • Managing stack cleanup after operations that leave excess data.
    • + *
    • Ensuring stack balance during function calls or control flow transitions.
    • + *
    + */ + // 4.2 POP (121-125) + public static final int POP = 121; + /** + * DUP Opcode: Represents a stack operation that duplicates the top element of the operand stack. + *

    This opcode is implemented by the {@link DupCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the top element from the operand stack.
    2. + *
    3. Duplicates the top value and pushes it back onto the stack.
    4. + *
    5. Increments the program counter (PC) to proceed with the next sequential instruction.
    6. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Preserving the top element of the operand stack for later use in later operations.
    • + *
    • Duplicating values that are needed multiple times in the execution flow.
    • + *
    • Managing stack balance when performing operations that require repeated access to the same data.
    • + *
    + */ + // 4.3 DUP (126-130) + public static final int DUP = 126; + /** + * SWAP Opcode: Represents a stack operation that swaps the top two values of the operand stack. + *

    This opcode is implemented by the {@link SwapCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Ensures that there are at least two elements on the operand stack.
    2. + *
    3. Pops the two topmost values from the stack.
    4. + *
    5. Pushes the two values back onto the stack in reversed order.
    6. + *
    7. Increments the program counter (PC) to proceed with the next sequential instruction.
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Reversing the order of the top two elements on the stack, which may be required in certain algorithms or operations.
    • + *
    • Handling data rearrangements that require immediate swapping of operands during execution.
    • + *
    • Ensuring proper operand placement for later instructions that depend on the order of stack elements.
    • + *
    + */ + // 4.4 SWAP (131-135) + public static final int SWAP = 131; + + // 5. Memory Operations (151–) + /** + * I_STORE Opcode: Represents a store operation that saves an int32 value from the operand stack into the local variable store. + * + *

    This opcode is implemented by the {@link IStoreCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index for the local variable from the instruction parameters.
    2. + *
    3. Pops the int32 value from the operand stack.
    4. + *
    5. Stores the value into the local variable store at the specified index of the current method frame.
    6. + *
    7. Increments the program counter (PC) to proceed with the next sequential instruction.
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Storing results of computations or intermediate values into local variables.
    • + *
    • Preserving state across instructions or method calls by saving values to the local frame.
    • + *
    • Transferring values from the operand stack to the method-local scope.
    • + *
    + */ + public static final int I_STORE = 151; + + + /** + * L_STORE Opcode: Represents a store operation that stores a long64 value from the operand stack into the local variable store. + *

    This opcode is implemented by the {@link LStoreCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index for the local variable from the instruction parameters.
    2. + *
    3. Pops a long64 value from the operand stack.
    4. + *
    5. Stores the value into the local variable store at the specified index of the current method frame.
    6. + *
    7. Increments the program counter (PC) to proceed with the next sequential instruction.
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Storing computed long64 values into local variables for reuse.
    • + *
    • Preserving long-type data across multiple instructions or calls.
    • + *
    • Moving data from the operand stack to a persistent method-local context.
    • + *
    + */ + public static final int L_STORE = 152; + + + /** + * S_STORE Opcode: Represents a store operation that stores a short16 value from the operand stack into the local variable store. + *

    This opcode is implemented by the {@link SStoreCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index for the local variable from the instruction parameters.
    2. + *
    3. Pops a short16 value from the operand stack.
    4. + *
    5. Stores the value into the local variable store at the specified index of the current method frame.
    6. + *
    7. Increments the program counter (PC) to proceed with the next sequential instruction.
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Storing short16 values resulting from calculations or conversions.
    • + *
    • Temporarily saving data in local variables for later instructions.
    • + *
    • Supporting typed local variable storage for short values.
    • + *
    + */ + public static final int S_STORE = 153; + + + /** + * B_STORE Opcode: Represents a store operation that stores a byte8 value from the operand stack into the local variable store. + *

    This opcode is implemented by the {@link BStoreCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index for the local variable from the instruction parameters.
    2. + *
    3. Pops a byte8 value from the operand stack.
    4. + *
    5. Stores the value into the local variable store at the specified index of the current method frame.
    6. + *
    7. Increments the program counter (PC) to proceed with the next sequential instruction.
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Saving byte8 results or constants into method-local variables.
    • + *
    • Enabling byte-level operations and temporary storage.
    • + *
    • Transferring values from the stack to local scope in compact form.
    • + *
    + */ + public static final int B_STORE = 154; + + + /** + * D_STORE Opcode: Represents a store operation that stores a double64 value from the operand stack into the local variable store. + *

    This opcode is implemented by the {@link DStoreCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index for the local variable from the instruction parameters.
    2. + *
    3. Pops a double64 value from the operand stack.
    4. + *
    5. Stores the value into the local variable store at the specified index of the current method frame.
    6. + *
    7. Increments the program counter (PC) to proceed with the next sequential instruction.
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Saving double64-precision results after arithmetic or conversion operations.
    • + *
    • Ensuring floating-point values persist in method-local storage.
    • + *
    • Managing precision-critical calculations across instruction sequences.
    • + *
    + */ + public static final int D_STORE = 155; + + /** + * F_STORE Opcode: Represents a store operation that stores a float32 value from the operand stack into the local variable store. + *

    This opcode is implemented by the {@link FStoreCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index for the local variable from the instruction parameters.
    2. + *
    3. Pops a float32 value from the operand stack.
    4. + *
    5. Stores the value into the local variable store at the specified index of the current method frame.
    6. + *
    7. Increments the program counter (PC) to proceed with the next sequential instruction.
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Saving float32 results into method-local variables.
    • + *
    • Supporting floating-point local variable operations.
    • + *
    • Preserving floating-point values between instructions or calls.
    • + *
    + */ + public static final int F_STORE = 156; + + + /** + * I_LOAD Opcode: Represents a load operation that retrieves an int32 value from the local variable store and pushes it onto the operand stack. + *

    This opcode is implemented by the {@link ILoadCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index for the local variable from the instruction parameters.
    2. + *
    3. Retrieves the corresponding value from the local variable store of the current method frame.
    4. + *
    5. Pushes the retrieved value onto the operand stack for subsequent operations.
    6. + *
    7. Increments the program counter (PC) to proceed with the next sequential instruction.
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Loading local variables onto the operand stack for further operations or computations.
    • + *
    • Retrieving stored values that are needed for subsequent instructions, such as arithmetic or logic operations.
    • + *
    • Preserving the necessary method-local state by pushing relevant values from the local variable store to the operand stack.
    • + *
    + */ + public static final int I_LOAD = 161; + /** + * L_LOAD Opcode: Represents a load operation that retrieves a long64 value from the local variable store and pushes it onto the operand stack. + *

    This opcode is implemented by the {@link LLoadCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index for the local variable from the instruction parameters.
    2. + *
    3. Retrieves the corresponding value from the local variable store of the current method frame.
    4. + *
    5. Pushes the retrieved value onto the operand stack for subsequent operations.
    6. + *
    7. Increments the program counter (PC) to proceed with the next sequential instruction.
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Loading local variables onto the operand stack for further operations or computations.
    • + *
    • Retrieving stored values that are needed for subsequent instructions, such as arithmetic or logic operations.
    • + *
    • Preserving the necessary method-local state by pushing relevant values from the local variable store to the operand stack.
    • + *
    + */ + public static final int L_LOAD = 162; + /** + * S_LOAD Opcode: Represents a load operation that retrieves a short16 value from the local variable store and pushes it onto the operand stack. + *

    This opcode is implemented by the {@link SLoadCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index for the local variable from the instruction parameters.
    2. + *
    3. Retrieves the corresponding value from the local variable store of the current method frame.
    4. + *
    5. Pushes the retrieved value onto the operand stack for subsequent operations.
    6. + *
    7. Increments the program counter (PC) to proceed with the next sequential instruction.
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Loading local variables onto the operand stack for further operations or computations.
    • + *
    • Retrieving stored values that are needed for subsequent instructions, such as arithmetic or logic operations.
    • + *
    • Preserving the necessary method-local state by pushing relevant values from the local variable store to the operand stack.
    • + *
    + */ + public static final int S_LOAD = 163; + /** + * B_LOAD Opcode: Represents a load operation that retrieves a byte8 value from the local variable store and pushes it onto the operand stack. + *

    This opcode is implemented by the {@link BLoadCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index for the local variable from the instruction parameters.
    2. + *
    3. Retrieves the corresponding value from the local variable store of the current method frame.
    4. + *
    5. Pushes the retrieved value onto the operand stack for subsequent operations.
    6. + *
    7. Increments the program counter (PC) to proceed with the next sequential instruction.
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Loading local variables onto the operand stack for further operations or computations.
    • + *
    • Retrieving stored values that are needed for subsequent instructions, such as arithmetic or logic operations.
    • + *
    • Preserving the necessary method-local state by pushing relevant values from the local variable store to the operand stack.
    • + *
    + */ + public static final int B_LOAD = 164; + /** + * D_LOAD Opcode: Represents a load operation that retrieves a double64 value from the local variable store and pushes it onto the operand stack. + *

    This opcode is implemented by the {@link DLoadCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index for the local variable from the instruction parameters.
    2. + *
    3. Retrieves the corresponding value from the local variable store of the current method frame.
    4. + *
    5. Pushes the retrieved value onto the operand stack for subsequent operations.
    6. + *
    7. Increments the program counter (PC) to proceed with the next sequential instruction.
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Loading local variables onto the operand stack for further operations or computations.
    • + *
    • Retrieving stored values that are needed for subsequent instructions, such as arithmetic or logic operations.
    • + *
    • Preserving the necessary method-local state by pushing relevant values from the local variable store to the operand stack.
    • + *
    + */ + public static final int D_LOAD = 165; + /** + * F_LOAD Opcode: Represents a load operation that retrieves a float32 value from the local variable store and pushes it onto the operand stack. + *

    This opcode is implemented by the {@link FLoadCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Retrieves the index for the local variable from the instruction parameters.
    2. + *
    3. Retrieves the corresponding value from the local variable store of the current method frame.
    4. + *
    5. Pushes the retrieved value onto the operand stack for subsequent operations.
    6. + *
    7. Increments the program counter (PC) to proceed with the next sequential instruction.
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Loading local variables onto the operand stack for further operations or computations.
    • + *
    • Retrieving stored values that are needed for subsequent instructions, such as arithmetic or logic operations.
    • + *
    • Preserving the necessary method-local state by pushing relevant values from the local variable store to the operand stack.
    • + *
    + */ + public static final int F_LOAD = 166; + + /** + * MOV Opcode: Represents a move operation that transfers a value from one local variable to another within the local variable store. + *

    This opcode is implemented by the {@link MovCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Parses the source and destination indices from the instruction parameters.
    2. + *
    3. Retrieves the value from the local variable store at the source index.
    4. + *
    5. Stores the retrieved value into the destination index of the local variable store.
    6. + *
    7. Increments the program counter (PC) to proceed with the next sequential instruction.
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Transferring values between local variables within a method frame.
    • + *
    • Preserving computed results by moving them to designated local variable slots.
    • + *
    • Facilitating intermediate storage of values needed for later computations.
    • + *
    + */ + public static final int MOV = 171; + /** + * CALL Opcode: Represents a function or subroutine call operation that transfers control to a specified function address. + *

    This opcode is implemented by the {@link CallCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Validates and extracts the target function address from the instruction parameters.
    2. + *
    3. Creates a new stack frame containing the return address (current PC + 1), the local variable store, and the method context.
    4. + *
    5. Pushes the newly created stack frame onto the call stack to manage the function invocation hierarchy.
    6. + *
    7. Updates the program counter (PC) to the target function address, effectively transferring control to the function.
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Invoking functions or subroutines within the virtual machine's execution flow.
    • + *
    • Managing method invocations by maintaining a call stack with return addresses and local variable contexts.
    • + *
    • Facilitating modular and reusable code execution by enabling jumps to specific function addresses.
    • + *
    + */ + public static final int CALL = 201; + /** + * RET Opcode: Represents a return operation that transfers control back to the calling method by restoring the saved return address. + *

    This opcode is implemented by the {@link RetCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Validates that the call stack is not empty to ensure there is a method to return to.
    2. + *
    3. Clears the local variables of the current method using {@link LocalVariableStore#clearVariables()}.
    4. + *
    5. Pops the current stack frame from the call stack to retrieve the saved return address.
    6. + *
    7. Restores the program counter (PC) to the return address, allowing execution to continue from the calling method.
    8. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Returning from a function or method to the caller in the virtual machine's execution flow.
    • + *
    • Restoring the program's control flow by using the return address saved during the function call.
    • + *
    • Cleaning up the local variables of the completed method to maintain memory consistency.
    • + *
    + */ + public static final int RET = 202; + /** + * HALT Opcode: Represents a termination operation that stops the execution of the virtual machine. + *

    This opcode is implemented by the {@link HaltCommand} class, which defines its specific execution logic.

    + * + *

    Execution Steps:

    + *
      + *
    1. Outputs the message "Process has ended" to indicate that the program execution has been terminated.
    2. + *
    3. Returns -1 as a signal to the virtual machine to halt execution, preventing any further instructions from being processed.
    4. + *
    + * + *

    This opcode is commonly used for:

    + *
      + *
    • Terminating program execution explicitly when the program has completed its intended operations.
    • + *
    • Serving as the final instruction in a program to indicate normal completion.
    • + *
    + */ + public static final int HALT = 255; + + + // VI. Function Operations (50–59) + + /** + * Default constructor for creating an instance of VMOpCode. + * This constructor is empty as no specific initialization is required. + */ + public VMOpCode() { + // Empty constructor + } +} diff --git a/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java b/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java new file mode 100644 index 0000000..14cef48 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java @@ -0,0 +1,179 @@ +package org.jcnc.snow.vm.engine; + +import org.jcnc.snow.vm.execution.CommandExecutionHandler; +import org.jcnc.snow.vm.module.*; + +import java.util.List; + +/** + * Virtual-Machine Engine ({@code VirtualMachineEngine}) + * + *

    Interprets and executes a list of VM instructions while maintaining + * a program counter (PC) and the runtime data structures required for + * operand manipulation and method invocation.

    + * + *
      + *
    • {@link OperandStack} — stores intermediate values
    • + *
    • {@link LocalVariableStore} — holds locals for the current + * stack frame
    • + *
    • {@link CallStack} — manages stack frames and return addresses
    • + *
    • {@link CommandExecutionHandler} — dispatches opcodes
    • + *
    + * + *

    Root-frame contract

    + * A root stack frame is pushed once via + * {@link #ensureRootFrame()} before the first instruction executes + * and is never popped. When a {@code RET} executed in the root frame + * returns {@link #PROGRAM_END}, the main loop exits gracefully. + */ +public class VirtualMachineEngine { + + /* ---------- Constants ---------- */ + + /** Sentinel PC value that signals “terminate program gracefully”. */ + private static final int PROGRAM_END = Integer.MAX_VALUE; + + /** Sentinel returned by {@link CommandExecutionHandler#handle} to halt immediately. */ + private static final int HALT = -1; + + /* ---------- Runtime state ---------- */ + + private final OperandStack operandStack; + private final LocalVariableStore localVariableStore; + private final CallStack callStack; + private final CommandExecutionHandler commandExecutionHandler; + + private int programCounter; + + /* ---------- Construction ---------- */ + + /** + * Builds a VM engine with fresh runtime structures. + * + * @param vmMode execution mode (DEBUG / RUN) + */ + public VirtualMachineEngine(VMMode vmMode) { + this.operandStack = new OperandStack(); + this.callStack = new CallStack(); + this.localVariableStore = new LocalVariableStore(vmMode); // shared with root frame + this.commandExecutionHandler = + new CommandExecutionHandler(operandStack, localVariableStore, callStack); + this.programCounter = 0; + } + + /* package-private accessor used by debug helpers */ + CallStack getCallStack() { return callStack; } + + /* ---------- Execution ---------- */ + + /** + * Executes the supplied program in place. + * + * @param program textual instructions (“opcode arg1 arg2 …”) + * @throws IllegalArgumentException if {@code program} is null / empty + */ + public void execute(List program) { + + if (program == null || program.isEmpty()) + throw new IllegalArgumentException("The command list cannot be empty or null."); + + /* Ensure a single root frame is present. */ + ensureRootFrame(); + + /* -------- Main interpreter loop -------- */ + while (true) { + + /* graceful termination */ + if (programCounter == PROGRAM_END) break; + + /* bounds check */ + if (programCounter < 0 || programCounter >= program.size()) break; + + /* ------------------------------------------------- + * 1) 取指并忽略空行 / 以 '#' 开头的注释行 + * ------------------------------------------------- */ + String rawLine = program.get(programCounter).trim(); + if (rawLine.isEmpty() || rawLine.startsWith("#")) { + programCounter++; // 跳过并继续 + continue; + } + + String[] parts = rawLine.split(" "); + + if (parts.length < 1) { + System.err.println("Invalid command format at PC=" + programCounter + + " -> Missing opcode"); + break; + } + + try { + int opCode = parseOpCode(parts[0]); + + int nextPC = commandExecutionHandler.handle(opCode, parts, programCounter); + + /* HALT / PROGRAM_END → exit */ + if (nextPC == HALT || nextPC == PROGRAM_END) { + programCounter = PROGRAM_END; + continue; + } + + /* 如果处理器未修改 PC,则默认顺序执行下一行 */ + programCounter = (nextPC == programCounter) ? programCounter + 1 : nextPC; + + } catch (IllegalArgumentException e) { + System.err.println("Command error at PC=" + programCounter + " -> " + + e.getMessage()); + break; + } + } + + /* ---------- compact root locals & print debug info ---------- */ + if (!callStack.isEmpty()) { + LocalVariableStore rootLvs = callStack.peekFrame().getLocalVariableStore(); + rootLvs.compact(); // trim leading / trailing null slots + } + } + + /* ---------- Helper: ensure root frame ---------- */ + + /** + * Pushes the root frame (returnAddress = {@value #PROGRAM_END}) iff it isn’t there yet. + * This frame is never popped during normal execution. + */ + private void ensureRootFrame() { + if (!callStack.isEmpty()) return; // already initialised + + /* The returnAddress of the root frame must be PROGRAM_END so that the main loop can exit correctly when the root function RETs.*/ + MethodContext rootCtx = new MethodContext("root", null); + StackFrame rootFrame = new StackFrame(PROGRAM_END, localVariableStore, rootCtx); + callStack.pushFrame(rootFrame); + } + + /* ---------- Debug helpers ---------- */ + + /** Prints operand stack + call-stack snapshot. */ + public void printStack() { + operandStack.printOperandStack(); + callStack.printCallStack(); + } + + /** Prints the local-variable table of the current top frame. */ + public void printLocalVariables() { + if (callStack.isEmpty()) { + System.out.println("Local variable table is empty"); + return; + } + callStack.peekFrame().getLocalVariableStore().printLv(); + } + + /* ---------- Utility ---------- */ + + /** Parses textual opcode to integer. */ + private int parseOpCode(String opCodeStr) { + try { + return Integer.parseInt(opCodeStr); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid opcode -> " + opCodeStr, e); + } + } +} diff --git a/src/main/java/org/jcnc/snow/vm/execution/CommandExecutionHandler.java b/src/main/java/org/jcnc/snow/vm/execution/CommandExecutionHandler.java new file mode 100644 index 0000000..470f7bc --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/execution/CommandExecutionHandler.java @@ -0,0 +1,60 @@ +package org.jcnc.snow.vm.execution; + + +import org.jcnc.snow.vm.factories.CommandFactory; +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * Command Execution Handler (CommandExecutionHandler) + *

    + * This class is responsible for invoking the appropriate command object based on the opcode + * and executing the command logic. It retrieves specific command instances through the {@link CommandFactory}, + * and calls the {@code execute} method of the command to handle the virtual machine's instruction execution process. + *

    + */ +public record CommandExecutionHandler(OperandStack operandStack, LocalVariableStore localVariableStore, + CallStack callStack) { + /** + * Constructor to initialize the command execution handler. + * + * @param operandStack The stack manager used to manage the virtual machine's stack data. + * @param localVariableStore The local variable table used to store the local variables used during instruction execution. + * @param callStack The call stack used to manage the method invocation hierarchy during execution. + */ + public CommandExecutionHandler { + } + + /** + * Handles the given instruction operation. + *

    + * This method retrieves the corresponding command instance based on the passed opcode + * and executes it. If the command is invalid or an error occurs during execution, + * an error message is printed, and -1 is returned to indicate that the program should terminate or an error occurred. + *

    + * + * @param opCode The opcode representing the type of instruction to execute (e.g., PUSH, POP, ADD, etc.). + * @param parts The array of parameters for the instruction, for example, `10` in `PUSH 10` is the parameter. + * @param currentPC The current Program Counter (PC) indicating the current instruction's position. + * @return The address of the next instruction to execute. A return value of -1 indicates termination or an error. + * @throws IllegalArgumentException If the opcode is invalid, this exception will be thrown. + */ + public int handle(int opCode, String[] parts, int currentPC) { + try { + Command command = CommandFactory + .getInstruction(opCode) + .orElseThrow(() -> new IllegalArgumentException( + "Unknown instruction: " + opCode)); + + LocalVariableStore currentLVS = callStack.peekFrame().getLocalVariableStore(); + + return command.execute(parts, currentPC, operandStack, currentLVS, callStack); + } catch (Exception e) { + System.err.println("Command execution error (PC=" + currentPC + ") -> " + + e.getMessage()); + return -1; // 让 VM 主循环安全终止 + } + } +} diff --git a/src/main/java/org/jcnc/snow/vm/execution/CommandLoader.java b/src/main/java/org/jcnc/snow/vm/execution/CommandLoader.java new file mode 100644 index 0000000..70d6c9b --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/execution/CommandLoader.java @@ -0,0 +1,49 @@ +package org.jcnc.snow.vm.execution; + + + +import org.jcnc.snow.vm.utils.LoggingUtils; +import org.jcnc.snow.vm.io.FileIOUtils; + +import java.util.List; + +/** + * Command Loader (CommandLoader) + *

    + * This class is used to load instructions from a specified file path. It reads instruction data from the file + * using {@link FileIOUtils}, and returns a list of instructions. If the file reading fails or the instruction list is empty, + * an error log will be recorded using {@link LoggingUtils}. + *

    + */ +public class CommandLoader { + /** + * Default constructor for creating an instance of CommandLoader. + * This constructor is empty as no specific initialization is required. + */ + public CommandLoader() { + // Empty constructor + } + + /** + * Loads instructions from a file. + *

    + * This method loads instructions from the given file path and returns them as a list. If the instruction reading fails or the list is empty, + * an error log will be recorded and an empty instruction list will be returned. + *

    + * + * @param filePath The file path pointing to the file containing the instructions. + * @return A list of instructions. If reading fails, an empty list will be returned. + */ + public static List loadInstructions(String filePath) { + // Read instructions from the file + List instructions = FileIOUtils.readInstructionsFromFile(filePath); + + // If the instruction list is empty, log an error message + if (instructions.isEmpty()) { + LoggingUtils.logError("Failed to read instructions or the instruction list is empty"); + } + + // Return the instruction list + return instructions; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java b/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java new file mode 100644 index 0000000..5c8c61b --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java @@ -0,0 +1,222 @@ +package org.jcnc.snow.vm.factories; + +import org.jcnc.snow.vm.commands.arithmetic.conversion.*; +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.commands.arithmetic.byte8.*; +import org.jcnc.snow.vm.commands.arithmetic.double64.*; +import org.jcnc.snow.vm.commands.arithmetic.float32.*; +import org.jcnc.snow.vm.commands.arithmetic.int32.*; +import org.jcnc.snow.vm.commands.arithmetic.long64.*; +import org.jcnc.snow.vm.commands.arithmetic.short16.*; +import org.jcnc.snow.vm.commands.bitwise.int32.IAndCommand; +import org.jcnc.snow.vm.commands.bitwise.int32.IOrCommand; +import org.jcnc.snow.vm.commands.bitwise.int32.IXorCommand; +import org.jcnc.snow.vm.commands.bitwise.long64.LAndCommand; +import org.jcnc.snow.vm.commands.bitwise.long64.LOrCommand; +import org.jcnc.snow.vm.commands.bitwise.long64.LXorCommand; +import org.jcnc.snow.vm.commands.control.all.JumpCommand; +import org.jcnc.snow.vm.commands.control.int32.*; +import org.jcnc.snow.vm.commands.function.CallCommand; +import org.jcnc.snow.vm.commands.function.RetCommand; +import org.jcnc.snow.vm.commands.memory.all.MovCommand; +import org.jcnc.snow.vm.commands.memory.byte8.BLoadCommand; +import org.jcnc.snow.vm.commands.memory.byte8.BStoreCommand; +import org.jcnc.snow.vm.commands.memory.double64.DLoadCommand; +import org.jcnc.snow.vm.commands.memory.double64.DStoreCommand; +import org.jcnc.snow.vm.commands.memory.float32.FLoadCommand; +import org.jcnc.snow.vm.commands.memory.float32.FStoreCommand; +import org.jcnc.snow.vm.commands.memory.int32.ILoadCommand; +import org.jcnc.snow.vm.commands.memory.int32.IStoreCommand; +import org.jcnc.snow.vm.commands.memory.long64.LLoadCommand; +import org.jcnc.snow.vm.commands.memory.long64.LStoreCommand; +import org.jcnc.snow.vm.commands.memory.short16.SLoadCommand; +import org.jcnc.snow.vm.commands.memory.short16.SStoreCommand; +import org.jcnc.snow.vm.commands.stack.all.DupCommand; +import org.jcnc.snow.vm.commands.stack.all.PopCommand; +import org.jcnc.snow.vm.commands.stack.all.SwapCommand; +import org.jcnc.snow.vm.commands.stack.byte8.BPushCommand; +import org.jcnc.snow.vm.commands.stack.double64.DPushCommand; +import org.jcnc.snow.vm.commands.stack.float32.FPushCommand; +import org.jcnc.snow.vm.commands.stack.int32.IPushCommand; +import org.jcnc.snow.vm.commands.stack.long64.LPushCommand; +import org.jcnc.snow.vm.commands.stack.short16.SPushCommand; +import org.jcnc.snow.vm.commands.vm.HaltCommand; +import org.jcnc.snow.vm.engine.VMOpCode; + +import java.util.Optional; + +/** + * The CommandFactory class is responsible + * for getting the corresponding instruction instance based on the operation code. + *

    This class uses an array for fast, constant-time access to corresponding command instances.

    + */ +public class CommandFactory { + private static final Command[] COMMANDS = new Command[1000]; // Adjust, according to your VMOpCode range + + static { + // Initialize the array with corresponding commands based on opCode values + + // 1 Arithmetic Operations (1–80) + // 1.1 int32 (1-10) + COMMANDS[VMOpCode.I_ADD] = new IAddCommand(); // 1 + COMMANDS[VMOpCode.I_SUB] = new ISubCommand(); // 2 + COMMANDS[VMOpCode.I_MUL] = new IMulCommand(); // 3 + COMMANDS[VMOpCode.I_DIV] = new IDivCommand(); // 4 + COMMANDS[VMOpCode.I_MOD] = new IModCommand(); // 5 + COMMANDS[VMOpCode.I_INC] = new IIncCommand(); // 6 + COMMANDS[VMOpCode.I_NEG] = new INegCommand(); // 7 + // 1.2 long64 (11-20) + COMMANDS[VMOpCode.L_ADD] = new LAddCommand(); // 11 + COMMANDS[VMOpCode.L_SUB] = new LSubCommand(); // 12 + COMMANDS[VMOpCode.L_MUL] = new LMulCommand(); // 13 + COMMANDS[VMOpCode.L_DIV] = new LDivCommand(); // 14 + COMMANDS[VMOpCode.L_MOD] = new LModCommand(); // 15 + COMMANDS[VMOpCode.L_INC] = new LIncCommand(); // 16 + COMMANDS[VMOpCode.L_NEG] = new LNegCommand(); // 17 + // 1.3 short16 (21-30) + COMMANDS[VMOpCode.S_ADD] = new SAddCommand(); // 21 + COMMANDS[VMOpCode.S_SUB] = new SSubCommand(); // 22 + COMMANDS[VMOpCode.S_MUL] = new SMulCommand(); // 23 + COMMANDS[VMOpCode.S_DIV] = new SDivCommand(); // 24 + COMMANDS[VMOpCode.S_MOD] = new SModCommand(); // 25 + COMMANDS[VMOpCode.S_INC] = new SIncCommand(); // 26 + COMMANDS[VMOpCode.S_NEG] = new SNegCommand(); // 27 + // 1.4 byte8 (31-40) + COMMANDS[VMOpCode.B_ADD] = new BAddCommand(); // 31 + COMMANDS[VMOpCode.B_SUB] = new BSubCommand(); // 32 + COMMANDS[VMOpCode.B_MUL] = new BMulCommand(); // 33 + COMMANDS[VMOpCode.B_DIV] = new BDivCommand(); // 34 + COMMANDS[VMOpCode.B_MOD] = new BModCommand(); // 35 + COMMANDS[VMOpCode.B_INC] = new BIncCommand(); // 36 + COMMANDS[VMOpCode.B_NEG] = new BNegCommand(); // 37 + // 1.5 double64 (41-50) + COMMANDS[VMOpCode.D_ADD] = new DAddCommand(); // 41 + COMMANDS[VMOpCode.D_SUB] = new DSubCommand(); // 42 + COMMANDS[VMOpCode.D_MUL] = new DMulCommand(); // 43 + COMMANDS[VMOpCode.D_DIV] = new DDivCommand(); // 44 + COMMANDS[VMOpCode.D_MOD] = new DModCommand(); // 45 + COMMANDS[VMOpCode.D_INC] = new DIncCommand(); // 46 + COMMANDS[VMOpCode.D_NEG] = new DNegCommand(); // 47 + // 1.6 float32 (51-60) + COMMANDS[VMOpCode.F_ADD] = new FAddCommand(); // 51 + COMMANDS[VMOpCode.F_SUB] = new FSubCommand(); // 52 + COMMANDS[VMOpCode.F_MUL] = new FMulCommand(); // 53 + COMMANDS[VMOpCode.F_DIV] = new FDivCommand(); // 54 + COMMANDS[VMOpCode.F_MOD] = new FModCommand(); // 55 + COMMANDS[VMOpCode.F_INC] = new FIncCommand(); // 56 + COMMANDS[VMOpCode.F_NEG] = new FNegCommand(); // 57 + + // 1.7 Type Conversion (61-80) + COMMANDS[VMOpCode.I2L] = new I2LCommand(); // 61 int -> long + COMMANDS[VMOpCode.I2S] = new I2SCommand(); // 62 int -> short + COMMANDS[VMOpCode.I2B] = new I2BCommand(); // 63 int -> byte + COMMANDS[VMOpCode.I2D] = new I2DCommand(); // 64 int -> double + COMMANDS[VMOpCode.I2F] = new I2FCommand(); // 65 int -> float + + COMMANDS[VMOpCode.L2I] = new L2ICommand(); // 66 long -> int + COMMANDS[VMOpCode.L2D] = new L2DCommand(); // 67 long -> double + COMMANDS[VMOpCode.L2F] = new L2FCommand(); // 68 long -> float + + COMMANDS[VMOpCode.F2I] = new F2ICommand(); // 69 float -> int + COMMANDS[VMOpCode.F2L] = new F2LCommand(); // 70 float -> long + COMMANDS[VMOpCode.F2D] = new F2DCommand(); // 71 float -> double + + COMMANDS[VMOpCode.D2I] = new D2ICommand(); // 72 double -> int + COMMANDS[VMOpCode.D2L] = new D2LCommand(); // 73 double -> long + COMMANDS[VMOpCode.D2F] = new D2FCommand(); // 74 double -> float + + COMMANDS[VMOpCode.S2I] = new S2ICommand(); // 75 short -> int + + COMMANDS[VMOpCode.B2I] = new B2ICommand(); // 76 byte -> int + + // 1.8 Other (77-80) + + // 2. Bitwise Operations (81–90) + // 2.1 int32 (81-85) + COMMANDS[VMOpCode.I_AND] = new IAndCommand(); // 81 + COMMANDS[VMOpCode.I_OR] = new IOrCommand(); // 82 + COMMANDS[VMOpCode.I_XOR] = new IXorCommand(); // 83 + // 2.2 Long64 (86-90) + COMMANDS[VMOpCode.L_AND] = new LAndCommand(); // 86 + COMMANDS[VMOpCode.L_OR] = new LOrCommand(); // 87 + COMMANDS[VMOpCode.L_XOR] = new LXorCommand(); // 88 + + // 3. Control Flow Operations (91–110) + COMMANDS[VMOpCode.JUMP] = new JumpCommand(); // 91 + COMMANDS[VMOpCode.IC_E] = new ICECommand(); // 92 + COMMANDS[VMOpCode.IC_NE] = new ICNECommand(); // 93 + COMMANDS[VMOpCode.IC_G] = new ICGCommand(); // 94 + COMMANDS[VMOpCode.IC_GE] = new ICGECommand(); // 95 + COMMANDS[VMOpCode.IC_L] = new ICLCommand(); // 96 + COMMANDS[VMOpCode.IC_LE] = new ICLECommand(); // 97 + + // 4. Stack Operations (111–150) + // 4.1 PUSH (111-120) + COMMANDS[VMOpCode.I_PUSH] = new IPushCommand(); // 111 + COMMANDS[VMOpCode.L_PUSH] = new LPushCommand(); // 112 + COMMANDS[VMOpCode.S_PUSH] = new SPushCommand(); // 113 + COMMANDS[VMOpCode.B_PUSH] = new BPushCommand(); // 114 + COMMANDS[VMOpCode.D_PUSH] = new DPushCommand(); // 115 + COMMANDS[VMOpCode.F_PUSH] = new FPushCommand(); // 116 + // 4.2 POP (121-125) + COMMANDS[VMOpCode.POP] = new PopCommand(); // 121 + // 4.3 DUP (126-130) + COMMANDS[VMOpCode.DUP] = new DupCommand(); // 126 + // 4.4 SWAP (131-135) + COMMANDS[VMOpCode.SWAP] = new SwapCommand(); // 131 + + // 4.5 Other (136-150) + + // 5. Memory Operations (151–200) + // 5.1 STORE (151-160) + COMMANDS[VMOpCode.I_STORE] = new IStoreCommand(); // 151 + COMMANDS[VMOpCode.L_STORE] = new LStoreCommand(); // 152 + COMMANDS[VMOpCode.S_STORE] = new SStoreCommand(); // 153 + COMMANDS[VMOpCode.B_STORE] = new BStoreCommand(); // 154 + COMMANDS[VMOpCode.D_STORE] = new DStoreCommand(); // 155 + COMMANDS[VMOpCode.F_STORE] = new FStoreCommand(); // 156 + // 5.2 LOAD (161-170) + COMMANDS[VMOpCode.I_LOAD] = new ILoadCommand(); // 161 + COMMANDS[VMOpCode.L_LOAD] = new LLoadCommand(); // 162 + COMMANDS[VMOpCode.S_LOAD] = new SLoadCommand(); // 163 + COMMANDS[VMOpCode.B_LOAD] = new BLoadCommand(); // 164 + COMMANDS[VMOpCode.D_LOAD] = new DLoadCommand(); // 165 + COMMANDS[VMOpCode.F_LOAD] = new FLoadCommand(); // 166 + + // 5.3 MOV (171-176) + COMMANDS[VMOpCode.MOV] = new MovCommand(); // 171 + + // 5.4 Other (176-200) + + // 6. Function Operations (201–205) + COMMANDS[VMOpCode.CALL] = new CallCommand(); // 201 + COMMANDS[VMOpCode.RET] = new RetCommand(); // 202 + + // 7. Virtual Machine Operations(241-255) + COMMANDS[VMOpCode.HALT] = new HaltCommand(); // 255 + } + + /** + * Default constructor for creating an instance of CommandFactory. + * This constructor is empty as no specific initialization is required. + */ + public CommandFactory() { + // Empty constructor + } + + /** + * Retrieves the corresponding command instance based on the operation code. + *

    This method looks up the given operation code and returns the corresponding command instance.

    + * + * @param opCode The operation code (instruction code) + * @return An Optional containing the command object. If a command exists for the operation code, it returns the command instance; otherwise, an empty Optional is returned. + */ + public static Optional getInstruction(int opCode) { + if (opCode >= 0 && opCode < COMMANDS.length) { + Command command = COMMANDS[opCode]; + return Optional.ofNullable(command); // Return the command if it's present, otherwise return empty Optional + } + // Return empty Optional if opCode is out of range + return Optional.empty(); + } +} diff --git a/src/main/java/org/jcnc/snow/vm/gui/LocalVariableStoreSwing.java b/src/main/java/org/jcnc/snow/vm/gui/LocalVariableStoreSwing.java new file mode 100644 index 0000000..d5d5f6e --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/gui/LocalVariableStoreSwing.java @@ -0,0 +1,131 @@ +package org.jcnc.snow.vm.gui; + + +import org.jcnc.snow.vm.module.LocalVariableStore; + +import javax.swing.*; +import javax.swing.table.DefaultTableModel; +import java.awt.*; +import java.util.List; +import java.util.Vector; + +/** + * The LocalVariableStoreSwing class provides a graphical user interface (GUI) to display the virtual machine's local variable store. + * This class displays the local variable table in a JTable within a Swing window for users to view the current local variables and their values. + * + *

    The class provides static methods to create windows, generate tables, and configure the table display, among other functionalities.

    + */ +public class LocalVariableStoreSwing { + /** + * Default constructor for creating an instance of LocalVariableStoreSwing. + * This constructor is empty as no specific initialization is required. + */ + public LocalVariableStoreSwing() { + // Empty constructor + } + + /** + * Displays the local variable table in a window. + * + *

    This method opens a new Swing window containing a table that shows the content of the current local variable store.

    + * + * @param localVariableStore The current local variable store. + * @param title The title of the window. + */ + public static void display(LocalVariableStore localVariableStore, String title) { + SwingUtilities.invokeLater(() -> { + JFrame frame = createFrame(title); + JTable table = createTableFromLocalVariables(localVariableStore); + frame.add(new JScrollPane(table), BorderLayout.CENTER); + frame.setVisible(true); + }); + } + + /** + * Creates and configures a new JFrame window. + * + * @param title The title of the window. + * @return The created and configured JFrame window. + */ + private static JFrame createFrame(String title) { + JFrame frame = new JFrame(title); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.setSize(500, 400); + frame.setLocationRelativeTo(null); // Center the window on the screen + return frame; + } + + /** + * Creates a JTable based on the local variable store. + * + *

    This method retrieves the local variable data from the store and converts it into a non-editable JTable.

    + * + * @param localVariableStore The current local variable store. + * @return The created JTable displaying the local variable table content. + */ + private static JTable createTableFromLocalVariables(LocalVariableStore localVariableStore) { + List variables = localVariableStore.getLocalVariables(); + + // Create column names + Vector columnNames = createColumnNames(); + + // Create table data + Vector> data = createTableData(variables); + + // Create a non-editable table model + DefaultTableModel model = new DefaultTableModel(data, columnNames) { + @Override + public boolean isCellEditable(int row, int column) { + return false; // Disable cell editing + } + }; + + JTable table = new JTable(model); // Create the table + + // Configure the table selection mode + configureTableSelection(table); + + return table; + } + + /** + * Creates the column names for the table. + * + * @return A Vector containing the column names. + */ + private static Vector createColumnNames() { + Vector columnNames = new Vector<>(); + columnNames.add("Index"); + columnNames.add("Local Variable Value"); + return columnNames; + } + + /** + * Creates the table data based on the local variable list. + * + * @param variables The list of local variables. + * @return The table data, formatted as a Vector. + */ + private static Vector> createTableData(List variables) { + Vector> data = new Vector<>(variables.size()); + for (int i = 0; i < variables.size(); i++) { + Vector row = new Vector<>(2); + row.add(String.valueOf(i)); + row.add(String.valueOf(variables.get(i))); + data.add(row); + } + return data; + } + + /** + * Configures the table's selection mode, disabling the selection of cells, rows, and columns. + * + * @param table The JTable to configure. + */ + private static void configureTableSelection(JTable table) { + table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + table.setCellSelectionEnabled(false); + table.setRowSelectionAllowed(false); + table.setColumnSelectionAllowed(false); + } +} diff --git a/src/main/java/org/jcnc/snow/vm/interfaces/Command.java b/src/main/java/org/jcnc/snow/vm/interfaces/Command.java new file mode 100644 index 0000000..afa47cb --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/interfaces/Command.java @@ -0,0 +1,53 @@ +package org.jcnc.snow.vm.interfaces; + +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The Command interface defines the common execution method for virtual machine instructions. + * All virtual machine instruction classes should implement this interface to provide the specific behavior for instruction execution. + * + *

    Each class that implements this interface must provide its own {@link #execute} method to execute the corresponding instruction operation.

    + * + *

    The execute method in this interface receives the instruction parameters, the current program counter (PC), + * as well as the virtual machine's stack manager, local variable store, and call stack. It modifies the virtual machine's state + * (such as the stack contents, program counter, and other internal states) based on the specific functionality of the instruction.

    + * + *

    This interface provides a blueprint for implementing various types of virtual machine instructions, such as arithmetic operations, + * control flow instructions (e.g., CALL, JUMP), and data manipulation instructions (e.g., PUSH, POP).

    + */ +public interface Command { + + /** + * Executes the virtual machine instruction's operation. + * + *

    This method retrieves the necessary data from the virtual machine stack and local variable store based on the instruction's + * specific implementation, performs the operation, and updates the program counter (PC) to reflect the next instruction + * to be executed.

    + * + *

    The parameters provided allow the command to manipulate the operand stack, modify the local variables, and control the flow + * of execution by updating the program counter.

    + * + *

    The exact behavior of this method will depend on the specific instruction being executed (e.g., arithmetic, branching, + * function calls, etc.). For example, a `CALL` instruction will modify the call stack by pushing a new frame, + * while a `POP` instruction will remove an item from the operand stack.

    + * + * @param parts The array of instruction parameters, which usually includes the operator and related arguments + * (such as target addresses, values, or function names). These parameters may vary based on + * the instruction being executed. + * @param currentPC The current program counter-value, indicating the address of the instruction being executed. + * This value is typically incremented after the execution of each instruction to point to the next one. + * @param operandStack The virtual machine's operand stack manager, responsible for performing operations on the operand stack, + * such as pushing, popping, and peeking values. + * @param localVariableStore The local variable store, typically used to manage method-local variables during instruction execution. + * The store may not be used in every command but can be leveraged by instructions that require access + * to local variables. + * @param callStack The virtual machine's call stack, which keeps track of the method invocation hierarchy. It is used by + * instructions that involve method calls or returns (such as `CALL` and `RETURN` instructions). + * @return The updated program counter-value, typically the current program counter-value incremented by 1, unless the + * instruction modifies control flow (such as a `JUMP` or `CALL`), in which case it may return a new address + * corresponding to the target of the jump or the subroutine to call. + */ + int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack); +} diff --git a/src/main/java/org/jcnc/snow/vm/io/FileIOUtils.java b/src/main/java/org/jcnc/snow/vm/io/FileIOUtils.java new file mode 100644 index 0000000..fbb0693 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/io/FileIOUtils.java @@ -0,0 +1,94 @@ +package org.jcnc.snow.vm.io; + +import org.jcnc.snow.vm.utils.LoggingUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.toList; + +/** + * The FileIOUtils class handles file input and output operations, primarily for reading instructions from files. + * This class provides various methods to handle file paths, read file contents, clean up unnecessary lines in files, etc. + *

    + * The primary functionality of this class is to read instructions from files and remove comments, empty lines, and other irrelevant content. + *

    + */ +public class FileIOUtils { + /** + * Default constructor for creating an instance of FileIOUtils. + * This constructor is empty as no specific initialization is required. + */ + public FileIOUtils() { + // Empty constructor + } + + /** + * Retrieves the file path from the command line arguments. + *

    + * This method checks the number of command line arguments and extracts the file path from the arguments. + *

    + * + * @param args Command line arguments + * @return The file path if the command line arguments contain a valid file path; otherwise, returns null. + */ + public static String getFilePathFromArgs(String[] args) { + if (args.length != 1) { + LoggingUtils.logError("Please provide a valid file path."); + return null; + } + return args[0]; + } + + /** + * Reads and processes instructions from the specified file path. + *

    + * This method reads each line of the file, removes comments and empty lines, and returns a list of valid instructions. + *

    + * + * @param filePath The file path + * @return A list of processed instructions from the file. If an error occurs while reading the file, returns an empty list. + */ + public static List readInstructionsFromFile(String filePath) { + try (Stream lines = Files.lines(Path.of(filePath))) { + return lines.map(FileIOUtils::cleanLine) // Remove comments from the line + .filter(FileIOUtils::isNotEmpty) // Filter out empty lines + .collect(toList()); // Collect into a list + } catch (IOException e) { + LoggingUtils.logError("Error reading file: " + e.getMessage()); + return List.of(); // Return an empty list if an error occurs + } + } + + /** + * Cleans up the content of each line by removing comments and trimming whitespace. + *

    + * This method removes comments (sections starting with "//") from the line and trims any unnecessary whitespace. + *

    + * + * @param line The original line + * @return The cleaned-up line with comments removed and whitespace trimmed. + */ + public static String cleanLine(String line) { + if (line == null || line.isEmpty()) { + return ""; // Return an empty string if the line is null or empty + } + return line.replaceAll("//.*", "").trim(); // Remove comments and trim whitespace + } + + /** + * Checks if the line is empty. + *

    + * This method checks if the line is an empty string or contains only whitespace characters. + *

    + * + * @param line The line to check + * @return True if the line is not empty or does not contain only spaces; otherwise, returns false. + */ + public static boolean isNotEmpty(String line) { + return !line.isEmpty(); // Return whether the line is not empty + } +} diff --git a/src/main/java/org/jcnc/snow/vm/io/FilePathResolver.java b/src/main/java/org/jcnc/snow/vm/io/FilePathResolver.java new file mode 100644 index 0000000..421499e --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/io/FilePathResolver.java @@ -0,0 +1,43 @@ +package org.jcnc.snow.vm.io; + +import org.jcnc.snow.vm.utils.LoggingUtils; + +/** + * The FilePathResolver class handles command-line arguments to retrieve a valid file path. + * This class contains a method that extracts the file path from the command-line arguments. + * If no file path is provided, an error message will be logged. + *

    + * The main functionality of this class is to obtain the file path from the command-line arguments and provide relevant error logging. + *

    + */ +public class FilePathResolver { + /** + * Default constructor for creating an instance of FilePathResolver. + * This constructor is empty as no specific initialization is required. + */ + public FilePathResolver() { + // Empty constructor + } + + /** + * Retrieves the file path. + *

    + * This method calls the method in the FileIOUtils class to obtain the file path from the command-line arguments. + * If a valid file path is not provided, an error message will be logged. + *

    + * + * @param args Command-line arguments containing the file path provided by the user + * @return The file path if a valid path is provided in the command-line arguments; otherwise, returns null. + */ + public static String getFilePath(String[] args) { + // Use the FileIOUtils class to obtain the file path + String filePath = FileIOUtils.getFilePathFromArgs(args); + + // Log an error message if no valid file path is provided + if (filePath == null) { + LoggingUtils.logError("No valid file path provided."); + } + + return filePath; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/module/CallStack.java b/src/main/java/org/jcnc/snow/vm/module/CallStack.java new file mode 100644 index 0000000..cd982d3 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/module/CallStack.java @@ -0,0 +1,96 @@ +package org.jcnc.snow.vm.module; + +import org.jcnc.snow.vm.utils.LoggingUtils; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * CallStack manages the stack frames, maintaining the function call hierarchy. + */ +public class CallStack { + private static final int MAX_STACK_DEPTH = 1024; // Stack overflow protection + private final Deque stack = new ArrayDeque<>(); + /** + * Default constructor for creating an instance of CallStack. + * This constructor is empty as no specific initialization is required. + */ + public CallStack() { + // Empty constructor + } + + /** + * Pushes a new stack frame onto the call stack. + * + * @param frame The stack frame to be pushed. + * @throws StackOverflowError If the stack exceeds the maximum allowed depth. + */ + public void pushFrame(StackFrame frame) { + if (stack.size() >= MAX_STACK_DEPTH) { + throw new StackOverflowError("Call stack overflow. Maximum depth: " + MAX_STACK_DEPTH); + } + stack.push(frame); + } + + /** + * Pops the current stack frame from the call stack. + * + * @return The popped stack frame. + */ + public StackFrame popFrame() { + if (stack.isEmpty()) { + throw new IllegalStateException("Call stack is empty, cannot pop."); + } + // Kill this StackFrame LocalVariableStore + + return stack.pop(); + } + + /** + * Retrieves the current (top) stack frame without removing it. + * + * @return The top stack frame. + */ + public StackFrame peekFrame() { + if (stack.isEmpty()) { + throw new IllegalStateException("Call stack is empty, cannot peek."); + } + return stack.peek(); + } + + /** + * Takes a snapshot of the current call stack. + * Useful for debugging during exceptions. + * + * @return A string representing the call stack snapshot. + */ + public String takeSnapshot() { + StringBuilder snapshot = new StringBuilder("--- Call Stack Snapshot ---\n"); + for (StackFrame frame : stack) { + snapshot.append("Method: ").append(frame.getMethodContext().methodName()) + .append(", Return Address: ").append(frame.getReturnAddress()) + .append(", Locals: ").append(frame.getLocalVariableStore().getLocalVariables()) + .append("\n"); + } + return snapshot.toString(); + } + + /** + * Checks if the call stack is empty. + * + * @return true if empty, false otherwise. + */ + public boolean isEmpty() { + return stack.isEmpty(); + } + + /** + * Prints the current state of the call stack. + */ + public void printCallStack() { + LoggingUtils.logInfo("--- Call Stack State ---", "\n"); + for (StackFrame frame : stack) { + frame.printFrame(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java b/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java new file mode 100644 index 0000000..a9bf75f --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java @@ -0,0 +1,117 @@ +package org.jcnc.snow.vm.module; + +import org.jcnc.snow.vm.gui.LocalVariableStoreSwing; +import org.jcnc.snow.vm.utils.LoggingUtils; +import org.jcnc.snow.vm.engine.VMMode; + +import java.util.ArrayList; +import java.util.Objects; + +/** + * The {@code LocalVariableStore} represents a simple dynamically-sized + * local-variable table (frame locals) of the VM. + * + *

    It supports random access via {@link #setVariable(int, Object)} + * / {@link #getVariable(int)} and can compact itself + * by trimming trailing {@code null} slots after execution has finished.

    + */ +public class LocalVariableStore { + + private final ArrayList localVariables; + private final VMMode vmMode; + + /* ---------- construction ---------- */ + + public LocalVariableStore(VMMode vmMode, int initialCapacity) { + this.localVariables = new ArrayList<>(initialCapacity); + this.vmMode = vmMode; + handleMode(); + } + + public LocalVariableStore(VMMode vmMode) { + this.localVariables = new ArrayList<>(); + this.vmMode = vmMode; + handleMode(); + } + + /* ---------- public API ---------- */ + + /** Sets the value at {@code index}, expanding the list if necessary. */ + public void setVariable(int index, Object value) { + ensureCapacity(index + 1); + localVariables.set(index, value); + } + + /* ------------------------------------------------------------ + * 兼容早期实现:VM 指令译码器可直接调用 store / load + * 而无需关心内部命名差异。 + * ------------------------------------------------------------ */ + public void store(int index, Object value) { setVariable(index, value); } + public Object load(int index) { return getVariable(index); } + + /** Returns the value at {@code index}. */ + public Object getVariable(int index) { + /* 修改点 #1 —— 自动扩容以避免 LOAD 越界异常 */ + if (index < 0) + throw new IndexOutOfBoundsException("Negative LV index: " + index); + ensureCapacity(index + 1); + return localVariables.get(index); // 可能为 null,符合 JVM 语义 + } + + /** Exposes the backing list (read-only preferred). */ + public ArrayList getLocalVariables() { + return localVariables; + } + + /** Prints every slot to the logger. */ + public void printLv() { + if (localVariables.isEmpty()) { + LoggingUtils.logInfo("Local variable table is empty", ""); + return; + } + LoggingUtils.logInfo("\n### VM Local Variable Table:", ""); + for (int i = 0; i < localVariables.size(); i++) { + LoggingUtils.logInfo("", + String.format("%d: %s", i, localVariables.get(i))); + } + } + + /** Clears all variables (used when a stack frame is popped). */ + public void clearVariables() { + localVariables.clear(); + } + + /** + * Compacts the table by removing trailing {@code null} slots. + *

    Call this once after program termination (e.g. in + * {@code VirtualMachineEngine.execute()} before printing) to get + * cleaner debug output without affecting execution-time indices.

    + */ + public void compact() { + /* 修改点 #2 —— 仅删除“尾部” null,而不是整表过滤 */ + int i = localVariables.size() - 1; + while (i >= 0 && localVariables.get(i) == null) { + localVariables.remove(i); + i--; + } + } + + + /* ---------- internal helpers ---------- */ + + /** Ensures backing list can hold {@code minCapacity} slots. */ + private void ensureCapacity(int minCapacity) { + /* 修改点 #3 —— 使用 while 循环填充 null,确保 slot 可随机写入 */ + while (localVariables.size() < minCapacity) { + localVariables.add(null); + } + } + + /** Mode-specific UI hook (unchanged). */ + private void handleMode() { + /* no-op */ + if (Objects.requireNonNull(vmMode) == VMMode.DEBUG) { + LocalVariableStoreSwing.display(this, "Local Variable Table"); + } + } +} diff --git a/src/main/java/org/jcnc/snow/vm/module/MethodContext.java b/src/main/java/org/jcnc/snow/vm/module/MethodContext.java new file mode 100644 index 0000000..bf72711 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/module/MethodContext.java @@ -0,0 +1,40 @@ +package org.jcnc.snow.vm.module; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents metadata about the current method invocation for debugging purposes. + *

    + * This class holds information about the method name and its arguments, which can be useful for debugging + * and tracking method invocations in the virtual machine context. If no arguments are provided, an empty list + * is used to avoid null references. + *

    + * + * @param methodName The name of the method being invoked. + * @param arguments The list of arguments passed to the method. If null, an empty list is used. + */ +public record MethodContext(String methodName, List arguments) { + + /** + * Constructs a new MethodContext with the provided method name and arguments. + * If the provided arguments are null, an empty list is used instead. + * + * @param methodName The name of the method being invoked. + * @param arguments The list of arguments passed to the method. If null, an empty list is used. + */ + public MethodContext { + arguments = arguments != null ? arguments : new ArrayList<>(); + } + + /** + * Returns a string representation of the method invocation. + * The format is: "Method: {methodName}, Args: {arguments}" + * + * @return A string representation of the method invocation, including its name and arguments. + */ + @Override + public String toString() { + return "Method: " + methodName + ", Args: " + arguments; + } +} diff --git a/src/main/java/org/jcnc/snow/vm/module/OperandStack.java b/src/main/java/org/jcnc/snow/vm/module/OperandStack.java new file mode 100644 index 0000000..347387f --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/module/OperandStack.java @@ -0,0 +1,103 @@ +package org.jcnc.snow.vm.module; + +import org.jcnc.snow.vm.utils.LoggingUtils; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.EmptyStackException; + +/** + * OperandStack class provides the stack management implementation. + *

    + * It uses Deque (ArrayDeque) to implement basic stack operations, including push, pop, checking if the stack is empty, + * getting the stack size, and printing the stack's current state. + *

    + */ +public class OperandStack { + private final Deque stack = new ArrayDeque<>(); + + /** + * Default constructor for creating an instance of OperandStack. + * This constructor is empty as no specific initialization is required. + */ + public OperandStack() { + // Empty constructor + } + + /** + * Push operation, adds an integer value to the stack. + *

    + * The push operation is implemented using Deque's push method to add the integer value to the top of the stack. + *

    + * + * @param value The value to be pushed onto the stack. + */ + public void push(Object value) { + stack.push(value); + } + + /** + * Pop operation, removes and returns the top element of the stack. + *

    + * The pop operation is implemented using Deque's pop method. If the stack is empty, an exception will be thrown. + *

    + * + * @return The top element of the stack. + * @throws IllegalStateException If the stack is empty, an exception is thrown indicating that the pop operation cannot be performed. + */ + public Object pop() { + if (stack.isEmpty()) { + throw new IllegalStateException("Stack is empty, cannot pop"); + } + return stack.pop(); + } + + + /** + * Checks if the stack is empty. + *

    + * The isEmpty method of Deque is used to check if the stack is empty. + *

    + * + * @return Returns true if the stack is empty, otherwise false. + */ + public boolean isEmpty() { + return stack.isEmpty(); + } + + /** + * Gets the current size of the stack. + *

    + * The size method of Deque is used to get the size of the stack, which is the number of elements in the stack. + *

    + * + * @return The number of elements in the stack. + */ + public int size() { + return stack.size(); + } + + /** + * Prints the current state of the stack. + *

    + * Logs the list of all elements in the stack, showing its content. Uses LoggingUtils to log the information. + *

    + */ + public void printOperandStack() { + LoggingUtils.logInfo("Operand Stack state:", stack + "\n"); + } + + /** + * Retrieves the top value of the stack without removing it. + *

    This method returns the top element of the stack without modifying the stack itself.

    + * + * @return The top value of the stack. + * @throws EmptyStackException if the stack is empty. + */ + public Object peek() { + if (stack.isEmpty()) { + throw new EmptyStackException(); + } + return stack.peek(); + } +} diff --git a/src/main/java/org/jcnc/snow/vm/module/StackFrame.java b/src/main/java/org/jcnc/snow/vm/module/StackFrame.java new file mode 100644 index 0000000..39e708c --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/module/StackFrame.java @@ -0,0 +1,105 @@ +package org.jcnc.snow.vm.module; + +import org.jcnc.snow.vm.utils.LoggingUtils; + +/** + * The {@code StackFrame} class represents a single frame in the call stack during program execution. + * It holds the execution context for a method invocation, including information such as the return + * address, local variables, operand stack, and method context. + * + *

    This class encapsulates the state of a method call, including the local variables, operand stack, + * and metadata regarding the method being invoked. It provides a mechanism for managing method invocation + * details in a virtual machine or interpreter context.

    + */ +public class StackFrame { + + /** + * The return address indicates the position in the program to return to after the method execution completes. + */ + private final int returnAddress; + + /** + * The {@code LocalVariableStore} holds the local variables specific to the current method invocation. + */ + private final LocalVariableStore localVariableStore; + + /** + * The {@code OperandStack} stores the operand stack for the method execution, which contains temporary + * values used during the evaluation of expressions. + */ + private final OperandStack operandStack; + + /** + * The {@code MethodContext} contains metadata about the method, such as its name and the method's arguments. + */ + private final MethodContext methodContext; + + /** + * Constructs a new {@code StackFrame} with the specified return address, local variable store, and method context. + * + * @param returnAddress The address to return to after the method execution. It is the point in the program + * where control will resume once the method has completed. + * @param localVariableStore The local variable store that holds all local variables for the current method + * invocation. It provides a way to access and manipulate local variables. + * @param methodContext The method context providing metadata for the method invocation, including the method + * name, parameter types, and other relevant data. + */ + public StackFrame(int returnAddress, LocalVariableStore localVariableStore, MethodContext methodContext) { + this.returnAddress = returnAddress; + this.localVariableStore = localVariableStore; + this.operandStack = new OperandStack(); + this.methodContext = methodContext; + } + + /** + * Retrieves the return address for the current method invocation. This address indicates where control should + * return after the method completes execution. + * + * @return The return address as an integer. + */ + public int getReturnAddress() { + return returnAddress; + } + + /** + * Retrieves the local variable store associated with this method invocation. The store contains the local + * variables that are specific to the current execution context. + * + * @return The local variable store for the current method. + */ + public LocalVariableStore getLocalVariableStore() { + return localVariableStore; + } + + /** + * Retrieves the operand stack used by the current method invocation. The operand stack contains temporary values + * required for evaluating expressions and managing method execution. + * + * @return The operand stack used during the method execution. + */ + public OperandStack getOperandStack() { + return operandStack; + } + + /** + * Retrieves the method context for the current method invocation. The method context includes metadata such as + * the method's name, parameter types, and other relevant data for the method execution. + * + * @return The method context that describes the invoked method. + */ + public MethodContext getMethodContext() { + return methodContext; + } + + /** + * Prints the details of the stack frame, including the return address, method context, local variables, and + * operand stack. This method is useful for debugging and inspecting the state of a method invocation. + */ + public void printFrame() { +// LoggingUtils.logInfo("----- Stack Frame -----", ""); +// LoggingUtils.logInfo("Return Address:", String.valueOf(returnAddress)); +// LoggingUtils.logInfo("Method Context:", methodContext.toString()); +// LoggingUtils.logInfo("Local Variables:", localVariableStore.getLocalVariables().toString()); +// operandStack.printOperandStack(); + } +} diff --git a/src/main/java/org/jcnc/snow/vm/utils/LoggingUtils.java b/src/main/java/org/jcnc/snow/vm/utils/LoggingUtils.java new file mode 100644 index 0000000..2367cb7 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/utils/LoggingUtils.java @@ -0,0 +1,56 @@ +package org.jcnc.snow.vm.utils; + +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * The LoggingUtils class provides logging functionality, supporting different log levels for output. + * This class uses Java's built-in logging system for logging, supporting both console output and log file recording. + *

    + * This class offers two methods for logging at different levels: + * 1. Error logs: Used for recording error messages, outputting them to both the console and log file. + * 2. Information logs: Used for recording general information, outputting to the console. + *

    + */ +public class LoggingUtils { + // Logger instance using Java's built-in Logger class + private static final Logger LOGGER = Logger.getLogger(LoggingUtils.class.getName()); + + /** + * Default constructor for creating an instance of LoggingUtils. + * This constructor is empty as no specific initialization is required. + */ + public LoggingUtils() { + // Empty constructor + } + + /** + * Logs an error message, recording it to both the console and log file. + *

    + * This method is used to log error-level messages. It outputs the error message to the console and logs it to a file, + * allowing developers to review error details. + *

    + * + * @param message The error message detailing the issue. + */ + public static void logError(String message) { + // Output the error message to the console + System.err.println("Error: " + message); + // Log the error message with SEVERE level to the log file + LOGGER.log(Level.SEVERE, message); + } + + /** + * Logs an informational message. + *

    + * This method is used to log general information. It outputs the specified message to the console for developers to review. + *

    + * + * @param title The log title, used to indicate the subject or type of the log. + * @param message The information content to be logged. + */ + public static void logInfo(String title, String message) { + // Output the informational message to the console + System.out.println(title + message); + } +} diff --git a/src/main/java/org/jcnc/snow/vm/utils/VMStateLogger.java b/src/main/java/org/jcnc/snow/vm/utils/VMStateLogger.java new file mode 100644 index 0000000..67bed0f --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/utils/VMStateLogger.java @@ -0,0 +1,48 @@ +package org.jcnc.snow.vm.utils; + +import org.jcnc.snow.vm.engine.VirtualMachineEngine; + +/** + * Utility class for logging the state of a virtual machine. + *

    + * The {@code VMStateLogger} class provides methods to log and display + * the current execution state of a {@link VirtualMachineEngine}, including + * its stack, local variables, and other relevant details. + *

    + *

    + * This class serves as a wrapper around the {@link VMUtils#printVMState(VirtualMachineEngine)} + * method, simplifying the logging process. + *

    + */ +public class VMStateLogger { + + /** + * Default constructor for creating an instance of {@code VMStateLogger}. + *

    + * This constructor is intentionally left empty as no specific initialization is required. + *

    + */ + public VMStateLogger() { + // Empty constructor + } + + /** + * Prints the current state of the virtual machine. + *

    + * This method logs the current state of the provided {@link VirtualMachineEngine}, + * including the stack contents, local variables, and other relevant execution details. + * It delegates the actual state printing to the {@link VMUtils#printVMState(VirtualMachineEngine)} method. + *

    + * + * @param virtualMachineEngine The virtual machine instance whose state will be printed. + * Must not be {@code null}. + * @throws IllegalArgumentException If the provided {@code virtualMachineEngine} is {@code null}. + */ + public static void printVMState(VirtualMachineEngine virtualMachineEngine) { + if (virtualMachineEngine == null) { + throw new IllegalArgumentException("VirtualMachineEngine instance cannot be null."); + } + + VMUtils.printVMState(virtualMachineEngine); + } +} diff --git a/src/main/java/org/jcnc/snow/vm/utils/VMUtils.java b/src/main/java/org/jcnc/snow/vm/utils/VMUtils.java new file mode 100644 index 0000000..21b526d --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/utils/VMUtils.java @@ -0,0 +1,42 @@ +package org.jcnc.snow.vm.utils; + +import org.jcnc.snow.vm.engine.VirtualMachineEngine; + +/** + * Utility class for virtual machine operations. + *

    + * This class provides helper methods for managing and inspecting the state + * of a {@link VirtualMachineEngine}. It is intended for debugging and + * monitoring purposes. + *

    + *

    + * The {@code VMUtils} class is not meant to be instantiated, as all methods + * are static. + *

    + */ +public class VMUtils { + /** + * Default constructor for creating an instance of VMUtils. + * This constructor is empty as no specific initialization is required. + */ + public VMUtils() { + // Empty constructor + } + + /** + * Prints the current state of the virtual machine, including the stack and local variables. + * This method provides a snapshot of the virtual machine's current execution context, + * useful for debugging or inspection purposes. + * + * @param vm The virtual machine instance whose state will be printed. + * The vm parameter must not be null. + * @throws IllegalArgumentException If the provided vm is null. + */ + public static void printVMState(VirtualMachineEngine vm) { + if (vm == null) { + throw new IllegalArgumentException("VirtualMachineEngine instance cannot be null."); + } + vm.printStack(); + vm.printLocalVariables(); + } +}