This commit is contained in:
Luke 2025-04-22 17:32:01 +08:00
parent ab7604f9d5
commit 1703e34818
16 changed files with 526 additions and 10 deletions

View File

@ -1,25 +1,34 @@
// 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.utils.TokenPrinter; import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.parser.ast.ASTModule;
import org.jcnc.snow.compiler.parser.ASTPrinter;
import org.jcnc.snow.compiler.parser.ParserEngine;
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" 文件 // 1. 文件
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); // 2. 词法分析
// 打印所有 Token LexerEngine lexer = new LexerEngine(source);
TokenPrinter.printTokens(lexerEngine.getAllTokens()); List<Token> tokens = lexer.getAllTokens();
}
// 3. 语法分析
ParserEngine parser = new ParserEngine(tokens);
ASTModule moduleAst = parser.parseModule();
// 4. 使用 AST
System.out.println("解析成功,模块名:" + moduleAst.getName());
ASTPrinter.print(moduleAst);
} }
}

View File

@ -0,0 +1,8 @@
package org.jcnc.snow.compiler.parser;
/**
* 抽象的表达式节点基类所有具体表达式都应继承此类
*/
public abstract class ASTExpression {
}

View File

@ -0,0 +1,82 @@
package org.jcnc.snow.compiler.parser;
import org.jcnc.snow.compiler.parser.ast.*;
// ----------------- AST 打印工具 -----------------
public class ASTPrinter {
private static final String INDENT = " ";
public static void print(ASTModule module) {
System.out.println("Module: " + module.getName());
for (ASTFunction fn : module.getFunctions()) {
printFunction(fn, 1);
}
}
private static void printFunction(ASTFunction fn, int level) {
String indent = INDENT.repeat(level);
System.out.println(indent + "Function: " + fn.getName() + " returns " + fn.getReturnType());
if (!fn.getParameters().isEmpty()) {
System.out.println(indent + " Parameters:");
for (ASTParameter p : fn.getParameters()) {
System.out.println(indent + " - " + p.getName() + ": " + p.getType());
}
}
System.out.println(indent + " Body:");
for (ASTStatement stmt : fn.getBody()) {
printStatement(stmt, level + 2);
}
}
private static void printStatement(ASTStatement stmt, int level) {
String indent = INDENT.repeat(level);
if (stmt instanceof ASTReturn) {
System.out.print(indent + "Return: ");
printExpression(((ASTReturn) stmt).getExpression());
System.out.println();
} else if (stmt instanceof ASTDeclare) {
ASTDeclare d = (ASTDeclare) stmt;
System.out.print(indent + "Declare " + d.getName() + ": " + d.getType());
if (d.getInitExpression() != null) {
System.out.print(" = "); printExpression(d.getInitExpression());
}
System.out.println();
} else if (stmt instanceof ASTLoop) {
ASTLoop loop = (ASTLoop) stmt;
System.out.println(indent + "Loop:");
System.out.print(indent + " Init: "); printStatement(loop.getInit(), 0);
System.out.print(indent + " Condition: "); printExpression(loop.getCondition()); System.out.println();
System.out.print(indent + " Update: "); printStatement(loop.getUpdate(), 0);
System.out.println(indent + " Body:");
for (ASTStatement s : loop.getBody()) {
printStatement(s, level + 2);
}
} else if (stmt instanceof ASTIf) {
ASTIf ifs = (ASTIf) stmt;
System.out.print(indent + "If condition: "); printExpression(ifs.getCondition()); System.out.println();
System.out.println(indent + " Then:");
for (ASTStatement s : ifs.getThenStatements()) {
printStatement(s, level + 2);
}
} else {
System.out.println(indent + stmt.getClass().getSimpleName());
}
}
private static void printExpression(ASTExpression expr) {
if (expr instanceof ASTLiteral) {
System.out.print(((ASTLiteral) expr).getValue());
} else if (expr instanceof ASTIdentifier) {
System.out.print(((ASTIdentifier) expr).getName());
} else if (expr instanceof ASTBinaryOp) {
ASTBinaryOp bin = (ASTBinaryOp) expr;
System.out.print("(");
printExpression(bin.getLeft());
System.out.print(" " + bin.getOperator() + " ");
printExpression(bin.getRight());
System.out.print(")");
} else {
System.out.print(expr.getClass().getSimpleName());
}
}
}

View File

@ -0,0 +1,201 @@
package org.jcnc.snow.compiler.parser;
import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.*;
import java.util.ArrayList;
import java.util.List;
/**
* 递归下降解析器实现对模块函数语句和表达式的完整解析
*/
public class ParserEngine {
private final List<Token> tokens;
private int pos = 0;
public ParserEngine(List<Token> tokens) {
this.tokens = tokens;
}
public ASTModule parseModule() {
match(TokenType.KEYWORD, "module");
match(TokenType.COLON);
String moduleName = current().getLexeme();
match(TokenType.IDENTIFIER);
match(TokenType.NEWLINE);
ASTModule module = new ASTModule(moduleName);
while (!peek(TokenType.KEYWORD, "end")) {
module.addFunction(parseFunction());
}
match(TokenType.KEYWORD, "end");
match(TokenType.KEYWORD, "module");
match(TokenType.NEWLINE);
return module;
}
private ASTFunction parseFunction() {
match(TokenType.KEYWORD, "function");
match(TokenType.COLON);
String funcName = current().getLexeme();
match(TokenType.IDENTIFIER);
match(TokenType.NEWLINE);
ASTFunction fn = new ASTFunction(funcName);
if (peek(TokenType.KEYWORD, "parameter")) {
match(TokenType.KEYWORD, "parameter");
match(TokenType.COLON);
match(TokenType.NEWLINE);
while (peek(TokenType.KEYWORD, "declare")) {
fn.addParameter(parseParameter());
}
}
match(TokenType.KEYWORD, "return_type");
match(TokenType.COLON);
String returnType = current().getLexeme();
match(TokenType.TYPE);
match(TokenType.NEWLINE);
fn.setReturnType(returnType);
match(TokenType.KEYWORD, "body");
match(TokenType.COLON);
match(TokenType.NEWLINE);
while (!peek(TokenType.KEYWORD, "end")) {
fn.addStatement(parseStatement());
}
match(TokenType.KEYWORD, "end");
match(TokenType.KEYWORD, "body");
match(TokenType.NEWLINE);
match(TokenType.KEYWORD, "end");
match(TokenType.KEYWORD, "function");
match(TokenType.NEWLINE);
return fn;
}
private ASTParameter parseParameter() {
match(TokenType.KEYWORD, "declare");
String name = current().getLexeme();
match(TokenType.IDENTIFIER);
match(TokenType.COLON);
String type = current().getLexeme();
match(TokenType.TYPE);
match(TokenType.NEWLINE);
return new ASTParameter(name, type);
}
private ASTStatement parseStatement() {
if (peek(TokenType.KEYWORD, "return")) {
return parseReturn();
} else if (peek(TokenType.KEYWORD, "declare")) {
return parseDeclare();
} else if (peek(TokenType.KEYWORD, "loop")) {
return parseLoop();
} else if (peek(TokenType.KEYWORD, "if")) {
return parseIf();
}
throw new ASTBinaryOp.ParseException("无法识别的语句:" + current());
}
private ASTReturn parseReturn() {
match(TokenType.KEYWORD, "return");
ASTExpression expr = parseExpression();
match(TokenType.NEWLINE);
return new ASTReturn(expr);
}
private ASTDeclare parseDeclare() {
match(TokenType.KEYWORD, "declare");
String name = current().getLexeme(); match(TokenType.IDENTIFIER);
match(TokenType.COLON);
String type = current().getLexeme(); match(TokenType.TYPE);
if (peek(TokenType.EQUALS)) {
match(TokenType.EQUALS);
ASTExpression init = parseExpression();
match(TokenType.NEWLINE);
return new ASTDeclare(name, type, init);
} else {
match(TokenType.NEWLINE);
return new ASTDeclare(name, type, null);
}
}
private ASTLoop parseLoop() {
match(TokenType.KEYWORD, "loop"); match(TokenType.COLON);
match(TokenType.NEWLINE);
match(TokenType.KEYWORD, "initializer"); match(TokenType.COLON); match(TokenType.NEWLINE);
ASTDeclare init = parseDeclare();
match(TokenType.KEYWORD, "condition"); match(TokenType.COLON); match(TokenType.NEWLINE);
ASTExpression cond = parseExpression(); match(TokenType.NEWLINE);
match(TokenType.KEYWORD, "update"); match(TokenType.COLON); match(TokenType.NEWLINE);
ASTDeclare update = parseDeclare();
match(TokenType.KEYWORD, "body"); match(TokenType.COLON); match(TokenType.NEWLINE);
List<ASTStatement> stmts = new ArrayList<>();
while (!peek(TokenType.KEYWORD, "end")) {
stmts.add(parseStatement());
}
match(TokenType.KEYWORD, "end"); match(TokenType.KEYWORD, "body"); match(TokenType.NEWLINE);
match(TokenType.KEYWORD, "end"); match(TokenType.KEYWORD, "loop"); match(TokenType.NEWLINE);
return new ASTLoop(init, cond, update, stmts);
}
private ASTIf parseIf() {
match(TokenType.KEYWORD, "if");
ASTExpression cond = parseExpression();
match(TokenType.KEYWORD, "then"); match(TokenType.NEWLINE);
List<ASTStatement> thenStmts = new ArrayList<>();
while (!peek(TokenType.KEYWORD, "end")) {
thenStmts.add(parseStatement());
}
match(TokenType.KEYWORD, "end"); match(TokenType.KEYWORD, "if"); match(TokenType.NEWLINE);
return new ASTIf(cond, thenStmts);
}
private ASTExpression parseExpression() {
ASTExpression left;
Token t = current();
if (t.getType() == TokenType.IDENTIFIER) {
left = new ASTIdentifier(t.getLexeme()); pos++;
} else if (t.getType() == TokenType.NUMBER_LITERAL) {
left = new ASTLiteral(Integer.parseInt(t.getLexeme())); pos++;
} else {
throw new ASTBinaryOp.ParseException("表达式解析错误,意外的令牌:" + t);
}
// 处理二元运算
Token opTok = current();
String lex = opTok.getLexeme();
if (opTok.getType() == TokenType.PLUS
|| opTok.getType() == TokenType.MINUS
|| opTok.getType() == TokenType.MULTIPLY
|| lex.equals("/")) {
String op = lex;
pos++;
ASTExpression right = parseExpression();
return new ASTBinaryOp(left, op, right);
}
return left;
}
// 工具方法
private Token current() {
if (pos >= tokens.size()) throw new ASTBinaryOp.ParseException("超出令牌范围");
return tokens.get(pos);
}
private boolean peek(TokenType type) {
return current().getType() == type;
}
private boolean peek(TokenType type, String lexeme) {
return peek(type) && current().getLexeme().equals(lexeme);
}
private void match(TokenType type) {
if (peek(type)) pos++; else throw new ASTBinaryOp.ParseException("期望 " + type + " 但找到 " + current());
}
private void match(TokenType type, String lexeme) {
if (peek(type, lexeme)) pos++; else throw new ASTBinaryOp.ParseException("期望 '" + lexeme + "' 但找到 " + current());
}
}

View File

@ -0,0 +1,82 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ASTExpression;
/**
* 表示一个二元运算表达式节点例如加法减法乘法除法
* AST 该节点有一个左子表达式一个运算符和一个右子表达式
*/
public class ASTBinaryOp extends ASTExpression {
/**
* 左侧操作数表达式
*/
private final ASTExpression left;
/**
* 运算符字符串 "+", "-", "*", "/"
*/
private final String op;
/**
* 右侧操作数表达式
*/
private final ASTExpression right;
/**
* 构造一个二元运算节点
*
* @param left 左侧子表达式不得为 {@code null}
* @param op 运算符文本例如 "+""-""*""/"
* @param right 右侧子表达式不得为 {@code null}
* @throws IllegalArgumentException 如果 {@code left} {@code right} {@code null}
*/
public ASTBinaryOp(ASTExpression left, String op, ASTExpression right) {
if (left == null || right == null) {
throw new IllegalArgumentException("左右表达式都必须非空");
}
this.left = left;
this.op = op;
this.right = right;
}
/**
* 获取左侧子表达式
*
* @return 左侧的 {@link ASTExpression} 节点
*/
public ASTExpression getLeft() {
return left;
}
/**
* 获取二元运算符
*
* @return 运算符字符串 "+""-"
*/
public String getOperator() {
return op;
}
/**
* 获取右侧子表达式
*
* @return 右侧的 {@link ASTExpression} 节点
*/
public ASTExpression getRight() {
return right;
}
/**
* 在解析过程中遇到语法错误时抛出此异常
*/
public static class ParseException extends RuntimeException {
/**
* 使用指定的错误消息构造一个解析异常
*
* @param msg 错误描述
*/
public ParseException(String msg) {
super(msg);
}
}
}

View File

@ -0,0 +1,14 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ASTExpression;
public class ASTDeclare extends ASTStatement {
final String name, type;
final ASTExpression init;
public ASTDeclare(String name, String type, ASTExpression init) {
this.name = name; this.type = type; this.init = init;
}
public String getName() { return name; }
public String getType() { return type; }
public ASTExpression getInitExpression() { return init; }
}

View File

@ -0,0 +1,19 @@
package org.jcnc.snow.compiler.parser.ast;
import java.util.ArrayList;
import java.util.List;
public class ASTFunction extends ASTNode {
private final String name;
private String returnType;
private final List<ASTParameter> params = new ArrayList<>();
private final List<ASTStatement> body = new ArrayList<>();
public ASTFunction(String name) { this.name = name; }
public void addParameter(ASTParameter p) { params.add(p); }
public void setReturnType(String t) { this.returnType = t; }
public void addStatement(ASTStatement stmt) { body.add(stmt); }
public String getName() { return name; }
public String getReturnType() { return returnType; }
public List<ASTParameter> getParameters() { return params; }
public List<ASTStatement> getBody() { return body; }
}

View File

@ -0,0 +1,9 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ASTExpression;
public class ASTIdentifier extends ASTExpression {
final String name;
public ASTIdentifier(String name) { this.name = name; }
public String getName() { return name; }
}

View File

@ -0,0 +1,15 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ASTExpression;
import java.util.List;
public class ASTIf extends ASTStatement {
final ASTExpression condition;
final List<ASTStatement> thenStmts;
public ASTIf(ASTExpression condition, List<ASTStatement> thenStmts) {
this.condition = condition; this.thenStmts = thenStmts;
}
public ASTExpression getCondition() { return condition; }
public List<ASTStatement> getThenStatements() { return thenStmts; }
}

View File

@ -0,0 +1,9 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ASTExpression;
public class ASTLiteral extends ASTExpression {
final int value;
public ASTLiteral(int value) { this.value = value; }
public int getValue() { return value; }
}

View File

@ -0,0 +1,19 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ASTExpression;
import java.util.List;
public class ASTLoop extends ASTStatement {
final ASTDeclare init;
final ASTExpression condition;
final ASTDeclare update;
final List<ASTStatement> body;
public ASTLoop(ASTDeclare init, ASTExpression condition, ASTDeclare update, List<ASTStatement> body) {
this.init = init; this.condition = condition; this.update = update; this.body = body;
}
public ASTDeclare getInit() { return init; }
public ASTExpression getCondition() { return condition; }
public ASTDeclare getUpdate() { return update; }
public List<ASTStatement> getBody() { return body; }
}

View File

@ -0,0 +1,25 @@
package org.jcnc.snow.compiler.parser.ast;
import java.util.ArrayList;
import java.util.List;
public class ASTModule extends ASTNode {
private final String name;
private final List<ASTFunction> functions = new ArrayList<>();
public ASTModule(String name) {
this.name = name;
}
public void addFunction(ASTFunction fn) {
functions.add(fn);
}
public String getName() {
return name;
}
public List<ASTFunction> getFunctions() {
return functions;
}
}

View File

@ -0,0 +1,4 @@
package org.jcnc.snow.compiler.parser.ast;
// ----------------- AST 节点定义 -----------------
public abstract class ASTNode {}

View File

@ -0,0 +1,8 @@
package org.jcnc.snow.compiler.parser.ast;
public class ASTParameter extends ASTNode {
final String name, type;
public ASTParameter(String name, String type) { this.name = name; this.type = type; }
public String getName() { return name; }
public String getType() { return type; }
}

View File

@ -0,0 +1,9 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ASTExpression;
class ASTReturn extends ASTStatement {
final ASTExpression expr;
public ASTReturn(ASTExpression expr) { this.expr = expr; }
public ASTExpression getExpression() { return expr; }
}

View File

@ -0,0 +1,3 @@
package org.jcnc.snow.compiler.parser.ast;
abstract class ASTStatement extends ASTNode {}