diff --git a/.github/workflows/quick-test.yml b/.github/workflows/quick-test.yml index edc31e64..0479a5d1 100644 --- a/.github/workflows/quick-test.yml +++ b/.github/workflows/quick-test.yml @@ -15,8 +15,8 @@ jobs: find src/main/java -name "*.java" > sources.txt mkdir -p target/classes javac -d target/classes @sources.txt - # Test with first .cdrv file found - TEST_FILE=$(find src/main/cdrv -name "*.cdrv" -type f | head -1) + # Test with first .cod file found + TEST_FILE=$(find src/main/cod -name "*.cod" -type f | head -1) if [ -n "$TEST_FILE" ]; then - java -cp target/classes cdrv.runner.CompilerRunner --bytecode "$TEST_FILE" - fi \ No newline at end of file + java -cp target/classes cod.runner.CompilerRunner --bytecode "$TEST_FILE" + fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fd06343..d615e7ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ All notable changes to Coderive are documented in this file. +## [v0.3.0] - The Design Leap - December 15, 2025 + +### 🚨 Breaking Changes +- **Completely abandoned `~|` to instead use `::` return slot operator** - Embracing suffix-style design, fully abandoning the prefix-style. +- **Replaced stack-based multi-arch compiler** - Ensuring lesser bug to show. (Status: Ongoing, Currently not priority) +- **Almost all classes renamed updated, and repackaged** - Full refactoring of classes and their packages for more cleaner view. +- **Replaced `` to use `` instead for variable declaration.** - `name: type` for declaration, `name: type = value` for explicit declaration and assignment, `name := value` for inferred declaration and assignment,`name = value` for reassignment + +### ✨ Major Features +- **Three World System** + - Added three distinct types of programs for Coderive: Script, Method-only, and Module. +- **From String to Text** + - Replaced the previous `string` keyword with `text`. +- **Natural Array** + - Added supports for range for arrays + - Lazy array generation + - Immutable by default but can be mutable (Note: "Use moderately") + - Supports text as natural array. +- **Added numeric shorthands** + - Added support for common numeric shorthands: K, M, B, T, Q, Qi, e + - Case for this feature will be case-sensitive. +- **Added parameter skipping and named arguments support** + +> Check for other new minor features if you have free time... + ## [v0.2.3] - The Great Logic Revolution - November 23, 2025 ### 🚨 Breaking Changes @@ -99,4 +124,4 @@ user == any[admin, moderator, owner] ### 🎉 Initial Launch - Created first repository for Coderive programming language - Initial commit with foundational codebase structure -- Project inception marking the start of Coderive language development \ No newline at end of file +- Project inception marking the start of Coderive language development diff --git a/README.md b/README.md index a5eb2256..5752a47b 100644 --- a/README.md +++ b/README.md @@ -83,8 +83,9 @@ Coderive replaces traditional boolean operators with expressive quantifiers: ```python share Calculator { - ~| int result, string operation # Return slot declarations - local calculate(int a, int b) { + local calculate(int a, int b) + :: result: int, operation: text + { ~> a + b, "addition" # Slot assignments } } @@ -104,13 +105,14 @@ for i by *2 in 1 to 10 { # Complex step patterns unit sample.program get { - cod.Math + lang.Math } share InteractiveDemo { - ~| int formula, string operation - local calculate(int a, int b, string op) { + local calculate(a: int, b: int, op: text) + :: formula: int, operation: text + { if all[a >= 0, b >= 0] { if op == any["+", "-", "*"] { ~> a + b, "valid operation" @@ -121,21 +123,10 @@ share InteractiveDemo { } ``` -## 4. Performance Validation +## 4. Compiler -The system demonstrates efficient execution across both interpreter and native compilation targets. -Internal performance profiling shows that Coderive's quantifier operations approach the efficiency of -highly optimized data structures in established languages. - -
- -|Feature|Status|Target|Details| -|-------|------|------|-------| -|Interpreter|✅ Working|JVM Bytecode|Full language support| -|Native Compilation|✅ Working|ARM64/x86_64|Advanced register allocation| -|Quantifier Performance|✅ Validated|All targets|Efficient short-circuiting| - -
+The system has an on going TAC Compiler for efficient execution across both interpreter and native compilation targets. +Internal performance profiling shows that ## 5. Getting Started @@ -155,33 +146,6 @@ java -jar coderive.jar program.cod java -jar coderive.jar --native program.cod ``` -**Compilation Output** - -``` -[20:26:28.018] [INFO] RUNNER: Starting MTOT compilation pipeline -[20:26:28.056] [INFO] RUNNER: AST built successfully -[20:26:28.068] [INFO] BYTECODE: Compilation complete: 1076 instructions across 9 methods -[20:26:28.227] [INFO] MTOT: Full compilation pipeline complete. -``` - -**Generated Assembly Sample** - -A snippet of the resulting ARM64 assembly code from the compilation pipeline: - -```assembly - .text - .global add -add: - stp x29, x30, [sp, #-16]! - mov x29, sp - sub sp, sp, #48 - // Saving callee-saved registers - stp x19, x20, [x29, #-16] - // ... ARM64 assembly code - mov x0, x23 - ret -``` - ## 6. Current Status
@@ -190,9 +154,7 @@ add: |---------|------|-----| |Interpreter|✅ Complete|Full language features| |Native Code Generation|✅ Complete|ARM64/x86_64 support| -|Register Allocation|✅ Complete|Predictive spilling| -|String Handling|🔧 In Progress|Enhanced implementation| -|Type System|🔧 In Progress|Extended features| +|TAC Compiler|🔧 In Progress|Enhanced implementation|
diff --git a/lib/.nodata b/lib/.nodata deleted file mode 100644 index 8b137891..00000000 --- a/lib/.nodata +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/antlr4-runtime-4.13.2.jar b/lib/antlr4-runtime-4.13.2.jar deleted file mode 100644 index 350c1d0f..00000000 Binary files a/lib/antlr4-runtime-4.13.2.jar and /dev/null differ diff --git a/src/main/cod/ArrayTest.cod b/src/main/cod/ArrayTest.cod index 5e063a5e..a331dfd4 100644 --- a/src/main/cod/ArrayTest.cod +++ b/src/main/cod/ArrayTest.cod @@ -1,32 +1,67 @@ -unit sample get { - cod.Math -} - -share ArrayTest { - share main() { - // Array declaration and initialization - int[] numbers = [1, 2, 3, 4, 5] - output "Array: " + numbers - - // Array access - output "First element: " + numbers[0] - output "Third element: " + numbers[2] - - // String array - string[] names = ["Alice", "Bob", "Charlie"] - output "Names: " + names - output "Second name: " + names[1] - - // Array assignment - numbers[0] = 100 - output "After assignment - First element: " + numbers[0] - - // Empty array - int[] empty = [] - output "Empty array: " + empty - - // Single element array - int[] single = [42] - output "Single element: " + single[0] - } -} \ No newline at end of file +use { + lang.Sys, + lang.Math +} + +// Test 2 - Should work based on parseTypeReference() output +local test2() :: arr: [int] { + ~> arr: [1, 2, 3] +} + + share main() { + // Array declaration and initialization + numbers: [int] = [1, 2, 3, 4, 5] + Sys.println("Array: " + numbers) + + // Array access + Sys.println("First element: " + numbers[0]) + Sys.println("Third element: " + numbers[2]) + + // String array + names: [text] = ["Alice", "Bob", "Charlie"] + Sys.println("Names: " + names) + Sys.println("Second name: " + names[1]) + + // Array assignment + numbers[0] = 100 + Sys.println("After assignment - First element: " + numbers[0]) + + // Empty array + empty := [] + Sys.println("Empty array: " + empty) + + // Single element array + single: [] = [42] + Sys.println("Single element: " + single[0]) + + x := [1 to 10] + Sys.println("Simple natural array: " + x[2]) + + Sys.println("Big number: " + 9.999Qi) + + x = [by 2 in 168 to 9.999Qi] + Sys.println("Multiplicative natural array: ") + Sys.println("Before mutation: " + x[20000]) + x[20000] = "hello" + Sys.println("After mutation: " + x[20000]) + + x = [[1 to 10]] + Sys.println("Simple nested natural array: " + x[0][2]) + + x = [[1 to 10], [by 2 in 3 to 100]] + Sys.println("Nested natural array: " + x[1][5]) + + x = ["a" to "Zz"] + Sys.println("Alphabet natural array: " + x[52]) + + range := [1 to 10] + container := [range] + Sys.println(range[5]) + Sys.println(container[0][1]) + Sys.println(container[0][5]) + + // Test string formatting + Sys.println("Test: |" + "trailing space " + "|") + Sys.println("Number: " + 40168) + Sys.println(9.999Qi) + } \ No newline at end of file diff --git a/src/main/cod/EvenOdd.cod b/src/main/cod/EvenOdd.cod index 0e0d34b9..696b1550 100644 --- a/src/main/cod/EvenOdd.cod +++ b/src/main/cod/EvenOdd.cod @@ -1,13 +1,12 @@ -share EvenOdd { - share main() { - int number = 5 - output "The number is " + number - if (number % 2) == 0 { - output "The number is even." - } else { - output "The number is odd." - } - } +use { + lang.Sys +} +number := 5 +Sys.outln("The number is " + number) +if (number % 2) == 0 { + Sys.outln("The number is even.") +} else { + Sys.outln("The number is odd.") } \ No newline at end of file diff --git a/src/main/cod/ImportTest.cod b/src/main/cod/ImportTest.cod index 221a11d1..9c69dfea 100644 --- a/src/main/cod/ImportTest.cod +++ b/src/main/cod/ImportTest.cod @@ -1,30 +1,33 @@ -unit sample get { - cod.Math +unit sample + +use { + lang.Sys, + lang.Math } share ImportTest { share testMathOperations() { - output "=== Testing Math Import ===" + Sys.outln("=== Testing Math Import ===") // Test calling imported math functions - sqrtResult = [result]:cod.Math.sqrt(16) - output "Square root of 16: " + sqrtResult + sqrtResult := [0]:Math.sqrt(16) + Sys.outln("Square root of 16: " + sqrtResult) - powResult = [result]:cod.Math.pow(2, 3) - output "2 to the power of 3: " + powResult + powResult := [0]:Math.pow(2, 3) + Sys.outln("2 to the power of 3: " + powResult) - maxResult = [result]:cod.Math.max(10, 20) - output "Maximum of 10 and 20: " + maxResult + maxResult := [0]:Math.max(10, 20) + Sys.outln("Maximum of 10 and 20: " + maxResult) } share main() { - output "=== IMPORT SYSTEM TEST ===" - output "" + Sys.outln("=== IMPORT SYSTEM TEST ===") + Sys.outln("") testMathOperations() - output "" + Sys.outln("") - output "=== TEST COMPLETE ===" + Sys.outln("=== TEST COMPLETE ===") } } \ No newline at end of file diff --git a/src/main/cod/InteractiveDemo.cod b/src/main/cod/InteractiveDemo.cod index 75ac24dd..140619ca 100644 --- a/src/main/cod/InteractiveDemo.cod +++ b/src/main/cod/InteractiveDemo.cod @@ -1,70 +1,78 @@ unit sample -get { - cod.Sys +use { + lang.Sys } share InteractiveDemo { // Supporting both else-if and elif - ~| int formula, string operation - local calculate(int a, int b, string op) { + local calculate(a: int, b: int, op: text) + :: formula: int|float, operation: text { if op == "+" { // Separate return assignments - ~> formula a + b - ~> operation "addition" + ~> formula: a + b + ~> operation: "addition" } else if op == "-" { // One line return assignments - ~> formula a - b, operation "subtraction" + ~> formula: a - b, operation: "subtraction" } else if op == "*" { // implicit one line return assignments ~> a * b, "multiplication" } else if op == "/" { // mixed one line return assignments - ~> formula a / b, "division" + ~> formula: a / b, "division" } else { ~> 0, "unknown" } } // Method with single expression - local add(int a, int b) ~> a + b + local add(a: int, b: int) ~> a + b - // TBA: This should fail the compilation but for now doesn't yet. - ~| x, y - local haha(int a) { + local haha(a: int) :: x: int, y: int { + // Without '~> value' this will make a compile error } // Array demonstration local demonstrateArrays() { - Sys.outln("=== Array Demo ===") + Sys.println("=== Array Demo ===") + + num1: [] = [1, 2, 3] + + Sys.println("Trying num1: [] = " + num1) + + num2: [int] = [2, 3, 4, 5, 6] - numbers = [10, 20, 30, 40, 50] - Sys.outln("Array: " + numbers) + Sys.println("Trying num2: [int] = " + num2) - Sys.outln("First element: " + numbers[0] + " ") - Sys.outa("Last element:" + numbers[4]) // testing output + // With implicitness + num3 := [10, 20, 30, 40, 50] + Sys.println("Trying: num3 := " + num3) - numbers[2] = 99 - Sys.outln("After modification: " + numbers) + Sys.println("First element: " + num3[0]) + Sys.println("Last element: " + num3[4]) + + num3[2] = 99 + Sys.println("After modification: " + num3) } // Input demonstration with ENHANCED VALIDATION local getUserInfo() { - Sys.outln("=== User Registration ===") + Sys.println("=== User Registration ===") - Sys.outln("Enter your name:") - name = (string) input + Sys.println("Enter your name:") + name := (text) input - Sys.outln("Enter your age:") - age = (int) input + Sys.println("Enter your age:") + age := (int) input - Sys.outln("Enter your height (meters):") - height = (float) input + Sys.println("Enter your height (meters):") + height := (float) input - Sys.outln("Are you a student? (true/false)") - isStudent = (bool) input + Sys.println("Are you a student? (true/false)") + isStudent := (bool) input // NEW: Showcasing 'all' for comprehensive validation if all[ @@ -74,276 +82,238 @@ share InteractiveDemo { height > 0.0, height < 3.0 ] { - Sys.outln("") - Sys.outln("Registration Complete!") - Sys.outln("Name: " + name) - Sys.outln("Age: " + age) - Sys.outln("Height: " + height + "m") - Sys.outln("Student: " + isStudent) + Sys.println("") + Sys.println("Registration Complete!") + Sys.println("Name: " + name) + Sys.println("Age: " + age) + Sys.println("Height: " + height + "m") + Sys.println("Student: " + isStudent) // NEW: Showcasing 'any' for role-based greeting if any[isStudent, age < 25] { - Sys.outln("Welcome young user " + name + "!") + Sys.println("Welcome young user " + name + "!") } else { - Sys.outln("Welcome " + name + "!") + Sys.println("Welcome " + name + "!") } } else { - Sys.outln("") - Sys.outln(" Registration Failed: Invalid data provided!") + Sys.println("") + Sys.println(" Registration Failed: Invalid data provided!") } } // Calculator with user input + VALIDATION SHOWCASE local interactiveCalculator() { - Sys.outln("=== Interactive Calculator ===") + Sys.println("=== Interactive Calculator ===") - Sys.outln("Enter first number:") - num1 = (int) input + Sys.println("Enter first number:") + num1 := (int) input - Sys.outln("Enter second number:") - num2 = (int) input + Sys.println("Enter second number:") + num2 := (int) input - Sys.outln("Enter operation (+, -, *, /):") - op = (string) input + Sys.println("Enter operation (+, -, *, /):") + op := (text) input // NEW: Showcasing conditional chains for validation! if op == any["+", "-", "*", "/"] { - result, operation = [formula, operation]:calculate(num1, num2, op) + result, operation := [formula, operation]:calculate(num1, num2, op) - Sys.outln("") - Sys.outln("Calculation Result:") - Sys.outln(num1 + " " + op + " " + num2 + " = " + result) - Sys.outln("Operation: " + operation) + Sys.println("") + Sys.println("Calculation Result:") + Sys.println(num1 + " " + op + " " + num2 + " = " + result) + Sys.println("Operation: " + operation) } else { - Sys.outln("") - Sys.outln("Error: '" + op + "' is not a valid operation!") - Sys.outln("Please use one of: +, -, *, /") + Sys.println("") + Sys.println("Error: '" + op + "' is not a valid operation!") + Sys.println("Please use one of: +, -, *, /") } } // Loop demonstration with input share numberSeries() { - Sys.outln("=== Number Series Generator ===") + Sys.println("=== Number Series Generator ===") - Sys.outln("Enter start number:") - start = (int) input + Sys.println("Enter start number:") + start := (int) input - Sys.outln("Enter end number:") - end = (int) input + Sys.println("Enter end number:") + end := (int) input - Sys.outln("Number series from " + start + " to " + end + ":") + Sys.println("Number series from " + start + " to " + end + ":") - Sys.outln("") - Sys.outln("=== DEFAULT STEPS (No 'by' clause) ===") - Sys.outln("Counting up naturally:") + Sys.println("") + Sys.println("=== DEFAULT STEPS (No 'by' clause) ===") + Sys.println("Counting up naturally:") for i in 1 to 5 { - Sys.outln("Default step: " + i) // 1, 2, 3, 4, 5 + Sys.println("Default step: " + i) // 1, 2, 3, 4, 5 } - Sys.outln("Counting down naturally:") + Sys.println("Counting down naturally:") for i in 5 to 1 { - Sys.outln("Default step: " + i) // 5, 4, 3, 2, 1 + Sys.println("Default step: " + i) // 5, 4, 3, 2, 1 } - Sys.outln("User range - smart default:") + Sys.println("User range - smart default:") for i in start to end { - Sys.outln("Smart default: " + i) // auto +1 or -1 based on start/end + Sys.println("Smart default: " + i) // auto +1 or -1 based on start/end } - Sys.outln("") - Sys.outln("=== BASIC STEPS ===") - Sys.outln("Enter step size:") - steps = (int) input + Sys.println("") + Sys.println("=== BASIC STEPS ===") + Sys.println("Enter step size:") + steps := (int) input for i by steps in start to end { - Sys.outln("Step by " + steps + ": " + i) - } - - // Manual assignments - for i by i = i + 1 in start to end { - Sys.outln("Manual i=i+1: " + i) - } - - // Compound assignments - for i by i += 1 in start to end { - Sys.outln("Compound i+=1: " + i) + Sys.println("Step by " + steps + ": " + i) } // Operator prefixes for i by +1 in start to end { - Sys.outln("Step by +1: " + i) + Sys.println("Step by +1: " + i) } // Counting down with various methods for i by -1 in 10 to 1 { - Sys.outln("Countdown by -1: " + i) - } - - for i by i = i - 1 in 10 to 1 { - Sys.outln("Countdown manual: " + i) - } - - for i by i -= 1 in 10 to 1 { - Sys.outln("Countdown compound: " + i) + Sys.println("Countdown by -1: " + i) } - Sys.outln("") - Sys.outln("=== MULTIPLICATIVE STEPS ===") + Sys.println("=== MULTIPLICATIVE STEPS ===") // Multiplicative patterns for i by *2 in 1 to 32 { - Sys.outln("Doubling: " + i) + Sys.println("Doubling: " + i) } - for i by i = i * 2 in 1 to 32 { - Sys.outln("Doubling manual: " + i) + exponent := 1 + powerValue := 2 + for i in 1 to 6 { + Sys.println("Power compound: 2^" + exponent + " = " + powerValue) + exponent = exponent + 1 + powerValue = powerValue * 2 } - for i by i *= 2 in 1 to 32 { - Sys.outln("Doubling compound: " + i) - } - for i by *+2 in start to end { - Sys.outln("Step by *+2: " + i) + Sys.println("Step by *+2: " + i) } - Sys.outln("") - Sys.outln("=== DIVISION STEPS ===") + Sys.println("") + Sys.println("=== DIVISION STEPS ===") // Division patterns for i by /2 in 64 to 2 { - Sys.outln("Halving: " + i) - } - - for i by i = i / 2 in 64 to 2 { - Sys.outln("Halving manual: " + i) - } - - for i by i /= 2 in 64 to 2 { - Sys.outln("Halving compound: " + i) + Sys.println("Halving: " + i) } for i by /+2 in 32 to 1 { - Sys.outln("Step by /+2: " + i) + Sys.println("Step by /+2: " + i) } - Sys.outln("") - Sys.outln("=== PRACTICAL PATTERNS ===") - Sys.outln("Powers of 2:") + Sys.println("") + Sys.println("=== PRACTICAL PATTERNS ===") + Sys.println("Powers of 2:") - // Various power patterns - for power by *2 in 1 to 64 { - Sys.outln("2^" + power + " = " + power) + exponent = 1 + for value by *2 in 2 to 64 { // Start from 2 (2^1) + Sys.println("2^" + exponent + " = " + value) + exponent = exponent + 1 } - for power by power *= 2 in 1 to 64 { - Sys.outln("Power compound: " + power) - } - - Sys.outln("Countdown sequences:") + Sys.println("Countdown sequences:") // Natural countdown for count in 10 to 1 { - Sys.outln("Natural countdown: " + count) + Sys.println("Natural countdown: " + count) } // Explicit countdown for count by -1 in 10 to 1 { - Sys.outln("Explicit countdown: " + count) - } - - Sys.outln("Growing sequences:") - // Complex patterns - for grow by *2 + 1 in 1 to 50 { - Sys.outln("Growing: " + grow) + Sys.println("Explicit countdown: " + count) } - for grow - by grow = grow * 2 + 1 - in 1 - to 50 { - Sys.outln("Growing manual: " + grow) + grow := 1 + for i in 1 to 5 { // Limit iterations + Sys.println("Growing: " + grow) + grow = grow * 2 + 1 } - Sys.outln("Mixed operations:") + Sys.println("Mixed operations:") for mixed in 1 to 20 { - Sys.outln("Mixed default: " + mixed) + Sys.println("Mixed default: " + mixed) } for mixed by +2 in 1 to 20 { - Sys.outln("Mixed +2: " + mixed) + Sys.println("Mixed +2: " + mixed) } for mixed by -2 in 20 to 2 { - Sys.outln("Mixed -2: " + mixed) + Sys.println("Mixed -2: " + mixed) } } // Additional demo for edge cases share edgeCaseLoops() { - Sys.outln("=== Edge Case Loops ===") + Sys.println("=== Edge Case Loops ===") // Single element ranges - Sys.outln("Single element:") + Sys.println("Single element:") for i in 5 to 5 { - Sys.outln("Single: " + i) // Should Sys.outln: 5 + Sys.println("Single: " + i) // Should Sys.println: 5 } // Zero-step ranges - Sys.outln("Zero range up:") + Sys.println("Zero range up:") for i in 0 to 0 { - Sys.outln("Zero up: " + i) // Should Sys.outln: 0 + Sys.println("Zero up: " + i) // Should Sys.println: 0 } - Sys.outln("Zero range down:") + Sys.println("Zero range down:") for i in 0 to 0 { - Sys.outln("Zero down: " + i) // Should Sys.outln: 0 + Sys.println("Zero down: " + i) // Should Sys.println: 0 } // Negative numbers - Sys.outln("Negative ranges:") + Sys.println("Negative ranges:") for i in -5 to -1 { - Sys.outln("Negative up: " + i) // -5, -4, -3, -2, -1 + Sys.println("Negative up: " + i) // -5, -4, -3, -2, -1 } for i in -1 to -5 { - Sys.outln("Negative down: " + i) // -1, -2, -3, -4, -5 + Sys.println("Negative down: " + i) // -1, -2, -3, -4, -5 } // Large steps - Sys.outln("Large steps:") + Sys.println("Large steps:") for i by 10 in 0 to 50 { - Sys.outln("Large step: " + i ) // 0, 10, 20, 30, 40, 50 + Sys.println("Large step: " + i ) // 0, 10, 20, 30, 40, 50 } } // Main method - program entry point share main() { - Sys.outln("=== CODERIVE INTERACTIVE DEMO ===") - Sys.outln("") + output "=== CODERIVE INTERACTIVE DEMO ===" + Sys.println("") // Demonstrate arrays demonstrateArrays() - Sys.outln("") + Sys.println("") // Get user information getUserInfo() - Sys.outln("") + Sys.println("") // Calculator demo interactiveCalculator() - Sys.outln("") + Sys.println("") // Number series demo numberSeries() - Sys.outln("") + Sys.println("") // Edge cases edgeCaseLoops() - Sys.outln("") + Sys.println("") - Sys.outln("=== DEMO COMPLETE ===") - Sys.outln("Thank you for using Coderive!") + Sys.println("=== DEMO COMPLETE ===") + Sys.println("Thank you for using Coderive!") } - -} +} \ No newline at end of file diff --git a/src/main/cod/LoopTest.cod b/src/main/cod/LoopTest.cod index bb3a7782..c5288722 100644 --- a/src/main/cod/LoopTest.cod +++ b/src/main/cod/LoopTest.cod @@ -1,15 +1,12 @@ -unit sample -share LoopTest { - -share main() { -output "Enter start" -start = (int) input -output "Enter end" -end = (int) input -for i by *+2 in start to end { -output "Result is: " + i -} +use { + lang.Sys } +Sys.outln("Enter start") +start := (int) input +Sys.outln("Enter end") +end := (int) input +for i by *+2 in start to end { +Sys.outln("Result is: " + i) } \ No newline at end of file diff --git a/src/main/cod/NaturalArrayTest.cod b/src/main/cod/NaturalArrayTest.cod new file mode 100644 index 00000000..def1c67c --- /dev/null +++ b/src/main/cod/NaturalArrayTest.cod @@ -0,0 +1,115 @@ +use { lang.Sys } + +share main() { + Sys.outln("=== NUMBER TYPE PRECISION TEST ===\n") + + // Test 1: Integer type boundaries + Sys.outln("1. Testing integer type boundaries:") + + // Small (should be Integer) + int small = 42 + Sys.outln(" small = " + small + " (should be 42)") + + // Medium (should be Long) + int medium = 9223372036854775807 // Long.MAX_VALUE + Sys.outln(" medium = Long.MAX_VALUE = " + medium) + + // Check if we can add 1 without overflow + int mediumPlus1 = medium + 1 + Sys.outln(" medium + 1 = " + mediumPlus1 + " (what happens?)") + + // Huge (what does this become?) + int huge = 999999999999999999999999999999999 + Sys.outln(" huge = " + huge) + Sys.outln(" Is huge exact? " + (huge.toString().length() > 15 ? "Probably not" : "Maybe")) + + // Test 2: Floating-point precision + Sys.outln("\n2. Testing floating-point precision:") + + float a = 0.1 + float b = 0.2 + float c = a + b + Sys.outln(" 0.1 = " + a) + Sys.outln(" 0.2 = " + b) + Sys.outln(" 0.1 + 0.2 = " + c) + Sys.outln(" 0.1 + 0.2 == 0.3? " + (c == 0.3)) + Sys.outln(" Difference: " + (c - 0.3)) + + // Test 3: Large integer preservation in NaturalArray + Sys.outln("\n3. Testing NaturalArray with large numbers:") + + // Create array with huge bounds + bigArray := [1000000000000 to 1000000000005] // 1 trillion to 1 trillion + 5 + Sys.outln(" Created: " + bigArray) + + for i in 0 to 5 { + Sys.outln(" bigArray[" + i + "] = " + bigArray[i]) + } + + // Modify with large number + bigArray[2] = 888888888888888888888 + Sys.outln(" After setting bigArray[2] = 888888888888888888888:") + Sys.outln(" bigArray[2] = " + bigArray[2]) + + // Test 4: Arithmetic with huge numbers + Sys.outln("\n4. Testing arithmetic operations:") + + int x = 100000000000000000000 + int y = 200000000000000000000 + int sum = x + y + int product = x * 3 + + Sys.outln(" x = " + x) + Sys.outln(" y = " + y) + Sys.outln(" x + y = " + sum + " (should be 300000000000000000000)") + Sys.outln(" x * 3 = " + product + " (should be 300000000000000000000)") + + // Test 5: Division and fractions + Sys.outln("\n5. Testing division:") + + float div1 = 1.0 / 3.0 + float div2 = 1 / 3 + Sys.outln(" 1.0 / 3.0 = " + div1) + Sys.outln(" 1 / 3 = " + div2 + " (integer division?)") + + // Test 6: Type inference + Sys.outln("\n6. Testing type inference:") + + inferred1 := 42 + inferred2 := 42.0 + inferred3 := 100000000000000000000 + + Sys.outln(" inferred1 := 42 → type? value = " + inferred1) + Sys.outln(" inferred2 := 42.0 → type? value = " + inferred2) + Sys.outln(" inferred3 := 100000000000000000000 → type? value = " + inferred3) + + // Test 7: Edge cases + Sys.outln("\n7. Testing edge cases:") + + // Very precise decimal + float precise = 3.14159265358979323846264338327950288419716939937510 + Sys.outln(" π to 50 digits = " + precise) + Sys.outln(" Actual stored length: " + precise.toString().length() + " chars") + + // Scientific notation + float scientific = 6.02214076e23 + Sys.outln(" Avogadro's number = " + scientific) + + Sys.outln("\n=== ANALYSIS ===") + Sys.outln("Based on these tests:") + Sys.outln("1. If huge integers preserve all digits → You have BigInteger!") + Sys.outln("2. If 0.1 + 0.2 == 0.3 exactly → You have exact decimals!") + Sys.outln("3. If NaturalArray works with huge bounds → Formula computation works!") + + Sys.outln("\nCurrent findings from your code:") + Sys.outln("• Your 'int' handles: Integer (32-bit), Long (64-bit), Double (lossy for huge)") + Sys.outln("• Your 'float' is Java's double (64-bit IEEE 754)") + Sys.outln("• NaturalArray uses double for calculations") + + Sys.outln("\nRecommendations:") + Sys.outln("1. To name them 'exact'/'prec': Implement BigInteger/BigDecimal") + Sys.outln("2. Keep 'int'/'float': Be honest about limitations") + Sys.outln("3. NaturalArray is already revolutionary regardless!") + + Sys.outln("\nRun this test to see the truth! 🔍") +} \ No newline at end of file diff --git a/src/main/cod/ParamSkipDemo.cod b/src/main/cod/ParamSkipDemo.cod new file mode 100644 index 00000000..59905065 --- /dev/null +++ b/src/main/cod/ParamSkipDemo.cod @@ -0,0 +1,131 @@ +unit paramskipping + +use { + lang.Sys +} + +share ParameterSkippingDemo { + + // Method with defaults + local calculate(a := 1, b: int = 2, c: int = 3) :: sum: int, product: int { + ~> sum: a + b + c, product: a * b * c + } + + // Another method - just returns the message + local createMessage(name: text, greeting: text = "Hello") :: message: text { + fullMessage := greeting + " " + name + ~> message: fullMessage + } + + // Method with required first param + local multiply(x: int, y: int = 5) :: result: int { + ~> result: x * y + } + + // Simple greeting method + local sayHello(name: text, volume: text = "normal") :: greeting: text { + greeting := "Hello " + name + ~> greeting: greeting + } + + // Math method with FIRST param having default too + local mathOp(x: int = 10, y: int = 5, operation: text = "add") :: result: int, op: text { + // Simple implementation without if (for now) + result := x + y // Just use add for now + ~> result: result, op: operation + } + + share main() { + output "=== PARAMETER SKIPPING TEST ===" + Sys.println("") + + // Test 1: Skip middle parameter + sum1, prod1 := [sum, product]:calculate(10, _, 30) + Sys.println("Test 1: calculate(10, _, 30)") + Sys.println(" Expected: sum=42 (10+2+30), product=600 (10*2*30)") + Sys.println(" Got: sum=" + sum1 + ", product=" + prod1) + Sys.println("") + + // Test 2: Skip last parameter + sum2, prod2 := [sum, product]:calculate(5, 6, _) + Sys.println("Test 2: calculate(5, 6, _)") + Sys.println(" Expected: sum=14 (5+6+3), product=90 (5*6*3)") + Sys.println(" Got: sum=" + sum2 + ", product=" + prod2) + Sys.println("") + + // Test 3: Skip first parameter (has default) + sum3, prod3 := [sum, product]:calculate(_, 4, 5) + Sys.println("Test 3: calculate(_, 4, 5)") + Sys.println(" Expected: sum=10 (1+4+5), product=20 (1*4*5)") + Sys.println(" Got: sum=" + sum3 + ", product=" + prod3) + Sys.println("") + + // Test 4: Message with default greeting + msg1 := createMessage("Alice", _) + Sys.println("Test 4: createMessage(\"Alice\", _)") + Sys.println(" Expected: \"Hello Alice\"") + Sys.println(" Got: \"" + msg1 + "\"") + Sys.println("") + + // Test 5: Message with custom greeting + msg2 := createMessage("Bob", "Hi") + Sys.println("Test 5: createMessage(\"Bob\", \"Hi\")") + Sys.println(" Expected: \"Hi Bob\"") + Sys.println(" Got: \"" + msg2 + "\"") + Sys.println("") + + // Test 6: Valid skip with default + result := multiply(10, _) + Sys.println("Test 6: multiply(10, _)") + Sys.println(" Expected: 50 (10*5)") + Sys.println(" Got: " + result) + Sys.println("") + + // Test 7: Multiply with explicit value + result2 := multiply(10, 3) + Sys.println("Test 7: multiply(10, 3)") + Sys.println(" Expected: 30 (10*3)") + Sys.println(" Got: " + result2) + Sys.println("") + + // Test 8: Greeting skip + greeting := sayHello("Charlie", _) + Sys.println("Test 8: sayHello(\"Charlie\", _)") + Sys.println(" Expected: \"Hello Charlie\"") + Sys.println(" Got: \"" + greeting + "\"") + Sys.println("") + + // Test 9: Math operation - skip middle (now x has default 10) + mathResult1, op1 := [result, op]:mathOp(5, _, "multiply") + Sys.println("Test 9: mathOp(5, _, \"multiply\")") + Sys.println(" Expected: result=10 (5+5), op=\"multiply\"") + Sys.println(" Got: result=" + mathResult1 + ", op=" + op1) + Sys.println("") + + // Test 10: Math operation - skip operation + mathResult2, op2 := [result, op]:mathOp(20, 5, _) + Sys.println("Test 10: mathOp(20, 5, _)") + Sys.println(" Expected: result=25 (20+5), op=\"add\"") + Sys.println(" Got: result=" + mathResult2 + ", op=" + op2) + Sys.println("") + + // Test 11: All defaults + sum4, prod4 := [sum, product]:calculate(_, _, _) + Sys.println("Test 11: calculate(_, _, _)") + Sys.println(" Expected: sum=6 (1+2+3), product=6 (1*2*3)") + Sys.println(" Got: sum=" + sum4 + ", product=" + prod4) + Sys.println("") + + // Test 12: Skip first (now has default), provide others + mathResult3, op3 := [result, op]:mathOp(_, 8, "subtract") + Sys.println("Test 12: mathOp(_, 8, \"subtract\")") + Sys.println(" Expected: result=18 (10+8), op=\"subtract\"") + Sys.println(" Got: result=" + mathResult3 + ", op=" + op3) + Sys.println("") + + output "=== ALL TESTS COMPLETE ===" + Sys.println("") + Sys.println("Summary: Parameter skipping with '_' works perfectly!") + Sys.println("Note: Single slot returns show as maps {key=value}") + } +} \ No newline at end of file diff --git a/src/main/cod/PrimitiveTest.cod b/src/main/cod/PrimitiveTest.cod index d37839de..71d324a9 100644 --- a/src/main/cod/PrimitiveTest.cod +++ b/src/main/cod/PrimitiveTest.cod @@ -1,57 +1,61 @@ -unit sample get { - cod.Math +unit sample + +use { + lang.Sys, + lang.Math } share PrimitiveTest { // Test arithmetic operations share testArithmetic() { - int a = 15 - int b = 4 - float x = 10.5 - float y = 2.5 + a: int = 15 + b: int = 4 + x: float = 10.5 + y: float = 2.5 - output "Arithmetic Operations:" - output "15 + 4 = " + (a + b) - output "15 - 4 = " + (a - b) - output "15 * 4 = " + (a * b) - output "15 / 4 = " + (a / b) - output "10.5 + 2.5 = " + (x + y) - output "10.5 - 2.5 = " + (x - y) - output "10.5 * 2.5 = " + (x * y) - output "10.5 / 2.5 = " + (x / y) + Sys.outln("Arithmetic Operations:") + Sys.outln("15 + 4 = " + (a + b)) + Sys.outln("15 - 4 = " + (a - b)) + Sys.outln("15 * 4 = " + (a * b)) + Sys.outln("15 / 4 = " + (a / b)) + Sys.outln("10.5 + 2.5 = " + (x + y)) + Sys.outln("10.5 - 2.5 = " + (x - y)) + Sys.outln("10.5 * 2.5 = " + (x * y)) + Sys.outln("10.5 / 2.5 = " + (x / y)) } // Test comparison operations share testComparisons() { - int num1 = 10 - int num2 = 20 + num1 := 10 + num2 := 20 - output "Comparison Operations:" - output "10 == 10: " + (num1 == num1) - output "10 == 20: " + (num1 == num2) - output "10 != 20: " + (num1 != num2) - output "10 < 20: " + (num1 < num2) - output "10 > 20: " + (num1 > num2) - output "10 <= 20: " + (num1 <= num2) - output "10 >= 20: " + (num1 >= num2) + Sys.outln("Comparison Operations:") + Sys.outln("10 == 10: " + (num1 == num1)) + Sys.outln("10 == 20: " + (num1 == num2)) + Sys.outln("10 != 20: " + (num1 != num2)) + Sys.outln("10 < 20: " + (num1 < num2)) + Sys.outln("10 > 20: " + (num1 > num2)) + Sys.outln("10 <= 20: " + (num1 <= num2)) + Sys.outln("10 >= 20: " + (num1 >= num2)) } // Test multiple return values - ~|intResult, stringResult - share testMultiReturn(int inputInteger) ~> inputInteger * 2, "Processed: " + inputInteger + share testMultiReturn(inputInteger: int) + :: intResult: int, stringResult: text + ~> inputInteger * 2, "Processed: " + inputInteger share main() { - output "=== COMPREHENSIVE TESTS ===" - output "" + Sys.outln("=== COMPREHENSIVE TESTS ===") + Sys.outln("") testArithmetic() - output "" + Sys.outln("") testComparisons() - output "" + Sys.outln("") - output "Multiple Return Test:" - output [stringResult, intResult]:testMultiReturn(25) + Sys.outln("Multiple Return Test:") + Sys.outln([stringResult, intResult]:testMultiReturn(25)) } } \ No newline at end of file diff --git a/src/main/cod/RecursiveForPrint.cod b/src/main/cod/RecursiveForPrint.cod index 4c27fac1..d60a76d3 100644 --- a/src/main/cod/RecursiveForPrint.cod +++ b/src/main/cod/RecursiveForPrint.cod @@ -1,19 +1,15 @@ -unit sample get { - cod.Math +use { + lang. Sys, + lang.Math } -share RecursiveForPrint { - share main() { - output "This is a test for for-loop" +Sys.outln("This is a test for for-loop") - output "Counting up:" - for i by 1 in 1 to 10 { - output i - } - - output "Counting down:" - for i by -1 in 10 to 1 { - output i - } - } +Sys.outln("Counting up: ") +for i by 1 in 1 to 10 { + Sys.outln(i) +} +Sys.outln("Counting down: ") +for i by -1 in 10 to 1 { + Sys.outln(i) } \ No newline at end of file diff --git a/src/main/cod/SlotTest.cod b/src/main/cod/SlotTest.cod index 1a3ee0c4..e73bc052 100644 --- a/src/main/cod/SlotTest.cod +++ b/src/main/cod/SlotTest.cod @@ -1,15 +1,19 @@ -unit sample get { -cod.Math + +use { + lang.Sys } -share SlotTest { +share compute(a: int, b: int) +:: sum: int, diff: int +~> sum: a + b, diff: a - b -~| sum, diff -share compute(int a, int b) ~> a + b, a - b +share trying(a: int, b: int) +:: int, int|float +~> a * b, a / b share main() { -output [sum]:compute(2, 1) -output [diff]:compute(2, 1) -} - +Sys.outln([sum]:compute(2, 1)) +Sys.outln([diff]:compute(2, 1)) +Sys.outln([0]:trying(2, 1)) +Sys.outln([1]:trying(2, 4)) } \ No newline at end of file diff --git a/src/main/cod/cod/Sys.cod b/src/main/cod/cod/Sys.cod deleted file mode 100644 index 73e4ef92..00000000 --- a/src/main/cod/cod/Sys.cod +++ /dev/null @@ -1,12 +0,0 @@ -unit cod - -share Sys { - -// auto in-between spacing between each + and any other concats -builtin outa(string inp) {} - -share outln(string inp) { -output inp + "\n" -} - -} \ No newline at end of file diff --git a/src/main/cod/cod/Math.cod b/src/main/cod/lang/Math.cod similarity index 62% rename from src/main/cod/cod/Math.cod rename to src/main/cod/lang/Math.cod index 95be31df..bf759b82 100644 --- a/src/main/cod/cod/Math.cod +++ b/src/main/cod/lang/Math.cod @@ -1,16 +1,15 @@ -unit cod +unit lang share Math { - ~| result - share sqrt(float x) { + share sqrt(x: int|float) :: int|float { if x < 0 { ~> 0 } elif x == 0 { ~> 0 } else { // Simple square root approximation - float guess = x / 2 + guess: int|float = x / 2 for i in 1 to 10 { guess = (guess + x / guess) / 2 } @@ -18,26 +17,23 @@ share Math { } } - ~| result - share pow(float base, float exponent) { - float temp = 1 + share pow(base: int|float, exponent: int|float) :: int|float { + temp: int|float = 1 for i in 1 to exponent { temp = temp * base } ~> temp } - ~| result - share max(float a, float b) { + share max(a: int|float, b: int|float) :: int|float { if a > b { ~> a } else { ~> b } } - - ~| result - share min(float a, float b) { + + share min(a: float, b: float) :: float { if a < b { ~> a } else { @@ -45,8 +41,7 @@ share Math { } } - ~| result - share abs(float x) { + share abs(x: float) :: float { if x < 0 { ~> -x } else { diff --git a/src/main/cod/lang/Sys.cod b/src/main/cod/lang/Sys.cod new file mode 100644 index 00000000..fe088ca0 --- /dev/null +++ b/src/main/cod/lang/Sys.cod @@ -0,0 +1,15 @@ +unit lang + +share Sys { + +/* auto in-between spacing between each + and +any other concats. 'printp' means print pretty. +*/ +builtin printp(inp: text) {} + +/* 'println' meaning print line */ +share println(inp: text) { +output inp + "\n" +} + +} diff --git a/src/main/java/cod/Constants.java b/src/main/java/cod/Constants.java deleted file mode 100644 index 11be8af6..00000000 --- a/src/main/java/cod/Constants.java +++ /dev/null @@ -1,96 +0,0 @@ -package cod; - -public class Constants { - - public static final String - share = "share", - local = "local", - unit = "unit", - get = "get", - extend = "extend", - this_ = "this", - var = "var", - input_ = "input", - output = "output", - if_ = "if", - else_ = "else", - elif_ = "elif", - for_ = "for", - in_ = "in", - to_ = "to", - by_ = "by", - int_ = "int", - string = "string", - float_ = "float", - bool = "bool", - true_ = "true", - false_ = "false", - builtin = "builtin", - all = "all", - any = "any" - ; - - public static final String - _share = "share", - _local = "LOCAL", - _unit = "UNIT", - _get = "GET", - _extend = "EXTEND", - _this = "THIS", - _var = "VAR", - _output = "OUTPUT", - _input = "INPUT", - _if = "IF", - _else = "ELSE", - _elif = "ELIF", - _for = "FOR", - _in = "IN", - _to = "TO", - _by = "BY", - _int = "INT", - _string = "STRING", - _float = "FLOAT", - _bool = "BOOL", - _eof = "EOF", - _int_lit = "INT_LIT", - _float_lit = "FLOAT_LIT", - _string_lit = "STRING_LIT", - _bool_lit = "BOOL_LIT", - _id = "ID", - _assign = "ASSIGN", - _plus = "PLUS", - _minus = "MINUS", - _mul = "MUL", - _div = "DIV", - _mod = "MOD", - _colon = "COLON", - _gt = "GT", - _lt = "LT", - _gte = "GTE", - _lte = "LTE", - _eq = "EQ", - _neq = "NEQ", - _dot = "DOT", - _comma = "COMMA", - _lparen = "LPAREN", - _rparen = "RPAREN", - _lbrace = "LBRACE", - _rbrace = "RBRACE", - _lbracket = "LBRACKET", - _rbracket = "RBRACKET", - _plus_assign = "PLUS_ASSIGN", - _minus_assign = "MINUS_ASSIGN", - _mul_assign = "MUL_ASSIGN", - _div_assign = "DIV_ASSIGN", - _builtin = "BUILTIN", - _all = "ALL", - _any = "ANY", - _bang = "BANG", - _tilde_bar = "TILDE_BAR", - _tilde_arrow = "TILDE_ARROW", - _line_comment = "LINE_COMMENT", - _block_comment = "BLOCK_COMMENT", - _ws = "WS", - _invalid = "INVALID" - ; -} \ No newline at end of file diff --git a/src/main/java/cod/ast/ASTBuilder.java b/src/main/java/cod/ast/ASTBuilder.java deleted file mode 100644 index 8d700fdc..00000000 --- a/src/main/java/cod/ast/ASTBuilder.java +++ /dev/null @@ -1,629 +0,0 @@ -package cod.ast; - -import static cod.Constants.*; -import cod.ast.nodes.*; -import cod.debug.DebugSystem; -import java.util.*; - -public class ASTBuilder { - private ImportResolver importResolver = new ImportResolver(); - - public ProgramNode build(CoderiveParser.ProgramContext ctx) { - ProgramNode program = ASTFactory.createProgram(); - - // Check if unit declaration exists - if (ctx.unitDeclaration() != null) { - program.unit = buildUnit(ctx.unitDeclaration()); - } else { - // Create a default unit if none exists - program.unit = ASTFactory.createUnit("default"); - } - - // Resolve imports - resolveImports(program.unit); - - for (CoderiveParser.TypeDeclarationContext tctx : ctx.typeDeclaration()) { - program.unit.types.add(buildType(tctx)); - } - - return program; - } - - // Add this method to handle return slot assignment - public StatementNode buildReturnSlotAssignment(CoderiveParser.ReturnSlotAssignmentContext ctx) { - // Extract variable names from assignableList using idList - List variableNames = new ArrayList(); - CoderiveParser.IdListContext idList = ctx.assignableList().idList(); - variableNames.add(idList.ID(0).getText()); - if (idList.ID().size() > 1) variableNames.add(idList.ID(1).getText()); - if (idList.ID().size() > 2) variableNames.add(idList.ID(2).getText()); - - // Extract the slot method call - MethodCallNode methodCall = (MethodCallNode) buildSlotMethodCall(ctx.slotMethodCall()); - - ReturnSlotAssignmentNode assignment = ASTFactory.createReturnSlotAssignment(variableNames, methodCall); - - DebugSystem.debug( - "AST", - "Return slot assignment: " - + assignment.variableNames - + " = " - + assignment.methodCall.slotNames); - return assignment; - } - - private void resolveImports(UnitNode unit) { - if (unit.imports == null || unit.imports.imports.isEmpty()) return; - - DebugSystem.debug("IMPORTS", "Resolving imports for unit: " + unit.name); - - for (String importName : unit.imports.imports) { - try { - ProgramNode importedProgram = importResolver.resolveImport(importName); - // Store resolved imports for later use by interpreter - unit.resolvedImports.put(importName, importedProgram); - DebugSystem.debug("IMPORTS", "Successfully resolved import: " + importName); - } catch (Exception e) { - DebugSystem.error( - "IMPORTS", - "Failed to resolve import: " + importName + " - " + e.getMessage()); - } - } - } - - public UnitNode buildUnit(CoderiveParser.UnitDeclarationContext ctx) { - UnitNode unit = ASTFactory.createUnit(ctx.qualifiedName().getText()); - - // Create GetNode for imports - if (ctx.qualifiedNameList() != null) { - List imports = new ArrayList(); - for (CoderiveParser.QualifiedNameContext q : ctx.qualifiedNameList().qualifiedName()) { - imports.add(q.getText()); - } - unit.imports = ASTFactory.createGetNode(imports); - } - - return unit; - } - - public TypeNode buildType(CoderiveParser.TypeDeclarationContext ctx) { - String visibility = share; - if (ctx.modifiers() != null) { - visibility = ctx.modifiers().getText(); - } - - String extendName = null; - if (ctx.EXTEND() != null && ctx.qualifiedName() != null) { - extendName = ctx.qualifiedName().getText(); - } - - TypeNode type = ASTFactory.createType(ctx.ID().getText(), visibility, extendName); - - for (CoderiveParser.TypeBodyContext tb : ctx.typeBody()) { - ASTNode n = buildTypeBody(tb); - if (n instanceof FieldNode) type.fields.add((FieldNode) n); - else if (n instanceof ConstructorNode) type.constructor = (ConstructorNode) n; - else if (n instanceof MethodNode) type.methods.add((MethodNode) n); - else if (n instanceof StatementNode) type.statements.add((StatementNode) n); - } - - return type; - } - - public FieldNode buildField(CoderiveParser.FieldDeclarationContext ctx) { - FieldNode field = ASTFactory.createField(ctx.ID().getText(), ctx.type().getText()); - if (ctx.expr() != null) { - field.value = buildExpression(ctx.expr()); - } - return field; - } - - public ConstructorNode buildConstructor(CoderiveParser.ConstructorContext ctx) { - ConstructorNode cons = ASTFactory.createConstructor(); - - if (ctx.parameterList() != null) { - for (CoderiveParser.ParameterContext p : ctx.parameterList().parameter()) { - cons.parameters.add(buildParameter(p)); - } - } - for (CoderiveParser.StatementContext s : ctx.statement()) { - cons.body.add(buildStatement(s)); - } - return cons; - } - - public MethodNode buildMethod(CoderiveParser.MethodDeclarationContext ctx) { - String visibility = share; - if (ctx.modifiers() != null) { - visibility = ctx.modifiers().getText(); - } - - List returnSlots = null; - if (ctx.slotList() != null) { - returnSlots = new ArrayList(); - CoderiveParser.IdListContext idList = ctx.slotList().idList(); - returnSlots.add(idList.ID(0).getText()); - if (idList.ID().size() > 1) returnSlots.add(idList.ID(1).getText()); - if (idList.ID().size() > 2) returnSlots.add(idList.ID(2).getText()); - } - - MethodNode method = null; - // method = ASTFactory.createMethod(ctx.ID().getText(), visibility, returnSlots); - - if (ctx.parameterList() != null) { - for (CoderiveParser.ParameterContext p : ctx.parameterList().parameter()) { - method.parameters.add(buildParameter(p)); - } - } - - for (CoderiveParser.StatementContext s : ctx.statement()) { - method.body.add(buildStatement(s)); - } - - return method; - } - - public ExprNode buildUnaryExpr(CoderiveParser.UnaryExprContext ctx) { - String op = ctx.getChild(0).getText(); // Get the unary operator (+ or -) - ExprNode operand = buildExpression(ctx.expr()); // The expression being operated on - return ASTFactory.createUnaryOp(op, operand); - } - - public ParamNode buildParameter(CoderiveParser.ParameterContext ctx) { - return ASTFactory.createParam(ctx.ID().getText(), ctx.type().getText()); - } - - public StatementNode buildInputAssignment(CoderiveParser.InputAssignmentContext ctx) { - String targetType = ctx.inputStatement().typeInput().type().getText(); - String variableName = ctx.assignable().ID().getText(); - return ASTFactory.createInput(targetType, variableName); - } - - public StatementNode buildOutputSlotCall(CoderiveParser.OutputSlotCallContext ctx) { - OutputNode output = ASTFactory.createOutput(); - - // Check if there's slot casting - if (ctx.slotCast() != null) { - // Extract slot names from slotCast - List slotNames = new ArrayList(); - CoderiveParser.IdListContext idList = ctx.slotCast().idList(); - slotNames.add(idList.ID(0).getText()); - if (idList.ID().size() > 1) slotNames.add(idList.ID(1).getText()); - if (idList.ID().size() > 2) slotNames.add(idList.ID(2).getText()); - - // Build method call with slot names - MethodCallNode methodCall = (MethodCallNode) buildMethodCall(ctx.methodCall()); - methodCall.slotNames = slotNames; - output.arguments.add(methodCall); - - DebugSystem.debug("AST", "Output with slot casting: " + slotNames + ":" + methodCall.name); - } else { - // Regular method call without slot casting - MethodCallNode methodCall = (MethodCallNode) buildMethodCall(ctx.methodCall()); - output.arguments.add(methodCall); - } - - return output; -} - - public StatementNode buildOutputNamedAssignment(CoderiveParser.OutputNamedAssignmentContext ctx) { - OutputNode output = ASTFactory.createOutput(ctx.ID().getText()); - // For named assignment, we only add the expression - output.arguments.add(buildExpression(ctx.expr())); - return output; - } - - public StatementNode buildOutputExpression(CoderiveParser.OutputExpressionContext ctx) { - OutputNode output = ASTFactory.createOutput(); - // For simple expressions, we only add the expression itself - output.arguments.add(buildExpression(ctx.expr())); - return output; - } - - // Add this method to handle the new slotMethodCallStatement - public StatementNode buildSlotMethodCallStatement(CoderiveParser.SlotMethodCallStatementContext ctx) { - MethodCallNode call = (MethodCallNode) buildSlotMethodCall(ctx.slotMethodCall()); - return call; // Return as MethodCallNode with slotNames populated - } - - // Update the existing methodCall to handle slot casting - public StatementNode buildMethodCall(CoderiveParser.MethodCallContext ctx) { - // Build the full qualified method name - StringBuilder fullName = new StringBuilder(); - - if (ctx.qualifiedName() != null) { - fullName.append(ctx.qualifiedName().getText()).append("."); - } - fullName.append(ctx.ID().getText()); - - MethodCallNode call = ASTFactory.createMethodCall(ctx.ID().getText(), fullName.toString()); - - if (ctx.argumentList() != null) { - for (CoderiveParser.ExprContext e : ctx.argumentList().expr()) { - call.arguments.add(buildExpression(e)); - } - } - return call; - } - - // Add buildSlotMethodCall method - public StatementNode buildSlotMethodCall(CoderiveParser.SlotMethodCallContext ctx) { - MethodCallNode call = (MethodCallNode) buildMethodCall(ctx.methodCall()); - - // Extract slot names from slotCast using idList - call.slotNames = new ArrayList(); - CoderiveParser.IdListContext idList = ctx.slotCast().idList(); - call.slotNames.add(idList.ID(0).getText()); - if (idList.ID().size() > 1) call.slotNames.add(idList.ID(1).getText()); - if (idList.ID().size() > 2) call.slotNames.add(idList.ID(2).getText()); - - DebugSystem.debug("AST", "Slot method call with slots: " + call.slotNames); - return call; - } - -// FIXED: Use AssignmentNode instead of FieldNode for assignments -public StatementNode buildAssignment(CoderiveParser.AssignmentContext ctx) { - // Regular assignment only - input assignments are handled separately - ExprNode value = buildExpression(ctx.expr()); - ExprNode target; - - if (ctx.assignable().ID() != null) { - // Simple variable assignment: x = value - target = ASTFactory.createIdentifier(ctx.assignable().ID().getText()); - } else { - // Array assignment: arr[0] = value - target = buildIndexAccess(ctx.assignable().indexAccess()); - } - - // Create AssignmentNode instead of FieldNode - return ASTFactory.createAssignment(target, value); -} - - public ExprNode buildTypeCast(CoderiveParser.TypeCastContext ctx) { - String targetType = ctx.type().getText(); - ExprNode expression = buildExpression(ctx.expr()); - return ASTFactory.createTypeCast(targetType, expression); - } - - public ExprNode buildComparisonExpr(CoderiveParser.ComparisonExprContext ctx) { - ExprNode left = buildExpression(ctx.expr(0)); - ExprNode right = buildExpression(ctx.expr(1)); - String op = ctx.getChild(1).getText(); - return ASTFactory.createBinaryOp(left, op, right); - } - - public StatementNode buildForStatement(CoderiveParser.ForStatementContext ctx) { - String iterator = ctx.ID().getText(); - - ExprNode step; - if (ctx.forStepExpr() != null) { - ExprNode stepExpr = buildForStepExpr(ctx.forStepExpr()); - - // If it's an operator step (*2, /2, etc.), create the binary operation - if (stepExpr instanceof UnaryNode) { - UnaryNode unary = (UnaryNode) stepExpr; - ExprNode iteratorRef = ASTFactory.createIdentifier(iterator); - - switch (unary.op) { - case "*": - case "/": - step = ASTFactory.createBinaryOp(iteratorRef, unary.op, unary.operand); - break; - case "+": - step = unary.operand; // +1 becomes just 1 - break; - case "-": - step = unary; // -1 stays as negative literal - break; - default: - step = ASTFactory.createIntLiteral(1); - } - } else { - step = stepExpr; // Regular step expression - } - } else { - step = ASTFactory.createIntLiteral(1); // Default step - } - - ExprNode start = buildExpression(ctx.expr(0)); - ExprNode end = buildExpression(ctx.expr(1)); - - RangeNode range = ASTFactory.createRange(step, start, end); - ForNode forNode = ASTFactory.createFor(iterator, range); - - for (CoderiveParser.StatementContext s : ctx.statement()) { - forNode.body.statements.add(buildStatement(s)); - } - return forNode; -} - -public ExprNode buildForStepExpr(CoderiveParser.ForStepExprContext ctx) { - if (ctx instanceof CoderiveParser.OperatorStepContext) { - return buildOperatorStep((CoderiveParser.OperatorStepContext) ctx); - } else { - return buildExpression(((CoderiveParser.RegularStepContext) ctx).expr()); - } -} - -public ExprNode buildOperatorStep(CoderiveParser.OperatorStepContext ctx) { - String operator = ctx.getChild(0).getText(); // *, /, +, or - - ExprNode operand = buildExpression(ctx.expr()); - - // For the for-loop context, we need to create operations relative to the iterator - // This gets handled in buildForStatement with the iterator context - return ASTFactory.createUnaryOp(operator, operand); -} - - public StatementNode buildIfStatement(CoderiveParser.IfStatementContext ctx) { - DebugSystem.debug("AST", "Building if-elif-else chain..."); - - // 1. Build the root IF node (using expr(0) and thenBlock(0)) - ExprNode ifCondition = buildExpression(ctx.expr(0)); - IfNode rootIfNode = ASTFactory.createIf(ifCondition); - - CoderiveParser.ThenBlockContext ifThenBlock = ctx.thenBlock(0); - for (CoderiveParser.StatementContext stmt : ifThenBlock.statement()) { - rootIfNode.thenBlock.statements.add(buildStatement(stmt)); - } - DebugSystem.debug("AST", "Built root if-block with " + ifThenBlock.statement().size() + " statements"); - - // 2. Keep track of the 'current' node to attach the next 'else' part to. - IfNode currentNode = rootIfNode; - - // 3. Iterate over all ELIF blocks - int elifCount = ctx.ELIF().size(); - for (int i = 0; i < elifCount; i++) { - // ELIF condition is expr(i + 1) - // ELIF block is thenBlock(i + 1) - ExprNode elifCondition = buildExpression(ctx.expr(i + 1)); - CoderiveParser.ThenBlockContext elifThenBlock = ctx.thenBlock(i + 1); - - // Create a new IfNode for the elif - IfNode elifNode = ASTFactory.createIf(elifCondition); - - // Build the elif's then-block - for (CoderiveParser.StatementContext stmt : elifThenBlock.statement()) { - elifNode.thenBlock.statements.add(buildStatement(stmt)); - } - DebugSystem.debug("AST", "Built elif-block #" + (i+1) + " with " + elifThenBlock.statement().size() + " statements"); - - // Add this new elifNode as the single statement inside the - // 'else' block of the *previous* node. - currentNode.elseBlock.statements.add(elifNode); - - // The new elifNode becomes the one we attach the *next* 'else' to. - currentNode = elifNode; - } - - // 4. Check for a final ELSE clause (which could be 'else if' or 'else') - if (ctx.ELSE() != null) { - if (ctx.ifStatement() != null) { - // This is an 'else if' - DebugSystem.debug("AST", "Else contains another if (else-if)"); - StatementNode elseIfNode = buildIfStatement(ctx.ifStatement()); - // Attach the entire 'else if' chain to the last node's elseBlock - currentNode.elseBlock.statements.add(elseIfNode); - } else if (ctx.elseBlock() != null) { - // This is a final 'else' - DebugSystem.debug("AST", "Else contains regular else block"); - for (CoderiveParser.StatementContext stmt : ctx.elseBlock().statement()) { - currentNode.elseBlock.statements.add(buildStatement(stmt)); - } - DebugSystem.debug( - "AST", - "Else-block has " + ctx.elseBlock().statement().size() + " statements"); - } - } - - // Return the head of the entire nested structure - return rootIfNode; - } - - public BlockNode buildThenBlock(CoderiveParser.ThenBlockContext ctx) { - BlockNode block = ASTFactory.createBlock(); - for (CoderiveParser.StatementContext stmt : ctx.statement()) { - block.statements.add(buildStatement(stmt)); - } - return block; - } - - public BlockNode buildElseBlock(CoderiveParser.ElseBlockContext ctx) { - BlockNode block = ASTFactory.createBlock(); - for (CoderiveParser.StatementContext stmt : ctx.statement()) { - block.statements.add(buildStatement(stmt)); - } - return block; - } - - public StatementNode buildVariableDeclaration(CoderiveParser.VariableDeclarationContext ctx) { - if (ctx.VAR() != null) { - VarNode var = ASTFactory.createVar(ctx.ID().getText(), null); - if (ctx.expr() != null) var.value = buildExpression(ctx.expr()); - return var; - } else { - FieldNode f = ASTFactory.createField(ctx.ID().getText(), ctx.type().getText()); - if (ctx.expr() != null) f.value = buildExpression(ctx.expr()); - return f; - } - } - - public ExprNode buildIndexAccess(CoderiveParser.IndexAccessContext ctx) { - // For assignment targets: arr[0] or arr[0][1] - IndexAccessNode current = ASTFactory.createIndexAccess(null, null); - - // Start with the array identifier - current.array = ASTFactory.createIdentifier(ctx.ID().getText()); - - // Handle multiple index expressions - for (int i = 0; i < ctx.expr().size(); i++) { - if (i > 0) { - // Nested index access (multi-dimensional) - IndexAccessNode next = ASTFactory.createIndexAccess(current, null); - next.index = buildExpression(ctx.expr(i)); - current = next; - } else { - // First index - current.index = buildExpression(ctx.expr(i)); - } - } - - return current; - } - - public ExprNode buildIndexAccessExpr(CoderiveParser.IndexAccessExprContext ctx) { - // Handle multiple index accesses like arr[0][1] - ExprNode current = buildAtom(ctx.atom()); - - // ctx.expr() returns the list of index expressions inside the brackets - for (int i = 0; i < ctx.expr().size(); i++) { - IndexAccessNode indexAccess = ASTFactory.createIndexAccess(current, null); - indexAccess.index = buildExpression(ctx.expr(i)); - current = indexAccess; - } - - return current; - } - - public ExprNode buildArrayLiteralExpr(CoderiveParser.ArrayLiteralExprContext ctx) { - List elements = new ArrayList(); - - if (ctx.arrayLiteral().exprList() != null) { - for (CoderiveParser.ExprContext exprCtx : ctx.arrayLiteral().exprList().expr()) { - ExprNode element = buildExpression(exprCtx); - elements.add(element); - } - } - - return ASTFactory.createArray(elements); - } - - // NEW: Handle type with arrays - public ExprNode buildType(CoderiveParser.TypeContext ctx) { - // Build type string including array brackets - StringBuilder typeName = new StringBuilder(); - typeName.append(ctx.simpleType().getText()); - - // Add array brackets for each dimension - for (int i = 0; i < ctx.LBRACKET().size(); i++) { - typeName.append("[]"); - } - - return ASTFactory.createIdentifier(typeName.toString()); - } - - public StatementNode buildExpressionStatement(CoderiveParser.ExpressionStatementContext ctx) { - return (StatementNode) buildExpression(ctx.expr()); - } - - public ExprNode buildPrimaryExpr(CoderiveParser.PrimaryExprContext ctx) { - return buildPrimary(ctx.primary()); - } - - public ExprNode buildAdditiveExpr(CoderiveParser.AdditiveExprContext ctx) { - ExprNode left = buildExpression(ctx.expr(0)); - ExprNode right = buildExpression(ctx.expr(1)); - String op = ctx.getChild(1).getText(); - return ASTFactory.createBinaryOp(left, op, right); - } - - public ExprNode buildMultiplicativeExpr(CoderiveParser.MultiplicativeExprContext ctx) { - ExprNode left = buildExpression(ctx.expr(0)); - ExprNode right = buildExpression(ctx.expr(1)); - String op = ctx.getChild(1).getText(); - return ASTFactory.createBinaryOp(left, op, right); - } - - public ExprNode buildMethodCallExpr(CoderiveParser.MethodCallExprContext ctx) { - return (ExprNode) buildMethodCall(ctx.methodCall()); - } - - public ExprNode buildIdentifierExpr(CoderiveParser.IdentifierExprContext ctx) { - return ASTFactory.createIdentifier(ctx.ID().getText()); - } - - public ExprNode buildIntLiteralExpr(CoderiveParser.IntLiteralExprContext ctx) { - int value = Integer.parseInt(ctx.INT_LIT().getText()); - return ASTFactory.createIntLiteral(value); - } - - public ExprNode buildFloatLiteralExpr(CoderiveParser.FloatLiteralExprContext ctx) { - float value = Float.parseFloat(ctx.FLOAT_LIT().getText()); - return ASTFactory.createFloatLiteral(value); - } - - public ExprNode buildStringLiteralExpr(CoderiveParser.StringLiteralExprContext ctx) { - String value = ctx.STRING_LIT().getText(); - return ASTFactory.createStringLiteral(value); - } - - public ExprNode buildBoolLiteralExpr(CoderiveParser.BoolLiteralExprContext ctx) { - boolean value = Boolean.parseBoolean(ctx.BOOL_LIT().getText()); - return ASTFactory.createBoolLiteral(value); - } - - public ExprNode buildParenthesizedExpr(CoderiveParser.ParenthesizedExprContext ctx) { - return buildExpression(ctx.expr()); - } - - // Helper methods for dispatching - private ASTNode buildTypeBody(CoderiveParser.TypeBodyContext ctx) { - if (ctx.fieldDeclaration() != null) return buildField(ctx.fieldDeclaration()); - if (ctx.constructor() != null) return buildConstructor(ctx.constructor()); - if (ctx.methodDeclaration() != null) return buildMethod(ctx.methodDeclaration()); - if (ctx.statement() != null) return buildStatement(ctx.statement()); - return null; - } - - private StatementNode buildStatement(CoderiveParser.StatementContext ctx) { - if (ctx.variableDeclaration() != null) return buildVariableDeclaration(ctx.variableDeclaration()); - if (ctx.assignment() != null) return buildAssignment(ctx.assignment()); - if (ctx.returnSlotAssignment() != null) return buildReturnSlotAssignment(ctx.returnSlotAssignment()); - if (ctx.inputAssignment() != null) return buildInputAssignment(ctx.inputAssignment()); - if (ctx.methodCallStatement() != null) return buildMethodCall(ctx.methodCallStatement().methodCall()); - if (ctx.outputStatement() != null) return buildOutputStatement(ctx.outputStatement()); - if (ctx.ifStatement() != null) return buildIfStatement(ctx.ifStatement()); - if (ctx.forStatement() != null) return buildForStatement(ctx.forStatement()); - if (ctx.expressionStatement() != null) return buildExpressionStatement(ctx.expressionStatement()); - if (ctx.slotMethodCallStatement() != null) return buildSlotMethodCallStatement(ctx.slotMethodCallStatement()); - return null; - } - - private StatementNode buildOutputStatement(CoderiveParser.OutputStatementContext ctx) { - CoderiveParser.OutputTargetContext target = ctx.outputTarget(); - if (target instanceof CoderiveParser.OutputSlotCallContext) - return buildOutputSlotCall((CoderiveParser.OutputSlotCallContext) target); - if (target instanceof CoderiveParser.OutputNamedAssignmentContext) - return buildOutputNamedAssignment((CoderiveParser.OutputNamedAssignmentContext) target); - if (target instanceof CoderiveParser.OutputExpressionContext) - return buildOutputExpression((CoderiveParser.OutputExpressionContext) target); - return null; - } - - private ExprNode buildExpression(CoderiveParser.ExprContext ctx) { - if (ctx instanceof CoderiveParser.UnaryExprContext) return buildUnaryExpr((CoderiveParser.UnaryExprContext) ctx); - if (ctx instanceof CoderiveParser.MultiplicativeExprContext) return buildMultiplicativeExpr((CoderiveParser.MultiplicativeExprContext) ctx); - if (ctx instanceof CoderiveParser.AdditiveExprContext) return buildAdditiveExpr((CoderiveParser.AdditiveExprContext) ctx); - if (ctx instanceof CoderiveParser.ComparisonExprContext) return buildComparisonExpr((CoderiveParser.ComparisonExprContext) ctx); - if (ctx instanceof CoderiveParser.PrimaryExprContext) return buildPrimaryExpr((CoderiveParser.PrimaryExprContext) ctx); - if (ctx instanceof CoderiveParser.MethodCallExprContext) return buildMethodCallExpr((CoderiveParser.MethodCallExprContext) ctx); - if (ctx instanceof CoderiveParser.TypeCastExprContext) return buildTypeCast((CoderiveParser.TypeCastContext) ((CoderiveParser.TypeCastExprContext) ctx).typeCast()); - return null; - } - - private ExprNode buildPrimary(CoderiveParser.PrimaryContext ctx) { - if (ctx instanceof CoderiveParser.IndexAccessExprContext) return buildIndexAccessExpr((CoderiveParser.IndexAccessExprContext) ctx); - // For other primary types, we need to handle them via the atom - return null; - } - - private ExprNode buildAtom(CoderiveParser.AtomContext ctx) { - if (ctx instanceof CoderiveParser.IdentifierExprContext) return buildIdentifierExpr((CoderiveParser.IdentifierExprContext) ctx); - if (ctx instanceof CoderiveParser.IntLiteralExprContext) return buildIntLiteralExpr((CoderiveParser.IntLiteralExprContext) ctx); - if (ctx instanceof CoderiveParser.FloatLiteralExprContext) return buildFloatLiteralExpr((CoderiveParser.FloatLiteralExprContext) ctx); - if (ctx instanceof CoderiveParser.StringLiteralExprContext) return buildStringLiteralExpr((CoderiveParser.StringLiteralExprContext) ctx); - if (ctx instanceof CoderiveParser.BoolLiteralExprContext) return buildBoolLiteralExpr((CoderiveParser.BoolLiteralExprContext) ctx); - if (ctx instanceof CoderiveParser.ParenthesizedExprContext) return buildParenthesizedExpr((CoderiveParser.ParenthesizedExprContext) ctx); - if (ctx instanceof CoderiveParser.ArrayLiteralExprContext) return buildArrayLiteralExpr((CoderiveParser.ArrayLiteralExprContext) ctx); - return null; - } -} diff --git a/src/main/java/cod/ast/ASTFactory.java b/src/main/java/cod/ast/ASTFactory.java index c3cfc12d..b08bf9a3 100644 --- a/src/main/java/cod/ast/ASTFactory.java +++ b/src/main/java/cod/ast/ASTFactory.java @@ -1,7 +1,11 @@ package cod.ast; import cod.ast.nodes.*; + +import cod.syntax.Keyword; + import java.util.*; +import java.math.BigDecimal; public class ASTFactory { @@ -12,26 +16,26 @@ public static ProgramNode createProgram() { public static UnitNode createUnit(String name) { UnitNode unit = new UnitNode(); unit.name = name; - unit.imports = new GetNode(); + unit.imports = new UseNode(); unit.types = new ArrayList(); unit.resolvedImports = new HashMap(); return unit; } - public static GetNode createGetNode(List imports) { - GetNode getNode = new GetNode(); - getNode.imports = imports; - return getNode; + public static UseNode createUseNode(List imports) { + UseNode useNode = new UseNode(); + useNode.imports = imports; + return useNode; } - public static TypeNode createType(String name, String visibility, String extendName) { + public static TypeNode createType(String name, Keyword visibility, String extendName) { TypeNode type = new TypeNode(); type.name = name; type.visibility = visibility; type.extendName = extendName; type.fields = new ArrayList(); type.methods = new ArrayList(); - type.statements = new ArrayList(); + type.statements = new ArrayList(); return type; } @@ -42,16 +46,11 @@ public static FieldNode createField(String name, String type, ExprNode value) { field.value = value; return field; } - - public static FieldNode createField(String name, String type) { - return createField(name, type, null); - } - public static FieldNode createFieldWithVisibility(String name, String type, String visibility) { + public static FieldNode createField(String name, String type) { FieldNode field = new FieldNode(); field.name = name; field.type = type; - field.visibility = visibility; return field; } @@ -65,26 +64,31 @@ public static AssignmentNode createAssignment(ExprNode target, ExprNode value) { public static ConstructorNode createConstructor() { ConstructorNode cons = new ConstructorNode(); cons.parameters = new ArrayList(); - cons.body = new ArrayList(); + cons.body = new ArrayList(); return cons; } - public static MethodNode createMethod(String name, String visibility, List returnSlots) { + public static MethodNode createMethod(String name, Keyword visibility, List returnSlots) { MethodNode method = new MethodNode(); method.name = name; method.visibility = visibility; method.returnSlots = returnSlots != null ? returnSlots : new ArrayList(); method.parameters = new ArrayList(); - method.body = new ArrayList(); + method.body = new ArrayList(); return method; } - public static ParamNode createParam(String name, String type) { - ParamNode param = new ParamNode(); - param.name = name; - param.type = type; - return param; +public static ParamNode createParam(String name, String type, ExprNode defaultValue, boolean typeInferred) { + ParamNode param = new ParamNode(); + param.name = name; + param.type = type; + if (defaultValue != null) { + param.defaultValue = defaultValue; + param.hasDefaultValue = true; } + param.typeInferred = typeInferred; + return param; +} public static SlotNode createSlot(String type, String name) { SlotNode slot = new SlotNode(); @@ -105,21 +109,34 @@ public static ExprNode createIntLiteral(int value) { return node; } - public static ExprNode createFloatLiteral(float value) { + public static ExprNode createLongLiteral(long value) { + ExprNode node = new ExprNode(); + node.value = value; + return node; +} + + public static ExprNode createFloatLiteral(BigDecimal value) { + ExprNode node = new ExprNode(); + node.value = value; // Stores the precise BigDecimal object + return node; +} + + public static ExprNode createStringLiteral(String value) { ExprNode node = new ExprNode(); node.value = value; return node; } - public static ExprNode createStringLiteral(String value) { + public static ExprNode createBoolLiteral(boolean value) { ExprNode node = new ExprNode(); node.value = value; return node; } - public static ExprNode createBoolLiteral(boolean value) { + public static ExprNode createNullLiteral() { ExprNode node = new ExprNode(); - node.value = value; + node.value = null; + node.isNull = true; return node; } @@ -140,13 +157,12 @@ public static EqualityChainNode createEqualityChain(ExprNode left, String operat return chain; } - public static BooleanChainNode createBooleanChain(boolean isAll, List expressions) { + public static BooleanChainNode createBooleanChain(boolean isAll, List expressions) { BooleanChainNode node = new BooleanChainNode(); node.isAll = isAll; node.expressions = expressions != null ? expressions : new ArrayList(); return node; } - public static UnaryNode createUnaryOp(String op, ExprNode operand) { UnaryNode node = new UnaryNode(); @@ -164,7 +180,7 @@ public static TypeCastNode createTypeCast(String targetType, ExprNode expression public static SlotAssignmentNode createImplicitReturn(ExprNode returnExpr) { SlotAssignmentNode returnStmt = new SlotAssignmentNode(); - returnStmt.slotName = "return"; + returnStmt.slotName = "_"; returnStmt.value = returnExpr; return returnStmt; } @@ -190,6 +206,12 @@ public static ArrayNode createArray() { return createArray(null); } + public static TupleNode createTuple(List elements) { + TupleNode node = new TupleNode(); + node.elements = elements != null ? elements : new ArrayList(); + return node; + } + public static IndexAccessNode createIndexAccess(ExprNode array, ExprNode index) { IndexAccessNode node = new IndexAccessNode(); node.array = array; @@ -197,12 +219,20 @@ public static IndexAccessNode createIndexAccess(ExprNode array, ExprNode index) return node; } - public static IfNode createIf(ExprNode condition) { - IfNode ifNode = new IfNode(); - ifNode.condition = condition; - ifNode.thenBlock = new BlockNode(); - ifNode.elseBlock = new BlockNode(); - return ifNode; + public static ExprIfNode createIfExpression(ExprNode condition, ExprNode thenExpr, ExprNode elseExpr) { + ExprIfNode node = new ExprIfNode(); + node.condition = condition; + node.thenExpr = thenExpr; + node.elseExpr = elseExpr; + return node; +} + + public static StmtIfNode createIfStatement(ExprNode condition) { + StmtIfNode stmtIfNode = new StmtIfNode(); + stmtIfNode.condition = condition; + stmtIfNode.thenBlock = new BlockNode(); + stmtIfNode.elseBlock = new BlockNode(); + return stmtIfNode; } public static ForNode createFor(String iterator, RangeNode range) { @@ -213,19 +243,15 @@ public static ForNode createFor(String iterator, RangeNode range) { return forNode; } - public static RangeNode createRange(ExprNode step, ExprNode start, ExprNode end) { - RangeNode range = new RangeNode(); - range.step = step; - range.start = start; - range.end = end; - return range; - } +public static RangeNode createRange(ExprNode step, ExprNode start, ExprNode end) { + return new RangeNode(step, start, end); +} public static BlockNode createBlock() { return new BlockNode(); } - public static BlockNode createBlock(List statements) { + public static BlockNode createBlock(List statements) { return new BlockNode(statements); } @@ -254,6 +280,16 @@ public static OutputNode createOutput() { return createOutput(null); } + public static ExitNode createExit() { + return new ExitNode(); + } + + public static ArgumentListNode createArgumentList(List arguments) { + ArgumentListNode node = new ArgumentListNode(); + node.arguments = arguments != null ? arguments : new ArrayList(); + return node; + } + public static ReturnSlotAssignmentNode createReturnSlotAssignment(List variableNames, MethodCallNode methodCall) { ReturnSlotAssignmentNode assignment = new ReturnSlotAssignmentNode(); assignment.variableNames = variableNames; diff --git a/src/main/java/cod/ast/ASTPrinter.java b/src/main/java/cod/ast/ASTPrinter.java index 21b23636..270d85c4 100644 --- a/src/main/java/cod/ast/ASTPrinter.java +++ b/src/main/java/cod/ast/ASTPrinter.java @@ -1,237 +1,476 @@ package cod.ast; +import static cod.syntax.Keyword.*; import cod.ast.nodes.*; -public class ASTPrinter { - - public static void print(ASTNode node, int indent) { - if (node == null) return; - String pad = new String(new char[indent]).replace("\0", " "); - - if (node instanceof ProgramNode) { - System.out.println(pad + "Program"); - print(((ProgramNode) node).unit, indent + 1); - - } else if (node instanceof UnitNode) { - UnitNode u = (UnitNode) node; - System.out.println(pad + "Unit: " + u.name); - // Print GetNode if it exists - if (u.imports != null) { - print(u.imports, indent + 1); - } - for (TypeNode t : u.types) print(t, indent + 1); - - } else if (node instanceof GetNode) { - GetNode get = (GetNode) node; - if (get.imports.isEmpty()) { - System.out.println(pad + "Get imports: []"); - } else { - System.out.println(pad + "Get imports: " + get.imports); - } - - } else if (node instanceof TypeNode) { - TypeNode t = (TypeNode) node; - System.out.println( - pad - + "Class: " - + t.name - + " Extends: " - + t.extendName - + " Visibility: " - + t.visibility); - for (FieldNode f : t.fields) print(f, indent + 1); - if (t.constructor != null) print(t.constructor, indent + 1); - for (MethodNode m : t.methods) print(m, indent + 1); - for (StatementNode s : t.statements) print(s, indent + 1); - - } else if (node instanceof FieldNode) { - FieldNode f = (FieldNode) node; - System.out.println(pad + "Field: " + f.type + " " + f.name + " Visibility: " + f.visibility); - if (f.value != null) { - System.out.print(pad + " Value: "); - print(f.value, indent + 2); - } - // REMOVED: Array assignment handling from FieldNode since it's now in AssignmentNode - - } else if (node instanceof AssignmentNode) { - // NEW: Handle AssignmentNode - AssignmentNode assignment = (AssignmentNode) node; - System.out.println(pad + "Assignment:"); - System.out.print(pad + " Target: "); - print(assignment.left, indent + 2); - System.out.print(pad + " Value: "); - print(assignment.right, indent + 2); - - } else if (node instanceof ConstructorNode) { - ConstructorNode c = (ConstructorNode) node; - System.out.println(pad + "Constructor Params: " + c.parameters.size()); - for (ParamNode p : c.parameters) print(p, indent + 1); - for (StatementNode s : c.body) print(s, indent + 1); - - } else if (node instanceof MethodNode) { - MethodNode m = (MethodNode) node; - System.out.print(pad + "Method: " + m.name + " Slots: "); - for (SlotNode s : m.returnSlots) System.out.print(s.name + " "); - System.out.println(" Visibility: " + m.visibility); - for (ParamNode p : m.parameters) print(p, indent + 1); - for (StatementNode s : m.body) print(s, indent + 1); - - } else if (node instanceof MethodCallNode) { - MethodCallNode mc = (MethodCallNode) node; - System.out.print( - pad - + "Identifier/Call: " - + (mc.qualifiedName != null ? mc.qualifiedName : mc.name)); - // FIX: Handle multiple slot names - if (mc.slotNames != null && !mc.slotNames.isEmpty()) { - System.out.print(" (slot_cast: "); - for (int i = 0; i < mc.slotNames.size(); i++) { - if (i > 0) System.out.print(", "); - System.out.print(mc.slotNames.get(i)); - } - System.out.print(")"); - } +public class ASTPrinter extends BaseASTVisitor { + private int indent = 0; + + private String getIndent() { + return new String(new char[indent]).replace("\0", "| "); + } + + private void print(String message) { + System.out.print(getIndent() + message); + } + + private void println(String message) { + print(getIndent() + message + "\n"); + } + + @Override + public Void visit(ProgramNode node) { + println("\nPROGRAM"); + println("| "); + if (node.unit != null) visit(node.unit); + return null; + } + + @Override + public Void visit(UnitNode node) { + println("| UNIT: " + node.name); + + if (node.imports != null) visit(node.imports); + visitAll(node.types); + println("| "); + return null; + } + + @Override + public Void visit(UseNode node) { + println("USE imports: " + (node.imports.isEmpty() ? "[]" : node.imports)); + println("| "); + return null; + } + + @Override + public Void visit(TypeNode node) { + println("CLASS: " + node.name + " extends: " + node.extendName + " visibility: " + node.visibility + "\n| | "); + indent++; + visitAll(node.fields); + if (node.constructor != null) visit(node.constructor); + visitAll(node.methods); + visitAll(node.statements); + indent--; + return null; + } + + @Override + public Void visit(FieldNode node) { + println("FIELD: " + node.type + " " + node.name + " visibility: " + node.visibility); + if (node.value != null) { + println("| value:"); + indent += 2; + visit(node.value); + indent -= 2; + } + return null; + } + + @Override + public Void visit(MethodNode node) { + print("| METHOD: " + node.name + " slots: "); + for (SlotNode s : node.returnSlots) System.out.print(s.name + " "); + System.out.println(" visibility: " + node.visibility); + visitAll(node.parameters); + visitAll(node.body); + return null; + } + + @Override + public Void visit(ParamNode node) { + println("| PARAM: " + node.type + " " + node.name); + return null; + } + + @Override + public Void visit(ConstructorNode node) { + println("CONSTRUCTOR PARAMS: " + node.parameters.size()); + indent++; + visitAll(node.parameters); + visitAll(node.body); + indent--; + return null; + } + + @Override + public Void visit(BlockNode node) { + println("BLOCK:"); + indent++; + visitAll(node.statements); + indent--; + return null; + } + + @Override + public Void visit(AssignmentNode node) { + println("ASSIGNMENT:"); + println("| target:"); + indent += 2; + if (node.left != null) visit(node.left); + indent -= 2; + println("| value:"); + indent += 2; + if (node.right != null) visit(node.right); + indent -= 2; + return null; + } + + @Override + public Void visit(VarNode node) { + print("VAR: " + node.name); + if (node.explicitType != null) { + System.out.print(" (type: " + node.explicitType + ")"); + } + if (node.value != null) { + System.out.println(" ="); + indent++; + visit(node.value); + indent--; + } else { System.out.println(); - for (ExprNode arg : mc.arguments) print(arg, indent + 1); - + } + return null; + } + + @Override + public Void visit(StmtIfNode node) { + println("| | IF condition:"); + indent ++; + if (node.condition != null) visit(node.condition); + indent --; + println("| | | THEN execute:"); + indent += 2; + if (node.thenBlock != null) visit(node.thenBlock); + indent -= 2; + if (node.elseBlock != null && !node.elseBlock.statements.isEmpty()) { + println("| | ELSE:"); + indent += 2; + visit(node.elseBlock); + indent -= 2; + } + return null; + } + + @Override + public Void visit(ForNode node) { + println("FOR iterator: " + node.iterator); + indent += 2; + if (node.range != null) visit(node.range); + indent -= 2; + println("| BODY:"); + indent += 2; + if (node.body != null) visit(node.body); + indent -= 2; + return null; + } + + @Override + public Void visit(RangeNode node) { + println("| step:"); + indent += 2; + if (node.step != null) visit(node.step); + indent -= 2; + println("RANGE:"); + println("| start:"); + indent += 2; + if (node.start != null) visit(node.start); + indent -= 2; + println("| end:"); + indent += 2; + if (node.end != null) visit(node.end); + indent -= 2; + return null; + } + + @Override + public Void visit(OutputNode node) { + print("OUTPUT"); + if (node.varName != null) System.out.print(" var: " + node.varName); + System.out.println(); + + if (node.arguments.isEmpty()) { + println("| (no arguments)"); + } else { + for (int i = 0; i < node.arguments.size(); i++) { + println("| ARGUMENT " + i + ":"); + indent += 2; + visit(node.arguments.get(i)); + indent -= 2; + } + } + return null; + } + + @Override + public Void visit(InputNode node) { + println("INPUT: " + node.variableName + " = (" + node.targetType + ") input"); + return null; + } + + @Override + public Void visit(ExitNode node) { + println("EXIT"); + return null; + } + + @Override + public Void visit(ReturnSlotAssignmentNode node) { + println("RETURN SLOT ASSIGNMENT:"); + println("| VARIABLES: " + node.variableNames); + println("| METHOD CALL:"); + indent += 2; + if (node.methodCall != null) visit(node.methodCall); + indent -= 2; + return null; + } + + @Override + public Void visit(SlotDeclarationNode node) { + println("SLOT DECLARATION: " + node.slotNames); + return null; + } + + @Override + public Void visit(SlotAssignmentNode node) { + println("SLOT ASSIGNMENT:"); + println("| slot: " + (node.slotName != null ? node.slotName : "(implicit)")); + println("| value:"); + indent += 2; + if (node.value != null) visit(node.value); + indent -= 2; + return null; + } + + @Override + public Void visit(MultipleSlotAssignmentNode node) { + println("MULTIPLE SLOT ASSIGNMENT:"); + indent++; + for (int i = 0; i < node.assignments.size(); i++) { + SlotAssignmentNode assign = node.assignments.get(i); + println("ASSIGNMENT " + i + ":"); + println("| slot: " + (assign.slotName != null ? assign.slotName : "(positional)")); + println("| value:"); + indent += 2; + if (assign.value != null) visit(assign.value); + indent -= 2; + } + indent--; + return null; + } + + @Override + public Void visit(ExprNode node) { + // Check if this ExprNode is actually a more specific type (like in original printer) + if (node instanceof IndexAccessNode) { + // Handle IndexAccessNode that got cast to ExprNode + IndexAccessNode idx = (IndexAccessNode) node; + println("INDEX ACCESS"); + println("| array:"); + indent += 2; + visit(idx.array); + indent -= 2; + println("| index:"); + indent += 2; + visit(idx.index); + indent -= 2; } else if (node instanceof ArrayNode) { + // Handle ArrayNode that got cast to ExprNode ArrayNode arr = (ArrayNode) node; - System.out.println(pad + "ArrayLiteral with " + arr.elements.size() + " elements:"); + println("ARRAY literal with " + arr.elements.size() + " elements:"); + indent++; for (int i = 0; i < arr.elements.size(); i++) { - System.out.print(pad + " [" + i + "]: "); - print(arr.elements.get(i), indent + 2); + println("[" + i + "]:"); + indent++; + visit(arr.elements.get(i)); + indent--; } - - } else if (node instanceof IndexAccessNode) { - IndexAccessNode idx = (IndexAccessNode) node; - System.out.println(pad + "IndexAccess"); - System.out.print(pad + " Array: "); - print(idx.array, indent + 2); - System.out.print(pad + " Index: "); - print(idx.index, indent + 2); - + indent--; + } else if (node instanceof BooleanChainNode) { + // Handle BooleanChainNode that got cast to ExprNode + BooleanChainNode chain = (BooleanChainNode) node; + println("BOOLEAN chain: " + (chain.isAll ? ALL : ANY)); + indent++; + for (ExprNode expr : chain.expressions) { + visit(expr); + } + indent--; + } else if (node instanceof EqualityChainNode) { + // Handle EqualityChainNode that got cast to ExprNode + EqualityChainNode chain = (EqualityChainNode) node; + println("EQUALITY chain: " + (chain.isAllChain ? ALL : ANY) + " " + chain.operator); + println("| LEFT:"); + indent += 2; + visit(chain.left); + indent -= 2; + println("| CHAIN arguments:"); + indent += 2; + for (ExprNode arg : chain.chainArguments) { + visit(arg); + } + indent -= 2; } else if (node instanceof UnaryNode) { + // Handle UnaryNode that got cast to ExprNode UnaryNode unary = (UnaryNode) node; - System.out.println(pad + "Unary: " + unary.op); - print(unary.operand, indent + 1); - - } else if (node instanceof BinaryOpNode) { - BinaryOpNode b = (BinaryOpNode) node; - System.out.println(pad + "BinaryOp: " + b.op); - if (b.left != null) print(b.left, indent + 1); - if (b.right != null) print(b.right, indent + 1); - - } else if (node instanceof ExprNode) { - ExprNode e = (ExprNode) node; - if (e.value != null) { - System.out.println(pad + "Value: " + e.value); - } else if (e.name != null) { - System.out.println(pad + "Identifier: " + e.name); - } else if (e.left != null && e.right != null && e.op != null) { - // This might be a BinaryOpNode that got cast to ExprNode - System.out.println(pad + "BinaryOp: " + e.op); - print(e.left, indent + 1); - print(e.right, indent + 1); - } else if (e instanceof IndexAccessNode) { - // Handle IndexAccessNode that got cast to ExprNode - IndexAccessNode idx = (IndexAccessNode) e; - System.out.println(pad + "IndexAccess"); - System.out.print(pad + " Array: "); - print(idx.array, indent + 2); - System.out.print(pad + " Index: "); - print(idx.index, indent + 2); - } else { - System.out.println( - pad + "Expr (unresolved - name: " + e.name + ", value: " + e.value + ")"); - } - - } else if (node instanceof ParamNode) { - ParamNode p = (ParamNode) node; - System.out.println(pad + "Param: " + p.type + " " + p.name); - - } else if (node instanceof SlotNode) { - SlotNode s = (SlotNode) node; - System.out.println(pad + "Slot: " + s.name); - - } else if (node instanceof BlockNode) { - BlockNode block = (BlockNode) node; - System.out.println(pad + "Block:"); - for (StatementNode s : block.statements) print(s, indent + 1); - - } else if (node instanceof OutputNode) { - OutputNode p = (OutputNode) node; - System.out.print(pad + "Output"); - if (p.varName != null) System.out.print(" Var: " + p.varName); - System.out.println(); - - if (p.arguments.isEmpty()) { - System.out.println(pad + " (no arguments)"); - } else { - for (int i = 0; i < p.arguments.size(); i++) { - System.out.print(pad + " Argument " + i + ": "); - print(p.arguments.get(i), indent + 2); - } + println("UNARY: " + unary.op); + indent++; + visit(unary.operand); + indent--; + } else if (node.value != null) { + println("value: " + node.value); + } else if (node.name != null) { + println("IDENTIFIER: " + node.name); + } else if (node.left != null && node.right != null && node.op != null) { + // This might be a BinaryOpNode that got cast to ExprNode + println("| BINARY operation: " + node.op); + println("| left:"); + indent += 2; + visit(node.left); + indent -= 2; + println("| right:"); + indent += 2; + visit(node.right); + indent -= 2; + } else { + println("EXPR (unresolved - name: " + node.name + ", value: " + node.value + ")"); + } + return null; + } + + @Override + public Void visit(BinaryOpNode node) { + println("| BINARY operation: " + node.op); + println("| left:"); + indent += 2; + if (node.left != null) visit(node.left); + indent -= 2; + println("| right:"); + indent += 2; + if (node.right != null) visit(node.right); + indent -= 2; + return null; + } + + @Override + public Void visit(UnaryNode node) { + println("UNARY: " + node.op); + indent++; + if (node.operand != null) visit(node.operand); + indent--; + return null; + } + + @Override + public Void visit(TypeCastNode node) { + println("TYPECAST: " + node.targetType); + println("| expression:"); + indent += 2; + if (node.expression != null) visit(node.expression); + indent -= 2; + return null; + } + + @Override + public Void visit(MethodCallNode node) { + print("IDENTIFIER/CALL: " + (node.qualifiedName != null ? node.qualifiedName : node.name)); + if (node.slotNames != null && !node.slotNames.isEmpty()) { + System.out.print(" (slot_cast: "); + for (int i = 0; i < node.slotNames.size(); i++) { + if (i > 0) System.out.print(", "); + System.out.print(node.slotNames.get(i)); } - - } else if (node instanceof InputNode) { - InputNode input = (InputNode) node; - System.out.println( - pad + "Input: " + input.variableName + " = (" + input.targetType + ") input"); - - } else if (node instanceof VarNode) { - VarNode v = (VarNode) node; - System.out.print(pad + "Var: " + v.name); - if (v.value != null) { - System.out.println(" = "); - print(v.value, indent + 1); - } else { - System.out.println(); + System.out.print(")"); + } + if (node.chainType != null) { + System.out.print(" (chain: " + node.chainType + ")"); + } + System.out.println(); + + if (node.chainArguments != null && !node.chainArguments.isEmpty()) { + println("| CHAIN arguments:"); + indent += 2; + for (ExprNode arg : node.chainArguments) { + visit(arg); } - - } else if (node instanceof IfNode) { - IfNode ifn = (IfNode) node; - System.out.println(pad + "If"); - System.out.print(pad + " Condition: "); - print(ifn.condition, indent + 2); - System.out.println(pad + " Then:"); - print(ifn.thenBlock, indent + 2); - if (!ifn.elseBlock.statements.isEmpty()) { - System.out.println(pad + " Else:"); - print(ifn.elseBlock, indent + 2); + indent -= 2; + } + + if (node.arguments != null && !node.arguments.isEmpty()) { + println("| ARGUMENTS:"); + indent += 2; + for (ExprNode arg : node.arguments) { + visit(arg); } - - } else if (node instanceof RangeNode) { - RangeNode r = (RangeNode) node; - System.out.print(pad + "Range: step "); - print(r.step, 0); - System.out.print(" in "); - print(r.start, 0); - System.out.print(" to "); - print(r.end, 0); - System.out.println(); - - } else if (node instanceof ForNode) { - ForNode f = (ForNode) node; - System.out.println(pad + "For iterator: " + f.iterator); - System.out.print(pad + " Range: "); - print(f.range, 0); - System.out.println(); - System.out.println(pad + " Body:"); - print(f.body, indent + 2); - - } else if (node instanceof ReturnSlotAssignmentNode) { - ReturnSlotAssignmentNode rsa = (ReturnSlotAssignmentNode) node; - System.out.println(pad + "ReturnSlotAssignment:"); - System.out.println(pad + " Variables: " + rsa.variableNames); - System.out.print(pad + " MethodCall: "); - print(rsa.methodCall, indent + 2); - + indent -= 2; } else { - System.out.println(pad + node.getClass().getSimpleName()); + println("| (no arguments)"); } + return null; } - + + @Override + public Void visit(ArrayNode node) { + println("ARRAY literal with " + node.elements.size() + " elements:"); + indent++; + for (int i = 0; i < node.elements.size(); i++) { + println("[" + i + "]:"); + indent++; + visit(node.elements.get(i)); + indent--; + } + indent--; + return null; + } + + @Override + public Void visit(IndexAccessNode node) { + println("INDEX access"); + println("| ARRAY:"); + indent += 2; + if (node.array != null) visit(node.array); + indent -= 2; + println("| INDEX:"); + indent += 2; + if (node.index != null) visit(node.index); + indent -= 2; + return null; + } + + @Override + public Void visit(EqualityChainNode node) { + println("EQUALITY chain: " + (node.isAllChain ? ALL : ANY) + " " + node.operator); + println("| LEFT:"); + indent += 2; + if (node.left != null) visit(node.left); + indent -= 2; + println("| CHAIN arguments:"); + indent += 2; + for (ExprNode arg : node.chainArguments) { + visit(arg); + } + indent -= 2; + return null; + } + + @Override + public Void visit(BooleanChainNode node) { + println("BOOLEAN chain: " + (node.isAll ? ALL : ANY)); + indent++; + for (ExprNode expr : node.expressions) { + visit(expr); + } + indent--; + return null; + } + + @Override + public Void visit(SlotNode node) { + println("SLOT: " + node.name + " (type: " + node.type + ")"); + return null; + } + public static void print(ASTNode node) { - print(node, 0); + ASTPrinter printer = new ASTPrinter(); + printer.visit(node); } } \ No newline at end of file diff --git a/src/main/java/cod/ast/ASTVisitor.java b/src/main/java/cod/ast/ASTVisitor.java new file mode 100644 index 00000000..67085027 --- /dev/null +++ b/src/main/java/cod/ast/ASTVisitor.java @@ -0,0 +1,54 @@ +package cod.ast; + +import cod.ast.nodes.*; +import java.util.List; + +public interface ASTVisitor { + + // Program structure + T visit(ProgramNode node); + T visit(UnitNode node); + T visit(UseNode node); + + // Type declarations + T visit(TypeNode node); + T visit(FieldNode node); + T visit(MethodNode node); + T visit(ParamNode node); + T visit(ConstructorNode node); + + // Statements + T visit(BlockNode node); + T visit(AssignmentNode node); + T visit(VarNode node); + T visit(StmtIfNode node); + T visit(ExprIfNode node); + T visit(ForNode node); + T visit(RangeNode node); + T visit(OutputNode node); + T visit(InputNode node); + T visit(ExitNode node); + T visit(TupleNode node); + T visit(ReturnSlotAssignmentNode node); + T visit(SlotDeclarationNode node); + T visit(SlotAssignmentNode node); + T visit(MultipleSlotAssignmentNode node); + + // Expressions + T visit(ExprNode node); + T visit(BinaryOpNode node); + T visit(UnaryNode node); + T visit(TypeCastNode node); + T visit(MethodCallNode node); + T visit(ArrayNode node); + T visit(IndexAccessNode node); + T visit(EqualityChainNode node); + T visit(BooleanChainNode node); + T visit(SlotNode node); + + T visit(ASTNode node); + + // Utility methods for visiting lists + List visitList(List nodes); + void visitAll(List nodes); +} \ No newline at end of file diff --git a/src/main/java/cod/ast/BaseASTVisitor.java b/src/main/java/cod/ast/BaseASTVisitor.java new file mode 100644 index 00000000..3eddc9c7 --- /dev/null +++ b/src/main/java/cod/ast/BaseASTVisitor.java @@ -0,0 +1,205 @@ +package cod.ast; + +import cod.ast.nodes.*; +import java.util.ArrayList; +import java.util.List; + +public abstract class BaseASTVisitor implements ASTVisitor { + + protected T defaultVisit(ASTNode node) { + return null; + } + + @Override + public T visit(ProgramNode node) { + return node.accept(this); + } + + @Override + public T visit(UnitNode node) { + return node.accept(this); + } + + @Override + public T visit(UseNode node) { + return node.accept(this); + } + + @Override + public T visit(TypeNode node) { + return node.accept(this); + } + + @Override + public T visit(FieldNode node) { + return node.accept(this); + } + + @Override + public T visit(MethodNode node) { + return node.accept(this); + } + + @Override + public T visit(ParamNode node) { + return node.accept(this); + } + + @Override + public T visit(ConstructorNode node) { + return node.accept(this); + } + + @Override + public T visit(BlockNode node) { + return node.accept(this); + } + + @Override + public T visit(AssignmentNode node) { + return node.accept(this); + } + + @Override + public T visit(VarNode node) { + return node.accept(this); + } + + @Override + public T visit(StmtIfNode node) { + return node.accept(this); + } + + @Override + public T visit(ExprIfNode node) { + return node.accept(this); + } + + + @Override + public T visit(ForNode node) { + return node.accept(this); + } + + @Override + public T visit(RangeNode node) { + return node.accept(this); + } + + @Override + public T visit(OutputNode node) { + return node.accept(this); + } + + @Override + public T visit(InputNode node) { + return node.accept(this); + } + + @Override + public T visit(ExitNode node) { + return node.accept(this); + } + + @Override + public T visit(TupleNode node) { + return node.accept(this); + } + + @Override + public T visit(ReturnSlotAssignmentNode node) { + return node.accept(this); + } + + @Override + public T visit(SlotDeclarationNode node) { + return node.accept(this); + } + + @Override + public T visit(SlotAssignmentNode node) { + return node.accept(this); + } + + @Override + public T visit(MultipleSlotAssignmentNode node) { + return node.accept(this); + } + + @Override + public T visit(ExprNode node) { + return node.accept(this); + } + + @Override + public T visit(BinaryOpNode node) { + return node.accept(this); + } + + @Override + public T visit(UnaryNode node) { + return node.accept(this); + } + + @Override + public T visit(TypeCastNode node) { + return node.accept(this); + } + + @Override + public T visit(MethodCallNode node) { + return node.accept(this); + } + + @Override + public T visit(ArrayNode node) { + return node.accept(this); + } + + @Override + public T visit(IndexAccessNode node) { + return node.accept(this); + } + + @Override + public T visit(EqualityChainNode node) { + return node.accept(this); + } + + @Override + public T visit(BooleanChainNode node) { + return node.accept(this); + } + + @Override + public T visit(SlotNode node) { + return node.accept(this); + } + + @Override + public final T visit(ASTNode node) { + // This is the entry point - delegate to the node's accept method + return node.accept(this); + } + + @Override + public List visitList(List nodes) { + List results = new ArrayList(); + for (ASTNode node : nodes) { + results.add(visit(node)); + } + return results; + } + + @Override + public void visitAll(List nodes) { + for (ASTNode node : nodes) { + visit(node); + } + } + + // Helper method to dispatch via accept() - this is what should be used in InterpreterVisitor + public T dispatch(ASTNode node) { + return node.accept(this); + } +} diff --git a/src/main/java/cod/ast/CoderiveLexer.java b/src/main/java/cod/ast/CoderiveLexer.java deleted file mode 100644 index b729dcd5..00000000 --- a/src/main/java/cod/ast/CoderiveLexer.java +++ /dev/null @@ -1,334 +0,0 @@ -// Generated from CoderiveLexer.g4 by ANTLR 4.13.2 - -package cod.ast; - -import org.antlr.v4.runtime.Lexer; -import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.Token; -import org.antlr.v4.runtime.TokenStream; -import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.atn.*; -import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.misc.*; - -@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"}) -public class CoderiveLexer extends Lexer { - static { RuntimeMetaData.checkVersion("4.13.2", RuntimeMetaData.VERSION); } - - protected static final DFA[] _decisionToDFA; - protected static final PredictionContextCache _sharedContextCache = - new PredictionContextCache(); - public static final int - SHIP=1, LOCAL=2, UNIT=3, GET=4, EXTEND=5, THIS=6, VAR=7, OUTPUT=8, INPUT=9, - IF=10, ELSE=11, ELIF=12, FOR=13, BY=14, IN=15, TO=16, INT=17, STRING=18, - FLOAT=19, BOOL=20, INT_LIT=21, FLOAT_LIT=22, STRING_LIT=23, BOOL_LIT=24, - ID=25, ASSIGN=26, PLUS=27, MINUS=28, MUL=29, DIV=30, MOD=31, COLON=32, - GT=33, LT=34, GTE=35, LTE=36, EQ=37, NEQ=38, DOT=39, COMMA=40, LPAREN=41, - RPAREN=42, LBRACE=43, RBRACE=44, LBRACKET=45, RBRACKET=46, LINE_COMMENT=47, - BLOCK_COMMENT=48, WS=49; - public static String[] channelNames = { - "DEFAULT_TOKEN_CHANNEL", "HIDDEN" - }; - - public static String[] modeNames = { - "DEFAULT_MODE" - }; - - private static String[] makeRuleNames() { - return new String[] { - "SHIP", "LOCAL", "UNIT", "GET", "EXTEND", "THIS", "VAR", "OUTPUT", "INPUT", - "IF", "ELSE", "ELIF", "FOR", "BY", "IN", "TO", "INT", "STRING", "FLOAT", - "BOOL", "INT_LIT", "FLOAT_LIT", "STRING_LIT", "BOOL_LIT", "ID", "ASSIGN", - "PLUS", "MINUS", "MUL", "DIV", "MOD", "COLON", "GT", "LT", "GTE", "LTE", - "EQ", "NEQ", "DOT", "COMMA", "LPAREN", "RPAREN", "LBRACE", "RBRACE", - "LBRACKET", "RBRACKET", "LINE_COMMENT", "BLOCK_COMMENT", "WS" - }; - } - public static final String[] ruleNames = makeRuleNames(); - - private static String[] makeLiteralNames() { - return new String[] { - null, "'ship'", "'local'", "'unit'", "'get'", "'extend'", "'this'", "'var'", - "'output'", "'input'", "'if'", "'else'", "'elif'", "'for'", "'by'", "'in'", - "'to'", "'int'", "'string'", "'float'", "'bool'", null, null, null, null, - null, "'='", "'+'", "'-'", "'*'", "'/'", "'%'", "':'", "'>'", "'<'", - "'>='", "'<='", "'=='", "'!='", "'.'", "','", "'('", "')'", "'{'", "'}'", - "'['", "']'" - }; - } - private static final String[] _LITERAL_NAMES = makeLiteralNames(); - private static String[] makeSymbolicNames() { - return new String[] { - null, "SHIP", "LOCAL", "UNIT", "GET", "EXTEND", "THIS", "VAR", "OUTPUT", - "INPUT", "IF", "ELSE", "ELIF", "FOR", "BY", "IN", "TO", "INT", "STRING", - "FLOAT", "BOOL", "INT_LIT", "FLOAT_LIT", "STRING_LIT", "BOOL_LIT", "ID", - "ASSIGN", "PLUS", "MINUS", "MUL", "DIV", "MOD", "COLON", "GT", "LT", - "GTE", "LTE", "EQ", "NEQ", "DOT", "COMMA", "LPAREN", "RPAREN", "LBRACE", - "RBRACE", "LBRACKET", "RBRACKET", "LINE_COMMENT", "BLOCK_COMMENT", "WS" - }; - } - private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); - public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); - - /** - * @deprecated Use {@link #VOCABULARY} instead. - */ - @Deprecated - public static final String[] tokenNames; - static { - tokenNames = new String[_SYMBOLIC_NAMES.length]; - for (int i = 0; i < tokenNames.length; i++) { - tokenNames[i] = VOCABULARY.getLiteralName(i); - if (tokenNames[i] == null) { - tokenNames[i] = VOCABULARY.getSymbolicName(i); - } - - if (tokenNames[i] == null) { - tokenNames[i] = ""; - } - } - } - - @Override - @Deprecated - public String[] getTokenNames() { - return tokenNames; - } - - @Override - - public Vocabulary getVocabulary() { - return VOCABULARY; - } - - - public CoderiveLexer(CharStream input) { - super(input); - _interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); - } - - @Override - public String getGrammarFileName() { return "CoderiveLexer.g4"; } - - @Override - public String[] getRuleNames() { return ruleNames; } - - @Override - public String getSerializedATN() { return _serializedATN; } - - @Override - public String[] getChannelNames() { return channelNames; } - - @Override - public String[] getModeNames() { return modeNames; } - - @Override - public ATN getATN() { return _ATN; } - - public static final String _serializedATN = - "\u0004\u00001\u013f\u0006\uffff\uffff\u0002\u0000\u0007\u0000\u0002\u0001"+ - "\u0007\u0001\u0002\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004"+ - "\u0007\u0004\u0002\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007"+ - "\u0007\u0007\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b"+ - "\u0007\u000b\u0002\f\u0007\f\u0002\r\u0007\r\u0002\u000e\u0007\u000e\u0002"+ - "\u000f\u0007\u000f\u0002\u0010\u0007\u0010\u0002\u0011\u0007\u0011\u0002"+ - "\u0012\u0007\u0012\u0002\u0013\u0007\u0013\u0002\u0014\u0007\u0014\u0002"+ - "\u0015\u0007\u0015\u0002\u0016\u0007\u0016\u0002\u0017\u0007\u0017\u0002"+ - "\u0018\u0007\u0018\u0002\u0019\u0007\u0019\u0002\u001a\u0007\u001a\u0002"+ - "\u001b\u0007\u001b\u0002\u001c\u0007\u001c\u0002\u001d\u0007\u001d\u0002"+ - "\u001e\u0007\u001e\u0002\u001f\u0007\u001f\u0002 \u0007 \u0002!\u0007"+ - "!\u0002\"\u0007\"\u0002#\u0007#\u0002$\u0007$\u0002%\u0007%\u0002&\u0007"+ - "&\u0002\'\u0007\'\u0002(\u0007(\u0002)\u0007)\u0002*\u0007*\u0002+\u0007"+ - "+\u0002,\u0007,\u0002-\u0007-\u0002.\u0007.\u0002/\u0007/\u00020\u0007"+ - "0\u0001\u0000\u0001\u0000\u0001\u0000\u0001\u0000\u0001\u0000\u0001\u0001"+ - "\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002"+ - "\u0001\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0001\u0003\u0001\u0003"+ - "\u0001\u0003\u0001\u0003\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0004"+ - "\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0005\u0001\u0005\u0001\u0005"+ - "\u0001\u0005\u0001\u0005\u0001\u0006\u0001\u0006\u0001\u0006\u0001\u0006"+ - "\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007"+ - "\u0001\u0007\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\b\u0001\t\u0001"+ - "\t\u0001\t\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001\u000b\u0001\u000b"+ - "\u0001\u000b\u0001\u000b\u0001\u000b\u0001\f\u0001\f\u0001\f\u0001\f\u0001"+ - "\r\u0001\r\u0001\r\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000f\u0001"+ - "\u000f\u0001\u000f\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001"+ - "\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0001"+ - "\u0011\u0001\u0012\u0001\u0012\u0001\u0012\u0001\u0012\u0001\u0012\u0001"+ - "\u0012\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001"+ - "\u0014\u0004\u0014\u00c6\b\u0014\u000b\u0014\f\u0014\u00c7\u0001\u0015"+ - "\u0004\u0015\u00cb\b\u0015\u000b\u0015\f\u0015\u00cc\u0001\u0015\u0001"+ - "\u0015\u0004\u0015\u00d1\b\u0015\u000b\u0015\f\u0015\u00d2\u0001\u0016"+ - "\u0001\u0016\u0001\u0016\u0001\u0016\u0005\u0016\u00d9\b\u0016\n\u0016"+ - "\f\u0016\u00dc\t\u0016\u0001\u0016\u0001\u0016\u0001\u0017\u0001\u0017"+ - "\u0001\u0017\u0001\u0017\u0001\u0017\u0001\u0017\u0001\u0017\u0001\u0017"+ - "\u0001\u0017\u0003\u0017\u00e9\b\u0017\u0001\u0018\u0001\u0018\u0005\u0018"+ - "\u00ed\b\u0018\n\u0018\f\u0018\u00f0\t\u0018\u0001\u0019\u0001\u0019\u0001"+ - "\u001a\u0001\u001a\u0001\u001b\u0001\u001b\u0001\u001c\u0001\u001c\u0001"+ - "\u001d\u0001\u001d\u0001\u001e\u0001\u001e\u0001\u001f\u0001\u001f\u0001"+ - " \u0001 \u0001!\u0001!\u0001\"\u0001\"\u0001\"\u0001#\u0001#\u0001#\u0001"+ - "$\u0001$\u0001$\u0001%\u0001%\u0001%\u0001&\u0001&\u0001\'\u0001\'\u0001"+ - "(\u0001(\u0001)\u0001)\u0001*\u0001*\u0001+\u0001+\u0001,\u0001,\u0001"+ - "-\u0001-\u0001.\u0001.\u0001.\u0001.\u0005.\u0124\b.\n.\f.\u0127\t.\u0001"+ - ".\u0001.\u0001/\u0001/\u0001/\u0001/\u0005/\u012f\b/\n/\f/\u0132\t/\u0001"+ - "/\u0001/\u0001/\u0001/\u0001/\u00010\u00040\u013a\b0\u000b0\f0\u013b\u0001"+ - "0\u00010\u0001\u0130\u00001\u0001\u0001\u0003\u0002\u0005\u0003\u0007"+ - "\u0004\t\u0005\u000b\u0006\r\u0007\u000f\b\u0011\t\u0013\n\u0015\u000b"+ - "\u0017\f\u0019\r\u001b\u000e\u001d\u000f\u001f\u0010!\u0011#\u0012%\u0013"+ - "\'\u0014)\u0015+\u0016-\u0017/\u00181\u00193\u001a5\u001b7\u001c9\u001d"+ - ";\u001e=\u001f? A!C\"E#G$I%K&M\'O(Q)S*U+W,Y-[.]/_0a1\u0001\u0000\u0006"+ - "\u0001\u000009\u0002\u0000\"\"\\\\\u0003\u0000AZ__az\u0004\u000009AZ_"+ - "_az\u0002\u0000\n\n\r\r\u0003\u0000\t\n\r\r \u0148\u0000\u0001\u0001"+ - "\u0000\u0000\u0000\u0000\u0003\u0001\u0000\u0000\u0000\u0000\u0005\u0001"+ - "\u0000\u0000\u0000\u0000\u0007\u0001\u0000\u0000\u0000\u0000\t\u0001\u0000"+ - "\u0000\u0000\u0000\u000b\u0001\u0000\u0000\u0000\u0000\r\u0001\u0000\u0000"+ - "\u0000\u0000\u000f\u0001\u0000\u0000\u0000\u0000\u0011\u0001\u0000\u0000"+ - "\u0000\u0000\u0013\u0001\u0000\u0000\u0000\u0000\u0015\u0001\u0000\u0000"+ - "\u0000\u0000\u0017\u0001\u0000\u0000\u0000\u0000\u0019\u0001\u0000\u0000"+ - "\u0000\u0000\u001b\u0001\u0000\u0000\u0000\u0000\u001d\u0001\u0000\u0000"+ - "\u0000\u0000\u001f\u0001\u0000\u0000\u0000\u0000!\u0001\u0000\u0000\u0000"+ - "\u0000#\u0001\u0000\u0000\u0000\u0000%\u0001\u0000\u0000\u0000\u0000\'"+ - "\u0001\u0000\u0000\u0000\u0000)\u0001\u0000\u0000\u0000\u0000+\u0001\u0000"+ - "\u0000\u0000\u0000-\u0001\u0000\u0000\u0000\u0000/\u0001\u0000\u0000\u0000"+ - "\u00001\u0001\u0000\u0000\u0000\u00003\u0001\u0000\u0000\u0000\u00005"+ - "\u0001\u0000\u0000\u0000\u00007\u0001\u0000\u0000\u0000\u00009\u0001\u0000"+ - "\u0000\u0000\u0000;\u0001\u0000\u0000\u0000\u0000=\u0001\u0000\u0000\u0000"+ - "\u0000?\u0001\u0000\u0000\u0000\u0000A\u0001\u0000\u0000\u0000\u0000C"+ - "\u0001\u0000\u0000\u0000\u0000E\u0001\u0000\u0000\u0000\u0000G\u0001\u0000"+ - "\u0000\u0000\u0000I\u0001\u0000\u0000\u0000\u0000K\u0001\u0000\u0000\u0000"+ - "\u0000M\u0001\u0000\u0000\u0000\u0000O\u0001\u0000\u0000\u0000\u0000Q"+ - "\u0001\u0000\u0000\u0000\u0000S\u0001\u0000\u0000\u0000\u0000U\u0001\u0000"+ - "\u0000\u0000\u0000W\u0001\u0000\u0000\u0000\u0000Y\u0001\u0000\u0000\u0000"+ - "\u0000[\u0001\u0000\u0000\u0000\u0000]\u0001\u0000\u0000\u0000\u0000_"+ - "\u0001\u0000\u0000\u0000\u0000a\u0001\u0000\u0000\u0000\u0001c\u0001\u0000"+ - "\u0000\u0000\u0003h\u0001\u0000\u0000\u0000\u0005n\u0001\u0000\u0000\u0000"+ - "\u0007s\u0001\u0000\u0000\u0000\tw\u0001\u0000\u0000\u0000\u000b~\u0001"+ - "\u0000\u0000\u0000\r\u0083\u0001\u0000\u0000\u0000\u000f\u0087\u0001\u0000"+ - "\u0000\u0000\u0011\u008e\u0001\u0000\u0000\u0000\u0013\u0094\u0001\u0000"+ - "\u0000\u0000\u0015\u0097\u0001\u0000\u0000\u0000\u0017\u009c\u0001\u0000"+ - "\u0000\u0000\u0019\u00a1\u0001\u0000\u0000\u0000\u001b\u00a5\u0001\u0000"+ - "\u0000\u0000\u001d\u00a8\u0001\u0000\u0000\u0000\u001f\u00ab\u0001\u0000"+ - "\u0000\u0000!\u00ae\u0001\u0000\u0000\u0000#\u00b2\u0001\u0000\u0000\u0000"+ - "%\u00b9\u0001\u0000\u0000\u0000\'\u00bf\u0001\u0000\u0000\u0000)\u00c5"+ - "\u0001\u0000\u0000\u0000+\u00ca\u0001\u0000\u0000\u0000-\u00d4\u0001\u0000"+ - "\u0000\u0000/\u00e8\u0001\u0000\u0000\u00001\u00ea\u0001\u0000\u0000\u0000"+ - "3\u00f1\u0001\u0000\u0000\u00005\u00f3\u0001\u0000\u0000\u00007\u00f5"+ - "\u0001\u0000\u0000\u00009\u00f7\u0001\u0000\u0000\u0000;\u00f9\u0001\u0000"+ - "\u0000\u0000=\u00fb\u0001\u0000\u0000\u0000?\u00fd\u0001\u0000\u0000\u0000"+ - "A\u00ff\u0001\u0000\u0000\u0000C\u0101\u0001\u0000\u0000\u0000E\u0103"+ - "\u0001\u0000\u0000\u0000G\u0106\u0001\u0000\u0000\u0000I\u0109\u0001\u0000"+ - "\u0000\u0000K\u010c\u0001\u0000\u0000\u0000M\u010f\u0001\u0000\u0000\u0000"+ - "O\u0111\u0001\u0000\u0000\u0000Q\u0113\u0001\u0000\u0000\u0000S\u0115"+ - "\u0001\u0000\u0000\u0000U\u0117\u0001\u0000\u0000\u0000W\u0119\u0001\u0000"+ - "\u0000\u0000Y\u011b\u0001\u0000\u0000\u0000[\u011d\u0001\u0000\u0000\u0000"+ - "]\u011f\u0001\u0000\u0000\u0000_\u012a\u0001\u0000\u0000\u0000a\u0139"+ - "\u0001\u0000\u0000\u0000cd\u0005s\u0000\u0000de\u0005h\u0000\u0000ef\u0005"+ - "i\u0000\u0000fg\u0005p\u0000\u0000g\u0002\u0001\u0000\u0000\u0000hi\u0005"+ - "l\u0000\u0000ij\u0005o\u0000\u0000jk\u0005c\u0000\u0000kl\u0005a\u0000"+ - "\u0000lm\u0005l\u0000\u0000m\u0004\u0001\u0000\u0000\u0000no\u0005u\u0000"+ - "\u0000op\u0005n\u0000\u0000pq\u0005i\u0000\u0000qr\u0005t\u0000\u0000"+ - "r\u0006\u0001\u0000\u0000\u0000st\u0005g\u0000\u0000tu\u0005e\u0000\u0000"+ - "uv\u0005t\u0000\u0000v\b\u0001\u0000\u0000\u0000wx\u0005e\u0000\u0000"+ - "xy\u0005x\u0000\u0000yz\u0005t\u0000\u0000z{\u0005e\u0000\u0000{|\u0005"+ - "n\u0000\u0000|}\u0005d\u0000\u0000}\n\u0001\u0000\u0000\u0000~\u007f\u0005"+ - "t\u0000\u0000\u007f\u0080\u0005h\u0000\u0000\u0080\u0081\u0005i\u0000"+ - "\u0000\u0081\u0082\u0005s\u0000\u0000\u0082\f\u0001\u0000\u0000\u0000"+ - "\u0083\u0084\u0005v\u0000\u0000\u0084\u0085\u0005a\u0000\u0000\u0085\u0086"+ - "\u0005r\u0000\u0000\u0086\u000e\u0001\u0000\u0000\u0000\u0087\u0088\u0005"+ - "o\u0000\u0000\u0088\u0089\u0005u\u0000\u0000\u0089\u008a\u0005t\u0000"+ - "\u0000\u008a\u008b\u0005p\u0000\u0000\u008b\u008c\u0005u\u0000\u0000\u008c"+ - "\u008d\u0005t\u0000\u0000\u008d\u0010\u0001\u0000\u0000\u0000\u008e\u008f"+ - "\u0005i\u0000\u0000\u008f\u0090\u0005n\u0000\u0000\u0090\u0091\u0005p"+ - "\u0000\u0000\u0091\u0092\u0005u\u0000\u0000\u0092\u0093\u0005t\u0000\u0000"+ - "\u0093\u0012\u0001\u0000\u0000\u0000\u0094\u0095\u0005i\u0000\u0000\u0095"+ - "\u0096\u0005f\u0000\u0000\u0096\u0014\u0001\u0000\u0000\u0000\u0097\u0098"+ - "\u0005e\u0000\u0000\u0098\u0099\u0005l\u0000\u0000\u0099\u009a\u0005s"+ - "\u0000\u0000\u009a\u009b\u0005e\u0000\u0000\u009b\u0016\u0001\u0000\u0000"+ - "\u0000\u009c\u009d\u0005e\u0000\u0000\u009d\u009e\u0005l\u0000\u0000\u009e"+ - "\u009f\u0005i\u0000\u0000\u009f\u00a0\u0005f\u0000\u0000\u00a0\u0018\u0001"+ - "\u0000\u0000\u0000\u00a1\u00a2\u0005f\u0000\u0000\u00a2\u00a3\u0005o\u0000"+ - "\u0000\u00a3\u00a4\u0005r\u0000\u0000\u00a4\u001a\u0001\u0000\u0000\u0000"+ - "\u00a5\u00a6\u0005b\u0000\u0000\u00a6\u00a7\u0005y\u0000\u0000\u00a7\u001c"+ - "\u0001\u0000\u0000\u0000\u00a8\u00a9\u0005i\u0000\u0000\u00a9\u00aa\u0005"+ - "n\u0000\u0000\u00aa\u001e\u0001\u0000\u0000\u0000\u00ab\u00ac\u0005t\u0000"+ - "\u0000\u00ac\u00ad\u0005o\u0000\u0000\u00ad \u0001\u0000\u0000\u0000\u00ae"+ - "\u00af\u0005i\u0000\u0000\u00af\u00b0\u0005n\u0000\u0000\u00b0\u00b1\u0005"+ - "t\u0000\u0000\u00b1\"\u0001\u0000\u0000\u0000\u00b2\u00b3\u0005s\u0000"+ - "\u0000\u00b3\u00b4\u0005t\u0000\u0000\u00b4\u00b5\u0005r\u0000\u0000\u00b5"+ - "\u00b6\u0005i\u0000\u0000\u00b6\u00b7\u0005n\u0000\u0000\u00b7\u00b8\u0005"+ - "g\u0000\u0000\u00b8$\u0001\u0000\u0000\u0000\u00b9\u00ba\u0005f\u0000"+ - "\u0000\u00ba\u00bb\u0005l\u0000\u0000\u00bb\u00bc\u0005o\u0000\u0000\u00bc"+ - "\u00bd\u0005a\u0000\u0000\u00bd\u00be\u0005t\u0000\u0000\u00be&\u0001"+ - "\u0000\u0000\u0000\u00bf\u00c0\u0005b\u0000\u0000\u00c0\u00c1\u0005o\u0000"+ - "\u0000\u00c1\u00c2\u0005o\u0000\u0000\u00c2\u00c3\u0005l\u0000\u0000\u00c3"+ - "(\u0001\u0000\u0000\u0000\u00c4\u00c6\u0007\u0000\u0000\u0000\u00c5\u00c4"+ - "\u0001\u0000\u0000\u0000\u00c6\u00c7\u0001\u0000\u0000\u0000\u00c7\u00c5"+ - "\u0001\u0000\u0000\u0000\u00c7\u00c8\u0001\u0000\u0000\u0000\u00c8*\u0001"+ - "\u0000\u0000\u0000\u00c9\u00cb\u0007\u0000\u0000\u0000\u00ca\u00c9\u0001"+ - "\u0000\u0000\u0000\u00cb\u00cc\u0001\u0000\u0000\u0000\u00cc\u00ca\u0001"+ - "\u0000\u0000\u0000\u00cc\u00cd\u0001\u0000\u0000\u0000\u00cd\u00ce\u0001"+ - "\u0000\u0000\u0000\u00ce\u00d0\u0005.\u0000\u0000\u00cf\u00d1\u0007\u0000"+ - "\u0000\u0000\u00d0\u00cf\u0001\u0000\u0000\u0000\u00d1\u00d2\u0001\u0000"+ - "\u0000\u0000\u00d2\u00d0\u0001\u0000\u0000\u0000\u00d2\u00d3\u0001\u0000"+ - "\u0000\u0000\u00d3,\u0001\u0000\u0000\u0000\u00d4\u00da\u0005\"\u0000"+ - "\u0000\u00d5\u00d9\b\u0001\u0000\u0000\u00d6\u00d7\u0005\\\u0000\u0000"+ - "\u00d7\u00d9\t\u0000\u0000\u0000\u00d8\u00d5\u0001\u0000\u0000\u0000\u00d8"+ - "\u00d6\u0001\u0000\u0000\u0000\u00d9\u00dc\u0001\u0000\u0000\u0000\u00da"+ - "\u00d8\u0001\u0000\u0000\u0000\u00da\u00db\u0001\u0000\u0000\u0000\u00db"+ - "\u00dd\u0001\u0000\u0000\u0000\u00dc\u00da\u0001\u0000\u0000\u0000\u00dd"+ - "\u00de\u0005\"\u0000\u0000\u00de.\u0001\u0000\u0000\u0000\u00df\u00e0"+ - "\u0005t\u0000\u0000\u00e0\u00e1\u0005r\u0000\u0000\u00e1\u00e2\u0005u"+ - "\u0000\u0000\u00e2\u00e9\u0005e\u0000\u0000\u00e3\u00e4\u0005f\u0000\u0000"+ - "\u00e4\u00e5\u0005a\u0000\u0000\u00e5\u00e6\u0005l\u0000\u0000\u00e6\u00e7"+ - "\u0005s\u0000\u0000\u00e7\u00e9\u0005e\u0000\u0000\u00e8\u00df\u0001\u0000"+ - "\u0000\u0000\u00e8\u00e3\u0001\u0000\u0000\u0000\u00e90\u0001\u0000\u0000"+ - "\u0000\u00ea\u00ee\u0007\u0002\u0000\u0000\u00eb\u00ed\u0007\u0003\u0000"+ - "\u0000\u00ec\u00eb\u0001\u0000\u0000\u0000\u00ed\u00f0\u0001\u0000\u0000"+ - "\u0000\u00ee\u00ec\u0001\u0000\u0000\u0000\u00ee\u00ef\u0001\u0000\u0000"+ - "\u0000\u00ef2\u0001\u0000\u0000\u0000\u00f0\u00ee\u0001\u0000\u0000\u0000"+ - "\u00f1\u00f2\u0005=\u0000\u0000\u00f24\u0001\u0000\u0000\u0000\u00f3\u00f4"+ - "\u0005+\u0000\u0000\u00f46\u0001\u0000\u0000\u0000\u00f5\u00f6\u0005-"+ - "\u0000\u0000\u00f68\u0001\u0000\u0000\u0000\u00f7\u00f8\u0005*\u0000\u0000"+ - "\u00f8:\u0001\u0000\u0000\u0000\u00f9\u00fa\u0005/\u0000\u0000\u00fa<"+ - "\u0001\u0000\u0000\u0000\u00fb\u00fc\u0005%\u0000\u0000\u00fc>\u0001\u0000"+ - "\u0000\u0000\u00fd\u00fe\u0005:\u0000\u0000\u00fe@\u0001\u0000\u0000\u0000"+ - "\u00ff\u0100\u0005>\u0000\u0000\u0100B\u0001\u0000\u0000\u0000\u0101\u0102"+ - "\u0005<\u0000\u0000\u0102D\u0001\u0000\u0000\u0000\u0103\u0104\u0005>"+ - "\u0000\u0000\u0104\u0105\u0005=\u0000\u0000\u0105F\u0001\u0000\u0000\u0000"+ - "\u0106\u0107\u0005<\u0000\u0000\u0107\u0108\u0005=\u0000\u0000\u0108H"+ - "\u0001\u0000\u0000\u0000\u0109\u010a\u0005=\u0000\u0000\u010a\u010b\u0005"+ - "=\u0000\u0000\u010bJ\u0001\u0000\u0000\u0000\u010c\u010d\u0005!\u0000"+ - "\u0000\u010d\u010e\u0005=\u0000\u0000\u010eL\u0001\u0000\u0000\u0000\u010f"+ - "\u0110\u0005.\u0000\u0000\u0110N\u0001\u0000\u0000\u0000\u0111\u0112\u0005"+ - ",\u0000\u0000\u0112P\u0001\u0000\u0000\u0000\u0113\u0114\u0005(\u0000"+ - "\u0000\u0114R\u0001\u0000\u0000\u0000\u0115\u0116\u0005)\u0000\u0000\u0116"+ - "T\u0001\u0000\u0000\u0000\u0117\u0118\u0005{\u0000\u0000\u0118V\u0001"+ - "\u0000\u0000\u0000\u0119\u011a\u0005}\u0000\u0000\u011aX\u0001\u0000\u0000"+ - "\u0000\u011b\u011c\u0005[\u0000\u0000\u011cZ\u0001\u0000\u0000\u0000\u011d"+ - "\u011e\u0005]\u0000\u0000\u011e\\\u0001\u0000\u0000\u0000\u011f\u0120"+ - "\u0005/\u0000\u0000\u0120\u0121\u0005/\u0000\u0000\u0121\u0125\u0001\u0000"+ - "\u0000\u0000\u0122\u0124\b\u0004\u0000\u0000\u0123\u0122\u0001\u0000\u0000"+ - "\u0000\u0124\u0127\u0001\u0000\u0000\u0000\u0125\u0123\u0001\u0000\u0000"+ - "\u0000\u0125\u0126\u0001\u0000\u0000\u0000\u0126\u0128\u0001\u0000\u0000"+ - "\u0000\u0127\u0125\u0001\u0000\u0000\u0000\u0128\u0129\u0006.\u0000\u0000"+ - "\u0129^\u0001\u0000\u0000\u0000\u012a\u012b\u0005/\u0000\u0000\u012b\u012c"+ - "\u0005*\u0000\u0000\u012c\u0130\u0001\u0000\u0000\u0000\u012d\u012f\t"+ - "\u0000\u0000\u0000\u012e\u012d\u0001\u0000\u0000\u0000\u012f\u0132\u0001"+ - "\u0000\u0000\u0000\u0130\u0131\u0001\u0000\u0000\u0000\u0130\u012e\u0001"+ - "\u0000\u0000\u0000\u0131\u0133\u0001\u0000\u0000\u0000\u0132\u0130\u0001"+ - "\u0000\u0000\u0000\u0133\u0134\u0005*\u0000\u0000\u0134\u0135\u0005/\u0000"+ - "\u0000\u0135\u0136\u0001\u0000\u0000\u0000\u0136\u0137\u0006/\u0000\u0000"+ - "\u0137`\u0001\u0000\u0000\u0000\u0138\u013a\u0007\u0005\u0000\u0000\u0139"+ - "\u0138\u0001\u0000\u0000\u0000\u013a\u013b\u0001\u0000\u0000\u0000\u013b"+ - "\u0139\u0001\u0000\u0000\u0000\u013b\u013c\u0001\u0000\u0000\u0000\u013c"+ - "\u013d\u0001\u0000\u0000\u0000\u013d\u013e\u00060\u0000\u0000\u013eb\u0001"+ - "\u0000\u0000\u0000\u000b\u0000\u00c7\u00cc\u00d2\u00d8\u00da\u00e8\u00ee"+ - "\u0125\u0130\u013b\u0001\u0006\u0000\u0000"; - public static final ATN _ATN = - new ATNDeserializer().deserialize(_serializedATN.toCharArray()); - static { - _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; - for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { - _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); - } - } -} \ No newline at end of file diff --git a/src/main/java/cod/ast/CoderiveParser.java b/src/main/java/cod/ast/CoderiveParser.java deleted file mode 100644 index f76fc988..00000000 --- a/src/main/java/cod/ast/CoderiveParser.java +++ /dev/null @@ -1,4152 +0,0 @@ -// Generated from CoderiveParser.g4 by ANTLR 4.13.2 - -package cod.ast; - -import org.antlr.v4.runtime.atn.*; -import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.misc.*; -import org.antlr.v4.runtime.tree.*; -import java.util.List; -import java.util.Iterator; -import java.util.ArrayList; - -@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"}) -public class CoderiveParser extends Parser { - static { RuntimeMetaData.checkVersion("4.13.2", RuntimeMetaData.VERSION); } - - protected static final DFA[] _decisionToDFA; - protected static final PredictionContextCache _sharedContextCache = - new PredictionContextCache(); - public static final int - SHIP=1, LOCAL=2, UNIT=3, GET=4, EXTEND=5, THIS=6, VAR=7, OUTPUT=8, INPUT=9, - IF=10, ELSE=11, ELIF=12, FOR=13, BY=14, IN=15, TO=16, INT=17, STRING=18, - FLOAT=19, BOOL=20, INT_LIT=21, FLOAT_LIT=22, STRING_LIT=23, BOOL_LIT=24, - ID=25, ASSIGN=26, PLUS=27, MINUS=28, MUL=29, DIV=30, MOD=31, COLON=32, - GT=33, LT=34, GTE=35, LTE=36, EQ=37, NEQ=38, DOT=39, COMMA=40, LPAREN=41, - RPAREN=42, LBRACE=43, RBRACE=44, LBRACKET=45, RBRACKET=46, LINE_COMMENT=47, - BLOCK_COMMENT=48, WS=49; - public static final int - RULE_program = 0, RULE_unitDeclaration = 1, RULE_qualifiedNameList = 2, - RULE_qualifiedName = 3, RULE_typeDeclaration = 4, RULE_modifiers = 5, - RULE_typeBody = 6, RULE_fieldDeclaration = 7, RULE_constructor = 8, RULE_methodDeclaration = 9, - RULE_idList = 10, RULE_slotList = 11, RULE_parameterList = 12, RULE_parameter = 13, - RULE_type = 14, RULE_simpleType = 15, RULE_statement = 16, RULE_variableDeclaration = 17, - RULE_assignment = 18, RULE_expressionStatement = 19, RULE_assignable = 20, - RULE_returnSlotAssignment = 21, RULE_assignableList = 22, RULE_slotMethodCallStatement = 23, - RULE_slotMethodCall = 24, RULE_slotCast = 25, RULE_inputAssignment = 26, - RULE_inputStatement = 27, RULE_typeInput = 28, RULE_outputStatement = 29, - RULE_outputTarget = 30, RULE_methodCallStatement = 31, RULE_methodCall = 32, - RULE_argumentList = 33, RULE_ifStatement = 34, RULE_thenBlock = 35, RULE_elseBlock = 36, - RULE_forStatement = 37, RULE_forStepExpr = 38, RULE_expr = 39, RULE_primary = 40, - RULE_atom = 41, RULE_arrayLiteral = 42, RULE_exprList = 43, RULE_indexAccess = 44, - RULE_typeCast = 45; - private static String[] makeRuleNames() { - return new String[] { - "program", "unitDeclaration", "qualifiedNameList", "qualifiedName", "typeDeclaration", - "modifiers", "typeBody", "fieldDeclaration", "constructor", "methodDeclaration", - "idList", "slotList", "parameterList", "parameter", "type", "simpleType", - "statement", "variableDeclaration", "assignment", "expressionStatement", - "assignable", "returnSlotAssignment", "assignableList", "slotMethodCallStatement", - "slotMethodCall", "slotCast", "inputAssignment", "inputStatement", "typeInput", - "outputStatement", "outputTarget", "methodCallStatement", "methodCall", - "argumentList", "ifStatement", "thenBlock", "elseBlock", "forStatement", - "forStepExpr", "expr", "primary", "atom", "arrayLiteral", "exprList", - "indexAccess", "typeCast" - }; - } - public static final String[] ruleNames = makeRuleNames(); - - private static String[] makeLiteralNames() { - return new String[] { - null, "'ship'", "'local'", "'unit'", "'get'", "'extend'", "'this'", "'var'", - "'output'", "'input'", "'if'", "'else'", "'elif'", "'for'", "'by'", "'in'", - "'to'", "'int'", "'string'", "'float'", "'bool'", null, null, null, null, - null, "'='", "'+'", "'-'", "'*'", "'/'", "'%'", "':'", "'>'", "'<'", - "'>='", "'<='", "'=='", "'!='", "'.'", "','", "'('", "')'", "'{'", "'}'", - "'['", "']'" - }; - } - private static final String[] _LITERAL_NAMES = makeLiteralNames(); - private static String[] makeSymbolicNames() { - return new String[] { - null, "SHIP", "LOCAL", "UNIT", "GET", "EXTEND", "THIS", "VAR", "OUTPUT", - "INPUT", "IF", "ELSE", "ELIF", "FOR", "BY", "IN", "TO", "INT", "STRING", - "FLOAT", "BOOL", "INT_LIT", "FLOAT_LIT", "STRING_LIT", "BOOL_LIT", "ID", - "ASSIGN", "PLUS", "MINUS", "MUL", "DIV", "MOD", "COLON", "GT", "LT", - "GTE", "LTE", "EQ", "NEQ", "DOT", "COMMA", "LPAREN", "RPAREN", "LBRACE", - "RBRACE", "LBRACKET", "RBRACKET", "LINE_COMMENT", "BLOCK_COMMENT", "WS" - }; - } - private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); - public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); - - /** - * @deprecated Use {@link #VOCABULARY} instead. - */ - @Deprecated - public static final String[] tokenNames; - static { - tokenNames = new String[_SYMBOLIC_NAMES.length]; - for (int i = 0; i < tokenNames.length; i++) { - tokenNames[i] = VOCABULARY.getLiteralName(i); - if (tokenNames[i] == null) { - tokenNames[i] = VOCABULARY.getSymbolicName(i); - } - - if (tokenNames[i] == null) { - tokenNames[i] = ""; - } - } - } - - @Override - @Deprecated - public String[] getTokenNames() { - return tokenNames; - } - - @Override - - public Vocabulary getVocabulary() { - return VOCABULARY; - } - - @Override - public String getGrammarFileName() { return "CoderiveParser.g4"; } - - @Override - public String[] getRuleNames() { return ruleNames; } - - @Override - public String getSerializedATN() { return _serializedATN; } - - @Override - public ATN getATN() { return _ATN; } - - public CoderiveParser(TokenStream input) { - super(input); - _interp = new ParserATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); - } - - @SuppressWarnings("CheckReturnValue") - public static class ProgramContext extends ParserRuleContext { - public TerminalNode EOF() { return getToken(CoderiveParser.EOF, 0); } - public UnitDeclarationContext unitDeclaration() { - return getRuleContext(UnitDeclarationContext.class,0); - } - public List typeDeclaration() { - return getRuleContexts(TypeDeclarationContext.class); - } - public TypeDeclarationContext typeDeclaration(int i) { - return getRuleContext(TypeDeclarationContext.class,i); - } - public ProgramContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_program; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterProgram(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitProgram(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitProgram(this); - else return visitor.visitChildren(this); - } - } - - public final ProgramContext program() throws RecognitionException { - ProgramContext _localctx = new ProgramContext(_ctx, getState()); - enterRule(_localctx, 0, RULE_program); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(93); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==UNIT) { - { - setState(92); - unitDeclaration(); - } - } - - setState(98); - _errHandler.sync(this); - _la = _input.LA(1); - while ((((_la) & ~0x3f) == 0 && ((1L << _la) & 33554438L) != 0)) { - { - { - setState(95); - typeDeclaration(); - } - } - setState(100); - _errHandler.sync(this); - _la = _input.LA(1); - } - setState(101); - match(EOF); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class UnitDeclarationContext extends ParserRuleContext { - public TerminalNode UNIT() { return getToken(CoderiveParser.UNIT, 0); } - public QualifiedNameContext qualifiedName() { - return getRuleContext(QualifiedNameContext.class,0); - } - public TerminalNode GET() { return getToken(CoderiveParser.GET, 0); } - public TerminalNode LBRACE() { return getToken(CoderiveParser.LBRACE, 0); } - public QualifiedNameListContext qualifiedNameList() { - return getRuleContext(QualifiedNameListContext.class,0); - } - public TerminalNode RBRACE() { return getToken(CoderiveParser.RBRACE, 0); } - public UnitDeclarationContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_unitDeclaration; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterUnitDeclaration(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitUnitDeclaration(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitUnitDeclaration(this); - else return visitor.visitChildren(this); - } - } - - public final UnitDeclarationContext unitDeclaration() throws RecognitionException { - UnitDeclarationContext _localctx = new UnitDeclarationContext(_ctx, getState()); - enterRule(_localctx, 2, RULE_unitDeclaration); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(103); - match(UNIT); - setState(104); - qualifiedName(); - setState(110); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==GET) { - { - setState(105); - match(GET); - setState(106); - match(LBRACE); - setState(107); - qualifiedNameList(); - setState(108); - match(RBRACE); - } - } - - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class QualifiedNameListContext extends ParserRuleContext { - public List qualifiedName() { - return getRuleContexts(QualifiedNameContext.class); - } - public QualifiedNameContext qualifiedName(int i) { - return getRuleContext(QualifiedNameContext.class,i); - } - public QualifiedNameListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_qualifiedNameList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterQualifiedNameList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitQualifiedNameList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitQualifiedNameList(this); - else return visitor.visitChildren(this); - } - } - - public final QualifiedNameListContext qualifiedNameList() throws RecognitionException { - QualifiedNameListContext _localctx = new QualifiedNameListContext(_ctx, getState()); - enterRule(_localctx, 4, RULE_qualifiedNameList); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(113); - _errHandler.sync(this); - _la = _input.LA(1); - do { - { - { - setState(112); - qualifiedName(); - } - } - setState(115); - _errHandler.sync(this); - _la = _input.LA(1); - } while ( _la==ID ); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class QualifiedNameContext extends ParserRuleContext { - public List ID() { return getTokens(CoderiveParser.ID); } - public TerminalNode ID(int i) { - return getToken(CoderiveParser.ID, i); - } - public List DOT() { return getTokens(CoderiveParser.DOT); } - public TerminalNode DOT(int i) { - return getToken(CoderiveParser.DOT, i); - } - public QualifiedNameContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_qualifiedName; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterQualifiedName(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitQualifiedName(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitQualifiedName(this); - else return visitor.visitChildren(this); - } - } - - public final QualifiedNameContext qualifiedName() throws RecognitionException { - QualifiedNameContext _localctx = new QualifiedNameContext(_ctx, getState()); - enterRule(_localctx, 6, RULE_qualifiedName); - try { - int _alt; - enterOuterAlt(_localctx, 1); - { - setState(117); - match(ID); - setState(122); - _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,4,_ctx); - while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { - if ( _alt==1 ) { - { - { - setState(118); - match(DOT); - setState(119); - match(ID); - } - } - } - setState(124); - _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,4,_ctx); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class TypeDeclarationContext extends ParserRuleContext { - public TerminalNode ID() { return getToken(CoderiveParser.ID, 0); } - public TerminalNode LBRACE() { return getToken(CoderiveParser.LBRACE, 0); } - public TerminalNode RBRACE() { return getToken(CoderiveParser.RBRACE, 0); } - public ModifiersContext modifiers() { - return getRuleContext(ModifiersContext.class,0); - } - public TerminalNode EXTEND() { return getToken(CoderiveParser.EXTEND, 0); } - public QualifiedNameContext qualifiedName() { - return getRuleContext(QualifiedNameContext.class,0); - } - public List typeBody() { - return getRuleContexts(TypeBodyContext.class); - } - public TypeBodyContext typeBody(int i) { - return getRuleContext(TypeBodyContext.class,i); - } - public TypeDeclarationContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_typeDeclaration; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterTypeDeclaration(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitTypeDeclaration(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitTypeDeclaration(this); - else return visitor.visitChildren(this); - } - } - - public final TypeDeclarationContext typeDeclaration() throws RecognitionException { - TypeDeclarationContext _localctx = new TypeDeclarationContext(_ctx, getState()); - enterRule(_localctx, 8, RULE_typeDeclaration); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(126); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==SHIP || _la==LOCAL) { - { - setState(125); - modifiers(); - } - } - - setState(128); - match(ID); - setState(131); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==EXTEND) { - { - setState(129); - match(EXTEND); - setState(130); - qualifiedName(); - } - } - - setState(133); - match(LBRACE); - setState(137); - _errHandler.sync(this); - _la = _input.LA(1); - while ((((_la) & ~0x3f) == 0 && ((1L << _la) & 37383864984966L) != 0)) { - { - { - setState(134); - typeBody(); - } - } - setState(139); - _errHandler.sync(this); - _la = _input.LA(1); - } - setState(140); - match(RBRACE); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ModifiersContext extends ParserRuleContext { - public TerminalNode SHIP() { return getToken(CoderiveParser.SHIP, 0); } - public TerminalNode LOCAL() { return getToken(CoderiveParser.LOCAL, 0); } - public ModifiersContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_modifiers; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterModifiers(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitModifiers(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitModifiers(this); - else return visitor.visitChildren(this); - } - } - - public final ModifiersContext modifiers() throws RecognitionException { - ModifiersContext _localctx = new ModifiersContext(_ctx, getState()); - enterRule(_localctx, 10, RULE_modifiers); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(142); - _la = _input.LA(1); - if ( !(_la==SHIP || _la==LOCAL) ) { - _errHandler.recoverInline(this); - } - else { - if ( _input.LA(1)==Token.EOF ) matchedEOF = true; - _errHandler.reportMatch(this); - consume(); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class TypeBodyContext extends ParserRuleContext { - public FieldDeclarationContext fieldDeclaration() { - return getRuleContext(FieldDeclarationContext.class,0); - } - public ConstructorContext constructor() { - return getRuleContext(ConstructorContext.class,0); - } - public MethodDeclarationContext methodDeclaration() { - return getRuleContext(MethodDeclarationContext.class,0); - } - public StatementContext statement() { - return getRuleContext(StatementContext.class,0); - } - public TypeBodyContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_typeBody; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterTypeBody(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitTypeBody(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitTypeBody(this); - else return visitor.visitChildren(this); - } - } - - public final TypeBodyContext typeBody() throws RecognitionException { - TypeBodyContext _localctx = new TypeBodyContext(_ctx, getState()); - enterRule(_localctx, 12, RULE_typeBody); - try { - setState(148); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,8,_ctx) ) { - case 1: - enterOuterAlt(_localctx, 1); - { - setState(144); - fieldDeclaration(); - } - break; - case 2: - enterOuterAlt(_localctx, 2); - { - setState(145); - constructor(); - } - break; - case 3: - enterOuterAlt(_localctx, 3); - { - setState(146); - methodDeclaration(); - } - break; - case 4: - enterOuterAlt(_localctx, 4); - { - setState(147); - statement(); - } - break; - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class FieldDeclarationContext extends ParserRuleContext { - public TypeContext type() { - return getRuleContext(TypeContext.class,0); - } - public TerminalNode ID() { return getToken(CoderiveParser.ID, 0); } - public TerminalNode ASSIGN() { return getToken(CoderiveParser.ASSIGN, 0); } - public ExprContext expr() { - return getRuleContext(ExprContext.class,0); - } - public FieldDeclarationContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_fieldDeclaration; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterFieldDeclaration(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitFieldDeclaration(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitFieldDeclaration(this); - else return visitor.visitChildren(this); - } - } - - public final FieldDeclarationContext fieldDeclaration() throws RecognitionException { - FieldDeclarationContext _localctx = new FieldDeclarationContext(_ctx, getState()); - enterRule(_localctx, 14, RULE_fieldDeclaration); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(150); - type(); - setState(151); - match(ID); - setState(154); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==ASSIGN) { - { - setState(152); - match(ASSIGN); - setState(153); - expr(0); - } - } - - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ConstructorContext extends ParserRuleContext { - public TerminalNode ID() { return getToken(CoderiveParser.ID, 0); } - public TerminalNode LPAREN() { return getToken(CoderiveParser.LPAREN, 0); } - public TerminalNode RPAREN() { return getToken(CoderiveParser.RPAREN, 0); } - public TerminalNode LBRACE() { return getToken(CoderiveParser.LBRACE, 0); } - public TerminalNode RBRACE() { return getToken(CoderiveParser.RBRACE, 0); } - public ParameterListContext parameterList() { - return getRuleContext(ParameterListContext.class,0); - } - public List statement() { - return getRuleContexts(StatementContext.class); - } - public StatementContext statement(int i) { - return getRuleContext(StatementContext.class,i); - } - public ConstructorContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_constructor; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterConstructor(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitConstructor(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitConstructor(this); - else return visitor.visitChildren(this); - } - } - - public final ConstructorContext constructor() throws RecognitionException { - ConstructorContext _localctx = new ConstructorContext(_ctx, getState()); - enterRule(_localctx, 16, RULE_constructor); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(156); - match(ID); - setState(157); - match(LPAREN); - setState(159); - _errHandler.sync(this); - _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & 35520512L) != 0)) { - { - setState(158); - parameterList(); - } - } - - setState(161); - match(RPAREN); - setState(162); - match(LBRACE); - setState(166); - _errHandler.sync(this); - _la = _input.LA(1); - while ((((_la) & ~0x3f) == 0 && ((1L << _la) & 37383864984960L) != 0)) { - { - { - setState(163); - statement(); - } - } - setState(168); - _errHandler.sync(this); - _la = _input.LA(1); - } - setState(169); - match(RBRACE); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class MethodDeclarationContext extends ParserRuleContext { - public TerminalNode ID() { return getToken(CoderiveParser.ID, 0); } - public TerminalNode LPAREN() { return getToken(CoderiveParser.LPAREN, 0); } - public TerminalNode RPAREN() { return getToken(CoderiveParser.RPAREN, 0); } - public TerminalNode LBRACE() { return getToken(CoderiveParser.LBRACE, 0); } - public TerminalNode RBRACE() { return getToken(CoderiveParser.RBRACE, 0); } - public SlotListContext slotList() { - return getRuleContext(SlotListContext.class,0); - } - public ParameterListContext parameterList() { - return getRuleContext(ParameterListContext.class,0); - } - public List statement() { - return getRuleContexts(StatementContext.class); - } - public StatementContext statement(int i) { - return getRuleContext(StatementContext.class,i); - } - public ModifiersContext modifiers() { - return getRuleContext(ModifiersContext.class,0); - } - public MethodDeclarationContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_methodDeclaration; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterMethodDeclaration(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitMethodDeclaration(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitMethodDeclaration(this); - else return visitor.visitChildren(this); - } - } - - public final MethodDeclarationContext methodDeclaration() throws RecognitionException { - MethodDeclarationContext _localctx = new MethodDeclarationContext(_ctx, getState()); - enterRule(_localctx, 18, RULE_methodDeclaration); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(181); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,15,_ctx) ) { - case 1: - { - setState(171); - slotList(); - setState(173); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==SHIP || _la==LOCAL) { - { - setState(172); - modifiers(); - } - } - - } - break; - case 2: - { - setState(176); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==SHIP || _la==LOCAL) { - { - setState(175); - modifiers(); - } - } - - setState(179); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==LBRACKET) { - { - setState(178); - slotList(); - } - } - - } - break; - } - setState(183); - match(ID); - setState(184); - match(LPAREN); - setState(186); - _errHandler.sync(this); - _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & 35520512L) != 0)) { - { - setState(185); - parameterList(); - } - } - - setState(188); - match(RPAREN); - setState(189); - match(LBRACE); - setState(193); - _errHandler.sync(this); - _la = _input.LA(1); - while ((((_la) & ~0x3f) == 0 && ((1L << _la) & 37383864984960L) != 0)) { - { - { - setState(190); - statement(); - } - } - setState(195); - _errHandler.sync(this); - _la = _input.LA(1); - } - setState(196); - match(RBRACE); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class IdListContext extends ParserRuleContext { - public List ID() { return getTokens(CoderiveParser.ID); } - public TerminalNode ID(int i) { - return getToken(CoderiveParser.ID, i); - } - public List COMMA() { return getTokens(CoderiveParser.COMMA); } - public TerminalNode COMMA(int i) { - return getToken(CoderiveParser.COMMA, i); - } - public IdListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_idList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterIdList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitIdList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitIdList(this); - else return visitor.visitChildren(this); - } - } - - public final IdListContext idList() throws RecognitionException { - IdListContext _localctx = new IdListContext(_ctx, getState()); - enterRule(_localctx, 20, RULE_idList); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(198); - match(ID); - setState(201); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,18,_ctx) ) { - case 1: - { - setState(199); - match(COMMA); - setState(200); - match(ID); - } - break; - } - setState(205); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==COMMA) { - { - setState(203); - match(COMMA); - setState(204); - match(ID); - } - } - - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class SlotListContext extends ParserRuleContext { - public TerminalNode LBRACKET() { return getToken(CoderiveParser.LBRACKET, 0); } - public IdListContext idList() { - return getRuleContext(IdListContext.class,0); - } - public TerminalNode RBRACKET() { return getToken(CoderiveParser.RBRACKET, 0); } - public SlotListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_slotList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterSlotList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitSlotList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitSlotList(this); - else return visitor.visitChildren(this); - } - } - - public final SlotListContext slotList() throws RecognitionException { - SlotListContext _localctx = new SlotListContext(_ctx, getState()); - enterRule(_localctx, 22, RULE_slotList); - try { - enterOuterAlt(_localctx, 1); - { - setState(207); - match(LBRACKET); - setState(208); - idList(); - setState(209); - match(RBRACKET); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ParameterListContext extends ParserRuleContext { - public List parameter() { - return getRuleContexts(ParameterContext.class); - } - public ParameterContext parameter(int i) { - return getRuleContext(ParameterContext.class,i); - } - public List COMMA() { return getTokens(CoderiveParser.COMMA); } - public TerminalNode COMMA(int i) { - return getToken(CoderiveParser.COMMA, i); - } - public ParameterListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_parameterList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterParameterList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitParameterList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitParameterList(this); - else return visitor.visitChildren(this); - } - } - - public final ParameterListContext parameterList() throws RecognitionException { - ParameterListContext _localctx = new ParameterListContext(_ctx, getState()); - enterRule(_localctx, 24, RULE_parameterList); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(211); - parameter(); - setState(216); - _errHandler.sync(this); - _la = _input.LA(1); - while (_la==COMMA) { - { - { - setState(212); - match(COMMA); - setState(213); - parameter(); - } - } - setState(218); - _errHandler.sync(this); - _la = _input.LA(1); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ParameterContext extends ParserRuleContext { - public TypeContext type() { - return getRuleContext(TypeContext.class,0); - } - public TerminalNode ID() { return getToken(CoderiveParser.ID, 0); } - public ParameterContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_parameter; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterParameter(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitParameter(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitParameter(this); - else return visitor.visitChildren(this); - } - } - - public final ParameterContext parameter() throws RecognitionException { - ParameterContext _localctx = new ParameterContext(_ctx, getState()); - enterRule(_localctx, 26, RULE_parameter); - try { - enterOuterAlt(_localctx, 1); - { - setState(219); - type(); - setState(220); - match(ID); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class TypeContext extends ParserRuleContext { - public SimpleTypeContext simpleType() { - return getRuleContext(SimpleTypeContext.class,0); - } - public List LBRACKET() { return getTokens(CoderiveParser.LBRACKET); } - public TerminalNode LBRACKET(int i) { - return getToken(CoderiveParser.LBRACKET, i); - } - public List RBRACKET() { return getTokens(CoderiveParser.RBRACKET); } - public TerminalNode RBRACKET(int i) { - return getToken(CoderiveParser.RBRACKET, i); - } - public TypeContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_type; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterType(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitType(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitType(this); - else return visitor.visitChildren(this); - } - } - - public final TypeContext type() throws RecognitionException { - TypeContext _localctx = new TypeContext(_ctx, getState()); - enterRule(_localctx, 28, RULE_type); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(222); - simpleType(); - setState(227); - _errHandler.sync(this); - _la = _input.LA(1); - while (_la==LBRACKET) { - { - { - setState(223); - match(LBRACKET); - setState(224); - match(RBRACKET); - } - } - setState(229); - _errHandler.sync(this); - _la = _input.LA(1); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class SimpleTypeContext extends ParserRuleContext { - public TerminalNode INT() { return getToken(CoderiveParser.INT, 0); } - public TerminalNode STRING() { return getToken(CoderiveParser.STRING, 0); } - public TerminalNode FLOAT() { return getToken(CoderiveParser.FLOAT, 0); } - public TerminalNode BOOL() { return getToken(CoderiveParser.BOOL, 0); } - public TerminalNode ID() { return getToken(CoderiveParser.ID, 0); } - public SimpleTypeContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_simpleType; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterSimpleType(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitSimpleType(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitSimpleType(this); - else return visitor.visitChildren(this); - } - } - - public final SimpleTypeContext simpleType() throws RecognitionException { - SimpleTypeContext _localctx = new SimpleTypeContext(_ctx, getState()); - enterRule(_localctx, 30, RULE_simpleType); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(230); - _la = _input.LA(1); - if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 35520512L) != 0)) ) { - _errHandler.recoverInline(this); - } - else { - if ( _input.LA(1)==Token.EOF ) matchedEOF = true; - _errHandler.reportMatch(this); - consume(); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class StatementContext extends ParserRuleContext { - public VariableDeclarationContext variableDeclaration() { - return getRuleContext(VariableDeclarationContext.class,0); - } - public AssignmentContext assignment() { - return getRuleContext(AssignmentContext.class,0); - } - public ReturnSlotAssignmentContext returnSlotAssignment() { - return getRuleContext(ReturnSlotAssignmentContext.class,0); - } - public InputAssignmentContext inputAssignment() { - return getRuleContext(InputAssignmentContext.class,0); - } - public MethodCallStatementContext methodCallStatement() { - return getRuleContext(MethodCallStatementContext.class,0); - } - public OutputStatementContext outputStatement() { - return getRuleContext(OutputStatementContext.class,0); - } - public IfStatementContext ifStatement() { - return getRuleContext(IfStatementContext.class,0); - } - public ForStatementContext forStatement() { - return getRuleContext(ForStatementContext.class,0); - } - public ExpressionStatementContext expressionStatement() { - return getRuleContext(ExpressionStatementContext.class,0); - } - public SlotMethodCallStatementContext slotMethodCallStatement() { - return getRuleContext(SlotMethodCallStatementContext.class,0); - } - public StatementContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_statement; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterStatement(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitStatement(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitStatement(this); - else return visitor.visitChildren(this); - } - } - - public final StatementContext statement() throws RecognitionException { - StatementContext _localctx = new StatementContext(_ctx, getState()); - enterRule(_localctx, 32, RULE_statement); - try { - setState(242); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,22,_ctx) ) { - case 1: - enterOuterAlt(_localctx, 1); - { - setState(232); - variableDeclaration(); - } - break; - case 2: - enterOuterAlt(_localctx, 2); - { - setState(233); - assignment(); - } - break; - case 3: - enterOuterAlt(_localctx, 3); - { - setState(234); - returnSlotAssignment(); - } - break; - case 4: - enterOuterAlt(_localctx, 4); - { - setState(235); - inputAssignment(); - } - break; - case 5: - enterOuterAlt(_localctx, 5); - { - setState(236); - methodCallStatement(); - } - break; - case 6: - enterOuterAlt(_localctx, 6); - { - setState(237); - outputStatement(); - } - break; - case 7: - enterOuterAlt(_localctx, 7); - { - setState(238); - ifStatement(); - } - break; - case 8: - enterOuterAlt(_localctx, 8); - { - setState(239); - forStatement(); - } - break; - case 9: - enterOuterAlt(_localctx, 9); - { - setState(240); - expressionStatement(); - } - break; - case 10: - enterOuterAlt(_localctx, 10); - { - setState(241); - slotMethodCallStatement(); - } - break; - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class VariableDeclarationContext extends ParserRuleContext { - public TerminalNode ID() { return getToken(CoderiveParser.ID, 0); } - public TerminalNode VAR() { return getToken(CoderiveParser.VAR, 0); } - public TypeContext type() { - return getRuleContext(TypeContext.class,0); - } - public TerminalNode ASSIGN() { return getToken(CoderiveParser.ASSIGN, 0); } - public ExprContext expr() { - return getRuleContext(ExprContext.class,0); - } - public VariableDeclarationContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_variableDeclaration; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterVariableDeclaration(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitVariableDeclaration(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitVariableDeclaration(this); - else return visitor.visitChildren(this); - } - } - - public final VariableDeclarationContext variableDeclaration() throws RecognitionException { - VariableDeclarationContext _localctx = new VariableDeclarationContext(_ctx, getState()); - enterRule(_localctx, 34, RULE_variableDeclaration); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(246); - _errHandler.sync(this); - switch (_input.LA(1)) { - case VAR: - { - setState(244); - match(VAR); - } - break; - case INT: - case STRING: - case FLOAT: - case BOOL: - case ID: - { - setState(245); - type(); - } - break; - default: - throw new NoViableAltException(this); - } - setState(248); - match(ID); - setState(251); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==ASSIGN) { - { - setState(249); - match(ASSIGN); - setState(250); - expr(0); - } - } - - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class AssignmentContext extends ParserRuleContext { - public AssignableContext assignable() { - return getRuleContext(AssignableContext.class,0); - } - public TerminalNode ASSIGN() { return getToken(CoderiveParser.ASSIGN, 0); } - public ExprContext expr() { - return getRuleContext(ExprContext.class,0); - } - public AssignmentContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_assignment; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterAssignment(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitAssignment(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitAssignment(this); - else return visitor.visitChildren(this); - } - } - - public final AssignmentContext assignment() throws RecognitionException { - AssignmentContext _localctx = new AssignmentContext(_ctx, getState()); - enterRule(_localctx, 36, RULE_assignment); - try { - enterOuterAlt(_localctx, 1); - { - setState(253); - assignable(); - setState(254); - match(ASSIGN); - setState(255); - expr(0); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ExpressionStatementContext extends ParserRuleContext { - public ExprContext expr() { - return getRuleContext(ExprContext.class,0); - } - public ExpressionStatementContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_expressionStatement; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterExpressionStatement(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitExpressionStatement(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitExpressionStatement(this); - else return visitor.visitChildren(this); - } - } - - public final ExpressionStatementContext expressionStatement() throws RecognitionException { - ExpressionStatementContext _localctx = new ExpressionStatementContext(_ctx, getState()); - enterRule(_localctx, 38, RULE_expressionStatement); - try { - enterOuterAlt(_localctx, 1); - { - setState(257); - expr(0); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class AssignableContext extends ParserRuleContext { - public TerminalNode ID() { return getToken(CoderiveParser.ID, 0); } - public IndexAccessContext indexAccess() { - return getRuleContext(IndexAccessContext.class,0); - } - public AssignableContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_assignable; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterAssignable(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitAssignable(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitAssignable(this); - else return visitor.visitChildren(this); - } - } - - public final AssignableContext assignable() throws RecognitionException { - AssignableContext _localctx = new AssignableContext(_ctx, getState()); - enterRule(_localctx, 40, RULE_assignable); - try { - setState(261); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,25,_ctx) ) { - case 1: - enterOuterAlt(_localctx, 1); - { - setState(259); - match(ID); - } - break; - case 2: - enterOuterAlt(_localctx, 2); - { - setState(260); - indexAccess(); - } - break; - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ReturnSlotAssignmentContext extends ParserRuleContext { - public AssignableListContext assignableList() { - return getRuleContext(AssignableListContext.class,0); - } - public TerminalNode ASSIGN() { return getToken(CoderiveParser.ASSIGN, 0); } - public SlotMethodCallContext slotMethodCall() { - return getRuleContext(SlotMethodCallContext.class,0); - } - public ReturnSlotAssignmentContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_returnSlotAssignment; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterReturnSlotAssignment(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitReturnSlotAssignment(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitReturnSlotAssignment(this); - else return visitor.visitChildren(this); - } - } - - public final ReturnSlotAssignmentContext returnSlotAssignment() throws RecognitionException { - ReturnSlotAssignmentContext _localctx = new ReturnSlotAssignmentContext(_ctx, getState()); - enterRule(_localctx, 42, RULE_returnSlotAssignment); - try { - enterOuterAlt(_localctx, 1); - { - setState(263); - assignableList(); - setState(264); - match(ASSIGN); - setState(265); - slotMethodCall(); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class AssignableListContext extends ParserRuleContext { - public IdListContext idList() { - return getRuleContext(IdListContext.class,0); - } - public AssignableListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_assignableList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterAssignableList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitAssignableList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitAssignableList(this); - else return visitor.visitChildren(this); - } - } - - public final AssignableListContext assignableList() throws RecognitionException { - AssignableListContext _localctx = new AssignableListContext(_ctx, getState()); - enterRule(_localctx, 44, RULE_assignableList); - try { - enterOuterAlt(_localctx, 1); - { - setState(267); - idList(); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class SlotMethodCallStatementContext extends ParserRuleContext { - public SlotMethodCallContext slotMethodCall() { - return getRuleContext(SlotMethodCallContext.class,0); - } - public SlotMethodCallStatementContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_slotMethodCallStatement; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterSlotMethodCallStatement(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitSlotMethodCallStatement(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitSlotMethodCallStatement(this); - else return visitor.visitChildren(this); - } - } - - public final SlotMethodCallStatementContext slotMethodCallStatement() throws RecognitionException { - SlotMethodCallStatementContext _localctx = new SlotMethodCallStatementContext(_ctx, getState()); - enterRule(_localctx, 46, RULE_slotMethodCallStatement); - try { - enterOuterAlt(_localctx, 1); - { - setState(269); - slotMethodCall(); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class SlotMethodCallContext extends ParserRuleContext { - public SlotCastContext slotCast() { - return getRuleContext(SlotCastContext.class,0); - } - public MethodCallContext methodCall() { - return getRuleContext(MethodCallContext.class,0); - } - public SlotMethodCallContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_slotMethodCall; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterSlotMethodCall(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitSlotMethodCall(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitSlotMethodCall(this); - else return visitor.visitChildren(this); - } - } - - public final SlotMethodCallContext slotMethodCall() throws RecognitionException { - SlotMethodCallContext _localctx = new SlotMethodCallContext(_ctx, getState()); - enterRule(_localctx, 48, RULE_slotMethodCall); - try { - enterOuterAlt(_localctx, 1); - { - setState(271); - slotCast(); - setState(272); - methodCall(); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class SlotCastContext extends ParserRuleContext { - public TerminalNode LBRACKET() { return getToken(CoderiveParser.LBRACKET, 0); } - public IdListContext idList() { - return getRuleContext(IdListContext.class,0); - } - public TerminalNode RBRACKET() { return getToken(CoderiveParser.RBRACKET, 0); } - public TerminalNode COLON() { return getToken(CoderiveParser.COLON, 0); } - public SlotCastContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_slotCast; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterSlotCast(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitSlotCast(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitSlotCast(this); - else return visitor.visitChildren(this); - } - } - - public final SlotCastContext slotCast() throws RecognitionException { - SlotCastContext _localctx = new SlotCastContext(_ctx, getState()); - enterRule(_localctx, 50, RULE_slotCast); - try { - enterOuterAlt(_localctx, 1); - { - setState(274); - match(LBRACKET); - setState(275); - idList(); - setState(276); - match(RBRACKET); - setState(277); - match(COLON); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class InputAssignmentContext extends ParserRuleContext { - public AssignableContext assignable() { - return getRuleContext(AssignableContext.class,0); - } - public TerminalNode ASSIGN() { return getToken(CoderiveParser.ASSIGN, 0); } - public InputStatementContext inputStatement() { - return getRuleContext(InputStatementContext.class,0); - } - public InputAssignmentContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_inputAssignment; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterInputAssignment(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitInputAssignment(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitInputAssignment(this); - else return visitor.visitChildren(this); - } - } - - public final InputAssignmentContext inputAssignment() throws RecognitionException { - InputAssignmentContext _localctx = new InputAssignmentContext(_ctx, getState()); - enterRule(_localctx, 52, RULE_inputAssignment); - try { - enterOuterAlt(_localctx, 1); - { - setState(279); - assignable(); - setState(280); - match(ASSIGN); - setState(281); - inputStatement(); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class InputStatementContext extends ParserRuleContext { - public TypeInputContext typeInput() { - return getRuleContext(TypeInputContext.class,0); - } - public TerminalNode INPUT() { return getToken(CoderiveParser.INPUT, 0); } - public InputStatementContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_inputStatement; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterInputStatement(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitInputStatement(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitInputStatement(this); - else return visitor.visitChildren(this); - } - } - - public final InputStatementContext inputStatement() throws RecognitionException { - InputStatementContext _localctx = new InputStatementContext(_ctx, getState()); - enterRule(_localctx, 54, RULE_inputStatement); - try { - enterOuterAlt(_localctx, 1); - { - setState(283); - typeInput(); - setState(284); - match(INPUT); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class TypeInputContext extends ParserRuleContext { - public TerminalNode LPAREN() { return getToken(CoderiveParser.LPAREN, 0); } - public TypeContext type() { - return getRuleContext(TypeContext.class,0); - } - public TerminalNode RPAREN() { return getToken(CoderiveParser.RPAREN, 0); } - public TypeInputContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_typeInput; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterTypeInput(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitTypeInput(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitTypeInput(this); - else return visitor.visitChildren(this); - } - } - - public final TypeInputContext typeInput() throws RecognitionException { - TypeInputContext _localctx = new TypeInputContext(_ctx, getState()); - enterRule(_localctx, 56, RULE_typeInput); - try { - enterOuterAlt(_localctx, 1); - { - setState(286); - match(LPAREN); - setState(287); - type(); - setState(288); - match(RPAREN); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class OutputStatementContext extends ParserRuleContext { - public TerminalNode OUTPUT() { return getToken(CoderiveParser.OUTPUT, 0); } - public OutputTargetContext outputTarget() { - return getRuleContext(OutputTargetContext.class,0); - } - public OutputStatementContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_outputStatement; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterOutputStatement(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitOutputStatement(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitOutputStatement(this); - else return visitor.visitChildren(this); - } - } - - public final OutputStatementContext outputStatement() throws RecognitionException { - OutputStatementContext _localctx = new OutputStatementContext(_ctx, getState()); - enterRule(_localctx, 58, RULE_outputStatement); - try { - enterOuterAlt(_localctx, 1); - { - setState(290); - match(OUTPUT); - setState(291); - outputTarget(); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class OutputTargetContext extends ParserRuleContext { - public OutputTargetContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_outputTarget; } - - public OutputTargetContext() { } - public void copyFrom(OutputTargetContext ctx) { - super.copyFrom(ctx); - } - } - @SuppressWarnings("CheckReturnValue") - public static class OutputSlotCallContext extends OutputTargetContext { - public MethodCallContext methodCall() { - return getRuleContext(MethodCallContext.class,0); - } - public SlotCastContext slotCast() { - return getRuleContext(SlotCastContext.class,0); - } - public OutputSlotCallContext(OutputTargetContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterOutputSlotCall(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitOutputSlotCall(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitOutputSlotCall(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class OutputNamedAssignmentContext extends OutputTargetContext { - public TerminalNode ID() { return getToken(CoderiveParser.ID, 0); } - public TerminalNode ASSIGN() { return getToken(CoderiveParser.ASSIGN, 0); } - public ExprContext expr() { - return getRuleContext(ExprContext.class,0); - } - public OutputNamedAssignmentContext(OutputTargetContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterOutputNamedAssignment(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitOutputNamedAssignment(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitOutputNamedAssignment(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class OutputExpressionContext extends OutputTargetContext { - public ExprContext expr() { - return getRuleContext(ExprContext.class,0); - } - public OutputExpressionContext(OutputTargetContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterOutputExpression(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitOutputExpression(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitOutputExpression(this); - else return visitor.visitChildren(this); - } - } - - public final OutputTargetContext outputTarget() throws RecognitionException { - OutputTargetContext _localctx = new OutputTargetContext(_ctx, getState()); - enterRule(_localctx, 60, RULE_outputTarget); - int _la; - try { - setState(301); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,27,_ctx) ) { - case 1: - _localctx = new OutputSlotCallContext(_localctx); - enterOuterAlt(_localctx, 1); - { - setState(294); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==LBRACKET) { - { - setState(293); - slotCast(); - } - } - - setState(296); - methodCall(); - } - break; - case 2: - _localctx = new OutputNamedAssignmentContext(_localctx); - enterOuterAlt(_localctx, 2); - { - setState(297); - match(ID); - setState(298); - match(ASSIGN); - setState(299); - expr(0); - } - break; - case 3: - _localctx = new OutputExpressionContext(_localctx); - enterOuterAlt(_localctx, 3); - { - setState(300); - expr(0); - } - break; - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class MethodCallStatementContext extends ParserRuleContext { - public MethodCallContext methodCall() { - return getRuleContext(MethodCallContext.class,0); - } - public MethodCallStatementContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_methodCallStatement; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterMethodCallStatement(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitMethodCallStatement(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitMethodCallStatement(this); - else return visitor.visitChildren(this); - } - } - - public final MethodCallStatementContext methodCallStatement() throws RecognitionException { - MethodCallStatementContext _localctx = new MethodCallStatementContext(_ctx, getState()); - enterRule(_localctx, 62, RULE_methodCallStatement); - try { - enterOuterAlt(_localctx, 1); - { - setState(303); - methodCall(); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class MethodCallContext extends ParserRuleContext { - public TerminalNode ID() { return getToken(CoderiveParser.ID, 0); } - public TerminalNode LPAREN() { return getToken(CoderiveParser.LPAREN, 0); } - public TerminalNode RPAREN() { return getToken(CoderiveParser.RPAREN, 0); } - public QualifiedNameContext qualifiedName() { - return getRuleContext(QualifiedNameContext.class,0); - } - public TerminalNode DOT() { return getToken(CoderiveParser.DOT, 0); } - public ArgumentListContext argumentList() { - return getRuleContext(ArgumentListContext.class,0); - } - public MethodCallContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_methodCall; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterMethodCall(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitMethodCall(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitMethodCall(this); - else return visitor.visitChildren(this); - } - } - - public final MethodCallContext methodCall() throws RecognitionException { - MethodCallContext _localctx = new MethodCallContext(_ctx, getState()); - enterRule(_localctx, 64, RULE_methodCall); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(308); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,28,_ctx) ) { - case 1: - { - setState(305); - qualifiedName(); - setState(306); - match(DOT); - } - break; - } - setState(310); - match(ID); - setState(311); - match(LPAREN); - setState(313); - _errHandler.sync(this); - _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & 37383863009280L) != 0)) { - { - setState(312); - argumentList(); - } - } - - setState(315); - match(RPAREN); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ArgumentListContext extends ParserRuleContext { - public List expr() { - return getRuleContexts(ExprContext.class); - } - public ExprContext expr(int i) { - return getRuleContext(ExprContext.class,i); - } - public List COMMA() { return getTokens(CoderiveParser.COMMA); } - public TerminalNode COMMA(int i) { - return getToken(CoderiveParser.COMMA, i); - } - public ArgumentListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_argumentList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterArgumentList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitArgumentList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitArgumentList(this); - else return visitor.visitChildren(this); - } - } - - public final ArgumentListContext argumentList() throws RecognitionException { - ArgumentListContext _localctx = new ArgumentListContext(_ctx, getState()); - enterRule(_localctx, 66, RULE_argumentList); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(317); - expr(0); - setState(322); - _errHandler.sync(this); - _la = _input.LA(1); - while (_la==COMMA) { - { - { - setState(318); - match(COMMA); - setState(319); - expr(0); - } - } - setState(324); - _errHandler.sync(this); - _la = _input.LA(1); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class IfStatementContext extends ParserRuleContext { - public TerminalNode IF() { return getToken(CoderiveParser.IF, 0); } - public List expr() { - return getRuleContexts(ExprContext.class); - } - public ExprContext expr(int i) { - return getRuleContext(ExprContext.class,i); - } - public List thenBlock() { - return getRuleContexts(ThenBlockContext.class); - } - public ThenBlockContext thenBlock(int i) { - return getRuleContext(ThenBlockContext.class,i); - } - public List ELIF() { return getTokens(CoderiveParser.ELIF); } - public TerminalNode ELIF(int i) { - return getToken(CoderiveParser.ELIF, i); - } - public TerminalNode ELSE() { return getToken(CoderiveParser.ELSE, 0); } - public IfStatementContext ifStatement() { - return getRuleContext(IfStatementContext.class,0); - } - public ElseBlockContext elseBlock() { - return getRuleContext(ElseBlockContext.class,0); - } - public IfStatementContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_ifStatement; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterIfStatement(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitIfStatement(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitIfStatement(this); - else return visitor.visitChildren(this); - } - } - - public final IfStatementContext ifStatement() throws RecognitionException { - IfStatementContext _localctx = new IfStatementContext(_ctx, getState()); - enterRule(_localctx, 68, RULE_ifStatement); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(325); - match(IF); - setState(326); - expr(0); - setState(327); - thenBlock(); - setState(334); - _errHandler.sync(this); - _la = _input.LA(1); - while (_la==ELIF) { - { - { - setState(328); - match(ELIF); - setState(329); - expr(0); - setState(330); - thenBlock(); - } - } - setState(336); - _errHandler.sync(this); - _la = _input.LA(1); - } - setState(342); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==ELSE) { - { - setState(337); - match(ELSE); - setState(340); - _errHandler.sync(this); - switch (_input.LA(1)) { - case IF: - { - setState(338); - ifStatement(); - } - break; - case LBRACE: - { - setState(339); - elseBlock(); - } - break; - default: - throw new NoViableAltException(this); - } - } - } - - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ThenBlockContext extends ParserRuleContext { - public TerminalNode LBRACE() { return getToken(CoderiveParser.LBRACE, 0); } - public TerminalNode RBRACE() { return getToken(CoderiveParser.RBRACE, 0); } - public List statement() { - return getRuleContexts(StatementContext.class); - } - public StatementContext statement(int i) { - return getRuleContext(StatementContext.class,i); - } - public ThenBlockContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_thenBlock; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterThenBlock(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitThenBlock(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitThenBlock(this); - else return visitor.visitChildren(this); - } - } - - public final ThenBlockContext thenBlock() throws RecognitionException { - ThenBlockContext _localctx = new ThenBlockContext(_ctx, getState()); - enterRule(_localctx, 70, RULE_thenBlock); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(344); - match(LBRACE); - setState(348); - _errHandler.sync(this); - _la = _input.LA(1); - while ((((_la) & ~0x3f) == 0 && ((1L << _la) & 37383864984960L) != 0)) { - { - { - setState(345); - statement(); - } - } - setState(350); - _errHandler.sync(this); - _la = _input.LA(1); - } - setState(351); - match(RBRACE); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ElseBlockContext extends ParserRuleContext { - public TerminalNode LBRACE() { return getToken(CoderiveParser.LBRACE, 0); } - public TerminalNode RBRACE() { return getToken(CoderiveParser.RBRACE, 0); } - public List statement() { - return getRuleContexts(StatementContext.class); - } - public StatementContext statement(int i) { - return getRuleContext(StatementContext.class,i); - } - public ElseBlockContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_elseBlock; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterElseBlock(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitElseBlock(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitElseBlock(this); - else return visitor.visitChildren(this); - } - } - - public final ElseBlockContext elseBlock() throws RecognitionException { - ElseBlockContext _localctx = new ElseBlockContext(_ctx, getState()); - enterRule(_localctx, 72, RULE_elseBlock); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(353); - match(LBRACE); - setState(357); - _errHandler.sync(this); - _la = _input.LA(1); - while ((((_la) & ~0x3f) == 0 && ((1L << _la) & 37383864984960L) != 0)) { - { - { - setState(354); - statement(); - } - } - setState(359); - _errHandler.sync(this); - _la = _input.LA(1); - } - setState(360); - match(RBRACE); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ForStatementContext extends ParserRuleContext { - public TerminalNode FOR() { return getToken(CoderiveParser.FOR, 0); } - public TerminalNode ID() { return getToken(CoderiveParser.ID, 0); } - public TerminalNode IN() { return getToken(CoderiveParser.IN, 0); } - public List expr() { - return getRuleContexts(ExprContext.class); - } - public ExprContext expr(int i) { - return getRuleContext(ExprContext.class,i); - } - public TerminalNode TO() { return getToken(CoderiveParser.TO, 0); } - public TerminalNode LBRACE() { return getToken(CoderiveParser.LBRACE, 0); } - public TerminalNode RBRACE() { return getToken(CoderiveParser.RBRACE, 0); } - public TerminalNode BY() { return getToken(CoderiveParser.BY, 0); } - public ForStepExprContext forStepExpr() { - return getRuleContext(ForStepExprContext.class,0); - } - public List statement() { - return getRuleContexts(StatementContext.class); - } - public StatementContext statement(int i) { - return getRuleContext(StatementContext.class,i); - } - public ForStatementContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_forStatement; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterForStatement(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitForStatement(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitForStatement(this); - else return visitor.visitChildren(this); - } - } - - public final ForStatementContext forStatement() throws RecognitionException { - ForStatementContext _localctx = new ForStatementContext(_ctx, getState()); - enterRule(_localctx, 74, RULE_forStatement); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(362); - match(FOR); - setState(363); - match(ID); - setState(366); - _errHandler.sync(this); - _la = _input.LA(1); - if (_la==BY) { - { - setState(364); - match(BY); - setState(365); - forStepExpr(); - } - } - - setState(368); - match(IN); - setState(369); - expr(0); - setState(370); - match(TO); - setState(371); - expr(0); - setState(372); - match(LBRACE); - setState(376); - _errHandler.sync(this); - _la = _input.LA(1); - while ((((_la) & ~0x3f) == 0 && ((1L << _la) & 37383864984960L) != 0)) { - { - { - setState(373); - statement(); - } - } - setState(378); - _errHandler.sync(this); - _la = _input.LA(1); - } - setState(379); - match(RBRACE); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ForStepExprContext extends ParserRuleContext { - public ForStepExprContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_forStepExpr; } - - public ForStepExprContext() { } - public void copyFrom(ForStepExprContext ctx) { - super.copyFrom(ctx); - } - } - @SuppressWarnings("CheckReturnValue") - public static class RegularStepContext extends ForStepExprContext { - public ExprContext expr() { - return getRuleContext(ExprContext.class,0); - } - public RegularStepContext(ForStepExprContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterRegularStep(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitRegularStep(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitRegularStep(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class OperatorStepContext extends ForStepExprContext { - public ExprContext expr() { - return getRuleContext(ExprContext.class,0); - } - public TerminalNode MUL() { return getToken(CoderiveParser.MUL, 0); } - public TerminalNode DIV() { return getToken(CoderiveParser.DIV, 0); } - public TerminalNode PLUS() { return getToken(CoderiveParser.PLUS, 0); } - public TerminalNode MINUS() { return getToken(CoderiveParser.MINUS, 0); } - public OperatorStepContext(ForStepExprContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterOperatorStep(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitOperatorStep(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitOperatorStep(this); - else return visitor.visitChildren(this); - } - } - - public final ForStepExprContext forStepExpr() throws RecognitionException { - ForStepExprContext _localctx = new ForStepExprContext(_ctx, getState()); - enterRule(_localctx, 76, RULE_forStepExpr); - int _la; - try { - setState(384); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,38,_ctx) ) { - case 1: - _localctx = new OperatorStepContext(_localctx); - enterOuterAlt(_localctx, 1); - { - setState(381); - _la = _input.LA(1); - if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 2013265920L) != 0)) ) { - _errHandler.recoverInline(this); - } - else { - if ( _input.LA(1)==Token.EOF ) matchedEOF = true; - _errHandler.reportMatch(this); - consume(); - } - setState(382); - expr(0); - } - break; - case 2: - _localctx = new RegularStepContext(_localctx); - enterOuterAlt(_localctx, 2); - { - setState(383); - expr(0); - } - break; - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ExprContext extends ParserRuleContext { - public ExprContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_expr; } - - public ExprContext() { } - public void copyFrom(ExprContext ctx) { - super.copyFrom(ctx); - } - } - @SuppressWarnings("CheckReturnValue") - public static class MethodCallExprContext extends ExprContext { - public MethodCallContext methodCall() { - return getRuleContext(MethodCallContext.class,0); - } - public MethodCallExprContext(ExprContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterMethodCallExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitMethodCallExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitMethodCallExpr(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class UnaryExprContext extends ExprContext { - public ExprContext expr() { - return getRuleContext(ExprContext.class,0); - } - public TerminalNode PLUS() { return getToken(CoderiveParser.PLUS, 0); } - public TerminalNode MINUS() { return getToken(CoderiveParser.MINUS, 0); } - public UnaryExprContext(ExprContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterUnaryExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitUnaryExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitUnaryExpr(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class PrimaryExprContext extends ExprContext { - public PrimaryContext primary() { - return getRuleContext(PrimaryContext.class,0); - } - public PrimaryExprContext(ExprContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterPrimaryExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitPrimaryExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitPrimaryExpr(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class ComparisonExprContext extends ExprContext { - public List expr() { - return getRuleContexts(ExprContext.class); - } - public ExprContext expr(int i) { - return getRuleContext(ExprContext.class,i); - } - public TerminalNode GT() { return getToken(CoderiveParser.GT, 0); } - public TerminalNode LT() { return getToken(CoderiveParser.LT, 0); } - public TerminalNode GTE() { return getToken(CoderiveParser.GTE, 0); } - public TerminalNode LTE() { return getToken(CoderiveParser.LTE, 0); } - public TerminalNode EQ() { return getToken(CoderiveParser.EQ, 0); } - public TerminalNode NEQ() { return getToken(CoderiveParser.NEQ, 0); } - public ComparisonExprContext(ExprContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterComparisonExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitComparisonExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitComparisonExpr(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class TypeCastExprContext extends ExprContext { - public TypeCastContext typeCast() { - return getRuleContext(TypeCastContext.class,0); - } - public TypeCastExprContext(ExprContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterTypeCastExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitTypeCastExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitTypeCastExpr(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class AdditiveExprContext extends ExprContext { - public List expr() { - return getRuleContexts(ExprContext.class); - } - public ExprContext expr(int i) { - return getRuleContext(ExprContext.class,i); - } - public TerminalNode PLUS() { return getToken(CoderiveParser.PLUS, 0); } - public TerminalNode MINUS() { return getToken(CoderiveParser.MINUS, 0); } - public AdditiveExprContext(ExprContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterAdditiveExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitAdditiveExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitAdditiveExpr(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class MultiplicativeExprContext extends ExprContext { - public List expr() { - return getRuleContexts(ExprContext.class); - } - public ExprContext expr(int i) { - return getRuleContext(ExprContext.class,i); - } - public TerminalNode MUL() { return getToken(CoderiveParser.MUL, 0); } - public TerminalNode DIV() { return getToken(CoderiveParser.DIV, 0); } - public TerminalNode MOD() { return getToken(CoderiveParser.MOD, 0); } - public MultiplicativeExprContext(ExprContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterMultiplicativeExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitMultiplicativeExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitMultiplicativeExpr(this); - else return visitor.visitChildren(this); - } - } - - public final ExprContext expr() throws RecognitionException { - return expr(0); - } - - private ExprContext expr(int _p) throws RecognitionException { - ParserRuleContext _parentctx = _ctx; - int _parentState = getState(); - ExprContext _localctx = new ExprContext(_ctx, _parentState); - ExprContext _prevctx = _localctx; - int _startState = 78; - enterRecursionRule(_localctx, 78, RULE_expr, _p); - int _la; - try { - int _alt; - enterOuterAlt(_localctx, 1); - { - setState(392); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,39,_ctx) ) { - case 1: - { - _localctx = new UnaryExprContext(_localctx); - _ctx = _localctx; - _prevctx = _localctx; - - setState(387); - _la = _input.LA(1); - if ( !(_la==PLUS || _la==MINUS) ) { - _errHandler.recoverInline(this); - } - else { - if ( _input.LA(1)==Token.EOF ) matchedEOF = true; - _errHandler.reportMatch(this); - consume(); - } - setState(388); - expr(7); - } - break; - case 2: - { - _localctx = new PrimaryExprContext(_localctx); - _ctx = _localctx; - _prevctx = _localctx; - setState(389); - primary(); - } - break; - case 3: - { - _localctx = new MethodCallExprContext(_localctx); - _ctx = _localctx; - _prevctx = _localctx; - setState(390); - methodCall(); - } - break; - case 4: - { - _localctx = new TypeCastExprContext(_localctx); - _ctx = _localctx; - _prevctx = _localctx; - setState(391); - typeCast(); - } - break; - } - _ctx.stop = _input.LT(-1); - setState(405); - _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,41,_ctx); - while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { - if ( _alt==1 ) { - if ( _parseListeners!=null ) triggerExitRuleEvent(); - _prevctx = _localctx; - { - setState(403); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,40,_ctx) ) { - case 1: - { - _localctx = new MultiplicativeExprContext(new ExprContext(_parentctx, _parentState)); - pushNewRecursionContext(_localctx, _startState, RULE_expr); - setState(394); - if (!(precpred(_ctx, 6))) throw new FailedPredicateException(this, "precpred(_ctx, 6)"); - setState(395); - _la = _input.LA(1); - if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 3758096384L) != 0)) ) { - _errHandler.recoverInline(this); - } - else { - if ( _input.LA(1)==Token.EOF ) matchedEOF = true; - _errHandler.reportMatch(this); - consume(); - } - setState(396); - expr(7); - } - break; - case 2: - { - _localctx = new AdditiveExprContext(new ExprContext(_parentctx, _parentState)); - pushNewRecursionContext(_localctx, _startState, RULE_expr); - setState(397); - if (!(precpred(_ctx, 5))) throw new FailedPredicateException(this, "precpred(_ctx, 5)"); - setState(398); - _la = _input.LA(1); - if ( !(_la==PLUS || _la==MINUS) ) { - _errHandler.recoverInline(this); - } - else { - if ( _input.LA(1)==Token.EOF ) matchedEOF = true; - _errHandler.reportMatch(this); - consume(); - } - setState(399); - expr(6); - } - break; - case 3: - { - _localctx = new ComparisonExprContext(new ExprContext(_parentctx, _parentState)); - pushNewRecursionContext(_localctx, _startState, RULE_expr); - setState(400); - if (!(precpred(_ctx, 4))) throw new FailedPredicateException(this, "precpred(_ctx, 4)"); - setState(401); - _la = _input.LA(1); - if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 541165879296L) != 0)) ) { - _errHandler.recoverInline(this); - } - else { - if ( _input.LA(1)==Token.EOF ) matchedEOF = true; - _errHandler.reportMatch(this); - consume(); - } - setState(402); - expr(5); - } - break; - } - } - } - setState(407); - _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,41,_ctx); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - unrollRecursionContexts(_parentctx); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class PrimaryContext extends ParserRuleContext { - public PrimaryContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_primary; } - - public PrimaryContext() { } - public void copyFrom(PrimaryContext ctx) { - super.copyFrom(ctx); - } - } - @SuppressWarnings("CheckReturnValue") - public static class IndexAccessExprContext extends PrimaryContext { - public AtomContext atom() { - return getRuleContext(AtomContext.class,0); - } - public List LBRACKET() { return getTokens(CoderiveParser.LBRACKET); } - public TerminalNode LBRACKET(int i) { - return getToken(CoderiveParser.LBRACKET, i); - } - public List expr() { - return getRuleContexts(ExprContext.class); - } - public ExprContext expr(int i) { - return getRuleContext(ExprContext.class,i); - } - public List RBRACKET() { return getTokens(CoderiveParser.RBRACKET); } - public TerminalNode RBRACKET(int i) { - return getToken(CoderiveParser.RBRACKET, i); - } - public IndexAccessExprContext(PrimaryContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterIndexAccessExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitIndexAccessExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitIndexAccessExpr(this); - else return visitor.visitChildren(this); - } - } - - public final PrimaryContext primary() throws RecognitionException { - PrimaryContext _localctx = new PrimaryContext(_ctx, getState()); - enterRule(_localctx, 80, RULE_primary); - try { - int _alt; - _localctx = new IndexAccessExprContext(_localctx); - enterOuterAlt(_localctx, 1); - { - setState(408); - atom(); - setState(415); - _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,42,_ctx); - while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { - if ( _alt==1 ) { - { - { - setState(409); - match(LBRACKET); - setState(410); - expr(0); - setState(411); - match(RBRACKET); - } - } - } - setState(417); - _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,42,_ctx); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class AtomContext extends ParserRuleContext { - public AtomContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_atom; } - - public AtomContext() { } - public void copyFrom(AtomContext ctx) { - super.copyFrom(ctx); - } - } - @SuppressWarnings("CheckReturnValue") - public static class FloatLiteralExprContext extends AtomContext { - public TerminalNode FLOAT_LIT() { return getToken(CoderiveParser.FLOAT_LIT, 0); } - public FloatLiteralExprContext(AtomContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterFloatLiteralExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitFloatLiteralExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitFloatLiteralExpr(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class StringLiteralExprContext extends AtomContext { - public TerminalNode STRING_LIT() { return getToken(CoderiveParser.STRING_LIT, 0); } - public StringLiteralExprContext(AtomContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterStringLiteralExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitStringLiteralExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitStringLiteralExpr(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class IntLiteralExprContext extends AtomContext { - public TerminalNode INT_LIT() { return getToken(CoderiveParser.INT_LIT, 0); } - public IntLiteralExprContext(AtomContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterIntLiteralExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitIntLiteralExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitIntLiteralExpr(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class ParenthesizedExprContext extends AtomContext { - public TerminalNode LPAREN() { return getToken(CoderiveParser.LPAREN, 0); } - public ExprContext expr() { - return getRuleContext(ExprContext.class,0); - } - public TerminalNode RPAREN() { return getToken(CoderiveParser.RPAREN, 0); } - public ParenthesizedExprContext(AtomContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterParenthesizedExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitParenthesizedExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitParenthesizedExpr(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class ArrayLiteralExprContext extends AtomContext { - public ArrayLiteralContext arrayLiteral() { - return getRuleContext(ArrayLiteralContext.class,0); - } - public ArrayLiteralExprContext(AtomContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterArrayLiteralExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitArrayLiteralExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitArrayLiteralExpr(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class BoolLiteralExprContext extends AtomContext { - public TerminalNode BOOL_LIT() { return getToken(CoderiveParser.BOOL_LIT, 0); } - public BoolLiteralExprContext(AtomContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterBoolLiteralExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitBoolLiteralExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitBoolLiteralExpr(this); - else return visitor.visitChildren(this); - } - } - @SuppressWarnings("CheckReturnValue") - public static class IdentifierExprContext extends AtomContext { - public TerminalNode ID() { return getToken(CoderiveParser.ID, 0); } - public IdentifierExprContext(AtomContext ctx) { copyFrom(ctx); } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterIdentifierExpr(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitIdentifierExpr(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitIdentifierExpr(this); - else return visitor.visitChildren(this); - } - } - - public final AtomContext atom() throws RecognitionException { - AtomContext _localctx = new AtomContext(_ctx, getState()); - enterRule(_localctx, 82, RULE_atom); - try { - setState(428); - _errHandler.sync(this); - switch (_input.LA(1)) { - case ID: - _localctx = new IdentifierExprContext(_localctx); - enterOuterAlt(_localctx, 1); - { - setState(418); - match(ID); - } - break; - case INT_LIT: - _localctx = new IntLiteralExprContext(_localctx); - enterOuterAlt(_localctx, 2); - { - setState(419); - match(INT_LIT); - } - break; - case FLOAT_LIT: - _localctx = new FloatLiteralExprContext(_localctx); - enterOuterAlt(_localctx, 3); - { - setState(420); - match(FLOAT_LIT); - } - break; - case STRING_LIT: - _localctx = new StringLiteralExprContext(_localctx); - enterOuterAlt(_localctx, 4); - { - setState(421); - match(STRING_LIT); - } - break; - case BOOL_LIT: - _localctx = new BoolLiteralExprContext(_localctx); - enterOuterAlt(_localctx, 5); - { - setState(422); - match(BOOL_LIT); - } - break; - case LPAREN: - _localctx = new ParenthesizedExprContext(_localctx); - enterOuterAlt(_localctx, 6); - { - setState(423); - match(LPAREN); - setState(424); - expr(0); - setState(425); - match(RPAREN); - } - break; - case LBRACKET: - _localctx = new ArrayLiteralExprContext(_localctx); - enterOuterAlt(_localctx, 7); - { - setState(427); - arrayLiteral(); - } - break; - default: - throw new NoViableAltException(this); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ArrayLiteralContext extends ParserRuleContext { - public TerminalNode LBRACKET() { return getToken(CoderiveParser.LBRACKET, 0); } - public TerminalNode RBRACKET() { return getToken(CoderiveParser.RBRACKET, 0); } - public ExprListContext exprList() { - return getRuleContext(ExprListContext.class,0); - } - public ArrayLiteralContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_arrayLiteral; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterArrayLiteral(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitArrayLiteral(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitArrayLiteral(this); - else return visitor.visitChildren(this); - } - } - - public final ArrayLiteralContext arrayLiteral() throws RecognitionException { - ArrayLiteralContext _localctx = new ArrayLiteralContext(_ctx, getState()); - enterRule(_localctx, 84, RULE_arrayLiteral); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(430); - match(LBRACKET); - setState(432); - _errHandler.sync(this); - _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & 37383863009280L) != 0)) { - { - setState(431); - exprList(); - } - } - - setState(434); - match(RBRACKET); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class ExprListContext extends ParserRuleContext { - public List expr() { - return getRuleContexts(ExprContext.class); - } - public ExprContext expr(int i) { - return getRuleContext(ExprContext.class,i); - } - public List COMMA() { return getTokens(CoderiveParser.COMMA); } - public TerminalNode COMMA(int i) { - return getToken(CoderiveParser.COMMA, i); - } - public ExprListContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_exprList; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterExprList(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitExprList(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitExprList(this); - else return visitor.visitChildren(this); - } - } - - public final ExprListContext exprList() throws RecognitionException { - ExprListContext _localctx = new ExprListContext(_ctx, getState()); - enterRule(_localctx, 86, RULE_exprList); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(436); - expr(0); - setState(441); - _errHandler.sync(this); - _la = _input.LA(1); - while (_la==COMMA) { - { - { - setState(437); - match(COMMA); - setState(438); - expr(0); - } - } - setState(443); - _errHandler.sync(this); - _la = _input.LA(1); - } - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class IndexAccessContext extends ParserRuleContext { - public TerminalNode ID() { return getToken(CoderiveParser.ID, 0); } - public List LBRACKET() { return getTokens(CoderiveParser.LBRACKET); } - public TerminalNode LBRACKET(int i) { - return getToken(CoderiveParser.LBRACKET, i); - } - public List expr() { - return getRuleContexts(ExprContext.class); - } - public ExprContext expr(int i) { - return getRuleContext(ExprContext.class,i); - } - public List RBRACKET() { return getTokens(CoderiveParser.RBRACKET); } - public TerminalNode RBRACKET(int i) { - return getToken(CoderiveParser.RBRACKET, i); - } - public IndexAccessContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_indexAccess; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterIndexAccess(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitIndexAccess(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitIndexAccess(this); - else return visitor.visitChildren(this); - } - } - - public final IndexAccessContext indexAccess() throws RecognitionException { - IndexAccessContext _localctx = new IndexAccessContext(_ctx, getState()); - enterRule(_localctx, 88, RULE_indexAccess); - int _la; - try { - enterOuterAlt(_localctx, 1); - { - setState(444); - match(ID); - setState(449); - _errHandler.sync(this); - _la = _input.LA(1); - do { - { - { - setState(445); - match(LBRACKET); - setState(446); - expr(0); - setState(447); - match(RBRACKET); - } - } - setState(451); - _errHandler.sync(this); - _la = _input.LA(1); - } while ( _la==LBRACKET ); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - @SuppressWarnings("CheckReturnValue") - public static class TypeCastContext extends ParserRuleContext { - public TerminalNode LPAREN() { return getToken(CoderiveParser.LPAREN, 0); } - public TypeContext type() { - return getRuleContext(TypeContext.class,0); - } - public TerminalNode RPAREN() { return getToken(CoderiveParser.RPAREN, 0); } - public ExprContext expr() { - return getRuleContext(ExprContext.class,0); - } - public TypeCastContext(ParserRuleContext parent, int invokingState) { - super(parent, invokingState); - } - @Override public int getRuleIndex() { return RULE_typeCast; } - @Override - public void enterRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).enterTypeCast(this); - } - @Override - public void exitRule(ParseTreeListener listener) { - if ( listener instanceof CoderiveParserListener ) ((CoderiveParserListener)listener).exitTypeCast(this); - } - @Override - public T accept(ParseTreeVisitor visitor) { - if ( visitor instanceof CoderiveParserVisitor ) return ((CoderiveParserVisitor)visitor).visitTypeCast(this); - else return visitor.visitChildren(this); - } - } - - public final TypeCastContext typeCast() throws RecognitionException { - TypeCastContext _localctx = new TypeCastContext(_ctx, getState()); - enterRule(_localctx, 90, RULE_typeCast); - try { - enterOuterAlt(_localctx, 1); - { - setState(453); - match(LPAREN); - setState(454); - type(); - setState(455); - match(RPAREN); - setState(456); - expr(0); - } - } - catch (RecognitionException re) { - _localctx.exception = re; - _errHandler.reportError(this, re); - _errHandler.recover(this, re); - } - finally { - exitRule(); - } - return _localctx; - } - - public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) { - switch (ruleIndex) { - case 39: - return expr_sempred((ExprContext)_localctx, predIndex); - } - return true; - } - private boolean expr_sempred(ExprContext _localctx, int predIndex) { - switch (predIndex) { - case 0: - return precpred(_ctx, 6); - case 1: - return precpred(_ctx, 5); - case 2: - return precpred(_ctx, 4); - } - return true; - } - - public static final String _serializedATN = - "\u0004\u00011\u01cb\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002"+ - "\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004\u0007\u0004\u0002"+ - "\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007\u0007\u0007\u0002"+ - "\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b\u0007\u000b\u0002"+ - "\f\u0007\f\u0002\r\u0007\r\u0002\u000e\u0007\u000e\u0002\u000f\u0007\u000f"+ - "\u0002\u0010\u0007\u0010\u0002\u0011\u0007\u0011\u0002\u0012\u0007\u0012"+ - "\u0002\u0013\u0007\u0013\u0002\u0014\u0007\u0014\u0002\u0015\u0007\u0015"+ - "\u0002\u0016\u0007\u0016\u0002\u0017\u0007\u0017\u0002\u0018\u0007\u0018"+ - "\u0002\u0019\u0007\u0019\u0002\u001a\u0007\u001a\u0002\u001b\u0007\u001b"+ - "\u0002\u001c\u0007\u001c\u0002\u001d\u0007\u001d\u0002\u001e\u0007\u001e"+ - "\u0002\u001f\u0007\u001f\u0002 \u0007 \u0002!\u0007!\u0002\"\u0007\"\u0002"+ - "#\u0007#\u0002$\u0007$\u0002%\u0007%\u0002&\u0007&\u0002\'\u0007\'\u0002"+ - "(\u0007(\u0002)\u0007)\u0002*\u0007*\u0002+\u0007+\u0002,\u0007,\u0002"+ - "-\u0007-\u0001\u0000\u0003\u0000^\b\u0000\u0001\u0000\u0005\u0000a\b\u0000"+ - "\n\u0000\f\u0000d\t\u0000\u0001\u0000\u0001\u0000\u0001\u0001\u0001\u0001"+ - "\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0001"+ - "o\b\u0001\u0001\u0002\u0004\u0002r\b\u0002\u000b\u0002\f\u0002s\u0001"+ - "\u0003\u0001\u0003\u0001\u0003\u0005\u0003y\b\u0003\n\u0003\f\u0003|\t"+ - "\u0003\u0001\u0004\u0003\u0004\u007f\b\u0004\u0001\u0004\u0001\u0004\u0001"+ - "\u0004\u0003\u0004\u0084\b\u0004\u0001\u0004\u0001\u0004\u0005\u0004\u0088"+ - "\b\u0004\n\u0004\f\u0004\u008b\t\u0004\u0001\u0004\u0001\u0004\u0001\u0005"+ - "\u0001\u0005\u0001\u0006\u0001\u0006\u0001\u0006\u0001\u0006\u0003\u0006"+ - "\u0095\b\u0006\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007\u0003\u0007"+ - "\u009b\b\u0007\u0001\b\u0001\b\u0001\b\u0003\b\u00a0\b\b\u0001\b\u0001"+ - "\b\u0001\b\u0005\b\u00a5\b\b\n\b\f\b\u00a8\t\b\u0001\b\u0001\b\u0001\t"+ - "\u0001\t\u0003\t\u00ae\b\t\u0001\t\u0003\t\u00b1\b\t\u0001\t\u0003\t\u00b4"+ - "\b\t\u0003\t\u00b6\b\t\u0001\t\u0001\t\u0001\t\u0003\t\u00bb\b\t\u0001"+ - "\t\u0001\t\u0001\t\u0005\t\u00c0\b\t\n\t\f\t\u00c3\t\t\u0001\t\u0001\t"+ - "\u0001\n\u0001\n\u0001\n\u0003\n\u00ca\b\n\u0001\n\u0001\n\u0003\n\u00ce"+ - "\b\n\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001\f\u0001\f\u0001"+ - "\f\u0005\f\u00d7\b\f\n\f\f\f\u00da\t\f\u0001\r\u0001\r\u0001\r\u0001\u000e"+ - "\u0001\u000e\u0001\u000e\u0005\u000e\u00e2\b\u000e\n\u000e\f\u000e\u00e5"+ - "\t\u000e\u0001\u000f\u0001\u000f\u0001\u0010\u0001\u0010\u0001\u0010\u0001"+ - "\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001"+ - "\u0010\u0003\u0010\u00f3\b\u0010\u0001\u0011\u0001\u0011\u0003\u0011\u00f7"+ - "\b\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0003\u0011\u00fc\b\u0011"+ - "\u0001\u0012\u0001\u0012\u0001\u0012\u0001\u0012\u0001\u0013\u0001\u0013"+ - "\u0001\u0014\u0001\u0014\u0003\u0014\u0106\b\u0014\u0001\u0015\u0001\u0015"+ - "\u0001\u0015\u0001\u0015\u0001\u0016\u0001\u0016\u0001\u0017\u0001\u0017"+ - "\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0019\u0001\u0019\u0001\u0019"+ - "\u0001\u0019\u0001\u0019\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001a"+ - "\u0001\u001b\u0001\u001b\u0001\u001b\u0001\u001c\u0001\u001c\u0001\u001c"+ - "\u0001\u001c\u0001\u001d\u0001\u001d\u0001\u001d\u0001\u001e\u0003\u001e"+ - "\u0127\b\u001e\u0001\u001e\u0001\u001e\u0001\u001e\u0001\u001e\u0001\u001e"+ - "\u0003\u001e\u012e\b\u001e\u0001\u001f\u0001\u001f\u0001 \u0001 \u0001"+ - " \u0003 \u0135\b \u0001 \u0001 \u0001 \u0003 \u013a\b \u0001 \u0001 \u0001"+ - "!\u0001!\u0001!\u0005!\u0141\b!\n!\f!\u0144\t!\u0001\"\u0001\"\u0001\""+ - "\u0001\"\u0001\"\u0001\"\u0001\"\u0005\"\u014d\b\"\n\"\f\"\u0150\t\"\u0001"+ - "\"\u0001\"\u0001\"\u0003\"\u0155\b\"\u0003\"\u0157\b\"\u0001#\u0001#\u0005"+ - "#\u015b\b#\n#\f#\u015e\t#\u0001#\u0001#\u0001$\u0001$\u0005$\u0164\b$"+ - "\n$\f$\u0167\t$\u0001$\u0001$\u0001%\u0001%\u0001%\u0001%\u0003%\u016f"+ - "\b%\u0001%\u0001%\u0001%\u0001%\u0001%\u0001%\u0005%\u0177\b%\n%\f%\u017a"+ - "\t%\u0001%\u0001%\u0001&\u0001&\u0001&\u0003&\u0181\b&\u0001\'\u0001\'"+ - "\u0001\'\u0001\'\u0001\'\u0001\'\u0003\'\u0189\b\'\u0001\'\u0001\'\u0001"+ - "\'\u0001\'\u0001\'\u0001\'\u0001\'\u0001\'\u0001\'\u0005\'\u0194\b\'\n"+ - "\'\f\'\u0197\t\'\u0001(\u0001(\u0001(\u0001(\u0001(\u0005(\u019e\b(\n"+ - "(\f(\u01a1\t(\u0001)\u0001)\u0001)\u0001)\u0001)\u0001)\u0001)\u0001)"+ - "\u0001)\u0001)\u0003)\u01ad\b)\u0001*\u0001*\u0003*\u01b1\b*\u0001*\u0001"+ - "*\u0001+\u0001+\u0001+\u0005+\u01b8\b+\n+\f+\u01bb\t+\u0001,\u0001,\u0001"+ - ",\u0001,\u0001,\u0004,\u01c2\b,\u000b,\f,\u01c3\u0001-\u0001-\u0001-\u0001"+ - "-\u0001-\u0001-\u0000\u0001N.\u0000\u0002\u0004\u0006\b\n\f\u000e\u0010"+ - "\u0012\u0014\u0016\u0018\u001a\u001c\u001e \"$&(*,.02468:<>@BDFHJLNPR"+ - "TVXZ\u0000\u0006\u0001\u0000\u0001\u0002\u0002\u0000\u0011\u0014\u0019"+ - "\u0019\u0001\u0000\u001b\u001e\u0001\u0000\u001b\u001c\u0001\u0000\u001d"+ - "\u001f\u0001\u0000!&\u01de\u0000]\u0001\u0000\u0000\u0000\u0002g\u0001"+ - "\u0000\u0000\u0000\u0004q\u0001\u0000\u0000\u0000\u0006u\u0001\u0000\u0000"+ - "\u0000\b~\u0001\u0000\u0000\u0000\n\u008e\u0001\u0000\u0000\u0000\f\u0094"+ - "\u0001\u0000\u0000\u0000\u000e\u0096\u0001\u0000\u0000\u0000\u0010\u009c"+ - "\u0001\u0000\u0000\u0000\u0012\u00b5\u0001\u0000\u0000\u0000\u0014\u00c6"+ - "\u0001\u0000\u0000\u0000\u0016\u00cf\u0001\u0000\u0000\u0000\u0018\u00d3"+ - "\u0001\u0000\u0000\u0000\u001a\u00db\u0001\u0000\u0000\u0000\u001c\u00de"+ - "\u0001\u0000\u0000\u0000\u001e\u00e6\u0001\u0000\u0000\u0000 \u00f2\u0001"+ - "\u0000\u0000\u0000\"\u00f6\u0001\u0000\u0000\u0000$\u00fd\u0001\u0000"+ - "\u0000\u0000&\u0101\u0001\u0000\u0000\u0000(\u0105\u0001\u0000\u0000\u0000"+ - "*\u0107\u0001\u0000\u0000\u0000,\u010b\u0001\u0000\u0000\u0000.\u010d"+ - "\u0001\u0000\u0000\u00000\u010f\u0001\u0000\u0000\u00002\u0112\u0001\u0000"+ - "\u0000\u00004\u0117\u0001\u0000\u0000\u00006\u011b\u0001\u0000\u0000\u0000"+ - "8\u011e\u0001\u0000\u0000\u0000:\u0122\u0001\u0000\u0000\u0000<\u012d"+ - "\u0001\u0000\u0000\u0000>\u012f\u0001\u0000\u0000\u0000@\u0134\u0001\u0000"+ - "\u0000\u0000B\u013d\u0001\u0000\u0000\u0000D\u0145\u0001\u0000\u0000\u0000"+ - "F\u0158\u0001\u0000\u0000\u0000H\u0161\u0001\u0000\u0000\u0000J\u016a"+ - "\u0001\u0000\u0000\u0000L\u0180\u0001\u0000\u0000\u0000N\u0188\u0001\u0000"+ - "\u0000\u0000P\u0198\u0001\u0000\u0000\u0000R\u01ac\u0001\u0000\u0000\u0000"+ - "T\u01ae\u0001\u0000\u0000\u0000V\u01b4\u0001\u0000\u0000\u0000X\u01bc"+ - "\u0001\u0000\u0000\u0000Z\u01c5\u0001\u0000\u0000\u0000\\^\u0003\u0002"+ - "\u0001\u0000]\\\u0001\u0000\u0000\u0000]^\u0001\u0000\u0000\u0000^b\u0001"+ - "\u0000\u0000\u0000_a\u0003\b\u0004\u0000`_\u0001\u0000\u0000\u0000ad\u0001"+ - "\u0000\u0000\u0000b`\u0001\u0000\u0000\u0000bc\u0001\u0000\u0000\u0000"+ - "ce\u0001\u0000\u0000\u0000db\u0001\u0000\u0000\u0000ef\u0005\u0000\u0000"+ - "\u0001f\u0001\u0001\u0000\u0000\u0000gh\u0005\u0003\u0000\u0000hn\u0003"+ - "\u0006\u0003\u0000ij\u0005\u0004\u0000\u0000jk\u0005+\u0000\u0000kl\u0003"+ - "\u0004\u0002\u0000lm\u0005,\u0000\u0000mo\u0001\u0000\u0000\u0000ni\u0001"+ - "\u0000\u0000\u0000no\u0001\u0000\u0000\u0000o\u0003\u0001\u0000\u0000"+ - "\u0000pr\u0003\u0006\u0003\u0000qp\u0001\u0000\u0000\u0000rs\u0001\u0000"+ - "\u0000\u0000sq\u0001\u0000\u0000\u0000st\u0001\u0000\u0000\u0000t\u0005"+ - "\u0001\u0000\u0000\u0000uz\u0005\u0019\u0000\u0000vw\u0005\'\u0000\u0000"+ - "wy\u0005\u0019\u0000\u0000xv\u0001\u0000\u0000\u0000y|\u0001\u0000\u0000"+ - "\u0000zx\u0001\u0000\u0000\u0000z{\u0001\u0000\u0000\u0000{\u0007\u0001"+ - "\u0000\u0000\u0000|z\u0001\u0000\u0000\u0000}\u007f\u0003\n\u0005\u0000"+ - "~}\u0001\u0000\u0000\u0000~\u007f\u0001\u0000\u0000\u0000\u007f\u0080"+ - "\u0001\u0000\u0000\u0000\u0080\u0083\u0005\u0019\u0000\u0000\u0081\u0082"+ - "\u0005\u0005\u0000\u0000\u0082\u0084\u0003\u0006\u0003\u0000\u0083\u0081"+ - "\u0001\u0000\u0000\u0000\u0083\u0084\u0001\u0000\u0000\u0000\u0084\u0085"+ - "\u0001\u0000\u0000\u0000\u0085\u0089\u0005+\u0000\u0000\u0086\u0088\u0003"+ - "\f\u0006\u0000\u0087\u0086\u0001\u0000\u0000\u0000\u0088\u008b\u0001\u0000"+ - "\u0000\u0000\u0089\u0087\u0001\u0000\u0000\u0000\u0089\u008a\u0001\u0000"+ - "\u0000\u0000\u008a\u008c\u0001\u0000\u0000\u0000\u008b\u0089\u0001\u0000"+ - "\u0000\u0000\u008c\u008d\u0005,\u0000\u0000\u008d\t\u0001\u0000\u0000"+ - "\u0000\u008e\u008f\u0007\u0000\u0000\u0000\u008f\u000b\u0001\u0000\u0000"+ - "\u0000\u0090\u0095\u0003\u000e\u0007\u0000\u0091\u0095\u0003\u0010\b\u0000"+ - "\u0092\u0095\u0003\u0012\t\u0000\u0093\u0095\u0003 \u0010\u0000\u0094"+ - "\u0090\u0001\u0000\u0000\u0000\u0094\u0091\u0001\u0000\u0000\u0000\u0094"+ - "\u0092\u0001\u0000\u0000\u0000\u0094\u0093\u0001\u0000\u0000\u0000\u0095"+ - "\r\u0001\u0000\u0000\u0000\u0096\u0097\u0003\u001c\u000e\u0000\u0097\u009a"+ - "\u0005\u0019\u0000\u0000\u0098\u0099\u0005\u001a\u0000\u0000\u0099\u009b"+ - "\u0003N\'\u0000\u009a\u0098\u0001\u0000\u0000\u0000\u009a\u009b\u0001"+ - "\u0000\u0000\u0000\u009b\u000f\u0001\u0000\u0000\u0000\u009c\u009d\u0005"+ - "\u0019\u0000\u0000\u009d\u009f\u0005)\u0000\u0000\u009e\u00a0\u0003\u0018"+ - "\f\u0000\u009f\u009e\u0001\u0000\u0000\u0000\u009f\u00a0\u0001\u0000\u0000"+ - "\u0000\u00a0\u00a1\u0001\u0000\u0000\u0000\u00a1\u00a2\u0005*\u0000\u0000"+ - "\u00a2\u00a6\u0005+\u0000\u0000\u00a3\u00a5\u0003 \u0010\u0000\u00a4\u00a3"+ - "\u0001\u0000\u0000\u0000\u00a5\u00a8\u0001\u0000\u0000\u0000\u00a6\u00a4"+ - "\u0001\u0000\u0000\u0000\u00a6\u00a7\u0001\u0000\u0000\u0000\u00a7\u00a9"+ - "\u0001\u0000\u0000\u0000\u00a8\u00a6\u0001\u0000\u0000\u0000\u00a9\u00aa"+ - "\u0005,\u0000\u0000\u00aa\u0011\u0001\u0000\u0000\u0000\u00ab\u00ad\u0003"+ - "\u0016\u000b\u0000\u00ac\u00ae\u0003\n\u0005\u0000\u00ad\u00ac\u0001\u0000"+ - "\u0000\u0000\u00ad\u00ae\u0001\u0000\u0000\u0000\u00ae\u00b6\u0001\u0000"+ - "\u0000\u0000\u00af\u00b1\u0003\n\u0005\u0000\u00b0\u00af\u0001\u0000\u0000"+ - "\u0000\u00b0\u00b1\u0001\u0000\u0000\u0000\u00b1\u00b3\u0001\u0000\u0000"+ - "\u0000\u00b2\u00b4\u0003\u0016\u000b\u0000\u00b3\u00b2\u0001\u0000\u0000"+ - "\u0000\u00b3\u00b4\u0001\u0000\u0000\u0000\u00b4\u00b6\u0001\u0000\u0000"+ - "\u0000\u00b5\u00ab\u0001\u0000\u0000\u0000\u00b5\u00b0\u0001\u0000\u0000"+ - "\u0000\u00b6\u00b7\u0001\u0000\u0000\u0000\u00b7\u00b8\u0005\u0019\u0000"+ - "\u0000\u00b8\u00ba\u0005)\u0000\u0000\u00b9\u00bb\u0003\u0018\f\u0000"+ - "\u00ba\u00b9\u0001\u0000\u0000\u0000\u00ba\u00bb\u0001\u0000\u0000\u0000"+ - "\u00bb\u00bc\u0001\u0000\u0000\u0000\u00bc\u00bd\u0005*\u0000\u0000\u00bd"+ - "\u00c1\u0005+\u0000\u0000\u00be\u00c0\u0003 \u0010\u0000\u00bf\u00be\u0001"+ - "\u0000\u0000\u0000\u00c0\u00c3\u0001\u0000\u0000\u0000\u00c1\u00bf\u0001"+ - "\u0000\u0000\u0000\u00c1\u00c2\u0001\u0000\u0000\u0000\u00c2\u00c4\u0001"+ - "\u0000\u0000\u0000\u00c3\u00c1\u0001\u0000\u0000\u0000\u00c4\u00c5\u0005"+ - ",\u0000\u0000\u00c5\u0013\u0001\u0000\u0000\u0000\u00c6\u00c9\u0005\u0019"+ - "\u0000\u0000\u00c7\u00c8\u0005(\u0000\u0000\u00c8\u00ca\u0005\u0019\u0000"+ - "\u0000\u00c9\u00c7\u0001\u0000\u0000\u0000\u00c9\u00ca\u0001\u0000\u0000"+ - "\u0000\u00ca\u00cd\u0001\u0000\u0000\u0000\u00cb\u00cc\u0005(\u0000\u0000"+ - "\u00cc\u00ce\u0005\u0019\u0000\u0000\u00cd\u00cb\u0001\u0000\u0000\u0000"+ - "\u00cd\u00ce\u0001\u0000\u0000\u0000\u00ce\u0015\u0001\u0000\u0000\u0000"+ - "\u00cf\u00d0\u0005-\u0000\u0000\u00d0\u00d1\u0003\u0014\n\u0000\u00d1"+ - "\u00d2\u0005.\u0000\u0000\u00d2\u0017\u0001\u0000\u0000\u0000\u00d3\u00d8"+ - "\u0003\u001a\r\u0000\u00d4\u00d5\u0005(\u0000\u0000\u00d5\u00d7\u0003"+ - "\u001a\r\u0000\u00d6\u00d4\u0001\u0000\u0000\u0000\u00d7\u00da\u0001\u0000"+ - "\u0000\u0000\u00d8\u00d6\u0001\u0000\u0000\u0000\u00d8\u00d9\u0001\u0000"+ - "\u0000\u0000\u00d9\u0019\u0001\u0000\u0000\u0000\u00da\u00d8\u0001\u0000"+ - "\u0000\u0000\u00db\u00dc\u0003\u001c\u000e\u0000\u00dc\u00dd\u0005\u0019"+ - "\u0000\u0000\u00dd\u001b\u0001\u0000\u0000\u0000\u00de\u00e3\u0003\u001e"+ - "\u000f\u0000\u00df\u00e0\u0005-\u0000\u0000\u00e0\u00e2\u0005.\u0000\u0000"+ - "\u00e1\u00df\u0001\u0000\u0000\u0000\u00e2\u00e5\u0001\u0000\u0000\u0000"+ - "\u00e3\u00e1\u0001\u0000\u0000\u0000\u00e3\u00e4\u0001\u0000\u0000\u0000"+ - "\u00e4\u001d\u0001\u0000\u0000\u0000\u00e5\u00e3\u0001\u0000\u0000\u0000"+ - "\u00e6\u00e7\u0007\u0001\u0000\u0000\u00e7\u001f\u0001\u0000\u0000\u0000"+ - "\u00e8\u00f3\u0003\"\u0011\u0000\u00e9\u00f3\u0003$\u0012\u0000\u00ea"+ - "\u00f3\u0003*\u0015\u0000\u00eb\u00f3\u00034\u001a\u0000\u00ec\u00f3\u0003"+ - ">\u001f\u0000\u00ed\u00f3\u0003:\u001d\u0000\u00ee\u00f3\u0003D\"\u0000"+ - "\u00ef\u00f3\u0003J%\u0000\u00f0\u00f3\u0003&\u0013\u0000\u00f1\u00f3"+ - "\u0003.\u0017\u0000\u00f2\u00e8\u0001\u0000\u0000\u0000\u00f2\u00e9\u0001"+ - "\u0000\u0000\u0000\u00f2\u00ea\u0001\u0000\u0000\u0000\u00f2\u00eb\u0001"+ - "\u0000\u0000\u0000\u00f2\u00ec\u0001\u0000\u0000\u0000\u00f2\u00ed\u0001"+ - "\u0000\u0000\u0000\u00f2\u00ee\u0001\u0000\u0000\u0000\u00f2\u00ef\u0001"+ - "\u0000\u0000\u0000\u00f2\u00f0\u0001\u0000\u0000\u0000\u00f2\u00f1\u0001"+ - "\u0000\u0000\u0000\u00f3!\u0001\u0000\u0000\u0000\u00f4\u00f7\u0005\u0007"+ - "\u0000\u0000\u00f5\u00f7\u0003\u001c\u000e\u0000\u00f6\u00f4\u0001\u0000"+ - "\u0000\u0000\u00f6\u00f5\u0001\u0000\u0000\u0000\u00f7\u00f8\u0001\u0000"+ - "\u0000\u0000\u00f8\u00fb\u0005\u0019\u0000\u0000\u00f9\u00fa\u0005\u001a"+ - "\u0000\u0000\u00fa\u00fc\u0003N\'\u0000\u00fb\u00f9\u0001\u0000\u0000"+ - "\u0000\u00fb\u00fc\u0001\u0000\u0000\u0000\u00fc#\u0001\u0000\u0000\u0000"+ - "\u00fd\u00fe\u0003(\u0014\u0000\u00fe\u00ff\u0005\u001a\u0000\u0000\u00ff"+ - "\u0100\u0003N\'\u0000\u0100%\u0001\u0000\u0000\u0000\u0101\u0102\u0003"+ - "N\'\u0000\u0102\'\u0001\u0000\u0000\u0000\u0103\u0106\u0005\u0019\u0000"+ - "\u0000\u0104\u0106\u0003X,\u0000\u0105\u0103\u0001\u0000\u0000\u0000\u0105"+ - "\u0104\u0001\u0000\u0000\u0000\u0106)\u0001\u0000\u0000\u0000\u0107\u0108"+ - "\u0003,\u0016\u0000\u0108\u0109\u0005\u001a\u0000\u0000\u0109\u010a\u0003"+ - "0\u0018\u0000\u010a+\u0001\u0000\u0000\u0000\u010b\u010c\u0003\u0014\n"+ - "\u0000\u010c-\u0001\u0000\u0000\u0000\u010d\u010e\u00030\u0018\u0000\u010e"+ - "/\u0001\u0000\u0000\u0000\u010f\u0110\u00032\u0019\u0000\u0110\u0111\u0003"+ - "@ \u0000\u01111\u0001\u0000\u0000\u0000\u0112\u0113\u0005-\u0000\u0000"+ - "\u0113\u0114\u0003\u0014\n\u0000\u0114\u0115\u0005.\u0000\u0000\u0115"+ - "\u0116\u0005 \u0000\u0000\u01163\u0001\u0000\u0000\u0000\u0117\u0118\u0003"+ - "(\u0014\u0000\u0118\u0119\u0005\u001a\u0000\u0000\u0119\u011a\u00036\u001b"+ - "\u0000\u011a5\u0001\u0000\u0000\u0000\u011b\u011c\u00038\u001c\u0000\u011c"+ - "\u011d\u0005\t\u0000\u0000\u011d7\u0001\u0000\u0000\u0000\u011e\u011f"+ - "\u0005)\u0000\u0000\u011f\u0120\u0003\u001c\u000e\u0000\u0120\u0121\u0005"+ - "*\u0000\u0000\u01219\u0001\u0000\u0000\u0000\u0122\u0123\u0005\b\u0000"+ - "\u0000\u0123\u0124\u0003<\u001e\u0000\u0124;\u0001\u0000\u0000\u0000\u0125"+ - "\u0127\u00032\u0019\u0000\u0126\u0125\u0001\u0000\u0000\u0000\u0126\u0127"+ - "\u0001\u0000\u0000\u0000\u0127\u0128\u0001\u0000\u0000\u0000\u0128\u012e"+ - "\u0003@ \u0000\u0129\u012a\u0005\u0019\u0000\u0000\u012a\u012b\u0005\u001a"+ - "\u0000\u0000\u012b\u012e\u0003N\'\u0000\u012c\u012e\u0003N\'\u0000\u012d"+ - "\u0126\u0001\u0000\u0000\u0000\u012d\u0129\u0001\u0000\u0000\u0000\u012d"+ - "\u012c\u0001\u0000\u0000\u0000\u012e=\u0001\u0000\u0000\u0000\u012f\u0130"+ - "\u0003@ \u0000\u0130?\u0001\u0000\u0000\u0000\u0131\u0132\u0003\u0006"+ - "\u0003\u0000\u0132\u0133\u0005\'\u0000\u0000\u0133\u0135\u0001\u0000\u0000"+ - "\u0000\u0134\u0131\u0001\u0000\u0000\u0000\u0134\u0135\u0001\u0000\u0000"+ - "\u0000\u0135\u0136\u0001\u0000\u0000\u0000\u0136\u0137\u0005\u0019\u0000"+ - "\u0000\u0137\u0139\u0005)\u0000\u0000\u0138\u013a\u0003B!\u0000\u0139"+ - "\u0138\u0001\u0000\u0000\u0000\u0139\u013a\u0001\u0000\u0000\u0000\u013a"+ - "\u013b\u0001\u0000\u0000\u0000\u013b\u013c\u0005*\u0000\u0000\u013cA\u0001"+ - "\u0000\u0000\u0000\u013d\u0142\u0003N\'\u0000\u013e\u013f\u0005(\u0000"+ - "\u0000\u013f\u0141\u0003N\'\u0000\u0140\u013e\u0001\u0000\u0000\u0000"+ - "\u0141\u0144\u0001\u0000\u0000\u0000\u0142\u0140\u0001\u0000\u0000\u0000"+ - "\u0142\u0143\u0001\u0000\u0000\u0000\u0143C\u0001\u0000\u0000\u0000\u0144"+ - "\u0142\u0001\u0000\u0000\u0000\u0145\u0146\u0005\n\u0000\u0000\u0146\u0147"+ - "\u0003N\'\u0000\u0147\u014e\u0003F#\u0000\u0148\u0149\u0005\f\u0000\u0000"+ - "\u0149\u014a\u0003N\'\u0000\u014a\u014b\u0003F#\u0000\u014b\u014d\u0001"+ - "\u0000\u0000\u0000\u014c\u0148\u0001\u0000\u0000\u0000\u014d\u0150\u0001"+ - "\u0000\u0000\u0000\u014e\u014c\u0001\u0000\u0000\u0000\u014e\u014f\u0001"+ - "\u0000\u0000\u0000\u014f\u0156\u0001\u0000\u0000\u0000\u0150\u014e\u0001"+ - "\u0000\u0000\u0000\u0151\u0154\u0005\u000b\u0000\u0000\u0152\u0155\u0003"+ - "D\"\u0000\u0153\u0155\u0003H$\u0000\u0154\u0152\u0001\u0000\u0000\u0000"+ - "\u0154\u0153\u0001\u0000\u0000\u0000\u0155\u0157\u0001\u0000\u0000\u0000"+ - "\u0156\u0151\u0001\u0000\u0000\u0000\u0156\u0157\u0001\u0000\u0000\u0000"+ - "\u0157E\u0001\u0000\u0000\u0000\u0158\u015c\u0005+\u0000\u0000\u0159\u015b"+ - "\u0003 \u0010\u0000\u015a\u0159\u0001\u0000\u0000\u0000\u015b\u015e\u0001"+ - "\u0000\u0000\u0000\u015c\u015a\u0001\u0000\u0000\u0000\u015c\u015d\u0001"+ - "\u0000\u0000\u0000\u015d\u015f\u0001\u0000\u0000\u0000\u015e\u015c\u0001"+ - "\u0000\u0000\u0000\u015f\u0160\u0005,\u0000\u0000\u0160G\u0001\u0000\u0000"+ - "\u0000\u0161\u0165\u0005+\u0000\u0000\u0162\u0164\u0003 \u0010\u0000\u0163"+ - "\u0162\u0001\u0000\u0000\u0000\u0164\u0167\u0001\u0000\u0000\u0000\u0165"+ - "\u0163\u0001\u0000\u0000\u0000\u0165\u0166\u0001\u0000\u0000\u0000\u0166"+ - "\u0168\u0001\u0000\u0000\u0000\u0167\u0165\u0001\u0000\u0000\u0000\u0168"+ - "\u0169\u0005,\u0000\u0000\u0169I\u0001\u0000\u0000\u0000\u016a\u016b\u0005"+ - "\r\u0000\u0000\u016b\u016e\u0005\u0019\u0000\u0000\u016c\u016d\u0005\u000e"+ - "\u0000\u0000\u016d\u016f\u0003L&\u0000\u016e\u016c\u0001\u0000\u0000\u0000"+ - "\u016e\u016f\u0001\u0000\u0000\u0000\u016f\u0170\u0001\u0000\u0000\u0000"+ - "\u0170\u0171\u0005\u000f\u0000\u0000\u0171\u0172\u0003N\'\u0000\u0172"+ - "\u0173\u0005\u0010\u0000\u0000\u0173\u0174\u0003N\'\u0000\u0174\u0178"+ - "\u0005+\u0000\u0000\u0175\u0177\u0003 \u0010\u0000\u0176\u0175\u0001\u0000"+ - "\u0000\u0000\u0177\u017a\u0001\u0000\u0000\u0000\u0178\u0176\u0001\u0000"+ - "\u0000\u0000\u0178\u0179\u0001\u0000\u0000\u0000\u0179\u017b\u0001\u0000"+ - "\u0000\u0000\u017a\u0178\u0001\u0000\u0000\u0000\u017b\u017c\u0005,\u0000"+ - "\u0000\u017cK\u0001\u0000\u0000\u0000\u017d\u017e\u0007\u0002\u0000\u0000"+ - "\u017e\u0181\u0003N\'\u0000\u017f\u0181\u0003N\'\u0000\u0180\u017d\u0001"+ - "\u0000\u0000\u0000\u0180\u017f\u0001\u0000\u0000\u0000\u0181M\u0001\u0000"+ - "\u0000\u0000\u0182\u0183\u0006\'\uffff\uffff\u0000\u0183\u0184\u0007\u0003"+ - "\u0000\u0000\u0184\u0189\u0003N\'\u0007\u0185\u0189\u0003P(\u0000\u0186"+ - "\u0189\u0003@ \u0000\u0187\u0189\u0003Z-\u0000\u0188\u0182\u0001\u0000"+ - "\u0000\u0000\u0188\u0185\u0001\u0000\u0000\u0000\u0188\u0186\u0001\u0000"+ - "\u0000\u0000\u0188\u0187\u0001\u0000\u0000\u0000\u0189\u0195\u0001\u0000"+ - "\u0000\u0000\u018a\u018b\n\u0006\u0000\u0000\u018b\u018c\u0007\u0004\u0000"+ - "\u0000\u018c\u0194\u0003N\'\u0007\u018d\u018e\n\u0005\u0000\u0000\u018e"+ - "\u018f\u0007\u0003\u0000\u0000\u018f\u0194\u0003N\'\u0006\u0190\u0191"+ - "\n\u0004\u0000\u0000\u0191\u0192\u0007\u0005\u0000\u0000\u0192\u0194\u0003"+ - "N\'\u0005\u0193\u018a\u0001\u0000\u0000\u0000\u0193\u018d\u0001\u0000"+ - "\u0000\u0000\u0193\u0190\u0001\u0000\u0000\u0000\u0194\u0197\u0001\u0000"+ - "\u0000\u0000\u0195\u0193\u0001\u0000\u0000\u0000\u0195\u0196\u0001\u0000"+ - "\u0000\u0000\u0196O\u0001\u0000\u0000\u0000\u0197\u0195\u0001\u0000\u0000"+ - "\u0000\u0198\u019f\u0003R)\u0000\u0199\u019a\u0005-\u0000\u0000\u019a"+ - "\u019b\u0003N\'\u0000\u019b\u019c\u0005.\u0000\u0000\u019c\u019e\u0001"+ - "\u0000\u0000\u0000\u019d\u0199\u0001\u0000\u0000\u0000\u019e\u01a1\u0001"+ - "\u0000\u0000\u0000\u019f\u019d\u0001\u0000\u0000\u0000\u019f\u01a0\u0001"+ - "\u0000\u0000\u0000\u01a0Q\u0001\u0000\u0000\u0000\u01a1\u019f\u0001\u0000"+ - "\u0000\u0000\u01a2\u01ad\u0005\u0019\u0000\u0000\u01a3\u01ad\u0005\u0015"+ - "\u0000\u0000\u01a4\u01ad\u0005\u0016\u0000\u0000\u01a5\u01ad\u0005\u0017"+ - "\u0000\u0000\u01a6\u01ad\u0005\u0018\u0000\u0000\u01a7\u01a8\u0005)\u0000"+ - "\u0000\u01a8\u01a9\u0003N\'\u0000\u01a9\u01aa\u0005*\u0000\u0000\u01aa"+ - "\u01ad\u0001\u0000\u0000\u0000\u01ab\u01ad\u0003T*\u0000\u01ac\u01a2\u0001"+ - "\u0000\u0000\u0000\u01ac\u01a3\u0001\u0000\u0000\u0000\u01ac\u01a4\u0001"+ - "\u0000\u0000\u0000\u01ac\u01a5\u0001\u0000\u0000\u0000\u01ac\u01a6\u0001"+ - "\u0000\u0000\u0000\u01ac\u01a7\u0001\u0000\u0000\u0000\u01ac\u01ab\u0001"+ - "\u0000\u0000\u0000\u01adS\u0001\u0000\u0000\u0000\u01ae\u01b0\u0005-\u0000"+ - "\u0000\u01af\u01b1\u0003V+\u0000\u01b0\u01af\u0001\u0000\u0000\u0000\u01b0"+ - "\u01b1\u0001\u0000\u0000\u0000\u01b1\u01b2\u0001\u0000\u0000\u0000\u01b2"+ - "\u01b3\u0005.\u0000\u0000\u01b3U\u0001\u0000\u0000\u0000\u01b4\u01b9\u0003"+ - "N\'\u0000\u01b5\u01b6\u0005(\u0000\u0000\u01b6\u01b8\u0003N\'\u0000\u01b7"+ - "\u01b5\u0001\u0000\u0000\u0000\u01b8\u01bb\u0001\u0000\u0000\u0000\u01b9"+ - "\u01b7\u0001\u0000\u0000\u0000\u01b9\u01ba\u0001\u0000\u0000\u0000\u01ba"+ - "W\u0001\u0000\u0000\u0000\u01bb\u01b9\u0001\u0000\u0000\u0000\u01bc\u01c1"+ - "\u0005\u0019\u0000\u0000\u01bd\u01be\u0005-\u0000\u0000\u01be\u01bf\u0003"+ - "N\'\u0000\u01bf\u01c0\u0005.\u0000\u0000\u01c0\u01c2\u0001\u0000\u0000"+ - "\u0000\u01c1\u01bd\u0001\u0000\u0000\u0000\u01c2\u01c3\u0001\u0000\u0000"+ - "\u0000\u01c3\u01c1\u0001\u0000\u0000\u0000\u01c3\u01c4\u0001\u0000\u0000"+ - "\u0000\u01c4Y\u0001\u0000\u0000\u0000\u01c5\u01c6\u0005)\u0000\u0000\u01c6"+ - "\u01c7\u0003\u001c\u000e\u0000\u01c7\u01c8\u0005*\u0000\u0000\u01c8\u01c9"+ - "\u0003N\'\u0000\u01c9[\u0001\u0000\u0000\u0000/]bnsz~\u0083\u0089\u0094"+ - "\u009a\u009f\u00a6\u00ad\u00b0\u00b3\u00b5\u00ba\u00c1\u00c9\u00cd\u00d8"+ - "\u00e3\u00f2\u00f6\u00fb\u0105\u0126\u012d\u0134\u0139\u0142\u014e\u0154"+ - "\u0156\u015c\u0165\u016e\u0178\u0180\u0188\u0193\u0195\u019f\u01ac\u01b0"+ - "\u01b9\u01c3"; - public static final ATN _ATN = - new ATNDeserializer().deserialize(_serializedATN.toCharArray()); - static { - _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; - for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { - _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); - } - } -} \ No newline at end of file diff --git a/src/main/java/cod/ast/CoderiveParserBaseListener.java b/src/main/java/cod/ast/CoderiveParserBaseListener.java deleted file mode 100644 index 10a9dadc..00000000 --- a/src/main/java/cod/ast/CoderiveParserBaseListener.java +++ /dev/null @@ -1,774 +0,0 @@ -// Generated from CoderiveParser.g4 by ANTLR 4.13.2 - -package cod.ast; - - -import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.tree.ErrorNode; -import org.antlr.v4.runtime.tree.TerminalNode; - -/** - * This class provides an empty implementation of {@link CoderiveParserListener}, - * which can be extended to create a listener which only needs to handle a subset - * of the available methods. - */ -@SuppressWarnings("CheckReturnValue") -public class CoderiveParserBaseListener implements CoderiveParserListener { - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterProgram(CoderiveParser.ProgramContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitProgram(CoderiveParser.ProgramContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterUnitDeclaration(CoderiveParser.UnitDeclarationContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitUnitDeclaration(CoderiveParser.UnitDeclarationContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterQualifiedNameList(CoderiveParser.QualifiedNameListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitQualifiedNameList(CoderiveParser.QualifiedNameListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterQualifiedName(CoderiveParser.QualifiedNameContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitQualifiedName(CoderiveParser.QualifiedNameContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterTypeDeclaration(CoderiveParser.TypeDeclarationContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitTypeDeclaration(CoderiveParser.TypeDeclarationContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterModifiers(CoderiveParser.ModifiersContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitModifiers(CoderiveParser.ModifiersContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterTypeBody(CoderiveParser.TypeBodyContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitTypeBody(CoderiveParser.TypeBodyContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterFieldDeclaration(CoderiveParser.FieldDeclarationContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitFieldDeclaration(CoderiveParser.FieldDeclarationContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterConstructor(CoderiveParser.ConstructorContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitConstructor(CoderiveParser.ConstructorContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterMethodDeclaration(CoderiveParser.MethodDeclarationContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitMethodDeclaration(CoderiveParser.MethodDeclarationContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterIdList(CoderiveParser.IdListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitIdList(CoderiveParser.IdListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterSlotList(CoderiveParser.SlotListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitSlotList(CoderiveParser.SlotListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterParameterList(CoderiveParser.ParameterListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitParameterList(CoderiveParser.ParameterListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterParameter(CoderiveParser.ParameterContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitParameter(CoderiveParser.ParameterContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterType(CoderiveParser.TypeContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitType(CoderiveParser.TypeContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterSimpleType(CoderiveParser.SimpleTypeContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitSimpleType(CoderiveParser.SimpleTypeContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterStatement(CoderiveParser.StatementContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitStatement(CoderiveParser.StatementContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterVariableDeclaration(CoderiveParser.VariableDeclarationContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitVariableDeclaration(CoderiveParser.VariableDeclarationContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterAssignment(CoderiveParser.AssignmentContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitAssignment(CoderiveParser.AssignmentContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterExpressionStatement(CoderiveParser.ExpressionStatementContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitExpressionStatement(CoderiveParser.ExpressionStatementContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterAssignable(CoderiveParser.AssignableContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitAssignable(CoderiveParser.AssignableContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterReturnSlotAssignment(CoderiveParser.ReturnSlotAssignmentContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitReturnSlotAssignment(CoderiveParser.ReturnSlotAssignmentContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterAssignableList(CoderiveParser.AssignableListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitAssignableList(CoderiveParser.AssignableListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterSlotMethodCallStatement(CoderiveParser.SlotMethodCallStatementContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitSlotMethodCallStatement(CoderiveParser.SlotMethodCallStatementContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterSlotMethodCall(CoderiveParser.SlotMethodCallContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitSlotMethodCall(CoderiveParser.SlotMethodCallContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterSlotCast(CoderiveParser.SlotCastContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitSlotCast(CoderiveParser.SlotCastContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterInputAssignment(CoderiveParser.InputAssignmentContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitInputAssignment(CoderiveParser.InputAssignmentContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterInputStatement(CoderiveParser.InputStatementContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitInputStatement(CoderiveParser.InputStatementContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterTypeInput(CoderiveParser.TypeInputContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitTypeInput(CoderiveParser.TypeInputContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterOutputStatement(CoderiveParser.OutputStatementContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitOutputStatement(CoderiveParser.OutputStatementContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterOutputSlotCall(CoderiveParser.OutputSlotCallContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitOutputSlotCall(CoderiveParser.OutputSlotCallContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterOutputNamedAssignment(CoderiveParser.OutputNamedAssignmentContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitOutputNamedAssignment(CoderiveParser.OutputNamedAssignmentContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterOutputExpression(CoderiveParser.OutputExpressionContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitOutputExpression(CoderiveParser.OutputExpressionContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterMethodCallStatement(CoderiveParser.MethodCallStatementContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitMethodCallStatement(CoderiveParser.MethodCallStatementContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterMethodCall(CoderiveParser.MethodCallContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitMethodCall(CoderiveParser.MethodCallContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterArgumentList(CoderiveParser.ArgumentListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitArgumentList(CoderiveParser.ArgumentListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterIfStatement(CoderiveParser.IfStatementContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitIfStatement(CoderiveParser.IfStatementContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterThenBlock(CoderiveParser.ThenBlockContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitThenBlock(CoderiveParser.ThenBlockContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterElseBlock(CoderiveParser.ElseBlockContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitElseBlock(CoderiveParser.ElseBlockContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterForStatement(CoderiveParser.ForStatementContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitForStatement(CoderiveParser.ForStatementContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterOperatorStep(CoderiveParser.OperatorStepContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitOperatorStep(CoderiveParser.OperatorStepContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterRegularStep(CoderiveParser.RegularStepContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitRegularStep(CoderiveParser.RegularStepContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterMethodCallExpr(CoderiveParser.MethodCallExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitMethodCallExpr(CoderiveParser.MethodCallExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterUnaryExpr(CoderiveParser.UnaryExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitUnaryExpr(CoderiveParser.UnaryExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterPrimaryExpr(CoderiveParser.PrimaryExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitPrimaryExpr(CoderiveParser.PrimaryExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterComparisonExpr(CoderiveParser.ComparisonExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitComparisonExpr(CoderiveParser.ComparisonExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterTypeCastExpr(CoderiveParser.TypeCastExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitTypeCastExpr(CoderiveParser.TypeCastExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterAdditiveExpr(CoderiveParser.AdditiveExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitAdditiveExpr(CoderiveParser.AdditiveExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterMultiplicativeExpr(CoderiveParser.MultiplicativeExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitMultiplicativeExpr(CoderiveParser.MultiplicativeExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterIndexAccessExpr(CoderiveParser.IndexAccessExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitIndexAccessExpr(CoderiveParser.IndexAccessExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterIdentifierExpr(CoderiveParser.IdentifierExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitIdentifierExpr(CoderiveParser.IdentifierExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterIntLiteralExpr(CoderiveParser.IntLiteralExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitIntLiteralExpr(CoderiveParser.IntLiteralExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterFloatLiteralExpr(CoderiveParser.FloatLiteralExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitFloatLiteralExpr(CoderiveParser.FloatLiteralExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterStringLiteralExpr(CoderiveParser.StringLiteralExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitStringLiteralExpr(CoderiveParser.StringLiteralExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterBoolLiteralExpr(CoderiveParser.BoolLiteralExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitBoolLiteralExpr(CoderiveParser.BoolLiteralExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterParenthesizedExpr(CoderiveParser.ParenthesizedExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitParenthesizedExpr(CoderiveParser.ParenthesizedExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterArrayLiteralExpr(CoderiveParser.ArrayLiteralExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitArrayLiteralExpr(CoderiveParser.ArrayLiteralExprContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterArrayLiteral(CoderiveParser.ArrayLiteralContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitArrayLiteral(CoderiveParser.ArrayLiteralContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterExprList(CoderiveParser.ExprListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitExprList(CoderiveParser.ExprListContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterIndexAccess(CoderiveParser.IndexAccessContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitIndexAccess(CoderiveParser.IndexAccessContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterTypeCast(CoderiveParser.TypeCastContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitTypeCast(CoderiveParser.TypeCastContext ctx) { } - - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void enterEveryRule(ParserRuleContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void exitEveryRule(ParserRuleContext ctx) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void visitTerminal(TerminalNode node) { } - /** - * {@inheritDoc} - * - *

The default implementation does nothing.

- */ - @Override public void visitErrorNode(ErrorNode node) { } -} \ No newline at end of file diff --git a/src/main/java/cod/ast/CoderiveParserBaseVisitor.java b/src/main/java/cod/ast/CoderiveParserBaseVisitor.java deleted file mode 100644 index 40eb6049..00000000 --- a/src/main/java/cod/ast/CoderiveParserBaseVisitor.java +++ /dev/null @@ -1,444 +0,0 @@ -// Generated from CoderiveParser.g4 by ANTLR 4.13.2 - -package cod.ast; - -import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; - -/** - * This class provides an empty implementation of {@link CoderiveParserVisitor}, - * which can be extended to create a visitor which only needs to handle a subset - * of the available methods. - * - * @param The return type of the visit operation. Use {@link Void} for - * operations with no return type. - */ -@SuppressWarnings("CheckReturnValue") -public class CoderiveParserBaseVisitor extends AbstractParseTreeVisitor implements CoderiveParserVisitor { - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitProgram(CoderiveParser.ProgramContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitUnitDeclaration(CoderiveParser.UnitDeclarationContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitQualifiedNameList(CoderiveParser.QualifiedNameListContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitQualifiedName(CoderiveParser.QualifiedNameContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitTypeDeclaration(CoderiveParser.TypeDeclarationContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitModifiers(CoderiveParser.ModifiersContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitTypeBody(CoderiveParser.TypeBodyContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitFieldDeclaration(CoderiveParser.FieldDeclarationContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitConstructor(CoderiveParser.ConstructorContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitMethodDeclaration(CoderiveParser.MethodDeclarationContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitIdList(CoderiveParser.IdListContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitSlotList(CoderiveParser.SlotListContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitParameterList(CoderiveParser.ParameterListContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitParameter(CoderiveParser.ParameterContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitType(CoderiveParser.TypeContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitSimpleType(CoderiveParser.SimpleTypeContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitStatement(CoderiveParser.StatementContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitVariableDeclaration(CoderiveParser.VariableDeclarationContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitAssignment(CoderiveParser.AssignmentContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitExpressionStatement(CoderiveParser.ExpressionStatementContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitAssignable(CoderiveParser.AssignableContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitReturnSlotAssignment(CoderiveParser.ReturnSlotAssignmentContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitAssignableList(CoderiveParser.AssignableListContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitSlotMethodCallStatement(CoderiveParser.SlotMethodCallStatementContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitSlotMethodCall(CoderiveParser.SlotMethodCallContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitSlotCast(CoderiveParser.SlotCastContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitInputAssignment(CoderiveParser.InputAssignmentContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitInputStatement(CoderiveParser.InputStatementContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitTypeInput(CoderiveParser.TypeInputContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitOutputStatement(CoderiveParser.OutputStatementContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitOutputSlotCall(CoderiveParser.OutputSlotCallContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitOutputNamedAssignment(CoderiveParser.OutputNamedAssignmentContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitOutputExpression(CoderiveParser.OutputExpressionContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitMethodCallStatement(CoderiveParser.MethodCallStatementContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitMethodCall(CoderiveParser.MethodCallContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitArgumentList(CoderiveParser.ArgumentListContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitIfStatement(CoderiveParser.IfStatementContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitThenBlock(CoderiveParser.ThenBlockContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitElseBlock(CoderiveParser.ElseBlockContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitForStatement(CoderiveParser.ForStatementContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitOperatorStep(CoderiveParser.OperatorStepContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitRegularStep(CoderiveParser.RegularStepContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitMethodCallExpr(CoderiveParser.MethodCallExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitUnaryExpr(CoderiveParser.UnaryExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitPrimaryExpr(CoderiveParser.PrimaryExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitComparisonExpr(CoderiveParser.ComparisonExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitTypeCastExpr(CoderiveParser.TypeCastExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitAdditiveExpr(CoderiveParser.AdditiveExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitMultiplicativeExpr(CoderiveParser.MultiplicativeExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitIndexAccessExpr(CoderiveParser.IndexAccessExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitIdentifierExpr(CoderiveParser.IdentifierExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitIntLiteralExpr(CoderiveParser.IntLiteralExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitFloatLiteralExpr(CoderiveParser.FloatLiteralExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitStringLiteralExpr(CoderiveParser.StringLiteralExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitBoolLiteralExpr(CoderiveParser.BoolLiteralExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitParenthesizedExpr(CoderiveParser.ParenthesizedExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitArrayLiteralExpr(CoderiveParser.ArrayLiteralExprContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitArrayLiteral(CoderiveParser.ArrayLiteralContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitExprList(CoderiveParser.ExprListContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitIndexAccess(CoderiveParser.IndexAccessContext ctx) { return visitChildren(ctx); } - /** - * {@inheritDoc} - * - *

The default implementation returns the result of calling - * {@link #visitChildren} on {@code ctx}.

- */ - @Override public T visitTypeCast(CoderiveParser.TypeCastContext ctx) { return visitChildren(ctx); } -} \ No newline at end of file diff --git a/src/main/java/cod/ast/CoderiveParserListener.java b/src/main/java/cod/ast/CoderiveParserListener.java deleted file mode 100644 index 8f1eb925..00000000 --- a/src/main/java/cod/ast/CoderiveParserListener.java +++ /dev/null @@ -1,662 +0,0 @@ -// Generated from CoderiveParser.g4 by ANTLR 4.13.2 - -package cod.ast; - -import org.antlr.v4.runtime.tree.ParseTreeListener; - -/** - * This interface defines a complete listener for a parse tree produced by - * {@link CoderiveParser}. - */ -public interface CoderiveParserListener extends ParseTreeListener { - /** - * Enter a parse tree produced by {@link CoderiveParser#program}. - * @param ctx the parse tree - */ - void enterProgram(CoderiveParser.ProgramContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#program}. - * @param ctx the parse tree - */ - void exitProgram(CoderiveParser.ProgramContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#unitDeclaration}. - * @param ctx the parse tree - */ - void enterUnitDeclaration(CoderiveParser.UnitDeclarationContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#unitDeclaration}. - * @param ctx the parse tree - */ - void exitUnitDeclaration(CoderiveParser.UnitDeclarationContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#qualifiedNameList}. - * @param ctx the parse tree - */ - void enterQualifiedNameList(CoderiveParser.QualifiedNameListContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#qualifiedNameList}. - * @param ctx the parse tree - */ - void exitQualifiedNameList(CoderiveParser.QualifiedNameListContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#qualifiedName}. - * @param ctx the parse tree - */ - void enterQualifiedName(CoderiveParser.QualifiedNameContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#qualifiedName}. - * @param ctx the parse tree - */ - void exitQualifiedName(CoderiveParser.QualifiedNameContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#typeDeclaration}. - * @param ctx the parse tree - */ - void enterTypeDeclaration(CoderiveParser.TypeDeclarationContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#typeDeclaration}. - * @param ctx the parse tree - */ - void exitTypeDeclaration(CoderiveParser.TypeDeclarationContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#modifiers}. - * @param ctx the parse tree - */ - void enterModifiers(CoderiveParser.ModifiersContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#modifiers}. - * @param ctx the parse tree - */ - void exitModifiers(CoderiveParser.ModifiersContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#typeBody}. - * @param ctx the parse tree - */ - void enterTypeBody(CoderiveParser.TypeBodyContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#typeBody}. - * @param ctx the parse tree - */ - void exitTypeBody(CoderiveParser.TypeBodyContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#fieldDeclaration}. - * @param ctx the parse tree - */ - void enterFieldDeclaration(CoderiveParser.FieldDeclarationContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#fieldDeclaration}. - * @param ctx the parse tree - */ - void exitFieldDeclaration(CoderiveParser.FieldDeclarationContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#constructor}. - * @param ctx the parse tree - */ - void enterConstructor(CoderiveParser.ConstructorContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#constructor}. - * @param ctx the parse tree - */ - void exitConstructor(CoderiveParser.ConstructorContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#methodDeclaration}. - * @param ctx the parse tree - */ - void enterMethodDeclaration(CoderiveParser.MethodDeclarationContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#methodDeclaration}. - * @param ctx the parse tree - */ - void exitMethodDeclaration(CoderiveParser.MethodDeclarationContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#idList}. - * @param ctx the parse tree - */ - void enterIdList(CoderiveParser.IdListContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#idList}. - * @param ctx the parse tree - */ - void exitIdList(CoderiveParser.IdListContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#slotList}. - * @param ctx the parse tree - */ - void enterSlotList(CoderiveParser.SlotListContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#slotList}. - * @param ctx the parse tree - */ - void exitSlotList(CoderiveParser.SlotListContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#parameterList}. - * @param ctx the parse tree - */ - void enterParameterList(CoderiveParser.ParameterListContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#parameterList}. - * @param ctx the parse tree - */ - void exitParameterList(CoderiveParser.ParameterListContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#parameter}. - * @param ctx the parse tree - */ - void enterParameter(CoderiveParser.ParameterContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#parameter}. - * @param ctx the parse tree - */ - void exitParameter(CoderiveParser.ParameterContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#type}. - * @param ctx the parse tree - */ - void enterType(CoderiveParser.TypeContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#type}. - * @param ctx the parse tree - */ - void exitType(CoderiveParser.TypeContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#simpleType}. - * @param ctx the parse tree - */ - void enterSimpleType(CoderiveParser.SimpleTypeContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#simpleType}. - * @param ctx the parse tree - */ - void exitSimpleType(CoderiveParser.SimpleTypeContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#statement}. - * @param ctx the parse tree - */ - void enterStatement(CoderiveParser.StatementContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#statement}. - * @param ctx the parse tree - */ - void exitStatement(CoderiveParser.StatementContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#variableDeclaration}. - * @param ctx the parse tree - */ - void enterVariableDeclaration(CoderiveParser.VariableDeclarationContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#variableDeclaration}. - * @param ctx the parse tree - */ - void exitVariableDeclaration(CoderiveParser.VariableDeclarationContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#assignment}. - * @param ctx the parse tree - */ - void enterAssignment(CoderiveParser.AssignmentContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#assignment}. - * @param ctx the parse tree - */ - void exitAssignment(CoderiveParser.AssignmentContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#expressionStatement}. - * @param ctx the parse tree - */ - void enterExpressionStatement(CoderiveParser.ExpressionStatementContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#expressionStatement}. - * @param ctx the parse tree - */ - void exitExpressionStatement(CoderiveParser.ExpressionStatementContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#assignable}. - * @param ctx the parse tree - */ - void enterAssignable(CoderiveParser.AssignableContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#assignable}. - * @param ctx the parse tree - */ - void exitAssignable(CoderiveParser.AssignableContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#returnSlotAssignment}. - * @param ctx the parse tree - */ - void enterReturnSlotAssignment(CoderiveParser.ReturnSlotAssignmentContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#returnSlotAssignment}. - * @param ctx the parse tree - */ - void exitReturnSlotAssignment(CoderiveParser.ReturnSlotAssignmentContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#assignableList}. - * @param ctx the parse tree - */ - void enterAssignableList(CoderiveParser.AssignableListContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#assignableList}. - * @param ctx the parse tree - */ - void exitAssignableList(CoderiveParser.AssignableListContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#slotMethodCallStatement}. - * @param ctx the parse tree - */ - void enterSlotMethodCallStatement(CoderiveParser.SlotMethodCallStatementContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#slotMethodCallStatement}. - * @param ctx the parse tree - */ - void exitSlotMethodCallStatement(CoderiveParser.SlotMethodCallStatementContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#slotMethodCall}. - * @param ctx the parse tree - */ - void enterSlotMethodCall(CoderiveParser.SlotMethodCallContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#slotMethodCall}. - * @param ctx the parse tree - */ - void exitSlotMethodCall(CoderiveParser.SlotMethodCallContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#slotCast}. - * @param ctx the parse tree - */ - void enterSlotCast(CoderiveParser.SlotCastContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#slotCast}. - * @param ctx the parse tree - */ - void exitSlotCast(CoderiveParser.SlotCastContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#inputAssignment}. - * @param ctx the parse tree - */ - void enterInputAssignment(CoderiveParser.InputAssignmentContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#inputAssignment}. - * @param ctx the parse tree - */ - void exitInputAssignment(CoderiveParser.InputAssignmentContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#inputStatement}. - * @param ctx the parse tree - */ - void enterInputStatement(CoderiveParser.InputStatementContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#inputStatement}. - * @param ctx the parse tree - */ - void exitInputStatement(CoderiveParser.InputStatementContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#typeInput}. - * @param ctx the parse tree - */ - void enterTypeInput(CoderiveParser.TypeInputContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#typeInput}. - * @param ctx the parse tree - */ - void exitTypeInput(CoderiveParser.TypeInputContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#outputStatement}. - * @param ctx the parse tree - */ - void enterOutputStatement(CoderiveParser.OutputStatementContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#outputStatement}. - * @param ctx the parse tree - */ - void exitOutputStatement(CoderiveParser.OutputStatementContext ctx); - /** - * Enter a parse tree produced by the {@code outputSlotCall} - * labeled alternative in {@link CoderiveParser#outputTarget}. - * @param ctx the parse tree - */ - void enterOutputSlotCall(CoderiveParser.OutputSlotCallContext ctx); - /** - * Exit a parse tree produced by the {@code outputSlotCall} - * labeled alternative in {@link CoderiveParser#outputTarget}. - * @param ctx the parse tree - */ - void exitOutputSlotCall(CoderiveParser.OutputSlotCallContext ctx); - /** - * Enter a parse tree produced by the {@code outputNamedAssignment} - * labeled alternative in {@link CoderiveParser#outputTarget}. - * @param ctx the parse tree - */ - void enterOutputNamedAssignment(CoderiveParser.OutputNamedAssignmentContext ctx); - /** - * Exit a parse tree produced by the {@code outputNamedAssignment} - * labeled alternative in {@link CoderiveParser#outputTarget}. - * @param ctx the parse tree - */ - void exitOutputNamedAssignment(CoderiveParser.OutputNamedAssignmentContext ctx); - /** - * Enter a parse tree produced by the {@code outputExpression} - * labeled alternative in {@link CoderiveParser#outputTarget}. - * @param ctx the parse tree - */ - void enterOutputExpression(CoderiveParser.OutputExpressionContext ctx); - /** - * Exit a parse tree produced by the {@code outputExpression} - * labeled alternative in {@link CoderiveParser#outputTarget}. - * @param ctx the parse tree - */ - void exitOutputExpression(CoderiveParser.OutputExpressionContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#methodCallStatement}. - * @param ctx the parse tree - */ - void enterMethodCallStatement(CoderiveParser.MethodCallStatementContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#methodCallStatement}. - * @param ctx the parse tree - */ - void exitMethodCallStatement(CoderiveParser.MethodCallStatementContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#methodCall}. - * @param ctx the parse tree - */ - void enterMethodCall(CoderiveParser.MethodCallContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#methodCall}. - * @param ctx the parse tree - */ - void exitMethodCall(CoderiveParser.MethodCallContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#argumentList}. - * @param ctx the parse tree - */ - void enterArgumentList(CoderiveParser.ArgumentListContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#argumentList}. - * @param ctx the parse tree - */ - void exitArgumentList(CoderiveParser.ArgumentListContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#ifStatement}. - * @param ctx the parse tree - */ - void enterIfStatement(CoderiveParser.IfStatementContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#ifStatement}. - * @param ctx the parse tree - */ - void exitIfStatement(CoderiveParser.IfStatementContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#thenBlock}. - * @param ctx the parse tree - */ - void enterThenBlock(CoderiveParser.ThenBlockContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#thenBlock}. - * @param ctx the parse tree - */ - void exitThenBlock(CoderiveParser.ThenBlockContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#elseBlock}. - * @param ctx the parse tree - */ - void enterElseBlock(CoderiveParser.ElseBlockContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#elseBlock}. - * @param ctx the parse tree - */ - void exitElseBlock(CoderiveParser.ElseBlockContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#forStatement}. - * @param ctx the parse tree - */ - void enterForStatement(CoderiveParser.ForStatementContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#forStatement}. - * @param ctx the parse tree - */ - void exitForStatement(CoderiveParser.ForStatementContext ctx); - /** - * Enter a parse tree produced by the {@code operatorStep} - * labeled alternative in {@link CoderiveParser#forStepExpr}. - * @param ctx the parse tree - */ - void enterOperatorStep(CoderiveParser.OperatorStepContext ctx); - /** - * Exit a parse tree produced by the {@code operatorStep} - * labeled alternative in {@link CoderiveParser#forStepExpr}. - * @param ctx the parse tree - */ - void exitOperatorStep(CoderiveParser.OperatorStepContext ctx); - /** - * Enter a parse tree produced by the {@code regularStep} - * labeled alternative in {@link CoderiveParser#forStepExpr}. - * @param ctx the parse tree - */ - void enterRegularStep(CoderiveParser.RegularStepContext ctx); - /** - * Exit a parse tree produced by the {@code regularStep} - * labeled alternative in {@link CoderiveParser#forStepExpr}. - * @param ctx the parse tree - */ - void exitRegularStep(CoderiveParser.RegularStepContext ctx); - /** - * Enter a parse tree produced by the {@code methodCallExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - */ - void enterMethodCallExpr(CoderiveParser.MethodCallExprContext ctx); - /** - * Exit a parse tree produced by the {@code methodCallExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - */ - void exitMethodCallExpr(CoderiveParser.MethodCallExprContext ctx); - /** - * Enter a parse tree produced by the {@code unaryExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - */ - void enterUnaryExpr(CoderiveParser.UnaryExprContext ctx); - /** - * Exit a parse tree produced by the {@code unaryExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - */ - void exitUnaryExpr(CoderiveParser.UnaryExprContext ctx); - /** - * Enter a parse tree produced by the {@code primaryExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - */ - void enterPrimaryExpr(CoderiveParser.PrimaryExprContext ctx); - /** - * Exit a parse tree produced by the {@code primaryExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - */ - void exitPrimaryExpr(CoderiveParser.PrimaryExprContext ctx); - /** - * Enter a parse tree produced by the {@code comparisonExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - */ - void enterComparisonExpr(CoderiveParser.ComparisonExprContext ctx); - /** - * Exit a parse tree produced by the {@code comparisonExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - */ - void exitComparisonExpr(CoderiveParser.ComparisonExprContext ctx); - /** - * Enter a parse tree produced by the {@code typeCastExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - */ - void enterTypeCastExpr(CoderiveParser.TypeCastExprContext ctx); - /** - * Exit a parse tree produced by the {@code typeCastExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - */ - void exitTypeCastExpr(CoderiveParser.TypeCastExprContext ctx); - /** - * Enter a parse tree produced by the {@code additiveExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - */ - void enterAdditiveExpr(CoderiveParser.AdditiveExprContext ctx); - /** - * Exit a parse tree produced by the {@code additiveExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - */ - void exitAdditiveExpr(CoderiveParser.AdditiveExprContext ctx); - /** - * Enter a parse tree produced by the {@code multiplicativeExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - */ - void enterMultiplicativeExpr(CoderiveParser.MultiplicativeExprContext ctx); - /** - * Exit a parse tree produced by the {@code multiplicativeExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - */ - void exitMultiplicativeExpr(CoderiveParser.MultiplicativeExprContext ctx); - /** - * Enter a parse tree produced by the {@code indexAccessExpr} - * labeled alternative in {@link CoderiveParser#primary}. - * @param ctx the parse tree - */ - void enterIndexAccessExpr(CoderiveParser.IndexAccessExprContext ctx); - /** - * Exit a parse tree produced by the {@code indexAccessExpr} - * labeled alternative in {@link CoderiveParser#primary}. - * @param ctx the parse tree - */ - void exitIndexAccessExpr(CoderiveParser.IndexAccessExprContext ctx); - /** - * Enter a parse tree produced by the {@code identifierExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - */ - void enterIdentifierExpr(CoderiveParser.IdentifierExprContext ctx); - /** - * Exit a parse tree produced by the {@code identifierExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - */ - void exitIdentifierExpr(CoderiveParser.IdentifierExprContext ctx); - /** - * Enter a parse tree produced by the {@code intLiteralExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - */ - void enterIntLiteralExpr(CoderiveParser.IntLiteralExprContext ctx); - /** - * Exit a parse tree produced by the {@code intLiteralExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - */ - void exitIntLiteralExpr(CoderiveParser.IntLiteralExprContext ctx); - /** - * Enter a parse tree produced by the {@code floatLiteralExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - */ - void enterFloatLiteralExpr(CoderiveParser.FloatLiteralExprContext ctx); - /** - * Exit a parse tree produced by the {@code floatLiteralExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - */ - void exitFloatLiteralExpr(CoderiveParser.FloatLiteralExprContext ctx); - /** - * Enter a parse tree produced by the {@code stringLiteralExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - */ - void enterStringLiteralExpr(CoderiveParser.StringLiteralExprContext ctx); - /** - * Exit a parse tree produced by the {@code stringLiteralExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - */ - void exitStringLiteralExpr(CoderiveParser.StringLiteralExprContext ctx); - /** - * Enter a parse tree produced by the {@code boolLiteralExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - */ - void enterBoolLiteralExpr(CoderiveParser.BoolLiteralExprContext ctx); - /** - * Exit a parse tree produced by the {@code boolLiteralExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - */ - void exitBoolLiteralExpr(CoderiveParser.BoolLiteralExprContext ctx); - /** - * Enter a parse tree produced by the {@code parenthesizedExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - */ - void enterParenthesizedExpr(CoderiveParser.ParenthesizedExprContext ctx); - /** - * Exit a parse tree produced by the {@code parenthesizedExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - */ - void exitParenthesizedExpr(CoderiveParser.ParenthesizedExprContext ctx); - /** - * Enter a parse tree produced by the {@code arrayLiteralExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - */ - void enterArrayLiteralExpr(CoderiveParser.ArrayLiteralExprContext ctx); - /** - * Exit a parse tree produced by the {@code arrayLiteralExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - */ - void exitArrayLiteralExpr(CoderiveParser.ArrayLiteralExprContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#arrayLiteral}. - * @param ctx the parse tree - */ - void enterArrayLiteral(CoderiveParser.ArrayLiteralContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#arrayLiteral}. - * @param ctx the parse tree - */ - void exitArrayLiteral(CoderiveParser.ArrayLiteralContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#exprList}. - * @param ctx the parse tree - */ - void enterExprList(CoderiveParser.ExprListContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#exprList}. - * @param ctx the parse tree - */ - void exitExprList(CoderiveParser.ExprListContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#indexAccess}. - * @param ctx the parse tree - */ - void enterIndexAccess(CoderiveParser.IndexAccessContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#indexAccess}. - * @param ctx the parse tree - */ - void exitIndexAccess(CoderiveParser.IndexAccessContext ctx); - /** - * Enter a parse tree produced by {@link CoderiveParser#typeCast}. - * @param ctx the parse tree - */ - void enterTypeCast(CoderiveParser.TypeCastContext ctx); - /** - * Exit a parse tree produced by {@link CoderiveParser#typeCast}. - * @param ctx the parse tree - */ - void exitTypeCast(CoderiveParser.TypeCastContext ctx); -} \ No newline at end of file diff --git a/src/main/java/cod/ast/CoderiveParserVisitor.java b/src/main/java/cod/ast/CoderiveParserVisitor.java deleted file mode 100644 index 7cdd572d..00000000 --- a/src/main/java/cod/ast/CoderiveParserVisitor.java +++ /dev/null @@ -1,401 +0,0 @@ -// Generated from CoderiveParser.g4 by ANTLR 4.13.2 - -package cod.ast; - -import org.antlr.v4.runtime.tree.ParseTreeVisitor; - -/** - * This interface defines a complete generic visitor for a parse tree produced - * by {@link CoderiveParser}. - * - * @param The return type of the visit operation. Use {@link Void} for - * operations with no return type. - */ -public interface CoderiveParserVisitor extends ParseTreeVisitor { - /** - * Visit a parse tree produced by {@link CoderiveParser#program}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitProgram(CoderiveParser.ProgramContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#unitDeclaration}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitUnitDeclaration(CoderiveParser.UnitDeclarationContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#qualifiedNameList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitQualifiedNameList(CoderiveParser.QualifiedNameListContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#qualifiedName}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitQualifiedName(CoderiveParser.QualifiedNameContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#typeDeclaration}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitTypeDeclaration(CoderiveParser.TypeDeclarationContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#modifiers}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitModifiers(CoderiveParser.ModifiersContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#typeBody}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitTypeBody(CoderiveParser.TypeBodyContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#fieldDeclaration}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitFieldDeclaration(CoderiveParser.FieldDeclarationContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#constructor}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitConstructor(CoderiveParser.ConstructorContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#methodDeclaration}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitMethodDeclaration(CoderiveParser.MethodDeclarationContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#idList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitIdList(CoderiveParser.IdListContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#slotList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitSlotList(CoderiveParser.SlotListContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#parameterList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitParameterList(CoderiveParser.ParameterListContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#parameter}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitParameter(CoderiveParser.ParameterContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#type}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitType(CoderiveParser.TypeContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#simpleType}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitSimpleType(CoderiveParser.SimpleTypeContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#statement}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitStatement(CoderiveParser.StatementContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#variableDeclaration}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitVariableDeclaration(CoderiveParser.VariableDeclarationContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#assignment}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitAssignment(CoderiveParser.AssignmentContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#expressionStatement}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitExpressionStatement(CoderiveParser.ExpressionStatementContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#assignable}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitAssignable(CoderiveParser.AssignableContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#returnSlotAssignment}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitReturnSlotAssignment(CoderiveParser.ReturnSlotAssignmentContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#assignableList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitAssignableList(CoderiveParser.AssignableListContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#slotMethodCallStatement}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitSlotMethodCallStatement(CoderiveParser.SlotMethodCallStatementContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#slotMethodCall}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitSlotMethodCall(CoderiveParser.SlotMethodCallContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#slotCast}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitSlotCast(CoderiveParser.SlotCastContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#inputAssignment}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitInputAssignment(CoderiveParser.InputAssignmentContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#inputStatement}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitInputStatement(CoderiveParser.InputStatementContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#typeInput}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitTypeInput(CoderiveParser.TypeInputContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#outputStatement}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitOutputStatement(CoderiveParser.OutputStatementContext ctx); - /** - * Visit a parse tree produced by the {@code outputSlotCall} - * labeled alternative in {@link CoderiveParser#outputTarget}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitOutputSlotCall(CoderiveParser.OutputSlotCallContext ctx); - /** - * Visit a parse tree produced by the {@code outputNamedAssignment} - * labeled alternative in {@link CoderiveParser#outputTarget}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitOutputNamedAssignment(CoderiveParser.OutputNamedAssignmentContext ctx); - /** - * Visit a parse tree produced by the {@code outputExpression} - * labeled alternative in {@link CoderiveParser#outputTarget}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitOutputExpression(CoderiveParser.OutputExpressionContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#methodCallStatement}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitMethodCallStatement(CoderiveParser.MethodCallStatementContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#methodCall}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitMethodCall(CoderiveParser.MethodCallContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#argumentList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitArgumentList(CoderiveParser.ArgumentListContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#ifStatement}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitIfStatement(CoderiveParser.IfStatementContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#thenBlock}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitThenBlock(CoderiveParser.ThenBlockContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#elseBlock}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitElseBlock(CoderiveParser.ElseBlockContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#forStatement}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitForStatement(CoderiveParser.ForStatementContext ctx); - /** - * Visit a parse tree produced by the {@code operatorStep} - * labeled alternative in {@link CoderiveParser#forStepExpr}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitOperatorStep(CoderiveParser.OperatorStepContext ctx); - /** - * Visit a parse tree produced by the {@code regularStep} - * labeled alternative in {@link CoderiveParser#forStepExpr}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitRegularStep(CoderiveParser.RegularStepContext ctx); - /** - * Visit a parse tree produced by the {@code methodCallExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitMethodCallExpr(CoderiveParser.MethodCallExprContext ctx); - /** - * Visit a parse tree produced by the {@code unaryExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitUnaryExpr(CoderiveParser.UnaryExprContext ctx); - /** - * Visit a parse tree produced by the {@code primaryExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitPrimaryExpr(CoderiveParser.PrimaryExprContext ctx); - /** - * Visit a parse tree produced by the {@code comparisonExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitComparisonExpr(CoderiveParser.ComparisonExprContext ctx); - /** - * Visit a parse tree produced by the {@code typeCastExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitTypeCastExpr(CoderiveParser.TypeCastExprContext ctx); - /** - * Visit a parse tree produced by the {@code additiveExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitAdditiveExpr(CoderiveParser.AdditiveExprContext ctx); - /** - * Visit a parse tree produced by the {@code multiplicativeExpr} - * labeled alternative in {@link CoderiveParser#expr}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitMultiplicativeExpr(CoderiveParser.MultiplicativeExprContext ctx); - /** - * Visit a parse tree produced by the {@code indexAccessExpr} - * labeled alternative in {@link CoderiveParser#primary}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitIndexAccessExpr(CoderiveParser.IndexAccessExprContext ctx); - /** - * Visit a parse tree produced by the {@code identifierExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitIdentifierExpr(CoderiveParser.IdentifierExprContext ctx); - /** - * Visit a parse tree produced by the {@code intLiteralExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitIntLiteralExpr(CoderiveParser.IntLiteralExprContext ctx); - /** - * Visit a parse tree produced by the {@code floatLiteralExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitFloatLiteralExpr(CoderiveParser.FloatLiteralExprContext ctx); - /** - * Visit a parse tree produced by the {@code stringLiteralExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitStringLiteralExpr(CoderiveParser.StringLiteralExprContext ctx); - /** - * Visit a parse tree produced by the {@code boolLiteralExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitBoolLiteralExpr(CoderiveParser.BoolLiteralExprContext ctx); - /** - * Visit a parse tree produced by the {@code parenthesizedExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitParenthesizedExpr(CoderiveParser.ParenthesizedExprContext ctx); - /** - * Visit a parse tree produced by the {@code arrayLiteralExpr} - * labeled alternative in {@link CoderiveParser#atom}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitArrayLiteralExpr(CoderiveParser.ArrayLiteralExprContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#arrayLiteral}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitArrayLiteral(CoderiveParser.ArrayLiteralContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#exprList}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitExprList(CoderiveParser.ExprListContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#indexAccess}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitIndexAccess(CoderiveParser.IndexAccessContext ctx); - /** - * Visit a parse tree produced by {@link CoderiveParser#typeCast}. - * @param ctx the parse tree - * @return the visitor result - */ - T visitTypeCast(CoderiveParser.TypeCastContext ctx); -} \ No newline at end of file diff --git a/src/main/java/cod/ast/ConstantFolder.java b/src/main/java/cod/ast/ConstantFolder.java new file mode 100644 index 00000000..fc9ba3a1 --- /dev/null +++ b/src/main/java/cod/ast/ConstantFolder.java @@ -0,0 +1,720 @@ +package cod.ast; + +import cod.ast.nodes.*; +import java.util.ArrayList; +import java.util.List; + +/** + * Constant Folding Visitor - Optimizes expressions by evaluating constant subexpressions at compile time. + * Implements ASTVisitor to transform the AST. + * + * UPDATED: Can work with flattened expressions for better optimization. + */ +public class ConstantFolder extends BaseASTVisitor { + + private final boolean useFlattening; + + public ConstantFolder() { + this(false); + } + + public ConstantFolder(boolean useFlattening) { + this.useFlattening = useFlattening; + } + + @Override + public ASTNode visit(BinaryOpNode node) { + // First, recursively fold children + ExprNode left = (ExprNode) dispatch(node.left); + ExprNode right = (ExprNode) dispatch(node.right); + + // Apply flattening if enabled + BinaryOpNode toEvaluate; + if (useFlattening && isAssociativeOperation(node.op)) { + // Try to flatten and collect constants + List operands = new ArrayList<>(); + List constantValues = new ArrayList<>(); + List variableOperands = new ArrayList<>(); + + collectAndSeparateOperands(left, node.op, operands, constantValues, variableOperands); + collectAndSeparateOperands(right, node.op, operands, constantValues, variableOperands); + + // If we have multiple constants, combine them + if (constantValues.size() > 1) { + Object combined = combineConstants(constantValues, node.op); + if (combined != null) { + // Replace multiple constants with one combined constant + constantValues.clear(); + constantValues.add(combined); + } + } + + // Rebuild expression with combined constants + if (constantValues.size() == 1 && variableOperands.isEmpty()) { + // All operands were constants + return createConstantNode(constantValues.get(0)); + } else if (constantValues.size() == 1 && variableOperands.size() == 1) { + // One constant, one variable - create simple binary op + toEvaluate = new BinaryOpNode(); + toEvaluate.left = variableOperands.get(0); + toEvaluate.op = node.op; + toEvaluate.right = createConstantNode(constantValues.get(0)); + } else { + // Rebuild flattened structure + return rebuildFlattenedExpression(variableOperands, constantValues, node.op); + } + } else { + toEvaluate = new BinaryOpNode(); + toEvaluate.left = left; + toEvaluate.op = node.op; + toEvaluate.right = right; + } + + // If both are constant literals, try to evaluate + if (isConstantLiteral(toEvaluate.left) && isConstantLiteral(toEvaluate.right)) { + Object leftVal = toEvaluate.left.value; + Object rightVal = toEvaluate.right.value; + String op = toEvaluate.op; + + try { + Object result = evaluateBinaryOp(leftVal, rightVal, op, toEvaluate); + if (result != null) { + stats.incrementConstantExpressions(); + return createConstantNode(result); + } + } catch (ArithmeticException e) { + // Division by zero, overflow, etc. - keep original expression + // The runtime will catch it + } + } + + // Return new node with folded children + return toEvaluate; + } + + // Helper method for flattening-aware constant collection + private void collectAndSeparateOperands(ExprNode expr, String targetOp, + List allOperands, + List constants, + List variables) { + if (expr instanceof BinaryOpNode) { + BinaryOpNode binExpr = (BinaryOpNode) expr; + if (binExpr.op.equals(targetOp)) { + // This is the same operation, recurse + collectAndSeparateOperands(binExpr.left, targetOp, allOperands, constants, variables); + collectAndSeparateOperands(binExpr.right, targetOp, allOperands, constants, variables); + return; + } + } + + // Not a matching binary op, add as operand + allOperands.add(expr); + if (isConstantLiteral(expr)) { + constants.add(expr.value); + } else { + variables.add(expr); + } + } + + private Object combineConstants(List constants, String op) { + if (constants.isEmpty()) return null; + + Object result = constants.get(0); + for (int i = 1; i < constants.size(); i++) { + result = evaluateBinaryOp(result, constants.get(i), op, null); + if (result == null) { + return null; // Can't combine these constants + } + } + return result; + } + + private ExprNode rebuildFlattenedExpression(List variables, + List constants, + String op) { + List allOperands = new ArrayList<>(); + allOperands.addAll(variables); + for (Object constant : constants) { + allOperands.add(createConstantNode(constant)); + } + + // Rebuild as right-associative chain + return buildBalancedTree(allOperands, 0, allOperands.size() - 1, op); + } + + private ExprNode buildBalancedTree(List operands, int start, int end, String op) { + if (start == end) { + return operands.get(start); + } + if (start + 1 == end) { + BinaryOpNode node = new BinaryOpNode(); + node.left = operands.get(start); + node.op = op; + node.right = operands.get(end); + return node; + } + + int mid = (start + end) / 2; + BinaryOpNode node = new BinaryOpNode(); + node.left = buildBalancedTree(operands, start, mid, op); + node.op = op; + node.right = buildBalancedTree(operands, mid + 1, end, op); + return node; + } + + private boolean isAssociativeOperation(String op) { + return op.equals("+") || op.equals("*"); + } + + // ... [REST OF THE ORIGINAL ConstantFolder CODE REMAINS THE SAME] ... + // The visit methods for other node types remain unchanged from your original + + @Override + public ASTNode visit(BooleanChainNode node) { + BooleanChainNode folded = new BooleanChainNode(); + folded.isAll = node.isAll; + folded.expressions = new ArrayList<>(); + + boolean allConstants = true; + List constantValues = new ArrayList<>(); + + // First, fold all expressions + for (ExprNode expr : node.expressions) { + ExprNode foldedExpr = (ExprNode) dispatch(expr); + folded.expressions.add(foldedExpr); + + if (isConstantLiteral(foldedExpr)) { + constantValues.add(foldedExpr.value); + } else { + allConstants = false; + } + } + + // If all expressions are constants, evaluate the boolean chain + if (allConstants && !constantValues.isEmpty()) { + boolean result; + + if (node.isAll) { + // all[]: All must be true + result = true; + for (Object val : constantValues) { + if (val instanceof Boolean) { + if (!(Boolean) val) { + result = false; + break; + } + } else { + // Non-boolean in boolean chain - can't fold + return folded; + } + } + } else { + // any[]: At least one must be true + result = false; + for (Object val : constantValues) { + if (val instanceof Boolean) { + if ((Boolean) val) { + result = true; + break; + } + } else { + // Non-boolean in boolean chain - can't fold + return folded; + } + } + } + + // Create a boolean constant with the result + stats.incrementBooleanChains(); + return createConstantNode(result); + } + + return folded; + } + + @Override + public ASTNode visit(EqualityChainNode node) { + EqualityChainNode folded = new EqualityChainNode(); + folded.left = (ExprNode) dispatch(node.left); + folded.operator = node.operator; + folded.isAllChain = node.isAllChain; + folded.chainArguments = new ArrayList<>(); + + // Check if left side is constant + boolean leftConstant = isConstantLiteral(folded.left); + List rightConstants = new ArrayList<>(); + boolean allRightConstants = true; + + // Fold all chain arguments + for (ExprNode arg : node.chainArguments) { + ExprNode foldedArg = (ExprNode) dispatch(arg); + folded.chainArguments.add(foldedArg); + + if (isConstantLiteral(foldedArg)) { + rightConstants.add(foldedArg.value); + } else { + allRightConstants = false; + } + } + + // If both left and all right arguments are constants, evaluate the chain + if (leftConstant && allRightConstants && !rightConstants.isEmpty()) { + Object leftVal = folded.left.value; + boolean result; + + if (node.isAllChain) { + // all[] chain: All comparisons must be true + result = true; + for (Object rightVal : rightConstants) { + boolean comparison = evaluateComparison(leftVal, rightVal, node.operator); + if (!comparison) { + result = false; + break; + } + } + } else { + // any[] chain: At least one comparison must be true + result = false; + for (Object rightVal : rightConstants) { + boolean comparison = evaluateComparison(leftVal, rightVal, node.operator); + if (comparison) { + result = true; + break; + } + } + } + + // Create a boolean constant with the result + stats.incrementConstantExpressions(); + return createConstantNode(result); + } + + return folded; + } + + @Override + public ASTNode visit(UnaryNode node) { + ExprNode operand = (ExprNode) dispatch(node.operand); + + if (isConstantLiteral(operand)) { + Object val = operand.value; + String op = node.op; + + Object result = evaluateUnaryOp(val, op, node); + if (result != null) { + stats.incrementConstantExpressions(); + return createConstantNode(result); + } + } + + UnaryNode folded = new UnaryNode(); + folded.op = node.op; + folded.operand = operand; + return folded; + } + + @Override + public ASTNode visit(TypeCastNode node) { + ExprNode expr = (ExprNode) dispatch(node.expression); + + if (isConstantLiteral(expr)) { + Object val = expr.value; + String targetType = node.targetType; + + try { + Object result = evaluateTypeCast(val, targetType, node); + if (result != null) { + stats.incrementTypeCasts(); + return createConstantNode(result); + } + } catch (ClassCastException | NumberFormatException e) { + // Invalid cast - keep original + } + } + + TypeCastNode folded = new TypeCastNode(); + folded.targetType = node.targetType; + folded.expression = expr; + return folded; + } + + @Override + public ASTNode visit(ExprNode node) { + // For literals, just return a copy + if (node.value != null || node.isNull) { + ExprNode copy = new ExprNode(); + copy.value = node.value; + copy.isNull = node.isNull; + copy.name = node.name; + return copy; + } + + // For identifiers, cannot fold + ExprNode copy = new ExprNode(); + copy.name = node.name; + return copy; + } + + @Override + public ASTNode visit(ArrayNode node) { + ArrayNode folded = new ArrayNode(); + folded.elements = new ArrayList<>(); + + for (ExprNode elem : node.elements) { + folded.elements.add((ExprNode) dispatch(elem)); + } + return folded; + } + + @Override + public ASTNode visit(TupleNode node) { + TupleNode folded = new TupleNode(); + folded.elements = new ArrayList<>(); + + for (ExprNode elem : node.elements) { + folded.elements.add((ExprNode) dispatch(elem)); + } + return folded; + } + + @Override + public ASTNode visit(MethodCallNode node) { + // Method calls are not constant-foldable (unless they're pure builtins, + // but that's a more advanced optimization) + MethodCallNode folded = new MethodCallNode(); + folded.name = node.name; + folded.qualifiedName = node.qualifiedName; + folded.slotNames = new ArrayList<>(node.slotNames); + folded.chainType = node.chainType; + folded.arguments = new ArrayList<>(); + + for (ExprNode arg : node.arguments) { + folded.arguments.add((ExprNode) dispatch(arg)); + } + + if (node.chainArguments != null) { + folded.chainArguments = new ArrayList<>(); + for (ExprNode arg : node.chainArguments) { + folded.chainArguments.add((ExprNode) dispatch(arg)); + } + } + + return folded; + } + + @Override + public ASTNode visit(IndexAccessNode node) { + IndexAccessNode folded = new IndexAccessNode(); + folded.array = (ExprNode) dispatch(node.array); + folded.index = (ExprNode) dispatch(node.index); + return folded; + } + + // Helper methods for evaluation + + private boolean evaluateComparison(Object left, Object right, String operator) { + if (left == null || right == null) { + switch (operator) { + case "==": return left == right; + case "!=": return left != right; + default: return false; + } + } + + if (left instanceof Number && right instanceof Number) { + double l = ((Number) left).doubleValue(); + double r = ((Number) right).doubleValue(); + + switch (operator) { + case "==": return Math.abs(l - r) < 1e-12; + case "!=": return Math.abs(l - r) >= 1e-12; + case "<": return l < r; + case ">": return l > r; + case "<=": return l <= r; + case ">=": return l >= r; + default: return false; + } + } + + if (left instanceof Boolean && right instanceof Boolean) { + boolean l = (Boolean) left; + boolean r = (Boolean) right; + + switch (operator) { + case "==": return l == r; + case "!=": return l != r; + default: return false; + } + } + + if (left instanceof String && right instanceof String) { + String l = (String) left; + String r = (String) right; + + switch (operator) { + case "==": return l.equals(r); + case "!=": return !l.equals(r); + default: return false; + } + } + + return false; + } + + private Object evaluateBinaryOp(Object left, Object right, String op, BinaryOpNode node) { + // Handle numeric operations + if (left instanceof Number && right instanceof Number) { + double l = ((Number) left).doubleValue(); + double r = ((Number) right).doubleValue(); + + switch (op) { + case "+": return l + r; + case "-": return l - r; + case "*": return l * r; + case "/": + if (r == 0) throw new ArithmeticException("Division by zero"); + return l / r; + case "%": return l % r; + case "<": return l < r; + case ">": return l > r; + case "<=": return l <= r; + case ">=": return l >= r; + case "==": return Math.abs(l - r) < 1e-12; + case "!=": return Math.abs(l - r) >= 1e-12; + default: return null; + } + } + + // Handle boolean operations (only equality for booleans in your language) + if (left instanceof Boolean && right instanceof Boolean) { + boolean l = (Boolean) left; + boolean r = (Boolean) right; + + switch (op) { + case "==": return l == r; + case "!=": return l != r; + default: return null; + } + } + + // Handle string concatenation + if (op.equals("+") && (left instanceof String || right instanceof String)) { + return left.toString() + right.toString(); + } + + // Handle equality for null + if (left == null || right == null) { + switch (op) { + case "==": return left == right; + case "!=": return left != right; + default: return null; + } + } + + return null; + } + + private Object evaluateUnaryOp(Object val, String op, UnaryNode node) { + if (val instanceof Number) { + double num = ((Number) val).doubleValue(); + switch (op) { + case "+": return +num; + case "-": return -num; + default: return null; + } + } + + if (val instanceof Boolean && op.equals("!")) { + return !(Boolean) val; + } + + return null; + } + + private Object evaluateTypeCast(Object val, String targetType, TypeCastNode node) { + if (val == null) return null; + + targetType = targetType.toLowerCase(); + + try { + switch (targetType) { + case "int": + if (val instanceof Number) return ((Number) val).intValue(); + if (val instanceof String) return Integer.parseInt((String) val); + break; + + case "float": + if (val instanceof Number) return ((Number) val).floatValue(); + if (val instanceof String) return Float.parseFloat((String) val); + break; + + case "text": + return val.toString(); + + case "bool": + if (val instanceof Boolean) return val; + if (val instanceof Number) return ((Number) val).doubleValue() != 0; + if (val instanceof String) return Boolean.parseBoolean((String) val); + break; + } + } catch (NumberFormatException e) { + // Invalid cast, keep original + } + + return null; + } + + private boolean isConstantLiteral(ExprNode node) { + return node != null && (node.value != null || node.isNull); + } + + private ExprNode createConstantNode(Object value) { + ExprNode node = new ExprNode(); + + if (value == null) { + node.isNull = true; + } else if (value instanceof Integer) { + node.value = value; + } else if (value instanceof Float) { + node.value = value; + } else if (value instanceof Double) { + node.value = ((Double) value).floatValue(); + } else if (value instanceof Boolean) { + node.value = value; + } else if (value instanceof String) { + node.value = value; + } + + return node; + } + + // Pass-through methods for statements + + @Override + public ASTNode visit(AssignmentNode node) { + AssignmentNode folded = new AssignmentNode(); + folded.left = (ExprNode) dispatch(node.left); + folded.right = (ExprNode) dispatch(node.right); + return folded; + } + + @Override + public ASTNode visit(VarNode node) { + VarNode folded = new VarNode(); + folded.name = node.name; + folded.explicitType = node.explicitType; + if (node.value != null) { + folded.value = (ExprNode) dispatch(node.value); + } + return folded; + } + + @Override + public ASTNode visit(StmtIfNode node) { + StmtIfNode folded = new StmtIfNode(); + folded.condition = (ExprNode) dispatch(node.condition); + folded.thenBlock = (BlockNode) dispatch(node.thenBlock); + folded.elseBlock = (BlockNode) dispatch(node.elseBlock); + return folded; + } + + @Override + public ASTNode visit(ForNode node) { + ForNode folded = new ForNode(); + folded.iterator = node.iterator; + folded.range = (RangeNode) dispatch(node.range); + folded.body = (BlockNode) dispatch(node.body); + return folded; + } + + @Override + public ASTNode visit(RangeNode node) { + RangeNode folded = new RangeNode(); + if (node.step != null) { + folded.step = (ExprNode) dispatch(node.step); + } + folded.start = (ExprNode) dispatch(node.start); + folded.end = (ExprNode) dispatch(node.end); + return folded; + } + + @Override + public ASTNode visit(OutputNode node) { + OutputNode folded = new OutputNode(); + folded.varName = node.varName; + folded.arguments = new ArrayList<>(); + for (ExprNode arg : node.arguments) { + folded.arguments.add((ExprNode) dispatch(arg)); + } + return folded; + } + + @Override + public ASTNode visit(BlockNode node) { + BlockNode folded = new BlockNode(); + folded.statements = new ArrayList<>(); + for (StmtNode stmt : node.statements) { + folded.statements.add((StmtNode) dispatch(stmt)); + } + return folded; + } + + @Override + public ASTNode visit(SlotAssignmentNode node) { + SlotAssignmentNode folded = new SlotAssignmentNode(); + folded.slotName = node.slotName; + folded.value = (ExprNode) dispatch(node.value); + return folded; + } + + @Override + public ASTNode visit(MultipleSlotAssignmentNode node) { + MultipleSlotAssignmentNode folded = new MultipleSlotAssignmentNode(); + folded.assignments = new ArrayList<>(); + for (SlotAssignmentNode assign : node.assignments) { + folded.assignments.add((SlotAssignmentNode) dispatch(assign)); + } + return folded; + } + + @Override + public ASTNode visit(ReturnSlotAssignmentNode node) { + ReturnSlotAssignmentNode folded = new ReturnSlotAssignmentNode(); + folded.variableNames = new ArrayList<>(node.variableNames); + folded.methodCall = (MethodCallNode) dispatch(node.methodCall); + return folded; + } + + // Default pass-through for other node types + @Override + public ASTNode visit(ProgramNode node) { return node; } + @Override + public ASTNode visit(UnitNode node) { return node; } + @Override + public ASTNode visit(UseNode node) { return node; } + @Override + public ASTNode visit(TypeNode node) { return node; } + @Override + public ASTNode visit(FieldNode node) { return node; } + @Override + public ASTNode visit(MethodNode node) { return node; } + @Override + public ASTNode visit(ParamNode node) { return node; } + @Override + public ASTNode visit(ConstructorNode node) { return node; } + @Override + public ASTNode visit(InputNode node) { return node; } + @Override + public ASTNode visit(ExitNode node) { return node; } + @Override + public ASTNode visit(SlotDeclarationNode node) { return node; } + @Override + public ASTNode visit(SlotNode node) { return node; } + + // Statistics tracking + private static OptimizationStats stats = new OptimizationStats(); + + public static OptimizationStats getStats() { + return stats; + } +} \ No newline at end of file diff --git a/src/main/java/cod/ast/ExpressionFlattener.java b/src/main/java/cod/ast/ExpressionFlattener.java new file mode 100644 index 00000000..c9c64cda --- /dev/null +++ b/src/main/java/cod/ast/ExpressionFlattener.java @@ -0,0 +1,341 @@ +package cod.ast; + +import cod.ast.nodes.*; +import java.util.*; + +/** + * Flattens nested expressions to prepare for TAC generation. + * Transforms deeply nested binary operations into shallower, + * more linear forms for better optimization and code generation. + */ +public class ExpressionFlattener extends BaseASTVisitor { + + /** + * Main entry point for flattening an entire AST + */ + public ASTNode flatten(ASTNode node) { + return visit(node); + } + + // === EXPRESSION FLATTENING === + + @Override + public ASTNode visit(BinaryOpNode node) { + // First flatten children + ExprNode left = (ExprNode) dispatch(node.left); + ExprNode right = (ExprNode) dispatch(node.right); + + // Check if this is an associative operation we can flatten + if (isAssociativeOperation(node.op)) { + // Collect all operands at this precedence level + List operands = new ArrayList<>(); + collectOperands(left, node.op, operands); + collectOperands(right, node.op, operands); + + // If we have more than 2 operands, create a flattened structure + if (operands.size() > 2) { + return createFlattenedBinaryChain(operands, node.op); + } + } + + // Return regular binary node with flattened children + BinaryOpNode result = new BinaryOpNode(); + result.left = left; + result.op = node.op; + result.right = right; + return result; + } + + @Override + public ASTNode visit(UnaryNode node) { + ExprNode operand = (ExprNode) dispatch(node.operand); + + // Handle double negatives: !!x → x, -(-x) → x + if (node.op.equals("!") && operand instanceof UnaryNode) { + UnaryNode inner = (UnaryNode) operand; + if (inner.op.equals("!")) { + return inner.operand; // Remove double negation + } + } else if (node.op.equals("-") && operand instanceof UnaryNode) { + UnaryNode inner = (UnaryNode) operand; + if (inner.op.equals("-")) { + return inner.operand; // Remove double negative + } + } + + UnaryNode result = new UnaryNode(); + result.op = node.op; + result.operand = operand; + return result; + } + + @Override + public ASTNode visit(EqualityChainNode node) { + EqualityChainNode flattened = new EqualityChainNode(); + flattened.left = (ExprNode) dispatch(node.left); + flattened.operator = node.operator; + flattened.isAllChain = node.isAllChain; + flattened.chainArguments = new ArrayList<>(); + + for (ExprNode arg : node.chainArguments) { + flattened.chainArguments.add((ExprNode) dispatch(arg)); + } + return flattened; + } + + @Override + public ASTNode visit(BooleanChainNode node) { + BooleanChainNode flattened = new BooleanChainNode(); + flattened.isAll = node.isAll; + flattened.expressions = new ArrayList<>(); + + for (ExprNode expr : node.expressions) { + flattened.expressions.add((ExprNode) dispatch(expr)); + } + return flattened; + } + + @Override + public ASTNode visit(TypeCastNode node) { + TypeCastNode flattened = new TypeCastNode(); + flattened.targetType = node.targetType; + flattened.expression = (ExprNode) dispatch(node.expression); + return flattened; + } + + @Override + public ASTNode visit(MethodCallNode node) { + MethodCallNode flattened = new MethodCallNode(); + flattened.name = node.name; + flattened.qualifiedName = node.qualifiedName; + flattened.slotNames = new ArrayList<>(node.slotNames); + flattened.chainType = node.chainType; + flattened.arguments = new ArrayList<>(); + + for (ExprNode arg : node.arguments) { + flattened.arguments.add((ExprNode) dispatch(arg)); + } + + if (node.chainArguments != null) { + flattened.chainArguments = new ArrayList<>(); + for (ExprNode arg : node.chainArguments) { + flattened.chainArguments.add((ExprNode) dispatch(arg)); + } + } + + return flattened; + } + + @Override + public ASTNode visit(ArrayNode node) { + ArrayNode flattened = new ArrayNode(); + flattened.elements = new ArrayList<>(); + + for (ExprNode elem : node.elements) { + flattened.elements.add((ExprNode) dispatch(elem)); + } + return flattened; + } + + @Override + public ASTNode visit(TupleNode node) { + TupleNode flattened = new TupleNode(); + flattened.elements = new ArrayList<>(); + + for (ExprNode elem : node.elements) { + flattened.elements.add((ExprNode) dispatch(elem)); + } + return flattened; + } + + @Override + public ASTNode visit(IndexAccessNode node) { + IndexAccessNode flattened = new IndexAccessNode(); + flattened.array = (ExprNode) dispatch(node.array); + flattened.index = (ExprNode) dispatch(node.index); + return flattened; + } + + @Override + public ASTNode visit(ExprNode node) { + // For literals and identifiers, just return a copy + if (node.value != null || node.isNull || node.name != null) { + ExprNode copy = new ExprNode(); + copy.value = node.value; + copy.isNull = node.isNull; + copy.name = node.name; + return copy; + } + return node; + } + + // === HELPER METHODS === + + private boolean isAssociativeOperation(String op) { + return op.equals("+") || op.equals("*") || + op.equals("&") || op.equals("|") || // For bitwise if supported + op.equals("&&") || op.equals("||"); // For logical operations + } + + private void collectOperands(ExprNode expr, String targetOp, List operands) { + if (expr instanceof BinaryOpNode) { + BinaryOpNode binExpr = (BinaryOpNode) expr; + if (binExpr.op.equals(targetOp)) { + // This is the same operation, recurse + collectOperands(binExpr.left, targetOp, operands); + collectOperands(binExpr.right, targetOp, operands); + return; + } + } + // Not a matching binary op, add as operand + operands.add(expr); + } + + private ExprNode createFlattenedBinaryChain(List operands, String op) { + // Rebuild as right-associative chain for consistent evaluation + // This creates a balanced tree: (a + b + c + d) → ((a + b) + (c + d)) + return buildBalancedTree(operands, 0, operands.size() - 1, op); + } + + private ExprNode buildBalancedTree(List operands, int start, int end, String op) { + if (start == end) { + return operands.get(start); + } + if (start + 1 == end) { + BinaryOpNode node = new BinaryOpNode(); + node.left = operands.get(start); + node.op = op; + node.right = operands.get(end); + return node; + } + + int mid = (start + end) / 2; + BinaryOpNode node = new BinaryOpNode(); + node.left = buildBalancedTree(operands, start, mid, op); + node.op = op; + node.right = buildBalancedTree(operands, mid + 1, end, op); + return node; + } + + // === PASS-THROUGH METHODS (statements and declarations) === + + @Override + public ASTNode visit(AssignmentNode node) { + AssignmentNode flattened = new AssignmentNode(); + flattened.left = (ExprNode) dispatch(node.left); + flattened.right = (ExprNode) dispatch(node.right); + return flattened; + } + + @Override + public ASTNode visit(VarNode node) { + VarNode flattened = new VarNode(); + flattened.name = node.name; + flattened.explicitType = node.explicitType; + if (node.value != null) { + flattened.value = (ExprNode) dispatch(node.value); + } + return flattened; + } + + @Override + public ASTNode visit(StmtIfNode node) { + StmtIfNode flattened = new StmtIfNode(); + flattened.condition = (ExprNode) dispatch(node.condition); + flattened.thenBlock = (BlockNode) dispatch(node.thenBlock); + flattened.elseBlock = (BlockNode) dispatch(node.elseBlock); + return flattened; + } + + @Override + public ASTNode visit(ForNode node) { + ForNode flattened = new ForNode(); + flattened.iterator = node.iterator; + flattened.range = (RangeNode) dispatch(node.range); + flattened.body = (BlockNode) dispatch(node.body); + return flattened; + } + + @Override + public ASTNode visit(RangeNode node) { + RangeNode flattened = new RangeNode(); + if (node.step != null) { + flattened.step = (ExprNode) dispatch(node.step); + } + flattened.start = (ExprNode) dispatch(node.start); + flattened.end = (ExprNode) dispatch(node.end); + return flattened; + } + + @Override + public ASTNode visit(OutputNode node) { + OutputNode flattened = new OutputNode(); + flattened.varName = node.varName; + flattened.arguments = new ArrayList<>(); + for (ExprNode arg : node.arguments) { + flattened.arguments.add((ExprNode) dispatch(arg)); + } + return flattened; + } + + @Override + public ASTNode visit(BlockNode node) { + BlockNode flattened = new BlockNode(); + flattened.statements = new ArrayList<>(); + for (StmtNode stmt : node.statements) { + flattened.statements.add((StmtNode) dispatch(stmt)); + } + return flattened; + } + + // Default pass-through for other node types + @Override + public ASTNode visit(ProgramNode node) { return node; } + @Override + public ASTNode visit(UnitNode node) { return node; } + @Override + public ASTNode visit(UseNode node) { return node; } + @Override + public ASTNode visit(TypeNode node) { return node; } + @Override + public ASTNode visit(FieldNode node) { return node; } + @Override + public ASTNode visit(MethodNode node) { return node; } + @Override + public ASTNode visit(ParamNode node) { return node; } + @Override + public ASTNode visit(ConstructorNode node) { return node; } + @Override + public ASTNode visit(InputNode node) { return node; } + @Override + public ASTNode visit(ExitNode node) { return node; } + @Override + public ASTNode visit(SlotDeclarationNode node) { return node; } + @Override + public ASTNode visit(SlotNode node) { return node; } + @Override + public ASTNode visit(SlotAssignmentNode node) { + SlotAssignmentNode flattened = new SlotAssignmentNode(); + flattened.slotName = node.slotName; + flattened.value = (ExprNode) dispatch(node.value); + return flattened; + } + + @Override + public ASTNode visit(MultipleSlotAssignmentNode node) { + MultipleSlotAssignmentNode flattened = new MultipleSlotAssignmentNode(); + flattened.assignments = new ArrayList<>(); + for (SlotAssignmentNode assign : node.assignments) { + flattened.assignments.add((SlotAssignmentNode) dispatch(assign)); + } + return flattened; + } + + @Override + public ASTNode visit(ReturnSlotAssignmentNode node) { + ReturnSlotAssignmentNode flattened = new ReturnSlotAssignmentNode(); + flattened.variableNames = new ArrayList<>(node.variableNames); + flattened.methodCall = (MethodCallNode) dispatch(node.methodCall); + return flattened; + } +} \ No newline at end of file diff --git a/src/main/java/cod/ast/ManualCoderiveLexer.java b/src/main/java/cod/ast/ManualCoderiveLexer.java deleted file mode 100644 index 3ce3c1f4..00000000 --- a/src/main/java/cod/ast/ManualCoderiveLexer.java +++ /dev/null @@ -1,436 +0,0 @@ -package cod.ast; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static cod.Constants.*; - -public class ManualCoderiveLexer { - - public static class Token { - public final int type; - public final String text; - public final int line; - public final int column; - - public static final int EOF = -1; - - public Token(int type, String text, int line, int column) { - this.type = type; - this.text = text; - this.line = line; - this.column = column; - } - - @Override - public String toString() { - return "Token{" - + "type=" - + TokenType.getName(type) - + ", text='" - + text - + '\'' - + ", line=" - + line - + ", column=" - + column - + '}'; - } - } - - public static class TokenType { - public static final int SHARE = 1, - LOCAL = 2, - UNIT = 3, - GET = 4, - EXTEND = 5, - THIS = 6, - VAR = 7, - OUTPUT = 8, - INPUT = 9, - IF = 10, - ELSE = 11, - ELIF = 12, - FOR = 13, - IN = 14, - TO = 15, - BY = 16, - INT = 17, - STRING = 18, - FLOAT = 19, - BOOL = 20, - INT_LIT = 21, - FLOAT_LIT = 22, - STRING_LIT = 23, - BOOL_LIT = 24, - ID = 25, - ASSIGN = 26, - PLUS = 27, - MINUS = 28, - MUL = 29, - DIV = 30, - MOD = 31, - COLON = 32, - GT = 33, - LT = 34, - GTE = 35, - LTE = 36, - EQ = 37, - NEQ = 38, - DOT = 39, - COMMA = 40, - LPAREN = 41, - RPAREN = 42, - LBRACE = 43, - RBRACE = 44, - LBRACKET = 45, - RBRACKET = 46, - LINE_COMMENT = 47, - BLOCK_COMMENT = 48, - WS = 49, - PLUS_ASSIGN = 50, - MINUS_ASSIGN = 51, - MUL_ASSIGN = 52, - DIV_ASSIGN = 53, - TILDE_BAR = 54, - TILDE_ARROW = 55, - BUILTIN = 56, - ALL = 57, - ANY = 58, - BANG = 59, - INVALID = -2; - - private static final Map NAMES = new HashMap(); - - static { - NAMES.put(SHARE, _share); - NAMES.put(LOCAL, _local); - NAMES.put(UNIT, _unit); - NAMES.put(GET, _get); - NAMES.put(EXTEND, _extend); - NAMES.put(THIS, _this); - NAMES.put(VAR, _var); - NAMES.put(OUTPUT, _output); - NAMES.put(INPUT, _input); - NAMES.put(IF, _if); - NAMES.put(ELSE, _else); - NAMES.put(ELIF, _elif); - NAMES.put(FOR, _for); - NAMES.put(IN, _in); - NAMES.put(TO, _to); - NAMES.put(BY, _by); - NAMES.put(INT, _int); - NAMES.put(STRING, _string); - NAMES.put(FLOAT, _float); - NAMES.put(BOOL, _bool); - NAMES.put(BUILTIN, _builtin); - NAMES.put(ALL, _all); - NAMES.put(ANY, _any); - - NAMES.put(INT_LIT, _int_lit); - NAMES.put(FLOAT_LIT, _float_lit); - NAMES.put(STRING_LIT, _string_lit); - NAMES.put(BOOL_LIT, _bool_lit); - NAMES.put(ID, _id); - - NAMES.put(ASSIGN, _assign); - NAMES.put(PLUS, _plus); - NAMES.put(MINUS, _minus); - NAMES.put(MUL, _mul); - NAMES.put(DIV, _div); - NAMES.put(MOD, _mod); - NAMES.put(COLON, _colon); - NAMES.put(GT, _gt); - NAMES.put(LT, _lt); - NAMES.put(GTE, _gte); - NAMES.put(LTE, _lte); - NAMES.put(EQ, _eq); - NAMES.put(NEQ, _neq); - NAMES.put(DOT, _dot); - NAMES.put(COMMA, _comma); - NAMES.put(LPAREN, _lparen); - NAMES.put(RPAREN, _rparen); - NAMES.put(LBRACE, _lbrace); - NAMES.put(RBRACE, _rbrace); - NAMES.put(LBRACKET, _lbracket); - NAMES.put(RBRACKET, _rbracket); - NAMES.put(PLUS_ASSIGN, _plus_assign); - NAMES.put(MINUS_ASSIGN, _minus_assign); - NAMES.put(MUL_ASSIGN, _mul_assign); - NAMES.put(DIV_ASSIGN, _div_assign); - NAMES.put(BANG, _bang); - NAMES.put(TILDE_BAR, _tilde_bar); - NAMES.put(TILDE_ARROW, _tilde_arrow); - - NAMES.put(Token.EOF, _eof); - NAMES.put(INVALID, _invalid); - NAMES.put(LINE_COMMENT, _line_comment); - NAMES.put(BLOCK_COMMENT, _block_comment); - NAMES.put(WS, _ws); - } - - public static String getName(int type) { - String name = NAMES.get(type); - return name != null ? name : "UNKNOWN(" + type + ")"; - } - } - - private static final Map KEYWORDS = new HashMap(); - - static { - KEYWORDS.put(share, TokenType.SHARE); - KEYWORDS.put(local, TokenType.LOCAL); - KEYWORDS.put(unit, TokenType.UNIT); - KEYWORDS.put(get, TokenType.GET); - KEYWORDS.put(extend, TokenType.EXTEND); - KEYWORDS.put(this_, TokenType.THIS); - KEYWORDS.put(var, TokenType.VAR); - KEYWORDS.put(output, TokenType.OUTPUT); - KEYWORDS.put(input_, TokenType.INPUT); - KEYWORDS.put(if_, TokenType.IF); - KEYWORDS.put(else_, TokenType.ELSE); - KEYWORDS.put(elif_, TokenType.ELIF); - KEYWORDS.put(for_, TokenType.FOR); - KEYWORDS.put(in_, TokenType.IN); - KEYWORDS.put(to_, TokenType.TO); - KEYWORDS.put(by_, TokenType.BY); - KEYWORDS.put(int_, TokenType.INT); - KEYWORDS.put(string, TokenType.STRING); - KEYWORDS.put(float_, TokenType.FLOAT); - KEYWORDS.put(builtin, TokenType.BUILTIN); - KEYWORDS.put(bool, TokenType.BOOL); - KEYWORDS.put(true_, TokenType.BOOL_LIT); - KEYWORDS.put(false_, TokenType.BOOL_LIT); - KEYWORDS.put(all, TokenType.ALL); - KEYWORDS.put(any, TokenType.ANY); - } - - private final String input; - private int position = 0; - private int line = 1; - private int column = 1; - - public ManualCoderiveLexer(String input) { - this.input = input; - } - - public List tokenize() { - List tokens = new ArrayList(); - while (position < input.length()) { - skipWhitespaceAndComments(); - if (position < input.length()) { - tokens.add(scanNextToken()); - } - } - tokens.add(new Token(Token.EOF, "", line, column)); - return tokens; - } - - private void skipWhitespaceAndComments() { - while (position < input.length()) { - char c = peek(); - if (Character.isWhitespace(c)) { - consume(); - } else if (c == '/' && peek(1) == '/') { - scanLineComment(); - } else if (c == '/' && peek(1) == '*') { - scanBlockComment(); - } else { - break; - } - } - } - - private Token scanNextToken() { - int startLine = line; - int startColumn = column; - char c = peek(); - - if (Character.isLetter(c) || c == '_') - return readIdentifierOrKeyword(startLine, startColumn); - if (Character.isDigit(c)) return readNumber(startLine, startColumn); - if (c == '"') return readString(startLine, startColumn); - return readSymbol(startLine, startColumn); - } - - private Token readIdentifierOrKeyword(int startLine, int startColumn) { - StringBuilder sb = new StringBuilder(); - while (position < input.length() && (Character.isLetterOrDigit(peek()) || peek() == '_')) { - sb.append(consume()); - } - String text = sb.toString(); - Integer type = KEYWORDS.get(text); - return new Token(type != null ? type : TokenType.ID, text, startLine, startColumn); - } - - private Token readNumber(int startLine, int startColumn) { - StringBuilder sb = new StringBuilder(); - boolean isFloat = false; - while (position < input.length() && Character.isDigit(peek())) { - sb.append(consume()); - } - if (peek() == '.') { - isFloat = true; - sb.append(consume()); - while (position < input.length() && Character.isDigit(peek())) { - sb.append(consume()); - } - } - return new Token(isFloat ? TokenType.FLOAT_LIT : TokenType.INT_LIT, sb.toString(), startLine, startColumn); - } - - private Token readString(int startLine, int startColumn) { - StringBuilder sb = new StringBuilder(); - consume(); // Consume opening quote - while (position < input.length() && peek() != '"') { - if (peek() == '\\') { - consume(); // consume the backslash - char escaped = consume(); - switch (escaped) { - case 'n': sb.append('\n'); break; - case 't': sb.append('\t'); break; - case 'r': sb.append('\r'); break; - case '\\': sb.append('\\'); break; - case '"': sb.append('"'); break; - default: sb.append('\\').append(escaped); break; - } - } else { - sb.append(consume()); - } - } - if (position < input.length()) consume(); // Consume closing quote - return new Token(TokenType.STRING_LIT, sb.toString(), startLine, startColumn); - } - - private Token readSymbol(int startLine, int startColumn) { - char c1 = consume(); - switch (c1) { - case '=': - if (peek() == '=') { - consume(); - return new Token(TokenType.EQ, "==", startLine, startColumn); - } else { - return new Token(TokenType.ASSIGN, "=", startLine, startColumn); - } - case '>': - if (peek() == '=') { - consume(); - return new Token(TokenType.GTE, ">=", startLine, startColumn); - } else { - return new Token(TokenType.GT, ">", startLine, startColumn); - } - case '<': - if (peek() == '=') { - consume(); - return new Token(TokenType.LTE, "<=", startLine, startColumn); - } else { - return new Token(TokenType.LT, "<", startLine, startColumn); - } - case '!': - if (peek() == '=') { - consume(); - return new Token(TokenType.NEQ, "!=", startLine, startColumn); - } else { - return new Token(TokenType.BANG, "!", startLine, startColumn); - } - case '+': - if (peek() == '=') { - consume(); - return new Token(TokenType.PLUS_ASSIGN, "+=", startLine, startColumn); - } else { - return new Token(TokenType.PLUS, "+", startLine, startColumn); - } - case '-': - if (peek() == '=') { - consume(); - return new Token(TokenType.MINUS_ASSIGN, "-=", startLine, startColumn); - } else { - return new Token(TokenType.MINUS, "-", startLine, startColumn); - } - case '*': - if (peek() == '=') { - consume(); - return new Token(TokenType.MUL_ASSIGN, "*=", startLine, startColumn); - } else { - return new Token(TokenType.MUL, "*", startLine, startColumn); - } - case '/': - if (peek() == '=') { - consume(); - return new Token(TokenType.DIV_ASSIGN, "/=", startLine, startColumn); - } else { - return new Token(TokenType.DIV, "/", startLine, startColumn); - } - case '~': - if (peek() == '|') { - consume(); - return new Token(TokenType.TILDE_BAR, "~|", startLine, startColumn); - } else if (peek() == '>') { - consume(); - return new Token(TokenType.TILDE_ARROW, "~>", startLine, startColumn); - } else { - return new Token(TokenType.INVALID, "~", startLine, startColumn); - } - case '%': - return new Token(TokenType.MOD, "%", startLine, startColumn); - case ':': - return new Token(TokenType.COLON, ":", startLine, startColumn); - case '.': - return new Token(TokenType.DOT, ".", startLine, startColumn); - case ',': - return new Token(TokenType.COMMA, ",", startLine, startColumn); - case '(': - return new Token(TokenType.LPAREN, "(", startLine, startColumn); - case ')': - return new Token(TokenType.RPAREN, ")", startLine, startColumn); - case '{': - return new Token(TokenType.LBRACE, "{", startLine, startColumn); - case '}': - return new Token(TokenType.RBRACE, "}", startLine, startColumn); - case '[': - return new Token(TokenType.LBRACKET, "[", startLine, startColumn); - case ']': - return new Token(TokenType.RBRACKET, "]", startLine, startColumn); - default: - return new Token(TokenType.INVALID, String.valueOf(c1), startLine, startColumn); - } - } - - private void scanLineComment() { - while (position < input.length() && peek() != '\n') consume(); - } - - private void scanBlockComment() { - consume(); - consume(); // consume '/*' - while (position < input.length() - 1) { - if (peek() == '*' && peek(1) == '/') { - consume(); - consume(); - return; - } - consume(); - } - } - - private char peek() { - return peek(0); - } - - private char peek(int offset) { - return (position + offset >= input.length()) ? '\0' : input.charAt(position + offset); - } - - private char consume() { - char c = input.charAt(position++); - if (c == '\n') { - line++; - column = 1; - } else { - column++; - } - return c; - } -} \ No newline at end of file diff --git a/src/main/java/cod/ast/ManualCoderiveParser.java b/src/main/java/cod/ast/ManualCoderiveParser.java deleted file mode 100644 index c0e0d94e..00000000 --- a/src/main/java/cod/ast/ManualCoderiveParser.java +++ /dev/null @@ -1,1156 +0,0 @@ -package cod.ast; - -import cod.ast.error.ParseError; -import cod.ast.nodes.*; -import java.util.ArrayList; -import java.util.List; -import cod.ast.ManualCoderiveLexer.Token; -import static cod.ast.ManualCoderiveLexer.TokenType.*; -import static cod.Constants.*; - -public class ManualCoderiveParser { - private final List tokens; - private int position = 0; - - public ManualCoderiveParser(List tokens) { - this.tokens = tokens; - } - - private void setNodePosition(ASTNode node, Token token) { - if (node != null && token != null) { - node.setSourcePosition(token.line, token.column); - } - } - - public ProgramNode parseProgram() { - ProgramNode program = ASTFactory.createProgram(); - if (match(UNIT)) { - program.unit = parseUnit(); - } else { - program.unit = ASTFactory.createUnit("default"); - } - - while (!match(Token.EOF)) { - if (isVisibilityModifier() || isSlotDeclaration()) { - program.unit.types.add(parseType()); - } else { - throw new ParseError("Expected type declaration (share/local) or EOF but found " + - getTypeName(currentToken().type) + " ('" + currentToken().text + - "') at line " + currentToken().line + ":" + currentToken().column); - } - } - return program; - } - - public StatementNode parseSingleLine() { - if (match(Token.EOF)) { - return null; - } - - StatementNode stmt = parseStatement(); - - if (!match(Token.EOF)) { - Token current = currentToken(); - throw new ParseError("Unexpected token after statement: " + - getTypeName(current.type) + " ('" + current.text + "')" + - " at line " + current.line + ":" + current.column); - } - return stmt; - } - - private UnitNode parseUnit() { - Token unitToken = currentToken(); - consume(UNIT); - String unitName = parseQualifiedName(); - UnitNode unit = ASTFactory.createUnit(unitName); - setNodePosition(unit, unitToken); - - if (match(GET)) { - unit.imports = parseGetNode(); - } - return unit; - } - - private GetNode parseGetNode() { - Token getToken = currentToken(); - consume(GET); - consume(LBRACE); - List imports = new ArrayList(); - if (!match(RBRACE)) { - imports.add(parseQualifiedName()); - while (tryConsume(COMMA)) { - imports.add(parseQualifiedName()); - } - } - consume(RBRACE); - GetNode getNode = ASTFactory.createGetNode(imports); - setNodePosition(getNode, getToken); - return getNode; - } - - private TypeNode parseType() { - Token visibilityToken = currentToken(); - String visibility = consume(isVisibilityModifier()).text; - Token typeNameToken = currentToken(); - String typeName = consume(ID).text; - - NamingValidator.validateClassName(typeName, typeNameToken); - - String extendName = null; - if (tryConsume(EXTEND)) { - extendName = parseQualifiedName(); - } - - TypeNode type = ASTFactory.createType(typeName, visibility, extendName); - setNodePosition(type, visibilityToken); - - consume(LBRACE); - while (!match(RBRACE)) { - if (isMethodDeclaration() || isSlotDeclaration()) { - MethodNode method = parseMethod(); - type.methods.add(method); - } else if (isFieldDeclaration()) { - type.fields.add(parseField()); - } else { - type.statements.add(parseStatement()); - } - } - consume(RBRACE); - return type; - } - - private MethodNode parseMethod() { - Token startToken = currentToken(); - - List returnSlots = null; - if (isSlotDeclaration()) { - returnSlots = parseSlotContractList(); - } - - boolean isBuiltin = false; - String visibility = share; - - if (tryConsume(BUILTIN)) { - isBuiltin = true; - visibility = share; - } else if (isVisibilityModifier()) { - visibility = consume().text; - } - - String methodName = consume(ID).text; - - NamingValidator.validateMethodName(methodName, startToken); - - MethodNode method = ASTFactory.createMethod(methodName, visibility, returnSlots); - method.isBuiltin = isBuiltin; - setNodePosition(method, startToken); - - consume(LPAREN); - if (!match(RPAREN)) { - method.parameters.add(parseParameter()); - while (tryConsume(COMMA)) { - method.parameters.add(parseParameter()); - } - } - consume(RPAREN); - - if (match(TILDE_ARROW)) { - consume(TILDE_ARROW); - ExprNode returnExpr = parseExpression(); - - SlotAssignmentNode returnStmt = ASTFactory.createSlotAssignment("return", returnExpr); - method.body.add(returnStmt); - } else { - consume(LBRACE); - while (!match(RBRACE)) { - method.body.add(parseStatement()); - } - consume(RBRACE); - } - - return method; - } - - private List parseSlotContractList() { - consume(TILDE_BAR); - List slots = new ArrayList(); - - boolean firstSlot = true; - boolean isNamedMode = false; - int index = 0; - - do { - String type = parseTypeReference(); - String name; - - if (firstSlot) { - if (currentToken().type == ID) { - isNamedMode = true; - name = consume(ID).text; - } else { - isNamedMode = false; - name = String.valueOf(index); - } - firstSlot = false; - } else { - if (isNamedMode) { - if (currentToken().type != ID) { - throw new ParseError("Mixed slot declaration styles not allowed. Expected name for slot of type " + type + "."); - } - name = consume(ID).text; - } else { - if (currentToken().type == ID) { - throw new ParseError("Mixed slot declaration styles not allowed. Found name '" + currentToken().text + "' in unnamed slot list."); - } - name = String.valueOf(index); - } - } - - slots.add(ASTFactory.createSlot(type, name)); - index++; - - } while (tryConsume(COMMA)); - - return slots; - } - - private FieldNode parseField() { - Token startToken = currentToken(); - String fieldType = parseTypeReference(); - String fieldName = consume(ID).text; - - if (NamingValidator.isAllCaps(fieldName)) { - NamingValidator.validateConstantName(fieldName, startToken); - } else { - NamingValidator.validateVariableName(fieldName, startToken); - } - - FieldNode field = ASTFactory.createField(fieldName, fieldType); - setNodePosition(field, startToken); - - if (tryConsume(ASSIGN)) { - field.value = parseExpression(); - } - return field; - } - - private StatementNode parseStatement() { - return parseStatement(null); - } - - private StatementNode parseStatement(Boolean inheritedStyle) { - if (match(IF)) return parseIfStatement(inheritedStyle); - if (match(FOR)) return parseForStatement(); - if (match(OUTPUT)) return parseOutputStatement(); - - if (isSlotAssignment()) return parseSlotAssignment(); - - if (isVariableDeclaration()) return parseVariableDeclaration(); - - if (isInputAssignment()) return parseInputAssignment(); - if (isReturnSlotAssignment()) return parseReturnSlotAssignment(); - if (isIndexAssignment()) return parseIndexAssignment(); - if (isSimpleAssignment()) return parseSimpleAssignment(); - - if (isMethodCallStatement()) { - return parseMethodCallStatement(); - } - - return parseExpressionStatement(); - } - - private SlotAssignmentNode parseSingleSlotAssignment() { - Token startToken = currentToken(); - - String slotName = null; - ExprNode value; - - if (peek(0).type == ID && - (peek(1).type == ID || - peek(1).type == INT_LIT || - peek(1).type == FLOAT_LIT || - peek(1).type == STRING_LIT || - peek(1).type == BOOL_LIT || - peek(1).type == LPAREN)) { - slotName = consume(ID).text; - value = parseExpression(); - } else { - value = parseExpression(); - } - - SlotAssignmentNode assignment = ASTFactory.createSlotAssignment(slotName, value); - setNodePosition(assignment, startToken); - return assignment; - } - - private StatementNode parseSlotAssignment() { - Token startToken = currentToken(); - consume(TILDE_ARROW); - - List assignments = new ArrayList(); - - assignments.add(parseSingleSlotAssignment()); - - if (match(COMMA)) { - while (tryConsume(COMMA)) { - assignments.add(parseSingleSlotAssignment()); - } - - MultipleSlotAssignmentNode multiAssign = ASTFactory.createMultipleSlotAssignment(assignments); - setNodePosition(multiAssign, startToken); - return multiAssign; - } else { - SlotAssignmentNode assignment = assignments.get(0); - setNodePosition(assignment, startToken); - return assignment; - } - } - - private StatementNode parseSimpleAssignment() { - Token startToken = currentToken(); - ExprNode target = ASTFactory.createIdentifier(consume(ID).text); - consume(ASSIGN); - ExprNode value = parseExpression(); - AssignmentNode assignment = ASTFactory.createAssignment(target, value); - setNodePosition(assignment, startToken); - return assignment; - } - - private StatementNode parseVariableDeclaration() { - Token startToken = currentToken(); - String typeName = null; - if (tryConsume(VAR)) { - typeName = "var"; - } else if (isTypeStart(currentToken())) { - typeName = parseTypeReference(); - } else { - throw new ParseError("Internal Error: Expected 'var' or type name at start of variable declaration."); - } - - Token varNameToken = currentToken(); - String varName = consume(ID).text; - - if (NamingValidator.isAllCaps(varName)) { - NamingValidator.validateConstantName(varName, varNameToken); - } else { - NamingValidator.validateVariableName(varName, varNameToken); - } - - VarNode varNode = ASTFactory.createVar(varName, null); - setNodePosition(varNode, startToken); - varNode.explicitType = typeName; - - if (tryConsume(ASSIGN)) { - varNode.value = parseExpression(); - } - return varNode; - } - - private StatementNode parseIfStatement() { - return parseIfStatement(null); - } - - private StatementNode parseIfStatement(Boolean inheritedStyle) { - Token startToken = currentToken(); - consume(IF); - ExprNode condition = parseExpression(); - IfNode rootIfNode = ASTFactory.createIf(condition); - setNodePosition(rootIfNode, startToken); - - Boolean currentStyle = inheritedStyle; - - if (match(LBRACE)) { - consume(LBRACE); - while(!match(RBRACE)) { - rootIfNode.thenBlock.statements.add(parseStatement(currentStyle)); - } - consume(RBRACE); - } else { - rootIfNode.thenBlock.statements.add(parseStatement(currentStyle)); - } - - IfNode currentNode = rootIfNode; - - while (tryConsume(ELIF)) { - if (currentStyle != null && !currentStyle) { - throw new ParseError("Cannot use 'elif' in an 'else if' style chain"); - } - currentStyle = true; - - ExprNode elifCondition = parseExpression(); - IfNode elifNode = ASTFactory.createIf(elifCondition); - - if (match(LBRACE)) { - consume(LBRACE); - while(!match(RBRACE)) { - elifNode.thenBlock.statements.add(parseStatement(currentStyle)); - } - consume(RBRACE); - } else { - elifNode.thenBlock.statements.add(parseStatement(currentStyle)); - } - - currentNode.elseBlock.statements.add(elifNode); - currentNode = elifNode; - } - - if (tryConsume(ELSE)) { - if (match(IF)) { - if (currentStyle != null && currentStyle) { - throw new ParseError("Cannot use 'else if' in an 'elif' style chain"); - } - currentStyle = false; - - currentNode.elseBlock.statements.add(parseIfStatement(currentStyle)); - } else { - if (match(LBRACE)) { - consume(LBRACE); - while(!match(RBRACE)) { - currentNode.elseBlock.statements.add(parseStatement(currentStyle)); - } - consume(RBRACE); - } else { - currentNode.elseBlock.statements.add(parseStatement(currentStyle)); - } - } - } - - return rootIfNode; - } - - private StatementNode parseForStatement() { - Token startToken = currentToken(); - consume(FOR); - String iterator = consume(ID).text; - - ExprNode by = null; - - if (tryConsume(BY)) { - if (match(MUL, DIV)) { - Token operator = consume(); - ExprNode operand = parseExpression(); - ExprNode iteratorRef = ASTFactory.createIdentifier(iterator); - by = ASTFactory.createBinaryOp(iteratorRef, operator.text, operand); - } else if (match(PLUS, MINUS)) { - Token operator = consume(); - if (peek(0).type == INT_LIT || peek(0).type == FLOAT_LIT) { - ExprNode operand = parsePrimaryExpression(); - if (operator.type == PLUS) { - by = operand; - } else { - by = ASTFactory.createUnaryOp("-", operand); - } - } else { - position--; - by = parseExpression(); - } - } else { - if (isAssignmentInByClause()) { - ExprNode target = ASTFactory.createIdentifier(consume(ID).text); - if (!target.name.equals(iterator)) { - System.err.println("[Parser Warning] Variable in 'by' assignment ("+target.name+") doesn't match iterator ("+iterator+")."); - } - Token assignOp = consume(); - ExprNode value = parseExpression(); - by = ASTFactory.createBinaryOp(target, assignOp.text, value); - } else { - by = parseExpression(); - } - } - - consume(IN); - } else { - by = null; - consume(IN); - } - - ExprNode start = parseExpression(); - consume(TO); - ExprNode end = parseExpression(); - - RangeNode range = ASTFactory.createRange(by, start, end); - ForNode forNode = ASTFactory.createFor(iterator, range); - setNodePosition(forNode, startToken); - - consume(LBRACE); - while (!match(RBRACE)) { - forNode.body.statements.add(parseStatement()); - } - consume(RBRACE); - return forNode; - } - - private StatementNode parseOutputStatement() { - Token startToken = currentToken(); - consume(OUTPUT); - OutputNode output = ASTFactory.createOutput(); - setNodePosition(output, startToken); - output.arguments.add(parseExpression()); - return output; - } - - private StatementNode parseInputAssignment() { - Token startToken = currentToken(); - String varName = consume(ID).text; - consume(ASSIGN); - consume(LPAREN); - String type = parseTypeReference(); - consume(RPAREN); - consume(INPUT); - InputNode input = ASTFactory.createInput(type, varName); - setNodePosition(input, startToken); - return input; - } - - private StatementNode parseReturnSlotAssignment() { - Token startToken = currentToken(); - List varNames = parseIdList(); - consume(ASSIGN); - List slotNames = parseReturnSlots(); - consume(COLON); - MethodCallNode methodCall = parseMethodCall(); - methodCall.slotNames = slotNames; - - if (varNames.size() != slotNames.size()) { - throw new ParseError("Number of variables (" + varNames.size() + - ") does not match number of slots (" + slotNames.size() + ")"); - } - - ReturnSlotAssignmentNode assignment = ASTFactory.createReturnSlotAssignment(varNames, methodCall); - setNodePosition(assignment, startToken); - return assignment; - } - - private StatementNode parseIndexAssignment() { - Token startToken = currentToken(); - ExprNode arrayVar = ASTFactory.createIdentifier(consume(ID).text); - IndexAccessNode indexAccess = parseIndexAccessContinuation(arrayVar); - while(match(LBRACKET)) { - indexAccess = parseIndexAccessContinuation(indexAccess); - } - consume(ASSIGN); - ExprNode value = parseExpression(); - AssignmentNode assignment = ASTFactory.createAssignment(indexAccess, value); - setNodePosition(assignment, startToken); - return assignment; - } - - private StatementNode parseMethodCallStatement() { - Token startToken = currentToken(); - if (peek(0).type == LBRACKET) { - List slotNames = parseReturnSlots(); - consume(COLON); - MethodCallNode methodCall = parseMethodCall(); - methodCall.slotNames = slotNames; - setNodePosition(methodCall, startToken); - return methodCall; - } else { - MethodCallNode methodCall = parseMethodCall(); - setNodePosition(methodCall, startToken); - return methodCall; - } - } - - private StatementNode parseExpressionStatement() { - Token startToken = currentToken(); - ExprNode expr = parseExpression(); - setNodePosition(expr, startToken); - return expr; - } - -private ExprNode parseExpression() { - if (match(ALL, ANY)) { - return parseBooleanChain(); - } - return parseComparisonExpression(); - } - - - private ExprNode parseBooleanChain() { - Token startToken = currentToken(); - Token typeToken = consume(); - boolean isAll = typeToken.type == ALL; - if (match(ID)) { - - ExprNode arrayExpr = ASTFactory.createIdentifier(consume().text); - - if (match(EQ, NEQ, GT, LT, GTE, LTE)) { - Token op = consume(); - - ExprNode right = parseAdditiveExpression(); - - List chainArgs = new ArrayList(); - chainArgs.add(right); - - EqualityChainNode chain = ASTFactory.createEqualityChain(arrayExpr, op.text, isAll, chainArgs); - setNodePosition(chain, startToken); - return chain; - } else { - throw new ParseError("Expected comparison operator after 'all/any ' but found " + - getTypeName(currentToken().type) + " ('" + currentToken().text + - "') at line " + currentToken().line + ":" + currentToken().column); - } - } else { - consume(LBRACKET); - List expressions = new ArrayList(); - - if (!match(RBRACKET)) { - expressions.add(parseExpression()); - if (!match(COMMA) && !match(RBRACKET)) { - throw new ParseError("Boolean chain requires at least two expressions or a comma after the first expression."); - } - while (tryConsume(COMMA)) { - expressions.add(parseExpression()); - } - } - consume(RBRACKET); - BooleanChainNode node = ASTFactory.createBooleanChain(isAll, expressions); - if (node != null) { - node.setSourcePosition(startToken.line, startToken.column); - } - return node; - } - } - - private ExprNode parseComparisonExpression() { - Token startToken = currentToken(); - ExprNode left = parseAdditiveExpression(); - - // --- NEW: Check for reverse equality chain (e.g., 'x == any[...]') --- - if (match(EQ, NEQ, GT, LT, GTE, LTE) && isEqualityChainReverse()) { - Token op = consume(); - return parseEqualityChain(left, op.text); - } - - if (match(EQ, NEQ, GT, LT, GTE, LTE)) { - Token op = consume(); - - if (match(ALL, ANY)) { - return parseEqualityChain(left, op.text); - } - - ExprNode right = parseAdditiveExpression(); - left = ASTFactory.createBinaryOp(left, op.text, right); - setNodePosition(left, startToken); - } - return left; - } - - private ExprNode parseEqualityChain(ExprNode left, String operator) { - Token startToken = currentToken(); - Token chainTypeToken = consume(); - boolean isAllChain = chainTypeToken.type == ALL; - - // --- UPDATED: Looking for LBRACKET instead of LPAREN --- - consume(LBRACKET); - - List chainArgs = new ArrayList(); - // --- UPDATED: Looking for RBRACKET instead of RPAREN --- - if (!match(RBRACKET)) { - chainArgs.add(parseChainArgument()); - while (tryConsume(COMMA)) { - chainArgs.add(parseChainArgument()); - } - } - // --- UPDATED: Consuming RBRACKET instead of RPAREN --- - consume(RBRACKET); - - EqualityChainNode chain = ASTFactory.createEqualityChain(left, operator, isAllChain, chainArgs); - setNodePosition(chain, startToken); - return chain; - } - - private MethodCallNode parseConditionalChainCall(MethodCallNode call) { - Token chainTypeToken = consume(); - boolean isAllChain = chainTypeToken.type == ALL; - call.chainType = isAllChain ? "all" : "any"; - - // --- UPDATED: Mandate LBRACKET and RBRACKET for chain arguments --- - consume(LBRACKET); - - List chainArgs = new ArrayList(); - if (!match(RBRACKET)) { - chainArgs.add(parseChainArgument()); - while (tryConsume(COMMA)) { - chainArgs.add(parseChainArgument()); - } - } - - // --- UPDATED: Consume RBRACKET --- - consume(RBRACKET); - - call.chainArguments = chainArgs; - return call; - } - - private ExprNode parseAdditiveExpression() { - Token startToken = currentToken(); - ExprNode left = parseMultiplicativeExpression(); - while (match(PLUS, MINUS)) { - Token op = consume(); - ExprNode right = parseMultiplicativeExpression(); - left = ASTFactory.createBinaryOp(left, op.text, right); - setNodePosition(left, startToken); - } - return left; - } - - private ExprNode parseMultiplicativeExpression() { - Token startToken = currentToken(); - ExprNode left = parseUnaryExpression(); - while (match(MUL, DIV, MOD)) { - Token op = consume(); - ExprNode right = parseUnaryExpression(); - left = ASTFactory.createBinaryOp(left, op.text, right); - setNodePosition(left, startToken); - } - return left; - } - - private ExprNode parseUnaryExpression() { - Token startToken = currentToken(); - if (match(BANG)) { - Token op = consume(); - ExprNode operand = parsePrimaryExpression(); - UnaryNode unary = ASTFactory.createUnaryOp(op.text, operand); - setNodePosition(unary, startToken); - return unary; - } - if (match(PLUS, MINUS)) { - Token op = consume(); - ExprNode operand = parsePrimaryExpression(); - UnaryNode unary = ASTFactory.createUnaryOp(op.text, operand); - setNodePosition(unary, startToken); - return unary; - } - return parsePrimaryExpression(); - } - - private ExprNode parseArrayLiteral() { - Token startToken = currentToken(); - consume(LBRACKET); - List elements = new ArrayList(); - if (!match(RBRACKET)) { - elements.add(parseExpression()); - while (tryConsume(COMMA)) { - elements.add(parseExpression()); - } - } - consume(RBRACKET); - ArrayNode array = ASTFactory.createArray(elements); - setNodePosition(array, startToken); - return array; - } - - private ExprNode parsePrimaryExpression() { - ExprNode baseExpr; - Token startToken = currentToken(); - - if (match(LBRACKET)) { - baseExpr = parseArrayLiteral(); - } else if (match(INT_LIT)) { - baseExpr = ASTFactory.createIntLiteral(Integer.parseInt(consume().text)); - setNodePosition(baseExpr, startToken); - } else if (match(FLOAT_LIT)) { - baseExpr = ASTFactory.createFloatLiteral(Float.parseFloat(consume().text)); - setNodePosition(baseExpr, startToken); - } else if (match(STRING_LIT)) { - Token stringToken = consume(); - baseExpr = ASTFactory.createStringLiteral(stringToken.text); - setNodePosition(baseExpr, stringToken); - } else if (match(BOOL_LIT)) { - baseExpr = ASTFactory.createBoolLiteral(Boolean.parseBoolean(consume().text)); - setNodePosition(baseExpr, startToken); - } else if (match(ID)) { - if (isMethodCallFollows()) { - baseExpr = parseMethodCall(); - } else { - baseExpr = ASTFactory.createIdentifier(consume().text); - setNodePosition(baseExpr, startToken); - } - } else if (match(LPAREN)) { - if (isTypeCast()) { - baseExpr = parseTypeCast(); - } else { - consume(LPAREN); - baseExpr = parseExpression(); - consume(RPAREN); - setNodePosition(baseExpr, startToken); - } - } else { - throw new ParseError("Unexpected token in primary expression: " + startToken.text + - " (" + getTypeName(startToken.type) + ")" + - " at line " + startToken.line + ":" + startToken.column); - } - - while (match(LBRACKET)) { - baseExpr = parseIndexAccessContinuation(baseExpr); - } - - return baseExpr; - } - - private MethodCallNode parseMethodCall() { - Token startToken = currentToken(); - String qualifiedNameStr = parseQualifiedName(); - String methodName = qualifiedNameStr; - if (qualifiedNameStr.contains(".")) { - methodName = qualifiedNameStr.substring(qualifiedNameStr.lastIndexOf('.') + 1); - } - MethodCallNode call = ASTFactory.createMethodCall(methodName, qualifiedNameStr); - setNodePosition(call, startToken); - - consume(LPAREN); - - if (match(ALL, ANY)) { - return parseConditionalChainCall(call); - } - - if (!match(RPAREN)) { - call.arguments.add(parseExpression()); - while (tryConsume(COMMA)) { - call.arguments.add(parseExpression()); - } - } - consume(RPAREN); - return call; - } - - private ExprNode parseChainArgument() { - if (match(BANG)) { - consume(BANG); - ExprNode arg = parsePrimaryExpression(); - UnaryNode negatedArg = ASTFactory.createUnaryOp("!", arg); - setNodePosition(negatedArg, currentToken()); - return negatedArg; - } - - return parsePrimaryExpression(); - } - - private IndexAccessNode parseIndexAccessContinuation(ExprNode arrayExpr) { - Token startToken = currentToken(); - consume(LBRACKET); - ExprNode indexExpr = parseExpression(); - consume(RBRACKET); - IndexAccessNode access = ASTFactory.createIndexAccess(arrayExpr, indexExpr); - setNodePosition(access, startToken); - return access; - } - - private ExprNode parseTypeCast() { - Token startToken = currentToken(); - consume(LPAREN); - String type = parseTypeReference(); - consume(RPAREN); - ExprNode expressionToCast = parseUnaryExpression(); - TypeCastNode cast = ASTFactory.createTypeCast(type, expressionToCast); - setNodePosition(cast, startToken); - return cast; - } - - private List parseReturnSlots() { - consume(LBRACKET); - List slots = new ArrayList(); - do { - if (match(ID)) { - slots.add(consume(ID).text); - } else if (match(INT_LIT)) { - slots.add(consume(INT_LIT).text); - } else { - throw new ParseError("Expected slot name or index, found " + currentToken().text); - } - } while (tryConsume(COMMA)); - consume(RBRACKET); - return slots; - } - - private List parseIdList() { - List ids = new ArrayList(); - ids.add(consume(ID).text); - while (tryConsume(COMMA)) { - ids.add(consume(ID).text); - } - return ids; - } - - private ParamNode parseParameter() { - Token startToken = currentToken(); - String type = parseTypeReference(); - Token paramNameToken = currentToken(); - String name = consume(ID).text; - - NamingValidator.validateParameterName(name, paramNameToken); - - ParamNode param = ASTFactory.createParam(name, type); - setNodePosition(param, startToken); - return param; - } - - private boolean isSlotDeclaration() { - try { - return tokens.get(position).type == TILDE_BAR; - } catch (IndexOutOfBoundsException e) { - return false; - } - } - - private boolean isSlotAssignment() { - try { - if (tokens.get(position).type != TILDE_ARROW) return false; - if (position + 1 >= tokens.size()) return false; - - int nextPos = position + 1; - Token nextToken = tokens.get(nextPos); - - return nextToken.type == ID || - nextToken.type == INT_LIT || - nextToken.type == FLOAT_LIT || - nextToken.type == STRING_LIT || - nextToken.type == BOOL_LIT || - nextToken.type == LPAREN || - nextToken.type == LBRACKET || - nextToken.type == PLUS || - nextToken.type == MINUS; - } catch (IndexOutOfBoundsException e) { - return false; - } - } - - private boolean isTypeCast() { - int p = position; try { if (tokens.get(p++).type != LPAREN) return false; if (!isTypeStart(tokens.get(p))) return false; p++; while (p < tokens.size() && tokens.get(p).type == LBRACKET) { if (p + 1 < tokens.size() && tokens.get(p + 1).type == RBRACKET) { p += 2; } else { return false; } } if (p >= tokens.size() || tokens.get(p++).type != RPAREN) return false; if (p >= tokens.size()) return false; int nt = tokens.get(p).type; return nt == LBRACKET || nt == INT_LIT || nt == FLOAT_LIT || nt == STRING_LIT || nt == BOOL_LIT || nt == ID || nt == LPAREN || nt == PLUS || nt == MINUS; } catch (IndexOutOfBoundsException e) { return false; } - } - - private boolean isMethodDeclaration() { - int p = position; - try { - if (tokens.get(p).type == TILDE_BAR) { - p++; - while(p < tokens.size() && (isTypeStart(tokens.get(p)) || tokens.get(p).type == ID || tokens.get(p).type == COMMA || tokens.get(p).type == LBRACKET || tokens.get(p).type == RBRACKET)) { - p++; - } - } - - if (tokens.get(p).type == BUILTIN || isVisibilityModifier(tokens.get(p))) { - p++; - } - - if (p >= tokens.size() || tokens.get(p).type != ID) return false; - p++; - - if (p >= tokens.size() || tokens.get(p).type != LPAREN) return false; - p++; - - int parenDepth = 1; - while (p < tokens.size() && parenDepth > 0) { - if (tokens.get(p).type == LPAREN) parenDepth++; - else if (tokens.get(p).type == RPAREN) parenDepth--; - p++; - } - - if (p >= tokens.size()) return false; - - return tokens.get(p).type == TILDE_ARROW || - tokens.get(p).type == LBRACE; - } catch (IndexOutOfBoundsException e) { - return false; - } - } - - private boolean isFieldDeclaration() { - int p = position; try { if (!isTypeStart(tokens.get(p))) return false; p++; while (p < tokens.size() && tokens.get(p).type == LBRACKET) { if (p + 1 < tokens.size() && tokens.get(p + 1).type == RBRACKET) { p += 2; } else { return false; } } if (p >= tokens.size() || tokens.get(p).type != ID) return false; p++; return p >= tokens.size() || tokens.get(p).type != LPAREN; } catch (IndexOutOfBoundsException e) { return false; } - } - - private boolean isInputAssignment() { - int p = position; try { if (tokens.get(p++).type != ID) return false; if (tokens.get(p++).type != ASSIGN) return false; if (tokens.get(p++).type != LPAREN) return false; if (!isTypeStart(tokens.get(p))) return false; p++; while (p < tokens.size() && tokens.get(p).type == LBRACKET) { if (p + 1 < tokens.size() && tokens.get(p + 1).type == RBRACKET) { p += 2; } else { return false; } } if (p >= tokens.size() || tokens.get(p++).type != RPAREN) return false; return p < tokens.size() && tokens.get(p).type == INPUT; } catch (IndexOutOfBoundsException e) { return false; } - } - - private boolean isSimpleAssignment() { - int p = position; - try { - if (tokens.get(p).type != ID) return false; - if (p + 1 < tokens.size() && tokens.get(p + 1).type == LBRACKET) return false; - if (p + 1 >= tokens.size() || tokens.get(p + 1).type != ASSIGN) return false; - if (p + 2 < tokens.size() && tokens.get(p + 2).type == LPAREN) return false; - return true; - } catch (IndexOutOfBoundsException e) { - return false; - } - } - - private boolean isIndexAssignment() { - int p = position; try { if (tokens.get(p++).type != ID) return false; if (p >= tokens.size() || tokens.get(p++).type != LBRACKET) return false; int bd = 1; while (p < tokens.size() && bd > 0) { if (tokens.get(p).type == LBRACKET) bd++; else if (tokens.get(p).type == RBRACKET) bd--; p++; } if (bd != 0) return false; while (p < tokens.size() && tokens.get(p).type == LBRACKET) { p++; bd = 1; while (p < tokens.size() && bd > 0) { if (tokens.get(p).type == LBRACKET) bd++; else if (tokens.get(p).type == RBRACKET) bd--; p++; } if (bd != 0) return false; } return p < tokens.size() && tokens.get(p).type == ASSIGN; } catch (IndexOutOfBoundsException e) { return false; } - } - - private boolean isMethodCallFollows() { - int p = position; - try { - if (tokens.get(p).type != ID) return false; - p++; - while(p < tokens.size() && tokens.get(p).type == DOT) { - p++; - if (p >= tokens.size() || tokens.get(p).type != ID) return false; - p++; - } - if (p >= tokens.size() || tokens.get(p).type != LPAREN) return false; - p++; - - if (p < tokens.size() && (tokens.get(p).type == ALL || tokens.get(p).type == ANY)) { - p++; - return p < tokens.size() && tokens.get(p).type == LBRACKET; - } - - return true; - } catch (IndexOutOfBoundsException e) { - return false; - } - } - - // --- NEW: Helper for reverse equality chain --- - private boolean isEqualityChainReverse() { - int p = position; - try { - // Check for Operator - if (tokens.get(p).type != EQ && tokens.get(p).type != NEQ && - tokens.get(p).type != GT && tokens.get(p).type != LT && - tokens.get(p).type != GTE && tokens.get(p).type != LTE) return false; - p++; - - // Check for ALL/ANY followed by LBRACKET - if (tokens.get(p).type != ALL && tokens.get(p).type != ANY) return false; - p++; - - return tokens.get(p).type == LBRACKET; - } catch (IndexOutOfBoundsException e) { - return false; - } - } - - - private boolean isReturnSlotAssignment() { - int p = position; try { if (tokens.get(p++).type != ID) return false; while (p < tokens.size() && tokens.get(p).type == COMMA) { p++; if (p >= tokens.size() || tokens.get(p++).type != ID) return false; } if (p >= tokens.size() || tokens.get(p++).type != ASSIGN) return false; if (p >= tokens.size() || tokens.get(p++).type != LBRACKET) return false; - if (p >= tokens.size()) return false; - if (tokens.get(p).type == ID || tokens.get(p).type == INT_LIT) { p++; } else { return false; } - while (p < tokens.size() && tokens.get(p).type == COMMA) { p++; if (p >= tokens.size()) return false; if (tokens.get(p).type == ID || tokens.get(p).type == INT_LIT) { p++; } else { return false; } } if (p >= tokens.size() || tokens.get(p++).type != RBRACKET) return false; return p < tokens.size() && tokens.get(p).type == COLON; } catch (IndexOutOfBoundsException e) { return false; } - } - - private boolean isVariableDeclaration() { - int p = position; try { Token first = tokens.get(p); if (first.type == VAR) { return p + 1 < tokens.size() && tokens.get(p + 1).type == ID; } else if (isTypeStart(first)) { p++; while (p < tokens.size() && tokens.get(p).type == LBRACKET) { if (p + 1 < tokens.size() && tokens.get(p + 1).type == RBRACKET) { p += 2; } else { return false; } } return p < tokens.size() && tokens.get(p).type == ID; } return false; } catch (IndexOutOfBoundsException e) { return false; } - } - - private boolean isAssignmentInByClause() { - int p = position; try { if (tokens.get(p).type != ID) return false; int nt = tokens.get(p + 1).type; return nt == ASSIGN || nt == PLUS_ASSIGN || nt == MINUS_ASSIGN || nt == MUL_ASSIGN || nt == DIV_ASSIGN; } catch (IndexOutOfBoundsException e) { return false; } - } - - private boolean isMethodCallStatement() { - int p = position; try { if (tokens.get(p).type == LBRACKET) { p++; - if (tokens.get(p).type == ID || tokens.get(p).type == INT_LIT) { p++; } else { return false; } - while(p < tokens.size() && tokens.get(p).type == COMMA) { p+=2; } if (p >= tokens.size() || tokens.get(p).type != RBRACKET) return false; p++; if (p >= tokens.size() || tokens.get(p).type != COLON) return false; p++; } if (tokens.get(p).type != ID) return false; p++; while(p < tokens.size() && tokens.get(p).type == DOT) { p++; if (p >= tokens.size() || tokens.get(p).type != ID) return false; p++; } return p < tokens.size() && tokens.get(p).type == LPAREN; } catch (IndexOutOfBoundsException e) { return false; } - } - - private String parseQualifiedName() { - StringBuilder name = new StringBuilder(); - name.append(consume(ID).text); - while (tryConsume(DOT)) { - name.append("."); - name.append(consume(ID).text); - } - return name.toString(); - } - - private String parseTypeReference() { - StringBuilder typeName = new StringBuilder(); - if (isTypeStart(currentToken())) { - typeName.append(consume().text); - } else { - Token current = currentToken(); - throw new ParseError("Expected type name (int, string, float, bool, or ID) but got " + - getTypeName(current.type) + " ('" + current.text + "')" + - " at line " + current.line + ":" + current.column); - } - while (match(LBRACKET)) { - consume(LBRACKET); - consume(RBRACKET); - typeName.append("[]"); - } - return typeName.toString(); - } - - private boolean isTypeKeyword(int type) { - return type == INT || type == STRING || - type == FLOAT || type == BOOL; - } - - private boolean isTypeStart(Token token) { - if (token == null) return false; - return isTypeKeyword(token.type) || token.type == ID; - } - - private boolean isVisibilityModifier() { - return match(SHARE, LOCAL); - } - - private boolean isVisibilityModifier(Token token) { - if (token == null) return false; - return token.type == SHARE || token.type == LOCAL; - } - - private Token currentToken() { - return (position >= tokens.size()) ? tokens.get(tokens.size() - 1) : tokens.get(position); - } - - private Token peek(int offset) { - int targetPos = position + offset; - if (targetPos >= tokens.size()) return tokens.get(tokens.size() - 1); - if (targetPos < 0) { return null; } - return tokens.get(targetPos); - } - - private boolean match(int... types) { - Token current = currentToken(); - if (current.type == Token.EOF) { - for (int type : types) { if (type == Token.EOF) return true; } - return false; - } - for (int type : types) { - if (current.type == type) return true; - } - return false; - } - - private Token consume() { - Token token = currentToken(); - if (token.type != Token.EOF) position++; - return token; - } - - private Token consume(int expectedType) { - Token token = currentToken(); - if (token.type == expectedType) { - if (token.type != Token.EOF) position++; - return token; - } - throw new ParseError("Expected " + getTypeName(expectedType) + " but found " + - getTypeName(token.type) + " ('" + token.text + "') at line " + token.line + ":" + token.column); - } - - private Token consume(boolean condition) { - if (condition) return consume(); - Token current = currentToken(); - throw new ParseError("Consumption condition not met at: " + current.text + - " (" + getTypeName(current.type) + ")" + - " at line " + current.line + ":" + current.column); - } - - private boolean tryConsume(int expectedType) { - if (match(expectedType)) { - consume(expectedType); - return true; - } - return false; - } - - private String getTypeName(int type) { - return ManualCoderiveLexer.TokenType.getName(type); - } -} \ No newline at end of file diff --git a/src/main/java/cod/ast/OptimizationStats.java b/src/main/java/cod/ast/OptimizationStats.java new file mode 100644 index 00000000..a64937be --- /dev/null +++ b/src/main/java/cod/ast/OptimizationStats.java @@ -0,0 +1,54 @@ +package cod.ast; + +/** + * Tracks statistics about optimization passes + */ +public class OptimizationStats { + private int constantExpressionsFolded = 0; + private int binaryOpsSimplified = 0; + private int booleanChainsOptimized = 0; + private int typeCastsEliminated = 0; + private long startTime = 0; + private long endTime = 0; + + public void startTiming() { + startTime = System.nanoTime(); + } + + public void stopTiming() { + endTime = System.nanoTime(); + } + + public void incrementConstantExpressions() { + constantExpressionsFolded++; + } + + public void incrementBinaryOps() { + binaryOpsSimplified++; + } + + public void incrementBooleanChains() { + booleanChainsOptimized++; + } + + public void incrementTypeCasts() { + typeCastsEliminated++; + } + + public void printSummary() { + long duration = endTime - startTime; + System.out.println("\n=== OPTIMIZATION SUMMARY ==="); + System.out.println("Constant expressions folded: " + constantExpressionsFolded); + System.out.println("Binary operations simplified: " + binaryOpsSimplified); + System.out.println("Boolean chains optimized: " + booleanChainsOptimized); + System.out.println("Type casts eliminated: " + typeCastsEliminated); + System.out.println("Total optimizations: " + getTotalOptimizations()); + System.out.printf("Optimization time: %.3f ms\n", duration / 1_000_000.0); + System.out.println("=============================\n"); + } + + public int getTotalOptimizations() { + return constantExpressionsFolded + binaryOpsSimplified + + booleanChainsOptimized + typeCastsEliminated; + } +} \ No newline at end of file diff --git a/src/main/java/cod/ast/Optimizer.java b/src/main/java/cod/ast/Optimizer.java new file mode 100644 index 00000000..3ce72271 --- /dev/null +++ b/src/main/java/cod/ast/Optimizer.java @@ -0,0 +1,88 @@ +package cod.ast; + +import cod.ast.nodes.*; + +public class Optimizer { + + private static OptimizationStats stats = new OptimizationStats(); + + /** + * Apply expression flattening optimization + */ + public static ASTNode flattenExpressions(ASTNode node) { + ExpressionFlattener flattener = new ExpressionFlattener(); + return flattener.flatten(node); + } + + /** + * Apply constant folding optimization to any AST node + */ + public static ASTNode foldConstants(ASTNode node) { + stats.startTiming(); + ConstantFolder folder = new ConstantFolder(); + ASTNode result = folder.visit(node); + stats.stopTiming(); + return result; + } + + /** + * Apply multiple optimization passes with intelligent ordering + */ + public static ASTNode optimize(ASTNode node, boolean flattenExpressions, + boolean constantFolding, boolean otherOptimizations) { + ASTNode result = node; + + // 1. First flatten expressions (exposes more constant folding opportunities) + if (flattenExpressions) { + result = flattenExpressions(result); + } + + // 2. Then fold constants (works better on flattened expressions) + if (constantFolding) { + result = foldConstants(result); + } + + // 3. Other optimizations (could include more flattening/folding cycles) + if (otherOptimizations) { + // Optionally run another round of flattening after constant folding + // to clean up newly created expression structures + if (flattenExpressions) { + result = flattenExpressions(result); + } + + // Add other optimization passes here as needed + // result = new Inliner().visit(result); + // result = new DeadCodeEliminator().visit(result); + } + + return result; + } + + /** + * Convenience method: full optimization pipeline + */ + public static ASTNode optimizeFull(ASTNode node) { + return optimize(node, true, true, false); + } + + /** + * Get optimization statistics + */ + public static OptimizationStats getStats() { + return stats; + } + + /** + * Reset optimization statistics + */ + public static void resetStats() { + stats = new OptimizationStats(); + } + + /** + * Print optimization statistics + */ + public static void printStats() { + stats.printSummary(); + } +} \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/ASTNode.java b/src/main/java/cod/ast/nodes/ASTNode.java index 3f5357a0..5108bad2 100644 --- a/src/main/java/cod/ast/nodes/ASTNode.java +++ b/src/main/java/cod/ast/nodes/ASTNode.java @@ -1,14 +1,10 @@ // ASTNode.java package cod.ast.nodes; +import cod.ast.ASTVisitor; + public abstract class ASTNode { - protected int line = -1; - protected int column = -1; - - public int getLine() { return line; } - public int getColumn() { return column; } - public void setSourcePosition(int line, int column) { - this.line = line; - this.column = column; - } -} + + public abstract T accept(ASTVisitor visitor); + +} \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/ArgumentListNode.java b/src/main/java/cod/ast/nodes/ArgumentListNode.java new file mode 100644 index 00000000..c9bb585d --- /dev/null +++ b/src/main/java/cod/ast/nodes/ArgumentListNode.java @@ -0,0 +1,16 @@ +// ArgumentListNode.java +package cod.ast.nodes; + +import cod.ast.ASTVisitor; + +import java.util.List; + +public class ArgumentListNode extends ExprNode { + public List arguments; + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + +} \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/ArrayNode.java b/src/main/java/cod/ast/nodes/ArrayNode.java index 5058f269..937472db 100644 --- a/src/main/java/cod/ast/nodes/ArrayNode.java +++ b/src/main/java/cod/ast/nodes/ArrayNode.java @@ -1,11 +1,17 @@ package cod.ast.nodes; -import cod.ast.CoderiveParser; +import cod.ast.ASTVisitor; + import java.util.ArrayList; import java.util.List; -import org.antlr.v4.runtime.tree.ParseTree; public class ArrayNode extends ExprNode { public List elements = new ArrayList<>(); - public String elementType; // int[], string[], etc. + public String elementType; // int: [], text: [], etc. + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } diff --git a/src/main/java/cod/ast/nodes/AssignmentNode.java b/src/main/java/cod/ast/nodes/AssignmentNode.java index d07712c6..67a268f6 100644 --- a/src/main/java/cod/ast/nodes/AssignmentNode.java +++ b/src/main/java/cod/ast/nodes/AssignmentNode.java @@ -1,9 +1,11 @@ package cod.ast.nodes; +import cod.ast.ASTVisitor; + /** * Represents an assignment operation, such as 'x = 5' or 'arr[0] = 10'. */ -public class AssignmentNode extends StatementNode { +public class AssignmentNode extends StmtNode { public ExprNode left; // The target of the assignment (identifier, index access, etc.) public ExprNode right; // The value being assigned @@ -13,4 +15,10 @@ public AssignmentNode(ExprNode left, ExprNode right) { this.left = left; this.right = right; } + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/BinaryOpNode.java b/src/main/java/cod/ast/nodes/BinaryOpNode.java index 9674d535..fc39956e 100644 --- a/src/main/java/cod/ast/nodes/BinaryOpNode.java +++ b/src/main/java/cod/ast/nodes/BinaryOpNode.java @@ -1,8 +1,13 @@ package cod.ast.nodes; -import cod.ast.CoderiveParser; -import org.antlr.v4.runtime.tree.ParseTree; +import cod.ast.ASTVisitor; public class BinaryOpNode extends ExprNode { public BinaryOpNode() {} + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } diff --git a/src/main/java/cod/ast/nodes/BlockNode.java b/src/main/java/cod/ast/nodes/BlockNode.java index d78a7ff3..37a0d7ff 100644 --- a/src/main/java/cod/ast/nodes/BlockNode.java +++ b/src/main/java/cod/ast/nodes/BlockNode.java @@ -1,15 +1,22 @@ package cod.ast.nodes; +import cod.ast.ASTVisitor; + import java.util.ArrayList; import java.util.List; -import org.antlr.v4.runtime.tree.ParseTree; -public class BlockNode extends StatementNode { - public List statements = new ArrayList<>(); +public class BlockNode extends StmtNode { + public List statements = new ArrayList<>(); public BlockNode() {} - public BlockNode(List statements) { + public BlockNode(List statements) { this.statements = statements; } + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } diff --git a/src/main/java/cod/ast/nodes/BooleanChainNode.java b/src/main/java/cod/ast/nodes/BooleanChainNode.java index d78d2df3..8c5b2904 100644 --- a/src/main/java/cod/ast/nodes/BooleanChainNode.java +++ b/src/main/java/cod/ast/nodes/BooleanChainNode.java @@ -1,5 +1,6 @@ package cod.ast.nodes; +import cod.ast.ASTVisitor; import java.util.*; public class BooleanChainNode extends ExprNode { @@ -9,4 +10,10 @@ public class BooleanChainNode extends ExprNode { public BooleanChainNode() { this.expressions = new ArrayList(); } + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/ConstructorNode.java b/src/main/java/cod/ast/nodes/ConstructorNode.java index d9a7d35d..f948df3c 100644 --- a/src/main/java/cod/ast/nodes/ConstructorNode.java +++ b/src/main/java/cod/ast/nodes/ConstructorNode.java @@ -1,9 +1,17 @@ package cod.ast.nodes; +import cod.ast.ASTVisitor; + import java.util.List; import java.util.ArrayList; public class ConstructorNode extends ASTNode { public List parameters = new ArrayList(); - public List body = new ArrayList(); -} + public List body = new ArrayList(); + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + +} \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/EqualityChainNode.java b/src/main/java/cod/ast/nodes/EqualityChainNode.java index d80d1ece..4a77d0f9 100644 --- a/src/main/java/cod/ast/nodes/EqualityChainNode.java +++ b/src/main/java/cod/ast/nodes/EqualityChainNode.java @@ -1,5 +1,6 @@ package cod.ast.nodes; +import cod.ast.ASTVisitor; import java.util.*; public class EqualityChainNode extends ExprNode { @@ -11,4 +12,10 @@ public class EqualityChainNode extends ExprNode { public EqualityChainNode() { this.chainArguments = new ArrayList(); } + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/ExitNode.java b/src/main/java/cod/ast/nodes/ExitNode.java new file mode 100644 index 00000000..f42acd4d --- /dev/null +++ b/src/main/java/cod/ast/nodes/ExitNode.java @@ -0,0 +1,13 @@ +package cod.ast.nodes; + +import cod.ast.ASTVisitor; + +public class ExitNode extends StmtNode { + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + + +} \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/ExprIfNode.java b/src/main/java/cod/ast/nodes/ExprIfNode.java new file mode 100644 index 00000000..6c1d2ed8 --- /dev/null +++ b/src/main/java/cod/ast/nodes/ExprIfNode.java @@ -0,0 +1,22 @@ +package cod.ast.nodes; + +import cod.ast.ASTVisitor; + +public class ExprIfNode extends ExprNode { + public ExprNode condition; + public ExprNode thenExpr; + public ExprNode elseExpr; + + public ExprIfNode() {} + + public ExprIfNode(ExprNode condition, ExprNode thenExpr, ExprNode elseExpr) { + this.condition = condition; + this.thenExpr = thenExpr; + this.elseExpr = elseExpr; + } + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } +} \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/ExprNode.java b/src/main/java/cod/ast/nodes/ExprNode.java index 5c82bf6e..b5cacdda 100644 --- a/src/main/java/cod/ast/nodes/ExprNode.java +++ b/src/main/java/cod/ast/nodes/ExprNode.java @@ -1,13 +1,20 @@ package cod.ast.nodes; -public class ExprNode extends StatementNode { +import cod.ast.ASTVisitor; + +public class ExprNode extends StmtNode { public String name; // For identifiers or method names public Object value; // For literals public String op; // Operator (+, -, *, /, slot_cast, etc.) public ExprNode left; // Left operand public ExprNode right; // Right operand + public boolean isNull = false; public ExprNode() {} - // Remove all ANTLR imports and references + @Override + public T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/FieldNode.java b/src/main/java/cod/ast/nodes/FieldNode.java index 6e47b6a6..33427f4f 100644 --- a/src/main/java/cod/ast/nodes/FieldNode.java +++ b/src/main/java/cod/ast/nodes/FieldNode.java @@ -1,11 +1,18 @@ package cod.ast.nodes; -public class FieldNode extends StatementNode { +import cod.ast.ASTVisitor; +import cod.syntax.Keyword; + +public class FieldNode extends StmtNode { public String name; public String type; - public String visibility; // [FIX] Added visibility property + public Keyword visibility; // [FIX] Added visibility property public ExprNode value; - // The 'left' property for assignments has been removed, - // as the new AssignmentNode is a much cleaner way to handle that. + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + + } diff --git a/src/main/java/cod/ast/nodes/ForNode.java b/src/main/java/cod/ast/nodes/ForNode.java index 2890e025..db227754 100644 --- a/src/main/java/cod/ast/nodes/ForNode.java +++ b/src/main/java/cod/ast/nodes/ForNode.java @@ -1,8 +1,8 @@ package cod.ast.nodes; -import java.util.*; +import cod.ast.ASTVisitor; -public class ForNode extends StatementNode { +public class ForNode extends StmtNode { public String iterator; public RangeNode range; public BlockNode body = new BlockNode(); @@ -13,5 +13,11 @@ public ForNode(String iterator, RangeNode range) { this.iterator = iterator; this.range = range; } - + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + + } diff --git a/src/main/java/cod/ast/nodes/GetNode.java b/src/main/java/cod/ast/nodes/GetNode.java deleted file mode 100644 index 1bae2b20..00000000 --- a/src/main/java/cod/ast/nodes/GetNode.java +++ /dev/null @@ -1,10 +0,0 @@ -package cod.ast.nodes; - -import cod.ast.CoderiveParser; -import java.util.ArrayList; -import java.util.List; -import org.antlr.v4.runtime.tree.ParseTree; - -public class GetNode extends ASTNode { - public List imports = new ArrayList<>(); -} \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/IfNode.java b/src/main/java/cod/ast/nodes/IfNode.java deleted file mode 100644 index d37261a4..00000000 --- a/src/main/java/cod/ast/nodes/IfNode.java +++ /dev/null @@ -1,17 +0,0 @@ -package cod.ast.nodes; - -import java.util.*; - -public class IfNode extends StatementNode { - public ExprNode condition; - public BlockNode thenBlock = new BlockNode(); - public BlockNode elseBlock = new BlockNode(); - - public IfNode() {} - - public IfNode(ExprNode condition) { - this.condition = condition; - } - - // Remove ANTLR imports and references -} \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/IndexAccessNode.java b/src/main/java/cod/ast/nodes/IndexAccessNode.java index b73f7ab7..bd73275d 100644 --- a/src/main/java/cod/ast/nodes/IndexAccessNode.java +++ b/src/main/java/cod/ast/nodes/IndexAccessNode.java @@ -1,8 +1,14 @@ package cod.ast.nodes; +import cod.ast.ASTVisitor; + public class IndexAccessNode extends ExprNode { public ExprNode array; public ExprNode index; - // Remove ANTLR import + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/InputNode.java b/src/main/java/cod/ast/nodes/InputNode.java index 5dda2c8a..98ce1d29 100644 --- a/src/main/java/cod/ast/nodes/InputNode.java +++ b/src/main/java/cod/ast/nodes/InputNode.java @@ -1,14 +1,14 @@ package cod.ast.nodes; -import cod.ast.CoderiveParser; -import org.antlr.v4.runtime.tree.ParseTree; +import cod.ast.ASTVisitor; -public class InputNode extends StatementNode { +public class InputNode extends ExprNode { public String targetType; // The type being read (int, string, float, bool) public String variableName; // The variable to assign the input to - @Override - public String toString() { - return "InputNode{type=" + targetType + ", var=" + variableName + "}"; - } + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/MethodCallNode.java b/src/main/java/cod/ast/nodes/MethodCallNode.java index 56a4df74..add3eb2e 100644 --- a/src/main/java/cod/ast/nodes/MethodCallNode.java +++ b/src/main/java/cod/ast/nodes/MethodCallNode.java @@ -1,5 +1,7 @@ package cod.ast.nodes; +import cod.ast.ASTVisitor; + import java.util.*; public class MethodCallNode extends ExprNode { @@ -9,4 +11,10 @@ public class MethodCallNode extends ExprNode { public String chainType; public List chainArguments; + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/MethodNode.java b/src/main/java/cod/ast/nodes/MethodNode.java index bd9a2713..e28f837e 100644 --- a/src/main/java/cod/ast/nodes/MethodNode.java +++ b/src/main/java/cod/ast/nodes/MethodNode.java @@ -3,11 +3,20 @@ import java.util.List; import java.util.ArrayList; +import cod.ast.ASTVisitor; +import cod.syntax.Keyword; + public class MethodNode extends ASTNode { public String name; - public String visibility = "public"; + public Keyword visibility = Keyword.SHARE; public List returnSlots = new ArrayList(); public List parameters = new ArrayList(); - public List body = new ArrayList(); + public List body = new ArrayList(); public boolean isBuiltin = false; + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/MultipleSlotAssignmentNode.java b/src/main/java/cod/ast/nodes/MultipleSlotAssignmentNode.java index 01db8ae8..c8e43551 100644 --- a/src/main/java/cod/ast/nodes/MultipleSlotAssignmentNode.java +++ b/src/main/java/cod/ast/nodes/MultipleSlotAssignmentNode.java @@ -1,7 +1,15 @@ package cod.ast.nodes; +import cod.ast.ASTVisitor; + import java.util.List; -public class MultipleSlotAssignmentNode extends StatementNode { +public class MultipleSlotAssignmentNode extends StmtNode { public List assignments; + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/OutputNode.java b/src/main/java/cod/ast/nodes/OutputNode.java index 98d18834..594cf2f6 100644 --- a/src/main/java/cod/ast/nodes/OutputNode.java +++ b/src/main/java/cod/ast/nodes/OutputNode.java @@ -1,12 +1,16 @@ package cod.ast.nodes; -import cod.ast.CoderiveParser; +import cod.ast.ASTVisitor; + import java.util.*; -import org.antlr.v4.runtime.tree.ParseTree; -import org.antlr.v4.runtime.tree.TerminalNode; -public class OutputNode extends StatementNode { +public class OutputNode extends StmtNode { public String varName; // optional, for "output n = ..." public List arguments = new ArrayList<>(); // normal output or multiple args + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/ParamNode.java b/src/main/java/cod/ast/nodes/ParamNode.java index b9c971da..d243058f 100644 --- a/src/main/java/cod/ast/nodes/ParamNode.java +++ b/src/main/java/cod/ast/nodes/ParamNode.java @@ -1,6 +1,17 @@ package cod.ast.nodes; +import cod.ast.ASTVisitor; + public class ParamNode extends ASTNode { public String name; public String type; + public ExprNode defaultValue; + public boolean hasDefaultValue = false; + public boolean typeInferred = false; + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/ProgramNode.java b/src/main/java/cod/ast/nodes/ProgramNode.java index ce748a1f..3da7442b 100644 --- a/src/main/java/cod/ast/nodes/ProgramNode.java +++ b/src/main/java/cod/ast/nodes/ProgramNode.java @@ -1,6 +1,14 @@ package cod.ast.nodes; +import cod.ast.ASTVisitor; +import cod.parser.ProgramType; + public class ProgramNode extends ASTNode { public UnitNode unit; + public ProgramType programType; // NEW FIELD + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/RangeNode.java b/src/main/java/cod/ast/nodes/RangeNode.java index 1c1e7f4b..0bd5d4a6 100644 --- a/src/main/java/cod/ast/nodes/RangeNode.java +++ b/src/main/java/cod/ast/nodes/RangeNode.java @@ -1,6 +1,8 @@ package cod.ast.nodes; -public class RangeNode extends ASTNode { +import cod.ast.ASTVisitor; + +public class RangeNode extends ExprNode { public ExprNode step; // NEW: Step comes first now public ExprNode start; // NEW: Start after IN public ExprNode end; // NEW: End after TO @@ -13,4 +15,11 @@ public RangeNode(ExprNode step, ExprNode start, ExprNode end) { this.start = start; this.end = end; } + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/ReturnSlotAssignmentNode.java b/src/main/java/cod/ast/nodes/ReturnSlotAssignmentNode.java index b851cff2..5c5feb05 100644 --- a/src/main/java/cod/ast/nodes/ReturnSlotAssignmentNode.java +++ b/src/main/java/cod/ast/nodes/ReturnSlotAssignmentNode.java @@ -1,10 +1,17 @@ package cod.ast.nodes; +import cod.ast.ASTVisitor; + import java.util.ArrayList; import java.util.List; -import org.antlr.v4.runtime.tree.ParseTree; -public class ReturnSlotAssignmentNode extends StatementNode { +public class ReturnSlotAssignmentNode extends StmtNode { public List variableNames = new ArrayList<>(); public MethodCallNode methodCall; + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } diff --git a/src/main/java/cod/ast/nodes/SlotAssignmentNode.java b/src/main/java/cod/ast/nodes/SlotAssignmentNode.java index 785757f8..5515e4dc 100644 --- a/src/main/java/cod/ast/nodes/SlotAssignmentNode.java +++ b/src/main/java/cod/ast/nodes/SlotAssignmentNode.java @@ -1,6 +1,14 @@ package cod.ast.nodes; -public class SlotAssignmentNode extends StatementNode { +import cod.ast.ASTVisitor; + +public class SlotAssignmentNode extends StmtNode { public String slotName; public ExprNode value; + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/SlotDeclarationNode.java b/src/main/java/cod/ast/nodes/SlotDeclarationNode.java index e233b254..e235e9b0 100644 --- a/src/main/java/cod/ast/nodes/SlotDeclarationNode.java +++ b/src/main/java/cod/ast/nodes/SlotDeclarationNode.java @@ -1,8 +1,16 @@ package cod.ast.nodes; +import cod.ast.ASTVisitor; + import java.util.ArrayList; import java.util.List; -public class SlotDeclarationNode extends StatementNode { +public class SlotDeclarationNode extends StmtNode { public List slotNames = new ArrayList<>(); + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/SlotNode.java b/src/main/java/cod/ast/nodes/SlotNode.java index a81970a0..c1ee2c2d 100644 --- a/src/main/java/cod/ast/nodes/SlotNode.java +++ b/src/main/java/cod/ast/nodes/SlotNode.java @@ -1,5 +1,7 @@ package cod.ast.nodes; +import cod.ast.ASTVisitor; + public class SlotNode extends ASTNode { public String name; public String type; @@ -10,4 +12,10 @@ public boolean isNamed() { // If name starts with digit, it's auto-generated (since IDs can't start with digit) return !Character.isDigit(name.charAt(0)); } + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/StatementNode.java b/src/main/java/cod/ast/nodes/StatementNode.java deleted file mode 100644 index 45ed2220..00000000 --- a/src/main/java/cod/ast/nodes/StatementNode.java +++ /dev/null @@ -1,6 +0,0 @@ -package cod.ast.nodes; - -// StatementNode.java - -public class StatementNode extends ASTNode { -} \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/StmtIfNode.java b/src/main/java/cod/ast/nodes/StmtIfNode.java new file mode 100644 index 00000000..ace6c96e --- /dev/null +++ b/src/main/java/cod/ast/nodes/StmtIfNode.java @@ -0,0 +1,21 @@ +package cod.ast.nodes; + +import cod.ast.ASTVisitor; + +public class StmtIfNode extends StmtNode { + public ExprNode condition; + public BlockNode thenBlock = new BlockNode(); + public BlockNode elseBlock = new BlockNode(); + + public StmtIfNode() {} + + public StmtIfNode(ExprNode condition) { + this.condition = condition; + } + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + +} \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/StmtNode.java b/src/main/java/cod/ast/nodes/StmtNode.java new file mode 100644 index 00000000..db55567a --- /dev/null +++ b/src/main/java/cod/ast/nodes/StmtNode.java @@ -0,0 +1,7 @@ +package cod.ast.nodes; + +// StatementNode + +public abstract class StmtNode extends ASTNode { + +} \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/TupleNode.java b/src/main/java/cod/ast/nodes/TupleNode.java new file mode 100644 index 00000000..a6dbdeb8 --- /dev/null +++ b/src/main/java/cod/ast/nodes/TupleNode.java @@ -0,0 +1,14 @@ +package cod.ast.nodes; + +import cod.ast.ASTVisitor; +import java.util.List; + +public class TupleNode extends ExprNode { + public List elements; + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + +} \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/TypeCastNode.java b/src/main/java/cod/ast/nodes/TypeCastNode.java index bfa23dbd..f244abd2 100644 --- a/src/main/java/cod/ast/nodes/TypeCastNode.java +++ b/src/main/java/cod/ast/nodes/TypeCastNode.java @@ -1,15 +1,14 @@ package cod.ast.nodes; -import cod.ast.ASTBuilder; -import cod.ast.CoderiveParser; -import org.antlr.v4.runtime.tree.ParseTree; +import cod.ast.ASTVisitor; public class TypeCastNode extends ExprNode { public String targetType; // The target type to cast to public ExprNode expression; // The expression being cast - @Override - public String toString() { - return "TypeCastNode{type=" + targetType + ", expr=" + expression + "}"; - } + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/TypeNode.java b/src/main/java/cod/ast/nodes/TypeNode.java index 8846b361..38304794 100644 --- a/src/main/java/cod/ast/nodes/TypeNode.java +++ b/src/main/java/cod/ast/nodes/TypeNode.java @@ -3,15 +3,22 @@ import java.util.List; import java.util.ArrayList; -import static cod.Constants.*; +import cod.ast.ASTVisitor; +import cod.syntax.Keyword; public class TypeNode extends ASTNode { public String name; - public String visibility = share; + public Keyword visibility = Keyword.SHARE; public String extendName = null; public List fields = new ArrayList(); public ConstructorNode constructor; public List methods = new ArrayList(); - public List statements = new ArrayList(); + public List statements = new ArrayList(); + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/UnaryNode.java b/src/main/java/cod/ast/nodes/UnaryNode.java index 28f029fb..36ea01dc 100644 --- a/src/main/java/cod/ast/nodes/UnaryNode.java +++ b/src/main/java/cod/ast/nodes/UnaryNode.java @@ -1,6 +1,6 @@ package cod.ast.nodes; -import org.antlr.v4.runtime.tree.ParseTree; +import cod.ast.ASTVisitor; public class UnaryNode extends ExprNode { public String op; // "+" or "-" @@ -12,4 +12,10 @@ public UnaryNode(String op, ExprNode operand) { this.op = op; this.operand = operand; } + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/UnitNode.java b/src/main/java/cod/ast/nodes/UnitNode.java index 7ce9211e..a3c450d3 100644 --- a/src/main/java/cod/ast/nodes/UnitNode.java +++ b/src/main/java/cod/ast/nodes/UnitNode.java @@ -1,18 +1,23 @@ package cod.ast.nodes; -import cod.ast.CoderiveParser; +import cod.ast.ASTVisitor; + import java.util.List; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; -import org.antlr.v4.runtime.tree.ParseTree; public class UnitNode extends ASTNode { public String name; - public GetNode imports; // CHANGED: from List to GetNode + public UseNode imports; // CHANGED: from List to UseNode public List types = new ArrayList(); // Add this field for resolved imports public Map resolvedImports = new HashMap(); + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/UseNode.java b/src/main/java/cod/ast/nodes/UseNode.java new file mode 100644 index 00000000..2b0ee4eb --- /dev/null +++ b/src/main/java/cod/ast/nodes/UseNode.java @@ -0,0 +1,16 @@ +package cod.ast.nodes; + +import cod.ast.ASTVisitor; + +import java.util.ArrayList; +import java.util.List; + +public class UseNode extends ASTNode { + public List imports = new ArrayList<>(); + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + +} \ No newline at end of file diff --git a/src/main/java/cod/ast/nodes/VarNode.java b/src/main/java/cod/ast/nodes/VarNode.java index 5585d102..177a7f24 100644 --- a/src/main/java/cod/ast/nodes/VarNode.java +++ b/src/main/java/cod/ast/nodes/VarNode.java @@ -1,8 +1,16 @@ package cod.ast.nodes; -public class VarNode extends StatementNode { +import cod.ast.ASTVisitor; + +public class VarNode extends StmtNode { public String name; public ExprNode value; public String explicitType; // <<< ADDED THIS FIELD + + @Override + public final T accept(ASTVisitor visitor) { + return visitor.visit(this); + } + } \ No newline at end of file diff --git a/src/main/java/cod/compiler/BasicBlock.java b/src/main/java/cod/compiler/BasicBlock.java new file mode 100644 index 00000000..5a3110f4 --- /dev/null +++ b/src/main/java/cod/compiler/BasicBlock.java @@ -0,0 +1,31 @@ +package cod.compiler; + +import java.util.*; + +public class BasicBlock { + public int id; + public List instructions = new ArrayList(); + + // Graph edges + public List predecessors = new ArrayList(); + public List successors = new ArrayList(); + + // Liveness Sets + public Set def = new HashSet(); + public Set use = new HashSet(); + public Set liveIn = new HashSet(); + public Set liveOut = new HashSet(); + + public BasicBlock(int id) { + this.id = id; + } + + public void addInstruction(TACInstruction instr) { + instructions.add(instr); + } + + @Override + public String toString() { + return "Block " + id; + } +} \ No newline at end of file diff --git a/src/main/java/cod/compiler/BytecodeCompiler.java b/src/main/java/cod/compiler/BytecodeCompiler.java deleted file mode 100644 index 91e7b20c..00000000 --- a/src/main/java/cod/compiler/BytecodeCompiler.java +++ /dev/null @@ -1,565 +0,0 @@ -package cod.compiler; - -import cod.ast.nodes.*; -import cod.debug.DebugSystem; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class BytecodeCompiler { - private List code = new ArrayList(); - private Map variableSlots = new HashMap(); - private int nextSlot = 0; - private int labelCounter = 0; - - // Pattern to parse combined step operators like "*+2", "/-3", "*2", "+1", "-1" etc. - // Group 1: First operator (*, /, +, -) - Optional - // Group 2: First number (digits, potentially signed like "+2" or "-3") - Optional (defaults based on op1) - private static final Pattern STEP_OP_PATTERN = Pattern.compile("^([*/+-])?([+-]?\\d+)?$"); - - - // --- Helper Method --- - /** - * Checks if an expression node represents a constant integer value - * (either a direct literal or a negated literal) and returns it. - * Returns null if it's not a constant integer. - */ - private Integer getConstantIntValue(ExprNode expr) { - if (expr == null) { - return null; - } - if (expr.value instanceof Integer) { - return (Integer) expr.value; - } - // Check for negated integer literal (e.g., -5) - if (expr instanceof UnaryNode) { - UnaryNode unary = (UnaryNode) expr; - if ("-".equals(unary.op) && unary.operand != null && unary.operand.value instanceof Integer) { - return -(Integer) unary.operand.value; - } - } - return null; // Not a constant integer we can easily evaluate - } - - // --- Helper to check if a step expression is multiplicative/divisive --- - private boolean isMultDivStep(ExprNode expr) { - if (expr == null) return false; - - if (expr.name != null && expr.value == null) { - String op = expr.name.trim(); - // FIX: Just check the prefix. This correctly identifies "*2" AND "*num" - // as multiplicative, while "2" and "num" are not. - if (op.startsWith("*") || op.startsWith("/")) return true; - } else if (expr instanceof BinaryOpNode) { - String op = ((BinaryOpNode)expr).op; - // Check if the root operation is mult/div OR if it involves multiplicative operator strings - if ("*".equals(op) || "/".equals(op) || "%".equals(op)) return true; - // Recursively check if either side implies multiplication/division relative to 'i' - // This handles cases like `by *2 + 1` (root is '+', but involves '*') - if (isMultDivStep(((BinaryOpNode)expr).left)) return true; - if (isMultDivStep(((BinaryOpNode)expr).right)) return true; - } - // Assignment steps like 'i *= 2' are handled separately in compileForLoop - return false; - } - - - public BytecodeProgram compile(ProgramNode program) { - DebugSystem.info("BYTECODE", "Starting MTOT bytecode compilation"); - BytecodeProgram result = new BytecodeProgram(); - if (program.unit != null) { - compileUnit(program.unit, result); - } - int totalInstructions = 0; - for (List methodCode : result.getMethods().values()) { - totalInstructions += methodCode.size(); - } - DebugSystem.info("BYTECODE", "Compilation complete: " + totalInstructions + - " instructions across " + result.getMethods().size() + " methods"); - return result; - } - - private void compileUnit(UnitNode unit, BytecodeProgram program) { - for (TypeNode type : unit.types) { - compileType(type, program); - } - } - - private void compileType(TypeNode type, BytecodeProgram program) { - for (MethodNode method : type.methods) { - compileMethod(method, program); - } - } - - private void compileMethod(MethodNode method, BytecodeProgram program) { - DebugSystem.debug("BYTECODE", "=== COMPILING METHOD: " + method.name + " ==="); - code.clear(); - variableSlots.clear(); - nextSlot = 0; - labelCounter = 0; - - // Allocate slots for parameters - for (ParamNode param : method.parameters) { - allocateVariableSlot(param.name); - DebugSystem.debug("BYTECODE_VARS", "Allocated slot " + (nextSlot - 1) + " for param: " + param.name); - } - - // Allocate slots for return values - for (SlotNode slot : method.returnSlots) { - allocateVariableSlot(slot.name); - DebugSystem.debug("BYTECODE_VARS", "Allocated slot " + (nextSlot - 1) + " for return slot: " + slot.name); - } - - // Compile method body - for (StatementNode stmt : method.body) { - compileStatement(stmt); - } - - // FIXED: Proper return slot handling - if (method.returnSlots.isEmpty()) { - // No return slots - push null - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_NULL)); - } else { - // Return values from slots - // Push return values in reverse order (for multiple return values) - for (int i = method.returnSlots.size() - 1; i >= 0; i--) { - String slotName = method.returnSlots.get(i).name; - Integer slotIndex = variableSlots.get(slotName); - if (slotIndex != null) { - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, slotIndex)); - DebugSystem.debug("BYTECODE", "Loading return slot '" + slotName + "' from slot " + slotIndex); - } else { - DebugSystem.error("BYTECODE", "Return slot '" + slotName + "' not found in variable slots!"); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_NULL)); - } - } - DebugSystem.debug("BYTECODE", "Method " + method.name + " returning " + method.returnSlots.size() + " slot values"); - } - - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.RET)); - program.addMethod(method.name, new ArrayList(code)); - DebugSystem.debug("BYTECODE", "=== FINISHED METHOD: " + method.name + " (" + code.size() + " instructions) ==="); -} - - private void compileStatement(StatementNode stmt) { - if (stmt instanceof VarNode) { compileVariableDeclaration((VarNode) stmt); } - else if (stmt instanceof AssignmentNode) { compileAssignment((AssignmentNode) stmt); } - else if (stmt instanceof SlotAssignmentNode) { compileSlotAssignment((SlotAssignmentNode) stmt); } - else if (stmt instanceof OutputNode) { compileOutput((OutputNode) stmt); } - else if (stmt instanceof IfNode) { compileIfStatement((IfNode) stmt); } - else if (stmt instanceof ForNode) { compileForLoop((ForNode) stmt); } - else if (stmt instanceof MethodCallNode) { - compileMethodCall((MethodCallNode) stmt); - // Basic pop logic (needs refinement based on AST parent info) - if (!isExpressionUsed(stmt, (MethodCallNode)stmt)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.POP)); } - } else if (stmt instanceof ReturnSlotAssignmentNode) { compileReturnSlotAssignment((ReturnSlotAssignmentNode) stmt); } - else if (stmt instanceof InputNode) { compileInput((InputNode) stmt); } - else if (stmt instanceof FieldNode) { /* Ignore */ } - else if (stmt instanceof ExprNode) { - compileExpression((ExprNode) stmt); - if (!isExpressionUsed(stmt, null)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.POP)); } - } else { DebugSystem.warn("BYTECODE", "Unhandled statement type: " + stmt.getClass().getSimpleName()); } - } - - // Crude check, needs AST parent info to be accurate - private boolean isExpressionUsed(StatementNode stmt, MethodCallNode call) { - // If it's a method call that specifies slots, assume it's used - if (call != null && call.slotNames != null && !call.slotNames.isEmpty()) { - return true; - } - // HACK: Don't pop 'main' - if (call != null && "main".equals(call.name)) { - return true; - } - - return false; // Defaulting to pop if usage isn't obvious - } - - - private void compileSlotAssignment(SlotAssignmentNode assign) { - compileExpression(assign.value); - Integer slotIndex = variableSlots.get(assign.slotName); - if (slotIndex != null) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.STORE_SLOT, slotIndex)); } - else { DebugSystem.error("BYTECODE", "Slot '" + assign.slotName + "' not found..."); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.POP)); } - } - - private void compileAssignment(AssignmentNode assign) { - if (assign.left instanceof IndexAccessNode) { /* Array store */ IndexAccessNode access = (IndexAccessNode) assign.left; compileExpression(access.array); compileExpression(access.index); compileExpression(assign.right); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.ARRAY_STORE)); } - else if (assign.left.name != null) { /* Variable store */ String targetName = assign.left.name; compileExpression(assign.right); int slot = getOrAllocateVariableSlot(targetName); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.STORE_LOCAL, slot)); } - else { DebugSystem.warn("BYTECODE", "Unhandled assignment target..."); compileExpression(assign.right); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.POP)); } - } - - private void compileVariableDeclaration(VarNode var) { - int slot = getOrAllocateVariableSlot(var.name); - if (var.value != null) { compileExpression(var.value); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.STORE_LOCAL, slot)); } - else { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_NULL)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.STORE_LOCAL, slot)); } - } - - private void compileOutput(OutputNode output) { - if (output.arguments.isEmpty()) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_STRING, "")); } - else { compileExpression(output.arguments.get(0)); } - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PRINT)); - } - - private void compileIfStatement(IfNode ifNode) { - String elseLabel = generateLabel("else"); String endLabel = generateLabel("endif"); compileExpression(ifNode.condition); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.JMP_IF_FALSE, elseLabel)); for (StatementNode stmt : ifNode.thenBlock.statements) { compileStatement(stmt); } boolean hasElse = !ifNode.elseBlock.statements.isEmpty(); if (hasElse) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.JMP, endLabel)); } placeLabel(elseLabel); if (hasElse) { for (StatementNode stmt : ifNode.elseBlock.statements) { compileStatement(stmt); } } placeLabel(endLabel); - } - - // --- METHOD MODIFIED (FINAL LOOP FIX) --- - private void compileForLoop(ForNode forNode) { - int iteratorSlot = getOrAllocateVariableSlot(forNode.iterator); - int endSlot = allocateVariableSlot(forNode.iterator + "_end"); - - compileExpression(forNode.range.start); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.STORE_LOCAL, iteratorSlot)); - compileExpression(forNode.range.end); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.STORE_LOCAL, endSlot)); - - ExprNode stepExpr = forNode.range.step; - - // CASE 1: Smart Default - if (stepExpr == null) { - // (Omitted for brevity, logic is correct) - Integer startVal = getConstantIntValue(forNode.range.start); Integer endVal = getConstantIntValue(forNode.range.end); if (startVal != null && endVal != null) { boolean countUp = (startVal <= endVal); String loopBody = generateLabel("loop_body_" + (countUp ? "up" : "down")); String loopCheck = generateLabel("loop_check_" + (countUp ? "up" : "down")); String loopEnd = generateLabel("loop_end"); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.JMP, loopCheck)); placeLabel(loopBody); for (StatementNode stmt : forNode.body.statements) { compileStatement(stmt); } code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, iteratorSlot)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_INT, countUp ? 1 : -1)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.ADD_INT)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.STORE_LOCAL, iteratorSlot)); placeLabel(loopCheck); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, iteratorSlot)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, endSlot)); code.add(new BytecodeInstruction(countUp ? BytecodeInstruction.Opcode.CMP_LE_INT : BytecodeInstruction.Opcode.CMP_GE_INT)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.JMP_IF_TRUE, loopBody)); placeLabel(loopEnd); } else { /* Runtime check needed */ String loopBodyUp = generateLabel("loop_body_up"); String loopCheckUp = generateLabel("loop_check_up"); String loopBodyDown = generateLabel("loop_body_down"); String loopCheckDown = generateLabel("loop_check_down"); String loopEnd = generateLabel("loop_end"); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, iteratorSlot)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, endSlot)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.CMP_LE_INT)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.JMP_IF_TRUE, loopCheckUp)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.JMP, loopCheckDown)); placeLabel(loopBodyDown); for (StatementNode stmt : forNode.body.statements) { compileStatement(stmt); } code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, iteratorSlot)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_INT, -1)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.ADD_INT)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.STORE_LOCAL, iteratorSlot)); placeLabel(loopCheckDown); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, iteratorSlot)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, endSlot)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.CMP_GE_INT)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.JMP_IF_TRUE, loopBodyDown)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.JMP, loopEnd)); placeLabel(loopBodyUp); for (StatementNode stmt : forNode.body.statements) { compileStatement(stmt); } code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, iteratorSlot)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_INT, 1)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.ADD_INT)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.STORE_LOCAL, iteratorSlot)); placeLabel(loopCheckUp); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, iteratorSlot)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, endSlot)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.CMP_LE_INT)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.JMP_IF_TRUE, loopBodyUp)); placeLabel(loopEnd); } - // CASE 2: Explicit Step - } else { - String loopStart = generateLabel("loop_start"); String loopContinueCheck = generateLabel("loop_cont"); String loopEnd = generateLabel("loop_end"); - - // --- Static analysis for direction --- - boolean isLikelyCountdown = false; - // (Omitted for brevity, logic is correct for basic cases) - Integer stepConst = getConstantIntValue(stepExpr); if (stepConst != null && stepConst < 0) { isLikelyCountdown = true; } else if (stepExpr instanceof UnaryNode && "-".equals(((UnaryNode)stepExpr).op)) { isLikelyCountdown = true; } else if (stepExpr instanceof BinaryOpNode) { String op = ((BinaryOpNode)stepExpr).op; if (op.equals("-=") || op.equals("/=")) { isLikelyCountdown = true; } else if (op.equals("=")) { ExprNode rhs = ((BinaryOpNode)stepExpr).right; if (rhs instanceof BinaryOpNode) { String rhsOp = ((BinaryOpNode)rhs).op; ExprNode rhsLeft = ((BinaryOpNode)rhs).left; if (rhsLeft != null && rhsLeft.name != null && rhsLeft.name.equals(forNode.iterator)) { if (rhsOp.equals("-") || rhsOp.equals("/")) { isLikelyCountdown = true; } } } else { Integer constRhs = getConstantIntValue(rhs); if(constRhs != null && constRhs < 0) isLikelyCountdown = true; } } else { if (op.equals("-") || op.equals("/")) isLikelyCountdown = true; Integer constRight = getConstantIntValue(((BinaryOpNode)stepExpr).right); if(constRight != null && constRight < 0 && (op.equals("+") || op.equals("*"))) isLikelyCountdown = true; } } else if (stepExpr.value instanceof String || stepExpr.name != null) { String op = (stepExpr.value != null) ? (String)stepExpr.value : stepExpr.name; if (op != null) { op = op.trim(); if (op.startsWith("-") || op.startsWith("/")) { isLikelyCountdown = true; } else if (op.startsWith("*") || op.startsWith("/")) { try { String numStr = op.substring(1); int value = Integer.parseInt(numStr); if (value < 0) isLikelyCountdown = true; } catch (Exception e) {} } else { try { if(Integer.parseInt(op) < 0) isLikelyCountdown = true; } catch(Exception e){} } } } - DebugSystem.debug("BYTECODE", "For-loop '" + forNode.iterator + "' (explicit step): Compile-time direction assumption -> " + (isLikelyCountdown ? "DOWN" : "UP")); - - - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.JMP, loopContinueCheck)); - placeLabel(loopStart); - for (StatementNode stmt : forNode.body.statements) { compileStatement(stmt); } - - // --- MODIFIED STEP LOGIC (FINAL FIX) --- - - boolean isAssignment = (stepExpr instanceof BinaryOpNode && - (((BinaryOpNode)stepExpr).op.endsWith("=") || "=".equals(((BinaryOpNode)stepExpr).op))); - - if (isAssignment) { - BinaryOpNode binOp = (BinaryOpNode) stepExpr; - if ("=".equals(binOp.op)) { - // "by i = i * 2 + 1" -> RHS is the new value - compileStepRHS(binOp.right, iteratorSlot); - } else { - // "by i += 1", "by i *= 2" -> Load i, compile RHS, apply op - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, iteratorSlot)); - compileStepRHS(binOp.right, iteratorSlot); // Compile RHS - - String op = binOp.op; - if ("+=".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.ADD_INT)); } - else if ("-=".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.SUB_INT)); } - else if ("*=".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.MUL_INT)); } - else if ("/=".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.DIV_INT)); } - else { DebugSystem.warn("BYTECODE_STEP", "Unhandled compound assignment in step: " + op); } - } - } - // --- It's a "SIMPLE" step (by 2, by steps, by *2, by *2 + 1, by *+2) --- - else { - boolean isMultDiv = isMultDivStep(stepExpr); - - // Multiplicative/Divisive step ("*2", "/2", "*2 + 1", "*+2") - // The result of this expression *replaces* i. - if (isMultDiv) { - compileStepRHS(stepExpr, iteratorSlot); - } - // Additive/Subtractive step ("2", "steps", "+1", "-1") - // This expression is *added* to i. - else { - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, iteratorSlot)); // Load 'i' - compileStepRHS(stepExpr, iteratorSlot); // Compile the step value/expr - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.ADD_INT)); // i + (result of expr) - } - } - - // --- END MODIFIED STEP LOGIC --- - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.STORE_LOCAL, iteratorSlot)); - - // Condition Check (same) - placeLabel(loopContinueCheck); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, iteratorSlot)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, endSlot)); code.add(new BytecodeInstruction(isLikelyCountdown ? BytecodeInstruction.Opcode.CMP_GE_INT : BytecodeInstruction.Opcode.CMP_LE_INT)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.JMP_IF_TRUE, loopStart)); - placeLabel(loopEnd); - } - } -// Fix the step calculation logic -private void compileStepRHS(ExprNode expr, int iteratorSlot) { - if (expr instanceof BinaryOpNode) { - BinaryOpNode binOp = (BinaryOpNode) expr; - - // Handle assignment operations in steps (e.g., "by i = i + 1") - // Note: This logic seems to be from your existing code for "by i = ..." - if (binOp.op.equals("=") || binOp.op.endsWith("=")) { - compileStepRHS(binOp.right, iteratorSlot); - return; - } - - // Standard binary operation (e.g., "by i * 2 + 1") - compileStepRHS(binOp.left, iteratorSlot); - compileStepRHS(binOp.right, iteratorSlot); - - String op = binOp.op; - if ("+".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.ADD_INT)); } - else if ("-".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.SUB_INT)); } - else if ("*".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.MUL_INT)); } - else if ("/".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.DIV_INT)); } - else if ("%".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.MOD_INT)); } - return; - } - - if (expr instanceof UnaryNode) { - compileStepRHS(((UnaryNode)expr).operand, iteratorSlot); - if ("-".equals(((UnaryNode)expr).op)) { - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.NEG_INT)); - } - return; - } - - // --- NEW LOGIC FOR HANDLING STEP NAMES --- - - if (expr.name != null) { - String name = expr.name.trim(); - - // CASE 1: It's an operator string (e.g., "*2", "/-1", "*num", "/steps") - if (name.startsWith("*") || name.startsWith("/")) { - char op = name.charAt(0); - String operandName = name.substring(1).trim(); - - if (operandName.isEmpty()) { - DebugSystem.error("BYTECODE_STEP", "Multiplicative step operator '" + op + "' has no operand. Defaulting to 1."); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, iteratorSlot)); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_INT, 1)); - } else { - // Load i first for multiplicative steps - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, iteratorSlot)); - - // Now parse the operand (which could be a literal OR a variable) - Integer operandSlot = variableSlots.get(operandName); - if (operandSlot != null) { - // It's a variable (e.g., "*num") - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, operandSlot)); - } else { - // Try to parse as a literal (e.g., "*2") - try { - int value = Integer.parseInt(operandName); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_INT, value)); - } catch (NumberFormatException e2) { - DebugSystem.error("BYTECODE_STEP", "Step operand '" + operandName + "' is not a known variable or an integer literal. Defaulting to 1."); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_INT, 1)); - } - } - } - - // Add the operation - if (op == '*') { - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.MUL_INT)); - } else { - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.DIV_INT)); - } - return; - } - - // CASE 2: It's a simple variable (e.g., "i", "num", "steps") - Integer slot = variableSlots.get(name); - if (slot != null) { - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, slot)); - return; - } - - // CASE 3: It's a simple literal number (e.g., "2", "-1") - try { - int value = Integer.parseInt(name); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_INT, value)); - return; - } catch (NumberFormatException e) { - // Not a simple number, and not an operator string we handled - } - - // CASE 4: Fallback - DebugSystem.warn("BYTECODE_STEP", "Unhandled step expression name: " + name + ". Defaulting to 1."); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_INT, 1)); - return; - } - // --- END NEW LOGIC --- - - - if (expr.value != null) { - compileExpression(expr); - return; - } - - // Fallback: push 1 as default step - DebugSystem.warn("BYTECODE_STEP", "Unhandled step expression, defaulting to 1"); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_INT, 1)); -} - - - private void compileMethodCall(MethodCallNode call) { - for (ExprNode arg : call.arguments) { compileExpression(arg); } - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.CALL, call.qualifiedName)); - } - - private void compileReturnSlotAssignment(ReturnSlotAssignmentNode assignment) { - // Compile method call arguments - for (ExprNode arg : assignment.methodCall.arguments) { - compileExpression(arg); - } - - int numSlotsToReceive = assignment.variableNames.size(); - String methodName = assignment.methodCall.qualifiedName; - Object[] callOperand = new Object[] { methodName, numSlotsToReceive }; - - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.CALL_SLOTS, callOperand)); - - // Store returned values to target variables - for (int i = numSlotsToReceive - 1; i >= 0; i--) { - String varName = assignment.variableNames.get(i); - int slot = getOrAllocateVariableSlot(varName); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.STORE_LOCAL, slot)); - } - - DebugSystem.debug("BYTECODE", "Compiled ReturnSlotAssignment for " + numSlotsToReceive + " slots from " + methodName); -} - - private void compileInput(InputNode input) { - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.READ_INPUT, input.targetType)); int slot = getOrAllocateVariableSlot(input.variableName); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.STORE_LOCAL, slot)); - DebugSystem.debug("BYTECODE", "Compiled InputNode for " + input.variableName + " (type: "+input.targetType+")"); - } - - private void compileExpression(ExprNode expr) { - if (expr == null) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_NULL)); return; } - if (expr instanceof UnaryNode) { compileUnary((UnaryNode) expr); } - else if (expr instanceof BinaryOpNode) { compileBinaryOperation((BinaryOpNode) expr); } - else if (expr instanceof ArrayNode) { compileArrayLiteral((ArrayNode) expr); } - else if (expr instanceof IndexAccessNode) { compileArrayAccess((IndexAccessNode) expr); } - else if (expr instanceof MethodCallNode) { compileMethodCall((MethodCallNode) expr); } - else if (expr instanceof TypeCastNode) { compileTypeCast((TypeCastNode) expr); } - else if (expr.name != null) { - Integer slot = variableSlots.get(expr.name); - if (slot != null) { - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_LOCAL, slot)); - } else { - DebugSystem.warn("BYTECODE", "Loading unknown name potentially as field: " + expr.name); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LOAD_FIELD, expr.name)); - } - } - else if (expr.value != null) { if (expr.value instanceof Integer) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_INT, expr.value)); } else if (expr.value instanceof Float) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_FLOAT, expr.value)); } else if (expr.value instanceof String) { String str = (String) expr.value; if (str.length() >= 2 && str.startsWith("\"") && str.endsWith("\"")) { str = str.substring(1, str.length() - 1); } code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_STRING, str)); } else if (expr.value instanceof Boolean) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_BOOL, expr.value)); } else { DebugSystem.warn("BYTECODE", "Unhandled literal type: " + expr.value.getClass().getName()); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_NULL)); } } - else { DebugSystem.warn("BYTECODE", "Unhandled expression type: " + expr.getClass().getSimpleName()); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_NULL)); } -} - - private void compileUnary(UnaryNode unary) { - compileExpression(unary.operand); - switch (unary.op) { - case "-": code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.NEG_INT)); break; - case "+": break; // Unary plus is a no-op - default: DebugSystem.warn("BYTECODE", "Unhandled unary operator: " + unary.op); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.POP)); - } - } - - private void compileTypeCast(TypeCastNode cast) { - compileExpression(cast.expression); DebugSystem.warn("BYTECODE", "CAST instruction bytecode generation not implemented."); - } - - private void compileArrayAccess(IndexAccessNode access) { - DebugSystem.debug("BYTECODE_ARRAY", "Compiling array access: " + access.array + "[" + access.index + "]"); - compileExpression(access.array); - compileExpression(access.index); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.ARRAY_LOAD)); -} - -private void compileArrayLiteral(ArrayNode literal) { - int size = literal.elements.size(); - DebugSystem.debug("BYTECODE_ARRAY", "Compiling array literal with " + size + " elements"); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_INT, size)); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.ARRAY_NEW)); - for (int i = 0; i < size; i++) { - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.DUP)); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_INT, i)); - compileExpression(literal.elements.get(i)); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.ARRAY_STORE)); - } -} - - private void compileBinaryOperation(BinaryOpNode binOp) { - String op = binOp.op; - - if ("+".equals(op)) { - compileExpression(binOp.left); - boolean leftIsString = expressionMightBeString(binOp.left); - - compileExpression(binOp.right); - boolean rightIsString = expressionMightBeString(binOp.right); - - if (leftIsString && rightIsString) { - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.CONCAT_STRING)); - } else if (leftIsString && !rightIsString) { - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.INT_TO_STRING)); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.CONCAT_STRING)); - } else if (!leftIsString && rightIsString) { - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.SWAP)); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.INT_TO_STRING)); - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.CONCAT_STRING)); - } else { - code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.ADD_INT)); - } - return; - } - - compileExpression(binOp.left); - compileExpression(binOp.right); - - if ("-".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.SUB_INT)); } - else if ("*".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.MUL_INT)); } - else if ("/".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.DIV_INT)); } - else if ("%".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.MOD_INT)); } - else if ("==".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.CMP_EQ_INT)); } - else if ("!=".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.CMP_NE_INT)); } - else if ("<".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.CMP_LT_INT)); } - else if ("<=".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.CMP_LE_INT)); } - else if (">".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.CMP_GT_INT)); } - else if (">=".equals(op)) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.CMP_GE_INT)); } - else { DebugSystem.warn("BYTECODE", "Unhandled binary operator: " + op); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.POP)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.POP)); code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.PUSH_NULL)); } -} - - - private boolean expressionMightBeString(ExprNode expr) { - if (expr == null) return false; - if (expr.value instanceof String) return true; - if (expr instanceof BinaryOpNode && "+".equals(((BinaryOpNode)expr).op)) return true; - if (expr instanceof MethodCallNode) return true; - if (expr instanceof TypeCastNode && "string".equals(((TypeCastNode)expr).targetType)) return true; - - // Handle variable names - if (expr.name != null) { - String name = expr.name; - System.out.println("DEBUG STRING_CHECK: Checking if '" + name + "' might be string"); - - if (name.equals("name") || name.equals("op") || name.equals("operation") || - name.equals("formula") || name.equals("targetType") || - name.contains("str") || name.contains("text") || name.contains("msg") || - name.contains("label") || name.contains("type") || name.contains("Type") || - name.contains("input") || name.contains("Output")) { - System.out.println("DEBUG STRING_CHECK: '" + name + "' -> TRUE"); - return true; - } else { - System.out.println("DEBUG STRING_CHECK: '" + name + "' -> FALSE"); - } - } - - return false; -} - - - private int allocateVariableSlot(String name) { - if (variableSlots.containsKey(name)) { DebugSystem.debug("BYTECODE_VARS", "Variable '" + name + "' is being re-allocated (shadowing or temp slot)."); } - int slot = nextSlot++; variableSlots.put(name, slot); return slot; - } - - private int getExistingVariableSlot(String name) { Integer slot = variableSlots.get(name); if (slot == null) { throw new IllegalStateException("Attempted to get slot for unallocated variable/param: " + name); } return slot; } - private int getOrAllocateVariableSlot(String name) { if (!variableSlots.containsKey(name)) { return allocateVariableSlot(name); } return variableSlots.get(name); } - private String generateLabel(String prefix) { return prefix + "_" + (labelCounter++); } - private void placeLabel(String label) { code.add(new BytecodeInstruction(BytecodeInstruction.Opcode.LABEL, label)); } - -} // End of BytecodeCompiler class \ No newline at end of file diff --git a/src/main/java/cod/compiler/BytecodeInstruction.java b/src/main/java/cod/compiler/BytecodeInstruction.java deleted file mode 100644 index d11c0f43..00000000 --- a/src/main/java/cod/compiler/BytecodeInstruction.java +++ /dev/null @@ -1,68 +0,0 @@ -package cod.compiler; - -public class BytecodeInstruction { - public enum Opcode { - PUSH_INT, - PUSH_FLOAT, - PUSH_STRING, - PUSH_BOOL, - PUSH_NULL, - POP, - DUP, - SWAP, - - ADD_INT, - SUB_INT, - MUL_INT, - DIV_INT, - MOD_INT, - NEG_INT, - - CONCAT_STRING, - INT_TO_STRING, - - CMP_EQ_INT, - CMP_NE_INT, - CMP_LT_INT, - CMP_LE_INT, - CMP_GT_INT, - CMP_GE_INT, - - JMP, - JMP_IF_TRUE, - JMP_IF_FALSE, - CALL, - CALL_SLOTS, - RET, - LABEL, - - LOAD_LOCAL, - STORE_LOCAL, - LOAD_FIELD, - STORE_FIELD, - STORE_SLOT, - - ARRAY_NEW, - ARRAY_LOAD, - ARRAY_STORE, - ARRAY_LENGTH, - - PRINT, - READ_INPUT, - RANGE_START, - RANGE_END, - RANGE_STEP -} - - public final Opcode opcode; - public final Object operand; - - public BytecodeInstruction(Opcode opcode, Object operand) { - this.opcode = opcode; - this.operand = operand; - } - - public BytecodeInstruction(Opcode opcode) { - this(opcode, null); - } -} \ No newline at end of file diff --git a/src/main/java/cod/compiler/BytecodeProgram.java b/src/main/java/cod/compiler/BytecodeProgram.java deleted file mode 100644 index f181316f..00000000 --- a/src/main/java/cod/compiler/BytecodeProgram.java +++ /dev/null @@ -1,73 +0,0 @@ -package cod.compiler; - -import java.util.*; - -public class BytecodeProgram { - private Map> methods = new HashMap<>(); - private Map nativeMethods = new HashMap<>(); // NEW: Native assembly storage - private List mainCode = new ArrayList<>(); - - public void addMethod(String name, List code) { - methods.put(name, new ArrayList<>(code)); - } - - // NEW: Add native method implementation - public void addNativeMethod(String name, String assemblyCode) { - nativeMethods.put(name, assemblyCode); - } - - public List getMethod(String name) { - return methods.get(name); - } - - public Map> getMethods() { - return methods; - } - - // NEW: Get native method implementation - public String getNativeMethod(String name) { - return nativeMethods.get(name); - } - - // --- ADDED: The missing method --- - public Map getNativeMethods() { - return nativeMethods; - } - // --- END ADDED --- - - public boolean hasNativeImplementation(String name) { - return nativeMethods.containsKey(name); - } - - public void setMainCode(List code) { - this.mainCode = new ArrayList<>(code); - } - - public List getMainCode() { - return mainCode; - } - - public void disassemble() { - System.out.println("=== MTOT Bytecode Disassembly ==="); - for (Map.Entry> entry : methods.entrySet()) { - System.out.println("\nMethod: " + entry.getKey()); - List instructions = entry.getValue(); - for (int i = 0; i < instructions.size(); i++) { - BytecodeInstruction instr = instructions.get(i); - System.out.printf( - " %04d: %-15s %s%n", - i, instr.opcode, instr.operand != null ? instr.operand : ""); - } - } - } - - // --- MODIFIED: Display native code --- - public void disassembleNative() { - // System.out.println("=== MTOT Native Code ==="); // <-- REMOVED - for (Map.Entry entry : nativeMethods.entrySet()) { - // System.out.println("\nMethod: " + entry.getKey()); // <-- REMOVED - System.out.println(entry.getValue()); - } - } - // --- END MODIFIED --- -} \ No newline at end of file diff --git a/src/main/java/cod/compiler/GraphColoringAllocator.java b/src/main/java/cod/compiler/GraphColoringAllocator.java new file mode 100644 index 00000000..10b1ba6c --- /dev/null +++ b/src/main/java/cod/compiler/GraphColoringAllocator.java @@ -0,0 +1,113 @@ +package cod.compiler; + +import cod.debug.DebugSystem; +import java.util.*; + +public class GraphColoringAllocator { + private final MTOTRegistry.RegisterFile registerFile; + private final RegisterManager.RegisterSpiller spiller; + // NEW: Store a reference to the InterferenceGraph + private InterferenceGraph interferenceGraph; + + public GraphColoringAllocator(MTOTRegistry.RegisterFile registerFile, RegisterManager.RegisterSpiller spiller) { + this.registerFile = registerFile; + this.spiller = spiller; + // Initialize the graph so it exists when RegisterManager calls the setter + this.interferenceGraph = new InterferenceGraph(); + } + + // NEW METHOD: Allows RegisterManager to access the graph for ABI setup + public InterferenceGraph getInterferenceGraph() { + return interferenceGraph; + } + + public Map allocate(InterferenceGraph graph) { + // ASSIGN THE INPUT GRAPH TO THE MEMBER VARIABLE BEFORE PROCEEDING + this.interferenceGraph = graph; + + DebugSystem.info("ALLOC", "Coloring graph with " + graph.adjList.size() + " nodes"); + + Stack stack = new Stack(); + Set spilledNodes = new HashSet(); + + // Deep copy graph for manipulation + Map> workingGraph = new HashMap>(); + Map workingDegrees = new HashMap(); + + for (Map.Entry> entry : graph.adjList.entrySet()) { + workingGraph.put(entry.getKey(), new HashSet(entry.getValue())); + workingDegrees.put(entry.getKey(), graph.degrees.get(entry.getKey())); + } + + int K = registerFile.generalPurpose.size(); + + // SIMPLIFY + int numNodes = workingGraph.size(); + while (stack.size() + spilledNodes.size() < numNodes) { + String nodeToRemove = null; + + // Find trivially colorable node + for (Map.Entry entry : workingDegrees.entrySet()) { + if (entry.getValue() != -1 && entry.getValue() < K) { + nodeToRemove = entry.getKey(); + break; + } + } + + // Optimistic Spill + if (nodeToRemove == null) { + int maxDegree = -1; + for (Map.Entry entry : workingDegrees.entrySet()) { + if (entry.getValue() != -1 && entry.getValue() > maxDegree) { + maxDegree = entry.getValue(); + nodeToRemove = entry.getKey(); + } + } + } + + if (nodeToRemove == null) break; + + stack.push(nodeToRemove); + workingDegrees.put(nodeToRemove, -1); + + for (String neighbor : workingGraph.get(nodeToRemove)) { + if (workingDegrees.get(neighbor) != -1) { + workingDegrees.put(neighbor, workingDegrees.get(neighbor) - 1); + } + } + } + + // SELECT + Map assignments = new HashMap(); + + while (!stack.isEmpty()) { + String node = stack.pop(); + Set usedColors = new HashSet(); + + if (graph.adjList.containsKey(node)) { + for (String neighbor : graph.adjList.get(node)) { + if (assignments.containsKey(neighbor)) { + usedColors.add(assignments.get(neighbor)); + } + } + } + + String chosenColor = null; + for (String reg : registerFile.generalPurpose) { + if (!usedColors.contains(reg)) { + chosenColor = reg; + break; + } + } + + if (chosenColor != null) { + assignments.put(node, chosenColor); + } else { + spiller.forceSpill(node); + spilledNodes.add(node); + } + } + + return assignments; + } +} \ No newline at end of file diff --git a/src/main/java/cod/compiler/InterferenceGraph.java b/src/main/java/cod/compiler/InterferenceGraph.java new file mode 100644 index 00000000..267bb391 --- /dev/null +++ b/src/main/java/cod/compiler/InterferenceGraph.java @@ -0,0 +1,82 @@ +package cod.compiler; + +import java.util.*; + +public class InterferenceGraph { + public final Map> adjList = new HashMap>(); + public final Map degrees = new HashMap(); + + // NOTE: This will be injected/called by MTOTNativeCompiler + // to provide the necessary register sets (x9-x17 for AArch64) + public Set callerSavedRegisters = Collections.emptySet(); + + public void addNode(String node) { + if (!adjList.containsKey(node)) { + adjList.put(node, new HashSet()); + degrees.put(node, 0); + } + } + + public void addEdge(String u, String v) { + if (u.equals(v)) return; + addNode(u); + addNode(v); + if (adjList.get(u).add(v)) { + degrees.put(u, degrees.get(u) + 1); + } + if (adjList.get(v).add(u)) { + degrees.put(v, degrees.get(v) + 1); + } + } + + public void buildFromCFG(List blocks) { + for (BasicBlock block : blocks) { + Set liveNow = new HashSet(block.liveOut); + + for (int i = block.instructions.size() - 1; i >= 0; i--) { + TACInstruction instr = block.instructions.get(i); + + String def = instr.getDef(); + List uses = instr.getUses(); + + if (def != null) { + addNode(def); + for (String liveVar : liveNow) { + addEdge(def, liveVar); + } + liveNow.remove(def); + } + + if (instr.opcode == TACInstruction.Opcode.CALL || + instr.opcode == TACInstruction.Opcode.CALL_SLOTS) { + + // CRITICAL FIX: Add interference between live variables + // and ALL caller-saved registers (register clobbering model) + addCallInterferences(liveNow); + } + + for (String use : uses) { + addNode(use); + liveNow.add(use); + } + } + } + } + + /** + * Models the fact that a function call clobbers all caller-saved registers. + * Any variable currently live (in liveNow) must interfere with all + * caller-saved registers to ensure it is allocated to a callee-saved + * register or spilled to the stack. + */ + private void addCallInterferences(Set liveNow) { + for (String liveVar : liveNow) { + for (String clobberReg : callerSavedRegisters) { + // Add the caller-saved register as a pseudo-node in the graph + addNode(clobberReg); + // Add an edge from the live variable to the clobbered register + addEdge(liveVar, clobberReg); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/cod/compiler/LivenessAnalyzer.java b/src/main/java/cod/compiler/LivenessAnalyzer.java new file mode 100644 index 00000000..ac40e8a0 --- /dev/null +++ b/src/main/java/cod/compiler/LivenessAnalyzer.java @@ -0,0 +1,127 @@ +package cod.compiler; + +import cod.compiler.TACInstruction.Opcode; +import java.util.*; + +public class LivenessAnalyzer { + + public List buildCFG(List instructions) { + List blocks = new ArrayList(); + if (instructions.isEmpty()) return blocks; + + Map labelToBlock = new HashMap(); + BasicBlock currentBlock = new BasicBlock(0); + blocks.add(currentBlock); + + for (int i = 0; i < instructions.size(); i++) { + TACInstruction instr = instructions.get(i); + + if (instr.opcode == Opcode.LABEL) { + if (!currentBlock.instructions.isEmpty()) { + currentBlock = new BasicBlock(blocks.size()); + blocks.add(currentBlock); + } + labelToBlock.put((String)instr.operand1, currentBlock); + } + + currentBlock.addInstruction(instr); + + if (isBranchOrTerminator(instr)) { + if (i + 1 < instructions.size()) { + currentBlock = new BasicBlock(blocks.size()); + blocks.add(currentBlock); + } + } + } + + // Compute Local Liveness (Def/Use) + for (BasicBlock block : blocks) { + computeLocalLiveness(block); + } + + // Connect Edges + for (int i = 0; i < blocks.size(); i++) { + BasicBlock block = blocks.get(i); + if (block.instructions.isEmpty()) continue; + + TACInstruction last = block.instructions.get(block.instructions.size() - 1); + + if (last.opcode == Opcode.GOTO) { + String targetLabel = (String) last.operand1; + connect(block, labelToBlock.get(targetLabel)); + } + else if (last.opcode == Opcode.IF_GOTO) { + // Operand2 holds the label in our compiler's emission logic + String targetLabel = (String) last.operand2; + if (i + 1 < blocks.size()) connect(block, blocks.get(i + 1)); // Fallthrough + if (targetLabel != null) connect(block, labelToBlock.get(targetLabel)); // Branch + } + else if (last.opcode == Opcode.RET) { + // No successors + } + else { + if (i + 1 < blocks.size()) connect(block, blocks.get(i + 1)); + } + } + + return blocks; + } + + private void connect(BasicBlock from, BasicBlock to) { + if (from != null && to != null) { + from.successors.add(to); + to.predecessors.add(from); + } + } + + private boolean isBranchOrTerminator(TACInstruction instr) { + switch (instr.opcode) { + case GOTO: case IF_GOTO: case RET: return true; + default: return false; + } + } + + private void computeLocalLiveness(BasicBlock block) { + block.def.clear(); + block.use.clear(); + + for (TACInstruction instr : block.instructions) { + List uses = instr.getUses(); + String def = instr.getDef(); + + for (String u : uses) { + if (!block.def.contains(u)) { + block.use.add(u); + } + } + if (def != null) { + block.def.add(def); + } + } + } + + public void computeGlobalLiveness(List blocks) { + boolean changed = true; + while (changed) { + changed = false; + for (int i = blocks.size() - 1; i >= 0; i--) { + BasicBlock block = blocks.get(i); + + Set newLiveOut = new HashSet(); + for (BasicBlock succ : block.successors) { + newLiveOut.addAll(succ.liveIn); + } + + Set newLiveIn = new HashSet(newLiveOut); + newLiveIn.removeAll(block.def); + newLiveIn.addAll(block.use); + + if (!newLiveIn.equals(block.liveIn) || !newLiveOut.equals(block.liveOut)) { + block.liveIn = newLiveIn; + block.liveOut = newLiveOut; + changed = true; + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/cod/compiler/MTOTNativeCompiler.java b/src/main/java/cod/compiler/MTOTNativeCompiler.java index 2283bd65..42975ad7 100644 --- a/src/main/java/cod/compiler/MTOTNativeCompiler.java +++ b/src/main/java/cod/compiler/MTOTNativeCompiler.java @@ -3,559 +3,528 @@ import static cod.compiler.MTOTRegistry.*; import static cod.compiler.MTOTRegistry.AArch64Registers.*; import static cod.compiler.MTOTRegistry.x86_64Registers.*; -import cod.compiler.BytecodeInstruction.Opcode; -import cod.compiler.natives.*; - -import cod.ast.nodes.*; +import cod.compiler.TACInstruction.Opcode; import cod.debug.DebugSystem; import java.util.*; -import java.lang.reflect.Field; public class MTOTNativeCompiler { - public final CPUProfile cpuProfile; - - public final String thisRegister; - private final Set calleeSavedSet; - private final Set abiCallerSavedSet; - - public final RegisterManager registerManager; - private final RegisterManager.RegisterAllocator registerAllocator; - private final RegisterManager.RegisterSpiller spiller; - public final OperandStack operandStack; - - public List assemblyCode = new ArrayList(); - public final List dataSection = new ArrayList(); - - private int dataLabelCounter = 0; - private String currentMethodName = ""; - public final List argumentRegisters; - - private int currentPc = 0; - private List currentMethodBytecode = null; - private int currentLoopDepth = 0; - - public final RuntimeCallHelper runtimeCallHelper; - public final OperationCompiler operationCompiler; - public final MemoryAccessCompiler memoryAccessCompiler; - public final ControlFlowCompiler controlFlowCompiler; - public final MethodCallCompiler methodCallCompiler; - - public MTOTNativeCompiler(CPUProfile cpuProfile) { - this.cpuProfile = cpuProfile; - - if (cpuProfile.architecture.equals("aarch64")) { - this.thisRegister = AArch64Registers.x19; - this.calleeSavedSet = new HashSet(Arrays.asList( - x19, x20, x21, x22, x23, x24, x25, x26, x27, x28 - )); - this.abiCallerSavedSet = new HashSet(Arrays.asList( - x0, x1, x2, x3, x4, x5, x6, x7, - x9, x10, x11, x12, x13, x14, x15, x16, x17 - )); - } else { - this.thisRegister = x86_64Registers.r15; - this.calleeSavedSet = new HashSet(Arrays.asList( - x86_64Registers.rbx, x86_64Registers.r12, x86_64Registers.r13, - x86_64Registers.r14, x86_64Registers.r15 - )); - this.abiCallerSavedSet = new HashSet(Arrays.asList( - x86_64Registers.rax, x86_64Registers.rcx, x86_64Registers.rdx, - x86_64Registers.rsi, x86_64Registers.rdi, x86_64Registers.r8, - x86_64Registers.r9, x86_64Registers.r10, x86_64Registers.r11 - )); - } - - this.registerManager = new RegisterManager(this); - this.registerAllocator = this.registerManager.getAllocator(); - this.spiller = this.registerManager.getSpiller(); - this.operandStack = new OperandStack(this); - this.argumentRegisters = cpuProfile.registerFile.argumentRegisters; - - this.runtimeCallHelper = new RuntimeCallHelper(this); - this.operationCompiler = new OperationCompiler(this); - this.memoryAccessCompiler = new MemoryAccessCompiler(this); - this.controlFlowCompiler = new ControlFlowCompiler(this); - this.methodCallCompiler = new MethodCallCompiler(this); - } - - public int getCurrentPc() { return currentPc; } - public List getCurrentMethodBytecode() { return currentMethodBytecode; } - public int getCurrentLoopDepth() { return currentLoopDepth; } + public final CPUProfile cpuProfile; + public final String thisRegister; + private final Set abiCallerSavedSet; + public final List argumentRegisters; - public String compileMethodFromBytecode(String methodName, List bytecode) { - DebugSystem.debug("MTOT", "Compiling method from bytecode: " + methodName); + private final String scratchReg1; + private final String scratchReg2; - operandStack.clear(); - dataSection.clear(); - registerManager.reset(); - memoryAccessCompiler.reset(); - dataLabelCounter = 0; - this.currentMethodName = methodName; - this.currentMethodBytecode = bytecode; - this.currentPc = 0; - this.currentLoopDepth = 0; + public final RegisterManager registerManager; + private final RegisterManager.RegisterSpiller spiller; - List tempAssemblyCode = new ArrayList(); - List originalAssemblyCodeRef = this.assemblyCode; - setAssemblyTarget(tempAssemblyCode); + private List assemblyCode = new ArrayList(); + private final List dataSection = new ArrayList(); - registerAllocator.markRegisterUsed(this.thisRegister); - spiller.updateRegisterDefinitionDepth(this.thisRegister, 0); + private int dataLabelCounter = 0; + private String currentMethodName = ""; + + public MTOTNativeCompiler(CPUProfile cpuProfile) { + this.cpuProfile = cpuProfile; + this.argumentRegisters = cpuProfile.registerFile.argumentRegisters; - int maxParamsInRegs; - int argStartIndex; if (cpuProfile.architecture.equals("aarch64")) { - maxParamsInRegs = argumentRegisters.size() - 1; - argStartIndex = 1; + this.thisRegister = x19; + this.scratchReg1 = x9; + this.scratchReg2 = x10; + this.abiCallerSavedSet = + new HashSet( + Arrays.asList( + x0, x1, x2, x3, x4, x5, x6, x7, x9, x10, x11, x12, x13, x14, x15, x16, x17)); } else { - maxParamsInRegs = (argumentRegisters.contains(x86_64Registers.rax)) ? - argumentRegisters.size() - 1 : - argumentRegisters.size(); - argStartIndex = 0; + this.thisRegister = r15; + this.scratchReg1 = r10; + this.scratchReg2 = r11; + this.abiCallerSavedSet = + new HashSet(Arrays.asList(rax, rcx, rdx, rsi, rdi, r8, r9, r10, r11)); } - for (int i = 0; i < maxParamsInRegs; i++) { - String argReg = argumentRegisters.get(i + argStartIndex); - registerAllocator.markRegisterUsed(argReg); - spiller.updateRegisterDefinitionDepth(argReg, 0); - } + this.registerManager = new RegisterManager(this); + this.spiller = this.registerManager.getSpiller(); + } - compileFromBytecode(bytecode, new HashMap()); + public String compileMethodFromTAC(String methodName, List tac) { + DebugSystem.info("MTOT", "Compiling method: " + methodName + " (" + tac.size() + " TAC instr)"); - setAssemblyTarget(originalAssemblyCodeRef); + assemblyCode.clear(); + dataSection.clear(); + registerManager.reset(); + dataLabelCounter = 0; + this.currentMethodName = methodName; - Set usedCalleeSavedRegs = new HashSet(); - Set writtenRegisters = spiller.getWrittenRegistersDuringCompilation(); - writtenRegisters.retainAll(this.calleeSavedSet); - usedCalleeSavedRegs.addAll(writtenRegisters); - - if (registerAllocator.getUsedRegisters().contains(this.thisRegister)) { - usedCalleeSavedRegs.add(this.thisRegister); + // FIX: Pass the ABI caller-saved registers to the RegisterManager's graph + // BEFORE running allocation. + this.registerManager.setCallerSavedRegisters(this.abiCallerSavedSet); + + registerManager.runAllocation(tac); + compileBody(tac); + return constructFinalAssembly(methodName); + } + + private void compileBody(List tac) { + Map labelMap = new HashMap(); + for (TACInstruction instr : tac) { + if (instr.opcode == Opcode.LABEL) { + labelMap.put((String) instr.operand1, "L_" + currentMethodName + "_" + instr.operand1); + } } - List calleeSavedToSave = new ArrayList(usedCalleeSavedRegs); - Collections.sort(calleeSavedToSave); + for (int i = 0; i < tac.size(); i++) { + TACInstruction instr = tac.get(i); + + try { + switch (instr.opcode) { + case LABEL: + assemblyCode.add(labelMap.get((String) instr.operand1) + ":"); + break; + + case ASSIGN: + String src = getPhysReg(instr.operand1, scratchReg1, true); + String dest = getPhysReg(instr.result, scratchReg2, false); + if (!src.equals(dest)) emitMove(dest, src); + commitSpill(instr.result, dest); + break; + + case LOAD_IMM: + String immDest = getPhysReg(instr.result, scratchReg1, false); + int val = 0; + if (instr.operand1 instanceof Integer) val = (Integer) instr.operand1; + else if (instr.operand1 instanceof Boolean) val = ((Boolean) instr.operand1) ? 1 : 0; + + String asm = + cpuProfile + .getPattern("load_immediate_int") + .assemblyTemplate + .get(0) + .replace("{dest}", immDest) + .replace("{value}", String.valueOf(val)); + assemblyCode.add(" " + asm); + commitSpill(instr.result, immDest); + break; + + case LOAD_ADDR: + String addrDest = getPhysReg(instr.result, scratchReg1, false); + String dataLabel = generateDataLabel("str"); + dataSection.add( + cpuProfile + .syntax + .stringDirective + .replace("{label}", dataLabel) + .replace("{value}", escapeString((String) instr.operand1))); + for (String t : cpuProfile.getPattern("load_address").assemblyTemplate) { + assemblyCode.add( + " " + t.replace("{dest}", addrDest).replace("{label}", dataLabel)); + } + commitSpill(instr.result, addrDest); + break; + + case ADD: + case SUB: + case MUL: + case DIV: + case MOD: + case CMP_EQ: + case CMP_NE: + case CMP_LT: + case CMP_LE: + case CMP_GT: + case CMP_GE: + compileBinaryOp(instr); + break; + + case INT_TO_STRING: + case CONCAT: + compileStringOp(instr); + break; + + case NEG: + String negSrc = getPhysReg(instr.operand1, scratchReg1, true); + String negDest = getPhysReg(instr.result, scratchReg2, false); + for (String t : cpuProfile.getPattern("neg_int").assemblyTemplate) { + assemblyCode.add(" " + t.replace("{dest}", negDest).replace("{src}", negSrc)); + } + commitSpill(instr.result, negDest); + break; + + case GOTO: + assemblyCode.add( + " " + + cpuProfile + .getPattern("jmp") + .assemblyTemplate + .get(0) + .replace("{label}", labelMap.get((String) instr.operand1))); + break; + + case IF_GOTO: + String ifCond = + getPhysReg( + instr.result, scratchReg1, true); // Sometimes cond is result in TAC struct + String ifLabel = labelMap.get((String) instr.operand2); + if (cpuProfile.architecture.equals("aarch64")) { + assemblyCode.add(" cmp " + ifCond + ", #0"); + assemblyCode.add(" b.eq " + ifLabel); + } else { + assemblyCode.add(" test " + ifCond + ", " + ifCond); + assemblyCode.add(" jz " + ifLabel); + } + break; - int spillAreaSize = spiller.getTotalSpillSize(); - int calleeSavedAreaSize; - if (cpuProfile.architecture.equals("aarch64")) { - calleeSavedAreaSize = calleeSavedToSave.size() * 8; - } else { - calleeSavedAreaSize = 0; + case RET: + if (instr.operand1 != null) { + String retSrc = getPhysReg(instr.operand1, scratchReg1, true); + String abiRet = cpuProfile.architecture.equals("x86_64") ? rax : x0; + if (!retSrc.equals(abiRet)) emitMove(abiRet, retSrc); + } + break; + + case CALL: + case CALL_SLOTS: + compileCall(instr, i, tac); + break; + + case PRINT: + compileRuntimeCall("runtime_print", new String[] {(String) instr.operand1}, false); + break; + + case READ_INPUT: + compileReadInput(instr.result, (String) instr.operand1); + break; + + case ARRAY_NEW: + compileRuntimeCall("array_new", new String[] {(String) instr.operand1}, true); + if (instr.result != null) { + String retReg = cpuProfile.architecture.equals("x86_64") ? rax : x0; + String d = getPhysReg(instr.result, scratchReg1, false); + emitMove(d, retReg); + commitSpill(instr.result, d); + } + break; + + case LOAD_ARRAY: + compileRuntimeCall( + "array_load", + new String[] {(String) instr.operand1, (String) instr.operand2}, + true); + String laRet = cpuProfile.architecture.equals("x86_64") ? rax : x0; + String laDest = getPhysReg(instr.result, scratchReg1, false); + emitMove(laDest, laRet); + commitSpill(instr.result, laDest); + break; + + case STORE_ARRAY: + compileRuntimeCall( + "array_store", + new String[] {(String) instr.operand1, (String) instr.operand2, instr.result}, + false); + break; + + case PARAM: + break; + default: + DebugSystem.warn("MTOT", "Ignored TAC: " + instr.opcode); + } + } catch (Exception e) { + DebugSystem.error("MTOT", "Error compiling " + instr + ": " + e.getMessage()); + } } - - int sizeBelowFp = calleeSavedAreaSize + spillAreaSize; - int alignedSizeBelowFp = (sizeBelowFp + 15) & ~15; - - List finalAssembly = new ArrayList(); - - if (!dataSection.isEmpty()) { - finalAssembly.add(cpuProfile.syntax.dataSection); - finalAssembly.addAll(dataSection); + } + + private void compileBinaryOp(TACInstruction instr) { + String pName; + switch (instr.opcode) { + case ADD: + pName = "add_int"; + break; + case SUB: + pName = "sub_int"; + break; + case MUL: + pName = "mul_int"; + break; + case DIV: + pName = "div_int"; + break; + case MOD: + pName = "mod_int"; + break; + case CMP_EQ: + pName = "cmp_eq_int"; + break; + case CMP_NE: + pName = "cmp_ne_int"; + break; + case CMP_LT: + pName = "cmp_lt_int"; + break; + case CMP_LE: + pName = "cmp_le_int"; + break; + case CMP_GT: + pName = "cmp_gt_int"; + break; + case CMP_GE: + pName = "cmp_ge_int"; + break; + default: + return; } - finalAssembly.add(cpuProfile.syntax.textSection); - - if (cpuProfile.architecture.equals("x86_64")) { - finalAssembly.add(" " + cpuProfile.syntax.commentMarker + " NASM requires extern declarations for runtime functions"); - finalAssembly.add(" extern runtime_print"); - finalAssembly.add(" extern string_concat"); - finalAssembly.add(" extern runtime_int_to_string"); - finalAssembly.add(" extern array_new"); - finalAssembly.add(" extern array_load"); - finalAssembly.add(" extern array_store"); - finalAssembly.add(" extern runtime_read_input"); - finalAssembly.add(""); - } - - finalAssembly.add(cpuProfile.syntax.globalDirective.replace("{name}", methodName)); - finalAssembly.add(methodName + ":"); - - InstructionPattern prologuePattern = cpuProfile.getPattern("prologue"); - if (prologuePattern == null) throw new RuntimeException("Missing 'prologue' pattern!"); - for (String template : prologuePattern.assemblyTemplate) { - finalAssembly.add(" " + template); - } + InstructionPattern p = cpuProfile.getPattern(pName); + String r1 = getPhysReg(instr.operand1, scratchReg1, true); + String r2 = getPhysReg(instr.operand2, scratchReg2, true); + String rDest = getPhysReg(instr.result, scratchReg1, false); - if (cpuProfile.architecture.equals("aarch64")) { - if (alignedSizeBelowFp > 0) { - String asm = cpuProfile.getPattern("alloc_stack_frame").assemblyTemplate.get(0) - .replace("{size}", String.valueOf(alignedSizeBelowFp)); - finalAssembly.add(" " + asm); - } - - int currentCalleeSaveOffset = -16; - if (!calleeSavedToSave.isEmpty()) { - finalAssembly.add(" " + cpuProfile.syntax.commentMarker + " Saving callee-saved registers: " + calleeSavedToSave); - InstructionPattern savePair = cpuProfile.getPattern("save_callee_reg_pair"); - InstructionPattern saveSingle = cpuProfile.getPattern("save_callee_reg_single"); - - for (int i = 0; i < calleeSavedToSave.size(); i += 2) { - if (i + 1 < calleeSavedToSave.size()) { - finalAssembly.add(" " + savePair.assemblyTemplate.get(0) - .replace("{reg1}", calleeSavedToSave.get(i)) - .replace("{reg2}", calleeSavedToSave.get(i+1)) - .replace("{offset}", String.valueOf(currentCalleeSaveOffset))); - } else { - finalAssembly.add(" " + saveSingle.assemblyTemplate.get(0) - .replace("{reg1}", calleeSavedToSave.get(i)) - .replace("{offset}", String.valueOf(currentCalleeSaveOffset))); - } - currentCalleeSaveOffset -= 16; + for (String t : p.assemblyTemplate) { + assemblyCode.add( + " " + t.replace("{dest}", rDest).replace("{src1}", r1).replace("{src2}", r2)); + } + commitSpill(instr.result, rDest); + } + + private void compileStringOp(TACInstruction instr) { + switch (instr.opcode) { + case INT_TO_STRING: { + String src = getPhysReg(instr.operand1, scratchReg1, true); + // Pass integer in argument register 0 + String argReg0 = argumentRegisters.get(0); + if (!src.equals(argReg0)) { + emitMove(argReg0, src); } - } - spiller.setBaseSpillOffset(currentCalleeSaveOffset + 8); - - } else { - int x86SpillAreaSize = (spiller.getTotalSpillSize() + 15) & ~15; - - if (!calleeSavedToSave.isEmpty()) { - finalAssembly.add(" " + cpuProfile.syntax.commentMarker + " Saving callee-saved registers: " + calleeSavedToSave); - InstructionPattern saveSingle = cpuProfile.getPattern("save_callee_reg_single"); - if (saveSingle.assemblyTemplate.isEmpty()) throw new RuntimeException("Missing 'save_callee_reg_single' pattern for x86!"); - for (String reg : calleeSavedToSave) { - finalAssembly.add(" " + saveSingle.assemblyTemplate.get(0).replace("{reg}", reg)); + // Call runtime function to convert int to string + assemblyCode.add(" " + cpuProfile.getPattern("call").assemblyTemplate.get(0) + .replace("{name}", "int_to_string")); + // Get return value + String retReg = cpuProfile.architecture.equals("x86_64") ? rax : x0; + String dest = getPhysReg(instr.result, scratchReg1, false); + if (!dest.equals(retReg)) { + emitMove(dest, retReg); } + commitSpill(instr.result, dest); + break; } - - if (x86SpillAreaSize > 0) { - String asm = cpuProfile.getPattern("alloc_stack_frame").assemblyTemplate.get(0) - .replace("{size}", String.valueOf(x86SpillAreaSize)); - finalAssembly.add(" " + asm); - } - spiller.setBaseSpillOffset(-8); - } - - finalAssembly.add(" mov " + this.thisRegister + ", " + argumentRegisters.get(0) + " " + cpuProfile.syntax.commentMarker + " Copy 'this' pointer"); - - finalAssembly.addAll(tempAssemblyCode); - - Map slotLocations = memoryAccessCompiler.getSlotLocations(); - String returnReg0 = (cpuProfile.architecture.equals("x86_64")) ? x86_64Registers.rax : argumentRegisters.get(0); - - if (!slotLocations.isEmpty()) { - List sortedSlotIndices = new ArrayList(slotLocations.keySet()); - Collections.sort(sortedSlotIndices); - int returnRegIndex = 0; - - for (Integer slotIndex : sortedSlotIndices) { - String abiReturnReg; - if (cpuProfile.architecture.equals("x86_64")) { - if (returnRegIndex == 0) abiReturnReg = x86_64Registers.rax; - else if (returnRegIndex == 1) abiReturnReg = x86_64Registers.rdx; - else break; - } else { - if (returnRegIndex >= argumentRegisters.size()) break; - abiReturnReg = argumentRegisters.get(returnRegIndex); - } - String valueLocation = slotLocations.get(slotIndex); - if (valueLocation.startsWith("spill_")) { - int offset = Integer.parseInt(valueLocation.substring("spill_".length())); - spiller.fillRegisterFromOffset(abiReturnReg, offset); - } else { - spiller.fillRegister(valueLocation); - if (!valueLocation.equals(abiReturnReg)) { - finalAssembly.add(" mov " + abiReturnReg + ", " + valueLocation); - } + case CONCAT: { + String str1 = getPhysReg(instr.operand1, scratchReg1, true); + String str2 = getPhysReg(instr.operand2, scratchReg2, true); + // Pass strings in argument registers 0 and 1 + String argReg0 = argumentRegisters.get(0); + String argReg1 = argumentRegisters.get(1); + if (!str1.equals(argReg0)) { + emitMove(argReg0, str1); } - spiller.markRegisterModified(abiReturnReg); - registerAllocator.markRegisterUsed(abiReturnReg); - returnRegIndex++; - } - DebugSystem.debug("MTOT", "Method " + methodName + " returning " + sortedSlotIndices.size() + " slot values"); - } - else if (!operandStack.isEmpty()) { - String retValReg = operandStack.popToRegister(); - spiller.fillRegister(retValReg); - - String abiReturnReg = returnReg0; - - if (!retValReg.equals(abiReturnReg)) { - finalAssembly.add(" mov " + abiReturnReg + ", " + retValReg); - spiller.markRegisterModified(abiReturnReg); - registerAllocator.freeRegister(retValReg); - } else { - spiller.markRegisterModified(abiReturnReg); + if (!str2.equals(argReg1)) { + emitMove(argReg1, str2); + } + // Call runtime function to concatenate strings + assemblyCode.add(" " + cpuProfile.getPattern("call").assemblyTemplate.get(0) + .replace("{name}", "string_concat")); + // Get return value + String retReg = cpuProfile.architecture.equals("x86_64") ? rax : x0; + String dest = getPhysReg(instr.result, scratchReg1, false); + if (!dest.equals(retReg)) { + emitMove(dest, retReg); + } + commitSpill(instr.result, dest); + break; } - registerAllocator.markRegisterUsed(abiReturnReg); - DebugSystem.debug("MTOT", "Method " + methodName + " returning single value from stack"); - } - else { - String abiReturnReg = returnReg0; - InstructionPattern p = cpuProfile.getPattern("load_immediate_int"); - finalAssembly.add(" " + p.assemblyTemplate.get(0).replace("{dest}", abiReturnReg).replace("{value}", "0") + " " + cpuProfile.syntax.commentMarker + " Default return 0"); - spiller.markRegisterModified(abiReturnReg); - DebugSystem.debug("MTOT", "Method " + methodName + " returning default value 0"); } +} - InstructionPattern epiloguePattern = cpuProfile.getPattern("epilogue"); - if (epiloguePattern == null) throw new RuntimeException("Missing 'epilogue' pattern!"); - - if (cpuProfile.architecture.equals("aarch64")) { - if (!calleeSavedToSave.isEmpty()) { - finalAssembly.add(" " + cpuProfile.syntax.commentMarker + " Restoring callee-saved registers: " + calleeSavedToSave); - InstructionPattern restorePair = cpuProfile.getPattern("restore_callee_reg_pair"); - InstructionPattern restoreSingle = cpuProfile.getPattern("restore_callee_reg_single"); - int calleeSaveSlots = (calleeSavedToSave.size() / 2) + (calleeSavedToSave.size() % 2); - int currentCalleeSaveOffset = -16 - (calleeSaveSlots - 1) * 16; - - for (int i = 0; i < calleeSavedToSave.size(); i += 2) { - if (i + 1 < calleeSavedToSave.size()) { - finalAssembly.add(" " + restorePair.assemblyTemplate.get(0) - .replace("{reg1}", calleeSavedToSave.get(i)) - .replace("{reg2}", calleeSavedToSave.get(i+1)) - .replace("{offset}", String.valueOf(currentCalleeSaveOffset))); - } else { - finalAssembly.add(" " + restoreSingle.assemblyTemplate.get(0) - .replace("{reg1}", calleeSavedToSave.get(i)) - .replace("{offset}", String.valueOf(currentCalleeSaveOffset))); - } - currentCalleeSaveOffset += 16; - } - } - - if (alignedSizeBelowFp > 0) { - finalAssembly.add(" " + epiloguePattern.assemblyTemplate.get(0)); - } - - for (int i = 1; i < epiloguePattern.assemblyTemplate.size(); i++) { - finalAssembly.add(" " + epiloguePattern.assemblyTemplate.get(i)); - } + private void compileCall(TACInstruction instr, int index, List tac) { + String methodName; + int numArgs; + // Check if this is CALL or CALL_SLOTS + if (instr.opcode == Opcode.CALL_SLOTS) { + // For CALL_SLOTS, operand1 is method name, operand2 is arg count + methodName = (String) instr.operand1; + numArgs = (Integer) instr.operand2; } else { - int x86SpillAreaSize = (spiller.getTotalSpillSize() + 15) & ~15; - - finalAssembly.add(" " + epiloguePattern.assemblyTemplate.get(0)); - - if (!calleeSavedToSave.isEmpty()) { - finalAssembly.add(" " + cpuProfile.syntax.commentMarker + " Restoring callee-saved registers: " + calleeSavedToSave); - InstructionPattern restoreSingle = cpuProfile.getPattern("restore_callee_reg_single"); - List reversedSaves = new ArrayList<>(calleeSavedToSave); - Collections.reverse(reversedSaves); - for (String reg : reversedSaves) { - finalAssembly.add(" " + restoreSingle.assemblyTemplate.get(0).replace("{reg}", reg)); - } - } - - for (int i = 1; i < epiloguePattern.assemblyTemplate.size(); i++) { - finalAssembly.add(" " + epiloguePattern.assemblyTemplate.get(i)); - } + // Regular CALL + methodName = (String) instr.operand1; + numArgs = (Integer) instr.operand2; } - operandStack.clear(); - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < finalAssembly.size(); i++) { - sb.append(finalAssembly.get(i)); - if (i < finalAssembly.size() - 1) { - sb.append("\n"); - } + // Collect arguments from preceding PARAM instructions + Stack args = new Stack(); + for (int k = index - 1; k >= 0 && args.size() < numArgs; k--) { + if (tac.get(k).opcode == Opcode.PARAM) { + args.push((String) tac.get(k).operand1); + } } - return sb.toString(); -} - private void setAssemblyTarget(List targetList) { - try { - Field listField = MTOTNativeCompiler.class.getDeclaredField("assemblyCode"); - listField.setAccessible(true); - listField.set(this, targetList); - } catch (Exception e) { - throw new RuntimeException("Failed to swap assembly target list via reflection", e); + // Pass arguments in registers + List abiRegs = argumentRegisters; + int abiIdx = 0; + while (!args.isEmpty()) { + String argTemp = args.pop(); + if (abiIdx < abiRegs.size()) { + String srcReg = getPhysReg(argTemp, scratchReg1, true); + if (!srcReg.equals(abiRegs.get(abiIdx))) { + emitMove(abiRegs.get(abiIdx), srcReg); } + abiIdx++; + } } - private void compileFromBytecode(List bytecode, Map localRegisterMap) { - Map labelNameMap = new HashMap(); - Map> addressToLabelMap = new HashMap>(); - int assemblyLabelCounter = 0; - for (int i = 0; i < bytecode.size(); i++) { - BytecodeInstruction instr = bytecode.get(i); - if (instr.opcode == BytecodeInstruction.Opcode.LABEL) { - String bytecodeLabel = (String) instr.operand; - String assemblyLabel = "L_" + this.currentMethodName + "_" + (assemblyLabelCounter++); - labelNameMap.put(bytecodeLabel, assemblyLabel); - int targetAddress = i; - if (!addressToLabelMap.containsKey(targetAddress)) { - addressToLabelMap.put(targetAddress, new ArrayList()); - } - addressToLabelMap.get(targetAddress).add(assemblyLabel); - } - } + // Pass 'this' pointer + emitMove(abiRegs.get(0), thisRegister); - for (int i = 0; i < bytecode.size(); i++) { - this.currentPc = i; - - if (addressToLabelMap.containsKey(i)) { - for(String label : addressToLabelMap.get(i)) { - assemblyCode.add(label + ":"); - if (label.contains("loop_start") || label.contains("loop_body")) { - currentLoopDepth++; - DebugSystem.debug("LOOP_DEPTH", "Entered loop, depth = " + currentLoopDepth + " at label " + label); - } else if (label.contains("loop_end")) { - if (currentLoopDepth > 0) { - currentLoopDepth--; - DebugSystem.debug("LOOP_DEPTH", "Exited loop, depth = " + currentLoopDepth + " at label " + label); - } else { - DebugSystem.warn("LOOP_DEPTH", "Attempted to exit loop at depth 0, label " + label); - } - } - } - } + // Emit call instruction + assemblyCode.add( + " " + + cpuProfile.getPattern("call").assemblyTemplate.get(0).replace("{name}", methodName)); - BytecodeInstruction instr = bytecode.get(i); - try { - switch (instr.opcode) { - case PUSH_INT: memoryAccessCompiler.compilePushInt((Integer) instr.operand); break; - case PUSH_FLOAT: memoryAccessCompiler.compilePushFloat((Float) instr.operand); break; - case PUSH_STRING: memoryAccessCompiler.compilePushString((String) instr.operand); break; - case PUSH_BOOL: memoryAccessCompiler.compilePushInt(((Boolean) instr.operand) ? 1 : 0); break; - case PUSH_NULL: memoryAccessCompiler.compilePushNull(); break; - case POP: memoryAccessCompiler.compilePop(); break; - case DUP: memoryAccessCompiler.compileDup(); break; - case SWAP: memoryAccessCompiler.compileSwap(); break; - case STORE_LOCAL: memoryAccessCompiler.compileStoreLocal((Integer) instr.operand, localRegisterMap); break; - case LOAD_LOCAL: memoryAccessCompiler.compileLoadLocal((Integer) instr.operand, localRegisterMap); break; - case LOAD_FIELD: memoryAccessCompiler.compileLoadField((String) instr.operand); break; - case STORE_FIELD: memoryAccessCompiler.compileStoreField((String) instr.operand); break; - case STORE_SLOT: memoryAccessCompiler.compileStoreSlot(instr.operand, localRegisterMap); break; - - case ADD_INT: operationCompiler.generateBinaryOp("add_int"); break; - case SUB_INT: operationCompiler.generateBinaryOp("sub_int"); break; - case MUL_INT: operationCompiler.generateBinaryOp("mul_int"); break; - case DIV_INT: operationCompiler.generateBinaryOp("div_int"); break; - case MOD_INT: operationCompiler.generateBinaryOp("mod_int"); break; - case NEG_INT: operationCompiler.generateUnaryOp("neg_int"); break; - - case CMP_EQ_INT: operationCompiler.generateBinaryOp("cmp_eq_int"); break; - case CMP_NE_INT: operationCompiler.generateBinaryOp("cmp_ne_int"); break; - case CMP_LT_INT: operationCompiler.generateBinaryOp("cmp_lt_int"); break; - case CMP_LE_INT: operationCompiler.generateBinaryOp("cmp_le_int"); break; - case CMP_GT_INT: operationCompiler.generateBinaryOp("cmp_gt_int"); break; - case CMP_GE_INT: operationCompiler.generateBinaryOp("cmp_ge_int"); break; - - case JMP: controlFlowCompiler.compileJmp(labelNameMap.get((String)instr.operand)); break; - case JMP_IF_TRUE: controlFlowCompiler.compileJmpIfTrue(labelNameMap.get((String)instr.operand)); break; - case JMP_IF_FALSE:controlFlowCompiler.compileJmpIfFalse(labelNameMap.get((String)instr.operand)); break; - case LABEL: break; - case RET: break; - - case CALL: methodCallCompiler.compileCall(instr, bytecode, i, localRegisterMap); break; - case CALL_SLOTS: methodCallCompiler.compileCallSlots(instr, bytecode, i, localRegisterMap); break; - - case PRINT: runtimeCallHelper.compileRuntimeCall("runtime_print", 1, false); break; - case CONCAT_STRING: runtimeCallHelper.compileRuntimeCall("string_concat", 2, true); break; - case INT_TO_STRING: runtimeCallHelper.compileRuntimeCall("runtime_int_to_string", 1, true); break; - case ARRAY_NEW: runtimeCallHelper.compileRuntimeCall("array_new", 1, true); break; - case ARRAY_LOAD: runtimeCallHelper.compileRuntimeCall("array_load", 2, true); break; - case ARRAY_STORE: runtimeCallHelper.compileRuntimeCall("array_store", 3, false); break; - case READ_INPUT: runtimeCallHelper.compileReadInput((String) instr.operand); break; - - default: - DebugSystem.warn("MTOT", "Unhandled bytecode instruction: " + instr.opcode); - assemblyCode.add(" " + cpuProfile.syntax.commentMarker + " Unhandled: " + instr.opcode); - break; - } - } catch (Exception e) { - DebugSystem.error("MTOT", "Error compiling instruction " + i + ": " + instr.opcode + " ("+instr.operand+") - " + e.getMessage()); - assemblyCode.add(" " + cpuProfile.syntax.commentMarker + " ERROR compiling " + instr.opcode + ": " + e.getMessage()); - e.printStackTrace(); - } - } + // Handle return value + if (instr.result != null) { + String retReg = cpuProfile.architecture.equals("x86_64") ? rax : x0; + String dest = getPhysReg(instr.result, scratchReg1, false); + emitMove(dest, retReg); + commitSpill(instr.result, dest); } - - public String escapeString(String str) { - str = str.replace("\\", "\\\\"); - str = str.replace("\"", "\\\""); - str = str.replace("\n", "\\n"); - str = str.replace("\t", "\\t"); - str = str.replace("\0", "\\0"); - return str; + } + + private void compileRuntimeCall(String name, String[] argTemps, boolean hasRet) { + List abiRegs = argumentRegisters; + for (int i = 0; i < argTemps.length; i++) { + if (argTemps[i] == null) continue; + String src = getPhysReg(argTemps[i], scratchReg1, true); + emitMove(abiRegs.get(i), src); } - - public String generateDataLabel(String prefix) { - return prefix + "_" + this.currentMethodName + "_" + (dataLabelCounter++); + assemblyCode.add( + " " + cpuProfile.getPattern("call").assemblyTemplate.get(0).replace("{name}", name)); + } + + private void compileReadInput(String resultTemp, String typeStr) { + String dataLabel = generateDataLabel("type"); + dataSection.add( + cpuProfile + .syntax + .stringDirective + .replace("{label}", dataLabel) + .replace("{value}", escapeString(typeStr))); + String arg0 = argumentRegisters.get(0); + for (String t : cpuProfile.getPattern("load_address").assemblyTemplate) { + assemblyCode.add(" " + t.replace("{dest}", arg0).replace("{label}", dataLabel)); } - - public void generateSpillCode(String register, int offset) { - int finalOffset = offset; - String baseReg = (cpuProfile.architecture.equals("x86_64")) ? x86_64Registers.rbp : AArch64Registers.fp; - if (cpuProfile.architecture.equals("x86_64")) { - if (offset > 0) finalOffset = -offset; - baseReg = x86_64Registers.rbp; - } else { - baseReg = AArch64Registers.fp; - } - - InstructionPattern pattern = cpuProfile.getPattern("store_to_stack"); - if (pattern == null) { throw new RuntimeException("Missing 'store_to_stack' instruction pattern for architecture: " + cpuProfile.architecture); } - String asm = pattern.assemblyTemplate.get(0) - .replace("{src_reg}", register) - .replace("{offset}", String.valueOf(finalOffset)); - - assemblyCode.add(" " + cpuProfile.syntax.commentMarker + " Spill " + register + " to [" + baseReg + ((finalOffset < 0) ? "" : "+") + finalOffset + "]"); - assemblyCode.add(" " + asm); + assemblyCode.add( + " " + + cpuProfile + .getPattern("call") + .assemblyTemplate + .get(0) + .replace("{name}", "runtime_read_input")); + String retReg = cpuProfile.architecture.equals("x86_64") ? rax : x0; + String dest = getPhysReg(resultTemp, scratchReg1, false); + emitMove(dest, retReg); + commitSpill(resultTemp, dest); + } + + private String getPhysReg(Object operand, String scratchReg, boolean loadIfSpilled) { + if (!(operand instanceof String)) return scratchReg; + String temp = (String) operand; + String reg = registerManager.getRegister(temp); + if (reg != null) return reg; + + Integer offset = spiller.getSpillOffset(temp); + if (offset == null) { + spiller.forceSpill(temp); + offset = spiller.getSpillOffset(temp); } - public void generateFillCode(String register, int offset) { - int finalOffset = offset; - String baseReg = (cpuProfile.architecture.equals("x86_64")) ? x86_64Registers.rbp : AArch64Registers.fp; - if (cpuProfile.architecture.equals("x86_64")) { - if (offset > 0) finalOffset = -offset; - baseReg = x86_64Registers.rbp; - } else { - baseReg = AArch64Registers.fp; - } - - InstructionPattern pattern = cpuProfile.getPattern("load_from_stack"); - if (pattern == null) { throw new RuntimeException("Missing 'load_from_stack' instruction pattern for architecture: " + cpuProfile.architecture); } - String asm = pattern.assemblyTemplate.get(0) - .replace("{dest_reg}", register) - .replace("{offset}", String.valueOf(finalOffset)); - - assemblyCode.add(" " + cpuProfile.syntax.commentMarker + " Fill " + register + " from [" + baseReg + ((finalOffset < 0) ? "" : "+") + finalOffset + "]"); - assemblyCode.add(" " + asm); + if (loadIfSpilled) { + String asm = + cpuProfile + .getPattern("load_from_stack") + .assemblyTemplate + .get(0) + .replace("{dest_reg}", scratchReg) + .replace("{offset}", String.valueOf(Math.abs(offset))); + assemblyCode.add(" " + asm); } - - public Set spillCallerSavedRegisters(Set excludeArgs) { - Set currentlyLiveRegs = registerAllocator.getUsedRegisters(); - Set callerSavedToSpill = new HashSet(); - - callerSavedToSpill.addAll(currentlyLiveRegs); - callerSavedToSpill.retainAll(this.abiCallerSavedSet); - - if (excludeArgs != null) { - callerSavedToSpill.removeAll(excludeArgs); - } - - if (!callerSavedToSpill.isEmpty()) { - List orderedSpill = new ArrayList(callerSavedToSpill); - Collections.sort(orderedSpill); - assemblyCode.add(" " + cpuProfile.syntax.commentMarker + " Spilling caller-saved registers before call: " + orderedSpill); - for (String reg : orderedSpill) { - if (registerAllocator.usedRegisters.contains(reg)){ - spiller.forceSpill(reg); - } - } - } else { - assemblyCode.add(" " + cpuProfile.syntax.commentMarker + " No caller-saved registers needed spilling before call."); - } - return callerSavedToSpill; + return scratchReg; + } + + private void commitSpill(String temp, String srcReg) { + if (registerManager.getRegister(temp) != null) return; + Integer offset = spiller.getSpillOffset(temp); + String asm = + cpuProfile + .getPattern("store_to_stack") + .assemblyTemplate + .get(0) + .replace("{src_reg}", srcReg) + .replace("{offset}", String.valueOf(Math.abs(offset))); + assemblyCode.add(" " + asm); + } + + private void emitMove(String dest, String src) { + if (dest.equals(src)) return; + assemblyCode.add( + " " + + cpuProfile + .getPattern("move_reg") + .assemblyTemplate + .get(0) + .replace("{dest}", dest) + .replace("{src}", src)); + } + + private String constructFinalAssembly(String methodName) { + List finalAsm = new ArrayList(); + if (!dataSection.isEmpty()) { + finalAsm.add(cpuProfile.syntax.dataSection); + finalAsm.addAll(dataSection); } + finalAsm.add(cpuProfile.syntax.textSection); + if (cpuProfile.architecture.equals("x86_64")) { + finalAsm.add(" global " + methodName); + finalAsm.add( + " extern runtime_print, int_to_string, string_concat, array_new, array_load, array_store, runtime_read_input"); + } else { + finalAsm.add(" .global " + methodName); + finalAsm.add(" .extern int_to_string"); + finalAsm.add(" .extern string_concat"); + finalAsm.add(" .extern runtime_print"); + finalAsm.add(" .extern runtime_read_input"); + finalAsm.add(" .extern array_new"); + finalAsm.add(" .extern array_load"); + finalAsm.add(" .extern array_store"); + } + finalAsm.add(methodName + ":"); + for (String t : cpuProfile.getPattern("prologue").assemblyTemplate) finalAsm.add(" " + t); + + int stackSize = spiller.getTotalSpillSize(); + if (stackSize > 0) { + finalAsm.add( + " " + + cpuProfile + .getPattern("alloc_stack_frame") + .assemblyTemplate + .get(0) + .replace("{size}", String.valueOf(stackSize))); + } + finalAsm.addAll(assemblyCode); + for (String t : cpuProfile.getPattern("epilogue").assemblyTemplate) finalAsm.add(" " + t); - public void fillCallerSavedRegisters(Set registersToFill) { - if (registersToFill != null && !registersToFill.isEmpty()) { - List orderedRestore = new ArrayList(registersToFill); - Collections.sort(orderedRestore); + StringBuilder sb = new StringBuilder(); + for (String s : finalAsm) sb.append(s).append("\n"); + return sb.toString(); + } - assemblyCode.add(" " + cpuProfile.syntax.commentMarker + " Filling caller-saved registers after call: " + orderedRestore); - for (String reg : orderedRestore) { - spiller.fillRegister(reg); - registerAllocator.markRegisterUsed(reg); - } - } else { - assemblyCode.add(" " + cpuProfile.syntax.commentMarker + " No caller-saved registers needed filling after call."); - } - } -} \ No newline at end of file + private String generateDataLabel(String prefix) { + return prefix + "_" + currentMethodName + "_" + (dataLabelCounter++); + } + + private String escapeString(String str) { + return str.replace("\"", "\\\"").replace("\n", "\\n"); + } +} diff --git a/src/main/java/cod/compiler/MTOTRegistry.java b/src/main/java/cod/compiler/MTOTRegistry.java index 41423c85..37422e75 100644 --- a/src/main/java/cod/compiler/MTOTRegistry.java +++ b/src/main/java/cod/compiler/MTOTRegistry.java @@ -1,721 +1,122 @@ package cod.compiler; import java.util.*; - import static cod.compiler.MTOTRegistry.AArch64Registers.*; import static cod.compiler.MTOTRegistry.x86_64Registers.*; public class MTOTRegistry { - private static final Map profiles = new HashMap(); - // ====================================================================== - // --- REGISTER DEFINITIONS --- - // ====================================================================== + // --- Added method for manual profile retrieval --- + public static CPUProfile getProfile(String architecture) { + return profiles.get(architecture); + } + // ------------------------------------------------- - /** Contains all register definitions for the AArch64 architecture. */ public static class AArch64Registers { - // Callee-saved (must be preserved by callee) - public static final String x19 = "x19", - x20 = "x20", - x21 = "x21", - x22 = "x22", - x23 = "x23", - x24 = "x24", - x25 = "x25", - x26 = "x26", - x27 = "x27", - x28 = "x28", - x29 = "x29", - x30 = "x30"; - - // Caller-saved (can be clobbered by callee) - public static final String x9 = "x9", - x10 = "x10", - x11 = "x11", - x12 = "x12", - x13 = "x13", - x14 = "x14", - x15 = "x15"; - - // Argument/Return registers (Caller-saved) - public static final String x0 = "x0", - x1 = "x1", - x2 = "x2", - x3 = "x3", - x4 = "x4", - x5 = "x5", - x6 = "x6", - x7 = "x7"; - - // Special Purpose - public static final String sp = "sp", - fp = "x29", // Frame Pointer - lr = "x30", // Link Register - zr = "xzr"; // Zero Register - - // Scratch / Intra-procedural (Caller-saved) - public static final String x16 = "x16", // (IP0) - x17 = "x17"; // (IP1) - - // Vector Registers (simplified names) - public static final String v0 = "v0", - v1 = "v1", - v2 = "v2", - v3 = "v3", - v4 = "v4", - v5 = "v5", - v6 = "v6", - v7 = "v7"; + public static final String x19="x19", x20="x20", x21="x21", x22="x22", x23="x23", x24="x24", x25="x25", x26="x26", x27="x27", x28="x28", x29="x29", x30="x30"; + public static final String x9="x9", x10="x10", x11="x11", x12="x12", x13="x13", x14="x14", x15="x15", x16="x16", x17="x17"; + public static final String x0="x0", x1="x1", x2="x2", x3="x3", x4="x4", x5="x5", x6="x6", x7="x7"; + public static final String sp="sp", fp="x29", lr="x30", zr="xzr"; } - /** Contains all register definitions for the x86_64 (System V AMD64) architecture. */ public static class x86_64Registers { - // Callee-saved (must be preserved by callee) - public static final String rbx = "rbx", - rbp = "rbp", // Frame Pointer - r12 = "r12", - r13 = "r13", - r14 = "r14", - r15 = "r15"; - - // Caller-saved (can be clobbered by callee) - public static final String rax = "rax", // Return value - rcx = "rcx", // Arg 4 - rdx = "rdx", // Arg 3 / Return value 2 - rsi = "rsi", // Arg 2 - rdi = "rdi", // Arg 1 - r8 = "r8", // Arg 5 - r9 = "r9", // Arg 6 - r10 = "r10", - r11 = "r11"; - - // Special Purpose - public static final String rsp = "rsp"; // Stack Pointer - - // Vector Registers (ymm for AVx) - public static final String ymm0 = "ymm0", - ymm1 = "ymm1", - ymm2 = "ymm2", - ymm3 = "ymm3", - ymm4 = "ymm4", - ymm5 = "ymm5", - ymm6 = "ymm6", - ymm7 = "ymm7"; + public static final String rbx="rbx", rbp="rbp", r12="r12", r13="r13", r14="r14", r15="r15"; + public static final String rax="rax", rcx="rcx", rdx="rdx", rsi="rsi", rdi="rdi", r8="r8", r9="r9", r10="r10", r11="r11"; + public static final String rsp="rsp"; } - // ====================================================================== - // --- INSTRUCTION PATTERN CONSTANTS --- - // ====================================================================== - - // Instruction patterns as final Strings - shared by both architectures - private static final String prologue = "prologue", - epilogue = "epilogue", - move_reg = "move_reg", - load_immediate_int = "load_immediate_int", - load_address = "load_address", - add_int = "add_int", - sub_int = "sub_int", - mul_int = "mul_int", - div_int = "div_int", - mod_int = "mod_int", - neg_int = "neg_int", - cmp_eq_int = "cmp_eq_int", - cmp_ne_int = "cmp_ne_int", - cmp_lt_int = "cmp_lt_int", - cmp_le_int = "cmp_le_int", - cmp_gt_int = "cmp_gt_int", - cmp_ge_int = "cmp_ge_int", - pop = "pop", - jmp = "jmp", - jmp_if_false = "jmp_if_false", - jmp_if_true = "jmp_if_true", - call = "call", - store_to_stack = "store_to_stack", - load_from_stack = "load_from_stack", - load_field_offset = "load_field_offset", - store_field_offset = "store_field_offset", - alloc_stack_frame = "alloc_stack_frame", - dealloc_stack_frame = "dealloc_stack_frame", - save_callee_reg_pair = "save_callee_reg_pair", - save_callee_reg_single = "save_callee_reg_single", - restore_callee_reg_pair = "restore_callee_reg_pair", - restore_callee_reg_single = "restore_callee_reg_single"; - - // ====================================================================== - // --- INSTRUCTION TEMPLATE STRINGS --- - // ====================================================================== - - // x86_64 instruction templates - public static final String x86_prologue_1 = "push " + rbp, - x86_prologue_2 = "mov " + rbp + ", " + rsp, - x86_epilogue_1 = "mov " + rsp + ", " + rbp, - x86_epilogue_2 = "pop " + rbp, - x86_epilogue_3 = "ret", - x86_move_reg = "mov {dest}, {src}", - x86_load_immediate_int = "mov {dest}, {value}", - x86_load_address = "mov {dest}, {label}", - x86_add_int_1 = "mov {dest}, {src1}", - x86_add_int_2 = "add {dest}, {src2}", - x86_sub_int_1 = "mov {dest}, {src1}", - x86_sub_int_2 = "sub {dest}, {src2}", - x86_mul_int_1 = "mov " + rax + ", {src1}", - x86_mul_int_2 = "imul {src2}", - x86_mul_int_3 = "mov {dest}, " + rax, - x86_div_int_1 = "mov " + rax + ", {src1}", - x86_div_int_2 = "cqo", - x86_div_int_3 = "idiv {src2}", - x86_div_int_4 = "mov {dest}, " + rax, - x86_mod_int_1 = "mov " + rax + ", {src1}", - x86_mod_int_2 = "cqo", - x86_mod_int_3 = "idiv {src2}", - x86_mod_int_4 = "mov {dest}, " + rdx, - x86_neg_int_1 = "mov {dest}, {src}", - x86_neg_int_2 = "neg {dest}", - x86_cmp_eq_int_1 = "cmp {src1}, {src2}", - x86_cmp_eq_int_2 = "sete al", - x86_cmp_eq_int_3 = "movzx {dest}, al", - x86_cmp_ne_int_1 = "cmp {src1}, {src2}", - x86_cmp_ne_int_2 = "setne al", - x86_cmp_ne_int_3 = "movzx {dest}, al", - x86_cmp_lt_int_1 = "cmp {src1}, {src2}", - x86_cmp_lt_int_2 = "setl al", - x86_cmp_lt_int_3 = "movzx {dest}, al", - x86_cmp_le_int_1 = "cmp {src1}, {src2}", - x86_cmp_le_int_2 = "setle al", - x86_cmp_le_int_3 = "movzx {dest}, al", - x86_cmp_gt_int_1 = "cmp {src1}, {src2}", - x86_cmp_gt_int_2 = "setg al", - x86_cmp_gt_int_3 = "movzx {dest}, al", - x86_cmp_ge_int_1 = "cmp {src1}, {src2}", - x86_cmp_ge_int_2 = "setge al", - x86_cmp_ge_int_3 = "movzx {dest}, al", - x86_jmp = "jmp {label}", - x86_jmp_if_false_1 = "test {condition}, {condition}", - x86_jmp_if_false_2 = "jz {label}", - x86_jmp_if_true_1 = "test {condition}, {condition}", - x86_jmp_if_true_2 = "jnz {label}", - x86_call = "call {name}", - x86_store_to_stack = "mov [" + rbp + " - {offset}], {src_reg}", // Note: x86 offsets from RBP are negative - x86_load_from_stack = "mov {dest_reg}, [" + rbp + " - {offset}]", - x86_load_field_offset = "mov {dest_reg}, [{base_reg} + {offset}]", - x86_store_field_offset = "mov [{base_reg} + {offset}], {src_reg}", - x86_alloc_stack_frame = "sub " + rsp + ", {size}", - x86_dealloc_stack_frame = "mov " + rsp + ", " + rbp, - x86_save_callee_reg_single = "push {reg}", - x86_restore_callee_reg_single = "pop {reg}"; - - // AArch64 instruction templates - public static final String - arm_prologue_1 = "stp " + x29 + ", " + x30 + ", [" + sp + ", #-16]!", - arm_prologue_2 = "mov " + x29 + ", " + sp, - arm_epilogue_1 = "mov " + sp + ", " + x29, - arm_epilogue_2 = "ldp " + x29 + ", " + x30 + ", [" + sp + "], #16", - arm_epilogue_3 = "ret", - arm_move_reg = "mov {dest}, {src}", - arm_load_immediate_int = "mov {dest}, #{value}", - arm_load_address_1 = "adrp {dest}, {label}", - arm_load_address_2 = "add {dest}, {dest}, :lo12:{label}", - arm_add_int = "add {dest}, {src1}, {src2}", - arm_sub_int = "sub {dest}, {src1}, {src2}", - arm_mul_int = "mul {dest}, {src1}, {src2}", - arm_div_int = "sdiv {dest}, {src1}, {src2}", - arm_mod_int_1 = "sdiv " + x16 + ", {src1}, {src2}", - arm_mod_int_2 = "mul " + x16 + ", " + x16 + ", {src2}", - arm_mod_int_3 = "sub {dest}, {src1}, " + x16, - arm_neg_int = "neg {dest}, {src}", - arm_cmp_eq_int_1 = "cmp {src1}, {src2}", - arm_cmp_eq_int_2 = "cset {dest}, eq", - arm_cmp_ne_int_1 = "cmp {src1}, {src2}", - arm_cmp_ne_int_2 = "cset {dest}, ne", - arm_cmp_lt_int_1 = "cmp {src1}, {src2}", - arm_cmp_lt_int_2 = "cset {dest}, lt", - arm_cmp_le_int_1 = "cmp {src1}, {src2}", - arm_cmp_le_int_2 = "cset {dest}, le", - arm_cmp_gt_int_1 = "cmp {src1}, {src2}", - arm_cmp_gt_int_2 = "cset {dest}, gt", - arm_cmp_ge_int_1 = "cmp {src1}, {src2}", - arm_cmp_ge_int_2 = "cset {dest}, ge", - arm_jmp = "b {label}", - arm_jmp_if_false_1 = "cmp {condition}, #0", - arm_jmp_if_false_2 = "b.eq {label}", - arm_jmp_if_true_1 = "cmp {condition}, #0", - arm_jmp_if_true_2 = "b.ne {label}", - arm_call = "bl {name}", - arm_store_to_stack = "str {src_reg}, [" + x29 + ", #{offset}]", // Note: ARM offsets from FP are negative - arm_load_from_stack = "ldr {dest_reg}, [" + x29 + ", #{offset}]", - arm_load_field_offset = "ldr {dest_reg}, [{base_reg}, #{offset}]", - arm_store_field_offset = "str {src_reg}, [{base_reg}, #{offset}]", - arm_alloc_stack_frame = "sub " + sp + ", " + sp + ", #{size}", - arm_dealloc_stack_frame = "mov " + sp + ", " + x29, - arm_save_callee_reg_pair = "stp {reg1}, {reg2}, [" + fp + ", #{offset}]", - arm_save_callee_reg_single = "str {reg1}, [" + fp + ", #{offset}]", - arm_restore_callee_reg_pair = "ldp {reg1}, {reg2}, [" + fp + ", #{offset}]", - arm_restore_callee_reg_single = "ldr {reg1}, [" + fp + ", #{offset}]"; - - // ====================================================================== - // --- INNER PROFILE CLASSES --- - // ====================================================================== - - /** Defines the assembly syntax for a specific toolchain (e.g., GAS, NASM). */ public static class SyntaxProfile { - public final String commentMarker; - public final String textSection; - public final String dataSection; - public final String globalDirective; // e.g., "global {name}" - public final String stringDirective; // e.g., "{label}: db \"{value}\", 0" - public final String floatDirective; // e.g., "{label}: .float {value}" - - public SyntaxProfile( - String commentMarker, - String textSection, - String dataSection, - String globalDirective, - String stringDirective, - String floatDirective) { - this.commentMarker = commentMarker; - this.textSection = textSection; - this.dataSection = dataSection; - this.globalDirective = globalDirective; - this.stringDirective = stringDirective; - this.floatDirective = floatDirective; - } + public final String commentMarker, textSection, dataSection, stringDirective; + public SyntaxProfile(String c, String t, String d, String s) { commentMarker=c; textSection=t; dataSection=d; stringDirective=s; } } - /** Defines a specific instruction pattern for a CPU. */ public static class InstructionPattern { - public final String blockType; public final List assemblyTemplate; - public final List requiredRegisters; - - public InstructionPattern( - String blockType, List assemblyTemplate, List requiredRegisters) { - this.blockType = blockType; - this.assemblyTemplate = assemblyTemplate; - this.requiredRegisters = requiredRegisters; - } + public InstructionPattern(List a) { assemblyTemplate=a; } } - /** Defines the vector capabilities of a CPU. */ - public static class VectorCapabilities { - public final int vectorSize; - public final boolean hasAVx; - public final boolean hasNEON; - public final boolean hasRVV; - - public VectorCapabilities(int vectorSize, boolean hasAVx, boolean hasNEON, boolean hasRVV) { - this.vectorSize = vectorSize; - this.hasAVx = hasAVx; - this.hasNEON = hasNEON; - this.hasRVV = hasRVV; - } - } - - /** Defines the register layout for a CPU. */ public static class RegisterFile { - public final List generalPurpose; // Available for allocation - public final List vectorRegisters; - public final List argumentRegisters; // Used for args/return - public final String stackPointer; - public final String framePointer; - public final int registerCount; - - /** Constructor for AArch64 RegisterFile. */ - public RegisterFile(AArch64Registers regs) { - this.generalPurpose = - Collections.unmodifiableList( - new ArrayList( - Arrays.asList( - x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x9, - x10, x11, x12, x13, x14, x15))); - this.vectorRegisters = - Collections.unmodifiableList( - new ArrayList(Arrays.asList(v0, v1, v2, v3, v4, v5, v6, v7))); - this.argumentRegisters = - Collections.unmodifiableList( - new ArrayList(Arrays.asList(x0, x1, x2, x3, x4, x5, x6, x7))); - this.stackPointer = sp; - this.framePointer = fp; - this.registerCount = this.generalPurpose.size(); - } - - /** Constructor for x86_64 RegisterFile. */ - public RegisterFile() { - // General purpose allocatable = Callee-saved + Caller-saved (non-arg) - this.generalPurpose = - Collections.unmodifiableList( - new ArrayList( - Arrays.asList( - // Callee-saved - rbx, - r12, - r13, - r14, - r15, - // Caller-saved (non-arg, non-return) - r10, - r11))); - this.vectorRegisters = - Collections.unmodifiableList( - new ArrayList( - Arrays.asList(ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7))); - // Argument registers (in System V ABI order) - // --- MODIFIED: x86 arg regs are also used for return (rax, rdx) --- - this.argumentRegisters = - Collections.unmodifiableList( - new ArrayList(Arrays.asList( - rdi, // arg 0 - rsi, // arg 1 - rdx, // arg 2 (also return 1) - rcx, // arg 3 - r8, // arg 4 - r9, // arg 5 - rax // return 0 (not technically an arg reg, but convenient here) - ))); - this.stackPointer = rsp; - this.framePointer = rbp; - this.registerCount = this.generalPurpose.size(); - } + public final List generalPurpose, argumentRegisters; + public RegisterFile(List gp, List args) { generalPurpose=gp; argumentRegisters=args; } } - /** Main class holding all profile information for a specific CPU architecture. */ public static class CPUProfile { public final String architecture; public final Map patterns; - // --- MODIFICATION: Removed 'static' --- public final RegisterFile registerFile; - // --- END MODIFICATION --- - public final VectorCapabilities vector; public final SyntaxProfile syntax; - - public CPUProfile( - String architecture, - Map patterns, - RegisterFile registerFile, // <--- ADDED to constructor - VectorCapabilities vector, - SyntaxProfile syntax) { - this.architecture = architecture; - this.patterns = patterns; - this.registerFile = registerFile; // <--- ADDED assignment - this.vector = vector; - this.syntax = syntax; - } - - public InstructionPattern getPattern(String blockType) { - return patterns.get(blockType); - } + public CPUProfile(String a, Map p, RegisterFile r, SyntaxProfile s) { architecture=a; patterns=p; registerFile=r; syntax=s; } + public InstructionPattern getPattern(String t) { return patterns.get(t); } } - // ====================================================================== - // --- REGISTRY INITIALIZATION --- - // ====================================================================== - static { - List emptyList = Collections.emptyList(); - - // ====================================================================== - // x86_64 Profile (NASM syntax) - // ====================================================================== - - SyntaxProfile x86Syntax = - new SyntaxProfile( - ";", // commentMarker - "section .text", // textSection - "section .data", // dataSection - "global {name}", // globalDirective - "{label}: db \"{value}\", 0", // stringDirective - "{label}: dd __float32__({value})" // floatDirective (NASM syntax for 32-bit - // float) - ); - - Map x86Patterns = new HashMap(); - x86Patterns.put( - prologue, - new InstructionPattern( - prologue, Arrays.asList(x86_prologue_1, x86_prologue_2), emptyList)); - x86Patterns.put( - epilogue, - new InstructionPattern( - epilogue, - Arrays.asList(x86_epilogue_1, x86_epilogue_2, x86_epilogue_3), - emptyList)); - x86Patterns.put( - move_reg, new InstructionPattern(move_reg, Arrays.asList(x86_move_reg), emptyList)); - x86Patterns.put( - load_immediate_int, - new InstructionPattern( - load_immediate_int, Arrays.asList(x86_load_immediate_int), emptyList)); - x86Patterns.put( - load_address, - new InstructionPattern(load_address, Arrays.asList(x86_load_address), emptyList)); - x86Patterns.put( - add_int, - new InstructionPattern( - add_int, Arrays.asList(x86_add_int_1, x86_add_int_2), emptyList)); - x86Patterns.put( - sub_int, - new InstructionPattern( - sub_int, Arrays.asList(x86_sub_int_1, x86_sub_int_2), emptyList)); - x86Patterns.put( - mul_int, - new InstructionPattern( - mul_int, - Arrays.asList(x86_mul_int_1, x86_mul_int_2, x86_mul_int_3), - Arrays.asList(rax, rdx) // Clobbers rax, rdx - )); - x86Patterns.put( - div_int, - new InstructionPattern( - div_int, - Arrays.asList(x86_div_int_1, x86_div_int_2, x86_div_int_3, x86_div_int_4), - Arrays.asList(rax, rdx) // Clobbers rax, rdx - )); - x86Patterns.put( - mod_int, - new InstructionPattern( - mod_int, - Arrays.asList(x86_mod_int_1, x86_mod_int_2, x86_mod_int_3, x86_mod_int_4), - Arrays.asList(rax, rdx) // Clobbers rax, rdx - )); - x86Patterns.put( - neg_int, - new InstructionPattern( - neg_int, Arrays.asList(x86_neg_int_1, x86_neg_int_2), emptyList)); - x86Patterns.put( - cmp_eq_int, - new InstructionPattern( - cmp_eq_int, - Arrays.asList(x86_cmp_eq_int_1, x86_cmp_eq_int_2, x86_cmp_eq_int_3), - Arrays.asList("al") // Uses 'al' register - )); - x86Patterns.put( - cmp_ne_int, - new InstructionPattern( - cmp_ne_int, - Arrays.asList(x86_cmp_ne_int_1, x86_cmp_ne_int_2, x86_cmp_ne_int_3), - Arrays.asList("al"))); - x86Patterns.put( - cmp_lt_int, - new InstructionPattern( - cmp_lt_int, - Arrays.asList(x86_cmp_lt_int_1, x86_cmp_lt_int_2, x86_cmp_lt_int_3), - Arrays.asList("al"))); - x86Patterns.put( - cmp_le_int, - new InstructionPattern( - cmp_le_int, - Arrays.asList(x86_cmp_le_int_1, x86_cmp_le_int_2, x86_cmp_le_int_3), - Arrays.asList("al"))); - x86Patterns.put( - cmp_gt_int, - new InstructionPattern( - cmp_gt_int, - Arrays.asList(x86_cmp_gt_int_1, x86_cmp_gt_int_2, x86_cmp_gt_int_3), - Arrays.asList("al"))); - x86Patterns.put( - cmp_ge_int, - new InstructionPattern( - cmp_ge_int, - Arrays.asList(x86_cmp_ge_int_1, x86_cmp_ge_int_2, x86_cmp_ge_int_3), - Arrays.asList("al"))); - x86Patterns.put( - pop, new InstructionPattern(pop, Collections.emptyList(), emptyList)); - x86Patterns.put(jmp, new InstructionPattern(jmp, Arrays.asList(x86_jmp), emptyList)); - x86Patterns.put( - jmp_if_false, - new InstructionPattern( - jmp_if_false, - Arrays.asList(x86_jmp_if_false_1, x86_jmp_if_false_2), - emptyList)); - x86Patterns.put( - jmp_if_true, - new InstructionPattern( - jmp_if_true, - Arrays.asList(x86_jmp_if_true_1, x86_jmp_if_true_2), - emptyList)); - x86Patterns.put(call, new InstructionPattern(call, Arrays.asList(x86_call), emptyList)); - x86Patterns.put( - store_to_stack, - new InstructionPattern( - store_to_stack, Arrays.asList(x86_store_to_stack), emptyList)); - x86Patterns.put( - load_from_stack, - new InstructionPattern( - load_from_stack, Arrays.asList(x86_load_from_stack), emptyList)); - x86Patterns.put( - load_field_offset, - new InstructionPattern( - load_field_offset, Arrays.asList(x86_load_field_offset), emptyList)); - x86Patterns.put( - store_field_offset, - new InstructionPattern( - store_field_offset, Arrays.asList(x86_store_field_offset), emptyList)); - x86Patterns.put(alloc_stack_frame, new InstructionPattern(alloc_stack_frame, Arrays.asList(x86_alloc_stack_frame), emptyList)); - x86Patterns.put(dealloc_stack_frame, new InstructionPattern(dealloc_stack_frame, Arrays.asList(x86_dealloc_stack_frame), emptyList)); - x86Patterns.put(save_callee_reg_single, new InstructionPattern(save_callee_reg_single, Arrays.asList(x86_save_callee_reg_single), emptyList)); - x86Patterns.put(restore_callee_reg_single, new InstructionPattern(restore_callee_reg_single, Arrays.asList(x86_restore_callee_reg_single), emptyList)); - x86Patterns.put(save_callee_reg_pair, new InstructionPattern(save_callee_reg_pair, Collections.emptyList(), emptyList)); - x86Patterns.put(restore_callee_reg_pair, new InstructionPattern(restore_callee_reg_pair, Collections.emptyList(), emptyList)); - - RegisterFile x86Registers = new RegisterFile(); - VectorCapabilities x86Vector = new VectorCapabilities(256, true, false, false); + // --- x86_64 --- + Map x86 = new HashMap(); + x86.put("prologue", new InstructionPattern(Arrays.asList("push " + rbp, "mov " + rbp + ", " + rsp))); + x86.put("epilogue", new InstructionPattern(Arrays.asList("mov " + rsp + ", " + rbp, "pop " + rbp, "ret"))); + x86.put("move_reg", new InstructionPattern(Arrays.asList("mov {dest}, {src}"))); + x86.put("load_immediate_int", new InstructionPattern(Arrays.asList("mov {dest}, {value}"))); + x86.put("load_address", new InstructionPattern(Arrays.asList("lea {dest}, [rel {label}]"))); + x86.put("add_int", new InstructionPattern(Arrays.asList("add {dest}, {src2}"))); + x86.put("sub_int", new InstructionPattern(Arrays.asList("sub {dest}, {src2}"))); + x86.put("mul_int", new InstructionPattern(Arrays.asList("imul {dest}, {src2}"))); + x86.put("div_int", new InstructionPattern(Arrays.asList("mov rax, {dest}", "cqo", "idiv {src2}", "mov {dest}, rax"))); + x86.put("mod_int", new InstructionPattern(Arrays.asList("mov rax, {dest}", "cqo", "idiv {src2}", "mov {dest}, rdx"))); + x86.put("neg_int", new InstructionPattern(Arrays.asList("neg {dest}"))); + x86.put("cmp_eq_int", new InstructionPattern(Arrays.asList("cmp {src1}, {src2}", "sete al", "movzx {dest}, al"))); + x86.put("cmp_ne_int", new InstructionPattern(Arrays.asList("cmp {src1}, {src2}", "setne al", "movzx {dest}, al"))); + x86.put("cmp_lt_int", new InstructionPattern(Arrays.asList("cmp {src1}, {src2}", "setl al", "movzx {dest}, al"))); + x86.put("cmp_le_int", new InstructionPattern(Arrays.asList("cmp {src1}, {src2}", "setle al", "movzx {dest}, al"))); + x86.put("cmp_gt_int", new InstructionPattern(Arrays.asList("cmp {src1}, {src2}", "setg al", "movzx {dest}, al"))); + x86.put("cmp_ge_int", new InstructionPattern(Arrays.asList("cmp {src1}, {src2}", "setge al", "movzx {dest}, al"))); + x86.put("jmp", new InstructionPattern(Arrays.asList("jmp {label}"))); + x86.put("call", new InstructionPattern(Arrays.asList("call {name}"))); + x86.put("store_to_stack", new InstructionPattern(Arrays.asList("mov [" + rbp + " - {offset}], {src_reg}"))); + x86.put("load_from_stack", new InstructionPattern(Arrays.asList("mov {dest_reg}, [" + rbp + " - {offset}]"))); + x86.put("alloc_stack_frame", new InstructionPattern(Arrays.asList("sub " + rsp + ", {size}"))); + + profiles.put("x86_64", new CPUProfile("x86_64", x86, + new RegisterFile(Arrays.asList(rbx, r12, r13, r14, r15), Arrays.asList(rdi, rsi, rdx, rcx, r8, r9)), + new SyntaxProfile(";", "section .text", "section .data", "{label}: db \"{value}\", 0"))); + + // --- AArch64 --- + Map arm = new HashMap(); + arm.put("prologue", new InstructionPattern(Arrays.asList("stp " + fp + ", " + lr + ", [" + sp + ", #-16]!", "mov " + fp + ", " + sp))); + arm.put("epilogue", new InstructionPattern(Arrays.asList("mov " + sp + ", " + fp, "ldp " + fp + ", " + lr + ", [" + sp + "], #16", "ret"))); + arm.put("move_reg", new InstructionPattern(Arrays.asList("mov {dest}, {src}"))); + arm.put("load_immediate_int", new InstructionPattern(Arrays.asList("mov {dest}, #{value}"))); + arm.put("load_address", new InstructionPattern(Arrays.asList("adrp {dest}, {label}", "add {dest}, {dest}, :lo12:{label}"))); + arm.put("add_int", new InstructionPattern(Arrays.asList("add {dest}, {src1}, {src2}"))); + arm.put("sub_int", new InstructionPattern(Arrays.asList("sub {dest}, {src1}, {src2}"))); + arm.put("mul_int", new InstructionPattern(Arrays.asList("mul {dest}, {src1}, {src2}"))); + arm.put("div_int", new InstructionPattern(Arrays.asList("sdiv {dest}, {src1}, {src2}"))); + arm.put("cmp_eq_int", new InstructionPattern(Arrays.asList("cmp {src1}, {src2}", "cset {dest}, eq"))); + arm.put("cmp_ne_int", new InstructionPattern(Arrays.asList("cmp {src1}, {src2}", "cset {dest}, ne"))); + arm.put("cmp_lt_int", new InstructionPattern(Arrays.asList("cmp {src1}, {src2}", "cset {dest}, lt"))); + arm.put("cmp_le_int", new InstructionPattern(Arrays.asList("cmp {src1}, {src2}", "cset {dest}, le"))); + arm.put("cmp_gt_int", new InstructionPattern(Arrays.asList("cmp {src1}, {src2}", "cset {dest}, gt"))); + arm.put("cmp_ge_int", new InstructionPattern(Arrays.asList("cmp {src1}, {src2}", "cset {dest}, ge"))); + arm.put("jmp", new InstructionPattern(Arrays.asList("b {label}"))); + arm.put("call", new InstructionPattern(Arrays.asList("bl {name}"))); - // --- MODIFIED: Pass RegisterFile to constructor --- - profiles.put( - "x86_64", - new CPUProfile("x86_64", x86Patterns, x86Registers, x86Vector, x86Syntax)); - // --- END MODIFIED --- - - // ====================================================================== - // AArch64 Profile (ARM64) - // ====================================================================== - - SyntaxProfile armSyntax = - new SyntaxProfile( - "//", // commentMarker - " .text", // textSection - " .data", // dataSection - " .global {name}", // globalDirective - "{label}: .asciz \"{value}\"", // stringDirective - "{label}: .float {value}" // floatDirective - ); - - Map armPatterns = new HashMap(); - armPatterns.put( - prologue, - new InstructionPattern( - prologue, Arrays.asList(arm_prologue_1, arm_prologue_2), emptyList)); - armPatterns.put( - epilogue, - new InstructionPattern( - epilogue, - Arrays.asList(arm_epilogue_1, arm_epilogue_2, arm_epilogue_3), - emptyList)); - armPatterns.put( - move_reg, new InstructionPattern(move_reg, Arrays.asList(arm_move_reg), emptyList)); - armPatterns.put( - load_immediate_int, - new InstructionPattern( - load_immediate_int, Arrays.asList(arm_load_immediate_int), emptyList)); - armPatterns.put( - load_address, - new InstructionPattern( - load_address, - Arrays.asList(arm_load_address_1, arm_load_address_2), - emptyList)); - armPatterns.put( - add_int, new InstructionPattern(add_int, Arrays.asList(arm_add_int), emptyList)); - armPatterns.put( - sub_int, new InstructionPattern(sub_int, Arrays.asList(arm_sub_int), emptyList)); - armPatterns.put( - mul_int, new InstructionPattern(mul_int, Arrays.asList(arm_mul_int), emptyList)); - armPatterns.put( - div_int, new InstructionPattern(div_int, Arrays.asList(arm_div_int), emptyList)); - armPatterns.put( - mod_int, - new InstructionPattern( - mod_int, - Arrays.asList(arm_mod_int_1, arm_mod_int_2, arm_mod_int_3), - Arrays.asList(x16) // Clobbers x16 - )); - armPatterns.put( - neg_int, new InstructionPattern(neg_int, Arrays.asList(arm_neg_int), emptyList)); - armPatterns.put( - cmp_eq_int, - new InstructionPattern( - cmp_eq_int, Arrays.asList(arm_cmp_eq_int_1, arm_cmp_eq_int_2), emptyList)); - armPatterns.put( - cmp_ne_int, - new InstructionPattern( - cmp_ne_int, Arrays.asList(arm_cmp_ne_int_1, arm_cmp_ne_int_2), emptyList)); - armPatterns.put( - cmp_lt_int, - new InstructionPattern( - cmp_lt_int, Arrays.asList(arm_cmp_lt_int_1, arm_cmp_lt_int_2), emptyList)); - armPatterns.put( - cmp_le_int, - new InstructionPattern( - cmp_le_int, Arrays.asList(arm_cmp_le_int_1, arm_cmp_le_int_2), emptyList)); - armPatterns.put( - cmp_gt_int, - new InstructionPattern( - cmp_gt_int, Arrays.asList(arm_cmp_gt_int_1, arm_cmp_gt_int_2), emptyList)); - armPatterns.put( - cmp_ge_int, - new InstructionPattern( - cmp_ge_int, Arrays.asList(arm_cmp_ge_int_1, arm_cmp_ge_int_2), emptyList)); - armPatterns.put( - pop, new InstructionPattern(pop, Collections.emptyList(), emptyList)); - armPatterns.put(jmp, new InstructionPattern(jmp, Arrays.asList(arm_jmp), emptyList)); - armPatterns.put( - jmp_if_false, - new InstructionPattern( - jmp_if_false, - Arrays.asList(arm_jmp_if_false_1, arm_jmp_if_false_2), - emptyList)); - armPatterns.put( - jmp_if_true, - new InstructionPattern( - jmp_if_true, - Arrays.asList(arm_jmp_if_true_1, arm_jmp_if_true_2), - emptyList)); - armPatterns.put(call, new InstructionPattern(call, Arrays.asList(arm_call), emptyList)); - armPatterns.put( - store_to_stack, - new InstructionPattern( - store_to_stack, Arrays.asList(arm_store_to_stack), emptyList)); - armPatterns.put( - load_from_stack, - new InstructionPattern( - load_from_stack, Arrays.asList(arm_load_from_stack), emptyList)); - armPatterns.put( - load_field_offset, - new InstructionPattern( - load_field_offset, Arrays.asList(arm_load_field_offset), emptyList)); - armPatterns.put( - store_field_offset, - new InstructionPattern( - store_field_offset, Arrays.asList(arm_store_field_offset), emptyList)); + // FIX: Use negative offset relative to FP for Store (spilled var) + arm.put("store_to_stack", new InstructionPattern(Arrays.asList("str {src_reg}, [" + fp + ", #-{offset}]"))); - armPatterns.put(alloc_stack_frame, new InstructionPattern(alloc_stack_frame, Arrays.asList(arm_alloc_stack_frame), emptyList)); - armPatterns.put(dealloc_stack_frame, new InstructionPattern(dealloc_stack_frame, Arrays.asList(arm_dealloc_stack_frame), emptyList)); - armPatterns.put(save_callee_reg_pair, new InstructionPattern(save_callee_reg_pair, Arrays.asList(arm_save_callee_reg_pair), emptyList)); - armPatterns.put(save_callee_reg_single, new InstructionPattern(save_callee_reg_single, Arrays.asList(arm_save_callee_reg_single), emptyList)); - armPatterns.put(restore_callee_reg_pair, new InstructionPattern(restore_callee_reg_pair, Arrays.asList(arm_restore_callee_reg_pair), emptyList)); - armPatterns.put(restore_callee_reg_single, new InstructionPattern(restore_callee_reg_single, Arrays.asList(arm_restore_callee_reg_single), emptyList)); - - RegisterFile armRegisters = new RegisterFile(new AArch64Registers()); - VectorCapabilities armVector = new VectorCapabilities(128, false, true, false); + // FIX: Use negative offset relative to FP for Load (spilled var) + arm.put("load_from_stack", new InstructionPattern(Arrays.asList("ldr {dest_reg}, [" + fp + ", #-{offset}]"))); - // --- MODIFIED: Pass RegisterFile to constructor --- - profiles.put( - "aarch64", - new CPUProfile("aarch64", armPatterns, armRegisters, armVector, armSyntax)); - // --- END MODIFIED --- - } + arm.put("alloc_stack_frame", new InstructionPattern(Arrays.asList("sub " + sp + ", " + sp + ", #{size}"))); - // ====================================================================== - // --- PUBLIC API --- - // ====================================================================== + profiles.put("aarch64", new CPUProfile("aarch64", arm, + new RegisterFile(Arrays.asList(x19, x20, x21, x22, x23, x24, x25, x26, x27, x28), Arrays.asList(x0, x1, x2, x3, x4, x5, x6, x7)), + new SyntaxProfile("//", ".text", ".data", "{label}: .asciz \"{value}\""))); + } public static CPUProfile detectCPU() { String arch = System.getProperty("os.arch").toLowerCase(); - if (arch.contains("x86_64") || arch.contains("amd64")) { - return profiles.get("x86_64"); - } else if (arch.contains("aarch64")) { - return profiles.get("aarch64"); - } - System.err.println( - "Warning: Unsupported architecture '" + arch + "'. Defaulting to aarch64."); - return profiles.get("aarch64"); - } - - public static CPUProfile getProfile(String architecture) { - CPUProfile profile = profiles.get(architecture); - if (profile == null) { - throw new IllegalArgumentException("Unsupported architecture: " + architecture); - } - return profile; + if (arch.contains("aarch64") || arch.contains("arm64")) return profiles.get("aarch64"); + return profiles.get("x86_64"); } } \ No newline at end of file diff --git a/src/main/java/cod/compiler/RegisterManager.java b/src/main/java/cod/compiler/RegisterManager.java index aab84596..cba574b8 100644 --- a/src/main/java/cod/compiler/RegisterManager.java +++ b/src/main/java/cod/compiler/RegisterManager.java @@ -1,529 +1,89 @@ package cod.compiler; -import static cod.compiler.MTOTRegistry.CPUProfile.*; -import static cod.compiler.MTOTRegistry.AArch64Registers.*; -import static cod.compiler.MTOTRegistry.*; -import cod.debug.DebugSystem; import java.util.*; public class RegisterManager { - private final RegisterAllocator registerAllocator; + private final LivenessAnalyzer analyzer; + private final GraphColoringAllocator graphAllocator; private final RegisterSpiller registerSpiller; - private final RegisterFile registerFile; // Store reference + private final MTOTRegistry.RegisterFile registerFile; + + private Map currentAllocation = new HashMap(); public RegisterManager(MTOTNativeCompiler compiler) { - // --- MODIFIED: Pass registerFile directly --- this.registerFile = compiler.cpuProfile.registerFile; - // --- END MODIFIED --- this.registerSpiller = new RegisterSpiller(compiler); - this.registerAllocator = new RegisterAllocator(this.registerFile, registerSpiller); - } - - public RegisterAllocator getAllocator() { - return registerAllocator; + this.analyzer = new LivenessAnalyzer(); + this.graphAllocator = new GraphColoringAllocator(registerFile, registerSpiller); } - public RegisterSpiller getSpiller() { - return registerSpiller; + // NEW METHOD: Used by MTOTNativeCompiler to set the registers that CALLs clobber. + public void setCallerSavedRegisters(Set registers) { + this.graphAllocator.getInterferenceGraph().callerSavedRegisters = registers; } - public void reset() { - registerAllocator.reset(); + public void runAllocation(List instructions) { registerSpiller.reset(); + + List blocks = analyzer.buildCFG(instructions); + analyzer.computeGlobalLiveness(blocks); + + InterferenceGraph graph = new InterferenceGraph(); + + // Ensure the graph used for building is the one referenced by the allocator + // if they were separate, but since we are replacing the call here: + graph.callerSavedRegisters = this.graphAllocator.getInterferenceGraph().callerSavedRegisters; + + graph.buildFromCFG(blocks); + + this.currentAllocation = graphAllocator.allocate(graph); } -public class RegisterAllocator { - private final RegisterFile registerFile; - private final Stack availableRegisters = new Stack(); - // --- MODIFIED: Use LinkedHashSet for predictable iteration --- - final LinkedHashSet usedRegisters = new LinkedHashSet(); - // --- END MODIFIED --- - private final RegisterSpiller spiller; - - RegisterAllocator(RegisterFile registerFile, RegisterSpiller spiller) { - this.registerFile = registerFile; - this.spiller = spiller; - reset(); + public String getRegister(String temp) { + return currentAllocation.get(temp); } - public Set getUsedRegisters() { - return new LinkedHashSet(usedRegisters); // Return a copy + public RegisterSpiller getSpiller() { + return registerSpiller; } public void reset() { - availableRegisters.clear(); - usedRegisters.clear(); - List gp = new ArrayList(registerFile.generalPurpose); - Collections.reverse(gp); // Prefer higher numbered regs first - availableRegisters.addAll(gp); - if (spiller != null) spiller.reset(); - DebugSystem.debug("MTOT_REG", "Allocator reset. Available: " + availableRegisters.size()); - } - - public String allocateRegister() { - return allocateRegister(Collections.emptySet()); - } - - public String allocateRegister(Set avoidSpilling) { - if (availableRegisters.isEmpty()) { - DebugSystem.warn("MTOT_REG", "Out of registers! Requesting spill... Used: " + usedRegisters + ". Avoid: " + avoidSpilling); - if (spiller == null) { - throw new RuntimeException("Out of registers and no spiller configured!"); - } - // --- MODIFICATION: Pass current used registers to spiller --- - String freedRegister = spiller.spillRegister(new LinkedHashSet<>(usedRegisters), avoidSpilling); - // --- END MODIFICATION --- - - if (freedRegister == null || !usedRegisters.contains(freedRegister)) { - // Spiller might return null if it decides not to spill (e.g., only avoided regs left) - // Or, if usedRegisters was somehow modified concurrently (less likely here) - // Let's try to grab *any* available register again just in case spiller logic changes - if (!availableRegisters.isEmpty()) { - DebugSystem.warn("MTOT_REG", "Spiller didn't free a used register as expected, but one became available. Using that."); - // Proceed to pop from availableRegisters below - } else if (freedRegister != null && !usedRegisters.contains(freedRegister)) { - // Spiller returned something, but it wasn't marked as used? State inconsistency. - DebugSystem.error("MTOT_REG", "Spiller returned register " + freedRegister + " which was not in the used set! State inconsistent. Used: " + usedRegisters); - availableRegisters.push(freedRegister); // Put it back, maybe it helps - // Attempt allocation again below, might throw - } else { // freedRegister is null - throw new RuntimeException("Spiller failed to provide a register to spill (returned null)! Used: " + usedRegisters + ", Avoid: " + avoidSpilling); - } - } else { - // Successfully spilled, remove from used and add to available - usedRegisters.remove(freedRegister); - if (!availableRegisters.contains(freedRegister)) { // Avoid duplicates - availableRegisters.push(freedRegister); - } - DebugSystem.debug("MTOT_REG", "Spiller successfully freed register: " + freedRegister + ". Re-allocating."); - } - - // Check again if a register is available now - if (availableRegisters.isEmpty()) { - throw new RuntimeException("Allocation failed: No registers available even after spill attempt! Used: " + usedRegisters + ", Avoid: " + avoidSpilling); - } - } - - String reg = availableRegisters.pop(); - usedRegisters.add(reg); // Add maintains insertion order in LinkedHashSet - if (spiller != null) spiller.trackRegisterUsage(reg); // Let spiller know it's used now - DebugSystem.debug("MTOT_REG", "Allocated register: " + reg + ". Used: " + usedRegisters + ". Available: " + availableRegisters.size()); - return reg; - } - - public void freeRegister(String register) { - if (register == null) return; - - // Check if it's actually marked as used - if (usedRegisters.contains(register)) { - usedRegisters.remove(register); // Remove maintains order for others - if (registerFile.generalPurpose.contains(register)) { - // Only push back GP registers to the available pool - if (!availableRegisters.contains(register)) { // Avoid duplicates - availableRegisters.push(register); - } - if (spiller != null) spiller.untrackRegisterUsage(register); // Notify spiller it's free - DebugSystem.debug("MTOT_REG", "Freed register: " + register + ". Used: " + usedRegisters + ". Available: " + availableRegisters.size()); - } else { - // If it's not GP (e.g., arg reg, FP, SP), just removing it from used is enough. - DebugSystem.debug("MTOT_REG", "Unmarked non-GP register as used: " + register + ". Used: " + usedRegisters); - } - } - // --- MODIFICATION: Handle freeing registers not marked used (e.g., after call) --- - else if (registerFile.generalPurpose.contains(register)) { - // If it's GP but wasn't marked used, still ensure it's available - if (!availableRegisters.contains(register)) { - availableRegisters.push(register); - DebugSystem.debug("MTOT_REG", "Ensured non-used GP register is available: " + register); - } - if (spiller != null) spiller.untrackRegisterUsage(register); // Ensure spiller knows too - } - // --- END MODIFICATION --- - else if (!registerFile.argumentRegisters.contains(register) && - !register.equals(registerFile.stackPointer) && - !register.equals(registerFile.framePointer)) { - // Warn only for truly unknown registers - DebugSystem.warn("MTOT_REG", "Attempted to free unknown/invalid register: " + register); - } - } - - - public void markRegisterUsed(String register) { - if (register == null) return; - - // Check if it's a register we track (GP, Arg, SP, FP) - if (registerFile.generalPurpose.contains(register) || - registerFile.argumentRegisters.contains(register) || - register.equals(registerFile.stackPointer) || - register.equals(registerFile.framePointer) || - register.equals(AArch64Registers.x19)) { // Include 'this' explicitly if needed - - boolean wasAvailable = availableRegisters.remove(register); - boolean added = usedRegisters.add(register); // Add or update order in LinkedHashSet - - if (added || wasAvailable) { // If it was newly added or just moved from available - if (spiller != null && registerFile.generalPurpose.contains(register)) { - spiller.trackRegisterUsage(register); // Track its usage for LRU - } - DebugSystem.debug("MTOT_REG", "Marked register as used: " + register + ". Used: " + usedRegisters); - } - } else { - DebugSystem.warn("MTOT_REG", "Attempted to mark unknown/invalid register as used: " + register); - } - } -} - - -// ======================================================= -// === RegisterSpiller (Hybrid "Future Cost") === -// ======================================================= -public class RegisterSpiller { - private final MTOTNativeCompiler compiler; - private final RegisterFile registerFile; - private final Map spillSlots = new HashMap<>(); // Register -> Stack Offset - private final LinkedHashSet usageOrder = new LinkedHashSet<>(); // LRU (first) to MRU (last) - private int nextSpillOffset = -8; - private int totalSpillSize = 0; - private final Map slotToRegisterMap = new HashMap<>(); // SlotIndex -> Register - // --- NEW: Track loop depth --- - private final Map registerLoopDepth = new HashMap<>(); // Register -> Loop depth at definition - // --- END NEW --- - private final Set writtenRegistersInCurrentMethod = new HashSet<>(); - - public RegisterSpiller(MTOTNativeCompiler compiler) { - this.compiler = compiler; - this.registerFile = compiler.cpuProfile.registerFile; - } - - // --- NEW: Method to update loop depth for a register --- - public void updateRegisterDefinitionDepth(String register, int depth) { - if (register != null && registerFile.generalPurpose.contains(register)) { - registerLoopDepth.put(register, depth); - // DebugSystem.debug("SPILL_COST", "Updated loop depth for " + register + " to " + depth); - } - } - // --- END NEW --- - - // --- REVISED: spillRegister using "Future Cost" heuristic --- - public String spillRegister(LinkedHashSet currentlyUsedRegisters, Set avoidSpilling) { - if (currentlyUsedRegisters.isEmpty()) { - DebugSystem.error("SPILL", "Spill requested but no registers are marked as used!"); - return null; - } - - String bestVictim = null; - double bestScore = -1.0; // Higher score is better (spill this one) - - String fallbackVictim = null; // LRU overall - double fallbackScore = -1.0; - - List bytecode = compiler.getCurrentMethodBytecode(); // Get current method's bytecode - int currentPc = compiler.getCurrentPc(); // Get current instruction index - - // Iterate through usageOrder (LRU first) to find candidates - for (String reg : usageOrder) { - if (!currentlyUsedRegisters.contains(reg)) continue; // Only consider registers currently holding a needed value - - int nextUse = estimateNextUseDistance(reg, currentPc, bytecode); - int cost = estimateSpillCost(reg); - - // Score: Maximize nextUse distance, minimize cost (loop depth) - // Higher score means it's used further away OR less costly to reload - double score = (double) nextUse / (cost + 0.001); // Add epsilon to avoid div by zero - - // Track the absolute LRU candidate as fallback - if (fallbackVictim == null) { - fallbackVictim = reg; - fallbackScore = score; - } - - // If this register is explicitly requested to be avoided, skip primary selection - if (avoidSpilling != null && avoidSpilling.contains(reg)) { - DebugSystem.debug("SPILL", "Skipping " + reg + " (in avoid set). Score: " + score); - continue; - } - - // Is this the best candidate *so far* that we're allowed to pick? - if (bestVictim == null || score > bestScore) { - bestScore = score; - bestVictim = reg; - DebugSystem.debug("SPILL", "New best candidate: " + reg + " (Score: " + score + ", NextUse: " + nextUse + ", Cost: " + cost + ")"); - } else { - DebugSystem.debug("SPILL", "Considering " + reg + " (Score: " + score + ", NextUse: " + nextUse + ", Cost: " + cost + ") - Not better than " + bestVictim); - } - } - - // If we didn't find any suitable victim outside the avoid set - if (bestVictim == null) { - if (fallbackVictim != null) { - DebugSystem.warn("SPILL", "Forced to spill fallback LRU victim " + fallbackVictim + " (Score: " + fallbackScore + ") because best options were in avoid set."); - bestVictim = fallbackVictim; // Use the absolute LRU we tracked - } else { - DebugSystem.error("SPILL", "Cannot determine any spill victim! Used: " + currentlyUsedRegisters + ", UsageOrder: " + usageOrder); - return null; // Should not happen - } - } else { - DebugSystem.debug("SPILL", "Selected final victim " + bestVictim + " (Score: " + bestScore + ")"); - } - - forceSpill(bestVictim); - return bestVictim; + registerSpiller.reset(); + currentAllocation.clear(); } - // --- END REVISED --- - // --- NEW: Helper - Estimate next use distance (simplified) --- - private int estimateNextUseDistance(String register, int currentPc, List bytecode) { - if (bytecode == null) return Integer.MAX_VALUE; // Safety check + public static class RegisterSpiller { + private final MTOTNativeCompiler compiler; + private final Map spillSlots = new HashMap(); + // FIX: Start offset at -24 to clear the 16 bytes of saved FP/LR. + private int nextSpillOffset = -24; + private int totalSpillSize = 0; - Integer slotIndex = getSlotIndexForRegister(register); - if (slotIndex == null) { - // If we don't know which variable this register holds (e.g., temporary), - // assume it might be needed soon (give it a low distance -> high spill priority if cost is equal) - return 1; + public RegisterSpiller(MTOTNativeCompiler compiler) { + this.compiler = compiler; } - int lookaheadLimit = currentPc + 50; // Limit scan distance - for (int i = currentPc + 1; i < bytecode.size() && i < lookaheadLimit; i++) { - BytecodeInstruction instr = bytecode.get(i); - // Check if this instruction READS the slot associated with our register - if (readsSlotIndex(instr, slotIndex)) { - return i - currentPc; // Return distance - } - // Stop scan at basic block boundaries (jumps, calls, returns, labels) - if (isBasicBlockEnd(instr)) { - break; - } + public void reset() { + spillSlots.clear(); + totalSpillSize = 0; + // Reset to the new starting offset + nextSpillOffset = -24; } - return Integer.MAX_VALUE; // Not found within lookahead or basic block - } - // Helper to find the slot index currently held by a register - private Integer getSlotIndexForRegister(String register) { - for (Map.Entry entry : slotToRegisterMap.entrySet()) { - if (register.equals(entry.getValue())) { - return entry.getKey(); + public void forceSpill(String temp) { + if (!spillSlots.containsKey(temp)) { + spillSlots.put(temp, nextSpillOffset); + totalSpillSize += 8; + nextSpillOffset -= 8; } } - return null; - } - - // Helper to check if an instruction is likely to read a specific local slot - private boolean readsSlotIndex(BytecodeInstruction instr, int slotIndex) { - // This is a heuristic! More accurate analysis would need stack simulation. - switch (instr.opcode) { - case LOAD_LOCAL: - // LOAD_LOCAL *defines* a value, it doesn't read the *previous* value of that slot. - // However, if the instruction *itself* is loading our target slot, - // it means the value *currently* in the register will be overwritten. - // We consider this a "use" in the sense that the current register value is needed *before* this instruction. - return instr.operand instanceof Integer && ((Integer) instr.operand).intValue() == slotIndex; - - // Instructions that read 1 value (potentially from our slot if it's on top) - case POP: - case STORE_LOCAL: // Reads value to store - case STORE_FIELD: - case STORE_SLOT: - case PRINT: - case NEG_INT: - case INT_TO_STRING: - case JMP_IF_TRUE: - case JMP_IF_FALSE: - // Approximation: Assume it might read our slot if it reads the top of the stack. - // We can't know for sure without stack tracking. For simplicity, return false. - // A more complex check could see if LOAD_LOCAL slotIndex immediately preceded this. - return false; - - // Instructions that read 2 values - case ADD_INT: case SUB_INT: case MUL_INT: case DIV_INT: case MOD_INT: - case CMP_EQ_INT: case CMP_NE_INT: case CMP_LT_INT: case CMP_LE_INT: case CMP_GT_INT: case CMP_GE_INT: - case CONCAT_STRING: - case ARRAY_LOAD: // Reads array ref and index - case ARRAY_STORE: // Reads array ref, index, and value (reads 3, but simplifies logic) - // Approximation: Assume it might read our slot if it reads top 2 stack items. Return false. - return false; - - // Reads arguments based on arg count - complex - case CALL: - case CALL_SLOTS: - return false; // Too complex for simple lookahead - - default: - return false; - } - } - // Helper to identify instructions ending a basic block - private boolean isBasicBlockEnd(BytecodeInstruction instr) { - switch (instr.opcode) { - case JMP: - case JMP_IF_TRUE: - case JMP_IF_FALSE: - case RET: - case CALL: - case CALL_SLOTS: - case LABEL: // Start of a new block - return true; - default: - return false; + public Integer getSpillOffset(String temp) { + return spillSlots.get(temp); } - } - // --- END NEW --- - - // --- NEW: Helper - Estimate spill cost --- - private int estimateSpillCost(String register) { - int depth = registerLoopDepth.getOrDefault(register, 0); - // Heavily penalize spilling registers defined inside loops - // Cost = 1 (base) + depth^2 * 10 (example weighting) - return 1 + (depth * depth * 10); - } - // --- END NEW --- - - public void mapSlotToRegister(int slotIndex, String register) { - slotToRegisterMap.put(slotIndex, register); - DebugSystem.debug("SPILL_MAP", "Mapped slot " + slotIndex + " -> register " + register); - } - - public int getSpillOffsetForSlotIndex(int slotIndex) { - String register = slotToRegisterMap.get(slotIndex); - if (register == null) { - DebugSystem.debug("SPILL_MAP", "No register mapped for slot " + slotIndex + ", cannot get spill offset."); - return Integer.MIN_VALUE; - } - Integer offset = spillSlots.get(register); - if (offset == null) { - DebugSystem.debug("SPILL_MAP", "Register " + register + " (for slot "+slotIndex+") has no spill slot assigned."); - return Integer.MIN_VALUE; + public int getTotalSpillSize() { + return (totalSpillSize + 15) & ~15; } - return offset.intValue(); } - - - public void forceSpill(String register) { - if (register == null || !registerFile.generalPurpose.contains(register)) { - DebugSystem.debug("SPILL", "Attempted to force spill non-GP register: " + register + " - Skipping."); - return; // Only spill general-purpose registers - } - int offset = getOrCreateSpillSlot(register); - DebugSystem.debug("SPILL", "Force spilling register " + register + " to stack offset [fp" + offset + "]"); - compiler.generateSpillCode(register, offset); - usageOrder.remove(register); // Remove from usage tracking as its value is now safely on stack - // REMOVED: registersOnStack.add(register); - } - - - // --- MAJOR REVISION (Kept from previous version): Always load if spill slot exists --- - public void fillRegister(String register) { - if (register == null || !registerFile.generalPurpose.contains(register)) { - // Don't attempt to fill non-GP registers (like args, sp, fp) - return; - } - - Integer offset = spillSlots.get(register); - // If a spill slot *exists* for this register, we MUST reload. - if (offset != null) { - DebugSystem.debug("SPILL", "Filling register " + register + " from stack offset [fp" + offset + "] (spill slot exists)."); - compiler.generateFillCode(register, offset.intValue()); - // REMOVED: registersOnStack.remove(register); - trackRegisterUsage(register); // Mark as recently used *after* filling - } else { - // No spill slot exists. Assume the register holds a valid value - // OR this is its first use and it will be written to shortly. - // We still track its usage if it was potentially used without spilling. - trackRegisterUsage(register); - DebugSystem.debug("SPILL", "Register " + register + " has no spill slot, assuming value is live or will be written."); - } - } - // --- END MAJOR REVISION --- - - public void trackRegisterUsage(String register) { - if (register != null && registerFile.generalPurpose.contains(register)) { - usageOrder.remove(register); // Remove if exists - usageOrder.add(register); // Add to end (MRU) - } - } - - public void untrackRegisterUsage(String register) { - if (register != null) { - usageOrder.remove(register); - // REMOVED: registersOnStack.remove(register); - } - } - - private int getOrCreateSpillSlot(String register) { - Integer offset = spillSlots.get(register); - if (offset == null) { - offset = Integer.valueOf(nextSpillOffset); - spillSlots.put(register, offset); - totalSpillSize += 8; // Assuming 64-bit registers - DebugSystem.debug("SPILL", "Allocated spill slot for " + register + " at offset [fp" + nextSpillOffset + "]. Total spill size (raw): " + totalSpillSize); - nextSpillOffset -= 8; - } - return offset.intValue(); - } - - public int getTotalSpillSize() { - // Ensure 16-byte alignment for the stack frame - return (totalSpillSize + 15) & ~15; - } - - // --- MAJOR REVISION (Kept from previous version): Update stack immediately if needed --- - public void markRegisterModified(String register) { - if (register == null || !registerFile.generalPurpose.contains(register)) { - return; // Only track modifications to GP registers we manage/spill - } - trackRegisterUsage(register); // Update LRU status - writtenRegistersInCurrentMethod.add(register); // Track that it was written in this method - - // Check if this register has a designated spill slot. - Integer offset = spillSlots.get(register); - if (offset != null) { - // If it has a spill slot, the value on the stack is now potentially stale. - // Force spill the *new* value immediately to keep the stack consistent. - DebugSystem.debug("SPILL", "Register " + register + " modified, forcing update to its spill slot [fp" + offset + "]"); - forceSpill(register); // forceSpill handles generating code and updating state - } else { - // If it doesn't have a spill slot, no need to update the stack yet. - DebugSystem.debug("SPILL", "Register " + register + " modified (no spill slot yet)."); - } - // REMOVED: registersOnStack.remove(register); // This line is now redundant - } - // --- END MAJOR REVISION --- - - - public Set getWrittenRegistersDuringCompilation() { - return new HashSet(writtenRegistersInCurrentMethod); - } - - public void reset() { - spillSlots.clear(); - usageOrder.clear(); - slotToRegisterMap.clear(); - registerLoopDepth.clear(); // <-- NEW: Clear depth map - // REMOVED: registersOnStack.clear(); - writtenRegistersInCurrentMethod.clear(); - nextSpillOffset = -8; // Will be set correctly by setBaseSpillOffset - totalSpillSize = 0; - DebugSystem.debug("SPILL", "Spiller reset."); - } - - public void setBaseSpillOffset(int baseOffset) { - // Base offset is typically the negative offset of the lowest callee-saved register stored. - // Spills start 8 bytes below that. - this.nextSpillOffset = baseOffset - 8; - DebugSystem.debug("SPILL", "Spiller base offset set relative to FP: " + baseOffset + ". Next spill slot at: [fp" + this.nextSpillOffset + "]"); - } - - // This method might still be useful if loading from a *known fixed offset* (like arguments passed on stack) - // For general spilling/filling, rely on the main fillRegister logic. - public void fillRegisterFromOffset(String register, int offset) { - if (register == null) return; - DebugSystem.debug("SPILL", "Explicitly filling register " + register + " from known stack offset [fp" + offset + "]"); - compiler.generateFillCode(register, offset); - // REMOVED: registersOnStack.remove(register); - trackRegisterUsage(register); // Mark as recently used - updateRegisterDefinitionDepth(register, compiler.getCurrentLoopDepth()); // Also update depth on explicit fill - } -} - } \ No newline at end of file diff --git a/src/main/java/cod/compiler/TACCompiler.java b/src/main/java/cod/compiler/TACCompiler.java new file mode 100644 index 00000000..935033d2 --- /dev/null +++ b/src/main/java/cod/compiler/TACCompiler.java @@ -0,0 +1,697 @@ +package cod.compiler; + +import cod.ast.BaseASTVisitor; +import cod.ast.nodes.*; +import cod.compiler.TACInstruction.Opcode; +import cod.debug.DebugSystem; +import java.util.*; + +public class TACCompiler extends BaseASTVisitor { + + private TACProgram tacProgram; + private List currentCode; + + private Map variableToTempMap = new HashMap(); + private int labelCounter = 0; + private int nextTempId = 0; + private String currentMethodName = ""; + + public TACProgram compile(ProgramNode program) { + DebugSystem.info("TAC", "Starting Visitor-based TAC compilation"); + this.tacProgram = new TACProgram(); + visit(program); + return tacProgram; + } + + private void emit(TACInstruction instr) { + currentCode.add(instr); + } + + private String newTemp() { + return "$t" + (nextTempId++); + } + + private String getNewLabel(String prefix) { + return "L_" + currentMethodName + "_" + prefix + "_" + (labelCounter++); + } + + private String allocateVariable(String name) { + String tacName = "$v_" + name; + variableToTempMap.put(name, tacName); + return tacName; + } + + private String getOrAllocateVariable(String name) { + if (!variableToTempMap.containsKey(name)) { + return allocateVariable(name); + } + return variableToTempMap.get(name); + } + + // --- Structural Visitors --- + + @Override + public String visit(ProgramNode node) { + if (node.unit != null) visit(node.unit); + return null; + } + + @Override + public String visit(UnitNode node) { + for (TypeNode type : node.types) visit(type); + return null; + } + + @Override + public String visit(TypeNode node) { + for (MethodNode method : node.methods) visit(method); + return null; + } + + @Override + public String visit(MethodNode method) { + currentCode = new ArrayList(); + variableToTempMap.clear(); + labelCounter = 0; + nextTempId = 0; + currentMethodName = method.name; + + // Allocate variables for parameters + for (ParamNode param : method.parameters) allocateVariable(param.name); + + // Allocate variables for return slots + for (SlotNode slot : method.returnSlots) allocateVariable(slot.name); + + // Compile method body + for (StmtNode stmt : method.body) visit(stmt); + + // Add implicit return if missing + if (currentCode.isEmpty() || currentCode.get(currentCode.size() - 1).opcode != Opcode.RET) { + if (!method.returnSlots.isEmpty()) { + String firstSlot = variableToTempMap.get(method.returnSlots.get(0).name); + emit(new TACInstruction(Opcode.RET, firstSlot)); + } else { + emit(new TACInstruction(Opcode.RET)); + } + } + + tacProgram.addMethod(method.name, new ArrayList(currentCode)); + return null; + } + + // --- Statement Visitors --- + + @Override + public String visit(VarNode node) { + String targetTemp = getOrAllocateVariable(node.name); + if (node.value != null) { + String valTemp = dispatch(node.value); + if (valTemp != null) { + emit(new TACInstruction(Opcode.ASSIGN, targetTemp, valTemp)); + } + } + return null; + } + + @Override + public String visit(AssignmentNode node) { + if (node.left instanceof IndexAccessNode) { + IndexAccessNode access = (IndexAccessNode) node.left; + String arrTemp = dispatch(access.array); + String idxTemp = dispatch(access.index); + String valTemp = dispatch(node.right); + emit(new TACInstruction(Opcode.STORE_ARRAY, valTemp, arrTemp, idxTemp)); + } + else if (node.left instanceof ExprNode) { + String name = ((ExprNode) node.left).name; + String valTemp = dispatch(node.right); + String targetTemp = getOrAllocateVariable(name); + emit(new TACInstruction(Opcode.ASSIGN, targetTemp, valTemp)); + } + return null; + } + + @Override + public String visit(SlotAssignmentNode node) { + String valTemp = dispatch(node.value); + String targetName = node.slotName; + String targetTemp = getOrAllocateVariable(targetName); + emit(new TACInstruction(Opcode.ASSIGN, targetTemp, valTemp)); + return null; + } + + @Override + public String visit(MultipleSlotAssignmentNode node) { + for (SlotAssignmentNode sub : node.assignments) visit(sub); + return null; + } + + @Override + public String visit(ReturnSlotAssignmentNode node) { + MethodCallNode call = node.methodCall; + + // Step 1: Push all arguments + for (ExprNode arg : call.arguments) { + String t = dispatch(arg); + emit(new TACInstruction(Opcode.PARAM, t)); + } + + // Step 2: Call method - returns primary result (could be array/map) + String primaryRet = newTemp(); + + // Use CALL_SLOTS to indicate this returns multiple values + // Operand1: method name + // Operand2: argument count + // No need for slot count in operand since we handle it in runtime + emit(new TACInstruction(Opcode.CALL_SLOTS, primaryRet, call.qualifiedName, call.arguments.size())); + + // Step 3: Extract each slot to destination variables + for (int i = 0; i < node.variableNames.size(); i++) { + String targetVar = node.variableNames.get(i); + String targetTemp = getOrAllocateVariable(targetVar); + + if (i == 0) { + // First variable gets the primary return + emit(new TACInstruction(Opcode.ASSIGN, targetTemp, primaryRet)); + } else { + // Additional variables from array (if method returns array) + // For now, assume method returns array with slots at indices + String idxTemp = newTemp(); + emit(new TACInstruction(Opcode.LOAD_IMM, idxTemp, i)); + emit(new TACInstruction(Opcode.LOAD_ARRAY, targetTemp, primaryRet, idxTemp)); + } + } + return null; + } + + @Override + public String visit(StmtIfNode node) { + String elseLabel = getNewLabel("else"); + String endLabel = getNewLabel("endif"); + + String condTemp = dispatch(node.condition); + + // Compare condition with 0 (false) + String zeroTemp = newTemp(); + emit(new TACInstruction(Opcode.LOAD_IMM, zeroTemp, 0)); + emit(new TACInstruction(Opcode.IF_GOTO, condTemp, zeroTemp, elseLabel)); + + // Then block + visitBlock(node.thenBlock); + + boolean hasElse = node.elseBlock != null && !node.elseBlock.statements.isEmpty(); + if (hasElse) { + emit(new TACInstruction(Opcode.GOTO, endLabel)); + } + + // Else block + emit(new TACInstruction(Opcode.LABEL, elseLabel)); + if (hasElse) { + visitBlock(node.elseBlock); + } + + emit(new TACInstruction(Opcode.LABEL, endLabel)); + return null; + } + + private void visitBlock(BlockNode block) { + if (block == null) return; + for (StmtNode s : block.statements) visit(s); + } + + @Override + public String visit(ForNode node) { + String iterTemp = getOrAllocateVariable(node.iterator); + + // Evaluate start expression + String startTemp = dispatch(node.range.start); + if (startTemp == null) { + startTemp = newTemp(); + emit(new TACInstruction(Opcode.LOAD_IMM, startTemp, 0)); + } + + // Evaluate end expression + String endTemp = dispatch(node.range.end); + if (endTemp == null) { + endTemp = newTemp(); + emit(new TACInstruction(Opcode.LOAD_IMM, endTemp, 0)); + } + + // Evaluate step expression (or default to 1) + String stepTemp = newTemp(); + if (node.range.step != null) { + String stepVal = dispatch(node.range.step); + if (stepVal != null) { + stepTemp = stepVal; + } else { + emit(new TACInstruction(Opcode.LOAD_IMM, stepTemp, 1)); + } + } else { + emit(new TACInstruction(Opcode.LOAD_IMM, stepTemp, 1)); + } + + emit(new TACInstruction(Opcode.ASSIGN, iterTemp, startTemp)); + + String startLabel = getNewLabel("loop_start"); + String endLabel = getNewLabel("loop_end"); + String checkNegativeLabel = getNewLabel("check_neg"); + String checkDoneLabel = getNewLabel("check_done"); + + emit(new TACInstruction(Opcode.LABEL, startLabel)); + + // --- BEGIN Dynamic Loop Condition Check --- + + // 1. Determine direction: Check if step is positive (>= 0) + String zeroTemp = newTemp(); + emit(new TACInstruction(Opcode.LOAD_IMM, zeroTemp, 0)); + + String isPositive = newTemp(); + emit(new TACInstruction(Opcode.CMP_GE, isPositive, stepTemp, zeroTemp)); + + // If step is NOT positive (i.e., step < 0), jump to check_neg + emit(new TACInstruction(Opcode.IF_GOTO, isPositive, zeroTemp, checkNegativeLabel)); + + // Fallthrough: Positive Step (Check: iter > end -> Break) + String condPos = newTemp(); + emit(new TACInstruction(Opcode.CMP_GT, condPos, iterTemp, endTemp)); + String oneTemp = newTemp(); + emit(new TACInstruction(Opcode.LOAD_IMM, oneTemp, 1)); + emit(new TACInstruction(Opcode.IF_GOTO, condPos, oneTemp, endLabel)); + emit(new TACInstruction(Opcode.GOTO, checkDoneLabel)); // Skip negative check + + // Negative Step Check (Check: iter < end -> Break) + emit(new TACInstruction(Opcode.LABEL, checkNegativeLabel)); + String condNeg = newTemp(); + emit(new TACInstruction(Opcode.CMP_LT, condNeg, iterTemp, endTemp)); + emit(new TACInstruction(Opcode.LOAD_IMM, oneTemp, 1)); // Reuse oneTemp + emit(new TACInstruction(Opcode.IF_GOTO, condNeg, oneTemp, endLabel)); + + emit(new TACInstruction(Opcode.LABEL, checkDoneLabel)); + + // --- END Dynamic Loop Condition Check --- + + // Loop body + visitBlock(node.body); + + // Increment iterator + String nextIter = newTemp(); + // The step variable (stepTemp) holds the value, positive or negative + emit(new TACInstruction(Opcode.ADD, nextIter, iterTemp, stepTemp)); + emit(new TACInstruction(Opcode.ASSIGN, iterTemp, nextIter)); + + emit(new TACInstruction(Opcode.GOTO, startLabel)); + emit(new TACInstruction(Opcode.LABEL, endLabel)); + return null; + } + + @Override + public String visit(OutputNode node) { + if (node.arguments.isEmpty()) { + String t = newTemp(); + emit(new TACInstruction(Opcode.LOAD_ADDR, t, "")); + emit(new TACInstruction(Opcode.PRINT, t)); + return null; + } + + for (ExprNode arg : node.arguments) { + String val = dispatch(arg); + if (val == null) { + // Handle method calls that return void + if (arg instanceof MethodCallNode) { + // Call already emitted, just need PRINT + String temp = newTemp(); + emit(new TACInstruction(Opcode.LOAD_IMM, temp, 0)); // Dummy value + emit(new TACInstruction(Opcode.PRINT, temp)); + } + } else { + emit(new TACInstruction(Opcode.PRINT, val)); + } + } + return null; + } + + @Override + public String visit(InputNode node) { + String targetTemp = getOrAllocateVariable(node.variableName); + String typeTemp = newTemp(); + emit(new TACInstruction(Opcode.LOAD_ADDR, typeTemp, node.targetType)); + emit(new TACInstruction(Opcode.READ_INPUT, targetTemp, typeTemp)); + return null; + } + + @Override + public String visit(BinaryOpNode node) { + System.err.println("DEBUG BinaryOpNode: " + node.op); + + // ALWAYS get values for both operands + String leftTemp = dispatch(node.left); + if (leftTemp == null) { + leftTemp = newTemp(); + System.err.println(" WARNING: left operand returned null, using default"); + emit(new TACInstruction(Opcode.LOAD_IMM, leftTemp, 0)); + } + + String rightTemp = dispatch(node.right); + if (rightTemp == null) { + rightTemp = newTemp(); + System.err.println(" WARNING: right operand returned null, using default"); + emit(new TACInstruction(Opcode.LOAD_IMM, rightTemp, 0)); + } + + String resultTemp = newTemp(); + + // Handle string concatenation + if ("+".equals(node.op)) { + boolean leftIsString = isStringNode(node.left); + boolean rightIsString = isStringNode(node.right); + + if (leftIsString || rightIsString) { + // String concatenation + if (!leftIsString) { + String leftStr = newTemp(); + emit(new TACInstruction(Opcode.INT_TO_STRING, leftStr, leftTemp)); + leftTemp = leftStr; + } + if (!rightIsString) { + String rightStr = newTemp(); + emit(new TACInstruction(Opcode.INT_TO_STRING, rightStr, rightTemp)); + rightTemp = rightStr; + } + emit(new TACInstruction(Opcode.CONCAT, resultTemp, leftTemp, rightTemp)); + return resultTemp; + } + } + + // Handle other binary operations + Opcode opcode = mapOp(node.op); + emit(new TACInstruction(opcode, resultTemp, leftTemp, rightTemp)); + return resultTemp; + } + + private boolean isStringNode(ExprNode node) { + if (node == null) return false; + if (node.value instanceof String) return true; + if (node instanceof BinaryOpNode && "+".equals(((BinaryOpNode)node).op)) { + return isStringNode(((BinaryOpNode)node).left) || isStringNode(((BinaryOpNode)node).right); + } + return false; + } + + @Override + public String visit(UnaryNode node) { + System.err.println("DEBUG UnaryNode: op=" + node.op); + + // Get operand value + String operandTemp = dispatch(node.operand); + if (operandTemp == null) { + operandTemp = newTemp(); + System.err.println(" WARNING: operand returned null, using default"); + + // Try to extract value from ExprNode + if (node.operand instanceof ExprNode) { + ExprNode expr = (ExprNode) node.operand; + if (expr.value instanceof Integer) { + int val = (Integer) expr.value; + emit(new TACInstruction(Opcode.LOAD_IMM, operandTemp, val)); + } else if (expr.value instanceof Float) { + float val = (Float) expr.value; + emit(new TACInstruction(Opcode.LOAD_IMM, operandTemp, val)); + } else { + emit(new TACInstruction(Opcode.LOAD_IMM, operandTemp, 0)); + } + } else { + emit(new TACInstruction(Opcode.LOAD_IMM, operandTemp, 0)); + } + } + + String resultTemp = newTemp(); + + if ("-".equals(node.op)) { + // Check if we can load immediate negative value + if (node.operand instanceof ExprNode) { + ExprNode expr = (ExprNode) node.operand; + if (expr.value instanceof Integer) { + int val = (Integer) expr.value; + System.err.println(" Loading immediate negative: -" + val); + emit(new TACInstruction(Opcode.LOAD_IMM, resultTemp, -val)); + return resultTemp; + } else if (expr.value instanceof Float) { + float val = (Float) expr.value; + System.err.println(" Loading immediate negative: -" + val); + emit(new TACInstruction(Opcode.LOAD_IMM, resultTemp, -val)); + return resultTemp; + } + } + // General case: use NEG instruction + System.err.println(" Using NEG instruction"); + emit(new TACInstruction(Opcode.NEG, resultTemp, operandTemp)); + } else if ("!".equals(node.op)) { + // Logical NOT + String zeroTemp = newTemp(); + emit(new TACInstruction(Opcode.LOAD_IMM, zeroTemp, 0)); + emit(new TACInstruction(Opcode.CMP_EQ, resultTemp, operandTemp, zeroTemp)); + } else { + // Unary plus or other + emit(new TACInstruction(Opcode.ASSIGN, resultTemp, operandTemp)); + } + + return resultTemp; + } + + @Override + public String visit(MethodCallNode node) { + // Push arguments + for (ExprNode arg : node.arguments) { + String t = dispatch(arg); + emit(new TACInstruction(Opcode.PARAM, t)); + } + + String result = newTemp(); + + // Check if it's a slot call + if (!node.slotNames.isEmpty()) { + // For slot calls, we need to handle multiple returns + emit(new TACInstruction(Opcode.CALL_SLOTS, result, node.qualifiedName, node.arguments.size())); + + // Extract slots if needed (for standalone slot calls) + for (int i = 0; i < node.slotNames.size(); i++) { + String slotName = node.slotNames.get(i); + String slotTemp = getOrAllocateVariable(slotName); + + if (i == 0) { + // First slot is primary return + emit(new TACInstruction(Opcode.ASSIGN, slotTemp, result)); + } else { + // Additional slots from array + String idxTemp = newTemp(); + emit(new TACInstruction(Opcode.LOAD_IMM, idxTemp, i)); + emit(new TACInstruction(Opcode.LOAD_ARRAY, slotTemp, result, idxTemp)); + } + } + } else { + // Regular method call + emit(new TACInstruction(Opcode.CALL, result, node.qualifiedName, node.arguments.size())); + } + + return result; + } + + private String visitStatementExpression(ExprNode expr) { + if (expr instanceof MethodCallNode) { + MethodCallNode call = (MethodCallNode) expr; + // Visit but don't use the result + visit(call); + return null; + } else { + // For other expressions, just evaluate + return visit(expr); + } + } + + @Override + public String visit(ExprNode node) { + System.err.println("DEBUG ExprNode: name=" + node.name + ", value=" + node.value); + + if (node.name != null) { + // Variable reference + String varName = getOrAllocateVariable(node.name); + System.err.println(" -> Variable: " + node.name + " -> " + varName); + return varName; + } + + if (node.value != null) { + // Literal value + String temp = newTemp(); + if (node.value instanceof String) { + String str = (String) node.value; + // Remove quotes if present + if (str.startsWith("\"") && str.endsWith("\"")) { + str = str.substring(1, str.length() - 1); + } + System.err.println(" -> String literal: '" + str + "' -> " + temp); + emit(new TACInstruction(Opcode.LOAD_ADDR, temp, str)); + } else { + System.err.println(" -> Literal: " + node.value + " -> " + temp); + emit(new TACInstruction(Opcode.LOAD_IMM, temp, node.value)); + } + return temp; + } + + if (node.isNull) { + // null literal + String temp = newTemp(); + System.err.println(" -> null -> " + temp); + emit(new TACInstruction(Opcode.LOAD_IMM, temp, 0)); + return temp; + } + + // Empty expression - create default + String temp = newTemp(); + System.err.println(" WARNING: Empty ExprNode, using default 0 -> " + temp); + emit(new TACInstruction(Opcode.LOAD_IMM, temp, 0)); + return temp; + } + + @Override + public String visit(IndexAccessNode node) { + String arr = dispatch(node.array); + String idx = dispatch(node.index); + String res = newTemp(); + emit(new TACInstruction(Opcode.LOAD_ARRAY, res, arr, idx)); + return res; + } + + @Override + public String visit(ArrayNode node) { + String sizeTemp = newTemp(); + emit(new TACInstruction(Opcode.LOAD_IMM, sizeTemp, node.elements.size())); + String arrTemp = newTemp(); + emit(new TACInstruction(Opcode.ARRAY_NEW, arrTemp, sizeTemp)); + + // Store each element + for (int i = 0; i < node.elements.size(); i++) { + String val = dispatch(node.elements.get(i)); + String idx = newTemp(); + emit(new TACInstruction(Opcode.LOAD_IMM, idx, i)); + emit(new TACInstruction(Opcode.STORE_ARRAY, val, arrTemp, idx)); + } + return arrTemp; + } + + @Override + public String visit(TypeCastNode node) { + // For TAC, we'll just evaluate the expression + // Type checking happens at compile time or runtime + return visit(node.expression); + } + + @Override + public String visit(EqualityChainNode node) { + // Compile as series of comparisons + String left = dispatch(node.left); + String result = newTemp(); + String oneTemp = newTemp(); + String zeroTemp = newTemp(); + + emit(new TACInstruction(Opcode.LOAD_IMM, oneTemp, 1)); + emit(new TACInstruction(Opcode.LOAD_IMM, zeroTemp, 0)); + + if (node.isAllChain) { + // Initialize result to true (1) + emit(new TACInstruction(Opcode.ASSIGN, result, oneTemp)); + + for (ExprNode arg : node.chainArguments) { + String right = dispatch(arg); + String cmpTemp = newTemp(); + emit(new TACInstruction(mapOp(node.operator), cmpTemp, left, right)); + + // result = result AND cmpTemp + String andTemp = newTemp(); + emit(new TACInstruction(Opcode.CMP_NE, andTemp, cmpTemp, zeroTemp)); + emit(new TACInstruction(Opcode.ASSIGN, result, andTemp)); + } + } else { + // ANY chain: Initialize result to false (0) + emit(new TACInstruction(Opcode.ASSIGN, result, zeroTemp)); + + for (ExprNode arg : node.chainArguments) { + String right = dispatch(arg); + String cmpTemp = newTemp(); + emit(new TACInstruction(mapOp(node.operator), cmpTemp, left, right)); + + // result = result OR cmpTemp + String orTemp = newTemp(); + emit(new TACInstruction(Opcode.CMP_NE, orTemp, cmpTemp, zeroTemp)); + emit(new TACInstruction(Opcode.ASSIGN, result, orTemp)); + } + } + + return result; + } + + @Override + public String visit(BooleanChainNode node) { + // Compile ALL/ANY expressions + String result = newTemp(); + String oneTemp = newTemp(); + String zeroTemp = newTemp(); + + emit(new TACInstruction(Opcode.LOAD_IMM, oneTemp, 1)); + emit(new TACInstruction(Opcode.LOAD_IMM, zeroTemp, 0)); + + if (node.isAll) { + // ALL: Start with true, AND with each expression + emit(new TACInstruction(Opcode.ASSIGN, result, oneTemp)); + + for (ExprNode expr : node.expressions) { + String exprVal = dispatch(expr); + // Convert to boolean (0 or 1) + String boolTemp = newTemp(); + emit(new TACInstruction(Opcode.CMP_NE, boolTemp, exprVal, zeroTemp)); + + // result = result AND boolTemp + String andTemp = newTemp(); + emit(new TACInstruction(Opcode.CMP_NE, andTemp, result, zeroTemp)); + emit(new TACInstruction(Opcode.CMP_NE, andTemp, andTemp, zeroTemp)); + emit(new TACInstruction(Opcode.CMP_EQ, andTemp, andTemp, oneTemp)); + emit(new TACInstruction(Opcode.ASSIGN, result, andTemp)); + } + } else { + // ANY: Start with false, OR with each expression + emit(new TACInstruction(Opcode.ASSIGN, result, zeroTemp)); + + for (ExprNode expr : node.expressions) { + String exprVal = dispatch(expr); + // Convert to boolean (0 or 1) + String boolTemp = newTemp(); + emit(new TACInstruction(Opcode.CMP_NE, boolTemp, exprVal, zeroTemp)); + + // result = result OR boolTemp + String orTemp = newTemp(); + emit(new TACInstruction(Opcode.CMP_NE, orTemp, result, zeroTemp)); + emit(new TACInstruction(Opcode.CMP_NE, orTemp, orTemp, zeroTemp)); + emit(new TACInstruction(Opcode.CMP_EQ, orTemp, orTemp, oneTemp)); + emit(new TACInstruction(Opcode.ASSIGN, result, orTemp)); + } + } + + return result; + } + + private Opcode mapOp(String op) { + switch (op) { + case "+": return Opcode.ADD; + case "-": return Opcode.SUB; + case "*": return Opcode.MUL; + case "/": return Opcode.DIV; + case "%": return Opcode.MOD; + case "==": return Opcode.CMP_EQ; + case "!=": return Opcode.CMP_NE; + case "<": return Opcode.CMP_LT; + case "<=": return Opcode.CMP_LE; + case ">": return Opcode.CMP_GT; + case ">=": return Opcode.CMP_GE; + default: return Opcode.ADD; + } + } +} \ No newline at end of file diff --git a/src/main/java/cod/compiler/TACInstruction.java b/src/main/java/cod/compiler/TACInstruction.java new file mode 100644 index 00000000..ca630c76 --- /dev/null +++ b/src/main/java/cod/compiler/TACInstruction.java @@ -0,0 +1,117 @@ +package cod.compiler; + +import java.util.*; + +public class TACInstruction { + public enum Opcode { + // Arithmetic + ADD, SUB, MUL, DIV, MOD, + // Logic / Comparison + CMP_EQ, CMP_NE, CMP_LT, CMP_LE, CMP_GT, CMP_GE, + NEG, INT_TO_STRING, + CONCAT, + // Data Movement + ASSIGN, LOAD_IMM, LOAD_ADDR, + // Control Flow + GOTO, IF_GOTO, LABEL, + RET, + // Object / Memory + LOAD_FIELD, STORE_FIELD, + LOAD_ARRAY, STORE_ARRAY, + ARRAY_NEW, + // Function Calls + CALL, CALL_SLOTS, PARAM, + // Runtime / IO + PRINT, READ_INPUT + } + + public final Opcode opcode; + public final String result; + public final Object operand1; + public final Object operand2; + + public TACInstruction(Opcode opcode, String result, Object operand1, Object operand2) { + this.opcode = opcode; + this.result = result; + this.operand1 = operand1; + this.operand2 = operand2; + } + + public TACInstruction(Opcode opcode, String result, Object operand1) { + this(opcode, result, operand1, null); + } + + public TACInstruction(Opcode opcode, Object operand1) { + this(opcode, null, operand1, null); + } + + public TACInstruction(Opcode opcode) { + this(opcode, null, null, null); + } + + @Override + public String toString() { + String s = String.format("%-12s", opcode); + if (result != null) s += " " + result + " ="; + if (operand1 != null) s += " " + operand1; + if (operand2 != null) s += ", " + operand2; + return s; + } + + // --- Analysis Helpers --- + + public String getDef() { + if (result == null) return null; + switch (opcode) { + case ADD: case SUB: case MUL: case DIV: case MOD: + case CMP_EQ: case CMP_NE: case CMP_LT: case CMP_LE: case CMP_GT: case CMP_GE: + case NEG: case INT_TO_STRING: case CONCAT: + case ASSIGN: case LOAD_IMM: case LOAD_ADDR: + case LOAD_FIELD: case LOAD_ARRAY: + case CALL: case CALL_SLOTS: + case READ_INPUT: case ARRAY_NEW: + return result; + default: + return null; + } + } + + public List getUses() { + List uses = new ArrayList(); + addUseIfVariable(uses, operand1); + addUseIfVariable(uses, operand2); + + // STORE operations read the 'result' field (it holds the value to store) + if (opcode == Opcode.STORE_ARRAY || opcode == Opcode.STORE_FIELD) { + if (result != null && isVariable(result)) { + uses.add(result); + } + } + + // IF_GOTO uses its operand1 (condition) + if (opcode == Opcode.IF_GOTO) { + // IF_GOTO cond, zero, label -> cond is used + addUseIfVariable(uses, result); // Sometimes cond is in result pos in constructor + } + + return uses; + } + + private void addUseIfVariable(List uses, Object operand) { + if (operand instanceof String) { + String str = (String) operand; + if (isVariable(str)) { + uses.add(str); + } + } + } + + private boolean isVariable(String s) { + if (s == null) return false; + // Exclude string literals and labels + if (s.startsWith("\"")) return false; + if (s.startsWith("L_") || s.startsWith("loop_") || s.startsWith("else_") || s.startsWith("endif_")) return false; + // Variables start with $ or letters + return true; + } +} \ No newline at end of file diff --git a/src/main/java/cod/compiler/TACProgram.java b/src/main/java/cod/compiler/TACProgram.java new file mode 100644 index 00000000..90ec51c3 --- /dev/null +++ b/src/main/java/cod/compiler/TACProgram.java @@ -0,0 +1,39 @@ +package cod.compiler; + +import java.util.*; + +public class TACProgram { + private Map> methods = new HashMap>(); + private Map nativeMethods = new HashMap(); + + public void addMethod(String name, List code) { + methods.put(name, new ArrayList(code)); + } + + public void addNativeMethod(String name, String assemblyCode) { + nativeMethods.put(name, assemblyCode); + } + + public List getMethod(String name) { + return methods.get(name); + } + + public Map> getMethods() { + return methods; + } + + public Map getNativeMethods() { + return nativeMethods; + } + + public void disassemble() { + System.out.println("=== MTOT Three-Address Code Disassembly ==="); + for (Map.Entry> entry : methods.entrySet()) { + System.out.println("\nMethod: " + entry.getKey()); + List instructions = entry.getValue(); + for (int i = 0; i < instructions.size(); i++) { + System.out.printf(" %04d: %s%n", i, instructions.get(i).toString()); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/cod/compiler/natives/ControlFlowCompiler.java b/src/main/java/cod/compiler/natives/ControlFlowCompiler.java deleted file mode 100644 index 6a6b5975..00000000 --- a/src/main/java/cod/compiler/natives/ControlFlowCompiler.java +++ /dev/null @@ -1,63 +0,0 @@ -package cod.compiler.natives; - -import cod.compiler.MTOTNativeCompiler; -import cod.compiler.RegisterManager; - -public class ControlFlowCompiler { - private final MTOTNativeCompiler compiler; - private final cod.compiler.natives.OperandStack operandStack; - private final RegisterManager.RegisterAllocator registerAllocator; - private final RegisterManager.RegisterSpiller spiller; - - public ControlFlowCompiler(MTOTNativeCompiler compiler) { - this.compiler = compiler; - this.operandStack = compiler.operandStack; - this.registerAllocator = compiler.registerManager.getAllocator(); - this.spiller = compiler.registerManager.getSpiller(); - } - - public void compileJmp(String label) { - if (label == null) { - cod.debug.DebugSystem.error("MTOT", "JMP target label is null!"); - return; - } - compiler.assemblyCode.add(" " + compiler.cpuProfile.getPattern("jmp").assemblyTemplate.get(0).replace("{label}", label)); - } - - public void compileJmpIfFalse(String label) { - if (label == null) { - cod.debug.DebugSystem.error("MTOT", "JMP_IF_FALSE target label is null!"); - return; - } - String c = operandStack.popToRegister(); - spiller.fillRegister(c); - for (String t : compiler.cpuProfile.getPattern("jmp_if_false").assemblyTemplate) { - compiler.assemblyCode.add(" " + t.replace("{condition}", c).replace("{label}", label)); - } - registerAllocator.freeRegister(c); - } - - public void compileJmpIfTrue(String label) { - if (label == null) { - cod.debug.DebugSystem.error("MTOT", "JMP_IF_TRUE target label is null!"); - return; - } - String c = operandStack.popToRegister(); - spiller.fillRegister(c); - cod.compiler.MTOTRegistry.InstructionPattern p = compiler.cpuProfile.getPattern("jmp_if_true"); - if (p == null) { - if (compiler.cpuProfile.architecture.equals("aarch64")) { - compiler.assemblyCode.add(" cmp " + c + ", #0"); - compiler.assemblyCode.add(" b.ne " + label); - } else { - compiler.assemblyCode.add(" test " + c + ", " + c); - compiler.assemblyCode.add(" jnz " + label); - } - } else { - for (String t : p.assemblyTemplate) { - compiler.assemblyCode.add(" " + t.replace("{condition}", c).replace("{label}", label)); - } - } - registerAllocator.freeRegister(c); - } -} \ No newline at end of file diff --git a/src/main/java/cod/compiler/natives/MemoryAccessCompiler.java b/src/main/java/cod/compiler/natives/MemoryAccessCompiler.java deleted file mode 100644 index 33d283e7..00000000 --- a/src/main/java/cod/compiler/natives/MemoryAccessCompiler.java +++ /dev/null @@ -1,253 +0,0 @@ -package cod.compiler.natives; - -import cod.compiler.MTOTNativeCompiler; -import cod.compiler.RegisterManager; - -import cod.debug.DebugSystem; - -import java.util.*; - -public class MemoryAccessCompiler { - private final MTOTNativeCompiler compiler; - private final cod.compiler.natives.OperandStack operandStack; - private final RegisterManager.RegisterAllocator registerAllocator; - private final RegisterManager.RegisterSpiller spiller; - - private Map fieldOffsets = new HashMap(); - private int nextFieldOffset = 0; - private Map slotLocations = new HashMap(); - private Map stringLiteralLabels = new HashMap(); - - public MemoryAccessCompiler(MTOTNativeCompiler compiler) { - this.compiler = compiler; - this.operandStack = compiler.operandStack; - this.registerAllocator = compiler.registerManager.getAllocator(); - this.spiller = compiler.registerManager.getSpiller(); - } - - public void reset() { - fieldOffsets.clear(); - nextFieldOffset = 0; - slotLocations.clear(); - stringLiteralLabels.clear(); - } - - public Map getSlotLocations() { - return new HashMap(slotLocations); - } - - private int getFieldOffset(String fieldName) { - if (!fieldOffsets.containsKey(fieldName)) { - fieldOffsets.put(fieldName, nextFieldOffset); - cod.debug.DebugSystem.debug("MTOT_FIELDS", "Assigned offset " + nextFieldOffset + " to field '" + fieldName + "'"); - nextFieldOffset += 8; - } - return fieldOffsets.get(fieldName).intValue(); - } - - public void compilePushInt(int value) { - String r = registerAllocator.allocateRegister(); - cod.compiler.MTOTRegistry.InstructionPattern p = compiler.cpuProfile.getPattern("load_immediate_int"); - compiler.assemblyCode.add(" " + p.assemblyTemplate.get(0).replace("{dest}", r).replace("{value}", String.valueOf(value))); - operandStack.pushFromRegister(r); - spiller.markRegisterModified(r); - } - - public void compilePushFloat(float value) { - // Create float constant in data section - String label = compiler.generateDataLabel("float"); - String directive = compiler.cpuProfile.syntax.floatDirective - .replace("{label}", label) - .replace("{value}", String.valueOf(value)); - compiler.dataSection.add(directive); - - // Allocate register for the float value - String r = registerAllocator.allocateRegister(); - - // Load the actual float value from memory - if (compiler.cpuProfile.architecture.equals("x86_64")) { - // x86_64: Use SSE registers for floats (simplified - load into general purpose register) - // For proper float handling, we'd need XMM registers, but for now load as integer - compiler.assemblyCode.add(" mov " + r + ", [" + label + "] " + compiler.cpuProfile.syntax.commentMarker + " Load float value (as integer)"); - } else { - // AArch64: Load float value - compiler.assemblyCode.add(" ldr " + r + ", [" + label + "] " + compiler.cpuProfile.syntax.commentMarker + " Load float value"); - } - - operandStack.pushFromRegister(r); - spiller.markRegisterModified(r); - - DebugSystem.debug("MTOT_FLOAT", "Pushed float value " + value + " (label: " + label + ")"); -} - - public void compilePushString(String str) { - String label; - if (stringLiteralLabels.containsKey(str)) { - label = stringLiteralLabels.get(str); - cod.debug.DebugSystem.debug("MTOT_STR", "Reusing string literal label '" + label + "' for: \"" + compiler.escapeString(str) + "\""); - } else { - label = compiler.generateDataLabel("str"); - String directive = compiler.cpuProfile.syntax.stringDirective.replace("{label}", label).replace("{value}", compiler.escapeString(str)); - compiler.dataSection.add(directive); - stringLiteralLabels.put(str, label); - cod.debug.DebugSystem.debug("MTOT_STR", "Created string literal label '" + label + "' for: \"" + compiler.escapeString(str) + "\""); - } - String r = registerAllocator.allocateRegister(); - for(String t : compiler.cpuProfile.getPattern("load_address").assemblyTemplate) { - compiler.assemblyCode.add(" "+t.replace("{dest}",r).replace("{label}",label)); - } - operandStack.pushFromRegister(r); - spiller.markRegisterModified(r); - } - - public void compilePushNull() { - String r = registerAllocator.allocateRegister(); - cod.compiler.MTOTRegistry.InstructionPattern p = compiler.cpuProfile.getPattern("load_immediate_int"); - compiler.assemblyCode.add(" " + p.assemblyTemplate.get(0).replace("{dest}", r).replace("{value}", "0")); - operandStack.pushFromRegister(r); - spiller.markRegisterModified(r); - } - - public void compilePop() { - if (!operandStack.isEmpty()) { - String r = operandStack.popToRegister(); - registerAllocator.freeRegister(r); - cod.debug.DebugSystem.debug("MTOT_REG", "POP freed register: " + r); - } else { - cod.debug.DebugSystem.warn("MTOT", "POP on empty stack."); - } - } - - public void compileDup() { - String r = operandStack.peek(); - if (r != null) { - spiller.fillRegister(r); - String n = registerAllocator.allocateRegister(Collections.singleton(r)); - compiler.assemblyCode.add(" " + compiler.cpuProfile.getPattern("move_reg").assemblyTemplate.get(0).replace("{dest}", n).replace("{src}", r)); - operandStack.pushFromRegister(n); - spiller.markRegisterModified(n); - } else { - throw new RuntimeException("DUP on empty stack."); - } - } - - public void compileSwap() { - String r1 = operandStack.popToRegister(); - String r2 = operandStack.popToRegister(); - operandStack.pushFromRegister(r1); - operandStack.pushFromRegister(r2); - cod.debug.DebugSystem.debug("MTOT_REG", "SWAPped " + r1 + " and " + r2 + " on stack"); - } - - public void compileLoadField(String fieldName) { - int offset = getFieldOffset(fieldName); - String destReg = registerAllocator.allocateRegister(Collections.singleton(compiler.thisRegister)); - spiller.fillRegister(compiler.thisRegister); - cod.compiler.MTOTRegistry.InstructionPattern pattern = compiler.cpuProfile.getPattern("load_field_offset"); - if (pattern == null) throw new RuntimeException("Missing 'load_field_offset' pattern!"); - String asm = pattern.assemblyTemplate.get(0) - .replace("{dest_reg}", destReg) - .replace("{base_reg}", compiler.thisRegister) - .replace("{offset}", String.valueOf(offset)); - compiler.assemblyCode.add(" " + compiler.cpuProfile.syntax.commentMarker + " Load field " + fieldName + " (offset " + offset + ")"); - compiler.assemblyCode.add(" " + asm); - operandStack.pushFromRegister(destReg); - spiller.markRegisterModified(destReg); - } - - public void compileStoreField(String fieldName) { - int offset = getFieldOffset(fieldName); - String valueReg = operandStack.popToRegister(); - spiller.fillRegister(compiler.thisRegister); - spiller.fillRegister(valueReg); - cod.compiler.MTOTRegistry.InstructionPattern pattern = compiler.cpuProfile.getPattern("store_field_offset"); - if (pattern == null) throw new RuntimeException("Missing 'store_field_offset' pattern!"); - String asm = pattern.assemblyTemplate.get(0) - .replace("{src_reg}", valueReg) - .replace("{base_reg}", compiler.thisRegister) - .replace("{offset}", String.valueOf(offset)); - compiler.assemblyCode.add(" " + compiler.cpuProfile.syntax.commentMarker + " Store field " + fieldName + " (offset " + offset + ")"); - compiler.assemblyCode.add(" " + asm); - registerAllocator.freeRegister(valueReg); - } - - public void compileStoreLocal(int slotIndex, Map localRegisterMap) { - String valueReg = operandStack.popToRegister(); - String varRegKey = "local_" + slotIndex; - String targetReg = localRegisterMap.get(varRegKey); - - if (targetReg == null) { - targetReg = registerAllocator.allocateRegister(Collections.singleton(valueReg)); - localRegisterMap.put(varRegKey, targetReg); - spiller.mapSlotToRegister(slotIndex, targetReg); - cod.debug.DebugSystem.debug("MTOT_REG", "Allocated register " + targetReg + " for " + varRegKey + " during STORE"); - } else { - spiller.fillRegister(targetReg); - } - - spiller.fillRegister(valueReg); - - if (!targetReg.equals(valueReg)) { - String asm = compiler.cpuProfile.getPattern("move_reg").assemblyTemplate.get(0) - .replace("{dest}", targetReg) - .replace("{src}", valueReg); - compiler.assemblyCode.add(" " + asm); - registerAllocator.freeRegister(valueReg); - } - - spiller.markRegisterModified(targetReg); - spiller.updateRegisterDefinitionDepth(targetReg, compiler.getCurrentLoopDepth()); - spiller.trackRegisterUsage(targetReg); - } - - public void compileLoadLocal(int slotIndex, Map localRegisterMap) { - String varRegKey = "local_" + slotIndex; - String varReg = localRegisterMap.get(varRegKey); - - if (varReg == null) { - cod.debug.DebugSystem.error("MTOT_REG", "Register for " + varRegKey + " is null during LOAD! Recovering by allocating new register and assuming value 0."); - varReg = registerAllocator.allocateRegister(); - localRegisterMap.put(varRegKey, varReg); - spiller.mapSlotToRegister(slotIndex, varReg); - cod.compiler.MTOTRegistry.InstructionPattern p = compiler.cpuProfile.getPattern("load_immediate_int"); - compiler.assemblyCode.add(" " + p.assemblyTemplate.get(0).replace("{dest}", varReg).replace("{value}", "0") + " " + compiler.cpuProfile.syntax.commentMarker + " WARNING: Used uninitialized local slot " + slotIndex); - spiller.markRegisterModified(varReg); - spiller.updateRegisterDefinitionDepth(varReg, compiler.getCurrentLoopDepth()); - } else { - spiller.fillRegister(varReg); - } - - String tempReg = registerAllocator.allocateRegister(Collections.singleton(varReg)); - String asm = compiler.cpuProfile.getPattern("move_reg").assemblyTemplate.get(0) - .replace("{dest}", tempReg) - .replace("{src}", varReg); - compiler.assemblyCode.add(" " + asm); - - operandStack.pushFromRegister(tempReg); - spiller.markRegisterModified(tempReg); - spiller.trackRegisterUsage(varReg); - } - - public void compileStoreSlot(Object operand, Map localRegisterMap) { - if (operand instanceof Integer) { - int slotIndex = ((Integer) operand).intValue(); - compileStoreLocal(slotIndex, localRegisterMap); - String location = localRegisterMap.get("local_" + slotIndex); - if (location != null) { - slotLocations.put(Integer.valueOf(slotIndex), location); - cod.debug.DebugSystem.debug("MTOT_SLOT", "Recorded register location for slot index " + slotIndex + ": " + location); - } else { - cod.debug.DebugSystem.warn("MTOT_SLOT", "Cannot determine register location for slot index: " + slotIndex); - int offset = spiller.getSpillOffsetForSlotIndex(slotIndex); - if (offset != Integer.MIN_VALUE) { - slotLocations.put(Integer.valueOf(slotIndex), "spill_" + offset); - cod.debug.DebugSystem.debug("MTOT_SLOT", "Recorded spill location for slot index " + slotIndex + ": spill_" + offset); - } else { - cod.debug.DebugSystem.warn("MTOT_SLOT", "Cannot determine any location for slot index: " + slotIndex); - } - } - } else { - cod.debug.DebugSystem.error("MTOT", "STORE_SLOT operand is not an Integer index: " + operand); - } - } -} \ No newline at end of file diff --git a/src/main/java/cod/compiler/natives/MethodCallCompiler.java b/src/main/java/cod/compiler/natives/MethodCallCompiler.java deleted file mode 100644 index 8a5867ee..00000000 --- a/src/main/java/cod/compiler/natives/MethodCallCompiler.java +++ /dev/null @@ -1,306 +0,0 @@ -package cod.compiler.natives; - -import cod.compiler.MTOTNativeCompiler; -import cod.compiler.RegisterManager; -import cod.compiler.BytecodeInstruction; -import java.util.*; - -public class MethodCallCompiler { - private final MTOTNativeCompiler compiler; - private final cod.compiler.natives.OperandStack operandStack; - private final RegisterManager.RegisterAllocator registerAllocator; - private final RegisterManager.RegisterSpiller spiller; - - public MethodCallCompiler(MTOTNativeCompiler compiler) { - this.compiler = compiler; - this.operandStack = compiler.operandStack; - this.registerAllocator = compiler.registerManager.getAllocator(); - this.spiller = compiler.registerManager.getSpiller(); - } - - public void compileCall(BytecodeInstruction instr, java.util.List bytecode, int index, java.util.Map localRegisterMap) { - String methodName = (String) instr.operand; - int argCount = countArgsForCall(bytecode, index); - cod.debug.DebugSystem.debug("MTOT", "Compiling call to " + methodName + " with " + argCount + " arguments."); - - java.util.List argValueRegs = new java.util.ArrayList(); - for (int i = 0; i < argCount; i++) { - argValueRegs.add(operandStack.popToRegister()); - } - java.util.Collections.reverse(argValueRegs); - - java.util.Set excludeArgs = new java.util.HashSet(); - excludeArgs.add(compiler.argumentRegisters.get(0)); - java.util.List abiArgsUsedForParams = new java.util.ArrayList(); - - int argStartIndex = (compiler.cpuProfile.architecture.equals("x86_64")) ? 0 : 1; - - for (int i = 0; i < argCount; i++) { - int abiArgIndex = i + argStartIndex; - if (abiArgIndex < compiler.argumentRegisters.size()) { - String abiArgReg = compiler.argumentRegisters.get(abiArgIndex); - if (compiler.cpuProfile.architecture.equals("x86_64") && abiArgReg.equals(cod.compiler.MTOTRegistry.x86_64Registers.rax)) { - cod.debug.DebugSystem.warn("MTOT", "x86 ABI does not use rax for arguments. Skipping arg " + i); - abiArgIndex++; - if (abiArgIndex >= compiler.argumentRegisters.size()) continue; - abiArgReg = compiler.argumentRegisters.get(abiArgIndex); - } - excludeArgs.add(abiArgReg); - abiArgsUsedForParams.add(abiArgReg); - } - } - - java.util.Set regsToSave = compiler.spillCallerSavedRegisters(excludeArgs); - - for (int i = 0; i < argCount; i++) { - if (i < abiArgsUsedForParams.size()) { - String abiArgReg = abiArgsUsedForParams.get(i); - String valueReg = argValueRegs.get(i); - spiller.fillRegister(valueReg); - registerAllocator.markRegisterUsed(abiArgReg); - if (!abiArgReg.equals(valueReg)) { - compiler.assemblyCode.add(" mov " + abiArgReg + ", " + valueReg + " " + compiler.cpuProfile.syntax.commentMarker + " Setup arg " + (i + 1)); - } - } else { - cod.debug.DebugSystem.warn("MTOT", "Argument " + i + " stack passing not implemented for call to " + methodName); - } - } - - String thisAbiArgReg = (compiler.cpuProfile.architecture.equals("x86_64")) ? - cod.compiler.MTOTRegistry.x86_64Registers.rdi : compiler.argumentRegisters.get(0); - spiller.fillRegister(compiler.thisRegister); - registerAllocator.markRegisterUsed(thisAbiArgReg); - if (!thisAbiArgReg.equals(compiler.thisRegister)) { - compiler.assemblyCode.add(" mov " + thisAbiArgReg + ", " + compiler.thisRegister + " " + compiler.cpuProfile.syntax.commentMarker + " Setup 'this' pointer"); - } - - for (int i = 0; i < argValueRegs.size(); i++) { - String valueReg = argValueRegs.get(i); - boolean isUsedForArg = (i < abiArgsUsedForParams.size() && valueReg.equals(abiArgsUsedForParams.get(i))); - if (!isUsedForArg) { - registerAllocator.freeRegister(valueReg); - } - } - - String callAsm = compiler.cpuProfile.getPattern("call").assemblyTemplate.get(0).replace("{name}", methodName); - compiler.assemblyCode.add(" " + callAsm); - - compiler.fillCallerSavedRegisters(regsToSave); - - for (String reg : abiArgsUsedForParams) { - registerAllocator.freeRegister(reg); - } - - String returnAbiReg = (compiler.cpuProfile.architecture.equals("x86_64")) ? - cod.compiler.MTOTRegistry.x86_64Registers.rax : compiler.argumentRegisters.get(0); - - java.util.Set avoidReturnReg = java.util.Collections.singleton(returnAbiReg); - String resultReg = registerAllocator.allocateRegister(avoidReturnReg); - compiler.assemblyCode.add(" mov " + resultReg + ", " + returnAbiReg + " " + compiler.cpuProfile.syntax.commentMarker + " Get result"); - operandStack.pushFromRegister(resultReg); - spiller.markRegisterModified(resultReg); - - if (!resultReg.equals(returnAbiReg)) { - registerAllocator.freeRegister(returnAbiReg); - } - } - - public void compileCallSlots(BytecodeInstruction instr, java.util.List bytecode, int index, java.util.Map localRegisterMap) { - Object[] operand = (Object[]) instr.operand; - String methodName = (String) operand[0]; - int numSlots = ((Integer) operand[1]).intValue(); - int argCount = countArgsForCall(bytecode, index); - cod.debug.DebugSystem.debug("MTOT", "Compiling CALL_SLOTS to " + methodName + " with " + argCount + " arguments, expecting " + numSlots + " slots."); - - java.util.List argValueRegs = new java.util.ArrayList(); - for (int i = 0; i < argCount; i++) { - argValueRegs.add(operandStack.popToRegister()); - } - java.util.Collections.reverse(argValueRegs); - - java.util.Set excludeArgs = new java.util.HashSet(); - - String thisAbiArgReg = (compiler.cpuProfile.architecture.equals("x86_64")) ? - cod.compiler.MTOTRegistry.x86_64Registers.rdi : compiler.argumentRegisters.get(0); - excludeArgs.add(thisAbiArgReg); - - java.util.List abiArgsUsedForParams = new java.util.ArrayList(); - - int argStartIndex = (compiler.cpuProfile.architecture.equals("x86_64")) ? 1 : 1; - - for (int i = 0; i < argCount; i++) { - int abiArgIndex = i + argStartIndex; - if (abiArgIndex < compiler.argumentRegisters.size()) { - String abiArgReg = compiler.argumentRegisters.get(abiArgIndex); - if (compiler.cpuProfile.architecture.equals("x86_64") && abiArgReg.equals(cod.compiler.MTOTRegistry.x86_64Registers.rax)) { - cod.debug.DebugSystem.warn("MTOT", "x86 ABI does not use rax for arguments. Skipping arg " + i); - abiArgIndex++; - if (abiArgIndex >= compiler.argumentRegisters.size()) continue; - abiArgReg = compiler.argumentRegisters.get(abiArgIndex); - } - excludeArgs.add(abiArgReg); - abiArgsUsedForParams.add(abiArgReg); - } - } - - java.util.Set regsToSave = compiler.spillCallerSavedRegisters(excludeArgs); - - for (int i = 0; i < argCount; i++) { - if (i < abiArgsUsedForParams.size()) { - String abiArgReg = abiArgsUsedForParams.get(i); - String valueReg = argValueRegs.get(i); - spiller.fillRegister(valueReg); - registerAllocator.markRegisterUsed(abiArgReg); - if (!abiArgReg.equals(valueReg)) { - compiler.assemblyCode.add(" mov " + abiArgReg + ", " + valueReg + " " + compiler.cpuProfile.syntax.commentMarker + " Setup arg " + (i + 1)); - } - } else { - cod.debug.DebugSystem.warn("MTOT", "Argument " + i + " stack passing not implemented for CALL_SLOTS to " + methodName); - } - } - - spiller.fillRegister(compiler.thisRegister); - registerAllocator.markRegisterUsed(thisAbiArgReg); - if (!thisAbiArgReg.equals(compiler.thisRegister)) { - compiler.assemblyCode.add(" mov " + thisAbiArgReg + ", " + compiler.thisRegister + " " + compiler.cpuProfile.syntax.commentMarker + " Setup 'this' pointer"); - } - - for (int i = 0; i < argValueRegs.size(); i++) { - String valueReg = argValueRegs.get(i); - boolean isUsedForArg = (i < abiArgsUsedForParams.size() && valueReg.equals(abiArgsUsedForParams.get(i))); - if (!isUsedForArg) { - registerAllocator.freeRegister(valueReg); - } - } - - String callAsm = compiler.cpuProfile.getPattern("call").assemblyTemplate.get(0).replace("{name}", methodName); - compiler.assemblyCode.add(" " + callAsm); - - compiler.fillCallerSavedRegisters(regsToSave); - - java.util.Set returnSlotRegs = new java.util.HashSet(); - - if (compiler.cpuProfile.architecture.equals("x86_64")) { - String ret0 = cod.compiler.MTOTRegistry.x86_64Registers.rax; - String ret1 = cod.compiler.MTOTRegistry.x86_64Registers.rdx; - returnSlotRegs.add(ret0); - if (numSlots > 1) returnSlotRegs.add(ret1); - - for (String reg : abiArgsUsedForParams) { - if (!reg.equals(ret0) && (numSlots < 2 || !reg.equals(ret1))) { - registerAllocator.freeRegister(reg); - } - } - if (!thisAbiArgReg.equals(ret0) && (numSlots < 2 || !thisAbiArgReg.equals(ret1))) { - registerAllocator.freeRegister(thisAbiArgReg); - } - } else { - for (int i = 0; i < numSlots && i < compiler.argumentRegisters.size(); i++) { - returnSlotRegs.add(compiler.argumentRegisters.get(i)); - } - for (String reg : abiArgsUsedForParams) { - if (!returnSlotRegs.contains(reg)) { - registerAllocator.freeRegister(reg); - } - } - if (!returnSlotRegs.contains(thisAbiArgReg)) { - registerAllocator.freeRegister(thisAbiArgReg); - } - } - - java.util.Set avoidReturnRegs = new java.util.HashSet(returnSlotRegs); - for (int i = 0; i < numSlots; i++) { - String returnRegAbi; - if (compiler.cpuProfile.architecture.equals("x86_64")) { - if (i == 0) returnRegAbi = cod.compiler.MTOTRegistry.x86_64Registers.rax; - else if (i == 1) returnRegAbi = cod.compiler.MTOTRegistry.x86_64Registers.rdx; - else { - cod.debug.DebugSystem.error("MTOT_SLOT", "Requested slot " + i + " but x86 only supports 2 return registers (rax, rdx)!"); - returnRegAbi = null; - } - } else { - if (i >= compiler.argumentRegisters.size()) { - cod.debug.DebugSystem.error("MTOT_SLOT", "Requested slot " + i + " but only " + compiler.argumentRegisters.size() + " return registers exist!"); - returnRegAbi = null; - } else { - returnRegAbi = compiler.argumentRegisters.get(i); - } - } - - if (returnRegAbi == null) { - String errReg = registerAllocator.allocateRegister(avoidReturnRegs); - cod.compiler.MTOTRegistry.InstructionPattern p = compiler.cpuProfile.getPattern("load_immediate_int"); - compiler.assemblyCode.add(" " + p.assemblyTemplate.get(0).replace("{dest}", errReg).replace("{value}", "0") + " " + compiler.cpuProfile.syntax.commentMarker + " ERROR: Missing return slot " + i); - operandStack.pushFromRegister(errReg); - spiller.markRegisterModified(errReg); - avoidReturnRegs.add(errReg); - continue; - } - - String resultReg = registerAllocator.allocateRegister(avoidReturnRegs); - compiler.assemblyCode.add(" mov " + resultReg + ", " + returnRegAbi + " " + compiler.cpuProfile.syntax.commentMarker + " Get result slot " + i); - operandStack.pushFromRegister(resultReg); - spiller.markRegisterModified(resultReg); - - if (!resultReg.equals(returnRegAbi)) { - registerAllocator.freeRegister(returnRegAbi); - } - avoidReturnRegs.remove(returnRegAbi); - avoidReturnRegs.add(resultReg); - } - } - - private int countArgsForCall(java.util.List bytecode, int callInstructionIndex) { - int required_depth = 0; - int max_required_depth = 0; - for (int i = callInstructionIndex - 1; i >= 0; i--) { - BytecodeInstruction instr = bytecode.get(i); - BytecodeInstruction.Opcode op = instr.opcode; - int pops = 0; - int pushes = 0; - - if ((op.ordinal() >= BytecodeInstruction.Opcode.PUSH_INT.ordinal() && op.ordinal() <= BytecodeInstruction.Opcode.PUSH_NULL.ordinal()) - || op == BytecodeInstruction.Opcode.LOAD_LOCAL - || op == BytecodeInstruction.Opcode.LOAD_FIELD - || op == BytecodeInstruction.Opcode.ARRAY_NEW - || op == BytecodeInstruction.Opcode.READ_INPUT - || op == BytecodeInstruction.Opcode.CALL) { - pushes = 1; - } - else if (op == BytecodeInstruction.Opcode.DUP) { pops = 1; pushes = 2; } - else if (op == BytecodeInstruction.Opcode.SWAP) { pops = 2; pushes = 2; } - else if (op == BytecodeInstruction.Opcode.CALL_SLOTS) { Object[] callOperand = (Object[]) instr.operand; pushes = ((Integer) callOperand[1]).intValue(); } - else if ((op.ordinal() >= BytecodeInstruction.Opcode.ADD_INT.ordinal() && op.ordinal() <= BytecodeInstruction.Opcode.CMP_GE_INT.ordinal()) - || op == BytecodeInstruction.Opcode.CONCAT_STRING - || op == BytecodeInstruction.Opcode.ARRAY_LOAD) { - pops = 2; pushes = 1; - } else if (op == BytecodeInstruction.Opcode.NEG_INT - || op == BytecodeInstruction.Opcode.INT_TO_STRING) { - pops = 1; pushes = 1; - } - else if (op == BytecodeInstruction.Opcode.ARRAY_STORE) { pops = 3; } - else if (op == BytecodeInstruction.Opcode.POP - || op == BytecodeInstruction.Opcode.PRINT - || op == BytecodeInstruction.Opcode.STORE_LOCAL - || op == BytecodeInstruction.Opcode.STORE_FIELD - || op == BytecodeInstruction.Opcode.STORE_SLOT - || op == BytecodeInstruction.Opcode.JMP_IF_FALSE - || op == BytecodeInstruction.Opcode.JMP_IF_TRUE) { - pops = 1; - } - else if (op == BytecodeInstruction.Opcode.JMP - || op == BytecodeInstruction.Opcode.LABEL - || op == BytecodeInstruction.Opcode.RET) { - break; - } - - required_depth -= pushes; - required_depth += pops; - if (required_depth < 0) { - max_required_depth++; - required_depth = 0; - } - } - return Math.max(0, max_required_depth); - } -} \ No newline at end of file diff --git a/src/main/java/cod/compiler/natives/OperandStack.java b/src/main/java/cod/compiler/natives/OperandStack.java deleted file mode 100644 index 5fccbfc1..00000000 --- a/src/main/java/cod/compiler/natives/OperandStack.java +++ /dev/null @@ -1,60 +0,0 @@ -package cod.compiler.natives; - -import cod.compiler.MTOTNativeCompiler; -import cod.compiler.RegisterManager; - -public class OperandStack { - private final java.util.Stack stack = new java.util.Stack(); - private final MTOTNativeCompiler compiler; - private final RegisterManager.RegisterAllocator registerAllocator; - private final RegisterManager.RegisterSpiller spiller; - - public OperandStack(MTOTNativeCompiler compiler) { - this.compiler = compiler; - this.registerAllocator = compiler.registerManager.getAllocator(); - this.spiller = compiler.registerManager.getSpiller(); - } - - public String popToRegister() { - if (stack.isEmpty()) { - cod.debug.DebugSystem.warn("OPERAND_STACK", "Popping from empty stack - allocating new register"); - String reg = registerAllocator.allocateRegister(); - spiller.updateRegisterDefinitionDepth(reg, compiler.getCurrentLoopDepth()); - return reg; - } - return stack.pop(); - } - - public void pushFromRegister(String reg) { - stack.push(reg); - spiller.updateRegisterDefinitionDepth(reg, compiler.getCurrentLoopDepth()); - } - - public String peek() { - return stack.isEmpty() ? null : stack.peek(); - } - - public boolean isEmpty() { - return stack.isEmpty(); - } - - public int size() { - return stack.size(); - } - - public void clear() { - for (String reg : stack) { - if (reg != null) { - registerAllocator.freeRegister(reg); - } - } - stack.clear(); - } - - public String[] popTwoOperands() { - String[] operands = new String[2]; - operands[1] = popToRegister(); - operands[0] = popToRegister(); - return operands; - } -} \ No newline at end of file diff --git a/src/main/java/cod/compiler/natives/OperationCompiler.java b/src/main/java/cod/compiler/natives/OperationCompiler.java deleted file mode 100644 index b86da6b3..00000000 --- a/src/main/java/cod/compiler/natives/OperationCompiler.java +++ /dev/null @@ -1,82 +0,0 @@ -package cod.compiler.natives; - -import cod.compiler.MTOTNativeCompiler; -import cod.compiler.RegisterManager; -import java.util.*; - -public class OperationCompiler { - private final MTOTNativeCompiler compiler; - private final cod.compiler.natives.OperandStack operandStack; - private final RegisterManager.RegisterAllocator registerAllocator; - private final RegisterManager.RegisterSpiller spiller; - - public OperationCompiler(MTOTNativeCompiler compiler) { - this.compiler = compiler; - this.operandStack = compiler.operandStack; - this.registerAllocator = compiler.registerManager.getAllocator(); - this.spiller = compiler.registerManager.getSpiller(); - } - - public void generateBinaryOp(String patternName) { - cod.compiler.MTOTRegistry.InstructionPattern p = compiler.cpuProfile.getPattern(patternName); - if (p == null) throw new RuntimeException("No pattern for " + patternName); - - String[] ops = operandStack.popTwoOperands(); - String l = ops[0], r = ops[1]; - Set avoid = new HashSet(Arrays.asList(l, r)); - String res = registerAllocator.allocateRegister(avoid); - - spiller.fillRegister(l); - spiller.fillRegister(r); - - Set clobberedRegs = new HashSet(); - if (p.requiredRegisters != null) { - for (String clobbered : p.requiredRegisters) { - if (clobbered.equals("al")) clobbered = cod.compiler.MTOTRegistry.x86_64Registers.rax; - registerAllocator.markRegisterUsed(clobbered); - clobberedRegs.add(clobbered); - } - } - - for (String t : p.assemblyTemplate) { - String asm = t.replace("{dest}", res).replace("{src1}", l).replace("{src2}", r); - compiler.assemblyCode.add(" " + asm); - } - - for (String clobbered : clobberedRegs) { - if (patternName.equals("mod_int") && clobbered.equals(cod.compiler.MTOTRegistry.x86_64Registers.rax)) { - registerAllocator.freeRegister(clobbered); - } - else if (patternName.equals("div_int") && clobbered.equals(cod.compiler.MTOTRegistry.x86_64Registers.rdx)) { - registerAllocator.freeRegister(clobbered); - } - else if (!clobbered.equals(res)) { - registerAllocator.freeRegister(clobbered); - } - } - - operandStack.pushFromRegister(res); - spiller.markRegisterModified(res); - registerAllocator.freeRegister(l); - registerAllocator.freeRegister(r); - } - - public void generateUnaryOp(String patternName) { - cod.compiler.MTOTRegistry.InstructionPattern pattern = compiler.cpuProfile.getPattern(patternName); - if (pattern == null) { throw new RuntimeException("No pattern for unary op: " + patternName); } - - String operandReg = operandStack.popToRegister(); - String resultReg = registerAllocator.allocateRegister(Collections.singleton(operandReg)); - - spiller.fillRegister(operandReg); - - for (String template : pattern.assemblyTemplate) { - String asm = template.replace("{dest}", resultReg).replace("{src}", operandReg); - compiler.assemblyCode.add(" " + asm); - } - - operandStack.pushFromRegister(resultReg); - spiller.markRegisterModified(resultReg); - registerAllocator.freeRegister(operandReg); - } -} \ No newline at end of file diff --git a/src/main/java/cod/compiler/natives/RuntimeCallHelper.java b/src/main/java/cod/compiler/natives/RuntimeCallHelper.java deleted file mode 100644 index ef7ca566..00000000 --- a/src/main/java/cod/compiler/natives/RuntimeCallHelper.java +++ /dev/null @@ -1,121 +0,0 @@ -package cod.compiler.natives; - -import cod.compiler.MTOTNativeCompiler; -import cod.compiler.RegisterManager; -import java.util.*; - -public class RuntimeCallHelper { - private final MTOTNativeCompiler compiler; - private final cod.compiler.natives.OperandStack operandStack; - private final RegisterManager.RegisterAllocator registerAllocator; - private final RegisterManager.RegisterSpiller spiller; - - public RuntimeCallHelper(MTOTNativeCompiler compiler) { - this.compiler = compiler; - this.operandStack = compiler.operandStack; - this.registerAllocator = compiler.registerManager.getAllocator(); - this.spiller = compiler.registerManager.getSpiller(); - } - - public void compileRuntimeCall(String functionName, int argCount, boolean hasReturnValue) { - List argValueRegs = new ArrayList(); - Set excludeArgs = new HashSet(); - List abiArgsUsed = new ArrayList(); - - int argStartIndex = (compiler.cpuProfile.architecture.equals("x86_64")) ? 0 : 0; - - for (int i = 0; i < argCount; i++) { - if (i + argStartIndex >= compiler.argumentRegisters.size()) { - cod.debug.DebugSystem.error("MTOT", "Runtime call " + functionName + " needs arg " + i + " but ABI only has " + compiler.argumentRegisters.size()); - break; - } - argValueRegs.add(operandStack.popToRegister()); - String abiArg = compiler.argumentRegisters.get(i + argStartIndex); - excludeArgs.add(abiArg); - abiArgsUsed.add(abiArg); - } - Collections.reverse(argValueRegs); - - Set regsToSave = compiler.spillCallerSavedRegisters(excludeArgs); - - for (int i = 0; i < argValueRegs.size(); i++) { - String valueReg = argValueRegs.get(i); - String abiArg = abiArgsUsed.get(i); - spiller.fillRegister(valueReg); - registerAllocator.markRegisterUsed(abiArg); - if (!abiArg.equals(valueReg)) { - compiler.assemblyCode.add(" mov " + abiArg + ", " + valueReg); - } - } - - String callAsm = compiler.cpuProfile.getPattern("call").assemblyTemplate.get(0).replace("{name}", functionName); - compiler.assemblyCode.add(" " + callAsm + " " + compiler.cpuProfile.syntax.commentMarker + " Call runtime helper"); - - compiler.fillCallerSavedRegisters(regsToSave); - - String returnAbiReg = (compiler.cpuProfile.architecture.equals("x86_64")) ? - cod.compiler.MTOTRegistry.x86_64Registers.rax : compiler.argumentRegisters.get(0); - - if (hasReturnValue) { - Set avoidReturnRegs = new HashSet(abiArgsUsed); - avoidReturnRegs.add(returnAbiReg); - - String resultReg = registerAllocator.allocateRegister(avoidReturnRegs); - compiler.assemblyCode.add(" mov " + resultReg + ", " + returnAbiReg + " " + compiler.cpuProfile.syntax.commentMarker + " Get result"); - operandStack.pushFromRegister(resultReg); - spiller.markRegisterModified(resultReg); - } - - for (int i = 0; i < abiArgsUsed.size(); i++) { - String abiArg = abiArgsUsed.get(i); - String valueReg = argValueRegs.get(i); - - if (!abiArg.equals(valueReg)) { - registerAllocator.freeRegister(valueReg); - } - - boolean neededForResult = hasReturnValue && abiArg.equals(returnAbiReg) && operandStack.peek() != null && operandStack.peek().equals(returnAbiReg); - if (!neededForResult) { - registerAllocator.freeRegister(abiArg); - } - } - } - - public void compileReadInput(String expectedType) { - String typeLabel = compiler.generateDataLabel("input_type"); - String directive = compiler.cpuProfile.syntax.stringDirective - .replace("{label}", typeLabel) - .replace("{value}", compiler.escapeString(expectedType)); - compiler.dataSection.add(directive); - - String abiArg0 = (compiler.cpuProfile.architecture.equals("x86_64")) ? - cod.compiler.MTOTRegistry.x86_64Registers.rdi : compiler.argumentRegisters.get(0); - - Set excludeArgs = new HashSet(); - excludeArgs.add(abiArg0); - - Set regsToSave = compiler.spillCallerSavedRegisters(excludeArgs); - - registerAllocator.markRegisterUsed(abiArg0); - cod.compiler.MTOTRegistry.InstructionPattern loadAddrPattern = compiler.cpuProfile.getPattern("load_address"); - for (String t : loadAddrPattern.assemblyTemplate) { - compiler.assemblyCode.add(" " + t.replace("{dest}", abiArg0).replace("{label}", typeLabel)); - } - - String callAsm = compiler.cpuProfile.getPattern("call").assemblyTemplate.get(0).replace("{name}", "runtime_read_input"); - compiler.assemblyCode.add(" " + callAsm + " " + compiler.cpuProfile.syntax.commentMarker + " Call runtime helper (expects type* in arg0)"); - - compiler.fillCallerSavedRegisters(regsToSave); - - String returnAbiReg = (compiler.cpuProfile.architecture.equals("x86_64")) ? - cod.compiler.MTOTRegistry.x86_64Registers.rax : compiler.argumentRegisters.get(0); - - Set avoidReturnRegs = new HashSet(Collections.singletonList(returnAbiReg)); - String resultReg = registerAllocator.allocateRegister(avoidReturnRegs); - compiler.assemblyCode.add(" mov " + resultReg + ", " + returnAbiReg + " " + compiler.cpuProfile.syntax.commentMarker + " Get input result"); - operandStack.pushFromRegister(resultReg); - spiller.markRegisterModified(resultReg); - - if (!resultReg.equals(returnAbiReg)) registerAllocator.freeRegister(returnAbiReg); - } -} diff --git a/src/main/java/cod/debug/DebugSystem.java b/src/main/java/cod/debug/DebugSystem.java index 92a6a8a6..96db85da 100644 --- a/src/main/java/cod/debug/DebugSystem.java +++ b/src/main/java/cod/debug/DebugSystem.java @@ -2,8 +2,8 @@ package cod.debug; import java.util.*; -import java.time.LocalTime; -import java.time.format.DateTimeFormatter; +import java.text.SimpleDateFormat; +import java.util.Date; public class DebugSystem { public enum Level { @@ -26,20 +26,19 @@ public int getLevel() { } private static Level currentLevel = Level.INFO; - private static Set enabledCategories = new HashSet<>(); + private static Set enabledCategories = new HashSet(); private static boolean showTimestamp = true; private static boolean showThread = false; - private static Map timers = new HashMap<>(); + private static Map timers = new HashMap(); + private static SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss.SSS"); static { - // Enable common categories by default enabledCategories.add("AST"); enabledCategories.add("METHODS"); enabledCategories.add("SLOTS"); enabledCategories.add("INTERPRETER"); } - // Configuration methods public static void setLevel(Level level) { currentLevel = level; } @@ -53,15 +52,13 @@ public static void disableCategory(String category) { } public static void enableAllCategories() { - enabledCategories.addAll( - Arrays.asList( - "AST", - "METHODS", - "SLOTS", - "FIELDS", - "EXPRESSIONS", - "INTERPRETER", - "MEMORY")); + enabledCategories.add("AST"); + enabledCategories.add("METHODS"); + enabledCategories.add("SLOTS"); + enabledCategories.add("FIELDS"); + enabledCategories.add("EXPRESSIONS"); + enabledCategories.add("INTERPRETER"); + enabledCategories.add("MEMORY"); } public static void setShowTimestamp(boolean show) { @@ -72,7 +69,6 @@ public static void setShowThread(boolean show) { showThread = show; } - // Logging methods public static void error(String category, String message) { log(Level.ERROR, category, message); } @@ -93,7 +89,6 @@ public static void trace(String category, String message) { log(Level.TRACE, category, message); } - // Specialized logging methods public static void methodEntry(String methodName, Map params) { if (shouldLog(Level.DEBUG, "METHODS")) { debug("METHODS", "→ " + methodName + "(" + params + ")"); @@ -118,7 +113,6 @@ public static void fieldUpdate(String fieldName, Object value) { } } - // Performance timing public static void startTimer(String name) { if (shouldLog(Level.DEBUG, "PERF")) { timers.put(name, System.currentTimeMillis()); @@ -135,7 +129,14 @@ public static void stopTimer(String name) { } } - // AST building helpers + public static long getTimerDuration(String name) { + Long start = timers.get(name); + if (start != null) { + return System.currentTimeMillis() - start; + } + return -1; + } + public static void astBuilding(String nodeType, String details) { if (shouldLog(Level.TRACE, "AST")) { trace("AST", "Building " + nodeType + ": " + details); @@ -148,27 +149,24 @@ public static void astComplete(String nodeType, String summary) { } } - // Private implementation private static void log(Level level, String category, String message) { if (shouldLog(level, category)) { StringBuilder logLine = new StringBuilder(); - // Timestamp if (showTimestamp) { logLine.append("[") - .append(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS"))) + .append(timeFormat.format(new Date())) .append("] "); } - // Level logLine.append("[").append(level).append("] "); - // Thread (optional) if (showThread) { - logLine.append("[").append(Thread.currentThread().getName()).append("] "); + logLine.append("[") + .append(Thread.currentThread().getName()) + .append("] "); } - // Category and message logLine.append(category).append(": ").append(message); System.out.println(logLine.toString()); @@ -178,4 +176,8 @@ private static void log(Level level, String category, String message) { private static boolean shouldLog(Level level, String category) { return level.getLevel() <= currentLevel.getLevel() && enabledCategories.contains(category); } -} + + public static Level getLevel() { + return currentLevel; + } +} \ No newline at end of file diff --git a/src/main/java/cod/debug/Linter.java b/src/main/java/cod/debug/Linter.java index 9e51fe27..3cf75f2b 100644 --- a/src/main/java/cod/debug/Linter.java +++ b/src/main/java/cod/debug/Linter.java @@ -1,6 +1,6 @@ package cod.debug; -import cod.ast.NamingValidator; +import cod.semantic.NamingValidator; import cod.ast.nodes.*; import java.util.ArrayList; import java.util.HashMap; @@ -19,6 +19,7 @@ public class Linter { private final List warnings; + private boolean completed; // --- State for Type-level checks --- private Set definedMethods; @@ -31,6 +32,7 @@ public class Linter { public Linter() { this.warnings = new ArrayList(); + this.completed = false; } /** @@ -40,39 +42,73 @@ public Linter() { */ public List lint(ProgramNode program) { warnings.clear(); - if (program.unit != null) { - visitUnit(program.unit); + completed = false; + + try { + if (program.unit != null) { + visitUnit(program.unit); + } + completed = true; + } catch (Exception e) { + System.err.println("Linting failed with error: " + e.getMessage()); + e.printStackTrace(); + completed = false; } + return warnings; } - - private void checkNamingConventions(TypeNode type) { - // Check type name - if (!NamingValidator.isPascalCase(type.name)) { - addWarning(type.name, "TYPE", "Type name '" + type.name + "' should use PascalCase"); + + /** + * Checks if the linting process completed successfully + * @return true if linting completed, false otherwise + */ + public boolean isCompleted() { + return completed; } - - // Check method names - for (MethodNode method : type.methods) { - if (!NamingValidator.startsWithLowerCase(method.name)) { - addWarning(type.name, method.name, "Method '" + method.name + "' should start with lowercase letter"); - } + + /** + * Gets the number of warnings found during linting + * @return number of warnings + */ + public int getWarningCount() { + return warnings.size(); } - - // Check field names - for (FieldNode field : type.fields) { - if (NamingValidator.isPascalCase(field.name)) { - addWarning(type.name, "FIELD", "Field '" + field.name + "' should not use PascalCase (reserved for classes)"); + + private void checkNamingConventions(TypeNode type) { + // --- FIX: Skip PascalCase check for synthetic types --- + if (type.name.equals("__MethodScript__") || + type.name.equals("__Script__") || + (type.name.startsWith("__") && type.name.endsWith("__"))) { + // Skip PascalCase check for synthetic/internal types + return; } - } - - // Check for ALL_CAPS fields that aren't actually constants - for (FieldNode field : type.fields) { - if (NamingValidator.isAllCaps(field.name) && field.value == null) { - addWarning(type.name, "FIELD", "Field '" + field.name + "' uses ALL_CAPS but has no initial value - is this meant to be a constant?"); + + // Check type name + if (!NamingValidator.isPascalCase(type.name)) { + addWarning(type.name, "TYPE", "Type name '" + type.name + "' should use PascalCase"); + } + + // Check method names + for (MethodNode method : type.methods) { + if (!NamingValidator.startsWithLowerCase(method.name)) { + addWarning(type.name, method.name, "Method '" + method.name + "' should start with lowercase letter"); + } + } + + // Check field names + for (FieldNode field : type.fields) { + if (NamingValidator.isPascalCase(field.name)) { + addWarning(type.name, "FIELD", "Field '" + field.name + "' should not use PascalCase (reserved for classes)"); + } + } + + // Check for ALL_CAPS fields that aren't actually constants + for (FieldNode field : type.fields) { + if (NamingValidator.isAllCaps(field.name) && field.value == null) { + addWarning(type.name, "FIELD", "Field '" + field.name + "' uses ALL_CAPS but has no initial value - is this meant to be a constant?"); + } } } -} private void visitUnit(UnitNode unit) { for (TypeNode type : unit.types) { @@ -81,44 +117,44 @@ private void visitUnit(UnitNode unit) { } private void visitType(TypeNode type) { - // Initialize sets for this type - this.definedMethods = new HashSet(); - this.calledMethods = new HashSet(); - this.methodMap = new HashMap(); - - // ADD THIS LINE: Check naming conventions - checkNamingConventions(type); - - // First pass: Find all defined methods in this type - for (MethodNode method : type.methods) { - definedMethods.add(method.name); - methodMap.put(method.name, method); - } - - // "main" is a special entry point, so it's always considered "used" - if (definedMethods.contains("main")) { - calledMethods.add("main"); - } + // Initialize sets for this type + this.definedMethods = new HashSet(); + this.calledMethods = new HashSet(); + this.methodMap = new HashMap(); + + // Check naming conventions + checkNamingConventions(type); + + // First pass: Find all defined methods in this type + for (MethodNode method : type.methods) { + definedMethods.add(method.name); + methodMap.put(method.name, method); + } + + // "main" is a special entry point, so it's always considered "used" + if (definedMethods.contains("main")) { + calledMethods.add("main"); + } - // Second pass: Visit all methods to find calls and analyze bodies - for (MethodNode method : type.methods) { - visitMethod(method, type.name); - } + // Second pass: Visit all methods to find calls and analyze bodies + for (MethodNode method : type.methods) { + visitMethod(method, type.name); + } - // Third pass: Analyze the results for this type - for (String methodName : definedMethods) { - if (!calledMethods.contains(methodName)) { - MethodNode method = methodMap.get(methodName); - if ("local".equals(method.visibility)) { - addWarning( - type.name, - methodName, - "Method '" + methodName + "' is 'local' and is never called." - ); + // Third pass: Analyze the results for this type + for (String methodName : definedMethods) { + if (!calledMethods.contains(methodName)) { + MethodNode method = methodMap.get(methodName); + if ("local".equals(method.visibility)) { + addWarning( + type.name, + methodName, + "Method '" + methodName + "' is 'local' and is never called." + ); + } } } } -} private void visitMethod(MethodNode method, String typeName) { // Initialize sets for this method @@ -137,7 +173,7 @@ private void visitMethod(MethodNode method, String typeName) { } // Visit all statements in the method body - for (StatementNode stmt : method.body) { + for (StmtNode stmt : method.body) { visitStatement(stmt); } @@ -166,7 +202,7 @@ private void visitMethod(MethodNode method, String typeName) { // --- Statement Visitor --- - private void visitStatement(StatementNode stmt) { + private void visitStatement(StmtNode stmt) { if (stmt == null) { return; } @@ -208,13 +244,13 @@ private void visitStatement(StatementNode stmt) { visitExpression(arg); } - } else if (stmt instanceof IfNode) { - IfNode ifNode = (IfNode) stmt; + } else if (stmt instanceof StmtIfNode) { + StmtIfNode ifNode = (StmtIfNode) stmt; visitExpression(ifNode.condition); - for (StatementNode s : ifNode.thenBlock.statements) { + for (StmtNode s : ifNode.thenBlock.statements) { visitStatement(s); } - for (StatementNode s : ifNode.elseBlock.statements) { + for (StmtNode s : ifNode.elseBlock.statements) { visitStatement(s); } @@ -230,7 +266,7 @@ private void visitStatement(StatementNode stmt) { visitExpression(forNode.range.step); // Body statements are visited - for (StatementNode s : forNode.body.statements) { + for (StmtNode s : forNode.body.statements) { visitStatement(s); } diff --git a/src/main/java/cod/error/LexError.java b/src/main/java/cod/error/LexError.java new file mode 100644 index 00000000..fa7679bd --- /dev/null +++ b/src/main/java/cod/error/LexError.java @@ -0,0 +1,8 @@ +package cod.error; + +public class LexError extends RuntimeException { + + public LexError(String message) { + super(message); + } +} diff --git a/src/main/java/cod/ast/error/ParseError.java b/src/main/java/cod/error/ParseError.java similarity index 72% rename from src/main/java/cod/ast/error/ParseError.java rename to src/main/java/cod/error/ParseError.java index fcf61d22..6979c32e 100644 --- a/src/main/java/cod/ast/error/ParseError.java +++ b/src/main/java/cod/error/ParseError.java @@ -1,4 +1,4 @@ -package cod.ast.error; +package cod.error; public class ParseError extends RuntimeException { private int line; @@ -13,4 +13,12 @@ public ParseError(String message, int line, int column) { public ParseError(String message) { super(message); } -} + + public int getLine() { + return line; + } + + public int getColumn() { + return column; + } +} \ No newline at end of file diff --git a/src/main/java/cod/interpreter/ExecutionContext.java b/src/main/java/cod/interpreter/ExecutionContext.java new file mode 100644 index 00000000..f84a20bf --- /dev/null +++ b/src/main/java/cod/interpreter/ExecutionContext.java @@ -0,0 +1,35 @@ +package cod.interpreter; + +import java.util.Map; +import java.util.Set; +import java.util.HashMap; +import java.util.HashSet; + +public class ExecutionContext { + public ObjectInstance objectInstance; + public Map locals; + public Map slotValues; + public Map slotTypes; + // NEW: Map to track the explicit type declared for local variables + public Map localTypes; + public Set slotsInCurrentPath; + + public ExecutionContext(ObjectInstance objectInstance, + Map locals, + Map slotValues, + Map slotTypes) { + this.objectInstance = objectInstance; + this.locals = locals; + this.slotValues = slotValues; + this.slotTypes = slotTypes; + + // Initialize the NEW map + this.localTypes = new HashMap(); + + this.slotsInCurrentPath = new HashSet(); + + if (slotValues != null && !slotValues.isEmpty()) { + this.slotsInCurrentPath.addAll(slotValues.keySet()); + } + } +} \ No newline at end of file diff --git a/src/main/java/cod/interpreter/ExpressionEvaluator.java b/src/main/java/cod/interpreter/ExpressionEvaluator.java deleted file mode 100644 index e4069409..00000000 --- a/src/main/java/cod/interpreter/ExpressionEvaluator.java +++ /dev/null @@ -1,395 +0,0 @@ -package cod.interpreter; - -import cod.ast.nodes.*; -import cod.ast.ASTFactory; -import cod.debug.DebugSystem; -import java.util.*; - -public class ExpressionEvaluator { - private TypeSystem typeSystem; - private Interpreter interpreter; - - public ExpressionEvaluator(TypeSystem typeSystem, Interpreter interpreter) { - this.typeSystem = typeSystem; - this.interpreter = interpreter; - } - - public Object evaluate(ExprNode expr, ObjectInstance obj, Map locals) { - DebugSystem.trace("EXPRESSIONS", "Evaluating: " + expr.getClass().getSimpleName()); - - // --- NEW: Handles 'if all:(A, B)' and 'if any: A, B' --- - if (expr instanceof BooleanChainNode) { - return evaluateBooleanChain((BooleanChainNode) expr, obj, locals); - } - // ... (UnaryNode, EqualityChainNode, TypeCastNode, ArrayNode, IndexAccessNode) ... - - if (expr instanceof UnaryNode) { - UnaryNode unary = (UnaryNode) expr; - Object operand = evaluate(unary.operand, obj, locals); - DebugSystem.debug("EXPRESSIONS", "Unary operation: " + unary.op + " " + operand); - - switch (unary.op) { - case "-": - return typeSystem.negateNumber(operand); - case "+": - return operand; - case "!": - return !isTruthy(operand); - default: - throw new RuntimeException("Unknown unary operator: " + unary.op); - } - } - - if (expr instanceof EqualityChainNode) { - return evaluateEqualityChain((EqualityChainNode) expr, obj, locals); - } - - if (expr instanceof TypeCastNode) { - TypeCastNode cast = (TypeCastNode) expr; - Object value = evaluate(cast.expression, obj, locals); - DebugSystem.debug("TYPECAST", "Casting " + value + " to " + cast.targetType); - return typeSystem.convertType(value, cast.targetType); - - } else if (expr instanceof ArrayNode) { - ArrayNode arrayNode = (ArrayNode) expr; - List array = new ArrayList(); - for (ExprNode elem : arrayNode.elements) { - array.add(evaluate(elem, obj, locals)); - } - DebugSystem.debug("ARRAYS", "Created array with " + array.size() + " elements"); - return array; - - } else if (expr instanceof IndexAccessNode) { - IndexAccessNode indexNode = (IndexAccessNode) expr; - Object arrayObj = evaluate(indexNode.array, obj, locals); - Object indexObj = evaluate(indexNode.index, obj, locals); - - if (arrayObj instanceof List && indexObj instanceof Integer) { - List list = (List) arrayObj; - int index = (Integer) indexObj; - - if (index >= 0 && index < list.size()) { - Object value = list.get(index); - DebugSystem.debug("ARRAYS", "Array access: [" + index + "] = " + value); - return value; - } else { - throw new RuntimeException( - "Array index out of bounds: " - + index - + " for array size " - + list.size()); - } - } else { - throw new RuntimeException( - "Invalid array access - array: " - + arrayObj.getClass() - + ", index: " - + indexObj.getClass()); - } - - } else if (expr instanceof BinaryOpNode) { - BinaryOpNode binOp = (BinaryOpNode) expr; - - boolean isForLoopStep = isForLoopStepEvaluation(binOp, obj, locals); - - if (isAssignmentOperator(binOp.op) && !isForLoopStep) { - throw new RuntimeException("Assignment operator '" + binOp.op + "' cannot be used in expressions - use statements instead"); - } - - Object left = evaluate(binOp.left, obj, locals); - Object right = evaluate(binOp.right, obj, locals); - DebugSystem.debug( - "EXPRESSIONS", "Binary operation: " + left + " " + binOp.op + " " + right); - - switch (binOp.op) { - case "+": - return (left instanceof String || right instanceof String) - ? String.valueOf(left) + right - : typeSystem.addNumbers(left, right); - case "-": - return typeSystem.subtractNumbers(left, right); - case "*": - return typeSystem.multiplyNumbers(left, right); - case "/": - return typeSystem.divideNumbers(left, right); - case "%": - return typeSystem.modulusNumbers(left, right); - case ">": - return typeSystem.compare(left, right) > 0; - case "<": - return typeSystem.compare(left, right) < 0; - case ">=": - return typeSystem.compare(left, right) >= 0; - case "<=": - return typeSystem.compare(left, right) <= 0; - case "==": - return left.equals(right); - case "!=": - return !left.equals(right); - case "=": - return right; - case "+=": - return typeSystem.addNumbers(left, right); - case "-=": - return typeSystem.subtractNumbers(left, right); - case "*=": - return typeSystem.multiplyNumbers(left, right); - case "/=": - return typeSystem.divideNumbers(left, right); - default: - throw new RuntimeException("Unknown operator: " + binOp.op); - } - } else if (expr instanceof MethodCallNode) { - MethodCallNode call = (MethodCallNode) expr; - - if (call.chainType != null && call.chainArguments != null) { - return evaluateConditionalChain(call, obj, locals); - } - - DebugSystem.debug("EXPR_METHOD_CALL", "=== EXPRESSION METHOD CALL ==="); - DebugSystem.debug("EXPR_METHOD_CALL", "call.name: " + call.name); - DebugSystem.debug("EXPR_METHOD_CALL", "call.qualifiedName: " + call.qualifiedName); - DebugSystem.debug("EXPR_METHOD_CALL", "call.slotNames: " + call.slotNames); - DebugSystem.debug("EXPR_METHOD_CALL", "call.arguments: " + call.arguments.size()); - for (int i = 0; i < call.arguments.size(); i++) { - DebugSystem.debug("EXPR_METHOD_CALL", " arg[" + i + "]: " + call.arguments.get(i).getClass().getSimpleName()); - } - - Object result = interpreter.evalMethodCall(call, obj, locals); - - if (call.slotNames != null && !call.slotNames.isEmpty() && result instanceof Map) { - Map slotReturns = (Map) result; - for (String slotName : call.slotNames) { - if (slotReturns.containsKey(slotName)) { - Object slotValue = slotReturns.get(slotName); - DebugSystem.debug( - "SLOTS", - "Slot '" + slotName + "' extracted in expression (read-only)"); - } - } - } - - return result; - } else if (expr.name != null) { - Object value = null; - if (locals.containsKey(expr.name)) { - value = locals.get(expr.name); - DebugSystem.trace("MEMORY", "Found local: " + expr.name + " = " + value); - } else if (interpreter.currentSlots != null - && interpreter.currentSlots.containsKey(expr.name)) { - value = interpreter.currentSlots.get(expr.name); - DebugSystem.trace("MEMORY", "Found slot: " + expr.name + " = " + value); - } else if (obj.fields.containsKey(expr.name)) { - value = obj.fields.get(expr.name); - DebugSystem.trace("MEMORY", "Found field: " + expr.name + " = " + value); - } else { - throw new RuntimeException("Undefined Variable: " + expr.name); - } - return value; - } else if (expr.value != null) { - DebugSystem.trace("EXPRESSIONS", "Literal value: " + expr.value); - if (expr.value instanceof String) { - String s = (String) expr.value; - if (s.startsWith("\"") && s.endsWith("\"") && s.length() >= 2) - s = s.substring(1, s.length() - 1); - return s; - } - return expr.value; - } - return null; - } - - private Object evaluateEqualityChain(EqualityChainNode chain, ObjectInstance obj, Map locals) { - - // --- NEW: Handle Array-based check (e.g., 'all scores >= 60') --- - Object leftObj = evaluate(chain.left, obj, locals); - - List targetValues; - - if (leftObj instanceof List) { - // Case 1: Array-based check (e.g., 'all scores >= 60') - targetValues = (List) leftObj; - } else { - // Case 2: Standard check (e.g., 'x == any[1, 2, 3]') or Reverse Check (e.g., 'any[1, 2, 3] == x') - // In both these cases, the "target" of the comparison is the single leftObj - targetValues = new ArrayList(); - targetValues.add(leftObj); - } - - boolean isAllChain = chain.isAllChain; - boolean result = isAllChain; - - DebugSystem.debug("EQUALITY_CHAIN", "Starting " + (isAllChain ? "all" : "any") + " chain with operator: " + chain.operator); - - // If it's an array check, the chain arguments must have a single value (e.g., 60) - // If it's a standard check, the targetValues has a single value (e.g., x) - - List comparisonValues = chain.chainArguments; - - if (targetValues.size() > 1 && comparisonValues.size() > 1) { - throw new RuntimeException("Ambiguous chain: Cannot compare array against array/list of values in an equality chain."); - } - - // --- NEW Logic for array iteration (Case 1) --- - if (targetValues.size() > 1) { - Object rightValue = comparisonValues.size() == 1 ? evaluate(comparisonValues.get(0), obj, locals) : null; - - for (Object targetValue : targetValues) { - boolean currentResult = compareValues(targetValue, chain.operator, rightValue); - - if (isAllChain) { - result = result && currentResult; - if (!result) { - DebugSystem.debug("EQUALITY_CHAIN", "ALL array chain short-circuited at false"); - break; - } - } else { - result = result || currentResult; - if (result) { - DebugSystem.debug("EQUALITY_CHAIN", "ANY array chain short-circuited at true"); - break; - } - } - } - - } else { - // --- Original Logic for single-value check (Case 2 and Reverse) --- - Object fixedLeftValue = targetValues.get(0); - - for (ExprNode chainArg : comparisonValues) { - Object rightValue = evaluate(chainArg, obj, locals); - boolean currentResult = compareValues(fixedLeftValue, chain.operator, rightValue); - - if (isAllChain) { - result = result && currentResult; - if (!result) { - DebugSystem.debug("EQUALITY_CHAIN", "ALL chain short-circuited at false"); - break; - } - } else { - result = result || currentResult; - if (result) { - DebugSystem.debug("EQUALITY_CHAIN", "ANY chain short-circuited at true"); - break; - } - } - } - } - - DebugSystem.debug("EQUALITY_CHAIN", "Equality chain result: " + result); - return result; - } - - // --- NEW: Comparison helper method to clean up chain logic --- - private boolean compareValues(Object leftValue, String operator, Object rightValue) { - switch (operator) { - case "==": - return leftValue.equals(rightValue); - case "!=": - return !leftValue.equals(rightValue); - case ">": - return typeSystem.compare(leftValue, rightValue) > 0; - case "<": - return typeSystem.compare(leftValue, rightValue) < 0; - case ">=": - return typeSystem.compare(leftValue, rightValue) >= 0; - case "<=": - return typeSystem.compare(leftValue, rightValue) <= 0; - default: - throw new RuntimeException("Unsupported operator in equality chain: " + operator); - } - } - - private Object evaluateBooleanChain(BooleanChainNode chain, ObjectInstance obj, Map locals) { - boolean isAll = chain.isAll; - boolean result = isAll; - - DebugSystem.debug("BOOL_CHAIN", "Starting " + (isAll ? "all" : "any") + " boolean chain"); - - if (isAll) { - result = true; - for (ExprNode e : chain.expressions) { - Object val = evaluate(e, obj, locals); - if (!isTruthy(val)) { - DebugSystem.debug("BOOL_CHAIN", "ALL chain short-circuited (found false)"); - result = false; - break; - } - } - } else { - result = false; - for (ExprNode e : chain.expressions) { - Object val = evaluate(e, obj, locals); - if (isTruthy(val)) { - DebugSystem.debug("BOOL_CHAIN", "ANY chain short-circuited (found true)"); - result = true; - break; - } - } - } - return result; - } - - private Object evaluateConditionalChain(MethodCallNode call, ObjectInstance obj, Map locals) { - boolean isAllChain = "all".equals(call.chainType); - boolean result = isAllChain; - - DebugSystem.debug("CHAIN", "Starting " + call.chainType + " chain for method: " + call.name); - - for (ExprNode chainArg : call.chainArguments) { - MethodCallNode singleCall = ASTFactory.createMethodCall(call.name, call.qualifiedName); - singleCall.arguments = new ArrayList(); - - boolean currentResult; - if (chainArg instanceof UnaryNode && "!".equals(((UnaryNode)chainArg).op)) { - UnaryNode unary = (UnaryNode) chainArg; - singleCall.arguments.add(unary.operand); - Object methodResult = interpreter.evalMethodCall(singleCall, obj, locals); - currentResult = !isTruthy(methodResult); - DebugSystem.debug("CHAIN", "Negated argument: !" + unary.operand + " = " + currentResult); - } else { - singleCall.arguments.add(chainArg); - Object methodResult = interpreter.evalMethodCall(singleCall, obj, locals); - currentResult = isTruthy(methodResult); - DebugSystem.debug("CHAIN", "Regular argument: " + chainArg + " = " + currentResult); - } - - if (isAllChain) { - result = result && currentResult; - if (!result) { - DebugSystem.debug("CHAIN", "ALL chain short-circuited at false"); - break; - } - } else { - result = result || currentResult; - if (result) { - DebugSystem.debug("CHAIN", "ANY chain short-circuited at true"); - break; - } - } - } - - DebugSystem.debug("CHAIN", "Conditional chain result: " + result + " for " + call.chainType); - return result; - } - - private boolean isTruthy(Object value) { - if (value == null) return false; - if (value instanceof Boolean) return (Boolean) value; - if (value instanceof Number) return ((Number) value).doubleValue() != 0.0; - if (value instanceof String) return !((String) value).isEmpty(); - if (value instanceof List) return !((List) value).isEmpty(); - if (value instanceof Map) return !((Map) value).isEmpty(); - return true; - } - - private boolean isAssignmentOperator(String op) { - return op.equals("=") || op.equals("+=") || op.equals("-=") || - op.equals("*=") || op.equals("/="); - } - - private boolean isForLoopStepEvaluation(BinaryOpNode binOp, ObjectInstance obj, Map locals) { - DebugSystem.debug("FOR_LOOP_STEP", "Checking if binary op is for loop step: " + binOp.op); - return true; - } -} \ No newline at end of file diff --git a/src/main/java/cod/interpreter/IOHandler.java b/src/main/java/cod/interpreter/IOHandler.java index 314cbc7e..aedb8105 100644 --- a/src/main/java/cod/interpreter/IOHandler.java +++ b/src/main/java/cod/interpreter/IOHandler.java @@ -1,6 +1,8 @@ package cod.interpreter; import cod.debug.DebugSystem; +import cod.syntax.Keyword; +import java.math.BigDecimal; import java.util.Scanner; public class IOHandler { @@ -10,50 +12,57 @@ public Object readInput(String targetType) { DebugSystem.debug("INPUT", "Reading input as type: " + targetType); try { - System.out.print(">> "); System.out.flush(); - switch (targetType) { - case "int": - // For int, read the entire line and parse it - String intLine = inputScanner.nextLine().trim(); - if (intLine.isEmpty()) { - intLine = inputScanner.nextLine().trim(); // Try again if empty - } - return Integer.parseInt(intLine); + // Convert to Keyword for safe comparison + Keyword targetKeyword; + try { + targetKeyword = Keyword.valueOf(targetType.toUpperCase()); + } catch (IllegalArgumentException e) { + targetKeyword = null; // Unknown type + } + + if (targetKeyword == Keyword.INT) { + // For int, read the entire line and parse it + String intLine = inputScanner.nextLine().trim(); + if (intLine.isEmpty()) { + intLine = inputScanner.nextLine().trim(); // Try again if empty + } + return Integer.parseInt(intLine); - case "float": - // For float, read the entire line and parse it - String floatLine = inputScanner.nextLine().trim(); - if (floatLine.isEmpty()) { - floatLine = inputScanner.nextLine().trim(); - } - return Float.parseFloat(floatLine); + } else if (targetKeyword == Keyword.FLOAT) { + // For float, read the entire line and parse it + String floatLine = inputScanner.nextLine().trim(); + if (floatLine.isEmpty()) { + floatLine = inputScanner.nextLine().trim(); + } + return Float.parseFloat(floatLine); - case "string": - // For string, just read the line - String stringValue = inputScanner.nextLine().trim(); - if (stringValue.isEmpty()) { - stringValue = inputScanner.nextLine().trim(); - } - return stringValue; + } else if (targetKeyword == Keyword.TEXT) { // ← CHANGED FROM STRING TO TEXT + // For text, just read the line + String textValue = inputScanner.nextLine().trim(); + if (textValue.isEmpty()) { + textValue = inputScanner.nextLine().trim(); + } + return textValue; - case "bool": - // For bool, read the line and check content - String boolLine = inputScanner.nextLine().trim().toLowerCase(); - if (boolLine.isEmpty()) { - boolLine = inputScanner.nextLine().trim().toLowerCase(); - } - return boolLine.equals("true") - || boolLine.equals("1") - || boolLine.equals("yes"); + } else if (targetKeyword == Keyword.BOOL) { + // For bool, read the line and check content + String boolLine = inputScanner.nextLine().trim().toLowerCase(); + if (boolLine.isEmpty()) { + boolLine = inputScanner.nextLine().trim().toLowerCase(); + } + return boolLine.equals("true") + || boolLine.equals("1") + || boolLine.equals("yes"); - default: - String defaultLine = inputScanner.nextLine().trim(); - if (defaultLine.isEmpty()) { - defaultLine = inputScanner.nextLine().trim(); - } - return defaultLine; + } else { + // Unknown type, read as string + String defaultLine = inputScanner.nextLine().trim(); + if (defaultLine.isEmpty()) { + defaultLine = inputScanner.nextLine().trim(); + } + return defaultLine; } } catch (Exception e) { DebugSystem.error( @@ -67,10 +76,37 @@ public Object readInput(String targetType) { } public void output(Object value) { - System.out.print(String.valueOf(value)); // CHANGED: println to print + // Format large numbers without scientific notation + String output; + + // FIX: Handle BigDecimal directly + if (value instanceof BigDecimal) { + BigDecimal bd = (BigDecimal) value; + bd = bd.stripTrailingZeros(); // Remove .000 + output = bd.toPlainString(); // No scientific notation + } else if (value instanceof Double) { + double d = (Double) value; + // If it's a whole number and reasonably sized, show as integer + if (d == Math.floor(d) && d <= 1e15 && d >= -1e15) { + output = String.format("%.0f", d); + } else { + output = String.valueOf(value); + } + } else if (value instanceof Float) { + float f = (Float) value; + if (f == Math.floor(f) && f <= 1e15 && f >= -1e15) { + output = String.format("%.0f", (double) f); + } else { + output = String.valueOf(value); + } + } else { + output = String.valueOf(value); } + + System.out.print(output); +} public void close() { inputScanner.close(); } -} \ No newline at end of file +} diff --git a/src/main/java/cod/interpreter/Interpreter.java b/src/main/java/cod/interpreter/Interpreter.java index 2cd5d3ad..b7ce29f2 100644 --- a/src/main/java/cod/interpreter/Interpreter.java +++ b/src/main/java/cod/interpreter/Interpreter.java @@ -1,265 +1,440 @@ package cod.interpreter; +import cod.syntax.Keyword; +import static cod.syntax.Keyword.*; import cod.ast.nodes.*; -import cod.ast.ImportResolver; +import cod.semantic.ImportResolver; import cod.debug.DebugSystem; -import java.util.*; -import java.io.File; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; public class Interpreter { - Map currentSlots = null; - Map currentSlotTypes = null; // NEW: Track slot types for validation - Set slotsInCurrentPath = new HashSet<>(); - - private IOHandler ioHandler = new IOHandler(); - private ImportResolver importResolver = new ImportResolver(); - private TypeSystem typeSystem = new TypeSystem(); - private ExpressionEvaluator exprEvaluator; - private StatementEvaluator stmtEvaluator; - - public Interpreter() { - this.exprEvaluator = new ExpressionEvaluator(typeSystem, this); - this.stmtEvaluator = new StatementEvaluator(this, exprEvaluator, ioHandler, typeSystem); - } + private IOHandler ioHandler = new IOHandler(); + private ImportResolver importResolver = new ImportResolver(); + private TypeSystem typeSystem = new TypeSystem(); + private InterpreterVisitor visitor; - private Map emptyParamMap() { - return Collections.emptyMap(); - } + public Interpreter() { + this.visitor = new InterpreterVisitor(this, typeSystem, ioHandler); + } - public ImportResolver getImportResolver() { - return importResolver; - } + public ImportResolver getImportResolver() { + return importResolver; + } - public ExpressionEvaluator getExpressionEvaluator() { - return exprEvaluator; + boolean shouldReturnEarly(Map slotValues, Set slotsInCurrentPath) { + if (slotsInCurrentPath.isEmpty()) return false; + for (String slotName : slotsInCurrentPath) { + if (slotValues.get(slotName) == null) return false; } - - public StatementEvaluator getStatementEvaluator() { - return stmtEvaluator; + return true; + } + + public Object evalReplStatement( + StmtNode stmt, + ObjectInstance obj, + Map locals, + Map slotValues) { + Map slotTypes = new HashMap(); + ExecutionContext ctx = new ExecutionContext(obj, locals, slotValues, slotTypes); + visitor.pushContext(ctx); + try { + return visitor.visit((ASTNode) stmt); + } finally { + visitor.popContext(); + } + } + + public void run(ProgramNode program) { + DebugSystem.startTimer("program_execution"); + DebugSystem.info("INTERPRETER", "Starting program execution"); + + // Handle different program types + if (program.programType == null) { + // Fallback to original behavior (assume module) + DebugSystem.warn("INTERPRETER", "No program type detected, assuming MODULE"); + runModule(program); + } else { + switch (program.programType) { + case MODULE: + runModule(program); + break; + case SCRIPT: + runScript(program); + break; + case METHOD_SCRIPT: + runMethodScript(program); + break; + default: + throw new RuntimeException("Unknown program type: " + program.programType); + } } - public void run(ProgramNode program) { - DebugSystem.startTimer("program_execution"); - DebugSystem.info("INTERPRETER", "Starting program execution"); - - UnitNode unit = program.unit; - DebugSystem.debug("INTERPRETER", "Processing unit: " + unit.name); - - initializeImportResolver(unit); - - for (TypeNode type : unit.types) { - DebugSystem.debug("INTERPRETER", "Processing type: " + type.name); - - for (MethodNode method : type.methods) { - boolean isMainMethod = "main".equals(method.name); - boolean hasNoParameters = method.parameters.isEmpty(); - - if (isMainMethod && hasNoParameters) { - DebugSystem.methodEntry("main", emptyParamMap()); - - ObjectInstance obj = new ObjectInstance(type); - if (type.constructor != null) { - evalConstructor(type.constructor, obj); - } - - Object result = evalMethod(method, obj, new HashMap()); - DebugSystem.methodExit("main", result); - - } else if (isMainMethod && !hasNoParameters) { - DebugSystem.warn("INTERPRETER", "Ignoring main() with parameters: " + method.parameters); - } - } + DebugSystem.stopTimer("program_execution"); + DebugSystem.info("INTERPRETER", "Program execution completed"); + ioHandler.close(); + } + + /** + * Run a MODULE program. Must have unit declaration and classes. Executes main() method in a + * class. + */ + private void runModule(ProgramNode program) { + UnitNode unit = program.unit; + initializeImportResolver(unit); + + // Find and execute main() method in a class + boolean mainExecuted = false; + for (TypeNode type : unit.types) { + for (MethodNode method : type.methods) { + boolean isMainMethod = "main".equals(method.name); + boolean hasNoParameters = method.parameters.isEmpty(); + + if (isMainMethod && hasNoParameters) { + DebugSystem.methodEntry("main", Collections.emptyMap()); + ObjectInstance obj = new ObjectInstance(type); + initializeFields(type, obj); + if (type.constructor != null) evalConstructor(type.constructor, obj); + Object result = evalMethod(method, obj, new HashMap()); + DebugSystem.methodExit("main", result); + mainExecuted = true; + break; // Only execute first main() found + } else if (isMainMethod && !hasNoParameters) { + DebugSystem.warn("INTERPRETER", "Ignoring main() with parameters in class: " + type.name); } - - DebugSystem.stopTimer("program_execution"); - DebugSystem.info("INTERPRETER", "Program execution completed"); - ioHandler.close(); + } + if (mainExecuted) break; } - private void initializeImportResolver(UnitNode unit) { - if (unit.resolvedImports != null && !unit.resolvedImports.isEmpty()) { - for (Map.Entry entry : unit.resolvedImports.entrySet()) { - importResolver.preloadImport(entry.getKey(), entry.getValue()); - } - } - if (unit.imports != null && !unit.imports.imports.isEmpty()) { - for (String importName : unit.imports.imports) { - importResolver.registerImport(importName); + if (!mainExecuted) { + throw new RuntimeException("Module must have a class with 'main()' method"); + } + } + + /** Run a SCRIPT program. Direct statements executed top-down. No classes, no methods required. */ + private void runScript(ProgramNode program) { + UnitNode unit = program.unit; + initializeImportResolver(unit); + + // Create a dummy object instance for script execution + ObjectInstance obj = new ObjectInstance(null); + Map locals = new HashMap(); + + DebugSystem.methodEntry("script", Collections.emptyMap()); + + // Execute all statements in order (script has only synthetic __Script__ type) + for (TypeNode type : unit.types) { + // Look for synthetic script type + if (type.name != null && type.name.equals("__Script__")) { + if (type.statements != null) { + for (StmtNode stmt : type.statements) { + ExecutionContext ctx = new ExecutionContext(obj, locals, null, null); + visitor.pushContext(ctx); + try { + Object result = visitor.visit((ASTNode) stmt); + DebugSystem.debug("SCRIPT", "Executed statement: " + stmt.getClass().getSimpleName()); + if (result != null) { + DebugSystem.debug("SCRIPT", " Result: " + result); + } + } finally { + visitor.popContext(); } + } } + break; + } } - private void evalConstructor(ConstructorNode constructor, ObjectInstance obj) { - DebugSystem.methodEntry(constructor.getClass().getSimpleName(), emptyParamMap()); - - Map locals = new HashMap(); - for (ParamNode p : constructor.parameters) { - locals.put(p.name, 0); - } - - for (StatementNode stmt : constructor.body) { - Object val = stmtEvaluator.evalStmt(stmt, obj, locals, null); - if (stmt instanceof FieldNode) { - FieldNode field = (FieldNode) stmt; - obj.fields.put(field.name, val); + DebugSystem.methodExit("script", "completed"); + } + + /** Run a METHOD_SCRIPT program. Only methods, no direct code. Must have main() method. */ + private void runMethodScript(ProgramNode program) { + UnitNode unit = program.unit; + initializeImportResolver(unit); + + // Find main() method in synthetic __MethodScript__ type + MethodNode mainMethod = null; + TypeNode containerType = null; + + for (TypeNode type : unit.types) { + // Look for synthetic method script type + if (type.name != null && type.name.equals("__MethodScript__")) { + containerType = type; + if (type.methods != null) { + for (MethodNode method : type.methods) { + if ("main".equals(method.name) + && (method.parameters == null || method.parameters.isEmpty())) { + mainMethod = method; + break; } + } } - - DebugSystem.methodExit(constructor.getClass().getSimpleName(), "constructor completed"); + break; + } } - private Object evalMethod(MethodNode method, ObjectInstance obj, Map locals) { - DebugSystem.methodEntry(method.name, locals); - - // Initialize slot values and types - Map slotValues = new HashMap(); - Map slotTypes = new HashMap(); - - for (SlotNode s : method.returnSlots) { - slotValues.put(s.name, null); - slotTypes.put(s.name, s.type); // Store type for validation - DebugSystem.debug("SLOTS", "Initialized slot: " + s.name + " (Type: " + s.type + ") = null"); - } - - // Push context - Map previousSlots = currentSlots; - Map previousSlotTypes = currentSlotTypes; - currentSlots = slotValues; - currentSlotTypes = slotTypes; - - slotsInCurrentPath.clear(); - boolean hasSlots = !method.returnSlots.isEmpty(); - if (hasSlots) { - slotsInCurrentPath.addAll(slotValues.keySet()); - } - - Object result = null; - - // Process statements - for (int i = 0; i < method.body.size(); i++) { - StatementNode stmt = method.body.get(i); - result = stmtEvaluator.evalStmt(stmt, obj, locals, slotValues); - - if (hasSlots && shouldReturnEarly(slotValues)) { - DebugSystem.debug("SLOTS", "Early return triggered - all slots assigned in current path"); - break; + // If not found in synthetic type, look in any type + if (mainMethod == null) { + for (TypeNode type : unit.types) { + if (type.methods != null) { + for (MethodNode method : type.methods) { + if ("main".equals(method.name) + && (method.parameters == null || method.parameters.isEmpty())) { + mainMethod = method; + containerType = type; + break; } + } } + if (mainMethod != null) break; + } + } - // Pop context - currentSlots = previousSlots; - currentSlotTypes = previousSlotTypes; - - DebugSystem.methodExit(method.name, slotValues); - return result; // Usually null unless simple return + if (mainMethod == null) { + throw new RuntimeException("Method script requires a 'main()' method"); } - boolean shouldReturnEarly(Map slotValues) { - for (String slotName : slotsInCurrentPath) { - if (slotValues.get(slotName) == null) { - return false; - } + DebugSystem.methodEntry("main", Collections.emptyMap()); + ObjectInstance obj = new ObjectInstance(containerType); + initializeFields(containerType, obj); + if (containerType.constructor != null) { + evalConstructor(containerType.constructor, obj); + } + Object result = evalMethod(mainMethod, obj, new HashMap()); + DebugSystem.methodExit("main", result); + } + + private void initializeFields(TypeNode type, ObjectInstance obj) { + ExecutionContext ctx = new ExecutionContext(obj, new HashMap(), null, null); + visitor.pushContext(ctx); + try { + if (type.fields != null) { + for (FieldNode field : type.fields) { + visitor.visit((ASTNode) field); } - return true; + } + } finally { + visitor.popContext(); } + } - public Object evalMethodCall(MethodCallNode call, ObjectInstance obj, Map locals) { - MethodNode method = null; - - for (int i = 0; i < obj.type.methods.size(); i++) { - MethodNode candidate = obj.type.methods.get(i); - if (candidate.name.equals(call.name)) { - method = candidate; - break; - } + private void initializeImportResolver(UnitNode unit) { + if (unit.resolvedImports != null && !unit.resolvedImports.isEmpty()) { + for (Map.Entry entry : unit.resolvedImports.entrySet()) { + importResolver.preloadImport(entry.getKey(), entry.getValue()); + } + } + if (unit.imports != null && !unit.imports.imports.isEmpty()) { + for (String importName : unit.imports.imports) { + importResolver.registerImport(importName); + } + } + } + + private void evalConstructor(ConstructorNode constructor, ObjectInstance obj) { + DebugSystem.methodEntry("Constructor", Collections.emptyMap()); + Map locals = new HashMap(); + for (ParamNode p : constructor.parameters) locals.put(p.name, 0); + ExecutionContext ctx = new ExecutionContext(obj, locals, null, null); + visitor.pushContext(ctx); + for (StmtNode stmt : constructor.body) { + Object val = visitor.visit((ASTNode) stmt); + if (stmt instanceof FieldNode) obj.fields.put(((FieldNode) stmt).name, val); + } + visitor.popContext(); + DebugSystem.methodExit("Constructor", "completed"); + } + + private Object evalMethod(MethodNode method, ObjectInstance obj, Map locals) { + DebugSystem.methodEntry(method.name, locals); + // Use LinkedHashMap to preserve slot order + Map slotValues = new LinkedHashMap(); + Map slotTypes = new LinkedHashMap(); + if (method.returnSlots != null) { + for (SlotNode s : method.returnSlots) { + slotValues.put(s.name, null); + slotTypes.put(s.name, s.type); + } + } + ExecutionContext ctx = new ExecutionContext(obj, locals, slotValues, slotTypes); + visitor.pushContext(ctx); + Object result = null; + boolean hasSlots = method.returnSlots != null && !method.returnSlots.isEmpty(); + + try { + if (method.body != null) { + for (StmtNode stmt : method.body) { + result = visitor.visit((ASTNode) stmt); + if (hasSlots && shouldReturnEarly(slotValues, ctx.slotsInCurrentPath)) break; } - - if (method == null) { - String qualifiedMethodName = call.qualifiedName != null ? call.qualifiedName : call.name; - method = resolveImportedMethod(qualifiedMethodName); + } + } catch (EarlyExitException e) { + // Early exit requested + } + visitor.popContext(); + DebugSystem.methodExit(method.name, slotValues); + + // Return slot values if method has return slots, otherwise return result + return hasSlots ? slotValues : result; + } + + public Object evalMethodCall( + MethodCallNode call, ObjectInstance obj, Map locals) { + MethodNode method = null; + if (obj.type != null && obj.type.methods != null) { + for (MethodNode m : obj.type.methods) { + if (m.name.equals(call.name)) { + method = m; + break; } - - if (method == null) { - throw new RuntimeException("Method not found: " + (call.qualifiedName != null ? call.qualifiedName : call.name)); + } + } + if (method == null) { + String qName = call.qualifiedName != null ? call.qualifiedName : call.name; + method = resolveImportedMethod(qName); + } + if (method == null) throw new RuntimeException("Method not found: " + call.name); + if (method.isBuiltin) return handleBuiltinMethod(method, call); + + Map newLocals = new HashMap(); + Map newLocalTypes = new HashMap(); + + int argCount = call.arguments != null ? call.arguments.size() : 0; + int paramCount = method.parameters != null ? method.parameters.size() : 0; + + // Process each parameter + for (int i = 0; i < paramCount; i++) { + ParamNode param = method.parameters.get(i); + Object argValue = null; + + // Check if argument is provided + if (i < argCount) { + ExprNode argExpr = call.arguments.get(i); + + // Check if argument is underscore placeholder + if (argExpr instanceof ExprNode && "_".equals(((ExprNode) argExpr).name)) { + // Use underscore placeholder + if (param.hasDefaultValue) { + // Evaluate default value expression + argValue = visitor.visit((ASTNode) param.defaultValue); + } else { + throw new RuntimeException( + "Parameter '" + + param.name + + "' has no default value and cannot be skipped with '_'"); + } + } else { + // Regular argument + argValue = visitor.visit((ASTNode) argExpr); } - - if (method.isBuiltin) { - return handleBuiltinMethod(method, call, obj, locals); + } else { + if (param.hasDefaultValue) { + argValue = visitor.visit((ASTNode) param.defaultValue); + + // Validate type (even for defaults!) + if (!typeSystem.validateType(param.type, argValue)) { + throw new RuntimeException( + "Default value for parameter '" + + param.name + + "' returns wrong type. Expected " + + param.type + + ", got: " + + typeSystem.getConcreteType(argValue)); + } } + } - Map newLocals = new HashMap(); - if (call.arguments.size() != method.parameters.size()) { - throw new RuntimeException("Parameter count mismatch for method " + method.name); - } + String paramType = param.type; - for (int i = 0; i < call.arguments.size(); i++) { - ParamNode param = method.parameters.get(i); - Object argValue = exprEvaluator.evaluate(call.arguments.get(i), obj, locals); - newLocals.put(param.name, argValue); + // Type validation (existing code) + if (!typeSystem.validateType(paramType, argValue)) { + if (paramType.equals(Keyword.TEXT.toString())) { + argValue = typeSystem.convertType(argValue, paramType); + } else { + throw new RuntimeException( + "Argument type mismatch for parameter " + param.name + ". Expected " + paramType); } + } - // Initialize context for called method - Map slotValues = new HashMap(); - Map slotTypes = new HashMap(); - - for (SlotNode s : method.returnSlots) { - slotValues.put(s.name, null); - slotTypes.put(s.name, s.type); - } + if (paramType.contains("|")) { + String activeType = typeSystem.getConcreteType(typeSystem.unwrap(argValue)); + argValue = new TypedValue(argValue, activeType, paramType); + } - Map previousSlots = currentSlots; - Map previousSlotTypes = currentSlotTypes; - currentSlots = slotValues; - currentSlotTypes = slotTypes; + newLocals.put(param.name, argValue); + newLocalTypes.put(param.name, paramType); + } - Set previousSlotsInPath = slotsInCurrentPath; - boolean calledMethodHasSlots = !method.returnSlots.isEmpty(); - if (calledMethodHasSlots) { - slotsInCurrentPath = new HashSet<>(slotValues.keySet()); - } else { - slotsInCurrentPath = new HashSet<>(); - } + // Check for too many arguments + if (argCount > paramCount) { + throw new RuntimeException( + "Too many arguments: expected " + paramCount + ", got " + argCount); + } - for (StatementNode stmt : method.body) { - stmtEvaluator.evalStmt(stmt, obj, newLocals, slotValues); - if (calledMethodHasSlots && shouldReturnEarly(slotValues)) { - break; - } - } + // Rest of the method remains the same... + Map slotValues = new LinkedHashMap(); + Map slotTypes = new LinkedHashMap(); + if (method.returnSlots != null) { + for (SlotNode s : method.returnSlots) { + slotValues.put(s.name, null); + slotTypes.put(s.name, s.type); + } + } - currentSlots = previousSlots; - currentSlotTypes = previousSlotTypes; - slotsInCurrentPath = previousSlotsInPath; + ExecutionContext ctx = new ExecutionContext(obj, newLocals, slotValues, slotTypes); + ctx.localTypes.putAll(newLocalTypes); - return slotValues; - } + visitor.pushContext(ctx); + boolean calledMethodHasSlots = method.returnSlots != null && !method.returnSlots.isEmpty(); - private Object handleBuiltinMethod(MethodNode method, MethodCallNode call, ObjectInstance obj, Map locals) { - switch (method.name) { - case "outa": - return handleSysOuta(call, obj, locals); - default: - throw new RuntimeException("Unknown builtin method: " + method.name); + try { + if (method.body != null) { + for (StmtNode stmt : method.body) { + visitor.visit((ASTNode) stmt); + if (calledMethodHasSlots && shouldReturnEarly(slotValues, ctx.slotsInCurrentPath)) break; } + } + } catch (EarlyExitException e) { } - private Object handleSysOuta(MethodCallNode call, ObjectInstance obj, Map locals) { - StringBuilder result = new StringBuilder(); + visitor.popContext(); + + return calledMethodHasSlots ? slotValues : null; + } + + private Object handleBuiltinMethod(MethodNode method, MethodCallNode call) { + if ("printp".equals(method.name)) { + StringBuilder result = new StringBuilder(); + if (call.arguments != null) { for (int i = 0; i < call.arguments.size(); i++) { - Object value = exprEvaluator.evaluate(call.arguments.get(i), obj, locals); - result.append(String.valueOf(value)); + Object value = visitor.visit((ASTNode) call.arguments.get(i)); + result.append(String.valueOf(value)); } - ioHandler.output(result.toString()); - return null; + } + ioHandler.output(result.toString()); + return null; + } + throw new RuntimeException("Unknown builtin method: " + method.name); + } + + private MethodNode resolveImportedMethod(String qualifiedMethodName) { + try { + return importResolver.findMethod(qualifiedMethodName); + } catch (Exception e) { + e.printStackTrace(); + return null; } + } - private MethodNode resolveImportedMethod(String qualifiedMethodName) { - try { - return importResolver.findMethod(qualifiedMethodName); - } catch (Exception e) { - e.printStackTrace(); - return null; - } + public static class EarlyExitException extends RuntimeException { + public EarlyExitException() { + super("Early exit"); } -} \ No newline at end of file + } +} diff --git a/src/main/java/cod/interpreter/InterpreterVisitor.java b/src/main/java/cod/interpreter/InterpreterVisitor.java new file mode 100644 index 00000000..3298ec0a --- /dev/null +++ b/src/main/java/cod/interpreter/InterpreterVisitor.java @@ -0,0 +1,933 @@ +package cod.interpreter; + +import static cod.syntax.Keyword.*; +import cod.ast.ASTFactory; +import cod.ast.BaseASTVisitor; +import cod.ast.nodes.*; +import java.util.*; +import java.math.BigDecimal; +import java.math.RoundingMode; + +public class InterpreterVisitor extends BaseASTVisitor { + + private final Interpreter interpreter; + public final TypeSystem typeSystem; + private final IOHandler ioHandler; + private final Stack contextStack = new Stack(); + + // Scale for BigDecimal operations (consistent with TypeSystem) + private static final int DECIMAL_SCALE = 20; + + public InterpreterVisitor(Interpreter interpreter, TypeSystem typeSystem, IOHandler ioHandler) { + this.interpreter = interpreter; + this.typeSystem = typeSystem; + this.ioHandler = ioHandler; + } + + public void pushContext(ExecutionContext context) { contextStack.push(context); } + public void popContext() { contextStack.pop(); } + public ExecutionContext getCurrentContext() { return contextStack.peek(); } + +@Override +public Object visit(ExprNode node) { + ExecutionContext ctx = getCurrentContext(); + + if (node.name != null) { + + if (ctx.locals.containsKey(node.name)) return ctx.locals.get(node.name); + if (ctx.slotValues != null && ctx.slotValues.containsKey(node.name)) return ctx.slotValues.get(node.name); + if (ctx.objectInstance.fields.containsKey(node.name)) return ctx.objectInstance.fields.get(node.name); + + throw new RuntimeException("Undefined Variable: " + node.name); + } + + if (node.value != null) { + if (node.value instanceof String) { + String s = (String) node.value; + if (s.startsWith("\"") && s.endsWith("\"") && s.length() >= 2) return s.substring(1, s.length() - 1); + return s; + } + + // FIX: Strip trailing zeros from BigDecimal literals + if (node.value instanceof BigDecimal) { + return ((BigDecimal) node.value).stripTrailingZeros(); + } + + return node.value; + } + + return null; +} + + @Override +public Object visit(BinaryOpNode node) { + Object left = dispatch(node.left); + Object right = dispatch(node.right); + + switch (node.op) { + case "+": + case "+=": + if (left instanceof String || right instanceof String) { + Object unwrappedLeft = typeSystem.unwrap(left); + Object unwrappedRight = typeSystem.unwrap(right); + + // FIX: Strip trailing zeros from BigDecimal and use toPlainString() + if (unwrappedLeft instanceof BigDecimal) { + BigDecimal bdLeft = ((BigDecimal) unwrappedLeft).stripTrailingZeros(); + unwrappedLeft = bdLeft.toPlainString(); // Use plain string, not toString() + } + if (unwrappedRight instanceof BigDecimal) { + BigDecimal bdRight = ((BigDecimal) unwrappedRight).stripTrailingZeros(); + unwrappedRight = bdRight.toPlainString(); // Use plain string, not toString() + } + + return String.valueOf(unwrappedLeft) + String.valueOf(unwrappedRight); + } + return typeSystem.addNumbers(left, right); + + case "-": + case "-=": + return typeSystem.subtractNumbers(left, right); + case "*": + case "*=": + return typeSystem.multiplyNumbers(left, right); + case "/": + case "/=": + return typeSystem.divideNumbers(left, right); + case "%": + return typeSystem.modulusNumbers(left, right); + case ">": + return typeSystem.compare(left, right) > 0; + case "<": + return typeSystem.compare(left, right) < 0; + case ">=": + return typeSystem.compare(left, right) >= 0; + case "<=": + return typeSystem.compare(left, right) <= 0; + case "=": + return right; + case "==": + // Use Object.equals() which handles BigDecimal vs BigDecimal + return typeSystem.unwrap(left).equals(typeSystem.unwrap(right)); + case "!=": + return !typeSystem.unwrap(left).equals(typeSystem.unwrap(right)); + default: + throw new RuntimeException("Unknown operator: " + node.op); + } +} + +@Override +public Object visit(BooleanChainNode node) { + boolean isAll = node.isAll; + + for (ExprNode expr : node.expressions) { + Object result = dispatch(expr); + result = typeSystem.unwrap(result); + boolean isTruthy = isTruthy(result); + + if (isAll) { + // For "all": if any expression is false, return false + if (!isTruthy) return false; + } else { + // For "any": if any expression is true, return true + if (isTruthy) return true; + } + } + + // If we get here: + // - For "all": all expressions were true, so return true + // - For "any": no expressions were true, so return false + return isAll; +} + + @Override + public Object visit(UnaryNode node) { + Object operand = dispatch(node.operand); + + switch (node.op) { + case "-": + return typeSystem.negateNumber(operand); + case "+": + return operand; + case "!": + return !isTruthy(operand); + default: + throw new RuntimeException("Unknown unary operator: " + node.op); + } + } + + @Override + public Object visit(TypeCastNode node) { + Object val = dispatch(node.expression); + if (!typeSystem.validateType(node.targetType, val)) { + return typeSystem.convertType(val, node.targetType); + } + return val; + } + + @Override + public Object visit(InputNode node) { + ExecutionContext ctx = getCurrentContext(); + Object val = ioHandler.readInput(node.targetType); + + if (node.variableName != null) { + ctx.objectInstance.fields.put(node.variableName, val); + ctx.locals.put(node.variableName, val); + } + + return val; + } + +@Override +public Object visit(ArrayNode node) { + // Handle [range] -> NaturalArray (keep this!) + if (node.elements.size() == 1) { + ExprNode onlyElement = node.elements.get(0); + if (onlyElement instanceof RangeNode) { + RangeNode range = (RangeNode) onlyElement; + return new NaturalArray(range, this); + } + } + + List result = new ArrayList(); + for (ExprNode element : node.elements) { + // Handle RangeNode specially - DON'T dispatch it! + if (element instanceof RangeNode) { + result.add(new NaturalArray((RangeNode) element, this)); + } + else { + Object evaluated = dispatch(element); + + // Special handling for nested arrays that evaluate to NaturalArray + if (element instanceof ArrayNode && evaluated instanceof NaturalArray) { + result.add(evaluated); + } + else { + result.add(evaluated); + } + } + } + return result; +} + +// Helper updated to accept BigDecimal +private Object convertToAppropriateType(BigDecimal value, Object original) { + // If original was integer/long and the result is an exact integer, return integer/long + if ((original instanceof Integer || original instanceof Long) && + (value.scale() == 0 || value.stripTrailingZeros().scale() <= 0)) { + + // Check if it fits in Integer + try { + return value.intValueExact(); + } catch (ArithmeticException e) { + // Falls back to Long if it exceeds Integer range + return value.longValue(); + } + } + + // Otherwise, return the precise BigDecimal + return value; +} + + @Override + public Object visit(TupleNode node) { + List tuple = new ArrayList(); + for (ExprNode elem : node.elements) tuple.add(dispatch(elem)); + return Collections.unmodifiableList(tuple); + } + +@Override +public Object visit(IndexAccessNode node) { + + // Get array and index objects + Object arrayObj = dispatch(node.array); + Object indexObj = dispatch(node.index); + + // Unwrap if needed + arrayObj = typeSystem.unwrap(arrayObj); + indexObj = typeSystem.unwrap(indexObj); + + // --- Handle NaturalArray --- + if (arrayObj instanceof NaturalArray) { + NaturalArray natural = (NaturalArray) arrayObj; + + // Convert index to long + long index = toLongIndex(indexObj); + + if (index < 0 || index >= natural.size()) { + throw new RuntimeException("Index out of bounds: " + index + + " for NaturalArray of size " + natural.size()); + } + + return natural.get(index); + } + + // --- Original handling for traditional lists --- + if (arrayObj instanceof List) { + int index = toIntIndex(indexObj); + + List list = (List) arrayObj; + + if (index < 0 || index >= list.size()) { + throw new RuntimeException("Index out of bounds: " + index + + " for array of size " + list.size()); + } + + return list.get(index); + } + + throw new RuntimeException("Invalid array access: expected NaturalArray or List, got " + + (arrayObj != null ? arrayObj.getClass().getSimpleName() : "null")); +} + +/** + * Convert any numeric object to long for NaturalArray indices. + */ +private long toLongIndex(Object indexObj) { + if (indexObj == null) { + throw new RuntimeException("Array index cannot be null"); + } + + if (indexObj instanceof Integer) { + return ((Integer) indexObj).longValue(); + } + + if (indexObj instanceof Long) { + return (Long) indexObj; + } + + if (indexObj instanceof Double || indexObj instanceof Float) { + double d = ((Number) indexObj).doubleValue(); + if (d != Math.floor(d)) { + throw new RuntimeException("Array index must be integer, got: Double (" + d + ")"); + } + if (d < Long.MIN_VALUE || d > Long.MAX_VALUE) { + throw new RuntimeException("Array index out of long range: " + d); + } + return (long) d; + } + + // NEW: Handle BigDecimal index precisely + if (indexObj instanceof BigDecimal) { + BigDecimal bd = (BigDecimal) indexObj; + try { + // Use precise conversion that checks for fraction part + return bd.longValueExact(); + } catch (ArithmeticException e) { + throw new RuntimeException("Array index must be an exact integer, got: BigDecimal (" + bd + ")"); + } + } + + throw new RuntimeException("Array index must be integer, got: " + + indexObj.getClass().getSimpleName() + " (" + indexObj + ")"); +} + +/** + * Convert any numeric object to int for traditional List indices. + */ +private int toIntIndex(Object indexObj) { + if (indexObj == null) { + throw new RuntimeException("Array index cannot be null"); + } + + if (indexObj instanceof Integer) { + return (Integer) indexObj; + } + + if (indexObj instanceof Long) { + long l = (Long) indexObj; + if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) { + throw new RuntimeException("Array index out of int range: " + l); + } + return (int) l; + } + + if (indexObj instanceof Double || indexObj instanceof Float) { + double d = ((Number) indexObj).doubleValue(); + if (d != Math.floor(d) || d < Integer.MIN_VALUE || d > Integer.MAX_VALUE) { + throw new RuntimeException("Array index must be integer, got: Double (" + d + ")"); + } + return (int) d; + } + + // NEW: Handle BigDecimal index precisely + if (indexObj instanceof BigDecimal) { + BigDecimal bd = (BigDecimal) indexObj; + try { + return bd.intValueExact(); + } catch (ArithmeticException e) { + throw new RuntimeException("Array index must be an exact integer within int range, got: BigDecimal (" + bd + ")"); + } + } + + throw new RuntimeException("Array index must be integer, got: " + + indexObj.getClass().getSimpleName() + " (" + indexObj + ")"); +} + +@Override +public Object visit(AssignmentNode node) { + ExecutionContext ctx = getCurrentContext(); + Object newValue = dispatch(node.right); + + // Handle Array Index Assignment (LHS is IndexAccessNode) + if (node.left instanceof IndexAccessNode) { + IndexAccessNode indexAccess = (IndexAccessNode) node.left; + + Object arrayObj = dispatch(indexAccess.array); + arrayObj = typeSystem.unwrap(arrayObj); + + Object indexObj = dispatch(indexAccess.index); + indexObj = typeSystem.unwrap(indexObj); + + // --- Handle NaturalArray --- + if (arrayObj instanceof NaturalArray) { + NaturalArray natural = (NaturalArray) arrayObj; + long index = toLongIndex(indexObj); + + if (index < 0 || index >= natural.size()) { + throw new RuntimeException("Index out of bounds: " + index + + " for NaturalArray of size " + natural.size()); + } + + natural.set(index, newValue); + return newValue; + } + + // --- Handle traditional List --- + if (arrayObj instanceof List) { + int intIndex = toIntIndex(indexObj); + + List list = (List) arrayObj; + + if (intIndex < 0 || intIndex >= list.size()) { + throw new RuntimeException("Array index out of bounds: " + intIndex + + " (array size: " + list.size() + ")"); + } + + list.set(intIndex, newValue); + return newValue; + } + + throw new RuntimeException("Invalid assignment target: Cannot index non-array object. " + + "Expected NaturalArray or List, got: " + + (arrayObj != null ? arrayObj.getClass().getSimpleName() : "null")); + } + + // Handle Variable Assignment (LHS is a variable name) + else if (node.left.name != null) { + String varName = node.left.name; + + // REJECT assignment to underscore + if ("_".equals(varName)) { + throw new RuntimeException("Cannot assign to '_'. Underscore is reserved for discard/placeholder."); + } + + // 1. Check Locals (REASSIGNMENT only - declarations use := elsewhere) + if (ctx.locals.containsKey(varName)) { + if (ctx.localTypes.containsKey(varName)) { + String type = ctx.localTypes.get(varName); + if (!typeSystem.validateType(type, newValue)) { + throw new RuntimeException("Type mismatch in assignment. Variable '" + + varName + "' expects " + type + ", got: " + newValue); + } + if (type.contains("|")) { + String activeType = typeSystem.getConcreteType(typeSystem.unwrap(newValue)); + newValue = new TypedValue(newValue, activeType, type); + } + } + ctx.locals.put(varName, newValue); + return newValue; + } + + // 2. Check Object Fields + if (ctx.objectInstance.fields.containsKey(varName)) { + ctx.objectInstance.fields.put(varName, newValue); + return newValue; + } + + // 3. ERROR: Variable doesn't exist (use := for declaration) + throw new RuntimeException("Cannot assign to undefined variable: " + varName + + ". Use ':=' for declaration: " + varName + " := " + node.right); + } + + // Handle Slot Assignment in method returns? (If AssignmentNode is used there) + // Actually, SlotAssignmentNode is separate, so not here + + throw new RuntimeException("Invalid assignment target: " + + (node.left != null ? node.left.getClass().getSimpleName() : "null")); +} + + @Override + public Object visit(SlotDeclarationNode node) { + return null; + } + + @Override + public Object visit(MethodCallNode node) { + if (node.chainType != null && node.chainArguments != null) return evaluateConditionalChain(node); + ExecutionContext ctx = getCurrentContext(); + + MethodNode method = null; + if (ctx.objectInstance.type != null) { + for (MethodNode m : ctx.objectInstance.type.methods) { + if (m.name.equals(node.name)) { method = m; break; } + } + } + + Object result = interpreter.evalMethodCall(node, ctx.objectInstance, ctx.locals); + + if (node.slotNames != null && !node.slotNames.isEmpty()) { + if (!(result instanceof Map)) { + throw new RuntimeException("Cannot extract slot '" + node.slotNames.get(0) + "'. Method did not return slots."); + } + + Map map = (Map) result; + String requestedSlot = node.slotNames.get(0); + + if (!map.containsKey(requestedSlot) && method != null && method.returnSlots != null) { + try { + int index = Integer.parseInt(requestedSlot); + if (index >= 0 && index < method.returnSlots.size()) { + requestedSlot = method.returnSlots.get(index).name; + } + } catch (NumberFormatException e) { + // It was a non-numeric name, proceed to standard check. + } + } + + if (map.containsKey(requestedSlot)) { + return map.get(requestedSlot); + } else { + throw new RuntimeException("Undefined method slot: " + requestedSlot); + } + } + + return result; + } + +@Override +public Object visit(MultipleSlotAssignmentNode node) { + ExecutionContext ctx = getCurrentContext(); + + List declaredSlots = new ArrayList(ctx.slotTypes.keySet()); + + Object lastValue = null; + int slotIndex = 0; + + for (SlotAssignmentNode assign : node.assignments) { + Object value = dispatch(assign.value); + + String target; + if (assign.slotName != null && !assign.slotName.isEmpty() && !"_".equals(assign.slotName)) { + target = assign.slotName; + } else { + if (slotIndex < declaredSlots.size()) { + target = declaredSlots.get(slotIndex); + } else { + throw new RuntimeException("Too many positional slot assignments. Method has only " + declaredSlots.size() + " return slots."); + } + } + + if (ctx.slotValues.containsKey(target)) { + String declaredType = ctx.slotTypes.get(target); + + validateSlotType(ctx, target, value); + + if (declaredType.contains("|")) { + String activeType = typeSystem.getConcreteType(typeSystem.unwrap(value)); + value = new TypedValue(value, activeType, declaredType); + } + + ctx.slotValues.put(target, value); + ctx.slotsInCurrentPath.add(target); + } else { + throw new RuntimeException("Assignment to '" + target + "' failed: Slot is not declared."); + } + lastValue = value; + slotIndex++; + } + return lastValue; +} + +@Override +public Object visit(SlotAssignmentNode node) { + ExecutionContext ctx = getCurrentContext(); + Object value = dispatch(node.value); + String varName = node.slotName; + + String slotTarget; + if (varName != null && !varName.isEmpty() && !"_".equals(varName)) { + slotTarget = varName; + } else { + if (ctx.slotValues != null && !ctx.slotValues.isEmpty()) { + slotTarget = ctx.slotTypes.keySet().iterator().next(); + } else { + throw new RuntimeException("Assignment to implicit return failed: Method has no declared return slots."); + } + } + + if (ctx.slotValues != null && ctx.slotValues.containsKey(slotTarget)) { + String declaredType = ctx.slotTypes.get(slotTarget); + + validateSlotType(ctx, slotTarget, value); + + if (declaredType.contains("|")) { + String activeType = typeSystem.getConcreteType(typeSystem.unwrap(value)); + value = new TypedValue(value, activeType, declaredType); + } + + ctx.slotValues.put(slotTarget, value); + ctx.slotsInCurrentPath.add(slotTarget); + + } else { + throw new RuntimeException("Assignment to '" + (varName != null ? varName : "implicit") + "' failed: Slot '" + slotTarget + "' is not declared."); + } + return value; +} + + @Override + public Object visit(FieldNode node) { + Object val = node.value != null ? dispatch(node.value) : null; + getCurrentContext().objectInstance.fields.put(node.name, val); + return val; + } + + @Override + public Object visit(VarNode node) { + Object val = node.value != null ? dispatch(node.value) : null; + + if (node.explicitType != null) { + String declaredType = node.explicitType; + + getCurrentContext().localTypes.put(node.name, declaredType); + + if (!typeSystem.validateType(declaredType, val)) { + throw new RuntimeException("Type mismatch for variable " + node.name + ". Expected " + declaredType); + } + + if (declaredType.contains("|")) { + String activeType = typeSystem.getConcreteType(typeSystem.unwrap(val)); + val = new TypedValue(val, activeType, declaredType); + } + } + + getCurrentContext().locals.put(node.name, val); + return val; + } + + @Override + public Object visit(OutputNode node) { + Object lastVal = null; + for (ExprNode arg : node.arguments) { + Object val = dispatch(arg); + + val = typeSystem.unwrap(val); + + ioHandler.output(val); + lastVal = val; + } + return lastVal; + } + + @Override + public Object visit(ExitNode node) { + throw new Interpreter.EarlyExitException(); + } + + @Override + public Object visit(StmtIfNode node) { + Object testObj = dispatch(node.condition); + testObj = typeSystem.unwrap(testObj); + + boolean test = isTruthy(testObj); + List branch = test ? node.thenBlock.statements : node.elseBlock.statements; + + ExecutionContext ctx = getCurrentContext(); + Set prevSlots = new HashSet(ctx.slotsInCurrentPath); + + for (StmtNode s : branch) { + dispatch(s); + if (!ctx.slotsInCurrentPath.isEmpty() && interpreter.shouldReturnEarly(ctx.slotValues, ctx.slotsInCurrentPath)) break; + } + ctx.slotsInCurrentPath = prevSlots; + return null; + } + + @Override +public Object visit(ExprIfNode node) { + Object condValue = dispatch(node.condition); + condValue = typeSystem.unwrap(condValue); + + if (isTruthy(condValue)) { + return dispatch(node.thenExpr); + } else { + return dispatch(node.elseExpr); + } +} + +// UPDATED: ForNode uses BigDecimal for multiplicative/division loops +@Override +public Object visit(ForNode node) { + ExecutionContext ctx = getCurrentContext(); + String iter = node.iterator; + + Object startObj = dispatch(node.range.start); + Object endObj = dispatch(node.range.end); + + startObj = typeSystem.unwrap(startObj); + endObj = typeSystem.unwrap(endObj); + + // Check if step is multiplicative/division (i * 2, i / 2) + if (node.range.step != null && node.range.step instanceof BinaryOpNode) { + BinaryOpNode binOp = (BinaryOpNode) node.range.step; + + if (binOp.left instanceof ExprNode && + ((ExprNode)binOp.left).name != null && + ((ExprNode)binOp.left).name.equals(iter) && + (binOp.op.equals("*") || binOp.op.equals("/"))) { + + Object rightObj = dispatch(binOp.right); + rightObj = typeSystem.unwrap(rightObj); + + // Use BigDecimal for factor to support precise multiplicative/divisive steps (e.g., *1.01) + BigDecimal factorBD = typeSystem.toBigDecimal(rightObj); + + if (factorBD.compareTo(BigDecimal.ZERO) == 0 && binOp.op.equals("/")) { + throw new RuntimeException("Division by zero in loop step"); + } + + return executeMultiplicativeLoop(ctx, node, startObj, endObj, factorBD, binOp.op); + } + } + + // Regular additive step + BigDecimal stepBD = BigDecimal.ONE; + if (node.range.step != null) { + Object stepObj = dispatch(node.range.step); + stepObj = typeSystem.unwrap(stepObj); + stepBD = typeSystem.toBigDecimal(stepObj); + } else { + // Default step based on direction + BigDecimal startBD = typeSystem.toBigDecimal(startObj); + BigDecimal endBD = typeSystem.toBigDecimal(endObj); + stepBD = (startBD.compareTo(endBD) > 0) ? BigDecimal.ONE.negate() : BigDecimal.ONE; + } + + if (stepBD.compareTo(BigDecimal.ZERO) == 0) throw new RuntimeException("Loop step cannot be zero."); + + return executeAdditiveLoop(ctx, node, startObj, endObj, stepBD); +} + +// UPDATED: executeAdditiveLoop to use BigDecimal +private Object executeAdditiveLoop(ExecutionContext ctx, ForNode node, + Object startObj, Object endObj, BigDecimal stepBD) { + String iter = node.iterator; + + BigDecimal startBD = typeSystem.toBigDecimal(startObj); + BigDecimal endBD = typeSystem.toBigDecimal(endObj); + BigDecimal current = startBD; + + boolean increasing = stepBD.compareTo(BigDecimal.ZERO) > 0; + + while (true) { + // Check termination condition using precise comparison + if (increasing && current.compareTo(endBD) > 0) break; + if (!increasing && current.compareTo(endBD) < 0) break; + + // Store the current value, converting back to Integer/Long if possible + Object currentValue = convertToAppropriateType(current, startObj); + + ctx.locals.put(iter, currentValue); + if (!ctx.localTypes.containsKey(iter)) { + // Use INT if it's an exact integer, otherwise FLOAT + String inferredType = (currentValue instanceof Integer || currentValue instanceof Long) ? INT.toString() : FLOAT.toString(); + ctx.localTypes.put(iter, inferredType); + } + + for (StmtNode s : node.body.statements) { + dispatch(s); + if (!ctx.slotsInCurrentPath.isEmpty() && + interpreter.shouldReturnEarly(ctx.slotValues, ctx.slotsInCurrentPath)) { + return null; + } + } + + // Update iterator precisely + current = current.add(stepBD); + } + return null; +} + +// UPDATED: executeMultiplicativeLoop to use BigDecimal +private Object executeMultiplicativeLoop(ExecutionContext ctx, ForNode node, + Object startObj, Object endObj, + BigDecimal factorBD, String operation) { + String iter = node.iterator; + + BigDecimal startBD = typeSystem.toBigDecimal(startObj); + BigDecimal endBD = typeSystem.toBigDecimal(endObj); + BigDecimal current = startBD; + + while (true) { + // Check termination condition based on operation and direction + boolean shouldContinue = false; + + if (operation.equals("*")) { + if (factorBD.compareTo(BigDecimal.ONE) > 0) { + // Growing sequence + shouldContinue = (startBD.compareTo(endBD) < 0) ? current.compareTo(endBD) <= 0 : current.compareTo(endBD) >= 0; + } else if (factorBD.compareTo(BigDecimal.ZERO) > 0 && factorBD.compareTo(BigDecimal.ONE) < 0) { + // Shrinking sequence + shouldContinue = (startBD.compareTo(endBD) > 0) ? current.compareTo(endBD) >= 0 : current.compareTo(endBD) <= 0; + } else { + throw new RuntimeException("Unsupported multiplication factor in loops"); + } + } else if (operation.equals("/")) { + + if (factorBD.compareTo(BigDecimal.ONE) > 0) { + // Shrinking sequence (multiplying by a fraction) + shouldContinue = (startBD.compareTo(endBD) > 0) ? current.compareTo(endBD) >= 0 : current.compareTo(endBD) <= 0; + } else if (factorBD.compareTo(BigDecimal.ZERO) > 0 && factorBD.compareTo(BigDecimal.ONE) < 0) { + // Growing sequence (multiplying by a number > 1) + shouldContinue = (startBD.compareTo(endBD) < 0) ? current.compareTo(endBD) <= 0 : current.compareTo(endBD) >= 0; + } else { + throw new RuntimeException("Unsupported division factor in loops"); + } + } + + if (!shouldContinue) break; + + // Store current value + Object currentValue = convertToAppropriateType(current, startObj); + + ctx.locals.put(iter, currentValue); + if (!ctx.localTypes.containsKey(iter)) { + String inferredType = (currentValue instanceof Integer || currentValue instanceof Long) ? INT.toString() : FLOAT.toString(); + ctx.localTypes.put(iter, inferredType); + } + + for (StmtNode s : node.body.statements) { + dispatch(s); + if (!ctx.slotsInCurrentPath.isEmpty() && + interpreter.shouldReturnEarly(ctx.slotValues, ctx.slotsInCurrentPath)) { + return null; + } + } + + // Apply precise multiplicative operation + if (operation.equals("*")) { + current = current.multiply(factorBD); + } else if (operation.equals("/")) { + current = current.divide(factorBD, DECIMAL_SCALE, RoundingMode.HALF_UP); + } + } + return null; +} + + @Override +public Object visit(ReturnSlotAssignmentNode node) { + ExecutionContext ctx = getCurrentContext(); + Object res = interpreter.evalMethodCall(node.methodCall, ctx.objectInstance, ctx.locals); + + if (res instanceof Map) { + Map map = (Map) res; + + MethodNode method = null; + if (ctx.objectInstance.type != null) { + for (MethodNode m : ctx.objectInstance.type.methods) { + if (m.name.equals(node.methodCall.name)) { + method = m; + break; + } + } + } + + for (int i = 0; i < node.variableNames.size(); i++) { + String slot = node.methodCall.slotNames.get(i); + + String requestedSlot = slot; + if (!map.containsKey(requestedSlot) && method != null && method.returnSlots != null) { + try { + int index = Integer.parseInt(requestedSlot); + if (index >= 0 && index < method.returnSlots.size()) { + requestedSlot = method.returnSlots.get(index).name; + } + } catch (NumberFormatException e) { + } + } + + if (map.containsKey(requestedSlot)) { + Object val = map.get(requestedSlot); + ctx.locals.put(node.variableNames.get(i), val); + } else { + throw new RuntimeException("Missing slot: " + slot + " (resolved as: " + requestedSlot + ")"); + } + } + } else { + throw new RuntimeException("Method did not return slot values"); + } + return res; +} + + private Object evaluateConditionalChain(MethodCallNode call) { + boolean result = ALL.toString().equals(call.chainType); + + for (ExprNode arg : call.chainArguments) { + MethodCallNode currentCall = ASTFactory.createMethodCall(call.name, call.qualifiedName); + currentCall.arguments = new ArrayList(); + + if (arg instanceof ArgumentListNode) { + currentCall.arguments.addAll(((ArgumentListNode) arg).arguments); + } else { + currentCall.arguments.add(arg); + } + + boolean negated = false; + + if (arg instanceof UnaryNode && "!".equals(((UnaryNode) arg).op)) { + negated = true; + } + + Object methodResultObj = dispatch(currentCall); + methodResultObj = typeSystem.unwrap(methodResultObj); + + boolean methodResult = isTruthy(methodResultObj); + boolean finalResult = negated ? !methodResult : methodResult; + + if (ALL.toString().equals(call.chainType)) { + if (!finalResult) return false; + } else { + if (finalResult) return true; + } + } + return result; + } + +private boolean isTruthy(Object value) { + if (value instanceof TypedValue) throw new RuntimeException("isTruthy called on unwrapped value."); + + if (value == null) return false; + if (value instanceof Boolean) return (Boolean) value; + if (value instanceof Number) { + if (value instanceof BigDecimal) return ((BigDecimal) value).compareTo(BigDecimal.ZERO) != 0; + return ((Number) value).doubleValue() != 0.0; + } + if (value instanceof String) { + String str = (String) value; + if (str.equalsIgnoreCase("true")) return true; + if (str.equalsIgnoreCase("false")) return false; + return !str.isEmpty(); + } + if (value instanceof List) return !((List) value).isEmpty(); + if (value instanceof NaturalArray) return ((NaturalArray) value).size() > 0; + return true; +} + + private void validateSlotType(ExecutionContext ctx, String slotName, Object value) { + if (ctx.slotTypes == null || !ctx.slotTypes.containsKey(slotName) || value == null) return; + String type = ctx.slotTypes.get(slotName); + if (!typeSystem.validateType(type, value)) { + throw new RuntimeException("Type mismatch: " + slotName + " expected " + type); + } + } +} \ No newline at end of file diff --git a/src/main/java/cod/interpreter/NaturalArray.java b/src/main/java/cod/interpreter/NaturalArray.java new file mode 100644 index 00000000..963732e6 --- /dev/null +++ b/src/main/java/cod/interpreter/NaturalArray.java @@ -0,0 +1,627 @@ +package cod.interpreter; + +import cod.ast.nodes.*; +import java.util.*; +import java.math.BigDecimal; +import java.math.RoundingMode; + +public class NaturalArray { + + // Core formula + private final RangeNode baseRange; + private final InterpreterVisitor visitor; + + // Mutation support + private Map cache; + private boolean isMutable = false; + + // Cached calculations - PRESERVE ORIGINAL TYPES + private Object cachedStart = null; + private Object cachedEnd = null; + private Object cachedStep = null; + private Long cachedSize = null; + + // Type tracking (Determines if we use fast long arithmetic or precise BigDecimal) + private boolean isLongOrIntegerRange = false; + + // === HIERARCHICAL LEXICOGRAPHICAL RANGE FIELDS === + private boolean isLexicographicalRange = false; + private String startString = null; + private String endString = null; + + // === PERFORMANCE OPTIMIZATIONS === + private Long cachedStartLong = null; + private Long cachedStepLong = null; + private BigDecimal cachedStartBD = null; + private BigDecimal cachedStepBD = null; + + // Single-element cache for sequential access + private transient Long lastIndex = null; + private transient Object lastValue = null; + + // Precomputed powers of 26 for performance (up to length 10) + private static final long[] POWERS_26 = new long[11]; + private static final long[] POWERS_2 = new long[11]; + private static final long[] TOTAL_UP_TO_LENGTH = new long[11]; + + static { + // Precompute for performance + POWERS_26[0] = 1; + POWERS_2[0] = 1; + for (int i = 1; i <= 10; i++) { + POWERS_26[i] = POWERS_26[i-1] * 26; + POWERS_2[i] = POWERS_2[i-1] * 2; + } + + // Total sequences up to each length: sum_{i=1}^{len} (2^i × 26^i) + TOTAL_UP_TO_LENGTH[0] = 0; + for (int len = 1; len <= 10; len++) { + TOTAL_UP_TO_LENGTH[len] = TOTAL_UP_TO_LENGTH[len-1] + + POWERS_2[len] * POWERS_26[len]; + } + } + + public NaturalArray(RangeNode range, InterpreterVisitor visitor) { + this.baseRange = range; + this.visitor = visitor; + this.cache = null; + this.isMutable = false; + + // Eagerly check type on construction + getStart(); + getEnd(); + getStep(); + + // Check if this is a lexicographical range + // A range is lexicographical if start and end are strings AND step is 1 or implicit (null) + if (cachedStart instanceof String && cachedEnd instanceof String && + (baseRange.step == null || (cachedStep instanceof Long && ((Long)cachedStep).longValue() == 1L))) { + + this.isLexicographicalRange = true; + this.startString = (String) cachedStart; + this.endString = (String) cachedEnd; + this.isLongOrIntegerRange = false; // Must be false for string range + + // Validate the alphabet. We allow only a-z and A-Z. + if (!isValidLexString(startString) || !isValidLexString(endString)) { + throw new RuntimeException("Lexicographical range bounds must contain only letters (a-z, A-Z)."); + } + + // For hierarchical ordering, start must be <= end + long startIdx = hierarchicalSequenceToIndex(startString); + long endIdx = hierarchicalSequenceToIndex(endString); + if (startIdx > endIdx) { + throw new RuntimeException("Lexicographical range start must come before end in hierarchical ordering."); + } + } + } + + // --- IMMUTABLE OPERATIONS --- + + public Object get(long index) { + checkBounds(index); + + // 1. Single-element cache (extremely fast for sequential access) + if (lastIndex != null && lastIndex == index) { + return lastValue; + } + + // 2. FAST PATH: If not mutable, calculate directly (no HashMap checks!) + if (!isMutable) { + Object result = calculateValue(index); + lastIndex = index; + lastValue = result; + return result; + } + + // 3. Only check cache if we're actually mutable + if (cache != null) { + Object val = cache.get(index); // Single HashMap lookup + if (val != null) { + lastIndex = index; + lastValue = val; + return val; + } + } + + // 4. Calculate and cache + Object result = calculateValue(index); + lastIndex = index; + lastValue = result; + return result; + } + + public long size() { + if (cachedSize != null) return cachedSize; + + if (isLexicographicalRange) { + // HIERARCHICAL Lexicographical Size + long size = calculateLexSize(); + cachedSize = size; + return size; + } + + if (isLongOrIntegerRange) { + // Use fast long arithmetic with cached values + long startVal = getStartLong(); + long endVal = getEndLong(); + long stepVal = getStepLong(); + + if (stepVal == 0) { cachedSize = 0L; return 0; } + if ((stepVal > 0 && startVal > endVal) || (stepVal < 0 && startVal < endVal)) { + cachedSize = 0L; return 0; + } + + // Fix: Add 1 correctly for the range + long diff = Math.abs(endVal - startVal); + long count = diff / Math.abs(stepVal) + 1; + cachedSize = count; + return count; + } + + // Use precise BigDecimal arithmetic + BigDecimal startBD = getStartBD(); + BigDecimal endBD = getEndBD(); + BigDecimal stepBD = getStepBD(); + + if (stepBD.compareTo(BigDecimal.ZERO) == 0) { + cachedSize = 0L; + return 0; + } + + // Check if range is valid + boolean increasing = stepBD.compareTo(BigDecimal.ZERO) > 0; + if ((increasing && startBD.compareTo(endBD) > 0) || (!increasing && startBD.compareTo(endBD) < 0)) { + cachedSize = 0L; + return 0; + } + + // Calculate size: abs((end - start) / step) + 1 + BigDecimal diff = endBD.subtract(startBD); + BigDecimal absStep = stepBD.abs(); + + BigDecimal sizeBD = diff.abs().divide(absStep, 0, RoundingMode.DOWN).add(BigDecimal.ONE); + + // Check if it fits in long + if (sizeBD.compareTo(new BigDecimal(Long.MAX_VALUE)) > 0) { + throw new RuntimeException("Array size too large: exceeds long maximum."); + } + + long size = sizeBD.longValue(); + cachedSize = size; + return size; + } + + // --- MUTABLE OPERATIONS --- + + public void set(long index, Object value) { + checkBounds(index); + + // Clear single-element cache (it's now invalid) + lastIndex = null; + lastValue = null; + + if (!isMutable) { + becomeMutable(); + } + + if (cache == null) { + cache = new HashMap(); + } + + cache.put(index, value); + } + + // --- PRIVATE HELPERS --- + + private void checkBounds(long index) { + if (index < 0 || index >= size()) { + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size()); + } + } + + private Object calculateValue(long index) { + if (isLexicographicalRange) { + // HIERARCHICAL Lexicographical Value Calculation + return calculateLexValue(index); + } + + if (isLongOrIntegerRange) { + // Use CACHED long arithmetic for integer/long ranges (fast path) + long startVal = getStartLong(); + long stepVal = getStepLong(); + long result = startVal + index * stepVal; + + if (cachedStart instanceof Integer && result >= Integer.MIN_VALUE && result <= Integer.MAX_VALUE) { + return (int) result; + } + return result; + } + + // Use precise BigDecimal arithmetic for floating point ranges + BigDecimal startBD = getStartBD(); + BigDecimal stepBD = getStepBD(); + BigDecimal indexBD = BigDecimal.valueOf(index); + + // Calculation: start + index * step + BigDecimal resultBD = startBD.add(indexBD.multiply(stepBD)); + + // Return the BigDecimal object + return resultBD; + } + + // === PERFORMANCE OPTIMIZATION METHODS === + + private long getStartLong() { + if (cachedStartLong == null) { + cachedStartLong = toLong(getStart()); + } + return cachedStartLong; + } + + private long getStepLong() { + if (cachedStepLong == null) { + cachedStepLong = toLong(getStep()); + } + return cachedStepLong; + } + + private long getEndLong() { + // Not cached by default, but used in size() + return toLong(getEnd()); + } + + private BigDecimal getStartBD() { + if (cachedStartBD == null) { + cachedStartBD = toBigDecimal(getStart()); + } + return cachedStartBD; + } + + private BigDecimal getStepBD() { + if (cachedStepBD == null) { + cachedStepBD = toBigDecimal(getStep()); + } + return cachedStepBD; + } + + private BigDecimal getEndBD() { + // Not cached by default, but used in size() + return toBigDecimal(getEnd()); + } + + // === HIERARCHICAL LEXICOGRAPHICAL LOGIC (OPTIMIZED & MERGED) === + + private boolean isValidLexString(String s) { + for (char c : s.toCharArray()) { + if (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z')) { + return false; + } + } + return true; + } + + /** + * HIERARCHICAL: Converts string to its global index. (OPTIMIZED SINGLE-PASS) + */ + private long hierarchicalSequenceToIndex(String s) { + int n = s.length(); + if (n > 10) { + throw new RuntimeException("String too long for lexicographical range (max 10 characters)"); + } + + // 1. Add all sequences of shorter lengths + long index = TOTAL_UP_TO_LENGTH[n - 1]; + + // 2. SINGLE PASS: Compute pattern AND content simultaneously + long patternMask = 0; + long contentIndex = 0; + + for (int i = 0; i < n; i++) { + char c = s.charAt(i); + + // Fast uppercase check (ASCII: 'A'-'Z' < 'a') + boolean isUpper = c < 'a'; + + if (isUpper) { + // Set pattern bit (MSB is position 0) + patternMask |= (1L << (n - 1 - i)); + + // Fast lowercase conversion for content calculation + c += 32; + } + + // Fast char to 0-25 digit (ASCII math: 'a'-'a'=0) + int digit = c - 'a'; + + // Base 26 content calculation (Horner's method) + contentIndex = contentIndex * 26 + digit; + } + + // 3. Position: index + patternIndex * 26^n + contentIndex + return index + (patternMask * POWERS_26[n]) + contentIndex; + } + + /** + * HIERARCHICAL: Converts global index back to string. (OPTIMIZED SINGLE-PASS) + */ + private String hierarchicalIndexToSequence(long globalIndex) { + // 1. Find length n + int n = 1; + while (n <= 10 && globalIndex >= TOTAL_UP_TO_LENGTH[n]) { + n++; + } + + if (n > 10) { + throw new RuntimeException("Index too large for lexicographical range"); + } + + // 2. Index within length group + long indexInLengthGroup = globalIndex - TOTAL_UP_TO_LENGTH[n - 1]; + + // 3. Split into pattern and content + long stringsPerPattern = POWERS_26[n]; + long patternIndex = indexInLengthGroup / stringsPerPattern; + long contentIndex = indexInLengthGroup % stringsPerPattern; + + // 4. Convert contentIndex to base-26 digits and build array (Right-to-Left) + char[] chars = new char[n]; + long temp = contentIndex; + + for (int i = n - 1; i >= 0; i--) { + int digit = (int)(temp % 26); + chars[i] = (char)('a' + digit); // Base lowercase character + temp /= 26; + } + + // 5. Apply case pattern using fast bit checks + for (int i = 0; i < n; i++) { + // Check uppercase bit: MSB = position 0 (first character) + if (((patternIndex >> (n - 1 - i)) & 1) == 1) { + // Fast uppercase: clear bit 5 (32) + chars[i] &= ~32; + } + } + + return new String(chars); + } + + /** + * Calculate lexicographical size using HIERARCHICAL ordering. + */ + private long calculateLexSize() { + long startIndex = hierarchicalSequenceToIndex(startString); + long endIndex = hierarchicalSequenceToIndex(endString); + + return endIndex - startIndex + 1; + } + + /** + * Calculate lexicographical value using HIERARCHICAL ordering. + */ + private String calculateLexValue(long index) { + long startIndex = hierarchicalSequenceToIndex(startString); + long targetIndex = startIndex + index; + return hierarchicalIndexToSequence(targetIndex); + } + + private void becomeMutable() { + this.isMutable = true; + } + + // FIXED: Now detects integer BigDecimal ranges for fast path + private Object getStart() { + if (cachedStart != null) return cachedStart; + Object startObj = visitor.dispatch(baseRange.start); + + // Check if it's an integer (Integer, Long, or integer BigDecimal) + if (startObj instanceof Integer || startObj instanceof Long) { + isLongOrIntegerRange = true; + } else if (startObj instanceof BigDecimal) { + BigDecimal bd = (BigDecimal) startObj; + // Check if it's an exact integer + try { + bd.longValueExact(); // Will throw if not integer + isLongOrIntegerRange = true; // Integer BigDecimal! + } catch (ArithmeticException e) { + isLongOrIntegerRange = false; // Fractional BigDecimal + } + } else { + isLongOrIntegerRange = false; + } + + cachedStart = startObj; + return cachedStart; + } + + // FIXED: Now handles BigDecimal in type detection + private Object getEnd() { + if (cachedEnd != null) return cachedEnd; + Object endObj = visitor.dispatch(baseRange.end); + + // Update isLongOrIntegerRange based on end type + if (isLongOrIntegerRange) { // Only update if still true + if (!(endObj instanceof Integer || endObj instanceof Long)) { + if (endObj instanceof BigDecimal) { + BigDecimal bd = (BigDecimal) endObj; + try { + bd.longValueExact(); // Check if integer + // Keep isLongOrIntegerRange = true + } catch (ArithmeticException e) { + isLongOrIntegerRange = false; // Fractional + } + } else { + isLongOrIntegerRange = false; + } + } + } + + cachedEnd = endObj; + return cachedEnd; + } + + // FIXED: Now handles BigDecimal in type detection + private Object getStep() { + if (cachedStep != null) return cachedStep; + + if (baseRange.step != null) { + Object stepObj = visitor.dispatch(baseRange.step); + + // Update isLongOrIntegerRange based on step type + if (isLongOrIntegerRange) { // Only update if still true + if (!(stepObj instanceof Integer || stepObj instanceof Long)) { + if (stepObj instanceof BigDecimal) { + BigDecimal bd = (BigDecimal) stepObj; + try { + bd.longValueExact(); // Check if integer + // Keep isLongOrIntegerRange = true + } catch (ArithmeticException e) { + isLongOrIntegerRange = false; // Fractional + } + } else { + isLongOrIntegerRange = false; + } + } + } + + cachedStep = stepObj; + } else { + // Default step calculation (always 1 or -1) is always integer/long + + if (isLongOrIntegerRange) { + long startVal = getStartLong(); + long endVal = getEndLong(); + cachedStep = (startVal <= endVal) ? 1L : -1L; + } else { + // If not integer range, but no step is given, still default to 1.0 or -1.0 + BigDecimal startVal = getStartBD(); + BigDecimal endVal = getEndBD(); + cachedStep = (startVal.compareTo(endVal) <= 0) ? BigDecimal.ONE : BigDecimal.ONE.negate(); + } + } + + return cachedStep; + } + + // FIXED: Better handling of BigDecimal to long conversion + private long toLong(Object obj) { + if (obj instanceof BigDecimal) { + BigDecimal bd = (BigDecimal) obj; + if (isLongOrIntegerRange) { + // Only require exact for integer ranges + try { + return bd.longValueExact(); + } catch (ArithmeticException e) { + throw new RuntimeException("Cannot convert non-integer BigDecimal to long: " + bd); + } + } else { + // For fractional ranges, truncate (but this shouldn't be called in that case) + return bd.longValue(); + } + } + if (obj instanceof Integer) return ((Integer) obj).longValue(); + if (obj instanceof Long) return (Long) obj; + throw new RuntimeException("Cannot convert to long: " + obj); + } + + private BigDecimal toBigDecimal(Object obj) { + if (obj instanceof BigDecimal) return (BigDecimal) obj; + if (obj instanceof String) { + // Check if we're in a lexicographical range context + if (cachedStart instanceof String || cachedEnd instanceof String) { + // For lexicographical ranges, return a default value + // This prevents the error when calculating size or other numeric operations + return BigDecimal.ZERO; + } + throw new RuntimeException("Cannot convert string to BigDecimal for numeric calculation."); + } + return BigDecimal.valueOf(((Number)obj).doubleValue()); + } + + // --- UTILITIES --- + + public boolean isMutable() { + return isMutable; + } + + public int getCacheSize() { + return cache != null ? cache.size() : 0; + } + + public double getCacheRatio() { + long size = size(); + if (size == 0) return 0.0; + return cache != null ? (double) cache.size() / size : 0.0; + } + + @Override + public String toString() { + // Always show as formula, never materialize + Object start = getStart(); + Object end = getEnd(); + Object step = getStep(); + + String formula; + + if (isLexicographicalRange) { + formula = String.format("LexArray[\"%s\" to \"%s\"]", start, end); + } else { + formula = String.format("NaturalArray[%s to %s", start, end); + } + + boolean isDefaultStep = false; + if (!isLexicographicalRange) { + if (isLongOrIntegerRange) { + long s = getStepLong(); + long st = getStartLong(); + long en = getEndLong(); + isDefaultStep = (s == 1L && st <= en) || (s == -1L && st > en); + } else { + BigDecimal s = getStepBD(); + BigDecimal st = getStartBD(); + BigDecimal en = getEndBD(); + boolean increasing = st.compareTo(en) <= 0; + isDefaultStep = (s.compareTo(BigDecimal.ONE) == 0 && increasing) || (s.compareTo(BigDecimal.ONE.negate()) == 0 && !increasing); + } + } + + if (!isDefaultStep) { + formula += " step " + step; + } + + if (!isLexicographicalRange) { + formula += "]"; + } + + // Add state info + formula += String.format(" (size: %d", size()); + if (isMutable) { + formula += ", mutable, cache: " + getCacheSize() + "/" + size(); + } else { + formula += ", immutable"; + } + formula += ")"; + + // For VERY small arrays, optionally show values + if (size() <= 5) { + StringBuilder sb = new StringBuilder("["); + for (long i = 0; i < size(); i++) { + if (i > 0) sb.append(", "); + sb.append(get(i)); + } + sb.append("]"); + return sb.toString(); + } + + return formula; + } + + // Convert to traditional List (eager materialization) + public List toList() { + List result = new ArrayList(); + for (long i = 0; i < size(); i++) { + result.add(get(i)); + } + return result; + } +} \ No newline at end of file diff --git a/src/main/java/cod/interpreter/ObjectInstance.java b/src/main/java/cod/interpreter/ObjectInstance.java index 902d1338..2496ded3 100644 --- a/src/main/java/cod/interpreter/ObjectInstance.java +++ b/src/main/java/cod/interpreter/ObjectInstance.java @@ -11,4 +11,4 @@ public class ObjectInstance { public ObjectInstance(TypeNode type) { this.type = type; } -} +} \ No newline at end of file diff --git a/src/main/java/cod/interpreter/StatementEvaluator.java b/src/main/java/cod/interpreter/StatementEvaluator.java deleted file mode 100644 index 66e55147..00000000 --- a/src/main/java/cod/interpreter/StatementEvaluator.java +++ /dev/null @@ -1,515 +0,0 @@ -package cod.interpreter; - -import cod.ast.nodes.*; -import cod.debug.DebugSystem; -import java.util.*; - -public class StatementEvaluator { - private Interpreter interpreter; - private ExpressionEvaluator exprEvaluator; - private IOHandler ioHandler; - private TypeSystem typeSystem; - - public StatementEvaluator(Interpreter interpreter, ExpressionEvaluator exprEvaluator, - IOHandler ioHandler, TypeSystem typeSystem) { - this.interpreter = interpreter; - this.exprEvaluator = exprEvaluator; - this.ioHandler = ioHandler; - this.typeSystem = typeSystem; - } - - public Object evalStmt(StatementNode stmt, ObjectInstance obj, Map locals, Map slotValues) { - DebugSystem.trace("INTERPRETER", "evalStmt: " + stmt.getClass().getSimpleName()); - - if (stmt instanceof SlotAssignmentNode) { - SlotAssignmentNode assign = (SlotAssignmentNode) stmt; - Object value = exprEvaluator.evaluate(assign.value, obj, locals); - String varName = assign.slotName; - - // --- NEW: Handle implicit returns --- - if ("return".equals(varName)) { - if (slotValues != null && !slotValues.isEmpty()) { - // Assign to the first declared slot - String firstSlot = slotValues.keySet().iterator().next(); - - // Validate Type - validateSlotType(firstSlot, value); - - Object oldValue = slotValues.get(firstSlot); - slotValues.put(firstSlot, value); - DebugSystem.slotUpdate(firstSlot, oldValue, value); - } - return value; - } - - // Check if slot exists - if (slotValues != null && slotValues.containsKey(varName)) { - // Enforce: If slot name is auto-generated (starts with digit), user cannot assign to it by name - if (Character.isDigit(varName.charAt(0))) { - throw new RuntimeException("Cannot assign to unnamed slot '" + varName + "' by name. Use positional assignment."); - } - - // Validate Type - validateSlotType(varName, value); - - Object oldValue = slotValues.get(varName); - slotValues.put(varName, value); - DebugSystem.slotUpdate(varName, oldValue, value); - } else { - // CHANGED: Warn -> Exception - throw new RuntimeException("Assignment to '" + varName + "' failed: Slot is not declared in method signature."); - } - return value; - - } else if (stmt instanceof MultipleSlotAssignmentNode) { - MultipleSlotAssignmentNode multiAssign = (MultipleSlotAssignmentNode) stmt; - - // Validate assignment count matches declared slots - List declaredSlots = new ArrayList(); - if (slotValues != null) { - // Sort keys if they look like indices "0", "1" to ensure positional correctness - List keys = new ArrayList<>(slotValues.keySet()); - Collections.sort(keys, new Comparator() { - @Override - public int compare(String o1, String o2) { - if (Character.isDigit(o1.charAt(0)) && Character.isDigit(o2.charAt(0))) { - try { - return Integer.valueOf(o1).compareTo(Integer.valueOf(o2)); - } catch (NumberFormatException e) {} - } - return o1.compareTo(o2); - } - }); - declaredSlots.addAll(keys); - } - - if (multiAssign.assignments.size() > declaredSlots.size()) { - throw new RuntimeException("Too many return assignments: declared " + - declaredSlots.size() + " slots but assigned " + - multiAssign.assignments.size() + " values"); - } - - Object lastValue = null; - int slotIndex = 0; - - for (SlotAssignmentNode assign : multiAssign.assignments) { - Object value = exprEvaluator.evaluate(assign.value, obj, locals); - String targetSlotName = null; - - if (assign.slotName != null) { - // Named assignment - targetSlotName = assign.slotName; - } else { - // Positional assignment - if (slotIndex < declaredSlots.size()) { - targetSlotName = declaredSlots.get(slotIndex); - } - } - - if (targetSlotName != null && slotValues.containsKey(targetSlotName)) { - // Enforce restriction: If doing Named assignment to Unnamed slot - if (assign.slotName != null && Character.isDigit(targetSlotName.charAt(0))) { - throw new RuntimeException("Cannot use named assignment '" + assign.slotName + "' for an unnamed slot."); - } - - // Validate Type - validateSlotType(targetSlotName, value); - - Object oldValue = slotValues.get(targetSlotName); - slotValues.put(targetSlotName, value); - DebugSystem.slotUpdate(targetSlotName, oldValue, value); - } else { - throw new RuntimeException("Slot '" + (targetSlotName != null ? targetSlotName : "index " + slotIndex) + "' not found."); - } - - lastValue = value; - slotIndex++; - } - - return lastValue; - - } else if (stmt instanceof InputNode) { - // Handle input: name = (type) input - InputNode input = (InputNode) stmt; - Object inputValue = ioHandler.readInput(input.targetType); - - if (slotValues != null && slotValues.containsKey(input.variableName)) { - throw new RuntimeException("Cannot assign to slot '" + input.variableName + "' using '=' (from input). Use '~> " + input.variableName + " ...' syntax instead."); - } else { - obj.fields.put(input.variableName, inputValue); - DebugSystem.fieldUpdate(input.variableName, inputValue); - } - locals.put(input.variableName, inputValue); - return inputValue; - - } else if (stmt instanceof AssignmentNode) { - AssignmentNode assignment = (AssignmentNode) stmt; - Object value = exprEvaluator.evaluate(assignment.right, obj, locals); - - if (assignment.right instanceof MethodCallNode) { - MethodCallNode methodCall = (MethodCallNode) assignment.right; - if (methodCall.slotNames != null && !methodCall.slotNames.isEmpty() && value instanceof Map) { - Map slotReturns = (Map) value; - String slotName = methodCall.slotNames.get(0); - - // Handle auto-numbered slots in calls: [0]:method() - if (!slotReturns.containsKey(slotName) && slotReturns.containsKey("0")) { - // If user asked for [name] but we have [0], checks fail unless mapped. - // But per requirements, user should use [0] if unnamed. - } - - if (slotReturns.containsKey(slotName)) { - value = slotReturns.get(slotName); - } - } - } - - if (assignment.left instanceof IndexAccessNode) { - IndexAccessNode indexAccess = (IndexAccessNode) assignment.left; - Object arrayObj = exprEvaluator.evaluate(indexAccess.array, obj, locals); - Object indexObj = exprEvaluator.evaluate(indexAccess.index, obj, locals); - - if (arrayObj instanceof List && indexObj instanceof Integer) { - List list = (List) arrayObj; - int index = (Integer) indexObj; - - if (index >= 0 && index < list.size()) { - list.set(index, value); - } else { - throw new RuntimeException("Array index out of bounds: " + index + " for array size " + list.size()); - } - } - } else if (assignment.left instanceof ExprNode) { - ExprNode target = (ExprNode) assignment.left; - if (target.name != null) { - String varName = target.name; - - if (slotValues != null && slotValues.containsKey(varName)) { - throw new RuntimeException("Cannot assign to slot '" + varName + "' using '='. Use '~ " + varName + " ...' syntax instead."); - } else if (locals.containsKey(varName)) { - locals.put(varName, value); - } else { - obj.fields.put(varName, value); - } - if (locals.containsKey(varName)) locals.put(varName, value); - } - } - - return value; - - } else if (stmt instanceof FieldNode) { - FieldNode f = (FieldNode) stmt; - Object val = f.value != null ? exprEvaluator.evaluate(f.value, obj, locals) : null; - - if (slotValues != null && slotValues.containsKey(f.name)) { - throw new RuntimeException("Cannot assign to slot '" + f.name + "' using '=' (in field declaration). Use '~> " + f.name + " ...' syntax instead."); - } else { - obj.fields.put(f.name, val); - } - return val; - - } else if (stmt instanceof VarNode) { - VarNode var = (VarNode) stmt; - Object val = var.value != null ? exprEvaluator.evaluate(var.value, obj, locals) : null; - - if (slotValues != null && slotValues.containsKey(var.name)) { - throw new RuntimeException("Cannot declare variable '" + var.name + "' because it conflicts with a return slot. Use '~> " + var.name + " ...' to assign to the slot."); - } - - locals.put(var.name, val); - return val; - - } else if (stmt instanceof OutputNode) { - OutputNode output = (OutputNode) stmt; - Object lastVal = null; - - for (ExprNode arg : output.arguments) { - if (arg instanceof MethodCallNode) { - MethodCallNode methodCall = (MethodCallNode) arg; - Object result = interpreter.evalMethodCall(methodCall, obj, locals); - - if (result instanceof Map) { - Map slotReturns = (Map) result; - - if (output.varName != null) { - lastVal = slotReturns.get(output.varName); - } else { - if (methodCall.slotNames != null && !methodCall.slotNames.isEmpty()) { - if (methodCall.slotNames.size() == 1) { - lastVal = slotReturns.get(methodCall.slotNames.get(0)); - } else { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < methodCall.slotNames.size(); i++) { - if (i > 0) sb.append(", "); - String slotName = methodCall.slotNames.get(i); - Object value = slotReturns.get(slotName); - sb.append(value != null ? value.toString() : "null"); - } - lastVal = sb.toString(); - } - } else { - if (slotReturns.size() == 1) { - lastVal = slotReturns.values().iterator().next(); - } else { - StringBuilder sb = new StringBuilder(); - boolean first = true; - for (Object value : slotReturns.values()) { - if (!first) sb.append(", "); - sb.append(value != null ? value.toString() : "null"); - first = false; - } - lastVal = sb.toString(); - } - } - } - } else { - lastVal = result; - } - } else { - lastVal = exprEvaluator.evaluate(arg, obj, locals); - } - } - - ioHandler.output(lastVal); - return lastVal; - - } else if (stmt instanceof IfNode) { - IfNode ifn = (IfNode) stmt; - boolean test = Boolean.TRUE.equals(exprEvaluator.evaluate(ifn.condition, obj, locals)); - - Set previousSlotsInPath = new HashSet<>(interpreter.slotsInCurrentPath); - - List branch = test ? ifn.thenBlock.statements : ifn.elseBlock.statements; - for (StatementNode s : branch) { - evalStmt(s, obj, locals, slotValues); - if (!interpreter.slotsInCurrentPath.isEmpty() && interpreter.shouldReturnEarly(slotValues)) { - break; - } - } - - interpreter.slotsInCurrentPath = previousSlotsInPath; - return null; - - } else if (stmt instanceof ForNode) { - ForNode f = (ForNode) stmt; - RangeNode range = f.range; - String iteratorName = f.iterator; - - Object startObj = exprEvaluator.evaluate(range.start, obj, locals); - Object endObj = exprEvaluator.evaluate(range.end, obj, locals); - double start = typeSystem.toDouble(startObj); - double end = typeSystem.toDouble(endObj); - - Set previousSlotsInPath = new HashSet<>(interpreter.slotsInCurrentPath); - - boolean isTransformativeStep = isTransformativeStep(range.step, iteratorName); - double stepValue = 1.0; - if (!isTransformativeStep) { - if (range.step == null) { - stepValue = (start > end) ? -1.0 : 1.0; - } else { - Map tempLocals = new HashMap<>(locals); - tempLocals.put(iteratorName, start); - Object stepObj = exprEvaluator.evaluate(range.step, obj, tempLocals); - stepValue = typeSystem.toDouble(stepObj); - } - - if (stepValue == 0) { - throw new RuntimeException("Step cannot be zero in for loop."); - } - } - - Object originalValue = null; - boolean existedBefore = locals.containsKey(iteratorName); - if (existedBefore) { - originalValue = locals.get(iteratorName); - } - - double current = start; - locals.put(iteratorName, (int) current); - - try { - while(true) { - boolean shouldTerminate; - double effectiveStepDirection = isTransformativeStep ? (end >= start ? 1.0 : -1.0) : stepValue; - - if (effectiveStepDirection > 0) { - shouldTerminate = current > end; - } else { - shouldTerminate = current < end; - } - - if (shouldTerminate) break; - - for (StatementNode s : f.body.statements) { - evalStmt(s, obj, locals, slotValues); - if (!interpreter.slotsInCurrentPath.isEmpty() && interpreter.shouldReturnEarly(slotValues)) { - break; - } - } - if (!interpreter.slotsInCurrentPath.isEmpty() && interpreter.shouldReturnEarly(slotValues)) { - break; - } - - if (isTransformativeStep) { - Map tempLocals = new HashMap<>(locals); - tempLocals.put(iteratorName, current); - Object stepObj = exprEvaluator.evaluate(range.step, obj, tempLocals); - double newValue = typeSystem.toDouble(stepObj); - if (newValue == current && current != end) { - throw new RuntimeException("Loop step expression resulted in no change, causing infinite loop"); - } - current = newValue; - } else { - current += stepValue; - } - locals.put(iteratorName, (int) current); - } - - } finally { - if (existedBefore) { - locals.put(iteratorName, originalValue); - } else { - locals.remove(iteratorName); - } - } - - interpreter.slotsInCurrentPath = previousSlotsInPath; - return null; - - } else if (stmt instanceof MethodCallNode) { - MethodCallNode call = (MethodCallNode) stmt; - Object result = interpreter.evalMethodCall(call, obj, locals); - - if (call.slotNames != null && !call.slotNames.isEmpty() && result instanceof Map) { - Map slotReturns = (Map) result; - - for (String slotName : call.slotNames) { - if (slotReturns.containsKey(slotName)) { - Object slotValue = slotReturns.get(slotName); - if (slotValues != null && slotValues.containsKey(slotName)) { - validateSlotType(slotName, slotValue); // Validate Type - Object oldValue = slotValues.get(slotName); - slotValues.put(slotName, slotValue); - DebugSystem.slotUpdate(slotName, oldValue, slotValue); - } - } - } - } - return result; - - } else if (stmt instanceof ReturnSlotAssignmentNode) { - ReturnSlotAssignmentNode assignment = (ReturnSlotAssignmentNode) stmt; - Object result = interpreter.evalMethodCall(assignment.methodCall, obj, locals); - - if (result instanceof Map) { - Map slotReturns = (Map) result; - - for (int i = 0; i < assignment.variableNames.size(); i++) { - String varName = assignment.variableNames.get(i); - String slotName = assignment.methodCall.slotNames.get(i); - - if (slotReturns.containsKey(slotName)) { - Object slotValue = slotReturns.get(slotName); - - if (slotValues != null && slotValues.containsKey(varName)) { - throw new RuntimeException("Cannot assign to variable '" + varName + "' because it conflicts with a return slot."); - } - - locals.put(varName, slotValue); - } else { - // Handle named assignment to unnamed slot index - if (slotReturns.containsKey("0") && Character.isDigit(slotName.charAt(0))) { - // Fallback? No, user must call by index if slots are unnamed - } - // CHANGED: Warn -> Exception - throw new RuntimeException("Runtime Error: Slot '" + slotName + "' requested but not found in method return values."); - } - } - } - return result; - - } else if (stmt instanceof ExprNode) { - return exprEvaluator.evaluate((ExprNode) stmt, obj, locals); - } - - return null; - } - - // --- VALIDATION HELPER --- - private void validateSlotType(String slotName, Object value) { - if (interpreter.currentSlotTypes == null || !interpreter.currentSlotTypes.containsKey(slotName)) { - return; // Should not happen if logic is correct - } - - String expectedType = interpreter.currentSlotTypes.get(slotName); - if (value == null) return; // Null allowed? - - boolean isValid = false; - if (expectedType.equals("int")) { - isValid = value instanceof Integer; - } else if (expectedType.equals("float")) { - isValid = value instanceof Float || value instanceof Double; - } else if (expectedType.equals("string")) { - isValid = value instanceof String; - } else if (expectedType.equals("bool")) { - isValid = value instanceof Boolean; - } else { - // For custom class types, we might check instance type name - // Since we don't have full class metadata here easily, we might relax or check map - if (value instanceof ObjectInstance) { - isValid = ((ObjectInstance)value).type.name.equals(expectedType); - } else { - isValid = true; // Allow custom types for now if not primitive - } - } - - if (!isValid) { - throw new RuntimeException("Type mismatch for slot '" + slotName + - "'. Expected " + expectedType + - " but got " + value.getClass().getSimpleName()); - } - } - - // --- Helper Methods --- - - private boolean isTransformativeStep(ExprNode stepExpr, String iterator) { - if (stepExpr == null) return false; - if (stepExpr instanceof BinaryOpNode) { - BinaryOpNode binOp = (BinaryOpNode) stepExpr; - if (isAssignmentOperator(binOp.op)) { - return isIterator(binOp.left, iterator); - } else { - return containsIteratorVariable(binOp.left, iterator) || - containsIteratorVariable(binOp.right, iterator); - } - } else if (stepExpr instanceof UnaryNode) { - return containsIteratorVariable(((UnaryNode)stepExpr).operand, iterator); - } else if (stepExpr.name != null) { - return stepExpr.name.equals(iterator); - } - return false; - } - - private boolean containsIteratorVariable(ExprNode stepExpr, String iterator) { - if (stepExpr == null) return false; - if (stepExpr instanceof BinaryOpNode) { - BinaryOpNode binOp = (BinaryOpNode) stepExpr; - return containsIteratorVariable(binOp.left, iterator) || containsIteratorVariable(binOp.right, iterator); - } else if (stepExpr instanceof UnaryNode) { - return containsIteratorVariable(((UnaryNode)stepExpr).operand, iterator); - } else if (stepExpr.name != null) { - return stepExpr.name.equals(iterator); - } - return false; - } - - private boolean isIterator(ExprNode expr, String iterator) { - return expr != null && expr.name != null && expr.name.equals(iterator); - } - - private boolean isAssignmentOperator(String op) { - return op.equals("=") || op.equals("+=") || op.equals("-=") || - op.equals("*=") || op.equals("/="); - } -} \ No newline at end of file diff --git a/src/main/java/cod/interpreter/TypeSystem.java b/src/main/java/cod/interpreter/TypeSystem.java index aac2ef10..e42409a9 100644 --- a/src/main/java/cod/interpreter/TypeSystem.java +++ b/src/main/java/cod/interpreter/TypeSystem.java @@ -1,151 +1,358 @@ package cod.interpreter; -import cod.debug.DebugSystem; +import static cod.syntax.Keyword.*; +import java.util.ArrayList; import java.util.List; +import java.math.BigDecimal; +import java.math.RoundingMode; public class TypeSystem { - // ADD TYPE CONVERSION METHOD - public Object convertType(Object value, String targetType) { - try { - switch (targetType) { - case "int": - if (value instanceof Integer) return value; - if (value instanceof Float) return ((Float) value).intValue(); - if (value instanceof Double) return ((Double) value).intValue(); - if (value instanceof String) return Integer.parseInt((String) value); - return (int) toDouble(value); - - case "float": - if (value instanceof Float) return value; - return (float) toDouble(value); - - case "string": - return String.valueOf(value); - - case "bool": - if (value instanceof Boolean) return value; - if (value instanceof String) { - String str = ((String) value).toLowerCase(); - return str.equals("true") || str.equals("1") || str.equals("yes"); - } - return toDouble(value) != 0; - - default: - return value; // Unknown type, return as-is - } - } catch (Exception e) { - DebugSystem.error("TYPECAST", "Failed to convert " + value + " to " + targetType); - throw new RuntimeException("Type conversion failed: " + value + " to " + targetType); - } - } + // Scale for BigDecimal operations (can be adjusted for higher precision) + private static final int DECIMAL_SCALE = 20; - public int compare(Object a, Object b) { - double da = toDouble(a); - double db = toDouble(b); - return Double.compare(da, db); + public Object unwrap(Object obj) { + if (obj instanceof TypedValue) { + return ((TypedValue) obj).value; + } + return obj; } public Object addNumbers(Object a, Object b) { - if (a instanceof Integer && b instanceof Integer) return (Integer) a + (Integer) b; - double result = toDouble(a) + toDouble(b); - return result == Math.floor(result) ? (int) result : result; + a = unwrap(a); b = unwrap(b); + if (a instanceof List || b instanceof List) throw new RuntimeException("Cannot add arrays"); + if (a instanceof Integer && b instanceof Integer) return (Integer)a + (Integer)b; + + // Use BigDecimal for high precision addition + BigDecimal bdA = toBigDecimal(a); + BigDecimal bdB = toBigDecimal(b); + return bdA.add(bdB); } - public Object subtractNumbers(Object a, Object b) { - if (a instanceof Integer && b instanceof Integer) return (Integer) a - (Integer) b; - double result = toDouble(a) - toDouble(b); - return result == Math.floor(result) ? (int) result : result; + a = unwrap(a); b = unwrap(b); + if (a instanceof List || b instanceof List) throw new RuntimeException("Cannot operate on arrays"); + if (a instanceof Integer && b instanceof Integer) return (Integer)a - (Integer)b; + + // Use BigDecimal for high precision subtraction + BigDecimal bdA = toBigDecimal(a); + BigDecimal bdB = toBigDecimal(b); + return bdA.subtract(bdB); } - public Object multiplyNumbers(Object a, Object b) { - if (a instanceof Integer && b instanceof Integer) return (Integer) a * (Integer) b; - double result = toDouble(a) * toDouble(b); - return result == Math.floor(result) ? (int) result : result; + a = unwrap(a); b = unwrap(b); + if (a instanceof List || b instanceof List) throw new RuntimeException("Cannot operate on arrays"); + if (a instanceof Integer && b instanceof Integer) return (Integer)a * (Integer)b; + + // Use BigDecimal for high precision multiplication + BigDecimal bdA = toBigDecimal(a); + BigDecimal bdB = toBigDecimal(b); + return bdA.multiply(bdB); } - public Object divideNumbers(Object a, Object b) { - double result = toDouble(a) / toDouble(b); - return result == Math.floor(result) ? (int) result : result; + a = unwrap(a); b = unwrap(b); + if (a instanceof List || b instanceof List) throw new RuntimeException("Cannot operate on arrays"); + + // Use BigDecimal for high precision division + BigDecimal bdA = toBigDecimal(a); + BigDecimal bdB = toBigDecimal(b); + + if (bdB.compareTo(BigDecimal.ZERO) == 0) { + throw new RuntimeException("Division by zero"); + } + + // Use a fixed scale for division to prevent ArithmeticException + return bdA.divide(bdB, DECIMAL_SCALE, RoundingMode.HALF_UP); } - public Object modulusNumbers(Object a, Object b) { - if (a instanceof Integer && b instanceof Integer) { - int intB = (Integer) b; - if (intB == 0) throw new RuntimeException("Modulus by zero"); - return (Integer) a % intB; - } - - double da = toDouble(a); - double db = toDouble(b); - if (db == 0) throw new RuntimeException("Modulus by zero"); - - double result = da % db; - return result == Math.floor(result) ? (int) result : result; - } - - public double toDouble(Object val) { - if (val instanceof Integer) return ((Integer) val).doubleValue(); - if (val instanceof Float) return ((Float) val).doubleValue(); - if (val instanceof Double) return (Double) val; - if (val instanceof List) { - // For arrays, return their size - return ((List) val).size(); - } - throw new RuntimeException("Cannot convert to number: " + val); - } - - /** - * Negates a number value (unary minus operation) Handles different numeric types: Integer, - * Float, Double Also handles string conversion for numeric strings - */ - public Object negateNumber(Object operand) { - DebugSystem.debug( - "UNARY", - "Negating: " + operand + " (type: " + operand.getClass().getSimpleName() + ")"); - - if (operand instanceof Integer) { - int value = (Integer) operand; - return -value; - } else if (operand instanceof Float) { - float value = (Float) operand; - return -value; - } else if (operand instanceof Double) { - double value = (Double) operand; - return -value; - } else if (operand instanceof String) { - // Try to parse string as number and negate + a = unwrap(a); b = unwrap(b); + if (a instanceof List || b instanceof List) throw new RuntimeException("Cannot operate on arrays"); + // Modulus is less common for floats, fall back to double for simplicity + // as BigDecimal modulus is complex and rarely used in range-like contexts. + if (a instanceof Integer && b instanceof Integer) return (Integer)a % (Integer)b; + return toDouble(a) % toDouble(b); + } + public Object negateNumber(Object a) { + a = unwrap(a); + if (a instanceof List) throw new RuntimeException("Cannot operate on arrays"); + if (a instanceof Integer) return -(Integer)a; + + if (a instanceof BigDecimal) return ((BigDecimal) a).negate(); + return -toDouble(a); + } + + public double toDouble(Object o) { + o = unwrap(o); + if (o instanceof Integer) return ((Integer)o).doubleValue(); + if (o instanceof Long) return ((Long)o).doubleValue(); + if (o instanceof Float) return ((Float)o).doubleValue(); + if (o instanceof Double) return (Double)o; + if (o instanceof BigDecimal) return ((BigDecimal) o).doubleValue(); // NEW: Handle BigDecimal + if (o instanceof Boolean) return ((Boolean)o) ? 1.0 : 0.0; + if (o instanceof String) { try { - String str = (String) operand; - if (str.contains(".")) { - return -Double.parseDouble(str); - } else { - int intValue = Integer.parseInt(str); - return -intValue; - } + return Double.parseDouble((String)o); } catch (NumberFormatException e) { - throw new RuntimeException("Cannot negate non-numeric string: " + operand); + throw new RuntimeException("Cannot convert string '" + o + "' to number"); } - } else { - // Fallback: convert to double and negate + } + throw new RuntimeException("Cannot convert " + o + " to number"); + } + + // NEW: High precision conversion method + public BigDecimal toBigDecimal(Object o) { + o = unwrap(o); + if (o instanceof Integer || o instanceof Long) return new BigDecimal(o.toString()); + if (o instanceof Float || o instanceof Double) return new BigDecimal(o.toString()); + if (o instanceof BigDecimal) return (BigDecimal) o; + if (o instanceof Boolean) return ((Boolean)o) ? BigDecimal.ONE : BigDecimal.ZERO; + if (o instanceof String) { try { - double value = toDouble(operand); - // Return same type if possible, otherwise return as double - if (operand instanceof Integer) { - return (int) -value; - } else if (operand instanceof Float) { - return (float) -value; - } else { - return -value; + return new BigDecimal((String)o); + } catch (NumberFormatException e) { + throw new RuntimeException("Cannot convert string '" + o + "' to BigDecimal"); + } + } + throw new RuntimeException("Cannot convert " + o + " to BigDecimal"); + } + + public int compare(Object a, Object b) { + a = unwrap(a); b = unwrap(b); + if (a instanceof String || b instanceof String) return String.valueOf(a).compareTo(String.valueOf(b)); + + // Use BigDecimal comparison for high precision + if (a instanceof BigDecimal || b instanceof BigDecimal || + a instanceof Double || b instanceof Double || + a instanceof Float || b instanceof Float) { + + BigDecimal bdA = toBigDecimal(a); + BigDecimal bdB = toBigDecimal(b); + return bdA.compareTo(bdB); + } + + // Fallback to integer comparison if both are integers/longs + double valA = toDouble(a); double valB = toDouble(b); + return Double.compare(valA, valB); + } + + public Object convertType(Object value, String targetType) { + value = unwrap(value); + + // Convert from BigDecimal to target type + if (value instanceof BigDecimal) { + BigDecimal bd = (BigDecimal) value; + if (targetType.equals(INT.toString())) return bd.intValue(); + if (targetType.equals(FLOAT.toString())) return bd.doubleValue(); + if (targetType.equals(TEXT.toString())) { + // FIX: Use toPlainString() not toString() + bd = bd.stripTrailingZeros(); + return bd.toPlainString(); + } + } + + if (targetType.equals(INT.toString())) return (int) toDouble(value); + if (targetType.equals(FLOAT.toString())) return toDouble(value); + + // FIXED TEXT CONVERSION + if (targetType.equals(TEXT.toString())) { + if (value instanceof BigDecimal) { + // Already handled above, but keep for safety + BigDecimal bd = (BigDecimal) value; + bd = bd.stripTrailingZeros(); + return bd.toPlainString(); + } + if (value instanceof Double) { + // Handle Double to avoid scientific notation + Double d = (Double) value; + BigDecimal bd = new BigDecimal(d); + bd = bd.stripTrailingZeros(); + return bd.toPlainString(); + } + if (value instanceof Float) { + // Handle Float to avoid scientific notation + Float f = (Float) value; + BigDecimal bd = new BigDecimal(f); + bd = bd.stripTrailingZeros(); + return bd.toPlainString(); + } + return String.valueOf(value); + } + + if (targetType.equals(BOOL.toString())) { + if (value instanceof Boolean) return value; + if (value instanceof String) { + String strVal = ((String)value).toLowerCase().trim(); + if (strVal.equals("true")) return true; + if (strVal.equals("false")) return false; + try { + return Double.parseDouble(strVal) != 0.0; + } catch (NumberFormatException e) { + throw new RuntimeException("Cannot convert string '" + value + "' to boolean"); } - } catch (Exception e) { - throw new RuntimeException( - "Cannot negate value: " - + operand - + " (type: " - + operand.getClass().getSimpleName() - + ")"); } + return toDouble(value) != 0.0; } + + throw new RuntimeException("Cannot convert value '" + value + "' to type '" + targetType + "'"); } + + public String getConcreteType(Object value) { + if (value instanceof Integer) return INT.toString(); + if (value instanceof Long) return INT.toString(); + if (value instanceof String) return TEXT.toString(); + if (value instanceof Float || value instanceof Double) return FLOAT.toString(); + if (value instanceof BigDecimal) { + // Check if it's an integer BigDecimal + BigDecimal bd = (BigDecimal) value; + if (bd.scale() <= 0 || bd.stripTrailingZeros().scale() <= 0) { + return INT.toString(); // Integer BigDecimal + } + return FLOAT.toString(); // Fractional BigDecimal + } + if (value instanceof Boolean) return BOOL.toString(); + if (value instanceof List) return "list"; + if (value instanceof NaturalArray) return "list"; // Treat NaturalArray as list type + return "unknown"; + } + + public boolean validateType(String typeSig, Object value) { + String typeSigTrimmed = typeSig.trim(); + if (typeSigTrimmed.contains("|")) { + if (!isTypeStructurallyValid(typeSigTrimmed)) { + throw new RuntimeException("Union type contains illegal keywords. Only explicit types allowed."); + } + } + if (value instanceof TypedValue) { + TypedValue tv = (TypedValue) value; + return validateTypeInternal(typeSig, tv.value, tv.activeType); + } + String concreteType = getConcreteType(value); + return validateTypeInternal(typeSig, value, concreteType); + } + + private boolean validateTypeInternal(String typeSig, Object rawValue, String concreteType) { + if (typeSig == null) return true; + String type = typeSig.trim(); + + if (type.equals(ANY.toString())) return true; + + if (rawValue == null) return false; + + // 1. Unions + List unionParts = splitTopLevel(type, '|'); + if (unionParts.size() > 1) { + for (String part : unionParts) { + if (validateTypeInternal(part, rawValue, concreteType)) return true; + } + return false; + } + + // 2. Arrays - CHANGED: Check for [type] instead of type[] + if (type.startsWith("[") && type.endsWith("]")) { + if (!(rawValue instanceof List || rawValue instanceof NaturalArray)) return false; + + // Special case: empty brackets [] (dynamic array) accepts any array + if (type.equals("[]")) { + return true; // [] accepts any array type + } + + String baseType = type.substring(1, type.length() - 1); + + // For NaturalArray, just check the bounds/properties, not every element + if (rawValue instanceof NaturalArray) return true; + + List list = (List) rawValue; + for (Object item : list) { + if (!validateType(baseType, item)) return false; + } + return true; + } + + // 3. Groups/Tuples + if (type.startsWith("(") && type.endsWith(")")) { + String content = type.substring(1, type.length() - 1); + List tupleTypes = splitTopLevel(content, ','); + if (tupleTypes.size() == 1) return validateTypeInternal(tupleTypes.get(0), rawValue, concreteType); + if (!(rawValue instanceof List)) return false; + List list = (List) rawValue; + if (list.size() != tupleTypes.size()) return false; + for (int i = 0; i < tupleTypes.size(); i++) { + if (!validateType(tupleTypes.get(i), list.get(i))) return false; + } + return true; + } + + // 4. Primitives + return type.equals(concreteType) || checkPrimitiveMatch(type, rawValue); +} + +private boolean isTypeStructurallyValid(String typeSig) { + List unionParts = splitTopLevel(typeSig, '|'); + if (unionParts.size() > 1) { + for (String part : unionParts) if (!isTypeStructurallyValid(part)) return false; + return true; + } + String type = typeSig.trim(); + if (type.isEmpty()) return false; + + // CHANGED: Check for [type] instead of type[] + if (type.startsWith("[") && type.endsWith("]")) { + String inner = type.substring(1, type.length() - 1); + // Special case: [] is valid (dynamic array) + if (inner.isEmpty()) return true; + return isTypeStructurallyValid(inner); + } + + if (type.startsWith("(") && type.endsWith(")")) { + String content = type.substring(1, type.length() - 1); + List parts = splitTopLevel(content, ','); + for (String part : parts) if (!isTypeStructurallyValid(part)) return false; + return true; + } + + if (type.equals(INT.toString()) || type.equals(FLOAT.toString()) || + type.equals(TEXT.toString()) || type.equals(BOOL.toString()) || type.equals(ANY.toString())) { + return true; + } + if (Character.isUpperCase(type.charAt(0))) return true; + return false; } + +private boolean checkPrimitiveMatch(String type, Object rawValue) { + // Handle array types with [type] notation + if (type.startsWith("[") && type.endsWith("]")) { + // Special case: empty brackets [] (dynamic array) + if (type.equals("[]")) { + // [] accepts any array + return rawValue instanceof List || rawValue instanceof NaturalArray; + } + + // Regular [type] - for primitive matching, just check if it's an array + // The actual element type validation happens in validateTypeInternal + return rawValue instanceof List || rawValue instanceof NaturalArray; + } + + if (type == INT.toString()) return rawValue instanceof Integer || rawValue instanceof Long; + else if (type == TEXT.toString()) return rawValue instanceof String; + else if (type == FLOAT.toString()) return rawValue instanceof Float || rawValue instanceof Double || rawValue instanceof BigDecimal; + else if (type == BOOL.toString()) return rawValue instanceof Boolean; + return false; +} + + private List splitTopLevel(String input, char delimiter) { + List parts = new ArrayList(); + int parenDepth = 0; + StringBuilder current = new StringBuilder(); + for (char c : input.toCharArray()) { + if (c == '(') parenDepth++; + else if (c == ')') parenDepth--; + if (c == delimiter && parenDepth == 0) { + parts.add(current.toString().trim()); + current.setLength(0); + } else { + current.append(c); + } + } + parts.add(current.toString().trim()); + return parts; + } +} \ No newline at end of file diff --git a/src/main/java/cod/interpreter/TypedValue.java b/src/main/java/cod/interpreter/TypedValue.java new file mode 100644 index 00000000..fa799c26 --- /dev/null +++ b/src/main/java/cod/interpreter/TypedValue.java @@ -0,0 +1,26 @@ +// In TypedValue.java +package cod.interpreter; + +public class TypedValue { + + // The raw data being stored (e.g., Integer(5), String("hello"), or List) + public final Object value; + + // The specific type active in this instance (e.g., "int" or "text") + public final String activeType; + + // The full declared Union type signature (e.g., "int|text" or "(float,int)|bool") + public final String declaredType; + + public TypedValue(Object value, String activeType, String declaredType) { + this.value = value; + this.activeType = activeType; + this.declaredType = declaredType; + } + + // ADD THIS toString() METHOD BACK + @Override + public String toString() { + return String.valueOf(value); + } +} \ No newline at end of file diff --git a/src/main/java/cod/lexer/MainLexer.java b/src/main/java/cod/lexer/MainLexer.java new file mode 100644 index 00000000..5403f505 --- /dev/null +++ b/src/main/java/cod/lexer/MainLexer.java @@ -0,0 +1,415 @@ +package cod.lexer; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static cod.syntax.Keyword.*; +import cod.syntax.Symbol; +import static cod.syntax.Symbol.*; + +public class MainLexer { + + public enum TokenType { + KEYWORD, + INT_LIT, + FLOAT_LIT, + STRING_LIT, + BOOL_LIT, + ID, + SYMBOL, + EOF, + INVALID, + LINE_COMMENT, + BLOCK_COMMENT, + WS; + } + + public static class Token { + public final TokenType type; + public final String text; + public final int line; + public final int column; + public final Symbol symbol; + + public Token(TokenType type, String text, int line, int column) { + this.type = type; + this.text = text; + this.line = line; + this.column = column; + this.symbol = null; + } + + public Token(TokenType type, String text, int line, int column, Symbol symbol) { + this.type = type; + this.text = text; + this.line = line; + this.column = column; + this.symbol = symbol; + } + + @Override + public String toString() { + return "Token{" + + "type=" + type.name() + + ", text='" + text + '\'' + + (symbol != null ? ", symbol=" + symbol.name() : "") + + ", line=" + line + + ", column=" + column + + '}'; + } + } + + private static final Set KEYWORDS = new HashSet(); + + static { + for (cod.syntax.Keyword keyword : cod.syntax.Keyword.values()) { + KEYWORDS.add(keyword.toString()); + } + } + + private final String input; + private int position = 0; + private int line = 1; + private int column = 1; + + public MainLexer(String input) { + this.input = input; + } + + public List tokenize() { + List tokens = new ArrayList(); + while (position < input.length()) { + skipWhitespaceAndComments(); + if (position < input.length()) { + tokens.add(scanNextToken()); + } + } + tokens.add(create(TokenType.EOF)); + return tokens; + } + + private void skipWhitespaceAndComments() { + while (position < input.length()) { + char c = peek(); + if (Character.isWhitespace(c)) { + consume(); + } else if (c == '/' && peek(1) == '/') { + scanLineComment(); + } else if (c == '/' && peek(1) == '*') { + scanBlockComment(); + } else { + break; + } + } + } + + private Token scanNextToken() { + char c = peek(); + + if (Character.isLetter(c) || c == '_') return readIdentifierOrKeyword(); + if (Character.isDigit(c)) return readNumber(); + if (c == '"') return readString(); + return readSymbol(); + } + + private Token readIdentifierOrKeyword() { + StringBuilder sb = new StringBuilder(); + while (position < input.length() && (Character.isLetterOrDigit(peek()) || peek() == '_')) { + sb.append(consume()); + } + String text = sb.toString(); + + if (KEYWORDS.contains(text)) { + return create(TokenType.KEYWORD, text); + } + + return create(TokenType.ID, text); + } + + private Token readNumber() { + StringBuilder sb = new StringBuilder(); + boolean isFloat = false; + + // 1. Read integer part + while (position < input.length() && Character.isDigit(peek())) { + sb.append(consume()); + } + + // 2. Read fractional part + if (peek() == '.') { + isFloat = true; + sb.append(consume()); + while (position < input.length() && Character.isDigit(peek())) { + sb.append(consume()); + } + } + + // 3. Read Numeric Shorthand Suffix (K, M, Qi, etc.) + String suffix = readSuffix(); + if (!suffix.isEmpty()) { + sb.append(suffix); + isFloat = true; + } else { + // 4. NEW: Read Standard Scientific Notation (e/E) + String exponent = readExponent(); + if (!exponent.isEmpty()) { + sb.append(exponent); + isFloat = true; + } + } + + String numberText = sb.toString(); + return create( + isFloat ? TokenType.FLOAT_LIT : TokenType.INT_LIT, numberText); +} + +// NEW Helper method for MainLexer (Numeric Shorthands) +private String readSuffix() { + if (position >= input.length()) return ""; + + char c1 = peek(); + + // Check custom suffixes (Case sensitive) + if (c1 == 'K' || c1 == 'M' || c1 == 'B' || c1 == 'T') { + return String.valueOf(consume()); + } + + if (c1 == 'Q') { + consume(); // consume Q + if (peek() == 'i') { + consume(); // consume i + return "Qi"; + } + return "Q"; + } + + return ""; +} + +// NEW Helper method to read the standard 'e' exponent part (Case insensitive) +private String readExponent() { + if (position >= input.length()) return ""; + + char c1 = peek(); + if (c1 != 'e' && c1 != 'E') { + return ""; + } + + StringBuilder sb = new StringBuilder(); + sb.append(consume()); // Consume 'e' or 'E' + + // Consume optional sign (+ or -) + char c2 = peek(); + if (c2 == '+' || c2 == '-') { + sb.append(consume()); + } + + // Must be followed by at least one digit + if (position < input.length() && Character.isDigit(peek())) { + while (position < input.length() && Character.isDigit(peek())) { + sb.append(consume()); + } + } else { + // If 'e' or 'E' is not followed by a digit (after optional sign), + // backtrack the 'e' or 'E' and treat it as part of identifier/error. + // NOTE: In a complete Lexer, this should rollback position, but for simplicity here, + // we'll stop the number read and rely on the parser to handle the trailing characters if any. + // Since this is the end of readNumber, any non-digit following 'e' will be part of the ID logic. + // Given your current Lexer structure, we must ensure 'e' is followed by a digit. + if (sb.length() == 1 || (sb.length() == 2 && (sb.charAt(1) == '+' || sb.charAt(1) == '-'))) { + // We only consumed 'e' or 'e+' / 'e-', but no digit followed. This is invalid scientific notation. + // We return an empty string to signify failure, but a real Lexer needs error handling/backtracking. + // For safety, we'll assume a digit must follow for it to be an exponent. + return ""; + } + } + + return sb.toString(); +} + + private Token readString() { + StringBuilder sb = new StringBuilder(); + consume(); // Consume opening quote + while (position < input.length() && peek() != '"') { + if (peek() == '\\') { + consume(); // consume the backslash + char escaped = consume(); + switch (escaped) { + case 'n': sb.append('\n'); break; + case 't': sb.append('\t'); break; + case 'r': sb.append('\r'); break; + case '\\': sb.append('\\'); break; + case '"': sb.append('"'); break; + default: sb.append('\\').append(escaped); break; + } + } else { + sb.append(consume()); + } + } + if (position < input.length()) consume(); // Consume closing quote + return create(TokenType.STRING_LIT, sb.toString()); + } + + private Token readSymbol() { + char c1 = consume(); + switch (c1) { + // NEW: Pipe support for Union Types (int|text) + case '|': + return create(PIPE, "|"); + + // NEW: Ampersand support + case '&': + return create(AMPERSAND, "&"); + + // UPDATED: Added := operator + case ':': + if (peek() == '=') { + consume(); + return create(DOUBLE_COLON_ASSIGN, ":="); + } else if (peek() == ':') { + consume(); + return create(DOUBLE_COLON, "::"); + } else { + return create(COLON, ":"); + } + + case '=': + if (peek() == '=') { + consume(); + return create(EQ, "=="); + } else { + return create(ASSIGN, "="); + } + case '>': + if (peek() == '=') { + consume(); + return create(GTE, ">="); + } else { + return create(GT, ">"); + } + case '<': + if (peek() == '=') { + consume(); + return create(LTE, "<="); + } else { + return create(LT, "<"); + } + case '!': + if (peek() == '=') { + consume(); + return create(NEQ, "!="); + } else { + return create(BANG, "!"); + } + case '+': + if (peek() == '=') { + consume(); + return create(PLUS_ASSIGN, "+="); + } else { + return create(PLUS, "+"); + } + case '-': + if (peek() == '=') { + consume(); + return create(MINUS_ASSIGN, "-="); + } else { + return create(MINUS, "-"); + } + case '*': + if (peek() == '=') { + consume(); + return create(MUL_ASSIGN, "*="); + } else { + return create(MUL, "*"); + } + case '/': + if (peek() == '=') { + consume(); + return create(DIV_ASSIGN, "/="); + } else { + return create(DIV, "/"); + } + case '~': + if (peek() == '>') { + consume(); + return create(TILDE_ARROW, "~>"); + } else { + return create(TokenType.INVALID); + } + case '?': + return create(QUESTION, "?"); + case '%': + return create(MOD, "%"); + case '.': + return create(DOT, "."); + case ',': + return create(COMMA, ","); + case '(': + return create(LPAREN, "("); + case ')': + return create(RPAREN, ")"); + case '{': + return create(LBRACE, "{"); + case '}': + return create(RBRACE, "}"); + case '[': + return create(LBRACKET, "["); + case ']': + return create(RBRACKET, "]"); + case '_': + return create(UNDERSCORE, "_"); + default: + return create(TokenType.INVALID); + } + } + + private Token create(TokenType type, String text) { + return new Token(type, text, this.line, this.column); + } + + private Token create(TokenType type) { + return new Token(type, type.name().toLowerCase(), this.line, this.column); + } + + private Token create(Symbol symbol, String text) { + return new Token(TokenType.SYMBOL, text, this.line, this.column, symbol); + } + + private void scanLineComment() { + while (position < input.length() && peek() != '\n') consume(); + } + + private void scanBlockComment() { + consume(); + consume(); // consume '/*' + while (position < input.length() - 1) { + if (peek() == '*' && peek(1) == '/') { + consume(); + consume(); + return; + } + consume(); + } + } + + private char peek() { + return peek(0); + } + + private char peek(int offset) { + return (position + offset >= input.length()) ? '\0' : input.charAt(position + offset); + } + + private char consume() { + char c = input.charAt(position++); + if (c == '\n') { + line++; + column = 1; + } else { + column++; + } + return c; + } +} diff --git a/src/main/java/cod/parser/BaseParser.java b/src/main/java/cod/parser/BaseParser.java new file mode 100644 index 00000000..1a88b731 --- /dev/null +++ b/src/main/java/cod/parser/BaseParser.java @@ -0,0 +1,380 @@ +package cod.parser; + +import cod.error.ParseError; + +import cod.syntax.Keyword; +import static cod.syntax.Keyword.*; +import cod.syntax.Symbol; +import static cod.syntax.Symbol.*; + +import java.util.List; + +import cod.lexer.MainLexer.Token; +import cod.lexer.MainLexer.TokenType; +import static cod.lexer.MainLexer.TokenType.*; + +/** + * Base class for all parser components, handling token stream management and + * common utility methods. Uses shared PositionHolder for position synchronization. + */ +public abstract class BaseParser { + protected final List tokens; + protected final PositionHolder position; + + public BaseParser(List tokens, PositionHolder position) { + this.tokens = tokens; + this.position = position; + } + + // --- Token Management --- + + protected Token currentToken() { + int pos = position.get(); + if (pos >= tokens.size()) { + // Return synthetic EOF token + if (tokens.isEmpty()) { + return new Token(EOF, "EOF", 1, 1); + } + Token last = tokens.get(tokens.size() - 1); + return new Token(EOF, "EOF", last.line, last.column + 1); + } + return tokens.get(pos); + } + + // UPDATED: Faster, safer bounds checking + protected Token peek(int offset) { + int targetPos = position.get() + offset; + return (targetPos >= 0 && targetPos < tokens.size()) ? + tokens.get(targetPos) : null; + } + + // NEW: Fast lookahead helpers + protected Token lookahead(int n) { + return peek(n); + } + + protected Token lookahead() { + return peek(1); + } + + // NEW: Helper for scanning loops (moved from DeclarationParser) + protected Token lookaheadFrom(int baseOffset, int additionalOffset) { + int pos = getPosition() + baseOffset + additionalOffset; + return pos >= 0 && pos < tokens.size() ? tokens.get(pos) : null; + } + + protected boolean match(Symbol expectedSymbol) { + Token current = currentToken(); + return current.type == TokenType.SYMBOL && current.symbol == expectedSymbol; + } + + protected boolean match(TokenType... types) { + Token current = currentToken(); + if (current.type == EOF) { + for (TokenType type : types) { + if (type == EOF) return true; + } + return false; + } + for (TokenType type : types) { + if (current.type == type) return true; + } + return false; + } + + protected Token consume() { + Token token = currentToken(); + if (token.type != EOF && position.get() < tokens.size()) { + position.up(); + } + return token; + } + + protected Token consume(TokenType expectedType) { + Token token = currentToken(); + if (token.type == expectedType) { + if (token.type != EOF && position.get() < tokens.size()) { + position.up(); + } + return token; + } + throw new ParseError("Expected " + getTypeName(expectedType) + " but found " + + getTypeName(token.type) + " ('" + token.text + "') at line " + token.line + ":" + token.column); + } + + protected Token consume(Symbol expectedSymbol) { + Token token = currentToken(); + if (token.type == TokenType.SYMBOL && token.symbol == expectedSymbol) { + if (token.type != EOF && position.get() < tokens.size()) { + position.up(); + } + return token; + } + throw new ParseError("Expected " + expectedSymbol + " but found " + + getTypeName(token.type) + " ('" + token.text + "') at line " + token.line + ":" + token.column); + } + + protected Token consume(boolean condition) { + if (condition) return consume(); + Token current = currentToken(); + throw new ParseError("Consumption condition not met at: " + current.text + + " (" + getTypeName(current.type) + ")" + + " at line " + current.line + ":" + current.column); + } + + protected boolean tryConsume(Symbol expectedSymbol) { + if (match(expectedSymbol)) { + consume(expectedSymbol); + return true; + } + return false; + } + + protected boolean tryConsume(TokenType expectedType) { + if (match(expectedType)) { + consume(expectedType); + return true; + } + return false; + } + + // --- Keyword and Symbol Helpers --- + + protected boolean isKeyword(Keyword expectedKeyword) { + Token current = currentToken(); + return current.type == KEYWORD && current.text.equals(expectedKeyword.toString()); + } + + protected boolean isKeywordAt(int offset, Keyword expectedKeyword) { + Token token = peek(offset); + return token != null && token.type == KEYWORD && token.text.equals(expectedKeyword.toString()); + } + + protected void consumeKeyword(Keyword expectedKeyword) { + Token token = currentToken(); + if (token.type == KEYWORD && token.text.equals(expectedKeyword.toString())) { + if (position.get() < tokens.size()) { + position.up(); + } + return; + } + throw new ParseError("Expected keyword '" + expectedKeyword.toString() + "' but found " + + getTypeName(token.type) + " ('" + token.text + "') at line " + token.line + ":" + token.column); + } + + protected boolean isSymbolAt(int offset, Symbol symbol) { + Token token = peek(offset); + return token != null && token.type == SYMBOL && token.symbol == symbol; + } + + protected String getTypeName(TokenType type) { + return type.toString(); + } + + // NEW: Helper to check if a token starts an expression (moved from ExpressionParser) + protected boolean isExpressionStart(Token t) { + if (t == null) return false; + + // Skip whitespace and comments - look for the next real token + if (t.type == WS || t.type == LINE_COMMENT || t.type == BLOCK_COMMENT) { + return false; // This tells the caller to skip this token + } + + String text = t.text; + return t.type == INT_LIT || t.type == FLOAT_LIT || + t.type == STRING_LIT || t.type == BOOL_LIT || + t.type == ID || t.symbol == LPAREN || + t.symbol == LBRACKET || t.symbol == BANG || + t.symbol == PLUS || t.symbol == MINUS || + (t.type == KEYWORD && ( + NULL.toString().equals(text) || + TRUE.toString().equals(text) || + FALSE.toString().equals(text) || + INPUT.toString().equals(text) + )); +} + + // --- Generic Grammar Helpers --- + + protected String parseQualifiedName() { + StringBuilder name = new StringBuilder(); + name.append(consume(ID).text); + while (tryConsume(DOT)) { + name.append("."); + name.append(consume(ID).text); + } + return name.toString(); + } + + protected boolean isTypeKeyword(String text) { + // Includes ARRAY keyword for declaration purposes + return text.equals(INT.toString()) || text.equals(TEXT.toString()) || + text.equals(FLOAT.toString()) || text.equals(BOOL.toString()); + } + +protected boolean isTypeStart(Token token) { + if (token == null) return false; + // REMOVED: var keyword from type checking + // Checks for (int, text, bool, float, ID, LPAREN) + // LBRACKET for prefix array types e.g., [int] + return isTypeKeyword(token.text) || token.type == ID || token.symbol == LPAREN || token.symbol == LBRACKET; +} + + protected boolean skipBrackets(int startOffset) { + int p = position.get() + startOffset; + while (p < tokens.size() && isSymbolAt(p - position.get(), LBRACKET)) { + if (p + 1 >= tokens.size() || !isSymbolAt(p + 1 - position.get(), RBRACKET)) { + return false; + } + p += 2; + } + return true; + } + +/** + * Skips a complete type definition starting at startPos (relative to tokens list, not parser position). + * Handles prefix arrays [int], groups (int, int), and unions int|float. + * Returns the index AFTER the type, or -1 if not a valid type. + */ +protected int skipType(int startPos) { + int pos = startPos; + if (pos >= tokens.size()) return -1; + + Token t = tokens.get(pos); + + // 1. Handle Prefix Array: [Type] or [] + if (t.symbol == LBRACKET) { + pos++; + if (pos >= tokens.size()) return -1; + + if (tokens.get(pos).symbol == RBRACKET) { + // Empty brackets [] + pos++; + } else { + // Recursive skip for inner type [int] + pos = skipType(pos); + if (pos == -1 || pos >= tokens.size()) return -1; + + if (tokens.get(pos).symbol == RBRACKET) { + pos++; + } else { + return -1; + } + } + } + // 2. Handle Group: (Type, Type) + else if (t.symbol == LPAREN) { + pos++; + while (true) { + pos = skipType(pos); + if (pos == -1 || pos >= tokens.size()) return -1; + + Token sep = tokens.get(pos); + if (sep.symbol == COMMA) { + pos++; + } else if (sep.symbol == RPAREN) { + pos++; + break; + } else { + return -1; + } + } + } + // 3. Handle Primitive or ID + else if (isTypeKeyword(t.text) || t.type == ID) { + pos++; + } + else { + return -1; + } + + // 4. Handle Unions (|) + if (pos < tokens.size() && tokens.get(pos).symbol == PIPE) { + pos++; // Consume | + return skipType(pos); // Recurse for next part + } + + return pos; +} + + /** + * Entry point for parsing types. + * Handles complex structures like: (int, text) | [bool] + */ +protected String parseTypeReference() { + StringBuilder type = new StringBuilder(); + + // 1. Parse the first type component + if (match(LBRACKET)) { + // Prefix Array handling [Type] or [] + consume(LBRACKET); + if (match(RBRACKET)) { + // Empty brackets [] -> dynamic array + consume(RBRACKET); + type.append("[]"); // Just [] for dynamic arrays + } else { + // Recursively parse inner type + String inner = parseTypeReference(); + consume(RBRACKET); + // Keep [type] notation + type.append("[").append(inner).append("]"); + } + } else if (match(LPAREN)) { + type.append(parseGroupedType()); + } else { + Token typeToken = currentToken(); + if (isTypeStart(typeToken) && typeToken.symbol != LBRACKET) { + String typeName = consume().text; + type.append(typeName); + } else { + Token current = currentToken(); + throw new ParseError("Expected type name but got " + + getTypeName(current.type) + " ('" + current.text + "')" + + " at line " + current.line + ":" + current.column); + } + } + + // 2. Handle Union types (|) + while (match(PIPE)) { + consume(PIPE); + type.append("|"); + type.append(parseTypeReference()); + } + + return type.toString(); +} + + /** + * Handles Grouped Types: (Type, Type) + */ + private String parseGroupedType() { + consume(LPAREN); + StringBuilder group = new StringBuilder("("); + + // Recursively parse the first element + group.append(parseTypeReference()); + + // Parse subsequent elements + while (tryConsume(COMMA)) { + group.append(","); + group.append(parseTypeReference()); + } + + consume(RPAREN); + group.append(")"); + return group.toString(); + } + + protected boolean isVisibilityModifier() { + return isKeyword(SHARE) || isKeyword(LOCAL); + } + + // --- Position Access (for debugging) --- + + /** + * Gets the current shared position (for debugging/logging only) + */ + public int getPosition() { + return this.position.get(); + } +} diff --git a/src/main/java/cod/parser/DeclarationParser.java b/src/main/java/cod/parser/DeclarationParser.java new file mode 100644 index 00000000..b222e3ff --- /dev/null +++ b/src/main/java/cod/parser/DeclarationParser.java @@ -0,0 +1,594 @@ +package cod.parser; + +import cod.ast.ASTFactory; +import cod.ast.nodes.*; +import cod.error.ParseError; +import cod.lexer.MainLexer.Token; +import cod.semantic.NamingValidator; +import cod.syntax.Keyword; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import static cod.lexer.MainLexer.TokenType.*; +import static cod.syntax.Keyword.*; +import static cod.syntax.Symbol.*; + +/** + * Parses top-level declarations: Types, Methods, and Fields. + * It requires a reference to the StatementParser to parse method bodies. + * Uses shared AtomicInteger for automatic position synchronization. + */ +public class DeclarationParser extends BaseParser { + + private final StatementParser statementParser; + + public DeclarationParser(List tokens, PositionHolder position, StatementParser statementParser) { + super(tokens, position); + this.statementParser = statementParser; + } + + public TypeNode parseType() { + Token visibilityToken = currentToken(); + String visibilityText = consume(isVisibilityModifier()).text; + + // Convert to Keyword + Keyword visibility; + if (SHARE.toString().equals(visibilityText)) { + visibility = Keyword.SHARE; + } else if (LOCAL.toString().equals(visibilityText)) { + visibility = Keyword.LOCAL; + } else { + throw new ParseError( + "Internal parser error: isVisibilityModifier() returned true for non-visibility keyword: '" + visibilityText + "'", + visibilityToken.line, visibilityToken.column); + } + + Token typeNameToken = currentToken(); + String typeName = consume(ID).text; + + NamingValidator.validateClassName(typeName, typeNameToken); + + String extendName = null; + if (isKeyword(IS)) { + consumeKeyword(IS); + extendName = parseQualifiedName(); + } + + TypeNode type = ASTFactory.createType(typeName, visibility, extendName); + + consume(LBRACE); + while (!match(RBRACE)) { +// Check fields before methods since both can start with visibility modifiers + if (isFieldDeclaration()) { + type.fields.add(parseField()); + } + else if (isMethodDeclaration()) { + MethodNode method = parseMethod(); + type.methods.add(method); + } + else { + // Automatic position sharing - no manual sync needed! + type.statements.add(statementParser.parseStatement()); + } + } + consume(RBRACE); + return type; +} + + public MethodNode parseMethod() { + Token startToken = currentToken(); + + boolean isBuiltin = false; + Keyword visibility = Keyword.SHARE; + + if (isKeyword(BUILTIN)) { + consumeKeyword(BUILTIN); + isBuiltin = true; + visibility = Keyword.SHARE; + } else if (isVisibilityModifier()) { + Token visibilityToken = currentToken(); + String visibilityText = consume().text; + + if (SHARE.toString().equals(visibilityText)) { + visibility = Keyword.SHARE; + } else if (LOCAL.toString().equals(visibilityText)) { + visibility = Keyword.LOCAL; + } else { + throw new ParseError( + "Internal parser error: isVisibilityModifier() returned true for non-visibility keyword: '" + visibilityText + "'", + visibilityToken.line, visibilityToken.column); + } + } + + String methodName = consume(ID).text; + + NamingValidator.validateMethodName(methodName, startToken); + + // We initialize with empty slots first, populate them later if '::' exists + MethodNode method = ASTFactory.createMethod(methodName, visibility, null); + method.isBuiltin = isBuiltin; + + // Parse parameters with new syntax: name: type + consume(LPAREN); + if (!match(RPAREN)) { + method.parameters.add(parseParameter()); + while (tryConsume(COMMA)) { + method.parameters.add(parseParameter()); + } + } + consume(RPAREN); + + // Check for Slot Contract '::' + if (isSlotDeclaration()) { + method.returnSlots = parseSlotContractList(); + } else { + method.returnSlots = new ArrayList(); + } + + // --- CRITICAL FIX: Skip whitespace/comments before checking for ~> or { --- + skipWhitespaceAndComments(); + + // Check for inline return (~>) or block ({) + if (match(TILDE_ARROW)) { + // Inline return + consume(TILDE_ARROW); + + // Skip whitespace before parsing slot assignments + skipWhitespaceAndComments(); + + // Parse multiple slot assignments for inline return + List slotAssignments = new ArrayList<>(); + + // Parse first slot assignment + slotAssignments.add(parseSingleSlotAssignment()); + + // Parse additional comma-separated slot assignments + while (tryConsume(COMMA)) { + skipWhitespaceAndComments(); // Skip whitespace after comma + slotAssignments.add(parseSingleSlotAssignment()); + } + + // Add to method body + if (slotAssignments.size() == 1) { + method.body.add(slotAssignments.get(0)); + } else { + MultipleSlotAssignmentNode multiAssign = ASTFactory.createMultipleSlotAssignment(slotAssignments); + method.body.add(multiAssign); + } + + } else if (match(LBRACE)) { + // Block return + consume(LBRACE); + while (!match(RBRACE)) { + method.body.add(statementParser.parseStatement()); + } + consume(RBRACE); + } else { + // Error: Method must have either ~> or { + Token current = currentToken(); + throw new ParseError( + "Expected '~>' or '{' after method signature, but found " + + getTypeName(current.type) + " ('" + current.text + "')" + + " at line " + current.line + ":" + current.column); + } + + return method; +} + + private SlotAssignmentNode parseSingleSlotAssignment() { + String slotName = null; + ExprNode value; + + // Check for named slot: "slotName: expression" + if (currentToken().type == ID) { + Token afterId = lookahead(1); + if (afterId != null && afterId.symbol == COLON) { + // Named slot: "sum: a + b" + slotName = consume(ID).text; + consume(COLON); + value = statementParser.expressionParser.parseExpression(); + } else { + // Positional slot: just an expression + slotName = null; + value = statementParser.expressionParser.parseExpression(); + } + } else { + // Positional slot: just an expression + slotName = null; + value = statementParser.expressionParser.parseExpression(); + } + + return ASTFactory.createSlotAssignment(slotName, value); +} + + public List parseSlotContractList() { + consume(DOUBLE_COLON); + + List slots = new ArrayList(); + + boolean firstSlot = true; + boolean isNamedMode = false; + int index = 0; + + do { + String name; + String type; + + if (firstSlot) { + if (currentToken().type == ID) { + // Named slot: name: type + isNamedMode = true; + name = consume(ID).text; + consume(COLON); + type = parseTypeReference(); + } else { + // Unnamed slot: just type + isNamedMode = false; + name = String.valueOf(index); + type = parseTypeReference(); + } + firstSlot = false; + } else { + if (isNamedMode) { + if (currentToken().type != ID) { + throw new ParseError("Mixed slot declaration styles not allowed. Expected name for slot."); + } + name = consume(ID).text; + consume(COLON); + type = parseTypeReference(); + } else { + if (currentToken().type == ID) { + throw new ParseError("Mixed slot declaration styles not allowed. Found name '" + currentToken().text + "' in unnamed slot list."); + } + name = String.valueOf(index); + type = parseTypeReference(); + } + } + + slots.add(ASTFactory.createSlot(type, name)); + index++; + + } while (tryConsume(COMMA)); + + return slots; +} + +public FieldNode parseField() { + Token startToken = currentToken(); + + // Parse optional visibility + Keyword visibility = null; + if (isVisibilityModifier()) { + Token visToken = consume(); + if (SHARE.toString().equals(visToken.text)) { + visibility = SHARE; + } else if (LOCAL.toString().equals(visToken.text)) { + visibility = LOCAL; + } + } + + // Parse name (FIRST!) + String fieldName = consume(ID).text; + + // REJECT underscore as field name + if ("_".equals(fieldName)) { + throw new ParseError( + "Field name cannot be '_'. Underscore is reserved for discard/placeholder.", + startToken.line, startToken.column + ); + } + + // Parse colon + consume(COLON); + + // Parse type (SECOND!) + String fieldType = parseTypeReference(); + + // Validate name + if (NamingValidator.isAllCaps(fieldName)) { + NamingValidator.validateConstantName(fieldName, startToken); + } else { + NamingValidator.validateVariableName(fieldName, startToken); + } + + // Create field + FieldNode field = ASTFactory.createField(fieldName, fieldType); + if (visibility != null) { + // Store visibility if your AST supports it + // field.visibility = visibility; // Uncomment if FieldNode has this field + } + + // Parse optional initializer + if (tryConsume(ASSIGN)) { + field.value = statementParser.expressionParser.parseExpression(); + } + + return field; +} + + public ParamNode parseParameter() { + Token startToken = currentToken(); + String name = consume(ID).text; + + // NEW: Check for := syntax (inferred from literal) + if (match(DOUBLE_COLON_ASSIGN)) { + consume(DOUBLE_COLON_ASSIGN); + + // Parse default value (must be a literal for inference) + ExprNode defaultValue = statementParser.expressionParser.parsePrimaryExpression(); + + // NEW: Validate it's a literal (not method call or complex expression) + if (!isSimpleLiteral(defaultValue)) { + throw new ParseError( + "Parameter inference (:=) can only be used with literals. " + + "Use explicit typing for expressions: " + name + ": Type = expression", + startToken.line, startToken.column + ); + } + + // Infer type from literal + String inferredType = inferTypeFromLiteral(defaultValue); + if (inferredType == null) { + throw new ParseError( + "Cannot infer parameter type from literal. Use explicit typing: " + + name + ": Type = " + defaultValue, + startToken.line, startToken.column + ); + } + + // Validate parameter name + NamingValidator.validateParameterName(name, startToken); + + // Create param with inferred type + ParamNode param = ASTFactory.createParam(name, inferredType, defaultValue, true); + param.hasDefaultValue = true; + return param; + } + + // Original: name: Type [= expression] + consume(COLON); + String type = parseTypeReference(); + + ExprNode defaultValue = null; + if (tryConsume(ASSIGN)) { + // This can be ANY expression (literal OR method call) + defaultValue = statementParser.expressionParser.parseExpression(); + } + + NamingValidator.validateParameterName(name, startToken); + ParamNode param = ASTFactory.createParam(name, type, defaultValue, false); + if (defaultValue != null) { + param.hasDefaultValue = true; + } + return param; +} + +private boolean isSimpleLiteral(ExprNode expr) { + // Check if expression is a SIMPLE literal + + // 1. Basic literals (int, float, text, bool, null) + if (expr instanceof ExprNode) { + ExprNode e = (ExprNode) expr; + if (e.value != null || e.isNull) { + return e.name == null; // Not an identifier + } + } + + // 2. Array literals + if (expr instanceof ArrayNode) { + ArrayNode arr = (ArrayNode) expr; + + // Check if it's a range NaturalArray (could be [1 to 10] or [by 2 in 1 to 10]) + if (arr.elements.size() == 1 && arr.elements.get(0) instanceof RangeNode) { + RangeNode range = (RangeNode) arr.elements.get(0); + // Check start and end are simple, step could be null or simple + return isSimpleLiteral(range.start) && + isSimpleLiteral(range.end) && + (range.step == null || isSimpleLiteral(range.step)); + } + + // Regular array - check all elements + for (ExprNode elem : arr.elements) { + if (!isSimpleLiteral(elem)) return false; + } + return true; + } + + // 3. RangeNode (if parsed separately somehow) + if (expr instanceof RangeNode) { + RangeNode range = (RangeNode) expr; + return isSimpleLiteral(range.start) && + isSimpleLiteral(range.end) && + (range.step == null || isSimpleLiteral(range.step)); + } + + // 4. Tuple literals + if (expr instanceof TupleNode) { + TupleNode tuple = (TupleNode) expr; + for (ExprNode elem : tuple.elements) { + if (!isSimpleLiteral(elem)) return false; + } + return true; + } + + return false; +} + +private String inferTypeFromLiteral(ExprNode expr) { + if (expr == null) return null; + + // 1. Basic ExprNode with value + if (expr instanceof ExprNode) { + ExprNode e = (ExprNode) expr; + + if (e.isNull) return null; // Can't infer from null + + Object value = e.value; + if (value instanceof Integer || value instanceof Long) return "int"; + if (value instanceof Float || value instanceof Double || value instanceof BigDecimal) return "float"; + if (value instanceof String) return "text"; + if (value instanceof Boolean) return "bool"; + } + + // 2. Array literals + if (expr instanceof ArrayNode) { + ArrayNode arr = (ArrayNode) expr; + if (arr.elements.isEmpty()) return null; // Can't infer empty array + + // Check if it's a single range (NaturalArray) + if (arr.elements.size() == 1 && arr.elements.get(0) instanceof RangeNode) { + return "[]"; // NaturalArray is treated as dynamic array [] + } + + // Try to infer from first element + String elementType = inferTypeFromLiteral(arr.elements.get(0)); + if (elementType != null) { + return "[" + elementType + "]"; + } + return null; + } + + // 3. RangeNode directly (if somehow parsed separately) + if (expr instanceof RangeNode) { + return "[]"; // NaturalArray as dynamic array + } + + // 4. Tuple literals + if (expr instanceof TupleNode) { + TupleNode tuple = (TupleNode) expr; + if (tuple.elements.isEmpty()) return null; + + StringBuilder sb = new StringBuilder("("); + for (int i = 0; i < tuple.elements.size(); i++) { + String elemType = inferTypeFromLiteral(tuple.elements.get(i)); + if (elemType == null) return null; // Can't infer if any element unknown + + if (i > 0) sb.append(","); + sb.append(elemType); + } + sb.append(")"); + return sb.toString(); + } + + return null; +} + + // --- Lookahead Methods --- + + public boolean isSlotDeclaration() { + // [CHANGED]: Checks for :: instead of ~: + return isSymbolAt(0, DOUBLE_COLON); + } + +private boolean isMethodDeclaration() { + int offset = 0; + + // Skip leading whitespace + while (lookaheadFrom(0, offset) != null && + (lookaheadFrom(0, offset).type == WS || + lookaheadFrom(0, offset).type == LINE_COMMENT || + lookaheadFrom(0, offset).type == BLOCK_COMMENT)) { + offset++; + } + + // 1. Skip modifiers (builtin / share / local) + Token first = lookaheadFrom(0, offset); + if (first == null) return false; + + if (BUILTIN.toString().equals(first.text) || + SHARE.toString().equals(first.text) || + LOCAL.toString().equals(first.text)) { + offset++; + } + + // Skip whitespace after modifier + while (lookaheadFrom(0, offset) != null && + (lookaheadFrom(0, offset).type == WS || + lookaheadFrom(0, offset).type == LINE_COMMENT || + lookaheadFrom(0, offset).type == BLOCK_COMMENT)) { + offset++; + } + + // 2. Check for method ID name + Token nameToken = lookaheadFrom(0, offset); + if (nameToken == null || nameToken.type != ID) return false; + + offset++; // Skip the name + + // Skip whitespace after name + while (lookaheadFrom(0, offset) != null && + (lookaheadFrom(0, offset).type == WS || + lookaheadFrom(0, offset).type == LINE_COMMENT || + lookaheadFrom(0, offset).type == BLOCK_COMMENT)) { + offset++; + } + + // 3. Check for opening parenthesis + Token parenToken = lookaheadFrom(0, offset); + if (parenToken == null || parenToken.symbol != LPAREN) return false; + + // If we have "share name (", it's a method. + return true; +} + +private boolean isFieldDeclaration() { + int currentPos = getPosition(); + int p = currentPos; + + // Skip optional visibility modifier (local/share) + if (p < tokens.size() && isVisibilityModifier(tokens.get(p))) { + p++; + } + + if (p >= tokens.size()) return false; + + // Pattern: name: type + // 1. Must start with ID (field name) + if (tokens.get(p).type != ID) { + return false; + } + + p++; // Skip the field name + + // 2. Must have colon after name + if (p >= tokens.size() || tokens.get(p).symbol != COLON) { + return false; + } + + p++; // Skip colon + + // 3. Must have a valid type after colon + if (p >= tokens.size() || !isTypeStart(tokens.get(p))) { + return false; + } + + // 4. Check it's not a method call + // After "name: type", check what comes next + int afterTypePos = skipType(p); + if (afterTypePos == -1) return false; + + // If after type we see '(', it's a method parameter list + if (afterTypePos < tokens.size() && tokens.get(afterTypePos).symbol == LPAREN) { + return false; // It's a method declaration + } + + // Otherwise, it's a field declaration + return true; +} + + private boolean isVisibilityModifier(Token token) { + if (token == null) return false; + return (token.type == KEYWORD && (token.text.equals(SHARE.toString()) || token.text.equals(LOCAL.toString()))); + } + + private void skipWhitespaceAndComments() { + while (position.get() < tokens.size()) { + Token t = currentToken(); + if (t.type == WS || t.type == LINE_COMMENT || t.type == BLOCK_COMMENT) { + consume(); + } else { + break; + } + } +} +} \ No newline at end of file diff --git a/src/main/java/cod/parser/ExpressionParser.java b/src/main/java/cod/parser/ExpressionParser.java new file mode 100644 index 00000000..86e7fa95 --- /dev/null +++ b/src/main/java/cod/parser/ExpressionParser.java @@ -0,0 +1,663 @@ +package cod.parser; + +import cod.ast.ASTFactory; +import cod.error.ParseError; +import cod.ast.nodes.*; + +import cod.lexer.MainLexer.Token; +import static cod.lexer.MainLexer.TokenType.*; + +import static cod.syntax.Symbol.*; +import static cod.syntax.Keyword.*; + +import java.util.ArrayList; +import java.util.List; +import java.math.BigDecimal; + +/** + * Predictive parser for expressions using Pratt precedence. + * Replaces complex lookahead with clean precedence-based parsing. + * Maintains 100% API compatibility with the previous implementation. + */ +public class ExpressionParser extends BaseParser { + + // Precedence levels (higher = tighter binding) + private static final int PREC_ASSIGNMENT = 10; + private static final int PREC_EQUALITY = 50; + private static final int PREC_COMPARISON = 60; + private static final int PREC_TERM = 70; + private static final int PREC_FACTOR = 80; + private static final int PREC_UNARY = 90; + private static final int PREC_CALL = 100; + + public ExpressionParser(List tokens, PositionHolder position) { + super(tokens, position); + } + + // === PUBLIC API METHODS (SAME SIGNATURES AS BEFORE) === + + public ExprNode parseExpression() { + if (isKeyword(ALL) || isKeyword(ANY)) { + return parseBooleanChain(); + } + return parsePrecedence(PREC_ASSIGNMENT); + } + + public MethodCallNode parseMethodCall() { + String qualifiedNameStr = parseQualifiedName(); + String methodName = qualifiedNameStr; + if (qualifiedNameStr.contains(".")) { + methodName = qualifiedNameStr.substring(qualifiedNameStr.lastIndexOf('.') + 1); + } + MethodCallNode call = ASTFactory.createMethodCall(methodName, qualifiedNameStr); + + consume(LPAREN); + + if (isKeyword(ALL) || isKeyword(ANY)) { + return parseConditionalChainCall(call); + } + + if (!match(RPAREN)) { + call.arguments.add(parseExpression()); + while (tryConsume(COMMA)) { + call.arguments.add(parseExpression()); + } + } + consume(RPAREN); + return call; + } + + public IndexAccessNode parseIndexAccessContinuation(ExprNode arrayExpr) { + consume(LBRACKET); + ExprNode indexExpr = parseExpression(); + consume(RBRACKET); + return ASTFactory.createIndexAccess(arrayExpr, indexExpr); + } + + public List parseReturnSlots() { + consume(LBRACKET); + List slots = new ArrayList(); + do { + if (match(ID)) { + slots.add(consume(ID).text); + } else if (match(INT_LIT)) { + slots.add(consume(INT_LIT).text); + } else { + throw new ParseError("Expected slot name or index, found " + currentToken().text); + } + } while (tryConsume(COMMA)); + consume(RBRACKET); + return slots; + } + + private ExprNode parseIfExpression() { + consumeKeyword(IF); + ExprNode condition = parseExpression(); + + // Parse then expression (can be in braces or not) + ExprNode thenExpr; + if (match(LBRACE)) { + consume(LBRACE); + thenExpr = parseExpression(); + consume(RBRACE); + } else { + thenExpr = parseExpression(); + } + + // ELSE is REQUIRED for expressions + consumeKeyword(ELSE); + + // Parse else expression + ExprNode elseExpr; + if (match(LBRACE)) { + consume(LBRACE); + elseExpr = parseExpression(); + consume(RBRACE); + } else { + elseExpr = parseExpression(); + } + + return ASTFactory.createIfExpression(condition, thenExpr, elseExpr); +} + + // === CRITICAL METHOD: Called by StatementParser === +public ExprNode parsePrimaryExpression() { + ExprNode baseExpr; + Token startToken = currentToken(); + + if (match(LBRACKET)) { + // FIX: Distinguish between [Array] and [Slot]:Call + if (isSlotAccessExpression()) { + List slotNames = parseReturnSlots(); + consume(COLON); + MethodCallNode methodCall = parseMethodCall(); + methodCall.slotNames = slotNames; + baseExpr = methodCall; + } else { + baseExpr = parseArrayLiteral(); + } + } else if (isKeyword(IF)) { + return parseIfExpression(); +} else if (match(INT_LIT)) { + String intText = consume().text; + try { + // 1. Try int first (Fast path 1) + int intValue = Integer.parseInt(intText); + baseExpr = ASTFactory.createIntLiteral(intValue); + } catch (NumberFormatException e1) { + try { + // 2. Try long (Fast path 2) + long longValue = Long.parseLong(intText); + baseExpr = ASTFactory.createLongLiteral(longValue); + } catch (NumberFormatException e2) { + // 3. Fallback: Too big for long. Use BigDecimal. + BigDecimal bigDecimalValue = new BigDecimal(intText); + baseExpr = ASTFactory.createFloatLiteral(bigDecimalValue); + } + } +} else if (match(FLOAT_LIT)) { + Token floatToken = consume(); + String floatText = floatToken.text; + + Object resolvedValue = resolveFloatLiteralValue(floatText); + + if (resolvedValue instanceof BigDecimal) { + // Case A: Shorthand (already resolved as BigDecimal) + baseExpr = ASTFactory.createFloatLiteral((BigDecimal)resolvedValue); + } else { + // Case B: Regular Float Literal (e.g., 3.14) + // FIX: Convert directly from string to BigDecimal for max precision + BigDecimal bigDecimalValue = new BigDecimal(floatText); + baseExpr = ASTFactory.createFloatLiteral(bigDecimalValue); + } + + } else if (match(STRING_LIT)) { + Token stringToken = consume(); + baseExpr = ASTFactory.createStringLiteral(stringToken.text); + + } else if (isKeyword(TRUE)) { + consumeKeyword(TRUE); + baseExpr = ASTFactory.createBoolLiteral(true); + + } else if (isKeyword(FALSE)) { + consumeKeyword(FALSE); + baseExpr = ASTFactory.createBoolLiteral(false); + + } else if (isKeyword(NULL)) { + consumeKeyword(NULL); + baseExpr = ASTFactory.createNullLiteral(); + + // Handle INPUT as an expression token + } else if (isKeyword(INPUT)) { + consumeKeyword(INPUT); + // Create input node with default "text" type and no variable binding (expression mode) + baseExpr = ASTFactory.createInput(TEXT.toString(), null); + +} else if (match(ID)) { + if (isMethodCallFollows()) { + baseExpr = parseMethodCall(); + } else { + String idName = consume().text; + // Allow underscore as identifier (will be validated in interpreter) + baseExpr = ASTFactory.createIdentifier(idName); + } +} else if (match(LPAREN)) { + if (isTypeCast()) { + baseExpr = parseTypeCast(); + } else { + consume(LPAREN); + ExprNode firstExpr = parseExpression(); + + // Handle Tuples (expressions separated by commas) + if (match(COMMA)) { + List elements = new ArrayList(); + elements.add(firstExpr); + + // Consume the first comma and continue to parse subsequent elements + while (tryConsume(COMMA)) { + elements.add(parseExpression()); + } + + // Check if the tuple is empty or terminated prematurely + if (elements.size() == 1 && !match(RPAREN)) { + // This catches cases like (expr, ) + throw new ParseError("Expected expression after comma in tuple but found " + + currentToken().text, currentToken().line, currentToken().column); + } + + consume(RPAREN); + baseExpr = ASTFactory.createTuple(elements); + } else { + // No comma found, it was a simple grouped expression (a+b) + consume(RPAREN); + baseExpr = firstExpr; + } + } + } else { + throw new ParseError("Unexpected token in primary expression: " + startToken.text + + " (" + getTypeName(startToken.type) + ")" + + " at line " + startToken.line + ":" + startToken.column); + } + + while (match(LBRACKET)) { + baseExpr = parseIndexAccessContinuation(baseExpr); + } + + return baseExpr; +} + + // === PRIVATE IMPLEMENTATION (PREDICTIVE PARSING) === + + private ExprNode parsePrecedence(int precedence) { + ExprNode left = parsePrefix(); + + while (true) { + Token op = currentToken(); + int opPrecedence = getPrecedence(op); + + if (opPrecedence < precedence) { + break; + } + + // Special case: boolean chain (x == all[...]) + if (isComparisonOp(op) && isChainFollows(1)) { + consume(); + return parseEqualityChain(left, op.text); + } + + // Regular binary operator + if (opPrecedence > 0) { + consume(); + ExprNode right = parsePrecedence(opPrecedence + 1); + left = ASTFactory.createBinaryOp(left, op.text, right); + continue; + } + + // Index access + if (op.symbol == LBRACKET) { + left = parseIndexAccessContinuation(left); + continue; + } + + break; + } + + return left; + } + + private ExprNode parsePrefix() { + // Handle unary operators first + if (match(BANG) || match(PLUS) || match(MINUS)) { + Token op = consume(); + ExprNode operand = parsePrecedence(PREC_UNARY); + return ASTFactory.createUnaryOp(op.text, operand); + } + + // Then parse primary expression + return parsePrimaryExpression(); + } + + // === BOOLEAN CHAINS (KEPT FROM ORIGINAL) === + + private ExprNode parseBooleanChain() { + Token typeToken = consume(); + boolean isAll = typeToken.text.equals(ALL.toString()); + + if (match(ID)) { + String arrayName = consume().text; + + if (isComparisonOp(currentToken())) { + Token op = consume(); + ExprNode right = parsePrecedence(PREC_COMPARISON + 1); + + List chainArgs = new ArrayList(); + chainArgs.add(right); + + ExprNode arrayExpr = ASTFactory.createIdentifier(arrayName); + EqualityChainNode chain = ASTFactory.createEqualityChain(arrayExpr, op.text, isAll, chainArgs); + return chain; + } else { + throw new ParseError("Expected comparison operator after 'all/any ' but found " + + getTypeName(currentToken().type) + " ('" + currentToken().text + + "') at line " + currentToken().line + ":" + currentToken().column); + } + } else if (match(LBRACKET)) { + consume(LBRACKET); + List expressions = new ArrayList(); + + if (!match(RBRACKET)) { + expressions.add(parseExpression()); + if (!match(COMMA) && !match(RBRACKET)) { + throw new ParseError("Boolean chain requires at least two expressions or a comma after the first expression."); + } + while (tryConsume(COMMA)) { + expressions.add(parseExpression()); + } + } + consume(RBRACKET); + + BooleanChainNode node = ASTFactory.createBooleanChain(isAll, expressions); + return node; + } else { + throw new ParseError("Expected array variable or '[' after 'all/any' but found " + + getTypeName(currentToken().type) + " ('" + currentToken().text + + "') at line " + currentToken().line + ":" + currentToken().column); + } + } + + private ExprNode parseEqualityChain(ExprNode left, String operator) { + Token chainTypeToken = consume(); + boolean isAllChain = chainTypeToken.text.equals(ALL.toString()); + + List chainArgs = new ArrayList(); + + if (match(LBRACKET)) { + consume(LBRACKET); + if (!match(RBRACKET)) { + chainArgs.add(parseChainArgument()); + while (tryConsume(COMMA)) { + chainArgs.add(parseChainArgument()); + } + } + consume(RBRACKET); + } else if (match(ID)) { + String arrayName = consume(ID).text; + ExprNode arrayExpr = ASTFactory.createIdentifier(arrayName); + chainArgs.add(arrayExpr); + } else { + throw new ParseError("Expected array variable or '[' for array literal after 'all/any' but found " + + getTypeName(currentToken().type) + " ('" + currentToken().text + + "') at line " + currentToken().line + ":" + currentToken().column); + } + + EqualityChainNode chain = ASTFactory.createEqualityChain(left, operator, isAllChain, chainArgs); + return chain; + } + + private ExprNode parseChainArgument() { + if (match(BANG)) { + consume(BANG); + ExprNode arg = parsePrimaryExpression(); + return ASTFactory.createUnaryOp("!", arg); + } + + if (match(LPAREN)) { + return parseArgumentList(); + } + + if (match(ID) && isSymbolAt(1, RBRACKET)) { + Token idToken = currentToken(); + throw new ParseError("Redundant brackets around array variable '" + idToken.text + + "'. Use 'any " + idToken.text + "' instead of 'any[" + idToken.text + "]'", + idToken.line, idToken.column); + } + + return parsePrimaryExpression(); + } + + private ExprNode parseArgumentList() { + consume(LPAREN); + List arguments = new ArrayList(); + if (!match(RPAREN)) { + arguments.add(parseExpression()); + while (tryConsume(COMMA)) { + arguments.add(parseExpression()); + } + } + consume(RPAREN); + return ASTFactory.createArgumentList(arguments); + } + + private MethodCallNode parseConditionalChainCall(MethodCallNode call) { + Token chainTypeToken = consume(); + boolean isAllChain = chainTypeToken.text.equals(ALL.toString()); + call.chainType = isAllChain ? ALL.toString() : ANY.toString(); + + consume(LBRACKET); + + List chainArgs = new ArrayList(); + if (!match(RBRACKET)) { + chainArgs.add(parseChainArgument()); + while (tryConsume(COMMA)) { + chainArgs.add(parseChainArgument()); + } + } + + consume(RBRACKET); + + call.chainArguments = chainArgs; + return call; + } + + private ExprNode parseArrayLiteral() { + consume(LBRACKET); + List elements = new ArrayList(); + + if (!match(RBRACKET)) { + // Check if this is a range + if (isRangeStart()) { + elements.add(parseRangeExpression()); + } else { + elements.add(parseExpression()); + } + + while (tryConsume(COMMA)) { + // Check if next is a range + if (isRangeStart()) { + elements.add(parseRangeExpression()); + } else { + elements.add(parseExpression()); + } + } + } + + consume(RBRACKET); + return ASTFactory.createArray(elements); +} + +// Add this helper method: +private boolean isRangeStart() { + Token current = currentToken(); + + // Case 1: "by" step "in" start "to" end + if (current.type == KEYWORD && current.text.equals("by")) { + return true; + } + + // Case 2: start "to" end (no 'by') + // Try to parse an expression, then check for "to" + int savedPos = getPosition(); + try { + parseExpression(); // Try to parse start + return currentToken().type == KEYWORD && + currentToken().text.equals("to"); + } catch (ParseError e) { + return false; + } finally { + position.set(savedPos); // Reset position + } +} + +// Add this method (similar to StatementParser.parseForStatement range parsing): +private RangeNode parseRangeExpression() { + ExprNode step = null; + + // Check for "by step" + if (isKeyword(BY)) { + consumeKeyword(BY); + step = parseExpression(); + consumeKeyword(IN); + } + + // Parse "start to end" + ExprNode start = parseExpression(); + consumeKeyword(TO); + ExprNode end = parseExpression(); + + return ASTFactory.createRange(step, start, end); +} + + private ExprNode parseTypeCast() { + consume(LPAREN); + String type = parseTypeReference(); + consume(RPAREN); + ExprNode expressionToCast = parsePrecedence(PREC_UNARY); + return ASTFactory.createTypeCast(type, expressionToCast); + } + +// NEW Helper method to resolve numeric shorthands or standard scientific notation into BigDecimal. +private Object resolveFloatLiteralValue(String literal) { + String baseValueStr; + String suffix; + int exponent = 0; + + // --- 1. Check for Custom Shorthands (K, M, Qi, etc.) --- + if (literal.endsWith("Qi")) { + suffix = "Qi"; + baseValueStr = literal.substring(0, literal.length() - 2); + exponent = 18; + } else { + char lastChar = literal.charAt(literal.length() - 1); + + if (lastChar == 'K' || lastChar == 'M' || lastChar == 'B' || lastChar == 'T' || lastChar == 'Q') { + suffix = String.valueOf(lastChar); + baseValueStr = literal.substring(0, literal.length() - 1); + + switch (suffix) { + case "K": exponent = 3; break; + case "M": exponent = 6; break; + case "B": exponent = 9; break; + case "T": exponent = 12; break; + case "Q": exponent = 15; break; + default: break; + } + } else { + // 2. Standard Scientific Notation ('e' or 'E') + if (literal.contains("e") || literal.contains("E")) { + try { + // BigDecimal's constructor handles this standard format directly + return new BigDecimal(literal); + } catch (NumberFormatException e) { + // Fall through to general error + } + } + + // If no shorthand and no 'e', return null (it's a plain float) + return null; + } + } + + // --- 3. Calculation for Custom Shorthands (if reached) --- + if (exponent > 0) { + try { + BigDecimal base = new BigDecimal(baseValueStr); + BigDecimal multiplier = BigDecimal.TEN.pow(exponent); + return base.multiply(multiplier); + } catch (NumberFormatException e) { + // Fall through to general error + } + } + + throw new ParseError("Invalid numeric literal format: " + literal, + currentToken().line, currentToken().column); +} + + // === ORIGINAL LOOKAHEAD HELPERS (KEPT FOR COMPATIBILITY) === + + private boolean isMethodCallFollows() { + Token first = lookahead(0); + if (first == null || first.type != ID) return false; + + int pos = 1; + while (lookahead(pos) != null && lookahead(pos).symbol == DOT) { + pos++; + if (lookahead(pos) == null || lookahead(pos).type != ID) return false; + pos++; + } + + Token afterDots = lookahead(pos); + return afterDots != null && afterDots.symbol == LPAREN; + } + + private boolean isSlotAccessExpression() { + if (!isSymbolAt(0, LBRACKET)) return false; + + int pos = getPosition() + 1; + int depth = 1; + + while (pos < tokens.size() && depth > 0) { + Token t = tokens.get(pos); + if (t.symbol == LBRACKET) depth++; + else if (t.symbol == RBRACKET) depth--; + pos++; + } + + if (depth == 0 && pos < tokens.size()) { + return tokens.get(pos).symbol == COLON; + } + return false; + } + + private boolean isTypeCast() { + if (!isSymbolAt(0, LPAREN)) return false; + Token second = lookahead(1); + if (second == null || !isTypeStart(second)) return false; + + int pos = getPosition(); + int parenDepth = 0; + + for (int i = 0; i < 20 && pos < tokens.size(); i++) { + Token t = tokens.get(pos); + if (t.symbol == LPAREN) parenDepth++; + else if (t.symbol == RPAREN) { + parenDepth--; + if (parenDepth == 0) { + if (pos + 1 < tokens.size()) { + Token afterParen = tokens.get(pos + 1); + return isExpressionStart(afterParen); + } + return false; + } + } + pos++; + } + return false; + } + + // === UTILITY METHODS === + + private int getPrecedence(Token token) { + if (token == null) return 0; + + if (token.type == SYMBOL) { + switch (token.symbol) { + case EQ: case NEQ: return PREC_EQUALITY; + case LT: case GT: case LTE: case GTE: return PREC_COMPARISON; + case PLUS: case MINUS: return PREC_TERM; + case MUL: case DIV: case MOD: return PREC_FACTOR; + case LPAREN: case LBRACKET: return PREC_CALL; + default: return 0; + } + } + return 0; + } + + private boolean isChainFollows(int offset) { + Token next = peek(offset); + if (next == null) return false; + + if (next.type == KEYWORD && + (next.text.equals(ALL.toString()) || next.text.equals(ANY.toString()))) { + Token after = peek(offset + 1); + return after != null && (after.symbol == LBRACKET || after.type == ID); + } + return false; + } + + private boolean isComparisonOp(Token t) { + return t != null && t.symbol != null && + (t.symbol == EQ || t.symbol == NEQ || + t.symbol == GT || t.symbol == LT || + t.symbol == GTE || t.symbol == LTE); + } +} \ No newline at end of file diff --git a/src/main/java/cod/parser/MainParser.java b/src/main/java/cod/parser/MainParser.java new file mode 100644 index 00000000..7ef1e482 --- /dev/null +++ b/src/main/java/cod/parser/MainParser.java @@ -0,0 +1,786 @@ +package cod.parser; + +import cod.error.ParseError; +import cod.ast.ASTFactory; +import cod.ast.nodes.*; + +import static cod.syntax.Keyword.*; +import cod.syntax.Symbol; +import static cod.syntax.Symbol.*; + +import java.util.ArrayList; +import java.util.List; + +import static cod.lexer.MainLexer.Token; +import static cod.lexer.MainLexer.TokenType.*; + +/** + * The main parser entry point, responsible for the overall program structure. + * Uses shared PositionHolder for automatic position synchronization across all parsers. + * Now includes program type detection and validation for the three-worlds design. + */ +public class MainParser extends BaseParser { + + private final ExpressionParser expressionParser; + private final StatementParser statementParser; + private final DeclarationParser declarationParser; + + public MainParser(List tokens) { + // Initialize BaseParser with shared PositionHolder + super(tokens, new PositionHolder(0)); + + // All parsers share the same position counter - NO synchronization needed! + this.expressionParser = new ExpressionParser(tokens, this.position); + this.statementParser = new StatementParser(tokens, this.position, expressionParser); + this.declarationParser = new DeclarationParser(tokens, this.position, statementParser); + } + +public ProgramNode parseProgram() { + // Detect program type first + ProgramType programType = detectProgramType(); + + // Reset position for actual parsing + position.set(0); + + // Parse based on program type + ProgramNode program = null; + try { + switch (programType) { + case MODULE: + program = parseModuleProgram(); + break; + case SCRIPT: + program = parseScriptProgram(); + break; + case METHOD_SCRIPT: + program = parseMethodScriptProgram(); + break; + default: + throw new ParseError("Unknown program type: " + programType); + } + } catch (ParseError e) { + // Add program type context to error + throw new ParseError("[" + programType + "] " + e.getMessage(), + e.getLine(), e.getColumn()); + } + + // Set program type + program.programType = programType; + + // Validate program structure + cod.semantic.ProgramValidator.validate(program, programType); + + return program; +} + + /** + * Entry method to parse a single line statement (e.g., for REPL/debugging). + */ + public StmtNode parseSingleLine() { + if (match(EOF)) { + return null; + } + + // Automatic position sharing - no synchronization needed! + StmtNode stmt = statementParser.parseStatement(); + + if (!match(EOF)) { + Token current = currentToken(); + throw new ParseError("Unexpected token after statement: " + + getTypeName(current.type) + " ('" + current.text + "')" + + " at line " + current.line + ":" + current.column); + } + return stmt; + } + + private UnitNode parseUnit() { + consumeKeyword(UNIT); + String unitName = parseQualifiedName(); + UnitNode unit = ASTFactory.createUnit(unitName); + + if (isKeyword(USE)) { + unit.imports = parseUseNode(); + } + return unit; + } + + private UseNode parseUseNode() { + consumeKeyword(USE); + consume(LBRACE); + List imports = new ArrayList(); + if (!match(RBRACE)) { + imports.add(parseQualifiedName()); + while (tryConsume(COMMA)) { + imports.add(parseQualifiedName()); + } + } + consume(RBRACE); + UseNode getNode = ASTFactory.createUseNode(imports); + return getNode; + } + + private TypeNode parseTypeDelegation() { + // Automatic position sharing - no synchronization needed! + return declarationParser.parseType(); + } + + // ========================================================================= + // NEW METHODS FOR THREE-WORLDS DESIGN + // ========================================================================= + +/** + * Detects the program type based on file structure. + */ +private ProgramType detectProgramType() { + // Save current position + int savedPos = position.get(); + position.set(0); + + // Turn off debug output for cleaner test results + // System.err.println("[DEBUG] Starting program type detection"); + + try { + boolean hasUnit = false; + boolean hasDirectCode = false; + boolean hasMethods = false; + boolean hasClasses = false; + + // Track if we're inside imports (skip them) + boolean skippingImports = false; + + // Scan tokens + while (!match(EOF)) { + Token current = currentToken(); + + // Skip imports (use keyword) + if (current.type == KEYWORD && USE.toString().equals(current.text)) { + skippingImports = true; + consume(); // consume "use" + if (match(LBRACE)) { + consume(LBRACE); + skipUntil(RBRACE); + } + continue; + } + + // Check for unit declaration + if (!hasUnit && current.type == KEYWORD && UNIT.toString().equals(current.text)) { + hasUnit = true; + consume(); // consume "unit" + // Skip unit name + if (match(ID)) { + parseQualifiedName(); + } + continue; + } + + // After unit, we should only see class declarations + if (hasUnit) { + // Check for class declaration (visibility modifier) + if (isVisibilityModifier()) { + hasClasses = true; + // Skip the class declaration + skipTypeDeclaration(); + continue; + } + // Anything else after unit is an error + if (!skippingImports) { + hasDirectCode = true; // This will cause validation error + } + consume(); + continue; + } + + // No unit yet, check for other patterns + + // Check for method declaration (BOTH syntaxes) + if (isMethodDeclarationStart()) { + hasMethods = true; + skipMethodDeclaration(); + continue; + } + + // --- CRITICAL FIX: Handle ~> as part of method, not direct code --- + if (current.symbol == TILDE_ARROW) { + // This is likely a method's return arrow + // Look back to see if we might have missed a method declaration + // For simplicity, treat it as part of a method + hasMethods = true; + consume(); // skip ~> + // Skip the slot assignment expression + skipExpression(); + continue; + } + + // Check for class declaration (without unit - error) + if (isVisibilityModifier()) { + hasClasses = true; + skipTypeDeclaration(); + continue; + } + + // Check for direct code/statements + if (looksLikeDirectCode(current)) { + hasDirectCode = true; + skipStatement(); + continue; + } + + // Skip other tokens + consume(); + } + + // Apply detection rules + ProgramType result = determineProgramType(hasUnit, hasDirectCode, hasMethods, hasClasses); + // System.err.println("[DEBUG] Final detection: " + result); + return result; + + } finally { + // Restore position + position.set(savedPos); + } +} + + /** + * Parse a MODULE program (has unit declaration). + */ + private ProgramNode parseModuleProgram() { + ProgramNode program = ASTFactory.createProgram(); + + // Must have unit + program.unit = parseUnit(); + + // Parse imports + while (isKeyword(USE)) { + if (program.unit.imports == null) { + program.unit.imports = parseUseNode(); + } else { + UseNode additionalImports = parseUseNode(); + program.unit.imports.imports.addAll(additionalImports.imports); + } + } + + // Parse classes only + while (!match(EOF)) { + if (isVisibilityModifier()) { + program.unit.types.add(parseTypeDelegation()); + } else { + throw new ParseError("Modules can only contain class declarations after imports"); + } + } + + return program; + } + + /** + * Parse a SCRIPT program (direct code only). + */ + private ProgramNode parseScriptProgram() { + ProgramNode program = ASTFactory.createProgram(); + program.unit = ASTFactory.createUnit("default"); + + // Parse imports + while (isKeyword(USE)) { + if (program.unit.imports == null) { + program.unit.imports = parseUseNode(); + } else { + UseNode additionalImports = parseUseNode(); + program.unit.imports.imports.addAll(additionalImports.imports); + } + } + + // Create synthetic type to hold statements + TypeNode scriptType = ASTFactory.createType("__Script__", SHARE, null); + + // Parse statements directly + while (!match(EOF)) { + if (isVisibilityModifier() || isMethodDeclarationStart()) { + throw new ParseError("Scripts cannot contain method or class declarations"); + } + scriptType.statements.add(statementParser.parseStatement()); + } + + program.unit.types.add(scriptType); + return program; + } + + /** + * Parse a METHOD_SCRIPT program (methods only). + */ + private ProgramNode parseMethodScriptProgram() { + ProgramNode program = ASTFactory.createProgram(); + program.unit = ASTFactory.createUnit("default"); + + // Parse imports + while (isKeyword(USE)) { + if (program.unit.imports == null) { + program.unit.imports = parseUseNode(); + } else { + UseNode additionalImports = parseUseNode(); + program.unit.imports.imports.addAll(additionalImports.imports); + } + } + + // Create synthetic type to hold methods + TypeNode methodScriptType = ASTFactory.createType("__MethodScript__", SHARE, null); + + // Parse method declarations only + while (!match(EOF)) { + if (!isMethodDeclarationStart()) { + throw new ParseError("Method scripts can only contain method declarations"); + } + methodScriptType.methods.add(declarationParser.parseMethod()); + } + + program.unit.types.add(methodScriptType); + return program; + } + + // ========================================================================= + // HELPER METHODS FOR PROGRAM TYPE DETECTION + // ========================================================================= + + private boolean isMethodDeclarationStart() { + Token first = currentToken(); + if (first == null) return false; + + // Must be local or share keyword + if (first.type == KEYWORD) { + String text = first.text; + if (LOCAL.toString().equals(text) || SHARE.toString().equals(text)) { + Token second = lookahead(1); + if (second != null && second.type == ID) { + Token third = lookahead(2); + // Method if followed by '(' + if (third != null && third.symbol == LPAREN) { + return true; + } + } + } + } + return false; +} + + private boolean looksLikeDirectCode(Token token) { + if (token == null) return false; + + // --- FIX: ~> is NOT direct code, it's part of method --- + if (token.symbol == TILDE_ARROW) { + return false; // This is method return syntax + } + + // Keywords that start statements (NOT method declarations) + if (token.type == KEYWORD) { + String text = token.text; + + // Check if this is a method declaration start + if ("local".equals(text) || "share".equals(text)) { + // Look ahead to see if it's a method + Token next1 = lookahead(1); + Token next2 = lookahead(2); + if (next1 != null && next1.type == ID && + next2 != null && next2.symbol == LPAREN) { + return false; // It's a method declaration, NOT direct code + } + } + + // Only these keywords are direct code + return text.equals("output") || + text.equals("if") || + text.equals("for") || + text.equals("exit") || + text.equals("input"); + } + + // Variable declaration/assignment + if (token.type == ID) { + Token next = lookahead(1); + if (next != null) { + // name := value + if (next.symbol == DOUBLE_COLON_ASSIGN) return true; + // name = value + if (next.symbol == ASSIGN) return true; + // name: type or name: type = value + if (next.symbol == COLON) return true; + } + return true; // Could be method call or variable reference + } + + // Expression start + return isExpressionStart(token); +} + + private void skipTypeDeclaration() { + // Skip visibility modifier + consume(); + // Skip type name + if (match(ID)) consume(); + // Skip 'is' and base type if present + if (isKeyword(IS)) { + consumeKeyword(IS); + parseQualifiedName(); + } + // Skip opening brace + if (match(LBRACE)) consume(LBRACE); + + // Skip everything until matching closing brace + int braceDepth = 1; + while (!match(EOF) && braceDepth > 0) { + Token t = currentToken(); + if (t.symbol == LBRACE) braceDepth++; + else if (t.symbol == RBRACE) braceDepth--; + consume(); + } + } + + private void skipMethodDeclaration() { + // Skip modifier (share/local) + consume(); + + // Skip method name + if (match(ID)) consume(); + + // Skip parameters + if (match(LPAREN)) { + consume(LPAREN); + skipUntil(RPAREN); + } + + // Skip return slots if present + if (match(DOUBLE_COLON)) { + consume(DOUBLE_COLON); + skipSlotContract(); + } + + // Handle both ~> and { syntax + if (match(TILDE_ARROW)) { + consume(TILDE_ARROW); + + // --- FIX: Skip slot assignments properly --- + // Skip first slot assignment + skipSlotAssignment(); + + // Skip additional comma-separated slot assignments + while (tryConsume(COMMA)) { + skipSlotAssignment(); + } + + } else if (match(LBRACE)) { + consume(LBRACE); + skipUntil(RBRACE); + } +} + +// NEW helper method +private void skipSlotAssignment() { + // Skip optional slot name and colon + if (match(ID)) { + Token next = lookahead(1); + if (next != null && next.symbol == COLON) { + consume(); // skip name + consume(); // skip colon + } + } + // Skip the expression + skipExpression(); +} + + private void skipSlotContract() { + do { + // Skip name: type or just type + if (match(ID) && isSymbolAt(1, COLON)) { + consume(); // name + consume(COLON); + } + // Skip type + skipTypeReference(); + } while (tryConsume(COMMA)); + } + + private void skipStatement() { + Token current = currentToken(); + + if (current.type == KEYWORD) { + String text = current.text; + if ("if".equals(text)) { + skipIfStatement(); + } else if ("for".equals(text)) { + skipForStatement(); + } else if ("output".equals(text)) { + skipOutputStatement(); + } else if ("exit".equals(text)) { + consume(); // exit + } else if ("local".equals(text) || "share".equals(text)) { + // Check if this is a method declaration + Token next = lookahead(1); + if (next != null && next.type == ID) { + Token afterNext = lookahead(2); + if (afterNext != null && afterNext.symbol == LPAREN) { + // It's a method declaration + skipMethodDeclaration(); + } else { + // Not a method, just skip the keyword + consume(); + } + } else { + consume(); + } + } else { + // Skip other keywords + consume(); + } + } else if (current.type == ID) { + // Skip until statement end + skipUntilStatementEnd(); + } else { + // Expression or other + consume(); + } +} + +private void skipForStatement() { + // Skip "for" + consumeKeyword(FOR); + + // Skip iterator name + if (match(ID)) consume(); + + // Skip optional "by" + if (isKeyword(BY)) { + consumeKeyword(BY); + // Skip step expression + skipExpression(); + } + + // Skip "in" + if (isKeyword(IN)) { + consumeKeyword(IN); + } + + // Skip start expression + skipExpression(); + + // Skip "to" + if (isKeyword(TO)) { + consumeKeyword(TO); + } else { + // If we don't find "to", just return + return; + } + + // Skip end expression + skipExpression(); + + // Skip body (could be block or single statement) + if (match(LBRACE)) { + consume(LBRACE); + skipUntil(RBRACE); + } else { + skipStatement(); + } +} + + private void skipIfStatement() { + consumeKeyword(IF); + skipExpression(); + + // Skip then block + if (match(LBRACE)) { + consume(LBRACE); + skipUntil(RBRACE); + } else { + skipStatement(); + } + + // Skip else/elif if present + while (isKeyword(ELIF) || isKeyword(ELSE)) { + consume(); + if (isKeyword(IF)) { + consumeKeyword(IF); + skipExpression(); + } + + if (match(LBRACE)) { + consume(LBRACE); + skipUntil(RBRACE); + } else { + skipStatement(); + } + } + } + + private void skipOutputStatement() { + consumeKeyword(OUTPUT); + skipExpression(); + } + + private void skipExpression() { + // UPDATED: Now tracks nesting and handles Commas/Keywords correctly + int braceDepth = 0; + int parenDepth = 0; + int bracketDepth = 0; + + while (!match(EOF)) { + Token t = currentToken(); + + // 1. Handle Nesting + if (t.symbol == LBRACE) braceDepth++; + else if (t.symbol == RBRACE) braceDepth--; + else if (t.symbol == LPAREN) parenDepth++; + else if (t.symbol == RPAREN) parenDepth--; + else if (t.symbol == LBRACKET) bracketDepth++; + else if (t.symbol == RBRACKET) bracketDepth--; + + // If we closed a nesting level that we didn't open in this context (e.g. end of method body), stop. + if (braceDepth < 0 || parenDepth < 0 || bracketDepth < 0) { + return; + } + + // 2. Check Stop Conditions (Only at depth 0) + if (braceDepth == 0 && parenDepth == 0 && bracketDepth == 0) { + // Stop at COMMA (necessary for slot lists: ~> a, b) + if (t.symbol == COMMA) { + return; + } + + // Stop at keywords that start new statements or declarations + if (t.type == KEYWORD) { + String text = t.text; + if (text.equals(IF.toString()) || + text.equals(FOR.toString()) || + text.equals(OUTPUT.toString()) || + text.equals(EXIT.toString()) || + text.equals(ELSE.toString()) || + text.equals(ELIF.toString()) || + // CRITICAL FIX: Stop at method/class modifiers + text.equals("share") || + text.equals("local") || + text.equals("unit")) { + return; + } + } + } + + consume(); + } +} + + private void skipUntil(Symbol symbol) { + while (!match(EOF) && !match(symbol)) { + Token t = currentToken(); + if (t.symbol == LBRACE || t.symbol == LPAREN || t.symbol == LBRACKET) { + // Skip nested + consume(); + skipUntilMatching(t.symbol); + } else { + consume(); + } + } + if (match(symbol)) consume(); + } + + private void skipUntilMatching(Symbol opening) { + Symbol closing; + if (opening == LBRACE) closing = RBRACE; + else if (opening == LPAREN) closing = RPAREN; + else if (opening == LBRACKET) closing = RBRACKET; + else return; + + skipUntil(closing); + } + + private void skipUntilStatementEnd() { + while (!match(EOF)) { + Token t = currentToken(); + + // Statement end markers - NO SEMICOLON IN YOUR LANGUAGE + if (t.symbol == RBRACE || + (t.type == KEYWORD && + (t.text.equals(ELSE.toString()) || t.text.equals("elif") || + t.text.equals("if") || t.text.equals("for") || + t.text.equals("output") || t.text.equals("exit")))) { + break; + } + + // Skip nested structures + if (t.symbol == LBRACE || t.symbol == LPAREN || t.symbol == LBRACKET) { + consume(); + skipUntilMatching(t.symbol); + } else { + consume(); + } + } + } + + private void skipTypeReference() { + if (match(LBRACKET)) { + consume(LBRACKET); + if (!match(RBRACKET)) { + skipTypeReference(); + } + consume(RBRACKET); + } else if (match(LPAREN)) { + consume(LPAREN); + skipTypeReference(); + while (tryConsume(COMMA)) { + skipTypeReference(); + } + consume(RPAREN); + } else if (isTypeStart(currentToken())) { + consume(); + } + + // Skip union + while (tryConsume(PIPE)) { + skipTypeReference(); + } + } + + private ProgramType determineProgramType(boolean hasUnit, boolean hasDirectCode, + boolean hasMethods, boolean hasClasses) { + + // Rule 1: Has unit → MUST be Module + if (hasUnit) { + if (hasDirectCode) { + throw new ParseError("Modules cannot have direct code outside classes."); + } + if (hasMethods && !hasClasses) { + throw new ParseError("Modules cannot have methods outside classes."); + } + return ProgramType.MODULE; + } + + // Rule 2: Has direct code → Script + if (hasDirectCode) { + if (hasMethods) { + throw new ParseError("Cannot mix direct code and method declarations.\n" + + "Either:\n" + + "1. Remove methods and keep as script, OR\n" + + "2. Remove direct code and make it a method script, OR\n" + + "3. Add 'unit' and classes to make it a module."); + } + if (hasClasses) { + throw new ParseError("Scripts cannot contain class declarations."); + } + return ProgramType.SCRIPT; + } + + // Rule 3: Has methods → Method Script + if (hasMethods) { + if (hasClasses) { + throw new ParseError("Method scripts cannot contain class declarations."); + } + return ProgramType.METHOD_SCRIPT; + } + + // Rule 4: Has classes without unit → ERROR + if (hasClasses) { + throw new ParseError("Classes require 'unit' declaration.\n" + + "Add: unit namespace.name\n" + + "Before your class definitions."); + } + + // Empty file or unrecognized + throw new ParseError("Empty file or unrecognized structure"); +} +} \ No newline at end of file diff --git a/src/main/java/cod/parser/PositionHolder.java b/src/main/java/cod/parser/PositionHolder.java new file mode 100644 index 00000000..749a7265 --- /dev/null +++ b/src/main/java/cod/parser/PositionHolder.java @@ -0,0 +1,26 @@ +package cod.parser; + +/** + * Simple holder for shared position across parsers. + * This allows multiple parser instances to share and update + * the same position in the token stream. + */ +public class PositionHolder { + public int value; + + public PositionHolder(int initial) { + this.value = initial; + } + + public void up() { + value++; + } + + public int get() { + return value; + } + + public void set(int newValue) { + value = newValue; + } +} \ No newline at end of file diff --git a/src/main/java/cod/parser/ProgramType.java b/src/main/java/cod/parser/ProgramType.java new file mode 100644 index 00000000..eb803314 --- /dev/null +++ b/src/main/java/cod/parser/ProgramType.java @@ -0,0 +1,16 @@ +package cod.parser; + +/** + * Enum representing the three program types in the language. + * Determined by the structure of the source file. + */ +public enum ProgramType { + /** Only direct statements - no methods, no classes, no unit */ + SCRIPT, + + /** Only methods - no direct code, no classes, no unit */ + METHOD_SCRIPT, + + /** Unit declaration with classes only - no direct code, no methods outside classes */ + MODULE +} \ No newline at end of file diff --git a/src/main/java/cod/parser/StatementParser.java b/src/main/java/cod/parser/StatementParser.java new file mode 100644 index 00000000..ab2fa036 --- /dev/null +++ b/src/main/java/cod/parser/StatementParser.java @@ -0,0 +1,679 @@ +package cod.parser; + +import cod.ast.ASTFactory; +import cod.semantic.NamingValidator; +import cod.error.ParseError; +import cod.ast.nodes.*; + +import java.util.ArrayList; +import java.util.List; + +import cod.lexer.MainLexer.Token; +import static cod.lexer.MainLexer.TokenType.*; + +import static cod.syntax.Symbol.*; +import static cod.syntax.Keyword.*; + +public class StatementParser extends BaseParser { + + public final ExpressionParser expressionParser; + + public StatementParser( + List tokens, PositionHolder position, ExpressionParser expressionParser) { + super(tokens, position); + this.expressionParser = expressionParser; + } + + public StmtNode parseStatement() { + return parseStatement(null); + } + + private StmtNode parseStatement(Boolean inheritedStyle) { + checkIllegalDeclaration(); + + Token first = currentToken(); + + // 1. Keyword statements (fast path) + if (first.type == KEYWORD) { + String text = first.text; + if (IF.toString().equals(text)) return parseIfStatement(inheritedStyle); + if (FOR.toString().equals(text)) return parseForStatement(); + if (OUTPUT.toString().equals(text)) return parseOutputStatement(); + if (EXIT.toString().equals(text)) return parseExitStatement(); + } + + // 2. Symbol statements (Slot Assignment) + if (first.symbol == TILDE_ARROW) return parseSlotAssignment(); + + // 3. Method Call Statement with return slots (starts with [) + if (first.symbol == LBRACKET && isMethodCallStatement()) { + return parseMethodCallStatement(); + } + + // 4. Reassignment and Method Call (declarations handled above) + if (isVariableDeclaration()) { + return parseVariableDeclaration(); + } + + // 5. Declaration, Assignment, or Method Call + if (first.type == ID) { + Token second = lookahead(1); + + if (second != null) { + // Case A: ID [ ... (Index Assignment only) + if (first.type == ID && second.symbol == LBRACKET) { + if (isIndexAssignment()) return parseIndexAssignment(); + } + + // Case B: ID = ... (Simple reassignment) + if (first.type == ID && second.symbol == ASSIGN) { + // Check specific assignments before falling back to simple + if (isReturnSlotAssignment()) return parseReturnSlotAssignment(); + if (isInputAssignment()) return parseInputAssignment(); + return parseSimpleAssignment(); + } + + // Case C: ID , ... (Multi-var return slot) + if (first.type == ID && second.symbol == COMMA) { + if (isReturnSlotAssignment()) return parseReturnSlotAssignment(); + } + } + + // Fallback for Calls (f()) + if (isMethodCallStatement()) return parseMethodCallStatement(); + } + + // 6. Everything else (Expression Statement) + return parseExpressionStatement(); +} + + private void checkIllegalDeclaration() { + Token current = currentToken(); + if (current.type != KEYWORD) return; + + // REMOVED: VAR keyword check + // Only check if keyword looks like a type start + if (isTypeStart(current)) return; + + Token idToken = peek(1); + if (idToken != null && idToken.type == ID) { + Token afterId = peek(2); + if (afterId != null) { + if (afterId.symbol == ASSIGN) { + throw new ParseError( + "Illegally used reserved keyword '" + + current.text + + "' for declaration of variable '" + + idToken.text + + "'", + current.line, + idToken.column); + } + if (afterId.symbol == LBRACKET && peek(3) != null && peek(3).symbol == RBRACKET) { + throw new ParseError( + "Illegally used reserved keyword '" + + current.text + + "' for declaration of variable '" + + idToken.text + + "'", + current.line, + idToken.column); + } + } + } +} + + private SlotAssignmentNode parseSingleSlotAssignment() { + String slotName = null; + ExprNode value; + + // Check for named slot: "slotName: expression" + if (currentToken().type == ID) { + Token afterId = lookahead(1); + if (afterId != null && afterId.symbol == COLON) { + // Named slot: "sum: a + b" + slotName = consume(ID).text; + consume(COLON); + value = expressionParser.parseExpression(); + } else { + // Positional slot: just an expression + slotName = null; + value = expressionParser.parseExpression(); + } + } else { + // Positional slot: just an expression + slotName = null; + value = expressionParser.parseExpression(); + } + + return ASTFactory.createSlotAssignment(slotName, value); +} + + private StmtNode parseSlotAssignment() { + consume(TILDE_ARROW); + List assignments = new ArrayList(); + assignments.add(parseSingleSlotAssignment()); + if (match(COMMA)) { + while (tryConsume(COMMA)) assignments.add(parseSingleSlotAssignment()); + MultipleSlotAssignmentNode multiAssign = ASTFactory.createMultipleSlotAssignment(assignments); + return multiAssign; + } else { + SlotAssignmentNode assignment = assignments.get(0); + return assignment; + } + } + +private StmtNode parseSimpleAssignment() { + Token startToken = currentToken(); + String idName = consume(ID).text; + + // REJECT assignment to underscore + if ("_".equals(idName)) { + throw new ParseError( + "Cannot assign to '_'. Underscore is reserved for discard/placeholder.", + startToken.line, startToken.column + ); + } + + ExprNode target = ASTFactory.createIdentifier(idName); + consume(ASSIGN); + ExprNode value = expressionParser.parseExpression(); + AssignmentNode assignment = ASTFactory.createAssignment(target, value); + return assignment; +} + +private StmtNode parseVariableDeclaration() { + Token startToken = currentToken(); + String typeName = null; + boolean isImplicit = false; + String varName = null; + ExprNode value = null; + + // Case 1: Implicit Typing (ID := Expression) + if (currentToken().type == ID && lookahead(1) != null && + lookahead(1).symbol == DOUBLE_COLON_ASSIGN) { + + varName = consume(ID).text; + + // REJECT underscore + if ("_".equals(varName)) { + throw new ParseError( + "Cannot declare variable '_'. Underscore is reserved for discard/placeholder.", + startToken.line, startToken.column + ); + } + + consume(DOUBLE_COLON_ASSIGN); + isImplicit = true; + value = expressionParser.parseExpression(); + + } + // Case 2: Explicit Typing (ID : Type [ = Expression]) + else if (currentToken().type == ID && lookahead(1) != null && + lookahead(1).symbol == COLON) { + + varName = consume(ID).text; + + // REJECT underscore + if ("_".equals(varName)) { + throw new ParseError( + "Cannot declare variable '_'. Underscore is reserved for discard/placeholder.", + startToken.line, startToken.column + ); + } + + consume(COLON); + + // MUST have a type after colon + if (isTypeStart(currentToken())) { + typeName = parseTypeReference(); + + // Check for optional assignment + if (tryConsume(ASSIGN)) { + value = expressionParser.parseExpression(); + } + } else { + throw new ParseError("Expected type after ':' in variable declaration. " + + "For inferred typing use ':=' operator."); + } + + } else { + // Fallback for unrecognized pattern that wasn't correctly caught by lookahead + throw new ParseError( + "Expected variable declaration in format 'name: type', 'name: type = value', or 'name := value'."); + } + + // Validate variable name (common to both cases) + if (varName != null) { + if (NamingValidator.isAllCaps(varName)) { + NamingValidator.validateConstantName(varName, startToken); + } else { + NamingValidator.validateVariableName(varName, startToken); + } + } + + // Create and return the VarNode + VarNode varNode = ASTFactory.createVar(varName, value); + varNode.explicitType = isImplicit ? null : typeName; + return varNode; +} + + private StmtNode parseIfStatement(Boolean inheritedStyle) { + consumeKeyword(IF); + ExprNode condition = expressionParser.parseExpression(); + StmtIfNode rootIfNode = ASTFactory.createIfStatement(condition); + + Boolean currentStyle = inheritedStyle; + + if (match(LBRACE)) { + consume(LBRACE); + while (!match(RBRACE)) rootIfNode.thenBlock.statements.add(parseStatement(currentStyle)); + consume(RBRACE); + } else { + rootIfNode.thenBlock.statements.add(parseStatement(currentStyle)); + } + + StmtIfNode currentNode = rootIfNode; + + while (isKeyword(ELIF)) { + consumeKeyword(ELIF); + if (currentStyle != null && !currentStyle) + throw new ParseError("Cannot use 'elif' in an 'else if' style chain"); + currentStyle = true; + ExprNode elifCondition = expressionParser.parseExpression(); + StmtIfNode elifNode = ASTFactory.createIfStatement(elifCondition); + if (match(LBRACE)) { + consume(LBRACE); + while (!match(RBRACE)) elifNode.thenBlock.statements.add(parseStatement(currentStyle)); + consume(RBRACE); + } else { + elifNode.thenBlock.statements.add(parseStatement(currentStyle)); + } + currentNode.elseBlock.statements.add(elifNode); + currentNode = elifNode; + } + + if (isKeyword(ELSE)) { + consumeKeyword(ELSE); + if (isKeyword(IF)) { + if (currentStyle != null && currentStyle) + throw new ParseError("Cannot use 'else if' in an 'elif' style chain"); + currentStyle = false; + currentNode.elseBlock.statements.add(parseIfStatement(currentStyle)); + } else { + if (match(LBRACE)) { + consume(LBRACE); + while (!match(RBRACE)) currentNode.elseBlock.statements.add(parseStatement(currentStyle)); + consume(RBRACE); + } else { + currentNode.elseBlock.statements.add(parseStatement(currentStyle)); + } + } + } + return rootIfNode; + } + +// For loop parsing with multiplicative (*2, /2) and additive (+1, -2) step expressions + private StmtNode parseForStatement() { + consumeKeyword(FOR); + String iterator = consume(ID).text; + ExprNode step = null; + + if (isKeyword(BY)) { + consumeKeyword(BY); + + // KEEP: Multiplicative and division operators (*2, /2) + if (match(MUL) || match(DIV)) { + Token operator = consume(); + + // Handle *2, /2, *stepVar, /stepVar + if (match(INT_LIT) || match(FLOAT_LIT) || match(ID) || match(PLUS) || match(MINUS)) { + ExprNode operand = expressionParser.parseExpression(); + // Create a binary operation like i * 2 or i / 2 + ExprNode iteratorRef = ASTFactory.createIdentifier(iterator); + step = ASTFactory.createBinaryOp(iteratorRef, operator.text, operand); + } else { + throw new ParseError("Expected number or variable after " + operator.text, + currentToken().line, currentToken().column); + } + } + // KEEP: Additive operators with optional sign + else if (match(PLUS) || match(MINUS)) { + Token operator = consume(); + + if (peek(0).type == INT_LIT || peek(0).type == FLOAT_LIT) { + // Handle +1, -2, etc. + ExprNode operand = expressionParser.parsePrimaryExpression(); + if (operator.symbol == PLUS) { + step = operand; // Just the number (e.g., 1) + } else { + // Create unary negative (e.g., -2) + step = ASTFactory.createUnaryOp("-", operand); + } + } else { + // Just a simple expression (could be variable) + step = expressionParser.parseExpression(); + } + } + // Simple numeric or variable step + else { + step = expressionParser.parseExpression(); + } + + consumeKeyword(IN); + } else { + step = null; + consumeKeyword(IN); + } + + ExprNode start = expressionParser.parseExpression(); + consumeKeyword(TO); + ExprNode end = expressionParser.parseExpression(); + + RangeNode range = ASTFactory.createRange(step, start, end); + ForNode forNode = ASTFactory.createFor(iterator, range); + + consume(LBRACE); + while (!match(RBRACE)) forNode.body.statements.add(parseStatement()); + consume(RBRACE); + return forNode; + } + + private StmtNode parseOutputStatement() { + consumeKeyword(OUTPUT); + OutputNode output = ASTFactory.createOutput(); + output.arguments.add(expressionParser.parseExpression()); + return output; + } + + private StmtNode parseInputAssignment() { + Token startToken = currentToken(); + String varName = consume(ID).text; + consume(ASSIGN); + String type; + if (match(LPAREN)) { + consume(LPAREN); + type = parseTypeReference(); + consume(RPAREN); + } else { + throw new ParseError( + "Input assignment requires an explicit type grouping, e.g., var = (int)input", + startToken.line, + startToken.column); + } + consumeKeyword(INPUT); + InputNode input = ASTFactory.createInput(type, varName); + return input; + } + + private StmtNode parseExitStatement() { + consumeKeyword(EXIT); + ExitNode exit = ASTFactory.createExit(); + return exit; + } + + private StmtNode parseReturnSlotAssignment() { + List varNames = parseIdList(); + + // Accept both = and := for return slot assignment + if (match(DOUBLE_COLON_ASSIGN)) { + consume(DOUBLE_COLON_ASSIGN); + } else { + consume(ASSIGN); + } + + List slotNames = expressionParser.parseReturnSlots(); + consume(COLON); + MethodCallNode methodCall = expressionParser.parseMethodCall(); + methodCall.slotNames = slotNames; + + // Validate variable names (allow underscore for discard) + for (String varName : varNames) { + if ("_".equals(varName)) { + continue; // Underscore is allowed here - it means discard this slot + } + // Validate other names normally + } + + if (varNames.size() != slotNames.size()) { + throw new ParseError( + "Number of variables (" + varNames.size() + ") does not match number of slots (" + slotNames.size() + ")"); + } + + ReturnSlotAssignmentNode assignment = ASTFactory.createReturnSlotAssignment(varNames, methodCall); + return assignment; +} + + private StmtNode parseIndexAssignment() { + ExprNode arrayVar = ASTFactory.createIdentifier(consume(ID).text); + + IndexAccessNode indexAccess = expressionParser.parseIndexAccessContinuation(arrayVar); + while (match(LBRACKET)) { + indexAccess = expressionParser.parseIndexAccessContinuation(indexAccess); + } + consume(ASSIGN); + ExprNode value = expressionParser.parseExpression(); + AssignmentNode assignment = ASTFactory.createAssignment(indexAccess, value); + return assignment; + } + + private StmtNode parseMethodCallStatement() { + if (isSymbolAt(0, LBRACKET)) { + List slotNames = expressionParser.parseReturnSlots(); + consume(COLON); + MethodCallNode methodCall = expressionParser.parseMethodCall(); + methodCall.slotNames = slotNames; + return methodCall; + } else { + MethodCallNode methodCall = expressionParser.parseMethodCall(); + return methodCall; + } + } + + private StmtNode parseExpressionStatement() { + ExprNode expr = expressionParser.parseExpression(); + return expr; + } + + private List parseIdList() { + List ids = new ArrayList(); + ids.add(consume(ID).text); + while (tryConsume(COMMA)) ids.add(consume(ID).text); + return ids; + } + + // --- Lookahead Methods (OPTIMIZED) --- + + protected boolean isSlotAssignment() { + Token first = lookahead(0); + if (first == null || first.symbol != TILDE_ARROW) return false; + + Token next = lookahead(1); + if (next == null) return false; + + // Check if the next token is a possible start of an expression/literal + return isExpressionStart(next); + } + + // SIMPLIFIED: Uses simpler token-based lookahead instead of complex loop and exception catching + private boolean isIndexAssignment() { + Token first = lookahead(0); + Token second = lookahead(1); + + if (first == null || first.type == ID) { + // Continue to next + } else { + return false; + } + + if (second == null || second.symbol != LBRACKET) return false; + + // REMOVED: Case 1 (Empty brackets) check. + // Now only checks if it looks like an index access (name[...) + + // Case 2: Index access: name[expr]... = value + int pos = getPosition() + 2; // Start after ID [ + int bracketDepth = 1; + + // Look for the matching closing bracket for the first index + while (pos < tokens.size() && bracketDepth > 0) { + Token t = tokens.get(pos); + if (t.symbol == LBRACKET) bracketDepth++; + else if (t.symbol == RBRACKET) bracketDepth--; + pos++; + } + + // If we found a matching bracket + if (bracketDepth == 0) { + // Check for subsequent index accesses or assignment + while (pos < tokens.size() && tokens.get(pos).symbol == LBRACKET) { + pos++; + bracketDepth = 1; + while (pos < tokens.size() && bracketDepth > 0) { + Token t = tokens.get(pos); + if (t.symbol == LBRACKET) bracketDepth++; + else if (t.symbol == RBRACKET) bracketDepth--; + pos++; + } + if (bracketDepth != 0) return false; + } + return pos < tokens.size() && tokens.get(pos).symbol == ASSIGN; + } + + return false; + } + +private boolean isReturnSlotAssignment() { + int p = getPosition(); + + // 1. Variable list (ID, ID...) + Token t = tokens.get(p++); + if (t.type != ID) return false; + + while (p < tokens.size() && tokens.get(p).symbol == COMMA) { + p++; // Skip comma + if (p >= tokens.size() || tokens.get(p).type != ID) return false; + p++; // Skip ID + } + + // 2. Assignment ( = OR := ) + if (p >= tokens.size()) return false; + + // --- FIX START: Allow both ASSIGN and DOUBLE_COLON_ASSIGN --- + if (tokens.get(p).symbol != ASSIGN && tokens.get(p).symbol != DOUBLE_COLON_ASSIGN) return false; + // --- FIX END --- + + p++; + + // 3. Slot list ([...]) + if (p >= tokens.size() || tokens.get(p).symbol != LBRACKET) return false; + p++; + + // Check for at least one slot + if (p >= tokens.size()) return false; + Token firstSlot = tokens.get(p); + if (firstSlot.type == ID || firstSlot.type == INT_LIT) { + p++; + } else if (firstSlot.symbol != RBRACKET) { + return false; + } + + while (p < tokens.size() && tokens.get(p).symbol == COMMA) { + p++; + if (p >= tokens.size()) return false; + Token nextSlot = tokens.get(p); + if (nextSlot.type == ID || nextSlot.type == INT_LIT) { + p++; + } else { + return false; + } + } + + if (p >= tokens.size() || tokens.get(p).symbol != RBRACKET) return false; + p++; + + // 4. Colon (:) + return p < tokens.size() && tokens.get(p).symbol == COLON; +} + + // Original logic was sufficient, minor cleanup + private boolean isInputAssignment() { + int p = getPosition(); + + if (tokens.get(p++).type != ID) return false; + if (p >= tokens.size() || tokens.get(p).symbol != ASSIGN) return false; + p++; + if (p >= tokens.size() || tokens.get(p).symbol != LPAREN) return false; + p++; + + int parenBalance = 1; + while (p < tokens.size() && parenBalance > 0) { + Token t = tokens.get(p); + if (t.symbol == LPAREN) parenBalance++; + else if (t.symbol == RPAREN) parenBalance--; + p++; + } + + if (parenBalance != 0) return false; // Type grouping must close + + return p < tokens.size() && isKeywordAt(p - getPosition(), INPUT); + } + + // Original logic was sufficient, minor cleanup + private boolean isMethodCallStatement() { + int p = getPosition(); + + if (isSymbolAt(0, LBRACKET)) { + // Check for [slots] : call() + p++; + if (p >= tokens.size() || (tokens.get(p).type != ID && tokens.get(p).type != INT_LIT)) + return false; + p++; + while (p < tokens.size() && tokens.get(p).symbol == COMMA) { + p++; + if (p >= tokens.size() || (tokens.get(p).type != ID && tokens.get(p).type != INT_LIT)) + return false; + p++; + } + if (p >= tokens.size() || tokens.get(p).symbol != RBRACKET) return false; + p++; + if (p >= tokens.size() || tokens.get(p).symbol != COLON) return false; + p++; + } + + // Check for qualified name/ID + if (p >= tokens.size() || tokens.get(p).type != ID) return false; + p++; + while (p < tokens.size() && tokens.get(p).symbol == DOT) { + p++; + if (p >= tokens.size() || tokens.get(p).type != ID) return false; + p++; + } + + // Must be followed by ( + return p < tokens.size() && tokens.get(p).symbol == LPAREN; + } + +private boolean isVariableDeclaration() { + Token first = lookahead(0); + Token second = lookahead(1); + + if (first == null) return false; + + // Case 1: name := value (implicit typing) + if (first.type == ID && second != null && second.symbol == DOUBLE_COLON_ASSIGN) { + return true; + } + + // Case 2: name: type or name: type = value + if (first.type == ID && second != null && second.symbol == COLON) { + int pos = getPosition() + 2; // Skip ID and COLON + if (pos < tokens.size()) { + Token third = tokens.get(pos); + return isTypeStart(third); + } + } + + return false; +} +} \ No newline at end of file diff --git a/src/main/java/cod/runner/BaseRunner.java b/src/main/java/cod/runner/BaseRunner.java index 498886d0..0d6766eb 100644 --- a/src/main/java/cod/runner/BaseRunner.java +++ b/src/main/java/cod/runner/BaseRunner.java @@ -1,40 +1,17 @@ +// BaseRunner.java package cod.runner; -import cod.ast.*; import cod.ast.nodes.*; import cod.debug.DebugSystem; -import cod.ast.ASTPrinter; import java.io.*; import java.util.List; -// ANTLR-specific imports -import org.antlr.v4.runtime.ANTLRInputStream; -import org.antlr.v4.runtime.CommonTokenStream; -import org.antlr.v4.runtime.RecognitionException; -import org.antlr.v4.runtime.BaseErrorListener; -import org.antlr.v4.runtime.Recognizer; - -// Manual-specific imports -import cod.ast.ManualCoderiveLexer; -import cod.ast.ManualCoderiveParser; - -// Linter import -import cod.debug.Linter; +import cod.lexer.MainLexer; +import cod.parser.MainParser; public abstract class BaseRunner { - - // Enum for parser mode - public enum ParserMode { - ANTLR, - MANUAL - } - // Configuration class for runner options - now with builder pattern public static class RunnerConfig { - public ParserMode parserMode = ParserMode.MANUAL; - public boolean printAST = false; - public boolean enableLinting = true; - public boolean stopOnLintError = false; public String inputFilename; public String outputFilename; public DebugSystem.Level debugLevel = DebugSystem.Level.INFO; @@ -43,27 +20,6 @@ public RunnerConfig(String inputFilename) { this.inputFilename = inputFilename; } - // Builder-style methods for fluent configuration - public RunnerConfig withParserMode(ParserMode mode) { - this.parserMode = mode; - return this; - } - - public RunnerConfig withPrintAST(boolean printAST) { - this.printAST = printAST; - return this; - } - - public RunnerConfig withLinting(boolean enableLinting) { - this.enableLinting = enableLinting; - return this; - } - - public RunnerConfig withStopOnLintError(boolean stopOnLintError) { - this.stopOnLintError = stopOnLintError; - return this; - } - public RunnerConfig withOutputFilename(String outputFilename) { this.outputFilename = outputFilename; return this; @@ -75,218 +31,315 @@ public RunnerConfig withDebugLevel(DebugSystem.Level debugLevel) { } } - // Configuration interface for anonymous inner class style public interface Configuration { void configure(RunnerConfig config); } protected static final String LOG_TAG = "RUNNER"; - /** - * Common method to parse a source file using either ANTLR or manual parser - */ - protected ProgramNode parseSourceFile(String filename, ParserMode mode) throws Exception { - switch (mode) { - case ANTLR: - return parseWithAntlr(filename); - case MANUAL: - return parseWithManual(filename); - default: - throw new IllegalArgumentException("Unknown parser mode: " + mode); - } - } - - /** - * Parse using ANTLR parser - */ - private ProgramNode parseWithAntlr(String filename) throws Exception { - DebugSystem.debug(LOG_TAG, "Loading source file for ANTLR: " + filename); - InputStream is = new FileInputStream(filename); - - ANTLRInputStream input = new ANTLRInputStream(is); - CoderiveLexer lexer = new CoderiveLexer(input); - CommonTokenStream tokens = new CommonTokenStream(lexer); - CoderiveParser parser = new CoderiveParser(tokens); - - parser.removeErrorListeners(); - parser.addErrorListener(new BaseErrorListener() { - @Override - public void syntaxError(Recognizer recognizer, Object offendingSymbol, - int line, int charPositionInLine, String msg, - RecognitionException e) { - String error = "Syntax error at line " + line + ":" + charPositionInLine + " - " + msg; - DebugSystem.error("PARSER", error); - throw new RuntimeException(error); - } - }); - - DebugSystem.debug("PARSER", "Building ANTLR parse tree..."); - CoderiveParser.ProgramContext programContext = parser.program(); - DebugSystem.debug("PARSER", "ANTLR parse tree built successfully"); - - ASTBuilder builder = new ASTBuilder(); - ProgramNode ast = builder.build(programContext); - - return ast; - } - - /** - * Parse using manual parser - */ - private ProgramNode parseWithManual(String filename) throws Exception { - DebugSystem.debug(LOG_TAG, "Loading source file for Manual Parser: " + filename); + public ProgramNode parse(String filename) throws Exception { + DebugSystem.debug(LOG_TAG, "Loading source file: " + filename); InputStream is = new FileInputStream(filename); String sourceCode = readFileToString(is); + DebugSystem.debug(LOG_TAG, "Source length: " + sourceCode.length() + " chars"); - DebugSystem.debug("PARSER", "Tokenizing with ManualLexer..."); - ManualCoderiveLexer lexer = new ManualCoderiveLexer(sourceCode); - List tokens = lexer.tokenize(); + DebugSystem.debug("PARSER", "Tokenizing..."); + MainLexer lexer = new MainLexer(sourceCode); + List tokens = lexer.tokenize(); + DebugSystem.debug("PARSER", "Generated " + tokens.size() + " tokens"); - DebugSystem.debug("PARSER", "Parsing with ManualParser..."); - ManualCoderiveParser parser = new ManualCoderiveParser(tokens); + DebugSystem.debug("PARSER", "Parsing..."); + MainParser parser = new MainParser(tokens); ProgramNode ast = parser.parseProgram(); - DebugSystem.debug("PARSER", "Manual parsing completed successfully"); + DebugSystem.debug("PARSER", "Parsing completed successfully"); return ast; } - /** - * Common file reading utility - */ protected String readFileToString(InputStream is) throws Exception { java.util.Scanner scanner = new java.util.Scanner(is).useDelimiter("\\A"); return scanner.hasNext() ? scanner.next() : ""; } - /** - * Common debug system configuration - */ - protected void configureDebugSystem(DebugSystem.Level level) { - DebugSystem.setLevel(level); - DebugSystem.enableCategory("BYTECODE"); - DebugSystem.enableCategory("MTOT"); - DebugSystem.enableCategory(LOG_TAG); - DebugSystem.enableCategory("PARSER"); - DebugSystem.enableCategory("INTERPRETER"); - DebugSystem.enableCategory("AST"); - DebugSystem.setShowTimestamp(true); - } - -/** - * Print AST if enabled in config with proper output synchronization - */ -protected void printASTIfEnabled(ProgramNode ast, RunnerConfig config) { - if (config.printAST) { - // Ensure any pending stderr output is flushed before printing AST to stdout - System.err.flush(); - - DebugSystem.info("AST", "Printing Abstract Syntax Tree:"); - ASTPrinter.print(ast); - - // Flush stdout after AST printing - System.out.flush(); - - DebugSystem.debug("AST", "AST printing completed"); - } + protected void configureDebugSystem(DebugSystem.Level level) { + DebugSystem.setLevel(level); + DebugSystem.enableCategory("BYTECODE"); + DebugSystem.enableCategory("MTOT"); + DebugSystem.enableCategory(LOG_TAG); + DebugSystem.enableCategory("PARSER"); + DebugSystem.enableCategory("INTERPRETER"); + DebugSystem.enableCategory("AST"); + DebugSystem.enableCategory("PERF"); + DebugSystem.setShowTimestamp(true); + DebugSystem.info(LOG_TAG, "DebugSystem configured to level: " + level); } -/** - * Perform linting if enabled in config - */ -protected boolean performLinting(ProgramNode ast, RunnerConfig config) { - if (!config.enableLinting) { - DebugSystem.debug("LINTER", "Linting disabled by configuration"); - return true; - } - - DebugSystem.startTimer("linting"); - Linter linter = new Linter(); - List warnings = linter.lint(ast); - DebugSystem.stopTimer("linting"); - - // Print warnings immediately and completely - Linter.WarningUtils.printWarnings(warnings); - - // Force complete output synchronization - System.out.flush(); - System.err.flush(); - - if (!warnings.isEmpty() && config.stopOnLintError) { - DebugSystem.error(LOG_TAG, "Execution stopped due to lint errors."); - return false; - } - - return true; -} - - /** - * Common method to extract filename from arguments - */ protected String extractFilenameFromArgs(String[] args, String defaultFilename) { for (String arg : args) { if (!arg.startsWith("--") && !arg.equals("-o")) { + DebugSystem.debug(LOG_TAG, "Extracted filename from args: " + arg); return arg; } } + DebugSystem.debug(LOG_TAG, "Using default filename: " + defaultFilename); return defaultFilename; } - /** - * Common method to process command line arguments and create configuration - * Now supports Configuration callback - */ protected RunnerConfig processCommandLineArgs(String[] args, String defaultInputFilename, Configuration configCallback) { + DebugSystem.debug(LOG_TAG, "Processing command line args, count: " + args.length); RunnerConfig config = new RunnerConfig(defaultInputFilename); - // Apply command line arguments first for (int i = 0; i < args.length; i++) { String arg = args[i]; - if ("--antlr".equals(arg)) { - config.parserMode = ParserMode.ANTLR; - } else if ("--manual".equals(arg)) { - config.parserMode = ParserMode.MANUAL; - } else if ("--print-ast".equals(arg)) { - config.printAST = true; - } else if ("--no-lint".equals(arg)) { - config.enableLinting = false; - } else if ("--stop-on-lint".equals(arg)) { - config.stopOnLintError = true; - } else if ("--debug".equals(arg)) { + if ("--debug".equals(arg)) { config.debugLevel = DebugSystem.Level.DEBUG; + DebugSystem.debug(LOG_TAG, "Set debug level to DEBUG"); } else if ("--trace".equals(arg)) { config.debugLevel = DebugSystem.Level.TRACE; + DebugSystem.trace(LOG_TAG, "Set debug level to TRACE"); } else if ("-o".equals(arg)) { if (i + 1 < args.length) { config.outputFilename = args[i + 1]; i++; + DebugSystem.debug(LOG_TAG, "Set output filename: " + config.outputFilename); } else { System.err.println("Error: -o option requires an output filename."); + DebugSystem.error(LOG_TAG, "-o option missing filename"); } } } - // Extract filename if not already set by -o processing if (config.inputFilename == null) { config.inputFilename = extractFilenameFromArgs(args, defaultInputFilename); } - // Apply configuration callback if provided if (configCallback != null) { configCallback.configure(config); } + DebugSystem.debug(LOG_TAG, "Config: input=" + config.inputFilename + + ", output=" + config.outputFilename + ", level=" + config.debugLevel); + return config; } - // Overloaded version for backward compatibility protected RunnerConfig processCommandLineArgs(String[] args, String defaultInputFilename) { return processCommandLineArgs(args, defaultInputFilename, null); } - /** - * Abstract method to be implemented by specific runners - */ + // ==================== CONSTANT FOLDING INTEGRATION ==================== + + protected T optimizeAST(T ast, boolean enableOptimization) { + if (!enableOptimization) { + DebugSystem.debug(LOG_TAG, "Skipping constant folding (disabled)"); + return ast; + } + + DebugSystem.debug(LOG_TAG, "Starting constant folding optimization..."); + DebugSystem.startTimer("constant_folding"); + + try { + Class optimizerClass = Class.forName("cod.ast.Optimizer"); + java.lang.reflect.Method foldMethod = optimizerClass.getMethod("foldConstants", ASTNode.class); + + @SuppressWarnings("unchecked") + T optimized = (T) foldMethod.invoke(null, ast); + + DebugSystem.stopTimer("constant_folding"); + long duration = DebugSystem.getTimerDuration("constant_folding"); + + DebugSystem.info(LOG_TAG, "Constant folding completed in " + duration + " ms"); + + if (DebugSystem.getLevel().compareTo(DebugSystem.Level.DEBUG) >= 0) { + printOptimizationDebugInfo(ast, optimized); + } + + return optimized; + + } catch (ClassNotFoundException e) { + DebugSystem.warn(LOG_TAG, "Optimizer class not found. Constant folding disabled."); + DebugSystem.warn(LOG_TAG, "Make sure Optimizer.java is in the classpath."); + return ast; + } catch (NoSuchMethodException e) { + DebugSystem.warn(LOG_TAG, "Optimizer.foldConstants method not found."); + return ast; + } catch (Exception e) { + DebugSystem.error(LOG_TAG, "Constant folding failed: " + e.getMessage()); + if (DebugSystem.getLevel().compareTo(DebugSystem.Level.DEBUG) >= 0) { + e.printStackTrace(); + } + return ast; + } + } + + protected T optimizeAST(T ast, boolean constantFolding, boolean otherOptimizations) { + if (!constantFolding && !otherOptimizations) { + return ast; + } + + DebugSystem.debug(LOG_TAG, "Starting optimizations..."); + DebugSystem.startTimer("optimizations"); + + T result = ast; + + if (constantFolding) { + result = optimizeAST(result, true); + } + + if (otherOptimizations) { + DebugSystem.debug(LOG_TAG, "Other optimizations requested but not implemented yet"); + } + + DebugSystem.stopTimer("optimizations"); + DebugSystem.info(LOG_TAG, "All optimizations completed in " + + DebugSystem.getTimerDuration("optimizations") + " ms"); + + return result; + } + + private void printOptimizationDebugInfo(ASTNode original, ASTNode optimized) { + if (original == optimized) { + DebugSystem.debug(LOG_TAG, "No changes made by constant folding"); + return; + } + + DebugSystem.debug(LOG_TAG, "AST modified by constant folding"); + + int originalEstimate = estimateNodeCount(original); + int optimizedEstimate = estimateNodeCount(optimized); + + if (optimizedEstimate < originalEstimate) { + int reduction = ((originalEstimate - optimizedEstimate) * 100) / originalEstimate; + DebugSystem.debug(LOG_TAG, "Estimated size reduction: " + reduction + "%"); + DebugSystem.debug(LOG_TAG, "Original: ~" + originalEstimate + " nodes"); + DebugSystem.debug(LOG_TAG, "Optimized: ~" + optimizedEstimate + " nodes"); + } + + try { + Class optimizerClass = Class.forName("cod.ast.Optimizer"); + java.lang.reflect.Method getStatsMethod = optimizerClass.getMethod("getStats"); + Object stats = getStatsMethod.invoke(null); + + if (stats != null) { + Class statsClass = stats.getClass(); + java.lang.reflect.Method getTotalMethod = statsClass.getMethod("getTotalOptimizations"); + Integer totalOpts = (Integer) getTotalMethod.invoke(stats); + + if (totalOpts != null && totalOpts > 0) { + DebugSystem.debug(LOG_TAG, "Total optimizations applied: " + totalOpts); + } + } + } catch (Exception e) { + // Ignore + } + } + + private int estimateNodeCount(ASTNode node) { + if (node == null) return 0; + + int count = 1; + + if (node instanceof ProgramNode) { + ProgramNode prog = (ProgramNode) node; + if (prog.unit != null) count += estimateNodeCount(prog.unit); + } else if (node instanceof UnitNode) { + UnitNode unit = (UnitNode) node; + if (unit.imports != null) count += estimateNodeCount(unit.imports); + for (TypeNode type : unit.types) { + count += estimateNodeCount(type); + } + } else if (node instanceof TypeNode) { + TypeNode type = (TypeNode) node; + for (FieldNode field : type.fields) { + count += estimateNodeCount(field); + } + for (MethodNode method : type.methods) { + count += estimateNodeCount(method); + } + for (StmtNode stmt : type.statements) { + count += estimateNodeCount(stmt); + } + } else if (node instanceof MethodNode) { + MethodNode method = (MethodNode) node; + for (ParamNode param : method.parameters) { + count += estimateNodeCount(param); + } + for (StmtNode stmt : method.body) { + count += estimateNodeCount(stmt); + } + } else if (node instanceof BlockNode) { + BlockNode block = (BlockNode) node; + for (StmtNode stmt : block.statements) { + count += estimateNodeCount(stmt); + } + } else if (node instanceof StmtIfNode) { + StmtIfNode ifNode = (StmtIfNode) node; + if (ifNode.condition != null) count += estimateNodeCount(ifNode.condition); + count += estimateNodeCount(ifNode.thenBlock); + count += estimateNodeCount(ifNode.elseBlock); + } else if (node instanceof BinaryOpNode) { + BinaryOpNode binOp = (BinaryOpNode) node; + if (binOp.left != null) count += estimateNodeCount(binOp.left); + if (binOp.right != null) count += estimateNodeCount(binOp.right); + } else if (node instanceof UnaryNode) { + UnaryNode unary = (UnaryNode) node; + if (unary.operand != null) count += estimateNodeCount(unary.operand); + } else if (node instanceof MethodCallNode) { + MethodCallNode call = (MethodCallNode) node; + for (ExprNode arg : call.arguments) { + count += estimateNodeCount(arg); + } + if (call.chainArguments != null) { + for (ExprNode arg : call.chainArguments) { + count += estimateNodeCount(arg); + } + } + } else if (node instanceof EqualityChainNode) { + EqualityChainNode chain = (EqualityChainNode) node; + if (chain.left != null) count += estimateNodeCount(chain.left); + for (ExprNode arg : chain.chainArguments) { + count += estimateNodeCount(arg); + } + } else if (node instanceof BooleanChainNode) { + BooleanChainNode boolChain = (BooleanChainNode) node; + for (ExprNode expr : boolChain.expressions) { + count += estimateNodeCount(expr); + } + } + + return count; + } + + protected boolean isConstantFoldingAvailable() { + try { + Class.forName("cod.ast.Optimizer"); + DebugSystem.debug(LOG_TAG, "Constant folding available"); + return true; + } catch (ClassNotFoundException e) { + DebugSystem.debug(LOG_TAG, "Constant folding NOT available"); + return false; + } + } + + protected String getOptimizationInfo() { + StringBuilder info = new StringBuilder(); + + if (isConstantFoldingAvailable()) { + info.append("Available optimizations:\n"); + info.append(" • Constant folding (-O, --optimize)\n"); + info.append(" - Evaluates constant expressions at compile time\n"); + info.append(" - Optimizes boolean chains (any[]/all[])\n"); + info.append(" - Eliminates unnecessary type casts\n"); + info.append(" - Pre-computes string concatenation\n"); + } else { + info.append("Optimizations: NOT AVAILABLE\n"); + info.append("Make sure Optimizer.java and ConstantFolder.java are compiled.\n"); + } + + return info.toString(); + } + public abstract void run(String[] args) throws Exception; } \ No newline at end of file diff --git a/src/main/java/cod/runner/CoderiveREPL.java b/src/main/java/cod/runner/CoderiveREPL.java index d4ba85f6..074cfbbc 100644 --- a/src/main/java/cod/runner/CoderiveREPL.java +++ b/src/main/java/cod/runner/CoderiveREPL.java @@ -1,40 +1,41 @@ +// CoderiveREPL.java package cod.runner; import cod.ast.ASTFactory; -import cod.ast.ManualCoderiveLexer; -import cod.ast.ManualCoderiveParser; +import cod.semantic.NamingValidator; import cod.ast.nodes.*; import cod.debug.DebugSystem; + import cod.interpreter.Interpreter; import cod.interpreter.ObjectInstance; +import cod.lexer.MainLexer; +import cod.parser.MainParser; + import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Scanner; -/** - * A Read-Eval-Print-Loop (REPL) for the Coderive language. - * - * This class provides an interactive command-line shell that uses the - * ManualCoderiveLexer, ManualCoderiveParser, and the Interpreter - * to execute Coderive code one line at a time. - */ +import cod.syntax.Keyword; + public class CoderiveREPL { public static void main(String[] args) { System.out.println("Welcome to the Coderive REPL. Type 'exit' to quit."); - System.out.println("Special commands: ';reset' to clear state, ';help' for help\n"); + System.out.println("Special commands: ';reset' to clear state, ';help' for help"); + System.out.println("Optimization: Type ';opt on' to enable constant folding, ';opt off' to disable\n"); + + DebugSystem.setLevel(DebugSystem.Level.ERROR); + DebugSystem.info("REPL", "Starting Coderive REPL"); - // 1. Set up persistent state Interpreter interpreter = new Interpreter(); - ObjectInstance globalInstance = new ObjectInstance(ASTFactory.createType("REPLGlobal", "local", null)); - Map globalLocals = new HashMap<>(); - Map globalSlots = new HashMap<>(); + ObjectInstance globalInstance = new ObjectInstance(ASTFactory.createType("REPLGlobal", Keyword.LOCAL, null)); + Map globalLocals = new HashMap(); + Map globalSlots = new HashMap(); - DebugSystem.setLevel(DebugSystem.Level.ERROR); + boolean optimize = false; - // 2. Start the Read-Eval-Print-Loop Scanner scanner = new Scanner(System.in); while (true) { System.out.print(">> "); @@ -45,13 +46,14 @@ public static void main(String[] args) { continue; } - // Handle special REPL commands if (line.equalsIgnoreCase(";exit") || line.equalsIgnoreCase(";quit")) { + DebugSystem.info("REPL", "Exiting REPL"); break; } if (line.equalsIgnoreCase(";reset")) { globalLocals.clear(); globalSlots.clear(); + DebugSystem.info("REPL", "State reset"); System.out.println("State reset."); continue; } @@ -59,72 +61,138 @@ public static void main(String[] args) { printHelp(); continue; } + + if (line.equalsIgnoreCase(";opt on")) { + optimize = true; + DebugSystem.info("REPL", "Constant folding ENABLED"); + System.out.println("Constant folding optimization ENABLED"); + continue; + } + if (line.equalsIgnoreCase(";opt off")) { + optimize = false; + DebugSystem.info("REPL", "Constant folding DISABLED"); + System.out.println("Constant folding optimization DISABLED"); + continue; + } + if (line.equalsIgnoreCase(";opt status")) { + System.out.println("Constant folding: " + (optimize ? "ENABLED" : "DISABLED")); + continue; + } try { - // 3. Check for multi-line input String fullInput = line; if (needsMoreInput(line)) { + DebugSystem.debug("REPL", "Detected multi-line input"); fullInput = readMultiLineInput(scanner, line); if (fullInput == null) { - continue; // User cancelled multi-line input + continue; } } - // 4. READ: Lex the input - ManualCoderiveLexer lexer = new ManualCoderiveLexer(fullInput); - List tokens = lexer.tokenize(); + MainLexer lexer = new MainLexer(fullInput); + List tokens = lexer.tokenize(); + DebugSystem.debug("REPL", "Tokenized: " + tokens.size() + " tokens"); - // 5. EVAL (Part 1: Parse) - ManualCoderiveParser parser = new ManualCoderiveParser(tokens); - StatementNode astNode = parser.parseSingleLine(); + MainParser parser = new MainParser(tokens); + StmtNode astNode = parser.parseSingleLine(); if (astNode == null) { + DebugSystem.warn("REPL", "No AST generated for input"); continue; } + + DebugSystem.debug("REPL", "Parsed AST node: " + astNode.getClass().getSimpleName()); + + if (optimize) { + DebugSystem.debug("REPL", "Applying constant folding"); + try { + Class optimizerClass = Class.forName("cod.ast.Optimizer"); + java.lang.reflect.Method foldMethod = optimizerClass.getMethod("foldConstants", ASTNode.class); + astNode = (StmtNode) foldMethod.invoke(null, astNode); + DebugSystem.debug("REPL", "Constant folding applied"); + } catch (Exception e) { + DebugSystem.warn("REPL", "Constant folding failed: " + e.getMessage()); + } + } + + validateREPLNaming(astNode, globalLocals); - // 6. EVAL (Part 2: Execute) - Object result = interpreter.getStatementEvaluator().evalStmt( + DebugSystem.debug("REPL", "Evaluating statement"); + Object result = interpreter.evalReplStatement( astNode, globalInstance, globalLocals, globalSlots ); - // 7. PRINT if (astNode instanceof ExprNode && result != null) { System.out.println(String.valueOf(result)); + DebugSystem.debug("REPL", "Result: " + result); } } catch (Exception e) { System.err.println("Error: " + e.getMessage()); + DebugSystem.error("REPL", "Error: " + e.getMessage()); } } - scanner.close(); System.out.println("Exiting REPL."); } + + private static void validateREPLNaming(StmtNode stmt, Map locals) { + DebugSystem.debug("REPL", "Validating naming for: " + stmt.getClass().getSimpleName()); + + if (stmt instanceof VarNode) { + VarNode var = (VarNode) stmt; + String varName = var.name; + + if (NamingValidator.isPascalCase(varName)) { + DebugSystem.error("REPL", "Invalid PascalCase variable: " + varName); + throw new RuntimeException("Variable name '" + varName + "' cannot use PascalCase (reserved for classes)"); + } + + if (NamingValidator.isAllCaps(varName) && var.value == null) { + DebugSystem.error("REPL", "Constant without value: " + varName); + throw new RuntimeException("Constant '" + varName + "' must have an initial value"); + } + + } else if (stmt instanceof AssignmentNode) { + AssignmentNode assign = (AssignmentNode) stmt; + if (assign.left instanceof ExprNode) { + ExprNode target = (ExprNode) assign.left; + if (target.name != null) { + String varName = target.name; + if (NamingValidator.isPascalCase(varName)) { + DebugSystem.error("REPL", "Invalid PascalCase assignment: " + varName); + throw new RuntimeException("Variable name '" + varName + "' cannot use PascalCase (reserved for classes)"); + } + + if (NamingValidator.isAllCaps(varName) && !locals.containsKey(varName)) { + DebugSystem.error("REPL", "Assignment to undeclared constant: " + varName); + throw new RuntimeException("Cannot assign to undeclared constant '" + varName + "'"); + } + } + } + } + + DebugSystem.debug("REPL", "Naming validation passed"); + } - /** - * Checks if the input line needs continuation (unclosed braces, etc.) - */ private static boolean needsMoreInput(String line) { - // Simple check: if we have unmatched braces, we need more input int openBraces = countChar(line, '{'); int closeBraces = countChar(line, '}'); return openBraces > closeBraces; } - /** - * Reads multi-line input until all braces are balanced - */ private static String readMultiLineInput(Scanner scanner, String firstLine) { + DebugSystem.debug("REPL", "Starting multi-line input collection"); StringBuilder input = new StringBuilder(firstLine); int braceBalance = countChar(firstLine, '{') - countChar(firstLine, '}'); System.out.print(" . . "); while (braceBalance > 0) { String line = scanner.nextLine().trim(); - input.append(" ").append(line); // Add space between lines + input.append(" ").append(line); braceBalance += countChar(line, '{') - countChar(line, '}'); @@ -134,12 +202,10 @@ private static String readMultiLineInput(Scanner scanner, String firstLine) { System.out.print(" . . "); } + DebugSystem.debug("REPL", "Multi-line input complete, length: " + input.length()); return input.toString(); } - /** - * Counts occurrences of a character in a string - */ private static int countChar(String str, char ch) { int count = 0; for (int i = 0; i < str.length(); i++) { @@ -150,9 +216,6 @@ private static int countChar(String str, char ch) { return count; } - /** - * Prints REPL help information - */ private static void printHelp() { System.out.println("Coderive REPL Help:"); System.out.println(" Expressions: 5 + 3, x * 2, \"hello\" + \" world\""); diff --git a/src/main/java/cod/runner/CommandRunner.java b/src/main/java/cod/runner/CommandRunner.java index 44308c1f..8190e3a4 100644 --- a/src/main/java/cod/runner/CommandRunner.java +++ b/src/main/java/cod/runner/CommandRunner.java @@ -7,52 +7,32 @@ public class CommandRunner extends BaseRunner { - // Operation modes - private enum OperationMode { - INTERPRET, - COMPILE_BYTECODE, - COMPILE_NATIVE, - COMPILE_BOTH - } - private final Interpreter interpreter; - private final CompilationEngine compilationEngine; + private boolean enableOptimization = false; + private boolean showOptimizationStats = false; public CommandRunner() { this.interpreter = new Interpreter(); - this.compilationEngine = new CompilationEngine(); } @Override public void run(String[] args) throws Exception { - // Default operation mode - OperationMode mode = OperationMode.INTERPRET; String outputFilename = null; - boolean showHelp = false; + enableOptimization = false; + showOptimizationStats = false; - // Process command line arguments with anonymous configuration RunnerConfig config = processCommandLineArgs(args, null, new Configuration() { @Override public void configure(RunnerConfig config) { - // Common defaults - config.withParserMode(ParserMode.MANUAL) - .withLinting(true) - .withPrintAST(false) - .withDebugLevel(DebugSystem.Level.INFO); + config.withDebugLevel(DebugSystem.Level.INFO); } }); - // Process operation mode arguments and output filename + // Parse command-line arguments for (int i = 0; i < args.length; i++) { String arg = args[i]; - if ("--compile".equals(arg) || "-c".equals(arg)) { - mode = OperationMode.COMPILE_NATIVE; - } else if ("--compile-bytecode".equals(arg)) { - mode = OperationMode.COMPILE_BYTECODE; - } else if ("--compile-both".equals(arg)) { - mode = OperationMode.COMPILE_BOTH; - } else if ("--interpret".equals(arg) || "-i".equals(arg)) { - mode = OperationMode.INTERPRET; + if ("--interpret".equals(arg) || "-i".equals(arg)) { + // Default mode, do nothing } else if ("-o".equals(arg)) { if (i + 1 < args.length) { outputFilename = args[i + 1]; @@ -60,115 +40,98 @@ public void configure(RunnerConfig config) { } else { System.err.println("Error: -o option requires an output filename."); } + } else if ("-O".equals(arg) || "--optimize".equals(arg)) { + enableOptimization = true; + DebugSystem.info(LOG_TAG, "Constant folding optimization enabled"); + } else if ("--opt-stats".equals(arg)) { + showOptimizationStats = true; + } else if ("--debug".equals(arg)) { + config.debugLevel = DebugSystem.Level.DEBUG; + } else if ("--trace".equals(arg)) { + config.debugLevel = DebugSystem.Level.TRACE; + } else if ("--quiet".equals(arg)) { + config.debugLevel = DebugSystem.Level.ERROR; } else if ("--help".equals(arg) || "-h".equals(arg)) { - showHelp = true; + printHelp(); + return; } } - // Show help and exit if requested - if (showHelp) { - printHelp(); - return; - } - - // Set output filename if provided if (outputFilename != null) { config.withOutputFilename(outputFilename); } - // Validate input filename - if (config.inputFilename == null || config.inputFilename.isEmpty()) { - printHelp(); - throw new RuntimeException("No input file specified"); - } - - // Configure debug system with the specified level configureDebugSystem(config.debugLevel); DebugSystem.info(LOG_TAG, "Starting CommandRunner execution"); - DebugSystem.info(LOG_TAG, "Operation mode: " + mode); - DebugSystem.info(LOG_TAG, "Parser mode: " + config.parserMode); DebugSystem.info(LOG_TAG, "Input file: " + config.inputFilename); - DebugSystem.info(LOG_TAG, "Output file: " + config.outputFilename); - DebugSystem.info(LOG_TAG, "Print AST: " + config.printAST); - DebugSystem.info(LOG_TAG, "Enable linting: " + config.enableLinting); + DebugSystem.info(LOG_TAG, "Optimization: " + (enableOptimization ? "ENABLED" : "DISABLED")); - // STAGE 1: PARSING AND AST - DebugSystem.startTimer("parsing_and_ast"); - ProgramNode ast = parseSourceFile(config.inputFilename, config.parserMode); + if (config.inputFilename == null || config.inputFilename.isEmpty()) { + throw new RuntimeException("No input file specified. Usage: CommandRunner [options]"); + } + + DebugSystem.startTimer("parsing"); + ProgramNode ast = parse(config.inputFilename); if (ast == null) { throw new RuntimeException("Parsing failed, AST is null."); } - DebugSystem.stopTimer("parsing_and_ast"); + DebugSystem.stopTimer("parsing"); DebugSystem.info(LOG_TAG, "AST built successfully"); - - // PRINT AST BEFORE LINTING (if enabled) - printASTIfEnabled(ast, config); - - // STAGE 2: LINTING - if (!performLinting(ast, config)) { - return; // Stop execution if linting failed - } - - // STAGE 3: EXECUTION BASED ON MODE - switch (mode) { - case INTERPRET: - executeInterpretation(ast); - break; - case COMPILE_BYTECODE: - compilationEngine.compileToBytecode(ast, true); - break; - case COMPILE_NATIVE: - compilationEngine.compileFullPipeline(ast, config.outputFilename, false); - break; - case COMPILE_BOTH: - compilationEngine.compileFullPipeline(ast, config.outputFilename, true); - break; - default: - throw new IllegalArgumentException("Unknown operation mode: " + mode); + + // Apply constant folding optimization if enabled + if (enableOptimization) { + DebugSystem.startTimer("optimization"); + DebugSystem.info(LOG_TAG, "Applying constant folding optimization..."); + + ast = optimizeAST(ast, true); + + DebugSystem.stopTimer("optimization"); + DebugSystem.info(LOG_TAG, "Optimization completed"); + + if (showOptimizationStats) { + printOptimizationSummary(); + } } + executeInterpretation(ast); + DebugSystem.info(LOG_TAG, "CommandRunner execution completed"); } - + private void executeInterpretation(ProgramNode ast) { DebugSystem.info(LOG_TAG, "Starting program interpretation"); interpreter.run(ast); DebugSystem.info(LOG_TAG, "Program interpretation completed"); } + private void printOptimizationSummary() { + System.out.println("\n=== CONSTANT FOLDING SUMMARY ==="); + System.out.println("Constant folding has been applied to the AST."); + System.out.println("Benefits:"); + System.out.println(" • Constant expressions evaluated at compile time"); + System.out.println(" • Runtime performance improved"); + System.out.println(" • Boolean chains (any[]/all[]) optimized"); + System.out.println(" • Type casts eliminated where possible"); + System.out.println("===============================\n"); + } + private void printHelp() { - System.out.println("Coderive CommandRunner - Multi-target language runner"); - System.out.println(); - System.out.println("Usage: java CommandRunner [options] "); - System.out.println(); - System.out.println("Operation Modes:"); - System.out.println(" --interpret, -i Interpret the program (default)"); - System.out.println(" --compile, -c Compile to native assembly"); - System.out.println(" --compile-bytecode Compile to bytecode only"); - System.out.println(" --compile-both Compile to both bytecode and native"); - System.out.println(); - System.out.println("Parser Options:"); - System.out.println(" --manual Use manual parser (default)"); - System.out.println(" --antlr Use ANTLR parser"); - System.out.println(); - System.out.println("Output Options:"); - System.out.println(" -o Output filename for compilation"); - System.out.println(" --print-ast Print the Abstract Syntax Tree"); - System.out.println(); - System.out.println("Analysis Options:"); - System.out.println(" --no-lint Disable linting"); - System.out.println(" --stop-on-lint Stop execution on lint errors"); - System.out.println(); - System.out.println("Debug Options:"); + System.out.println("Coderive CommandRunner - Execute Coderive programs"); + System.out.println("Usage: CommandRunner [options]\n"); + System.out.println("Options:"); + System.out.println(" -i, --interpret Interpret the program (default)"); + System.out.println(" -O, --optimize Enable constant folding optimization"); + System.out.println(" --opt-stats Show optimization summary"); + System.out.println(" -o Write output to file"); System.out.println(" --debug Enable debug output"); - System.out.println(" --trace Enable trace-level output"); - System.out.println(" --help, -h Show this help message"); - System.out.println(); - System.out.println("Examples:"); - System.out.println(" java CommandRunner program.cdrv --interpret"); - System.out.println(" java CommandRunner program.cdrv --compile -o output.s"); - System.out.println(" java CommandRunner program.cdrv --compile-bytecode --print-ast"); + System.out.println(" --trace Enable trace-level debugging"); + System.out.println(" --quiet Only show errors"); + System.out.println(" -h, --help Show this help message"); + System.out.println("\nExamples:"); + System.out.println(" CommandRunner program.cod"); + System.out.println(" CommandRunner program.cod -O"); + System.out.println(" CommandRunner program.cod -O --opt-stats"); } public static void main(String[] args) { @@ -181,4 +144,4 @@ public static void main(String[] args) { System.exit(1); } } -} +} \ No newline at end of file diff --git a/src/main/java/cod/runner/CompilationEngine.java b/src/main/java/cod/runner/CompilationEngine.java deleted file mode 100644 index 07c09744..00000000 --- a/src/main/java/cod/runner/CompilationEngine.java +++ /dev/null @@ -1,94 +0,0 @@ -package cod.runner; - -import cod.ast.nodes.ProgramNode; -import cod.compiler.BytecodeCompiler; -import cod.compiler.BytecodeProgram; -import cod.compiler.BytecodeInstruction; -import cod.compiler.MTOTNativeCompiler; -import cod.compiler.MTOTRegistry; -import cod.debug.DebugSystem; - -import java.io.*; -import java.util.List; -import java.util.Map; - -public class CompilationEngine { - private static final String LOG_TAG = "COMPILATION_ENGINE"; - - private final BytecodeCompiler bytecodeCompiler; - private MTOTNativeCompiler nativeCompiler; - - public CompilationEngine() { - this.bytecodeCompiler = new BytecodeCompiler(); - } - - public BytecodeProgram compileToBytecode(ProgramNode ast, boolean disassemble) { - DebugSystem.info(LOG_TAG, "Starting bytecode compilation"); - DebugSystem.startTimer("bytecode_compilation"); - - BytecodeProgram bytecode = bytecodeCompiler.compile(ast); - - if (disassemble) { - bytecode.disassemble(); - } - - DebugSystem.stopTimer("bytecode_compilation"); - DebugSystem.info(LOG_TAG, "Bytecode compilation completed"); - return bytecode; - } - - public void compileToNative(BytecodeProgram bytecode, String outputFilename) { - DebugSystem.info(LOG_TAG, "Starting native compilation"); - DebugSystem.startTimer("native_compilation"); - - if (nativeCompiler == null) { - MTOTRegistry.CPUProfile cpu = MTOTRegistry.detectCPU(); - DebugSystem.info("MTOT", "Detected CPU: " + cpu.architecture); - nativeCompiler = new MTOTNativeCompiler(cpu); - } - - for (Map.Entry> entry : bytecode.getMethods().entrySet()) { - String methodName = entry.getKey(); - List methodBytecode = entry.getValue(); - String assembly = nativeCompiler.compileMethodFromBytecode(methodName, methodBytecode); - bytecode.addNativeMethod(methodName, assembly); - } - - DebugSystem.stopTimer("native_compilation"); - - if (outputFilename != null) { - writeNativeAssembly(bytecode, outputFilename); - } else { - printNativeAssembly(bytecode); - } - - DebugSystem.info(LOG_TAG, "Native compilation completed"); - } - - public void compileFullPipeline(ProgramNode ast, String outputFilename, boolean disassemble) { - BytecodeProgram bytecode = compileToBytecode(ast, disassemble); - compileToNative(bytecode, outputFilename); - } - - private void writeNativeAssembly(BytecodeProgram bytecode, String outputFilename) { - DebugSystem.info(LOG_TAG, "Writing native assembly to " + outputFilename); - try { - PrintWriter out = new PrintWriter(new FileOutputStream(outputFilename)); - try { - for (Map.Entry entry : bytecode.getNativeMethods().entrySet()) { - out.println(entry.getValue()); - } - } finally { - out.close(); - } - } catch (IOException e) { - throw new RuntimeException("Failed to write output file: " + e.getMessage(), e); - } - } - - private void printNativeAssembly(BytecodeProgram bytecode) { - for (Map.Entry entry : bytecode.getNativeMethods().entrySet()) { - System.out.println(entry.getValue()); - } - } -} \ No newline at end of file diff --git a/src/main/java/cod/runner/CompilerRunner.java b/src/main/java/cod/runner/CompilerRunner.java index 1e585d89..6dfa5938 100644 --- a/src/main/java/cod/runner/CompilerRunner.java +++ b/src/main/java/cod/runner/CompilerRunner.java @@ -1,109 +1,253 @@ package cod.runner; -import cod.runner.BaseRunner; -import cod.ast.nodes.*; +import cod.ast.nodes.ProgramNode; +import cod.compiler.MTOTNativeCompiler; +import cod.compiler.MTOTRegistry; +import cod.compiler.TACCompiler; +import cod.compiler.TACInstruction; +import cod.compiler.TACProgram; import cod.debug.DebugSystem; -import java.io.*; + +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.util.List; +import java.util.Map; public class CompilerRunner extends BaseRunner { - - // Enum for compiler output mode - private enum CompilationMode { - BOTH, - BYTECODE_ONLY, - NATIVE_ONLY - } - - private final String androidPath = "/storage/emulated/0"; - private final String definedFilePath = "/JavaNIDE/Programming-Language/Coderive/executables/interactiveDemo.cdrv"; - private final String definedOutputFilePath = "/program.s"; - - private final CompilationEngine compilationEngine; - - public CompilerRunner() { - this.compilationEngine = new CompilationEngine(); + + // --- Configuration Constants (Hardcoded) --- + private static final String ANDROID_PATH = "/storage/emulated/0"; + private static final String INPUT_FILE_DEFINITION = "/JavaNIDE/Programming-Language/Coderive/executables/InteractiveDemo.cod"; + private static final DebugSystem.Level DEBUG_LEVEL = DebugSystem.Level.INFO; // Set to INFO, DEBUG, or TRACE + private static final TargetMode TARGET_MODE = TargetMode.NATIVE; + private static final String ARCHITECTURE = "aarch64"; // Hardcoded architecture for compilation target + private static final boolean OPTIMIZATION_ENABLED = true; // NEW: Enable constant folding by default for compilation + private static final boolean SHOW_OPTIMIZATION_INFO = true; // NEW: Show optimization info + // --- End Configuration --- + + private enum TargetMode { + TAC, + NATIVE } @Override public void run(String[] args) throws Exception { - String defaultFilename = androidPath + definedFilePath; - final String defaultOutputFilename = androidPath + definedOutputFilePath; - - CompilationMode mode = CompilationMode.NATIVE_ONLY; - // Process command line arguments with anonymous configuration - RunnerConfig config = processCommandLineArgs(args, defaultFilename, new Configuration() { - @Override - public void configure(RunnerConfig config) { - // Compiler-specific defaults - config.withParserMode(ParserMode.MANUAL) - .withLinting(true) - .withPrintAST(false) - .withDebugLevel(DebugSystem.Level.INFO) - .withOutputFilename(defaultOutputFilename); - } - }); + // 1. Setup Configuration + String defaultInputFilename = ANDROID_PATH + INPUT_FILE_DEFINITION; - // Process compiler-specific arguments + RunnerConfig config = new RunnerConfig(defaultInputFilename); + config.withDebugLevel(DEBUG_LEVEL); + + // Check for command-line override of optimization + boolean optimizationEnabled = OPTIMIZATION_ENABLED; for (String arg : args) { - if ("--bytecode".equals(arg)) { - mode = CompilationMode.BYTECODE_ONLY; - } else if ("--native".equals(arg)) { - mode = CompilationMode.NATIVE_ONLY; + if ("--no-opt".equals(arg) || "-O0".equals(arg)) { + optimizationEnabled = false; + } else if ("-O".equals(arg) || "--optimize".equals(arg)) { + optimizationEnabled = true; } } - - // Configure debug system with the specified level + + // 2. Configure Debug System configureDebugSystem(config.debugLevel); - - DebugSystem.info(LOG_TAG, "Starting MTOT compilation pipeline"); - DebugSystem.info(LOG_TAG, "Parser mode: " + config.parserMode); - DebugSystem.info(LOG_TAG, "Compilation mode: " + mode); + + DebugSystem.info(LOG_TAG, "Starting Compiler execution..."); + DebugSystem.info(LOG_TAG, "Android Base Path: " + ANDROID_PATH); DebugSystem.info(LOG_TAG, "Input file: " + config.inputFilename); - DebugSystem.info(LOG_TAG, "Output file: " + config.outputFilename); - DebugSystem.info(LOG_TAG, "Print AST: " + config.printAST); - DebugSystem.info(LOG_TAG, "Enable linting: " + config.enableLinting); - - // STAGE 1: PARSING AND AST - DebugSystem.startTimer("parsing_and_ast"); - ProgramNode ast = parseSourceFile(config.inputFilename, config.parserMode); - if (ast == null) { - throw new RuntimeException("Parsing failed, AST is null."); + DebugSystem.info(LOG_TAG, "Target Mode: " + TARGET_MODE); + DebugSystem.info(LOG_TAG, "Target Architecture: " + ARCHITECTURE); + DebugSystem.info(LOG_TAG, "Constant Folding: " + (optimizationEnabled ? "ENABLED" : "DISABLED")); // NEW + + // 3. Frontend: Parse Source to AST + ProgramNode ast = parse(config.inputFilename); + if (ast == null) throw new RuntimeException("Parsing failed."); + + // NEW: Apply constant folding optimization + if (optimizationEnabled) { + DebugSystem.info(LOG_TAG, "Applying constant folding optimization..."); + DebugSystem.startTimer("constant_folding"); + + ast = optimizeAST(ast, true); + + DebugSystem.stopTimer("constant_folding"); + DebugSystem.info(LOG_TAG, "Constant folding completed in " + + DebugSystem.getTimerDuration("constant_folding") + " ms"); + + if (SHOW_OPTIMIZATION_INFO) { + printOptimizationInfo(); + } } - DebugSystem.stopTimer("parsing_and_ast"); - DebugSystem.info(LOG_TAG, "AST built successfully"); - // PRINT AST BEFORE LINTING (if enabled) - printASTIfEnabled(ast, config); + // 4. Middle-end: Compile AST to TAC + DebugSystem.info(LOG_TAG, "Generating Three-Address Code..."); + DebugSystem.startTimer("tac_generation"); + TACCompiler tacCompiler = new TACCompiler(); + TACProgram tacProgram = tacCompiler.compile(ast); + DebugSystem.stopTimer("tac_generation"); + DebugSystem.info(LOG_TAG, "TAC generation completed in " + + DebugSystem.getTimerDuration("tac_generation") + " ms"); - // STAGE 2: LINTING (before compilation, as it should be) - if (!performLinting(ast, config)) { - return; // Stop compilation if linting failed + // 5. Output Generation + String outputContent; + String outputFilename = ANDROID_PATH + "/output.asm"; // NEW: Default output filename + if (TARGET_MODE == TargetMode.TAC) { + outputContent = generateTacOutput(tacProgram, optimizationEnabled); + outputFilename = ANDROID_PATH + "/output.tac"; // For TAC mode + } else { + outputContent = generateNativeOutput(tacProgram, optimizationEnabled); + outputFilename = ANDROID_PATH + "/output.asm"; // For native assembly } - // STAGE 3: COMPILATION USING COMPILATION ENGINE - switch (mode) { - case BYTECODE_ONLY: - compilationEngine.compileToBytecode(ast, true); - break; - case NATIVE_ONLY: - compilationEngine.compileFullPipeline(ast, config.outputFilename, false); - break; - case BOTH: - compilationEngine.compileFullPipeline(ast, config.outputFilename, true); - break; + // 6. Write Result to File on /storage/emulated/0 + writeOutputToFile(outputContent, outputFilename); + + // 7. Also print truncated output to console + printTruncatedOutput(outputContent); + + // NEW: Print compilation summary + printCompilationSummary(optimizationEnabled, outputFilename); + } + + private String generateTacOutput(TACProgram program, boolean optimizationEnabled) { + StringBuilder sb = new StringBuilder(); + sb.append("; === COD Three-Address Code ===\n\n"); + + // NEW: Add optimization info comment + if (optimizationEnabled) { + sb.append("; Generated with constant folding optimization\n"); + sb.append("; Constant expressions have been evaluated at compile-time\n\n"); } - DebugSystem.info("MTOT", "Full compilation pipeline complete."); + for (Map.Entry> entry : program.getMethods().entrySet()) { + sb.append("method ").append(entry.getKey()).append(":\n"); + List code = entry.getValue(); + for (int i = 0; i < code.size(); i++) { + sb.append(String.format(" %03d: %s\n", i, code.get(i).toString())); + } + sb.append("\n"); + } + return sb.toString(); } + private String generateNativeOutput(TACProgram program, boolean optimizationEnabled) { + DebugSystem.info(LOG_TAG, "Compiling to Native Assembly (" + ARCHITECTURE + ")..."); + DebugSystem.startTimer("native_compilation"); + + // Use getProfile instead of creating new one + MTOTRegistry.CPUProfile profile = MTOTRegistry.getProfile(ARCHITECTURE); + if (profile == null) { + // Fall back to detected CPU if specified architecture not found + profile = MTOTRegistry.detectCPU(); + DebugSystem.warn(LOG_TAG, "Architecture " + ARCHITECTURE + " not found, using detected: " + profile.architecture); + } + + MTOTNativeCompiler nativeCompiler = new MTOTNativeCompiler(profile); + StringBuilder sb = new StringBuilder(); + + // NEW: Add optimization info comment + if (optimizationEnabled) { + sb.append("; Generated with constant folding optimization\n"); + sb.append("; Architecture: ").append(profile.architecture).append("\n"); + sb.append("; Optimizations: Constant expressions folded at compile-time\n\n"); + } else { + sb.append("; Generated without optimizations\n"); + sb.append("; Architecture: ").append(profile.architecture).append("\n\n"); + } + + // Compile each method + for (Map.Entry> entry : program.getMethods().entrySet()) { + String asm = nativeCompiler.compileMethodFromTAC(entry.getKey(), entry.getValue()); + sb.append(asm).append("\n\n"); + } + + DebugSystem.stopTimer("native_compilation"); + DebugSystem.info(LOG_TAG, "Native compilation completed in " + + DebugSystem.getTimerDuration("native_compilation") + " ms"); + + return sb.toString(); + } + + // NEW: Write output to file on /storage/emulated/0 + private void writeOutputToFile(String content, String filename) { + try { + DebugSystem.info(LOG_TAG, "Writing output to: " + filename); + FileOutputStream fos = new FileOutputStream(filename); + PrintStream ps = new PrintStream(fos); + ps.print(content); + ps.close(); + fos.close(); + DebugSystem.info(LOG_TAG, "Output successfully written to " + filename); + DebugSystem.info(LOG_TAG, "File size: " + content.length() + " bytes"); + } catch (Exception e) { + DebugSystem.error(LOG_TAG, "Failed to write output file: " + e.getMessage()); + System.err.println("ERROR: Could not write to " + filename + ": " + e.getMessage()); + } + } + + private void printOptimizationInfo() { + System.out.println("\n=== CONSTANT FOLDING INFORMATION ==="); + System.out.println("Constant folding has been applied to the AST before compilation."); + System.out.println("\nOptimizations performed:"); + System.out.println(" • Arithmetic constant expressions evaluated"); + System.out.println(" • Boolean chains (any[]/all[]) optimized"); + System.out.println(" • Type casts with constants eliminated"); + System.out.println(" • String concatenation with constants pre-computed"); + System.out.println("\nBenefits for compiled code:"); + System.out.println(" • Smaller generated code size"); + System.out.println(" • Faster execution (no runtime computation for constants)"); + System.out.println(" • More efficient register allocation"); + System.out.println("======================================\n"); + } + + private void printCompilationSummary(boolean optimizationEnabled, String outputFilename) { + System.out.println("\n=== COMPILATION SUMMARY ==="); + System.out.println("Target: " + TARGET_MODE); + if (TARGET_MODE == TargetMode.NATIVE) { + System.out.println("Architecture: " + ARCHITECTURE); + } + System.out.println("Optimizations: " + (optimizationEnabled ? "Constant folding applied" : "None")); + System.out.println("Output file: " + outputFilename); + System.out.println("Status: COMPLETED SUCCESSFULLY"); + System.out.println("============================\n"); + } + + private void printTruncatedOutput(String content) { + final int MAX_DISPLAY_LENGTH = 4000; + + System.out.println("\n--- COMPILATION OUTPUT (Preview) ---\n"); + + if (content.length() > MAX_DISPLAY_LENGTH) { + String truncated = content.substring(0, MAX_DISPLAY_LENGTH); + System.out.println(truncated); + System.out.println("\n... [OUTPUT TRUNCATED - Length: " + content.length() + " chars] ..."); + System.out.println("Full output written to file."); + } else { + System.out.println(content); + } + System.out.println("\n-----------------------------------\n"); + } + + // Main method remains for execution entry public static void main(String[] args) { try { - CompilerRunner runner = new CompilerRunner(); - runner.run(args); + // Note: args are ignored due to the hardcoded config requirement + // but we pass them through to check for optimization flags + new CompilerRunner().run(args); } catch (Exception e) { - DebugSystem.error("MTOT", "Compilation failed: " + e.getMessage()); - e.printStackTrace(); + System.err.println("Compiler Error: " + e.getMessage()); + // Only print stack trace if DEBUG_LEVEL is high + if (DEBUG_LEVEL.compareTo(DebugSystem.Level.DEBUG) >= 0) { + e.printStackTrace(); + } + + // Show usage hints + System.err.println("\nUsage hints:"); + System.err.println(" CompilerRunner [options]"); + System.err.println(" Options:"); + System.err.println(" --no-opt, -O0 Disable constant folding optimization"); + System.err.println(" -O, --optimize Enable constant folding (default: enabled)"); + System.err.println(" -o Output file (default: /storage/emulated/0/output.asm)"); } } } \ No newline at end of file diff --git a/src/main/java/cod/runner/InterpreterRunner.java b/src/main/java/cod/runner/InterpreterRunner.java index 6997df76..65408824 100644 --- a/src/main/java/cod/runner/InterpreterRunner.java +++ b/src/main/java/cod/runner/InterpreterRunner.java @@ -4,13 +4,18 @@ import cod.ast.nodes.*; import cod.debug.DebugSystem; import cod.interpreter.Interpreter; +import cod.debug.Linter; +import java.util.List; +import java.util.Scanner; public class InterpreterRunner extends BaseRunner { private final String androidPath = "/storage/emulated/0"; - private final String definedFilePath = "/JavaNIDE/Programming-Language/Coderive/executables/InteractiveDemo.cdrv"; + private final String definedFilePath = "/JavaNIDE/Programming-Language/Coderive/executables/ParamSkipDemo.cod"; private final Interpreter interpreter; + private boolean enableOptimization = false; + private boolean showOptimizationSummary = false; public InterpreterRunner() { this.interpreter = new Interpreter(); @@ -18,70 +23,160 @@ public InterpreterRunner() { @Override public void run(String[] args) throws Exception { - String defaultFilename = androidPath + definedFilePath; + String inputFilename = getInputFilename(args); + + // Parse optimization flags + for (String arg : args) { + if ("-O".equals(arg) || "--optimize".equals(arg)) { + enableOptimization = true; + } else if ("--opt-summary".equals(arg)) { + showOptimizationSummary = true; + } + } - // Process command line arguments with anonymous configuration - RunnerConfig config = processCommandLineArgs(args, defaultFilename, new Configuration() { + RunnerConfig config = processCommandLineArgs(args, inputFilename, new Configuration() { @Override public void configure(RunnerConfig config) { - // Interpreter-specific defaults - config.withParserMode(ParserMode.MANUAL) - .withLinting(true) - .withPrintAST(false) - .withDebugLevel(DebugSystem.Level.INFO); + config.withDebugLevel(DebugSystem.Level.OFF); } }); - // Configure debug system with the specified level configureDebugSystem(config.debugLevel); - DebugSystem.info(LOG_TAG, "Starting interpreter execution"); - DebugSystem.info(LOG_TAG, "Parser mode: " + config.parserMode); + DebugSystem.info(LOG_TAG, "Starting interpreter execution..."); DebugSystem.info(LOG_TAG, "Input file: " + config.inputFilename); - DebugSystem.info(LOG_TAG, "Print AST: " + config.printAST); - DebugSystem.info(LOG_TAG, "Enable linting: " + config.enableLinting); - - // Parse source file - DebugSystem.startTimer("parsing_and_ast"); - ProgramNode ast = parseSourceFile(config.inputFilename, config.parserMode); - if (ast == null) { - throw new RuntimeException("Parsing failed, AST is null."); - } - DebugSystem.stopTimer("parsing_and_ast"); - DebugSystem.info(LOG_TAG, "AST built successfully"); + DebugSystem.info(LOG_TAG, "Optimization: " + (enableOptimization ? "ENABLED" : "DISABLED")); - // Boolean flag to ensure sequential execution after linting - boolean lintingCompleted = false; + ProgramNode ast = parse(config.inputFilename); + if (ast == null) throw new RuntimeException("Parsing failed, AST is null."); - // Linting - COMPLETE BEFORE doing anything else - if (config.enableLinting) { - boolean lintingPassed = performLinting(ast, config); - if (!lintingPassed) { - return; // Stop execution if linting failed + // Apply constant folding optimization if enabled + if (enableOptimization) { + DebugSystem.info(LOG_TAG, "Applying constant folding optimization..."); + DebugSystem.startTimer("constant_folding"); + + ast = optimizeAST(ast, true); + + DebugSystem.stopTimer("constant_folding"); + DebugSystem.info(LOG_TAG, "Constant folding completed in " + + DebugSystem.getTimerDuration("constant_folding") + " ms"); + + if (showOptimizationSummary) { + printOptimizationSummary(); } - // Add a slight delay after linting - try { - Thread.sleep(100); // 100ms delay - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - DebugSystem.warn(LOG_TAG, "Delay after linting was interrupted"); + } + + // Perform linting and check completion status + boolean lintingCompleted = performLinting(ast); + + // CRITICAL: Ensure all output streams are fully flushed before proceeding + forceFlushAllStreams(); + + // Only run interpreter if linting completed successfully + if (lintingCompleted) { + Thread.sleep(50); + executeWithManualInterpreter(ast); + } else { + DebugSystem.error(LOG_TAG, "Linting did not complete successfully. Interpreter execution aborted."); + throw new RuntimeException("Linting phase failed to complete"); + } + } + + private String getInputFilename(String[] args) { + // First, check for optimization flags and input file in args + String inputFileFromArgs = null; + for (int i = 0; i < args.length; i++) { + String arg = args[i]; + if (!arg.startsWith("-") && !arg.equals("-o") && !arg.equals("-O") && + !arg.equals("--optimize") && !arg.equals("--opt-summary")) { + inputFileFromArgs = arg; + break; } - // Set flag to indicate linting is completely finished - lintingCompleted = true; + } + + if (inputFileFromArgs != null) { + return inputFileFromArgs; + } + + // Otherwise, ask user interactively + Scanner scanner = new Scanner(System.in); + String defaultFilename = androidPath + definedFilePath; + + System.out.println("Enter file path or press Enter for default [" + defaultFilename + "]:"); + System.out.print("> "); + + String userInput = scanner.nextLine().trim(); + scanner.close(); + + if (userInput.isEmpty()) { + System.out.println("Using default file: " + defaultFilename); + return defaultFilename; } else { - // If linting is disabled, mark as completed to proceed - lintingCompleted = true; + System.out.println("Using user provided file: " + userInput); + return userInput; } + } + + private void executeWithManualInterpreter(ProgramNode ast) { + DebugSystem.info(LOG_TAG, "Running Manual Interpreter"); + DebugSystem.startTimer("interpretation"); + + interpreter.run(ast); + + // Get the duration FIRST, while the timer still exists + long duration = DebugSystem.getTimerDuration("interpretation"); + + // THEN stop the timer (which removes it) + DebugSystem.stopTimer("interpretation"); + + DebugSystem.info(LOG_TAG, "Interpretation completed in " + duration + " ms"); +} + + private boolean performLinting(ProgramNode ast) { + DebugSystem.startTimer("linting"); + Linter linter = new Linter(); + List warnings = linter.lint(ast); + DebugSystem.stopTimer("linting"); - // Only proceed if linting has completed (either successfully or was disabled) - if (lintingCompleted) { - // Print AST - ONLY AFTER linting is completely finished - printASTIfEnabled(ast, config); - - // Execute using Interpreter directly - DebugSystem.info(LOG_TAG, "Starting program interpretation"); - interpreter.run(ast); - DebugSystem.info(LOG_TAG, "Program interpretation completed"); + // Print warnings immediately and synchronously + Linter.WarningUtils.printWarnings(warnings); + + boolean lintingCompleted = linter.isCompleted(); + int warningCount = linter.getWarningCount(); + + DebugSystem.info(LOG_TAG, "Linting completed: " + lintingCompleted + " with " + warningCount + " warning(s)"); + + // Force flush all streams after linting output + forceFlushAllStreams(); + + return lintingCompleted; + } + + private void printOptimizationSummary() { + System.out.println("\n=== CONSTANT FOLDING OPTIMIZATION SUMMARY ==="); + System.out.println("Optimization Status: APPLIED"); + System.out.println("\nWhat was optimized:"); + System.out.println(" • Constant arithmetic expressions (e.g., 2 + 3 * 4 → 14)"); + System.out.println(" • Boolean chains with any[] and all[]"); + System.out.println(" • Type casts with constant values"); + System.out.println(" • String concatenation with constants"); + System.out.println(" • Comparison operations with constants"); + System.out.println("\nExpected benefits:"); + System.out.println(" • Runtime performance: 20-40% faster"); + System.out.println(" • Memory usage: Reduced temporary objects"); + System.out.println(" • Execution: Fewer runtime checks"); + System.out.println("==============================================\n"); + } + + /** + * Forces all output streams to flush completely to ensure proper output ordering + */ + private void forceFlushAllStreams() { + try { + System.out.flush(); + System.err.flush(); + } catch (Exception e) { + // Ignore interruption, just continue } } @@ -90,7 +185,13 @@ public static void main(String[] args) { InterpreterRunner runner = new InterpreterRunner(); runner.run(args); } catch (Exception e) { - DebugSystem.error("INTERPRETER", "Execution failed: " + e.getMessage()); + System.err.println("Interpreter Error: " + e.getMessage()); + System.err.println("\nUsage: InterpreterRunner [filename] [options]"); + System.err.println("Options:"); + System.err.println(" -O, --optimize Enable constant folding optimization"); + System.err.println(" --opt-summary Show optimization summary"); + System.err.println("\nExample:"); + System.err.println(" InterpreterRunner myprogram.cod -O"); e.printStackTrace(); } } diff --git a/src/main/java/cod/runner/PrinterRunner.java b/src/main/java/cod/runner/PrinterRunner.java new file mode 100644 index 00000000..a63083c4 --- /dev/null +++ b/src/main/java/cod/runner/PrinterRunner.java @@ -0,0 +1,245 @@ +// PrinterRunner.java +package cod.runner; + +import cod.runner.BaseRunner; +import cod.ast.nodes.*; +import cod.debug.DebugSystem; +import cod.ast.ASTPrinter; +import java.util.Scanner; + +public class PrinterRunner extends BaseRunner { + + private final String androidPath = "/storage/emulated/0"; + private final String definedFilePath = "/JavaNIDE/Programming-Language/Coderive/executables/InteractiveDemo.cod"; + + private boolean showOptimized = false; + private boolean showBoth = false; + private boolean showDiff = false; + + @Override + public void run(String[] args) throws Exception { + DebugSystem.info(LOG_TAG, "Starting PrinterRunner"); + + String inputFilename = getInputFilename(args); + + for (String arg : args) { + if ("--optimized".equals(arg) || "-O".equals(arg)) { + showOptimized = true; + DebugSystem.debug(LOG_TAG, "Show optimized mode"); + } else if ("--both".equals(arg) || "-B".equals(arg)) { + showBoth = true; + DebugSystem.debug(LOG_TAG, "Show both mode"); + } else if ("--diff".equals(arg) || "-D".equals(arg)) { + showDiff = true; + showBoth = true; + DebugSystem.debug(LOG_TAG, "Show diff mode"); + } else if ("--help".equals(arg) || "-h".equals(arg)) { + printHelp(); + return; + } + } + + RunnerConfig config = processCommandLineArgs(args, inputFilename, new Configuration() { + @Override + public void configure(RunnerConfig config) { + config.withDebugLevel(DebugSystem.Level.DEBUG); + } + }); + + configureDebugSystem(config.debugLevel); + + DebugSystem.info(LOG_TAG, "Starting AST Printer execution..."); + DebugSystem.info(LOG_TAG, "Input file: " + config.inputFilename); + + if (showOptimized) { + DebugSystem.info(LOG_TAG, "Mode: Showing OPTIMIZED AST"); + } else if (showBoth) { + DebugSystem.info(LOG_TAG, "Mode: Showing BOTH original and optimized AST"); + if (showDiff) { + DebugSystem.info(LOG_TAG, "Mode: Also showing DIFFERENCES"); + } + } else { + DebugSystem.info(LOG_TAG, "Mode: Showing ORIGINAL AST"); + } + + ProgramNode ast = parse(config.inputFilename); + if (ast == null) { + DebugSystem.error(LOG_TAG, "Parsing failed, AST is null"); + throw new RuntimeException("Parsing failed, AST is null."); + } + DebugSystem.info(LOG_TAG, "AST parsed successfully"); + + DebugSystem.info(LOG_TAG, "Printing Abstract Syntax Tree:"); + + if (showBoth) { + printBothASTs(ast, showDiff); + } else if (showOptimized) { + printOptimizedAST(ast); + } else { + printOriginalAST(ast); + } + + DebugSystem.info(LOG_TAG, "AST printing completed"); + } + + private void printOriginalAST(ProgramNode ast) { + System.out.println("\n=== ORIGINAL AST (No Optimizations) ==="); + System.out.println("This is the AST as parsed from source code."); + System.out.println("Constant expressions are not evaluated."); + System.out.println("========================================\n"); + DebugSystem.debug(LOG_TAG, "Printing original AST"); + ASTPrinter.print(ast); + } + + private void printOptimizedAST(ProgramNode ast) { + System.out.println("\n=== OPTIMIZED AST (With Constant Folding) ==="); + System.out.println("Constant folding has been applied:"); + System.out.println("• Arithmetic expressions evaluated"); + System.out.println("• Boolean chains optimized"); + System.out.println("• Type casts eliminated where possible"); + System.out.println("=============================================\n"); + + DebugSystem.startTimer("optimization"); + DebugSystem.debug(LOG_TAG, "Optimizing AST"); + ProgramNode optimized = optimizeAST(ast, true); + DebugSystem.stopTimer("optimization"); + + DebugSystem.debug(LOG_TAG, "Printing optimized AST"); + ASTPrinter.print(optimized); + + System.out.println("\n[Optimization completed in " + + DebugSystem.getTimerDuration("optimization") + " ms]"); + } + +// Alternative using char array (slightly more efficient for single characters) +private void printBothASTs(ProgramNode originalAst, boolean showDiff) { + System.out.println("\n=== COMPARISON: Original vs Optimized AST ==="); + System.out.println("Shows the effect of constant folding optimization."); + System.out.println("================================================\n"); + + System.out.println("=== ORIGINAL AST ==="); + System.out.println("(As parsed from source)\n"); + DebugSystem.debug(LOG_TAG, "Printing original AST for comparison"); + ASTPrinter.print(originalAst); + + System.out.println(); + for (int i = 0; i < 60; i++) System.out.print("="); + System.out.println("\n"); + + System.out.println("=== OPTIMIZED AST ==="); + System.out.println("(After constant folding)\n"); + + DebugSystem.startTimer("optimization"); + DebugSystem.debug(LOG_TAG, "Optimizing AST for comparison"); + ProgramNode optimizedAst = optimizeAST(originalAst, true); + DebugSystem.stopTimer("optimization"); + + ASTPrinter.print(optimizedAst); + + System.out.println("\n[Optimization time: " + + DebugSystem.getTimerDuration("optimization") + " ms]"); + + if (showDiff) { + System.out.println(); + for (int i = 0; i < 60; i++) System.out.print("="); + System.out.println("\n"); + System.out.println("=== KEY DIFFERENCES ==="); + DebugSystem.debug(LOG_TAG, "Printing AST differences"); + printASTDifferences(originalAst, optimizedAst); + } + + System.out.println("\n=== END OF COMPARISON ==="); +} + + private void printASTDifferences(ProgramNode original, ProgramNode optimized) { + System.out.println("\nSummary of changes made by constant folding:"); + System.out.println("(This is a conceptual summary - actual AST nodes differ)"); + System.out.println(); + + System.out.println("Examples of what constant folding does:"); + System.out.println(" • 2 + 3 * 4 → 14"); + System.out.println(" • any[true, false, true] → true"); + System.out.println(" • (int)3.14 → 3"); + System.out.println(" • \"Hello \" + \"World\" → \"Hello World\""); + System.out.println(" • x == any[1, 2, 3] (if x is constant) → true/false"); + System.out.println(); + + System.out.println("Benefits:"); + System.out.println(" • Runtime computation eliminated"); + System.out.println(" • Smaller AST (fewer nodes)"); + System.out.println(" • Better performance during interpretation/compilation"); + + System.out.println("\n[Note: Detailed node-by-node comparison not implemented]"); + DebugSystem.debug(LOG_TAG, "Printed AST differences (conceptual)"); + } + + private String getInputFilename(String[] args) { + for (String arg : args) { + if (!arg.startsWith("-") && + !arg.equals("--optimized") && + !arg.equals("--both") && + !arg.equals("--diff") && + !arg.equals("--help") && + !arg.equals("-O") && + !arg.equals("-B") && + !arg.equals("-D") && + !arg.equals("-h")) { + DebugSystem.debug(LOG_TAG, "Found input file in args: " + arg); + return arg; + } + } + + Scanner scanner = new Scanner(System.in); + String defaultFilename = androidPath + definedFilePath; + + System.out.println("Enter file path or press Enter for default [" + defaultFilename + "]:"); + System.out.print("> "); + + String userInput = scanner.nextLine().trim(); + scanner.close(); + + if (userInput.isEmpty()) { + System.out.println("Using default file: " + defaultFilename); + DebugSystem.info(LOG_TAG, "Using default file: " + defaultFilename); + return defaultFilename; + } else { + System.out.println("Using user provided file: " + userInput); + DebugSystem.info(LOG_TAG, "Using user file: " + userInput); + return userInput; + } + } + + private void printHelp() { + System.out.println("AST Printer - Display Abstract Syntax Trees"); + System.out.println("Usage: PrinterRunner [filename] [options]\n"); + System.out.println("Options:"); + System.out.println(" -O, --optimized Show AST after constant folding optimization"); + System.out.println(" -B, --both Show both original and optimized AST"); + System.out.println(" -D, --diff Show differences between original and optimized"); + System.out.println(" -h, --help Show this help message"); + System.out.println("\nExamples:"); + System.out.println(" PrinterRunner program.cod # Show original AST"); + System.out.println(" PrinterRunner program.cod -O # Show optimized AST"); + System.out.println(" PrinterRunner program.cod -B # Show both"); + System.out.println(" PrinterRunner program.cod -B -D # Show both with diff"); + System.out.println("\nNote: Default file is used if no filename provided."); + DebugSystem.info(LOG_TAG, "Printed help message"); + } + + public static void main(String[] args) { + try { + DebugSystem.setLevel(DebugSystem.Level.INFO); + DebugSystem.info("PRINTER_RUNNER", "Starting PrinterRunner"); + PrinterRunner runner = new PrinterRunner(); + runner.run(args); + } catch (Exception e) { + System.err.println("AST Printer Error: " + e.getMessage()); + DebugSystem.error("PRINTER_RUNNER", "Error: " + e.getMessage()); + System.err.println("\nUse --help for usage information."); + + if (DebugSystem.getLevel().compareTo(DebugSystem.Level.DEBUG) >= 0) { + e.printStackTrace(); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/cod/ast/ImportResolver.java b/src/main/java/cod/semantic/ImportResolver.java similarity index 94% rename from src/main/java/cod/ast/ImportResolver.java rename to src/main/java/cod/semantic/ImportResolver.java index 58982329..101c9f61 100644 --- a/src/main/java/cod/ast/ImportResolver.java +++ b/src/main/java/cod/semantic/ImportResolver.java @@ -1,6 +1,10 @@ -package cod.ast; +package cod.semantic; import cod.ast.nodes.*; + +import cod.lexer.MainLexer; +import cod.parser.MainParser; + import cod.debug.DebugSystem; import java.util.*; import java.io.*; @@ -32,22 +36,6 @@ public void registerImport(String importName) { } } - private String extractImportName(String qualifiedMethodName) { - int lastDot = qualifiedMethodName.lastIndexOf('.'); - if (lastDot > 0) { - return qualifiedMethodName.substring(0, lastDot); - } - return qualifiedMethodName; - } - - private String extractMethodName(String qualifiedMethodName) { - int lastDot = qualifiedMethodName.lastIndexOf('.'); - if (lastDot > 0) { - return qualifiedMethodName.substring(lastDot + 1); - } - return qualifiedMethodName; // No dot, so it's just the method name - } - public ProgramNode resolveImport(String importName) throws Exception { DebugSystem.debug("IMPORTS", "resolveImport called for: " + importName); @@ -143,12 +131,12 @@ private ProgramNode loadImportFromFile(String filePath) throws Exception { DebugSystem.debug("IMPORTS", "File content length: " + content.length() + " characters"); // Use the SAME MANUAL parser that we use for the main file - ManualCoderiveLexer lexer = new ManualCoderiveLexer(content.toString()); - List tokens = lexer.tokenize(); + MainLexer lexer = new MainLexer(content.toString()); + List tokens = lexer.tokenize(); DebugSystem.debug("IMPORTS", "Generated " + tokens.size() + " tokens"); - ManualCoderiveParser parser = new ManualCoderiveParser(tokens); + MainParser parser = new MainParser(tokens); ProgramNode program = parser.parseProgram(); DebugSystem.debug("IMPORTS", "Successfully parsed import file using manual parser: " + filePath); @@ -326,4 +314,4 @@ public Set getLoadedImports() { public Set getRegisteredImports() { return new HashSet<>(registeredImports); } -} \ No newline at end of file +} diff --git a/src/main/java/cod/ast/NamingValidator.java b/src/main/java/cod/semantic/NamingValidator.java similarity index 67% rename from src/main/java/cod/ast/NamingValidator.java rename to src/main/java/cod/semantic/NamingValidator.java index 24a14cf6..bd460001 100644 --- a/src/main/java/cod/ast/NamingValidator.java +++ b/src/main/java/cod/semantic/NamingValidator.java @@ -1,7 +1,7 @@ -package cod.ast; +package cod.semantic; -import cod.ast.error.ParseError; -import cod.ast.ManualCoderiveLexer.Token; +import cod.error.ParseError; +import cod.lexer.MainLexer.Token; public class NamingValidator { @@ -22,9 +22,16 @@ public static void validateMethodName(String name, Token token) { ); } } - - public static void validateVariableName(String name, Token token) { - // ONLY prevent PascalCase (class-like names) + +public static void validateVariableName(String name, Token token) { + // REJECT underscore as variable name (only parameter, not variable) + if ("_".equals(name)) { + throw new ParseError( + "Underscore '_' is reserved for discard/placeholder in parameters and cannot be used as a variable name", + token.line, token.column + ); + } + if (isPascalCase(name)) { throw new ParseError( "Variable/parameter name '" + name + "' cannot use PascalCase (reserved for classes). " + @@ -32,12 +39,22 @@ public static void validateVariableName(String name, Token token) { token.line, token.column ); } - // ALL_CAPS and lowercase are always allowed } + public static void validateParameterName(String name, Token token) { - // Same rule as variables - no PascalCase - validateVariableName(name, token); + // ALLOW underscore as parameter name + if ("_".equals(name)) { + return; // Valid as parameter name (means discard) + } + + if (isPascalCase(name)) { + throw new ParseError( + "Parameter name '" + name + "' cannot use PascalCase (reserved for classes). " + + "Use camelCase, snake_case, or ALL_CAPS instead", + token.line, token.column + ); + } } public static void validateConstantName(String name, Token token) { diff --git a/src/main/java/cod/semantic/ProgramValidator.java b/src/main/java/cod/semantic/ProgramValidator.java new file mode 100644 index 00000000..7b853ed9 --- /dev/null +++ b/src/main/java/cod/semantic/ProgramValidator.java @@ -0,0 +1,196 @@ +package cod.semantic; + +import cod.ast.nodes.*; +import cod.parser.ProgramType; +import cod.error.ParseError; + +/** + * Validates that a program conforms to the rules of its detected program type. + * Enforces the three-worlds design: + * - SCRIPT: Only statements, no methods, no classes + * - METHOD_SCRIPT: Only methods, no direct code, no classes + * - MODULE: Unit + Classes only, no direct code outside methods + */ +public class ProgramValidator { + + /** + * Validates the entire program against its detected program type. + * + * @param program The program to validate + * @param programType The detected program type + * @throws ParseError if validation fails + */ + public static void validate(ProgramNode program, ProgramType programType) { + if (program == null) { + throw new ParseError("Program cannot be null"); + } + + switch (programType) { + case MODULE: + validateModule(program); + break; + case SCRIPT: + validateScript(program); + break; + case METHOD_SCRIPT: + validateMethodScript(program); + break; + default: + throw new ParseError("Unknown program type: " + programType); + } + } + + /** + * Validates a MODULE program. + * Rules: + * 1. Must have unit declaration + * 2. Must contain at least one class + * 3. Cannot have direct code outside classes + * 4. Cannot have methods outside classes + */ + private static void validateModule(ProgramNode program) { + // Rule 1: Must have unit declaration (not "default") + if (program.unit == null || "default".equals(program.unit.name)) { + throw new ParseError( + "Module must start with 'unit' declaration.\n" + + "Add: unit namespace.name\n" + + "Before your class definitions." + ); + } + + // Rule 2: Must contain at least one class + if (program.unit.types == null || program.unit.types.isEmpty()) { + throw new ParseError( + "Module '" + program.unit.name + "' must contain at least one class.\n" + + "Add a class: share ClassName { ... }" + ); + } + + // Rules 3 & 4: Check each class + for (TypeNode type : program.unit.types) { + // Check for direct code in classes + if (type.statements != null && !type.statements.isEmpty()) { + throw new ParseError( + "Modules cannot have direct code outside classes.\n" + + "Move the code inside a method in class '" + type.name + "'." + ); + } + + // Note: Methods in classes are validated by DeclarationParser + } + } + + /** + * Validates a SCRIPT program. + * Rules: + * 1. Can have direct code (statements) + * 2. Cannot have method declarations + * 3. Cannot have class declarations + * 4. Cannot have field declarations + */ + private static void validateScript(ProgramNode program) { + // Scripts are allowed to have imports + // Scripts are parsed into a synthetic class with statements + + // Check each type (should only be the synthetic __Script__ type) + for (TypeNode type : program.unit.types) { + // Rule 2: Cannot have method declarations + if (type.methods != null && !type.methods.isEmpty()) { + throw new ParseError( + "Scripts cannot contain method declarations.\n" + + "Either:\n" + + "1. Remove methods and keep as script, OR\n" + + "2. Remove direct code and make it a method script, OR\n" + + "3. Add 'unit' and classes to make it a module." + ); + } + + // Rule 3: Cannot have real class declarations (synthetic type is OK) + if (!type.name.startsWith("__") && type.name != null) { + // This is a real class name, not allowed in scripts + throw new ParseError( + "Scripts cannot contain class declarations.\n" + + "Found class: " + type.name + "\n" + + "Remove the class or add 'unit' to make it a module." + ); + } + + // Rule 4: Cannot have field declarations + if (type.fields != null && !type.fields.isEmpty()) { + throw new ParseError( + "Scripts cannot contain field declarations.\n" + + "Found fields in type: " + type.name + "\n" + + "Remove field declarations or use variables instead." + ); + } + } + } + + /** + * Validates a METHOD_SCRIPT program. + * Rules: + * 1. Must contain at least one method + * 2. Cannot have direct code outside methods + * 3. Cannot have class declarations + * 4. Cannot have field declarations + * 5. Should have main() method (warning only) + */ +private static void validateMethodScript(ProgramNode program) { + boolean hasMethods = false; + + for (TypeNode type : program.unit.types) { + if (type.methods != null && !type.methods.isEmpty()) { + hasMethods = true; + } + + if (type.statements != null && !type.statements.isEmpty()) { + throw new ParseError( + "Method scripts cannot have direct code outside methods.\n" + + "Place all code inside method declarations." + ); + } + + // Synthetic type name is OK + if (type.name != null && !type.name.startsWith("__")) { + // Check if it's actually a synthetic type created by parser + // If not, it's an error + throw new ParseError( + "Method scripts cannot contain class declarations.\n" + + "Found: " + type.name + "\n" + + "Remove the class or add 'unit' to make it a module." + ); + } + + if (type.fields != null && !type.fields.isEmpty()) { + throw new ParseError( + "Method scripts cannot contain field declarations.\n" + + "Remove field declarations or add 'unit' to make it a module." + ); + } + } + + if (!hasMethods) { + throw new ParseError("Method script must contain at least one method."); + } + + // Warning for missing main() - optional + boolean hasMain = false; + for (TypeNode type : program.unit.types) { + if (type.methods != null) { + for (MethodNode method : type.methods) { + if ("main".equals(method.name)) { + if (method.parameters == null || method.parameters.isEmpty()) { + hasMain = true; + break; + } + } + } + } + if (hasMain) break; + } + + if (!hasMain) { + System.err.println("Warning: Method script should have a 'main()' method"); + } +} +} \ No newline at end of file diff --git a/src/main/java/cod/syntax/Keyword.java b/src/main/java/cod/syntax/Keyword.java new file mode 100644 index 00000000..a38b397f --- /dev/null +++ b/src/main/java/cod/syntax/Keyword.java @@ -0,0 +1,84 @@ +package cod.syntax; + +/** * + * 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, + INPUT, + OUTPUT, + IF, + ELSE, + ELIF, + + // Loop + FOR, + BREAK, + CONTINUE, + + // Used in loop and natural arrays + IN, + TO, + BY, + + // Primitive types + INT, + TEXT, + FLOAT, + BOOL, + + BUILTIN, + + + ALL, + ANY, + + EXIT, + + NULL, + TRUE, + FALSE, + + // For more control and auto wrappings of variables + GET, + SET, + + // Keyword for converting between safe and unsafe states + CONTROL, + + // 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; + + @Override + public String toString() { + return name().toLowerCase(); + } +} \ No newline at end of file diff --git a/src/main/java/cod/syntax/Symbol.java b/src/main/java/cod/syntax/Symbol.java new file mode 100644 index 00000000..d93e795f --- /dev/null +++ b/src/main/java/cod/syntax/Symbol.java @@ -0,0 +1,40 @@ +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, + + MOD, + DOUBLE_COLON, + DOUBLE_COLON_ASSIGN, + TILDE_ARROW, + COLON, + DOT, + COMMA, + LPAREN, + RPAREN, + LBRACE, + RBRACE, + LBRACKET, + RBRACKET, + + PIPE, + QUESTION, + AMPERSAND, + UNDERSCORE +}