From f4e2cf52f502fd20d8245d2ed730ae64a50da926 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 16:56:34 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8Adoc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ir/builder/ExpressionBuilder.java | 10 ++- .../lexer/scanners/OperatorTokenScanner.java | 72 ++++++++-------- .../parser/ast/UnaryExpressionNode.java | 22 ++++- .../expression/UnaryOperatorParselet.java | 44 +++++++++- .../expression/UnaryExpressionAnalyzer.java | 82 ++++++++++++++++--- .../snow/vm/engine/VirtualMachineEngine.java | 3 +- 6 files changed, 179 insertions(+), 54 deletions(-) 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 index b725ff1..2d2e75e 100644 --- a/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java +++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java @@ -33,8 +33,15 @@ public record ExpressionBuilder(IRContext ctx) { *

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

@@ -43,6 +50,7 @@ public record ExpressionBuilder(IRContext ctx) { * @return 该表达式的计算结果寄存器 * @throws IllegalStateException 如果遇到未定义的标识符或不支持的表达式类型 */ + public IRVirtualRegister build(ExpressionNode expr) { return switch (expr) { // 数字字面量 @@ -176,7 +184,7 @@ public record ExpressionBuilder(IRContext ctx) { .toList(); // 获取完整调用目标名称(支持成员/模块调用和普通调用) String fullName = switch (call.callee()) { - case MemberExpressionNode member when member.object() instanceof IdentifierNode mod -> + case MemberExpressionNode member when member.object() instanceof IdentifierNode _ -> ((IdentifierNode)member.object()).name() + "." + member.member(); case IdentifierNode id -> id.name(); default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName()); 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 index 1d52c02..ec2f2bf 100644 --- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java +++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java @@ -5,26 +5,29 @@ import org.jcnc.snow.compiler.lexer.token.Token; import org.jcnc.snow.compiler.lexer.token.TokenType; /** - * 运算符扫描器:识别逻辑与比较运算符,包括单字符和双字符组合。 - *

- * 支持的运算符包括: + * 运算符扫描器(OperatorTokenScanner) + * + *

负责在词法分析阶段识别由 = ! < > | & % 等字符 + * 起始的单字符或双字符运算符,并生成相应 {@link Token}:

+ * * - *

- * 不符合上述组合的字符会返回 {@code UNKNOWN} 类型的 Token。 + * + *

如果无法匹配到合法组合,将返回 {@link TokenType#UNKNOWN}。

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

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

+ * 判断当前字符是否可能是运算符的起始字符。 * * @param c 当前字符 - * @param ctx 当前词法上下文 - * @return 如果是潜在的运算符起始字符,则返回 true + * @param ctx 词法上下文 + * @return 若是关注的起始字符则返回 {@code true} */ @Override public boolean canHandle(char c, LexerContext ctx) { @@ -32,14 +35,12 @@ public class OperatorTokenScanner extends AbstractTokenScanner { } /** - * 扫描并识别运算符 Token。 - *

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

+ * 按最长匹配优先原则扫描并生成运算符 token。 * - * @param ctx 词法上下文 + * @param ctx 词法上下文 * @param line 当前行号 - * @param col 当前列号 - * @return 对应的运算符 Token,无法识别的运算符返回 {@code UNKNOWN} + * @param col 当前列号 + * @return 已识别的 {@link Token} */ @Override protected Token scanToken(LexerContext ctx, int line, int col) { @@ -51,64 +52,71 @@ public class OperatorTokenScanner extends AbstractTokenScanner { case '=': if (ctx.match('=')) { lexeme = "=="; - type = TokenType.DOUBLE_EQUALS; + type = TokenType.DOUBLE_EQUALS; } else { lexeme = "="; - type = TokenType.EQUALS; + type = TokenType.EQUALS; } break; + case '!': if (ctx.match('=')) { lexeme = "!="; - type = TokenType.NOT_EQUALS; + type = TokenType.NOT_EQUALS; } else { lexeme = "!"; - type = TokenType.NOT; + type = TokenType.NOT; } break; + case '>': if (ctx.match('=')) { lexeme = ">="; - type = TokenType.GREATER_EQUAL; + type = TokenType.GREATER_EQUAL; } else { lexeme = ">"; - type = TokenType.GREATER_THAN; + type = TokenType.GREATER_THAN; } break; + case '<': if (ctx.match('=')) { lexeme = "<="; - type = TokenType.LESS_EQUAL; + type = TokenType.LESS_EQUAL; } else { lexeme = "<"; - type = TokenType.LESS_THAN; + type = TokenType.LESS_THAN; } break; + case '%': lexeme = "%"; - type = TokenType.MODULO; + type = TokenType.MODULO; break; + case '&': if (ctx.match('&')) { lexeme = "&&"; - type = TokenType.AND; + type = TokenType.AND; } else { lexeme = "&"; - type = TokenType.UNKNOWN; + type = TokenType.UNKNOWN; } break; + case '|': if (ctx.match('|')) { lexeme = "||"; - type = TokenType.OR; + type = TokenType.OR; } else { lexeme = "|"; - type = TokenType.UNKNOWN; + type = TokenType.UNKNOWN; } break; + default: lexeme = String.valueOf(c); - type = TokenType.UNKNOWN; + type = TokenType.UNKNOWN; } return new Token(type, lexeme, line, col); diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/UnaryExpressionNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/UnaryExpressionNode.java index be60c24..d956fc8 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/ast/UnaryExpressionNode.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/UnaryExpressionNode.java @@ -3,15 +3,29 @@ package org.jcnc.snow.compiler.parser.ast; import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; /** - * 一元表达式节点,例如 -x 或 !x。 + * {@code UnaryExpressionNode} —— 前缀一元运算 AST 节点。 * - * @param operator 运算符字符串 ("-" / "!") - * @param operand 操作数表达式 + *

代表两种受支持的一元前缀表达式: + *

+ * + * {@link #equals(Object)}、{@link #hashCode()} 等方法。

+ * + * @param operator 一元运算符(仅 "-" 或 "!") + * @param operand 运算对象 / 右操作数 */ public record UnaryExpressionNode(String operator, ExpressionNode operand) implements ExpressionNode { - @Override public String toString() { + /** + * 生成调试友好的字符串表示,例如 {@code "-x"} 或 {@code "!flag"}。 + * + * @return 一元表达式的串表示 + */ + @Override + public String toString() { return operator + operand; } } diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/UnaryOperatorParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/UnaryOperatorParselet.java index 9d7a323..abfa97a 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/expression/UnaryOperatorParselet.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/UnaryOperatorParselet.java @@ -6,14 +6,50 @@ 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 UnaryOperatorParselet} —— 前缀一元运算符的 Pratt 解析器。 + * + *

当前 parselet 负责解析两种前缀运算: + *

+ * + * 解析过程: + * + *
    + *
  1. 该 parselet 在外层解析器已消费运算符 {@code token} 后被调用。
  2. + *
  3. 以 {@link Precedence#UNARY} 作为 绑定强度 递归解析右侧子表达式, + * 保证任何更高优先级的表达式(括号、字面量等)优先归属右侧。
  4. + *
  5. 最终生成 {@link UnaryExpressionNode} AST 节点,记录运算符与操作数。
  6. + *
+ * + *

此类仅负责语法结构的构建: + *

+ */ public class UnaryOperatorParselet implements PrefixParselet { + /** + * 解析前缀一元表达式。 + * + * @param ctx 当前解析上下文 + * @param token 已被消费的运算符 Token(字面值应为 {@code "-" 或 "!"}) + * @return 构建出的 {@link UnaryExpressionNode} + */ @Override public ExpressionNode parse(ParserContext ctx, Token token) { - // 递归解析右侧,使用自身优先级 - ExpressionNode right = + /* ------------------------------------------------------------ + * 1. 以 UNARY 优先级递归解析操作数,避免错误结合顺序。 + * ------------------------------------------------------------ */ + ExpressionNode operand = new PrattExpressionParser().parseExpression(ctx, Precedence.UNARY); - return new UnaryExpressionNode(token.getLexeme(), right); + + /* ------------------------------------------------------------ + * 2. 封装成 AST 节点并返回。 + * ------------------------------------------------------------ */ + return new UnaryExpressionNode(token.getLexeme(), operand); } } diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnaryExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnaryExpressionAnalyzer.java index 37f2ec0..53f6c8f 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnaryExpressionAnalyzer.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnaryExpressionAnalyzer.java @@ -10,38 +10,96 @@ 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 UnaryExpressionAnalyzer} — 一元表达式的语义分析器。 + * + *

目前实现两种一元运算: + *

+ * + *

分析流程: + *

    + *
  1. 递归分析操作数表达式,获取其类型 {@code operandType}。
  2. + *
  3. 根据运算符检查类型合法性: + *
      + *
    • 若类型不符,记录 {@link SemanticError} 并返回一个占位类型 + * (取负返回 {@link BuiltinType#INT},逻辑非返回 + * {@link BuiltinType#BOOLEAN})。
    • + *
    • 若合法,则返回运算后的结果类型 + * (取负为 {@code operandType},逻辑非为 {@link BuiltinType#BOOLEAN})。
    • + *
    + *
  4. + *
+ * + *

若遇到未支持的运算符,将生成错误并返回 {@code int} 作为占位类型。

+ * + */ public class UnaryExpressionAnalyzer implements ExpressionAnalyzer { + /** + * 对一元表达式进行语义分析。 + * + * @param ctx 全局编译上下文,持有错误列表、注册表等 + * @param mi 当前模块信息 + * @param fn 所在函数节点(可为 {@code null} 表示顶层) + * @param locals 当前作用域符号表 + * @param expr 要分析的一元表达式节点 + * @return 表达式的结果类型;若有错误,返回占位类型并在 {@code ctx.getErrors()} + * 中记录 {@link SemanticError} + */ @Override - public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, - SymbolTable locals, UnaryExpressionNode expr) { + public Type analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + UnaryExpressionNode expr) { - // 先分析操作数 + /* ------------------------------------------------------------------ + * 1. 先递归分析操作数,确定其类型 + * ------------------------------------------------------------------ */ Type operandType = ctx.getRegistry() - .getExpressionAnalyzer(expr.operand()) - .analyze(ctx, mi, fn, locals, expr.operand()); + .getExpressionAnalyzer(expr.operand()) + .analyze(ctx, mi, fn, locals, expr.operand()); + /* ------------------------------------------------------------------ + * 2. 根据运算符校验类型并给出结果类型 + * ------------------------------------------------------------------ */ switch (expr.operator()) { + /* -------------- 取负运算 -------------- */ case "-" -> { if (!operandType.isNumeric()) { - ctx.getErrors().add(new SemanticError(expr, - "'-' 只能应用于数值类型,当前为 " + operandType)); + ctx.getErrors().add(new SemanticError( + expr, + "'-' 只能应用于数值类型,当前为 " + operandType + )); + // 返回占位类型,避免后续阶段 NPE return BuiltinType.INT; } + // 合法:结果类型与操作数相同 return operandType; } + + /* -------------- 逻辑非运算 -------------- */ case "!" -> { if (operandType != BuiltinType.BOOLEAN) { - ctx.getErrors().add(new SemanticError(expr, - "'!' 只能应用于 boolean 类型,当前为 " + operandType)); + ctx.getErrors().add(new SemanticError( + expr, + "'!' 只能应用于 boolean 类型,当前为 " + operandType + )); return BuiltinType.BOOLEAN; } + // 合法:结果类型恒为 boolean return BuiltinType.BOOLEAN; } + + /* -------------- 未知运算符 -------------- */ default -> { - ctx.getErrors().add(new SemanticError(expr, - "未知一元运算符: " + expr.operator())); + ctx.getErrors().add(new SemanticError( + expr, + "未知一元运算符: " + expr.operator() + )); return BuiltinType.INT; } } diff --git a/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java b/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java index 14cef48..0f188ab 100644 --- a/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java +++ b/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java @@ -20,7 +20,8 @@ import java.util.List; *
  • {@link CommandExecutionHandler} — dispatches opcodes
  • * * - *

    Root-frame contract

    + * 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