增加注释
This commit is contained in:
parent
3a992ccd37
commit
55526d530a
@ -18,53 +18,71 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 顶层的函数定义解析器。
|
||||
* {@code FunctionParser} 是顶层函数定义的语法解析器,
|
||||
* 实现 {@link TopLevelParser} 接口,用于将源代码中的函数块解析为抽象语法树(AST)中的 {@link FunctionNode}。
|
||||
*
|
||||
* <p>
|
||||
* 通过 FlexibleSectionParser 按区块顺序解析函数的各个部分:
|
||||
* 函数头(名称)、参数列表、返回类型、函数体,并最终生成 FunctionNode。
|
||||
* 支持在参数或返回类型声明中出现注释,自动跳过注释和空行而不干扰语法解析。
|
||||
* 本类使用 {@link FlexibleSectionParser} 机制,按照语义区块结构对函数进行模块化解析,支持以下部分:
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>函数头(关键字 {@code function:} 与函数名)</li>
|
||||
* <li>参数列表(parameter 区块)</li>
|
||||
* <li>返回类型(return_type 区块)</li>
|
||||
* <li>函数体(body 区块)</li>
|
||||
* <li>函数结束(关键字 {@code end function})</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* 各区块允许包含注释(类型 {@code COMMENT})与空行(类型 {@code NEWLINE}),解析器将自动跳过无效 token 保持语法连续性。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 最终将函数结构封装为 {@link FunctionNode} 并返回,供后续编译阶段使用。
|
||||
* </p>
|
||||
*/
|
||||
public class FunctionParser implements TopLevelParser {
|
||||
|
||||
/**
|
||||
* 顶层解析入口。解析完整个函数定义,并返回 FunctionNode。
|
||||
* 顶层语法解析入口。
|
||||
*
|
||||
* @param ctx 解析上下文,包含 TokenStream 和作用域信息
|
||||
* @return 构建好的 FunctionNode
|
||||
* <p>
|
||||
* 该方法负责完整解析函数定义,包括其所有组成部分,并构建对应的 {@link FunctionNode}。
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 当前解析上下文,包含 {@link TokenStream} 和符号表等作用域信息。
|
||||
* @return 构建完成的 {@link FunctionNode} 抽象语法树节点。
|
||||
*/
|
||||
@Override
|
||||
public FunctionNode parse(ParserContext ctx) {
|
||||
TokenStream ts = ctx.getTokens();
|
||||
|
||||
// 1. 解析函数头(function:)
|
||||
parseFunctionHeader(ts);
|
||||
// 2. 读取并消费函数名称
|
||||
String functionName = parseFunctionName(ts);
|
||||
|
||||
// 容器用于收集解析结果
|
||||
List<ParameterNode> parameters = new ArrayList<>();
|
||||
String[] returnType = new String[1]; // 用一元素数组模拟可变引用
|
||||
String[] returnType = new String[1];
|
||||
List<StatementNode> body = new ArrayList<>();
|
||||
|
||||
// 3. 构建区块解析逻辑,并使用 FlexibleSectionParser 解析
|
||||
Map<String, SectionDefinition> sections = getSectionDefinitions(parameters, returnType, body);
|
||||
FlexibleSectionParser.parse(ctx, ts, sections);
|
||||
|
||||
// 4. 解析函数尾部(end function)
|
||||
parseFunctionFooter(ts);
|
||||
|
||||
// 5. 构造并返回 AST
|
||||
return new FunctionNode(functionName, parameters, returnType[0], body);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造各区块的解析定义:
|
||||
* 参数(parameter)、返回类型(return_type)、函数体(body)。
|
||||
* 构造函数定义中各区块的解析规则(parameter、return_type、body)。
|
||||
*
|
||||
* @param params 参数节点列表容器
|
||||
* @param returnType 返回类型容器
|
||||
* @param body 函数体语句列表容器
|
||||
* @return 区块关键字到解析逻辑的映射
|
||||
* <p>
|
||||
* 每个 {@link SectionDefinition} 包含两个部分:区块起始判断器(基于关键字)与具体的解析逻辑。
|
||||
* </p>
|
||||
*
|
||||
* @param params 参数节点收集容器,解析结果将存入此列表。
|
||||
* @param returnType 返回类型容器,以单元素数组方式模拟引用传递。
|
||||
* @param body 函数体语句节点列表容器。
|
||||
* @return 区块关键字到解析定义的映射表。
|
||||
*/
|
||||
private Map<String, SectionDefinition> getSectionDefinitions(
|
||||
List<ParameterNode> params,
|
||||
@ -72,19 +90,16 @@ public class FunctionParser implements TopLevelParser {
|
||||
List<StatementNode> body) {
|
||||
Map<String, SectionDefinition> 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))
|
||||
@ -94,9 +109,9 @@ public class FunctionParser implements TopLevelParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析函数头部:匹配 "function:",并跳过后续的注释和空行
|
||||
* 解析函数头部标识符 {@code function:},并跳过其后多余注释与空行。
|
||||
*
|
||||
* @param ts TokenStream
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
*/
|
||||
private void parseFunctionHeader(TokenStream ts) {
|
||||
ts.expect("function");
|
||||
@ -106,10 +121,10 @@ public class FunctionParser implements TopLevelParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析函数名称:IDENTIFIER + 换行
|
||||
* 解析函数名称(标识符)并跳过换行。
|
||||
*
|
||||
* @param ts TokenStream
|
||||
* @return 函数名称
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
* @return 函数名字符串。
|
||||
*/
|
||||
private String parseFunctionName(TokenStream ts) {
|
||||
String name = ts.expectType(TokenType.IDENTIFIER).getLexeme();
|
||||
@ -118,9 +133,9 @@ public class FunctionParser implements TopLevelParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析函数尾部:匹配 "end function" + 换行
|
||||
* 解析函数结束标记 {@code end function}。
|
||||
*
|
||||
* @param ts TokenStream
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
*/
|
||||
private void parseFunctionFooter(TokenStream ts) {
|
||||
ts.expect("end");
|
||||
@ -129,16 +144,19 @@ public class FunctionParser implements TopLevelParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析参数区块参数列表,支持在声明行尾添加注释
|
||||
* 格式:
|
||||
* 解析函数参数列表。
|
||||
*
|
||||
* <p>
|
||||
* 支持声明后附加注释,格式示例:
|
||||
* <pre>
|
||||
* parameter:
|
||||
* declare x: int // 注释
|
||||
* ...
|
||||
* declare x: int // 说明文字
|
||||
* declare y: float
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @param ts TokenStream
|
||||
* @return ParameterNode 列表
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
* @return 所有参数节点的列表。
|
||||
*/
|
||||
private List<ParameterNode> parseParameters(TokenStream ts) {
|
||||
ts.expect("parameter");
|
||||
@ -162,7 +180,6 @@ public class FunctionParser implements TopLevelParser {
|
||||
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));
|
||||
@ -171,35 +188,37 @@ public class FunctionParser implements TopLevelParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析返回类型区块,支持在类型声明前后添加注释
|
||||
* 格式:<pre>return_type: TYPE</pre>
|
||||
* 解析返回类型声明。
|
||||
*
|
||||
* @param ts TokenStream
|
||||
* @return 返回类型字符串
|
||||
* <p>
|
||||
* 格式为 {@code return_type: TYPE},支持前置或行尾注释。
|
||||
* </p>
|
||||
*
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
* @return 返回类型名称字符串。
|
||||
*/
|
||||
private String parseReturnType(TokenStream ts) {
|
||||
ts.expect("return_type");
|
||||
ts.expect(":");
|
||||
// 跳过块前注释
|
||||
skipComments(ts);
|
||||
// 捕获类型 token
|
||||
Token typeToken = ts.expectType(TokenType.TYPE);
|
||||
String rtype = typeToken.getLexeme();
|
||||
// 跳过行内注释
|
||||
skipComments(ts);
|
||||
// 匹配换行
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
// 跳过多余空行
|
||||
skipNewlines(ts);
|
||||
return rtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析函数体区块,直到遇到 "end body"
|
||||
* 解析函数体区块,直到遇到 {@code end body}。
|
||||
*
|
||||
* @param ctx ParserContext
|
||||
* @param ts TokenStream
|
||||
* @return StatementNode 列表
|
||||
* <p>
|
||||
* 每一行由对应的语句解析器处理,可嵌套控制结构、返回语句、表达式等。
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 当前解析上下文。
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
* @return 所有函数体语句节点的列表。
|
||||
*/
|
||||
private List<StatementNode> parseFunctionBody(ParserContext ctx, TokenStream ts) {
|
||||
ts.expect("body");
|
||||
@ -228,9 +247,9 @@ public class FunctionParser implements TopLevelParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳过所有连续的注释行(COMMENT)
|
||||
* 跳过连续的注释 token(类型 {@code COMMENT})。
|
||||
*
|
||||
* @param ts TokenStream
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
*/
|
||||
private void skipComments(TokenStream ts) {
|
||||
while (ts.peek().getType() == TokenType.COMMENT) {
|
||||
@ -239,9 +258,9 @@ public class FunctionParser implements TopLevelParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳过所有连续的空行(NEWLINE)
|
||||
* 跳过连续的空行 token(类型 {@code NEWLINE})。
|
||||
*
|
||||
* @param ts TokenStream
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
*/
|
||||
private void skipNewlines(TokenStream ts) {
|
||||
while (ts.peek().getType() == TokenType.NEWLINE) {
|
||||
|
||||
@ -8,36 +8,57 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 负责解析 import 语句的解析器。
|
||||
* 支持在一行中导入多个模块,格式如:
|
||||
* {@code import: mod1, mod2, mod3}
|
||||
* {@code ImportParser} 类用于解析源码中的 import 导入语句。
|
||||
* <p>
|
||||
* 支持以下格式的语法:
|
||||
* <pre>
|
||||
* import: module1, module2, module3
|
||||
* </pre>
|
||||
* 每个模块名称必须为合法的标识符,多个模块之间使用英文逗号(,)分隔,语句末尾必须以换行符结尾。
|
||||
* 本类的职责是识别并提取所有导入模块的名称,并将其封装为 {@link ImportNode} 节点,供后续语法树构建或语义分析阶段使用。
|
||||
*/
|
||||
public class ImportParser {
|
||||
|
||||
/**
|
||||
* 解析一条 import 声明语句,返回对应的 {@link ImportNode} 列表。
|
||||
* 格式必须为:{@code import: MODULE[, MODULE]* NEWLINE}
|
||||
* 解析 import 语句,并返回表示被导入模块的语法树节点列表。
|
||||
* <p>
|
||||
* 该方法会依次执行以下操作:
|
||||
* <ol>
|
||||
* <li>确认当前语句以关键字 {@code import} 开头。</li>
|
||||
* <li>确认后跟一个冒号 {@code :}。</li>
|
||||
* <li>解析至少一个模块名称(标识符),多个模块使用逗号分隔。</li>
|
||||
* <li>确认语句以换行符 {@code NEWLINE} 结束。</li>
|
||||
* </ol>
|
||||
* 若语法不符合上述规则,将在解析过程中抛出异常。
|
||||
*
|
||||
* @param ctx 当前的解析上下文。
|
||||
* @return 所有被导入模块的节点列表。
|
||||
* @param ctx 表示当前解析器所处的上下文环境,包含词法流及语法状态信息。
|
||||
* @return 返回一个包含所有被导入模块的 {@link ImportNode} 实例列表。
|
||||
*/
|
||||
public List<ImportNode> parse(ParserContext ctx) {
|
||||
// 期望第一个 token 是 "import" 关键字
|
||||
ctx.getTokens().expect("import");
|
||||
|
||||
// 紧接其后必须是冒号 ":"
|
||||
ctx.getTokens().expect(":");
|
||||
|
||||
// 用于存储解析得到的 ImportNode 对象
|
||||
List<ImportNode> imports = new ArrayList<>();
|
||||
|
||||
// 读取第一个模块名,然后继续读取逗号分隔的模块名
|
||||
// 解析一个或多个模块名(标识符),允许使用逗号分隔多个模块
|
||||
do {
|
||||
// 获取当前标识符类型的词法单元,并提取其原始词素
|
||||
String mod = ctx.getTokens()
|
||||
.expectType(TokenType.IDENTIFIER)
|
||||
.getLexeme();
|
||||
imports.add(new ImportNode(mod));
|
||||
} while (ctx.getTokens().match(","));
|
||||
|
||||
// 消费行尾换行符
|
||||
// 创建 ImportNode 节点并加入列表
|
||||
imports.add(new ImportNode(mod));
|
||||
} while (ctx.getTokens().match(",")); // 如果匹配到逗号,继续解析下一个模块名
|
||||
|
||||
// 最后必须匹配换行符,标志 import 语句的结束
|
||||
ctx.getTokens().expectType(TokenType.NEWLINE);
|
||||
|
||||
// 返回完整的 ImportNode 列表
|
||||
return imports;
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,79 +13,84 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 模块解析器,用于解析形如 module 块的顶层结构。
|
||||
* 语法示例:
|
||||
* <pre>{@code
|
||||
* module: my.module
|
||||
* import: mod1, mod2
|
||||
* function: ...
|
||||
* end module
|
||||
* }</pre>
|
||||
* 支持导入语句和多个函数定义,允许中间包含空行。
|
||||
* {@code ModuleParser} 类负责解析源码中的模块定义结构,属于顶层结构解析器的一种。
|
||||
* <p>
|
||||
* 模块中可包含多个导入语句和函数定义,导入语句可在模块中任意位置出现,
|
||||
* 同时支持空行,空行将被自动忽略,不影响语法结构的正确性。
|
||||
*/
|
||||
public class ModuleParser implements TopLevelParser {
|
||||
|
||||
/**
|
||||
* 解析模块定义块,返回 {@link ModuleNode} 表示模块结构。
|
||||
* 解析一个模块定义块,返回构建好的 {@link ModuleNode} 对象。
|
||||
* <p>
|
||||
* 本方法的语法流程包括:
|
||||
* <ol>
|
||||
* <li>匹配模块声明开头 {@code module: IDENTIFIER}。</li>
|
||||
* <li>收集模块体中的 import 语句与 function 定义,允许穿插空行。</li>
|
||||
* <li>模块结尾必须为 {@code end module},且后接换行符。</li>
|
||||
* </ol>
|
||||
* 所有语法错误将在解析过程中抛出异常,以便准确反馈问题位置和原因。
|
||||
*
|
||||
* @param ctx 解析上下文。
|
||||
* @return 模块节点,包含模块名、导入列表和函数列表。
|
||||
* @param ctx 当前解析器上下文,包含词法流、状态信息等。
|
||||
* @return 返回一个 {@link ModuleNode} 实例,表示完整模块的语法结构。
|
||||
* @throws IllegalStateException 当模块体中出现未识别的语句时抛出。
|
||||
*/
|
||||
@Override
|
||||
public ModuleNode parse(ParserContext ctx) {
|
||||
// 获取当前上下文中提供的词法流
|
||||
TokenStream ts = ctx.getTokens();
|
||||
|
||||
// 期望以 "module" 开头
|
||||
// 期望模块声明以关键字 "module:" 开始
|
||||
ts.expect("module");
|
||||
ts.expect(":");
|
||||
|
||||
// 读取模块名称(标识符)
|
||||
// 读取模块名称(要求为标识符类型的词法单元)
|
||||
String name = ts.expectType(TokenType.IDENTIFIER).getLexeme();
|
||||
|
||||
// 模块声明后的换行
|
||||
// 模块声明必须以换行符结束
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
|
||||
// 初始化导入列表与函数列表
|
||||
// 初始化模块的导入节点列表与函数节点列表
|
||||
List<ImportNode> imports = new ArrayList<>();
|
||||
List<FunctionNode> functions = new ArrayList<>();
|
||||
|
||||
// 创建导入语句和函数解析器
|
||||
// 创建 import 与 function 的子解析器
|
||||
ImportParser importParser = new ImportParser();
|
||||
FunctionParser funcParser = new FunctionParser();
|
||||
|
||||
// 解析模块主体内容(支持多个 import 和 function 语句)
|
||||
// 进入模块主体内容解析循环
|
||||
while (true) {
|
||||
// 跳过空行(多个空行不会导致错误)
|
||||
// 跳过所有空行(即连续的 NEWLINE)
|
||||
if (ts.peek().getType() == TokenType.NEWLINE) {
|
||||
ts.next();
|
||||
continue;
|
||||
}
|
||||
|
||||
// 模块体结束判断:遇到 "end" 关键字
|
||||
// 若遇到 "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" 结尾
|
||||
// 确保模块体以 "end module" 结束
|
||||
ts.expect("end");
|
||||
ts.expect("module");
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
|
||||
// 返回构建完成的模块语法树节点
|
||||
// 构建并返回完整的模块语法树节点
|
||||
return new ModuleNode(name, imports, functions);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,47 +7,68 @@ import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||
import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
|
||||
|
||||
/**
|
||||
* 解析变量声明语句的解析器。
|
||||
* 支持语法格式:
|
||||
* {@code DeclarationStatementParser} 类负责解析变量声明语句,是语句级解析器的一部分。
|
||||
* <p>
|
||||
* 本解析器支持以下两种形式的声明语法:
|
||||
* <pre>{@code
|
||||
* declare name:type
|
||||
* declare name:type = expression
|
||||
* declare myVar:Integer
|
||||
* declare myVar:Integer = 42 + 3
|
||||
* }</pre>
|
||||
* 每条语句必须以换行(NEWLINE)结束。
|
||||
* 其中:
|
||||
* <ul>
|
||||
* <li>{@code myVar} 为变量名(必须为标识符类型);</li>
|
||||
* <li>{@code Integer} 为类型标注(必须为类型标记);</li>
|
||||
* <li>可选的初始化表达式由 {@link PrattExpressionParser} 解析;</li>
|
||||
* <li>每条声明语句必须以换行符({@code NEWLINE})结束。</li>
|
||||
* </ul>
|
||||
* 若语法不满足上述结构,将在解析过程中抛出异常。
|
||||
*/
|
||||
public class DeclarationStatementParser implements StatementParser {
|
||||
|
||||
/**
|
||||
* 解析一条 declare 声明语句。
|
||||
* 解析一条 {@code declare} 声明语句,并返回对应的抽象语法树节点 {@link DeclarationNode}。
|
||||
* <p>
|
||||
* 解析流程如下:
|
||||
* <ol>
|
||||
* <li>匹配关键字 {@code declare};</li>
|
||||
* <li>读取变量名称(标识符类型);</li>
|
||||
* <li>读取类型标注(在冒号后,要求为 {@code TYPE} 类型);</li>
|
||||
* <li>若存在 {@code =},则继续解析其后的表达式作为初始化值;</li>
|
||||
* <li>最终必须匹配 {@code NEWLINE} 表示语句结束。</li>
|
||||
* </ol>
|
||||
* 若遇到非法语法结构,将触发异常并中断解析过程。
|
||||
*
|
||||
* @param ctx 当前解析上下文。
|
||||
* @return 构造好的 {@link DeclarationNode} AST 节点。
|
||||
* @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);
|
||||
}
|
||||
|
||||
// 声明语句必须以 NEWLINE 结束
|
||||
// 声明语句必须以换行符结尾
|
||||
ctx.getTokens().expectType(TokenType.NEWLINE);
|
||||
|
||||
// 返回构建好的声明语法树节点
|
||||
return new DeclarationNode(name, type, init);
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,46 +10,61 @@ import org.jcnc.snow.compiler.parser.context.TokenStream;
|
||||
import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
|
||||
|
||||
/**
|
||||
* 表达式语句解析器:将赋值表达式或任意表达式作为语句进行解析。
|
||||
* 支持的形式包括:
|
||||
* <pre>
|
||||
* identifier = expression
|
||||
* expression
|
||||
* </pre>
|
||||
* 两者都必须以换行(NEWLINE)结尾。
|
||||
* {@code ExpressionStatementParser} 负责解析通用表达式语句,包括赋值语句和单一表达式语句。
|
||||
* <p>
|
||||
* 支持的语法结构如下:
|
||||
* <pre>{@code
|
||||
* x = 1 + 2 // 赋值语句
|
||||
* doSomething() // 函数调用等普通表达式语句
|
||||
* }</pre>
|
||||
* <ul>
|
||||
* <li>若以标识符开头,且后接等号 {@code =},则视为赋值语句,解析为 {@link AssignmentNode}。</li>
|
||||
* <li>否则视为普通表达式,解析为 {@link ExpressionStatementNode}。</li>
|
||||
* <li>所有表达式语句必须以换行符 {@code NEWLINE} 结束。</li>
|
||||
* </ul>
|
||||
* 不允许以关键字或空行作为表达式的起始,若遇到非法开头,将抛出解析异常。
|
||||
*/
|
||||
public class ExpressionStatementParser implements StatementParser {
|
||||
|
||||
/**
|
||||
* 解析一个表达式语句,可能是赋值语句或普通表达式。
|
||||
* 解析一个表达式语句,根据上下文决定其为赋值或一般表达式。
|
||||
* <p>
|
||||
* 具体逻辑如下:
|
||||
* <ol>
|
||||
* <li>若当前行为标识符后接等号,则作为赋值处理。</li>
|
||||
* <li>否则解析整个表达式作为单独语句。</li>
|
||||
* <li>所有语句都必须以换行符结束。</li>
|
||||
* <li>若表达式以关键字或空行开头,将立即抛出异常,避免非法解析。</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param ctx 当前的解析上下文。
|
||||
* @return 表达式语句节点或赋值语句节点。
|
||||
* @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 = expr)
|
||||
// 处理赋值语句:格式为 identifier = expression
|
||||
if (ts.peek().getType() == TokenType.IDENTIFIER
|
||||
&& ts.peek(1).getLexeme().equals("=")) {
|
||||
|
||||
String varName = ts.next().getLexeme(); // consume identifier
|
||||
ts.expect("="); // consume '='
|
||||
ExpressionNode value = new PrattExpressionParser().parse(ctx);
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
return new AssignmentNode(varName, value);
|
||||
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);
|
||||
ts.expectType(TokenType.NEWLINE); // 语句必须以换行符结束
|
||||
return new ExpressionStatementNode(expr); // 返回表达式语句节点
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -12,75 +12,96 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 解析 if 语句:
|
||||
* {@code IfStatementParser} 类负责解析 if 条件语句,是语句级解析器中的条件分支处理器。
|
||||
* <p>
|
||||
* 支持格式:
|
||||
* 本解析器支持以下结构的条件语法:
|
||||
* <pre>{@code
|
||||
* if <condition> then
|
||||
* <then-statements>
|
||||
* [else
|
||||
* <else-statements>]
|
||||
* end if
|
||||
* }</pre>
|
||||
* 其中:
|
||||
* <ul>
|
||||
* <li>{@code <condition>} 为任意可解析的布尔或数值表达式,使用 {@link PrattExpressionParser} 解析;</li>
|
||||
* <li>{@code <then-statements>} 与 {@code <else-statements>} 可包含多条语句,自动跳过空行;</li>
|
||||
* <li>{@code else} 分支为可选,若存在,必须紧跟换行与语句;</li>
|
||||
* <li>{@code end if} 为终止标识,表示整个 if 语句块的结束。</li>
|
||||
* </ul>
|
||||
* 所有语句的实际解析由 {@link StatementParserFactory} 根据关键词动态分派处理。
|
||||
*/
|
||||
public class IfStatementParser implements StatementParser {
|
||||
|
||||
/**
|
||||
* 解析一条完整的 if 条件语句,返回语法树中对应的 {@link IfNode} 节点。
|
||||
* <p>
|
||||
* 本方法支持 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 流管理器
|
||||
var ts = ctx.getTokens(); // 获取 token 流引用
|
||||
|
||||
ts.expect("if"); // 消费 "if" 关键字
|
||||
// 消耗起始关键字 "if"
|
||||
ts.expect("if");
|
||||
|
||||
// 使用 Pratt 解析器解析布尔表达式或其他条件表达式
|
||||
// 使用 Pratt 算法解析 if 条件表达式
|
||||
var condition = new PrattExpressionParser().parse(ctx);
|
||||
|
||||
// 消费 "then" 关键字和随后的换行符
|
||||
// 条件表达式后必须紧跟 "then" 和换行
|
||||
ts.expect("then");
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
|
||||
// 准备容器存储 then 和 else 分支的语句
|
||||
// 初始化 then 和 else 分支语句列表
|
||||
List<StatementNode> thenBranch = new ArrayList<>();
|
||||
List<StatementNode> elseBranch = new ArrayList<>();
|
||||
|
||||
// -------------------
|
||||
// 解析 THEN 分支语句
|
||||
// -------------------
|
||||
// -------------------------
|
||||
// 解析 THEN 分支语句块
|
||||
// -------------------------
|
||||
while (true) {
|
||||
Token peek = ts.peek();
|
||||
|
||||
// 忽略空行
|
||||
// 跳过空行
|
||||
if (peek.getType() == TokenType.NEWLINE) {
|
||||
ts.next();
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检测是否进入 else 或 end,跳出 then 区块
|
||||
// 遇到 else 或 end 表示 then 分支结束
|
||||
if (peek.getType() == TokenType.KEYWORD &&
|
||||
(peek.getLexeme().equals("else") || peek.getLexeme().equals("end"))) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 提取关键词,交由 StatementParserFactory 派发对应解析器
|
||||
// 获取当前语句的关键字,调用工厂获取对应解析器
|
||||
String keyword = peek.getType() == TokenType.KEYWORD ? peek.getLexeme() : "";
|
||||
StatementNode stmt = StatementParserFactory.get(keyword).parse(ctx);
|
||||
thenBranch.add(stmt);
|
||||
}
|
||||
|
||||
// -------------------
|
||||
// 解析 ELSE 分支语句
|
||||
// -------------------
|
||||
// -------------------------
|
||||
// 解析 ELSE 分支语句块(可选)
|
||||
// -------------------------
|
||||
if (ts.peek().getLexeme().equals("else")) {
|
||||
ts.next(); // 消费 "else"
|
||||
ts.expectType(TokenType.NEWLINE); // 消费换行
|
||||
ts.next(); // 消耗 "else"
|
||||
ts.expectType(TokenType.NEWLINE); // 消耗换行符
|
||||
|
||||
while (true) {
|
||||
Token peek = ts.peek();
|
||||
|
||||
// 忽略空行
|
||||
// 跳过空行
|
||||
if (peek.getType() == TokenType.NEWLINE) {
|
||||
ts.next();
|
||||
continue;
|
||||
}
|
||||
|
||||
// "end" 关键字表示 else 块结束
|
||||
// "end" 表示 else 分支结束
|
||||
if (peek.getType() == TokenType.KEYWORD && peek.getLexeme().equals("end")) {
|
||||
break;
|
||||
}
|
||||
@ -91,12 +112,14 @@ public class IfStatementParser implements StatementParser {
|
||||
}
|
||||
}
|
||||
|
||||
// 统一消费 "end if" 和其后的换行
|
||||
// -------------------------
|
||||
// 统一结束处理:end if
|
||||
// -------------------------
|
||||
ts.expect("end");
|
||||
ts.expect("if");
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
|
||||
// 构造 AST 节点,返回 IfNode 包含条件、then 分支、else 分支
|
||||
// 构建并返回 IfNode,包含条件、then 分支和 else 分支
|
||||
return new IfNode(condition, thenBranch, elseBranch);
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 用于解析 loop 语句块,支持如下结构:
|
||||
* {@code LoopStatementParser} 类负责解析自定义结构化的 {@code loop} 语句块。
|
||||
* <p>
|
||||
* 该语法结构参考了传统的 for-loop,并将其拆解为命名的语义区块:
|
||||
* <pre>{@code
|
||||
* loop:
|
||||
* initializer:
|
||||
@ -28,31 +30,53 @@ import java.util.Map;
|
||||
* update:
|
||||
* i = i + 1
|
||||
* body:
|
||||
* ...语句...
|
||||
* print(i)
|
||||
* end body
|
||||
* end loop
|
||||
* }</pre>
|
||||
* 使用 FlexibleSectionParser 解析各区块,并统一入口出口处理。
|
||||
*
|
||||
* 各区块说明:
|
||||
* <ul>
|
||||
* <li>{@code initializer}:初始化语句,通常为变量声明。</li>
|
||||
* <li>{@code condition}:循环判断条件,必须为布尔或数值表达式。</li>
|
||||
* <li>{@code update}:每轮执行后更新逻辑,通常为赋值语句。</li>
|
||||
* <li>{@code body}:主执行语句块,支持任意多条语句。</li>
|
||||
* </ul>
|
||||
* 本类依赖 {@link FlexibleSectionParser} 实现各区块的统一处理,确保结构明确、可扩展。
|
||||
*/
|
||||
public class LoopStatementParser implements StatementParser {
|
||||
|
||||
/**
|
||||
* 解析 {@code loop} 语句块,构建出对应的 {@link LoopNode} 抽象语法树节点。
|
||||
* <p>
|
||||
* 本方法会按顺序检查各个命名区块(可乱序书写),并分别绑定其对应语义解析器:
|
||||
* <ul>
|
||||
* <li>通过 {@link ParserUtils#matchHeader} 匹配区块开头;</li>
|
||||
* <li>通过 {@link FlexibleSectionParser} 派发区块逻辑;</li>
|
||||
* <li>通过 {@link StatementParserFactory} 调用实际语句解析;</li>
|
||||
* <li>最后以 {@code end loop} 表示结构终止。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param ctx 当前解析上下文。
|
||||
* @return {@link LoopNode},包含初始化、条件、更新与循环体等信息。
|
||||
*/
|
||||
@Override
|
||||
public LoopNode parse(ParserContext ctx) {
|
||||
TokenStream ts = ctx.getTokens();
|
||||
|
||||
// 匹配 loop: 开头
|
||||
// 匹配 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<StatementNode> body = new ArrayList<>();
|
||||
|
||||
// 构造区块定义
|
||||
// 定义各命名区块的识别与处理逻辑
|
||||
Map<String, FlexibleSectionParser.SectionDefinition> sections = new HashMap<>();
|
||||
|
||||
// initializer 区块:解析初始化语句
|
||||
// initializer 区块:仅支持一条语句,通常为 declare
|
||||
sections.put("initializer", new FlexibleSectionParser.SectionDefinition(
|
||||
ts1 -> ts1.peek().getLexeme().equals("initializer"),
|
||||
(ctx1, ts1) -> {
|
||||
@ -62,7 +86,7 @@ public class LoopStatementParser implements StatementParser {
|
||||
}
|
||||
));
|
||||
|
||||
// condition 区块:解析布尔条件表达式
|
||||
// condition 区块:支持任意可解析为布尔的表达式
|
||||
sections.put("condition", new FlexibleSectionParser.SectionDefinition(
|
||||
ts1 -> ts1.peek().getLexeme().equals("condition"),
|
||||
(ctx1, ts1) -> {
|
||||
@ -73,7 +97,7 @@ public class LoopStatementParser implements StatementParser {
|
||||
}
|
||||
));
|
||||
|
||||
// update 区块:解析变量赋值表达式
|
||||
// update 区块:目前仅支持单一变量赋值语句
|
||||
sections.put("update", new FlexibleSectionParser.SectionDefinition(
|
||||
ts1 -> ts1.peek().getLexeme().equals("update"),
|
||||
(ctx1, ts1) -> {
|
||||
@ -87,7 +111,7 @@ public class LoopStatementParser implements StatementParser {
|
||||
}
|
||||
));
|
||||
|
||||
// body 区块:解析语句块
|
||||
// body 区块:支持多条语句,直到遇到 end body
|
||||
sections.put("body", new FlexibleSectionParser.SectionDefinition(
|
||||
ts1 -> ts1.peek().getLexeme().equals("body"),
|
||||
(ctx1, ts1) -> {
|
||||
@ -109,13 +133,13 @@ public class LoopStatementParser implements StatementParser {
|
||||
}
|
||||
));
|
||||
|
||||
// 使用 FlexibleSectionParser 解析所有结构部分
|
||||
// 使用通用区块解析器处理各命名结构块
|
||||
FlexibleSectionParser.parse(ctx, ts, sections);
|
||||
|
||||
// 匹配 end loop
|
||||
// 解析结尾的 end loop 标记
|
||||
ParserUtils.matchFooter(ts, "loop");
|
||||
|
||||
// 构造并返回 LoopNode 抽象语法树节点
|
||||
// 返回构造完成的 LoopNode
|
||||
return new LoopNode(initializer[0], condition[0], update[0], body);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,35 +7,48 @@ import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||
import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
|
||||
|
||||
/**
|
||||
* 用于解析 return 语句。
|
||||
* 支持有无返回值两种形式:
|
||||
* {@code ReturnStatementParser} 负责解析 return 语句,是语句级解析器的一部分。
|
||||
* <p>
|
||||
* 支持以下两种 return 语句形式:
|
||||
* <pre>{@code
|
||||
* return
|
||||
* return expression
|
||||
* return // 无返回值
|
||||
* return expression // 带返回值
|
||||
* }</pre>
|
||||
* 语句必须以换行符(NEWLINE)结束。
|
||||
* 所有 return 语句都必须以换行符({@code NEWLINE})结束,返回值表达式(若存在)由 {@link PrattExpressionParser} 负责解析。
|
||||
* 若语法结构不满足要求,将在解析过程中抛出异常。
|
||||
*/
|
||||
public class ReturnStatementParser implements StatementParser {
|
||||
|
||||
/**
|
||||
* 解析 return 语句并构建 {@link ReturnNode}。
|
||||
* 解析一条 return 语句,并返回对应的 {@link ReturnNode} 抽象语法树节点。
|
||||
* <p>
|
||||
* 解析逻辑如下:
|
||||
* <ol>
|
||||
* <li>匹配起始关键字 {@code return}。</li>
|
||||
* <li>判断其后是否为 {@code NEWLINE},若否则表示存在返回值表达式。</li>
|
||||
* <li>使用 {@link PrattExpressionParser} 解析返回值表达式(若存在)。</li>
|
||||
* <li>最后匹配换行符,标志语句结束。</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param ctx 当前解析上下文。
|
||||
* @return 表示 return 语句的 AST 节点。
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,16 +4,23 @@ import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||
|
||||
/**
|
||||
* 语句解析器接口,用于将解析上下文中的 Token 转换为 {@link StatementNode}。
|
||||
* 各类语句(如声明、赋值、条件、循环、返回等)均应实现该接口。
|
||||
* {@code StatementParser} 是所有语句解析器的通用接口。
|
||||
* <p>
|
||||
* 其职责是从给定的 {@link ParserContext} 中读取并分析当前语句,构造并返回相应的抽象语法树节点。
|
||||
* 所有语句类型(如变量声明、赋值语句、控制结构、函数返回等)应提供对应的实现类。
|
||||
*
|
||||
* <p>
|
||||
* 通常,此接口的实现由 {@code StatementParserFactory} 根据当前关键字动态派发,用于解析模块体、
|
||||
* 条件分支、循环体或其他语句块中的单条语句。
|
||||
*/
|
||||
public interface StatementParser {
|
||||
|
||||
/**
|
||||
* 从给定的解析上下文中解析出一个语句节点。
|
||||
* 解析一条语句,将其从词法表示转换为结构化语法树节点。
|
||||
*
|
||||
* @param ctx 当前的解析上下文。
|
||||
* @return 表示语句的 AST 节点。
|
||||
* @param ctx 当前的解析上下文,提供 token 流、状态与符号环境等。
|
||||
* @return 表示该语句的 AST 节点,类型为 {@link StatementNode} 或其子类。
|
||||
* @throws IllegalStateException 若语法非法或结构不完整。
|
||||
*/
|
||||
StatementNode parse(ParserContext ctx);
|
||||
}
|
||||
|
||||
@ -7,19 +7,32 @@ import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* ASTJsonSerializer 工具类
|
||||
* {@code ASTJsonSerializer} 是抽象语法树(AST)序列化工具类。
|
||||
* <p>
|
||||
* 将编译器生成的 AST(抽象语法树)节点列表转换为通用的 Map/List 结构
|
||||
* 并借助 JSONParser.toJson(Object) 方法序列化为 JSON 字符串。
|
||||
* 该工具可将编译器内部构建的 AST 节点对象转换为通用的 {@code Map} 和 {@code List} 结构,
|
||||
* 并可借助 {@code JSONParser.toJson(Object)} 方法将其序列化为 JSON 字符串,用于调试、
|
||||
* 可视化或跨语言数据传输。
|
||||
* <p>
|
||||
* 支持的节点类型包括:ModuleNode、FunctionNode、DeclarationNode、
|
||||
* AssignmentNode、IfNode、LoopNode、ReturnNode、ExpressionStatementNode
|
||||
* 以及各种 ExpressionNode(如 BinaryExpressionNode、IdentifierNode 等)。
|
||||
* 支持的节点类型包括:
|
||||
* <ul>
|
||||
* <li>{@link ModuleNode}</li>
|
||||
* <li>{@link FunctionNode}</li>
|
||||
* <li>{@link DeclarationNode}</li>
|
||||
* <li>{@link AssignmentNode}</li>
|
||||
* <li>{@link IfNode}</li>
|
||||
* <li>{@link LoopNode}</li>
|
||||
* <li>{@link ReturnNode}</li>
|
||||
* <li>{@link ExpressionStatementNode}</li>
|
||||
* <li>各类 {@link ExpressionNode} 子类型,如 {@code BinaryExpressionNode}, {@code IdentifierNode} 等</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class ASTJsonSerializer {
|
||||
|
||||
/**
|
||||
* 快速创建一个 LinkedHashMap,并写入 type 字段
|
||||
* 创建包含 {@code type} 字段的节点 Map,用于标识节点类型。
|
||||
*
|
||||
* @param type 节点类型字符串。
|
||||
* @return 一个初始化后的 Map 实例。
|
||||
*/
|
||||
private static Map<String, Object> newNodeMap(String type) {
|
||||
Map<String, Object> m = new LinkedHashMap<>();
|
||||
@ -28,7 +41,11 @@ public class ASTJsonSerializer {
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于构建表达式节点的 Map
|
||||
* 构建表达式节点的 Map 表示,支持动态键值对传参。
|
||||
*
|
||||
* @param type 表达式类型。
|
||||
* @param kv 可变参数(key-value 键值对)。
|
||||
* @return 表示表达式节点的 Map。
|
||||
*/
|
||||
private static Map<String, Object> exprMap(String type, Object... kv) {
|
||||
Map<String, Object> m = new LinkedHashMap<>();
|
||||
@ -40,10 +57,10 @@ public class ASTJsonSerializer {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 AST 根节点列表序列化为 JSON 字符串。
|
||||
* 将 AST 根节点列表转换为 JSON 字符串。
|
||||
*
|
||||
* @param ast 表示抽象语法树根节点的 List<Node>
|
||||
* @return 对应的 JSON 格式字符串
|
||||
* @param ast 表示顶层语法树结构的节点列表。
|
||||
* @return 对应的 JSON 字符串表示形式。
|
||||
*/
|
||||
public static String toJsonString(List<Node> ast) {
|
||||
List<Object> list = new ArrayList<>(ast.size());
|
||||
@ -54,20 +71,20 @@ public class ASTJsonSerializer {
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归地将 AST 节点转换为 Map 或 List 等通用结构,
|
||||
* 便于后续统一序列化。
|
||||
* 将任意 AST 节点递归转换为 Map/List 结构。
|
||||
*
|
||||
* @param n 要转换的 AST 节点。
|
||||
* @return 以 Map/List 表示的结构化数据。
|
||||
*/
|
||||
private static Object nodeToMap(Node n) {
|
||||
return switch (n) {
|
||||
// 模块节点
|
||||
case ModuleNode(String name, List<ImportNode> imports, List<FunctionNode> functions) -> {
|
||||
Map<String, Object> map = newNodeMap("Module");
|
||||
map.put("name", name);
|
||||
List<Object> imps = new ArrayList<>(imports.size());
|
||||
for (ImportNode imp : imports) {
|
||||
imps.add(Map.of(
|
||||
"type", "Import",
|
||||
"module", imp.moduleName()
|
||||
));
|
||||
imps.add(Map.of("type", "Import", "module", imp.moduleName()));
|
||||
}
|
||||
map.put("imports", imps);
|
||||
List<Object> funcs = new ArrayList<>(functions.size());
|
||||
@ -77,15 +94,13 @@ public class ASTJsonSerializer {
|
||||
map.put("functions", funcs);
|
||||
yield map;
|
||||
}
|
||||
// 函数定义节点
|
||||
case FunctionNode f -> {
|
||||
Map<String, Object> map = newNodeMap("Function");
|
||||
map.put("name", f.name());
|
||||
List<Object> params = new ArrayList<>(f.parameters().size());
|
||||
for (var p : f.parameters()) {
|
||||
params.add(Map.of(
|
||||
"name", p.name(),
|
||||
"type", p.type()
|
||||
));
|
||||
params.add(Map.of("name", p.name(), "type", p.type()));
|
||||
}
|
||||
map.put("parameters", params);
|
||||
map.put("returnType", f.returnType());
|
||||
@ -96,19 +111,20 @@ public class ASTJsonSerializer {
|
||||
map.put("body", body);
|
||||
yield map;
|
||||
}
|
||||
// 变量声明节点
|
||||
case DeclarationNode d -> {
|
||||
Map<String, Object> map = newNodeMap("Declaration");
|
||||
map.put("name", d.getName());
|
||||
map.put("varType", d.getType());
|
||||
map.put("initializer",
|
||||
d.getInitializer().map(ASTJsonSerializer::exprToMap).orElse(null)
|
||||
);
|
||||
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<String, Object> map = newNodeMap("If");
|
||||
map.put("condition", exprToMap(i.condition()));
|
||||
@ -122,6 +138,7 @@ public class ASTJsonSerializer {
|
||||
}
|
||||
yield map;
|
||||
}
|
||||
// 循环语句节点
|
||||
case LoopNode l -> {
|
||||
Map<String, Object> map = newNodeMap("Loop");
|
||||
map.put("initializer", l.initializer() != null ? nodeToMap(l.initializer()) : null);
|
||||
@ -132,21 +149,28 @@ public class ASTJsonSerializer {
|
||||
map.put("body", body);
|
||||
yield map;
|
||||
}
|
||||
// return 语句节点
|
||||
case ReturnNode r -> {
|
||||
Map<String, Object> 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 表示。
|
||||
* 将表达式类型节点转换为 Map 表示形式。
|
||||
*
|
||||
* @param expr 表达式 AST 节点。
|
||||
* @return 表示该表达式的 Map。
|
||||
*/
|
||||
private static Object exprToMap(ExpressionNode expr) {
|
||||
return switch (expr) {
|
||||
@ -167,6 +191,7 @@ public class ASTJsonSerializer {
|
||||
"object", exprToMap(object),
|
||||
"member", member
|
||||
);
|
||||
// 默认兜底处理:只写类型
|
||||
default -> Map.of("type", expr.getClass().getSimpleName());
|
||||
};
|
||||
}
|
||||
|
||||
@ -9,63 +9,84 @@ import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* 通用的解析器,用于解析结构化的内容部分,完全解耦合关键字和语法。
|
||||
* {@code FlexibleSectionParser} 是一个通用的语法块解析工具。
|
||||
* <p>
|
||||
* FlexibleSectionParser 提供了一个灵活的机制来解析可变的语法块。每个语法块的解析逻辑通过外部提供,
|
||||
* 该类仅负责按顺序解析不同的区块,直到遇到结束标记,同时能够跳过并收集注释供后续使用。
|
||||
* <p>
|
||||
* 使用此类可以解析如函数定义中的多个部分(如参数、返回类型、函数体等),而无需在解析器中硬编码这些结构,
|
||||
* 并且保留注释信息以便 IDE 或工具链进行注释跳转、重构等操作。
|
||||
* 该工具支持解析由关键字标识的多段结构化区块内容,常用于解析函数、类、模块、循环等语法单元中的命名子结构。
|
||||
* 相比传统硬编码方式,提供更灵活、可组合的解析能力,允许解析器模块动态注册处理逻辑,而非将所有逻辑写死在主流程中。
|
||||
*
|
||||
* <p>典型应用包括:
|
||||
* <ul>
|
||||
* <li>函数体解析中的 {@code params}、{@code returns}、{@code body} 等部分</li>
|
||||
* <li>模块定义中的 {@code imports}、{@code functions} 等部分</li>
|
||||
* <li>用户自定义 DSL 的可扩展语法结构</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>该工具具备以下能力:
|
||||
* <ul>
|
||||
* <li>自动跳过注释与空行</li>
|
||||
* <li>根据区块名称调用外部提供的解析器</li>
|
||||
* <li>支持终止标志(如 {@code end})来退出解析流程</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class FlexibleSectionParser {
|
||||
|
||||
/**
|
||||
* 解析一系列的可变区块,解析顺序和具体内容由外部定义。
|
||||
* 在解析过程中会跳过并收集注释,以及跳过空行。
|
||||
* 启动结构化区块的统一解析流程。
|
||||
* <p>
|
||||
* 每次调用会:
|
||||
* <ol>
|
||||
* <li>从 token 流中跳过空行与注释</li>
|
||||
* <li>依照当前 token 判断是否匹配某个区块</li>
|
||||
* <li>调用对应 {@link SectionDefinition} 执行区块解析逻辑</li>
|
||||
* <li>若遇到 {@code end} 关键字,则终止解析过程</li>
|
||||
* <li>若当前 token 不匹配任何已注册区块,抛出异常</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param ctx 当前的解析上下文,包含语法分析所需的所有信息(如作用域、错误处理等)
|
||||
* @param tokens 当前的词法 token 流,用于逐个查看或消耗 token
|
||||
* @param sectionDefinitions 区块定义映射:每个关键字(如 "params", "returns", "body")对应一个区块定义
|
||||
* @param ctx 当前解析上下文,提供语法环境与作用域信息
|
||||
* @param tokens 当前 token 流
|
||||
* @param sectionDefinitions 各个区块的定义映射(key 为关键字,value 为判断 + 解析逻辑组合)
|
||||
* @throws RuntimeException 若出现无法识别的关键字或未满足的匹配条件
|
||||
*/
|
||||
public static void parse(ParserContext ctx,
|
||||
TokenStream tokens,
|
||||
Map<String, SectionDefinition> sectionDefinitions) {
|
||||
|
||||
// 在开始解析之前,跳过并收集所有紧邻开头的注释和空行
|
||||
// 跳过开头的注释或空行
|
||||
skipCommentsAndNewlines(tokens);
|
||||
|
||||
while (true) {
|
||||
// 在每次解析前,跳过并收集注释和空行
|
||||
// 跳过当前区块之间的空白与注释
|
||||
skipCommentsAndNewlines(tokens);
|
||||
|
||||
String keyword = tokens.peek().getLexeme();
|
||||
|
||||
// 结束关键字表示解析流程终止
|
||||
if ("end".equals(keyword)) {
|
||||
// 遇到 'end' 则终止解析
|
||||
break;
|
||||
}
|
||||
|
||||
// 查找匹配的区块定义
|
||||
SectionDefinition definition = sectionDefinitions.get(keyword);
|
||||
if (definition != null && definition.condition().test(tokens)) {
|
||||
// 执行该区块的解析逻辑
|
||||
definition.parser().accept(ctx, tokens);
|
||||
definition.parser().accept(ctx, tokens); // 执行解析逻辑
|
||||
} else {
|
||||
// 无法识别该关键字或条件不满足则报错
|
||||
throw new RuntimeException("未识别的关键字或条件不满足: " + keyword);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳过所有连续的注释行和空行。
|
||||
* 跳过连续出现的注释行或空行(NEWLINE)。
|
||||
* <p>
|
||||
* 该方法用于在区块之间清理无效 token,避免影响结构判断。
|
||||
*
|
||||
* @param tokens 词法流
|
||||
* @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();
|
||||
tokens.next(); // 跳过注释或换行
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
@ -73,13 +94,17 @@ public class FlexibleSectionParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 区块定义类:表示一个语法区块的匹配条件和解析逻辑。
|
||||
* 每个区块由两个部分组成:
|
||||
* - 条件:用于判断当前 token 流是否应进入该区块的解析。
|
||||
* - 解析器:具体的解析逻辑,通常是消费若干 token 并更新解析上下文。
|
||||
* 表示一个结构区块的定义,包含匹配条件与解析器。
|
||||
* <p>
|
||||
* 每个区块由两部分组成:
|
||||
* <ul>
|
||||
* <li>{@code condition}:用于判断当前 token 是否应进入该区块</li>
|
||||
* <li>{@code parser}:该区块对应的实际解析逻辑</li>
|
||||
* </ul>
|
||||
* 可实现懒加载、多语言支持或 DSL 的结构化扩展。
|
||||
*
|
||||
* @param condition 匹配条件,返回 true 表示此区块应被解析
|
||||
* @param parser 实际解析逻辑
|
||||
* @param condition 判断是否触发该区块的谓词函数
|
||||
* @param parser 区块解析逻辑(消费语法上下文与 token 流)
|
||||
*/
|
||||
public record SectionDefinition(Predicate<TokenStream> condition,
|
||||
BiConsumer<ParserContext, TokenStream> parser) {
|
||||
|
||||
@ -16,7 +16,7 @@ import java.util.Map.Entry;
|
||||
* 4. 序列化器基于 StringBuilder,预分配容量,减少中间字符串创建
|
||||
*/
|
||||
public class JSONParser {
|
||||
// 私有构造,禁止外部实例化
|
||||
|
||||
private JSONParser() {}
|
||||
|
||||
/**
|
||||
|
||||
@ -4,44 +4,61 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||
import org.jcnc.snow.compiler.parser.context.TokenStream;
|
||||
|
||||
/**
|
||||
* 语法结构通用辅助工具类。
|
||||
* 提供常用的结构匹配和容错功能。
|
||||
* {@code ParserUtils} 是语法结构解析过程中的通用辅助工具类。
|
||||
* <p>
|
||||
* 提供一系列静态方法用于标准语法结构(如结构头、结构尾)的匹配校验,以及常用的容错处理操作。
|
||||
* 这些方法可在函数定义、模块定义、循环、条件语句等语法块中复用,有效减少冗余代码,提高解析器稳定性。
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>匹配结构性语法起始标记(如 {@code loop:}、{@code function:})</li>
|
||||
* <li>匹配结构性语法结尾标记(如 {@code end loop}、{@code end function})</li>
|
||||
* <li>跳过多余换行符,增强语法容错性</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class ParserUtils {
|
||||
|
||||
/**
|
||||
* 匹配形如 "keyword:" 的语法结构头部,并跳过换行。
|
||||
* 匹配结构语法的标准起始格式 {@code keyword:},并跳过其后的换行符。
|
||||
* <p>
|
||||
* 该方法适用于需要标识结构起点的语法元素,如 {@code loop:}、{@code function:} 等。
|
||||
* 若格式不匹配,将抛出语法异常。
|
||||
*
|
||||
* @param ts Token 流
|
||||
* @param keyword 结构标识关键字(如 "loop", "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);
|
||||
ts.expect(keyword); // 匹配关键字
|
||||
ts.expect(":"); // 匹配冒号
|
||||
ts.expectType(TokenType.NEWLINE); // 匹配行尾换行
|
||||
skipNewlines(ts); // 跳过多余空行
|
||||
}
|
||||
|
||||
/**
|
||||
* 匹配形如 "end keyword" 的语法结构结尾。
|
||||
* 匹配结构语法的标准结尾格式 {@code end keyword}。
|
||||
* <p>
|
||||
* 该方法用于验证结构块的结束,例如 {@code end loop}、{@code end if} 等。
|
||||
* 若格式不正确,将抛出异常。
|
||||
*
|
||||
* @param ts Token 流
|
||||
* @param keyword 结构标识关键字(如 "loop", "function" 等)
|
||||
* @param ts 当前的 token 流
|
||||
* @param keyword 对应的结构关键字(必须与开始标记一致)
|
||||
*/
|
||||
public static void matchFooter(TokenStream ts, String keyword) {
|
||||
ts.expect("end");
|
||||
ts.expect(keyword);
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
ts.expect("end"); // 匹配 'end'
|
||||
ts.expect(keyword); // 匹配结构名
|
||||
ts.expectType(TokenType.NEWLINE); // 匹配行尾
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳过连续的换行符,常用于容错与美化语法结构。
|
||||
* 跳过连续的换行符({@code NEWLINE})。
|
||||
* <p>
|
||||
* 通常用于解析器之间的过渡阶段,以消除格式干扰,提升容错性。
|
||||
*
|
||||
* @param ts Token 流
|
||||
* @param ts 当前的 token 流
|
||||
*/
|
||||
public static void skipNewlines(TokenStream ts) {
|
||||
while (ts.peek().getType() == TokenType.NEWLINE) {
|
||||
ts.next();
|
||||
ts.next(); // 连续消费换行符
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user