diff --git a/source_.jar b/source_.jar index e93cc14..2780569 100644 Binary files a/source_.jar and b/source_.jar differ diff --git a/src/main/java/cod/interpreter/Index.java b/src/main/java/cod/interpreter/Index.java index 6d76db6..8156aac 100644 --- a/src/main/java/cod/interpreter/Index.java +++ b/src/main/java/cod/interpreter/Index.java @@ -1,19 +1,21 @@ package cod.interpreter; import cod.error.ProgramError; +import cod.ir.IRManager; import cod.lexer.*; import static cod.lexer.TokenType.*; import static cod.syntax.Symbol.*; import static cod.syntax.Keyword.*; import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.*; /** * Index file for Coderive units. * Stores classname → filename mappings for O(1) import resolution. * - * File format: {projectRoot}/src/idx/{unit}.toml + * File format (preferred): {projectRoot}/src/bin/project.codc -> HOOK.toml * * Example: * # unit sample @@ -29,10 +31,9 @@ */ public final class Index { - private static final String IDX_DIR_NAME = "idx"; - private static final String SRC_DIR_NAME = "src"; - private static final String FILE_EXTENSION = ".toml"; private static final String CLASSES_SECTION = "classes"; + private static final String CLASSES_SECTION_PREFIX = CLASSES_SECTION + ":"; + private static final String SRC_DIR_NAME = "src"; private static final String DEFAULT_GENERATOR = "Coderive 1.0"; private final String unit; @@ -82,15 +83,8 @@ public static String getProjectRoot() { return projectRoot; } - /** - * Gets the index file path for a unit. - */ - private static File getIndexFile(String unitName) { - if (projectRoot == null) { - return new File("src/" + IDX_DIR_NAME + "/" + unitName + FILE_EXTENSION); - } - return new File(projectRoot + File.separator + SRC_DIR_NAME + - File.separator + IDX_DIR_NAME + File.separator + unitName + FILE_EXTENSION); + private static String getUnitSectionName(String unitName) { + return CLASSES_SECTION_PREFIX + unitName; } /** @@ -134,71 +128,20 @@ public static Index load(String unitName) { if (unitName == null || unitName.trim().isEmpty()) { return null; } - - File file = getIndexFile(unitName); - if (!file.exists()) { + String docText = loadPreferredDocumentText(unitName); + if (docText == null) { return null; } - - BufferedReader reader = null; - try { - reader = new BufferedReader(new FileReader(file)); - Index index = new Index(unitName); - String currentSection = ""; - String line; - - while ((line = reader.readLine()) != null) { - line = line.trim(); - - if (line.isEmpty() || line.startsWith("#")) { - continue; - } - - if (line.startsWith("[") && line.endsWith("]")) { - currentSection = line.substring(1, line.length() - 1); - continue; - } - - int eq = line.indexOf('='); - if (eq == -1) { - continue; - } - - String key = line.substring(0, eq).trim(); - String value = line.substring(eq + 1).trim(); - - if (value.startsWith("\"") && value.endsWith("\"")) { - value = value.substring(1, value.length() - 1); - } - - if (currentSection.isEmpty()) { - if ("timestamp".equals(key)) { - try { - index.timestamp = Long.parseLong(value); - } catch (NumberFormatException e) { - // Keep existing timestamp - } - } else if ("generator".equals(key)) { - index.generator = value; - } - } else if (CLASSES_SECTION.equals(currentSection)) { - index.classes.put(key, value); - } - } - - return index; - - } catch (IOException e) { + + IndexDocument doc = parseDocument(docText, unitName); + Map unitMappings = doc.unitMappings.get(unitName); + if (unitMappings == null || unitMappings.isEmpty()) { return null; - } finally { - if (reader != null) { - try { - reader.close(); - } catch (IOException e) { - // Ignore - } - } } + + Index index = new Index(unitName, doc.timestamp, doc.generator); + index.classes.putAll(unitMappings); + return index; } /** @@ -207,42 +150,23 @@ public static Index load(String unitName) { * @return true if saved successfully, false otherwise */ public boolean save() { - File file = getIndexFile(unit); - - File parent = file.getParentFile(); - if (parent != null && !parent.exists()) { - if (!parent.mkdirs()) { - return false; - } + if (projectRoot == null) { + return false; } - - PrintWriter writer = null; + + IndexDocument merged = loadExistingDocument(unit); + merged.timestamp = timestamp; + merged.generator = (generator == null || generator.isEmpty()) ? DEFAULT_GENERATOR : generator; + merged.unitMappings.put(unit, new HashMap(classes)); + + String documentText = writeDocumentText(merged); + + IRManager manager = new IRManager(projectRoot); try { - writer = new PrintWriter(new FileWriter(file)); - - writer.println("# unit " + unit); - writer.println("timestamp = \"" + timestamp + "\""); - writer.println("generator = \"" + generator + "\""); - writer.println(); - writer.println("[" + CLASSES_SECTION + "]"); - - List sorted = new ArrayList(classes.keySet()); - Collections.sort(sorted); - - for (String className : sorted) { - String fileName = classes.get(className); - writer.println(className + " = \"" + fileName + "\""); - } - - writer.flush(); + manager.saveIndex(unit, documentText); return true; - } catch (IOException e) { return false; - } finally { - if (writer != null) { - writer.close(); - } } } @@ -562,10 +486,121 @@ public Map getMappings() { } // ========== Private Helpers ========== - + + private static String loadPreferredDocumentText(String unitName) { + if (projectRoot == null) { + return null; + } + + IRManager manager = new IRManager(projectRoot); + return manager.loadIndex(unitName); + } + + private static IndexDocument loadExistingDocument(String unitName) { + String content = loadPreferredDocumentText(unitName); + if (content == null) { + return new IndexDocument(System.currentTimeMillis(), DEFAULT_GENERATOR, new HashMap>()); + } + return parseDocument(content, unitName); + } + + private static String writeDocumentText(IndexDocument doc) { + StringBuilder out = new StringBuilder(); + out.append("# project-wide multi-unit class index\n"); + out.append("timestamp = \"").append(doc.timestamp).append("\"\n"); + out.append("generator = \"").append(doc.generator).append("\"\n"); + out.append("\n"); + + List units = new ArrayList(doc.unitMappings.keySet()); + Collections.sort(units); + for (String unitName : units) { + out.append("[").append(getUnitSectionName(unitName)).append("]\n"); + Map mappings = doc.unitMappings.get(unitName); + List classNames = new ArrayList(mappings.keySet()); + Collections.sort(classNames); + for (String className : classNames) { + out.append(className).append(" = \"").append(mappings.get(className)).append("\"\n"); + } + out.append("\n"); + } + return out.toString(); + } + + private static IndexDocument parseDocument(String content, String fallbackUnitName) { + IndexDocument doc = new IndexDocument(System.currentTimeMillis(), DEFAULT_GENERATOR, new HashMap>()); + if (content == null) return doc; + + BufferedReader reader = new BufferedReader(new StringReader(content)); + String currentSection = ""; + try { + String line; + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.isEmpty() || line.startsWith("#")) { + continue; + } + if (line.startsWith("[") && line.endsWith("]")) { + currentSection = line.substring(1, line.length() - 1); + continue; + } + + int eq = line.indexOf('='); + if (eq == -1) { + continue; + } + + String key = line.substring(0, eq).trim(); + String value = line.substring(eq + 1).trim(); + if (value.startsWith("\"") && value.endsWith("\"")) { + value = value.substring(1, value.length() - 1); + } + + if (currentSection.isEmpty()) { + if ("timestamp".equals(key)) { + try { + doc.timestamp = Long.parseLong(value); + } catch (NumberFormatException ignored) {} + } else if ("generator".equals(key)) { + doc.generator = value; + } + continue; + } + + String targetUnit = null; + if (CLASSES_SECTION.equals(currentSection)) { + targetUnit = fallbackUnitName; + } else if (currentSection.startsWith(CLASSES_SECTION_PREFIX)) { + targetUnit = currentSection.substring(CLASSES_SECTION_PREFIX.length()); + } + if (targetUnit == null || targetUnit.isEmpty()) continue; + + Map map = doc.unitMappings.get(targetUnit); + if (map == null) { + map = new HashMap(); + doc.unitMappings.put(targetUnit, map); + } + map.put(key, value); + } + } catch (IOException ignored) {} + + return doc; + } + + private static final class IndexDocument { + long timestamp; + String generator; + Map> unitMappings; + + IndexDocument(long timestamp, String generator, Map> unitMappings) { + this.timestamp = timestamp; + this.generator = generator; + this.unitMappings = unitMappings; + } + } + private static String readFileToString(File file) throws IOException { StringBuilder content = new StringBuilder(); - BufferedReader reader = new BufferedReader(new FileReader(file)); + BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)); try { String line; while ((line = reader.readLine()) != null) { @@ -648,4 +683,4 @@ public Index build() { return index; } } -} \ No newline at end of file +} diff --git a/src/main/java/cod/ir/IRManager.java b/src/main/java/cod/ir/IRManager.java index 4b4c252..220d910 100644 --- a/src/main/java/cod/ir/IRManager.java +++ b/src/main/java/cod/ir/IRManager.java @@ -18,6 +18,7 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @@ -31,6 +32,8 @@ public class IRManager { private static final String BIN_DIR = "bin"; private static final String IR_EXT = ".codb"; private static final String CONTAINER_EXT = ".codc"; + private static final String PROJECT_CONTAINER_NAME = "project"; + private static final String PROJECT_INDEX_FILE_NAME = "HOOK.toml"; private static final int BUFFER_SIZE = 8192; private static final Map CONTAINER_LOCKS = new ConcurrentHashMap(); @@ -154,6 +157,24 @@ public void clearCache() { artifactCache.clear(); } + public String loadIndex(String unit) { + if (unit == null || unit.isEmpty()) return null; + String entryName = getProjectIndexEntryName(); + try { + byte[] data = readContainerEntry(unit, entryName); + if (data == null) return null; + return new String(data, StandardCharsets.UTF_8); + } catch (IOException e) { + return null; + } + } + + public void saveIndex(String unit, String indexContent) throws IOException { + if (unit == null || unit.isEmpty() || indexContent == null) return; + String entryName = getProjectIndexEntryName(); + writeContainerEntry(unit, entryName, indexContent.getBytes(StandardCharsets.UTF_8)); + } + public Map getCacheStats() { Map stats = new HashMap(); int total = 0; @@ -194,38 +215,43 @@ private File getIRFile(String unit, String className) { } private File getContainerFile(String unit) { - String rootUnit = getRootUnit(unit); - String path = projectRoot + "/src/" + BIN_DIR + "/" + rootUnit + CONTAINER_EXT; + String path = projectRoot + "/src/" + BIN_DIR + "/" + PROJECT_CONTAINER_NAME + CONTAINER_EXT; return new File(path); } - private String getRootUnit(String unit) { - if (unit == null || unit.isEmpty()) return "default"; - int dot = unit.indexOf('.'); - if (dot < 0) return unit; - if (dot == 0) return "default"; - return unit.substring(0, dot); - } - private String getContainerEntryName(String unit, String className) { return unit + "/" + className + IR_EXT; } + private String getProjectIndexEntryName() { + return PROJECT_INDEX_FILE_NAME; + } + private Artifact readArtifactFromContainer(String unit, String className) throws IOException { + byte[] data = readContainerEntry(unit, getContainerEntryName(unit, className)); + if (data == null) return null; + return readArtifactFromBytes(data); + } + + private void writeArtifactToContainer(String unit, String className, Artifact artifact) throws IOException { + if (unit == null || className == null || artifact == null) return; + writeContainerEntry(unit, getContainerEntryName(unit, className), writeArtifactToBytes(artifact)); + } + + private byte[] readContainerEntry(String unit, String entryName) throws IOException { + if (unit == null || entryName == null) return null; File container = getContainerFile(unit); if (!container.exists() || !container.isFile()) { return null; } - String targetEntry = getContainerEntryName(unit, className); ZipInputStream in = null; try { in = new ZipInputStream(new BufferedInputStream(new FileInputStream(container))); ZipEntry entry; while ((entry = in.getNextEntry()) != null) { - if (!entry.isDirectory() && targetEntry.equals(entry.getName())) { - byte[] data = readAllBytes(in); - return readArtifactFromBytes(data); + if (!entry.isDirectory() && entryName.equals(entry.getName())) { + return readAllBytes(in); } } return null; @@ -238,8 +264,8 @@ private Artifact readArtifactFromContainer(String unit, String className) throws } } - private void writeArtifactToContainer(String unit, String className, Artifact artifact) throws IOException { - if (unit == null || className == null || artifact == null) return; + private void writeContainerEntry(String unit, String entryName, byte[] entryData) throws IOException { + if (unit == null || entryName == null || entryData == null) return; File container = getContainerFile(unit); File parent = container.getParentFile(); @@ -250,7 +276,7 @@ private void writeArtifactToContainer(String unit, String className, Artifact ar Object containerLock = getContainerLock(container); synchronized (containerLock) { Map entries = readContainerEntries(container); - entries.put(getContainerEntryName(unit, className), writeArtifactToBytes(artifact)); + entries.put(entryName, entryData); File temp = new File(container.getAbsolutePath() + ".tmp"); ZipOutputStream out = null; diff --git a/src/main/java/cod/runner/CommandRunner.java b/src/main/java/cod/runner/CommandRunner.java index f43ba78..3b17d70 100644 --- a/src/main/java/cod/runner/CommandRunner.java +++ b/src/main/java/cod/runner/CommandRunner.java @@ -139,7 +139,7 @@ private void handleCompileCommand(String[] args) throws Exception { int compiled = 0; for (Type type : ast.unit.types) { bm.save(ast.unit.name, type); - System.out.println("Compiled (CodP-TAC artifact): " + type.name + " → .codc/" + ast.unit.name + "/" + type.name + ".codb"); + System.out.println("Compiled (CodP-TAC artifact): " + type.name + " → .codc/" + ast.unit.name + "/" + type.name + ".codb"); compiled++; } @@ -227,7 +227,7 @@ private void compileToBytecode(Program ast) { try { irManager.save(unitName, type); compiled++; - DebugSystem.debug(NAME + LOG_TAG, "Compiled CodP-TAC artifact: " + type.name + " → .codc/" + unitName + "/" + type.name + ".codb"); + DebugSystem.debug(NAME + LOG_TAG, "Compiled CodP-TAC artifact: " + type.name + " → .codc/" + unitName + "/" + type.name + ".codb"); } catch (Exception e) { DebugSystem.warn(NAME + LOG_TAG, "Failed to compile " + type.name + ": " + e.getMessage()); } diff --git a/src/main/java/cod/runner/TestRunner.java b/src/main/java/cod/runner/TestRunner.java index 4a53340..28d36df 100644 --- a/src/main/java/cod/runner/TestRunner.java +++ b/src/main/java/cod/runner/TestRunner.java @@ -312,7 +312,7 @@ private void compileToBytecode(Program ast) { try { irManager.save(unitName, type); compiled++; - DebugSystem.debug(NAME + LOG_TAG, "Compiled CodP-TAC artifact: " + type.name + " → .codc/" + unitName + "/" + type.name + ".codb"); + DebugSystem.debug(NAME + LOG_TAG, "Compiled CodP-TAC artifact: " + type.name + " → .codc/" + unitName + "/" + type.name + ".codb"); } catch (Exception e) { DebugSystem.warn(NAME + LOG_TAG, "Failed to compile " + type.name + ": " + e.getMessage()); } diff --git a/src/main/java/cod/semantic/ImportResolver.java b/src/main/java/cod/semantic/ImportResolver.java index 10eb1d5..e87daf0 100644 --- a/src/main/java/cod/semantic/ImportResolver.java +++ b/src/main/java/cod/semantic/ImportResolver.java @@ -208,7 +208,7 @@ public void setCurrentFileDirectory(String filePath) { } } - // Set the project root for Index class (for src/idx/ location) + // Set the project root for Index class (for src/bin/project.codc index location) Index.setProjectRoot(srcMainRoot); DebugSystem.debug("IMPORTS", "Set Index project root from: " + srcMainRoot);