实现AST
This commit is contained in:
parent
2b7a2b86b8
commit
8d1b180c2d
60
src/main/java/org/jcnc/snow/compiler/JsonFormatter.java
Normal file
60
src/main/java/org/jcnc/snow/compiler/JsonFormatter.java
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/JsonFormatter.java
|
||||||
|
package org.jcnc.snow.compiler;
|
||||||
|
|
||||||
|
public class JsonFormatter {
|
||||||
|
/**
|
||||||
|
* 对一个紧凑的 JSON 字符串进行缩进美化。
|
||||||
|
* @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);
|
||||||
|
break;
|
||||||
|
case '}', ']':
|
||||||
|
sb.append('\n');
|
||||||
|
indent--;
|
||||||
|
appendIndent(sb, indent);
|
||||||
|
sb.append(c);
|
||||||
|
break;
|
||||||
|
case ',':
|
||||||
|
sb.append(c)
|
||||||
|
.append('\n');
|
||||||
|
appendIndent(sb, indent);
|
||||||
|
break;
|
||||||
|
case ':':
|
||||||
|
sb.append(c).append(' ');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (!Character.isWhitespace(c)) {
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 在字符串内原样输出
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void appendIndent(StringBuilder sb, int indent) {
|
||||||
|
for (int i = 0; i < indent; i++) {
|
||||||
|
sb.append(" "); // 两个空格
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,25 +1,46 @@
|
|||||||
// File: org/jcnc/snow/compiler/Main.java
|
|
||||||
package org.jcnc.snow.compiler;
|
package org.jcnc.snow.compiler;
|
||||||
|
|
||||||
|
|
||||||
import org.jcnc.snow.compiler.lexer.LexerEngine;
|
import org.jcnc.snow.compiler.lexer.LexerEngine;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
import org.jcnc.snow.compiler.lexer.utils.TokenPrinter;
|
import org.jcnc.snow.compiler.lexer.utils.TokenPrinter;
|
||||||
|
import org.jcnc.snow.compiler.parser.ParserEngine;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.ASTJsonSerializer;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.Node;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.ASTPrinter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
// 如果命令行中提供了文件名,就读取该文件;否则默认读取当前目录下的 "opcode" 文件
|
// 读取源文件
|
||||||
String filePath = args.length > 0 ? args[0] : "test";
|
String filePath = args.length > 0 ? args[0] : "test";
|
||||||
|
|
||||||
String source = Files.readString(Path.of(filePath), StandardCharsets.UTF_8);
|
String source = Files.readString(Path.of(filePath), StandardCharsets.UTF_8);
|
||||||
// 词法分析
|
|
||||||
LexerEngine lexerEngine = new LexerEngine(source);
|
|
||||||
// 打印所有 Token
|
|
||||||
TokenPrinter.printTokens(lexerEngine.getAllTokens());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
// 1. 词法分析
|
||||||
|
LexerEngine lexerEngine = new LexerEngine(source);
|
||||||
|
List<Token> tokens = lexerEngine.getAllTokens();
|
||||||
|
TokenPrinter.printTokens(tokens); // 可选:打印 Token 列表
|
||||||
|
|
||||||
|
// 2. 语法分析
|
||||||
|
ParserContext ctx = new ParserContext(tokens);
|
||||||
|
List<Node> ast = new ParserEngine(ctx).parse();
|
||||||
|
|
||||||
|
// 3. 可读地打印 AST
|
||||||
|
ASTPrinter.print(ast);
|
||||||
|
|
||||||
|
// 1) 输出紧凑 JSON
|
||||||
|
String compact = ASTJsonSerializer.toJsonString(ast);
|
||||||
|
System.out.println("=== Compact JSON ===");
|
||||||
|
System.out.println(compact);
|
||||||
|
|
||||||
|
// 2) 输出美化后的 JSON
|
||||||
|
System.out.println("=== Pretty JSON ===");
|
||||||
|
System.out.println(JsonFormatter.prettyPrint(compact));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ParserEngine.java
|
||||||
|
package org.jcnc.snow.compiler.parser;
|
||||||
|
|
||||||
|
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 org.jcnc.snow.compiler.parser.factory.TopLevelParserFactory;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.Node;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析引擎入口:循环读取顶层块直到 EOF,自动跳过空行(NEWLINE)。
|
||||||
|
*/
|
||||||
|
public class ParserEngine {
|
||||||
|
private final ParserContext ctx;
|
||||||
|
|
||||||
|
public ParserEngine(ParserContext ctx) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Node> parse() {
|
||||||
|
List<Node> nodes = new ArrayList<>();
|
||||||
|
TokenStream ts = ctx.getTokens();
|
||||||
|
while (!ts.isAtEnd()) {
|
||||||
|
// 跳过空行(NEWLINE)
|
||||||
|
if (ts.peek().getType() == TokenType.NEWLINE) {
|
||||||
|
ts.next();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String lex = ts.peek().getLexeme();
|
||||||
|
TopLevelParser p = TopLevelParserFactory.get(lex);
|
||||||
|
if (p == null) {
|
||||||
|
throw new IllegalStateException("Unexpected top-level token: " + lex);
|
||||||
|
}
|
||||||
|
nodes.add(p.parse(ctx));
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/TopLevelParser.java
|
||||||
|
package org.jcnc.snow.compiler.parser;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.Node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 顶层解析器接口,如 module、import 等。
|
||||||
|
*/
|
||||||
|
public interface TopLevelParser {
|
||||||
|
Node parse(ParserContext ctx);
|
||||||
|
}
|
||||||
@ -0,0 +1,210 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/ASTJsonSerializer.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ASTJsonSerializer {
|
||||||
|
|
||||||
|
public static String toJsonString(List<Node> ast) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("[");
|
||||||
|
for (int i = 0; i < ast.size(); i++) {
|
||||||
|
sb.append(nodeToJson(ast.get(i)));
|
||||||
|
if (i + 1 < ast.size()) sb.append(",");
|
||||||
|
}
|
||||||
|
sb.append("]");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String nodeToJson(Node n) {
|
||||||
|
if (n instanceof ModuleNode m) return moduleToJson(m);
|
||||||
|
if (n instanceof FunctionNode f) return functionToJson(f);
|
||||||
|
if (n instanceof DeclarationNode d) return declarationToJson(d);
|
||||||
|
if (n instanceof AssignmentNode a) return assignmentToJson(a);
|
||||||
|
if (n instanceof IfNode i) return ifToJson(i);
|
||||||
|
if (n instanceof LoopNode l) return loopToJson(l);
|
||||||
|
if (n instanceof ReturnNode r) return returnToJson(r);
|
||||||
|
if (n instanceof ExpressionStatementNode es)
|
||||||
|
return exprStmtToJson(es);
|
||||||
|
// fallback:仅输出类型名
|
||||||
|
return "{\"type\":\"" + n.getClass().getSimpleName() + "\"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String moduleToJson(ModuleNode m) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("{\"type\":\"Module\",")
|
||||||
|
.append("\"name\":").append(quote(m.getName())).append(",");
|
||||||
|
// imports
|
||||||
|
sb.append("\"imports\":[");
|
||||||
|
for (int i = 0; i < m.getImports().size(); i++) {
|
||||||
|
ImportNode imp = m.getImports().get(i);
|
||||||
|
sb.append("{\"type\":\"Import\",\"module\":")
|
||||||
|
.append(quote(imp.getModuleName())).append("}");
|
||||||
|
if (i + 1 < m.getImports().size()) sb.append(",");
|
||||||
|
}
|
||||||
|
sb.append("],");
|
||||||
|
// functions
|
||||||
|
sb.append("\"functions\":[");
|
||||||
|
for (int i = 0; i < m.getFunctions().size(); i++) {
|
||||||
|
sb.append(functionToJson(m.getFunctions().get(i)));
|
||||||
|
if (i + 1 < m.getFunctions().size()) sb.append(",");
|
||||||
|
}
|
||||||
|
sb.append("]}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String functionToJson(FunctionNode f) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("{\"type\":\"Function\",")
|
||||||
|
.append("\"name\":").append(quote(f.getName())).append(",");
|
||||||
|
// parameters
|
||||||
|
sb.append("\"parameters\":[");
|
||||||
|
for (int i = 0; i < f.getParameters().size(); i++) {
|
||||||
|
ParameterNode p = f.getParameters().get(i);
|
||||||
|
sb.append("{\"name\":").append(quote(p.getName()))
|
||||||
|
.append(",\"type\":").append(quote(p.getType())).append("}");
|
||||||
|
if (i + 1 < f.getParameters().size()) sb.append(",");
|
||||||
|
}
|
||||||
|
sb.append("],");
|
||||||
|
sb.append("\"returnType\":").append(quote(f.getReturnType())).append(",");
|
||||||
|
// body
|
||||||
|
sb.append("\"body\":[");
|
||||||
|
List<StatementNode> body = f.getBody();
|
||||||
|
for (int i = 0; i < body.size(); i++) {
|
||||||
|
sb.append(nodeToJson(body.get(i)));
|
||||||
|
if (i + 1 < body.size()) sb.append(",");
|
||||||
|
}
|
||||||
|
sb.append("]}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String declarationToJson(DeclarationNode d) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("{\"type\":\"Declaration\",")
|
||||||
|
.append("\"name\":").append(quote(d.getName())).append(",")
|
||||||
|
.append("\"varType\":").append(quote(d.getType()));
|
||||||
|
d.getInitializer().ifPresent(init ->
|
||||||
|
sb.append(",\"initializer\":").append(exprToJson(init))
|
||||||
|
);
|
||||||
|
sb.append("}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String assignmentToJson(AssignmentNode a) {
|
||||||
|
return "{\"type\":\"Assignment\",\"variable\":"
|
||||||
|
+ quote(a.getVariable())
|
||||||
|
+ ",\"value\":"
|
||||||
|
+ exprToJson(a.getValue())
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String ifToJson(IfNode i) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("{\"type\":\"If\",\"condition\":")
|
||||||
|
.append(exprToJson(i.getCondition())).append(",");
|
||||||
|
sb.append("\"then\":[");
|
||||||
|
List<StatementNode> thenBranch = i.getThenBranch();
|
||||||
|
for (int j = 0; j < thenBranch.size(); j++) {
|
||||||
|
sb.append(nodeToJson(thenBranch.get(j)));
|
||||||
|
if (j + 1 < thenBranch.size()) sb.append(",");
|
||||||
|
}
|
||||||
|
sb.append("]}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String loopToJson(LoopNode l) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("{\"type\":\"Loop\",")
|
||||||
|
.append("\"initializer\":").append(nodeToJson(l.getInitializer())).append(",")
|
||||||
|
.append("\"condition\":").append(exprToJson(l.getCondition())).append(",")
|
||||||
|
.append("\"update\":").append(nodeToJson(l.getUpdate())).append(",")
|
||||||
|
.append("\"body\":[");
|
||||||
|
List<StatementNode> body = l.getBody();
|
||||||
|
for (int i = 0; i < body.size(); i++) {
|
||||||
|
sb.append(nodeToJson(body.get(i)));
|
||||||
|
if (i + 1 < body.size()) sb.append(",");
|
||||||
|
}
|
||||||
|
sb.append("]}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String returnToJson(ReturnNode r) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("{\"type\":\"Return\"");
|
||||||
|
r.getExpression().ifPresent(expr ->
|
||||||
|
sb.append(",\"value\":").append(exprToJson(expr))
|
||||||
|
);
|
||||||
|
sb.append("}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String exprStmtToJson(ExpressionStatementNode es) {
|
||||||
|
return "{\"type\":\"ExpressionStatement\",\"expression\":"
|
||||||
|
+ exprToJson(es.getExpression())
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String exprToJson(ExpressionNode expr) {
|
||||||
|
if (expr instanceof BinaryExpressionNode b) return binaryToJson(b);
|
||||||
|
if (expr instanceof IdentifierNode id) return idToJson(id);
|
||||||
|
if (expr instanceof NumberLiteralNode num) return numToJson(num);
|
||||||
|
if (expr instanceof StringLiteralNode str) return strToJson(str);
|
||||||
|
if (expr instanceof CallExpressionNode c) return callToJson(c);
|
||||||
|
if (expr instanceof MemberExpressionNode m) return memberToJson(m);
|
||||||
|
// fallback
|
||||||
|
return "{\"type\":\"" + expr.getClass().getSimpleName() + "\"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String binaryToJson(BinaryExpressionNode b) {
|
||||||
|
return "{\"type\":\"BinaryExpression\","
|
||||||
|
+ "\"left\":" + exprToJson(b.getLeft()) + ","
|
||||||
|
+ "\"operator\":" + quote(b.getOperator()) + ","
|
||||||
|
+ "\"right\":" + exprToJson(b.getRight())
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
private static String idToJson(IdentifierNode id) {
|
||||||
|
return "{\"type\":\"Identifier\",\"name\":" + quote(id.getName()) + "}";
|
||||||
|
}
|
||||||
|
private static String numToJson(NumberLiteralNode num) {
|
||||||
|
return "{\"type\":\"NumberLiteral\",\"value\":" + quote(num.getValue()) + "}";
|
||||||
|
}
|
||||||
|
private static String strToJson(StringLiteralNode str) {
|
||||||
|
return "{\"type\":\"StringLiteral\",\"value\":" + quote(str.getValue()) + "}";
|
||||||
|
}
|
||||||
|
private static String callToJson(CallExpressionNode c) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("{\"type\":\"CallExpression\",\"callee\":")
|
||||||
|
.append(exprToJson(c.getCallee())).append(",")
|
||||||
|
.append("\"arguments\":[");
|
||||||
|
for (int i = 0; i < c.getArguments().size(); i++) {
|
||||||
|
sb.append(exprToJson(c.getArguments().get(i)));
|
||||||
|
if (i + 1 < c.getArguments().size()) sb.append(",");
|
||||||
|
}
|
||||||
|
sb.append("]}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
private static String memberToJson(MemberExpressionNode m) {
|
||||||
|
return "{\"type\":\"MemberExpression\",\"object\":"
|
||||||
|
+ exprToJson(m.getObject())
|
||||||
|
+ ",\"member\":" + quote(m.getMember())
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 把 Java 字符串内容转成 JSON 字符串常量(加双引号并转义) */
|
||||||
|
private static String quote(String s) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
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("\"");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/ASTPrinter.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一个简单的 AST 打印器,将所有节点格式化成可读的多行文本。
|
||||||
|
*/
|
||||||
|
public class ASTPrinter {
|
||||||
|
public static void print(List<Node> nodes) {
|
||||||
|
for (Node n : nodes) {
|
||||||
|
print(n, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void print(Node n, int indent) {
|
||||||
|
String pad = " ".repeat(indent);
|
||||||
|
|
||||||
|
if (n instanceof ModuleNode m) {
|
||||||
|
System.out.println(pad + "module " + m.getName());
|
||||||
|
for (ImportNode imp : m.getImports()) {
|
||||||
|
System.out.println(pad + " import " + imp.getModuleName());
|
||||||
|
}
|
||||||
|
for (FunctionNode fn : m.getFunctions()) {
|
||||||
|
print(fn, indent + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (n instanceof FunctionNode f) {
|
||||||
|
System.out.println(pad + "function " + f.getName()
|
||||||
|
+ "(params=" + f.getParameters() + ", return=" + f.getReturnType() + ")");
|
||||||
|
for (StatementNode stmt : f.getBody()) {
|
||||||
|
print(stmt, indent + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (n instanceof DeclarationNode d) {
|
||||||
|
String init = d.getInitializer()
|
||||||
|
.map(Object::toString)
|
||||||
|
.map(s -> " = " + s)
|
||||||
|
.orElse("");
|
||||||
|
System.out.println(pad + "declare " + d.getName() + ":" + d.getType() + init);
|
||||||
|
|
||||||
|
} else if (n instanceof AssignmentNode a) {
|
||||||
|
System.out.println(pad + a.getVariable() + " = " + a.getValue());
|
||||||
|
|
||||||
|
} else if (n instanceof IfNode i) {
|
||||||
|
System.out.println(pad + "if " + i.getCondition());
|
||||||
|
for (StatementNode stmt : i.getThenBranch()) {
|
||||||
|
print(stmt, indent + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (n instanceof LoopNode l) {
|
||||||
|
System.out.println(pad + "loop {");
|
||||||
|
print(l.getInitializer(), indent + 1);
|
||||||
|
System.out.println(pad + " condition: " + l.getCondition());
|
||||||
|
System.out.println(pad + " update: " + l.getUpdate());
|
||||||
|
System.out.println(pad + " body {");
|
||||||
|
for (StatementNode stmt : l.getBody()) {
|
||||||
|
print(stmt, indent + 2);
|
||||||
|
}
|
||||||
|
System.out.println(pad + " }");
|
||||||
|
System.out.println(pad + "}");
|
||||||
|
|
||||||
|
} else if (n instanceof ReturnNode r) {
|
||||||
|
System.out.println(pad + "return" +
|
||||||
|
r.getExpression().map(e -> " " + e).orElse(""));
|
||||||
|
|
||||||
|
} else if (n instanceof ExpressionStatementNode es) {
|
||||||
|
System.out.println(pad + es.getExpression());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 回退:直接调用 toString()
|
||||||
|
System.out.println(pad + n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/AssignmentNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 赋值语句节点,例如 x = expr。
|
||||||
|
*/
|
||||||
|
public class AssignmentNode implements StatementNode {
|
||||||
|
private final String variable;
|
||||||
|
private final ExpressionNode value;
|
||||||
|
|
||||||
|
public AssignmentNode(String variable, ExpressionNode value) {
|
||||||
|
this.variable = variable;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVariable() {
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpressionNode getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return variable + " = " + value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/BinaryExpressionNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 二元运算表达式节点,如 a + b。
|
||||||
|
*/
|
||||||
|
public class BinaryExpressionNode implements ExpressionNode {
|
||||||
|
private final ExpressionNode left;
|
||||||
|
private final String operator;
|
||||||
|
private final ExpressionNode right;
|
||||||
|
|
||||||
|
public BinaryExpressionNode(ExpressionNode left, String operator, ExpressionNode right) {
|
||||||
|
this.left = left;
|
||||||
|
this.operator = operator;
|
||||||
|
this.right = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpressionNode getLeft() { return left; }
|
||||||
|
public String getOperator() { return operator; }
|
||||||
|
public ExpressionNode getRight() { return right; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return left + " " + operator + " " + right;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/CallExpressionNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 函数调用表达式节点:callee(args…)。
|
||||||
|
*/
|
||||||
|
public class CallExpressionNode implements ExpressionNode {
|
||||||
|
private final ExpressionNode callee;
|
||||||
|
private final List<ExpressionNode> arguments;
|
||||||
|
|
||||||
|
public CallExpressionNode(ExpressionNode callee, List<ExpressionNode> arguments) {
|
||||||
|
this.callee = callee;
|
||||||
|
this.arguments = arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpressionNode getCallee() { return callee; }
|
||||||
|
public List<ExpressionNode> getArguments() { return arguments; }
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/DeclarationNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* declare 语句节点:声明变量,可选初始化表达式。
|
||||||
|
*/
|
||||||
|
public class DeclarationNode implements StatementNode {
|
||||||
|
private final String name;
|
||||||
|
private final String type;
|
||||||
|
private final Optional<ExpressionNode> initializer;
|
||||||
|
|
||||||
|
public DeclarationNode(String name, String type, ExpressionNode initializer) {
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
this.initializer = Optional.ofNullable(initializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
public String getType() { return type; }
|
||||||
|
public Optional<ExpressionNode> getInitializer() { return initializer; }
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/ExpressionNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标记所有表达式节点。
|
||||||
|
*/
|
||||||
|
public interface ExpressionNode extends Node {}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/ExpressionStatementNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单独的表达式语句节点(如函数调用、赋值等)。
|
||||||
|
*/
|
||||||
|
public class ExpressionStatementNode implements StatementNode {
|
||||||
|
private final ExpressionNode expression;
|
||||||
|
|
||||||
|
public ExpressionStatementNode(ExpressionNode expression) {
|
||||||
|
this.expression = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpressionNode getExpression() { return expression; }
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/FunctionNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 函数定义节点:包含函数名、参数列表、返回类型和函数体语句列表。
|
||||||
|
*/
|
||||||
|
public class FunctionNode implements Node {
|
||||||
|
private final String name;
|
||||||
|
private final List<ParameterNode> parameters;
|
||||||
|
private final String returnType;
|
||||||
|
private final List<StatementNode> body;
|
||||||
|
|
||||||
|
public FunctionNode(String name, List<ParameterNode> parameters,
|
||||||
|
String returnType, List<StatementNode> body) {
|
||||||
|
this.name = name;
|
||||||
|
this.parameters = parameters;
|
||||||
|
this.returnType = returnType;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
public List<ParameterNode> getParameters() { return parameters; }
|
||||||
|
public String getReturnType() { return returnType; }
|
||||||
|
public List<StatementNode> getBody() { return body; }
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/IdentifierNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识符表达式节点。
|
||||||
|
*/
|
||||||
|
public class IdentifierNode implements ExpressionNode {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public IdentifierNode(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/main/java/org/jcnc/snow/compiler/parser/ast/IfNode.java
Normal file
20
src/main/java/org/jcnc/snow/compiler/parser/ast/IfNode.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/IfNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if 语句节点:包含条件表达式和 then 分支语句列表。
|
||||||
|
*/
|
||||||
|
public class IfNode implements StatementNode {
|
||||||
|
private final ExpressionNode condition;
|
||||||
|
private final List<StatementNode> thenBranch;
|
||||||
|
|
||||||
|
public IfNode(ExpressionNode condition, List<StatementNode> thenBranch) {
|
||||||
|
this.condition = condition;
|
||||||
|
this.thenBranch = thenBranch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpressionNode getCondition() { return condition; }
|
||||||
|
public List<StatementNode> getThenBranch() { return thenBranch; }
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/ImportNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* import 语句节点:仅保存被导入模块名。
|
||||||
|
*/
|
||||||
|
public class ImportNode implements Node {
|
||||||
|
private final String moduleName;
|
||||||
|
|
||||||
|
public ImportNode(String moduleName) {
|
||||||
|
this.moduleName = moduleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModuleName() { return moduleName; }
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/LoopNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* loop 语句节点:包含初始化语句、条件表达式、更新语句和循环体。
|
||||||
|
*/
|
||||||
|
public class LoopNode implements StatementNode {
|
||||||
|
private final StatementNode initializer;
|
||||||
|
private final ExpressionNode condition;
|
||||||
|
private final StatementNode update;
|
||||||
|
private final List<StatementNode> body;
|
||||||
|
|
||||||
|
public LoopNode(StatementNode initializer,
|
||||||
|
ExpressionNode condition,
|
||||||
|
StatementNode update,
|
||||||
|
List<StatementNode> body) {
|
||||||
|
this.initializer = initializer;
|
||||||
|
this.condition = condition;
|
||||||
|
this.update = update;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatementNode getInitializer() { return initializer; }
|
||||||
|
public ExpressionNode getCondition() { return condition; }
|
||||||
|
public StatementNode getUpdate() { return update; }
|
||||||
|
public List<StatementNode> getBody() { return body; }
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/MemberExpressionNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成员访问表达式节点,如 obj.prop。
|
||||||
|
*/
|
||||||
|
public class MemberExpressionNode implements ExpressionNode {
|
||||||
|
private final ExpressionNode object;
|
||||||
|
private final String member;
|
||||||
|
|
||||||
|
public MemberExpressionNode(ExpressionNode object, String member) {
|
||||||
|
this.object = object;
|
||||||
|
this.member = member;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpressionNode getObject() { return object; }
|
||||||
|
public String getMember() { return member; }
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return object + "." + member;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/ModuleNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模块定义节点:包含模块名、import 列表和函数列表。
|
||||||
|
*/
|
||||||
|
public class ModuleNode implements Node {
|
||||||
|
private final String name;
|
||||||
|
private final List<ImportNode> imports;
|
||||||
|
private final List<FunctionNode> functions;
|
||||||
|
|
||||||
|
public ModuleNode(String name, List<ImportNode> imports, List<FunctionNode> functions) {
|
||||||
|
this.name = name;
|
||||||
|
this.imports = imports;
|
||||||
|
this.functions = functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ImportNode> getImports() {
|
||||||
|
return imports;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<FunctionNode> getFunctions() {
|
||||||
|
return functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回模块的字符串表示,便于调试和打印。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringJoiner importJoiner = new StringJoiner(", ");
|
||||||
|
for (ImportNode imp : imports) {
|
||||||
|
importJoiner.add(imp.getModuleName());
|
||||||
|
}
|
||||||
|
StringJoiner funcJoiner = new StringJoiner(", ");
|
||||||
|
for (FunctionNode fn : functions) {
|
||||||
|
funcJoiner.add(fn.getName());
|
||||||
|
}
|
||||||
|
return "Module(name=" + name
|
||||||
|
+ ", imports=[" + importJoiner.toString() + "]"
|
||||||
|
+ ", functions=[" + funcJoiner.toString() + "])";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/Node.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有 AST 节点的标记接口。
|
||||||
|
*/
|
||||||
|
public interface Node {}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/NumberLiteralNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数字字面量表达式节点。
|
||||||
|
*/
|
||||||
|
public class NumberLiteralNode implements ExpressionNode {
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
public NumberLiteralNode(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() { return value; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/ParameterNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 函数参数节点:包含参数名和类型。
|
||||||
|
*/
|
||||||
|
public class ParameterNode implements Node {
|
||||||
|
private final String name;
|
||||||
|
private final String type;
|
||||||
|
|
||||||
|
public ParameterNode(String name, String type) {
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回参数的字符串表示,例如 "num1:int",便于列表打印。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name + ":" + type;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/ReturnNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return 语句节点:可选返回表达式。
|
||||||
|
*/
|
||||||
|
public class ReturnNode implements StatementNode {
|
||||||
|
private final Optional<ExpressionNode> expression;
|
||||||
|
|
||||||
|
public ReturnNode(ExpressionNode expression) {
|
||||||
|
this.expression = Optional.ofNullable(expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<ExpressionNode> getExpression() { return expression; }
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/StatementNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标记所有语句节点。
|
||||||
|
*/
|
||||||
|
public interface StatementNode extends Node {}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/ast/StringLiteralNode.java
|
||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串字面量表达式节点,如 "hello"。
|
||||||
|
*/
|
||||||
|
public class StringLiteralNode implements ExpressionNode {
|
||||||
|
/** 不包含引号的字符串内容 */
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
public StringLiteralNode(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印时在两端加上引号,
|
||||||
|
* 例如 value = Result:,则 toString() 返回 "Result:"。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "\"" + value + "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/context/ParseException.java
|
||||||
|
package org.jcnc.snow.compiler.parser.context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析时抛出的异常,用于语法错误报告。
|
||||||
|
*/
|
||||||
|
public class ParseException extends RuntimeException {
|
||||||
|
public ParseException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/context/ParserContext.java
|
||||||
|
package org.jcnc.snow.compiler.parser.context;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parser 共享上下文,包含 TokenStream,可扩展错误收集、符号表等功能。
|
||||||
|
*/
|
||||||
|
public class ParserContext {
|
||||||
|
private final TokenStream tokens;
|
||||||
|
|
||||||
|
public ParserContext(List<Token> tokens) {
|
||||||
|
this.tokens = new TokenStream(tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenStream getTokens() {
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 封装 Token 列表并维护当前位置,提供 peek/next/match/expect 等操作,
|
||||||
|
* 现在新增 peek(offset) 以便在解析赋值时向前看一个 Token。
|
||||||
|
*/
|
||||||
|
public class TokenStream {
|
||||||
|
private final List<Token> tokens;
|
||||||
|
private int pos = 0;
|
||||||
|
|
||||||
|
public TokenStream(List<Token> tokens) {
|
||||||
|
this.tokens = tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 向前 offset 个位置的 Token,不消费 */
|
||||||
|
public Token peek(int offset) {
|
||||||
|
int idx = pos + offset;
|
||||||
|
if (idx >= tokens.size()) {
|
||||||
|
return Token.eof(tokens.size() + 1);
|
||||||
|
}
|
||||||
|
return tokens.get(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 默认 peek(0) */
|
||||||
|
public Token peek() {
|
||||||
|
return peek(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 消费并返回当前 Token */
|
||||||
|
public Token next() {
|
||||||
|
Token t = peek();
|
||||||
|
pos++;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 如果当前词素等于 lexeme,则消费并返回 true */
|
||||||
|
public boolean match(String lexeme) {
|
||||||
|
if (peek().getLexeme().equals(lexeme)) {
|
||||||
|
next();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 断言 lexeme,否则抛错 */
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 断言类型,否则抛错 */
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAtEnd() {
|
||||||
|
return peek().getType() == TokenType.EOF;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/expression/BinaryOperatorParselet.java
|
||||||
|
package org.jcnc.snow.compiler.parser.expression;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.BinaryExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析二元运算符表达式,支持左/右结合。
|
||||||
|
*/
|
||||||
|
public class BinaryOperatorParselet implements InfixParselet {
|
||||||
|
private final Precedence precedence;
|
||||||
|
private final boolean leftAssoc;
|
||||||
|
|
||||||
|
public BinaryOperatorParselet(Precedence precedence, boolean leftAssoc) {
|
||||||
|
this.precedence = precedence;
|
||||||
|
this.leftAssoc = leftAssoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Precedence getPrecedence() {
|
||||||
|
return precedence;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/expression/CallParselet.java
|
||||||
|
package org.jcnc.snow.compiler.parser.expression;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.CallExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析函数调用:left ( args… )
|
||||||
|
*/
|
||||||
|
public class CallParselet implements InfixParselet {
|
||||||
|
@Override
|
||||||
|
public ExpressionNode parse(ParserContext ctx, ExpressionNode left) {
|
||||||
|
// 已知 peek 是 "("
|
||||||
|
ctx.getTokens().next(); // consume "("
|
||||||
|
List<ExpressionNode> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Precedence getPrecedence() {
|
||||||
|
return Precedence.CALL;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/expression/ExpressionParser.java
|
||||||
|
package org.jcnc.snow.compiler.parser.expression;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表达式解析接口。
|
||||||
|
*/
|
||||||
|
public interface ExpressionParser {
|
||||||
|
ExpressionNode parse(ParserContext ctx);
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/expression/GroupingParselet.java
|
||||||
|
package org.jcnc.snow.compiler.parser.expression;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析括号表达式: ( expr )
|
||||||
|
*/
|
||||||
|
public class GroupingParselet implements PrefixParselet {
|
||||||
|
@Override
|
||||||
|
public ExpressionNode parse(ParserContext ctx, Token token) {
|
||||||
|
ExpressionNode expr = new PrattExpressionParser().parse(ctx);
|
||||||
|
ctx.getTokens().expect(")");
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/expression/IdentifierParselet.java
|
||||||
|
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.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析标识符。
|
||||||
|
*/
|
||||||
|
public class IdentifierParselet implements PrefixParselet {
|
||||||
|
@Override
|
||||||
|
public ExpressionNode parse(ParserContext ctx, Token token) {
|
||||||
|
return new IdentifierNode(token.getLexeme());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.expression;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 中缀解析器接口:处理二元运算、函数调用、成员访问等。
|
||||||
|
*/
|
||||||
|
public interface InfixParselet {
|
||||||
|
ExpressionNode parse(ParserContext ctx, ExpressionNode left);
|
||||||
|
Precedence getPrecedence();
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/expression/MemberParselet.java
|
||||||
|
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.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.TokenStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析成员访问表达式: left . IDENTIFIER
|
||||||
|
*/
|
||||||
|
public class MemberParselet implements InfixParselet {
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Precedence getPrecedence() {
|
||||||
|
return Precedence.CALL;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/expression/NumberLiteralParselet.java
|
||||||
|
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.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析数字字面量。
|
||||||
|
*/
|
||||||
|
public class NumberLiteralParselet implements PrefixParselet {
|
||||||
|
@Override
|
||||||
|
public ExpressionNode parse(ParserContext ctx, Token token) {
|
||||||
|
return new NumberLiteralNode(token.getLexeme());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
|
||||||
|
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.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pratt 算法实现的表达式解析器,支持算术、比较、调用、成员访问、字面量等。
|
||||||
|
*/
|
||||||
|
public class PrattExpressionParser implements ExpressionParser {
|
||||||
|
private static final Map<String, PrefixParselet> prefixes = new HashMap<>();
|
||||||
|
private static final Map<String, InfixParselet> infixes = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
// 注册前缀 parselet
|
||||||
|
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()); // 新增
|
||||||
|
|
||||||
|
// 注册中缀 parselet(略,保持原样)
|
||||||
|
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.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());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExpressionNode parse(ParserContext ctx) {
|
||||||
|
return parseExpression(ctx, Precedence.LOWEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionNode parseExpression(ParserContext ctx, Precedence prec) {
|
||||||
|
Token token = ctx.getTokens().next();
|
||||||
|
PrefixParselet prefix = prefixes.get(token.getType().name());
|
||||||
|
if (prefix == null) {
|
||||||
|
throw new IllegalStateException("No prefix for " + 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int nextPrecedence(ParserContext ctx) {
|
||||||
|
InfixParselet infix = infixes.get(ctx.getTokens().peek().getLexeme());
|
||||||
|
return infix != null ? infix.getPrecedence().ordinal() : -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.expression;// File: src/main/java/org/jcnc/snow/compiler/parser/expression/Precedence.java
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运算符优先级枚举,用于 Pratt 算法比较。
|
||||||
|
*/
|
||||||
|
public enum Precedence {
|
||||||
|
LOWEST, // 最低
|
||||||
|
SUM, // + -
|
||||||
|
PRODUCT, // * /
|
||||||
|
CALL // 函数调用、成员访问
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/expression/PrefixParselet.java
|
||||||
|
package org.jcnc.snow.compiler.parser.expression;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 前缀解析器接口:处理数字字面量、标识符、括号等前缀。
|
||||||
|
*/
|
||||||
|
public interface PrefixParselet {
|
||||||
|
ExpressionNode parse(ParserContext ctx, Token token);
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/expression/StringLiteralParselet.java
|
||||||
|
package org.jcnc.snow.compiler.parser.expression;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.StringLiteralNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析字符串字面量前缀,并去掉两端的引号。
|
||||||
|
*/
|
||||||
|
public class StringLiteralParselet implements PrefixParselet {
|
||||||
|
@Override
|
||||||
|
public ExpressionNode parse(ParserContext ctx, Token token) {
|
||||||
|
// token.getRaw() 包含原始 "\"Result:\""
|
||||||
|
String raw = token.getRaw();
|
||||||
|
// 去掉首尾引号
|
||||||
|
String content = raw.substring(1, raw.length() - 1);
|
||||||
|
return new StringLiteralNode(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/factory/StatementParserFactory.java
|
||||||
|
package org.jcnc.snow.compiler.parser.factory;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.statement.*;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语句解析器工厂:关键字 → 对应 StatementParser。
|
||||||
|
*/
|
||||||
|
public class StatementParserFactory {
|
||||||
|
private static final Map<String, StatementParser> 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StatementParser get(String keyword) {
|
||||||
|
return registry.getOrDefault(keyword, registry.get(""));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/factory/TopLevelParserFactory.java
|
||||||
|
package org.jcnc.snow.compiler.parser.factory;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.TopLevelParser;
|
||||||
|
import org.jcnc.snow.compiler.parser.module.ModuleParser;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 顶层解析器工厂:根据首个关键字(如 module)分发对应解析器。
|
||||||
|
*/
|
||||||
|
public class TopLevelParserFactory {
|
||||||
|
private static final Map<String, TopLevelParser> registry = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
registry.put("module", new ModuleParser());
|
||||||
|
// 如需支持 standalone import,可在此注册
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TopLevelParser get(String keyword) {
|
||||||
|
return registry.get(keyword);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,97 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/function/FunctionParser.java
|
||||||
|
package org.jcnc.snow.compiler.parser.function;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||||
|
import org.jcnc.snow.compiler.parser.TopLevelParser;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.TokenStream;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.*;
|
||||||
|
import org.jcnc.snow.compiler.parser.factory.StatementParserFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 function 块:
|
||||||
|
* function: NAME NEWLINE
|
||||||
|
* parameter: (declare name:type | name:type)*
|
||||||
|
* return_type: TYPE NEWLINE
|
||||||
|
* body: ... end body
|
||||||
|
* end function NEWLINE
|
||||||
|
*/
|
||||||
|
public class FunctionParser implements TopLevelParser {
|
||||||
|
@Override
|
||||||
|
public FunctionNode parse(ParserContext ctx) {
|
||||||
|
TokenStream ts = ctx.getTokens();
|
||||||
|
ts.expect("function");
|
||||||
|
ts.expect(":");
|
||||||
|
String name = ts.expectType(TokenType.IDENTIFIER).getLexeme();
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
|
||||||
|
// 参数列表
|
||||||
|
ts.expect("parameter");
|
||||||
|
ts.expect(":");
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
List<ParameterNode> params = new ArrayList<>();
|
||||||
|
while (true) {
|
||||||
|
if (ts.peek().getType() == TokenType.NEWLINE) {
|
||||||
|
ts.next();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ("return_type".equals(ts.peek().getLexeme())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ("declare".equals(ts.peek().getLexeme())) {
|
||||||
|
ts.next(); // consume 'declare'
|
||||||
|
}
|
||||||
|
String paramName = ts.expectType(TokenType.IDENTIFIER).getLexeme();
|
||||||
|
ts.expect(":");
|
||||||
|
String paramType = ts.expectType(TokenType.TYPE).getLexeme();
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
params.add(new ParameterNode(paramName, paramType));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回类型
|
||||||
|
ts.expect("return_type");
|
||||||
|
ts.expect(":");
|
||||||
|
String returnType = ts.expectType(TokenType.TYPE).getLexeme();
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
|
||||||
|
// 函数体解析
|
||||||
|
ts.expect("body");
|
||||||
|
ts.expect(":");
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
List<StatementNode> body = new ArrayList<>();
|
||||||
|
parseBody(ctx, body);
|
||||||
|
|
||||||
|
// end body
|
||||||
|
ts.expect("end");
|
||||||
|
ts.expect("body");
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
|
||||||
|
// end function
|
||||||
|
ts.expect("end");
|
||||||
|
ts.expect("function");
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
|
||||||
|
return new FunctionNode(name, params, returnType, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 辅助方法:根据 ParserContext 解析函数体中所有语句,直到遇到 end
|
||||||
|
*/
|
||||||
|
private void parseBody(ParserContext ctx, List<StatementNode> body) {
|
||||||
|
TokenStream ts = ctx.getTokens();
|
||||||
|
while (true) {
|
||||||
|
if (ts.peek().getType() == TokenType.NEWLINE) {
|
||||||
|
ts.next();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ("end".equals(ts.peek().getLexeme())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
String key = ts.peek().getLexeme();
|
||||||
|
body.add(StatementParserFactory.get(key).parse(ctx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/module/ImportParser.java
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 import 语句:import: MOD1, MOD2, ... NEWLINE
|
||||||
|
* 可以在一行声明多个模块,用逗号分隔。
|
||||||
|
*/
|
||||||
|
public class ImportParser {
|
||||||
|
/**
|
||||||
|
* 读取一行 import 声明,返回多个 ImportNode
|
||||||
|
*/
|
||||||
|
public List<ImportNode> parse(ParserContext ctx) {
|
||||||
|
ctx.getTokens().expect("import");
|
||||||
|
ctx.getTokens().expect(":");
|
||||||
|
List<ImportNode> imports = new ArrayList<>();
|
||||||
|
// 读取第一个模块名
|
||||||
|
String name = ctx.getTokens()
|
||||||
|
.expectType(TokenType.IDENTIFIER)
|
||||||
|
.getLexeme();
|
||||||
|
imports.add(new ImportNode(name));
|
||||||
|
// 继续读取逗号分隔的模块名
|
||||||
|
while (ctx.getTokens().match(",")) {
|
||||||
|
String mod = ctx.getTokens()
|
||||||
|
.expectType(TokenType.IDENTIFIER)
|
||||||
|
.getLexeme();
|
||||||
|
imports.add(new ImportNode(mod));
|
||||||
|
}
|
||||||
|
// 消费行尾
|
||||||
|
ctx.getTokens().expectType(TokenType.NEWLINE);
|
||||||
|
return imports;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java
|
||||||
|
package org.jcnc.snow.compiler.parser.module;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||||
|
import org.jcnc.snow.compiler.parser.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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 module 块:
|
||||||
|
* module: NAME NEWLINE
|
||||||
|
* (import… | function… )*
|
||||||
|
* end module NEWLINE
|
||||||
|
*/
|
||||||
|
public class ModuleParser implements TopLevelParser {
|
||||||
|
@Override
|
||||||
|
public ModuleNode parse(ParserContext ctx) {
|
||||||
|
TokenStream ts = ctx.getTokens();
|
||||||
|
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<>();
|
||||||
|
ImportParser importParser = new ImportParser();
|
||||||
|
FunctionParser funcParser = new FunctionParser();
|
||||||
|
|
||||||
|
// 解析导入和函数,支持空行
|
||||||
|
while (true) {
|
||||||
|
// 跳过空行
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ts.expect("end");
|
||||||
|
ts.expect("module");
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
return new ModuleNode(name, imports, functions);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java
|
||||||
|
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.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 declare 语句: declare name:type (= expr)? NEWLINE
|
||||||
|
*/
|
||||||
|
public class DeclarationStatementParser implements StatementParser {
|
||||||
|
@Override
|
||||||
|
public DeclarationNode parse(ParserContext ctx) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
|
||||||
|
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.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.ExpressionStatementNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将任意表达式或赋值当作语句:
|
||||||
|
* identifier = expr NEWLINE
|
||||||
|
* expr NEWLINE
|
||||||
|
*/
|
||||||
|
public class ExpressionStatementParser implements StatementParser {
|
||||||
|
@Override
|
||||||
|
public StatementNode parse(ParserContext ctx) {
|
||||||
|
TokenStream ts = ctx.getTokens();
|
||||||
|
|
||||||
|
// 先检测赋值语句:IDENTIFIER '=' ...
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则当作普通表达式语句
|
||||||
|
ExpressionNode expr = new PrattExpressionParser().parse(ctx);
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
return new ExpressionStatementNode(expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/statement/IfStatementParser.java
|
||||||
|
package org.jcnc.snow.compiler.parser.statement;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.IfNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 if 语句: if expr then NEWLINE stmts end if NEWLINE
|
||||||
|
*/
|
||||||
|
public class IfStatementParser implements StatementParser {
|
||||||
|
@Override
|
||||||
|
public IfNode parse(ParserContext ctx) {
|
||||||
|
ctx.getTokens().expect("if");
|
||||||
|
var cond = new PrattExpressionParser().parse(ctx);
|
||||||
|
ctx.getTokens().expect("then");
|
||||||
|
ctx.getTokens().expectType(TokenType.NEWLINE);
|
||||||
|
|
||||||
|
List<StatementNode> thenBranch = new ArrayList<>();
|
||||||
|
while (!ctx.getTokens().peek().getLexeme().equals("end")) {
|
||||||
|
thenBranch.add(
|
||||||
|
StatementParserFactory.get(ctx.getTokens().peek().getLexeme()).parse(ctx)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.getTokens().expect("end");
|
||||||
|
ctx.getTokens().expect("if");
|
||||||
|
ctx.getTokens().expectType(TokenType.NEWLINE);
|
||||||
|
|
||||||
|
return new IfNode(cond, thenBranch);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,100 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/statement/LoopStatementParser.java
|
||||||
|
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.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.LoopNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.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 java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 loop 语句:
|
||||||
|
* loop: NEWLINE
|
||||||
|
* initializer: … NEWLINE
|
||||||
|
* condition: … NEWLINE
|
||||||
|
* update: … NEWLINE
|
||||||
|
* body: … NEWLINE
|
||||||
|
* end body NEWLINE
|
||||||
|
* end loop NEWLINE
|
||||||
|
*/
|
||||||
|
public class LoopStatementParser implements StatementParser {
|
||||||
|
@Override
|
||||||
|
public LoopNode parse(ParserContext ctx) {
|
||||||
|
TokenStream ts = ctx.getTokens();
|
||||||
|
|
||||||
|
// loop:
|
||||||
|
ts.expect("loop");
|
||||||
|
ts.expect(":");
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
skipNewlines(ts);
|
||||||
|
|
||||||
|
// initializer:
|
||||||
|
ts.expect("initializer");
|
||||||
|
ts.expect(":");
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
skipNewlines(ts);
|
||||||
|
StatementNode initializer =
|
||||||
|
StatementParserFactory.get(ts.peek().getLexeme()).parse(ctx);
|
||||||
|
skipNewlines(ts);
|
||||||
|
|
||||||
|
// condition:
|
||||||
|
ts.expect("condition");
|
||||||
|
ts.expect(":");
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
skipNewlines(ts);
|
||||||
|
ExpressionNode condition =
|
||||||
|
new PrattExpressionParser().parse(ctx);
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
skipNewlines(ts);
|
||||||
|
|
||||||
|
// update (专用赋值解析):
|
||||||
|
ts.expect("update");
|
||||||
|
ts.expect(":");
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
skipNewlines(ts);
|
||||||
|
String varName = ts.expectType(TokenType.IDENTIFIER).getLexeme();
|
||||||
|
ts.expect("=");
|
||||||
|
ExpressionNode updateExpr = new PrattExpressionParser().parse(ctx);
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
AssignmentNode update = new AssignmentNode(varName, updateExpr);
|
||||||
|
skipNewlines(ts);
|
||||||
|
|
||||||
|
// body:
|
||||||
|
ts.expect("body");
|
||||||
|
ts.expect(":");
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
skipNewlines(ts);
|
||||||
|
List<StatementNode> body = new ArrayList<>();
|
||||||
|
while (!"end".equals(ts.peek().getLexeme())) {
|
||||||
|
body.add(StatementParserFactory.get(ts.peek().getLexeme()).parse(ctx));
|
||||||
|
skipNewlines(ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// end body
|
||||||
|
ts.expect("end");
|
||||||
|
ts.expect("body");
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
skipNewlines(ts);
|
||||||
|
|
||||||
|
// end loop
|
||||||
|
ts.expect("end");
|
||||||
|
ts.expect("loop");
|
||||||
|
ts.expectType(TokenType.NEWLINE);
|
||||||
|
|
||||||
|
return new LoopNode(initializer, condition, update, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 连续跳过所有空行(NEWLINE) */
|
||||||
|
private void skipNewlines(TokenStream ts) {
|
||||||
|
while (ts.peek().getType() == TokenType.NEWLINE) {
|
||||||
|
ts.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/statement/ReturnStatementParser.java
|
||||||
|
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.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 return 语句: return expr? NEWLINE
|
||||||
|
*/
|
||||||
|
public class ReturnStatementParser implements StatementParser {
|
||||||
|
@Override
|
||||||
|
public ReturnNode parse(ParserContext ctx) {
|
||||||
|
ctx.getTokens().expect("return");
|
||||||
|
ExpressionNode expr = null;
|
||||||
|
// 如果下一个不是换行,就解析表达式
|
||||||
|
if (ctx.getTokens().peek().getType() != TokenType.NEWLINE) {
|
||||||
|
expr = new PrattExpressionParser().parse(ctx);
|
||||||
|
}
|
||||||
|
// 用 expectType 来消费真正的 NEWLINE token
|
||||||
|
ctx.getTokens().expectType(TokenType.NEWLINE);
|
||||||
|
return new ReturnNode(expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
// File: src/main/java/org/jcnc/snow/compiler/parser/statement/StatementParser.java
|
||||||
|
package org.jcnc.snow.compiler.parser.statement;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.StatementNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语句解析器接口。
|
||||||
|
*/
|
||||||
|
public interface StatementParser {
|
||||||
|
StatementNode parse(ParserContext ctx);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user