diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e3030ed..c9c9abdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,34 @@ All notable changes to Coderive are documented in this file. +## [v0.9.6] - Keyword Cleanup & Parity Fix - May 2, 2026 + +### 🔑 Keyword Rename: `exit` → `fin` +- Renamed void early-return keyword from `exit` to `fin` throughout the language. +- `TokenType.Keyword.EXIT` renamed to `TokenType.Keyword.FIN`. +- AST node `Exit` renamed to `Fin`; exception `EarlyExitException` renamed to `EarlyFinException`. +- IR serialization tag updated from `"Exit"` to `"Fin"` (no backward compatibility required; pre-release). +- All parser, printer, lowerer, and visitor references updated accordingly. + +### 🚫 Removed Keyword Aliases +- Removed `continue` → `skip` and `return` → `fin` compatibility aliases from `IdentifierLexer`. +- Only explicit `skip` and `fin` keywords are now recognized as control-flow statements. + +### 📝 Source File Updates +- Updated all `.cod` demo and standard-library files to use `fin` instead of `exit`/`return` and `skip` instead of `continue`: + - `std/json/Json.cod` + - `std/scimath/SciMath.cod` + - `demo/src/main/test/controlflow/ControlFlow.cod` + - `demo/src/main/test/json/JsonStandardLibraryComprehensive.cod` + +### ✅ Parity & Java Compatibility +- Fixed `CodPTACParityRunner` normalize function to handle timing output of the form `"... at ms"` (fixes `BMark.cod` parity mismatch). +- Verified Java 7 source/target compatibility; all sources compile cleanly with `-source 1.7 -target 1.7`. +- All parity tests pass after keyword migration. + +### 📊 BMark primeCount Baseline (primeCount(5000)) +- Baseline run (PTAC executor, Java 17): `~1280 ms`, checksum `2666668685121930669`. + ## [v0.9.5] - JSON Stabilization - April 18, 2026 ### 🧩 JSON Parser & Serializer Fixes diff --git a/src/main/cod/demo/src/main/test/controlflow/ControlFlow.cod b/src/main/cod/demo/src/main/test/controlflow/ControlFlow.cod index 98891142..df66a01c 100644 --- a/src/main/cod/demo/src/main/test/controlflow/ControlFlow.cod +++ b/src/main/cod/demo/src/main/test/controlflow/ControlFlow.cod @@ -17,8 +17,8 @@ share ControlFlow { out("sum=" + sum) - out("exit:before") - exit - out("exit:after-should-not-print") + out("fin:before") + fin + out("fin:after-should-not-print") } } diff --git a/src/main/cod/demo/src/main/test/json/JsonStandardLibraryComprehensive.cod b/src/main/cod/demo/src/main/test/json/JsonStandardLibraryComprehensive.cod index f6f509bc..680a8d52 100644 --- a/src/main/cod/demo/src/main/test/json/JsonStandardLibraryComprehensive.cod +++ b/src/main/cod/demo/src/main/test/json/JsonStandardLibraryComprehensive.cod @@ -6,7 +6,7 @@ share JsonStandardLibraryComprehensive { share check(label: text, actual: text, expected: text) { if actual == expected { out("PASS " + label) - return + fin } out("FAIL " + label) out(" actual: " + actual) @@ -16,7 +16,7 @@ share JsonStandardLibraryComprehensive { share checkBool(label: text, actual: bool, expected: bool) { if actual == expected { out("PASS " + label) - return + fin } out("FAIL " + label) out(" actual: " + actual) diff --git a/src/main/cod/std/json/Json.cod b/src/main/cod/std/json/Json.cod index 65138505..ffb2b962 100644 --- a/src/main/cod/std/json/Json.cod +++ b/src/main/cod/std/json/Json.cod @@ -35,7 +35,7 @@ share JsonValue { } share add(item: JsonValue) { - if this.kind != 4 { return } + if this.kind != 4 { fin } idx: int = this.arrayData.size arrayData[idx] = item } @@ -48,11 +48,11 @@ share JsonValue { } share set(key: text, value: JsonValue) { - if this.kind != 5 { return } + if this.kind != 5 { fin } for i of 0 to this.objectKeys.size - 1 { if this.objectKeys[i] == key { objectValues[i] = value - return + fin } } idx: int = this.objectKeys.size @@ -262,41 +262,41 @@ share Json { if Json.isUnicodeEscapeAt(raw, idx) { outText = outText + raw[idx to idx + 5] idx = idx + 6 - continue + skip } outText = outText + "\\\\" idx = idx + 1 - continue + skip } if ch == "\"" { outText = outText + "\\\"" idx = idx + 1 - continue + skip } if ch == "\n" { outText = outText + "\\n" idx = idx + 1 - continue + skip } if ch == "\r" { outText = outText + "\\r" idx = idx + 1 - continue + skip } if ch == "\t" { outText = outText + "\\t" idx = idx + 1 - continue + skip } if ch == "\b" { outText = outText + "\\b" idx = idx + 1 - continue + skip } if ch == "\f" { outText = outText + "\\f" idx = idx + 1 - continue + skip } outText = outText + ch idx = idx + 1 @@ -491,14 +491,14 @@ share JsonParser { esc := this.source[this.index] index = this.index + 1 - if esc == "\"" { outText = outText + "\"" continue } - if esc == "\\" { outText = outText + "\\" continue } - if esc == "/" { outText = outText + "/" continue } - if esc == "b" { outText = outText + "\b" continue } - if esc == "f" { outText = outText + "\f" continue } - if esc == "n" { outText = outText + "\n" continue } - if esc == "r" { outText = outText + "\r" continue } - if esc == "t" { outText = outText + "\t" continue } + if esc == "\"" { outText = outText + "\"" skip } + if esc == "\\" { outText = outText + "\\" skip } + if esc == "/" { outText = outText + "/" skip } + if esc == "b" { outText = outText + "\b" skip } + if esc == "f" { outText = outText + "\f" skip } + if esc == "n" { outText = outText + "\n" skip } + if esc == "r" { outText = outText + "\r" skip } + if esc == "t" { outText = outText + "\t" skip } if esc == "u" { firstUnit := this.parseHex4At(this.index) @@ -522,7 +522,7 @@ share JsonParser { } index = this.index + 4 outText = outText + firstText + "\\u" + Json.hex4(secondUnit) - continue + skip } if Json.isLowSurrogate(firstUnit) { @@ -530,7 +530,7 @@ share JsonParser { } outText = outText + firstText - continue + skip } ~> (JsonValue.makeError("invalid escape sequence \\" + esc + " at index " + (this.index - 1))) @@ -627,7 +627,7 @@ share JsonParser { if all[this.index < this.source.length, this.source[this.index] == "]"] { ~> (JsonValue.makeError("trailing comma in array at index " + this.index)) } - continue + skip } if ch == "]" { @@ -683,7 +683,7 @@ share JsonParser { if all[this.index < this.source.length, this.source[this.index] == "\}"] { ~> (JsonValue.makeError("trailing comma in object at index " + this.index)) } - continue + skip } if ch == "\}" { @@ -699,13 +699,13 @@ share JsonParser { share skipWhitespace() { for n of this.index to this.source.length { - if this.index >= this.source.length { return } + if this.index >= this.source.length { fin } ch := this.source[this.index] if any[ch == " ", ch == "\n", ch == "\r", ch == "\t"] { index = this.index + 1 - continue + skip } - return + fin } } diff --git a/src/main/cod/std/scimath/SciMath.cod b/src/main/cod/std/scimath/SciMath.cod index 7a5781e2..39ca2bd6 100644 --- a/src/main/cod/std/scimath/SciMath.cod +++ b/src/main/cod/std/scimath/SciMath.cod @@ -201,7 +201,7 @@ share SciMath { ssBetween: int|float = 0 ssWithin: int|float = 0 for g of 0 to groups.size - 1 { - if groups[g].size == 0 { continue } + if groups[g].size == 0 { skip } meanG: int|float = 0 for i of 0 to groups[g].size - 1 { meanG = meanG + groups[g][i] @@ -251,7 +251,7 @@ share SciMath { if n <= 0 { ~> (0) } chi2: int|float = 0 for i of 0 to n - 1 { - if expected[i] <= 0 { continue } + if expected[i] <= 0 { skip } d := observed[i] - expected[i] chi2 = chi2 + d * d / expected[i] } @@ -295,7 +295,7 @@ share SciMath { aug[c][j] = aug[c][j] / pivot } for r of 0 to n - 1 { - if r == c { continue } + if r == c { skip } factor := aug[r][c] for j of 0 to (2 * n) - 1 { aug[r][j] = aug[r][j] - factor * aug[c][j] diff --git a/src/main/java/cod/ast/ASTFactory.java b/src/main/java/cod/ast/ASTFactory.java index 85ee25a2..30ec1b86 100644 --- a/src/main/java/cod/ast/ASTFactory.java +++ b/src/main/java/cod/ast/ASTFactory.java @@ -591,12 +591,12 @@ public static Var createVar(String name, Expr value, Token nameToken) { return var; } - public static Exit createExit(Token exitToken) { - Exit exit = new Exit(); - if (exitToken != null) { - exit.setSourceSpan(span(exitToken)); + public static Fin createFin(Token finToken) { + Fin fin = new Fin(); + if (finToken != null) { + fin.setSourceSpan(span(finToken)); } - return exit; + return fin; } public static ArgumentList createArgumentList(List arguments, Token lparenToken) { diff --git a/src/main/java/cod/ast/ASTPrinter.java b/src/main/java/cod/ast/ASTPrinter.java index 29bb6149..1e5f52da 100644 --- a/src/main/java/cod/ast/ASTPrinter.java +++ b/src/main/java/cod/ast/ASTPrinter.java @@ -183,8 +183,8 @@ public Void visit(Range node) { } @Override - public Void visit(Exit node) { - println("EXIT"); + public Void visit(Fin node) { + println("FIN"); return null; } diff --git a/src/main/java/cod/ast/ASTVisitor.java b/src/main/java/cod/ast/ASTVisitor.java index 2fd4f92d..4e5ef0ee 100644 --- a/src/main/java/cod/ast/ASTVisitor.java +++ b/src/main/java/cod/ast/ASTVisitor.java @@ -111,7 +111,7 @@ public T visit(Range n) { } @Override - public T visit(Exit n) { + public T visit(Fin n) { return n.accept(this); } diff --git a/src/main/java/cod/ast/VisitorImpl.java b/src/main/java/cod/ast/VisitorImpl.java index 7bf728ee..0aa9c178 100644 --- a/src/main/java/cod/ast/VisitorImpl.java +++ b/src/main/java/cod/ast/VisitorImpl.java @@ -49,7 +49,7 @@ public interface VisitorImpl { T visit(Range n); - T visit(Exit n); + T visit(Fin n); T visit(Tuple n); diff --git a/src/main/java/cod/ast/node/Exit.java b/src/main/java/cod/ast/node/Exit.java deleted file mode 100644 index 6d4dac7a..00000000 --- a/src/main/java/cod/ast/node/Exit.java +++ /dev/null @@ -1,13 +0,0 @@ -package cod.ast.node; - -import cod.ast.VisitorImpl; - -public class Exit extends Stmt { - - @Override - public final T accept(VisitorImpl visitor) { - return visitor.visit(this); - } - - -} \ No newline at end of file diff --git a/src/main/java/cod/ast/node/Fin.java b/src/main/java/cod/ast/node/Fin.java new file mode 100644 index 00000000..9c11b2f6 --- /dev/null +++ b/src/main/java/cod/ast/node/Fin.java @@ -0,0 +1,12 @@ +package cod.ast.node; + +import cod.ast.VisitorImpl; + +public class Fin extends Stmt { + + @Override + public final T accept(VisitorImpl visitor) { + return visitor.visit(this); + } + +} diff --git a/src/main/java/cod/interpreter/Interpreter.java b/src/main/java/cod/interpreter/Interpreter.java index ec6495cf..00fedc22 100644 --- a/src/main/java/cod/interpreter/Interpreter.java +++ b/src/main/java/cod/interpreter/Interpreter.java @@ -789,7 +789,7 @@ private void executeMainMethod(Type ownerType, Method mainMethod, String entryTa } } DebugSystem.methodExit("main", null); - } catch (EarlyExitException e) { + } catch (EarlyFinException e) { DebugSystem.methodExit("main", null); } catch (ProgramError e) { throw e; @@ -939,7 +939,7 @@ public Object evalMethod(Method node, ObjectInstance obj, Map lo } } } - } catch (EarlyExitException e) { + } catch (EarlyFinException e) { // Normal exit } catch (ProgramError e) { throw e; @@ -1151,7 +1151,7 @@ public Object evalMethodCall( } } } - } catch (EarlyExitException e) { + } catch (EarlyFinException e) { } catch (ProgramError e) { throw e; } catch (Exception e) { diff --git a/src/main/java/cod/interpreter/InterpreterVisitor.java b/src/main/java/cod/interpreter/InterpreterVisitor.java index ca23536c..6ea78f51 100644 --- a/src/main/java/cod/interpreter/InterpreterVisitor.java +++ b/src/main/java/cod/interpreter/InterpreterVisitor.java @@ -557,8 +557,8 @@ public Object visit(Range n) { } @Override - public Object visit(Exit node) { - throw new EarlyExitException(); + public Object visit(Fin node) { + throw new EarlyFinException(); } @Override @@ -780,8 +780,8 @@ private Object evaluateLambdaAssignment( } else { throw tailCallSignal; } - } catch (EarlyExitException e) { - // normal lambda early exit + } catch (EarlyFinException e) { + // normal lambda early fin } finally { popContext(); } @@ -1583,8 +1583,8 @@ public Object visit(MethodCall node) { } else { throw tailCallSignal; } - } catch (EarlyExitException e) { - // Normal exit - method completed + } catch (EarlyFinException e) { + // Normal fin - method completed } catch (ProgramError e) { throw e; } catch (Exception e) { diff --git a/src/main/java/cod/interpreter/exception/EarlyExitException.java b/src/main/java/cod/interpreter/exception/EarlyExitException.java deleted file mode 100644 index 2eaeadba..00000000 --- a/src/main/java/cod/interpreter/exception/EarlyExitException.java +++ /dev/null @@ -1,8 +0,0 @@ -package cod.interpreter.exception; - -@SuppressWarnings("serial") -public class EarlyExitException extends RuntimeException { - public EarlyExitException() { - super("Early exit"); - } - } \ No newline at end of file diff --git a/src/main/java/cod/interpreter/exception/EarlyFinException.java b/src/main/java/cod/interpreter/exception/EarlyFinException.java new file mode 100644 index 00000000..eab38d2f --- /dev/null +++ b/src/main/java/cod/interpreter/exception/EarlyFinException.java @@ -0,0 +1,8 @@ +package cod.interpreter.exception; + +@SuppressWarnings("serial") +public class EarlyFinException extends RuntimeException { + public EarlyFinException() { + super("Early fin"); + } + } diff --git a/src/main/java/cod/interpreter/handler/LambdaHandler.java b/src/main/java/cod/interpreter/handler/LambdaHandler.java index 00f5cc70..3a0881bf 100644 --- a/src/main/java/cod/interpreter/handler/LambdaHandler.java +++ b/src/main/java/cod/interpreter/handler/LambdaHandler.java @@ -316,8 +316,8 @@ private Object evaluateLambdaBlockBody( if (lambda.body != null) { dispatcher.visit((Base) lambda.body); } - } catch (cod.interpreter.exception.EarlyExitException e) { - // normal lambda early exit + } catch (cod.interpreter.exception.EarlyFinException e) { + // normal lambda early fin } finally { dispatcher.popContext(); } diff --git a/src/main/java/cod/ir/DeserializationVisitor.java b/src/main/java/cod/ir/DeserializationVisitor.java index 40dbe69d..21eab89f 100644 --- a/src/main/java/cod/ir/DeserializationVisitor.java +++ b/src/main/java/cod/ir/DeserializationVisitor.java @@ -74,7 +74,7 @@ private static Base instantiateNode(String nodeName, Map values) if ("Skip".equals(nodeName)) return new Skip(); if ("Break".equals(nodeName)) return new Break(); if ("Range".equals(nodeName)) return new Range(); - if ("Exit".equals(nodeName)) return new Exit(); + if ("Fin".equals(nodeName)) return new Fin(); if ("Tuple".equals(nodeName)) return new Tuple(); if ("ReturnSlotAssignment".equals(nodeName)) return new ReturnSlotAssignment(); if ("SlotDeclaration".equals(nodeName)) return new SlotDeclaration(); diff --git a/src/main/java/cod/ir/SerializationVisitor.java b/src/main/java/cod/ir/SerializationVisitor.java index 751e2c26..3b984469 100644 --- a/src/main/java/cod/ir/SerializationVisitor.java +++ b/src/main/java/cod/ir/SerializationVisitor.java @@ -240,8 +240,8 @@ public Void visit(Range n) { } @Override - public Void visit(Exit n) { - writeNodeStart("Exit", 0); + public Void visit(Fin n) { + writeNodeStart("Fin", 0); return null; } diff --git a/src/main/java/cod/lexer/IdentifierLexer.java b/src/main/java/cod/lexer/IdentifierLexer.java index e47f964c..34bcaea0 100644 --- a/src/main/java/cod/lexer/IdentifierLexer.java +++ b/src/main/java/cod/lexer/IdentifierLexer.java @@ -86,14 +86,6 @@ private Token readIdentifierOrKeyword() { } } - if (matchesExactly(input, startPos, length, "continue")) { - return Token.createKeyword(input, startPos, length, startLine, startCol, Keyword.SKIP); - } - - if (matchesExactly(input, startPos, length, "return")) { - return Token.createKeyword(input, startPos, length, startLine, startCol, Keyword.EXIT); - } - if (extractionMode) extractedIdentifiers.add(new String(input, startPos, length)); return Token.createIdentifier(input, startPos, length, startLine, startCol); } diff --git a/src/main/java/cod/lexer/TokenType.java b/src/main/java/cod/lexer/TokenType.java index 4f2e0b0c..31b7d898 100644 --- a/src/main/java/cod/lexer/TokenType.java +++ b/src/main/java/cod/lexer/TokenType.java @@ -52,7 +52,7 @@ public enum Keyword { BUILTIN, ALL, ANY, - EXIT, + FIN, NONE, TRUE, FALSE, diff --git a/src/main/java/cod/parser/BaseParser.java b/src/main/java/cod/parser/BaseParser.java index a824cf94..3b69775b 100644 --- a/src/main/java/cod/parser/BaseParser.java +++ b/src/main/java/cod/parser/BaseParser.java @@ -274,7 +274,7 @@ protected boolean isClassStart() { protected boolean isStmtStart() { Token token = now(); if (token == null) return false; - if (is(token, IF, FOR, EXIT, SKIP, BREAK)) return true; + if (is(token, IF, FOR, FIN, SKIP, BREAK)) return true; if (is(token, TILDE_ARROW)) return true; if (is(token, ID)) { diff --git a/src/main/java/cod/parser/StatementParser.java b/src/main/java/cod/parser/StatementParser.java index 7152d2a6..e6a289dc 100644 --- a/src/main/java/cod/parser/StatementParser.java +++ b/src/main/java/cod/parser/StatementParser.java @@ -47,7 +47,7 @@ private Stmt parseStmtInternal() { if (match(IF)) return parseIfStmt(); if (match(FOR)) return parseForStmt(); - if (match(EXIT)) return parseExitStmt(); + if (match(FIN)) return parseFinStmt(); if (match(SKIP)) return parseSkipStmt(); if (match(BREAK)) return parseBreakStmt(); @@ -97,7 +97,7 @@ private void checkIllegalDeclaration() { Token current = now(); if (current.type != KEYWORD) return; - if (is(current, IF, ELSE, ELIF, FOR, EXIT, SKIP, BREAK)) return; + if (is(current, IF, ELSE, ELIF, FOR, FIN, SKIP, BREAK)) return; if (isTypeStart(current)) return; if (match(ANY, ID, ASSIGN) || match(ANY, ID, LBRACKET, RBRACKET)) { @@ -236,7 +236,7 @@ private void parseControlFlowBlock(Block block) { } private boolean isInControlFlow(Token token) { - return is(token, IF, FOR, ELSE, ELIF, EXIT, SKIP, BREAK); + return is(token, IF, FOR, ELSE, ELIF, FIN, SKIP, BREAK); } private Stmt parseForStmt() { @@ -315,9 +315,9 @@ private Stmt parseForLoopBody(For forNode) { return forNode; } - private Stmt parseExitStmt() { - Token exitToken = expect(EXIT); - return ASTFactory.createExit(exitToken); + private Stmt parseFinStmt() { + Token finToken = expect(FIN); + return ASTFactory.createFin(finToken); } private Stmt parseReturnSlotAssignment() { diff --git a/src/main/java/cod/parser/context/TokenSkipper.java b/src/main/java/cod/parser/context/TokenSkipper.java index bbfc32f3..118df36f 100644 --- a/src/main/java/cod/parser/context/TokenSkipper.java +++ b/src/main/java/cod/parser/context/TokenSkipper.java @@ -69,7 +69,7 @@ public void untilStmtEnd() { while (!is(EOF)) { Token t = now(); - if (any(is(t, RBRACE), is(t, ELSE, ELIF, IF, FOR, EXIT))) { + if (any(is(t, RBRACE), is(t, ELSE, ELIF, IF, FOR, FIN))) { break; } @@ -126,7 +126,7 @@ public void expr() { if (braceDepth == 0 && parenDepth == 0 && bracketDepth == 0) { if (is(t, COMMA)) return; - if (is(t, IF, FOR, EXIT, ELSE, ELIF, SHARE, LOCAL, UNIT)) return; + if (is(t, IF, FOR, FIN, ELSE, ELIF, SHARE, LOCAL, UNIT)) return; } consume(); @@ -191,7 +191,7 @@ public void slotAsmt() { private boolean isStmtEnd() { Token t = now(); - return any(is(t, RBRACE), is(t, ELSE, ELIF, IF, FOR, EXIT), is(t, EOF)); + return any(is(t, RBRACE), is(t, ELSE, ELIF, IF, FOR, FIN), is(t, EOF)); } public void slotContract() { @@ -210,7 +210,7 @@ public void stmt() { ifStmt(); } else if (is(current, FOR)) { forStmt(); - } else if (is(current, EXIT)) { + } else if (is(current, FIN)) { consume(); } else if (is(current, SHARE, LOCAL)) { if (is(current, ID)) { diff --git a/src/main/java/cod/ptac/Lowerer.java b/src/main/java/cod/ptac/Lowerer.java index 47b37717..963ecfda 100644 --- a/src/main/java/cod/ptac/Lowerer.java +++ b/src/main/java/cod/ptac/Lowerer.java @@ -196,7 +196,7 @@ private void lowerStmt(Stmt stmt, Function fn, Unit unit) { return; } - if (stmt instanceof Exit) { + if (stmt instanceof Fin) { fn.instructions.add(new Instruction( Opcode.RETURN, null, diff --git a/src/main/java/cod/runner/CodPTACParityRunner.java b/src/main/java/cod/runner/CodPTACParityRunner.java index 8ac91370..51918496 100644 --- a/src/main/java/cod/runner/CodPTACParityRunner.java +++ b/src/main/java/cod/runner/CodPTACParityRunner.java @@ -341,6 +341,7 @@ private String normalize(String text) { cleaned = cleaned.replaceFirst("(?i)(.*(?:time|elapsed|duration|latency)[^=]*=)" + NUMBER_REGEX_PATTERN + "$", "$1