优化打印逻辑
This commit is contained in:
parent
4df44686e7
commit
210a6942f4
@ -34,14 +34,9 @@ public class Main {
|
|||||||
// 3. 可读地打印 AST
|
// 3. 可读地打印 AST
|
||||||
ASTPrinter.print(ast);
|
ASTPrinter.print(ast);
|
||||||
|
|
||||||
// 1) 输出紧凑 JSON
|
// 4. AST 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("=== Pretty JSON ===");
|
||||||
System.out.println(JsonFormatter.prettyPrint(compact));
|
System.out.println(JsonFormatter.prettyPrint(ASTJsonSerializer.toJsonString(ast)));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4,183 +4,170 @@ import org.jcnc.snow.compiler.parser.ast.*;
|
|||||||
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ASTJsonSerializer 工具类
|
||||||
|
* <p>
|
||||||
|
* 将编译器生成的 AST(抽象语法树)节点列表转换为通用的 Map/List 结构
|
||||||
|
* 并借助 JSONParser.toJson(Object) 方法序列化为 JSON 字符串。
|
||||||
|
* <p>
|
||||||
|
* 支持的节点类型包括:ModuleNode、FunctionNode、DeclarationNode、
|
||||||
|
* AssignmentNode、IfNode、LoopNode、ReturnNode、ExpressionStatementNode
|
||||||
|
* 以及各种 ExpressionNode(如 BinaryExpressionNode、IdentifierNode 等)。
|
||||||
|
*/
|
||||||
public class ASTJsonSerializer {
|
public class ASTJsonSerializer {
|
||||||
|
|
||||||
public static String toJsonString(List<Node> ast) {
|
/**
|
||||||
return ast.stream()
|
* 快速创建一个 LinkedHashMap,并写入 type 字段
|
||||||
.map(ASTJsonSerializer::nodeToJson)
|
*/
|
||||||
.collect(Collectors.joining(",", "[", "]"));
|
private static Map<String, Object> newNodeMap(String type) {
|
||||||
|
Map<String, Object> m = new LinkedHashMap<>();
|
||||||
|
m.put("type", type);
|
||||||
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String nodeToJson(Node n) {
|
/**
|
||||||
return switch (n) {
|
* 用于构建表达式节点的 Map
|
||||||
case ModuleNode m -> moduleToJson(m);
|
*/
|
||||||
case FunctionNode f -> functionToJson(f);
|
private static Map<String, Object> exprMap(String type, Object... kv) {
|
||||||
case DeclarationNode d -> declarationToJson(d);
|
Map<String, Object> m = new LinkedHashMap<>();
|
||||||
case AssignmentNode a -> assignmentToJson(a);
|
m.put("type", type);
|
||||||
case IfNode i -> ifToJson(i);
|
for (int i = 0; i < kv.length; i += 2) {
|
||||||
case LoopNode l -> loopToJson(l);
|
m.put((String) kv[i], kv[i + 1]);
|
||||||
case ReturnNode r -> returnToJson(r);
|
|
||||||
case ExpressionStatementNode e -> exprStmtToJson(e);
|
|
||||||
default -> simpleTypeJson(n.getClass().getSimpleName());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String moduleToJson(ModuleNode m) {
|
|
||||||
return """
|
|
||||||
{"type":"Module","name":%1$s,"imports":%2$s,"functions":%3$s}
|
|
||||||
""".formatted(
|
|
||||||
quote(m.name()),
|
|
||||||
listToJsonArray(m.imports(),
|
|
||||||
imp -> String.format("{\"type\":\"Import\",\"module\":%s}", quote(imp.moduleName()))
|
|
||||||
),
|
|
||||||
listToJsonArray(m.functions(), ASTJsonSerializer::functionToJson)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String functionToJson(FunctionNode f) {
|
|
||||||
return """
|
|
||||||
{"type":"Function","name":%1$s,"parameters":%2$s,"returnType":%3$s,"body":%4$s}
|
|
||||||
""".formatted(
|
|
||||||
quote(f.name()),
|
|
||||||
listToJsonArray(f.parameters(),
|
|
||||||
p -> String.format("{\"name\":%s,\"type\":%s}", quote(p.name()), quote(p.type()))
|
|
||||||
),
|
|
||||||
quote(f.returnType()),
|
|
||||||
listToJsonArray(f.body(), ASTJsonSerializer::nodeToJson)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String declarationToJson(DeclarationNode d) {
|
|
||||||
var base = new StringBuilder();
|
|
||||||
base.append("""
|
|
||||||
{"type":"Declaration","name":%1$s,"varType":%2$s
|
|
||||||
""".formatted(quote(d.getName()), quote(d.getType())));
|
|
||||||
d.getInitializer().ifPresent(init ->
|
|
||||||
base.append(",\"initializer\":").append(exprToJson(init))
|
|
||||||
);
|
|
||||||
base.append("}");
|
|
||||||
return base.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String assignmentToJson(AssignmentNode a) {
|
|
||||||
return """
|
|
||||||
{"type":"Assignment","variable":%1$s,"value":%2$s}
|
|
||||||
""".formatted(quote(a.variable()), exprToJson(a.value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String ifToJson(IfNode i) {
|
|
||||||
var thenJson = listToJsonArray(i.thenBranch(), ASTJsonSerializer::nodeToJson);
|
|
||||||
var elseJson = i.elseBranch().isEmpty() ? ""
|
|
||||||
: ",\"else\":" + listToJsonArray(i.elseBranch(), ASTJsonSerializer::nodeToJson);
|
|
||||||
return """
|
|
||||||
{"type":"If","condition":%1$s,"then":%2$s%3$s}
|
|
||||||
""".formatted(exprToJson(i.condition()), thenJson, elseJson);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String loopToJson(LoopNode l) {
|
|
||||||
return """
|
|
||||||
{"type":"Loop","initializer":%1$s,"condition":%2$s,"update":%3$s,"body":%4$s}
|
|
||||||
""".formatted(
|
|
||||||
l.initializer() != null ? nodeToJson(l.initializer()) : "null",
|
|
||||||
l.condition() != null ? exprToJson(l.condition()) : "null",
|
|
||||||
l.update() != null ? nodeToJson(l.update()) : "null",
|
|
||||||
listToJsonArray(l.body(), ASTJsonSerializer::nodeToJson)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String returnToJson(ReturnNode r) {
|
|
||||||
var sb = new StringBuilder("{\"type\":\"Return\"");
|
|
||||||
r.getExpression().ifPresent(expr ->
|
|
||||||
sb.append(",\"value\":").append(exprToJson(expr))
|
|
||||||
);
|
|
||||||
sb.append("}");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String exprStmtToJson(ExpressionStatementNode e) {
|
|
||||||
return """
|
|
||||||
{"type":"ExpressionStatement","expression":%s}
|
|
||||||
""".formatted(exprToJson(e.expression()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String exprToJson(ExpressionNode expr) {
|
|
||||||
return switch (expr) {
|
|
||||||
case BinaryExpressionNode b -> binaryToJson(b);
|
|
||||||
case IdentifierNode id -> idToJson(id);
|
|
||||||
case NumberLiteralNode n -> numToJson(n);
|
|
||||||
case StringLiteralNode s -> strToJson(s);
|
|
||||||
case CallExpressionNode c -> callToJson(c);
|
|
||||||
case MemberExpressionNode m -> memberToJson(m);
|
|
||||||
default -> simpleTypeJson(expr.getClass().getSimpleName());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String binaryToJson(BinaryExpressionNode b) {
|
|
||||||
return """
|
|
||||||
{"type":"BinaryExpression","left":%1$s,"operator":%2$s,"right":%3$s}
|
|
||||||
""".formatted(exprToJson(b.left()), quote(b.operator()), exprToJson(b.right()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String idToJson(IdentifierNode id) {
|
|
||||||
return """
|
|
||||||
{"type":"Identifier","name":%s}
|
|
||||||
""".formatted(quote(id.name()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String numToJson(NumberLiteralNode n) {
|
|
||||||
return """
|
|
||||||
{"type":"NumberLiteral","value":%s}
|
|
||||||
""".formatted(quote(n.value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String strToJson(StringLiteralNode s) {
|
|
||||||
return """
|
|
||||||
{"type":"StringLiteral","value":%s}
|
|
||||||
""".formatted(quote(s.value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String callToJson(CallExpressionNode c) {
|
|
||||||
return """
|
|
||||||
{"type":"CallExpression","callee":%1$s,"arguments":%2$s}
|
|
||||||
""".formatted(exprToJson(c.callee()), listToJsonArray(c.arguments(), ASTJsonSerializer::exprToJson));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String memberToJson(MemberExpressionNode m) {
|
|
||||||
return """
|
|
||||||
{"type":"MemberExpression","object":%1$s,"member":%2$s}
|
|
||||||
""".formatted(exprToJson(m.object()), quote(m.member()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ======== 通用辅助 ========
|
|
||||||
|
|
||||||
private static String simpleTypeJson(String typeName) {
|
|
||||||
return "{\"type\":" + quote(typeName) + "}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <T> String listToJsonArray(List<T> list, Function<T, String> mapper) {
|
|
||||||
return list.stream()
|
|
||||||
.map(mapper)
|
|
||||||
.collect(Collectors.joining(",", "[", "]"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String quote(String s) {
|
|
||||||
var sb = new StringBuilder("\"");
|
|
||||||
for (char c : s.toCharArray()) {
|
|
||||||
switch (c) {
|
|
||||||
case '\\' -> sb.append("\\\\");
|
|
||||||
case '\"' -> sb.append("\\\"");
|
|
||||||
case '\n' -> sb.append("\\n");
|
|
||||||
case '\r' -> sb.append("\\r");
|
|
||||||
case '\t' -> sb.append("\\t");
|
|
||||||
default -> sb.append(c);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sb.append('"');
|
return m;
|
||||||
return sb.toString();
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 AST 根节点列表序列化为 JSON 字符串。
|
||||||
|
*
|
||||||
|
* @param ast 表示抽象语法树根节点的 List<Node>
|
||||||
|
* @return 对应的 JSON 格式字符串
|
||||||
|
*/
|
||||||
|
public static String toJsonString(List<Node> ast) {
|
||||||
|
List<Object> list = new ArrayList<>(ast.size());
|
||||||
|
for (Node n : ast) {
|
||||||
|
list.add(nodeToMap(n));
|
||||||
|
}
|
||||||
|
return JSONParser.toJson(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归地将 AST 节点转换为 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()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
map.put("imports", imps);
|
||||||
|
List<Object> funcs = new ArrayList<>(functions.size());
|
||||||
|
for (FunctionNode f : functions) {
|
||||||
|
funcs.add(nodeToMap(f));
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
map.put("parameters", params);
|
||||||
|
map.put("returnType", f.returnType());
|
||||||
|
List<Object> body = new ArrayList<>(f.body().size());
|
||||||
|
for (Node stmt : f.body()) {
|
||||||
|
body.add(nodeToMap(stmt));
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
);
|
||||||
|
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()));
|
||||||
|
List<Object> thenList = new ArrayList<>(i.thenBranch().size());
|
||||||
|
for (Node stmt : i.thenBranch()) thenList.add(nodeToMap(stmt));
|
||||||
|
map.put("then", thenList);
|
||||||
|
if (!i.elseBranch().isEmpty()) {
|
||||||
|
List<Object> elseList = new ArrayList<>(i.elseBranch().size());
|
||||||
|
for (Node stmt : i.elseBranch()) elseList.add(nodeToMap(stmt));
|
||||||
|
map.put("else", elseList);
|
||||||
|
}
|
||||||
|
yield map;
|
||||||
|
}
|
||||||
|
case LoopNode l -> {
|
||||||
|
Map<String, Object> map = newNodeMap("Loop");
|
||||||
|
map.put("initializer", l.initializer() != null ? nodeToMap(l.initializer()) : null);
|
||||||
|
map.put("condition", l.condition() != null ? exprToMap(l.condition()) : null);
|
||||||
|
map.put("update", l.update() != null ? nodeToMap(l.update()) : null);
|
||||||
|
List<Object> body = new ArrayList<>(l.body().size());
|
||||||
|
for (Node stmt : l.body()) body.add(nodeToMap(stmt));
|
||||||
|
map.put("body", body);
|
||||||
|
yield map;
|
||||||
|
}
|
||||||
|
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 表示。
|
||||||
|
*/
|
||||||
|
private static Object exprToMap(ExpressionNode expr) {
|
||||||
|
return switch (expr) {
|
||||||
|
case BinaryExpressionNode(ExpressionNode left, String operator, ExpressionNode right) -> exprMap("BinaryExpression",
|
||||||
|
"left", exprToMap(left),
|
||||||
|
"operator", operator,
|
||||||
|
"right", exprToMap(right)
|
||||||
|
);
|
||||||
|
case IdentifierNode(String name) -> exprMap("Identifier", "name", name);
|
||||||
|
case NumberLiteralNode(String value) -> exprMap("NumberLiteral", "value", value);
|
||||||
|
case StringLiteralNode(String value) -> exprMap("StringLiteral", "value", value);
|
||||||
|
case CallExpressionNode(ExpressionNode callee, List<ExpressionNode> arguments) -> {
|
||||||
|
List<Object> args = new ArrayList<>(arguments.size());
|
||||||
|
for (ExpressionNode arg : arguments) args.add(exprToMap(arg));
|
||||||
|
yield exprMap("CallExpression", "callee", exprToMap(callee), "arguments", args);
|
||||||
|
}
|
||||||
|
case MemberExpressionNode(ExpressionNode object, String member) -> exprMap("MemberExpression",
|
||||||
|
"object", exprToMap(object),
|
||||||
|
"member", member
|
||||||
|
);
|
||||||
|
default -> Map.of("type", expr.getClass().getSimpleName());
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
371
src/main/java/org/jcnc/snow/compiler/parser/util/JSONParser.java
Normal file
371
src/main/java/org/jcnc/snow/compiler/parser/util/JSONParser.java
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.util;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON 工具类,提供线程安全、可重用的解析与序列化功能
|
||||||
|
* <p>
|
||||||
|
* - 解析:将合法的 JSON 文本转换为 Java 原生对象(Map、List、String、Number、Boolean 或 null)
|
||||||
|
* - 序列化:将 Java 原生对象转换为符合 JSON 标准的字符串
|
||||||
|
* <p>
|
||||||
|
* 设计要点:
|
||||||
|
* 1. 使用静态方法作为唯一入口,避免状态共享导致的线程安全问题
|
||||||
|
* 2. 解析器内部使用 char[] 缓冲区,提高访问性能
|
||||||
|
* 3. 维护行列号信息,抛出异常时能精确定位错误位置
|
||||||
|
* 4. 序列化器基于 StringBuilder,预分配容量,减少中间字符串创建
|
||||||
|
*/
|
||||||
|
public class JSONParser {
|
||||||
|
// 私有构造,禁止外部实例化
|
||||||
|
private JSONParser() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 JSON 文本解析为对应的 Java 对象
|
||||||
|
* @param input JSON 格式字符串
|
||||||
|
* @return 对应的 Java 原生对象:
|
||||||
|
* - JSON 对象 -> Map<String, Object>
|
||||||
|
* - JSON 数组 -> List<Object>
|
||||||
|
* - JSON 字符串 -> String
|
||||||
|
* - JSON 数值 -> Long 或 Double
|
||||||
|
* - JSON 布尔 -> Boolean
|
||||||
|
* - JSON null -> null
|
||||||
|
* @throws RuntimeException 如果遇到语法错误或多余字符,异常消息中包含行列信息
|
||||||
|
*/
|
||||||
|
public static Object parse(String input) {
|
||||||
|
return new Parser(input).parseInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 Java 原生对象序列化为 JSON 字符串
|
||||||
|
* @param obj 支持的类型:Map、Collection、String、Number、Boolean 或 null
|
||||||
|
* @return 符合 JSON 规范的字符串
|
||||||
|
*/
|
||||||
|
public static String toJson(Object obj) {
|
||||||
|
return Writer.write(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======= 内部解析器 =======
|
||||||
|
/**
|
||||||
|
* 负责将 char[] 缓冲区中的 JSON 文本解析为 Java 对象
|
||||||
|
*/
|
||||||
|
private static class Parser {
|
||||||
|
/** 输入缓冲区 */
|
||||||
|
private final char[] buf;
|
||||||
|
/** 当前解析到的位置索引 */
|
||||||
|
private int pos;
|
||||||
|
/** 当前字符所在行号,从 1 开始 */
|
||||||
|
private int line;
|
||||||
|
/** 当前字符所在列号,从 1 开始 */
|
||||||
|
private int col;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造解析器,初始化缓冲区和行列信息
|
||||||
|
* @param input 待解析的 JSON 文本
|
||||||
|
*/
|
||||||
|
Parser(String input) {
|
||||||
|
this.buf = input.toCharArray();
|
||||||
|
this.pos = 0;
|
||||||
|
this.line = 1;
|
||||||
|
this.col = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入口方法,跳过空白后调用 parseValue,再校验尾部无多余字符
|
||||||
|
*/
|
||||||
|
Object parseInternal() {
|
||||||
|
skipWhitespace();
|
||||||
|
Object value = parseValue();
|
||||||
|
skipWhitespace();
|
||||||
|
if (pos < buf.length) {
|
||||||
|
error("存在无法识别的多余字符");
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据下一个字符决定解析哪种 JSON 值
|
||||||
|
*/
|
||||||
|
private Object parseValue() {
|
||||||
|
skipWhitespace();
|
||||||
|
if (match("null")) return null;
|
||||||
|
if (match("true")) return Boolean.TRUE;
|
||||||
|
if (match("false")) return Boolean.FALSE;
|
||||||
|
char c = currentChar();
|
||||||
|
if (c == '"') return parseString();
|
||||||
|
if (c == '{') return parseObject();
|
||||||
|
if (c == '[') return parseArray();
|
||||||
|
if (c == '-' || isDigit(c)) return parseNumber();
|
||||||
|
error("遇到意外字符 '" + c + "'");
|
||||||
|
return null; // 永不到达
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 JSON 对象,返回 Map<String, Object>
|
||||||
|
*/
|
||||||
|
private Map<String, Object> parseObject() {
|
||||||
|
expect('{'); // 跳过 '{'
|
||||||
|
skipWhitespace();
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
// 空对象 {}
|
||||||
|
if (currentChar() == '}') {
|
||||||
|
advance(); // 跳过 '}'
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
// 多成员对象解析
|
||||||
|
while (true) {
|
||||||
|
skipWhitespace();
|
||||||
|
String key = parseString(); // 解析键
|
||||||
|
skipWhitespace(); expect(':'); skipWhitespace();
|
||||||
|
Object val = parseValue(); // 解析值
|
||||||
|
map.put(key, val);
|
||||||
|
skipWhitespace();
|
||||||
|
if (currentChar() == '}') {
|
||||||
|
advance(); // 跳过 '}'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
expect(','); skipWhitespace();
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 JSON 数组,返回 List<Object>
|
||||||
|
*/
|
||||||
|
private List<Object> parseArray() {
|
||||||
|
expect('[');
|
||||||
|
skipWhitespace();
|
||||||
|
List<Object> list = new ArrayList<>();
|
||||||
|
// 空数组 []
|
||||||
|
if (currentChar() == ']') {
|
||||||
|
advance(); // 跳过 ']'
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
// 多元素数组解析
|
||||||
|
while (true) {
|
||||||
|
skipWhitespace();
|
||||||
|
list.add(parseValue());
|
||||||
|
skipWhitespace();
|
||||||
|
if (currentChar() == ']') {
|
||||||
|
advance();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
expect(','); skipWhitespace();
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 JSON 字符串文字,处理转义字符
|
||||||
|
*/
|
||||||
|
private String parseString() {
|
||||||
|
expect('"'); // 跳过开头 '"'
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
while (true) {
|
||||||
|
char c = currentChar();
|
||||||
|
if (c == '"') {
|
||||||
|
advance(); // 跳过结束 '"'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c == '\\') {
|
||||||
|
advance(); // 跳过 '\'
|
||||||
|
c = currentChar();
|
||||||
|
switch (c) {
|
||||||
|
case '"': sb.append('"'); break;
|
||||||
|
case '\\': sb.append('\\'); break;
|
||||||
|
case '/': sb.append('/'); break;
|
||||||
|
case 'b': sb.append('\b'); break;
|
||||||
|
case 'f': sb.append('\f'); break;
|
||||||
|
case 'n': sb.append('\n'); break;
|
||||||
|
case 'r': sb.append('\r'); break;
|
||||||
|
case 't': sb.append('\t'); break;
|
||||||
|
case 'u': // 解析 Unicode 转义
|
||||||
|
String hex = new String(buf, pos+1, 4);
|
||||||
|
sb.append((char) Integer.parseInt(hex, 16));
|
||||||
|
pos += 4; col += 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error("无效转义字符 '\\" + c + "'");
|
||||||
|
}
|
||||||
|
advance();
|
||||||
|
} else {
|
||||||
|
sb.append(c);
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 JSON 数值,支持整数、浮点及科学计数法
|
||||||
|
*/
|
||||||
|
private Number parseNumber() {
|
||||||
|
int start = pos;
|
||||||
|
if (currentChar() == '-') advance();
|
||||||
|
while (isDigit(currentChar())) advance();
|
||||||
|
if (currentChar() == '.') {
|
||||||
|
advance();
|
||||||
|
while (isDigit(currentChar())) advance();
|
||||||
|
}
|
||||||
|
if (currentChar() == 'e' || currentChar() == 'E') {
|
||||||
|
advance();
|
||||||
|
if (currentChar() == '+' || currentChar() == '-') advance();
|
||||||
|
while (isDigit(currentChar())) advance();
|
||||||
|
}
|
||||||
|
String num = new String(buf, start, pos - start);
|
||||||
|
// 判断返回 Long 还是 Double
|
||||||
|
if (num.indexOf('.') >= 0 || num.indexOf('e') >= 0 || num.indexOf('E') >= 0) {
|
||||||
|
return Double.parseDouble(num);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Long.parseLong(num);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return Double.parseDouble(num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跳过所有空白字符,支持空格、制表符、回车、换行
|
||||||
|
*/
|
||||||
|
private void skipWhitespace() {
|
||||||
|
while (pos < buf.length) {
|
||||||
|
char c = buf[pos];
|
||||||
|
if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
|
||||||
|
advance();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前位置字符,超出范围返回 '\0'
|
||||||
|
*/
|
||||||
|
private char currentChar() {
|
||||||
|
return pos < buf.length ? buf[pos] : '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推进到下一个字符,并更新行列信息
|
||||||
|
*/
|
||||||
|
private void advance() {
|
||||||
|
if (pos < buf.length) {
|
||||||
|
if (buf[pos] == '\n') {
|
||||||
|
line++; col = 1;
|
||||||
|
} else {
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证当前位置字符等于预期字符,否则抛出错误
|
||||||
|
*/
|
||||||
|
private void expect(char c) {
|
||||||
|
if (currentChar() != c) {
|
||||||
|
error("期望 '" + c + "',但遇到 '" + currentChar() + "'");
|
||||||
|
}
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试匹配给定字符串,匹配成功则移动位置并返回 true
|
||||||
|
*/
|
||||||
|
private boolean match(String s) {
|
||||||
|
int len = s.length();
|
||||||
|
if (pos + len > buf.length) return false;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (buf[pos + i] != s.charAt(i)) return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < len; i++) advance();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断字符是否为数字
|
||||||
|
*/
|
||||||
|
private boolean isDigit(char c) {
|
||||||
|
return c >= '0' && c <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抛出带行列定位的解析错误
|
||||||
|
*/
|
||||||
|
private void error(String msg) {
|
||||||
|
throw new RuntimeException("Error at line " + line + ", column " + col + ": " + msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======= 内部序列化器 =======
|
||||||
|
/**
|
||||||
|
* 负责高效地将 Java 对象写为 JSON 文本
|
||||||
|
*/
|
||||||
|
private static class Writer {
|
||||||
|
/** 默认 StringBuilder 初始容量,避免频繁扩容 */
|
||||||
|
private static final int DEFAULT_CAPACITY = 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入口方法,根据 obj 类型分派写入逻辑
|
||||||
|
*/
|
||||||
|
static String write(Object obj) {
|
||||||
|
StringBuilder sb = new StringBuilder(DEFAULT_CAPACITY);
|
||||||
|
writeValue(obj, sb);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据对象类型选择合适的写入方式
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static void writeValue(Object obj, StringBuilder sb) {
|
||||||
|
if (obj == null) {
|
||||||
|
sb.append("null");
|
||||||
|
} else if (obj instanceof String) {
|
||||||
|
quote((String) obj, sb);
|
||||||
|
} else if (obj instanceof Number || obj instanceof Boolean) {
|
||||||
|
sb.append(obj.toString());
|
||||||
|
} else if (obj instanceof Map) {
|
||||||
|
sb.append('{');
|
||||||
|
boolean first = true;
|
||||||
|
for (Entry<?, ?> e : ((Map<?, ?>) obj).entrySet()) {
|
||||||
|
if (!first) sb.append(',');
|
||||||
|
first = false;
|
||||||
|
quote(e.getKey().toString(), sb);
|
||||||
|
sb.append(':');
|
||||||
|
writeValue(e.getValue(), sb);
|
||||||
|
}
|
||||||
|
sb.append('}');
|
||||||
|
} else if (obj instanceof Collection) {
|
||||||
|
sb.append('[');
|
||||||
|
boolean first = true;
|
||||||
|
for (Object item : (Collection<?>) obj) {
|
||||||
|
if (!first) sb.append(',');
|
||||||
|
first = false;
|
||||||
|
writeValue(item, sb);
|
||||||
|
}
|
||||||
|
sb.append(']');
|
||||||
|
} else {
|
||||||
|
// 其他类型,使用 toString 并加引号
|
||||||
|
quote(obj.toString(), sb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为字符串添加双引号并转义必须的字符
|
||||||
|
*/
|
||||||
|
private static void quote(String s, StringBuilder sb) {
|
||||||
|
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('"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -37,28 +37,24 @@ public class JsonFormatter {
|
|||||||
sb.append(c);
|
sb.append(c);
|
||||||
} else if (!inQuotes) {
|
} else if (!inQuotes) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '{', '[':
|
case '{', '[' -> {
|
||||||
sb.append(c).append('\n');
|
sb.append(c).append('\n');
|
||||||
indent++;
|
indent++;
|
||||||
appendIndent(sb, indent);
|
appendIndent(sb, indent);
|
||||||
break;
|
}
|
||||||
case '}', ']':
|
case '}', ']' -> {
|
||||||
sb.append('\n');
|
sb.append('\n');
|
||||||
indent--;
|
indent--;
|
||||||
appendIndent(sb, indent);
|
appendIndent(sb, indent);
|
||||||
sb.append(c);
|
sb.append(c);
|
||||||
break;
|
}
|
||||||
case ',':
|
case ',' -> sb.append(c).append('\n').append(" ".repeat(indent));
|
||||||
sb.append(c).append('\n');
|
case ':' -> sb.append(c).append(' ');
|
||||||
appendIndent(sb, indent);
|
default -> {
|
||||||
break;
|
|
||||||
case ':':
|
|
||||||
sb.append(c).append(' ');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (!Character.isWhitespace(c)) {
|
if (!Character.isWhitespace(c)) {
|
||||||
sb.append(c);
|
sb.append(c);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 字符串内部原样输出
|
// 字符串内部原样输出
|
||||||
|
|||||||
10
test
10
test
@ -1,15 +1,11 @@
|
|||||||
module: CommonTasks
|
module: CommonTasks
|
||||||
function: add_numbers
|
function: add_numbers
|
||||||
|
|
||||||
body:
|
body:
|
||||||
return num1 + num2
|
return num1 + num2
|
||||||
end body
|
end body
|
||||||
|
|
||||||
parameter:
|
parameter:
|
||||||
|
|
||||||
declare num1: int
|
declare num1: int
|
||||||
declare num2: int
|
declare num2: int
|
||||||
|
|
||||||
return_type: int
|
return_type: int
|
||||||
|
|
||||||
end function
|
end function
|
||||||
@ -41,7 +37,6 @@ end module
|
|||||||
module: MainModule
|
module: MainModule
|
||||||
import: CommonTasks, MathUtils, StringUtils, BuiltinUtils
|
import: CommonTasks, MathUtils, StringUtils, BuiltinUtils
|
||||||
|
|
||||||
|
|
||||||
function: test
|
function: test
|
||||||
parameter:
|
parameter:
|
||||||
declare first_str: string
|
declare first_str: string
|
||||||
@ -52,7 +47,6 @@ module: MainModule
|
|||||||
end body
|
end body
|
||||||
end function
|
end function
|
||||||
|
|
||||||
|
|
||||||
function: main
|
function: main
|
||||||
parameter:
|
parameter:
|
||||||
declare args: string
|
declare args: string
|
||||||
@ -89,9 +83,9 @@ module: MainModule
|
|||||||
BuiltinUtils.print(" first inner")
|
BuiltinUtils.print(" first inner")
|
||||||
else
|
else
|
||||||
if j % 2 == 0 then
|
if j % 2 == 0 then
|
||||||
BuiltinUtils.print(" j even")
|
BuiltinUtils.print("j even")
|
||||||
else
|
else
|
||||||
BuiltinUtils.print(" j odd")
|
BuiltinUtils.print("j odd")
|
||||||
end if
|
end if
|
||||||
end if
|
end if
|
||||||
end body
|
end body
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user