增加注释

This commit is contained in:
Luke 2025-05-11 23:22:53 +08:00
parent c0d47ae3ef
commit 2b8f648e54
11 changed files with 392 additions and 277 deletions

View File

@ -6,93 +6,139 @@ import java.util.ArrayList;
import java.util.List;
/**
* An intermediaterepresentation function (IRFunction).
*
* <p>For every sourcelevel function we build one IRFunction that records:</p>
* <ul>
* <li>the functions name,</li>
* <li>a list of IR instructions (the <em>body</em>),</li>
* <li>a list of <strong>formal parameters</strong> (in declaration order),</li>
* <li>a counter that hands out unique register numbers.</li>
* </ul>
* 表示单个函数的中间表示IR
* <p>
* IRFunction 跟踪代码生成和优化所需的所有信息
* 包括函数标识符IR 指令序列
* 声明参数列表以及生成唯一虚拟寄存器的机制
* </p>
*/
public class IRFunction {
/* ---------- basic info ---------- */
/** function name */
/**
* 函数名对应源级函数的标识
*/
private final String name;
/** linear list of IR instructions */
/**
* IR 指令列表组成函数体
*/
private final List<IRInstruction> body = new ArrayList<>();
/* ---------- virtualregister management ---------- */
/** running counter for new virtual registers */
/**
* 用于生成新的虚拟寄存器编号的计数器
*/
private int regCounter = 0;
/** list of formalparameter registers in declaration order */
/**
* 正式参数所对应的虚拟寄存器列表按声明顺序排列
*/
private final List<IRVirtualRegister> parameters = new ArrayList<>();
/* ---------- construction ---------- */
/**
* 构造一个具有指定名称的 IRFunction 实例
*
* @param name 要关联的函数名称
*/
public IRFunction(String name) {
this.name = name;
}
/* ---------- register helpers ---------- */
/** Allocates and returns a brandnew virtual register. */
/**
* 分配一个新的虚拟寄存器
* 每次调用会生成一个带有唯一编号的 IRVirtualRegister
*
* @return 新分配的虚拟寄存器
*/
public IRVirtualRegister newRegister() {
return new IRVirtualRegister(regCounter++);
}
/* ---------- parameter helpers ---------- */
/**
* Adds a virtual register to the formalparameter list.
* Call this once for each parameter <em>in declaration order</em>
* when building the IR.
* 将一个虚拟寄存器添加到函数的正式参数列表中
* <p>
* 应按源函数签名中参数的声明顺序逐一调用此方法
* </p>
*
* @param vr 表示函数某个参数的虚拟寄存器
*/
public void addParameter(IRVirtualRegister vr) {
parameters.add(vr);
}
/** Returns the immutable list of formal parameters. */
/**
* 获取函数正式参数的只读列表
*
* @return 按声明顺序排列的虚拟寄存器列表
*/
public List<IRVirtualRegister> parameters() {
return parameters;
return List.copyOf(parameters);
}
/* ---------- body helpers ---------- */
/**
* 向函数体末尾追加一条 IR 指令
*
* @param inst 要追加的 IRInstruction 实例
*/
public void add(IRInstruction inst) {
body.add(inst);
}
/** Appends an IR instruction to the function body. */
public void add(IRInstruction inst) { body.add(inst); }
/**
* 获取函数体中所有指令的只读列表
*
* @return 表示函数体的 IRInstruction 列表
*/
public List<IRInstruction> body() {
return List.copyOf(body);
}
/** Returns all instructions. */
public List<IRInstruction> body() { return body; }
/**
* 获取函数的源级名称
*
* @return 函数名称
*/
public String name() {
return name;
}
/* ---------- meta data ---------- */
public String name() { return name; }
public int registerCount() { return regCounter; }
/* ---------- debugging ---------- */
/**
* 获取已分配的虚拟寄存器总数
*
* @return 虚拟寄存器计数
*/
public int registerCount() {
return regCounter;
}
/**
* 以IR代码表示示例
* <pre>
* func 名称(%0, %1, ...) {
* 指令0
* 指令1
* ...
* }
* </pre>
*
* @return 函数的 IR 文本表示
*/
@Override
public String toString() {
// Print function header with explicit parameter list: func name(%0, %1) {
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(", ");
if (i < parameters.size() - 1) {
sb.append(", ");
}
}
sb.append(") {\n");
// Body instructions indented by two spaces
body.forEach(i -> sb.append(" ").append(i).append('\n'));
return sb.append('}').toString();
for (IRInstruction inst : body) {
sb.append(" ").append(inst).append('\n');
}
sb.append('}');
return sb.toString();
}
}
}

View File

@ -7,40 +7,56 @@ import java.util.List;
* IRInstruction 所有 IR中间表示指令的抽象基类
* <p>
* 本类定义了编译器中间表示系统中所有指令的基本结构和行为
* 子类需实现具体的操作码Opcode和指令行为
* 这是采用访问者模式设计的一部分便于对指令进行统一访问和操作
* 具体指令通过继承此类并实现各自的操作码Opcode和访问者方法
* 以支持统一的指令处理和访问模式
* </p>
*/
public abstract class IRInstruction {
/**
* 获取该指令的操作码Opcode
* 每条具体指令子类都必须实现此方法以返回对应的操作码枚举
* <p>
* 每个具体指令子类必须实现此方法返回对应的 IROpCode 枚举值
* </p>
*
* @return 表示该指令类型的 IROpCode 实例
* @return 表示指令类型的 IROpCode 实例
*/
public abstract IROpCode op();
/**
* 获取该指令的目标虚拟寄存器destination register
* 默认为 null部分子类如赋值运算类指令应重写以返回具体目标寄存器
* 获取指令的目标虚拟寄存器destination register
* <p>
* 默认实现返回 null只有具有目标寄存器的指令如赋值算术运算
* 应重写此方法以返回相应的 IRVirtualRegister
* </p>
*
* @return 指令的目标虚拟寄存器或为 null若无目标寄存器
* @return 目标虚拟寄存器若无目标寄存器则返回 null
*/
public IRVirtualRegister dest() { return null; }
public IRVirtualRegister dest() {
return null;
}
/**
* 获取该指令的操作数列表
* 默认为空列表子类应重写以提供实际使用的操作数集合
* 获取指令的操作数列表
* <p>
* 默认实现返回空列表具体指令子类应根据需要重写此方法
* 提供所有参与运算或调用的 IRValue 操作数集合
* </p>
*
* @return 一个包含所有操作数IRValue的列表
* @return 包含本指令所有操作数的列表
*/
public List<IRValue> operands() { return List.of(); }
public List<IRValue> operands() {
return List.of();
}
/**
* 接收一个 IRVisitor 实例实现访问者模式的入口
* 子类必须实现此方法以允许访问者根据具体类型进行相应处理
* 接受一个 IRVisitor 实例实现访问者模式的入口
* <p>
* 具体指令子类必须实现此方法以便 IRVisitor 根据指令类型
* 调用相应的访问逻辑
* </p>
*
* @param visitor 实现 IRVisitor 接口的访问者实例
* @param visitor 实现 IRVisitor 接口的访问者对象
*/
public abstract void accept(IRVisitor visitor);
}
}

View File

@ -1,95 +1,79 @@
package org.jcnc.snow.compiler.ir.core;
/**
* IROpCode IR 层支持的操作码Opcode枚举类型
* {@code IROpCode} 枚举类型定义了中间表示IR层支持的全部操作码
* <p>
* 本枚举类定义了中间表示层可用的所有基本指令类型 {@link IRInstruction} 使用
* 每个枚举值代表一种语义明确的 IR 操作涵盖不同位宽的整数和浮点算术逻辑数据操作和控制流指令
* 可根据后续需求继续扩展
* 每个操作码代表一种低层次语义明确的中间指令用于构建目标函数的中间表示
* 这些操作涵盖了不同数据位宽的整数与浮点数算术运算逻辑与比较操作
* 数据加载与存储指令控制流指令如跳转条件跳转标签
* 以及函数调用与返回等功能
* <p>
* 本枚举用于 {@link IRInstruction} 体系结构中 IR 指令识别和转换的核心部分
* </p>
*/
public enum IROpCode {
/* ───── 算术运算 (8位整数: byte) ───── */
/** 8位整型加法 (byte): a = b + c */
ADD_B8,
/** 8位整型减法 */
SUB_B8,
/** 8位整型乘法 */
MUL_B8,
/** 8位整型除法 */
DIV_B8,
/** 8位整型取负 */
NEG_B8,
/* ───── 算术运算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) ───── */
/** 16位整型加法 (short) */
ADD_S16,
/** 16位整型减法 */
SUB_S16,
/** 16位整型乘法 */
MUL_S16,
/** 16位整型除法 */
DIV_S16,
/** 16位整型取负 */
NEG_S16,
/* ───── 算术运算16位整数short───── */
ADD_S16, // 16位整型加法
SUB_S16, // 16位整型减法
MUL_S16, // 16位整型乘法
DIV_S16, // 16位整型除法
NEG_S16, // 16位整型取负
/* ───── 算术运算 (32位整数: int) ───── */
/** 32位整型加法 (int) */
ADD_I32,
/** 32位整型减法 */
SUB_I32,
/** 32位整型乘法 */
MUL_I32,
/** 32位整型除法 */
DIV_I32,
/** 32位整型取负 */
NEG_I32,
/* ───── 算术运算32位整数int───── */
ADD_I32, // 32位整型加法
SUB_I32, // 32位整型减法
MUL_I32, // 32位整型乘法
DIV_I32, // 32位整型除法
NEG_I32, // 32位整型取负
/* ───── 算术运算 (64位整数: long) ───── */
/** 64位整型加法 (long) */
ADD_L64,
/** 64位整型减法 */
SUB_L64,
/** 64位整型乘法 */
MUL_L64,
/** 64位整型除法 */
DIV_L64,
/** 64位整型取负 */
NEG_L64,
/* ───── 算术运算64位整数long───── */
ADD_L64, // 64位整型加法
SUB_L64, // 64位整型减法
MUL_L64, // 64位整型乘法
DIV_L64, // 64位整型除法
NEG_L64, // 64位整型取负
/* ───── 算术运算 (单精度浮点: float) ───── */
ADD_F32,
SUB_F32,
MUL_F32,
DIV_F32,
NEG_F32,
/* ───── 算术运算32位浮点数float───── */
ADD_F32, // 32位浮点加法
SUB_F32, // 32位浮点减法
MUL_F32, // 32位浮点乘法
DIV_F32, // 32位浮点除法
NEG_F32, // 32位浮点取负
/* ───── 算术运算 (双精度浮点: double) ───── */
ADD_D64,
SUB_D64,
MUL_D64,
DIV_D64,
NEG_D64,
/* ───── 算术运算64位浮点数double───── */
ADD_D64, // 64位浮点加法
SUB_D64, // 64位浮点减法
MUL_D64, // 64位浮点乘法
DIV_D64, // 64位浮点除法
NEG_D64, // 64位浮点取负
/* ───── 逻辑/比较运算 ───── */
CMP_EQ,
CMP_NE,
CMP_LT,
CMP_GT,
CMP_LE,
CMP_GE,
/* ───── 逻辑与比较运算指令 ───── */
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,
/* ───── 数据访问与常量操作 ───── */
LOAD, // 从内存加载数据至寄存器
STORE, // 将寄存器数据写回内存
CONST, // 将常量写入目标寄存器
/* ───── 控制流 ───── */
JUMP,
JUMP_IF_ZERO,
LABEL,
/* ───── 控制流指令 ───── */
JUMP, // 无条件跳转至标签
JUMP_IF_ZERO, // 若寄存器为零则跳转
LABEL, // 标签定义
/* ───── 函数调用相关 ───── */
CALL,
RET
/* ───── 函数调用与返回 ───── */
CALL, // 函数调用
RET // 函数返回
}

View File

@ -5,20 +5,25 @@ import org.jcnc.snow.compiler.ir.instruction.IRJumpInstruction;
import org.jcnc.snow.compiler.ir.instruction.IRReturnInstruction;
/**
* IRPrinter 一个示例访问者Visitor用于打印 IR 指令的信息
* {@code IRPrinter} 是一个用于打印 IR 指令的访问者实现
* <p>
* 本类实现了 IRVisitor 接口用于演示如何使用访问者模式处理 IRInstruction 子类
* 每个 visit 方法负责处理特定类型的 IR 指令此实现以控制台输出System.out形式展示指令内容
* 本类实现 {@link IRVisitor} 接口通过覆盖各类指令的访问方法
* 提供对不同类型 IR 指令的格式化输出通常用于调试或测试
* 默认行为是在控制台System.out输出指令的基本信息
* </p>
* <p>
* 子类可扩展该类以进行更复杂的打印或格式化或添加对更多 IR 指令类型的支持
* 可通过继承该类进一步扩展对更多指令类型的支持或重写输出格式以适配不同的前端/后端需求
* </p>
*/
public abstract class IRPrinter implements IRVisitor {
/**
* 处理 IRAddInstruction 类型的指令
* 打印加法指令的基本信息
* 访问 {@link IRAddInstruction} 加法指令
* <p>
* 默认输出形式为 "Add: <inst>"其中 <inst> 为指令对象的字符串表示
* </p>
*
* @param inst 被访问的加法指令
* @param inst 加法 IR 指令实例
*/
@Override
public void visit(IRAddInstruction inst) {
@ -26,10 +31,12 @@ public abstract class IRPrinter implements IRVisitor {
}
/**
* 处理 IRJumpInstruction 类型的指令
* 打印跳转指令的基本信息
* 访问 {@link IRJumpInstruction} 跳转指令
* <p>
* 默认输出形式为 "Jump: <inst>"
* </p>
*
* @param inst 被访问的跳转指令
* @param inst 跳转 IR 指令实例
*/
@Override
public void visit(IRJumpInstruction inst) {
@ -37,14 +44,16 @@ public abstract class IRPrinter implements IRVisitor {
}
/**
* 处理 IRReturnInstruction 类型的指令
* 打印返回指令的基本信息
* 访问 {@link IRReturnInstruction} 返回指令
* <p>
* 默认输出形式为 "Return: <inst>"
* </p>
*
* @param inst 被访问的返回指令
* @param inst 返回 IR 指令实例
*/
@Override
public void visit(IRReturnInstruction inst) {
System.out.println("Return: " + inst);
}
}
}

View File

@ -5,42 +5,51 @@ import java.util.Collections;
import java.util.List;
/**
* IRProgram 表示一份完整的中间表示IR程序
* {@code IRProgram} 表示一份完整的中间表示Intermediate Representation, IR程序
* <p>
* 本类作为编译器后端的主要数据结构之一承载着所有 IRFunction 的集合
* 每个函数封装一段 IR 指令序列整体表示源代码编译后的结构化中间结果
* 通常用于代码生成优化分析等阶段
* 它作为编译器后端处理阶段的核心结构承载所有由源代码翻译得到的 {@link IRFunction} 实例
* 形成整体性的中间表示单元便于进行后续的优化目标代码生成或静态分析
* </p>
*/
public final class IRProgram {
/** 存储程序中所有函数的列表 */
/**
* 存储程序中所有函数的有序集合
*/
private final List<IRFunction> functions = new ArrayList<>();
/**
* IRProgram 中添加一个函数
* 将一个 {@link IRFunction} 添加到程序中
* <p>
* 函数会按添加顺序保留在内部集合中
* </p>
*
* @param irFunction 要添加的 IRFunction 对象
* @param irFunction IR 函数对象
*/
public void add(IRFunction irFunction) {
functions.add(irFunction);
}
/**
* 获取程序中所有函数的不可变视图
* 获取程序中全部函数的只读视图
* <p>
* 返回的列表不能被外部修改确保 IRProgram 的封装性和安全性
* 外部调用者无法通过返回的列表修改函数集合从而确保封装性与结构完整性
* </p>
*
* @return 包含所有 IRFunction 的只读列表
* @return 不可变的函数列表
*/
public List<IRFunction> functions() {
return Collections.unmodifiableList(functions);
}
/**
* 将整个 IRProgram 转换为字符串表示方便打印或调试
* 每个函数调用其 toString 方法占据单独一行
* 返回该 IR 程序的字符串形式
* <p>
* 每个函数按其 {@code toString()} 表示输出换行分隔
* 通常用于调试与日志输出
* </p>
*
* @return 程序的字符串表示
* @return 表示整个 IR 程序的格式化字符串
*/
@Override
public String toString() {

View File

@ -5,24 +5,23 @@ import org.jcnc.snow.compiler.ir.value.IRLabel;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
/**
* IRValue 表示在中间表示IR系统中可作为指令操作数的基本单位
* {@code IRValue} 表示 IR 指令系统中可被操作的值类型
* <p>
* 本接口定义了 IR 指令可接受的值类型是一个值对象的抽象
* 所有实现 IRInstruction 的类在定义操作数时其元素类型均为 IRValue
* <p>
* 典型用途包括操作数跳转目标返回值函数参数等
* <p>
* 支持的具体子类型包括
* 它定义了所有 IR 指令在使用操作数参数结果或跳转目标时的统一抽象
* 实现该接口的类型可以作为 {@link IRInstruction} 中的操作数出现
* </p>
*
* <p>当前支持的 IR 值类型包括</p>
* <ul>
* <li>{@link IRVirtualRegister} 虚拟寄存器用于存储计算结果或中间值</li>
* <li>{@link IRConstant} 常量值如字面整型常数等</li>
* <li>{@link IRLabel} 跳转标签用于表示代码中的控制流目标</li>
* <li>{@link IRVirtualRegister}虚拟寄存器表示计算结果或中间变量</li>
* <li>{@link IRConstant}常量值表示不可变的字面量或数值</li>
* <li>{@link IRLabel}标签表示跳转指令的目标地址</li>
* </ul>
*
* <p>
* 本接口为 <code>sealed interface</code>仅允许指定的子类型实现它
* 保证了类型系统的封闭性便于编译器在处理 IRValue 时进行穷尽性检查
* 增强类型安全与维护性
* 该接口声明为 {@code sealed interface}限制只能被上述类型实现
* 种设计允许编译器对 {@code IRValue} 的使用进行静态穷尽性检查
* 有助于提升类型安全性与维护性
* </p>
*/
public sealed interface IRValue

View File

@ -3,57 +3,78 @@ package org.jcnc.snow.compiler.ir.core;
import org.jcnc.snow.compiler.ir.instruction.*;
/**
* IRVisitor 访问者接口用于对不同类型的 IR 指令进行操作
* {@code IRVisitor} 是中间表示IR指令体系的访问者接口
* <p>
* 本接口定义了访问者模式的核心机制通过将每种 IRInstruction 子类对应为一个独立的 visit 方法
* 可实现类型安全的指令操作逻辑分离例如打印优化代码生成等
* <p>
* 使用场景包括
* 它定义了访问者模式的核心机制通过对每种 {@link IRInstruction} 子类
* 提供独立的 {@code visit} 方法实现对指令的分发与处理
* 不同的访问者实现可用于执行不同任务例如
* </p>
* <ul>
* <li>IRPrinter打印每条 IR 指令内容</li>
* <li>IROptimizer IR 进行模式匹配和重写优化</li>
* <li>IRCodeGenerator IR 转换为目标平台指令</li>
* <li>{@code IRPrinter}打印指令内容</li>
* <li>{@code IROptimizer}分析与重写 IR 以优化性能</li>
* <li>{@code IRCodeGenerator}生成平台相关的机器码或汇编代码</li>
* </ul>
*
* 每添加一种新的指令子类通常也需在本接口中添加对应的 visit 方法以保持访问能力的完备
* <p>
* 每当添加新的 {@code IRInstruction} 子类应同步扩展该接口
* 以确保访问行为的一致性与完整性
* </p>
*/
public interface IRVisitor {
/**
* 访问加法指令简化形式
* 访问加法指令示例实现
*
* @param inst 加法指令实例
*/
void visit(IRAddInstruction inst);
/**
* 访问跳转指令
*
* @param inst 跳转指令实例
*/
void visit(IRJumpInstruction inst);
/**
* 访问返回指令简化形式
* 访问返回指令无返回值
*
* @param inst 返回指令实例
*/
void visit(IRReturnInstruction inst);
/**
* 访问通用的二元运算指令 ADD_I32SUB_I32
* 访问二元运算指令如加减乘除等
*
* @param inst 二元运算 IR 指令实例
*/
void visit(BinaryOperationInstruction inst);
/**
* 访问加载常量的指令将常量加载到虚拟寄存器
* 访问加载常量的指令
*
* @param inst 常量加载指令实例
*/
void visit(LoadConstInstruction inst);
/**
* 访问更通用的返回指令可带返回值
* 访问返回指令支持返回值
*
* @param inst 通用返回指令实例
*/
void visit(ReturnInstruction inst);
/**
* 访问一元运算指令 NEG_I32
* 访问一元运算指令如取负等
*
* @param inst 一元运算指令实例
*/
void visit(UnaryOperationInstruction inst);
/**
* 访问函数调用指令
*
* @param instruction 函数调用指令实例
*/
void visit(CallInstruction instruction);
}

View File

@ -6,28 +6,41 @@ import org.jcnc.snow.compiler.lexer.token.Token;
import java.util.List;
/**
* TokenScanner 接口定义所有词法扫描器的通用规范
* 每种 Token 类型如标识符数字字符串等都应该实现此接口
* {@code TokenScanner} 接口定义了所有词法扫描器的统一行为规范
* <p>
* 编译器前端中的词法分析阶段将源代码字符流解析为语义上有意义的记号token
* 每种类型的记号如标识符数字字符串符号等应有对应的 {@code TokenScanner} 实现类
* 词法分析器根据当前输入字符判断并分派给能处理该字符的扫描器进行处理
* </p>
* <p>
* 实现类通常会结合 {@link LexerContext} 提供的流访问与状态接口
* 完成一个完整 Token 的提取并将其添加到结果集中
* </p>
*/
public interface TokenScanner {
/**
* 判断当前字符是否可以由该扫描器处理
* 词法分析器会遍历所有已注册的 TokenScanner
* 用此方法来找到合适的处理器
* <p>
* 词法分析器会按顺序查询已注册的 {@code TokenScanner} 实例
* 使用该方法决定当前字符是否可由某个扫描器识别与处理
* </p>
*
* @param c 当前字符
* @param ctx 词法分析上下文提供字符流访问和辅助方法
* @return 如果该扫描器能处理当前字符则返回 true
* @param c 当前读取的字符
* @param ctx 当前词法分析上下文提供字符流和辅助状态
* @return 若该扫描器可处理当前字符则返回 {@code true}否则返回 {@code false}
*/
boolean canHandle(char c, LexerContext ctx);
/**
* 处理当前字符起始的 token并将生成的 Token 添加到列表中
* 实现中通常会读取多个字符直到确定该 token 的结束
* 处理以当前字符为起始的 token并将扫描结果添加至 tokens 列表中
* <p>
* 扫描器需消费一定数量的字符构建合法的 {@link Token} 实例
* 并调用 {@code tokens.add(...)} 添加至结果集中
* </p>
*
* @param ctx 词法分析上下文用于读取字符和获取位置信息
* @param tokens Token 列表扫描出的 token 应添加到该列表中
* @param ctx 当前词法上下文
* @param tokens 存储扫描结果的 token 列表
*/
void handle(LexerContext ctx, List<Token> tokens);
}
}

View File

@ -3,31 +3,35 @@ package org.jcnc.snow.compiler.lexer.core;
import org.jcnc.snow.compiler.lexer.base.TokenScanner;
/**
* {@code LexerContext} 是词法分析中的状态管理器负责追踪当前字符位置
* 行号列号并提供字符读取匹配等功能
* {@code LexerContext} 是词法分析阶段的上下文状态管理器
* <p>
* 它是 {@link LexerEngine} {@link TokenScanner}
* 之间共享的上下文对象用于读取字符并记录位置信息
* 该类提供对源代码字符流的读取访问追踪当前行号与列号
* 并支持字符匹配回看与指针推进等操作 {@link TokenScanner} 实现进行词法识别的重要支撑工具
* </p>
* <p>
* 所有源代码输入在构造时统一将 Windows 风格的换行符\r\n转换为 Unix 风格\n
* 保证换行行为一致性
* </p>
*/
public class LexerContext {
/** 源代码文本,换行符统一替换为 \n */
/** 源代码字符串,换行符已标准化为 \n */
private final String source;
/** 当前在源代码中的偏移位置 */
/** 当前扫描位置(从 0 开始的偏移) */
private int pos = 0;
/** 当前行号,从 1 开始计数 */
/** 当前行号,从 1 开始 */
private int line = 1;
/** 当前列号,从 1 开始计数 */
/** 当前列号,从 1 开始 */
private int col = 1;
/** 上一个字符的列号(用于生成准确的 Token 位置信息 */
/** 上一个字符对应的列号(用于位置精确记录 */
private int lastCol = 1;
/**
* 创建一个新的 {@code LexerContext} 实例并初始化源代码内容
* 所有 \r\nWindows 换行会被标准化为 \n
* 构造一个新的 {@code LexerContext} 实例并标准化换行符
*
* @param source 原始源代码字符串
*/
@ -36,19 +40,18 @@ public class LexerContext {
}
/**
* 判断是否已源代码末尾
* 判断是否已读取到源代码末尾
*
* @return 如果读取完毕返回 true否则返回 false
* @return 若已结束返回 {@code true}否则返回 {@code false}
*/
public boolean isAtEnd() {
return pos >= source.length();
}
/**
* 读取当前字符并将指针移动到下一个字符位置
* 同时更新行号与列号
* 消费当前字符并前进一个位置自动更新行列信息
*
* @return 当前字符若已结束返回 '\0'
* @return 当前字符若已结束则返回空字符'\0'
*/
public char advance() {
if (isAtEnd()) return '\0';
@ -64,28 +67,28 @@ public class LexerContext {
}
/**
* 查看当前字符但不前进指针
* 查看当前位置的字符但不前进
*
* @return 当前字符已结束返回 '\0'
* @return 当前字符结束则返回空字符
*/
public char peek() {
return isAtEnd() ? '\0' : source.charAt(pos);
}
/**
* 查看下一个字符但不会前进指针
* 查看下一个字符但不改变位置
*
* @return 下一个字符已结束返回 '\0'
* @return 下一个字符结束则返回空字符
*/
public char peekNext() {
return pos + 1 >= source.length() ? '\0' : source.charAt(pos + 1);
}
/**
* 如果当前字符是指定字符则前进指针并返回 true
* 若当前字符与期望字符相同则前进并返回 {@code true}否则不动并返回 {@code false}
*
* @param expected 匹配的字符
* @return 是否成功匹配并前进
* @param expected 期待匹配的字符
* @return 是否匹配成功并消费
*/
public boolean match(char expected) {
if (isAtEnd() || source.charAt(pos) != expected) return false;
@ -94,18 +97,18 @@ public class LexerContext {
}
/**
* 获取当前行号
* 获取当前位置的行号
*
* @return 当前行 1 开始计数
* @return 当前行 1 开始
*/
public int getLine() {
return line;
}
/**
* 获取当前列号
* 获取当前位置的列号
*
* @return 当前列 1 开始计数
* @return 当前列 1 开始
*/
public int getCol() {
return col;
@ -113,9 +116,8 @@ public class LexerContext {
/**
* 获取上一个字符所在的列号
* 主要用于生成换行符等 Token 的精确列号
*
* @return 上一个字符的列号
* @return 上一个字符对应的列位置
*/
public int getLastCol() {
return lastCol;

View File

@ -8,55 +8,58 @@ import java.util.ArrayList;
import java.util.List;
/**
* 词法分析器LexerEngine用于将源代码字符串转换为一系列语法标记Token
* {@code LexerEngine} 是编译器前端的词法分析器核心实现
* <p>
* 它使用多个 {@link TokenScanner} 实现类处理不同类型的词法单元数字标识符运算符注释等
* 并追踪位置信息行号列号以便后续语法分析或错误报告
* 它负责将源代码字符串按顺序扫描并转换为一系列 {@link Token} 实例
* 每个 Token 表示语法上可识别的最小单位如标识符关键字常量运算符等
* <p>
* 分析流程通过注册多个 {@link TokenScanner} 扫描器实现类型识别
* 并由 {@link LexerContext} 提供字符流与位置信息支持
* </p>
*/
public class LexerEngine {
/** 所有已识别的 Token含 EOF */
/** 扫描生成的 Token 序列(含 EOF */
private final List<Token> tokens = new ArrayList<>();
/** 扫描上下文,记录当前扫描位置、行列号等 */
/** 词法上下文,提供字符流读取与位置信息 */
private final LexerContext context;
/** Token 扫描器列表,按优先级顺序处理不同类型的 Token */
/** Token 扫描器集合,按优先级顺序组织,用于识别不同类别的 Token */
private final List<TokenScanner> scanners;
/**
* 创建并初始化 LexerEngine构建扫描器列表并立即开始扫描源代码
* 构造一个 {@code LexerEngine} 实例并初始化内部扫描器与上下文
* 调用构造函数时即开始词法扫描生成完整 Token 序列
*
* @param source 输入的源代码字符串
* @param source 原始源代码文本
*/
public LexerEngine(String source) {
this.context = new LexerContext(source);
// 注册所有 Token 扫描器按优先级顺序
// 按优先级注册所有支持的 Token 扫描器
this.scanners = List.of(
new WhitespaceTokenScanner(), // 跳过空格制表符等
new NewlineTokenScanner(), // 处理换行符生成 NEWLINE Token
new CommentTokenScanner(), // 处理行注释与块注释
new NumberTokenScanner(), // 处理数字字面量整数小数
new IdentifierTokenScanner(), // 处理标识符及关键字
new StringTokenScanner(), // 处理字符串字面
new OperatorTokenScanner(), // 处理运算符==!=&&
new SymbolTokenScanner(), // 处理符号括号逗号等
new UnknownTokenScanner() // 默认处理器捕捉未知字符
new CommentTokenScanner(), // 处理单行/多行注释
new NumberTokenScanner(), // 识别整数与浮点数
new IdentifierTokenScanner(), // 识别标识符与关键字
new StringTokenScanner(), // 处理字符串
new OperatorTokenScanner(), // 处理运算符
new SymbolTokenScanner(), // 处理括号分号等符号
new UnknownTokenScanner() // 捕捉无法识别的字符
);
scanAllTokens();
}
/**
* 扫描整个源代码识别并构建 Token 序列
* 执行主扫描流程将整个源代码转换为 Token 序列
* <p>
* 每次迭代
* <ul>
* <li>获取当前位置字符</li>
* <li>尝试交由扫描器处理</li>
* <li>若无扫描器可处理则标记为 UNKNOWN</li>
* </ul>
* 最后追加 EOF文件结束Token
* 每次扫描尝试依次使用各个 {@link TokenScanner}直到某一扫描器能够处理当前字符
* 若无匹配扫描器交由 {@code UnknownTokenScanner} 处理
* 扫描结束后自动附加一个 EOF文件结束Token
* </p>
*/
private void scanAllTokens() {
while (!context.isAtEnd()) {
@ -73,13 +76,12 @@ public class LexerEngine {
tokens.add(Token.eof(context.getLine()));
}
/**
* 返回所有扫描到的 Token EOF
* 返回词法分析生成的所有 Token EOF
*
* @return Token 的不可变副本列表
*/
public List<Token> getAllTokens() {
return List.copyOf(tokens);
}
}
}

View File

@ -8,12 +8,22 @@ import java.util.List;
import java.util.function.Predicate;
/**
* 抽象 TokenScanner 实现封装常用的扫描行为和模板方法
* 子类只需实现 {@code scanToken} 方法关注 token 的核心逻辑
* 行列追踪和 token 添加交由此类统一处理
* {@code AbstractTokenScanner} {@link TokenScanner} 的抽象实现
* 封装了常用的扫描行为与模板逻辑简化子类的实现负担
* <p>
* 子类只需实现 {@link #scanToken(LexerContext, int, int)} 方法
* 专注于处理具体的 Token 构造逻辑
* 而位置信息提取Token 添加等通用操作由本类统一完成
* </p>
*/
public abstract class AbstractTokenScanner implements TokenScanner {
/**
* 处理当前字符起始的 Token附带行列信息并加入 Token 列表
*
* @param ctx 当前词法分析上下文
* @param tokens 存储扫描结果的 Token 列表
*/
@Override
public void handle(LexerContext ctx, List<Token> tokens) {
int line = ctx.getLine();
@ -25,21 +35,25 @@ public abstract class AbstractTokenScanner implements TokenScanner {
}
/**
* 子类实现的具体扫描逻辑
* 抽象方法由子类实现具体的扫描逻辑
* <p>
* 实现应消费一定字符并根据规则构造 Token
* 若无需生成 Token可返回 null
* </p>
*
* @param ctx 扫描上下文
* @param ctx 当前扫描上下文
* @param line 当前行号
* @param col 当前列号
* @return 建的 Token如果为 null 表示无需添加
* @return 造的 Token null
*/
protected abstract Token scanToken(LexerContext ctx, int line, int col);
/**
* 连续读取字符直到不满足条件
* 工具方法连续读取字符直到遇到不满足指定条件的字符
*
* @param ctx 扫描上下文
* @param predicate 字符判断条件
* @return 匹配的字符串
* @param ctx 当前词法上下文
* @param predicate 字符匹配条件
* @return 满足条件的字符组成的字符串
*/
protected String readWhile(LexerContext ctx, Predicate<Character> predicate) {
StringBuilder sb = new StringBuilder();
@ -48,4 +62,4 @@ public abstract class AbstractTokenScanner implements TokenScanner {
}
return sb.toString();
}
}
}