docs: 增加注释doc
This commit is contained in:
parent
7679da2657
commit
f4e2cf52f5
@ -33,8 +33,15 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
* <p>会根据节点的实际类型分别处理:
|
||||
* <ul>
|
||||
* <li>数字字面量:新建常量寄存器</li>
|
||||
* <li>布尔字面量:生成值为 0 或 1 的常量寄存器</li>
|
||||
* <li>标识符:查找当前作用域中的寄存器</li>
|
||||
* <li>二元表达式:递归处理子表达式并进行相应运算</li>
|
||||
* <li>一元运算符:
|
||||
* <ul>
|
||||
* <li><code>-x</code>(取负,生成 <code>NEG_I32</code> 指令)与</li>
|
||||
* <li>code>!x</code>(逻辑非,转换为 <code>x == 0</code> 比较指令)</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>函数调用:生成对应的Call指令</li>
|
||||
* <li>其它类型不支持,抛出异常</li>
|
||||
* </ul>
|
||||
@ -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());
|
||||
|
||||
@ -5,26 +5,29 @@ import org.jcnc.snow.compiler.lexer.token.Token;
|
||||
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||
|
||||
/**
|
||||
* 运算符扫描器:识别逻辑与比较运算符,包括单字符和双字符组合。
|
||||
* <p>
|
||||
* 支持的运算符包括:
|
||||
* 运算符扫描器(OperatorTokenScanner)
|
||||
*
|
||||
* <p>负责在词法分析阶段识别由 <b>= ! < > | & %</b> 等字符
|
||||
* 起始的单字符或双字符运算符,并生成相应 {@link Token}:</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>赋值与比较:=、==、!=</li>
|
||||
* <li>关系运算符:>、>=、<、<=</li>
|
||||
* <li>逻辑运算符:&&、||</li>
|
||||
* <li>赋值 / 比较:{@code =}, {@code ==}, {@code !=}</li>
|
||||
* <li>关系运算:{@code >}, {@code >=}, {@code <}, {@code <=}</li>
|
||||
* <li>逻辑运算:{@code &&}, {@code ||}</li>
|
||||
* <li>取模运算:{@code %}</li>
|
||||
* <li>逻辑非:{@code !}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 不符合上述组合的字符会返回 {@code UNKNOWN} 类型的 Token。
|
||||
*
|
||||
* <p>如果无法匹配到合法组合,将返回 {@link TokenType#UNKNOWN}。</p>
|
||||
*/
|
||||
public class OperatorTokenScanner extends AbstractTokenScanner {
|
||||
|
||||
/**
|
||||
* 判断是否可以处理当前位置的字符。
|
||||
* <p>运算符扫描器关注的起始字符包括:=、!、<、>、|、&</p>
|
||||
* 判断当前字符是否可能是运算符的起始字符。
|
||||
*
|
||||
* @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。
|
||||
* <p>支持组合运算符判断,如 ==、!=、>= 等,
|
||||
* 若无法匹配组合形式则退回单字符形式。</p>
|
||||
* 按最长匹配优先原则扫描并生成运算符 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);
|
||||
|
||||
@ -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 操作数表达式
|
||||
* <p>代表两种受支持的一元前缀表达式:
|
||||
* <ul>
|
||||
* <li><b>取负</b>:{@code -x}</li>
|
||||
* <li><b>逻辑非</b>:{@code !x}</li>
|
||||
* </ul>
|
||||
*
|
||||
* {@link #equals(Object)}、{@link #hashCode()} 等方法。</p>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 解析器。
|
||||
*
|
||||
* <p>当前 parselet 负责解析两种前缀运算:
|
||||
* <ul>
|
||||
* <li><b>取负</b>:{@code -x}</li>
|
||||
* <li><b>逻辑非</b>:{@code !x}</li>
|
||||
* </ul>
|
||||
*
|
||||
* 解析过程:
|
||||
*
|
||||
* <ol>
|
||||
* <li>该 parselet 在外层解析器已消费运算符 {@code token} 后被调用。</li>
|
||||
* <li>以 {@link Precedence#UNARY} 作为 <em>绑定强度</em> 递归解析右侧子表达式,
|
||||
* 保证任何更高优先级的表达式(括号、字面量等)优先归属右侧。</li>
|
||||
* <li>最终生成 {@link UnaryExpressionNode} AST 节点,记录运算符与操作数。</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>此类仅负责<strong>语法结构</strong>的构建:
|
||||
* <ul>
|
||||
* <li>类型正确性在 {@code UnaryExpressionAnalyzer} 中校验;</li>
|
||||
* <li>IR 生成在 {@code ExpressionBuilder.buildUnary} 中完成。</li>
|
||||
* </ul>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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} — 一元表达式的语义分析器。
|
||||
*
|
||||
* <p>目前实现两种一元运算:
|
||||
* <ul>
|
||||
* <li>{@code -x} 取负:仅允许作用于数值类型(int / float 等)。</li>
|
||||
* <li>{@code !x} 逻辑非:仅允许作用于 {@code boolean} 类型。</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>分析流程:
|
||||
* <ol>
|
||||
* <li>递归分析操作数表达式,获取其类型 {@code operandType}。</li>
|
||||
* <li>根据运算符检查类型合法性:
|
||||
* <ul>
|
||||
* <li>若类型不符,记录 {@link SemanticError} 并返回一个占位类型
|
||||
* (取负返回 {@link BuiltinType#INT},逻辑非返回
|
||||
* {@link BuiltinType#BOOLEAN})。</li>
|
||||
* <li>若合法,则返回运算后的结果类型
|
||||
* (取负为 {@code operandType},逻辑非为 {@link BuiltinType#BOOLEAN})。</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>若遇到未支持的运算符,将生成错误并返回 {@code int} 作为占位类型。</p>
|
||||
*
|
||||
*/
|
||||
public class UnaryExpressionAnalyzer implements ExpressionAnalyzer<UnaryExpressionNode> {
|
||||
|
||||
/**
|
||||
* 对一元表达式进行语义分析。
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,8 @@ import java.util.List;
|
||||
* <li>{@link CommandExecutionHandler} — dispatches opcodes</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Root-frame contract</h2>
|
||||
* Root-frame contract:
|
||||
* <p>
|
||||
* A <strong>root stack frame</strong> is pushed <em>once</em> via
|
||||
* {@link #ensureRootFrame()} before the first instruction executes
|
||||
* and is never popped. When a {@code RET} executed in the root frame
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user