diff --git a/rule/mcfunction_processor/McfunctionProcessor.java b/rule/mcfunction_processor/McfunctionProcessor.java index ec9aafb..22f544c 100644 --- a/rule/mcfunction_processor/McfunctionProcessor.java +++ b/rule/mcfunction_processor/McfunctionProcessor.java @@ -12,12 +12,15 @@ public class McfunctionProcessor extends Worker { private static final Pattern FUNCTION_CALL_PATTERN = Pattern.compile("^function\\s+([a-z0-9_.-]+:[a-z0-9_./\\-]+)(?:\\s+(.+))?$"); private static final Pattern FORCE_FUNCTION_PATTERN = Pattern.compile("^#function\\s+([a-z0-9_.-]+:[a-z0-9_./\\-]+)$"); private static final Pattern RETURN_PATTERN = Pattern.compile("^(?:return|execute\\s+.*\\s+run\\s+return)\\b"); + private static final Pattern FUNCTION_CALL_DETECTION_PATTERN = Pattern.compile("\\bfunction\\s+([a-z0-9_.-]+:[a-z0-9_./\\-]+)"); private Map> functionCache = new HashMap<>(); private String currentPackId; private Path dataPackRoot; private List dependencyPaths = new ArrayList<>(); private Path workspaceRoot; + private Map> functionCallGraph = new HashMap<>(); + private Set detectedCycles = new HashSet<>(); public static void main(String[] args) throws Exception { new McfunctionProcessor().run(); @@ -65,7 +68,7 @@ protected int handleRequest(WorkRequest request, PrintWriter out) { try { processFile(inputFile, outputFile); - } catch (IOException e) { + } catch (Exception e) { out.println("Error processing file: " + e.getMessage()); e.printStackTrace(out); return 1; @@ -317,6 +320,8 @@ private List loadFunction(String functionName) { // 对加载的函数内容进行基础处理(注释去除和反斜杠拼接) List processedContent = processBasicLines(content); functionCache.put(functionName, processedContent); + // 解析该函数调用的其他函数,构建调用图 + parseFunctionCalls(functionName, processedContent); return processedContent; } catch (IOException e) { // 继续尝试下一个路径 @@ -551,6 +556,69 @@ private List splitSNBTPairs(String content) { return pairs; } + private void parseFunctionCalls(String functionName, List functionContent) { + Set calls = new HashSet<>(); + for (String line : functionContent) { + String trimmed = line.trim(); + Matcher matcher = FUNCTION_CALL_DETECTION_PATTERN.matcher(trimmed); + while (matcher.find()) { + String calledFunction = matcher.group(1); + // 验证命名空间ID格式 + if (!NAMESPACE_ID_PATTERN.matcher(calledFunction).matches()) { + continue; + } + // 跳过以#开头的函数名 + if (calledFunction.startsWith("#")) { + continue; + } + calls.add(calledFunction); + } + } + functionCallGraph.put(functionName, calls); + // 检测调用图中是否有环 + detectCycles(functionName); + } + + private void detectCycles(String startFunction) { + Set visited = new HashSet<>(); + Set stack = new HashSet<>(); + List path = new ArrayList<>(); + if (hasCycle(startFunction, visited, stack, path)) { + // 避免重复报告同一个环 + String cycleKey = String.join("->", path); + if (!detectedCycles.contains(cycleKey)) { + detectedCycles.add(cycleKey); + throw new RuntimeException("Detected function call cycle: " + cycleKey); + } + } + } + + private boolean hasCycle(String function, Set visited, Set stack, List path) { + if (stack.contains(function)) { + path.add(function); + return true; + } + if (visited.contains(function)) { + return false; + } + visited.add(function); + stack.add(function); + path.add(function); + + Set calls = functionCallGraph.get(function); + if (calls != null) { + for (String callee : calls) { + if (hasCycle(callee, visited, stack, path)) { + return true; + } + } + } + + stack.remove(function); + path.remove(path.size() - 1); + return false; + } + private String processSNBTValue(String value) { value = value.trim();