From ad2d28f8d6443fa4c56e806ecf6a4aef32e9f4c1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 14:37:47 +0000 Subject: [PATCH 01/19] refactor: move Keyword and Symbol into TokenType Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/a70a1008-ea05-437a-b5e6-06da46bd1675 Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- src/main/java/cod/ast/ASTFactory.java | 2 +- src/main/java/cod/ast/ASTPrinter.java | 2 +- src/main/java/cod/ast/node/Field.java | 2 +- src/main/java/cod/ast/node/Method.java | 2 +- src/main/java/cod/ast/node/Policy.java | 2 +- src/main/java/cod/ast/node/Type.java | 2 +- src/main/java/cod/interpreter/Index.java | 4 +- .../java/cod/interpreter/Interpreter.java | 4 +- .../cod/interpreter/InterpreterVisitor.java | 2 +- .../handler/ArrayOperationHandler.java | 2 +- .../cod/interpreter/handler/TypeHandler.java | 2 +- .../java/cod/ir/DeserializationVisitor.java | 8 +- src/main/java/cod/ir/IRCodec.java | 5 +- src/main/java/cod/lexer/IdentifierLexer.java | 2 +- src/main/java/cod/lexer/SymbolLexer.java | 4 +- src/main/java/cod/lexer/Token.java | 4 +- src/main/java/cod/lexer/TokenType.java | 108 +++++++++++++++++- src/main/java/cod/parser/BaseParser.java | 8 +- .../java/cod/parser/DeclarationParser.java | 6 +- .../java/cod/parser/ExpressionParser.java | 4 +- src/main/java/cod/parser/MainParser.java | 4 +- src/main/java/cod/parser/SlotParser.java | 2 +- src/main/java/cod/parser/StatementParser.java | 4 +- .../cod/parser/context/ParserContext.java | 3 +- .../java/cod/parser/context/TokenSkipper.java | 8 +- src/main/java/cod/runner/REPLRunner.java | 2 +- .../cod/semantic/ConstructorResolver.java | 2 +- .../java/cod/semantic/ObjectValidator.java | 5 +- src/main/java/cod/syntax/Keyword.java | 100 ---------------- src/main/java/cod/syntax/Symbol.java | 46 -------- 30 files changed, 157 insertions(+), 194 deletions(-) delete mode 100644 src/main/java/cod/syntax/Keyword.java delete mode 100644 src/main/java/cod/syntax/Symbol.java diff --git a/src/main/java/cod/ast/ASTFactory.java b/src/main/java/cod/ast/ASTFactory.java index a08e7364..85ee25a2 100644 --- a/src/main/java/cod/ast/ASTFactory.java +++ b/src/main/java/cod/ast/ASTFactory.java @@ -1,7 +1,7 @@ package cod.ast; import cod.ast.node.*; -import cod.syntax.Keyword; +import cod.lexer.TokenType.Keyword; import cod.lexer.Token; import java.util.*; import cod.math.AutoStackingNumber; diff --git a/src/main/java/cod/ast/ASTPrinter.java b/src/main/java/cod/ast/ASTPrinter.java index d0757f21..29bb6149 100644 --- a/src/main/java/cod/ast/ASTPrinter.java +++ b/src/main/java/cod/ast/ASTPrinter.java @@ -1,6 +1,6 @@ package cod.ast; -import static cod.syntax.Keyword.*; +import static cod.lexer.TokenType.Keyword.*; import cod.ast.node.*; public class ASTPrinter extends ASTVisitor { diff --git a/src/main/java/cod/ast/node/Field.java b/src/main/java/cod/ast/node/Field.java index fb062a93..a66fa3d6 100644 --- a/src/main/java/cod/ast/node/Field.java +++ b/src/main/java/cod/ast/node/Field.java @@ -1,7 +1,7 @@ package cod.ast.node; import cod.ast.VisitorImpl; -import cod.syntax.Keyword; +import cod.lexer.TokenType.Keyword; public class Field extends Stmt { public String name; diff --git a/src/main/java/cod/ast/node/Method.java b/src/main/java/cod/ast/node/Method.java index 842b5dcb..0151c2bd 100644 --- a/src/main/java/cod/ast/node/Method.java +++ b/src/main/java/cod/ast/node/Method.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import cod.ast.VisitorImpl; -import cod.syntax.Keyword; +import cod.lexer.TokenType.Keyword; public class Method extends Base { public String methodName; diff --git a/src/main/java/cod/ast/node/Policy.java b/src/main/java/cod/ast/node/Policy.java index 4e4a51d8..041dbd90 100644 --- a/src/main/java/cod/ast/node/Policy.java +++ b/src/main/java/cod/ast/node/Policy.java @@ -1,7 +1,7 @@ package cod.ast.node; import cod.ast.VisitorImpl; -import cod.syntax.Keyword; +import cod.lexer.TokenType.Keyword; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/cod/ast/node/Type.java b/src/main/java/cod/ast/node/Type.java index 9d3557c3..10d106f4 100644 --- a/src/main/java/cod/ast/node/Type.java +++ b/src/main/java/cod/ast/node/Type.java @@ -7,7 +7,7 @@ import cod.ast.VisitorImpl; import cod.lexer.Token; -import cod.syntax.Keyword; +import cod.lexer.TokenType.Keyword; public class Type extends Base { public String name; diff --git a/src/main/java/cod/interpreter/Index.java b/src/main/java/cod/interpreter/Index.java index 8156aac6..8ee96bc7 100644 --- a/src/main/java/cod/interpreter/Index.java +++ b/src/main/java/cod/interpreter/Index.java @@ -4,8 +4,8 @@ import cod.ir.IRManager; import cod.lexer.*; import static cod.lexer.TokenType.*; -import static cod.syntax.Symbol.*; -import static cod.syntax.Keyword.*; +import static cod.lexer.TokenType.Symbol.*; +import static cod.lexer.TokenType.Keyword.*; import java.io.*; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/cod/interpreter/Interpreter.java b/src/main/java/cod/interpreter/Interpreter.java index 7f31faf1..0e6f0420 100644 --- a/src/main/java/cod/interpreter/Interpreter.java +++ b/src/main/java/cod/interpreter/Interpreter.java @@ -12,8 +12,8 @@ import cod.parser.MainParser; import cod.semantic.ImportResolver; import cod.semantic.ConstructorResolver; -import static cod.syntax.Keyword.*; -import static cod.syntax.Symbol.*; +import static cod.lexer.TokenType.Keyword.*; +import static cod.lexer.TokenType.Symbol.*; import java.io.File; import java.io.BufferedReader; diff --git a/src/main/java/cod/interpreter/InterpreterVisitor.java b/src/main/java/cod/interpreter/InterpreterVisitor.java index 75a86de1..3e58c817 100644 --- a/src/main/java/cod/interpreter/InterpreterVisitor.java +++ b/src/main/java/cod/interpreter/InterpreterVisitor.java @@ -15,7 +15,7 @@ import cod.interpreter.exception.*; import cod.interpreter.handler.*; import java.util.*; -import static cod.syntax.Keyword.*; +import static cod.lexer.TokenType.Keyword.*; import cod.semantic.ConstructorResolver; import cod.semantic.NamingValidator; diff --git a/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java b/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java index f3422763..4bfff84a 100644 --- a/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java +++ b/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java @@ -208,7 +208,7 @@ public void executeIteration( if (ctx.getVariableType(iter) == null) { String inferredType = (current.fitsInStacks(1) && (current.getWords()[0] & 0x7FFFFFFFFFFFFFFFL) < Long.MAX_VALUE) - ? cod.syntax.Keyword.INT.toString() : cod.syntax.Keyword.FLOAT.toString(); + ? cod.lexer.TokenType.Keyword.INT.toString() : cod.lexer.TokenType.Keyword.FLOAT.toString(); ctx.setVariableType(iter, inferredType); } executeLoopBody(ctx, node); diff --git a/src/main/java/cod/interpreter/handler/TypeHandler.java b/src/main/java/cod/interpreter/handler/TypeHandler.java index 346165ab..276009f3 100644 --- a/src/main/java/cod/interpreter/handler/TypeHandler.java +++ b/src/main/java/cod/interpreter/handler/TypeHandler.java @@ -5,7 +5,7 @@ import cod.error.ProgramError; import cod.math.AutoStackingNumber; import cod.range.NaturalArray; -import static cod.syntax.Keyword.*; +import static cod.lexer.TokenType.Keyword.*; import java.math.BigDecimal; import java.math.BigInteger; diff --git a/src/main/java/cod/ir/DeserializationVisitor.java b/src/main/java/cod/ir/DeserializationVisitor.java index 8ac65cf5..40dbe69d 100644 --- a/src/main/java/cod/ir/DeserializationVisitor.java +++ b/src/main/java/cod/ir/DeserializationVisitor.java @@ -126,7 +126,7 @@ private static void applyNodeFields(Base node, Map values) { if (node instanceof Type) { Type n = (Type) node; if (values.containsKey("name")) n.name = (String) values.get("name"); - if (values.containsKey("visibility")) n.visibility = (cod.syntax.Keyword) values.get("visibility"); + if (values.containsKey("visibility")) n.visibility = (cod.lexer.TokenType.Keyword) values.get("visibility"); if (values.containsKey("extendName")) n.extendName = (String) values.get("extendName"); if (values.containsKey("fields")) n.fields = castList(values.get("fields")); if (values.containsKey("constructor")) n.constructor = (Constructor) values.get("constructor"); @@ -143,7 +143,7 @@ private static void applyNodeFields(Base node, Map values) { Field n = (Field) node; if (values.containsKey("name")) n.name = (String) values.get("name"); if (values.containsKey("type")) n.type = (String) values.get("type"); - if (values.containsKey("visibility")) n.visibility = (cod.syntax.Keyword) values.get("visibility"); + if (values.containsKey("visibility")) n.visibility = (cod.lexer.TokenType.Keyword) values.get("visibility"); if (values.containsKey("value")) n.value = (Expr) values.get("value"); return; } @@ -152,7 +152,7 @@ private static void applyNodeFields(Base node, Map values) { Method n = (Method) node; if (values.containsKey("methodName")) n.methodName = (String) values.get("methodName"); if (values.containsKey("associatedClass")) n.associatedClass = (String) values.get("associatedClass"); - if (values.containsKey("visibility")) n.visibility = (cod.syntax.Keyword) values.get("visibility"); + if (values.containsKey("visibility")) n.visibility = (cod.lexer.TokenType.Keyword) values.get("visibility"); if (values.containsKey("returnSlots")) n.returnSlots = castList(values.get("returnSlots")); if (values.containsKey("parameters")) n.parameters = castList(values.get("parameters")); if (values.containsKey("body")) n.body = castList(values.get("body")); @@ -192,7 +192,7 @@ private static void applyNodeFields(Base node, Map values) { if (node instanceof Policy) { Policy n = (Policy) node; if (values.containsKey("name")) n.name = (String) values.get("name"); - if (values.containsKey("visibility")) n.visibility = (cod.syntax.Keyword) values.get("visibility"); + if (values.containsKey("visibility")) n.visibility = (cod.lexer.TokenType.Keyword) values.get("visibility"); if (values.containsKey("methods")) n.methods = castList(values.get("methods")); if (values.containsKey("sourceUnit")) n.sourceUnit = (String) values.get("sourceUnit"); if (values.containsKey("composedPolicies")) n.composedPolicies = castList(values.get("composedPolicies")); diff --git a/src/main/java/cod/ir/IRCodec.java b/src/main/java/cod/ir/IRCodec.java index 0df07cc9..6d1aad10 100644 --- a/src/main/java/cod/ir/IRCodec.java +++ b/src/main/java/cod/ir/IRCodec.java @@ -3,7 +3,7 @@ import cod.ast.node.*; import cod.math.AutoStackingNumber; import cod.parser.MainParser.ProgramType; -import cod.syntax.Keyword; +import cod.lexer.TokenType.Keyword; import java.io.DataInput; import java.io.DataOutput; @@ -234,7 +234,8 @@ private static Enum readEnum(DataInput in) throws IOException { String enumClassName = readString(in); String enumName = readString(in); - if (Keyword.class.getName().equals(enumClassName)) { + if (Keyword.class.getName().equals(enumClassName) + || "cod.syntax.Keyword".equals(enumClassName)) { try { return Keyword.valueOf(enumName); } catch (IllegalArgumentException e) { diff --git a/src/main/java/cod/lexer/IdentifierLexer.java b/src/main/java/cod/lexer/IdentifierLexer.java index 58fe9b4e..08be91ee 100644 --- a/src/main/java/cod/lexer/IdentifierLexer.java +++ b/src/main/java/cod/lexer/IdentifierLexer.java @@ -1,6 +1,6 @@ package cod.lexer; -import cod.syntax.Keyword; +import cod.lexer.TokenType.Keyword; import cod.error.LexError; import java.util.*; diff --git a/src/main/java/cod/lexer/SymbolLexer.java b/src/main/java/cod/lexer/SymbolLexer.java index 252f5770..d1057a70 100644 --- a/src/main/java/cod/lexer/SymbolLexer.java +++ b/src/main/java/cod/lexer/SymbolLexer.java @@ -1,7 +1,7 @@ package cod.lexer; -import cod.syntax.Symbol; -import static cod.syntax.Symbol.*; +import cod.lexer.TokenType.Symbol; +import static cod.lexer.TokenType.Symbol.*; import java.util.*; public class SymbolLexer { diff --git a/src/main/java/cod/lexer/Token.java b/src/main/java/cod/lexer/Token.java index 168ebe4e..c255a128 100644 --- a/src/main/java/cod/lexer/Token.java +++ b/src/main/java/cod/lexer/Token.java @@ -1,7 +1,7 @@ package cod.lexer; -import cod.syntax.Symbol; -import cod.syntax.Keyword; +import cod.lexer.TokenType.Symbol; +import cod.lexer.TokenType.Keyword; import java.util.List; import java.util.ArrayList; diff --git a/src/main/java/cod/lexer/TokenType.java b/src/main/java/cod/lexer/TokenType.java index b31a1e23..931304c7 100644 --- a/src/main/java/cod/lexer/TokenType.java +++ b/src/main/java/cod/lexer/TokenType.java @@ -1,5 +1,8 @@ package cod.lexer; +import java.util.HashMap; +import java.util.Map; + public enum TokenType { KEYWORD, INT_LIT, @@ -19,4 +22,107 @@ public enum TokenType { public String toString() { return name().toLowerCase(); } - } \ No newline at end of file + + public enum Keyword { + SHARE, + LOCAL, + UNIT, + USE, + IS, + THIS, + SUPER, + IF, + ELSE, + ELIF, + OF, + FOR, + BREAK, + SKIP, + TO, + BY, + INT, + TEXT, + FLOAT, + BOOL, + TYPE, + POLICY, + WITH, + BUILTIN, + ALL, + ANY, + EXIT, + NONE, + TRUE, + FALSE, + GET, + SET, + UNSAFE, + I8, + I16, + I32, + I64, + U8, + U16, + U32, + U64, + F32, + F64; + + private static final Map STRING_TO_KEYWORD = new HashMap(); + + static { + for (Keyword keyword : values()) { + STRING_TO_KEYWORD.put(keyword.toString(), keyword); + } + } + + public static Keyword fromString(String text) { + return STRING_TO_KEYWORD.get(text.toLowerCase()); + } + + @Override + public String toString() { + return name().toLowerCase(); + } + } + + public enum Symbol { + EQ, + ASSIGN, + GT, + GTE, + LT, + LTE, + NEQ, + BANG, + PLUS, + PLUS_ASSIGN, + MINUS, + MINUS_ASSIGN, + MUL, + MUL_ASSIGN, + DIV, + DIV_ASSIGN, + LAMBDA, + MOD, + DOUBLE_COLON, + DOUBLE_COLON_ASSIGN, + TILDE_ARROW, + COLON, + DOT, + COMMA, + LPAREN, + RPAREN, + LBRACE, + RBRACE, + LBRACKET, + RBRACKET, + PIPE, + QUESTION, + AMPERSAND, + DOLLAR, + UNDERSCORE, + RANGE_DOTDOT, + RANGE_HASH + } +} diff --git a/src/main/java/cod/parser/BaseParser.java b/src/main/java/cod/parser/BaseParser.java index a55f53bf..b8deb690 100644 --- a/src/main/java/cod/parser/BaseParser.java +++ b/src/main/java/cod/parser/BaseParser.java @@ -6,10 +6,10 @@ import cod.lexer.TokenType; import static cod.lexer.TokenType.*; import cod.parser.context.*; -import cod.syntax.Keyword; -import static cod.syntax.Keyword.*; -import cod.syntax.Symbol; -import static cod.syntax.Symbol.*; +import cod.lexer.TokenType.Keyword; +import static cod.lexer.TokenType.Keyword.*; +import cod.lexer.TokenType.Symbol; +import static cod.lexer.TokenType.Symbol.*; import cod.semantic.ObjectValidator; import java.util.List; diff --git a/src/main/java/cod/parser/DeclarationParser.java b/src/main/java/cod/parser/DeclarationParser.java index e3749a81..2a676071 100644 --- a/src/main/java/cod/parser/DeclarationParser.java +++ b/src/main/java/cod/parser/DeclarationParser.java @@ -8,13 +8,13 @@ import cod.semantic.NamingValidator; import cod.semantic.PolicyValidator; import cod.semantic.ReturnContractValidator; -import cod.syntax.Keyword; +import cod.lexer.TokenType.Keyword; import java.util.*; import cod.lexer.Token; import static cod.lexer.TokenType.*; -import static cod.syntax.Keyword.*; -import static cod.syntax.Symbol.*; +import static cod.lexer.TokenType.Keyword.*; +import static cod.lexer.TokenType.Symbol.*; public class DeclarationParser extends BaseParser { diff --git a/src/main/java/cod/parser/ExpressionParser.java b/src/main/java/cod/parser/ExpressionParser.java index 56241a58..ee5043fa 100644 --- a/src/main/java/cod/parser/ExpressionParser.java +++ b/src/main/java/cod/parser/ExpressionParser.java @@ -9,8 +9,8 @@ import static cod.lexer.TokenType.*; import cod.math.AutoStackingNumber; import cod.parser.context.*; -import static cod.syntax.Symbol.*; -import static cod.syntax.Keyword.*; +import static cod.lexer.TokenType.Symbol.*; +import static cod.lexer.TokenType.Keyword.*; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/cod/parser/MainParser.java b/src/main/java/cod/parser/MainParser.java index 49bf90bd..c4f8044d 100644 --- a/src/main/java/cod/parser/MainParser.java +++ b/src/main/java/cod/parser/MainParser.java @@ -11,8 +11,8 @@ import cod.lexer.Token; import static cod.lexer.TokenType.*; import cod.parser.context.*; -import static cod.syntax.Keyword.*; -import static cod.syntax.Symbol.*; +import static cod.lexer.TokenType.Keyword.*; +import static cod.lexer.TokenType.Symbol.*; public class MainParser extends BaseParser { private static final String DEFAULT_UNIT_NAME = "default"; diff --git a/src/main/java/cod/parser/SlotParser.java b/src/main/java/cod/parser/SlotParser.java index 8ab3182f..7f898761 100644 --- a/src/main/java/cod/parser/SlotParser.java +++ b/src/main/java/cod/parser/SlotParser.java @@ -4,7 +4,7 @@ import cod.ast.node.*; import cod.lexer.Token; import static cod.lexer.TokenType.*; -import static cod.syntax.Symbol.*; +import static cod.lexer.TokenType.Symbol.*; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/cod/parser/StatementParser.java b/src/main/java/cod/parser/StatementParser.java index 7ed38132..6f2c1b13 100644 --- a/src/main/java/cod/parser/StatementParser.java +++ b/src/main/java/cod/parser/StatementParser.java @@ -12,8 +12,8 @@ import static cod.lexer.TokenType.*; import cod.parser.context.*; -import static cod.syntax.Symbol.*; -import static cod.syntax.Keyword.*; +import static cod.lexer.TokenType.Symbol.*; +import static cod.lexer.TokenType.Keyword.*; public class StatementParser extends BaseParser { diff --git a/src/main/java/cod/parser/context/ParserContext.java b/src/main/java/cod/parser/context/ParserContext.java index b5a86ac4..14f9ed60 100644 --- a/src/main/java/cod/parser/context/ParserContext.java +++ b/src/main/java/cod/parser/context/ParserContext.java @@ -2,8 +2,9 @@ import cod.lexer.Token; import cod.lexer.TokenType; +import cod.lexer.TokenType.Keyword; +import cod.lexer.TokenType.Symbol; import cod.error.ParseError; -import cod.syntax.*; import static cod.semantic.ObjectValidator.is; import static cod.semantic.ObjectValidator.nil; diff --git a/src/main/java/cod/parser/context/TokenSkipper.java b/src/main/java/cod/parser/context/TokenSkipper.java index 205eba57..d7a1d79a 100644 --- a/src/main/java/cod/parser/context/TokenSkipper.java +++ b/src/main/java/cod/parser/context/TokenSkipper.java @@ -4,10 +4,10 @@ import cod.lexer.TokenType; import static cod.lexer.TokenType.*; import cod.parser.context.*; -import cod.syntax.Keyword; -import static cod.syntax.Keyword.*; -import cod.syntax.Symbol; -import static cod.syntax.Symbol.*; +import cod.lexer.TokenType.Keyword; +import static cod.lexer.TokenType.Keyword.*; +import cod.lexer.TokenType.Symbol; +import static cod.lexer.TokenType.Symbol.*; import cod.semantic.ObjectValidator; import java.util.List; diff --git a/src/main/java/cod/runner/REPLRunner.java b/src/main/java/cod/runner/REPLRunner.java index 8c00fda9..e62fc19d 100644 --- a/src/main/java/cod/runner/REPLRunner.java +++ b/src/main/java/cod/runner/REPLRunner.java @@ -18,7 +18,7 @@ import java.util.Map; import java.util.Scanner; -import cod.syntax.Keyword; +import cod.lexer.TokenType.Keyword; public class REPLRunner { diff --git a/src/main/java/cod/semantic/ConstructorResolver.java b/src/main/java/cod/semantic/ConstructorResolver.java index ab94a92d..630d5c3a 100644 --- a/src/main/java/cod/semantic/ConstructorResolver.java +++ b/src/main/java/cod/semantic/ConstructorResolver.java @@ -8,7 +8,7 @@ import cod.interpreter.*; import cod.interpreter.context.*; import cod.interpreter.handler.*; -import static cod.syntax.Keyword.*; +import static cod.lexer.TokenType.Keyword.*; import java.util.*; diff --git a/src/main/java/cod/semantic/ObjectValidator.java b/src/main/java/cod/semantic/ObjectValidator.java index 6510e7e2..9f5cd970 100644 --- a/src/main/java/cod/semantic/ObjectValidator.java +++ b/src/main/java/cod/semantic/ObjectValidator.java @@ -1,7 +1,8 @@ package cod.semantic; import cod.lexer.*; -import cod.syntax.*; +import cod.lexer.TokenType.Keyword; +import cod.lexer.TokenType.Symbol; public final class ObjectValidator { private ObjectValidator() {} @@ -57,4 +58,4 @@ public static boolean nil(Object... objects) { } return false; } -} \ No newline at end of file +} diff --git a/src/main/java/cod/syntax/Keyword.java b/src/main/java/cod/syntax/Keyword.java deleted file mode 100644 index fb1f297f..00000000 --- a/src/main/java/cod/syntax/Keyword.java +++ /dev/null @@ -1,100 +0,0 @@ -package cod.syntax; - -import java.util.HashMap; -import java.util.Map; - -/** - * This enum contains all of the keywords for the Coderive language. - */ -public enum Keyword { - // Visibility modifiers - SHARE, - LOCAL, - - // namespace - UNIT, - - // import - USE, - - // extends & instanceof - IS, - - THIS, - SUPER, - - IF, - ELSE, - ELIF, - - OF, - - // Loop - FOR, - BREAK, - SKIP, /* Equivalent of CONTINUE */ - - // Used in loop and natural arrays - TO, - BY, - - // Primitive types - INT, - TEXT, - FLOAT, - BOOL, - - // metaprimitive type - TYPE, - - // for policy classes declaration. - POLICY, - WITH, - - BUILTIN, - - ALL, - ANY, - - EXIT, - - NONE, - TRUE, - FALSE, - - // For more control and auto wrappings of variables - GET, - SET, - - // Toggle strictly after visibility modifier to be able to use unsafe primitives and do low level operations - UNSAFE, - - // Unsafe primitives (for low-level operations) - I8, - I16, - I32, - I64, - U8, - U16, - U32, - U64, - F32, - F64; - - private static final Map STRING_TO_KEYWORD = new HashMap<>(); - - static { - for (Keyword keyword : values()) { - STRING_TO_KEYWORD.put(keyword.toString(), keyword); - } - } - - public static Keyword fromString(String text) { - return STRING_TO_KEYWORD.get(text.toLowerCase()); - } - - @Override - public String toString() { - return name().toLowerCase(); - } -} diff --git a/src/main/java/cod/syntax/Symbol.java b/src/main/java/cod/syntax/Symbol.java deleted file mode 100644 index 4bf6b07b..00000000 --- a/src/main/java/cod/syntax/Symbol.java +++ /dev/null @@ -1,46 +0,0 @@ -package cod.syntax; - -public enum Symbol { - EQ, - ASSIGN, - GT, - GTE, - LT, - LTE, - NEQ, - BANG, - - PLUS, - PLUS_ASSIGN, - MINUS, - MINUS_ASSIGN, - MUL, - MUL_ASSIGN, - DIV, - DIV_ASSIGN, - - LAMBDA, - - MOD, - DOUBLE_COLON, - DOUBLE_COLON_ASSIGN, - TILDE_ARROW, - COLON, - DOT, - COMMA, - LPAREN, - RPAREN, - LBRACE, - RBRACE, - LBRACKET, - RBRACKET, - - PIPE, - QUESTION, - AMPERSAND, - DOLLAR, - UNDERSCORE, - - RANGE_DOTDOT, // ".." for range - RANGE_HASH // "#" for step -} From f3f7d1dba5cb90f777839c69f36486393072024b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 14:45:08 +0000 Subject: [PATCH 02/19] fix: allow get/set keyword method names Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/a70a1008-ea05-437a-b5e6-06da46bd1675 Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- src/main/java/cod/parser/BaseParser.java | 2 +- src/main/java/cod/parser/context/TokenSkipper.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/cod/parser/BaseParser.java b/src/main/java/cod/parser/BaseParser.java index b8deb690..88b23e76 100644 --- a/src/main/java/cod/parser/BaseParser.java +++ b/src/main/java/cod/parser/BaseParser.java @@ -319,7 +319,7 @@ protected String parseQualifiedName() { } protected boolean canBeMethod(Token token) { - return is(token, OF, ALL, ANY); + return is(token, OF, ALL, ANY, GET, SET); } protected String parseTypeReference() { diff --git a/src/main/java/cod/parser/context/TokenSkipper.java b/src/main/java/cod/parser/context/TokenSkipper.java index d7a1d79a..645d56b6 100644 --- a/src/main/java/cod/parser/context/TokenSkipper.java +++ b/src/main/java/cod/parser/context/TokenSkipper.java @@ -376,7 +376,7 @@ else if (braceDepth == 1) { } public boolean canBeMethod(Token t) { - return is(t, OF, ALL, ANY); + return is(t, OF, ALL, ANY, GET, SET); } public boolean isPolicyMethod() { From 4cbb28a137335e7fda4b79c4bf48b05f50f95d18 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:03:13 +0000 Subject: [PATCH 03/19] fix: stabilize std json parsing with current grammar Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/a70a1008-ea05-437a-b5e6-06da46bd1675 Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- src/bin/project.codc | Bin 0 -> 659088 bytes .../json/JsonStandardLibraryComprehensive.cod | 16 +- src/main/cod/std/json/Json.cod | 175 +++++++++--------- src/main/java/cod/parser/BaseParser.java | 2 +- .../java/cod/parser/context/TokenSkipper.java | 2 +- 5 files changed, 101 insertions(+), 94 deletions(-) create mode 100644 src/bin/project.codc diff --git a/src/bin/project.codc b/src/bin/project.codc new file mode 100644 index 0000000000000000000000000000000000000000..ca484181bce3a9b343f8239cdeb132b2922b44b0 GIT binary patch literal 659088 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
k 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 uKb{p6OcB9; (this.kind == 0) } @@ -37,10 +37,10 @@ share JsonValue { share add(item: JsonValue) { if this.kind != 4 { return } idx: int = this.arrayData.size - this.arrayData[idx] = item + arrayData[idx] = item } - share get(index: int) :: JsonValue { + share get(index: int) :: value: JsonValue { if this.kind != 4 { ~> (JsonValue.makeError("value is not an array")) } if index < 0 { ~> (JsonValue.makeError("array index out of bounds")) } if index >= this.arrayData.size { ~> (JsonValue.makeError("array index out of bounds")) } @@ -51,13 +51,13 @@ share JsonValue { if this.kind != 5 { return } for i of 0 to this.objectKeys.size - 1 { if this.objectKeys[i] == key { - this.objectValues[i] = value + objectValues[i] = value return } } idx: int = this.objectKeys.size - this.objectKeys[idx] = key - this.objectValues[idx] = value + objectKeys[idx] = key + objectValues[idx] = value } share has(key: text) :: bool { @@ -68,7 +68,7 @@ share JsonValue { ~> (false) } - share getKey(key: text) :: JsonValue { + share getKey(key: text) :: value: JsonValue { if this.kind != 5 { ~> (JsonValue.makeError("value is not an object")) } for i of 0 to this.objectKeys.size - 1 { if this.objectKeys[i] == key { @@ -85,7 +85,7 @@ share JsonValue { ~> (this.objectKeys[index]) } - share valueAt(index: int) :: JsonValue { + share valueAt(index: int) :: value: JsonValue { if this.kind != 5 { ~> (JsonValue.makeError("value is not an object")) } if index < 0 { ~> (JsonValue.makeError("object index out of bounds")) } if index >= this.objectValues.size { ~> (JsonValue.makeError("object index out of bounds")) } @@ -95,51 +95,58 @@ share JsonValue { share toJson() :: text { ~> (Json.serializeValue(this, false, 0)) } share toJsonPretty() :: text { ~> (Json.serializeValue(this, true, 0)) } - share makeNull() :: JsonValue { + share setBoolData(value: bool) { boolData = value } + share setTextData(value: text) { textData = value } + share setArrayData(value: []) { arrayData = value } + share setObjectKeys(value: [text]) { objectKeys = value } + share setObjectValues(value: []) { objectValues = value } + share setErrorData(value: text) { errorData = value } + + share makeNull() :: value: JsonValue { v := JsonValue(0) ~> (v) } - share makeBool(value: bool) :: JsonValue { + share makeBool(value: bool) :: value: JsonValue { v := JsonValue(1) - v.boolData = value + v.setBoolData(value) ~> (v) } - share makeNumber(textValue: text) :: JsonValue { + share makeNumber(textValue: text) :: value: JsonValue { v := JsonValue(2) - v.textData = textValue + v.setTextData(textValue) ~> (v) } - share makeText(textValue: text) :: JsonValue { + share makeText(textValue: text) :: value: JsonValue { v := JsonValue(3) - v.textData = textValue + v.setTextData(textValue) ~> (v) } - share makeArray(items: []) :: JsonValue { + share makeArray(items: []) :: value: JsonValue { v := JsonValue(4) - v.arrayData = items + v.setArrayData(items) ~> (v) } - share makeObject(keys: [text], values: []) :: JsonValue { + share makeObject(keys: [text], values: []) :: value: JsonValue { v := JsonValue(5) - v.objectKeys = keys - v.objectValues = values + v.setObjectKeys(keys) + v.setObjectValues(values) ~> (v) } - share makeError(message: text) :: JsonValue { + share makeError(message: text) :: value: JsonValue { v := JsonValue(6) - v.errorData = message + v.setErrorData(message) ~> (v) } } share Json { - share parse(source: text) :: JsonValue { + share parse(source: text) :: value: JsonValue { parser := JsonParser(source) ~> (parser.parseRoot()) } @@ -152,13 +159,13 @@ share Json { ~> (Json.serializeValue(value, true, 0)) } - share nullValue() :: JsonValue { ~> (JsonValue.makeNull()) } - share bool(value: bool) :: JsonValue { ~> (JsonValue.makeBool(value)) } - share number(value: int|float) :: JsonValue { ~> (JsonValue.makeNumber("" + value)) } - share numberText(value: text) :: JsonValue { ~> (JsonValue.makeNumber(value)) } - share text(value: text) :: JsonValue { ~> (JsonValue.makeText(value)) } - share array() :: JsonValue { ~> (JsonValue.makeArray([])) } - share object() :: JsonValue { ~> (JsonValue.makeObject([], [])) } + share nullValue() :: value: JsonValue { ~> (JsonValue.makeNull()) } + share bool(value: bool) :: value: JsonValue { ~> (JsonValue.makeBool(value)) } + share number(value: int|float) :: value: JsonValue { ~> (JsonValue.makeNumber("" + value)) } + share numberText(value: text) :: value: JsonValue { ~> (JsonValue.makeNumber(value)) } + share text(value: text) :: value: JsonValue { ~> (JsonValue.makeText(value)) } + share array() :: value: JsonValue { ~> (JsonValue.makeArray([])) } + share object() :: value: JsonValue { ~> (JsonValue.makeObject([], [])) } share serializeValue(value: JsonValue, pretty: bool, depth: int) :: text { if value.isError() { @@ -213,10 +220,10 @@ share Json { if value.isObject() { count := value.size() - if count == 0 { ~> ("{}") } + if count == 0 { ~> ("\{\}") } if pretty { - outText := "{\n" + outText := "\{\n" for i of 0 to count - 1 { key := value.keyAt(i) item := value.valueAt(i) @@ -226,11 +233,11 @@ share Json { if i < count - 1 { outText = outText + "," } outText = outText + "\n" } - outText = outText + Json.indent(depth) + "}" + outText = outText + Json.indent(depth) + "\}" ~> (outText) } - outText := "{" + outText := "\{" for i of 0 to count - 1 { key := value.keyAt(i) item := value.valueAt(i) @@ -238,7 +245,7 @@ share Json { outText = outText + Json.serializeValue(item, false, depth + 1) if i < count - 1 { outText = outText + "," } } - outText = outText + "}" + outText = outText + "\}" ~> (outText) } @@ -370,12 +377,12 @@ share Json { ~> (Json.hexDigit(d0) + Json.hexDigit(d1) + Json.hexDigit(d2) + Json.hexDigit(d3)) } - share isHighSurrogate(unit: int) :: bool { - ~> (all[unit >= 55296, unit <= 56319]) + share isHighSurrogate(codeUnit: int) :: bool { + ~> (all[codeUnit >= 55296, codeUnit <= 56319]) } - share isLowSurrogate(unit: int) :: bool { - ~> (all[unit >= 56320, unit <= 57343]) + share isLowSurrogate(codeUnit: int) :: bool { + ~> (all[codeUnit >= 56320, codeUnit <= 57343]) } share indent(depth: int) :: text { @@ -392,11 +399,11 @@ share JsonParser { index: int = 0 share this(sourceText: text) { - this.source = sourceText - this.index = 0 + source = sourceText + index = 0 } - share parseRoot() :: JsonValue { + share parseRoot() :: value: JsonValue { this.skipWhitespace() if this.index >= this.source.length { ~> (JsonValue.makeError("empty json input")) @@ -413,7 +420,7 @@ share JsonParser { ~> (value) } - share parseValue() :: JsonValue { + share parseValue() :: value: JsonValue { this.skipWhitespace() if this.index >= this.source.length { ~> (JsonValue.makeError("unexpected end of input")) @@ -426,42 +433,42 @@ share JsonParser { if ch == "f" { ~> (this.parseFalse()) } if ch == "\"" { ~> (this.parseText()) } if ch == "[" { ~> (this.parseArray()) } - if ch == "{" { ~> (this.parseObject()) } + if ch == "\{" { ~> (this.parseObject()) } if any[ch == "-", Json.isDigit(ch)] { ~> (this.parseNumber()) } ~> (JsonValue.makeError("invalid json value at index " + this.index)) } - share parseNull() :: JsonValue { + share parseNull() :: value: JsonValue { if this.matchKeyword("null") { - this.index = this.index + 4 + index = this.index + 4 ~> (JsonValue.makeNull()) } ~> (JsonValue.makeError("invalid token, expected null at index " + this.index)) } - share parseTrue() :: JsonValue { + share parseTrue() :: value: JsonValue { if this.matchKeyword("true") { - this.index = this.index + 4 + index = this.index + 4 ~> (JsonValue.makeBool(true)) } ~> (JsonValue.makeError("invalid token, expected true at index " + this.index)) } - share parseFalse() :: JsonValue { + share parseFalse() :: value: JsonValue { if this.matchKeyword("false") { - this.index = this.index + 5 + index = this.index + 5 ~> (JsonValue.makeBool(false)) } ~> (JsonValue.makeError("invalid token, expected false at index " + this.index)) } - share parseText() :: JsonValue { + share parseText() :: value: JsonValue { if this.source[this.index] != "\"" { ~> (JsonValue.makeError("expected text at index " + this.index)) } - this.index = this.index + 1 + index = this.index + 1 outText := "" for i of this.index to this.source.length { @@ -470,7 +477,7 @@ share JsonParser { } ch := this.source[this.index] - this.index = this.index + 1 + index = this.index + 1 if ch == "\"" { ~> (JsonValue.makeText(outText)) @@ -482,7 +489,7 @@ share JsonParser { } esc := this.source[this.index] - this.index = this.index + 1 + index = this.index + 1 if esc == "\"" { outText = outText + "\"" continue } if esc == "\\" { outText = outText + "\\" continue } @@ -498,7 +505,7 @@ share JsonParser { if firstUnit < 0 { ~> (JsonValue.makeError("invalid unicode escape at index " + this.index)) } - this.index = this.index + 4 + index = this.index + 4 firstText := "\\u" + Json.hex4(firstUnit) if Json.isHighSurrogate(firstUnit) { @@ -508,12 +515,12 @@ share JsonParser { if any[this.source[this.index] != "\\", this.source[this.index + 1] != "u"] { ~> (JsonValue.makeError("expected low surrogate escape at index " + this.index)) } - this.index = this.index + 2 + index = this.index + 2 secondUnit := this.parseHex4At(this.index) if any[secondUnit < 0, !Json.isLowSurrogate(secondUnit)] { ~> (JsonValue.makeError("invalid low surrogate at index " + this.index)) } - this.index = this.index + 4 + index = this.index + 4 outText = outText + firstText + "\\u" + Json.hex4(secondUnit) continue } @@ -535,18 +542,18 @@ share JsonParser { ~> (JsonValue.makeError("unterminated text")) } - share parseNumber() :: JsonValue { + share parseNumber() :: value: JsonValue { start := this.index if this.source[this.index] == "-" { - this.index = this.index + 1 + index = this.index + 1 if this.index >= this.source.length { ~> (JsonValue.makeError("invalid number at index " + start)) } } if this.source[this.index] == "0" { - this.index = this.index + 1 + index = this.index + 1 } else { if !Json.isDigit(this.source[this.index]) { ~> (JsonValue.makeError("invalid number at index " + start)) @@ -554,26 +561,26 @@ share JsonParser { for i of this.index to this.source.length { if this.index >= this.source.length { break } if !Json.isDigit(this.source[this.index]) { break } - this.index = this.index + 1 + index = this.index + 1 } } if all[this.index < this.source.length, this.source[this.index] == "."] { - this.index = this.index + 1 + index = this.index + 1 if any[this.index >= this.source.length, !Json.isDigit(this.source[this.index])] { ~> (JsonValue.makeError("invalid number fraction at index " + start)) } for i of this.index to this.source.length { if this.index >= this.source.length { break } if !Json.isDigit(this.source[this.index]) { break } - this.index = this.index + 1 + index = this.index + 1 } } if all[this.index < this.source.length, any[this.source[this.index] == "e", this.source[this.index] == "E"]] { - this.index = this.index + 1 + index = this.index + 1 if all[this.index < this.source.length, any[this.source[this.index] == "+", this.source[this.index] == "-"]] { - this.index = this.index + 1 + index = this.index + 1 } if any[this.index >= this.source.length, !Json.isDigit(this.source[this.index])] { ~> (JsonValue.makeError("invalid number exponent at index " + start)) @@ -581,7 +588,7 @@ share JsonParser { for i of this.index to this.source.length { if this.index >= this.source.length { break } if !Json.isDigit(this.source[this.index]) { break } - this.index = this.index + 1 + index = this.index + 1 } } @@ -589,17 +596,17 @@ share JsonParser { ~> (JsonValue.makeNumber(numberText)) } - share parseArray() :: JsonValue { + share parseArray() :: value: JsonValue { if this.source[this.index] != "[" { ~> (JsonValue.makeError("expected [ at index " + this.index)) } arr := JsonValue.makeArray([]) - this.index = this.index + 1 + index = this.index + 1 this.skipWhitespace() if all[this.index < this.source.length, this.source[this.index] == "]"] { - this.index = this.index + 1 + index = this.index + 1 ~> (arr) } @@ -615,7 +622,7 @@ share JsonParser { ch := this.source[this.index] if ch == "," { - this.index = this.index + 1 + index = this.index + 1 this.skipWhitespace() if all[this.index < this.source.length, this.source[this.index] == "]"] { ~> (JsonValue.makeError("trailing comma in array at index " + this.index)) @@ -624,7 +631,7 @@ share JsonParser { } if ch == "]" { - this.index = this.index + 1 + index = this.index + 1 ~> (arr) } @@ -634,17 +641,17 @@ share JsonParser { ~> (JsonValue.makeError("unterminated array")) } - share parseObject() :: JsonValue { - if this.source[this.index] != "{" { - ~> (JsonValue.makeError("expected { at index " + this.index)) + share parseObject() :: value: JsonValue { + if this.source[this.index] != "\{" { + ~> (JsonValue.makeError("expected \{ at index " + this.index)) } obj := JsonValue.makeObject([], []) - this.index = this.index + 1 + index = this.index + 1 this.skipWhitespace() - if all[this.index < this.source.length, this.source[this.index] == "}"] { - this.index = this.index + 1 + if all[this.index < this.source.length, this.source[this.index] == "\}"] { + index = this.index + 1 ~> (obj) } @@ -658,7 +665,7 @@ share JsonParser { if any[this.index >= this.source.length, this.source[this.index] != ":"] { ~> (JsonValue.makeError("expected : after object key at index " + this.index)) } - this.index = this.index + 1 + index = this.index + 1 value := this.parseValue() if value.isError() { ~> (value) } @@ -671,20 +678,20 @@ share JsonParser { ch := this.source[this.index] if ch == "," { - this.index = this.index + 1 + index = this.index + 1 this.skipWhitespace() - if all[this.index < this.source.length, this.source[this.index] == "}"] { + if all[this.index < this.source.length, this.source[this.index] == "\}"] { ~> (JsonValue.makeError("trailing comma in object at index " + this.index)) } continue } - if ch == "}" { - this.index = this.index + 1 + if ch == "\}" { + index = this.index + 1 ~> (obj) } - ~> (JsonValue.makeError("expected , or } in object at index " + this.index)) + ~> (JsonValue.makeError("expected , or \} in object at index " + this.index)) } ~> (JsonValue.makeError("unterminated object")) @@ -695,7 +702,7 @@ share JsonParser { if this.index >= this.source.length { return } ch := this.source[this.index] if any[ch == " ", ch == "\n", ch == "\r", ch == "\t"] { - this.index = this.index + 1 + index = this.index + 1 continue } return diff --git a/src/main/java/cod/parser/BaseParser.java b/src/main/java/cod/parser/BaseParser.java index 88b23e76..dfaf3b48 100644 --- a/src/main/java/cod/parser/BaseParser.java +++ b/src/main/java/cod/parser/BaseParser.java @@ -319,7 +319,7 @@ protected String parseQualifiedName() { } protected boolean canBeMethod(Token token) { - return is(token, OF, ALL, ANY, GET, SET); + return is(token, OF, ALL, ANY, GET, SET, INT, TEXT, FLOAT, BOOL, TYPE); } protected String parseTypeReference() { diff --git a/src/main/java/cod/parser/context/TokenSkipper.java b/src/main/java/cod/parser/context/TokenSkipper.java index 645d56b6..7890bd8a 100644 --- a/src/main/java/cod/parser/context/TokenSkipper.java +++ b/src/main/java/cod/parser/context/TokenSkipper.java @@ -376,7 +376,7 @@ else if (braceDepth == 1) { } public boolean canBeMethod(Token t) { - return is(t, OF, ALL, ANY, GET, SET); + return is(t, OF, ALL, ANY, GET, SET, INT, TEXT, FLOAT, BOOL, TYPE); } public boolean isPolicyMethod() { From 3c0d61b8c042044e721c29407f2670b59e984f19 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:04:13 +0000 Subject: [PATCH 04/19] chore: drop generated src/bin/project.codc Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/a70a1008-ea05-437a-b5e6-06da46bd1675 Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- src/bin/project.codc | Bin 659088 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/bin/project.codc diff --git a/src/bin/project.codc b/src/bin/project.codc deleted file mode 100644 index ca484181bce3a9b343f8239cdeb132b2922b44b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 659088 zcmeFac|4Tg`#(O5WoGPTFCwC}s8rgdB$aH57E(w`wu&TL5h+WX3PrLNvTKtfA(3RQ zltK|L+9<;BoL=wW=Zl$}nZelZKk6~=`<}Vaxt8a(oaR^kH{jk!UW ziJQP5yh__z;`Np0ewnqcrA3%+a@Y(jaFvN{7GDGX$|THZs=%AF21M0}v-7?>wFFLu ze{Xp2DTb8`>JXMI&QSsv@aw{ZDNhjNT+?<|zK*>fJjEPn<*NFPSkxqc)Mv&tC>nyc zOZ2W(s~l}Rutcvu+VMifW;NCNMjd}}3rqGzQmkpr>SNfzSa>&xo+)_+j3R2&t$gRFFJZ}-_myQlLSv0%;PLWEHl|;T9|%*6+Wj7 z<&=pe{n?7@$tdda%;)(zH41!DC}*kfVgnNifk#Vy?Z&9eD_0s{GTUsbuom3Ik#Pz8 zVX?R8X7S+Dt)!|}hjppNmrD!ULN@WjQbwCuvC4S(EZ zUZlexTi5Dl+4OS0CHPDr>fa9_Huy+E{lZ);YO@bWva$gm*+|&JChjv4Z35#8>EdgL zgY)dH1+mM)Z;>YD>s-|*Ec9PoK{m+@N8bpJ(>ku;F_uc{X8WT2;Wp8|^ZzIuj>+}~ zGhpus%YMJk%Tn-fLe3AesSa;`I<rVV%XB#ub-B1aDhK~EKTCyrsmE$VQdl~^`m}Tt`oS3BQvU9OcT;Nef6b74k2FpCWyXUao6y^gyQ}?H}Ug13F|qo_P-3Vrt$LbN{~q#VRWQSV=g( z4O|sv{$Oc-de-VAgb$WeUh}Pez?=P7!k;7KQFq6O5U0HK2Is zs-fbQF;?-g9IN=GX;-QK+Y7{RDDBi`DQ1K2iR`F_VJ}9h9IyW1WCPf2JRPP2? z?}qid0Hx{y#tL+$ZsqcAVz1$P6T9^dZ9chsb7Bl`ZQ~tPV%>eEbOSODU*1F!a_4pg8AdDRyc=sqo{2HCB7ZC|1>0|+$p+Qktfu_!>Jeh1z4 z2owv57>m#v^?pJhgl;SZ0?nkAZQ?Sj=Je-<#;|6CkC`+1ujUe; z+XT+!n^|wDv}oakV#{60=q9jccMm zOX)&x$gK9r;0`=>M3E?-Sz~@&XfZ6F;uY6+j$k<-H-lH(`P0hqNLz7O3yU>B7@8j0 z$PxrTlRoV}RT27Fl-OWDP?cFj!bbOH5cJk(Q7i%`ufwAE=euJaXH4c+)iA+#P#RpF zO2WJeH7e3IN;W$^r}GMEPMguE1c7uWRn)ACZ3@*;|FOFRWv31nr*vP*&= z`IY{h2yQZI9ZqZ|s}85QZOzR~SkEVP-D2=9c7vH2U&h=2DKc76SdV!zIsa>!0H+tY z!S&w8?2KoWdi1;f$|)|lv~U>7MQ)^^V^(lm5Ibp1Q;SN2ff~Dbbt?`NCTGfzeaI98 z?(k$f_-xsS)yix$i{G-3W%Gkd97g7i{^skA`@mhym>q1PmDA_nH8o>%EuTF5`kLK5 zm-BHLWqjeX=Re`TGz^EGgKL_^SsQ69bN}$U7)mAUsCV3tmFcV*pfdgBHWxs8T!;ZV z`t<=12G~2M*H?R#ecNTObIfKiW0t8ogr(og2EtogFg+@Hd8U3HcnkN5dBNL@%MJLC z%?q9o(54BOweV!BEO+3mu-wh8NO&_nq=0OX+|bw`F&KuL!M;20&gWQhdE64f2zIIc z%6qcBi}vz6Dev#Tca^xXZh@QKno0A)gMu5D&)HbBB9M3O@;N+F`vbIeH5UrM)eD5o zB-^awT3DBxecVw7+!R_S@cCm@K-l$_0-r-_uH1jS6q^%z%`2oC+{0;I9+RSw6$inc zSuG<;uYm4s5Jd{Q6nOji9$9_@{(P0}`(Mp;|9m&mB?l3DRkgc$TQS94oD?_qDi|_H zuFX#yXEu%b8QWi&?ZROgo7*a`G8JmM?g-h0_Z|CY2zwm#Y&avs9_rkRe)et1+^u0kBmDl0@Q1Hk)BhpeyYzi+?`H6FckUynoJo&3a)e`DllOuN4MLr1i|?W8|qEmgOt^wdm1akoHxCHJ3J zI6sebhoU+h#5Z}s5n=q7glO2T)rCbsgtZd0B&HNdef&36jIu$#O!~?i*g=FX=DbMK zy2wfRFb`=Q=1>0F#y#5ZaC<7D`KO?GP*e0d97fU|`$7e5`TwPg8jL)8E-GQP7iWc3YqHbtG;0F1Ur|z)Z z1g`!LIb8H5K<>9L27MU>y)a8L#w4c?y+7Z5g$)JNL3X`RKpkZFtN#OqzQ9JmVb_Su zj*Gc zis1Y+{u(bUz%?c|+tlVizRc6XXTtJbzfSn&h&y6qO>02jqD2Z^#A>XuzQvQ^Wtaf| zV1BTuy2OsC{Qkr~sYA7J*_~I^LzE=^`;@hShv*Xh7p~L9kAkcCST;@JaUtwq!Dptx zec4u+FPqNCsK!W(!ymEK$(#G4pW3cwG~$ICb+utLG`x+{+~HiK+7}mzUKwO`p;EVq zH`X*5$l0+AfP2_v@_?=n`LRwdw6+o2w>zm`c>-R-cD-n>2v3YT-|dX?Wc43?s-eV= zN?X8qhnlj$lKH`OuQzu=zHZXmTc<^Om9U#L+5hPW+%T#qK3yjDLYv`2<%6Qd(QrA6qIRYz;D3h@N@L<47jJn}^Pk1nV>b(XYp9hQ| zv>-HN6KS*7^9fJ?dQJsKF>*fxhj1c|o|>=zYBEk&8GAP+97&lvN;xUCfPHurun%xf zs{a>o`f!@eKokg}X|3UM>;anA>V6Oen${ZPCv~7{t^PQ-q?_l}|7k5OaVVKN>_C{g zp)^4T?}K@?!b;q5s>{EQ_>Vtq5ZpgzgoQ<9;V4;bLUD&}KW~NUrln+K7{-+hXnZul zM^6plPMBdAAC2Jpc14|~+(}OL@N74M>Jx(L_9lwz$*yA_$WK@-)Z)QYxMvPp+&*x{ z?P#QnM!Lv{15#cb8MyVOZcE~50mty}i-$(Kqc+m*OI~gmSL6@;`em0AqT!Jg81l|5 zeXU37-~9b-#>#8N1U%fBJ;uT!eg6X){l@La<(9dNc5v1{|T1>)b&-O_-EUNe7}O)ow+!qQMZug-T*|MYYAiL8o;EdPWy zVnMqv1Jz@aH6V8HzKo`yASMM`xvG96(r@aXd=IQ~m7E|Aehbr+SsMsX25s}PSxvRR zQO6(99!s_!K>hLp}*hzi*FL5TMO8GdxyE#g*nG zFJ+S%l5Wur+D!BRvOOahD$!^$I(rYHW(GLL>zMMg$ zs~rIK1qWmey#71!q5t#4^csd2XL42cwKc=z^*HaH_TiBXZ@C(EC2-7~Smcu~7V+3W zua@U+1mEGiW4zhxFN>Yl#XW|ek=$yAf#!OMCtcu)bk^h%`>?UQpL+PwZd>qxJj3<4 z48~@`J8t-%YW|Hm4G6~m!6w1Ay}jM5+3uwG2up=`St%z=K%Yr~b=OkTI}_=4^~ruGUQJBsq3- z*jb(}2)FN0p+MYyFFq?&xdNv`UE1*8Qw-bPo}rJQ|d1$B%`xzTa$m3+By!=FG#yC7P$XL(e}vgKAeN?IIe>u_k{?4g*BOwb!A# zMWb#F@L^R$*K)CZzF)6zCqEX*4Tp^$M%bgo!)&_~&-9C^c6HLO3`gGxj?+4>fJ!Wt z(#`fo`KX4`s9}@~7e|BrGARBi7kW9-8wd|qTWNj6N|$)WmiHYwMkgasCF`VQs0piZ zG%2Nf1q5l0Wi+Umi~B(}qMuk1P%-vkr>fPn+3UWYKUB3H)vr$ag$7_4raozyQ)}o` zA%EQ~K+1@j5_Jx#Z~eI*j0W{8|L|s`X3hf>FmVFcm8)`NqCAs^uxioM?wrg+Crb{} zSe8$5p%X`>`IL@W(}vZ@a4bYXd+Z^yQ4-nrSWJ8!@TCOY9-DHcOVq1^CeoPMr>)qX(Smtv5kfmqRW#NiQ>U)-3U@1OUKs zEiGJ*F!t7s#Nwp+<9fhsdU1|S2WYSQzz*A*j{zwEqn8hZXm8FZ1bijh?}hbHq^p8r zmRG)1{)%EkEzST~ob}&=Ysb~4+y|s4J}L6u5ubi(>Ps@KGs42SQEqi&L7TB)e!y|8 z3?r=$!h$uj)v>^q$K;jeo`hDXr6%;+i<4VhIYr?F~?$E<=@pnIfxRMl4i=?VPlz% z8lC|#JWBgq?`tiYVgo46%(7leGLJ0S8BuG(quk_N119HOH8eS8j5Ik&<%7tvj#gVT zPkiZFFj5TX-aBE%WnXWj*uBi~ zp3=Xn^yLPb)5#3Zu&(r@hawU(P6Z+w=B|*CyMwTd~Q{BcF!?O0zz``RYpXHY}Mxfv#Y7S{ z|G%C_x@OutHPEk2!hEI*pz^;K+5NW-fFl2T=yWjoN@n1q zlHc#aNcc{k3)A^?EpyqaOYv4eB6C)rycGUVP)R)LmxC>&<%g~}Q? zgTx3p>F2L!ej~sS%O1YrIWE5?6MQDbb8cwceE)_7c6_S!8$nJ**z$>Khq|#N4W;MD z3)Y#HYh2+2U**_aFz@_gSF?zG5N%!E4C%~aGlcj&ie*szfFx+Jlr;BH{z=^TPoLv(9c5Twum~(~^@b~fm zrfychX3VyfBsFk>MK-g9Xge26Xf_FD@lj$6(g*1%%E)cGkqgq!JG6xSRqAqT^n zY_a(m>HZ(iw6iRSzm9{yj_WRB*iD?KhCd3NjlfNkprYqJ z!sHcWM5mFt57@et!-siJE5SN4sS2H@#qWM(WEJW(+1+@S>0V&)*6dGu$%&U5?{sb^ zco&{z`5{aBZ4pkTfmh3x!BdWiMMVD(v2bdSFc=iYu*U^U@&{m@vC|X zXM;hamHU3bKiMB$;USBJi#g$E!XGUz>P|5XjWjywBPg|TeH#Q_rlawKoMZ$3HyU!< zo;^aSHGF(M7>?!pS8x6#!^|wQ?{;G!ZL^&vV&NaV%n;fUmV9CEmK-Un=ZftMhcJ|f zd4r^m+Zomp3x>n?Fd&DxZSsnY2N$2>Fn?xWn18b7`!jHZJ@N914y}6a`i5sn(!R=oG;+OyG^?!HS{a%`wGyFW#4TVEOos23-3OY_&paMRk2!|x-EWtKAv@w)UQ4&R;Pkn+C*&_TC42N?4xbA zBUrJbG%Fl3T0g=BG)uOBYy?*%!_}${sMW?es8*Rf21u<4yV7g;^R`}rYL&qjxxg)f zd13)uq;2jgym=uu&G^z`-kq6(_w}CtP+Od_?uJ7j)v8lHA#H*-9M!5b`)Hf(2v)1w z{nI~h$>Nv;1des{L3iFDO+2IZDu)T^)ixMy;ohwoAiauyu42Mj`2H<;$pjbAl?YkUT_qaZ)Ri~OM+5~MFs#O>6(Kg!=tX5bL z+4~$h8!Ujl5%81UZAvs6>=~|Cc%WBXtDs)_=?suwU0PePAw+-Wbnp_X)e~o)R7@S8 zxMbqYY=@wU-#1T@Djn}Vw|&)o>8hQrCLLl2ZL<2PSe@!1XcM*Js92rZN84;iuwqr{ z?6rBY(h~~=j&=JV_Jnc56Bw>nIG|UiP~>q2dIO|axA+b&o)%3qfO@59mV+_kF!!5n zmZQ!y&A3d?=FP_Ci)~*iIaXQ|#3%SDE`BP~N4@H_8c3U@4M)A|%s$#?JA(D9#^!^< zx}ViYWNF|S zsugp$ZkxvTA4aJ++iX`G-kOy5{*N(978R%eZOW?Dtz5oM>@^o49J}=mZ9chsb7Bl` zZR16S>Kpd-%+#ZnwNqe;q|{~=m!;JO%Tw2=xDca1DYva&c}}RcP3PN&&1a{?motTv zw2BQ-q57Xt`TK2i|IPH@aZL`q+Lt;|aZ>$;jZ+B;GL7&QhvMY?{-H*D;oQr+-allC z|30JA=4*-IhwAY~`~MVAh&nUAbn@-=ji@;NPn?46*1Wba(U<`U$GmoNgh(t3PT#O~ z=N2wcPjZ<6h%!O>)3dKKTJN$nK0OQFZdvQD`ynfN@qwlj%Nw(g?Aa7rE|}nk3e*3D zsrxycbjlJZbmS>{PrX(su!5juC(MrHUJg_eZF;fg<)cn(;1)Zn=0)Ps9pb^*mPO*V zaSOwhYvvN0YNy^@{Y`?95lu`G`?hUdbsvKgy0d73BGdQqXukdf#!0;E!?ju7T(#sr z)ywt>WgOI&8g%9ZWdiJoe2rH-yzCMf9&;gc22P8-V&SeTsX=<}3O6`|$AaTEytj%J zB+#a7;nSlo=zTAe20y`Kek4)!RW;QVKqy?GV%#yeGW^*dDY1Z;5>^=po3&rcJ4f5F z=QQ&QXEYFDwG5tn`Q7eg#!{pVQV@<%M1iDZ!!bhfUuAdab4K8Q*8fNN!&j_INxOc8 zdz_V0{AKee?&8DQlm6`g8Xt3fnRDxu=qoS#mm8G$-A~Nu1@QD04hpN>Yn}q-WPO$o z(n}GPncDcTFgR>{`GieS4<~$>sn)clw78>TrrJHuU(eHiDe6RTsCk(1cAEBurI$`6 zy;~?sS>LHH$)Y>EIf#JQjSrM5Km~Gmx~2JAPsGgwlqT74TEp_|*~@mqOxmP%P#~{K zOFsj%bpLoTaH6HOjCDA1K{gL<o`f&xznsCauGXtvwYm6OddN;`r&T$;s0H zddtn+LgqE;D;xmU@|PnV4o9X3lC<{GT5{iacIDqQSqi*GN=;R8!F#SJN~=@_ZLd$s zKeSd0YuvV|V$YHd7`+tr7rXd{m)lV`7F5gKaEuSSF@iD$s4|y#eXXV2#sgB5EJJiy zewDmz=Xpw-`VEq~GyvxED1>fe(Q9BXnHFS6(Khbh%_Sw;Ajv$9=CZe!H)&CrUq8Kj zm$h;%mvjTH(`SjfwRd?PCz+zl9r!9NcQY#zJc3UTDL{HLLWD=HTYSWpaBDWFuON7f zlwF5~=nE&NSjP?vZNK%nFXq3Tm!z-ctJ+iwC_M1t^{;M_EUyO9ymgM_+!byP2SkrX$$Y7FU@HwOn^>h9d7f z_DxTw)}_GP$M?wc6W~$iD%tnHntQUff{KRB5ZzB)IsX;$>@tw=COjCY^TDcsEmetkd~Jn;TUqi{1#g zwOtrNnF3VV+6N#sWrik;RJ0LjhbL>iI$`qgm*pDyswVouJ0r*AoUbczl z*W}XnMmgHVad3cr*@q0wJact0a3ZUsp>_Ce`ON*ajk`BB@}x+ce$u1ffnB~CXws`j zs7bCLk0ZTyhygokMZ%k%O}9^iQ44>5;!27*EwQz(!dpE)d*S5j%2Hx{_C)Ir+SoJ< z!M!W%Xq#?tQrSdF@R6j0MsTl>-zI!vMg`pDkN5Vlqk!&zflSue!26=5Cz0NYAd~%y z?$pNVPmbTXVKd-twJo0d@! zd?e|hQ7DI!v85K`V^{iZulRWA1B7@tZm;je_lu5raC_Zep0(hCpGZI?Pub)=7k2-^ zO~0mGED|exPa8KzaN6(*cBS{(yF4L)a2!uP@EpJ9(AsfeSh%MU$x zU32}YDu;aatFR;w-LdB1bFW>wyhc{;@P!*cDwh%|+gIvbnnjOS3C}U$hKm+>cY7Lb zRR7B#b%WOL$_C0xgVc)$OGhuBqYK=H4XZd|*)3jsrysHm;;`KaiX}@~^(2?6= zvbUu6R68J-*@xlye*UC2(?+da78H!nd}XBr!#=4qO2l7bx_94QRwAa=+ujR4tRNnH z&p0~zoTOMl6aTfpISRyEl;x7TG#to%49j|}u8g2e0jhEztAXb`X(J#tNnkJVd?icU zx6@d+G)SKB8{qkFNCX2XcCY}?_lSI^CuIdilJ(v_UrMw=!eSbojNV?}w2Z?1`sv*l zI4%jd?97G-SK^nSoYNAEslNlMllzKB>QFbQt_+||9qM91M0b!P zoe-NN4w({-6Rc~lU3z&8cHNY^`7tdqM`BOdosVh1-EsCcq1BNepYd(2i~n`)b-)(W z#G7yL$Iv#-!7I_|aB!ZTwIFsmm=|eMzRp#B!b1PW6_hzooecG3jYc=uY`D;L@BBXs zhhwsR0ZrLE!m{75^Rg8Dn~?Kkh)|Wx!KIC^{U1Y6hujE$0aDaJPY2Ig>-mJIe?6xH z7>Aep8HgM;V7o^RJ;eyzNSNPijNk~ohk_0`niGGI(g^s}#t1Xcddj|t`1Aw3MatLZ zVe`sAZ^bXUJWOSI`N7ELcu9=Tljb#kLB$ux-HXvZRe97sTGYudDLaAJfNHW`?OZlA%xiC123unw1B_~->~8%KEJ+(O2 zv}v{7V|A-n5XWxerq}W>o>ZQUn;yM!?DUIKgn9QkIF!z%i76gel~v5T!#TNwvKCXv zoxXG?rX~vV1AhL;e=X1*X!Y}{o?ZOGlB%sP9Y5|kDbrqmBiDdyWi4sNaj=B`32r)(+B$H?gOyaKNVBo|@ z(|}1_xbRUwZ;bOiNr^T{LQkW?7=|VhOPg5qZW1YQaP(4_M)?`RQQ)H$R^osU8+q$+LO{&0hS5b|e|?qXU(5G$Ksx*YkX6_HNuewIj2* z>BH4~rzB4_H%8pM={~FC!@H=+E5}$*`Q829nwVir8S|<3KZfOEQCCJ#rT{fo&*z!1 z^)!39o0TLqc_YiOv}f&GRB3~^!LfQ9EHW^w8euzi;^)}u*5R|KX86)J?%rV8lOk=9 zWS&OU>b*=#^$<`sOSndX-?qL3>9q)|MqO&W|93zr>MZ3>a;k?dzXhsK2&UVcD5@vB zj(H$IVX;t)2T$RiIkfR;C^-kz`O*4a`9|AxdlShfN>Yy`8Z=5|ufek_h_CWY&p?5! zUH2R5tq1~1ZSZ{WkX{JCSKfacP_KSm@NgpA8ON(1ZBH_DJU74AVf<>$*f+cHx9j=M zU0$*4*QY6zk&kNZAJ8Slb0B!Gh0uzVDyETKR8OG4g6FQxr6{%i7d)#v0ZB50D?Rv` zI;}L4hFVFoeSCLe_&XkE@D{0U2i-h2<=ivcdC=`4Z^K=2d%H8wRa>s}Z816hf#~r~ z%~aOakFq3Dr}V=%c<#yx$|Ru5jmQUXgzW-AYm&os;6_}1*3LCN+=!jPjrb0glqeJf z+z5rK8735^A2I0f4YNIcDA5K<=xGG)p=2MaRm{ZiFP47y2oHsdnf>@v#f0m}F6BP{ zbl&p#;fmsiEyosIIQ1;Oq&2}VHM{iE`OjJX%Z{cE6gb{b+-MQS3kREs{RA_^CvTic zRyU!qdJF)c!@mvfE-F8f-ilyCsj~^#XERn_BPQTs(sqo6Mf&~+GWw0%jms@_7xCOR z+3zulvXV0Z=csUb-2i#^E`#KgU;2wi^7OmQOC3qN@su`U9ZKp;?R2~Fh_>mD;8cxm zf%x}xw=`hs8}oPB^x|VZWopQ_`u4PFKL#o+J-M1CO~NXVHEf|aG3^^DFY8VIMZR&D z-u$;j5&vh_?)uw}squgIEqZ)EKIHD#9Tw#`?%j;}vPq)l+O7IKVa}A%hdO8*u8a0v z89|u>)HqpZ^nI;oc*C9uq*H~eEWZ@<+UwG2)4;*W)fj%sz-Y3-O$kJg4RY4u@v;wp z(Kha0oh*v9L9!sj*vX9+>O7dJ)|D51Ks9ZEYOW9*NVTs3sww5QUmgxE z{F@9v0aU}o2K+=`Lx5_DvJZFA#=hQBjS_8;#2!U8(4P!&gHmwU8>J~Sa|9BaTG)#6 zIeGQdJ#Ib-TegQ@WMp)-x*a(5;<#5uTc)Xf!G)LkpK|t^K8^PK`Z>U1lGhvBpq=3Mvzyp)Q)j$rfe~&^KXW0 z>Y^(nC{uu{NI(PF%tNOEsY!3f1DiP^uRUTpZ8|tOk-(bQ85q@bhG5`C?%eU#;YTMv zu%vC=y&<{h8A`N4l6e|!X74$7C10Rw7HUw{W*O4DB+>6Vcj~kWi@CNmY#Nlujk-UOUWx$yXpTn}OC1gLyV99{WcLFt`4wz+A+n24R|dxw zJZ?8rp-mhI3E5u&WcP+!V~G`o0NIaCd=N_8xO+u*inKuz>HbIdlR%S9ouDRh=`|v~ z7D1B+3)$0_G$n<_&JPBo7AaW&=t0?0rUqQbMxm9+6`D5b-w z)y^)wqiwpqiDlCo3X+c`9yCg2uL&_F(APW5$OH#cv$r;!27hmDJD3uJ9_l6lF#XJeW7lH|S}aM2hePa88vaPn~F-0zJCUK$Gky0MsW`f>*K(5VE|Q<2lZ z!<{JQ!KuJF4uElbWf0@8ntVqRVkXKuOdZC(IFqZgudVqhcn9ab(>^?s;VoCAt^|&m z6HED5sTDgHJ?CoPxk<1PUPStp?!u#75;3x`bRF_#7uC=HiL)ycQb>_@r~D4;G9@?! z45IGaeW_mAQ!izJ~+j$x|X&)Iw|h0)o%BXt zCKxbP=LD+0&J(J7`xeC2^dYJ0O1d{=gAZJTb&?|SS;F2=GiA6_vxFZVsa~+|%Ey&w z1538D)?Vn7sv{izhpIZ6V}{jV&_$9^RsWx=hoKhH>7GW>n8r^S4 z)^sdwO^W{|n<$xiD!MCr1&*_h^p+Y2#zr`#{w{N}bKNH-YmcA`)Y$CxGiM$qF3~*A z9eV!ZnSLW`Uv!S*v8kxi)dOZR2U@wRenUu)_Gc?dme2R=_3h-xf_HM*=wXCCN<7TA zJMm1vI3y&5a@ST=L>MX}2E`xcLN6zJ0}=@PHl^1dU-=w!r@5E7&|w5XUc zR7_ml52_LU#EPJ)33jSlJ)6Dm+xbIP+lPdVIJJg874p~p*U%+XqRt`ptv}ZTR6rOi zAmtz4Y}Cwo09WS-Tvx8jjfwJ1LKY7CqI@bm{aDk6)yHrw@c3TrAsR)1+SHp|)!&xi z(|k3+JEW&|tSSxg@nfFQvFcLseJ@p|8bhy(9$p(bzb!p`C%;t8IaK)ZW7>j%;g|jD zLUh_DJA%FQzhX=8mz5>LKA(i}=cx&8Q+N3yYXpta@V`VArEq76=*Uwc*{NDwr7NJ0 z$lcy)yxPF>?Zn$VC*yZI1TP3VFkjQqA(ic>=>#h=iQfm`FLW43o7hm1+UElKz731O z`|&HS2d07rA>%4q{B-qPXT@Xfy!Sr8^lAx1CQ2_)4_j3m1(@ zR|Ul^uY9Te6~)vOc&z^xTsy8VIj`;LTQ(yM9YBtmXL`Ny(f@0znxMN*9 zO{GlWPyDJwi7I8AQjX{>zOg=Cqcufm!ex!hwdKu*CiN>_46pY)fDizHXsieC$4}W$ z>plYN)&>!BVtoM+igG|3X;2Uu86cvC3zBcJWZMTMn80tr>by)4==jx#k1Y(+p_|sV}-j zCIqz61@%8hhX1kHVe(srpkx7>0gc%C$5YCBc-HE?*p**Srj8f`;W@^f-jHkv4nW=4O zzn4hFR$l-7;!(+>pxmUNEiOl=ybruhdB(9C&Vg?Q*l9HI?{iw%3IQ`I%a;eDv$rRsb=@5y-%;C zTGUuqm_EA|BztdCxUuKEkF==?6%fJ|MfIDzz9?6I+)z6$&|&D3VANi>I8Jh=F;w2mcV z+^GeB4t&}VE^rWi_yhGD)yoJ$WBl~+@R7SFSyL_$>gR&btV?|MH>h4?|95@a!#6y~ z<+s2-euQ|=4Q-q6-;ltLPqls{$f*b)VA`Q>>=>)1#vRmA%dwjb(sGKx<1eN zdQ3{ht})pST;Qt7auIdZs10~qm?-45?iU&T`~<^j6ca z%>+0_EHP~9?blCBIJm$cJeDT$Z&P#UoG>zUY@C62matbF)w&~ak&Biv3bE00@^fJf@^p( zBE7XfR6Zx(&mY`msc(L$dXIyQc|`U-7873wd?^9Xj7>Sx@k33RRr2nUj$4FDV@<;S zKSrvGjXmh+(+uGWPZ>VGwrIGd7nc1CKPQH!#THHR2qcEpOkVqXRSU$_+UBZHQ(9?yg5$u{blqs@U_n1HqLo#cJ9j;GhulT;i6H>4cBm@ zFov9jle1pdCi?Br2agHPX8C^U*tG;EMV9ZSkI!EA}8U)Jfv}$Klx`H_h`Fc1~&-JKLy2unxfC)Fakenw&;ARhGNMQcrN_)un3|o5-*qSq(HJx4|Poq3-BSaF_Jv+g+XJFOgBN zzThw{ZFlxo=Ovy8*EwoRgWYDoI%V;y^v*F6%&NP#!M3I;ir|j0dJ&gEHBZ?@yCQ!6 zyjjIk>KQn(Vx^Zdn=mKmak{kF20Q~#5pUPc^r<(|c%O#DBpNIdInY`L8P3TS8pXSJ ztzxcRf)Bs_1{|gj5Z{p%WVA{4KAaUB+YUhlL%t&lU_nun{8677 z)1Y4`XuCx3O0~+-wgXG_>Z2VmL~K@5t#8zUmX!RfU3cKp4!F=gM=_Tgw|d3Y`tg1$ zna~;G%D6J4W67~)GmFp8__6l#S`>LPq>s_dbbCw0k5Y}AMV^h8-gXKo_?9k|QprX(G>BRwAabxZQhA>q+RbEF` zF=+v5rb0eE+l?`PPANN-|{(uN*nY~qY5Zn+p}Fdkx)h3t>! z=;U-KRr_zhPsM*V)|!imP1yd6I%yC2uxP+X)jG=I8Ic@&ni}2$-m-XK4&d4T5(PR& z1Kq_FDVdHjC;h^i?sm_uIH{9ccuUB96n-L^Zc9&4^tA#S?askz(hje(NbT#F{<*)mByFMHk&HI zv$=6(PIQ+8!^(f$k$v~OmK{9FX?8Z=$FTB9r0I!^=QQ4c{&#WutjYRYz;ZbTXU2_< zH4R^V4C|W<{=~l9d;PlZx-sR<;16bp_&tkdP~0B+izA8ivXTN`I|u@%ksNVxe+rYV zN^S5cb~75gN7x$pPzsVS2PsrP^jxuxzA97I!YT{k^NKQCCt7j z2#28U=iS6^{yuB#RLk8uX7#OG_?>OodhkwBm>crT09u#Fq$p&?O@z+4T1Jvy0g7{o zI5*^%04Q!cMb_&Q9p?$+ZQd~D%-7s@dYApUMZx94z6Vzbb4L_>b?rg1^_p)v9RGU@ z7X0zPD!ULN@WjQbwCuvC4S(EZUZjJoxOJ^=mQ63`!x};YQU87bvB5{`Uy<6iqBi@0 zBrCYjm5qcgY~nr>(IzmikdB1~H04HJ>9SXQ>_zQLt`1y?t40&r2wH!wr04*Pg-=sYT0u|b5 zc@{P0S;(a;BL_nIt1cE#&#niu4C{!i&Z37Z`rM@CT+J4^5rgHmhg@3v9x}2F&)rT3yoosKOZw^ z@?Xs*KDP;+$v3gaJ}tbmH{mpk)sF5K)V4DMpNWe^mG+1>O}i^k_ZoGD&R&}bD?MTP zKkN2C>r@@2l(rN#XC-fQLDx4(F!tk<4uqJX8E?E2252!@(W4Myjg4aZU7R zDP71InbjT{+<~W#C=$gpYs`-eEruI$c*V7yBUsMI&EVB`{lh| zU+qshxzC87Lev{9-3lFKx8}8diN*}@Hs-a9BSd162ewB;Q^NZ@<_>t2EzkIJ>#{Af z*c_X~h=UqU%1pa^J$wUm+Lo>9S84*cHEYt-O>l!U&iqhMip&|4xm7hx@Ev%P=;~BD zeqv;Xm1m!z6vKGaDz+;|bCb~TVlWAodnfc|CDV~rk+?@X@K=Y)Dex?NE&j~mKWrlV zD+SM7M0F8th#Ve?`HBjR#>xH< z#^|>h$t_jsOBHYL1GHckD_=V!l+nasc&6f}H%{l$+-O@cV}fv{W8K0n4Udq4V(%ln zMM+-klfl!R59hs0U%xS$xpnH>()BE;;TRCZfv3H%%kidk;Vm6H(CNij%X^Szg^5ZR zv;!`E_Jy^!qBb4J6I&*H;x_3x{yP~{ujBkH=wlU6>!uymu#X3htlF2}=)O|7a``r~ z*RYciyY&riKDm2yVhnC=<3$#=6jgrP$!Fr0gKzN47j|ssUm*2}@Th$Szd28lQYI>H zK-|cSpXp_f!K?>gbtbnT2gGJ-JE^h|nJwL)L>U1Xf!t4s;dtJxF=mZu$eV+JldOU< zwRZj3;imN%!@;~Cn^P5yq{+d&CcI3VVfmG<;V{QD~J zWsV^**Uc=L&px?@JV8RQP0^_L6Z#-@VKGv(4_(;7OA1hk$pJ zaJLdRSD``zgk%6>Y5H6pdLs%l0qf?_5LnzYV8;3P|#*_oNz#6eDl(aRch8#}m_cVxG zegY=1mxoN=xuz#3BcsNRWVWQHYXxvq8nel7wSb4{68#sh)5MR0tN2(pP2q7N>|eoWroesKR+ulF&c~?6NQ=WCvDC?% z`=X!P8fG-&g&K9WVRH<;jnZ7=(89V{(_lcyV;8{au*v-jb7C#abev8H@h{H=7ZM=Zdg8NW6g>{-nGl;@I>to z(9+dhDEwA05ZoefkV3D@Q_wAP3Nn`66o%Bzxb@PUg+rr~-yjP(G3%)I&WRVG^&xWpStT|*`idoc58_QP0Ch2fqe)-`f%m|YM7`xk!Cm_kkFH9qPui}u|8oJD?#SU!~-||Dm*F-t2Wrq zYj1fOhQuoUAGKrAz@(w?Mv49$MXc!+AmbkDP{z%BN>TKn>jROZLS2=?m5OX26*({n z#g@@26(dQp8EcaU%|{*BJO#ah99hO;GzD{HE=iBwgZp3ywsNmNhFb;?I>aW^tMNmp zE{=i<4TP1VIsZaoT;wU3zHm_ha8XMHa&bvdTtvo#q{uCuSN`}RG^s(ntKKgLZd6JhwOchoDCKr z6+ysHcDE_fXs}0K>HhN6b*2@^vmfATPPcetp>@|IE~O@z6LcC8D?{km9D{a|{J$=e z=+*=OB_SF%Yjt6VA;MaTSrStU{tK>$t&kwc*282izPIccy7d6aDs!NftLnEtoLflv zsm_}0D*`v~;EZ})@*RkbIo+a2um3taw;m9FDzGN|{NRR6-uVYz3HYZpq%KV((5(lI z*?K^Sr?KeqD&aW>+@FB-;@#~f`)?t@wm|&*xmz0WAa&;NvgyUgkUdi6_u9L~9;ADN zN7;S{NADCo=q=rRI0?}mrT@t7E@__8w6=}xu+Y4&I&f=8S5seoj8B~lcRsQ1(0`VB z(mw}TXy&LcfBO2qQ6zYn+4mJp(6Jtn?D>n(v{w6fGlUtn3eFX4iBS_E@4fuI=U{k0!s7 zHZ-Qcny$o9O0Yct={kU2c>AB9dy1k*34qZntbGO*w6bOC_*49lGm|o}=Oz>tu9Ed62<5y6X;PD$&Z80<*X^QUh3w-Djgjzh{B&x?@5s zTWZ?Q%Ga^~JID0j@&stxgzmay8XE^38>R|JlTrrP==K>7==D(tHgiU=%TAU9LnF+A z=tbKI_G@u^KwthBz40Zw>#p2@|Jc0X2?1@ID*+|(nW-^%yiG-qK_EdgGh~rdA1W$h zO`8B0h2W8?*z}&bh`e%v?z$U5N3XZKB8IfiV92gJX4*S~b;~1?>jk}L4P9$LhrhVC%yT`h(%L$tz9L)X})H9!Pc1&#K{aumwd%&a)gjA)(Bc_r!V(K-?!*> z$NLi0(HZ;M6u*@`g6u9IOH+biX;e-<%pFJ0t>nbiX;e-<-T9`#|iO8bP7f zund3D9KE=N7{<7g=rG=gQ4k>jVwVhSM_Q4sfy75+g*$y_Wz;; z{Q;~&K<1q_M+h}woJ^hvJ}dPUGMt>p>C$2w@C-alyj?fbr`|;4eHspvXs}4+Kx^4H zaG8@UG>UicTE$$s1Rs9;4d4`p`4t;I#yc?cf}4E1EciG-PKq4kY{91*E_hpWzXcz- zOb9DJSf!{!ECQcNX_j-fy+r&o(=4%+kMaC^OiIMAG1(0~%vF=+BI>A78}PO;QOIfC zFRWU%O+Ej`f@*M&=~`-&la8Faijn+-|Bhn2rZQJTQcY3kHcNJ8Ov0+nVjl3A=!&3gT;LBLOOyDwskw7b7@NdL{ivUo3!7x)DCEsz zKp)w_Sormzh_my)I<*8&1%EJYc<(8OZDIQPRrs7TJnWl^B>mY+A!z|=rb0g4okjW> zy6y^Mhgn-F)t?Pe=QT>^a3}y-rJa+@r+WBez#va#pRY3 z4kNk9jTCgu3QmHZG^VLVrNKarUA(##hY6E2<;Ol`iUD_cG97%j?89nhwwc9m*~hZ^ z0m$^qywTr$y%8RRff=)dEwpm_{JW-RY_8>#XJ22lo9A*q4x@}OT=x7Y4nDzQ=ir(q zan?rK%G^JEF4@^YkVZrA{HrJ518ZC*CqUW6^rUmhNk*D;({cyC3d`Nhf*Y{#=^+K= zs~LR=Z90iE(ulEgK^?+!#W_mw{p-SmiKA>?Pw9eoz@^W=>|pw$HXX+kTPA$sHt9J2 zI~h~2z6#8{Q?%DV0v-OI_Z^gHOWTX{eM&GG=i z5Nvi&o0eNrvylKx4 ze75q72455S@E+%)dwY{*MhE9n;pCmG3Yy1xEDS~5WR9a-z`JP<-M`aKQD|Ql177fO z?Rnh)LW>_sn_}q&^**m&4M96}@{HkPrnGtRbaz2P{g;!Crq_mu=)W}P;rw33XV`3E zFs}jJqULyBda2q?Q3tn4PPf5yvq!xg*4l?iFA^jz&cb7xvCKnh3Ar0N$bD+YIt$%~ z!!Yhd0q$Icg}&HBJ#mL#4S0UPBsyPsTPSz|A)DxM`_bLpn*-rjqgnmFH!Gpv|Yl=N1>j4&Yon9(LC&?a-$UiI_-_@i916b0ftGQXRv}oYP*4;{`E*(h$m`` z0dSsT08B8Bl@kT$@CcdPaE(szRjCTqm8jip1;gAk6r3dr)uV0`0l)YyN(Xuq`=Eg{ zm0Oija2|Q!yb*x&X)OrOe8Z;vx*Hmzo^w9xIgea$egIM=slS1S_Pnbc1?Q0m&YA$6 z55O!j?j{%NIisHQ|LcOlsKh3k*0^N0*;E0R$l}PVA3&3zOaRUgGa)$V_oSdGIFCGV zUJJn4V;mHWC8+0&MZp;bXM~0egO=8)1mOHF41zO*3z5*84$3E8(w4XE%YvX00OyOM z5S-Oodos*D1`-t9bob>to3I3l_Oyz>rlddTr*)A<8h?Kcd?^(Oti5qCZRN z0u3X|toF#@4m@>4ktm*7V}4v{u`{^JOKT?;>;QC9vD_e1LOfWc3C1HdHC03=X~$G5 zPnQ~y4aN*!FC2o~z6J^bSTn#L{FB>U0Hbjs2ISEL{n^q%+_8&l1Yjq=SncR;L2WxD z@R_(sRB4ZB)3m$tbe@u#2G0Cm^24(ztaGy>7Vx-mA7is~-1?ymK$ zk6eZ`fagK+9#`0A@%Uo%)THAuHw2$NUUif$cG=wgM9qF&}&`7N}W0F z;1AYQ%M}(B&HG5Cw?ToB`Uhbb(PIwaw@6~YbUzq}NFEaVj@+3bQ|=3!)7_Qx!1G*t z<`?rzyRScKIHQBt(tR^xO9CtrMV;>97#V+iY@po-|2-DSO)x){8yVU=F<{X~4FA{X z5XCbfLwV`&eLzpme(d^i<4#s^*sx5&P9g1aA? z7ZC!M`Qb1hM2&Nw*%ZR2{A~Hx#6x0vxBFz@9}5pHAkn#GMemAfS3&5Oa3u^}gJaW9 zhX?euyv8J8U0Vp|QO*>Veq2!1)?bHqzB}c63 zb&xwd?+089>=l$8v8Ks@9M{z#IS!%Zh&4?D8?+rkS9SdPBKAsGrqHBW()q5gYkR0n>Yw0pG-W!81C$PC~o%xhK0cTF60V7nadNjyFI$zJ7<&aSD}=Sko7P9JPBOIRsI1#F~}^ za=d>4$q|NV0J%&=F1*1CvIb@k3In+=sj5Tcs z_i7t3>hzmRMvitcrolm2UH9hFNRds+>LlK>_i7t3>hzm zRMt9_i7t3>hzmRMz&6crolm2UH9hFNRdsRF8Nu>_mql zC58i3n~57Ol^tkpChSSc9BAdL`VBO_p|b90gsVI2@6)VFcL=bo9CuC;)%WdBo-`?dwBzKjrxoqVnzU$>q($0Sttv~aHZ4@NNQ+SPYSBW9NE>O6a8%TXi&!BDKf7s1Y}9Kw+fmmta+_9~OD=^Uy!+=Y~! zMv|Z=R`EFXUlXfLR;Gz4eo2atyu8g#2Q@=ye_WyRnJGa{ta22m{%c~D$;vdbu4!VG z(>V2C6RS*Cripb;6RRA^ssEZ-WwJ6&tZSNBm)5N-_iB-<$)PGH^GFh1>)-6rsKRQzX zRgvU@A=xI&|J#SHt?fR=@~WpA>c1+IJj)_eMMDpoC1uK%h?vN&BFsz`tRrYMz3B&&97w-l2%&*|>uHE9PRpJErCRh%s= z!=&YkLV?PbdR~Uf;r8i#I&Mgk^G8r5waw;*b~wByCs6zo4EHDy`CbCeBMSS-A~En`VjZ;KVoYK5@UQb@{&TeYk* z$}I+}#gBR;OouKxb!ya@CQs1?Hu6hj7Q)>?b+?AJ@5eqH7df`?4P(_BH>BtIykQ|4 zPD6UCpI#AcH_EoJcFyP!c#FPH6nr3x3#ehn9=g2f7Dr*^jZ)|Sn7dNRrg!3uXYTV^ z3~_inDJehYgI+*?&x8JkzX8OUuW1yE!MDl{erqSF!tctP&y0(W5l7fn$8@-M&qCEK z)qNn<K57fZFuR!{I)@jb5Aj&MlC8b{(0yfdMD~@kkyM8J)eY?9UAPLXQFKA zcBJ)Tc!OIx>XzoPnHGUx{-x|fC+Pe^kl6ywnAd7Qf1wBX?^$#D?^4}~{^$XW1C~c@ z<~oh)0e^CwT6b%0eBa>Hzb7*+T+)pahUX>0J4%szo5zlcXfJo%-;l-lnZCPlp}pTB z^p&z7TDpfz&+K6_G>h}5I261^D-#MKJ|R8j0~zP_a_mMm8pVW{)Mn^S>fPFJWR~NA zsYc4{M!se-Qa}3#cmL?~AH2(Z_5F%NSwY0<>;e`;;m_qIZ||Pp3$HiI%L?7tBYVfJ z?5xYa%^4%E%nP08cEJqZ(YDS|)qJb|r0t@NrcLj5%QUb~WVOod|J-OILx8EmAzBJd zg)cGR2u4fwQSw#89+~7DlH{vKIx@*OB*|BeZW@y0Yj+r*s$*7z^W$U6To<1+j_=h@ug?X8Ai zzgf?Bb;NO*tO%{^neTO6QyE5zvoEf z+>4A|{v^No`4!Fm>HSBgNwrI}yES}0UViE}2Oh7&wm5!H?Xkyu-3i;`8^4vYEG--7 zSh^)+StI8}xeqImF|C}IocjD(*L?V!vwzZsvm3&>+6=)(BN@wzyysoi?(U!DR}Zxj z(-?pFT9V#?SoV^UhQcjJn>J|fnk!tt_o!W#^|ml8+WaXKEea{@yu7s_WMo5OS21^o zTm1CX62`e8#2)h7iW3*Ls^`=yM}2Q@?Dx5z_H)qKFlFdOWy365~pFCJ?tT2EVnKq7I7uC;gop28RA%COa+sBKz7VkDMGg$Ls z8N8yQ91qy{A`1Xt`EE_7o}vY-SUX?H~5uabN7?hxL+AGICGADBatZZWBb*^qwo<~5l0m$KM=-RJ5p|Cf)i-!HmFT=wl=INrao9VIo zSVBah0$La6eOa*M#j!g&+|P^uw=iDYbs(4;D;RPMw`(D6j(|C+Y6()5Ta63g!kd`l ztq8@~ZJ8)aXW9Iy$;fJ`7~#^t-9#&xLbzmnt!2zU_8@3_ANlS8nev!fqnqX)dC0Mt zHN5G+t(XpBNg@3nm!s{W##{CrcldcSY0!>?F>{I|Qig9i82;}wyzRDgR_EP9yDXZU zkNIhGI^3JKIdm_N)%UgghBJOzoPG@ZdG}lSrW`?)n3}!PE`L~XShsJxgs``B_N(`8 zcl%_K_h}Ym+P>F$8(q$yfX}cWz3lp(gZ>zF^Obsl=@hi#!--KD_h<6;v#ptW@m{aA zRLtbi2WDEkE)SnDOJ3go`EU3)_wa=-OO_b)OWV&J`gU%9)BX3~qEDTSFZB+%KgRv3 zO;}_2ccUfu%}=#Zzx-j|V}t*cn98<(C&EhqY=L*=e}`;%lsIj~gw}5H?)T6SMyNev zjP7B`yNc%86b(vVV9QP52WnV=VlK&kp}&1ur;G4Q3Tal2{#ag8d(wJmX4eWdN#)4h zmZ3RLvC|^=OgMp_$o>a|d%k0n=@(*$qVKdzdd!{Ii#gxRB1+Zp^B43|o(`+i!rCo{)u&C{MSL8zocA;0YA;H!;#ObsmB{Wk{fV{yt84!qx0sW1dR zg^8ZR`W6!Oq_s!af$%rGoeOs`bE1mdgyQ=vC&3FmEwfEmIUR;I(J>KliOdMUgE> z(ctAb^$f~=jt0TNo9Z1C?-1*tc_-l`?EEgz6$K{BNnt3)&6+_6P8D| zUY^HA1F$pvaj%r~kt;aGg!AzOZuyk7fKPEfcNbn5U-07W3~JNxTM_g#`O^lp1Hjpz z622?^)_{!aQ(@o@o`o%;)!VfU#*h8;5Pp;G6QiXfFz|n%3QBhOI?jg)cTR<(u@rsV z)--lPl3-ODlOI=Vv+dS=)`6%&6Of-&NY_@YNHHKiR~&6T;hhz7^vNKk&fE;{f%i`j z_bz@dIAQEzp5*?o{#T7pg3ydw3dx)z`r#EvPltsx=^2HspXBsR03YlhBc}i? z9F-^cZC;dHl|I7Qx`ZbnDKv0CjiEN%dTijsI_2vH4NQswEmutp>+;vqcTm^N-Cc^E z4ms5cGp__QpWuefT+0j3Vbpsw7s*RBG&8TNF)}m%zb4dKhjZLtC>>k{hQw|B(IdLs zy5ghF@C}?HF0cIu%y~SzW0#dQ6VIG+w4I#Se)|5^c-xd4Q{fRD)v%)-o1EShOHB); z(X~~DmD+6Uv1_AHDj`^gFD3&>VS@ooM|msjjU%1`;3Pog4B&SwtLsmmoJZ?_obX5S z=l*n>ABY`e3K_6c(=REsCO#Qan{7R|rnOxn8Z9jSoCA8LuJ76MT~+7k&X!vo&dCmZ~D6Hrsm4Ea%N{ z#|FP#s)f{QIV<|-!`L}lbLD7b7ZDbjQkY;;u4sph#c`GrlM+~9qNZokXjN6EL~XY9 z*s3TLSy1!VqSzA$>}ifGvZtovaZz@x!=|4-mCz19r|B%kZPO}N^pT*~mPTu;DkGUS z{oneD-JvEIy5EjQ)hO~q6Jtl;$9GTST02Qm1ml@~78%cT@uZF(%Ts2JdHs7NJc8T$ zU~Jcpmz5NHU|SE_?b5HdoE~4^twYTcbPg7*OLX$?eKW9NU4YK;zK0K&AAf1@xhymJ z+y-iTCXH5ARe029TaSIz*2<{;_ODedKsyxn%!%t!kzMO%<=lO(_Q0zZsOyH@`?Rfi zLEb;7C+tq*fTbOBx*s0o3D4j$4SzU94Qn}G)l@XYDsIBE>(_5$J7ieaUB@*xnLp*_ zkgns#x(=MaZn(iJUWYDi1DkoNi1v`x8F_#E4CyukJ*52v{icWZp0GEVpg-y1@i66% zBNUVm`Mzmk?7WnkK1-kVR?`t`)2+wmi@!sTZ^m+h!6wsLjTe_{TM-Frrts3glbN9;)TPKxi^MyLM0T6Gkz+c zYpLCang)e5@4Qgm_T^9b0q&LUs*_DU20y-DeJzY?H4VA-nQHi8v&qbemvTeb4mt?q zlYc0m$g&eiL7AI2UJdkW%RrZ+@=_-|DwJeAul%nIH5kG>pWZjRCxPFHIlf%{*rp3SL*8k?iRMwX&tVnJp=3FlL!%^r zyg7)pt&+v8JZ;Kt))bz>n>TxwLw4}!^K(404T7v~@gYwQ6QY%I=k6;2lENtnk|qh8 zDMCVWX}oDhr}J^t)!iaio4_=f0m?iG~ZGH0j308*nK?m9cUjH#bql zg{)xtu*b%NB*YvwFF0O1aU6`7<*|ku7Sb4K@kxi;bcuKtlgjZFfOeWStIMAzo%f$cz&)jOFLP^K0%Av;8V2}*k4Em7BFrR@|Wx3~iP4Ei8 zXqt4F+PDeX1{0gwGl1V<0C{tf0qA6H!F#Mo?ly1k`|e|6_w+yp;Pk%K%w-cb1SM(! zeDH{102W{XO-hjgT($-SFv+S+(xV20gbW}b48RMO9B^H_fC0prCh1cfH?aX=VS}fM z6yr^4bhi`seRHt6ejFfYKTJ34PA}S}gD?czaKVnN$W5>^yM(sE<>T-;3v@!e# zO=uGbH(i*(?OzIDZgK}+t;c(;NZkJ4HkQM|UT5aPGdS9>mw)U#H%4bdK7 z66c)*N)xoF{B_s?Q}{jUXK8oc=n*{l=Om{Q4^8hVd|N>cV`5A1v_yO0f2r@$BzOMYh=(rDB+0E!m)SIN z)H(8MH9SL6EAqn<{V5bZlOzqDA0OcjeanbIuZ}M0$Ng$j53h&YP)?{kqCNy+RZ17Z(qM=J+7$?G+VIrtYv7}iKktVl`P>tc~ z)#}ng7#@-)s7cWJa-jA5e-W*3{_TVJSdnP`wC#(fiN8LfYB`1E15qbbBd7r=QC8=J zM+8>423DVlJR7eo3s~JSqteEU8VnL*^|`?68^0n}4|@)*ZrbX8461^><|9cpoSocNx)v+e;Mv* z66!f?@g`B%Hr+e%K~D{n3WDQk&YfUK|G&r05G0=Ow^M|Ke8QnNT_S2N z9frZ)c&0!*vkr!8-n(;dA+?JcSBdvnk(l+viq2oB+-}qpo}oB-wV%deTRCb7N;0Rl{-{WoCZl69Lxt5wOMd}xhTd3Iic2RtReCOMV-!r!8hHVj%s%0 zwmRaDUnKu0fD*Pjlks7BqO$nfd0&meQo|lp?ioZ)3KFtZSFqGK&d5@mUjs|+-zkye zatyJhVqrs*`jGJ3=K`adlOC{BV2X+-<{#rtOUc7~I8FdU)&!^g023T*icD~}BQ@dg zYjjY%VsAugY$TwV%*{{$ic8$zMzN(IdSsG9@xx4Hl*W|9VQk`=H9i>;|8@GidyBc-4qtI zL{G#zK1R6SV_B{}@Q!^Jd|mE{`%jA4keiXfK{#x9NCPdjrVuX(TK)ty90L#}=5 zqCsxAR`AXgY&gha>&!1dOl`sK)J?1$r2vNwDj|_$FZ)Fi4ssK~;9Jq~;FBOVJZi-~ zkb}k9k`H=n^B|$;P(6Idt_>3Gm=#o0J92uSnd{c9;?(jo^o+n3uS5|l-<2rTpdcaA zc9x;Hg0g;~z8Gw~wdZlw9LL?0l_wwK)Fwe}r@uj*V~!xGv!?zLi=9dniT?pK#*Id3 zwD$ashXzj5H*(62e{5V7J+H2b*uO>2PqIW*S3ZrRTU{$aKu&1wO>USg?)sic(JAe2 zg-@rb;X#6fFouh#w<5}w4|;0zAj~?fpsMxGl-5YDqemCwO+X~M(!F3PbS%l$UZfZo zwXzXRmSx#aAmwoGzf*hQ?KzPMi)SvSV!?vXEn;&8N$12bIBHrU$t4+nkg*{kvvRu2 zF3_usPdNyS7yELsLP_hpmFaQRV35pu%mgT6x2ZtAKe)S&w}V4a?QVXcHg3Z87))&N z^qJzyDPdsKe+O96Y}8yyZrj8%yr+u9C>Yd>tw`kjF5KT22E`5j)-YzK2Q^vLB}c)J zJhgcc=~O9}+Tsb9c9jYQm70a_=923+;RoJR>zGO6`Mg-TvL#$#E0Q%`QJ77A#*ki z{?2sn&xbgsfQ8@B>c0uZIpyEBxvMj7X)TVflk)%qQJpA302q$Y3sfYOI{?s_gsvHr z)0~033|rsV$#L!x6V_ z<0eYXHeq6e7co-I%m~+d`f$OD?39odtyw0X-6qnWf?Q5eU)IU_k*8OuO0m?o)>MiI zDrJ8SsZ^>5)%8?zUY2&1svk>cefW^4rc$ycGl8x6QmpfGG+7aGlGR5Z%xKsEWJcC& za4YR_iaN-xATa&gU2l{!ZWcU)BiF%i?qi$Ji>YaiL}L>^fJBH*pqo`M-z5M7ulF;E zO&o4jT2k!iDxuiqGsGsje_-I;n5z(*#I{bL*r<&-Ho?NC)=a<=N?D)WIEqZ*N%;r7 z$BJYE_2oDxKUCC=nCLjCBUGS1NknINL*rCcrO)}`U)&H`XsW{ci+HL*A2QCLT%hc+Or808=BOoRu+s&n_zdvxb4Mi z$OLEqqy{44g0Ul1<3CA#48JWGR`doBy*lN(h2FO!vtu0uJJz2R5!6fT{Ejuh>@XFB z&K+yVU!W>?^pUDKf6t+)D&+FbdG~dT_FS~t56_S`yWAy>#IjiZh)bByBm6Qg=mK{g4zMA|lF^4|QrXC0wm{gu_Hn?)oE<{{B&bHvt@x zl2b}4Dnqb?vf#{@D-aqgc~tXKatcmw;jS~k{AgAVJ2>)?`MMK=W8C|9Vo?s#>+a-q z&6!_<)bJod;uyolgGDr>=ZBu!JP3{64oZVFpEp5ro$~fQ-ULK4dJLejd7NZS%h#uq z8XUxa%?|4IGqaNs9_Mp$R|RqKAZPaUUwB$Ao}h*av5G)pYGxK{_$>GQ8`ap2tgZ5? zA+~yu{V6^P(v1bwjb}b}MOZX_Bc7DU*^UoyY%EBc%Hs(JT2IsOjX~pG{NpP%FzOTs z^5ITx9>fX`^_Q6$!w^V)^K-?*fzlNCK&fkH<}ZZC_OEa8(5UbAmm<1sK8>PVUHw5o z_j8eef=a*O2?)nUkt0#kE}PuXIot8+6g52R6b2Z+{Pu+!4iecGOtWQ%&nx%2ga%&n@?fjPNxdrq&QTjU z;cf#aICv^eabJ)qx-mDk21|Oq3t7^Kf}D1EkF6blK{9TwAKeP7LrzQc#o}PT2&t!7MPpzLr+bs2viW(%u3~CFA{@7 z?AU~~Ln(xV+baGrVrB_7FzQhLVQ$KgJGF__r3xaUje^>gi6XO7Q;bp*JZ+}vLTg2D zNUMJ-sDCqbnx2_J4MO6y)d{$u;1%M6Q?GN{;Gri{Bv5D4Rx)KEd!P*D1G>P6Wspe` z4RYe7uW^C7DZlVARfEPl5OdSfpe%*xa%J|rgdE(;2OMP~7hK2+K|WUPT~Hl!Oc~== zvk=FN-5wR_WK`ToBE6E9+mWKilM|_|6y(otQ+}ydgPodG2}9m)4?(CuTOgs%NXZq? zS|sU^mmDj?(akFNH&VlcgqCLlAeB83AeQ%Ysjf$o({d@>7&h;g0hs*uL70@L=Za@# z$-J5-VrK#5!b$UP{6eIrBg8gy58(KL?k!_4y_GATC6VfwW5YqxU=;6eQ;gkx`zwr? zb)VuHj#`Os$%>U9c)G=!4q`2`M`AU&nM-v&gxnMaRxm`7t61Cfn0I>saFoqNaJ0Og zi@O+7-}i?_>d#}|jbEqe*4XCoa>Dwc&COT@0A;~@B zYA|EFCK7wZewNQ?ci3h19OR`G)bfLsN^Gm*&kIje141&J#3MF@&nsDmdUNu683ndV z%uwTz;jD{#Q*->4B znt6TF~2ciZ|I1JxbNY_@YNHM66Hv|*nf}3_}OD$?=5*n58w^u^M^M7wad4Ui`%d+2QBja z%K+t*;v+9_bJOvKf3rWXQ2ETnUc~Dj+lSY+dpbIt%KLO?$S99VHyl1+LuZAV%z)Aa z?J0j9(58soTT_3vC#`p8cCAQ)e=0}rwhYa2ik%j@XTk~e;qfT=!n#nkRx%)R7&Fs~ zLh?4~w?SH0tvrShy9fmd0F@DalbT|cRLUhK-}#)iROj;qQHiUOkUXEes+gI z{f&T-(w$X#Rx15ni(}P}Q3FCUd7)Ea_`E-F{?}sV+P+qa_w_2AGpP-nD1i6FMhA~K zC^9a=({a5py^mq0VFF3h9J`-uIEtFik%$&JN1PSoM(CqcpRDu|c#^;g#>h>$zmFf|%v)%U<&Sifb_h0h^C$uJA9QTWwv`3-5HV{SCVS&egkt$n~KbkHjTVl zDW232!|<@ZG&zl8a_x;3<6aH=*L!z)GiuTxp;8#b#gm4JJi(7WwTTc*_7zCB5Uo71 zzb90R=j=)~>k~z)qEz;g5NfrNib%NA0ZF!$mS`mxmXQ8KBF%l`2B zY}JV{a-O=qt8U`$&J{BUP#ZXrA^G*jM299zBIIR#I8$`XF9ey=#|z)3qZ)~8-wHgg zu?q~ErKmzp93;rO);w-C?5RzJSh8?tE{>`X9#@|qa%uu4 zB8oIjGNOqm2t6(w{J6CiRcEk|#e5e}nn$b06}tb3L_ID)%EEhG$GfvTgdG|P;!;vg zcxc>c~((3a= zPEDXhM3Lr#MMgC71d+!zd{&^7dbm&16ew*8T$$E}942p;zd zc--h8Fny(q-@xNuYE*n{!#-;IC>f7CwsH7;MoA`&oVV|kyKdr0e?5RzJShCq5*}K<}WK;hX z$#UFU@VJKjkW&*Vp~uA}Bbs=E(Bs0sTJqz5NSTJ`6-hykn*<*BCOW;qOs6IWiF#aq zl!f=WcDA!S9NOs!(^qPho^Pe1r&jECf?}PCggmYqc-+D(Fmhg56nI=aR4HpQk>%&@?%ep8Hgnd`vu8Y7b3|jZYh%G zxV7PN`5~t!P-2gZMMgC71d+$pxl^EXecVx`OiyM6NI{PK8a(dz<1k)nsvNI|vIrh` z6nNYN4`KRB2XBGL?Wb0p6Lo@`K1#;pPTd$jpQo$}Bj*hqHCH$Bxn_lVUupv$_{AVy;=5=af zkYtXl!;iA?9@jc&c8B1w9WZ^RmzCeFRJJuK4&56?4G0N&+_m6wkycZL|J&}zLr-lYgfyXRTcDFTe>oCtBa0I8oO`c!Pykz~4X41e8syXj zT9yn}9J3Chmk- zX>(nE$g#m7dwM$l{b6H9zBdY;4K_5{W@7VY=gxp8+uT|AcW3NvwQterR~ujZaQ0g? zKmBlXhRw6-)BuxYwpvXV*f5bf=QC{b><;?^{Qx1QwEJJIRJv#tUpR4+8W57nRww-l zpRe#u4@S=OjCRvaJkp^;>m{{;69u7u*yzv{h1MnCMlKv-Q9Kh!gl`(A3~7RwTt^69 zJ0P`%#-K&)u~sGGSrH)B5rSNFA>b{jDLhxNvtnGF;Yye;t1CvEFI{IqV~sjO!`-$- zJXy4PHOR5Ki=0AAYh$QtIAcRVioRWdz5`HMic(n?(0325;!W2gsVRkI=zB5~=)2?# zjGPyG3g~-M$BOKm)CNvOFMh)?(V>Z7Wb~beDMOmzB@E83#~0{)xRHXmD*pRx@$8-o z&@Hy5k|QfoPD_$=X4Vq{l3V}l()i=Gcxp3fbY|w&L`cp0iF}#03xJX6jr2pc__cVF zH}h(Ui=rNIL*GYea>yuz5i>VZ!$Oj&NHv_XAt2?;#(*#L`3BQhI{FfPnYmW+tGF;~ zKuE@y%~TJcuj-C^R`b60o~4^;+p=Q+Xler|_GOsp&_uQ}UzUa`Lz>_fi+Pe=1krEP zT+jAc(@&tCI-6G9m|{6|jI!T~8y!{bYz78<=B*mkNn^+hy&&Q3cFM@v9U@t1Elz3F z#AjA2`U8qhdhe&E{t~j=CSbR-(7oTh(jH*9)-5Z1CQ}^5yCmJDfv zSE6=1b9_pVY^5S}wnb;s!MP<)S4@VEIym>5&cW^+yUo`5&S$+;Ik&osV7G7>BT^o< z_fonu9_&_cKyhrpU~1|wA-mN8yY)bq$n%<+f!*4)tnjp_HgICQ#YBfD#gf@=+Dt4N z(gd$W?bbEKSkvQz5^B$+^LN^#;Y%lfv@4qSh-J3Ch*7Yh)Y(Mm)YyoA>qM|yxDX&+io$@p-Hi1cAJJNLz>`~ zsNGIp+=tO}aZhw%yUv?&>fKz2G3S37r@qX@OQ(OKU61xXC#lWI8)YNB-A>4z-QiTT z4#;k&4}E2&vPG^~r}Y79>Mt38>%Jv?zOqdkvRglmnYxJ?>J>#RsSTXa-(sUflVZv2 zHVspT4HsS}HTA8>MLB9cPU}ovp@Ht`Q~c)IUuk8cC4MOmlgVFaSX152zb=JXbvJd% z8p*DrB%M=fwI!g10-&VtzVPdjHLGst&sg~Nlj+Epr)Mqym^*QLM&yhM*DjfuJ^9n! zM5bK-ci&?(>-XC0b$pj1*U_qE)O*(>`fFMh$@EL$B0+;KVP)UXEU-ZO)uv_2>!3&F zJd>6wyPv&%Yi+ERemM5dR~wVA`*!c#6|3H6kW9b+PrrJ6$zO2h=$~!?8^>i|c12^~ z{r2=|*8~*kVX+s{aV=YD;hmrLzuOf)WQeCsj-Q0FLsD{^lCbL(2^E7OQ7~KPVQP9M zmT;Y19gKN3l(8Wov+Fbgj%%H2f;zG&`CCI&zD=&Uq?1%M0dwC5QMp4G7&&j13Pj}( z)hjqtsY#GHD#t{p!G?{ng)g+V5MiB$DNmZ9CL}FrK$8FTf4BX7$`DT(9Id2v21x6` zZ$w&B)JMsdA7v3p>ot&8V^pW7G;t)5R*YP+UI&WoEup1^9Y9*nBM@mRaDlY$saGtV zLN{q)qC=A<)urTxoQoHw?ueMQu;fV-)C8nu+Pq7F=9cr>piAtib@6TSo+?ted|F8> z3P>xoDIzT+YAP?mNUJ)^B9PW|AT8cbL|UK#|6(8-klNt~bBCQQTS{5kD;H}XG z(#lq^;4P;%a3VwUn}CT9o=#IN#Ft9a653jbuugNwk|#}26OvX}Ag#D0s?^(;hMwl9FRwAp#4=L@I-AtBOg z3Z#`(1|#QP2?o*{q3o0Vo!Y>O3@OkB6CFIAZV03$w6zdn9lZ=oo-{#CNLuZHw2q;6 zR_wf>cqv$j7y9VFL`jPuWf4eg36R!0J49M?uYk0gvGZ2Ne5Gdl5+W@Qkk;88L|SLQ z18I#>_IVIQZQw+P6zGD94o#LMmFc0-)5hAT?xjA}yhF^j)Kg zNNcK1tP$Q*>ketf0%=9;Mx;ftE0rWks~XB8kk(ottrP=9S~Kmfmn zU;h6GCoc6X>oGdbq~)b07umL(jJ4udJvOn}V$$_<>k|fp9vBQ1-jtmJW_Ql?LE)lO z_{-3tDj$^co<_f?W)TuHsW&+C-HpTDb}A6saGI2IyC8*6edlIM?C8~40;LISiQ2O6t9R$NAD*`gP}6Uv`ztIX=v0@+y1(*6PQ=Vp zZ(8~0`_}7A8k?TFxNiOBcEbmYV9V>lmX}>bw*12gZ27BF-lh>0Lvjh(@_w*oweQH5 zuiAkv-{$$;G^AI(0-Z3?p-I1_(w29DE%Ok8vZnW;7wqWOSOTR9Yl+(Or;eMFLU|TV zkS#Cm?6vLq&&kOvI(yy9PZ{L9%y9NSzuXaPS1|hQDZAKWbDQNN*z!iO<&~|FEjzXW zTmGz+XKb5GO-d57WymA9HrkAAIoT0xIfdtQsW-KO6S;~&Cror`(l4pB<=tS*$|$d4 z_32M9*wL%81WFUu61C+H4{UQT2bZEY)A~>M|J}W=-!b;%{eNHIQ@%d<$*zjbqitgb zKE4q3;?asX&7bcP!Isy6Ezd`r-%6n-V9V-Cd9Maa#ct|q*iGHaGeovLb2Qj;D$hr& z8@=ik=!A(5P5LF3w!8;yxzSZ*%Ufu-Wh{ZxgtbI%d0?si7+d1TAe%Z``1nSGlx zyX<(pG53e@%s-+3S+=R@F#BogXVZ333*62KZ_8sF&F=hUKPnkkvb=qCsEV6n-hfjd zsF{Rh{IU--+;w}|S{OO+ZbZIn;-pfa^S7xDoX{_0qeGK^Nu@1s16yW31<+Zade94Y zG^Rjl!djxXY!|1KWV<`e1yIvByH_~n_>2VC9`_16=8t=>*DI++&u?L7+a7PijDL6Z zT`~Kn2)3*Qw!G&AJVxnrD%i5SV&0x`saVGLkpo+KD=<(K!r5KTYlq8R1-&+`jpeW>JvB?6CIk2N%}&0;4Pu8g_yUn zmjRyCl7=PeuW*`4o=L=m!5|HhYx zsx;NevshM1Eo_lYytOeP+;vxzxrn#gSeL3MW_|Z@p?k|78y%XANh*0uXlo(lElhdR z1T`UV9RuECh9cg|>~=;x2d(JlEulN2NR+quffj+c;55;_5!VrK6*mRmYNC<1b@d;q z;H?e7TT19QN?yDZ@K*MBpQHiQ22Ny4fi{@v&}2+f$y-8O3lY}Q8?od`6V!yf6$8Ab z^&jG`n}%n^bJmJ(-V(YgibQ#fA7~MHYccSal?6ylNx3EPmX1c=f+gkD%wIzBmLKqz zIu}OH8*B@__4K<>Ke|sY*kGbVlQBspZwYNJ#Jq(iPnw`6;w=L>Zne8;IOr0)-;FTw z9JivIw}kGCB1zt=2DAvgwE=j`1=R#8eP;u2Ek_Bq+_CV z^On%9Q6$P+{6GuOTMlxwJ3pN1ig>G2&*MW?=BVV2njjVXi2+K$TSmr+x27xu-g5fm zquqpN-oi!)Pp#|yNl{pr7us5gc?(OPG(k|*Yc@LNqcpZV@YXDqytb3_sF}ZH^27k>@)xAv81dHRRlr+g{`j<|owqR2 z!Bgvoz*|CF3o&nD$&)6iiFixzYJuj?MspBvX%9Fio|{PMj<wiJks4o$`+m3gAj z)GeJ)p36w+<}IOnq)3#v_<_WAiEJql8y%XANh*0uXlo(D zIvP`+G(kG>Ofq#%YD4PsSTXSm;#4jqC=B4NhNXp9_h={{{o;>Z5&91 zbu^|tX@Z)NxMB-5cU0;l;&Q%wLK+I@70ZS1ej`!h;zwEp;wlB=s=S4WDB~O~5CL*qu<^`JD%0?jK>VNB!G*rwhmJ8qNN0P)<4Qb(tYXlT2-Bq56h|6%) zMJVBFn&;9-Dk84!5OD=>Ky7n*GqNio;`-&&IF4Rp3dBZ-CTo&P;;IDVYU_xIOPyY< zqcP=46V!yn)g6ed0391+Uw?5)8cOCB%Z2Z2BvInxM_L5pS^&hQXpe}i#aJM&7ESY} zno32)wLJ)kYmE{juAzEBTz7u?^tGnfm;y&&qC=B4NhNXp1mbF$frx7_y;w(M%9AFj z35lyS5ZC*Wh`7u$FG)kqykfcVO`If3T>MCjKwNMO$6eL?h`83<1953L&HH0kK}{zm zlPYe9)|J6)uOZ?pdJV*t^2;aSKWYOfN?!s;V4_2lHAy9Ll>>3L&;!t!?(||EjVaHD z4D~O7x;46?j_Tz}^|%)jZ=TcL$!pRM04nUFvx>86>w~0^D3tA<@7QGeg;;cBl|s9u z$J}|nnDf0XqErn(qh(!%$csycd{jC9S;50<<-I-V3*S$x_Jq>L5)hS^BD}y{gQu}f zY>p<^1V~^*nwsr#wHGPIMXf}gEm@ZB1jcXLL$5&d=%i>gp(M{>@thgq0|^@oa-C^- zQEpP%$Eg~~*btEE<1}I=xa?-=8dN3LP;l8AP4iZEE}^Col5yD+8R4$;o(00lc`tmM zswPfu?-N0>fQi^;W1@p+l@!;O#Y_g`Hvl5|qA^886M9^hzG7U6dKRLg9fL7w$Pj4A zqcfr*m(juEIRZi-4HLj9~ck6(PCbak2@s--ZiEJql8y%XAsnzQJ+HtGe ztcMxM-SafZuqHSNdenU-vL?=$VDYRX(rHb9!I~x}B5V3FBQEwFHCc$6n8`(Hrakx} zr#1y5b&8&drLr_>Q_i3j+B;J3%s|>?yfy?wo8Z*g(OLgu-Zx`|#Z$gSpEeC#S@Lw( z7L%Pw`NLbq9j7JM)ZTe(+* zau+W|%B__k9_6lvIW^@bmgpsr=<-Y?(NOFC;wkf~8_G?}6R!ese!qdlc`Q*(;^YUM znmF&6c!yXA%|nS7JHN|wMZrk33!FO%_4Y#_C?39_>H(jjCoGR_y*!T#U+35v{mrguq^i@ThDe2uoObBcRzG0E za&hC^VyMVP0AyKf1jy(S`^A&mnpV?8QPjh<3%>-Z!9l`g;sB3@XoT#QmiwtDXOdEo zs*Z;mBE$-E0RUNqF8^cSAF^Le30fC^Efh^b_$5dU4iXA-9`KlZ1mTf2aX;1cMp6n= z)s<00gjhkK)IN0nXaq>>f&0aj<8^<_2$XHT?R`nyv-< z*RBR@JG?Hb64?u}5+!zI&OPfExyf{X|zVvco$mNN0F2RA72QDtNSA3Hiv9o|HBQ*y5;|i6} zOm7RbqRpQ&(ekUp&dXa1LPpl4VT+%BTEaLNlnN4>dVeZe0;2&=C+Wrjan2d)GdX8`N&-zhVJ5 zg#|6~#7q%Ku&;bn}&Zfa#PZc%2iYGVagh>t|at z_2Ru=X{nf@53pxiyDkr(FiT$E{`qhCH}~*`E=!ge^h?{%{8nMJKH=dLPk5Er_Sm(k z3}gQg?(v03bJuYWUqKy^mAfQ<2h>v>rIg^ak<^yiq3y_G+y^)Xd?MLh|d# zb0d;6)!}8G1y@3+y-GQ8#H}LG@L5XCt{KO|ynd#HN3RYKZB`k3Y}nl=s6I#j^Wm+{ z-wgdanw#4xkA;5DBV?SPN0*~*=dNQ6R~a|QV8iMc@Up^XtvAEC9Z)SRi+f|BO|Qd; zxD(+Y8vUnN#5niwRnTpEMTNFfhvQuk_pk+~u_XJ2{`O^^&?6PntQ!5XymVr>`8V&1 zkWT31QxM8GfCEJx7K>bj39rt=?PxN2k;9#bzm1fdgEow${95 z6}YI$U{j0Nt_R&GyLCr>p;!Xnr?@G!9CfxDxU+s>WDQNdyZcKP6>G@`qn)+<-VuJcTYR$78 z9rkvh&!1J6qw~)!-*;m2(82kI4yX>ADKK6B3fT%HmpJzLo7>@|N?*=&Uz*U~asRI~ z?HD`4QttS-`7sV&SM0uHOiuWjM@Z_*+}nykV5S;Vyd^7JcB{)`v4Uo`EuNq-_+QfVPS{Q&*(R)>wXPtk! zeJkUj$uhaetuDR1_qY+N;#2pW8j};B+HA0&tK>r|2=C?|6Qs((f{8&2P}`+%yk;o1K!~{weHs1_`boXe{Wd0 zq#Gp+&qGcClKUh?+t`MvOZqr9xpjXkn=%*xKX?Ax3%;>x_xd2ScX;2mx23{}my z>QCA(%4pj3ZnsPW>qJ(o%>K`fCNj8te|7Er=%MR$_*i3kmAK!wmfJ6JRO0?)!gcW) z?a=e&GKcys(6V1N3I68x)9JrZr_|(%DR-%T>xb5-FQGlLrn9o{yGJ`3jPuC4Z}Qj8 zTzf{^BX$`2s(=pF3H^I)!3;lPbP2f=t?aCU0m%!x|H{f5nCL&uAUZ4EHsSuXuJ9I9 zK>q|tnG@}-80S0%LJkJYU5&|6^HYqy=ZAKUS^1X*^}bF_UX(!y3!I?;76eDw-_BPb zGoX3Np*R3t;n}a?Rn`M?D+?XZd^H#auW*e<`X{P|>{w^U)lJIt2xv9{m9A>&+OIsJ zus6DkhW*Mf!^0!y=^_u$^jLi?A)-(L5d!baf*mi8-O=HGUX09}$&E6zC?3uUKxU#N z|K8JMXSb2-IPX1;Up98Sp^@U<`+$*iqTC1cxxzqIQu7EIJ+U7tBnzN z2up&w`o9hyII!#=Z53&M|NAt+e(P6!{(BkHKJ*&T3)LAl_H60?pmbPa#6Sy=*J&f4 zML98-;gTRzgurOjlV8oAJe~Ido~ahsGUBLf_Eo*>t;(-0*?h#Pra*~0?=l(EO1vtO zNz(=&*LnjOaW{Bp>9vhWoU=(JC1r5?bUqz7B*__h6}8Rgp))d{x-|7Q4{a0t$96cp zCMQ5>0#S#+3(_4h*zP-e>2NPCcbGh{>5{=;UX=XQS6hH@EZCpNwULGtD6lSnjnXw` zSpt;G1}iKU-rkn6q^Y;X3K1d*VHxm+!;-l-hHpZVlH!b?%I8{Yx4{?bwxi0LvEgqV z;lqs#)}|&b)KC4RwJS4wnoOz?{z-?c6gD&vU^8)$t|;-8ETdw`GAda{Rb`p0T1KUq z2pQFu>F7Kf>xheB(oiRiPn1CjOKq}@N`TyjURJ8Lj7qpA6X6%A7b|$BMLI1C0qZgR z)V#~%D@yLnpK6PqT~eK^UDCYU)(GYd=FjGOTTeA_+oDLV%j3^AC1=$vGs-P|q%Sit z6@Hufrj8n>u*M~@&Lc}qMOi&zh+*|aSkVfw#ujsx^%e7i(OL!f(#Njb^QYRpyT)Ri zcj?!B)vqULy+=j$*lCR=PG+ec&uwqIcpU8K2)~zsonXo~Glbd9P?GMo3H*%-{A3pY zJF;w6wwWQ@%%HTLDXa^Th28>AwC;!A!Uxetk)6jaY_8N7A_1-8?rW6VkGMB#+}z%p zy{xwm8{ctZ%l6}+)f6_%LPya;N6LE|oFlL&J3{{~$cvReLQqvj$<}FGuiNnOV`_51 zaw8aIvq73)Kb;OfermYp*CTmh*K@3xDaTH3JyM1yV0Wm=h3>bb(e9o6(8So$_qp`3 zmOz$MH{vN_Cnzu-G3;0fm%s}d`0jB{R_KbTns^E+VK9693UPdG`B9RD@ z90nrU@D344vW!S1N+cVCkEt3zwnXdLFIuwpCFrqI+jh3ijVUJT)l5d2NT7yv)2Gmdka_EAbgd< zlHa3z%#2GWfDN?h5j=ieLGOLeJ$(l{A2B@GynUeZxYC7tnwbSUHLJ<%RNI7*9ZC4t ziUjXOo%EiSuiYAb%pwX|iM}u;`Yhqw%Sd(E&X1p-7d)>`M4iBu&HARQzA_5gs^9f4 z2j9cveFN9Ui_)LNB{=D_<2p_r43E8O)z|(tajY!#6{UWa#qImC&&EZL?Rx{(TH}WF z9G^EVM8j!FPxaF)g6&4x_SMcAjgIFDHcApUQxqtJ6f6xC&ehimgQN`=YN3M&J@ZaN z0=?%gh?x0gn6Za0FS-S)h-#EN_s86oN;bU{XFPMC4@b)CPQ>~2{j2e|DL1CVC^@QO zM>{q-y(t!-w;YQNr*tn|iP|=57^xl}-*eDmBaY+2(MlFqjhJe<>?KQ3L}8V>q%QUL z@N~&E166C)?xUIKFY&;4`FP*Jb@n2qDiij7bFjI799WqB zFx{+sO&r^hc!vH}dReMMZTqs!XL$-`_pNDAUE>=@NIX6Tb&VpBW7LMl*jplI_QB0w;evH z>*h<`U13gxJ8!=-@=4y<@vQ^GP?~(SCN}wWp-&9(6D@~r%qjt)x@$5DXfrXIt}$Y4T5?O5jJHaU{hwd zDT>+UrDId3&SyG8Z0JaD@FUA~q%zYvHso#GtHt5y$_*p`6uIlqHtBEJbEnmn?EBLb z_GhVGP4Ly<;Fqi&`QWC}gM|A!mmdUAugM9^QXN^ULw|=@%(JetOh=eZM_%~qGvO9t z>cqCKthL<#E?334cTikQ@_D7QSotHJTD;r5%wWw2w0f(d91qy{A`1Xt`EE_7o}vY-SUX?H~5!sf4m?PSaV6+on~l z=!0M9t!*hExS6v?3qGweQQ70&?P;An7dW`*|C-Wtb!-G?=e<9WD$vI5s8cz zYNUl}>d-oJNHA!4ZMU*+B+LA>1_mTA=>97!Yha@PFoWovQ^S5AzpD?;~ z5j0gRn*wmM%wI66s`Hzt!Y}G~x?MT<#dl?NWWcGh9rWUJ|7XfZEDw`F$UcLrGlgqv%xSk_h1z;$oSvC4&c}c(x z)D38ajdum6BU=Udl&k)0;>@>)z!q2r(eomYuv#_NNgGwLe$4CcG)T9Hl++QqfJ`y$m8({wNrIS7>8%Kt|&e?}qFfsHlk0R86A z;Zm*#n>2Uzm|k12qf6Ae{j&ZJYSWR~tFvGkqbNtPm408yNhI`2NmM{KE)1U#Q^}LY=@z*gZ0V(y)ef5CXppPD%)(3ULJQOP` zMec1LJ0_yN+;M+H7UO67?!txkeyA*!Q}#nk_i*W%JuHT1ao!Y%g0~;xRfQ0rke>2^ zjPrUqcB2}NV!}&mGxR3)ZtXWR%W=R|Bjt4?U$YpgpZ$ZofAske-sQdee#N1zAmVg( z0gIvV=kk)bchB#I*Bj+!g>LMTy<=8()@9%3j1gDnh0b%kUZ6`d+)QhWS(-zK$=?=?Zfl!z zx%Q(J<6imBf?=_G7Sl(n!X7r_&2zdtc}>~@7!SMXtm16^$oO<(rsY@LfEEZ)=Ze9aTc4C1Xj!k3!3nq`c*;D2hOm2u|Mue(mK7}+7kQl>NXduI9raE|cWCLE^< z_pSOBOi~M^MabQ%T&Pj)nX=|)IA_CUN1O$tCt>|cz3mTu^UbQTi%z?~fbY{?S7DTv z(%ARy+TU&VJ>u+&S^uNQ-K2?Y|GQh!`$dv`0)4Sx{o=re6zbKf!&#p)HBO%YcpR;j zYsaK+Ihf)anjD|HCCktLitn`>0Wb4c#I22oH(9HmYge>;?v1(v%J<_04+>&2-s`xg zKA8J30$x%|KdE~pN@K?M-{3I zt0c{U3cHRW075lACpdyELU3Q@5P!92iqW>*B)0V}+bSYmPbK_TQP8NYw07rzjRA*l z)OGz$n}{Plip*-Z6^rb>rCWiyPO#FY}ahvQMTSoBBxeyWpZnok&cC zU3tN&Z^B>A$N54Hi}-Z{AKId}hO`_ZhJvU%wx}SiT3~ZSwdgv^trkkHxK5k$BL=Zt z@`0u_0=iG1b!UI&iVfYTxAwiB*JGbPyvh<7RtPZWn!AWGzxLfa=G<_Ho~Sv6;*KHR z`(&7Z;tI~9z1Wosnyl~pvmSI=IrjeyIKdga2+g5wPdN5EY+%^hgAKFNd+8yjrF!7tR#{w z&a3T=9OIOyV41OS^(3|VEny=XjOkO;2w@+LSyM^v(Za7l$|@&`ZZE)2QW{dEThn@w zuu5O&o2u6D^sT{{1Ix0vx8bD^^VM-QrTc@@VTBO`Ej(VQ zjeHj6#9UUx@_}%UU3hnOcSKHWx7@yiVWj$|j($208{1#h8=I(VyLZ-gQ`-O(Gfps2q$fUZNN%=1q?#B(w4iDTXH3FI z8W=;O7F-MaV9dB)nq#RbGmGNkoPgjD5 z$Vhu!hm}pHElIsPy4TDm%T{bF7~Nxy+VquaS3T)-p~9L`&22{r>b-#lf`>!KI zQB1^BXq%P&zVh$PNgnjUA)K^j5rs)(kciei%h6$P2m1V3WjQ+k%<_FFCJ!B)U+93& zTQUV$h8N{lrFKsiQHXXYX+2t%6qb=B7E$nDIA2pJ(Ewew@Kj&)S93te}9{T`GKTsp3M7;9L6tNvKDoHRf>GWRve(qJaYXV`w%L@ zZy2IiN1YtltF``6GlxqG$*&{NjY!H=hnIC0TnU}_D&@oxw~9c+XDKneW*iIi`k4|Q zy*fO!S!L|8VRxUPi(BPCAKu#hjcSDDX%p}DYvBc@qW7vU&pQ8d`&Pz5CvuqF<5rhm z-h12#Rbi-mPL0WlPwlzcb!yD%;(U`-)G3IS6d!qco12a={LTKiwJPANPV6@S=3Noe z34LPRyG^Z_Q$rr;?|rx10$$+qK5Y8AD5g-$uzPGDUf1sF`{702r!zxFc}%+D@c9}V z1S1^~xqYYW9{7`8HZk%#dQp*j7qfzidm{%&8tTHgyA(EJ6+ngB3YZ9Mxm7Z+n&Q=l zNV4J*rmWY5tk*vrgjWPf$} zM>RQ7YRoDu2MEOH4Jj2B!$z4HezLYZJ=KW-MV2_!tHhy7Gvb;!kN(7g$*t{mW`Ph{ zmN>w@Zvv=Lb8li#9O&n5mhik?cg~Qh^*F6Fb%h36XjJ^>+Fxm9q9y*}F;@$Vo_b&3 zqWkTY3j%#?o3-y|n7(0yQw?QbGGC*RX4UAAu#-$?;Cvjx531ab-35zeqQ_!B<|TbrP$Jw_fdV0 z+ThUir;nEC4~f_xAB3uEo2}%M8|hSO%|X==kgKw99vRY5MC}IzQWBqU!^%%0(&J zYPUS1{0;tQhb&8;!w^Ws>zM;cER<_jILe-fCO@}-uevciQNj;lS6phhQSEe2^2OLcMCKkZjq#-Y&a8 zi;|?n8Ux=t&gLFc>$7%SxVMSKxwkg@WxlT;`+D9<)It{jI&# z*{3DHZNv6Qg!Vm|1hcsH?!K@uxBTYDN$ii4y6;KZSX3GeGHI@nAe<&$>D{woKJM8t zOZP9#G9O+-NGVst+=7~swAI4P1Mg2rx`P~Z z@f#%ETarm()({y3RR#k8GfsDyT_D49xJjd)C5^bn3l?`wXe&15W{&ML*ZNH#ROX8p zI_KTgw2O1G0p_hBSe`0vmN51`Pgj=A;{2^XL#j(e)+W;4i*@>oFV9qSSn#_ryIbIo z6MHdhVhpF<|B{KZshi{AZpZ$J?HD_1Q8{x4$n?!#ZWRN!ih;W!?Xxw%e-DFk zs~EUd48H9kswHLoriBGNc85&+PHtfY>$Wv`5i`#FbUmp_U|G#`PbR_aPNR3Xnv@=> z{9k)!$E~+b>yx7-3b(SK?Uns4 z4`RH(tb2&`pHrBfC+XCdmHkd9y`O7C>V-?oUWz$3{ES`?;0l<10UGzJIrpl0sLDg+eOD=%6Agx(D|bWMv#aKuVK8oGf8VJ7_|K^< z;|YA9JiwT$b~Ac*;?mE6c>23)1#Eu%Zsi~ctpFvzjs7XQrd(HN2}-S4Gy+p0G*tOA79pq3s{`aOLOY>uEzy&0JN#Y)&?yf;k3Unj!RXTyUY@9x(`63- z%(Da^i;7*nIbg5p>YCp|un}vS8VMDp5`>^;` zPCqg5Lvia3k)?5|ny%|NMBZ$u8k>3$e8|h-`WT#C=y|Q$gKhp}z;BaRd~z8^+Tj0M z@xrI`+Ta5UwbG?^cVwNE@`QU+spBVPTm&CdzucO=c+;Av#??=LJsy(*5J`GD-V6DH z=GEcx$cKW=mjd@;*8Vd+bx*QDEW0zuED18~4ZArs!30Oad#5c=0-fUxTFL0qm~SQ( zu~8D-w^%3xzL})+yCWrb$|J*ATW41CJF?B^-#rh+L*Bqc;y8t+G` zK12HzT!dwJ?D4y|Z9DjZFqM?m)Za6-Lvm;q&%dOF*!wAN%8i?HBXBM6k8nWDveeo6 zd;=<$UX}$6AiBA6ruQr><{u0`p6*{Df)MtQ`YAr zc)9%_xcwi9^nYM|+KSn{VYf{)J2vu6b{qc)`VO@q=SU~Ai3u}QX6zD7zL)wYqgFoX|6tmmR1wO$KuS}A%0J={dw0G9mqQ+Lm z{yVX^nCQRH-CnA!=E2TaG2McbS*aEo@7?}%t_ulBRd92w$lamRO4uTG4;GQU7xKXb z{Bl3qvD9a~6jM8-Jl_^>CL z{eZOoOxu7F4-lh$Yj9$x)S}&Mz`rTeWoPV?Z5sPXy)U;tVwPrc(rTrQe_rzSwL>aDDD~3cyWTl3a{GqOf5rYI z1D;TAkVhjWtcO~>ZkkhfT~B*cN!tAU2vd?BCJB-#%oiUW-gTC08}y|Oj5*+pW9~q^ zb{Pr3OUZ*&>qugL9>2@}{Y$=>9Wfg8##kt5sbKUx#oGvFWz1uUjsA>yfm@l#{>F-I z*!D7a_Qb4@q1WVYHEwJ?*f0+SSw|;BU_hizzoRzRdnImTYi`#)2OJqh_(7I!im z?_h7>u!A1#zrl=mxSKvAn?7y@4dbqkS%-zUo8(r|a4TrUCm;4$Z}-YcdE#LY)k$B@ zZy15Z86m5-^7Ah$K;9wyZD%EJ+|2&Cx%UE1&Q;K`vmRAvUlPUWizIc@S zfF1K=`UsvmYi1S>PURvv=OVa8mfTzfppO{7 z)KPe!KYSkeL{TkgPj-y+L+!1{mGi@jkCT<2E9ZrsdAxdSx~=zJ&lsKYZrr#D{T{%- zbnf;C_3`Po%?$I~67E^u;EzK~2ci7t*1)#jJ zcYFPm`s(a3d6K^p%t?4uOuE7#(H55(v8mi=^=HuB)L?EV+nfCuohkb|S6jXd$akRca`n+?ysJi*tXwz3C!s>g zZ`R~3zY{q(8`%02;6`!sI(oBfpWJFEZZ>eyp|5j8uJ4dTD)LF2j$D0qy-olrC!~Je zdA8{4OIb?wo?jrrU$c@K_dr_V+r9$uWqi?t92v{WnBBh4+^1SP;fnsRUWFv(AI1@L zhMI|~V;2B83C)x7wAE?`6B+n;-ms4Oqc(P&lNwTQt7cGjY1M#A%rXF83LDz}u;V-7 zcWk^oeKysNU7W`4A;j$=#O)yjXs(||xWvbhrmU*r-V$XuNZbmj-|pABJ%suOZA60F z+Tepgy-5a)g;#$>Sz}Ko!R$`6cdyg*<(ppI3aG!S0?ND&q&Y;L5ISi5-JX~AX%6o3 zHureDj~#D!+l#ZK8i(6Mi11UMEW1YW_s1K(@}6k_KjUU2v-vRW?HzVb4Et{|vtj?# zr5NbDv%w=0Cv+^<&F-Hy;k)u z-k_Xj>?iMb!E*eS!Iu@zW~%&72e^fZ+(N{ehkz;#kXv*pRN%s$aRUe!B66#|`bN1% z2yh1;sfauHz*w%)gNGe%f$t?y7E44 zrl`2j0vI>fT)$aZ!^y{b%935bqwk(D<#DZRRNcm`?3_krLS1rK^^s2L4@hsj91 zOg{_IEuqZx_b_1oPZjF-ZFh;Cq{Y@w?7zWO(tqkyooVmLT*sb2;$VSUzUJm%wMikN zDi4vT)};!2^l&HR_JdiWREaN}Dbsj48q7%`|EJ$!!06;OL`Q!{z`#9~VE?HE z_8rb%4iT7j1oWEREuGDMU?7M*!+~DQe=rxwGT+H_7;T=&>P2snZVHNGA>$^jQ<}Gb z<%5~(>qn9I4f-*2rlp#})|Jt;!xl8{gM2@Jmqh8Nk~rb#9}yg~%pAE1B)P=$P0oXv zE#+ZW5Zo6A=wdsLB_lZIBe*RXxGfk6Re`a-={~cZ)e9{cSie|q+|=!bf%^p`*Eejh4GNMkbUk(toTaW^vi_3ATJs4=!&zR2BR~@OIG0eZV(-2&fSncplb;#jnQ(6^ zb^L^ki$K$;UvAA_ylKr-Qoz0ymG%=~c=b2+e{$c<$!b(mEEh9mB7*);AY z>y3ZHC2MvR`7x_9FOnagoOCFBEg(;UF)2d2-}Bf}$PayciarO$5ZC<~9M3K7>XW5i zU)~M~i~4-+4N!UM$kfx9Qq~6~$EBXmbzJnw;mK3y;;IAD$Kt>bc@Gyzw~Z(`h-@wR zrwjS%d-MLTtp}OOwjRt{F1?9#XL3z22)6lJV*V*aQn!u%4rF?swb_-4}D z-z6me6*!Us94W;J9C=(gBFvuxIO19mIDXql`CrkFL;%P0Lz8zA;A7;2l7l5Tu#;#cN?LSt*~&M;+TeEwD5c-ubJG6Vr?KhxXQYR| zeW>sXd@95+_>Sf6O`4l$PDj3e-Xa+jhVk3TKWe4Dw2vUaKvn*>?UbAJk~3GA%>@4! zSvfwTMt)Cg_L`|#RXq8s8MS$mqHf>^VxP8Kc)Hv)y!GLL_WhGF3we1Ir=Ox;aoC1@ ziwJdKQ=4A(N&^wOk3V^Nu8lGuM0!p}`V@&L?mM&2joxs|sCA>T;@0YH< zQD+B!$}@0_@YNEvDUZx8gzev{PP}2VQ~YuUFON1~mj0U`-~laA54(Gx{S$@r+m*L% z@(r29gO*g}>+O69H;V#W65o>y60=;_-3%B5erCU!@rq)%okoOt6fpmg4?urOC5+?t z1Pzwu(|R%qW`XP7WGw6BM|RXrTCb4Fp&ZFZE>{j76DOl`Z_5~6`8XNtpUXA*6t74f zzOxy53!8_;kniE;VMkVq$@7_Vpzm!oWS&MQHZkIN-B#TN|KWc+{bSB}O{Th7`@I?3uR&9@&bcLIFM7a_brdT5YHE%-Ot-!6F@PtEEJ;j_Y(<&ZD2 zHcp?EPwBy}n*aFX?&l)CPvy+o4oB%M0EpMY2MB`{%}8fGkj#G?P4&vr38}Y3g`Ti1 z%c%a(%*pn?>9Gt@W^EYnEDEQxo7rI_%>P5r-9i<-Zl%4iJKc$n0W_w21<~QajSiS- z49WWPN9MyHQtzfNI_6hbEOv83PDq4~KI}GCN=6Z0vT@S*_+aO%Mya5SUjk}G>t@ik9 zt36onn@nA*uIuxztOANOzdO?AZ51SEDfGBN#sD$!h1Z2^L zB+E(5`c&$IHT>8s+ipGzw0fP7-+a+BAW;*@h2JDRS?n95dNF%*X5!bXowtHJG{9F2 zt@J5ulW@4W2)p$N@8|Kuf=Lls;J5qv71?B{o4Flue6=n2AZEWw61NhIM<7kNUx&^^ z#=Tpg3PG5Sd?L*MLn{B$MI0=y?!kjoy{j+`w8P-xf7`>Q2b}yLkJmR+6aP|l)Y}hY zFgQELtUGgd!Q5Xl%YzKdV6di85PY3G{44lkTq4ufB-r91crTV8NmKBOX)p}>+h;SF zT`L1~%o)8&j(TTEFcLVVZvV0Ukg(B|gw6RDw!A!SxUb0Uv>8OiL{@Fnk*m+H*9lw! zE2VzjdA8_34NR@BzV^19%0B`8NMza8C4vV^p^&K>l>%V3Kw7B%g-Wsk&GgYQhz$x_xxTossi{KFN^a`&%-nWBo9_1hl^BeC((`17k7kg@?=Oh z{bgk`y#99@0cZx*c@nCL$}r{xpSbcxem*Nj`k67|ns zirm(4J!S6cOCi7RX;RND*mk7+#b@w4lCD=BC52R9ZFCK|K;@lQVqt&FQA%h!&!Qcf z!yHnAbdX<}longL^NQTG5zhiYZo4QxZA4O7{lS~OJR5yV%1^C~3I)GIep5XDyAbjZ z{pat+oz>SNwZ1};qANPq#@|*T$;a8emlB=Khy218!}a@7yPuJ2+l)>=%gVC`zeOE> z^+`nbXeWQlHRsG_7VnG#CBUcs_GW6`7$P29xg&@6K$!F_DsJYB6OBW_Pbk*s>+YM? z6y4suao#Eg^EeBnPL6WD{~Mkq6P2ltkyVQz)8l6-okRT+Z1&;lpQmu{?f5oieInuK zbplFF?`;hCS`~^6G58k(7F8w#E$`nm03Qg9NX((^m_(|Iw3PN4SM(Bm#OLm``&zZY zCf!4?e%+J(ep5`7B$V7K7`=6sIrurRl+DBq9`)zDB9SLogj$~YR(N&_%tpSR7x{YL zT7U06g-zr2z`w~OR#Ik>R*RaiYREja27Ex3n(XpeNOjIY*|7{PPr6f|)_=*ht1jU8 zXkXHv9kh*cxLlt0Onrvmv68lDc?w%x&Sux<^&)}H;CGbuBS^NpS3mL!W(Ja1dWz%WMV3e=s@<#64WHLSAKnPL0 z>9eFhTN!+Xz^3C-vV|WBBS2z$-r+H6zQQkzjKQEnLk9#{be>Q5hnu!$^8o3DY6_yS zeEnxX>ON-@Dl%B2^{+@VqORK(3LP#i2as2B#>TbT@SHpwk2)cOwYg zjUEzPQRIgJce6z%b2%Ic9CA0Az}=V*1@jg%76$IdBi#pX+IU9(Xch`GSZWAq<$aG% z8OWKnV28&rijzfHQ{>eIqob9P_iuT}uVY|Sp%g(tGgh~e9~YQljfvsdiBjpIQ}>LXm7_nu_-@kjQ@Jy=qFJKiyGCtYbA?<(<@mywQ+MYzUftb-Zq7tUTY1ps z+1}2CZ^&rw;l9fQTE+I94EL3&Euc)e6ndIBCT;L!r${%=ACDGnDGIz~04GY0a^8Ps zg$@!S&Z7d(n{x`_LwP9$IB%m&=3O`tIK+7gfb(VufO!keTMszzQo0x1wDCC)1sN=x zfaJV3Sw8824a0tb+4Du44QDXKr4x=x>pgBehy1)E%?)c*3`-;!5$_SONeegK?GFY~ z{vtCj=aH}lskQ&^{D)%Or5j~5+^&;0lUilu-EPPXA5!0cTuwE*k`cAU7THMmd%P9b z!Em%RXG`DOgzdRW&X!pLAGTb0cXG$m<~lpS3@_Us@!4vZQ%k0vgp($22(@&=aR6!i zECkYV8K)s3B^E{^Hg?%nhN4wAm-Si8vxZn=2&93I2S7+6{WY0iBQ=^lnK8Zujb zBlK4xM;ihe(*4^~Xx%9ca|4XA5hKP~7;?EyHRQFr)`r|G7JlxRLa|hxlkoK7D_)+~ zi9SoB2Qw#?{l#*ADdgro>x&%qV(K@HttggPfc2C0;8_BCqD<*tXw-ao*IA?Op z@ckMwrE{l5Y_!(ynFDy|sug-=r($p}vavzD4`aa4HZyjP1?Y@Z@I$Udo106R7nE4Px`K2SmR>&TcR$)Q8{>OS-SmFphHR%tFNbwhmCO~YIVTHhX@l8#(fSWfSQMjB!!v;&AA(`S&B4whi zGWg=U7b^_xz5vW=E>cSSu1b8R;^yW@8ZyjW8xIr4xSdCAYHfO#@K@S!>6-W z98C*RB+epf;8B`-tF&Hh2|}lQir-wZ2K{p=?aO=zUfy7Y((>Shz)4D@2e}87_5x6v z=rFK)s);e6w3`K;YLnn#;0&ede@fudvOqdhP~J{iWfKyso_HN@-uRS;h7FcJ{{xh^ zxlC}lYuES`fI02vYQJj+yS-i;X0?&22Qf4w2}N{Su2hv zG>NlF8n~3!2`H^H2P~W~FvlHh&_9RL3PF@sl@F#%iiZ;dM=6aCWacT&S=Ym6#wx!2omG z!QlPLo;Ru1dxQ6%E^-nXvpH=0CD)5X6%NPSge5+dJ8&@UXbEe@(WH+eaTZAfkJ85M zmezZad<-m{@5c{EtU>>nC~c!8*my3K0n;VvzzKnqltvG74=8OuptO-@VD(hVy@1jb zb2~SS!@IVu<5LZlATM`<*bCkRwYo*bok0Nju2?LkW_5nsq zzX;aN_pQqwYtTQ2(af6wqYaJ)(Nz`H!h>0VuPj6{{W+zq)uG3e%?zYqNk;X zj9oZ$_LD=&A!ErqEMlhT-I;CW zZ$NHlQVXX8QBaP!7Dbu?^Pq+ES*KNehm zu-w2kYCB@;B1N@ZP6>Iqk&0>+Q%8BH%jLFeT$
k z{v=B$x?DOL!7cYMt~M_Hw0P;=X(P4(-K9v+UGPj}sK;r3CX0=KHE%#GHs)y55%_A( zj_JB>!x3_rr&#+j9{e@KpzK$Mk70YT@RMA9tW(gT<7PTMS%J zH|vPBUi#LxV10a}*0f`dis9*qI-xzYj)XMMtwHwJwEW{^3X}VdTlx0*7!RkhlVnm| zryehvIb5>j(v)ZqIe8tIydBTb^^BNbrAC;CffE<6k6yprNd8~?N>E7 z;>kI?a^u+bLi!$;^tX;UsKb-vs$;IAHmiTmX)~*_`^e}tL6nrjp6#0O#pEG!kCXO2 zS2KADPqbruuI6bOrKqH`BKb>3ow(r8>j{*x9?5Zw{OU7%ks0^hufMh9)wOsL`w6| zq=)CR6>(1P#dz)hdNrp9Wh+UMOv*0u zy^&gOYkEJ!AOZ3^7XcGK1u)&Kk6@~<+X#s%F%xD(TurA4a2nnh4Vq3yaLfIRt19e*#gs5BY2Fw$Vo(0xdXMc=-zbE7a7(~S>g)cteMbX~t<*-ME znU>Ek=JtR5%t6uKFdZi&oX6WU;A02vkXyA8{p)i_GXNe3K)>4UpcpZL1X*0 zxs`JtkaF7&FZ;GOWNC7>t?th?=VU)VRvPd`wi-^J1Z=nifVyG{0;+;F>{%f(8)kiE ztwW~?aFFyxi>8wi+;aco$lBiTs@Uip9burml!Nl2Yq!>QwjX6Ou#Q5k7_F8Ar-ScO zT^?XRYitnvk+J%YMTHooM3f|U#tpGb%6f!UlF~fiMt`PmPg!;OZFKOg`LBi^dmnsv z!0HlluaqDMvr+H#p1M@lp;HpEz=srJ-Y0K2(hC9|0YY1hMPQ3ze-l7Wttj zm-|A4-lkPw!4`uLdBK7*U=`S6M2&d91&%B5yuXVl8a7xu4;g9UR<-DofTgWN9^&F# zvZM}cR18ZasQXwdwQC|yX}A{dHPwAmyc1osh+6Xd zJCia#O&o|6h|*>rG)}vvpIA5kpz*Pfn-U^N+Zvp`w>SH-)~4Cl+Lt&#+BSxq4JSZ+ zcR%Btv|iG-a0JBsl}(T!a?0J)OCAoAKIrc0WdyfeJQGG`2`n{WwFS>O3zW~Q4N*S# z)y-I=VpuRy3w!fu7~e~i=3`(KrH?+El^eEYw%7P*-Ws#u<>R_@@{bR!eHZ!3Ucq_& z(UVb;p5xHXnHXJw9KnB{Lx&U8pOYh?mkP^Qao6JxY0YbL3Xq3jsO?qi#*}g_$zzn) z5fcU}t=_-fUQtOI3B zsJTzG4RT#i#QB0jXsy{5*B(p=t@)8%QPUz77*Um9*A*dfC%7x~ds>-7^|gqS3XB^= zMD1kb)O!7P?X8N)Xgd6~H_FEs&YRCqySO!IuCR&Nc&F)6%LNUjHR5a!j_9db! z28ndxSV6S@2m|D$9J0B<&Y_4sp~ca7ugxu;;SR{b64`k3c`d4E6U6xs+Uza2m)<}Y z{ql_PoKMcrn-k9n^WC`d;(o!Yp#wS|y^YQE95VcY&#~IeCq($r*?}m;BLejt*UY+$ zq^oI9iWPQLsOJbo6)Q{)lgKs*&zq#$$(vvp^GtKtoxMIr@vrwzL528N+iWyM*-~Xr zu#}ZvSm-Db3AVjsJ!~e}1OR}jU&m<+G`@Y=>AeaL2F~R3O-d7ZX8G;`6Q^jskhckm zAO3VU+`RETwF@dXSat!)cmx7PH(-tyW5ELX%oaCb4U55eEDsb3x`6sF$(f_}&*TNO z7T;&wZ-LQ4Z+~;cAq(X;__{#eOz4YoBb~@ttBp-)3Xi1~kjh9P6r%4Mj5RJs{6Lhh zmo08TnQ}(wHP9Yf)Y$5ZvNJ0CpNy>z55J`_Ie^v?*Y;R_LiG4{rw56fCRXhDauW_= z-2Ls0)4<})4ep!50_KN+Zs`ABW7rhRBe6wwZqmJYK zTcNcJ9rmOqgatla;UR!d?ZnLSFA0z7!~!RCyp9?(oPo&KAonTwBsasBixYFz^p?{T zarV{3AU(+F2oQ=@;z6vkPYwu+x_AVLRkB`oj?{+J2M)z5pgHZVAmr^UN=yfcRbq!f zg?!`!KUP7*28*U4-2;BCGF3!c@3|=rF{4H1?1@zt@?KG|widPnV+ak}D(Jqk?Z8xY zRU&4VD7#`fRqBKB3B8!$mW!u>O(rNx&LRcGWAUkCK>b!sN7Qew4QyhXn7Xi%#W#wB zzDTqEz;ydk^GbS<(M^vCi~E8Gjgy>x833;OMECX)dcsAsMiVcg52si&Tl?tqA(G$dqQa8bt_!13&Uye(mIy}lino-kY zgG#PjJ(^YdAgJUc%||aJ?E1_4UCt&q;+~`=&A7QFqN=4J9$n9fj@EuiC3eJi;X-7z zn2o{$g{DGpqBjZ~o!=+wdDCX-s^^w+SDx4n+3c=AEAg|2nIW7wIhp^V7Zy57gsgD} zutxjm03hm_LST)HUUdGr0|x_#tP!-~Gf1xh6Q|tp1lD+5;Thyv9lkZ9VZ$P8BoI>x z19LP%-qzvcw}8FHz^VMtn553_Mc+k`yB9-FY&i9BsAq(px83=eg?`ZsybqmkAL935 z`MC|}9>Qr@UktVAqzAWLJjHBUf};B@(n|uVpyxcDgX`AE{&EzBFL|*B?qM#-`?0~* zYqx!+fF{v)H>SG2nD8=Ye`D%X7pctFO_** z1M8c88nM0>TXvOGI}1;|W=(#LG>)VFh+n6hIQwX5Yy3KjtHu6`%ILj=i*|3Wst?{b z+H={qM-8RZDCji*FXe*h#R4aDyj_GzqqJV$6Qsn8&)%T|ORX^y?!;_xKQfc3Fb@6f|CL#Q++0MD5KjGA!1$#h&k^tLd@fw z05K~hbj~V;gMmZD`~?v6b2EgPCH(<0-`9V&0dC&-#EgawmO?{X5iT(=^pMtjTYLa9 zBhhlq+T0$Q5zC30c~kn6h?yRF*6LJ~I*Qt|NTu+IdEptLQH9BfMn#oXVh!3Ojbbe^ zmjGh^ehtjGFP(_eqs%-p2V{8IO#6u3V5BO>%@Jq}?&@6j22L3{L(CdL!&O_yfr(QN zhb#odY>>$ZH*Z{GM#Tn8p;=DMpz`c(?rp@33TjKSM(vRqv7VTjHl;s`nCXybtxh$e zsV$apAyo>Om|cNJz5R)3)R}J|u?FptMzNNd`{dKn3+Yhq0WoI*Vy>Ek5cA$>K+N%7 zoqUiFWO9g@zX4+I>OhD&G5`?sU4zUt)*6vZ02($}3JqyR_z|-cFr&;e#EiU~zG98q zBQs(>F*9vSe-ts(AQpk>$6AzJz zHx^$Smat|@n=@p03;duN4IC_$hBPERZq`zt2P~<$6fBT@{%h$-tYLd(NvtR5WMEAx z$URXuyl#RrTP#$1bO6{LdN^~y(<*e$#aalYR^igKF;J>6$Ws~Q$ake5Gp7#C1O&ZwO56JDa1+NTXjF8tlp4~Ma0y!DO^s}JTP9*lkJ8FUVGY|OTVg#y zCjx?gj`Y_iLEeh`lL)#S^sH5@v;}CYizQx2t->Q{jclM)?I#eWYRV`Zfi-H6l!~OP7-uCn3*SNoop*rPt8WKeBp;%Ds@F44{Jyi>=Oh%?{LVDMr&^{ zb83Nx$FKy&8Ev6!;3SAk(CFx3DK(@i5g?>B+m9@pt;$CLxs?T^p>c&s~dVO#s@&YO!jtf{WM`1!`Bu?MO`u7p)f zz(Gxs-Ypm%ai||y39rrwhQ4cB8m|FwG$YS7lZPgiv1hk{iYB`05Zf<+#wX#x9r;Iq zX_KzN0m4!Cpa&Zr0wUp^&MUwkWA}pP3s12H>=7`m!9Wf&5y6@8Zr(A#9=3?!s2|4y z_9&Urc3B@zg81x#iVl|fLb3;bc$Wz*s_{NzQ3;7AmL>BMOk8ZHMBJ7l>iQuzIPwCyOiM8i)YYjsHB0V2`snge4u~=_YehqI_VQ^ z>Vv46?F!MK`!L#D4>2?O)~pL7CcRlbrZuZ*)4;bwG#ZjK)cIfZAO0z+^~k%(_Gp?fGD!Vx zK`V5j5~Cc{APWu@T-P>oJ;0>G0GX~vUJL9gIx*!WQ5G#0jDGL16^z2G+0YY6w7SU< z4h;g^gu`PbHb{?GybGpEd26V>RnlM%e-aiiVQdqKNmr{I^l(cdpzW)GWQ$V}$;N$c z!QAR#1TDn0y=tD7(Y7e5mtYXFfQ>)Ns(jK{9$6GoN6V@8M9M5sd`|8OM$#jXjs%fC z!HjuUsvd)pHv)y_49ayyY3my{sP>7Bf~kARj*epFG?9AugwL>qEcv$d8aNHd3n3iQ z(ZRA+R>u${*8=k@LwawMy|P^h;8!A)icg|A=^TwL2Zp6jYpa44Q^|^K1|4j zSD^0W!@1IWq7oHlZcDTzR_ojfL02_mEDb^kreE*F>r|yRaydXN7fFbaohn;kLw=%! zN-q+0Fo+_b9N{IQ^12RbS51?=a*wjJqVS9Cm3z9=Uld>G_X=9P^=r6+n>pu~99aJ3 zpVmlv9e@L+4`wX7NeOPfc=nuz${8#&XSTQlqQ+(Tw)&+twWb!!sK>zx zi8J29_*BS_pFaQVf0}%GSVDnvTkQ?FiQ{RPBRV=PQZL*r!03;F-&)BCzwJorggt5| z=q> zHp$rkDx63Oc#SfEYBN&zLRL=dWX}$q^3`T+s^9W@BPG-8F@i;!)6?{8?*@#b#m=Est zEb}*-UM_GdM_?Iolb&8piaB)@EQ2aiS{Rmkb;BD;%!hU{)YCRgIxYq z^f}%!>b~zcW3S^K>soI%^}pcSVs-v_i=@}>+mk+=xiR!rF1lV53XRf1Xe5HXX(Bv! zF$j%RWRC7rNo31Wv5YzT?ybpXyV`(j8$FD1D3;@NYWF&by|mN_fF zt&x4<%8MxA-q`Q3nH-{KHhSg|_ZaKR;$Rr1oy&y0YquNn9$h9>95CNVZ~f|F>CLNG zE!=G=*HXP-XwHZ=tI=(m7~C;#2V0;h4f+grsPAoSDGh|r%s{tkP{P3-i6aCttl zuR}eDW;HC}*1`)%e*~7#_rJ5}-mg|S?0cf>-RcP7(7^XiilE;9h4VrLj;S%spHRiioZwM8At(ig0ic*ju64IhOCj z*jN!=2tD#}3&C~5s0Z{|3xPa4Ctzxzmh#1klZaL|M}3DqJSS#q=La}!D*mE@ygx2V z*1mSPf6k@~xmE6Q5+8e_UwynZMUftObR>vuSxwTcR0I7z0Jy?M&vSG|uk~vPynY@| zNjbA+UFjKOOXeR1W=`E?wq7nFU9C+{18(AY*5!zf4vWmor{5*%HmhVL1;#h~+(^a? zAc{r6x_o|>Al8I^huyLgjd4GZxERl`udNTJK~qtaAN9U!Rg$Wj{JOI*%^W8vYuYBg zGjvtZm^91%*06lr9fzl(>l*umTznS?b~`V8jlep{S~tJL?n#L)fRsM3RKdJ#+AJ`N zqPmHrtnsOlG4jh3#1>rG8n3>B(koOC z4cxdl_3@HPTL#)4aIaZ1@o0a`Lurq#d*TDSUF(Ju5WWEx0*un1AQ+`~)WdF+33@SQ zdO@J0K@e{dKaVp%gA9g)8B+y@=N49Uc5PX+2@Vz9s7o0niIcPP5LALo>tT1wM5C@0 z=@rL^4%&1D3`1)dvehV6-B$5k$kr}`Vij7;yJ<_y-~%_?23oGZGwty0+vL%sdV(8B zE#3l$t-eUvE^Lbkd?f-7Ir%m606BTlhkDqvX`)I*uc_!@5dAKC`NC-rfv~C+;E^*O z!DDWDJ?yP*PK8y<^iqTar7wn6^iqObFJ4&Hg!W!_uQ|RFpnusei2ik!)x)M_h}jTI z?yM&!N&mt-oGkvRaEcy!xP=g47LZ*{e!L5jtl;T-*mMBPnT4RwuJ4kZIcon*n$^j3AI9-M=k`);&kWgT*Qs zDQjXf!$=d~rW*2EU28+`A_zbCOQBe*&PjNB@f9ym>qMU^r_1YKgCB@QU3TqSShXlR z+WvScANYU=AqM`$xbRqab6E%QZ}R5@qSd6#-Nt6S_Z~yqz6ytbAPdfvBL&5OB2TzC zl{$Vx#zpWk^~?f1bj*x8vK8Hdi9y=XC-~P48_C_gWQ-{P0(d zO}3~q8EAR`9&!g(U_@dLWyd5E0d}ZaeIa~SxUwAhD}HO^^hx=UkP;2}1nceG%Y$-| zTsf^YHYv4M*kRRZ|9#pa~Yh>eB z1cSlJd7GVK_u53)n15r>mEfCph>Vm%r83<*1>QH`kjhMoRDO5=-q8HKiU}`c1BKsx z44dy+8zAF}?lXw#D0n$E0K~KoK42_TG#prLsYN5^Ikmb80v!S(iTj0%tyC}jW`X4k zUwTocD>}V@Luo`P91xsI+;2D)VoMbf12d=UE#5AdP$1c+H4Sd!coCQ*IyzXUjX`*b zaL`xrZnABbAp?0h|o$^G+@1R8DX{oRse_3+J__jj*0l(;!^ zMBIk?LwIAAr{6HQGd)ndXY-fgE6{a~n7Q?23rT>4d#PTxn_S>@4)<;>D2S7dU$-5R zx~#D?>`s`d)NR6As!mWnk(`OT|RQgR(qjj3Iz-rH=fX&HH;{0#W)r(lnz!0Iri3opVDGQ1T;>~jqlmbnhVN(Vy z-&hhuMN5`mws4^I!L4LBDZ#B5&rZ`epm70<%oKlcw1|*JgX8UtPL@1zV7X%^3WU%qr{V|_o%t>WkkPRy}Fo|%o>TJB_C?Pye|_-HO*=CeRqf7 zRUDlz2=Y-M?ZDYh5azx%A^W}nPzyW101o-?z=j{Ug5m6iEs+UPWl>rauXE&u z_Ij`a8D6!(Me^yY{lwG)Ww%$U>q=;yl2OdxHhcMg<^h_s)=JgK!w~@}E%U3c=s2l{ z`=McQ0C6T_SD^M#BW=^%fV=k8vq}nD`3^#E+GIB&mhed3JyLFT^1axG*4y=$o zaCIZ>nJhtu=<^YaoC!e54k^MSkDJ>Fdq&Hth^3os;qVl?F=>IldKYhEAUe7zYA zqI5o4BqH+CI@OEGBITV=EUg?fWE#UF_H8;>DEq9!Wz42$ev8mO0@1<5_0M{Zjofe+ z3`VQ(m&5a;OnqiozZ}Cy&oqyG5PWX-^|i{*AL0o%L5(W3A4{@jP{m0iT{u<{tv|v* z4SWsRTwv!=WKw8JG~9HG+(KaEyGA2_zFfBvc2`1l)e_6aSKEuKBP}hJC~LRgn6_c` zbn@D*A9m@e9+FvdsE)R1>JBlz!->|rrz@H9Ozj!OE^lHQGi2|$fx&1sB}YG=PYb#F zvE=CW!p{MpI&!u=Jlz`k<@58+ZV7Htk80kIZ9|3l*NF-mqHKv-2yVu@2@4%2BEj37 zl^`~8egqH_Zq5a<$wa9JmnY$HKyW5DSxW-3Nh8ujiyCkP#3pM-wG}>xn>ZflIijP3 z#XgW}ZJg0D&l*`f3@Hs2NEWS;EjB`wYv$!X7-`gc0eMPI*01G~`JBX{jovMnMhKS0 zjB=@Yc*MxD($2r(QV)!0DKMVn(-7m?@g5k@WT}RJ_XFT`kVD2J6k;n#LQb=&m)2~M zOIS9ltzt6V#BtP@NSg0j-$cN)j0uE`6Bim1d66OcyQVnv+QXug9fKr4k}YglN#`9 zn_bGtj6stwY6_npb3q!T`wfu|!aC4P{Z=RPio0-Q;5*PueP)AG(q%X)afZ^ih=$mT zt#d*sEpg^nxdh{(ZFd&KO&pif(9vO$e&PFn@GwVZY8zM~+5Kk;Y+8vWH>h%Ad*YA6{^*h> z#43hXs^L7=U>ITfwfVZD$}<~QPxgTWibJd-0a#@VQUyyj)C8=^&jWpQ3H(VfVY6S+N2~9n^PBmPln1AZg zQ5No0f7x+cri{NL>mjPLS@g&wodlC@BSxUxEYX<5BKLI1B9BQ4$Y_Dfe;o1e3NpO4 z{Kv%XqN~)qCS^4HjA(&S{g3_AYs<)2#e&fLkg)J@WC7?rTUA6xjC{wpY<6uPRin#I zhF=)|4OzAO+y)_y3(v?W=Qijax|clblHMniQP*#=YRu@#v=7|fh%DzQZ_;jMU^!XY>b z;*p-?I#hI6#CkZR_ga1z6-XAXku5etlwoI2oO|HPhp$M7I@u*BPesa3(b>HGXyU2@vJJfQoS({xtk>HWcXfJ)Sa)PaZH~G@h#ke@fag1JnCUl zU_4pkZ9*GZY&_`but>4~7~??$l0|F8O^z%8eFv&?tAP~BS1PmFl^l88de$#xnLEk~DGf9{sCxImcS*iv!)9iPMg}=kBvnu4J+Vh9tuia>rlG}x zpH*2=rR3RjVt*7~`@VSDiu)fjuD%$$b8Y3_g6PNSy7o^|8ogZLbdIB}Vw9qz?|$?; z@MNB5_qOPUa?6aZGCo4{K{xw)xqNY*>Lj%1l$&ydWs{S(-9k^TK@XG`TrS(~hr}DiGDzR6Vw3 zb-2jvjZ-~j(RJ+~VwLW4VXe-kS)l1Pmh2%*4mnC`f;q!0IwY@V07X;0owhUEaY^9F z;Iy4SDH~%q2R_^9d2PWxo8WSVv{SPpC$(er#2`Rv9xHcSsTCal0)`R3rg+;xREAt$ zXjBa+C5}+q9lHS`>mB3YgPBv$m6nMm7>S&yaf6#U9;IQR!y^5{3zXDl#sf-=b_XjY zw>icsV-1TzX~al|)bb$4`^&nANael4>^w=Qwyf-TI_dpf8&WS^TJ}=Ru_5>Qy9F8A z$6AWs91}yQGhzu%b{}D>%S;4t#fBnqWjn>ec5>#>^`7oRhEw3acZAhlN^tANGgx#| zz%m?2e?a62%L^P~Rhj}7@94fxvXJ~lUm&M@hyYbeb^w^^d2HN`i0UQ2xYzp|M}0j~AO(%#iS3V8ZB zIOF2i$opAUcZ0Er?1wXaWB64SD&72f9OHXtFL}XG4f$VF*ctt?|f(J z+5%*>hwApP8@05J?y1}R#mdYUxiRIO%QxX=Jn^GFp9Jn-Dp+Qd;}2&uYzbqCQGR!6 zp{pXHFvSjpDfY-4PQtB)AWV@Ympk68g#&^^VTvROQ<83gnNuq+f-q&S$O+#%xQXMj zp2td5bg*m#lJ;;$@2xYeJ9cP5vS^KLu@R!?zj^H}iSdd8k~~(TqQfG^!Wq5yj0X)!7OfF?A0v6QM$*~W1W1uQ{CW(#QZw-p zV`M+Q81uhoDY}7a2i6px?WY@fY~tbK*AjCcoZVoZUL>&Q{4>@coAx_)G+J7nv%wwWOOSc79FCC2o9S* zxB>1aEV%Xl#m$|cd@-S%YN|$zh$`-4-()S}m(FvJ$==S|h+?9(JD|v`2}VaN9|Aw- z9lwsDL5I3zC@>|@UoEHW17l#5NJI(aqMKEucYP;PRzwRu>mBoYaO;B`XT2M)OzmpA z=Xb0uMYX0a(Cv7x!|M3>byD0Iw(>x%eymrSv)oe@W#R+-u-~;7s^?&@*Jc%j?fz=G3caYQ+-L#ZKgZgqt`{ z07~*eM+eJ=K<*v!L&NL9+`P2F3du&HF|gMT2pT}sSut55Av2^S{=haxyKQy{f1Haa zjo7kXt;Of(%pE-}@}6tLJ8NPa<$tYpEicQ`Y=>_eYr7Vk@IKyi+t*m^n!;2r7wDYdMUxJ7te_A zpb`a^CP3OS0SycR8hADv(ZJB)7}%UFvG8-#{*14OOffE&htx4t5NNN5Z zQAj~P-->6vSYpE1lMwZu^fG~41U_v?0f~oJArimr6~nHk<>>=L%x8adNCLyqu6=Rd z%$s9bcKwU9l2OyT;QJr1jF+jjjR0IB7)w=m-xaH!R*d0_e zW|4&vsZcl%Dil5i0_Eq;%EB5JW3ND1=flApOa2jH4AK>JrY35u%0rz=gCwN-A+~B` zw{PmYwk^WucKddyR41UI*e z+#M>d1h)*fL@WdY>}~*|>p78##MIeJ?d=Ifp@c(<0}Mnfh0EXFsel85LlKD|h)4!5 z12d<}8iI%H1#aSabm@VP4wlhDMkIaDH!;u;YvI0g1WlwYLYn~Ti~yu_3VAk) z?ET;#Y&MV>=~N0om*!WmKx*{(CY~@ww_9Qy&_iM1^|Dr%7z5yNM-D{DgW~S7YnQ#I zaQ+0ZXWGc+8QKxGYmb)9jM%YD?OL78kg##vl%qZv^^2Kqr*Suj?`W*G@}9=4=!g;Z zYftU^&1nA8`M>56XWjDG9ucWIdDiV*|JBrpOMZSQ4d%-RJGuEj5VBg>==8EM2~NK` z>JaFqg$@%Thj0owghRuCJcLb@fJ3MjE+4(1Pn7E%xd0qOKO|1ijGU){Cc+cTlySMG8q^M?(J&I@Y)tfjUvzR(Lpaj*aPw zwLpPr)4r)|k2K2=8~06J*m8f`0=3HFGUGNE=uY{rGWJM+SBY&OEX?2_#(8Z^T`aIh z^5oVK#OU5EzeB@XBgVFsm@}t`5uLpWT3dv9AlLvq8G`weHo+mmNe9*qWpoILII!jY ztkm)@BAa$$oups`QFFoanKvrofZ)uI<^(BZ{h8)tVCK~H>s4Y2Yno0(L53iBbm4)H z4wgRTe||IageFZ#u&(+2`eR$?3)aP* z)pZzU(yt;sY4HUW1KG-p&WZ{0GL+_cbX_B6ZhyzgO*gr~84FujgQ%=aO~ap5RIOVL zWUV+u%sr*#z5FW;G53=mwNuZG<9BuHcrnpgL;ii1dy3_28;sY62(bzUu*x|dFpO}& z48SUz1j}~?LRQ0Yh*kIjt8^kq*HoTBz$#`f;-nr} zo)ngc6DEElL=2qXpWLO4;AyOLhdmV)5W5Uw5tOyx4kg^ogsPC%~mB5B~qP7d2}Hx67JNjG6`~ zK|Dk9SdNMgi!2E@+E)QpmOesAgL?AdDYw{FQQ<0WdA|6A0FqszUEFf2p>fc*Yv;F| ziiy>uy+(r7s*gX=%K zF4>pIyDIVpobKX>zA85Wpu%quKtr@{!6qnJo?s{HP3hGD4wSxU(CXc!1h-xSN&t2e zK72@*HL}XMTbR`+7ztmND*-_>{l`yPROp2UrxSni#l})!ogF4m@>c?F79JIot}sZn z#bripDw8)GO%Qr#=4PV`$SL^n3m|{JbJ8u?oB~nz(e-0De~^L*^3ocuz5}g$E6e1zm9llw0lk2?qpcQlh6ELe_hVEd?{D$}DOUOUVCz zVuKdk#BsM?=;-uXU>FI^P3i$yA^ExTE!g56f(Br8rtIrnZTT)R%0S=c>Z8wiSB)%L zxo(6{LWPpwtjSx@Rg0)C$E4X^>X02W0DQZW?XIKQ?ZWZihjtzHi5l@RZ^QmA&gELy z3-@i`{J3Ragd6S1VL0&-2mc?CjRVX^@qkcn{bg>DoaoyE4>njq6KVv+6NOX`HO1s~S z<$j!k46@?%U4?kTW&KT0-`!d_(Rlt!-~Vgx+T)?z_P#Nr7z_!ymKaHCk9!xFk`s<@ zqoNB!E)}BdHd1L1QtdEB3MCFn7e(8>3yC5fN~n}l%}zUUsO*sHtY`1{{k-pt=Q*=h zjcv0}efY=cdDdFr-|hSRJ?pnt{H#|C#=1$?cc3W*#C{hQg97D=?LkdoTyma5B#|-@ zY!9}8x@e!U+E@)C86h;Yaj1$aEoS(Vp zV#k_jPIyGAon^DwK<*JGd|S}6jk0%1F-oqhzL$IN5bjBFovL&nU(zs)(}C>AxVs+P zk}NsR^rS6q1C6`XWmtTK-zS;j5wD{MW=?fyy^|?h_KB@5k0xP3(TanQ4v~iv_SPP{ z4)7`;)_@gK6f06uizcv|c1EwP(&_VY2`~)Z_iZBUbk2(KwQm!RULIOq5!`b?Gbe0u zm48Q+Vt%7p~Cx6{^V_bGYU?XO$c_R0czt0nUP$+V*VP-rzA%j{`9uO&a_$RgU z&WZ12V_T#f!*OLL)b`1=$>qkMmYRxz8HoohopRPO)Aa@sj06l0)=`De8MnWsZ8Da< z(l^=0)U6OnFf*tTY2Lfktit=1stqbVidO}e5p{|Wx$MKJe;S8anx9wq^I z+>Ld?QXbt*C9Jm*tP(DH7}gz>bY6h)0|gW*p#W0iCl8R8Twx4Ii7$;iX*CyDnF-}N zG-a@T;((NBjs-KPy6ps{WcerdB%zZMTy%(x4v~}$zwm_TfzVV%aE!u}s*nJO@H(xP56=@#Eq}&v&#kWXx`UJZVhccm))e3d``cypzFc3q*Jc_MIV~P zYcfO6T|M8p_R^vjf0 z_pp1mp-o(nrE~Dn5t6A3EF)=L?od1=iGBT|)XYprEhE9vQPQRoo6!aD=XHWnv=3W^ z*LfK>DclLIZ}c#1BHx)3-@Hb<+963J>WGiw38mzT2Wtt+NU*GJueeJbsreChRwKPX zw}{cr*pbndTjW(NwY~LUS1)X9SNCh{%RFzD=51Mc>R+!t(F7{kdum()m{PI(Nhw=q zq@(WVfn#mL-+9fa322}UzZkC1CndD?3bN{KT+i``8FevGKymDN0*c10bkzM%ux1u; zqVjP>fLjY@W?}l&vsW&)&572hw!A&sPU6f`>pHowUphihz2f!h(@u4MQZ4ua1kQzw zjM}gUuUybn4ubCtI81^_;RqNHtmJ1c-Zk%|ww4Me`QQas4RmQU`K=4AlKsB3aP+lV z9%DXRZ)GUc>C#5^pw%HNW2?{>3gKW2fPRzBF#TR^kWSbtM6gOY2Rp3OVhvt^@GTD} zEw*{*$EfT*2auL*$+&DwJFd)4uI)#Y=MYi11VG&uEd(>ClEwh)cB6+K5`s2yK~A27 zj}DR1jrg>9fcp_H+}Q{4L@Fe-A)HP=a5|*DnA7PuN++zveba}0wppaI%X69*7=+C9 zi^sQHxV)4v_x+YveWP3jo>8Pey+bkj9T!8EbFGMQiv&SQ95!fa_O6OBIR7iwU`ij8 zaBBirH%dw^;nuRV)E^g$#y+0cubOBzE~0r;!OY4ribqZmTuTBwTI9w3q=Jjp?qD=6 za(ZKed+&|YwDiV0MpNl4+4H5>S=D#BVujZ}m|lsz=#lIXJ z`QXQ>5@7*=B#qs!*wPY|x%Q@AXg~~6Xo&)$WhQptG4-Sm2rYFz?5Bv%U2tf@MduH5 zcIq>LNp>FrOQgKGS%g{^fMZw)DJ)!)Kw1_}-gnt9YY~|CShMTq4?hO9&SE^A_TaSt z8_TIRh;4bpFs0>_8k#K$QpxNCcxo>sjf6;Uc-w7>#wFnVux>k3t$VI4^~pw{LG-aX z0hU^O*6kUe6R^$yWWY_4ODr$5+|X);tYGG*>-0qH*7cLoAQsfMtZoFXt{rP8rT9NB zBCJ;v1VT6m&I=>H`-U{PVR}XpAIkU;z(g?nEq;vK5@aAQ$;D2EwlsAGu7TxeG?f^d z2o?j5=uC>-4`xoi@!&H!b@e0rN-o;O1-T0jJ~~8pix@uz*0t4*fhl!VW2RK{xCr$a zP*`^n($r#k{k7gTFpSnetfSlAV$VBXu#V1<&CZn1`TNlV+tebZbhm`C(Gkz*o>ZyB zS2Z|m8yOWaKCz&w+@R22SW){Jne@I3bjD63(+n_{Y(8pZYv*Y!8Gk>>MZ&|9cD&ei ziHxV}>WUNOQA~4!rU@vg5(J!zOf48jGK>kFin0Rt(~LeepoYk)fRj6uc3%ZEr@nm( zoJ!?K_PzqNi3@frxaj<0PK5!S%F6{{g_IffMTDQl3F0%Hos-7X?WJ6q^&yy|$z&K9 zyKC78I~UB>_n1lvQg}amwhzPB*Ex^A)0;jgg!~@grNAn6i1h`I)1FhmH)>ZtXLUWE z?zpn@xzW`x2J*IL?=^qv&`?@Z->sOIymiPiCR;=}i@?%5MaB0s` z=T!qVP=;e#!7C-S^$IfS>=-<^AtZ|yJUAW^64Ky;*uDwq3`+B?!j%7tLlWv|;vr!B zouE;cwinEo)QzSCgA%ym0MO?{8Q)*R1m^F<{1{zszaObB5@Abg*X8Q1MeGZa{liLYyLG+<=V zeLlIM>D=&(l!M#0fNREon1yKr3tP*;EX>(43AHo@r}L!zCS8y2Pim9FC|V1>qBx?i zC3WKZ&52_rPQ_boZcZdymo&JTAN#>-PHx>ouV#Gvg5|!Us$nYj>$jz0gO#dCdxdlc zI{Z^bnqhozz3e?%RZGt@mfDR`D%8-{DR$RnrOiK53&?UWosw07Pou5to*h5WPy6Rx?UN!m`F!O$MKsLA<8@BKblDvOU3Sm0Jz}Es z>xrTyfnmXhke0M7+BU6c{Jh80}%)sBw=u z_oS=Y1eXiLj`li3Re7=O7O94NKl_3qot z!F21>&YU;-fhO{W_Q!ft0!S{E%KuDn(fvk@RjziXruO-b!A9FmNscKEyJZ|oNiL5` zxxLMUwzuIyPIK^9rG3vh<$JUyyOqV_VocZ|Hl%qlue9*F6I33&2jszCSjkW_#0=!Y zuXMR$E{GRg3{f5oda5?IP6jildKH2^c)2cH_9>bK1?R!I=n(lI#IIh0M$dM|TcMB! z|Fe9ePo+=$x$9Yj8)l;B#Su@#S2JS_iX-BxcRJNDvSTBT`P-Jw$~csBUu{gn+V;DZ zXaXJ-6NUA4$4qC=sb2MJ5?Gq%yhzKRC)&@wXcuXDJ|JY}JNqcJlVpeG-FBJl&P;TX z$(r!s>rHq&xai?M>l9|fZ92QbH5l65!&c>JKn;xvyMqJmw~TvsxYjk8P>R6va$=pTL0+xmGlCe^orG* zl=SF_WiGpx%gr(=8=vKHmtscRH}exIa!v>fU)SLBg^^Lp;**PTbuJrMuZ5&?BRJ!D z-|L_6f7R6ysIk-x^II}cbdE)78r~Ks&NoP;jVty>rb_Q{D zW-wRvp=Y=?BbdvKpTib4*z(runnNs&?)`3Cj83_8@K&xAz8S-g)^DKEQJ?=I6&tPW zXkA~-(s^~YN9%G*H(JNuJyf1@#AC^c^6-kdr*cb^569klgJvTVqMD=gj*Y<%8WBA> z?_}GkwR3mW{8k$2IxAyJ(l^Khh;I2!AUmuQP-`WS^(_Nlv3(m}*Q-`31+7@S8^h~` zxcSOp-7D=K;^uOu)Jx|&MW>KGRL`XREI)O+-pn)a3n;#b{qKXqRA6D^l?6UbFv-Mr zDqtepR{?}16}15q@zUn5v%ZO@I77rlsDO!Rc!QZ!bwdFYag=Af2>aL#d~}2q7XD1t zSU4gfD(rO09Z!Zrg7>KOapgY0n+iZd$To*`ar_zRCra6R3H+T>Ym-3OPxOr?sv{X2 zs`eS5!0tn*|Io=il{vny=s+j)O{+zH&!Ik>PuY4E?<3#XR(+N_*j;ylT!e4RaPs*U zq6bsaI|wRzEqlRAM0@TNNr?b1=miKoHw)H7@=6!iqrunWj4yUYvssU@K1}hJM9oKc z^FO!`&?p`9uN`CTn6j8gd3mx>H41KF{Ok!|U*O(#!IKN_xb35Z36mD$B1Bk8lh9y{ z&dbxrKA(vi;z98NK;dv5gQ8#!3zaOu5~AXU3K@Tod!NBD^y8Ps<_6@a#&Ir-NlZL^ zsv$amtkU=4`45v)UAV}4B$LWkT3qM*}b?D0%*7V5S*xVSW6Dfs>!nt+`Llpq;9fSBZfreT7< zSL%>I0yI{{U}${wW1&`7;n3iR93Ko=elc;(f}774T}S~_re80eQkyZ`Tc)OTO875o zo{R3>GCzD(dh^_h#}=ubJGaa|STnT&4X`2Q1iZ4qhY5lc9JzYV&nU|d+j1beeB484 zTG_J~lUXOwfEW@^P!z3~;Zxlf4`xnn$~`Y$wxw@>#sjp83*rO>=#0Ri1#3~2FY*(k z&weonsH?bfYEwQ|9C^8fQ`?sNBI@*_J#B?w>h5H%su8Xf@FguIAwYbU9dLXH(a;3F z)6lN_|K-kT2*M=)9FY7!X#jE)J^7Mo0fV0WLqj1biovJ;0yh!rko!SgTQwIzLor>? z8l1BlOy0cSd@k*i5g0_=`RJp*v3nAj_)Z(v5+yuL_8lws#XfY>3&fsWd}X7cQ!MtR z_muZopLChZSDjl$eRGtn9$z9Yu`Fm`}9S3KEEv%~6~suOg4GfJk;-zBHx+ukYdU_diiR8;6b z%3%CP3*m~PC>l@HLXyIGmy^nQ?hZeI6_IaK^alF0QNUr^bCki~DI3y42B(TWfD+*w zG^9@(-NPB3TP$V?rYtIBgeD6?8wqqqflSFh+6RL}?n(%1B^%Dg>+oTYj|SYk280VV zJRk!10O1j7=C=iIKH$-o@n@=+!pUs_s%Q%y5%oRhe3B?tgbFyWh=>b|;}ZV3KGU~^ zB!c~57i{qU^W72{z_16uoIL!ud4J~_=(UFb1-gz|HAeMh5$uP?^K=sS+W&n4?z2Bl uKb{p6OcB9; Date: Thu, 16 Apr 2026 15:06:10 +0000 Subject: [PATCH 05/19] style: use diamond operator in TokenType keyword map Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/a70a1008-ea05-437a-b5e6-06da46bd1675 Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- src/main/java/cod/lexer/TokenType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cod/lexer/TokenType.java b/src/main/java/cod/lexer/TokenType.java index 931304c7..f19c19ef 100644 --- a/src/main/java/cod/lexer/TokenType.java +++ b/src/main/java/cod/lexer/TokenType.java @@ -68,7 +68,7 @@ public enum Keyword { F32, F64; - private static final Map STRING_TO_KEYWORD = new HashMap(); + private static final Map STRING_TO_KEYWORD = new HashMap<>(); static { for (Keyword keyword : values()) { From f9eb369f0697b71ce12d471fe52e4c1049b761eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:42:20 +0000 Subject: [PATCH 06/19] feat: add cross-language benchmark suite and remove legacy enum fallback Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/c0b02bfd-08ae-45a6-b95e-561a00c02888 Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- benchmarks/README.md | 47 +++++++ benchmarks/c/cross_language_benchmark.c | 50 +++++++ .../coderive/CrossLanguageBenchmark.cod | 57 ++++++++ benchmarks/cpp/cross_language_benchmark.cpp | 50 +++++++ benchmarks/java/CrossLanguageBenchmark.java | 48 +++++++ benchmarks/python/cross_language_benchmark.py | 43 ++++++ benchmarks/run_cross_language_benchmark.sh | 129 ++++++++++++++++++ benchmarks/rust/cross_language_benchmark.rs | 49 +++++++ src/main/java/cod/ir/IRCodec.java | 3 +- 9 files changed, 474 insertions(+), 2 deletions(-) create mode 100644 benchmarks/README.md create mode 100644 benchmarks/c/cross_language_benchmark.c create mode 100644 benchmarks/coderive/CrossLanguageBenchmark.cod create mode 100644 benchmarks/cpp/cross_language_benchmark.cpp create mode 100644 benchmarks/java/CrossLanguageBenchmark.java create mode 100644 benchmarks/python/cross_language_benchmark.py create mode 100644 benchmarks/run_cross_language_benchmark.sh create mode 100644 benchmarks/rust/cross_language_benchmark.rs diff --git a/benchmarks/README.md b/benchmarks/README.md new file mode 100644 index 00000000..760cc305 --- /dev/null +++ b/benchmarks/README.md @@ -0,0 +1,47 @@ +# Cross-language benchmark suite + +This benchmark compares **Coderive** against: + +- Java +- C +- C++ +- Rust +- Python + +## Workload + +Each implementation runs the same deterministic kernels: + +1. Sum of squares from `1..2,000,000` +2. Iterative `fib(35)` repeated `2,000` times +3. Naive prime counting in `2..5,000` + +Each program prints a single line: + +`CHECKSUM:` + +Expected checksum: + +`2666668685121930669` + +## Run + +From repository root: + +```bash +bash /home/runner/work/Coderive/Coderive/benchmarks/run_cross_language_benchmark.sh +``` + +Optional runs per language (median-based): + +```bash +bash /home/runner/work/Coderive/Coderive/benchmarks/run_cross_language_benchmark.sh 5 +``` + +The runner: + +- compiles required binaries +- runs each language multiple times +- verifies checksum consistency +- prints median time in milliseconds +- skips languages missing toolchains diff --git a/benchmarks/c/cross_language_benchmark.c b/benchmarks/c/cross_language_benchmark.c new file mode 100644 index 00000000..0a47701b --- /dev/null +++ b/benchmarks/c/cross_language_benchmark.c @@ -0,0 +1,50 @@ +#include +#include + +static int64_t fib(int n) { + if (n == 0) return 0; + int64_t a = 0; + int64_t b = 1; + for (int i = 1; i <= n; i++) { + int64_t next = a + b; + a = b; + b = next; + } + return a; +} + +static int64_t sum_squares(int limit) { + int64_t total = 0; + for (int i = 1; i <= limit; i++) { + total += (int64_t)i * (int64_t)i; + } + return total; +} + +static int64_t prime_count(int limit) { + int64_t count = 0; + for (int candidate = 2; candidate <= limit; candidate++) { + int is_prime = 1; + if (candidate > 2) { + for (int divisor = 2; divisor < candidate; divisor++) { + if (candidate % divisor == 0) { + is_prime = 0; + break; + } + } + } + if (is_prime) { + count++; + } + } + return count; +} + +int main(void) { + int64_t sum_part = sum_squares(2000000); + int64_t fib_part = fib(35) * 2000; + int64_t prime_part = prime_count(5000); + int64_t checksum = sum_part + fib_part + prime_part; + printf("CHECKSUM:%lld\n", (long long)checksum); + return 0; +} diff --git a/benchmarks/coderive/CrossLanguageBenchmark.cod b/benchmarks/coderive/CrossLanguageBenchmark.cod new file mode 100644 index 00000000..cfbe76dd --- /dev/null +++ b/benchmarks/coderive/CrossLanguageBenchmark.cod @@ -0,0 +1,57 @@ +unit benchmarks.coderive + +share CrossLanguageBenchmark { + share fib(n: int) :: value: int { + if n == 0 { ~> (value: 0) } + a: int = 0 + b: int = 1 + for i of 1 to n { + nextValue := a + b + a = b + b = nextValue + } + ~> (value: a) + } + + share sumSquares(limit: int) :: value: int { + total: int = 0 + for i of 1 to limit { + total = total + (i * i) + } + ~> (value: total) + } + + share primeCount(limit: int) :: value: int { + count: int = 0 + for candidate of 2 to limit { + isPrime: bool = true + if candidate > 2 { + for divisor of 2 to candidate - 1 { + if candidate % divisor == 0 { + isPrime = false + break + } + } + } + if isPrime { + count = count + 1 + } + } + ~> (value: count) + } + + share main() { + sumPart := CrossLanguageBenchmark.sumSquares(2M) + + fibValue := CrossLanguageBenchmark.fib(35) + fibTotal: int = 0 + for i of 1 to 2000 { + fibTotal = fibTotal + fibValue + } + + primePart := CrossLanguageBenchmark.primeCount(5000) + + checksum := sumPart + fibTotal + primePart + out("CHECKSUM:" + checksum) + } +} diff --git a/benchmarks/cpp/cross_language_benchmark.cpp b/benchmarks/cpp/cross_language_benchmark.cpp new file mode 100644 index 00000000..0395caf4 --- /dev/null +++ b/benchmarks/cpp/cross_language_benchmark.cpp @@ -0,0 +1,50 @@ +#include +#include + +static std::int64_t fib(int n) { + if (n == 0) return 0; + std::int64_t a = 0; + std::int64_t b = 1; + for (int i = 1; i <= n; ++i) { + std::int64_t next = a + b; + a = b; + b = next; + } + return a; +} + +static std::int64_t sumSquares(int limit) { + std::int64_t total = 0; + for (int i = 1; i <= limit; ++i) { + total += static_cast(i) * static_cast(i); + } + return total; +} + +static std::int64_t primeCount(int limit) { + std::int64_t count = 0; + for (int candidate = 2; candidate <= limit; ++candidate) { + bool isPrime = true; + if (candidate > 2) { + for (int divisor = 2; divisor < candidate; ++divisor) { + if (candidate % divisor == 0) { + isPrime = false; + break; + } + } + } + if (isPrime) { + ++count; + } + } + return count; +} + +int main() { + std::int64_t sumPart = sumSquares(2000000); + std::int64_t fibPart = fib(35) * 2000; + std::int64_t primePart = primeCount(5000); + std::int64_t checksum = sumPart + fibPart + primePart; + std::cout << "CHECKSUM:" << checksum << '\n'; + return 0; +} diff --git a/benchmarks/java/CrossLanguageBenchmark.java b/benchmarks/java/CrossLanguageBenchmark.java new file mode 100644 index 00000000..ff7b438f --- /dev/null +++ b/benchmarks/java/CrossLanguageBenchmark.java @@ -0,0 +1,48 @@ +public final class CrossLanguageBenchmark { + private CrossLanguageBenchmark() {} + + private static long fib(int n) { + if (n == 0) return 0L; + long a = 0L; + long b = 1L; + for (int i = 1; i <= n; i++) { + long next = a + b; + a = b; + b = next; + } + return a; + } + + private static long sumSquares(int limit) { + long total = 0L; + for (int i = 1; i <= limit; i++) { + total += (long) i * (long) i; + } + return total; + } + + private static long primeCount(int limit) { + long count = 0L; + for (int candidate = 2; candidate <= limit; candidate++) { + boolean isPrime = true; + if (candidate > 2) { + for (int divisor = 2; divisor < candidate; divisor++) { + if (candidate % divisor == 0) { + isPrime = false; + break; + } + } + } + if (isPrime) count++; + } + return count; + } + + public static void main(String[] args) { + long sumPart = sumSquares(2_000_000); + long fibPart = fib(35) * 2_000L; + long primePart = primeCount(5_000); + long checksum = sumPart + fibPart + primePart; + System.out.println("CHECKSUM:" + checksum); + } +} diff --git a/benchmarks/python/cross_language_benchmark.py b/benchmarks/python/cross_language_benchmark.py new file mode 100644 index 00000000..8e609ac0 --- /dev/null +++ b/benchmarks/python/cross_language_benchmark.py @@ -0,0 +1,43 @@ +def fib(n: int) -> int: + if n == 0: + return 0 + a = 0 + b = 1 + for _ in range(1, n + 1): + nxt = a + b + a = b + b = nxt + return a + + +def sum_squares(limit: int) -> int: + total = 0 + for i in range(1, limit + 1): + total += i * i + return total + + +def prime_count(limit: int) -> int: + count = 0 + for candidate in range(2, limit + 1): + is_prime = True + if candidate > 2: + for divisor in range(2, candidate): + if candidate % divisor == 0: + is_prime = False + break + if is_prime: + count += 1 + return count + + +def main() -> None: + sum_part = sum_squares(2_000_000) + fib_part = fib(35) * 2_000 + prime_part = prime_count(5_000) + checksum = sum_part + fib_part + prime_part + print(f"CHECKSUM:{checksum}") + + +if __name__ == "__main__": + main() diff --git a/benchmarks/run_cross_language_benchmark.sh b/benchmarks/run_cross_language_benchmark.sh new file mode 100644 index 00000000..62d6a1c2 --- /dev/null +++ b/benchmarks/run_cross_language_benchmark.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +BENCH_DIR="$ROOT_DIR/benchmarks" +WORK_DIR="/tmp/coderive-cross-bench" +RUNS="${1:-3}" +EXPECTED_CHECKSUM="2666668685121930669" + +mkdir -p "$WORK_DIR" + +has_cmd() { + command -v "$1" >/dev/null 2>&1 +} + +extract_checksum() { + local output="$1" + local line + line="$(printf '%s\n' "$output" | grep '^CHECKSUM:' | tail -n 1 || true)" + if [[ -z "$line" ]]; then + return 1 + fi + printf '%s' "${line#CHECKSUM:}" +} + +median_ms() { + local values=("$@") + local sorted + sorted="$(printf '%s\n' "${values[@]}" | sort -n)" + local count + count="$(printf '%s\n' "$sorted" | wc -l | tr -d ' ')" + local index=$(( (count + 1) / 2 )) + printf '%s\n' "$sorted" | sed -n "${index}p" +} + +run_many() { + local lang="$1" + shift + local -a command=("$@") + local -a times=() + + local i + for ((i=1; i<=RUNS; i++)); do + local start_ns end_ns elapsed_ns output checksum elapsed_ms + start_ns="$(date +%s%N)" + output="$("${command[@]}")" + end_ns="$(date +%s%N)" + elapsed_ns=$((end_ns - start_ns)) + elapsed_ms=$((elapsed_ns / 1000000)) + checksum="$(extract_checksum "$output")" || { + echo "[${lang}] ERROR: missing CHECKSUM output" + echo "$output" + return 1 + } + if [[ "$checksum" != "$EXPECTED_CHECKSUM" ]]; then + echo "[${lang}] ERROR: checksum mismatch (got ${checksum}, expected ${EXPECTED_CHECKSUM})" + return 1 + fi + times+=("$elapsed_ms") + done + + local median + median="$(median_ms "${times[@]}")" + echo "${lang}|${median}" +} + +echo "Cross-language benchmark (runs per language: ${RUNS})" +echo "Expected checksum: ${EXPECTED_CHECKSUM}" +echo + +# Build Coderive runtime for benchmark execution. +echo "[setup] compiling Coderive Java runtime..." +rm -rf "$WORK_DIR/coderive-java" "$WORK_DIR/java-bin" "$WORK_DIR/c-bin" "$WORK_DIR/cpp-bin" "$WORK_DIR/rust-bin" +mkdir -p "$WORK_DIR/coderive-java" "$WORK_DIR/java-bin" +javac -d "$WORK_DIR/coderive-java" $(find "$ROOT_DIR/src/main/java" -name '*.java') + +# Build language-specific binaries where toolchains exist. +if has_cmd javac; then + javac -d "$WORK_DIR/java-bin" "$BENCH_DIR/java/CrossLanguageBenchmark.java" +else + echo "[skip] Java benchmark: javac not found" +fi + +if has_cmd gcc; then + gcc -O3 -std=c11 "$BENCH_DIR/c/cross_language_benchmark.c" -o "$WORK_DIR/c-bin" +else + echo "[skip] C benchmark: gcc not found" +fi + +if has_cmd g++; then + g++ -O3 -std=c++17 "$BENCH_DIR/cpp/cross_language_benchmark.cpp" -o "$WORK_DIR/cpp-bin" +else + echo "[skip] C++ benchmark: g++ not found" +fi + +if has_cmd rustc; then + rustc -O "$BENCH_DIR/rust/cross_language_benchmark.rs" -o "$WORK_DIR/rust-bin" +else + echo "[skip] Rust benchmark: rustc not found" +fi + +echo +echo "Language | Median ms" +echo "---------|----------" + +run_many "Coderive" java -cp "$WORK_DIR/coderive-java" cod.runner.CommandRunner \ + "$BENCH_DIR/coderive/CrossLanguageBenchmark.cod" --quiet | awk -F'|' '{printf "%-8s | %s\n", $1, $2}' + +if [[ -x "$WORK_DIR/java-bin/CrossLanguageBenchmark.class" || -f "$WORK_DIR/java-bin/CrossLanguageBenchmark.class" ]]; then + run_many "Java" java -cp "$WORK_DIR/java-bin" CrossLanguageBenchmark | awk -F'|' '{printf "%-8s | %s\n", $1, $2}' +fi + +if [[ -x "$WORK_DIR/c-bin" ]]; then + run_many "C" "$WORK_DIR/c-bin" | awk -F'|' '{printf "%-8s | %s\n", $1, $2}' +fi + +if [[ -x "$WORK_DIR/cpp-bin" ]]; then + run_many "C++" "$WORK_DIR/cpp-bin" | awk -F'|' '{printf "%-8s | %s\n", $1, $2}' +fi + +if [[ -x "$WORK_DIR/rust-bin" ]]; then + run_many "Rust" "$WORK_DIR/rust-bin" | awk -F'|' '{printf "%-8s | %s\n", $1, $2}' +fi + +if has_cmd python3; then + run_many "Python" python3 "$BENCH_DIR/python/cross_language_benchmark.py" | awk -F'|' '{printf "%-8s | %s\n", $1, $2}' +else + echo "[skip] Python benchmark: python3 not found" +fi diff --git a/benchmarks/rust/cross_language_benchmark.rs b/benchmarks/rust/cross_language_benchmark.rs new file mode 100644 index 00000000..8c44adc5 --- /dev/null +++ b/benchmarks/rust/cross_language_benchmark.rs @@ -0,0 +1,49 @@ +fn fib(n: i64) -> i128 { + if n == 0 { + return 0; + } + let mut a: i128 = 0; + let mut b: i128 = 1; + for _ in 1..=n { + let next = a + b; + a = b; + b = next; + } + a +} + +fn sum_squares(limit: i64) -> i128 { + let mut total: i128 = 0; + for i in 1..=limit { + let v = i as i128; + total += v * v; + } + total +} + +fn prime_count(limit: i64) -> i128 { + let mut count: i128 = 0; + for candidate in 2..=limit { + let mut is_prime = true; + if candidate > 2 { + for divisor in 2..candidate { + if candidate % divisor == 0 { + is_prime = false; + break; + } + } + } + if is_prime { + count += 1; + } + } + count +} + +fn main() { + let sum_part = sum_squares(2_000_000); + let fib_part = fib(35) * 2_000; + let prime_part = prime_count(5_000); + let checksum = sum_part + fib_part + prime_part; + println!("CHECKSUM:{}", checksum); +} diff --git a/src/main/java/cod/ir/IRCodec.java b/src/main/java/cod/ir/IRCodec.java index 6d1aad10..48892032 100644 --- a/src/main/java/cod/ir/IRCodec.java +++ b/src/main/java/cod/ir/IRCodec.java @@ -234,8 +234,7 @@ private static Enum readEnum(DataInput in) throws IOException { String enumClassName = readString(in); String enumName = readString(in); - if (Keyword.class.getName().equals(enumClassName) - || "cod.syntax.Keyword".equals(enumClassName)) { + if (Keyword.class.getName().equals(enumClassName)) { try { return Keyword.valueOf(enumName); } catch (IllegalArgumentException e) { From 55e0785c44a204f9eaf05753f4ca5e00d8320a05 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:47:24 +0000 Subject: [PATCH 07/19] docs: use relative benchmark runner paths Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/c0b02bfd-08ae-45a6-b95e-561a00c02888 Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- benchmarks/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/README.md b/benchmarks/README.md index 760cc305..eaf47181 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -29,13 +29,13 @@ Expected checksum: From repository root: ```bash -bash /home/runner/work/Coderive/Coderive/benchmarks/run_cross_language_benchmark.sh +bash benchmarks/run_cross_language_benchmark.sh ``` Optional runs per language (median-based): ```bash -bash /home/runner/work/Coderive/Coderive/benchmarks/run_cross_language_benchmark.sh 5 +bash benchmarks/run_cross_language_benchmark.sh 5 ``` The runner: From bb26cd74ce9ab136e9002a8f8e7585e56f1bb06d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 22:43:14 +0000 Subject: [PATCH 08/19] Remove legacy IR enum compatibility and add cross-language benchmark suite (Coderive/Java/C/C++/Rust/Python) Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/8eac235e-4b65-4b0f-9d7b-087afef3f52b Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- src/bin/project.codc | Bin 0 -> 29635 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/bin/project.codc diff --git a/src/bin/project.codc b/src/bin/project.codc new file mode 100644 index 0000000000000000000000000000000000000000..57410bf6c0f73b72e90efd3d817e7de9bb15ed62 GIT binary patch literal 29635 zcmeHQ2~WGqiA6%`sZNR&r; zUXm0gX`l>I5yg|?i9Gr3?XCY>x6U~_=iY9HKbE!5+54Qc_xJPte1|<<7G_Kq7eP1( zQkNOx5bCt}3DX-v;J={=f;hS?cUtVe&}*spP$v&(7q2BgE<=sIJiNW9EnM!pYN4x( z(f>W54eA|JrTS!mw@~mkMC|0xd&j(6|I`;;OJTtZ(Y7sSH#srA!)%!FksQZ@!4aIM zQQ%s9Vz6(s$uY@K<&!SnlijYfARm--GNfmJnE%&tb<|yYN%7do>~)|}zEADz`A1nE zYDz_!^N&a^RZ}SbnBd^XW?R(!3#zf0)5CmxG{#*$fZlNTp4PHYX%Z+^6nFX8f5HUG zbNw7&h7TOZIj6O;eeZZ>?;~}aIWajV;0eiLdC%Q%?8;;6mA9X`8; z^oWT$^_CxBLoOP^{{e6YG4hF!8WcTZm?LVaC42N8^^iD@-`CAt6?|J@e?4 z;+to{W9;Ub-6{_zKeNH=hBPqX=Uk&_Z*v`Xq`${zWUBORca4AzcR;aNw$g(s*dTFm ziL`0iXR+p73VxO$Bg^uuITMt=$Mi6s!98bA1Gwtl)kRv zL#WPU$tFj;Zsr*7aNUvv z;FkL&)zr#$Uan>H`Tv@~n$=oof8&TDsFUkge0r6Cz=em}#iz#?Csv$QzXnPZb*wZ&!dyRIRJFx9;ZI1Ru4V03MdKAD;Jj-&OC*=Zz_ALaGOqJinJoIqc zw~6jGOHzORCksXCU#vIw_zRS!tj~Hh*>S zf;Fgl{s)(g1#86S=hiY1t$ViflUvq+TBP?9iKDec4RV}ZBm!;?8ks!POZHF+12JTl zO}gq{yK7{^D5L~%q@ZZ{eo(_|otyR-1c;4=zaKvIM%lte;6|P; zo%QdGiNR+%(pi6?3xnI_2K)hvnH7__cqv(|oCmJ4ajN6JRo`gm=wPcY)XU6L&SIcc z!L6#)<-|;l*{)Tk+8>=p^A=yd!HR>gYMk&)JIwm&1h{71S?gGZRT`oRiCSl&#VOsc zGTwgKOZRJ0A*T3GoToh`w`w4r$^c}}#6t2f^AVf{p#rqs0&iSs9$G__bp zf+`fpJ8rsD8el?nI;@SUYj2sE`b@(L++bSMg7SJr@7=7AspU7gZdaZFeW;w8#kxeX z@Ds!2EY>}pnvmqFW({9--Ej0mQj1s&xaL0f+I#0{kt)`_>OXd->l?nB%GnOVS7kq1 z=KA}HX`4BZmJPluZ=EYw=w}eAWt+!rgdbg6#_@c_KW&3 zV=5?P5^&%SP{-3p>c0Uz-HxkBaw1jf^k7r3_zByLfAH3DeB(~&M<(dndro@))i=18q!yHS(^IYq@ z5cW`T%`si^&(5$ywY6xvQv+zKO#?PHedh8rvL|;toCG)Q+;R1lD>1`|JJ)DzY=rJf zPBloC%X_-pj6}{%NJM#D3@HCS&O*QzDWtPRG zs)Zpe9}amfOyw3U3+N)49^*%KkKb9uX2l!|PctNhu#zBtb+lwmOG zGs`-ez3S%;%xcgt?2koaOZqt%sGq|#tfxITpS5=K*y&CQfJY%$K!?!>c!w=`kl?d0 zGD|?Pv5&`WT{&~00cc%mwyEBnhAGi<)~0&0DZWwWRx`%(?%PCzs_u+kd`O5$+9`uw z)wm~qIs!T_b;aYg$2y7W26>CCT~;u@guHxe=stM~S^hb>dmy?B&Qv~1>>4fY-{u*0 z9q0}_`*?V8ljHP<=NZT;`*AAk-`|AEfwaug6geMj{VTto3|3e*8|YNn@{F1`DaA5j{Y;kex+ITG6AF&sxwd|d0<^a|G@dMQ@VcDBQ~ z0oTz)kPH649+*CAheuWL2+KEA`9`w@oQ``oQ}DU^Yi0v}9;SRKbbfe}eZ|wSKk9 z-^v_4A*XTC{uH280-Q)jYw^)uh$Nqns-l>x7ic_SJvm|Cdi}95w-M*r%;-I&Xyi9# z<2{=tQsYl7;7?!#r3dICXEUYoprp^G)>%^;tBfaKJZCozvPJ~U zk|Ow|;PG2f3n0*%C}LtyFQnjde>@d?*2&>I)XC3uvCbt#w!;^#K4U_nAoEwes)AET z&(4>q&2~I|ErMAH91|7rw21sHE2a zpLG0KYEVH<ErF+#L6szKUkLGvR`8jS z(qOM!M&K2x5oz-E32(}p`iIHaC8JA9p2TmG(eVd0>bXV>URA}XiZ_@nZ_G_e62Cvo z@xje4^(t@XI=r|Z^p|8ygSMom(_wYEeFtl|x}-Q|iDAgo|90>2CMhIWxFnnhRlK`J zXxr6AI-{-^p&0>I1AJ<44E*z+r{0-l&CeJ{m~ z+FtM8!-j(PZ%p~PT5WC+YzT>*czC*pwbe*)$+1=+p+2}&*Gg(JZ&LR&5XDyw3oQSxhdXY>qPIlru|tqk zeeauFNm-$wsMq4Y^9`HS`lt%2DUsa=qtdcp!SWvN7gAz!Y*>kTzm)RDRQH_k6N9!U6ONZ-r8yPt7<&q z`n$?~MwVCzqO6n8&XEh0{z)fS`H$Xvwe0k-$bE)P0>=XJkN)DBxl)y&WyT}@A1pHT z6@W!iv2J~C9-n^;vgo{CvHyxb7h3Tw5?DtaHWHGN|Nr|Eq`>GZkJpwMx%1!EKzS^a z;v9Al4c~h@`z7M&mrj6*`a~}W7IcV1=GU-BC|ZN!7P*whP>udU&wWL#x2xjN^lBij^Y{oj9)T zTxc8^A&NM&XT{pmSkFE8p_NshHw^g^X#|QiNhbRTz8^4E4#3D+p*}sr=#kDPJgWp7 z#tQb9pcY7w)Ax+&e>tZ^=o84|If4o58+ffzJoTK?kANf0knB=&r_kxa&zQYn)X=w) zHIRKhBn5OtxzH$Ng1w`<;w7VymAy@L3~Lu2beZp@3wO0Ka0Es@LmjhQv#-7dwOEe- z$IKVQE{w~&^(uS7gSU;6pitT+G$Yudc5{-`&I9|W-(w&>LmeXP6TKrtznX!U023nQ z=U0%WN3Vx18pGBF`joVDvr(u!T*fhUo2u|>)H*0GKYCo{+lQc1e%JnZ%Xi1}C+$6$ z;r=SAaO$pu37?+f2G4Nc&~OP3NELS;-KNMHbj^8ZY&e%Ow$>#eb(<1r9Ae>F+9$B^ zFg%Z;IO=M4qgRaD*Z~*f>fH~>jvY{N?4@4{196V1ZP;pmDiJ)xx^Z{-3l6;K$s>>3 zOYK~4O^<^ojHG-@M<@Rzo89PHsqu#d+IddM`8Nl3r*kgwjq*oQj_~9#DWDo z&Bo1bbQ*w?&(xsca0cJGwO%*Xf3Fzc24@B&(?Tc4-nY(lPTTL02%D1mgpW(p%-)Mm zXHEtMVgvFkG0#yb=e@mnR+u#t6fsQM@y%kcM(eKa2Y0L&DKdH}=W@GvmYahUtX5h+ z(lO*^=*J8=2f%rIA@b3_b}>`_FG z^F0cg4*3Omx6wfc;twVIRYf1UISC%w!pgD8N4H`OUe)5h)bZ?=(vi4hTU+PA7U1Nq3o(H%&SS zy2mbK=P}H2!lOAR%w|pNv9VBsr%rvR?{QZt4WS1@!qAimo0sL9;1go5n@_3UuBVK^ zr-RCP22kjC+L^175|^f1f<`$%%f-61hZaZE>$YC2951gKgJ%_)y-agD_n8TpX{ zi+pxTPx=umZjtGe9N@a7SP8FJTcJj0$3mKFkXQ~iA4`%G9p{=cmd3ZGgYHONj4H$y z4Vsddd*B*ai5wYWu&d!EJlrUG>d?-Q=1mq^Spl2x!$HO;Ux6R<5U=NcZs>#CQ~bH% zk2sm5rr@D)Co*Un2Q`d^8N5&l#Ni+iH)R9-8CPzT3GkH!Vw`3Pt%Q0o?&A>8%UWQ$ z83@lTW)1FaGbpE!1%mM+IOt_Om`M{Hsz%RuGD)~#%=aNeTu6g)mpMrx6TskH2Vn@a z6bTGCQxXP>j%uTaMGu%gHDYi&fKE?{?;sfB2tf~l?>Hb5uN8J%KvWSTAmX;%k@*Ut zk`7S^#4r#LKW(XEtQJ{7P|QOp@nN^Vq~ER$zm;`ESaZA0JQX!SN-htqb{~CZ;!q>E9d^3`|>_ z!abANAs(2vc2y~Dpa;Sinzk+h)7BQB_8`8O0n^sM7e~_Tc2{$!gvgLlnq-JZ=H(-# z4)WTTURD68GfjUVNMJ|ez>bz4hqSH{O-d{ZJK~!Oq^AoiBdGl#U^ISO`qro*Qv-=- z5yTM?TNE`P#VO=hA0ZSphEE#~@UqtT^M_oywrn(xE4I2Dc*cLW4uz^0)!hg{d0PuXNh#VR5>Pq;PfQrf)Z_cvOL|Cr!5GvDWnu&< z1fd;U&xb7U&lnA~#a%Ubrr1B%YuY)(>A~*@(vMjxR zcNK<+u^}Tg%{cTmWui2@-UV)?GqK&<3`)p=ZqiN{BBE($@HtR7HT?yh3c=7v2*;8K z918}2n3y}&7C4sFns(iB^bGV1IhHxVv222?>sV|pa4hUGm#gXZyDRqW*h+*=H}R*d z$xd5cUl5;L76AE}y)Wz}Yn3FQqn2af=1GWYL^(nWoZV9JvbesIAwn{zLk*EmsCO@) z-k1|mmP`zqUidqhYQ<@o9{S($RBPw`n&gwL$miA=dqMLi+FMwp?H6qp=6MWOHw~*1 zCm2Eb0cZ3QZY5>9Ufe;(SrTV7IdS6Q-PxY-NBKGCny;_D;XKB08^loJ1y-Rv62XC1 zdYq!*mk16VkijFQXX>B=dU8-ua1hVBEI4O6tI&POxpp+Z5ugy-Hwqu-7ipeIR}=Bx7# zsJ9mWzzFk~XQCPC5hN+Zx}|2lM26Mfpu7d3+&uz8x%oVZOsgawpPCf59f21H5JD=_ zLxck1iOva6Jbve7Ycs9 z9RSk^&-OFpD(SAsP#a`*I-4sDBDOjm^hghk?_#iSVTLle2=~*DVWR(06DX4J56%By U)544`2H!@(U-%1E7T)mgfBlo#h5!Hn literal 0 HcmV?d00001 From 6dc451ed96b2afb1dab158de3d933023aaa726e1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 22:50:20 +0000 Subject: [PATCH 09/19] Implement phase A perf optimizations and primitive loop fast path Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/1e6bdde3-5346-4be9-aa69-6afe1e76142e Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- benchmarks/run_cross_language_benchmark.sh | 2 +- src/main/java/cod/debug/DebugSystem.java | 15 ++- src/main/java/cod/interpreter/Index.java | 9 +- .../java/cod/interpreter/Interpreter.java | 2 +- .../cod/interpreter/InterpreterVisitor.java | 4 +- .../handler/ArrayOperationHandler.java | 113 ++++++++++++++++++ .../handler/AssignmentHandler.java | 9 +- .../cod/interpreter/handler/TypeHandler.java | 18 ++- src/main/java/cod/lexer/TokenType.java | 14 ++- src/main/java/cod/runner/BaseRunner.java | 6 +- src/main/java/cod/runner/CommandRunner.java | 7 +- .../java/cod/semantic/NamingValidator.java | 14 ++- 12 files changed, 192 insertions(+), 21 deletions(-) diff --git a/benchmarks/run_cross_language_benchmark.sh b/benchmarks/run_cross_language_benchmark.sh index 62d6a1c2..be7b7822 100644 --- a/benchmarks/run_cross_language_benchmark.sh +++ b/benchmarks/run_cross_language_benchmark.sh @@ -103,7 +103,7 @@ echo echo "Language | Median ms" echo "---------|----------" -run_many "Coderive" java -cp "$WORK_DIR/coderive-java" cod.runner.CommandRunner \ +run_many "Coderive" env COD_BENCHMARK_MODE=true java -cp "$WORK_DIR/coderive-java" cod.runner.CommandRunner \ "$BENCH_DIR/coderive/CrossLanguageBenchmark.cod" --quiet | awk -F'|' '{printf "%-8s | %s\n", $1, $2}' if [[ -x "$WORK_DIR/java-bin/CrossLanguageBenchmark.class" || -f "$WORK_DIR/java-bin/CrossLanguageBenchmark.class" ]]; then diff --git a/src/main/java/cod/debug/DebugSystem.java b/src/main/java/cod/debug/DebugSystem.java index d8518984..e59d41fa 100644 --- a/src/main/java/cod/debug/DebugSystem.java +++ b/src/main/java/cod/debug/DebugSystem.java @@ -30,6 +30,19 @@ public int getLevel() { private static boolean showThread = false; private static Map timers = new HashMap(); // Stores nanoseconds private static SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss.SSS"); + private static final boolean BENCHMARK_MODE = parseBenchmarkMode(); + + private static boolean parseBenchmarkMode() { + String raw = System.getProperty("cod.benchmark.mode"); + if (raw == null || raw.trim().isEmpty()) { + raw = System.getenv("COD_BENCHMARK_MODE"); + } + return raw != null && "true".equalsIgnoreCase(raw.trim()); + } + + public static boolean isBenchmarkMode() { + return BENCHMARK_MODE; + } public static void setLevel(Level level) { currentLevel = level; @@ -159,4 +172,4 @@ private static boolean shouldLog(Level level) { public static Level getLevel() { return currentLevel; } -} \ No newline at end of file +} diff --git a/src/main/java/cod/interpreter/Index.java b/src/main/java/cod/interpreter/Index.java index 8ee96bc7..b9a20bd1 100644 --- a/src/main/java/cod/interpreter/Index.java +++ b/src/main/java/cod/interpreter/Index.java @@ -1,6 +1,7 @@ package cod.interpreter; import cod.error.ProgramError; +import cod.debug.DebugSystem; import cod.ir.IRManager; import cod.lexer.*; import static cod.lexer.TokenType.*; @@ -70,9 +71,13 @@ public static void setProjectRoot(String srcMainRoot) { } } - System.err.println("[INDEX] Project root set to: " + projectRoot); + if (!DebugSystem.isBenchmarkMode()) { + System.err.println("[INDEX] Project root set to: " + projectRoot); + } } catch (Exception e) { - System.err.println("[INDEX] Failed to set project root: " + e.getMessage()); + if (!DebugSystem.isBenchmarkMode()) { + System.err.println("[INDEX] Failed to set project root: " + e.getMessage()); + } } } diff --git a/src/main/java/cod/interpreter/Interpreter.java b/src/main/java/cod/interpreter/Interpreter.java index 0e6f0420..44c7b7f8 100644 --- a/src/main/java/cod/interpreter/Interpreter.java +++ b/src/main/java/cod/interpreter/Interpreter.java @@ -1087,7 +1087,7 @@ public Object evalMethodCall( argValue = typeSystem.normalizeForDeclaredType(paramType, argValue); - if (paramType.contains("|")) { + if (paramType != null && paramType.indexOf('|') >= 0) { String activeType = typeSystem.getConcreteType(typeSystem.unwrap(argValue)); argValue = new TypeHandler.Value(argValue, activeType, paramType); } diff --git a/src/main/java/cod/interpreter/InterpreterVisitor.java b/src/main/java/cod/interpreter/InterpreterVisitor.java index 3e58c817..3b865cbf 100644 --- a/src/main/java/cod/interpreter/InterpreterVisitor.java +++ b/src/main/java/cod/interpreter/InterpreterVisitor.java @@ -416,7 +416,7 @@ public Object visit(Var node) { } } - if (resolvedDeclaredType.contains("|")) { + if (resolvedDeclaredType != null && resolvedDeclaredType.indexOf('|') >= 0) { String activeType = typeSystem.getConcreteType(typeSystem.unwrap(val)); val = new TypeHandler.Value(val, activeType, resolvedDeclaredType); ctx.setVariable(node.name, val); @@ -2148,7 +2148,7 @@ public Object visit(MethodCall node) { } } - if (paramType.contains("|")) { + if (paramType != null && paramType.indexOf('|') >= 0) { String activeType = typeSystem.getConcreteType(typeSystem.unwrap(argValue)); argValue = new TypeHandler.Value(argValue, activeType, paramType); } diff --git a/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java b/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java index 4bfff84a..7b0b3234 100644 --- a/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java +++ b/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java @@ -135,6 +135,15 @@ public Object executeRangeLoop(cod.interpreter.context.ExecutionContext ctx, For throw new ProgramError("Loop step cannot be zero."); } + Long startLong = tryFastLongValue(startObj); + Long endLong = tryFastLongValue(endObj); + Long stepLong = tryFastLongValue(step); + if (startLong != null && endLong != null && stepLong != null + && canUsePrimitiveAdditiveLoop(startLong.longValue(), endLong.longValue(), stepLong.longValue())) { + return executeAdditiveLoopPrimitive( + ctx, node, startLong.longValue(), endLong.longValue(), stepLong.longValue()); + } + return executeAdditiveLoop(ctx, node, startObj, endObj, step); } catch (ProgramError e) { throw e; @@ -221,6 +230,64 @@ public void executeIteration( } } + public void executePrimitiveIteration( + cod.interpreter.context.ExecutionContext ctx, For node, long current) { + try { + String iter = node.iterator; + Object currentValue = (current >= Integer.MIN_VALUE && current <= Integer.MAX_VALUE) + ? Integer.valueOf((int) current) + : Long.valueOf(current); + ctx.setVariable(iter, currentValue); + if (ctx.getVariableType(iter) == null) { + ctx.setVariableType(iter, cod.lexer.TokenType.Keyword.INT.toString()); + } + executeLoopBody(ctx, node); + } catch (BreakLoopException e) { + throw e; + } catch (ProgramError e) { + throw e; + } catch (Exception e) { + throw new InternalError("Primitive loop iteration failed", e); + } + } + + public Object executeAdditiveLoopPrimitive( + cod.interpreter.context.ExecutionContext ctx, For node, long start, long end, long step) { + try { + long current = start; + if (step > 0L) { + while (current <= end) { + try { + executePrimitiveIteration(ctx, node, current); + } catch (BreakLoopException e) { + break; + } + if (current == end) { + break; + } + current += step; + } + } else { + while (current >= end) { + try { + executePrimitiveIteration(ctx, node, current); + } catch (BreakLoopException e) { + break; + } + if (current == end) { + break; + } + current += step; + } + } + return null; + } catch (ProgramError e) { + throw e; + } catch (Exception e) { + throw new InternalError("Primitive additive loop execution failed", e); + } + } + public void executeLoopBody(cod.interpreter.context.ExecutionContext ctx, For node) { try { for (Stmt s : node.body.statements) { @@ -547,6 +614,52 @@ private boolean shouldContinueAdditive( return increasing ? current.compareTo(end) <= 0 : current.compareTo(end) >= 0; } + private Long tryFastLongValue(Object value) { + Object unwrapped = typeSystem.unwrap(value); + if (unwrapped instanceof Integer || unwrapped instanceof Long + || unwrapped instanceof Short || unwrapped instanceof Byte) { + return Long.valueOf(((Number) unwrapped).longValue()); + } + if (unwrapped instanceof IntLiteral) { + try { + return Long.valueOf(((IntLiteral) unwrapped).value.longValue()); + } catch (ArithmeticException ignored) { + return null; + } + } + if (unwrapped instanceof AutoStackingNumber) { + AutoStackingNumber asn = (AutoStackingNumber) unwrapped; + try { + return Long.valueOf(asn.longValue()); + } catch (ArithmeticException ignored) { + return null; + } + } + return null; + } + + private boolean canUsePrimitiveAdditiveLoop(long start, long end, long step) { + if (step == 0L) { + return false; + } + if (step == Long.MIN_VALUE) { + return false; + } + if (step > 0L && start > end) { + return false; + } + if (step < 0L && start < end) { + return false; + } + if (step > 0L && end > Long.MAX_VALUE - step) { + return false; + } + if (step < 0L && end < Long.MIN_VALUE - step) { + return false; + } + return true; + } + private void validateFactor(AutoStackingNumber factor, String operation) { if (factor.compareTo(AutoStackingNumber.zero(1)) <= 0) { throw new ProgramError("Factor must be positive"); diff --git a/src/main/java/cod/interpreter/handler/AssignmentHandler.java b/src/main/java/cod/interpreter/handler/AssignmentHandler.java index c1ad5f25..1fbe1921 100644 --- a/src/main/java/cod/interpreter/handler/AssignmentHandler.java +++ b/src/main/java/cod/interpreter/handler/AssignmentHandler.java @@ -148,8 +148,9 @@ private Object assignToSlot(String slotTarget, Object value, ExecutionContext ct String declaredType = ctx.getSlotType(slotTarget); // O(1) validateAssignmentType(declaredType, value, slotTarget); value = typeSystem.normalizeForDeclaredType(declaredType, value); - - value = typeSystem.wrapUnionType(value, declaredType); + if (declaredType != null && declaredType.indexOf('|') >= 0) { + value = typeSystem.wrapUnionType(value, declaredType); + } ctx.setSlotValue(slotTarget, value); // O(1) ctx.markSlotAssigned(slotTarget); // O(1) @@ -438,7 +439,9 @@ private Object updateVariableInScope(String varName, Object newValue, if (declaredType != null) { validateAssignmentType(declaredType, newValue, varName); newValue = typeSystem.normalizeForDeclaredType(declaredType, newValue); - newValue = typeSystem.wrapUnionType(newValue, declaredType); + if (declaredType.indexOf('|') >= 0) { + newValue = typeSystem.wrapUnionType(newValue, declaredType); + } } ctx.setVariable(varName, newValue); diff --git a/src/main/java/cod/interpreter/handler/TypeHandler.java b/src/main/java/cod/interpreter/handler/TypeHandler.java index 276009f3..dbdf0fe8 100644 --- a/src/main/java/cod/interpreter/handler/TypeHandler.java +++ b/src/main/java/cod/interpreter/handler/TypeHandler.java @@ -296,7 +296,7 @@ public boolean isValidForNullableType(String declaredType, Object value) { // === TypeHandler Conversion Helpers === public Object wrapUnionType(Object value, String declaredType) { - if (declaredType.contains("|")) { + if (declaredType != null && declaredType.indexOf('|') >= 0) { String activeType = getConcreteType(unwrap(value)); return new Value(value, activeType, declaredType); } @@ -1243,6 +1243,12 @@ public boolean validateType(String typeSig, Object value) { return true; } String typeSigTrimmed = normalizeTypeSignature(typeSig); + if (value != null && isFastPrimitiveSignature(typeSigTrimmed)) { + String concreteType = getConcreteType(value); + if (typeSigTrimmed.equals(concreteType)) { + return true; + } + } if (typeSigTrimmed.contains("|")) { if (!isTypeStructurallyValid(typeSigTrimmed)) { throw new ProgramError("Union type contains illegal keywords: " + typeSig); @@ -1255,6 +1261,16 @@ public boolean validateType(String typeSig, Object value) { String concreteType = getConcreteType(value); return validateTypeInternal(typeSig, value, concreteType); } + + private boolean isFastPrimitiveSignature(String typeSig) { + return INT.toString().equals(typeSig) + || FLOAT.toString().equals(typeSig) + || TEXT.toString().equals(typeSig) + || BOOL.toString().equals(typeSig) + || "none".equals(typeSig) + || TYPE.toString().equals(typeSig) + || "list".equals(typeSig); + } public boolean areEqual(Object a, Object b) { a = unwrap(a); diff --git a/src/main/java/cod/lexer/TokenType.java b/src/main/java/cod/lexer/TokenType.java index f19c19ef..4f2e0b0c 100644 --- a/src/main/java/cod/lexer/TokenType.java +++ b/src/main/java/cod/lexer/TokenType.java @@ -18,9 +18,11 @@ public enum TokenType { WS, INTERPOL; - @Override + private final String lowerCaseName = name().toLowerCase(); + + @Override public String toString() { - return name().toLowerCase(); + return lowerCaseName; } public enum Keyword { @@ -68,11 +70,13 @@ public enum Keyword { F32, F64; - private static final Map STRING_TO_KEYWORD = new HashMap<>(); + private final String lowerCaseName = name().toLowerCase(); + + private static final Map STRING_TO_KEYWORD = new HashMap(); static { for (Keyword keyword : values()) { - STRING_TO_KEYWORD.put(keyword.toString(), keyword); + STRING_TO_KEYWORD.put(keyword.lowerCaseName, keyword); } } @@ -82,7 +86,7 @@ public static Keyword fromString(String text) { @Override public String toString() { - return name().toLowerCase(); + return lowerCaseName; } } diff --git a/src/main/java/cod/runner/BaseRunner.java b/src/main/java/cod/runner/BaseRunner.java index 49c4db1a..3b9e6e5f 100644 --- a/src/main/java/cod/runner/BaseRunner.java +++ b/src/main/java/cod/runner/BaseRunner.java @@ -96,8 +96,10 @@ public Program parse(String filename, Interpreter interpreter) throws Exception protected void configureDebugSystem(DebugSystem.Level level) { DebugSystem.setLevel(level); - DebugSystem.setShowTimestamp(true); - DebugSystem.info(LOG_TAG, "DebugSystem configured to level: " + level); + DebugSystem.setShowTimestamp(!DebugSystem.isBenchmarkMode()); + if (!DebugSystem.isBenchmarkMode()) { + DebugSystem.info(LOG_TAG, "DebugSystem configured to level: " + level); + } } protected String extractFilenameFromArgs(String[] args, String defaultFilename) { diff --git a/src/main/java/cod/runner/CommandRunner.java b/src/main/java/cod/runner/CommandRunner.java index d82ddff4..13ac6c45 100644 --- a/src/main/java/cod/runner/CommandRunner.java +++ b/src/main/java/cod/runner/CommandRunner.java @@ -106,8 +106,11 @@ public void configure(RunnerConfig config) { DebugSystem.info(NAME + LOG_TAG, "CommandRunner execution completed"); } finally { - System.out.println("\n-----------------------------"); - System.out.println("Execution completed! Duration: " + DebugSystem.stopTimer("exec") + "ms"); + double duration = DebugSystem.stopTimer("exec"); + if (!DebugSystem.isBenchmarkMode()) { + System.out.println("\n-----------------------------"); + System.out.println("Execution completed! Duration: " + duration + "ms"); + } } } diff --git a/src/main/java/cod/semantic/NamingValidator.java b/src/main/java/cod/semantic/NamingValidator.java index f14a3d9a..b9db2f73 100644 --- a/src/main/java/cod/semantic/NamingValidator.java +++ b/src/main/java/cod/semantic/NamingValidator.java @@ -128,6 +128,18 @@ public static boolean startsWithLowerCase(String name) { } public static boolean isAllCaps(String name) { - return name != null && name.matches("[A-Z0-9_]+"); + if (name == null || name.isEmpty()) { + return false; + } + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + boolean ok = (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || c == '_'; + if (!ok) { + return false; + } + } + return true; } } From bd56ebcde64b7ccad2ae404868af0783bee01110 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 22:54:36 +0000 Subject: [PATCH 10/19] Address review feedback on loop overflow checks and benchmark script Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/1e6bdde3-5346-4be9-aa69-6afe1e76142e Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- benchmarks/run_cross_language_benchmark.sh | 2 +- .../interpreter/handler/ArrayOperationHandler.java | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/benchmarks/run_cross_language_benchmark.sh b/benchmarks/run_cross_language_benchmark.sh index be7b7822..180a6dbe 100644 --- a/benchmarks/run_cross_language_benchmark.sh +++ b/benchmarks/run_cross_language_benchmark.sh @@ -106,7 +106,7 @@ echo "---------|----------" run_many "Coderive" env COD_BENCHMARK_MODE=true java -cp "$WORK_DIR/coderive-java" cod.runner.CommandRunner \ "$BENCH_DIR/coderive/CrossLanguageBenchmark.cod" --quiet | awk -F'|' '{printf "%-8s | %s\n", $1, $2}' -if [[ -x "$WORK_DIR/java-bin/CrossLanguageBenchmark.class" || -f "$WORK_DIR/java-bin/CrossLanguageBenchmark.class" ]]; then +if [[ -f "$WORK_DIR/java-bin/CrossLanguageBenchmark.class" ]]; then run_many "Java" java -cp "$WORK_DIR/java-bin" CrossLanguageBenchmark | awk -F'|' '{printf "%-8s | %s\n", $1, $2}' fi diff --git a/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java b/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java index 7b0b3234..d949581f 100644 --- a/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java +++ b/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java @@ -265,6 +265,9 @@ public Object executeAdditiveLoopPrimitive( if (current == end) { break; } + if (current > Long.MAX_VALUE - step) { + break; + } current += step; } } else { @@ -277,6 +280,9 @@ public Object executeAdditiveLoopPrimitive( if (current == end) { break; } + if (current < Long.MIN_VALUE - step) { + break; + } current += step; } } @@ -651,12 +657,6 @@ private boolean canUsePrimitiveAdditiveLoop(long start, long end, long step) { if (step < 0L && start < end) { return false; } - if (step > 0L && end > Long.MAX_VALUE - step) { - return false; - } - if (step < 0L && end < Long.MIN_VALUE - step) { - return false; - } return true; } From d52f8b8107f14d9f0c44108dd7df778c6a958a02 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 23:03:56 +0000 Subject: [PATCH 11/19] Phase B: add direct loop variable binding for faster iteration updates Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/ac3b24cd-b3a0-43c3-a58a-f7a9984aa518 Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- .../interpreter/context/ExecutionContext.java | 53 +++++++++++ .../handler/ArrayOperationHandler.java | 92 ++++++++++++++----- 2 files changed, 123 insertions(+), 22 deletions(-) diff --git a/src/main/java/cod/interpreter/context/ExecutionContext.java b/src/main/java/cod/interpreter/context/ExecutionContext.java index 688cd9fd..a78180d8 100644 --- a/src/main/java/cod/interpreter/context/ExecutionContext.java +++ b/src/main/java/cod/interpreter/context/ExecutionContext.java @@ -391,6 +391,59 @@ public void setVariable(String name, Object value) { Object previous = currentScope.put(name, value); replaceTrackedValue(previous, value); } + + public int resolveVariableScopeIndex(String name) { + if (name == null) return -1; + for (int i = localsStack.size() - 1; i >= 0; i--) { + Map scope = localsStack.get(i); + if (scope.containsKey(name)) { + return i; + } + } + return localsStack.size() - 1; + } + + public int resolveVariableTypeScopeIndex(String name) { + if (name == null) return -1; + for (int i = localTypesStack.size() - 1; i >= 0; i--) { + Map scope = localTypesStack.get(i); + if (scope.containsKey(name)) { + return i; + } + } + return localTypesStack.size() - 1; + } + + public String getVariableTypeAtScope(int scopeIndex, String name) { + if (name == null || scopeIndex < 0 || scopeIndex >= localTypesStack.size()) { + return null; + } + return localTypesStack.get(scopeIndex).get(name); + } + + public void setVariableAtScope(int scopeIndex, String name, Object value) { + if (name == null) { + throw new InternalError("setVariableAtScope called with null name"); + } + if (scopeIndex < 0 || scopeIndex >= localsStack.size()) { + setVariable(name, value); + return; + } + Map scope = localsStack.get(scopeIndex); + Object previous = scope.put(name, value); + replaceTrackedValue(previous, value); + } + + public void setVariableTypeAtScope(int scopeIndex, String name, String type) { + if (name == null) { + throw new InternalError("setVariableTypeAtScope called with null name"); + } + if (scopeIndex < 0 || scopeIndex >= localTypesStack.size()) { + setVariableType(name, type); + return; + } + localTypesStack.get(scopeIndex).put(name, type); + } public String getVariableType(String name) { if (name == null) return null; diff --git a/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java b/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java index d949581f..6690edd2 100644 --- a/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java +++ b/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java @@ -15,6 +15,20 @@ import java.util.List; public class ArrayOperationHandler { + private static final class LoopVariableBinding { + final String name; + final int valueScopeIndex; + final int typeScopeIndex; + boolean typeInitialized; + + LoopVariableBinding(String name, int valueScopeIndex, int typeScopeIndex, boolean typeInitialized) { + this.name = name; + this.valueScopeIndex = valueScopeIndex; + this.typeScopeIndex = typeScopeIndex; + this.typeInitialized = typeInitialized; + } + } + private final InterpreterVisitor dispatcher; private final Interpreter interpreter; private final TypeHandler typeSystem; @@ -67,12 +81,13 @@ public Object executeForLoopNormally(For node) { public Object executeArrayLoop( cod.interpreter.context.ExecutionContext ctx, For node, String iter, Object arrayObj) { try { + LoopVariableBinding binding = resolveLoopVariableBinding(ctx, iter); if (arrayObj instanceof NaturalArray) { NaturalArray natural = (NaturalArray) arrayObj; long size = natural.size(); for (long i = 0; i < size; i++) { Object currentValue = natural.get(i); - ctx.setVariable(iter, currentValue); + writeLoopVariable(ctx, binding, currentValue); try { executeLoopBody(ctx, node); } catch (BreakLoopException e) { @@ -82,7 +97,7 @@ public Object executeArrayLoop( } else if (arrayObj instanceof List) { List list = (List) arrayObj; for (Object currentValue : list) { - ctx.setVariable(iter, currentValue); + writeLoopVariable(ctx, binding, currentValue); try { executeLoopBody(ctx, node); } catch (BreakLoopException e) { @@ -113,15 +128,17 @@ public Object executeRangeLoop(cod.interpreter.context.ExecutionContext ctx, For if (binOp.left instanceof Identifier && ((Identifier) binOp.left).name.equals(iter) && (binOp.op.equals("*") || binOp.op.equals("/"))) { + LoopVariableBinding loopBinding = resolveLoopVariableBinding(ctx, iter); Object rightObj = dispatcher.dispatch(binOp.right); rightObj = typeSystem.unwrap(rightObj); AutoStackingNumber factor = typeSystem.toAutoStackingNumber(rightObj); validateFactor(factor, binOp.op); - return executeMultiplicativeLoop(ctx, node, startObj, endObj, factor, binOp.op); + return executeMultiplicativeLoop(ctx, node, startObj, endObj, factor, binOp.op, loopBinding); } } AutoStackingNumber step; + LoopVariableBinding loopBinding = resolveLoopVariableBinding(ctx, iter); if (node.range.step != null) { Object stepObj = dispatcher.dispatch(node.range.step); step = typeSystem.toAutoStackingNumber(typeSystem.unwrap(stepObj)); @@ -141,10 +158,10 @@ public Object executeRangeLoop(cod.interpreter.context.ExecutionContext ctx, For if (startLong != null && endLong != null && stepLong != null && canUsePrimitiveAdditiveLoop(startLong.longValue(), endLong.longValue(), stepLong.longValue())) { return executeAdditiveLoopPrimitive( - ctx, node, startLong.longValue(), endLong.longValue(), stepLong.longValue()); + ctx, node, startLong.longValue(), endLong.longValue(), stepLong.longValue(), loopBinding); } - return executeAdditiveLoop(ctx, node, startObj, endObj, step); + return executeAdditiveLoop(ctx, node, startObj, endObj, step, loopBinding); } catch (ProgramError e) { throw e; } catch (Exception e) { @@ -153,7 +170,12 @@ && canUsePrimitiveAdditiveLoop(startLong.longValue(), endLong.longValue(), stepL } public Object executeAdditiveLoop( - cod.interpreter.context.ExecutionContext ctx, For node, Object startObj, Object endObj, AutoStackingNumber step) { + cod.interpreter.context.ExecutionContext ctx, + For node, + Object startObj, + Object endObj, + AutoStackingNumber step, + LoopVariableBinding loopBinding) { try { AutoStackingNumber start = typeSystem.toAutoStackingNumber(startObj); AutoStackingNumber end = typeSystem.toAutoStackingNumber(endObj); @@ -162,7 +184,7 @@ public Object executeAdditiveLoop( while (shouldContinueAdditive(current, end, step, increasing)) { try { - executeIteration(ctx, node, current, startObj); + executeIteration(ctx, node, current, startObj, loopBinding); } catch (BreakLoopException e) { break; } @@ -182,7 +204,8 @@ public Object executeMultiplicativeLoop( Object startObj, Object endObj, AutoStackingNumber factor, - String operation) { + String operation, + LoopVariableBinding loopBinding) { try { AutoStackingNumber start = typeSystem.toAutoStackingNumber(startObj); AutoStackingNumber end = typeSystem.toAutoStackingNumber(endObj); @@ -190,7 +213,7 @@ public Object executeMultiplicativeLoop( while (shouldContinueMultiplicative(current, start, end, factor, operation)) { try { - executeIteration(ctx, node, current, startObj); + executeIteration(ctx, node, current, startObj, loopBinding); } catch (BreakLoopException e) { break; } @@ -209,16 +232,20 @@ public Object executeMultiplicativeLoop( } public void executeIteration( - cod.interpreter.context.ExecutionContext ctx, For node, AutoStackingNumber current, Object startObj) { + cod.interpreter.context.ExecutionContext ctx, + For node, + AutoStackingNumber current, + Object startObj, + LoopVariableBinding loopBinding) { try { - String iter = node.iterator; Object currentValue = convertToAppropriateType(current, startObj); - ctx.setVariable(iter, currentValue); - if (ctx.getVariableType(iter) == null) { + writeLoopVariable(ctx, loopBinding, currentValue); + if (!loopBinding.typeInitialized) { String inferredType = (current.fitsInStacks(1) && (current.getWords()[0] & 0x7FFFFFFFFFFFFFFFL) < Long.MAX_VALUE) ? cod.lexer.TokenType.Keyword.INT.toString() : cod.lexer.TokenType.Keyword.FLOAT.toString(); - ctx.setVariableType(iter, inferredType); + ctx.setVariableTypeAtScope(loopBinding.typeScopeIndex, loopBinding.name, inferredType); + loopBinding.typeInitialized = true; } executeLoopBody(ctx, node); } catch (BreakLoopException e) { @@ -231,15 +258,18 @@ public void executeIteration( } public void executePrimitiveIteration( - cod.interpreter.context.ExecutionContext ctx, For node, long current) { + cod.interpreter.context.ExecutionContext ctx, + For node, + long current, + LoopVariableBinding loopBinding) { try { - String iter = node.iterator; Object currentValue = (current >= Integer.MIN_VALUE && current <= Integer.MAX_VALUE) ? Integer.valueOf((int) current) : Long.valueOf(current); - ctx.setVariable(iter, currentValue); - if (ctx.getVariableType(iter) == null) { - ctx.setVariableType(iter, cod.lexer.TokenType.Keyword.INT.toString()); + writeLoopVariable(ctx, loopBinding, currentValue); + if (!loopBinding.typeInitialized) { + ctx.setVariableTypeAtScope(loopBinding.typeScopeIndex, loopBinding.name, cod.lexer.TokenType.Keyword.INT.toString()); + loopBinding.typeInitialized = true; } executeLoopBody(ctx, node); } catch (BreakLoopException e) { @@ -252,13 +282,18 @@ public void executePrimitiveIteration( } public Object executeAdditiveLoopPrimitive( - cod.interpreter.context.ExecutionContext ctx, For node, long start, long end, long step) { + cod.interpreter.context.ExecutionContext ctx, + For node, + long start, + long end, + long step, + LoopVariableBinding loopBinding) { try { long current = start; if (step > 0L) { while (current <= end) { try { - executePrimitiveIteration(ctx, node, current); + executePrimitiveIteration(ctx, node, current, loopBinding); } catch (BreakLoopException e) { break; } @@ -273,7 +308,7 @@ public Object executeAdditiveLoopPrimitive( } else { while (current >= end) { try { - executePrimitiveIteration(ctx, node, current); + executePrimitiveIteration(ctx, node, current, loopBinding); } catch (BreakLoopException e) { break; } @@ -660,6 +695,19 @@ private boolean canUsePrimitiveAdditiveLoop(long start, long end, long step) { return true; } + private LoopVariableBinding resolveLoopVariableBinding( + cod.interpreter.context.ExecutionContext ctx, String iter) { + int valueScopeIndex = ctx.resolveVariableScopeIndex(iter); + int typeScopeIndex = ctx.resolveVariableTypeScopeIndex(iter); + String existingType = ctx.getVariableTypeAtScope(typeScopeIndex, iter); + return new LoopVariableBinding(iter, valueScopeIndex, typeScopeIndex, existingType != null); + } + + private void writeLoopVariable( + cod.interpreter.context.ExecutionContext ctx, LoopVariableBinding binding, Object value) { + ctx.setVariableAtScope(binding.valueScopeIndex, binding.name, value); + } + private void validateFactor(AutoStackingNumber factor, String operation) { if (factor.compareTo(AutoStackingNumber.zero(1)) <= 0) { throw new ProgramError("Factor must be positive"); From 5222689b82d87af2e99ca6475f628c707debd97f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 23:07:30 +0000 Subject: [PATCH 12/19] Add clarifying benchmark and loop-boundary comments Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/ac3b24cd-b3a0-43c3-a58a-f7a9984aa518 Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- benchmarks/run_cross_language_benchmark.sh | 2 ++ .../java/cod/interpreter/handler/ArrayOperationHandler.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/benchmarks/run_cross_language_benchmark.sh b/benchmarks/run_cross_language_benchmark.sh index 180a6dbe..fe06f0a7 100644 --- a/benchmarks/run_cross_language_benchmark.sh +++ b/benchmarks/run_cross_language_benchmark.sh @@ -5,6 +5,8 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" BENCH_DIR="$ROOT_DIR/benchmarks" WORK_DIR="/tmp/coderive-cross-bench" RUNS="${1:-3}" +# Deterministic checksum for the three benchmark kernels (sum of squares, repeated fib, prime counting). +# Update this only if benchmark workload logic intentionally changes across all language implementations. EXPECTED_CHECKSUM="2666668685121930669" mkdir -p "$WORK_DIR" diff --git a/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java b/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java index 6690edd2..505e051e 100644 --- a/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java +++ b/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java @@ -683,6 +683,8 @@ private boolean canUsePrimitiveAdditiveLoop(long start, long end, long step) { if (step == 0L) { return false; } + // Long.MIN_VALUE is excluded because negation/absolute-distance style reasoning is not representable, + // and overflow-safe primitive loop progression checks become ambiguous at this boundary. if (step == Long.MIN_VALUE) { return false; } From a5cc4460b56430fb04367e7ca63331778df6a435 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 23:09:19 +0000 Subject: [PATCH 13/19] Fix primitive loop overflow check and refine benchmark docs Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/ac3b24cd-b3a0-43c3-a58a-f7a9984aa518 Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- benchmarks/run_cross_language_benchmark.sh | 4 ++-- .../interpreter/handler/ArrayOperationHandler.java | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/benchmarks/run_cross_language_benchmark.sh b/benchmarks/run_cross_language_benchmark.sh index fe06f0a7..7b29c70c 100644 --- a/benchmarks/run_cross_language_benchmark.sh +++ b/benchmarks/run_cross_language_benchmark.sh @@ -5,8 +5,8 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" BENCH_DIR="$ROOT_DIR/benchmarks" WORK_DIR="/tmp/coderive-cross-bench" RUNS="${1:-3}" -# Deterministic checksum for the three benchmark kernels (sum of squares, repeated fib, prime counting). -# Update this only if benchmark workload logic intentionally changes across all language implementations. +# Deterministic checksum combining: sum of squares (1..2M), repeated fib(35) x2000, +# and prime count (2..5000). Update only when workload logic changes across implementations. EXPECTED_CHECKSUM="2666668685121930669" mkdir -p "$WORK_DIR" diff --git a/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java b/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java index 505e051e..ba21a8a1 100644 --- a/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java +++ b/src/main/java/cod/interpreter/handler/ArrayOperationHandler.java @@ -300,10 +300,11 @@ public Object executeAdditiveLoopPrimitive( if (current == end) { break; } - if (current > Long.MAX_VALUE - step) { + long next = current + step; + if (next < current) { break; } - current += step; + current = next; } } else { while (current >= end) { @@ -315,10 +316,11 @@ public Object executeAdditiveLoopPrimitive( if (current == end) { break; } - if (current < Long.MIN_VALUE - step) { + long next = current + step; + if (next > current) { break; } - current += step; + current = next; } } return null; @@ -683,8 +685,8 @@ private boolean canUsePrimitiveAdditiveLoop(long start, long end, long step) { if (step == 0L) { return false; } - // Long.MIN_VALUE is excluded because negation/absolute-distance style reasoning is not representable, - // and overflow-safe primitive loop progression checks become ambiguous at this boundary. + // Long.MIN_VALUE is excluded because it cannot be safely negated (would overflow) + // and makes overflow-safe progression checks ambiguous. if (step == Long.MIN_VALUE) { return false; } From 00550d333dd82d4c1fa52292d09061ffb1f00ea6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 23:24:27 +0000 Subject: [PATCH 14/19] Phase B: add JVM integral fast path in scalar arithmetic Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/0a17ecc5-8663-48c9-918f-60759478dd1d Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- .../cod/interpreter/handler/TypeHandler.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/main/java/cod/interpreter/handler/TypeHandler.java b/src/main/java/cod/interpreter/handler/TypeHandler.java index dbdf0fe8..eed26e78 100644 --- a/src/main/java/cod/interpreter/handler/TypeHandler.java +++ b/src/main/java/cod/interpreter/handler/TypeHandler.java @@ -362,6 +362,10 @@ public double toDouble(Object o) { return num.doubleValue(); } + private boolean isJvmIntegralNumber(Object o) { + return o instanceof Integer || o instanceof Long || o instanceof Short || o instanceof Byte; + } + private boolean tryFastLongInto(Object o, long[] out, int index) { if (o instanceof Integer || o instanceof Long || o instanceof Short || o instanceof Byte) { out[index] = ((Number) o).longValue(); @@ -416,6 +420,16 @@ private Object addScalars(Object a, Object b) { return String.valueOf(a) + String.valueOf(b); } + if (isJvmIntegralNumber(a) && isJvmIntegralNumber(b)) { + long av = ((Number) a).longValue(); + long bv = ((Number) b).longValue(); + long sum = av + bv; + if (((av ^ sum) & (bv ^ sum)) >= 0) { + return AutoStackingNumber.fromLong(sum); + } + return AutoStackingNumber.fromDouble((double) av + (double) bv); + } + long[] fastPair = getFastLongPair(a, b); if (fastPair != null) { long av = fastPair[0]; @@ -443,6 +457,15 @@ public Object subtractNumbers(Object a, Object b) { } private Object subtractScalars(Object a, Object b) { + if (isJvmIntegralNumber(a) && isJvmIntegralNumber(b)) { + long av = ((Number) a).longValue(); + long bv = ((Number) b).longValue(); + long diff = av - bv; + if (((av ^ bv) & (av ^ diff)) >= 0) { + return AutoStackingNumber.fromLong(diff); + } + return AutoStackingNumber.fromDouble((double) av - (double) bv); + } long[] fastPair = getFastLongPair(a, b); if (fastPair != null) { @@ -486,6 +509,18 @@ private Object multiplyScalars(Object a, Object b) { return multiplyString(a, b); } + if (isJvmIntegralNumber(a) && isJvmIntegralNumber(b)) { + long av = ((Number) a).longValue(); + long bv = ((Number) b).longValue(); + if (av == 0L || bv == 0L) { + return AutoStackingNumber.fromLong(0L); + } + if (!isLongMultiplicationOverflow(av, bv)) { + return AutoStackingNumber.fromLong(av * bv); + } + return AutoStackingNumber.fromDouble((double) av * (double) bv); + } + long[] fastPair = getFastLongPair(a, b); if (fastPair != null) { long av = fastPair[0]; @@ -868,6 +903,18 @@ public Object divideNumbers(Object a, Object b) { } private Object divideScalars(Object a, Object b) { + if (isJvmIntegralNumber(a) && isJvmIntegralNumber(b)) { + long av = ((Number) a).longValue(); + long bv = ((Number) b).longValue(); + if (bv == 0L) { + throw new ProgramError("Division by zero"); + } + if (av % bv == 0L) { + return AutoStackingNumber.fromLong(av / bv); + } + return AutoStackingNumber.fromDouble((double) av / (double) bv); + } + long[] fastPair = getFastLongPair(a, b); if (fastPair != null) { long av = fastPair[0]; @@ -902,6 +949,15 @@ public Object modulusNumbers(Object a, Object b) { } private Object modulusScalars(Object a, Object b) { + if (isJvmIntegralNumber(a) && isJvmIntegralNumber(b)) { + long av = ((Number) a).longValue(); + long bv = ((Number) b).longValue(); + if (bv == 0L) { + throw new ProgramError("Modulus by zero"); + } + return AutoStackingNumber.fromLong(av % bv); + } + long[] fastPair = getFastLongPair(a, b); if (fastPair != null) { long av = fastPair[0]; @@ -951,6 +1007,12 @@ public int compare(Object a, Object b) { return strA.compareTo(strB); } + if (isJvmIntegralNumber(a) && isJvmIntegralNumber(b)) { + long av = ((Number) a).longValue(); + long bv = ((Number) b).longValue(); + return av < bv ? -1 : (av == bv ? 0 : 1); + } + long[] fastPair = getFastLongPair(a, b); if (fastPair != null) { long av = fastPair[0]; From c42e30f6180c393c7506e76173260c829bdd7897 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 23:28:05 +0000 Subject: [PATCH 15/19] Document overflow checks in Phase B arithmetic fast paths Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/0a17ecc5-8663-48c9-918f-60759478dd1d Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- src/main/java/cod/interpreter/handler/TypeHandler.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/cod/interpreter/handler/TypeHandler.java b/src/main/java/cod/interpreter/handler/TypeHandler.java index eed26e78..b3262756 100644 --- a/src/main/java/cod/interpreter/handler/TypeHandler.java +++ b/src/main/java/cod/interpreter/handler/TypeHandler.java @@ -424,6 +424,7 @@ private Object addScalars(Object a, Object b) { long av = ((Number) a).longValue(); long bv = ((Number) b).longValue(); long sum = av + bv; + // Signed-add overflow check: if a and b share a sign but sum flips sign, overflow occurred. if (((av ^ sum) & (bv ^ sum)) >= 0) { return AutoStackingNumber.fromLong(sum); } @@ -461,6 +462,7 @@ private Object subtractScalars(Object a, Object b) { long av = ((Number) a).longValue(); long bv = ((Number) b).longValue(); long diff = av - bv; + // Signed-sub overflow check: if operands differ in sign and diff flips relative to a, overflow occurred. if (((av ^ bv) & (av ^ diff)) >= 0) { return AutoStackingNumber.fromLong(diff); } From fd56ff6dc7fa70f127916896dd0369acdf6c5d97 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 23:47:28 +0000 Subject: [PATCH 16/19] Revert non-improving Phase B scalar fast-path continuation Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/39b21282-f452-436b-be40-3ff291018197 Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- .../cod/interpreter/handler/TypeHandler.java | 65 ------------------- 1 file changed, 65 deletions(-) diff --git a/src/main/java/cod/interpreter/handler/TypeHandler.java b/src/main/java/cod/interpreter/handler/TypeHandler.java index b3262756..f97ac563 100644 --- a/src/main/java/cod/interpreter/handler/TypeHandler.java +++ b/src/main/java/cod/interpreter/handler/TypeHandler.java @@ -362,10 +362,6 @@ public double toDouble(Object o) { return num.doubleValue(); } - private boolean isJvmIntegralNumber(Object o) { - return o instanceof Integer || o instanceof Long || o instanceof Short || o instanceof Byte; - } - private boolean tryFastLongInto(Object o, long[] out, int index) { if (o instanceof Integer || o instanceof Long || o instanceof Short || o instanceof Byte) { out[index] = ((Number) o).longValue(); @@ -420,17 +416,6 @@ private Object addScalars(Object a, Object b) { return String.valueOf(a) + String.valueOf(b); } - if (isJvmIntegralNumber(a) && isJvmIntegralNumber(b)) { - long av = ((Number) a).longValue(); - long bv = ((Number) b).longValue(); - long sum = av + bv; - // Signed-add overflow check: if a and b share a sign but sum flips sign, overflow occurred. - if (((av ^ sum) & (bv ^ sum)) >= 0) { - return AutoStackingNumber.fromLong(sum); - } - return AutoStackingNumber.fromDouble((double) av + (double) bv); - } - long[] fastPair = getFastLongPair(a, b); if (fastPair != null) { long av = fastPair[0]; @@ -458,17 +443,6 @@ public Object subtractNumbers(Object a, Object b) { } private Object subtractScalars(Object a, Object b) { - if (isJvmIntegralNumber(a) && isJvmIntegralNumber(b)) { - long av = ((Number) a).longValue(); - long bv = ((Number) b).longValue(); - long diff = av - bv; - // Signed-sub overflow check: if operands differ in sign and diff flips relative to a, overflow occurred. - if (((av ^ bv) & (av ^ diff)) >= 0) { - return AutoStackingNumber.fromLong(diff); - } - return AutoStackingNumber.fromDouble((double) av - (double) bv); - } - long[] fastPair = getFastLongPair(a, b); if (fastPair != null) { long av = fastPair[0]; @@ -511,18 +485,6 @@ private Object multiplyScalars(Object a, Object b) { return multiplyString(a, b); } - if (isJvmIntegralNumber(a) && isJvmIntegralNumber(b)) { - long av = ((Number) a).longValue(); - long bv = ((Number) b).longValue(); - if (av == 0L || bv == 0L) { - return AutoStackingNumber.fromLong(0L); - } - if (!isLongMultiplicationOverflow(av, bv)) { - return AutoStackingNumber.fromLong(av * bv); - } - return AutoStackingNumber.fromDouble((double) av * (double) bv); - } - long[] fastPair = getFastLongPair(a, b); if (fastPair != null) { long av = fastPair[0]; @@ -905,18 +867,6 @@ public Object divideNumbers(Object a, Object b) { } private Object divideScalars(Object a, Object b) { - if (isJvmIntegralNumber(a) && isJvmIntegralNumber(b)) { - long av = ((Number) a).longValue(); - long bv = ((Number) b).longValue(); - if (bv == 0L) { - throw new ProgramError("Division by zero"); - } - if (av % bv == 0L) { - return AutoStackingNumber.fromLong(av / bv); - } - return AutoStackingNumber.fromDouble((double) av / (double) bv); - } - long[] fastPair = getFastLongPair(a, b); if (fastPair != null) { long av = fastPair[0]; @@ -951,15 +901,6 @@ public Object modulusNumbers(Object a, Object b) { } private Object modulusScalars(Object a, Object b) { - if (isJvmIntegralNumber(a) && isJvmIntegralNumber(b)) { - long av = ((Number) a).longValue(); - long bv = ((Number) b).longValue(); - if (bv == 0L) { - throw new ProgramError("Modulus by zero"); - } - return AutoStackingNumber.fromLong(av % bv); - } - long[] fastPair = getFastLongPair(a, b); if (fastPair != null) { long av = fastPair[0]; @@ -1009,12 +950,6 @@ public int compare(Object a, Object b) { return strA.compareTo(strB); } - if (isJvmIntegralNumber(a) && isJvmIntegralNumber(b)) { - long av = ((Number) a).longValue(); - long bv = ((Number) b).longValue(); - return av < bv ? -1 : (av == bv ? 0 : 1); - } - long[] fastPair = getFastLongPair(a, b); if (fastPair != null) { long av = fastPair[0]; From 1b62f1dd24bdf40180a72cf6aa49599459636472 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 04:40:40 +0000 Subject: [PATCH 17/19] Make CodP-TAC default and optimize PTAC executor hot path Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/f3e1f61c-bb9b-4d3a-89ab-0514e5aff383 Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- src/main/java/cod/ptac/Executor.java | 533 +++++++++++++++++--- src/main/java/cod/ptac/Options.java | 6 +- src/main/java/cod/runner/CommandRunner.java | 17 +- 3 files changed, 488 insertions(+), 68 deletions(-) diff --git a/src/main/java/cod/ptac/Executor.java b/src/main/java/cod/ptac/Executor.java index 2ad13b9a..50d10a79 100644 --- a/src/main/java/cod/ptac/Executor.java +++ b/src/main/java/cod/ptac/Executor.java @@ -5,16 +5,103 @@ import cod.ast.node.Program; import java.math.BigInteger; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; public final class Executor { private final Options options; private static final Object FALLBACK_SENTINEL = new Object(); + private static final class RuntimeState { + int fallbackCount; + final Map memory = new HashMap(); + final Map slots = new HashMap(); + } + + private static final class FastRegisterMap extends AbstractMap { + private final Map indexByName = new HashMap(); + private final Object[] values; + private final Map overflow = new HashMap(); + + FastRegisterMap(Function function) { + int index = 0; + if (function != null && function.parameters != null) { + for (String parameter : function.parameters) { + if (parameter != null && !indexByName.containsKey(parameter)) { + indexByName.put(parameter, Integer.valueOf(index++)); + } + } + } + if (function != null && function.instructions != null) { + for (Instruction instruction : function.instructions) { + if (instruction == null) continue; + if (instruction.dest != null && !indexByName.containsKey(instruction.dest)) { + indexByName.put(instruction.dest, Integer.valueOf(index++)); + } + if (instruction.operands == null) continue; + for (Operand operand : instruction.operands) { + if (operand == null) continue; + if (operand.kind == OperandKind.REGISTER && operand.value instanceof String) { + String name = (String) operand.value; + if (!indexByName.containsKey(name)) { + indexByName.put(name, Integer.valueOf(index++)); + } + } + } + } + } + this.values = new Object[index]; + } + + @Override + public Object get(Object key) { + if (!(key instanceof String)) return null; + String name = (String) key; + Integer index = indexByName.get(name); + if (index != null) { + return values[index.intValue()]; + } + return overflow.get(name); + } + + @Override + public boolean containsKey(Object key) { + if (!(key instanceof String)) return false; + String name = (String) key; + return indexByName.containsKey(name) || overflow.containsKey(name); + } + + @Override + public Object put(String key, Object value) { + if (key == null) return null; + Integer index = indexByName.get(key); + if (index != null) { + int idx = index.intValue(); + Object previous = values[idx]; + values[idx] = value; + return previous; + } + return overflow.put(key, value); + } + + @Override + public Set> entrySet() { + Set> entries = new HashSet>(); + for (Map.Entry item : indexByName.entrySet()) { + int idx = item.getValue().intValue(); + entries.add(new AbstractMap.SimpleEntry(item.getKey(), values[idx])); + } + entries.addAll(overflow.entrySet()); + return entries; + } + } + private static final class Range { final BigInteger start; final BigInteger end; @@ -65,16 +152,26 @@ public Object execute(Artifact artifact, Interpreter fallbackInterpreter) { if (artifact == null) { throw new ProgramError("Cannot execute null CodP-TAC artifact"); } + RuntimeState state = new RuntimeState(); if (artifact.unit == null || artifact.unit.functions == null || artifact.unit.functions.isEmpty()) { - return fallback(artifact, fallbackInterpreter, "No executable CodP-TAC unit in artifact"); + return fallback(artifact, fallbackInterpreter, "No executable CodP-TAC unit in artifact", state); } - Function entry = findEntry(artifact.unit); + Map functionIndex = indexFunctions(artifact.unit); + Function entry = findEntry(artifact.unit, functionIndex); if (entry == null) { - return fallback(artifact, fallbackInterpreter, "No entry function found in CodP-TAC unit"); - } - Object result = executeFunction(artifact.unit, entry, new ArrayList(), fallbackInterpreter, artifact); + return fallback(artifact, fallbackInterpreter, "No entry function found in CodP-TAC unit", state); + } + Object result = executeFunction( + artifact.unit, + entry, + new ArrayList(), + fallbackInterpreter, + artifact, + functionIndex, + state + ); return result == FALLBACK_SENTINEL ? null : result; } @@ -83,9 +180,11 @@ private Object executeFunction( Function function, List args, Interpreter fallbackInterpreter, - Artifact artifact + Artifact artifact, + Map functionIndex, + RuntimeState state ) { - Map registers = new HashMap(); + Map registers = new FastRegisterMap(function); if (function.parameters != null) { for (int i = 0; i < function.parameters.size(); i++) { Object arg = i < args.size() ? args.get(i) : null; @@ -103,7 +202,16 @@ private Object executeFunction( continue; } ExecutionResult result = runInstruction( - unit, inst, registers, fallbackInterpreter, artifact, labels, pc + unit, + function, + inst, + registers, + fallbackInterpreter, + artifact, + labels, + pc, + functionIndex, + state ); if (result.fallback) { return FALLBACK_SENTINEL; @@ -122,12 +230,15 @@ private Object executeFunction( private ExecutionResult runInstruction( Unit unit, + Function currentFunction, Instruction inst, Map registers, Interpreter fallbackInterpreter, Artifact artifact, Map labels, - int currentPc + int currentPc, + Map functionIndex, + RuntimeState state ) { if (inst.opcode == Opcode.ASSIGN) { Object value = operandValue(inst.operands, 0, registers); @@ -162,7 +273,8 @@ private ExecutionResult runInstruction( Object fallback = fallback( artifact, fallbackInterpreter, - "Non-numeric range bounds are not yet natively executed" + "Non-numeric range bounds are not yet natively executed", + state ); if (fallback == FALLBACK_SENTINEL) return ExecutionResult.fallback(); } @@ -193,7 +305,8 @@ private ExecutionResult runInstruction( Object fallback = fallback( artifact, fallbackInterpreter, - "Unknown branch label: " + label + "Unknown branch label: " + label, + state ); if (fallback == FALLBACK_SENTINEL) return ExecutionResult.fallback(); } @@ -209,7 +322,8 @@ private ExecutionResult runInstruction( Object fallback = fallback( artifact, fallbackInterpreter, - "Unknown branch-if label: " + label + "Unknown branch-if label: " + label, + state ); if (fallback == FALLBACK_SENTINEL) return ExecutionResult.fallback(); } @@ -249,7 +363,15 @@ private ExecutionResult runInstruction( } if (inst.opcode == Opcode.MAP) { - Object mapped = runMap(unit, inst, registers, fallbackInterpreter, artifact); + Object mapped = runMap( + unit, + inst, + registers, + fallbackInterpreter, + artifact, + functionIndex, + state + ); if (mapped == FALLBACK_SENTINEL) { return ExecutionResult.fallback(); } @@ -258,7 +380,15 @@ private ExecutionResult runInstruction( } if (inst.opcode == Opcode.FILTER) { - Object filtered = runFilter(unit, inst, registers, fallbackInterpreter, artifact); + Object filtered = runFilter( + unit, + inst, + registers, + fallbackInterpreter, + artifact, + functionIndex, + state + ); if (filtered == FALLBACK_SENTINEL) { return ExecutionResult.fallback(); } @@ -267,7 +397,15 @@ private ExecutionResult runInstruction( } if (inst.opcode == Opcode.REDUCE) { - Object reduced = runReduce(unit, inst, registers, fallbackInterpreter, artifact); + Object reduced = runReduce( + unit, + inst, + registers, + fallbackInterpreter, + artifact, + functionIndex, + state + ); if (reduced == FALLBACK_SENTINEL) { return ExecutionResult.fallback(); } @@ -276,7 +414,15 @@ private ExecutionResult runInstruction( } if (inst.opcode == Opcode.FILTER_MAP) { - Object filteredMapped = runFilterMap(unit, inst, registers, fallbackInterpreter, artifact); + Object filteredMapped = runFilterMap( + unit, + inst, + registers, + fallbackInterpreter, + artifact, + functionIndex, + state + ); if (filteredMapped == FALLBACK_SENTINEL) { return ExecutionResult.fallback(); } @@ -284,17 +430,66 @@ private ExecutionResult runInstruction( return ExecutionResult.normal(filteredMapped); } + if (inst.opcode == Opcode.SLOT_SET) { + String slotName = String.valueOf(operandValue(inst.operands, 0, registers)); + Object value = operandValue(inst.operands, 1, registers); + state.slots.put(slotName, value); + if (inst.dest != null) registers.put(inst.dest, value); + return ExecutionResult.normal(value); + } + + if (inst.opcode == Opcode.SLOT_GET) { + String slotName = String.valueOf(operandValue(inst.operands, 0, registers)); + Object value = state.slots.get(slotName); + if (inst.dest != null) registers.put(inst.dest, value); + return ExecutionResult.normal(value); + } + + if (inst.opcode == Opcode.SLOT_UNPACK) { + Object source = operandValue(inst.operands, 0, registers); + if (source instanceof Map) { + @SuppressWarnings("unchecked") + Map sourceMap = (Map) source; + for (Map.Entry entry : sourceMap.entrySet()) { + if (entry.getKey() instanceof String) { + String key = (String) entry.getKey(); + state.slots.put(key, entry.getValue()); + registers.put(key, entry.getValue()); + } + } + } + return ExecutionResult.normal(source); + } + + if (inst.opcode == Opcode.SLOT_RET) { + return ExecutionResult.returned(new HashMap(state.slots)); + } + + if (inst.opcode == Opcode.SLOT_DIV) { + return ExecutionResult.normal(null); + } + + if (inst.opcode == Opcode.STORE) { + Object address = operandValue(inst.operands, 0, registers); + Object value = operandValue(inst.operands, 1, registers); + state.memory.put(address, value); + if (inst.dest != null) registers.put(inst.dest, value); + return ExecutionResult.normal(value); + } + + if (inst.opcode == Opcode.LOAD) { + Object address = operandValue(inst.operands, 0, registers); + Object value = state.memory.get(address); + if (inst.dest != null) registers.put(inst.dest, value); + return ExecutionResult.normal(value); + } + if (inst.opcode == Opcode.FILTER || inst.opcode == Opcode.SCAN || inst.opcode == Opcode.ZIP || inst.opcode == Opcode.WHERE || inst.opcode == Opcode.FILTER_MAP_REDUCE || inst.opcode == Opcode.LAZY_SLICE - || inst.opcode == Opcode.SLOT_GET - || inst.opcode == Opcode.SLOT_SET - || inst.opcode == Opcode.SLOT_RET - || inst.opcode == Opcode.SLOT_UNPACK - || inst.opcode == Opcode.SLOT_DIV || inst.opcode == Opcode.ANCESTOR || inst.opcode == Opcode.SELF || inst.opcode == Opcode.TAIL_CALL @@ -302,25 +497,41 @@ private ExecutionResult runInstruction( || inst.opcode == Opcode.FORMULA_SEQ || inst.opcode == Opcode.FORMULA_COND || inst.opcode == Opcode.FORMULA_RECUR - || inst.opcode == Opcode.FORMULA_FUSE - || inst.opcode == Opcode.STORE - || inst.opcode == Opcode.LOAD) { - Object fallback = fallback(artifact, fallbackInterpreter, "Opcode not yet natively executed: " + inst.opcode); + || inst.opcode == Opcode.FORMULA_FUSE) { + Object fallback = fallback( + artifact, + fallbackInterpreter, + "Opcode not yet natively executed: " + inst.opcode, + state + ); if (fallback == FALLBACK_SENTINEL) return ExecutionResult.fallback(); } if (inst.opcode == Opcode.CALL) { String functionName = String.valueOf(operandValue(inst.operands, 0, registers)); - Function target = findFunction(unit, functionName); + Function target = findFunction(functionIndex, functionName); if (target == null) { - Object fallback = fallback(artifact, fallbackInterpreter, "Unknown function: " + functionName); + Object fallback = fallback( + artifact, + fallbackInterpreter, + "Unknown function: " + functionName, + state + ); if (fallback == FALLBACK_SENTINEL) return ExecutionResult.fallback(); } List args = new ArrayList(); for (int i = 1; i < inst.operands.size(); i++) { args.add(operandValue(inst.operands, i, registers)); } - Object result = executeFunction(unit, target, args, fallbackInterpreter, artifact); + Object result = executeFunction( + unit, + target, + args, + fallbackInterpreter, + artifact, + functionIndex, + state + ); if (result == FALLBACK_SENTINEL) { return ExecutionResult.fallback(); } @@ -335,7 +546,15 @@ private ExecutionResult runInstruction( return ExecutionResult.normal(null); } - private Object fallback(Artifact artifact, Interpreter fallbackInterpreter, String reason) { + private Object fallback( + Artifact artifact, + Interpreter fallbackInterpreter, + String reason, + RuntimeState state + ) { + if (state != null) { + state.fallbackCount++; + } if (!options.isFallbackEnabled()) { throw new ProgramError("CodP-TAC execution failed without fallback: " + reason); } @@ -353,20 +572,29 @@ private Object fallback(Artifact artifact, Interpreter fallbackInterpreter, Stri throw new ProgramError("CodP-TAC fallback unavailable: " + reason); } - private Function findEntry(Unit unit) { + private Function findEntry(Unit unit, Map functionIndex) { if (unit.entryFunction != null) { - Function explicit = findFunction(unit, unit.entryFunction); + Function explicit = findFunction(functionIndex, unit.entryFunction); if (explicit != null) return explicit; } - return findFunction(unit, "main"); + return findFunction(functionIndex, "main"); } - private Function findFunction(Unit unit, String name) { - if (unit == null || unit.functions == null || name == null) return null; - for (Function fn : unit.functions) { - if (fn != null && name.equals(fn.name)) return fn; + private Map indexFunctions(Unit unit) { + if (unit == null || unit.functions == null || unit.functions.isEmpty()) { + return Collections.emptyMap(); } - return null; + Map functions = new HashMap(); + for (Function function : unit.functions) { + if (function == null || function.name == null) continue; + functions.put(function.name, function); + } + return functions; + } + + private Function findFunction(Map functionIndex, String name) { + if (functionIndex == null || functionIndex.isEmpty() || name == null) return null; + return functionIndex.get(name); } private Object operandValue(List operands, int index, Map registers) { @@ -416,6 +644,53 @@ private boolean isCompare(Opcode opcode) { } private Object evaluateMath(Opcode opcode, Object a, Object b) { + if (isFloatingLike(a) || isFloatingLike(b)) { + double left = toDouble(a); + double right = toDouble(b); + if (opcode == Opcode.ADD) return Double.valueOf(left + right); + if (opcode == Opcode.SUB) return Double.valueOf(left - right); + if (opcode == Opcode.MUL) return Double.valueOf(left * right); + if (opcode == Opcode.DIV) { + if (right == 0.0d) { + throw new ProgramError("CodP-TAC division by zero"); + } + return Double.valueOf(left / right); + } + if (opcode == Opcode.MOD) { + if (right == 0.0d) { + throw new ProgramError("CodP-TAC modulo by zero"); + } + return Double.valueOf(left % right); + } + } + + if (isLongLike(a) && isLongLike(b)) { + long left = toLong(a); + long right = toLong(b); + if (opcode == Opcode.ADD && !willOverflowAdd(left, right)) { + return Long.valueOf(left + right); + } + if (opcode == Opcode.SUB && !willOverflowSub(left, right)) { + return Long.valueOf(left - right); + } + if (opcode == Opcode.MUL && !willOverflowMul(left, right)) { + return Long.valueOf(left * right); + } + if (opcode == Opcode.DIV) { + if (right == 0L) { + throw new ProgramError("CodP-TAC division by zero"); + } + return Long.valueOf(left / right); + } + if (opcode == Opcode.MOD) { + if (right == 0L) { + throw new ProgramError("CodP-TAC modulo by zero"); + } + long modulus = right < 0L ? -right : right; + return Long.valueOf(left % modulus); + } + } + BigInteger left = toBigInt(a); BigInteger right = toBigInt(b); if (opcode == Opcode.ADD) return left.add(right); @@ -437,6 +712,30 @@ private Object evaluateMath(Opcode opcode, Object a, Object b) { } private Boolean evaluateCompare(Opcode opcode, Object a, Object b) { + if (isFloatingLike(a) || isFloatingLike(b)) { + double left = toDouble(a); + double right = toDouble(b); + int cmp = left < right ? -1 : (left > right ? 1 : 0); + if (opcode == Opcode.EQ) return cmp == 0; + if (opcode == Opcode.NE) return cmp != 0; + if (opcode == Opcode.GT) return cmp > 0; + if (opcode == Opcode.LT) return cmp < 0; + if (opcode == Opcode.GTE) return cmp >= 0; + if (opcode == Opcode.LTE) return cmp <= 0; + return false; + } + if (isLongLike(a) && isLongLike(b)) { + long left = toLong(a); + long right = toLong(b); + int cmp = left < right ? -1 : (left > right ? 1 : 0); + if (opcode == Opcode.EQ) return cmp == 0; + if (opcode == Opcode.NE) return cmp != 0; + if (opcode == Opcode.GT) return cmp > 0; + if (opcode == Opcode.LT) return cmp < 0; + if (opcode == Opcode.GTE) return cmp >= 0; + if (opcode == Opcode.LTE) return cmp <= 0; + return false; + } BigInteger left = toBigInt(a); BigInteger right = toBigInt(b); int cmp = left.compareTo(right); @@ -488,23 +787,33 @@ private Object runMap( Instruction inst, Map registers, Interpreter fallbackInterpreter, - Artifact artifact + Artifact artifact, + Map functionIndex, + RuntimeState state ) { List source = asSequence(operandValue(inst.operands, 0, registers)); if (source == null) { - return fallback(artifact, fallbackInterpreter, "MAP source is not a sequence"); + return fallback(artifact, fallbackInterpreter, "MAP source is not a sequence", state); } String mapperName = String.valueOf(operandValue(inst.operands, 1, registers)); - Function mapper = findFunction(unit, mapperName); + Function mapper = findFunction(functionIndex, mapperName); if (mapper == null) { - return fallback(artifact, fallbackInterpreter, "MAP function not found: " + mapperName); + return fallback(artifact, fallbackInterpreter, "MAP function not found: " + mapperName, state); } List out = new ArrayList(source.size()); for (int i = 0; i < source.size(); i++) { Object element = source.get(i); List args = new ArrayList(); args.add(element); - Object mapped = executeFunction(unit, mapper, args, fallbackInterpreter, artifact); + Object mapped = executeFunction( + unit, + mapper, + args, + fallbackInterpreter, + artifact, + functionIndex, + state + ); if (mapped == FALLBACK_SENTINEL) { return FALLBACK_SENTINEL; } @@ -518,23 +827,38 @@ private Object runFilter( Instruction inst, Map registers, Interpreter fallbackInterpreter, - Artifact artifact + Artifact artifact, + Map functionIndex, + RuntimeState state ) { List source = asSequence(operandValue(inst.operands, 0, registers)); if (source == null) { - return fallback(artifact, fallbackInterpreter, "FILTER source is not a sequence"); + return fallback(artifact, fallbackInterpreter, "FILTER source is not a sequence", state); } String predicateName = String.valueOf(operandValue(inst.operands, 1, registers)); - Function predicate = findFunction(unit, predicateName); + Function predicate = findFunction(functionIndex, predicateName); if (predicate == null) { - return fallback(artifact, fallbackInterpreter, "FILTER function not found: " + predicateName); + return fallback( + artifact, + fallbackInterpreter, + "FILTER function not found: " + predicateName, + state + ); } List out = new ArrayList(); for (int i = 0; i < source.size(); i++) { Object element = source.get(i); List args = new ArrayList(); args.add(element); - Object keep = executeFunction(unit, predicate, args, fallbackInterpreter, artifact); + Object keep = executeFunction( + unit, + predicate, + args, + fallbackInterpreter, + artifact, + functionIndex, + state + ); if (keep == FALLBACK_SENTINEL) { return FALLBACK_SENTINEL; } @@ -550,26 +874,41 @@ private Object runReduce( Instruction inst, Map registers, Interpreter fallbackInterpreter, - Artifact artifact + Artifact artifact, + Map functionIndex, + RuntimeState state ) { List source = asSequence(operandValue(inst.operands, 0, registers)); if (source == null) { - return fallback(artifact, fallbackInterpreter, "REDUCE source is not a sequence"); + return fallback(artifact, fallbackInterpreter, "REDUCE source is not a sequence", state); } if (source.isEmpty()) { return null; } String reducerName = String.valueOf(operandValue(inst.operands, 1, registers)); - Function reducer = findFunction(unit, reducerName); + Function reducer = findFunction(functionIndex, reducerName); if (reducer == null) { - return fallback(artifact, fallbackInterpreter, "REDUCE function not found: " + reducerName); + return fallback( + artifact, + fallbackInterpreter, + "REDUCE function not found: " + reducerName, + state + ); } Object accumulator = source.get(0); for (int i = 1; i < source.size(); i++) { List args = new ArrayList(); args.add(accumulator); args.add(source.get(i)); - Object reduced = executeFunction(unit, reducer, args, fallbackInterpreter, artifact); + Object reduced = executeFunction( + unit, + reducer, + args, + fallbackInterpreter, + artifact, + functionIndex, + state + ); if (reduced == FALLBACK_SENTINEL) { return FALLBACK_SENTINEL; } @@ -583,21 +922,24 @@ private Object runFilterMap( Instruction inst, Map registers, Interpreter fallbackInterpreter, - Artifact artifact + Artifact artifact, + Map functionIndex, + RuntimeState state ) { List source = asSequence(operandValue(inst.operands, 0, registers)); if (source == null) { - return fallback(artifact, fallbackInterpreter, "FILTER_MAP source is not a sequence"); + return fallback(artifact, fallbackInterpreter, "FILTER_MAP source is not a sequence", state); } String predicateName = String.valueOf(operandValue(inst.operands, 1, registers)); String mapperName = String.valueOf(operandValue(inst.operands, 2, registers)); - Function predicate = findFunction(unit, predicateName); - Function mapper = findFunction(unit, mapperName); + Function predicate = findFunction(functionIndex, predicateName); + Function mapper = findFunction(functionIndex, mapperName); if (predicate == null || mapper == null) { return fallback( artifact, fallbackInterpreter, - "FILTER_MAP function not found: predicate=" + predicateName + ", mapper=" + mapperName + "FILTER_MAP function not found: predicate=" + predicateName + ", mapper=" + mapperName, + state ); } List out = new ArrayList(); @@ -605,14 +947,30 @@ private Object runFilterMap( Object element = source.get(i); List predicateArgs = new ArrayList(); predicateArgs.add(element); - Object keep = executeFunction(unit, predicate, predicateArgs, fallbackInterpreter, artifact); + Object keep = executeFunction( + unit, + predicate, + predicateArgs, + fallbackInterpreter, + artifact, + functionIndex, + state + ); if (keep == FALLBACK_SENTINEL) { return FALLBACK_SENTINEL; } if (isTruthy(keep)) { List mapperArgs = new ArrayList(); mapperArgs.add(element); - Object mapped = executeFunction(unit, mapper, mapperArgs, fallbackInterpreter, artifact); + Object mapped = executeFunction( + unit, + mapper, + mapperArgs, + fallbackInterpreter, + artifact, + functionIndex, + state + ); if (mapped == FALLBACK_SENTINEL) { return FALLBACK_SENTINEL; } @@ -749,4 +1107,61 @@ private BigInteger toBigInt(Object value) { return BigInteger.ZERO; } } + + private boolean isFloatingLike(Object value) { + if (value instanceof Float || value instanceof Double) return true; + if (value instanceof Number) return false; + if (value == null) return false; + String text = String.valueOf(value).trim(); + return text.indexOf('.') >= 0 || text.indexOf('e') >= 0 || text.indexOf('E') >= 0; + } + + private boolean isLongLike(Object value) { + if (value == null) return false; + if (value instanceof Float || value instanceof Double) return false; + if (value instanceof Number || value instanceof BigInteger) return true; + try { + Long.parseLong(String.valueOf(value).trim()); + return true; + } catch (Exception ignored) { + return false; + } + } + + private long toLong(Object value) { + if (value == null) return 0L; + if (value instanceof Number) return ((Number) value).longValue(); + if (value instanceof BigInteger) return ((BigInteger) value).longValue(); + try { + return Long.parseLong(String.valueOf(value).trim()); + } catch (Exception ignored) { + return 0L; + } + } + + private double toDouble(Object value) { + if (value == null) return 0.0d; + if (value instanceof Number) return ((Number) value).doubleValue(); + try { + return Double.parseDouble(String.valueOf(value).trim()); + } catch (Exception ignored) { + return 0.0d; + } + } + + private boolean willOverflowAdd(long a, long b) { + return (b > 0L && a > Long.MAX_VALUE - b) || (b < 0L && a < Long.MIN_VALUE - b); + } + + private boolean willOverflowSub(long a, long b) { + return (b < 0L && a > Long.MAX_VALUE + b) || (b > 0L && a < Long.MIN_VALUE + b); + } + + private boolean willOverflowMul(long a, long b) { + if (a == 0L || b == 0L) return false; + if (a == Long.MIN_VALUE && b == -1L) return true; + if (b == Long.MIN_VALUE && a == -1L) return true; + long result = a * b; + return result / b != a; + } } diff --git a/src/main/java/cod/ptac/Options.java b/src/main/java/cod/ptac/Options.java index 1f479bbb..ab41e4aa 100644 --- a/src/main/java/cod/ptac/Options.java +++ b/src/main/java/cod/ptac/Options.java @@ -51,14 +51,14 @@ public boolean isFallbackEnabled() { } private static Mode parseMode(String raw) { - if (raw == null) return Mode.INTERPRETER; + if (raw == null) return Mode.COMPILE_EXECUTE; String normalized = raw.trim().toLowerCase(); if ("interpreter".equals(normalized)) return Mode.INTERPRETER; - if ("compile-only".equals(normalized)) return Mode.COMPILE_ONLY; + if ("compile-only".equals(normalized) || "compile_only".equals(normalized)) return Mode.COMPILE_ONLY; if ("compile_execute".equals(normalized) || "compile-execute".equals(normalized)) { return Mode.COMPILE_EXECUTE; } - return Mode.INTERPRETER; + return Mode.COMPILE_EXECUTE; } private static String firstNonEmpty(String a, String b) { diff --git a/src/main/java/cod/runner/CommandRunner.java b/src/main/java/cod/runner/CommandRunner.java index 13ac6c45..a72db633 100644 --- a/src/main/java/cod/runner/CommandRunner.java +++ b/src/main/java/cod/runner/CommandRunner.java @@ -36,6 +36,7 @@ public void run(String[] args) throws Exception { } String outputFilename = null; + boolean forceInterpreter = false; RunnerConfig config = processArgs( @@ -52,7 +53,7 @@ public void configure(RunnerConfig config) { for (int i = 0; i < args.length; i++) { String arg = args[i]; if ("--interpret".equals(arg) || "-i".equals(arg)) { - // Default mode, do nothing + forceInterpreter = true; } else if ("-o".equals(arg)) { if (i + 1 < args.length) { outputFilename = args[i + 1]; @@ -102,7 +103,7 @@ public void configure(RunnerConfig config) { // Initialize IR manager initializeIRManager(); - executeInterpretation(ast); + executeInterpretation(ast, forceInterpreter); DebugSystem.info(NAME + LOG_TAG, "CommandRunner execution completed"); } finally { @@ -256,7 +257,7 @@ private void initializeIRManager() { } } - private void executeInterpretation(Program ast) { + private void executeInterpretation(Program ast, boolean forceInterpreter) { DebugSystem.info(NAME + LOG_TAG, "Starting program interpretation"); boolean hasImports = @@ -278,7 +279,11 @@ private void executeInterpretation(Program ast) { DebugSystem.debug(NAME + LOG_TAG, "Skipping index/IR generation (no imports)"); } - if (ptacOptions.isCompileExecuteEnabled() && irManager != null && ast != null && ast.unit != null) { + if (!forceInterpreter + && ptacOptions.isCompileExecuteEnabled() + && irManager != null + && ast != null + && ast.unit != null) { Type entryType = findMainType(ast); if (entryType != null) { Artifact artifact = irManager.loadArtifact(ast.unit.name, entryType.name); @@ -335,7 +340,7 @@ private void printHelp() { out(" CommandRunner compile [-f|--full]"); out(); out("Options:"); - out(" -i, --interpret Interpret the program (default)"); + out(" -i, --interpret Force AST interpreter execution"); out(" -o Write output to file"); out(" --debug Enable debug output"); out(" --trace Enable trace-level debugging"); @@ -346,7 +351,7 @@ private void printHelp() { out(" compile Compile source to bytecode container (.codc with .codb entries)"); out(" -f, --full Full compile all .cod files under src/main"); out("Environment flags:"); - out(" COD_PTAC_MODE=interpreter|compile-only|compile-execute"); + out(" COD_PTAC_MODE=interpreter|compile-only|compile-execute (default: compile-execute)"); out(" COD_PTAC_FALLBACK=true|false"); out(); out("Examples:"); From 1320b075c63fea257fcedf884559cf937203f095 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 06:35:11 +0000 Subject: [PATCH 18/19] Add lexer parser throughput benchmark suite Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/937e7bdf-e1e4-4a0a-b2e8-a9a9c2a85014 Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- .../lexer_parser/go/lexer_parser_bench.go | 148 +++++++++++++++++ .../java/CoderiveLexerParserBenchmark.java | 66 ++++++++ .../java/JavaLexerParserBenchmark.java | 143 ++++++++++++++++ .../lexer_parser/kotlin/LexerParserBench.kt | 106 ++++++++++++ .../lexer_parser/lua/lexer_parser_bench.lua | 103 ++++++++++++ .../lexer_parser/python/lexer_parser_bench.py | 118 ++++++++++++++ .../run_lexer_parser_benchmark.sh | 152 ++++++++++++++++++ 7 files changed, 836 insertions(+) create mode 100644 benchmarks/lexer_parser/go/lexer_parser_bench.go create mode 100644 benchmarks/lexer_parser/java/CoderiveLexerParserBenchmark.java create mode 100644 benchmarks/lexer_parser/java/JavaLexerParserBenchmark.java create mode 100644 benchmarks/lexer_parser/kotlin/LexerParserBench.kt create mode 100644 benchmarks/lexer_parser/lua/lexer_parser_bench.lua create mode 100755 benchmarks/lexer_parser/python/lexer_parser_bench.py create mode 100755 benchmarks/lexer_parser/run_lexer_parser_benchmark.sh diff --git a/benchmarks/lexer_parser/go/lexer_parser_bench.go b/benchmarks/lexer_parser/go/lexer_parser_bench.go new file mode 100644 index 00000000..0e526d6b --- /dev/null +++ b/benchmarks/lexer_parser/go/lexer_parser_bench.go @@ -0,0 +1,148 @@ +package main + +import ( +"bufio" +"fmt" +"os" +"strconv" +"strings" +) + +func isAlpha(c byte) bool { +return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' +} + +func isDigit(c byte) bool { +return c >= '0' && c <= '9' +} + +func lexParse(text string) uint64 { +n := len(text) +i := 0 +var tokens, stmts, depth, maxDepth, kindSum uint64 + +for i < n { +c := text[i] +if c == ' ' || c == '\t' || c == '\r' || c == '\n' { +if c == '\n' { +stmts++ +} +i++ +continue +} +if c == '/' && i+1 < n && text[i+1] == '/' { +i += 2 +for i < n && text[i] != '\n' { +i++ +} +continue +} +if c == '/' && i+1 < n && text[i+1] == '*' { +i += 2 +for i+1 < n && !(text[i] == '*' && text[i+1] == '/') { +i++ +} +i += 2 +if i > n { +i = n +} +continue +} +if isAlpha(c) { +start := i +i++ +for i < n && (isAlpha(text[i]) || isDigit(text[i])) { +i++ +} +tokens++ +kindSum += uint64((i - start) % 97) +continue +} +if isDigit(c) { +i++ +for i < n && (isDigit(text[i]) || text[i] == '.') { +i++ +} +tokens++ +kindSum += 3 +continue +} +if c == '"' || c == '\'' { +quote := c +i++ +for i < n { +d := text[i] +if d == '\\' { +i += 2 +continue +} +if d == quote { +i++ +break +} +i++ +} +tokens++ +kindSum += 7 +continue +} +if c == '(' || c == '[' || c == '{' { +depth++ +if depth > maxDepth { +maxDepth = depth +} +} else if (c == ')' || c == ']' || c == '}') && depth > 0 { +depth-- +} +if c == ';' { +stmts++ +} +tokens++ +kindSum++ +i++ +} +return tokens*31 + stmts*17 + depth*13 + maxDepth*7 + kindSum +} + +func main() { +if len(os.Args) < 3 { +fmt.Fprintln(os.Stderr, "usage: lexer_parser_bench ") +os.Exit(2) +} +fileList := os.Args[1] +iterations, err := strconv.Atoi(os.Args[2]) +if err != nil { +panic(err) +} + +file, err := os.Open(fileList) +if err != nil { +panic(err) +} +defer file.Close() + +var paths []string +scanner := bufio.NewScanner(file) +for scanner.Scan() { +line := strings.TrimSpace(scanner.Text()) +if line != "" { +paths = append(paths, line) +} +} +if err := scanner.Err(); err != nil { +panic(err) +} + +var digest uint64 = 1469598103934665603 +for i := 0; i < iterations; i++ { +for _, p := range paths { +b, err := os.ReadFile(p) +if err != nil { +panic(err) +} +digest = digest*1315423911 + lexParse(string(b)) +} +} + +fmt.Printf("DIGEST:%d\n", digest) +} diff --git a/benchmarks/lexer_parser/java/CoderiveLexerParserBenchmark.java b/benchmarks/lexer_parser/java/CoderiveLexerParserBenchmark.java new file mode 100644 index 00000000..ace18540 --- /dev/null +++ b/benchmarks/lexer_parser/java/CoderiveLexerParserBenchmark.java @@ -0,0 +1,66 @@ +package benchmarks.lexer_parser.java; + +import cod.ast.node.Program; +import cod.lexer.MainLexer; +import cod.lexer.Token; +import cod.parser.MainParser; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +public final class CoderiveLexerParserBenchmark { + private static long mix(long digest, long value) { + return digest * 1315423911L + value; + } + + private static List readPaths(String fileList) throws Exception { + List out = new ArrayList(); + BufferedReader reader = new BufferedReader(new FileReader(fileList)); + try { + String line; + while ((line = reader.readLine()) != null) { + String trimmed = line.trim(); + if (!trimmed.isEmpty()) out.add(trimmed); + } + } finally { + reader.close(); + } + return out; + } + + public static void main(String[] args) throws Exception { + if (args.length < 2) { + System.err.println("usage: CoderiveLexerParserBenchmark "); + System.exit(2); + } + + String fileList = args[0]; + int iterations = Integer.parseInt(args[1]); + List paths = readPaths(fileList); + + long digest = 1469598103934665603L; + for (int i = 0; i < iterations; i++) { + for (String p : paths) { + String source = new String(Files.readAllBytes(Paths.get(p)), StandardCharsets.UTF_8); + MainLexer lexer = new MainLexer(source); + List tokens = lexer.tokenize(); + MainParser parser = new MainParser(tokens); + Program program = parser.parseProgram(); + + long value = tokens.size(); + if (program != null && program.unit != null) { + value += (program.unit.types != null ? program.unit.types.size() : 0) * 17L; + value += (program.unit.policies != null ? program.unit.policies.size() : 0) * 29L; + } + digest = mix(digest, value); + } + } + + System.out.println("DIGEST:" + Long.toUnsignedString(digest)); + } +} diff --git a/benchmarks/lexer_parser/java/JavaLexerParserBenchmark.java b/benchmarks/lexer_parser/java/JavaLexerParserBenchmark.java new file mode 100644 index 00000000..0bd3d372 --- /dev/null +++ b/benchmarks/lexer_parser/java/JavaLexerParserBenchmark.java @@ -0,0 +1,143 @@ +package benchmarks.lexer_parser.java; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +public final class JavaLexerParserBenchmark { + private static boolean isAlpha(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; + } + + private static boolean isDigit(char c) { + return c >= '0' && c <= '9'; + } + + private static long mix(long digest, long value) { + return digest * 1315423911L + value; + } + + private static long lexParse(String text) { + int n = text.length(); + int i = 0; + long tokens = 0; + long stmts = 0; + long depth = 0; + long maxDepth = 0; + long kindSum = 0; + + while (i < n) { + char c = text.charAt(i); + + if (Character.isWhitespace(c)) { + if (c == '\n') stmts++; + i++; + continue; + } + + if (c == '/' && i + 1 < n && text.charAt(i + 1) == '/') { + i += 2; + while (i < n && text.charAt(i) != '\n') i++; + continue; + } + + if (c == '/' && i + 1 < n && text.charAt(i + 1) == '*') { + i += 2; + while (i + 1 < n && !(text.charAt(i) == '*' && text.charAt(i + 1) == '/')) i++; + i = Math.min(i + 2, n); + continue; + } + + if (isAlpha(c)) { + int start = i; + i++; + while (i < n && (isAlpha(text.charAt(i)) || isDigit(text.charAt(i)))) i++; + tokens++; + kindSum += (i - start) % 97; + continue; + } + + if (isDigit(c)) { + i++; + while (i < n && (isDigit(text.charAt(i)) || text.charAt(i) == '.')) i++; + tokens++; + kindSum += 3; + continue; + } + + if (c == '"' || c == '\'') { + char quote = c; + i++; + while (i < n) { + char d = text.charAt(i); + if (d == '\\') { + i += 2; + continue; + } + if (d == quote) { + i++; + break; + } + i++; + } + tokens++; + kindSum += 7; + continue; + } + + if (c == '(' || c == '[' || c == '{') { + depth++; + if (depth > maxDepth) maxDepth = depth; + } else if ((c == ')' || c == ']' || c == '}') && depth > 0) { + depth--; + } + if (c == ';') stmts++; + + tokens++; + kindSum++; + i++; + } + + return (((tokens * 31 + stmts * 17 + depth * 13 + maxDepth * 7) + kindSum)); + } + + private static List readPaths(String fileList) throws Exception { + List out = new ArrayList(); + BufferedReader reader = new BufferedReader(new FileReader(fileList)); + try { + String line; + while ((line = reader.readLine()) != null) { + String trimmed = line.trim(); + if (!trimmed.isEmpty()) out.add(trimmed); + } + } finally { + reader.close(); + } + return out; + } + + public static void main(String[] args) throws Exception { + if (args.length < 2) { + System.err.println("usage: JavaLexerParserBenchmark "); + System.exit(2); + } + + String fileList = args[0]; + int iterations = Integer.parseInt(args[1]); + List paths = readPaths(fileList); + long digest = 1469598103934665603L; + + for (int i = 0; i < iterations; i++) { + for (String p : paths) { + String text = new String(Files.readAllBytes(Paths.get(p)), StandardCharsets.UTF_8); + digest = mix(digest, lexParse(text)); + } + } + + System.out.println("DIGEST:" + Long.toUnsignedString(digest)); + } +} diff --git a/benchmarks/lexer_parser/kotlin/LexerParserBench.kt b/benchmarks/lexer_parser/kotlin/LexerParserBench.kt new file mode 100644 index 00000000..22b7ce9d --- /dev/null +++ b/benchmarks/lexer_parser/kotlin/LexerParserBench.kt @@ -0,0 +1,106 @@ +import java.io.File + +private fun isAlpha(c: Char): Boolean = c == '_' || c in 'a'..'z' || c in 'A'..'Z' +private fun isDigit(c: Char): Boolean = c in '0'..'9' + +private fun lexParse(text: String): Long { + var i = 0 + val n = text.length + var tokens = 0L + var stmts = 0L + var depth = 0L + var maxDepth = 0L + var kindSum = 0L + + while (i < n) { + val c = text[i] + + if (c.isWhitespace()) { + if (c == '\n') stmts++ + i++ + continue + } + + if (c == '/' && i + 1 < n && text[i + 1] == '/') { + i += 2 + while (i < n && text[i] != '\n') i++ + continue + } + + if (c == '/' && i + 1 < n && text[i + 1] == '*') { + i += 2 + while (i + 1 < n && !(text[i] == '*' && text[i + 1] == '/')) i++ + i = minOf(i + 2, n) + continue + } + + if (isAlpha(c)) { + val start = i + i++ + while (i < n && (isAlpha(text[i]) || isDigit(text[i]))) i++ + tokens++ + kindSum += (i - start) % 97 + continue + } + + if (isDigit(c)) { + i++ + while (i < n && (isDigit(text[i]) || text[i] == '.')) i++ + tokens++ + kindSum += 3 + continue + } + + if (c == '"' || c == '\'') { + val quote = c + i++ + while (i < n) { + val d = text[i] + if (d == '\\') { + i += 2 + continue + } + if (d == quote) { + i++ + break + } + i++ + } + tokens++ + kindSum += 7 + continue + } + + if (c == '(' || c == '[' || c == '{') { + depth++ + if (depth > maxDepth) maxDepth = depth + } else if ((c == ')' || c == ']' || c == '}') && depth > 0) { + depth-- + } + if (c == ';') stmts++ + + tokens++ + kindSum++ + i++ + } + + return tokens * 31 + stmts * 17 + depth * 13 + maxDepth * 7 + kindSum +} + +fun main(args: Array) { + require(args.size >= 2) { "usage: LexerParserBench " } + + val fileList = File(args[0]) + val iterations = args[1].toInt() + val paths = fileList.readLines().map { it.trim() }.filter { it.isNotEmpty() } + + var digest = 1469598103934665603L + repeat(iterations) { + for (p in paths) { + val text = File(p).readText() + digest = digest * 1315423911L + lexParse(text) + } + } + + println("DIGEST:${java.lang.Long.toUnsignedString(digest)}") +} diff --git a/benchmarks/lexer_parser/lua/lexer_parser_bench.lua b/benchmarks/lexer_parser/lua/lexer_parser_bench.lua new file mode 100644 index 00000000..e5f20eba --- /dev/null +++ b/benchmarks/lexer_parser/lua/lexer_parser_bench.lua @@ -0,0 +1,103 @@ +local function is_alpha(c) + return c == '_' or (c >= 'a' and c <= 'z') or (c >= 'A' and c <= 'Z') +end + +local function is_digit(c) + return c >= '0' and c <= '9' +end + +local function lex_parse(text) + local i = 1 + local n = #text + local tokens, stmts, depth, max_depth, kind_sum = 0, 0, 0, 0, 0 + + while i <= n do + local c = text:sub(i, i) + + if c:match('%s') then + if c == '\n' then stmts = stmts + 1 end + i = i + 1 + elseif c == '/' and i + 1 <= n and text:sub(i + 1, i + 1) == '/' then + i = i + 2 + while i <= n and text:sub(i, i) ~= '\n' do i = i + 1 end + elseif c == '/' and i + 1 <= n and text:sub(i + 1, i + 1) == '*' then + i = i + 2 + while i + 1 <= n and not (text:sub(i, i) == '*' and text:sub(i + 1, i + 1) == '/') do i = i + 1 end + i = math.min(i + 2, n + 1) + elseif is_alpha(c) then + local start = i + i = i + 1 + while i <= n do + local d = text:sub(i, i) + if not (is_alpha(d) or is_digit(d)) then break end + i = i + 1 + end + tokens = tokens + 1 + kind_sum = kind_sum + ((i - start) % 97) + elseif is_digit(c) then + i = i + 1 + while i <= n do + local d = text:sub(i, i) + if not (is_digit(d) or d == '.') then break end + i = i + 1 + end + tokens = tokens + 1 + kind_sum = kind_sum + 3 + elseif c == '"' or c == "'" then + local quote = c + i = i + 1 + while i <= n do + local d = text:sub(i, i) + if d == '\\' then + i = i + 2 + elseif d == quote then + i = i + 1 + break + else + i = i + 1 + end + end + tokens = tokens + 1 + kind_sum = kind_sum + 7 + else + if c == '(' or c == '[' or c == '{' then + depth = depth + 1 + if depth > max_depth then max_depth = depth end + elseif (c == ')' or c == ']' or c == '}') and depth > 0 then + depth = depth - 1 + end + if c == ';' then stmts = stmts + 1 end + tokens = tokens + 1 + kind_sum = kind_sum + 1 + i = i + 1 + end + end + + return tokens * 31 + stmts * 17 + depth * 13 + max_depth * 7 + kind_sum +end + +if #arg < 2 then + io.stderr:write('usage: lexer_parser_bench.lua \n') + os.exit(2) +end + +local file_list = arg[1] +local iterations = tonumber(arg[2]) + +local paths = {} +for line in io.lines(file_list) do + line = line:gsub('^%s+', ''):gsub('%s+$', '') + if line ~= '' then table.insert(paths, line) end +end + +local digest = 1469598103934665603 +for _ = 1, iterations do + for _, p in ipairs(paths) do + local f = assert(io.open(p, 'rb')) + local text = f:read('*a') + f:close() + digest = (digest * 1315423911 + lex_parse(text)) % 18446744073709551616 + end +end + +print(string.format('DIGEST:%.0f', digest)) diff --git a/benchmarks/lexer_parser/python/lexer_parser_bench.py b/benchmarks/lexer_parser/python/lexer_parser_bench.py new file mode 100755 index 00000000..60a40a17 --- /dev/null +++ b/benchmarks/lexer_parser/python/lexer_parser_bench.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +import sys + + +def is_alpha(c: str) -> bool: + return c == '_' or ('a' <= c <= 'z') or ('A' <= c <= 'Z') + + +def is_digit(c: str) -> bool: + return '0' <= c <= '9' + + +def lex_parse(text: str) -> int: + i = 0 + n = len(text) + tokens = 0 + stmts = 0 + depth = 0 + max_depth = 0 + kind_sum = 0 + + while i < n: + c = text[i] + + if c.isspace(): + if c == '\n': + stmts += 1 + i += 1 + continue + + if c == '/' and i + 1 < n and text[i + 1] == '/': + i += 2 + while i < n and text[i] != '\n': + i += 1 + continue + + if c == '/' and i + 1 < n and text[i + 1] == '*': + i += 2 + while i + 1 < n and not (text[i] == '*' and text[i + 1] == '/'): + i += 1 + i = min(i + 2, n) + continue + + if is_alpha(c): + start = i + i += 1 + while i < n and (is_alpha(text[i]) or is_digit(text[i])): + i += 1 + tokens += 1 + kind_sum += (i - start) % 97 + continue + + if is_digit(c): + i += 1 + while i < n and (is_digit(text[i]) or text[i] == '.'): + i += 1 + tokens += 1 + kind_sum += 3 + continue + + if c == '"' or c == "'": + quote = c + i += 1 + while i < n: + d = text[i] + if d == '\\': + i += 2 + continue + if d == quote: + i += 1 + break + i += 1 + tokens += 1 + kind_sum += 7 + continue + + if c in '([{': + depth += 1 + if depth > max_depth: + max_depth = depth + elif c in ')]}' and depth > 0: + depth -= 1 + + if c == ';': + stmts += 1 + + tokens += 1 + kind_sum += 1 + i += 1 + + return tokens * 31 + stmts * 17 + depth * 13 + max_depth * 7 + kind_sum + + +def main() -> None: + if len(sys.argv) < 3: + print('usage: lexer_parser_bench.py ', file=sys.stderr) + raise SystemExit(2) + + file_list = sys.argv[1] + iterations = int(sys.argv[2]) + + with open(file_list, 'r', encoding='utf-8') as f: + paths = [line.strip() for line in f if line.strip()] + + digest = 1469598103934665603 + mask = (1 << 64) - 1 + + for _ in range(iterations): + for p in paths: + with open(p, 'r', encoding='utf-8') as f: + text = f.read() + digest = ((digest * 1315423911) + lex_parse(text)) & mask + + print(f'DIGEST:{digest}') + + +if __name__ == '__main__': + main() diff --git a/benchmarks/lexer_parser/run_lexer_parser_benchmark.sh b/benchmarks/lexer_parser/run_lexer_parser_benchmark.sh new file mode 100755 index 00000000..52141674 --- /dev/null +++ b/benchmarks/lexer_parser/run_lexer_parser_benchmark.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +BENCH_DIR="$ROOT_DIR/benchmarks/lexer_parser" +WORK_DIR="/tmp/coderive-lexer-parser-bench" +RUNS="${1:-3}" +ITERATIONS="${2:-20}" + +mkdir -p "$WORK_DIR" + +has_cmd() { + command -v "$1" >/dev/null 2>&1 +} + +extract_digest() { + local output="$1" + local line + line="$(printf '%s\n' "$output" | grep '^DIGEST:' | tail -n 1 || true)" + if [[ -z "$line" ]]; then + return 1 + fi + printf '%s' "${line#DIGEST:}" +} + +median_ms() { + local values=("$@") + local sorted + sorted="$(printf '%s\n' "${values[@]}" | sort -n)" + local count + count="$(printf '%s\n' "$sorted" | wc -l | tr -d ' ')" + local index=$(( (count + 1) / 2 )) + printf '%s\n' "$sorted" | sed -n "${index}p" +} + +run_many() { + local lang="$1" + shift + local -a command=("$@") + local -a times=() + local baseline_digest="" + + local i + for ((i=1; i<=RUNS; i++)); do + local start_ns end_ns elapsed_ns elapsed_ms output digest + start_ns="$(date +%s%N)" + output="$("${command[@]}")" + end_ns="$(date +%s%N)" + elapsed_ns=$((end_ns - start_ns)) + elapsed_ms=$((elapsed_ns / 1000000)) + digest="$(extract_digest "$output")" || { + echo "[${lang}] ERROR: missing DIGEST output" + echo "$output" + return 1 + } + if [[ -z "$baseline_digest" ]]; then + baseline_digest="$digest" + elif [[ "$digest" != "$baseline_digest" ]]; then + echo "[${lang}] ERROR: non-deterministic digest (got ${digest}, expected ${baseline_digest})" + return 1 + fi + times+=("$elapsed_ms") + done + + local median + median="$(median_ms "${times[@]}")" + echo "${lang}|${median}" +} + +total_bytes() { + awk '{sum += $1} END {print sum}' +} + +echo "Lexer/parser throughput benchmark (runs: ${RUNS}, iterations per run: ${ITERATIONS})" + +{ + find "$ROOT_DIR/benchmarks/coderive" -type f -name '*.cod' +} | sort > "$WORK_DIR/files.txt" +if [[ ! -s "$WORK_DIR/files.txt" ]]; then + echo "No .cod files found under src/main/cod" + exit 1 +fi + +BYTES_PER_ITER="$(xargs -d '\n' wc -c < "$WORK_DIR/files.txt" | total_bytes)" +if [[ -z "$BYTES_PER_ITER" || "$BYTES_PER_ITER" -le 0 ]]; then + echo "Failed to compute corpus size" + exit 1 +fi +TOTAL_BYTES=$((BYTES_PER_ITER * ITERATIONS)) + +rm -rf "$WORK_DIR/coderive-java" "$WORK_DIR/java-bin" "$WORK_DIR/go-bin" "$WORK_DIR/kotlin" +mkdir -p "$WORK_DIR/coderive-java" "$WORK_DIR/java-bin" "$WORK_DIR/kotlin" + +javac -d "$WORK_DIR/coderive-java" $(find "$ROOT_DIR/src/main/java" -name '*.java') \ + "$BENCH_DIR/java/CoderiveLexerParserBenchmark.java" + +if has_cmd javac; then + javac -d "$WORK_DIR/java-bin" "$BENCH_DIR/java/JavaLexerParserBenchmark.java" +fi + +if has_cmd go; then + go build -o "$WORK_DIR/go-bin" "$BENCH_DIR/go/lexer_parser_bench.go" +fi + +if has_cmd kotlinc; then + kotlinc "$BENCH_DIR/kotlin/LexerParserBench.kt" -include-runtime -d "$WORK_DIR/kotlin/bench.jar" +fi + +echo +printf "%-10s | %-9s | %s\n" "Language" "Median ms" "Throughput MB/s" +echo "-----------|-----------|----------------" + +print_row() { + local lang="$1" + local median="$2" + local throughput + throughput="$(awk -v bytes="$TOTAL_BYTES" -v ms="$median" 'BEGIN { if (ms <= 0) print "inf"; else printf "%.2f", (bytes / 1000000.0) / (ms / 1000.0) }')" + printf "%-10s | %-9s | %s\n" "$lang" "$median" "$throughput" +} + +row="$(run_many "Coderive" java -cp "$WORK_DIR/coderive-java" benchmarks.lexer_parser.java.CoderiveLexerParserBenchmark "$WORK_DIR/files.txt" "$ITERATIONS")" +print_row "$(echo "$row" | cut -d'|' -f1)" "$(echo "$row" | cut -d'|' -f2)" + +if [[ -f "$WORK_DIR/java-bin/benchmarks/lexer_parser/java/JavaLexerParserBenchmark.class" ]]; then + row="$(run_many "Java" java -cp "$WORK_DIR/java-bin" benchmarks.lexer_parser.java.JavaLexerParserBenchmark "$WORK_DIR/files.txt" "$ITERATIONS")" + print_row "$(echo "$row" | cut -d'|' -f1)" "$(echo "$row" | cut -d'|' -f2)" +fi + +if [[ -x "$WORK_DIR/go-bin" ]]; then + row="$(run_many "Go" "$WORK_DIR/go-bin" "$WORK_DIR/files.txt" "$ITERATIONS")" + print_row "$(echo "$row" | cut -d'|' -f1)" "$(echo "$row" | cut -d'|' -f2)" +fi + +if [[ -f "$WORK_DIR/kotlin/bench.jar" ]]; then + row="$(run_many "Kotlin" java -jar "$WORK_DIR/kotlin/bench.jar" "$WORK_DIR/files.txt" "$ITERATIONS")" + print_row "$(echo "$row" | cut -d'|' -f1)" "$(echo "$row" | cut -d'|' -f2)" +fi + +if has_cmd python3; then + row="$(run_many "Python" python3 "$BENCH_DIR/python/lexer_parser_bench.py" "$WORK_DIR/files.txt" "$ITERATIONS")" + print_row "$(echo "$row" | cut -d'|' -f1)" "$(echo "$row" | cut -d'|' -f2)" +fi + +if has_cmd lua; then + row="$(run_many "Lua" lua "$BENCH_DIR/lua/lexer_parser_bench.lua" "$WORK_DIR/files.txt" "$ITERATIONS")" + print_row "$(echo "$row" | cut -d'|' -f1)" "$(echo "$row" | cut -d'|' -f2)" +elif has_cmd lua5.4; then + row="$(run_many "Lua" lua5.4 "$BENCH_DIR/lua/lexer_parser_bench.lua" "$WORK_DIR/files.txt" "$ITERATIONS")" + print_row "$(echo "$row" | cut -d'|' -f1)" "$(echo "$row" | cut -d'|' -f2)" +else + echo "[skip] Lua benchmark: lua interpreter not found" +fi From 6a3846e0518fc047be3592040e0d7f8f18d0b472 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 06:40:55 +0000 Subject: [PATCH 19/19] Add v0.9.2 changelog and lexer parser benchmark docs Agent-Logs-Url: https://github.com/coderive-lang/Coderive/sessions/937e7bdf-e1e4-4a0a-b2e8-a9a9c2a85014 Co-authored-by: DanexCodr <216312766+DanexCodr@users.noreply.github.com> --- CHANGELOG.md | 24 +++++++++++++++++++++++ benchmarks/README.md | 6 ++++++ benchmarks/lexer_parser/README.md | 32 +++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 benchmarks/lexer_parser/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index be165ed9..c8f9841a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,30 @@ All notable changes to Coderive are documented in this file. +## [v0.9.2] - Why Slow? - April 17, 2026 + +### 🔬 Lexer/Parser Throughput Baseline (New) +- Added a dedicated cross-language lexer/parser throughput suite under `benchmarks/lexer_parser/`. +- New runner: `bash benchmarks/lexer_parser/run_lexer_parser_benchmark.sh `. +- Current baseline (run: `5` medians, `1000` iterations): + +| Language | Median ms | Throughput MB/s | +|----------|-----------|-----------------| +| Coderive | 632 | 2.22 | +| Java | 111 | 12.61 | +| Go | 17 | 82.35 | +| Kotlin | 162 | 8.64 | +| Python | 270 | 5.19 | +| Lua | 228 | 6.14 | + +### 🧭 What This Suggests To Improve Next +- **Parser construction/validation overhead is dominant** in Coderive relative to pure scanner+light-parser baselines. +- **Tokenizer object churn remains significant** (token allocation and parser handoff pressure). +- **Near-term focus**: + 1. add a lexer-only throughput mode in Coderive benchmarks to isolate lexing cost from parse cost, + 2. reduce parser backtracking/rewind-heavy paths in `MainParser` declaration probing, + 3. introduce low-allocation token-stream views for parser hot paths. + ## [v0.9.0] - Platform Snapshot - April 13, 2026 ### 🔀 Merge Coverage for This Snapshot diff --git a/benchmarks/README.md b/benchmarks/README.md index eaf47181..35eb6ef2 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -45,3 +45,9 @@ The runner: - verifies checksum consistency - prints median time in milliseconds - skips languages missing toolchains + +## Lexer/Parser throughput benchmark + +See: + +`benchmarks/lexer_parser/README.md` diff --git a/benchmarks/lexer_parser/README.md b/benchmarks/lexer_parser/README.md new file mode 100644 index 00000000..1b34c161 --- /dev/null +++ b/benchmarks/lexer_parser/README.md @@ -0,0 +1,32 @@ +# Lexer/Parser Throughput Benchmark + +This suite benchmarks lexer/parser throughput for: + +- Coderive (real `MainLexer` + `MainParser`) +- Java +- Go +- Kotlin +- Python +- Lua + +## Run + +From repository root: + +```bash +bash benchmarks/lexer_parser/run_lexer_parser_benchmark.sh +``` + +Optional: + +```bash +bash benchmarks/lexer_parser/run_lexer_parser_benchmark.sh +``` + +- `runs`: median sample count (default `3`) +- `iterations`: full corpus passes per run (default `20`) + +Output columns: + +- **Median ms**: median wall time per language +- **Throughput MB/s**: processed corpus bytes per second