commit c9cf356cbd077b61b46dff6d3049c0237a584c0d Author: retoor Date: Tue Dec 2 06:54:32 2025 +0100 Initial commit of dem beast./plot.py diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..60e05ac --- /dev/null +++ b/Makefile @@ -0,0 +1,157 @@ +CC = gcc +CFLAGS = -Wall -Wextra -Werror -std=gnu99 -O3 -march=native -I. +LDFLAGS = -ldl + +LEXER_SOURCES = lexer/lexer_tokenizer.c lexer/lexer_keywords.c lexer/lexer_literals.c +LEXER_OBJECTS = $(LEXER_SOURCES:.c=.o) + +PARSER_SOURCES = parser/parser.c parser/parser_expressions.c parser/parser_statements.c parser/parser_declarations.c parser/parser_printer.c +PARSER_OBJECTS = $(PARSER_SOURCES:.c=.o) + +TYPES_SOURCES = types/types.c +TYPES_OBJECTS = $(TYPES_SOURCES:.c=.o) + +SEMANTIC_SOURCES = semantic/symbol_table.c semantic/semantic.c +SEMANTIC_OBJECTS = $(SEMANTIC_SOURCES:.c=.o) + +IR_SOURCES = ir/ir.c ir/ir_gen.c +IR_OBJECTS = $(IR_SOURCES:.c=.o) + +RUNTIME_SOURCES = runtime/runtime.c runtime/labeltable.c runtime/methodcache.c runtime/fastframe.c runtime/superinst.c +RUNTIME_OBJECTS = $(RUNTIME_SOURCES:.c=.o) + +PHASE0_SOURCES = runtime/fastframe.c runtime/labeltable.c runtime/methodcache.c +PHASE0_OBJECTS = $(PHASE0_SOURCES:.c=.o) + +TEST_LEXER_SOURCES = tests/test_lexer.c +TEST_LEXER_OBJECTS = $(TEST_LEXER_SOURCES:.c=.o) + +TEST_PARSER_SOURCES = tests/test_parser.c +TEST_PARSER_OBJECTS = $(TEST_PARSER_SOURCES:.c=.o) + +TEST_SEMANTIC_SOURCES = tests/test_semantic.c +TEST_SEMANTIC_OBJECTS = $(TEST_SEMANTIC_SOURCES:.c=.o) + +TEST_IR_SOURCES = tests/test_ir.c +TEST_IR_OBJECTS = $(TEST_IR_SOURCES:.c=.o) + +TEST_RUNTIME_SOURCES = tests/test_runtime.c +TEST_RUNTIME_OBJECTS = $(TEST_RUNTIME_SOURCES:.c=.o) + +TEST_STRINGS_SOURCES = tests/test_strings.c +TEST_STRINGS_OBJECTS = $(TEST_STRINGS_SOURCES:.c=.o) + +TEST_ARRAYS_SOURCES = tests/test_arrays.c +TEST_ARRAYS_OBJECTS = $(TEST_ARRAYS_SOURCES:.c=.o) + +TEST_OBJECTS_SOURCES = tests/test_objects.c +TEST_OBJECTS_OBJECTS = $(TEST_OBJECTS_SOURCES:.c=.o) + +TEST_INSTANCE_SOURCES = tests/test_instance_methods.c +TEST_INSTANCE_OBJECTS = $(TEST_INSTANCE_SOURCES:.c=.o) + +TEST_FILEIO_SOURCES = tests/test_fileio.c +TEST_FILEIO_OBJECTS = $(TEST_FILEIO_SOURCES:.c=.o) + +all: test_lexer test_parser test_semantic test_ir test_runtime test_strings test_arrays test_objects test_instance_methods test_fileio + +test_lexer: $(LEXER_OBJECTS) $(TEST_LEXER_OBJECTS) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +test_parser: $(LEXER_OBJECTS) $(PARSER_OBJECTS) $(TEST_PARSER_OBJECTS) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +test_semantic: $(LEXER_OBJECTS) $(PARSER_OBJECTS) $(TYPES_OBJECTS) $(SEMANTIC_OBJECTS) $(TEST_SEMANTIC_OBJECTS) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +test_ir: $(LEXER_OBJECTS) $(PARSER_OBJECTS) $(TYPES_OBJECTS) $(SEMANTIC_OBJECTS) $(IR_OBJECTS) runtime/labeltable.o $(TEST_IR_OBJECTS) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +test_runtime: $(LEXER_OBJECTS) $(PARSER_OBJECTS) $(TYPES_OBJECTS) $(SEMANTIC_OBJECTS) $(IR_OBJECTS) $(RUNTIME_OBJECTS) $(TEST_RUNTIME_OBJECTS) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +test_strings: $(LEXER_OBJECTS) $(PARSER_OBJECTS) $(TYPES_OBJECTS) $(SEMANTIC_OBJECTS) $(IR_OBJECTS) $(RUNTIME_OBJECTS) $(TEST_STRINGS_OBJECTS) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +test_arrays: $(LEXER_OBJECTS) $(PARSER_OBJECTS) $(TYPES_OBJECTS) $(SEMANTIC_OBJECTS) $(IR_OBJECTS) $(RUNTIME_OBJECTS) $(TEST_ARRAYS_OBJECTS) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +test_objects: $(LEXER_OBJECTS) $(PARSER_OBJECTS) $(TYPES_OBJECTS) $(SEMANTIC_OBJECTS) $(IR_OBJECTS) $(RUNTIME_OBJECTS) $(TEST_OBJECTS_OBJECTS) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +test_instance_methods: $(LEXER_OBJECTS) $(PARSER_OBJECTS) $(TYPES_OBJECTS) $(SEMANTIC_OBJECTS) $(IR_OBJECTS) $(RUNTIME_OBJECTS) $(TEST_INSTANCE_OBJECTS) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +test_fileio: $(LEXER_OBJECTS) $(PARSER_OBJECTS) $(TYPES_OBJECTS) $(SEMANTIC_OBJECTS) $(IR_OBJECTS) $(RUNTIME_OBJECTS) $(TEST_FILEIO_OBJECTS) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +TEST_BENCHMARK_SOURCES = tests/test_benchmark.c +TEST_BENCHMARK_OBJECTS = $(TEST_BENCHMARK_SOURCES:.c=.o) + +test_benchmark: $(LEXER_OBJECTS) $(PARSER_OBJECTS) $(TYPES_OBJECTS) $(SEMANTIC_OBJECTS) $(IR_OBJECTS) $(RUNTIME_OBJECTS) $(TEST_BENCHMARK_OBJECTS) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +benchmark: test_benchmark + @echo "" + @echo "========================================" + @echo " RAVA INTERPRETER" + @echo "========================================" + @./test_benchmark + @echo "" + @echo "========================================" + @echo " PYTHON 3" + @echo "========================================" + @python3 examples/benchmark.py + @echo "" + +test_nanbox: tests/test_nanbox.c runtime/nanbox.h + $(CC) $(CFLAGS) -o $@ tests/test_nanbox.c $(LDFLAGS) + +test_fastframe: tests/test_fastframe.c runtime/fastframe.c runtime/nanbox.h runtime/fastframe.h + $(CC) $(CFLAGS) -o $@ tests/test_fastframe.c runtime/fastframe.c $(LDFLAGS) + +test_labeltable: tests/test_labeltable.c runtime/labeltable.c $(IR_OBJECTS) $(TYPES_OBJECTS) $(SEMANTIC_OBJECTS) + $(CC) $(CFLAGS) -o $@ tests/test_labeltable.c runtime/labeltable.c $(IR_OBJECTS) $(TYPES_OBJECTS) $(SEMANTIC_OBJECTS) $(LDFLAGS) + +test_methodcache: tests/test_methodcache.c runtime/methodcache.c runtime/methodcache.h + $(CC) $(CFLAGS) -o $@ tests/test_methodcache.c runtime/methodcache.c $(LDFLAGS) + +test_phase0: test_nanbox test_fastframe test_labeltable test_methodcache + @echo "=== Running Phase 0 Tests ===" + @./test_nanbox + @./test_fastframe + @./test_labeltable + @./test_methodcache + @echo "=== All Phase 0 Tests Passed ===" + +ALL_SOURCES = $(LEXER_SOURCES) $(PARSER_SOURCES) $(TYPES_SOURCES) $(SEMANTIC_SOURCES) $(IR_SOURCES) $(RUNTIME_SOURCES) $(TEST_BENCHMARK_SOURCES) + +test_benchmark_pgo_gen: + rm -f *.gcda */*.gcda + $(CC) -Wall -Wextra -std=gnu99 -O3 -march=native -I. -fprofile-generate -o test_benchmark_pgo $(ALL_SOURCES) $(LDFLAGS) + +pgo_run: test_benchmark_pgo_gen + ./test_benchmark_pgo + ./test_benchmark_pgo + ./test_benchmark_pgo + +test_benchmark_pgo: pgo_run + $(CC) -Wall -Wextra -std=gnu99 -O3 -march=native -I. -fprofile-use -fprofile-correction -o test_benchmark_pgo $(ALL_SOURCES) $(LDFLAGS) + +pgo: test_benchmark_pgo + @echo "=== PGO Benchmark ===" + ./test_benchmark_pgo + +clean: + rm -f $(LEXER_OBJECTS) $(PARSER_OBJECTS) $(TYPES_OBJECTS) $(SEMANTIC_OBJECTS) $(IR_OBJECTS) $(RUNTIME_OBJECTS) \ + $(PHASE0_OBJECTS) \ + $(TEST_LEXER_OBJECTS) $(TEST_PARSER_OBJECTS) $(TEST_SEMANTIC_OBJECTS) $(TEST_IR_OBJECTS) $(TEST_RUNTIME_OBJECTS) \ + $(TEST_STRINGS_OBJECTS) $(TEST_ARRAYS_OBJECTS) $(TEST_OBJECTS_OBJECTS) $(TEST_INSTANCE_OBJECTS) $(TEST_FILEIO_OBJECTS) \ + $(TEST_BENCHMARK_OBJECTS) \ + test_lexer test_parser test_semantic test_ir test_runtime test_strings test_arrays test_objects test_instance_methods test_fileio test_benchmark \ + test_nanbox test_fastframe test_labeltable test_methodcache test_benchmark_pgo *.gcda */*.gcda + +.PHONY: all clean benchmark test_phase0 pgo test_benchmark_pgo_gen pgo_run test_benchmark_pgo diff --git a/examples/01_Fibonacci.java b/examples/01_Fibonacci.java new file mode 100644 index 0000000..b7edb4a --- /dev/null +++ b/examples/01_Fibonacci.java @@ -0,0 +1,23 @@ +public class Fibonacci { + public static int fib(int n) { + if (n <= 1) { + return n; + } + return fib(n - 1) + fib(n - 2); + } + + public static int main() { + System.out.println(fib(0)); + System.out.println(fib(1)); + System.out.println(fib(2)); + System.out.println(fib(3)); + System.out.println(fib(4)); + System.out.println(fib(5)); + System.out.println(fib(6)); + System.out.println(fib(7)); + System.out.println(fib(8)); + System.out.println(fib(9)); + System.out.println(fib(10)); + return 0; + } +} diff --git a/examples/02_PrimeNumbers.java b/examples/02_PrimeNumbers.java new file mode 100644 index 0000000..8a42191 --- /dev/null +++ b/examples/02_PrimeNumbers.java @@ -0,0 +1,31 @@ +public class PrimeNumbers { + public static int isPrime(int n) { + if (n <= 1) { + return 0; + } + if (n <= 3) { + return 1; + } + int i = 2; + while (i * i <= n) { + if (n - (n / i) * i == 0) { + return 0; + } + i = i + 1; + } + return 1; + } + + public static int main() { + int count = 0; + int num = 2; + while (count < 20) { + if (isPrime(num) == 1) { + System.out.println(num); + count = count + 1; + } + num = num + 1; + } + return 0; + } +} diff --git a/examples/03_FactorialVariations.java b/examples/03_FactorialVariations.java new file mode 100644 index 0000000..a2e179f --- /dev/null +++ b/examples/03_FactorialVariations.java @@ -0,0 +1,28 @@ +public class FactorialVariations { + public static int factorialRecursive(int n) { + if (n <= 1) { + return 1; + } + return n * factorialRecursive(n - 1); + } + + public static int factorialIterative(int n) { + int result = 1; + int i = 1; + while (i <= n) { + result = result * i; + i = i + 1; + } + return result; + } + + public static int main() { + System.out.println(factorialRecursive(5)); + System.out.println(factorialIterative(5)); + System.out.println(factorialRecursive(7)); + System.out.println(factorialIterative(7)); + System.out.println(factorialRecursive(10)); + System.out.println(factorialIterative(10)); + return 0; + } +} diff --git a/examples/04_GCD_LCM.java b/examples/04_GCD_LCM.java new file mode 100644 index 0000000..bf061b6 --- /dev/null +++ b/examples/04_GCD_LCM.java @@ -0,0 +1,25 @@ +public class GCD_LCM { + public static int gcd(int a, int b) { + while (b != 0) { + int temp = b; + b = a - (a / b) * b; + a = temp; + } + return a; + } + + public static int lcm(int a, int b) { + return (a * b) / gcd(a, b); + } + + public static int main() { + System.out.println(gcd(48, 18)); + System.out.println(gcd(100, 75)); + System.out.println(gcd(17, 19)); + + System.out.println(lcm(12, 18)); + System.out.println(lcm(15, 25)); + System.out.println(lcm(7, 13)); + return 0; + } +} diff --git a/examples/05_PowerFunction.java b/examples/05_PowerFunction.java new file mode 100644 index 0000000..a3dcb32 --- /dev/null +++ b/examples/05_PowerFunction.java @@ -0,0 +1,26 @@ +public class PowerFunction { + public static int power(int base, int exp) { + if (exp == 0) { + return 1; + } + if (exp == 1) { + return base; + } + int half = power(base, exp / 2); + if (exp - (exp / 2) * 2 == 0) { + return half * half; + } + return base * half * half; + } + + public static int main() { + System.out.println(power(2, 0)); + System.out.println(power(2, 1)); + System.out.println(power(2, 3)); + System.out.println(power(2, 5)); + System.out.println(power(2, 10)); + System.out.println(power(3, 4)); + System.out.println(power(5, 3)); + return 0; + } +} diff --git a/examples/06_ArrayOperations.java b/examples/06_ArrayOperations.java new file mode 100644 index 0000000..74ccd2a --- /dev/null +++ b/examples/06_ArrayOperations.java @@ -0,0 +1,55 @@ +public class ArrayOperations { + public static int sumArray(int[] arr) { + int sum = 0; + int i = 0; + while (i < arr.length) { + sum = sum + arr[i]; + i = i + 1; + } + return sum; + } + + public static int findMax(int[] arr) { + if (arr.length == 0) { + return 0; + } + int max = arr[0]; + int i = 1; + while (i < arr.length) { + if (arr[i] > max) { + max = arr[i]; + } + i = i + 1; + } + return max; + } + + public static int main() { + int[] numbers = new int[5]; + numbers[0] = 10; + numbers[1] = 25; + numbers[2] = 15; + numbers[3] = 30; + numbers[4] = 20; + + System.out.println(sumArray(numbers)); + System.out.println(findMax(numbers)); + + int[] fibonacci = new int[10]; + fibonacci[0] = 0; + fibonacci[1] = 1; + int i = 2; + while (i < 10) { + fibonacci[i] = fibonacci[i - 1] + fibonacci[i - 2]; + i = i + 1; + } + + i = 0; + while (i < 10) { + System.out.println(fibonacci[i]); + i = i + 1; + } + + return 0; + } +} diff --git a/examples/06_BubbleSort.java b/examples/06_BubbleSort.java new file mode 100644 index 0000000..87a36cc --- /dev/null +++ b/examples/06_BubbleSort.java @@ -0,0 +1,64 @@ +public class BubbleSort { + public static int main() { + int n0 = 64; + int n1 = 34; + int n2 = 25; + int n3 = 12; + int n4 = 22; + int n5 = 11; + int n6 = 90; + + int swapped = 1; + int temp = 0; + while (swapped == 1) { + swapped = 0; + + if (n0 > n1) { + temp = n0; + n0 = n1; + n1 = temp; + swapped = 1; + } + if (n1 > n2) { + temp = n1; + n1 = n2; + n2 = temp; + swapped = 1; + } + if (n2 > n3) { + temp = n2; + n2 = n3; + n3 = temp; + swapped = 1; + } + if (n3 > n4) { + temp = n3; + n3 = n4; + n4 = temp; + swapped = 1; + } + if (n4 > n5) { + temp = n4; + n4 = n5; + n5 = temp; + swapped = 1; + } + if (n5 > n6) { + temp = n5; + n5 = n6; + n6 = temp; + swapped = 1; + } + } + + System.out.println(n0); + System.out.println(n1); + System.out.println(n2); + System.out.println(n3); + System.out.println(n4); + System.out.println(n5); + System.out.println(n6); + + return 0; + } +} diff --git a/examples/07_CollatzConjecture.java b/examples/07_CollatzConjecture.java new file mode 100644 index 0000000..60860d7 --- /dev/null +++ b/examples/07_CollatzConjecture.java @@ -0,0 +1,25 @@ +public class CollatzConjecture { + public static int collatzSteps(int n) { + int steps = 0; + while (n != 1) { + if (n - (n / 2) * 2 == 0) { + n = n / 2; + } else { + n = 3 * n + 1; + } + steps = steps + 1; + } + return steps; + } + + public static int main() { + System.out.println(collatzSteps(1)); + System.out.println(collatzSteps(2)); + System.out.println(collatzSteps(3)); + System.out.println(collatzSteps(10)); + System.out.println(collatzSteps(15)); + System.out.println(collatzSteps(27)); + System.out.println(collatzSteps(100)); + return 0; + } +} diff --git a/examples/08_PascalTriangle.java b/examples/08_PascalTriangle.java new file mode 100644 index 0000000..8b45e3c --- /dev/null +++ b/examples/08_PascalTriangle.java @@ -0,0 +1,41 @@ +public class PascalTriangle { + public static int binomialCoeff(int n, int k) { + if (k > n) { + return 0; + } + if (k == 0) { + return 1; + } + if (k == n) { + return 1; + } + if (k > n - k) { + k = n - k; + } + + int result = 1; + int i = 0; + while (i < k) { + result = result * (n - i); + result = result / (i + 1); + i = i + 1; + } + return result; + } + + public static int main() { + System.out.println(binomialCoeff(0, 0)); + System.out.println(binomialCoeff(1, 0)); + System.out.println(binomialCoeff(1, 1)); + System.out.println(binomialCoeff(2, 0)); + System.out.println(binomialCoeff(2, 1)); + System.out.println(binomialCoeff(2, 2)); + System.out.println(binomialCoeff(3, 0)); + System.out.println(binomialCoeff(3, 1)); + System.out.println(binomialCoeff(3, 2)); + System.out.println(binomialCoeff(3, 3)); + System.out.println(binomialCoeff(4, 2)); + System.out.println(binomialCoeff(5, 2)); + return 0; + } +} diff --git a/examples/09_TowerOfHanoi.java b/examples/09_TowerOfHanoi.java new file mode 100644 index 0000000..66dd3fc --- /dev/null +++ b/examples/09_TowerOfHanoi.java @@ -0,0 +1,19 @@ +public class TowerOfHanoi { + public static int hanoi(int n, int from, int to, int aux, int moveCount) { + if (n == 1) { + return moveCount + 1; + } + moveCount = hanoi(n - 1, from, aux, to, moveCount); + moveCount = moveCount + 1; + moveCount = hanoi(n - 1, aux, to, from, moveCount); + return moveCount; + } + + public static int main() { + System.out.println(hanoi(1, 1, 3, 2, 0)); + System.out.println(hanoi(2, 1, 3, 2, 0)); + System.out.println(hanoi(3, 1, 3, 2, 0)); + System.out.println(hanoi(4, 1, 3, 2, 0)); + return 0; + } +} diff --git a/examples/10_AckermannFunction.java b/examples/10_AckermannFunction.java new file mode 100644 index 0000000..6457200 --- /dev/null +++ b/examples/10_AckermannFunction.java @@ -0,0 +1,22 @@ +public class AckermannFunction { + public static int ackermann(int m, int n) { + if (m == 0) { + return n + 1; + } + if (n == 0) { + return ackermann(m - 1, 1); + } + return ackermann(m - 1, ackermann(m, n - 1)); + } + + public static int main() { + System.out.println(ackermann(0, 0)); + System.out.println(ackermann(0, 5)); + System.out.println(ackermann(1, 0)); + System.out.println(ackermann(1, 5)); + System.out.println(ackermann(2, 0)); + System.out.println(ackermann(2, 5)); + System.out.println(ackermann(3, 0)); + return 0; + } +} diff --git a/examples/11_ArrayOperations.java b/examples/11_ArrayOperations.java new file mode 100644 index 0000000..e5287d0 --- /dev/null +++ b/examples/11_ArrayOperations.java @@ -0,0 +1,68 @@ +public class ArrayOperations { + public static int sumArray(int[] arr, int length) { + int sum = 0; + int i = 0; + while (i < length) { + sum = sum + arr[i]; + i = i + 1; + } + return sum; + } + + public static int findMax(int[] arr, int length) { + int max = arr[0]; + int i = 1; + while (i < length) { + if (arr[i] > max) { + max = arr[i]; + } + i = i + 1; + } + return max; + } + + public static int findMin(int[] arr, int length) { + int min = arr[0]; + int i = 1; + while (i < length) { + if (arr[i] < min) { + min = arr[i]; + } + i = i + 1; + } + return min; + } + + public static int main() { + int[] numbers = new int[5]; + numbers[0] = 10; + numbers[1] = 25; + numbers[2] = 5; + numbers[3] = 30; + numbers[4] = 15; + + int sum = sumArray(numbers, 5); + System.out.println(sum); + + int max = findMax(numbers, 5); + System.out.println(max); + + int min = findMin(numbers, 5); + System.out.println(min); + + int[] squares = new int[5]; + int i = 0; + while (i < 5) { + squares[i] = (i + 1) * (i + 1); + i = i + 1; + } + + System.out.println(squares[0]); + System.out.println(squares[1]); + System.out.println(squares[2]); + System.out.println(squares[3]); + System.out.println(squares[4]); + + return 0; + } +} diff --git a/examples/11_SimpleArray.java b/examples/11_SimpleArray.java new file mode 100644 index 0000000..c708390 --- /dev/null +++ b/examples/11_SimpleArray.java @@ -0,0 +1,14 @@ +public class SimpleArray { + public static int main() { + int[] arr = new int[3]; + arr[0] = 10; + arr[1] = 20; + arr[2] = 30; + System.out.println(arr[0]); + System.out.println(arr[1]); + System.out.println(arr[2]); + int sum = arr[0] + arr[1] + arr[2]; + System.out.println(sum); + return 0; + } +} diff --git a/examples/11_TowerOfHanoiStatic.java b/examples/11_TowerOfHanoiStatic.java new file mode 100644 index 0000000..7bcfc54 --- /dev/null +++ b/examples/11_TowerOfHanoiStatic.java @@ -0,0 +1,30 @@ +public class TowerOfHanoiStatic { + public static int moveCount = 0; + + public static int hanoi(int n, int from, int to, int aux) { + if (n == 1) { + moveCount = moveCount + 1; + return moveCount; + } + hanoi(n - 1, from, aux, to); + moveCount = moveCount + 1; + hanoi(n - 1, aux, to, from); + return moveCount; + } + + public static int main() { + moveCount = 0; + System.out.println(hanoi(1, 1, 3, 2)); + + moveCount = 0; + System.out.println(hanoi(2, 1, 3, 2)); + + moveCount = 0; + System.out.println(hanoi(3, 1, 3, 2)); + + moveCount = 0; + System.out.println(hanoi(4, 1, 3, 2)); + + return 0; + } +} diff --git a/examples/12_StringBasics.java b/examples/12_StringBasics.java new file mode 100644 index 0000000..f2caa08 --- /dev/null +++ b/examples/12_StringBasics.java @@ -0,0 +1,44 @@ +public class StringBasics { + public static int main() { + System.out.println("Hello, World!"); + System.out.println("String support in Rava!"); + + String greeting = "Hello"; + String name = "Rava"; + String message = greeting + ", " + name + "!"; + System.out.println(message); + + int x = 42; + System.out.println("The answer is: " + x); + + String result = "Sum: " + 10 + 20; + System.out.println(result); + + String test = "Hello"; + int len = test.length(); + System.out.println(len); + + String longer = "Hello World"; + System.out.println(longer.length()); + + String s1 = "test"; + String s2 = "test"; + String s3 = "other"; + + if (s1.equals(s2)) { + System.out.println(1); + } else { + System.out.println(0); + } + + if (s1.equals(s3)) { + System.out.println(1); + } else { + System.out.println(0); + } + + System.out.println("String tests completed!"); + + return 0; + } +} diff --git a/examples/13_SimpleObject.java b/examples/13_SimpleObject.java new file mode 100644 index 0000000..d2045dd --- /dev/null +++ b/examples/13_SimpleObject.java @@ -0,0 +1,22 @@ +public class SimpleObject { + public static int main() { + Point p = new Point(); + p.x = 10; + p.y = 20; + + System.out.println(p.x); + System.out.println(p.y); + + int sum = p.x + p.y; + System.out.println(sum); + + Point p2 = new Point(); + p2.x = 5; + p2.y = 15; + + int total = p.x + p2.x; + System.out.println(total); + + return 0; + } +} diff --git a/examples/14_InstanceMethods.java b/examples/14_InstanceMethods.java new file mode 100644 index 0000000..ed5fd75 --- /dev/null +++ b/examples/14_InstanceMethods.java @@ -0,0 +1,23 @@ +public class InstanceMethods { + int value; + + InstanceMethods(int v) { + this.value = v; + } + + int getValue() { + return this.value; + } + + void setValue(int v) { + this.value = v; + } + + public static int main() { + InstanceMethods obj = new InstanceMethods(42); + System.out.println(obj.getValue()); + obj.setValue(100); + System.out.println(obj.getValue()); + return 0; + } +} diff --git a/examples/15_FileIO.java b/examples/15_FileIO.java new file mode 100644 index 0000000..bb35504 --- /dev/null +++ b/examples/15_FileIO.java @@ -0,0 +1,32 @@ +public class FileIO { + public static int main() { + String content = "Hello from Rava!"; + boolean written = Files.write("/tmp/rava_test.txt", content); + + if (written) { + System.out.println("File written successfully"); + } + + boolean exists = Files.exists("/tmp/rava_test.txt"); + if (exists) { + System.out.println("File exists"); + } + + String readContent = Files.read("/tmp/rava_test.txt"); + System.out.println(readContent); + + boolean deleted = Files.delete("/tmp/rava_test.txt"); + if (deleted) { + System.out.println("File deleted"); + } + + exists = Files.exists("/tmp/rava_test.txt"); + if (exists) { + System.out.println("File still exists"); + } else { + System.out.println("File is gone"); + } + + return 0; + } +} diff --git a/examples/16_ForLoop.java b/examples/16_ForLoop.java new file mode 100644 index 0000000..812b8bc --- /dev/null +++ b/examples/16_ForLoop.java @@ -0,0 +1,21 @@ +public class ForLoop { + public static int main() { + int sum = 0; + for (int i = 1; i <= 5; i++) { + sum = sum + i; + } + System.out.println(sum); + + int product = 1; + for (int j = 1; j <= 4; j++) { + product = product * j; + } + System.out.println(product); + + for (int k = 0; k < 3; k++) { + System.out.println(k); + } + + return 0; + } +} diff --git a/examples/17_Inheritance.java b/examples/17_Inheritance.java new file mode 100644 index 0000000..5ad5588 --- /dev/null +++ b/examples/17_Inheritance.java @@ -0,0 +1,29 @@ +public class Inheritance { + public static int main() { + Dog dog = new Dog(); + dog.age = 5; + + dog.speak(); + dog.showAge(); + + return 0; + } +} + +class Animal { + int age; + + void speak() { + System.out.println("Animal speaks"); + } + + void showAge() { + System.out.println(this.age); + } +} + +class Dog extends Animal { + void speak() { + System.out.println("Woof!"); + } +} diff --git a/examples/18_ElseIf.java b/examples/18_ElseIf.java new file mode 100644 index 0000000..9fe97a2 --- /dev/null +++ b/examples/18_ElseIf.java @@ -0,0 +1,28 @@ +public class ElseIf { + public static int main() { + int x = 75; + + if (x >= 90) { + System.out.println("A"); + } else if (x >= 80) { + System.out.println("B"); + } else if (x >= 70) { + System.out.println("C"); + } else if (x >= 60) { + System.out.println("D"); + } else { + System.out.println("F"); + } + + int y = 50; + if (y > 100) { + System.out.println("Over 100"); + } else if (y > 50) { + System.out.println("Over 50"); + } else { + System.out.println("50 or less"); + } + + return 0; + } +} diff --git a/examples/19_BreakContinue.java b/examples/19_BreakContinue.java new file mode 100644 index 0000000..e36c13b --- /dev/null +++ b/examples/19_BreakContinue.java @@ -0,0 +1,42 @@ +public class BreakContinue { + public static int main() { + int sum = 0; + for (int i = 1; i <= 10; i++) { + if (i == 6) { + break; + } + sum += i; + } + System.out.println(sum); + + int count = 0; + for (int j = 1; j <= 10; j++) { + if (j % 2 == 0) { + continue; + } + count++; + } + System.out.println(count); + + int x = 10; + x += 5; + System.out.println(x); + + x -= 3; + System.out.println(x); + + x *= 2; + System.out.println(x); + + int k = 0; + while (k < 100) { + k++; + if (k == 5) { + break; + } + } + System.out.println(k); + + return 0; + } +} diff --git a/examples/20_Benchmark.java b/examples/20_Benchmark.java new file mode 100644 index 0000000..ddecdcd --- /dev/null +++ b/examples/20_Benchmark.java @@ -0,0 +1,94 @@ +public class Benchmark { + public static int fibonacci(int n) { + if (n <= 1) { + return n; + } + return fibonacci(n - 1) + fibonacci(n - 2); + } + + public static int fibonacciIterative(int n) { + if (n <= 1) { + return n; + } + int a = 0; + int b = 1; + for (int i = 2; i <= n; i++) { + int temp = a + b; + a = b; + b = temp; + } + return b; + } + + public static int countPrimes(int limit) { + int count = 0; + for (int n = 2; n <= limit; n++) { + boolean isPrime = true; + for (int i = 2; i * i <= n; i++) { + if (n % i == 0) { + isPrime = false; + break; + } + } + if (isPrime) { + count++; + } + } + return count; + } + + public static long sumLoop(int iterations) { + long sum = 0L; + for (int i = 0; i < iterations; i++) { + sum = sum + i; + } + return sum; + } + + public static int main() { + System.out.println("=== Rava Benchmark Suite ==="); + + long start = System.nanoTime(); + int fib30 = fibonacci(30); + long end = System.nanoTime(); + long fibTime = (end - start) / 1000000; + System.out.print("Fibonacci(30) recursive = "); + System.out.println(fib30); + System.out.print("Time: "); + System.out.print(fibTime); + System.out.println(" ms"); + + start = System.nanoTime(); + int fib40iter = fibonacciIterative(40); + end = System.nanoTime(); + long fibIterTime = (end - start) / 1000000; + System.out.print("Fibonacci(40) iterative = "); + System.out.println(fib40iter); + System.out.print("Time: "); + System.out.print(fibIterTime); + System.out.println(" ms"); + + start = System.nanoTime(); + int primes = countPrimes(100000); + end = System.nanoTime(); + long primeTime = (end - start) / 1000000; + System.out.print("Primes up to 100000 = "); + System.out.println(primes); + System.out.print("Time: "); + System.out.print(primeTime); + System.out.println(" ms"); + + start = System.nanoTime(); + long sumResult = sumLoop(10000000); + end = System.nanoTime(); + long sumTime = (end - start) / 1000000; + System.out.print("Sum 0..10000000 = "); + System.out.println(sumResult); + System.out.print("Time: "); + System.out.print(sumTime); + System.out.println(" ms"); + + System.out.println("=== Benchmark Complete ==="); + return 0; + } +} diff --git a/examples/HelloWorld.java b/examples/HelloWorld.java new file mode 100644 index 0000000..975cc6b --- /dev/null +++ b/examples/HelloWorld.java @@ -0,0 +1,10 @@ +public class HelloWorld { + public static int main() { + System.out.println(42); + System.out.println(100); + int x = 10; + int y = 20; + System.out.println(x + y); + return 0; + } +} diff --git a/examples/benchmark.py b/examples/benchmark.py new file mode 100644 index 0000000..404669c --- /dev/null +++ b/examples/benchmark.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +import time + +def fibonacci(n): + if n <= 1: + return n + return fibonacci(n - 1) + fibonacci(n - 2) + +def fibonacci_iterative(n): + if n <= 1: + return n + a, b = 0, 1 + for i in range(2, n + 1): + a, b = b, a + b + return b + +def count_primes(limit): + count = 0 + for n in range(2, limit + 1): + is_prime = True + i = 2 + while i * i <= n: + if n % i == 0: + is_prime = False + break + i += 1 + if is_prime: + count += 1 + return count + +def sum_loop(iterations): + total = 0 + for i in range(iterations): + total += i + return total + +def main(): + print("=== Python Benchmark Suite ===") + + start = time.perf_counter_ns() + fib30 = fibonacci(30) + end = time.perf_counter_ns() + fib_time = (end - start) // 1000000 + print(f"Fibonacci(30) recursive = {fib30}") + print(f"Time: {fib_time} ms") + + start = time.perf_counter_ns() + fib40_iter = fibonacci_iterative(40) + end = time.perf_counter_ns() + fib_iter_time = (end - start) // 1000000 + print(f"Fibonacci(40) iterative = {fib40_iter}") + print(f"Time: {fib_iter_time} ms") + + start = time.perf_counter_ns() + primes = count_primes(100000) + end = time.perf_counter_ns() + prime_time = (end - start) // 1000000 + print(f"Primes up to 100000 = {primes}") + print(f"Time: {prime_time} ms") + + start = time.perf_counter_ns() + sum_result = sum_loop(10000000) + end = time.perf_counter_ns() + sum_time = (end - start) // 1000000 + print(f"Sum 0..10000000 = {sum_result}") + print(f"Time: {sum_time} ms") + + print("=== Benchmark Complete ===") + +if __name__ == "__main__": + main() diff --git a/ir/ir.c b/ir/ir.c new file mode 100644 index 0000000..da1d92e --- /dev/null +++ b/ir/ir.c @@ -0,0 +1,200 @@ +#define _POSIX_C_SOURCE 200809L +#include "ir.h" +#include "../runtime/labeltable.h" +#include +#include +#include + +RavaInstructionList_t* rava_instruction_list_create() { + RavaInstructionList_t *list = calloc(1, sizeof(RavaInstructionList_t)); + list->capacity = 16; + list->instructions = malloc(sizeof(RavaInstruction_t) * list->capacity); + list->count = 0; + list->next_label_id = 1; + return list; +} + +void rava_instruction_list_destroy(RavaInstructionList_t *list) { + if (!list) return; + free(list->instructions); + free(list); +} + +void rava_instruction_list_add(RavaInstructionList_t *list, RavaInstruction_t instr) { + if (list->count >= list->capacity) { + list->capacity *= 2; + list->instructions = realloc(list->instructions, + sizeof(RavaInstruction_t) * list->capacity); + } + list->instructions[list->count++] = instr; +} + +int rava_instruction_list_new_label(RavaInstructionList_t *list) { + return list->next_label_id++; +} + +RavaMethod_t* rava_method_create(const char *name, RavaType_t *return_type) { + RavaMethod_t *method = calloc(1, sizeof(RavaMethod_t)); + method->name = strdup(name); + method->return_type = return_type; + method->param_types = NULL; + method->param_count = 0; + method->local_count = 0; + method->instructions = rava_instruction_list_create(); + method->label_table = NULL; + return method; +} + +void rava_method_destroy(RavaMethod_t *method) { + if (!method) return; + free(method->name); + free(method->param_types); + rava_instruction_list_destroy(method->instructions); + if (method->label_table) { + rava_labeltable_destroy(method->label_table); + } + free(method); +} + +RavaClass_t* rava_class_create(const char *name) { + RavaClass_t *class = calloc(1, sizeof(RavaClass_t)); + class->name = strdup(name); + class->superclass = NULL; + class->methods = NULL; + class->method_count = 0; + class->method_capacity = 0; + return class; +} + +void rava_class_destroy(RavaClass_t *class) { + if (!class) return; + free(class->name); + free(class->superclass); + for (size_t i = 0; i < class->method_count; i++) { + rava_method_destroy(class->methods[i]); + } + free(class->methods); + free(class); +} + +void rava_class_add_method(RavaClass_t *class, RavaMethod_t *method) { + if (class->method_count >= class->method_capacity) { + size_t new_capacity = class->method_capacity == 0 ? 4 : class->method_capacity * 2; + class->methods = realloc(class->methods, sizeof(RavaMethod_t*) * new_capacity); + class->method_capacity = new_capacity; + } + class->methods[class->method_count++] = method; +} + +RavaProgram_t* rava_program_create() { + RavaProgram_t *program = calloc(1, sizeof(RavaProgram_t)); + program->classes = NULL; + program->class_count = 0; + program->class_capacity = 0; + return program; +} + +void rava_program_destroy(RavaProgram_t *program) { + if (!program) return; + for (size_t i = 0; i < program->class_count; i++) { + rava_class_destroy(program->classes[i]); + } + free(program->classes); + free(program); +} + +void rava_program_add_class(RavaProgram_t *program, RavaClass_t *class) { + if (program->class_count >= program->class_capacity) { + size_t new_capacity = program->class_capacity == 0 ? 4 : program->class_capacity * 2; + program->classes = realloc(program->classes, sizeof(RavaClass_t*) * new_capacity); + program->class_capacity = new_capacity; + } + program->classes[program->class_count++] = class; +} + +static const char* _rava_opcode_name(RavaOpCode_e opcode) { + switch (opcode) { + case RAVA_OP_NOP: return "NOP"; + case RAVA_OP_LOAD_CONST: return "LOAD_CONST"; + case RAVA_OP_LOAD_LOCAL: return "LOAD_LOCAL"; + case RAVA_OP_STORE_LOCAL: return "STORE_LOCAL"; + case RAVA_OP_ADD: return "ADD"; + case RAVA_OP_SUB: return "SUB"; + case RAVA_OP_MUL: return "MUL"; + case RAVA_OP_DIV: return "DIV"; + case RAVA_OP_MOD: return "MOD"; + case RAVA_OP_NEG: return "NEG"; + case RAVA_OP_EQ: return "EQ"; + case RAVA_OP_NE: return "NE"; + case RAVA_OP_LT: return "LT"; + case RAVA_OP_LE: return "LE"; + case RAVA_OP_GT: return "GT"; + case RAVA_OP_GE: return "GE"; + case RAVA_OP_JUMP: return "JUMP"; + case RAVA_OP_JUMP_IF_TRUE: return "JUMP_IF_TRUE"; + case RAVA_OP_JUMP_IF_FALSE: return "JUMP_IF_FALSE"; + case RAVA_OP_LABEL: return "LABEL"; + case RAVA_OP_CALL: return "CALL"; + case RAVA_OP_CALL_STATIC: return "CALL_STATIC"; + case RAVA_OP_RETURN: return "RETURN"; + case RAVA_OP_RETURN_VOID: return "RETURN_VOID"; + case RAVA_OP_NEW: return "NEW"; + case RAVA_OP_NEW_ARRAY: return "NEW_ARRAY"; + case RAVA_OP_ARRAY_LENGTH: return "ARRAY_LENGTH"; + case RAVA_OP_LOAD_ARRAY: return "LOAD_ARRAY"; + case RAVA_OP_STORE_ARRAY: return "STORE_ARRAY"; + case RAVA_OP_POP: return "POP"; + case RAVA_OP_DUP: return "DUP"; + case RAVA_OP_PRINT: return "PRINT"; + case RAVA_OP_PRINTLN: return "PRINTLN"; + default: return "UNKNOWN"; + } +} + +void rava_ir_print(RavaProgram_t *program) { + if (!program) return; + + printf("IR Program (%zu classes)\n", program->class_count); + printf("================================================================================\n\n"); + + for (size_t i = 0; i < program->class_count; i++) { + RavaClass_t *class = program->classes[i]; + printf("Class: %s\n", class->name); + + for (size_t j = 0; j < class->method_count; j++) { + RavaMethod_t *method = class->methods[j]; + printf(" Method: %s (locals: %d)\n", method->name, method->local_count); + + for (size_t k = 0; k < method->instructions->count; k++) { + RavaInstruction_t *instr = &method->instructions->instructions[k]; + printf(" %04zu %-15s", k, _rava_opcode_name(instr->opcode)); + + switch (instr->opcode) { + case RAVA_OP_LOAD_CONST: + printf(" %lld", (long long)instr->operand.int_value); + break; + case RAVA_OP_LOAD_LOCAL: + case RAVA_OP_STORE_LOCAL: + printf(" [%d]", instr->operand.var.index); + break; + case RAVA_OP_LABEL: + case RAVA_OP_JUMP: + case RAVA_OP_JUMP_IF_TRUE: + case RAVA_OP_JUMP_IF_FALSE: + printf(" L%d", instr->operand.label_id); + break; + case RAVA_OP_CALL_STATIC: + printf(" %s.%s", + instr->operand.call.class_name, + instr->operand.call.method_name); + break; + default: + break; + } + + printf("\n"); + } + printf("\n"); + } + } +} diff --git a/ir/ir.h b/ir/ir.h new file mode 100644 index 0000000..174ac9d --- /dev/null +++ b/ir/ir.h @@ -0,0 +1,197 @@ +#ifndef RAVA_IR_H +#define RAVA_IR_H + +#include "../types/types.h" +#include +#include +#include + +typedef enum { + RAVA_OP_NOP, + + RAVA_OP_LOAD_CONST, + RAVA_OP_LOAD_LONG, + RAVA_OP_LOAD_DOUBLE, + RAVA_OP_LOAD_STRING, + RAVA_OP_LOAD_LOCAL, + RAVA_OP_LOAD_FIELD, + RAVA_OP_LOAD_STATIC, + RAVA_OP_LOAD_ARRAY, + + RAVA_OP_STORE_LOCAL, + RAVA_OP_STORE_FIELD, + RAVA_OP_STORE_STATIC, + RAVA_OP_STORE_ARRAY, + + RAVA_OP_ADD, + RAVA_OP_SUB, + RAVA_OP_MUL, + RAVA_OP_DIV, + RAVA_OP_MOD, + RAVA_OP_NEG, + + RAVA_OP_AND, + RAVA_OP_OR, + RAVA_OP_XOR, + RAVA_OP_NOT, + RAVA_OP_SHL, + RAVA_OP_SHR, + RAVA_OP_USHR, + + RAVA_OP_EQ, + RAVA_OP_NE, + RAVA_OP_LT, + RAVA_OP_LE, + RAVA_OP_GT, + RAVA_OP_GE, + + RAVA_OP_JUMP, + RAVA_OP_JUMP_IF_TRUE, + RAVA_OP_JUMP_IF_FALSE, + RAVA_OP_LABEL, + + RAVA_OP_CALL, + RAVA_OP_CALL_STATIC, + RAVA_OP_CALL_VIRTUAL, + RAVA_OP_CALL_NATIVE, + RAVA_OP_RETURN, + RAVA_OP_RETURN_VOID, + + RAVA_OP_NEW, + RAVA_OP_NEW_ARRAY, + RAVA_OP_ARRAY_LENGTH, + RAVA_OP_GET_FIELD, + RAVA_OP_PUT_FIELD, + + RAVA_OP_CAST, + RAVA_OP_INSTANCEOF, + + RAVA_OP_THROW, + RAVA_OP_POP, + RAVA_OP_DUP, + + RAVA_OP_PRINT, + RAVA_OP_PRINTLN, + + RAVA_OP_STRING_LENGTH, + RAVA_OP_STRING_CHARAT, + RAVA_OP_STRING_SUBSTRING, + RAVA_OP_STRING_EQUALS, + RAVA_OP_STRING_COMPARETO, + + RAVA_OP_LOAD_THIS, + + RAVA_OP_FILE_READ, + RAVA_OP_FILE_WRITE, + RAVA_OP_FILE_EXISTS, + RAVA_OP_FILE_DELETE, + + RAVA_OP_CURRENT_TIME_MILLIS, + RAVA_OP_NANO_TIME, + + RAVA_OP_INC_LOCAL, + RAVA_OP_DEC_LOCAL, + RAVA_OP_LOAD_LOCAL_CONST_ADD, + RAVA_OP_LOAD_LOCAL_CONST_LT_JUMPFALSE, + RAVA_OP_LOAD_LOCAL_CONST_LE_JUMPFALSE, + RAVA_OP_LOAD_TWO_LOCALS, + RAVA_OP_ADD_LOCAL_TO_LOCAL, + RAVA_OP_LOAD_LOCAL_LT_LOCAL_JUMPFALSE +} RavaOpCode_e; + +typedef union { + int64_t int_value; + double float_value; + char *string_value; + int label_id; + struct { + int index; + RavaType_t *type; + } var; + struct { + char *class_name; + char *method_name; + int arg_count; + } call; + struct { + char *class_name; + char *field_name; + } field; + struct { + int local_index; + int64_t const_value; + int label_id; + } super; + struct { + int index1; + int index2; + } two_locals; + struct { + int dest_index; + int src_index; + } add_locals; + struct { + int local1; + int local2; + int label_id; + } cmp_locals; +} RavaOperand_u; + +typedef struct { + RavaOpCode_e opcode; + RavaOperand_u operand; + int line; +} RavaInstruction_t; + +typedef struct { + RavaInstruction_t *instructions; + size_t count; + size_t capacity; + int next_label_id; +} RavaInstructionList_t; + +struct LabelTable_s; + +typedef struct { + char *name; + RavaType_t *return_type; + RavaType_t **param_types; + size_t param_count; + int local_count; + RavaInstructionList_t *instructions; + struct LabelTable_s *label_table; +} RavaMethod_t; + +typedef struct { + char *name; + char *superclass; + RavaMethod_t **methods; + size_t method_count; + size_t method_capacity; +} RavaClass_t; + +typedef struct { + RavaClass_t **classes; + size_t class_count; + size_t class_capacity; +} RavaProgram_t; + +RavaInstructionList_t* rava_instruction_list_create(); +void rava_instruction_list_destroy(RavaInstructionList_t *list); +void rava_instruction_list_add(RavaInstructionList_t *list, RavaInstruction_t instr); +int rava_instruction_list_new_label(RavaInstructionList_t *list); + +RavaMethod_t* rava_method_create(const char *name, RavaType_t *return_type); +void rava_method_destroy(RavaMethod_t *method); + +RavaClass_t* rava_class_create(const char *name); +void rava_class_destroy(RavaClass_t *class); +void rava_class_add_method(RavaClass_t *class, RavaMethod_t *method); + +RavaProgram_t* rava_program_create(); +void rava_program_destroy(RavaProgram_t *program); +void rava_program_add_class(RavaProgram_t *program, RavaClass_t *class); + +void rava_ir_print(RavaProgram_t *program); + +#endif diff --git a/ir/ir_gen.c b/ir/ir_gen.c new file mode 100644 index 0000000..e829c21 --- /dev/null +++ b/ir/ir_gen.c @@ -0,0 +1,669 @@ +#define _POSIX_C_SOURCE 200809L +#include "ir_gen.h" +#include +#include +#include + +static void _rava_ir_gen_expression(RavaIRGenerator_t *gen, RavaASTNode_t *expr); +static void _rava_ir_gen_statement(RavaIRGenerator_t *gen, RavaASTNode_t *stmt); + +static void _rava_ir_clear_locals(RavaIRGenerator_t *gen) { + RavaLocalVar_t *current = gen->locals; + while (current) { + RavaLocalVar_t *next = current->next; + free(current->name); + free(current); + current = next; + } + gen->locals = NULL; +} + +static void _rava_ir_add_local(RavaIRGenerator_t *gen, const char *name, size_t index) { + RavaLocalVar_t *local = calloc(1, sizeof(RavaLocalVar_t)); + local->name = strdup(name); + local->index = index; + local->next = gen->locals; + gen->locals = local; +} + +static int _rava_ir_find_local(RavaIRGenerator_t *gen, const char *name) { + for (RavaLocalVar_t *local = gen->locals; local; local = local->next) { + if (strcmp(local->name, name) == 0) { + return (int)local->index; + } + } + return -1; +} + +RavaIRGenerator_t* rava_ir_generator_create(RavaSemanticAnalyzer_t *analyzer) { + RavaIRGenerator_t *gen = calloc(1, sizeof(RavaIRGenerator_t)); + gen->program = rava_program_create(); + gen->current_class = NULL; + gen->current_method = NULL; + gen->analyzer = analyzer; + gen->next_local = 0; + gen->locals = NULL; + gen->loop_context = NULL; + gen->error_message = NULL; + gen->had_error = false; + return gen; +} + +void rava_ir_generator_destroy(RavaIRGenerator_t *generator) { + if (!generator) return; + _rava_ir_clear_locals(generator); + free(generator->error_message); + free(generator); +} + +static void _rava_ir_emit(RavaIRGenerator_t *gen, RavaInstruction_t instr) { + if (!gen->current_method) return; + rava_instruction_list_add(gen->current_method->instructions, instr); +} + +static void _rava_ir_gen_binary_expr(RavaIRGenerator_t *gen, RavaASTNode_t *expr) { + _rava_ir_gen_expression(gen, expr->data.binary.left); + _rava_ir_gen_expression(gen, expr->data.binary.right); + + RavaInstruction_t instr = {0}; + instr.line = expr->line; + + switch (expr->data.binary.op) { + case RAVA_BINOP_ADD: instr.opcode = RAVA_OP_ADD; break; + case RAVA_BINOP_SUB: instr.opcode = RAVA_OP_SUB; break; + case RAVA_BINOP_MUL: instr.opcode = RAVA_OP_MUL; break; + case RAVA_BINOP_DIV: instr.opcode = RAVA_OP_DIV; break; + case RAVA_BINOP_MOD: instr.opcode = RAVA_OP_MOD; break; + case RAVA_BINOP_EQ: instr.opcode = RAVA_OP_EQ; break; + case RAVA_BINOP_NE: instr.opcode = RAVA_OP_NE; break; + case RAVA_BINOP_LT: instr.opcode = RAVA_OP_LT; break; + case RAVA_BINOP_LE: instr.opcode = RAVA_OP_LE; break; + case RAVA_BINOP_GT: instr.opcode = RAVA_OP_GT; break; + case RAVA_BINOP_GE: instr.opcode = RAVA_OP_GE; break; + case RAVA_BINOP_AND: instr.opcode = RAVA_OP_AND; break; + case RAVA_BINOP_OR: instr.opcode = RAVA_OP_OR; break; + default: instr.opcode = RAVA_OP_NOP; break; + } + + _rava_ir_emit(gen, instr); +} + +static void _rava_ir_gen_expression(RavaIRGenerator_t *gen, RavaASTNode_t *expr) { + if (!expr) return; + + RavaInstruction_t instr = {0}; + instr.line = expr->line; + + switch (expr->type) { + case RAVA_AST_LITERAL_EXPR: + if (expr->data.literal.literal_type == RAVA_TOKEN_LITERAL_STRING) { + instr.opcode = RAVA_OP_LOAD_STRING; + instr.operand.string_value = strdup(expr->data.literal.value.string_value); + _rava_ir_emit(gen, instr); + } else if (expr->data.literal.literal_type == RAVA_TOKEN_LITERAL_LONG) { + instr.opcode = RAVA_OP_LOAD_LONG; + instr.operand.int_value = expr->data.literal.value.int_value; + _rava_ir_emit(gen, instr); + } else if (expr->data.literal.literal_type == RAVA_TOKEN_LITERAL_DOUBLE) { + instr.opcode = RAVA_OP_LOAD_DOUBLE; + instr.operand.float_value = expr->data.literal.value.float_value; + _rava_ir_emit(gen, instr); + } else { + instr.opcode = RAVA_OP_LOAD_CONST; + instr.operand.int_value = expr->data.literal.value.int_value; + _rava_ir_emit(gen, instr); + } + break; + + case RAVA_AST_IDENTIFIER_EXPR: { + int local_index = _rava_ir_find_local(gen, expr->data.identifier.name); + if (local_index >= 0) { + instr.opcode = RAVA_OP_LOAD_LOCAL; + instr.operand.var.index = (size_t)local_index; + _rava_ir_emit(gen, instr); + } + break; + } + + case RAVA_AST_BINARY_EXPR: + _rava_ir_gen_binary_expr(gen, expr); + break; + + case RAVA_AST_UNARY_EXPR: + if (expr->data.unary.op == RAVA_UNOP_MINUS) { + _rava_ir_gen_expression(gen, expr->data.unary.operand); + instr.opcode = RAVA_OP_NEG; + _rava_ir_emit(gen, instr); + } else if (expr->data.unary.op == RAVA_UNOP_NOT) { + _rava_ir_gen_expression(gen, expr->data.unary.operand); + instr.opcode = RAVA_OP_NOT; + _rava_ir_emit(gen, instr); + } else if (expr->data.unary.op == RAVA_UNOP_PREINC || expr->data.unary.op == RAVA_UNOP_PREDEC) { + if (expr->data.unary.operand->type == RAVA_AST_IDENTIFIER_EXPR) { + int local_index = _rava_ir_find_local(gen, expr->data.unary.operand->data.identifier.name); + if (local_index >= 0) { + instr.opcode = RAVA_OP_LOAD_LOCAL; + instr.operand.var.index = (size_t)local_index; + _rava_ir_emit(gen, instr); + instr.opcode = RAVA_OP_LOAD_CONST; + instr.operand.int_value = 1; + _rava_ir_emit(gen, instr); + instr.opcode = (expr->data.unary.op == RAVA_UNOP_PREINC) ? RAVA_OP_ADD : RAVA_OP_SUB; + _rava_ir_emit(gen, instr); + instr.opcode = RAVA_OP_DUP; + _rava_ir_emit(gen, instr); + instr.opcode = RAVA_OP_STORE_LOCAL; + instr.operand.var.index = (size_t)local_index; + _rava_ir_emit(gen, instr); + } + } + } else if (expr->data.unary.op == RAVA_UNOP_POSTINC || expr->data.unary.op == RAVA_UNOP_POSTDEC) { + if (expr->data.unary.operand->type == RAVA_AST_IDENTIFIER_EXPR) { + int local_index = _rava_ir_find_local(gen, expr->data.unary.operand->data.identifier.name); + if (local_index >= 0) { + instr.opcode = RAVA_OP_LOAD_LOCAL; + instr.operand.var.index = (size_t)local_index; + _rava_ir_emit(gen, instr); + instr.opcode = RAVA_OP_LOAD_LOCAL; + instr.operand.var.index = (size_t)local_index; + _rava_ir_emit(gen, instr); + instr.opcode = RAVA_OP_LOAD_CONST; + instr.operand.int_value = 1; + _rava_ir_emit(gen, instr); + instr.opcode = (expr->data.unary.op == RAVA_UNOP_POSTINC) ? RAVA_OP_ADD : RAVA_OP_SUB; + _rava_ir_emit(gen, instr); + instr.opcode = RAVA_OP_STORE_LOCAL; + instr.operand.var.index = (size_t)local_index; + _rava_ir_emit(gen, instr); + } + } + } else { + _rava_ir_gen_expression(gen, expr->data.unary.operand); + } + break; + + case RAVA_AST_ASSIGN_EXPR: + if (expr->data.assign.target->type == RAVA_AST_ARRAY_ACCESS_EXPR) { + _rava_ir_gen_expression(gen, expr->data.assign.target->data.array_access.array); + _rava_ir_gen_expression(gen, expr->data.assign.target->data.array_access.index); + _rava_ir_gen_expression(gen, expr->data.assign.value); + instr.opcode = RAVA_OP_STORE_ARRAY; + _rava_ir_emit(gen, instr); + } else if (expr->data.assign.target->type == RAVA_AST_MEMBER_ACCESS_EXPR) { + _rava_ir_gen_expression(gen, expr->data.assign.target->data.member_access.object); + _rava_ir_gen_expression(gen, expr->data.assign.value); + instr.opcode = RAVA_OP_PUT_FIELD; + instr.operand.field.field_name = strdup(expr->data.assign.target->data.member_access.member); + _rava_ir_emit(gen, instr); + } else { + _rava_ir_gen_expression(gen, expr->data.assign.value); + + if (expr->data.assign.target->type == RAVA_AST_IDENTIFIER_EXPR) { + int local_index = _rava_ir_find_local(gen, expr->data.assign.target->data.identifier.name); + if (local_index >= 0) { + instr.opcode = RAVA_OP_STORE_LOCAL; + instr.operand.var.index = (size_t)local_index; + _rava_ir_emit(gen, instr); + } + } + } + break; + + case RAVA_AST_MEMBER_ACCESS_EXPR: + _rava_ir_gen_expression(gen, expr->data.member_access.object); + instr.opcode = RAVA_OP_GET_FIELD; + instr.operand.field.field_name = strdup(expr->data.member_access.member); + _rava_ir_emit(gen, instr); + break; + + case RAVA_AST_ARRAY_ACCESS_EXPR: + _rava_ir_gen_expression(gen, expr->data.array_access.array); + _rava_ir_gen_expression(gen, expr->data.array_access.index); + instr.opcode = RAVA_OP_LOAD_ARRAY; + _rava_ir_emit(gen, instr); + break; + + case RAVA_AST_NEW_EXPR: + if (expr->data.new_expr.type && expr->data.new_expr.type->data.type.is_array) { + if (expr->data.new_expr.arguments_count > 0) { + _rava_ir_gen_expression(gen, expr->data.new_expr.arguments[0]); + } + instr.opcode = RAVA_OP_NEW_ARRAY; + _rava_ir_emit(gen, instr); + } else if (expr->data.new_expr.type) { + instr.opcode = RAVA_OP_NEW; + instr.operand.call.class_name = strdup(expr->data.new_expr.type->data.type.type_name); + instr.operand.call.arg_count = expr->data.new_expr.arguments_count; + _rava_ir_emit(gen, instr); + instr.opcode = RAVA_OP_DUP; + _rava_ir_emit(gen, instr); + for (size_t i = 0; i < expr->data.new_expr.arguments_count; i++) { + _rava_ir_gen_expression(gen, expr->data.new_expr.arguments[i]); + } + instr.opcode = RAVA_OP_CALL_VIRTUAL; + instr.operand.call.method_name = strdup(""); + _rava_ir_emit(gen, instr); + } + break; + + case RAVA_AST_CALL_EXPR: { + bool is_println = false; + bool is_print = false; + bool is_string_length = false; + bool is_string_charat = false; + bool is_string_equals = false; + bool is_file_read = false; + bool is_file_write = false; + bool is_file_exists = false; + bool is_file_delete = false; + bool is_current_time_millis = false; + bool is_nano_time = false; + RavaASTNode_t *string_object = NULL; + + if (expr->data.call.callee->type == RAVA_AST_MEMBER_ACCESS_EXPR) { + RavaASTNode_t *member = expr->data.call.callee; + if (member->data.member_access.object->type == RAVA_AST_IDENTIFIER_EXPR && + strcmp(member->data.member_access.object->data.identifier.name, "System") == 0) { + if (strcmp(member->data.member_access.member, "currentTimeMillis") == 0) { + is_current_time_millis = true; + } else if (strcmp(member->data.member_access.member, "nanoTime") == 0) { + is_nano_time = true; + } + } + if (member->data.member_access.object->type == RAVA_AST_MEMBER_ACCESS_EXPR) { + RavaASTNode_t *system_out = member->data.member_access.object; + if (system_out->data.member_access.object->type == RAVA_AST_IDENTIFIER_EXPR && + strcmp(system_out->data.member_access.object->data.identifier.name, "System") == 0 && + strcmp(system_out->data.member_access.member, "out") == 0) { + if (strcmp(member->data.member_access.member, "println") == 0) { + is_println = true; + } else if (strcmp(member->data.member_access.member, "print") == 0) { + is_print = true; + } + } + } + if (member->data.member_access.object->type == RAVA_AST_IDENTIFIER_EXPR && + strcmp(member->data.member_access.object->data.identifier.name, "Files") == 0) { + if (strcmp(member->data.member_access.member, "read") == 0 && expr->data.call.arguments_count == 1) { + is_file_read = true; + } else if (strcmp(member->data.member_access.member, "write") == 0 && expr->data.call.arguments_count == 2) { + is_file_write = true; + } else if (strcmp(member->data.member_access.member, "exists") == 0 && expr->data.call.arguments_count == 1) { + is_file_exists = true; + } else if (strcmp(member->data.member_access.member, "delete") == 0 && expr->data.call.arguments_count == 1) { + is_file_delete = true; + } + } + if (strcmp(member->data.member_access.member, "length") == 0 && expr->data.call.arguments_count == 0) { + is_string_length = true; + string_object = member->data.member_access.object; + } else if (strcmp(member->data.member_access.member, "charAt") == 0 && expr->data.call.arguments_count == 1) { + is_string_charat = true; + string_object = member->data.member_access.object; + } else if (strcmp(member->data.member_access.member, "equals") == 0 && expr->data.call.arguments_count == 1) { + is_string_equals = true; + string_object = member->data.member_access.object; + } + } + + if (is_current_time_millis) { + instr.opcode = RAVA_OP_CURRENT_TIME_MILLIS; + _rava_ir_emit(gen, instr); + } else if (is_nano_time) { + instr.opcode = RAVA_OP_NANO_TIME; + _rava_ir_emit(gen, instr); + } else if (is_file_read) { + _rava_ir_gen_expression(gen, expr->data.call.arguments[0]); + instr.opcode = RAVA_OP_FILE_READ; + _rava_ir_emit(gen, instr); + } else if (is_file_write) { + _rava_ir_gen_expression(gen, expr->data.call.arguments[0]); + _rava_ir_gen_expression(gen, expr->data.call.arguments[1]); + instr.opcode = RAVA_OP_FILE_WRITE; + _rava_ir_emit(gen, instr); + } else if (is_file_exists) { + _rava_ir_gen_expression(gen, expr->data.call.arguments[0]); + instr.opcode = RAVA_OP_FILE_EXISTS; + _rava_ir_emit(gen, instr); + } else if (is_file_delete) { + _rava_ir_gen_expression(gen, expr->data.call.arguments[0]); + instr.opcode = RAVA_OP_FILE_DELETE; + _rava_ir_emit(gen, instr); + } else if (is_string_length) { + _rava_ir_gen_expression(gen, string_object); + instr.opcode = RAVA_OP_STRING_LENGTH; + _rava_ir_emit(gen, instr); + } else if (is_string_charat) { + _rava_ir_gen_expression(gen, string_object); + _rava_ir_gen_expression(gen, expr->data.call.arguments[0]); + instr.opcode = RAVA_OP_STRING_CHARAT; + _rava_ir_emit(gen, instr); + } else if (is_string_equals) { + _rava_ir_gen_expression(gen, string_object); + _rava_ir_gen_expression(gen, expr->data.call.arguments[0]); + instr.opcode = RAVA_OP_STRING_EQUALS; + _rava_ir_emit(gen, instr); + } else if (is_println || is_print) { + for (size_t i = 0; i < expr->data.call.arguments_count; i++) { + _rava_ir_gen_expression(gen, expr->data.call.arguments[i]); + } + if (is_println) { + instr.opcode = RAVA_OP_PRINTLN; + } else { + instr.opcode = RAVA_OP_PRINT; + } + _rava_ir_emit(gen, instr); + } else if (expr->data.call.callee->type == RAVA_AST_MEMBER_ACCESS_EXPR) { + RavaASTNode_t *member = expr->data.call.callee; + _rava_ir_gen_expression(gen, member->data.member_access.object); + for (size_t i = 0; i < expr->data.call.arguments_count; i++) { + _rava_ir_gen_expression(gen, expr->data.call.arguments[i]); + } + instr.opcode = RAVA_OP_CALL_VIRTUAL; + instr.operand.call.class_name = strdup(gen->current_class->name); + instr.operand.call.method_name = strdup(member->data.member_access.member); + instr.operand.call.arg_count = expr->data.call.arguments_count; + _rava_ir_emit(gen, instr); + } else { + for (size_t i = 0; i < expr->data.call.arguments_count; i++) { + _rava_ir_gen_expression(gen, expr->data.call.arguments[i]); + } + instr.opcode = RAVA_OP_CALL_STATIC; + if (expr->data.call.callee->type == RAVA_AST_IDENTIFIER_EXPR) { + instr.operand.call.class_name = strdup(gen->current_class->name); + instr.operand.call.method_name = strdup(expr->data.call.callee->data.identifier.name); + instr.operand.call.arg_count = expr->data.call.arguments_count; + } + _rava_ir_emit(gen, instr); + } + break; + } + + case RAVA_AST_THIS_EXPR: + instr.opcode = RAVA_OP_LOAD_THIS; + _rava_ir_emit(gen, instr); + break; + + default: + break; + } +} + +static void _rava_ir_gen_statement(RavaIRGenerator_t *gen, RavaASTNode_t *stmt) { + if (!stmt) return; + + RavaInstruction_t instr = {0}; + instr.line = stmt->line; + + switch (stmt->type) { + case RAVA_AST_BLOCK_STMT: + for (size_t i = 0; i < stmt->children_count; i++) { + _rava_ir_gen_statement(gen, stmt->children[i]); + } + break; + + case RAVA_AST_IF_STMT: { + _rava_ir_gen_expression(gen, stmt->data.if_stmt.condition); + + int else_label = rava_instruction_list_new_label(gen->current_method->instructions); + int end_label = rava_instruction_list_new_label(gen->current_method->instructions); + + instr.opcode = RAVA_OP_JUMP_IF_FALSE; + instr.operand.label_id = else_label; + _rava_ir_emit(gen, instr); + + _rava_ir_gen_statement(gen, stmt->data.if_stmt.then_stmt); + + instr.opcode = RAVA_OP_JUMP; + instr.operand.label_id = end_label; + _rava_ir_emit(gen, instr); + + instr.opcode = RAVA_OP_LABEL; + instr.operand.label_id = else_label; + _rava_ir_emit(gen, instr); + + if (stmt->data.if_stmt.else_stmt) { + _rava_ir_gen_statement(gen, stmt->data.if_stmt.else_stmt); + } + + instr.opcode = RAVA_OP_LABEL; + instr.operand.label_id = end_label; + _rava_ir_emit(gen, instr); + break; + } + + case RAVA_AST_WHILE_STMT: { + int start_label = rava_instruction_list_new_label(gen->current_method->instructions); + int end_label = rava_instruction_list_new_label(gen->current_method->instructions); + + RavaLoopContext_t loop_ctx = { .break_label = end_label, .continue_label = start_label, .parent = gen->loop_context }; + gen->loop_context = &loop_ctx; + + instr.opcode = RAVA_OP_LABEL; + instr.operand.label_id = start_label; + _rava_ir_emit(gen, instr); + + _rava_ir_gen_expression(gen, stmt->data.while_stmt.condition); + + instr.opcode = RAVA_OP_JUMP_IF_FALSE; + instr.operand.label_id = end_label; + _rava_ir_emit(gen, instr); + + _rava_ir_gen_statement(gen, stmt->data.while_stmt.body); + + instr.opcode = RAVA_OP_JUMP; + instr.operand.label_id = start_label; + _rava_ir_emit(gen, instr); + + instr.opcode = RAVA_OP_LABEL; + instr.operand.label_id = end_label; + _rava_ir_emit(gen, instr); + + gen->loop_context = loop_ctx.parent; + break; + } + + case RAVA_AST_FOR_STMT: { + if (stmt->data.for_stmt.init) { + if (stmt->data.for_stmt.init->type == RAVA_AST_VAR_DECL) { + _rava_ir_gen_statement(gen, stmt->data.for_stmt.init); + } else { + _rava_ir_gen_expression(gen, stmt->data.for_stmt.init); + instr.opcode = RAVA_OP_POP; + _rava_ir_emit(gen, instr); + } + } + + int start_label = rava_instruction_list_new_label(gen->current_method->instructions); + int continue_label = rava_instruction_list_new_label(gen->current_method->instructions); + int end_label = rava_instruction_list_new_label(gen->current_method->instructions); + + RavaLoopContext_t loop_ctx = { .break_label = end_label, .continue_label = continue_label, .parent = gen->loop_context }; + gen->loop_context = &loop_ctx; + + instr.opcode = RAVA_OP_LABEL; + instr.operand.label_id = start_label; + _rava_ir_emit(gen, instr); + + if (stmt->data.for_stmt.condition) { + _rava_ir_gen_expression(gen, stmt->data.for_stmt.condition); + instr.opcode = RAVA_OP_JUMP_IF_FALSE; + instr.operand.label_id = end_label; + _rava_ir_emit(gen, instr); + } + + _rava_ir_gen_statement(gen, stmt->data.for_stmt.body); + + instr.opcode = RAVA_OP_LABEL; + instr.operand.label_id = continue_label; + _rava_ir_emit(gen, instr); + + if (stmt->data.for_stmt.update) { + _rava_ir_gen_expression(gen, stmt->data.for_stmt.update); + instr.opcode = RAVA_OP_POP; + _rava_ir_emit(gen, instr); + } + + instr.opcode = RAVA_OP_JUMP; + instr.operand.label_id = start_label; + _rava_ir_emit(gen, instr); + + instr.opcode = RAVA_OP_LABEL; + instr.operand.label_id = end_label; + _rava_ir_emit(gen, instr); + + gen->loop_context = loop_ctx.parent; + break; + } + + case RAVA_AST_RETURN_STMT: + if (stmt->data.return_stmt.value) { + _rava_ir_gen_expression(gen, stmt->data.return_stmt.value); + instr.opcode = RAVA_OP_RETURN; + } else { + instr.opcode = RAVA_OP_RETURN_VOID; + } + _rava_ir_emit(gen, instr); + break; + + case RAVA_AST_EXPR_STMT: + if (stmt->children_count > 0) { + _rava_ir_gen_expression(gen, stmt->children[0]); + instr.opcode = RAVA_OP_POP; + _rava_ir_emit(gen, instr); + } + break; + + case RAVA_AST_VAR_DECL: { + _rava_ir_add_local(gen, stmt->data.var_decl.name, gen->next_local); + gen->next_local++; + if (stmt->data.var_decl.initializer) { + _rava_ir_gen_expression(gen, stmt->data.var_decl.initializer); + instr.opcode = RAVA_OP_STORE_LOCAL; + instr.operand.var.index = gen->next_local - 1; + _rava_ir_emit(gen, instr); + } + break; + } + + case RAVA_AST_BREAK_STMT: + if (gen->loop_context) { + instr.opcode = RAVA_OP_JUMP; + instr.operand.label_id = gen->loop_context->break_label; + _rava_ir_emit(gen, instr); + } + break; + + case RAVA_AST_CONTINUE_STMT: + if (gen->loop_context) { + instr.opcode = RAVA_OP_JUMP; + instr.operand.label_id = gen->loop_context->continue_label; + _rava_ir_emit(gen, instr); + } + break; + + default: + break; + } +} + +static void _rava_ir_gen_method(RavaIRGenerator_t *gen, RavaASTNode_t *method_node) { + if (method_node->type != RAVA_AST_METHOD_DECL) return; + + RavaType_t *return_type = rava_type_from_name( + method_node->data.method_decl.return_type->data.type.type_name); + + RavaMethod_t *method = rava_method_create(method_node->data.method_decl.name, return_type); + gen->current_method = method; + gen->next_local = 0; + _rava_ir_clear_locals(gen); + + rava_symbol_table_enter_scope(gen->analyzer->symbol_table, method_node->data.method_decl.name); + + for (size_t i = 0; i < method_node->children_count; i++) { + RavaASTNode_t *child = method_node->children[i]; + + if (child->type == RAVA_AST_PARAM_DECL) { + _rava_ir_add_local(gen, child->data.var_decl.name, gen->next_local); + gen->next_local++; + } else if (child->type == RAVA_AST_BLOCK_STMT) { + _rava_ir_gen_statement(gen, child); + } + } + + rava_symbol_table_exit_scope(gen->analyzer->symbol_table); + + method->local_count = gen->next_local; + rava_class_add_method(gen->current_class, method); + gen->current_method = NULL; +} + +static void _rava_ir_gen_constructor(RavaIRGenerator_t *gen, RavaASTNode_t *ctor_node) { + if (ctor_node->type != RAVA_AST_CONSTRUCTOR_DECL) return; + + RavaType_t *void_type = rava_type_from_name("void"); + RavaMethod_t *method = rava_method_create("", void_type); + gen->current_method = method; + gen->next_local = 0; + _rava_ir_clear_locals(gen); + + for (size_t i = 0; i < ctor_node->children_count; i++) { + RavaASTNode_t *child = ctor_node->children[i]; + if (child->type == RAVA_AST_PARAM_DECL) { + _rava_ir_add_local(gen, child->data.var_decl.name, gen->next_local); + gen->next_local++; + } else if (child->type == RAVA_AST_BLOCK_STMT) { + _rava_ir_gen_statement(gen, child); + } + } + + RavaInstruction_t ret_instr = {0}; + ret_instr.opcode = RAVA_OP_RETURN_VOID; + _rava_ir_emit(gen, ret_instr); + + method->local_count = gen->next_local; + rava_class_add_method(gen->current_class, method); + gen->current_method = NULL; +} + +static void _rava_ir_gen_class(RavaIRGenerator_t *gen, RavaASTNode_t *class_node) { + if (class_node->type != RAVA_AST_CLASS_DECL) return; + + RavaClass_t *class = rava_class_create(class_node->data.class_decl.name); + if (class_node->data.class_decl.superclass) { + class->superclass = strdup(class_node->data.class_decl.superclass); + } + gen->current_class = class; + + rava_symbol_table_enter_scope(gen->analyzer->symbol_table, class_node->data.class_decl.name); + + for (size_t i = 0; i < class_node->children_count; i++) { + RavaASTNode_t *child = class_node->children[i]; + + if (child->type == RAVA_AST_METHOD_DECL) { + _rava_ir_gen_method(gen, child); + } else if (child->type == RAVA_AST_CONSTRUCTOR_DECL) { + _rava_ir_gen_constructor(gen, child); + } + } + + rava_symbol_table_exit_scope(gen->analyzer->symbol_table); + + rava_program_add_class(gen->program, class); + gen->current_class = NULL; +} + +RavaProgram_t* rava_ir_generate(RavaIRGenerator_t *generator, RavaASTNode_t *root) { + if (!generator || !root) return NULL; + + if (root->type == RAVA_AST_COMPILATION_UNIT) { + for (size_t i = 0; i < root->children_count; i++) { + RavaASTNode_t *child = root->children[i]; + if (child->type == RAVA_AST_CLASS_DECL) { + _rava_ir_gen_class(generator, child); + } + } + } + + return generator->program; +} diff --git a/ir/ir_gen.h b/ir/ir_gen.h new file mode 100644 index 0000000..983bfdf --- /dev/null +++ b/ir/ir_gen.h @@ -0,0 +1,37 @@ +#ifndef RAVA_IR_GEN_H +#define RAVA_IR_GEN_H + +#include "../parser/parser.h" +#include "../semantic/semantic.h" +#include "ir.h" + +typedef struct RavaLocalVar_t { + char *name; + size_t index; + struct RavaLocalVar_t *next; +} RavaLocalVar_t; + +typedef struct RavaLoopContext_t { + int break_label; + int continue_label; + struct RavaLoopContext_t *parent; +} RavaLoopContext_t; + +typedef struct { + RavaProgram_t *program; + RavaClass_t *current_class; + RavaMethod_t *current_method; + RavaSemanticAnalyzer_t *analyzer; + int next_local; + RavaLocalVar_t *locals; + RavaLoopContext_t *loop_context; + char *error_message; + bool had_error; +} RavaIRGenerator_t; + +RavaIRGenerator_t* rava_ir_generator_create(RavaSemanticAnalyzer_t *analyzer); +void rava_ir_generator_destroy(RavaIRGenerator_t *generator); + +RavaProgram_t* rava_ir_generate(RavaIRGenerator_t *generator, RavaASTNode_t *root); + +#endif diff --git a/lexer/lexer.h b/lexer/lexer.h new file mode 100644 index 0000000..f12e601 --- /dev/null +++ b/lexer/lexer.h @@ -0,0 +1,160 @@ +#ifndef RAVA_LEXER_H +#define RAVA_LEXER_H + +#include +#include +#include + +typedef enum { + RAVA_TOKEN_EOF, + + RAVA_TOKEN_KEYWORD_ABSTRACT, + RAVA_TOKEN_KEYWORD_ASSERT, + RAVA_TOKEN_KEYWORD_BOOLEAN, + RAVA_TOKEN_KEYWORD_BREAK, + RAVA_TOKEN_KEYWORD_BYTE, + RAVA_TOKEN_KEYWORD_CASE, + RAVA_TOKEN_KEYWORD_CATCH, + RAVA_TOKEN_KEYWORD_CHAR, + RAVA_TOKEN_KEYWORD_CLASS, + RAVA_TOKEN_KEYWORD_CONST, + RAVA_TOKEN_KEYWORD_CONTINUE, + RAVA_TOKEN_KEYWORD_DEFAULT, + RAVA_TOKEN_KEYWORD_DO, + RAVA_TOKEN_KEYWORD_DOUBLE, + RAVA_TOKEN_KEYWORD_ELSE, + RAVA_TOKEN_KEYWORD_ENUM, + RAVA_TOKEN_KEYWORD_EXTENDS, + RAVA_TOKEN_KEYWORD_FINAL, + RAVA_TOKEN_KEYWORD_FINALLY, + RAVA_TOKEN_KEYWORD_FLOAT, + RAVA_TOKEN_KEYWORD_FOR, + RAVA_TOKEN_KEYWORD_GOTO, + RAVA_TOKEN_KEYWORD_IF, + RAVA_TOKEN_KEYWORD_IMPLEMENTS, + RAVA_TOKEN_KEYWORD_IMPORT, + RAVA_TOKEN_KEYWORD_INSTANCEOF, + RAVA_TOKEN_KEYWORD_INT, + RAVA_TOKEN_KEYWORD_INTERFACE, + RAVA_TOKEN_KEYWORD_LONG, + RAVA_TOKEN_KEYWORD_NATIVE, + RAVA_TOKEN_KEYWORD_NEW, + RAVA_TOKEN_KEYWORD_PACKAGE, + RAVA_TOKEN_KEYWORD_PRIVATE, + RAVA_TOKEN_KEYWORD_PROTECTED, + RAVA_TOKEN_KEYWORD_PUBLIC, + RAVA_TOKEN_KEYWORD_RETURN, + RAVA_TOKEN_KEYWORD_SHORT, + RAVA_TOKEN_KEYWORD_STATIC, + RAVA_TOKEN_KEYWORD_STRICTFP, + RAVA_TOKEN_KEYWORD_SUPER, + RAVA_TOKEN_KEYWORD_SWITCH, + RAVA_TOKEN_KEYWORD_SYNCHRONIZED, + RAVA_TOKEN_KEYWORD_THIS, + RAVA_TOKEN_KEYWORD_THROW, + RAVA_TOKEN_KEYWORD_THROWS, + RAVA_TOKEN_KEYWORD_TRANSIENT, + RAVA_TOKEN_KEYWORD_TRY, + RAVA_TOKEN_KEYWORD_VOID, + RAVA_TOKEN_KEYWORD_VOLATILE, + RAVA_TOKEN_KEYWORD_WHILE, + + RAVA_TOKEN_LITERAL_TRUE, + RAVA_TOKEN_LITERAL_FALSE, + RAVA_TOKEN_LITERAL_NULL, + RAVA_TOKEN_LITERAL_INTEGER, + RAVA_TOKEN_LITERAL_LONG, + RAVA_TOKEN_LITERAL_FLOAT, + RAVA_TOKEN_LITERAL_DOUBLE, + RAVA_TOKEN_LITERAL_CHARACTER, + RAVA_TOKEN_LITERAL_STRING, + + RAVA_TOKEN_IDENTIFIER, + + RAVA_TOKEN_LPAREN, + RAVA_TOKEN_RPAREN, + RAVA_TOKEN_LBRACE, + RAVA_TOKEN_RBRACE, + RAVA_TOKEN_LBRACKET, + RAVA_TOKEN_RBRACKET, + RAVA_TOKEN_SEMICOLON, + RAVA_TOKEN_COMMA, + RAVA_TOKEN_DOT, + RAVA_TOKEN_ELLIPSIS, + RAVA_TOKEN_AT, + RAVA_TOKEN_COLONCOLON, + + RAVA_TOKEN_ASSIGN, + RAVA_TOKEN_GT, + RAVA_TOKEN_LT, + RAVA_TOKEN_BANG, + RAVA_TOKEN_TILDE, + RAVA_TOKEN_QUESTION, + RAVA_TOKEN_COLON, + RAVA_TOKEN_ARROW, + RAVA_TOKEN_EQUAL, + RAVA_TOKEN_GE, + RAVA_TOKEN_LE, + RAVA_TOKEN_NE, + RAVA_TOKEN_AND, + RAVA_TOKEN_OR, + RAVA_TOKEN_INC, + RAVA_TOKEN_DEC, + RAVA_TOKEN_PLUS, + RAVA_TOKEN_MINUS, + RAVA_TOKEN_STAR, + RAVA_TOKEN_SLASH, + RAVA_TOKEN_AMP, + RAVA_TOKEN_PIPE, + RAVA_TOKEN_CARET, + RAVA_TOKEN_PERCENT, + RAVA_TOKEN_LSHIFT, + RAVA_TOKEN_RSHIFT, + RAVA_TOKEN_URSHIFT, + RAVA_TOKEN_PLUSASSIGN, + RAVA_TOKEN_MINUSASSIGN, + RAVA_TOKEN_STARASSIGN, + RAVA_TOKEN_SLASHASSIGN, + RAVA_TOKEN_ANDASSIGN, + RAVA_TOKEN_ORASSIGN, + RAVA_TOKEN_CARETASSIGN, + RAVA_TOKEN_PERCENTASSIGN, + RAVA_TOKEN_LSHIFTASSIGN, + RAVA_TOKEN_RSHIFTASSIGN, + RAVA_TOKEN_URSHIFTASSIGN, + + RAVA_TOKEN_ERROR +} RavaTokenType_e; + +typedef union { + int64_t int_value; + double float_value; + char *string_value; + char char_value; +} RavaLiteralValue_u; + +typedef struct { + RavaTokenType_e type; + char *lexeme; + int line; + int column; + RavaLiteralValue_u value; +} RavaToken_t; + +typedef struct { + const char *source; + size_t source_length; + size_t current; + size_t start; + int line; + int column; + int start_column; + char *error_message; +} RavaLexer_t; + +RavaLexer_t* rava_lexer_create(const char *source); +void rava_lexer_destroy(RavaLexer_t *lexer); +RavaToken_t* rava_lexer_next_token(RavaLexer_t *lexer); +void rava_token_destroy(RavaToken_t *token); + +#endif diff --git a/lexer/lexer_keywords.c b/lexer/lexer_keywords.c new file mode 100644 index 0000000..a4a0d48 --- /dev/null +++ b/lexer/lexer_keywords.c @@ -0,0 +1,74 @@ +#include "lexer.h" +#include + +typedef struct { + const char *keyword; + RavaTokenType_e type; +} RavaKeyword_t; + +static const RavaKeyword_t KEYWORDS[] = { + {"abstract", RAVA_TOKEN_KEYWORD_ABSTRACT}, + {"assert", RAVA_TOKEN_KEYWORD_ASSERT}, + {"boolean", RAVA_TOKEN_KEYWORD_BOOLEAN}, + {"break", RAVA_TOKEN_KEYWORD_BREAK}, + {"byte", RAVA_TOKEN_KEYWORD_BYTE}, + {"case", RAVA_TOKEN_KEYWORD_CASE}, + {"catch", RAVA_TOKEN_KEYWORD_CATCH}, + {"char", RAVA_TOKEN_KEYWORD_CHAR}, + {"class", RAVA_TOKEN_KEYWORD_CLASS}, + {"const", RAVA_TOKEN_KEYWORD_CONST}, + {"continue", RAVA_TOKEN_KEYWORD_CONTINUE}, + {"default", RAVA_TOKEN_KEYWORD_DEFAULT}, + {"do", RAVA_TOKEN_KEYWORD_DO}, + {"double", RAVA_TOKEN_KEYWORD_DOUBLE}, + {"else", RAVA_TOKEN_KEYWORD_ELSE}, + {"enum", RAVA_TOKEN_KEYWORD_ENUM}, + {"extends", RAVA_TOKEN_KEYWORD_EXTENDS}, + {"false", RAVA_TOKEN_LITERAL_FALSE}, + {"final", RAVA_TOKEN_KEYWORD_FINAL}, + {"finally", RAVA_TOKEN_KEYWORD_FINALLY}, + {"float", RAVA_TOKEN_KEYWORD_FLOAT}, + {"for", RAVA_TOKEN_KEYWORD_FOR}, + {"goto", RAVA_TOKEN_KEYWORD_GOTO}, + {"if", RAVA_TOKEN_KEYWORD_IF}, + {"implements", RAVA_TOKEN_KEYWORD_IMPLEMENTS}, + {"import", RAVA_TOKEN_KEYWORD_IMPORT}, + {"instanceof", RAVA_TOKEN_KEYWORD_INSTANCEOF}, + {"int", RAVA_TOKEN_KEYWORD_INT}, + {"interface", RAVA_TOKEN_KEYWORD_INTERFACE}, + {"long", RAVA_TOKEN_KEYWORD_LONG}, + {"native", RAVA_TOKEN_KEYWORD_NATIVE}, + {"new", RAVA_TOKEN_KEYWORD_NEW}, + {"null", RAVA_TOKEN_LITERAL_NULL}, + {"package", RAVA_TOKEN_KEYWORD_PACKAGE}, + {"private", RAVA_TOKEN_KEYWORD_PRIVATE}, + {"protected", RAVA_TOKEN_KEYWORD_PROTECTED}, + {"public", RAVA_TOKEN_KEYWORD_PUBLIC}, + {"return", RAVA_TOKEN_KEYWORD_RETURN}, + {"short", RAVA_TOKEN_KEYWORD_SHORT}, + {"static", RAVA_TOKEN_KEYWORD_STATIC}, + {"strictfp", RAVA_TOKEN_KEYWORD_STRICTFP}, + {"super", RAVA_TOKEN_KEYWORD_SUPER}, + {"switch", RAVA_TOKEN_KEYWORD_SWITCH}, + {"synchronized", RAVA_TOKEN_KEYWORD_SYNCHRONIZED}, + {"this", RAVA_TOKEN_KEYWORD_THIS}, + {"throw", RAVA_TOKEN_KEYWORD_THROW}, + {"throws", RAVA_TOKEN_KEYWORD_THROWS}, + {"transient", RAVA_TOKEN_KEYWORD_TRANSIENT}, + {"true", RAVA_TOKEN_LITERAL_TRUE}, + {"try", RAVA_TOKEN_KEYWORD_TRY}, + {"void", RAVA_TOKEN_KEYWORD_VOID}, + {"volatile", RAVA_TOKEN_KEYWORD_VOLATILE}, + {"while", RAVA_TOKEN_KEYWORD_WHILE}, +}; + +static const size_t KEYWORDS_COUNT = sizeof(KEYWORDS) / sizeof(KEYWORDS[0]); + +RavaTokenType_e rava_lexer_lookup_keyword(const char *identifier) { + for (size_t i = 0; i < KEYWORDS_COUNT; i++) { + if (strcmp(identifier, KEYWORDS[i].keyword) == 0) { + return KEYWORDS[i].type; + } + } + return RAVA_TOKEN_IDENTIFIER; +} diff --git a/lexer/lexer_literals.c b/lexer/lexer_literals.c new file mode 100644 index 0000000..1447315 --- /dev/null +++ b/lexer/lexer_literals.c @@ -0,0 +1,216 @@ +#define _POSIX_C_SOURCE 200809L +#include "lexer.h" +#include +#include +#include +#include + +extern RavaToken_t* _rava_lexer_create_token(RavaLexer_t *lexer, RavaTokenType_e type); +extern char _rava_lexer_peek(RavaLexer_t *lexer); +extern char _rava_lexer_peek_next(RavaLexer_t *lexer); +extern char _rava_lexer_advance(RavaLexer_t *lexer); +extern bool _rava_lexer_is_at_end(RavaLexer_t *lexer); + +static char _rava_unescape_char(char c) { + switch (c) { + case 'n': return '\n'; + case 't': return '\t'; + case 'r': return '\r'; + case 'b': return '\b'; + case 'f': return '\f'; + case '\\': return '\\'; + case '\'': return '\''; + case '"': return '"'; + default: return c; + } +} + +RavaToken_t* rava_lexer_parse_string(RavaLexer_t *lexer) { + size_t capacity = 128; + size_t length = 0; + char *str = malloc(capacity); + + while (!_rava_lexer_is_at_end(lexer) && _rava_lexer_peek(lexer) != '"') { + char c = _rava_lexer_advance(lexer); + + if (c == '\\') { + if (_rava_lexer_is_at_end(lexer)) { + free(str); + lexer->error_message = strdup("Unterminated string escape"); + return _rava_lexer_create_token(lexer, RAVA_TOKEN_ERROR); + } + + char next = _rava_lexer_advance(lexer); + if (next == 'u') { + int codepoint = 0; + for (int i = 0; i < 4; i++) { + if (_rava_lexer_is_at_end(lexer) || !isxdigit(_rava_lexer_peek(lexer))) { + free(str); + lexer->error_message = strdup("Invalid Unicode escape"); + return _rava_lexer_create_token(lexer, RAVA_TOKEN_ERROR); + } + char hex = _rava_lexer_advance(lexer); + codepoint = codepoint * 16 + (isdigit(hex) ? hex - '0' : tolower(hex) - 'a' + 10); + } + if (codepoint < 128) { + c = (char)codepoint; + } else { + c = '?'; + } + } else if (next >= '0' && next <= '7') { + int octal = next - '0'; + if (!_rava_lexer_is_at_end(lexer) && _rava_lexer_peek(lexer) >= '0' && _rava_lexer_peek(lexer) <= '7') { + octal = octal * 8 + (_rava_lexer_advance(lexer) - '0'); + if (!_rava_lexer_is_at_end(lexer) && _rava_lexer_peek(lexer) >= '0' && _rava_lexer_peek(lexer) <= '7') { + octal = octal * 8 + (_rava_lexer_advance(lexer) - '0'); + } + } + c = (char)octal; + } else { + c = _rava_unescape_char(next); + } + } + + if (length + 1 >= capacity) { + capacity *= 2; + str = realloc(str, capacity); + } + str[length++] = c; + } + + if (_rava_lexer_is_at_end(lexer)) { + free(str); + lexer->error_message = strdup("Unterminated string"); + return _rava_lexer_create_token(lexer, RAVA_TOKEN_ERROR); + } + + _rava_lexer_advance(lexer); + + str[length] = '\0'; + RavaToken_t *token = _rava_lexer_create_token(lexer, RAVA_TOKEN_LITERAL_STRING); + token->value.string_value = str; + return token; +} + +RavaToken_t* rava_lexer_parse_character(RavaLexer_t *lexer) { + if (_rava_lexer_is_at_end(lexer)) { + lexer->error_message = strdup("Unterminated character literal"); + return _rava_lexer_create_token(lexer, RAVA_TOKEN_ERROR); + } + + char c = _rava_lexer_advance(lexer); + + if (c == '\\') { + if (_rava_lexer_is_at_end(lexer)) { + lexer->error_message = strdup("Unterminated character escape"); + return _rava_lexer_create_token(lexer, RAVA_TOKEN_ERROR); + } + char next = _rava_lexer_advance(lexer); + c = _rava_unescape_char(next); + } + + if (_rava_lexer_is_at_end(lexer) || _rava_lexer_peek(lexer) != '\'') { + lexer->error_message = strdup("Unterminated character literal"); + return _rava_lexer_create_token(lexer, RAVA_TOKEN_ERROR); + } + + _rava_lexer_advance(lexer); + + RavaToken_t *token = _rava_lexer_create_token(lexer, RAVA_TOKEN_LITERAL_CHARACTER); + token->value.char_value = c; + return token; +} + +RavaToken_t* rava_lexer_parse_number(RavaLexer_t *lexer) { + bool is_hex = false; + bool is_octal = false; + bool is_binary = false; + bool is_float = false; + bool has_exponent = false; + + char first_char = lexer->source[lexer->start]; + if (first_char == '0' && !_rava_lexer_is_at_end(lexer)) { + char next = _rava_lexer_peek(lexer); + if (next == 'x' || next == 'X') { + is_hex = true; + _rava_lexer_advance(lexer); + _rava_lexer_advance(lexer); + } else if (next == 'b' || next == 'B') { + is_binary = true; + _rava_lexer_advance(lexer); + _rava_lexer_advance(lexer); + } else if (next >= '0' && next <= '7') { + is_octal = true; + _rava_lexer_advance(lexer); + } + } + + while (!_rava_lexer_is_at_end(lexer)) { + char c = _rava_lexer_peek(lexer); + + if (is_hex && isxdigit(c)) { + _rava_lexer_advance(lexer); + } else if (is_binary && (c == '0' || c == '1')) { + _rava_lexer_advance(lexer); + } else if (is_octal && c >= '0' && c <= '7') { + _rava_lexer_advance(lexer); + } else if (!is_hex && !is_binary && !is_octal && isdigit(c)) { + _rava_lexer_advance(lexer); + } else if (c == '.' && !is_float && !is_hex && !is_binary && !is_octal) { + is_float = true; + _rava_lexer_advance(lexer); + } else if ((c == 'e' || c == 'E') && !has_exponent && !is_hex && !is_binary && !is_octal) { + is_float = true; + has_exponent = true; + _rava_lexer_advance(lexer); + if (!_rava_lexer_is_at_end(lexer) && (_rava_lexer_peek(lexer) == '+' || _rava_lexer_peek(lexer) == '-')) { + _rava_lexer_advance(lexer); + } + } else if (c == '_') { + _rava_lexer_advance(lexer); + } else { + break; + } + } + + RavaTokenType_e type = RAVA_TOKEN_LITERAL_INTEGER; + + if (!_rava_lexer_is_at_end(lexer)) { + char suffix = _rava_lexer_peek(lexer); + if (suffix == 'L' || suffix == 'l') { + type = RAVA_TOKEN_LITERAL_LONG; + _rava_lexer_advance(lexer); + } else if (suffix == 'F' || suffix == 'f') { + is_float = true; + type = RAVA_TOKEN_LITERAL_FLOAT; + _rava_lexer_advance(lexer); + } else if (suffix == 'D' || suffix == 'd') { + is_float = true; + type = RAVA_TOKEN_LITERAL_DOUBLE; + _rava_lexer_advance(lexer); + } + } + + if (is_float && type == RAVA_TOKEN_LITERAL_INTEGER) { + type = RAVA_TOKEN_LITERAL_DOUBLE; + } + + RavaToken_t *token = _rava_lexer_create_token(lexer, type); + + if (is_float) { + token->value.float_value = strtod(token->lexeme, NULL); + } else { + char *endptr; + if (is_hex) { + token->value.int_value = strtoll(token->lexeme, &endptr, 16); + } else if (is_octal) { + token->value.int_value = strtoll(token->lexeme, &endptr, 8); + } else if (is_binary) { + token->value.int_value = strtoll(token->lexeme + 2, &endptr, 2); + } else { + token->value.int_value = strtoll(token->lexeme, &endptr, 10); + } + } + + return token; +} diff --git a/lexer/lexer_tokenizer.c b/lexer/lexer_tokenizer.c new file mode 100644 index 0000000..bd2a319 --- /dev/null +++ b/lexer/lexer_tokenizer.c @@ -0,0 +1,319 @@ +#include "lexer.h" +#include +#include +#include +#include + +extern RavaTokenType_e rava_lexer_lookup_keyword(const char *identifier); +extern RavaToken_t* rava_lexer_parse_string(RavaLexer_t *lexer); +extern RavaToken_t* rava_lexer_parse_character(RavaLexer_t *lexer); +extern RavaToken_t* rava_lexer_parse_number(RavaLexer_t *lexer); + +RavaLexer_t* rava_lexer_create(const char *source) { + RavaLexer_t *lexer = malloc(sizeof(RavaLexer_t)); + lexer->source = source; + lexer->source_length = strlen(source); + lexer->current = 0; + lexer->start = 0; + lexer->line = 1; + lexer->column = 1; + lexer->start_column = 1; + lexer->error_message = NULL; + return lexer; +} + +void rava_lexer_destroy(RavaLexer_t *lexer) { + if (lexer) { + if (lexer->error_message) { + free(lexer->error_message); + } + free(lexer); + } +} + +void rava_token_destroy(RavaToken_t *token) { + if (token) { + if (token->lexeme) { + free(token->lexeme); + } + if (token->type == RAVA_TOKEN_LITERAL_STRING && token->value.string_value) { + free(token->value.string_value); + } + free(token); + } +} + +bool _rava_lexer_is_at_end(RavaLexer_t *lexer) { + return lexer->current >= lexer->source_length; +} + +char _rava_lexer_peek(RavaLexer_t *lexer) { + if (_rava_lexer_is_at_end(lexer)) return '\0'; + return lexer->source[lexer->current]; +} + +char _rava_lexer_peek_next(RavaLexer_t *lexer) { + if (lexer->current + 1 >= lexer->source_length) return '\0'; + return lexer->source[lexer->current + 1]; +} + +char _rava_lexer_advance(RavaLexer_t *lexer) { + if (_rava_lexer_is_at_end(lexer)) return '\0'; + char c = lexer->source[lexer->current++]; + if (c == '\n') { + lexer->line++; + lexer->column = 1; + } else { + lexer->column++; + } + return c; +} + +bool _rava_lexer_match(RavaLexer_t *lexer, char expected) { + if (_rava_lexer_is_at_end(lexer)) return false; + if (lexer->source[lexer->current] != expected) return false; + lexer->current++; + lexer->column++; + return true; +} + +void _rava_lexer_skip_whitespace(RavaLexer_t *lexer) { + while (!_rava_lexer_is_at_end(lexer)) { + char c = _rava_lexer_peek(lexer); + switch (c) { + case ' ': + case '\t': + case '\r': + case '\n': + case '\f': + _rava_lexer_advance(lexer); + break; + case '/': + if (_rava_lexer_peek_next(lexer) == '/') { + while (!_rava_lexer_is_at_end(lexer) && _rava_lexer_peek(lexer) != '\n') { + _rava_lexer_advance(lexer); + } + } else if (_rava_lexer_peek_next(lexer) == '*') { + _rava_lexer_advance(lexer); + _rava_lexer_advance(lexer); + while (!_rava_lexer_is_at_end(lexer)) { + if (_rava_lexer_peek(lexer) == '*' && _rava_lexer_peek_next(lexer) == '/') { + _rava_lexer_advance(lexer); + _rava_lexer_advance(lexer); + break; + } + _rava_lexer_advance(lexer); + } + } else { + return; + } + break; + default: + return; + } + } +} + +RavaToken_t* _rava_lexer_create_token(RavaLexer_t *lexer, RavaTokenType_e type) { + RavaToken_t *token = malloc(sizeof(RavaToken_t)); + token->type = type; + size_t length = lexer->current - lexer->start; + token->lexeme = malloc(length + 1); + memcpy(token->lexeme, lexer->source + lexer->start, length); + token->lexeme[length] = '\0'; + token->line = lexer->line; + token->column = lexer->start_column; + token->value.int_value = 0; + return token; +} + +static bool _rava_is_alpha(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$'; +} + +static bool _rava_is_alnum(char c) { + return _rava_is_alpha(c) || (c >= '0' && c <= '9'); +} + +static RavaToken_t* _rava_lexer_identifier(RavaLexer_t *lexer) { + while (_rava_is_alnum(_rava_lexer_peek(lexer))) { + _rava_lexer_advance(lexer); + } + + RavaToken_t *token = _rava_lexer_create_token(lexer, RAVA_TOKEN_IDENTIFIER); + RavaTokenType_e keyword_type = rava_lexer_lookup_keyword(token->lexeme); + token->type = keyword_type; + + if (keyword_type == RAVA_TOKEN_LITERAL_TRUE) { + token->value.int_value = 1; + } else if (keyword_type == RAVA_TOKEN_LITERAL_FALSE) { + token->value.int_value = 0; + } + + return token; +} + +RavaToken_t* rava_lexer_next_token(RavaLexer_t *lexer) { + _rava_lexer_skip_whitespace(lexer); + + lexer->start = lexer->current; + lexer->start_column = lexer->column; + + if (_rava_lexer_is_at_end(lexer)) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_EOF); + } + + char c = _rava_lexer_advance(lexer); + + if (_rava_is_alpha(c)) { + return _rava_lexer_identifier(lexer); + } + + if (isdigit(c)) { + return rava_lexer_parse_number(lexer); + } + + switch (c) { + case '(': return _rava_lexer_create_token(lexer, RAVA_TOKEN_LPAREN); + case ')': return _rava_lexer_create_token(lexer, RAVA_TOKEN_RPAREN); + case '{': return _rava_lexer_create_token(lexer, RAVA_TOKEN_LBRACE); + case '}': return _rava_lexer_create_token(lexer, RAVA_TOKEN_RBRACE); + case '[': return _rava_lexer_create_token(lexer, RAVA_TOKEN_LBRACKET); + case ']': return _rava_lexer_create_token(lexer, RAVA_TOKEN_RBRACKET); + case ';': return _rava_lexer_create_token(lexer, RAVA_TOKEN_SEMICOLON); + case ',': return _rava_lexer_create_token(lexer, RAVA_TOKEN_COMMA); + case '~': return _rava_lexer_create_token(lexer, RAVA_TOKEN_TILDE); + case '?': return _rava_lexer_create_token(lexer, RAVA_TOKEN_QUESTION); + case '@': return _rava_lexer_create_token(lexer, RAVA_TOKEN_AT); + + case '.': + if (_rava_lexer_peek(lexer) == '.' && _rava_lexer_peek_next(lexer) == '.') { + _rava_lexer_advance(lexer); + _rava_lexer_advance(lexer); + return _rava_lexer_create_token(lexer, RAVA_TOKEN_ELLIPSIS); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_DOT); + + case ':': + if (_rava_lexer_match(lexer, ':')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_COLONCOLON); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_COLON); + + case '+': + if (_rava_lexer_match(lexer, '+')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_INC); + } + if (_rava_lexer_match(lexer, '=')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_PLUSASSIGN); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_PLUS); + + case '-': + if (_rava_lexer_match(lexer, '-')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_DEC); + } + if (_rava_lexer_match(lexer, '=')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_MINUSASSIGN); + } + if (_rava_lexer_match(lexer, '>')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_ARROW); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_MINUS); + + case '*': + if (_rava_lexer_match(lexer, '=')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_STARASSIGN); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_STAR); + + case '/': + if (_rava_lexer_match(lexer, '=')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_SLASHASSIGN); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_SLASH); + + case '%': + if (_rava_lexer_match(lexer, '=')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_PERCENTASSIGN); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_PERCENT); + + case '&': + if (_rava_lexer_match(lexer, '&')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_AND); + } + if (_rava_lexer_match(lexer, '=')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_ANDASSIGN); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_AMP); + + case '|': + if (_rava_lexer_match(lexer, '|')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_OR); + } + if (_rava_lexer_match(lexer, '=')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_ORASSIGN); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_PIPE); + + case '^': + if (_rava_lexer_match(lexer, '=')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_CARETASSIGN); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_CARET); + + case '!': + if (_rava_lexer_match(lexer, '=')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_NE); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_BANG); + + case '=': + if (_rava_lexer_match(lexer, '=')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_EQUAL); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_ASSIGN); + + case '<': + if (_rava_lexer_match(lexer, '<')) { + if (_rava_lexer_match(lexer, '=')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_LSHIFTASSIGN); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_LSHIFT); + } + if (_rava_lexer_match(lexer, '=')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_LE); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_LT); + + case '>': + if (_rava_lexer_match(lexer, '>')) { + if (_rava_lexer_match(lexer, '>')) { + if (_rava_lexer_match(lexer, '=')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_URSHIFTASSIGN); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_URSHIFT); + } + if (_rava_lexer_match(lexer, '=')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_RSHIFTASSIGN); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_RSHIFT); + } + if (_rava_lexer_match(lexer, '=')) { + return _rava_lexer_create_token(lexer, RAVA_TOKEN_GE); + } + return _rava_lexer_create_token(lexer, RAVA_TOKEN_GT); + + case '"': + return rava_lexer_parse_string(lexer); + + case '\'': + return rava_lexer_parse_character(lexer); + + default: + lexer->error_message = malloc(100); + snprintf(lexer->error_message, 100, "Unexpected character '%c'", c); + return _rava_lexer_create_token(lexer, RAVA_TOKEN_ERROR); + } +} diff --git a/parser/parser.c b/parser/parser.c new file mode 100644 index 0000000..facdbaf --- /dev/null +++ b/parser/parser.c @@ -0,0 +1,236 @@ +#define _POSIX_C_SOURCE 200809L +#include "parser.h" +#include +#include +#include + +RavaASTNode_t* rava_ast_node_create(RavaASTNodeType_e type, int line, int column) { + RavaASTNode_t *node = calloc(1, sizeof(RavaASTNode_t)); + node->type = type; + node->line = line; + node->column = column; + node->parent = NULL; + node->children = NULL; + node->children_count = 0; + node->children_capacity = 0; + return node; +} + +void rava_ast_node_destroy(RavaASTNode_t *node) { + if (!node) return; + + for (size_t i = 0; i < node->children_count; i++) { + rava_ast_node_destroy(node->children[i]); + } + free(node->children); + + switch (node->type) { + case RAVA_AST_IDENTIFIER_EXPR: + free(node->data.identifier.name); + break; + case RAVA_AST_CLASS_DECL: + free(node->data.class_decl.name); + free(node->data.class_decl.modifiers); + rava_ast_node_destroy(node->data.class_decl.type); + break; + case RAVA_AST_METHOD_DECL: + free(node->data.method_decl.name); + free(node->data.method_decl.modifiers); + rava_ast_node_destroy(node->data.method_decl.return_type); + break; + case RAVA_AST_VAR_DECL: + free(node->data.var_decl.name); + rava_ast_node_destroy(node->data.var_decl.type); + rava_ast_node_destroy(node->data.var_decl.initializer); + break; + case RAVA_AST_TYPE: + free(node->data.type.type_name); + break; + case RAVA_AST_MEMBER_ACCESS_EXPR: + free(node->data.member_access.member); + rava_ast_node_destroy(node->data.member_access.object); + break; + case RAVA_AST_CALL_EXPR: + rava_ast_node_destroy(node->data.call.callee); + for (size_t i = 0; i < node->data.call.arguments_count; i++) { + rava_ast_node_destroy(node->data.call.arguments[i]); + } + free(node->data.call.arguments); + break; + case RAVA_AST_NEW_EXPR: + rava_ast_node_destroy(node->data.new_expr.type); + for (size_t i = 0; i < node->data.new_expr.arguments_count; i++) { + rava_ast_node_destroy(node->data.new_expr.arguments[i]); + } + free(node->data.new_expr.arguments); + break; + case RAVA_AST_LITERAL_EXPR: + if (node->data.literal.literal_type == RAVA_TOKEN_LITERAL_STRING) { + free(node->data.literal.value.string_value); + } + break; + default: + break; + } + + free(node); +} + +void rava_ast_node_add_child(RavaASTNode_t *parent, RavaASTNode_t *child) { + if (!parent || !child) return; + + if (parent->children_count >= parent->children_capacity) { + size_t new_capacity = parent->children_capacity == 0 ? 4 : parent->children_capacity * 2; + parent->children = realloc(parent->children, sizeof(RavaASTNode_t*) * new_capacity); + parent->children_capacity = new_capacity; + } + + parent->children[parent->children_count++] = child; + child->parent = parent; +} + +RavaParser_t* rava_parser_create(RavaLexer_t *lexer) { + RavaParser_t *parser = malloc(sizeof(RavaParser_t)); + parser->lexer = lexer; + parser->current_token = rava_lexer_next_token(lexer); + parser->peek_token = rava_lexer_next_token(lexer); + parser->error_message = NULL; + parser->had_error = false; + return parser; +} + +void rava_parser_destroy(RavaParser_t *parser) { + if (!parser) return; + + rava_token_destroy(parser->current_token); + rava_token_destroy(parser->peek_token); + free(parser->error_message); + free(parser); +} + +void _rava_parser_advance(RavaParser_t *parser) { + rava_token_destroy(parser->current_token); + parser->current_token = parser->peek_token; + parser->peek_token = rava_lexer_next_token(parser->lexer); +} + +bool _rava_parser_check(RavaParser_t *parser, RavaTokenType_e type) { + return parser->current_token->type == type; +} + +bool _rava_parser_match(RavaParser_t *parser, RavaTokenType_e type) { + if (_rava_parser_check(parser, type)) { + _rava_parser_advance(parser); + return true; + } + return false; +} + +bool _rava_parser_expect(RavaParser_t *parser, RavaTokenType_e type, const char *message) { + if (_rava_parser_check(parser, type)) { + _rava_parser_advance(parser); + return true; + } + + parser->had_error = true; + if (parser->error_message) free(parser->error_message); + parser->error_message = malloc(256); + snprintf(parser->error_message, 256, "%s at line %d, column %d", + message, parser->current_token->line, parser->current_token->column); + return false; +} + +extern RavaASTNode_t* _rava_parser_parse_type(RavaParser_t *parser); +extern RavaASTNode_t* _rava_parser_parse_expression(RavaParser_t *parser); +extern RavaASTNode_t* _rava_parser_parse_statement(RavaParser_t *parser); +extern RavaASTNode_t* _rava_parser_parse_block(RavaParser_t *parser); + +static RavaModifier_e _rava_token_to_modifier(RavaTokenType_e type) { + switch (type) { + case RAVA_TOKEN_KEYWORD_PUBLIC: return RAVA_MODIFIER_PUBLIC; + case RAVA_TOKEN_KEYWORD_PRIVATE: return RAVA_MODIFIER_PRIVATE; + case RAVA_TOKEN_KEYWORD_PROTECTED: return RAVA_MODIFIER_PROTECTED; + case RAVA_TOKEN_KEYWORD_STATIC: return RAVA_MODIFIER_STATIC; + case RAVA_TOKEN_KEYWORD_FINAL: return RAVA_MODIFIER_FINAL; + case RAVA_TOKEN_KEYWORD_ABSTRACT: return RAVA_MODIFIER_ABSTRACT; + case RAVA_TOKEN_KEYWORD_NATIVE: return RAVA_MODIFIER_NATIVE; + case RAVA_TOKEN_KEYWORD_SYNCHRONIZED: return RAVA_MODIFIER_SYNCHRONIZED; + case RAVA_TOKEN_KEYWORD_TRANSIENT: return RAVA_MODIFIER_TRANSIENT; + case RAVA_TOKEN_KEYWORD_VOLATILE: return RAVA_MODIFIER_VOLATILE; + case RAVA_TOKEN_KEYWORD_STRICTFP: return RAVA_MODIFIER_STRICTFP; + default: return RAVA_MODIFIER_PUBLIC; + } +} + +static bool _rava_is_modifier(RavaTokenType_e type) { + return type == RAVA_TOKEN_KEYWORD_PUBLIC || + type == RAVA_TOKEN_KEYWORD_PRIVATE || + type == RAVA_TOKEN_KEYWORD_PROTECTED || + type == RAVA_TOKEN_KEYWORD_STATIC || + type == RAVA_TOKEN_KEYWORD_FINAL || + type == RAVA_TOKEN_KEYWORD_ABSTRACT || + type == RAVA_TOKEN_KEYWORD_NATIVE || + type == RAVA_TOKEN_KEYWORD_SYNCHRONIZED || + type == RAVA_TOKEN_KEYWORD_TRANSIENT || + type == RAVA_TOKEN_KEYWORD_VOLATILE || + type == RAVA_TOKEN_KEYWORD_STRICTFP; +} + +void _rava_parser_parse_modifiers(RavaParser_t *parser, RavaModifier_e **modifiers, size_t *count) { + size_t capacity = 4; + *modifiers = malloc(sizeof(RavaModifier_e) * capacity); + *count = 0; + + while (_rava_is_modifier(parser->current_token->type)) { + if (*count >= capacity) { + capacity *= 2; + *modifiers = realloc(*modifiers, sizeof(RavaModifier_e) * capacity); + } + (*modifiers)[(*count)++] = _rava_token_to_modifier(parser->current_token->type); + _rava_parser_advance(parser); + } +} + +RavaASTNode_t* _rava_parser_parse_type(RavaParser_t *parser) { + RavaASTNode_t *type_node = rava_ast_node_create(RAVA_AST_TYPE, + parser->current_token->line, + parser->current_token->column); + + if (_rava_parser_check(parser, RAVA_TOKEN_IDENTIFIER) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_INT) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_BOOLEAN) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_CHAR) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_BYTE) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_SHORT) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_LONG) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_FLOAT) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_DOUBLE) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_VOID)) { + + type_node->data.type.type_name = strdup(parser->current_token->lexeme); + type_node->data.type.is_array = false; + type_node->data.type.array_dimensions = 0; + _rava_parser_advance(parser); + + while (_rava_parser_check(parser, RAVA_TOKEN_LBRACKET)) { + if (parser->peek_token && parser->peek_token->type == RAVA_TOKEN_RBRACKET) { + _rava_parser_advance(parser); + _rava_parser_advance(parser); + type_node->data.type.is_array = true; + type_node->data.type.array_dimensions++; + } else { + break; + } + } + } else { + parser->had_error = true; + if (parser->error_message) free(parser->error_message); + parser->error_message = strdup("Expected type name"); + rava_ast_node_destroy(type_node); + return NULL; + } + + return type_node; +} + +RavaASTNode_t* rava_parser_parse(RavaParser_t *parser); diff --git a/parser/parser.h b/parser/parser.h new file mode 100644 index 0000000..05823d7 --- /dev/null +++ b/parser/parser.h @@ -0,0 +1,251 @@ +#ifndef RAVA_PARSER_H +#define RAVA_PARSER_H + +#include "../lexer/lexer.h" +#include +#include + +typedef enum { + RAVA_AST_COMPILATION_UNIT, + + RAVA_AST_PACKAGE_DECL, + RAVA_AST_IMPORT_DECL, + + RAVA_AST_CLASS_DECL, + RAVA_AST_INTERFACE_DECL, + RAVA_AST_ENUM_DECL, + + RAVA_AST_FIELD_DECL, + RAVA_AST_METHOD_DECL, + RAVA_AST_CONSTRUCTOR_DECL, + RAVA_AST_INITIALIZER_BLOCK, + + RAVA_AST_PARAM_DECL, + RAVA_AST_VAR_DECL, + + RAVA_AST_BLOCK_STMT, + RAVA_AST_IF_STMT, + RAVA_AST_WHILE_STMT, + RAVA_AST_DO_WHILE_STMT, + RAVA_AST_FOR_STMT, + RAVA_AST_ENHANCED_FOR_STMT, + RAVA_AST_SWITCH_STMT, + RAVA_AST_CASE_STMT, + RAVA_AST_TRY_STMT, + RAVA_AST_CATCH_CLAUSE, + RAVA_AST_THROW_STMT, + RAVA_AST_RETURN_STMT, + RAVA_AST_BREAK_STMT, + RAVA_AST_CONTINUE_STMT, + RAVA_AST_EXPR_STMT, + RAVA_AST_EMPTY_STMT, + RAVA_AST_SYNCHRONIZED_STMT, + RAVA_AST_ASSERT_STMT, + + RAVA_AST_LITERAL_EXPR, + RAVA_AST_IDENTIFIER_EXPR, + RAVA_AST_BINARY_EXPR, + RAVA_AST_UNARY_EXPR, + RAVA_AST_TERNARY_EXPR, + RAVA_AST_ASSIGN_EXPR, + RAVA_AST_CALL_EXPR, + RAVA_AST_MEMBER_ACCESS_EXPR, + RAVA_AST_ARRAY_ACCESS_EXPR, + RAVA_AST_NEW_EXPR, + RAVA_AST_CAST_EXPR, + RAVA_AST_INSTANCEOF_EXPR, + RAVA_AST_THIS_EXPR, + RAVA_AST_SUPER_EXPR, + RAVA_AST_CLASS_LITERAL_EXPR, + RAVA_AST_ARRAY_INIT_EXPR, + + RAVA_AST_TYPE, + RAVA_AST_TYPE_PARAM, + RAVA_AST_MODIFIER +} RavaASTNodeType_e; + +typedef enum { + RAVA_MODIFIER_PUBLIC, + RAVA_MODIFIER_PRIVATE, + RAVA_MODIFIER_PROTECTED, + RAVA_MODIFIER_STATIC, + RAVA_MODIFIER_FINAL, + RAVA_MODIFIER_ABSTRACT, + RAVA_MODIFIER_NATIVE, + RAVA_MODIFIER_SYNCHRONIZED, + RAVA_MODIFIER_TRANSIENT, + RAVA_MODIFIER_VOLATILE, + RAVA_MODIFIER_STRICTFP +} RavaModifier_e; + +typedef enum { + RAVA_BINOP_ADD, + RAVA_BINOP_SUB, + RAVA_BINOP_MUL, + RAVA_BINOP_DIV, + RAVA_BINOP_MOD, + RAVA_BINOP_EQ, + RAVA_BINOP_NE, + RAVA_BINOP_LT, + RAVA_BINOP_LE, + RAVA_BINOP_GT, + RAVA_BINOP_GE, + RAVA_BINOP_AND, + RAVA_BINOP_OR, + RAVA_BINOP_BITAND, + RAVA_BINOP_BITOR, + RAVA_BINOP_BITXOR, + RAVA_BINOP_LSHIFT, + RAVA_BINOP_RSHIFT, + RAVA_BINOP_URSHIFT +} RavaBinaryOp_e; + +typedef enum { + RAVA_UNOP_PLUS, + RAVA_UNOP_MINUS, + RAVA_UNOP_NOT, + RAVA_UNOP_BITNOT, + RAVA_UNOP_PREINC, + RAVA_UNOP_PREDEC, + RAVA_UNOP_POSTINC, + RAVA_UNOP_POSTDEC +} RavaUnaryOp_e; + +typedef struct RavaASTNode_t RavaASTNode_t; + +struct RavaASTNode_t { + RavaASTNodeType_e type; + int line; + int column; + RavaASTNode_t *parent; + RavaASTNode_t **children; + size_t children_count; + size_t children_capacity; + + union { + struct { + char *name; + } identifier; + + struct { + RavaTokenType_e literal_type; + RavaLiteralValue_u value; + } literal; + + struct { + RavaBinaryOp_e op; + RavaASTNode_t *left; + RavaASTNode_t *right; + } binary; + + struct { + RavaUnaryOp_e op; + RavaASTNode_t *operand; + } unary; + + struct { + RavaASTNode_t *condition; + RavaASTNode_t *true_expr; + RavaASTNode_t *false_expr; + } ternary; + + struct { + RavaASTNode_t *target; + RavaASTNode_t *value; + } assign; + + struct { + char *name; + char *superclass; + RavaASTNode_t *type; + RavaModifier_e *modifiers; + size_t modifiers_count; + } class_decl; + + struct { + char *name; + RavaASTNode_t *return_type; + RavaModifier_e *modifiers; + size_t modifiers_count; + } method_decl; + + struct { + char *name; + RavaASTNode_t *type; + RavaASTNode_t *initializer; + } var_decl; + + struct { + RavaASTNode_t *condition; + RavaASTNode_t *then_stmt; + RavaASTNode_t *else_stmt; + } if_stmt; + + struct { + RavaASTNode_t *condition; + RavaASTNode_t *body; + } while_stmt; + + struct { + RavaASTNode_t *init; + RavaASTNode_t *condition; + RavaASTNode_t *update; + RavaASTNode_t *body; + } for_stmt; + + struct { + RavaASTNode_t *value; + } return_stmt; + + struct { + char *type_name; + bool is_array; + int array_dimensions; + } type; + + struct { + RavaASTNode_t *callee; + RavaASTNode_t **arguments; + size_t arguments_count; + } call; + + struct { + RavaASTNode_t *object; + char *member; + } member_access; + + struct { + RavaASTNode_t *array; + RavaASTNode_t *index; + } array_access; + + struct { + RavaASTNode_t *type; + RavaASTNode_t **arguments; + size_t arguments_count; + } new_expr; + + struct { + RavaASTNode_t *type; + RavaASTNode_t *expression; + } cast; + } data; +}; + +typedef struct { + RavaLexer_t *lexer; + RavaToken_t *current_token; + RavaToken_t *peek_token; + char *error_message; + bool had_error; +} RavaParser_t; + +RavaParser_t* rava_parser_create(RavaLexer_t *lexer); +void rava_parser_destroy(RavaParser_t *parser); +RavaASTNode_t* rava_parser_parse(RavaParser_t *parser); + +RavaASTNode_t* rava_ast_node_create(RavaASTNodeType_e type, int line, int column); +void rava_ast_node_destroy(RavaASTNode_t *node); +void rava_ast_node_add_child(RavaASTNode_t *parent, RavaASTNode_t *child); + +#endif diff --git a/parser/parser_declarations.c b/parser/parser_declarations.c new file mode 100644 index 0000000..3c0d51d --- /dev/null +++ b/parser/parser_declarations.c @@ -0,0 +1,196 @@ +#define _POSIX_C_SOURCE 200809L +#include "parser.h" +#include +#include + +extern void _rava_parser_advance(RavaParser_t *parser); +extern bool _rava_parser_check(RavaParser_t *parser, RavaTokenType_e type); +extern bool _rava_parser_match(RavaParser_t *parser, RavaTokenType_e type); +extern bool _rava_parser_expect(RavaParser_t *parser, RavaTokenType_e type, const char *message); +extern RavaASTNode_t* _rava_parser_parse_type(RavaParser_t *parser); +extern RavaASTNode_t* _rava_parser_parse_expression(RavaParser_t *parser); +extern RavaASTNode_t* _rava_parser_parse_block(RavaParser_t *parser); +extern void _rava_parser_parse_modifiers(RavaParser_t *parser, RavaModifier_e **modifiers, size_t *count); + +static RavaASTNode_t* _rava_parser_parse_method_declaration(RavaParser_t *parser, + RavaModifier_e *modifiers, + size_t modifiers_count, + RavaASTNode_t *return_type) { + RavaASTNode_t *method = rava_ast_node_create(RAVA_AST_METHOD_DECL, + parser->current_token->line, + parser->current_token->column); + + method->data.method_decl.modifiers = modifiers; + method->data.method_decl.modifiers_count = modifiers_count; + method->data.method_decl.return_type = return_type; + + if (_rava_parser_check(parser, RAVA_TOKEN_IDENTIFIER)) { + method->data.method_decl.name = strdup(parser->current_token->lexeme); + _rava_parser_advance(parser); + } + + _rava_parser_expect(parser, RAVA_TOKEN_LPAREN, "Expected '(' after method name"); + + while (!_rava_parser_check(parser, RAVA_TOKEN_RPAREN) && + !_rava_parser_check(parser, RAVA_TOKEN_EOF)) { + RavaASTNode_t *param_type = _rava_parser_parse_type(parser); + RavaASTNode_t *param = rava_ast_node_create(RAVA_AST_PARAM_DECL, + parser->current_token->line, + parser->current_token->column); + param->data.var_decl.type = param_type; + + if (_rava_parser_check(parser, RAVA_TOKEN_IDENTIFIER)) { + param->data.var_decl.name = strdup(parser->current_token->lexeme); + _rava_parser_advance(parser); + } + + rava_ast_node_add_child(method, param); + + if (!_rava_parser_match(parser, RAVA_TOKEN_COMMA)) { + break; + } + } + + _rava_parser_expect(parser, RAVA_TOKEN_RPAREN, "Expected ')' after parameters"); + + if (_rava_parser_check(parser, RAVA_TOKEN_LBRACE)) { + RavaASTNode_t *body = _rava_parser_parse_block(parser); + rava_ast_node_add_child(method, body); + } else { + _rava_parser_expect(parser, RAVA_TOKEN_SEMICOLON, "Expected ';' or '{' after method declaration"); + } + + return method; +} + +RavaASTNode_t* _rava_parser_parse_class_declaration(RavaParser_t *parser) { + RavaModifier_e *modifiers = NULL; + size_t modifiers_count = 0; + + _rava_parser_parse_modifiers(parser, &modifiers, &modifiers_count); + + _rava_parser_expect(parser, RAVA_TOKEN_KEYWORD_CLASS, "Expected 'class' keyword"); + + RavaASTNode_t *class_decl = rava_ast_node_create(RAVA_AST_CLASS_DECL, + parser->current_token->line, + parser->current_token->column); + + class_decl->data.class_decl.modifiers = modifiers; + class_decl->data.class_decl.modifiers_count = modifiers_count; + class_decl->data.class_decl.superclass = NULL; + + if (_rava_parser_check(parser, RAVA_TOKEN_IDENTIFIER)) { + class_decl->data.class_decl.name = strdup(parser->current_token->lexeme); + _rava_parser_advance(parser); + } + + if (_rava_parser_match(parser, RAVA_TOKEN_KEYWORD_EXTENDS)) { + if (_rava_parser_check(parser, RAVA_TOKEN_IDENTIFIER)) { + class_decl->data.class_decl.superclass = strdup(parser->current_token->lexeme); + _rava_parser_advance(parser); + } + } + + _rava_parser_expect(parser, RAVA_TOKEN_LBRACE, "Expected '{' after class name"); + + while (!_rava_parser_check(parser, RAVA_TOKEN_RBRACE) && + !_rava_parser_check(parser, RAVA_TOKEN_EOF)) { + + RavaModifier_e *member_modifiers = NULL; + size_t member_modifiers_count = 0; + + _rava_parser_parse_modifiers(parser, &member_modifiers, &member_modifiers_count); + + if (_rava_parser_check(parser, RAVA_TOKEN_IDENTIFIER) && + strcmp(parser->current_token->lexeme, class_decl->data.class_decl.name) == 0) { + + RavaASTNode_t *constructor = rava_ast_node_create(RAVA_AST_CONSTRUCTOR_DECL, + parser->current_token->line, + parser->current_token->column); + _rava_parser_advance(parser); + + _rava_parser_expect(parser, RAVA_TOKEN_LPAREN, "Expected '(' after constructor name"); + + while (!_rava_parser_check(parser, RAVA_TOKEN_RPAREN) && + !_rava_parser_check(parser, RAVA_TOKEN_EOF)) { + RavaASTNode_t *param_type = _rava_parser_parse_type(parser); + RavaASTNode_t *param = rava_ast_node_create(RAVA_AST_PARAM_DECL, + parser->current_token->line, + parser->current_token->column); + param->data.var_decl.type = param_type; + + if (_rava_parser_check(parser, RAVA_TOKEN_IDENTIFIER)) { + param->data.var_decl.name = strdup(parser->current_token->lexeme); + _rava_parser_advance(parser); + } + + rava_ast_node_add_child(constructor, param); + + if (!_rava_parser_match(parser, RAVA_TOKEN_COMMA)) { + break; + } + } + + _rava_parser_expect(parser, RAVA_TOKEN_RPAREN, "Expected ')' after constructor parameters"); + + if (_rava_parser_check(parser, RAVA_TOKEN_LBRACE)) { + RavaASTNode_t *body = _rava_parser_parse_block(parser); + rava_ast_node_add_child(constructor, body); + } + + rava_ast_node_add_child(class_decl, constructor); + free(member_modifiers); + continue; + } + + RavaASTNode_t *member_type = _rava_parser_parse_type(parser); + + if (_rava_parser_check(parser, RAVA_TOKEN_IDENTIFIER)) { + char *name = strdup(parser->current_token->lexeme); + _rava_parser_advance(parser); + + if (_rava_parser_check(parser, RAVA_TOKEN_LPAREN)) { + RavaASTNode_t *method = _rava_parser_parse_method_declaration(parser, + member_modifiers, + member_modifiers_count, + member_type); + method->data.method_decl.name = name; + rava_ast_node_add_child(class_decl, method); + } else { + RavaASTNode_t *field = rava_ast_node_create(RAVA_AST_FIELD_DECL, + parser->current_token->line, + parser->current_token->column); + field->data.var_decl.type = member_type; + field->data.var_decl.name = name; + if (_rava_parser_match(parser, RAVA_TOKEN_ASSIGN)) { + field->data.var_decl.initializer = _rava_parser_parse_expression(parser); + } else { + field->data.var_decl.initializer = NULL; + } + _rava_parser_expect(parser, RAVA_TOKEN_SEMICOLON, "Expected ';' after field declaration"); + rava_ast_node_add_child(class_decl, field); + } + } + } + + _rava_parser_expect(parser, RAVA_TOKEN_RBRACE, "Expected '}' after class body"); + + return class_decl; +} + +RavaASTNode_t* rava_parser_parse(RavaParser_t *parser) { + RavaASTNode_t *root = rava_ast_node_create(RAVA_AST_COMPILATION_UNIT, 1, 1); + + while (!_rava_parser_check(parser, RAVA_TOKEN_EOF)) { + if (parser->had_error) { + break; + } + + RavaASTNode_t *decl = _rava_parser_parse_class_declaration(parser); + if (decl) { + rava_ast_node_add_child(root, decl); + } + } + + return root; +} diff --git a/parser/parser_expressions.c b/parser/parser_expressions.c new file mode 100644 index 0000000..a4fade9 --- /dev/null +++ b/parser/parser_expressions.c @@ -0,0 +1,433 @@ +#define _POSIX_C_SOURCE 200809L +#include "parser.h" +#include +#include + +extern void _rava_parser_advance(RavaParser_t *parser); +extern bool _rava_parser_check(RavaParser_t *parser, RavaTokenType_e type); +extern bool _rava_parser_match(RavaParser_t *parser, RavaTokenType_e type); +extern bool _rava_parser_expect(RavaParser_t *parser, RavaTokenType_e type, const char *message); +extern RavaASTNode_t* _rava_parser_parse_type(RavaParser_t *parser); + +RavaASTNode_t* _rava_parser_parse_expression(RavaParser_t *parser); + +static RavaASTNode_t* _rava_parser_parse_primary(RavaParser_t *parser) { + RavaASTNode_t *node = NULL; + + if (_rava_parser_check(parser, RAVA_TOKEN_LITERAL_INTEGER) || + _rava_parser_check(parser, RAVA_TOKEN_LITERAL_LONG) || + _rava_parser_check(parser, RAVA_TOKEN_LITERAL_FLOAT) || + _rava_parser_check(parser, RAVA_TOKEN_LITERAL_DOUBLE) || + _rava_parser_check(parser, RAVA_TOKEN_LITERAL_STRING) || + _rava_parser_check(parser, RAVA_TOKEN_LITERAL_CHARACTER) || + _rava_parser_check(parser, RAVA_TOKEN_LITERAL_TRUE) || + _rava_parser_check(parser, RAVA_TOKEN_LITERAL_FALSE) || + _rava_parser_check(parser, RAVA_TOKEN_LITERAL_NULL)) { + + node = rava_ast_node_create(RAVA_AST_LITERAL_EXPR, + parser->current_token->line, + parser->current_token->column); + node->data.literal.literal_type = parser->current_token->type; + node->data.literal.value = parser->current_token->value; + + if (parser->current_token->type == RAVA_TOKEN_LITERAL_STRING) { + node->data.literal.value.string_value = strdup(parser->current_token->value.string_value); + } + + _rava_parser_advance(parser); + return node; + } + + if (_rava_parser_check(parser, RAVA_TOKEN_IDENTIFIER)) { + node = rava_ast_node_create(RAVA_AST_IDENTIFIER_EXPR, + parser->current_token->line, + parser->current_token->column); + node->data.identifier.name = strdup(parser->current_token->lexeme); + _rava_parser_advance(parser); + return node; + } + + if (_rava_parser_match(parser, RAVA_TOKEN_KEYWORD_THIS)) { + return rava_ast_node_create(RAVA_AST_THIS_EXPR, + parser->current_token->line, + parser->current_token->column); + } + + if (_rava_parser_match(parser, RAVA_TOKEN_KEYWORD_SUPER)) { + return rava_ast_node_create(RAVA_AST_SUPER_EXPR, + parser->current_token->line, + parser->current_token->column); + } + + if (_rava_parser_match(parser, RAVA_TOKEN_LPAREN)) { + RavaASTNode_t *expr = _rava_parser_parse_expression(parser); + _rava_parser_expect(parser, RAVA_TOKEN_RPAREN, "Expected ')' after expression"); + return expr; + } + + if (_rava_parser_match(parser, RAVA_TOKEN_KEYWORD_NEW)) { + node = rava_ast_node_create(RAVA_AST_NEW_EXPR, + parser->current_token->line, + parser->current_token->column); + node->data.new_expr.type = _rava_parser_parse_type(parser); + + if (_rava_parser_match(parser, RAVA_TOKEN_LBRACKET)) { + size_t args_capacity = 4; + node->data.new_expr.arguments = malloc(sizeof(RavaASTNode_t*) * args_capacity); + node->data.new_expr.arguments_count = 0; + + node->data.new_expr.arguments[node->data.new_expr.arguments_count++] = + _rava_parser_parse_expression(parser); + + _rava_parser_expect(parser, RAVA_TOKEN_RBRACKET, "Expected ']' after array size"); + + while (_rava_parser_match(parser, RAVA_TOKEN_LBRACKET)) { + if (node->data.new_expr.arguments_count >= args_capacity) { + args_capacity *= 2; + node->data.new_expr.arguments = realloc(node->data.new_expr.arguments, + sizeof(RavaASTNode_t*) * args_capacity); + } + if (!_rava_parser_check(parser, RAVA_TOKEN_RBRACKET)) { + node->data.new_expr.arguments[node->data.new_expr.arguments_count++] = + _rava_parser_parse_expression(parser); + } + _rava_parser_expect(parser, RAVA_TOKEN_RBRACKET, "Expected ']'"); + } + + if (node->data.new_expr.type) { + node->data.new_expr.type->data.type.is_array = true; + node->data.new_expr.type->data.type.array_dimensions = (int)node->data.new_expr.arguments_count; + } + return node; + } + + _rava_parser_expect(parser, RAVA_TOKEN_LPAREN, "Expected '(' or '[' after type in new expression"); + + size_t args_capacity = 4; + node->data.new_expr.arguments = malloc(sizeof(RavaASTNode_t*) * args_capacity); + node->data.new_expr.arguments_count = 0; + + if (!_rava_parser_check(parser, RAVA_TOKEN_RPAREN)) { + do { + if (node->data.new_expr.arguments_count >= args_capacity) { + args_capacity *= 2; + node->data.new_expr.arguments = realloc(node->data.new_expr.arguments, + sizeof(RavaASTNode_t*) * args_capacity); + } + node->data.new_expr.arguments[node->data.new_expr.arguments_count++] = + _rava_parser_parse_expression(parser); + } while (_rava_parser_match(parser, RAVA_TOKEN_COMMA)); + } + + _rava_parser_expect(parser, RAVA_TOKEN_RPAREN, "Expected ')' after arguments"); + return node; + } + + parser->had_error = true; + return NULL; +} + +static RavaASTNode_t* _rava_parser_parse_postfix(RavaParser_t *parser) { + RavaASTNode_t *expr = _rava_parser_parse_primary(parser); + + while (true) { + if (_rava_parser_match(parser, RAVA_TOKEN_DOT)) { + if (_rava_parser_check(parser, RAVA_TOKEN_IDENTIFIER)) { + RavaASTNode_t *member_access = rava_ast_node_create(RAVA_AST_MEMBER_ACCESS_EXPR, + parser->current_token->line, + parser->current_token->column); + member_access->data.member_access.object = expr; + member_access->data.member_access.member = strdup(parser->current_token->lexeme); + _rava_parser_advance(parser); + expr = member_access; + } + } else if (_rava_parser_match(parser, RAVA_TOKEN_LPAREN)) { + RavaASTNode_t *call = rava_ast_node_create(RAVA_AST_CALL_EXPR, + parser->current_token->line, + parser->current_token->column); + call->data.call.callee = expr; + + size_t args_capacity = 4; + call->data.call.arguments = malloc(sizeof(RavaASTNode_t*) * args_capacity); + call->data.call.arguments_count = 0; + + if (!_rava_parser_check(parser, RAVA_TOKEN_RPAREN)) { + do { + if (call->data.call.arguments_count >= args_capacity) { + args_capacity *= 2; + call->data.call.arguments = realloc(call->data.call.arguments, + sizeof(RavaASTNode_t*) * args_capacity); + } + call->data.call.arguments[call->data.call.arguments_count++] = + _rava_parser_parse_expression(parser); + } while (_rava_parser_match(parser, RAVA_TOKEN_COMMA)); + } + + _rava_parser_expect(parser, RAVA_TOKEN_RPAREN, "Expected ')' after arguments"); + expr = call; + } else if (_rava_parser_match(parser, RAVA_TOKEN_LBRACKET)) { + RavaASTNode_t *array_access = rava_ast_node_create(RAVA_AST_ARRAY_ACCESS_EXPR, + parser->current_token->line, + parser->current_token->column); + array_access->data.array_access.array = expr; + array_access->data.array_access.index = _rava_parser_parse_expression(parser); + _rava_parser_expect(parser, RAVA_TOKEN_RBRACKET, "Expected ']' after array index"); + expr = array_access; + } else if (_rava_parser_match(parser, RAVA_TOKEN_INC)) { + RavaASTNode_t *unary = rava_ast_node_create(RAVA_AST_UNARY_EXPR, + parser->current_token->line, + parser->current_token->column); + unary->data.unary.op = RAVA_UNOP_POSTINC; + unary->data.unary.operand = expr; + expr = unary; + } else if (_rava_parser_match(parser, RAVA_TOKEN_DEC)) { + RavaASTNode_t *unary = rava_ast_node_create(RAVA_AST_UNARY_EXPR, + parser->current_token->line, + parser->current_token->column); + unary->data.unary.op = RAVA_UNOP_POSTDEC; + unary->data.unary.operand = expr; + expr = unary; + } else { + break; + } + } + + return expr; +} + +static RavaASTNode_t* _rava_parser_parse_unary(RavaParser_t *parser) { + if (_rava_parser_match(parser, RAVA_TOKEN_PLUS)) { + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_UNARY_EXPR, + parser->current_token->line, + parser->current_token->column); + node->data.unary.op = RAVA_UNOP_PLUS; + node->data.unary.operand = _rava_parser_parse_unary(parser); + return node; + } + + if (_rava_parser_match(parser, RAVA_TOKEN_MINUS)) { + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_UNARY_EXPR, + parser->current_token->line, + parser->current_token->column); + node->data.unary.op = RAVA_UNOP_MINUS; + node->data.unary.operand = _rava_parser_parse_unary(parser); + return node; + } + + if (_rava_parser_match(parser, RAVA_TOKEN_BANG)) { + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_UNARY_EXPR, + parser->current_token->line, + parser->current_token->column); + node->data.unary.op = RAVA_UNOP_NOT; + node->data.unary.operand = _rava_parser_parse_unary(parser); + return node; + } + + if (_rava_parser_match(parser, RAVA_TOKEN_TILDE)) { + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_UNARY_EXPR, + parser->current_token->line, + parser->current_token->column); + node->data.unary.op = RAVA_UNOP_BITNOT; + node->data.unary.operand = _rava_parser_parse_unary(parser); + return node; + } + + if (_rava_parser_match(parser, RAVA_TOKEN_INC)) { + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_UNARY_EXPR, + parser->current_token->line, + parser->current_token->column); + node->data.unary.op = RAVA_UNOP_PREINC; + node->data.unary.operand = _rava_parser_parse_unary(parser); + return node; + } + + if (_rava_parser_match(parser, RAVA_TOKEN_DEC)) { + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_UNARY_EXPR, + parser->current_token->line, + parser->current_token->column); + node->data.unary.op = RAVA_UNOP_PREDEC; + node->data.unary.operand = _rava_parser_parse_unary(parser); + return node; + } + + return _rava_parser_parse_postfix(parser); +} + +static RavaASTNode_t* _rava_parser_parse_multiplicative(RavaParser_t *parser) { + RavaASTNode_t *left = _rava_parser_parse_unary(parser); + + while (_rava_parser_check(parser, RAVA_TOKEN_STAR) || + _rava_parser_check(parser, RAVA_TOKEN_SLASH) || + _rava_parser_check(parser, RAVA_TOKEN_PERCENT)) { + + RavaBinaryOp_e op; + if (_rava_parser_match(parser, RAVA_TOKEN_STAR)) { + op = RAVA_BINOP_MUL; + } else if (_rava_parser_match(parser, RAVA_TOKEN_SLASH)) { + op = RAVA_BINOP_DIV; + } else { + _rava_parser_advance(parser); + op = RAVA_BINOP_MOD; + } + + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_BINARY_EXPR, + parser->current_token->line, + parser->current_token->column); + node->data.binary.op = op; + node->data.binary.left = left; + node->data.binary.right = _rava_parser_parse_unary(parser); + left = node; + } + + return left; +} + +static RavaASTNode_t* _rava_parser_parse_additive(RavaParser_t *parser) { + RavaASTNode_t *left = _rava_parser_parse_multiplicative(parser); + + while (_rava_parser_check(parser, RAVA_TOKEN_PLUS) || + _rava_parser_check(parser, RAVA_TOKEN_MINUS)) { + + RavaBinaryOp_e op = _rava_parser_match(parser, RAVA_TOKEN_PLUS) ? + RAVA_BINOP_ADD : RAVA_BINOP_SUB; + if (op == RAVA_BINOP_SUB) _rava_parser_advance(parser); + + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_BINARY_EXPR, + parser->current_token->line, + parser->current_token->column); + node->data.binary.op = op; + node->data.binary.left = left; + node->data.binary.right = _rava_parser_parse_multiplicative(parser); + left = node; + } + + return left; +} + +static RavaASTNode_t* _rava_parser_parse_relational(RavaParser_t *parser) { + RavaASTNode_t *left = _rava_parser_parse_additive(parser); + + while (_rava_parser_check(parser, RAVA_TOKEN_LT) || + _rava_parser_check(parser, RAVA_TOKEN_GT) || + _rava_parser_check(parser, RAVA_TOKEN_LE) || + _rava_parser_check(parser, RAVA_TOKEN_GE)) { + + RavaBinaryOp_e op; + if (_rava_parser_match(parser, RAVA_TOKEN_LT)) op = RAVA_BINOP_LT; + else if (_rava_parser_match(parser, RAVA_TOKEN_GT)) op = RAVA_BINOP_GT; + else if (_rava_parser_match(parser, RAVA_TOKEN_LE)) op = RAVA_BINOP_LE; + else { _rava_parser_advance(parser); op = RAVA_BINOP_GE; } + + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_BINARY_EXPR, + parser->current_token->line, + parser->current_token->column); + node->data.binary.op = op; + node->data.binary.left = left; + node->data.binary.right = _rava_parser_parse_additive(parser); + left = node; + } + + return left; +} + +static RavaASTNode_t* _rava_parser_parse_equality(RavaParser_t *parser) { + RavaASTNode_t *left = _rava_parser_parse_relational(parser); + + while (_rava_parser_check(parser, RAVA_TOKEN_EQUAL) || + _rava_parser_check(parser, RAVA_TOKEN_NE)) { + + RavaBinaryOp_e op = _rava_parser_match(parser, RAVA_TOKEN_EQUAL) ? + RAVA_BINOP_EQ : RAVA_BINOP_NE; + if (op == RAVA_BINOP_NE) _rava_parser_advance(parser); + + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_BINARY_EXPR, + parser->current_token->line, + parser->current_token->column); + node->data.binary.op = op; + node->data.binary.left = left; + node->data.binary.right = _rava_parser_parse_relational(parser); + left = node; + } + + return left; +} + +static RavaASTNode_t* _rava_parser_parse_logical_and(RavaParser_t *parser) { + RavaASTNode_t *left = _rava_parser_parse_equality(parser); + + while (_rava_parser_match(parser, RAVA_TOKEN_AND)) { + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_BINARY_EXPR, + parser->current_token->line, + parser->current_token->column); + node->data.binary.op = RAVA_BINOP_AND; + node->data.binary.left = left; + node->data.binary.right = _rava_parser_parse_equality(parser); + left = node; + } + + return left; +} + +static RavaASTNode_t* _rava_parser_parse_logical_or(RavaParser_t *parser) { + RavaASTNode_t *left = _rava_parser_parse_logical_and(parser); + + while (_rava_parser_match(parser, RAVA_TOKEN_OR)) { + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_BINARY_EXPR, + parser->current_token->line, + parser->current_token->column); + node->data.binary.op = RAVA_BINOP_OR; + node->data.binary.left = left; + node->data.binary.right = _rava_parser_parse_logical_and(parser); + left = node; + } + + return left; +} + +static RavaASTNode_t* _rava_parser_parse_assignment(RavaParser_t *parser) { + RavaASTNode_t *left = _rava_parser_parse_logical_or(parser); + + if (_rava_parser_match(parser, RAVA_TOKEN_ASSIGN)) { + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_ASSIGN_EXPR, + parser->current_token->line, + parser->current_token->column); + node->data.assign.target = left; + node->data.assign.value = _rava_parser_parse_assignment(parser); + return node; + } + + RavaBinaryOp_e compound_op = -1; + if (_rava_parser_match(parser, RAVA_TOKEN_PLUSASSIGN)) { + compound_op = RAVA_BINOP_ADD; + } else if (_rava_parser_match(parser, RAVA_TOKEN_MINUSASSIGN)) { + compound_op = RAVA_BINOP_SUB; + } else if (_rava_parser_match(parser, RAVA_TOKEN_STARASSIGN)) { + compound_op = RAVA_BINOP_MUL; + } else if (_rava_parser_match(parser, RAVA_TOKEN_SLASHASSIGN)) { + compound_op = RAVA_BINOP_DIV; + } else if (_rava_parser_match(parser, RAVA_TOKEN_PERCENTASSIGN)) { + compound_op = RAVA_BINOP_MOD; + } + + if (compound_op != (RavaBinaryOp_e)-1) { + RavaASTNode_t *binary = rava_ast_node_create(RAVA_AST_BINARY_EXPR, + parser->current_token->line, + parser->current_token->column); + binary->data.binary.op = compound_op; + binary->data.binary.left = left; + binary->data.binary.right = _rava_parser_parse_assignment(parser); + + RavaASTNode_t *assign = rava_ast_node_create(RAVA_AST_ASSIGN_EXPR, + parser->current_token->line, + parser->current_token->column); + assign->data.assign.target = left; + assign->data.assign.value = binary; + return assign; + } + + return left; +} + +RavaASTNode_t* _rava_parser_parse_expression(RavaParser_t *parser) { + return _rava_parser_parse_assignment(parser); +} diff --git a/parser/parser_printer.c b/parser/parser_printer.c new file mode 100644 index 0000000..371528c --- /dev/null +++ b/parser/parser_printer.c @@ -0,0 +1,216 @@ +#include "parser.h" +#include +#include + +static void _rava_print_indent(int depth) { + for (int i = 0; i < depth; i++) { + printf(" "); + } +} + +static const char* _rava_ast_node_type_name(RavaASTNodeType_e type) { + switch (type) { + case RAVA_AST_COMPILATION_UNIT: return "CompilationUnit"; + case RAVA_AST_CLASS_DECL: return "ClassDecl"; + case RAVA_AST_METHOD_DECL: return "MethodDecl"; + case RAVA_AST_FIELD_DECL: return "FieldDecl"; + case RAVA_AST_CONSTRUCTOR_DECL: return "ConstructorDecl"; + case RAVA_AST_VAR_DECL: return "VarDecl"; + case RAVA_AST_PARAM_DECL: return "ParamDecl"; + case RAVA_AST_BLOCK_STMT: return "BlockStmt"; + case RAVA_AST_IF_STMT: return "IfStmt"; + case RAVA_AST_WHILE_STMT: return "WhileStmt"; + case RAVA_AST_FOR_STMT: return "ForStmt"; + case RAVA_AST_RETURN_STMT: return "ReturnStmt"; + case RAVA_AST_BREAK_STMT: return "BreakStmt"; + case RAVA_AST_CONTINUE_STMT: return "ContinueStmt"; + case RAVA_AST_EXPR_STMT: return "ExprStmt"; + case RAVA_AST_LITERAL_EXPR: return "LiteralExpr"; + case RAVA_AST_IDENTIFIER_EXPR: return "IdentifierExpr"; + case RAVA_AST_BINARY_EXPR: return "BinaryExpr"; + case RAVA_AST_UNARY_EXPR: return "UnaryExpr"; + case RAVA_AST_ASSIGN_EXPR: return "AssignExpr"; + case RAVA_AST_CALL_EXPR: return "CallExpr"; + case RAVA_AST_MEMBER_ACCESS_EXPR: return "MemberAccessExpr"; + case RAVA_AST_ARRAY_ACCESS_EXPR: return "ArrayAccessExpr"; + case RAVA_AST_NEW_EXPR: return "NewExpr"; + case RAVA_AST_TYPE: return "Type"; + default: return "Unknown"; + } +} + +void rava_ast_print(RavaASTNode_t *node, int depth) { + if (!node) { + _rava_print_indent(depth); + printf("\n"); + return; + } + + _rava_print_indent(depth); + printf("%s", _rava_ast_node_type_name(node->type)); + + switch (node->type) { + case RAVA_AST_CLASS_DECL: + printf(": %s\n", node->data.class_decl.name); + break; + case RAVA_AST_METHOD_DECL: + printf(": %s\n", node->data.method_decl.name); + break; + case RAVA_AST_FIELD_DECL: + case RAVA_AST_VAR_DECL: + case RAVA_AST_PARAM_DECL: + printf(": %s\n", node->data.var_decl.name ? node->data.var_decl.name : ""); + break; + case RAVA_AST_IDENTIFIER_EXPR: + printf(": %s\n", node->data.identifier.name); + break; + case RAVA_AST_LITERAL_EXPR: + printf(": "); + if (node->data.literal.literal_type == RAVA_TOKEN_LITERAL_INTEGER || + node->data.literal.literal_type == RAVA_TOKEN_LITERAL_LONG) { + printf("%lld\n", (long long)node->data.literal.value.int_value); + } else if (node->data.literal.literal_type == RAVA_TOKEN_LITERAL_FLOAT || + node->data.literal.literal_type == RAVA_TOKEN_LITERAL_DOUBLE) { + printf("%f\n", node->data.literal.value.float_value); + } else if (node->data.literal.literal_type == RAVA_TOKEN_LITERAL_STRING) { + printf("\"%s\"\n", node->data.literal.value.string_value); + } else if (node->data.literal.literal_type == RAVA_TOKEN_LITERAL_TRUE) { + printf("true\n"); + } else if (node->data.literal.literal_type == RAVA_TOKEN_LITERAL_FALSE) { + printf("false\n"); + } else if (node->data.literal.literal_type == RAVA_TOKEN_LITERAL_NULL) { + printf("null\n"); + } else { + printf("?\n"); + } + break; + case RAVA_AST_TYPE: + printf(": %s%s\n", node->data.type.type_name, + node->data.type.is_array ? "[]" : ""); + break; + case RAVA_AST_MEMBER_ACCESS_EXPR: + printf(": .%s\n", node->data.member_access.member); + break; + default: + printf("\n"); + break; + } + + if (node->type == RAVA_AST_BINARY_EXPR) { + _rava_print_indent(depth + 1); + printf("Left:\n"); + rava_ast_print(node->data.binary.left, depth + 2); + _rava_print_indent(depth + 1); + printf("Right:\n"); + rava_ast_print(node->data.binary.right, depth + 2); + } else if (node->type == RAVA_AST_UNARY_EXPR) { + _rava_print_indent(depth + 1); + printf("Operand:\n"); + rava_ast_print(node->data.unary.operand, depth + 2); + } else if (node->type == RAVA_AST_ASSIGN_EXPR) { + _rava_print_indent(depth + 1); + printf("Target:\n"); + rava_ast_print(node->data.assign.target, depth + 2); + _rava_print_indent(depth + 1); + printf("Value:\n"); + rava_ast_print(node->data.assign.value, depth + 2); + } else if (node->type == RAVA_AST_IF_STMT) { + _rava_print_indent(depth + 1); + printf("Condition:\n"); + rava_ast_print(node->data.if_stmt.condition, depth + 2); + _rava_print_indent(depth + 1); + printf("Then:\n"); + rava_ast_print(node->data.if_stmt.then_stmt, depth + 2); + if (node->data.if_stmt.else_stmt) { + _rava_print_indent(depth + 1); + printf("Else:\n"); + rava_ast_print(node->data.if_stmt.else_stmt, depth + 2); + } + } else if (node->type == RAVA_AST_WHILE_STMT) { + _rava_print_indent(depth + 1); + printf("Condition:\n"); + rava_ast_print(node->data.while_stmt.condition, depth + 2); + _rava_print_indent(depth + 1); + printf("Body:\n"); + rava_ast_print(node->data.while_stmt.body, depth + 2); + } else if (node->type == RAVA_AST_FOR_STMT) { + if (node->data.for_stmt.init) { + _rava_print_indent(depth + 1); + printf("Init:\n"); + rava_ast_print(node->data.for_stmt.init, depth + 2); + } + if (node->data.for_stmt.condition) { + _rava_print_indent(depth + 1); + printf("Condition:\n"); + rava_ast_print(node->data.for_stmt.condition, depth + 2); + } + if (node->data.for_stmt.update) { + _rava_print_indent(depth + 1); + printf("Update:\n"); + rava_ast_print(node->data.for_stmt.update, depth + 2); + } + _rava_print_indent(depth + 1); + printf("Body:\n"); + rava_ast_print(node->data.for_stmt.body, depth + 2); + } else if (node->type == RAVA_AST_RETURN_STMT) { + if (node->data.return_stmt.value) { + _rava_print_indent(depth + 1); + printf("Value:\n"); + rava_ast_print(node->data.return_stmt.value, depth + 2); + } + } else if (node->type == RAVA_AST_VAR_DECL || node->type == RAVA_AST_FIELD_DECL || node->type == RAVA_AST_PARAM_DECL) { + if (node->data.var_decl.type) { + _rava_print_indent(depth + 1); + printf("Type:\n"); + rava_ast_print(node->data.var_decl.type, depth + 2); + } + if (node->data.var_decl.initializer) { + _rava_print_indent(depth + 1); + printf("Initializer:\n"); + rava_ast_print(node->data.var_decl.initializer, depth + 2); + } + } else if (node->type == RAVA_AST_METHOD_DECL) { + if (node->data.method_decl.return_type) { + _rava_print_indent(depth + 1); + printf("Return Type:\n"); + rava_ast_print(node->data.method_decl.return_type, depth + 2); + } + } else if (node->type == RAVA_AST_CALL_EXPR) { + _rava_print_indent(depth + 1); + printf("Callee:\n"); + rava_ast_print(node->data.call.callee, depth + 2); + if (node->data.call.arguments_count > 0) { + _rava_print_indent(depth + 1); + printf("Arguments:\n"); + for (size_t i = 0; i < node->data.call.arguments_count; i++) { + rava_ast_print(node->data.call.arguments[i], depth + 2); + } + } + } else if (node->type == RAVA_AST_MEMBER_ACCESS_EXPR) { + _rava_print_indent(depth + 1); + printf("Object:\n"); + rava_ast_print(node->data.member_access.object, depth + 2); + } else if (node->type == RAVA_AST_ARRAY_ACCESS_EXPR) { + _rava_print_indent(depth + 1); + printf("Array:\n"); + rava_ast_print(node->data.array_access.array, depth + 2); + _rava_print_indent(depth + 1); + printf("Index:\n"); + rava_ast_print(node->data.array_access.index, depth + 2); + } else if (node->type == RAVA_AST_NEW_EXPR) { + _rava_print_indent(depth + 1); + printf("Type:\n"); + rava_ast_print(node->data.new_expr.type, depth + 2); + if (node->data.new_expr.arguments_count > 0) { + _rava_print_indent(depth + 1); + printf("Arguments:\n"); + for (size_t i = 0; i < node->data.new_expr.arguments_count; i++) { + rava_ast_print(node->data.new_expr.arguments[i], depth + 2); + } + } + } + + for (size_t i = 0; i < node->children_count; i++) { + rava_ast_print(node->children[i], depth + 1); + } +} diff --git a/parser/parser_statements.c b/parser/parser_statements.c new file mode 100644 index 0000000..bd5e109 --- /dev/null +++ b/parser/parser_statements.c @@ -0,0 +1,216 @@ +#define _POSIX_C_SOURCE 200809L +#include "parser.h" +#include +#include + +extern void _rava_parser_advance(RavaParser_t *parser); +extern bool _rava_parser_check(RavaParser_t *parser, RavaTokenType_e type); +extern bool _rava_parser_match(RavaParser_t *parser, RavaTokenType_e type); +extern bool _rava_parser_expect(RavaParser_t *parser, RavaTokenType_e type, const char *message); +extern RavaASTNode_t* _rava_parser_parse_expression(RavaParser_t *parser); +extern RavaASTNode_t* _rava_parser_parse_type(RavaParser_t *parser); + +RavaASTNode_t* _rava_parser_parse_statement(RavaParser_t *parser); +RavaASTNode_t* _rava_parser_parse_block(RavaParser_t *parser); + +RavaASTNode_t* _rava_parser_parse_block(RavaParser_t *parser) { + RavaASTNode_t *block = rava_ast_node_create(RAVA_AST_BLOCK_STMT, + parser->current_token->line, + parser->current_token->column); + + _rava_parser_expect(parser, RAVA_TOKEN_LBRACE, "Expected '{' at start of block"); + + while (!_rava_parser_check(parser, RAVA_TOKEN_RBRACE) && + !_rava_parser_check(parser, RAVA_TOKEN_EOF)) { + RavaASTNode_t *stmt = _rava_parser_parse_statement(parser); + if (stmt) { + rava_ast_node_add_child(block, stmt); + } + } + + _rava_parser_expect(parser, RAVA_TOKEN_RBRACE, "Expected '}' at end of block"); + + return block; +} + +RavaASTNode_t* _rava_parser_parse_statement(RavaParser_t *parser) { + if (_rava_parser_check(parser, RAVA_TOKEN_LBRACE)) { + return _rava_parser_parse_block(parser); + } + + if (_rava_parser_match(parser, RAVA_TOKEN_KEYWORD_IF)) { + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_IF_STMT, + parser->current_token->line, + parser->current_token->column); + + _rava_parser_expect(parser, RAVA_TOKEN_LPAREN, "Expected '(' after 'if'"); + node->data.if_stmt.condition = _rava_parser_parse_expression(parser); + _rava_parser_expect(parser, RAVA_TOKEN_RPAREN, "Expected ')' after if condition"); + + node->data.if_stmt.then_stmt = _rava_parser_parse_statement(parser); + + if (_rava_parser_match(parser, RAVA_TOKEN_KEYWORD_ELSE)) { + node->data.if_stmt.else_stmt = _rava_parser_parse_statement(parser); + } else { + node->data.if_stmt.else_stmt = NULL; + } + + return node; + } + + if (_rava_parser_match(parser, RAVA_TOKEN_KEYWORD_WHILE)) { + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_WHILE_STMT, + parser->current_token->line, + parser->current_token->column); + + _rava_parser_expect(parser, RAVA_TOKEN_LPAREN, "Expected '(' after 'while'"); + node->data.while_stmt.condition = _rava_parser_parse_expression(parser); + _rava_parser_expect(parser, RAVA_TOKEN_RPAREN, "Expected ')' after while condition"); + + node->data.while_stmt.body = _rava_parser_parse_statement(parser); + + return node; + } + + if (_rava_parser_match(parser, RAVA_TOKEN_KEYWORD_FOR)) { + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_FOR_STMT, + parser->current_token->line, + parser->current_token->column); + + _rava_parser_expect(parser, RAVA_TOKEN_LPAREN, "Expected '(' after 'for'"); + + if (!_rava_parser_check(parser, RAVA_TOKEN_SEMICOLON)) { + bool is_type_decl = false; + if (_rava_parser_check(parser, RAVA_TOKEN_KEYWORD_INT) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_LONG) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_DOUBLE) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_BOOLEAN) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_CHAR)) { + is_type_decl = true; + } else if (_rava_parser_check(parser, RAVA_TOKEN_IDENTIFIER)) { + if (parser->peek_token && parser->peek_token->type == RAVA_TOKEN_IDENTIFIER) { + is_type_decl = true; + } + } + if (is_type_decl) { + RavaASTNode_t *type = _rava_parser_parse_type(parser); + RavaASTNode_t *var_decl = rava_ast_node_create(RAVA_AST_VAR_DECL, + parser->current_token->line, + parser->current_token->column); + var_decl->data.var_decl.type = type; + if (_rava_parser_check(parser, RAVA_TOKEN_IDENTIFIER)) { + var_decl->data.var_decl.name = strdup(parser->current_token->lexeme); + _rava_parser_advance(parser); + } + if (_rava_parser_match(parser, RAVA_TOKEN_ASSIGN)) { + var_decl->data.var_decl.initializer = _rava_parser_parse_expression(parser); + } + node->data.for_stmt.init = var_decl; + } else { + node->data.for_stmt.init = _rava_parser_parse_expression(parser); + } + } else { + node->data.for_stmt.init = NULL; + } + _rava_parser_expect(parser, RAVA_TOKEN_SEMICOLON, "Expected ';' after for init"); + + if (!_rava_parser_check(parser, RAVA_TOKEN_SEMICOLON)) { + node->data.for_stmt.condition = _rava_parser_parse_expression(parser); + } else { + node->data.for_stmt.condition = NULL; + } + _rava_parser_expect(parser, RAVA_TOKEN_SEMICOLON, "Expected ';' after for condition"); + + if (!_rava_parser_check(parser, RAVA_TOKEN_RPAREN)) { + node->data.for_stmt.update = _rava_parser_parse_expression(parser); + } else { + node->data.for_stmt.update = NULL; + } + _rava_parser_expect(parser, RAVA_TOKEN_RPAREN, "Expected ')' after for update"); + + node->data.for_stmt.body = _rava_parser_parse_statement(parser); + + return node; + } + + if (_rava_parser_match(parser, RAVA_TOKEN_KEYWORD_RETURN)) { + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_RETURN_STMT, + parser->current_token->line, + parser->current_token->column); + + if (!_rava_parser_check(parser, RAVA_TOKEN_SEMICOLON)) { + node->data.return_stmt.value = _rava_parser_parse_expression(parser); + } else { + node->data.return_stmt.value = NULL; + } + + _rava_parser_expect(parser, RAVA_TOKEN_SEMICOLON, "Expected ';' after return statement"); + + return node; + } + + if (_rava_parser_match(parser, RAVA_TOKEN_KEYWORD_BREAK)) { + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_BREAK_STMT, + parser->current_token->line, + parser->current_token->column); + _rava_parser_expect(parser, RAVA_TOKEN_SEMICOLON, "Expected ';' after break"); + return node; + } + + if (_rava_parser_match(parser, RAVA_TOKEN_KEYWORD_CONTINUE)) { + RavaASTNode_t *node = rava_ast_node_create(RAVA_AST_CONTINUE_STMT, + parser->current_token->line, + parser->current_token->column); + _rava_parser_expect(parser, RAVA_TOKEN_SEMICOLON, "Expected ';' after continue"); + return node; + } + + bool is_type_decl = false; + if (_rava_parser_check(parser, RAVA_TOKEN_KEYWORD_INT) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_BOOLEAN) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_CHAR) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_BYTE) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_SHORT) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_LONG) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_FLOAT) || + _rava_parser_check(parser, RAVA_TOKEN_KEYWORD_DOUBLE)) { + is_type_decl = true; + } else if (_rava_parser_check(parser, RAVA_TOKEN_IDENTIFIER)) { + if (parser->peek_token && parser->peek_token->type == RAVA_TOKEN_IDENTIFIER) { + is_type_decl = true; + } + } + + if (is_type_decl) { + RavaASTNode_t *type = _rava_parser_parse_type(parser); + RavaASTNode_t *var_decl = rava_ast_node_create(RAVA_AST_VAR_DECL, + parser->current_token->line, + parser->current_token->column); + var_decl->data.var_decl.type = type; + + if (_rava_parser_check(parser, RAVA_TOKEN_IDENTIFIER)) { + var_decl->data.var_decl.name = strdup(parser->current_token->lexeme); + _rava_parser_advance(parser); + } + + if (_rava_parser_match(parser, RAVA_TOKEN_ASSIGN)) { + var_decl->data.var_decl.initializer = _rava_parser_parse_expression(parser); + } else { + var_decl->data.var_decl.initializer = NULL; + } + + _rava_parser_expect(parser, RAVA_TOKEN_SEMICOLON, "Expected ';' after variable declaration"); + + return var_decl; + } + + RavaASTNode_t *expr = _rava_parser_parse_expression(parser); + _rava_parser_expect(parser, RAVA_TOKEN_SEMICOLON, "Expected ';' after expression"); + + RavaASTNode_t *stmt = rava_ast_node_create(RAVA_AST_EXPR_STMT, + parser->current_token->line, + parser->current_token->column); + rava_ast_node_add_child(stmt, expr); + + return stmt; +} diff --git a/runtime/fastframe.c b/runtime/fastframe.c new file mode 100644 index 0000000..65d023c --- /dev/null +++ b/runtime/fastframe.c @@ -0,0 +1,48 @@ +#include "fastframe.h" +#include + +FastFrame_t rava_frame_pool[RAVA_MAX_CALL_DEPTH]; +size_t rava_frame_depth = 0; + +void rava_fastframe_init(void) { + rava_frame_depth = 0; + memset(rava_frame_pool, 0, sizeof(rava_frame_pool)); +} + +FastFrame_t* rava_fastframe_push(RavaMethod_t* method, int local_count) { + if (rava_frame_depth >= RAVA_MAX_CALL_DEPTH) { + return NULL; + } + + FastFrame_t* frame = &rava_frame_pool[rava_frame_depth++]; + frame->method = method; + frame->stack_top = 0; + frame->pc = 0; + frame->has_this = false; + frame->this_ref = rava_nanbox_null(); + + if (local_count > 0 && local_count <= RAVA_MAX_LOCALS_FIXED) { + memset(frame->locals, 0, (size_t)local_count * sizeof(RavaNanboxValue_t)); + } + + return frame; +} + +void rava_fastframe_pop(void) { + if (rava_frame_depth > 0) { + rava_frame_depth--; + } +} + +FastFrame_t* rava_fastframe_current(void) { + if (rava_frame_depth == 0) return NULL; + return &rava_frame_pool[rava_frame_depth - 1]; +} + +size_t rava_fastframe_get_depth(void) { + return rava_frame_depth; +} + +void rava_fastframe_reset(void) { + rava_frame_depth = 0; +} diff --git a/runtime/fastframe.h b/runtime/fastframe.h new file mode 100644 index 0000000..3c952a5 --- /dev/null +++ b/runtime/fastframe.h @@ -0,0 +1,63 @@ +#ifndef RAVA_FASTFRAME_H +#define RAVA_FASTFRAME_H + +#include +#include +#include +#include +#include "nanbox.h" +#include "../ir/ir.h" + +#define RAVA_MAX_CALL_DEPTH 1024 +#define RAVA_MAX_LOCALS_FIXED 64 +#define RAVA_MAX_STACK_FIXED 512 + +typedef struct { + RavaMethod_t* method; + RavaNanboxValue_t locals[RAVA_MAX_LOCALS_FIXED]; + RavaNanboxValue_t stack[RAVA_MAX_STACK_FIXED]; + size_t stack_top; + size_t pc; + bool has_this; + RavaNanboxValue_t this_ref; +} FastFrame_t; + +extern FastFrame_t rava_frame_pool[RAVA_MAX_CALL_DEPTH]; +extern size_t rava_frame_depth; + +void rava_fastframe_init(void); +FastFrame_t* rava_fastframe_push(RavaMethod_t* method, int local_count); +void rava_fastframe_pop(void); +FastFrame_t* rava_fastframe_current(void); +size_t rava_fastframe_get_depth(void); +void rava_fastframe_reset(void); + +static inline void rava_fastframe_stack_push(FastFrame_t* frame, RavaNanboxValue_t v) { + if (frame->stack_top < RAVA_MAX_STACK_FIXED) { + frame->stack[frame->stack_top++] = v; + } +} + +static inline RavaNanboxValue_t rava_fastframe_stack_pop(FastFrame_t* frame) { + if (frame->stack_top > 0) { + return frame->stack[--frame->stack_top]; + } + return rava_nanbox_null(); +} + +static inline RavaNanboxValue_t rava_fastframe_stack_peek(FastFrame_t* frame) { + if (frame->stack_top > 0) { + return frame->stack[frame->stack_top - 1]; + } + return rava_nanbox_null(); +} + +static inline void rava_fastframe_stack_clear(FastFrame_t* frame) { + frame->stack_top = 0; +} + +static inline bool rava_fastframe_stack_is_empty(FastFrame_t* frame) { + return frame->stack_top == 0; +} + +#endif diff --git a/runtime/labeltable.c b/runtime/labeltable.c new file mode 100644 index 0000000..16e34f3 --- /dev/null +++ b/runtime/labeltable.c @@ -0,0 +1,52 @@ +#include "labeltable.h" +#include +#include + +LabelTable_t* rava_labeltable_create(RavaInstructionList_t* instructions) { + if (!instructions) return NULL; + + LabelTable_t* table = malloc(sizeof(LabelTable_t)); + if (!table) return NULL; + + int max_label = 0; + for (size_t i = 0; i < instructions->count; i++) { + if (instructions->instructions[i].opcode == RAVA_OP_LABEL) { + int lid = instructions->instructions[i].operand.label_id; + if (lid > max_label) max_label = lid; + } + } + + table->max_label_id = (size_t)max_label; + table->label_to_pc = calloc((size_t)(max_label + 1), sizeof(int)); + if (!table->label_to_pc) { + free(table); + return NULL; + } + + for (int i = 0; i <= max_label; i++) { + table->label_to_pc[i] = -1; + } + + for (size_t i = 0; i < instructions->count; i++) { + if (instructions->instructions[i].opcode == RAVA_OP_LABEL) { + int lid = instructions->instructions[i].operand.label_id; + table->label_to_pc[lid] = (int)(i + 1); + } + } + + return table; +} + +void rava_labeltable_destroy(LabelTable_t* table) { + if (!table) return; + free(table->label_to_pc); + free(table); +} + +size_t rava_labeltable_lookup(LabelTable_t* table, int label_id) { + if (!table || label_id < 0 || (size_t)label_id > table->max_label_id) { + return 0; + } + int pc = table->label_to_pc[label_id]; + return (pc >= 0) ? (size_t)pc : 0; +} diff --git a/runtime/labeltable.h b/runtime/labeltable.h new file mode 100644 index 0000000..9b190cb --- /dev/null +++ b/runtime/labeltable.h @@ -0,0 +1,17 @@ +#ifndef RAVA_LABELTABLE_H +#define RAVA_LABELTABLE_H + +#include +#include +#include "../ir/ir.h" + +typedef struct LabelTable_s { + int* label_to_pc; + size_t max_label_id; +} LabelTable_t; + +LabelTable_t* rava_labeltable_create(RavaInstructionList_t* instructions); +void rava_labeltable_destroy(LabelTable_t* table); +size_t rava_labeltable_lookup(LabelTable_t* table, int label_id); + +#endif diff --git a/runtime/methodcache.c b/runtime/methodcache.c new file mode 100644 index 0000000..a6af2ef --- /dev/null +++ b/runtime/methodcache.c @@ -0,0 +1,57 @@ +#include "methodcache.h" +#include +#include + +MethodCache_t* rava_methodcache_create(void) { + MethodCache_t* cache = malloc(sizeof(MethodCache_t)); + if (cache) { + memset(cache, 0, sizeof(MethodCache_t)); + } + return cache; +} + +void rava_methodcache_destroy(MethodCache_t* cache) { + if (cache) free(cache); +} + +RavaMethod_t* rava_methodcache_lookup(MethodCache_t* cache, + const char* classname, + const char* methodname) { + if (!cache || !classname || !methodname) return NULL; + + uint32_t class_hash = rava_methodcache_hash(classname); + uint32_t method_hash = rava_methodcache_hash(methodname); + uint32_t idx = (class_hash ^ method_hash) & RAVA_METHOD_CACHE_MASK; + + MethodCacheEntry_t* entry = &cache->entries[idx]; + + if (entry->class_hash == class_hash && + entry->method_hash == method_hash && + entry->method != NULL) { + return entry->method; + } + + return NULL; +} + +void rava_methodcache_insert(MethodCache_t* cache, + const char* classname, + const char* methodname, + RavaMethod_t* method) { + if (!cache || !classname || !methodname) return; + + uint32_t class_hash = rava_methodcache_hash(classname); + uint32_t method_hash = rava_methodcache_hash(methodname); + uint32_t idx = (class_hash ^ method_hash) & RAVA_METHOD_CACHE_MASK; + + MethodCacheEntry_t* entry = &cache->entries[idx]; + entry->class_hash = class_hash; + entry->method_hash = method_hash; + entry->method = method; +} + +void rava_methodcache_clear(MethodCache_t* cache) { + if (cache) { + memset(cache->entries, 0, sizeof(cache->entries)); + } +} diff --git a/runtime/methodcache.h b/runtime/methodcache.h new file mode 100644 index 0000000..70bd9e1 --- /dev/null +++ b/runtime/methodcache.h @@ -0,0 +1,41 @@ +#ifndef RAVA_METHODCACHE_H +#define RAVA_METHODCACHE_H + +#include +#include +#include "../ir/ir.h" + +#define RAVA_METHOD_CACHE_SIZE 256 +#define RAVA_METHOD_CACHE_MASK (RAVA_METHOD_CACHE_SIZE - 1) + +typedef struct { + uint32_t class_hash; + uint32_t method_hash; + RavaMethod_t* method; +} MethodCacheEntry_t; + +typedef struct MethodCache_s { + MethodCacheEntry_t entries[RAVA_METHOD_CACHE_SIZE]; +} MethodCache_t; + +static inline uint32_t rava_methodcache_hash(const char* str) { + uint32_t hash = 5381; + while (*str) { + hash = ((hash << 5) + hash) ^ (uint32_t)*str; + str++; + } + return hash; +} + +MethodCache_t* rava_methodcache_create(void); +void rava_methodcache_destroy(MethodCache_t* cache); +RavaMethod_t* rava_methodcache_lookup(MethodCache_t* cache, + const char* classname, + const char* methodname); +void rava_methodcache_insert(MethodCache_t* cache, + const char* classname, + const char* methodname, + RavaMethod_t* method); +void rava_methodcache_clear(MethodCache_t* cache); + +#endif diff --git a/runtime/nanbox.h b/runtime/nanbox.h new file mode 100644 index 0000000..f30d0ec --- /dev/null +++ b/runtime/nanbox.h @@ -0,0 +1,156 @@ +#ifndef RAVA_NANBOX_H +#define RAVA_NANBOX_H + +#include +#include +#include +#include + +typedef uint64_t RavaNanboxValue_t; + +#define RAVA_SIGN_BIT 0x8000000000000000ULL +#define RAVA_EXPONENT_MASK 0x7FF0000000000000ULL +#define RAVA_QNAN 0x7FF8000000000000ULL +#define RAVA_TAG_MASK 0xFFFF000000000000ULL +#define RAVA_PAYLOAD_MASK 0x0000FFFFFFFFFFFFULL + +#define RAVA_TAG_INT (RAVA_QNAN | 0x0001000000000000ULL) +#define RAVA_TAG_LONG (RAVA_QNAN | 0x0002000000000000ULL) +#define RAVA_TAG_BOOL (RAVA_QNAN | 0x0003000000000000ULL) +#define RAVA_TAG_NULL (RAVA_QNAN | 0x0004000000000000ULL) +#define RAVA_TAG_OBJECT (RAVA_QNAN | 0x0005000000000000ULL) +#define RAVA_TAG_STRING (RAVA_QNAN | 0x0006000000000000ULL) +#define RAVA_TAG_ARRAY (RAVA_QNAN | 0x0007000000000000ULL) + +static inline RavaNanboxValue_t rava_nanbox_int(int32_t v) { + return RAVA_TAG_INT | (uint64_t)(uint32_t)v; +} + +static inline RavaNanboxValue_t rava_nanbox_long(int64_t v) { + return RAVA_TAG_LONG | ((uint64_t)v & RAVA_PAYLOAD_MASK); +} + +static inline RavaNanboxValue_t rava_nanbox_bool(bool v) { + return RAVA_TAG_BOOL | (v ? 1ULL : 0ULL); +} + +static inline RavaNanboxValue_t rava_nanbox_null(void) { + return RAVA_TAG_NULL; +} + +static inline RavaNanboxValue_t rava_nanbox_double(double v) { + union { double d; uint64_t u; } u = {.d = v}; + return u.u; +} + +static inline RavaNanboxValue_t rava_nanbox_object(void* ptr) { + return RAVA_TAG_OBJECT | ((uint64_t)(uintptr_t)ptr & RAVA_PAYLOAD_MASK); +} + +static inline RavaNanboxValue_t rava_nanbox_string(const char* ptr) { + return RAVA_TAG_STRING | ((uint64_t)(uintptr_t)ptr & RAVA_PAYLOAD_MASK); +} + +static inline RavaNanboxValue_t rava_nanbox_array(void* ptr) { + return RAVA_TAG_ARRAY | ((uint64_t)(uintptr_t)ptr & RAVA_PAYLOAD_MASK); +} + +static inline bool rava_nanbox_is_int(RavaNanboxValue_t v) { + return (v & RAVA_TAG_MASK) == RAVA_TAG_INT; +} + +static inline bool rava_nanbox_is_long(RavaNanboxValue_t v) { + return (v & RAVA_TAG_MASK) == RAVA_TAG_LONG; +} + +static inline bool rava_nanbox_is_bool(RavaNanboxValue_t v) { + return (v & RAVA_TAG_MASK) == RAVA_TAG_BOOL; +} + +static inline bool rava_nanbox_is_null(RavaNanboxValue_t v) { + return v == RAVA_TAG_NULL; +} + +static inline bool rava_nanbox_is_object(RavaNanboxValue_t v) { + return (v & RAVA_TAG_MASK) == RAVA_TAG_OBJECT; +} + +static inline bool rava_nanbox_is_string(RavaNanboxValue_t v) { + return (v & RAVA_TAG_MASK) == RAVA_TAG_STRING; +} + +static inline bool rava_nanbox_is_array(RavaNanboxValue_t v) { + return (v & RAVA_TAG_MASK) == RAVA_TAG_ARRAY; +} + +static inline bool rava_nanbox_is_double(RavaNanboxValue_t v) { + return (v & RAVA_QNAN) != RAVA_QNAN; +} + +static inline int32_t rava_nanbox_as_int(RavaNanboxValue_t v) { + return (int32_t)(v & 0xFFFFFFFF); +} + +static inline int64_t rava_nanbox_as_long(RavaNanboxValue_t v) { + int64_t payload = (int64_t)(v & RAVA_PAYLOAD_MASK); + if (payload & 0x800000000000ULL) { + payload |= (int64_t)0xFFFF000000000000ULL; + } + return payload; +} + +static inline bool rava_nanbox_as_bool(RavaNanboxValue_t v) { + return (v & 1) != 0; +} + +static inline double rava_nanbox_as_double(RavaNanboxValue_t v) { + union { double d; uint64_t u; } u = {.u = v}; + return u.d; +} + +static inline void* rava_nanbox_as_object(RavaNanboxValue_t v) { + return (void*)(uintptr_t)(v & RAVA_PAYLOAD_MASK); +} + +static inline const char* rava_nanbox_as_string(RavaNanboxValue_t v) { + return (const char*)(uintptr_t)(v & RAVA_PAYLOAD_MASK); +} + +static inline void* rava_nanbox_as_array(RavaNanboxValue_t v) { + return (void*)(uintptr_t)(v & RAVA_PAYLOAD_MASK); +} + +static inline int32_t rava_nanbox_to_int(RavaNanboxValue_t v) { + if (rava_nanbox_is_int(v)) return rava_nanbox_as_int(v); + if (rava_nanbox_is_long(v)) return (int32_t)rava_nanbox_as_long(v); + if (rava_nanbox_is_bool(v)) return rava_nanbox_as_bool(v) ? 1 : 0; + if (rava_nanbox_is_double(v)) return (int32_t)rava_nanbox_as_double(v); + return 0; +} + +static inline int64_t rava_nanbox_to_long(RavaNanboxValue_t v) { + if (rava_nanbox_is_int(v)) return (int64_t)rava_nanbox_as_int(v); + if (rava_nanbox_is_long(v)) return rava_nanbox_as_long(v); + if (rava_nanbox_is_bool(v)) return rava_nanbox_as_bool(v) ? 1 : 0; + if (rava_nanbox_is_double(v)) return (int64_t)rava_nanbox_as_double(v); + return 0; +} + +static inline double rava_nanbox_to_double(RavaNanboxValue_t v) { + if (rava_nanbox_is_double(v)) return rava_nanbox_as_double(v); + if (rava_nanbox_is_int(v)) return (double)rava_nanbox_as_int(v); + if (rava_nanbox_is_long(v)) return (double)rava_nanbox_as_long(v); + if (rava_nanbox_is_bool(v)) return rava_nanbox_as_bool(v) ? 1.0 : 0.0; + return 0.0; +} + +static inline bool rava_nanbox_to_bool(RavaNanboxValue_t v) { + if (rava_nanbox_is_null(v)) return false; + if (rava_nanbox_is_bool(v)) return rava_nanbox_as_bool(v); + if (rava_nanbox_is_int(v)) return rava_nanbox_as_int(v) != 0; + if (rava_nanbox_is_long(v)) return rava_nanbox_as_long(v) != 0; + if (rava_nanbox_is_double(v)) return rava_nanbox_as_double(v) != 0.0; + return true; +} + +#endif diff --git a/runtime/runtime.c b/runtime/runtime.c new file mode 100644 index 0000000..b685adb --- /dev/null +++ b/runtime/runtime.c @@ -0,0 +1,2810 @@ +#define _POSIX_C_SOURCE 200809L +#include "runtime.h" +#include "labeltable.h" +#include "methodcache.h" +#include "nanbox.h" +#include "fastframe.h" +#include "superinst.h" +#include +#include +#include +#include + +#define STACK_ENSURE_CAPACITY(stack) do { \ + if ((stack)->top >= (stack)->capacity) { \ + (stack)->capacity *= 2; \ + (stack)->values = realloc((stack)->values, sizeof(RavaValue_t) * (stack)->capacity); \ + } \ +} while(0) + +#define STACK_PUSH(stack, val) do { \ + STACK_ENSURE_CAPACITY(stack); \ + (stack)->values[(stack)->top++] = (val); \ +} while(0) + +#define STACK_POP(stack) ((stack)->top > 0 ? (stack)->values[--(stack)->top] : (RavaValue_t){0}) + +#define STACK_PEEK(stack) ((stack)->top > 0 ? (stack)->values[(stack)->top - 1] : (RavaValue_t){0}) + +#define STACK_PUSH_INT(stack, v) do { \ + STACK_ENSURE_CAPACITY(stack); \ + RavaValue_t _v; _v.type = RAVA_VAL_INT; _v.data.int_val = (v); \ + (stack)->values[(stack)->top++] = _v; \ +} while(0) + +#define STACK_PUSH_LONG(stack, v) do { \ + STACK_ENSURE_CAPACITY(stack); \ + RavaValue_t _v; _v.type = RAVA_VAL_LONG; _v.data.long_val = (v); \ + (stack)->values[(stack)->top++] = _v; \ +} while(0) + +#define STACK_PUSH_BOOL(stack, v) do { \ + STACK_ENSURE_CAPACITY(stack); \ + RavaValue_t _v; _v.type = RAVA_VAL_BOOLEAN; _v.data.bool_val = (v); \ + (stack)->values[(stack)->top++] = _v; \ +} while(0) + +#define VALUE_IS_INT(v) ((v).type == RAVA_VAL_INT) +#define VALUE_IS_LONG(v) ((v).type == RAVA_VAL_LONG) +#define VALUE_AS_INT_FAST(v) ((v).data.int_val) +#define VALUE_AS_LONG_FAST(v) ((v).data.long_val) + +RavaValue_t rava_value_int(int32_t value) { + RavaValue_t val; + val.type = RAVA_VAL_INT; + val.data.int_val = value; + return val; +} + +RavaValue_t rava_value_long(int64_t value) { + RavaValue_t val; + val.type = RAVA_VAL_LONG; + val.data.long_val = value; + return val; +} + +RavaValue_t rava_value_float(float value) { + RavaValue_t val; + val.type = RAVA_VAL_FLOAT; + val.data.float_val = value; + return val; +} + +RavaValue_t rava_value_double(double value) { + RavaValue_t val; + val.type = RAVA_VAL_DOUBLE; + val.data.double_val = value; + return val; +} + +RavaValue_t rava_value_boolean(bool value) { + RavaValue_t val; + val.type = RAVA_VAL_BOOLEAN; + val.data.bool_val = value; + return val; +} + +RavaValue_t rava_value_null() { + RavaValue_t val; + val.type = RAVA_VAL_NULL; + val.data.object_val = NULL; + return val; +} + +RavaValue_t rava_value_array(RavaArray_t *array) { + RavaValue_t val; + val.type = RAVA_VAL_ARRAY; + val.data.array_val = array; + return val; +} + +RavaValue_t rava_value_string(const char *str) { + RavaValue_t val; + val.type = RAVA_VAL_STRING; + val.data.string_val = str ? strdup(str) : NULL; + return val; +} + +const char* rava_value_as_string(RavaValue_t value) { + if (value.type == RAVA_VAL_STRING) { + return value.data.string_val ? value.data.string_val : ""; + } + return ""; +} + +static inline RavaNanboxValue_t rava_value_to_nanbox(RavaValue_t val) { + switch (val.type) { + case RAVA_VAL_INT: return rava_nanbox_int(val.data.int_val); + case RAVA_VAL_LONG: return rava_nanbox_long(val.data.long_val); + case RAVA_VAL_DOUBLE: return rava_nanbox_double(val.data.double_val); + case RAVA_VAL_FLOAT: return rava_nanbox_double((double)val.data.float_val); + case RAVA_VAL_BOOLEAN: return rava_nanbox_bool(val.data.bool_val); + case RAVA_VAL_NULL: return rava_nanbox_null(); + case RAVA_VAL_STRING: return rava_nanbox_string(val.data.string_val); + case RAVA_VAL_OBJECT: return rava_nanbox_object(val.data.object_val); + case RAVA_VAL_ARRAY: return rava_nanbox_array(val.data.array_val); + default: return rava_nanbox_null(); + } +} + +static inline RavaValue_t rava_nanbox_to_value(RavaNanboxValue_t nb) { + RavaValue_t val; + if (rava_nanbox_is_int(nb)) { + val.type = RAVA_VAL_INT; + val.data.int_val = rava_nanbox_as_int(nb); + } else if (rava_nanbox_is_long(nb)) { + val.type = RAVA_VAL_LONG; + val.data.long_val = rava_nanbox_as_long(nb); + } else if (rava_nanbox_is_double(nb)) { + val.type = RAVA_VAL_DOUBLE; + val.data.double_val = rava_nanbox_as_double(nb); + } else if (rava_nanbox_is_bool(nb)) { + val.type = RAVA_VAL_BOOLEAN; + val.data.bool_val = rava_nanbox_as_bool(nb); + } else if (rava_nanbox_is_null(nb)) { + val.type = RAVA_VAL_NULL; + val.data.object_val = NULL; + } else if (rava_nanbox_is_string(nb)) { + val.type = RAVA_VAL_STRING; + val.data.string_val = (char*)rava_nanbox_as_string(nb); + } else if (rava_nanbox_is_object(nb)) { + val.type = RAVA_VAL_OBJECT; + val.data.object_val = rava_nanbox_as_object(nb); + } else if (rava_nanbox_is_array(nb)) { + val.type = RAVA_VAL_ARRAY; + val.data.array_val = rava_nanbox_as_array(nb); + } else { + val.type = RAVA_VAL_NULL; + val.data.object_val = NULL; + } + return val; +} + +RavaArray_t* rava_array_create(RavaValueType_e element_type, size_t length) { + if (length > 1000000) { + length = 1000000; + } + + RavaArray_t *array = malloc(sizeof(RavaArray_t)); + if (!array) return NULL; + + array->element_type = element_type; + array->length = length; + array->data = NULL; + + switch (element_type) { + case RAVA_VAL_INT: + array->data = calloc(length, sizeof(int32_t)); + break; + case RAVA_VAL_LONG: + array->data = calloc(length, sizeof(int64_t)); + break; + case RAVA_VAL_DOUBLE: + array->data = calloc(length, sizeof(double)); + break; + case RAVA_VAL_BOOLEAN: + array->data = calloc(length, sizeof(bool)); + break; + default: + array->data = calloc(length, sizeof(void*)); + break; + } + + if (!array->data && length > 0) { + free(array); + return NULL; + } + + return array; +} + +void rava_array_destroy(RavaArray_t *array) { + if (!array) return; + free(array->data); + free(array); +} + +void rava_array_set_int(RavaArray_t *array, size_t index, int32_t value) { + if (!array || !array->data || index >= array->length) return; + ((int32_t*)array->data)[index] = value; +} + +int32_t rava_array_get_int(RavaArray_t *array, size_t index) { + if (!array || !array->data || index >= array->length) return 0; + return ((int32_t*)array->data)[index]; +} + +size_t rava_array_length(RavaArray_t *array) { + if (!array) return 0; + return array->length; +} + +RavaObject_t* rava_object_create(const char *class_name) { + RavaObject_t *obj = calloc(1, sizeof(RavaObject_t)); + if (!obj) return NULL; + obj->class_name = strdup(class_name); + obj->field_capacity = 8; + obj->field_names = calloc(obj->field_capacity, sizeof(char*)); + obj->field_values = calloc(obj->field_capacity, sizeof(RavaValue_t)); + obj->field_count = 0; + return obj; +} + +void rava_object_destroy(RavaObject_t *obj) { + if (!obj) return; + free(obj->class_name); + for (size_t i = 0; i < obj->field_count; i++) { + free(obj->field_names[i]); + } + free(obj->field_names); + free(obj->field_values); + free(obj); +} + +void rava_object_set_field(RavaObject_t *obj, const char *name, RavaValue_t value) { + if (!obj || !name) return; + for (size_t i = 0; i < obj->field_count; i++) { + if (strcmp(obj->field_names[i], name) == 0) { + obj->field_values[i] = value; + return; + } + } + if (obj->field_count >= obj->field_capacity) { + obj->field_capacity *= 2; + obj->field_names = realloc(obj->field_names, obj->field_capacity * sizeof(char*)); + obj->field_values = realloc(obj->field_values, obj->field_capacity * sizeof(RavaValue_t)); + } + obj->field_names[obj->field_count] = strdup(name); + obj->field_values[obj->field_count] = value; + obj->field_count++; +} + +RavaValue_t rava_object_get_field(RavaObject_t *obj, const char *name) { + if (!obj || !name) return rava_value_null(); + for (size_t i = 0; i < obj->field_count; i++) { + if (strcmp(obj->field_names[i], name) == 0) { + return obj->field_values[i]; + } + } + return rava_value_null(); +} + +RavaValue_t rava_value_object(RavaObject_t *obj) { + RavaValue_t val; + val.type = RAVA_VAL_OBJECT; + val.data.object_val = obj; + return val; +} + +int32_t rava_value_as_int(RavaValue_t value) { + switch (value.type) { + case RAVA_VAL_INT: return value.data.int_val; + case RAVA_VAL_LONG: return (int32_t)value.data.long_val; + case RAVA_VAL_FLOAT: return (int32_t)value.data.float_val; + case RAVA_VAL_DOUBLE: return (int32_t)value.data.double_val; + case RAVA_VAL_BOOLEAN: return value.data.bool_val ? 1 : 0; + case RAVA_VAL_CHAR: return value.data.char_val; + default: return 0; + } +} + +int64_t rava_value_as_long(RavaValue_t value) { + switch (value.type) { + case RAVA_VAL_INT: return value.data.int_val; + case RAVA_VAL_LONG: return value.data.long_val; + case RAVA_VAL_FLOAT: return (int64_t)value.data.float_val; + case RAVA_VAL_DOUBLE: return (int64_t)value.data.double_val; + default: return 0; + } +} + +double rava_value_as_double(RavaValue_t value) { + switch (value.type) { + case RAVA_VAL_INT: return value.data.int_val; + case RAVA_VAL_LONG: return (double)value.data.long_val; + case RAVA_VAL_FLOAT: return value.data.float_val; + case RAVA_VAL_DOUBLE: return value.data.double_val; + default: return 0.0; + } +} + +bool rava_value_as_boolean(RavaValue_t value) { + if (value.type == RAVA_VAL_BOOLEAN) { + return value.data.bool_val; + } + return rava_value_as_int(value) != 0; +} + +RavaStack_t* rava_stack_create(size_t capacity) { + RavaStack_t *stack = malloc(sizeof(RavaStack_t)); + stack->capacity = capacity; + stack->values = malloc(sizeof(RavaValue_t) * capacity); + stack->top = 0; + return stack; +} + +void rava_stack_destroy(RavaStack_t *stack) { + if (!stack) return; + free(stack->values); + free(stack); +} + +void rava_stack_push(RavaStack_t *stack, RavaValue_t value) { + if (stack->top >= stack->capacity) { + stack->capacity *= 2; + stack->values = realloc(stack->values, sizeof(RavaValue_t) * stack->capacity); + } + stack->values[stack->top++] = value; +} + +RavaValue_t rava_stack_pop(RavaStack_t *stack) { + if (stack->top == 0) { + return rava_value_int(0); + } + return stack->values[--stack->top]; +} + +RavaValue_t rava_stack_peek(RavaStack_t *stack) { + if (stack->top == 0) { + return rava_value_int(0); + } + return stack->values[stack->top - 1]; +} + +bool rava_stack_is_empty(RavaStack_t *stack) { + return stack->top == 0; +} + +RavaCallFrame_t* rava_call_frame_create(RavaMethod_t *method) { + RavaCallFrame_t *frame = calloc(1, sizeof(RavaCallFrame_t)); + frame->method = method; + frame->local_count = method->local_count; + frame->locals = calloc(method->local_count, sizeof(RavaValue_t)); + frame->pc = 0; + frame->operand_stack = rava_stack_create(256); + frame->has_this = false; + frame->this_ref = rava_value_null(); + return frame; +} + +void rava_call_frame_destroy(RavaCallFrame_t *frame) { + if (!frame) return; + free(frame->locals); + rava_stack_destroy(frame->operand_stack); + free(frame); +} + +RavaCallStack_t* rava_call_stack_create() { + RavaCallStack_t *stack = malloc(sizeof(RavaCallStack_t)); + stack->capacity = 256; + stack->frames = malloc(sizeof(RavaCallFrame_t*) * stack->capacity); + stack->count = 0; + return stack; +} + +void rava_call_stack_destroy(RavaCallStack_t *stack) { + if (!stack) return; + for (size_t i = 0; i < stack->count; i++) { + rava_call_frame_destroy(stack->frames[i]); + } + free(stack->frames); + free(stack); +} + +void rava_call_stack_push(RavaCallStack_t *stack, RavaCallFrame_t *frame) { + if (stack->count >= stack->capacity) { + stack->capacity *= 2; + stack->frames = realloc(stack->frames, sizeof(RavaCallFrame_t*) * stack->capacity); + } + stack->frames[stack->count++] = frame; +} + +RavaCallFrame_t* rava_call_stack_pop(RavaCallStack_t *stack) { + if (stack->count == 0) return NULL; + RavaCallFrame_t *frame = stack->frames[--stack->count]; + return frame; +} + +RavaCallFrame_t* rava_call_stack_current(RavaCallStack_t *stack) { + if (stack->count == 0) return NULL; + return stack->frames[stack->count - 1]; +} + +static RavaStaticFieldTable_t* rava_static_field_table_create() { + RavaStaticFieldTable_t *table = malloc(sizeof(RavaStaticFieldTable_t)); + table->capacity = 16; + table->fields = calloc(table->capacity, sizeof(RavaStaticField_t)); + table->count = 0; + return table; +} + +static void rava_static_field_table_destroy(RavaStaticFieldTable_t *table) { + if (!table) return; + for (size_t i = 0; i < table->count; i++) { + free(table->fields[i].class_name); + free(table->fields[i].field_name); + } + free(table->fields); + free(table); +} + +static RavaValue_t* rava_static_field_table_get(RavaStaticFieldTable_t *table, + const char *class_name, + const char *field_name) { + for (size_t i = 0; i < table->count; i++) { + if (strcmp(table->fields[i].class_name, class_name) == 0 && + strcmp(table->fields[i].field_name, field_name) == 0) { + return &table->fields[i].value; + } + } + return NULL; +} + +static void rava_static_field_table_set(RavaStaticFieldTable_t *table, + const char *class_name, + const char *field_name, + RavaValue_t value) { + RavaValue_t *existing = rava_static_field_table_get(table, class_name, field_name); + if (existing) { + *existing = value; + return; + } + + if (table->count >= table->capacity) { + table->capacity *= 2; + table->fields = realloc(table->fields, sizeof(RavaStaticField_t) * table->capacity); + } + + table->fields[table->count].class_name = strdup(class_name); + table->fields[table->count].field_name = strdup(field_name); + table->fields[table->count].value = value; + table->count++; +} + +RavaVM_t* rava_vm_create(RavaProgram_t *program) { + RavaVM_t *vm = calloc(1, sizeof(RavaVM_t)); + vm->program = program; + vm->call_stack = rava_call_stack_create(); + vm->static_fields = rava_static_field_table_create(); + vm->method_cache = rava_methodcache_create(); + vm->error_message = NULL; + vm->had_error = false; + return vm; +} + +void rava_vm_destroy(RavaVM_t *vm) { + if (!vm) return; + rava_call_stack_destroy(vm->call_stack); + rava_static_field_table_destroy(vm->static_fields); + if (vm->method_cache) { + rava_methodcache_destroy((MethodCache_t*)vm->method_cache); + } + free(vm->error_message); + free(vm); +} + +static RavaClass_t* _rava_vm_find_class(RavaVM_t *vm, const char *class_name) { + for (size_t i = 0; i < vm->program->class_count; i++) { + if (strcmp(vm->program->classes[i]->name, class_name) == 0) { + return vm->program->classes[i]; + } + } + return NULL; +} + +static RavaMethod_t* _rava_vm_find_method(RavaVM_t *vm, const char *class_name, const char *method_name) { + const char *current_class = class_name; + while (current_class) { + RavaClass_t *class = _rava_vm_find_class(vm, current_class); + if (!class) break; + for (size_t j = 0; j < class->method_count; j++) { + if (strcmp(class->methods[j]->name, method_name) == 0) { + return class->methods[j]; + } + } + current_class = class->superclass; + } + return NULL; +} + +static RavaMethod_t* _rava_vm_find_method_cached(RavaVM_t *vm, const char *class_name, const char *method_name) { + MethodCache_t *cache = (MethodCache_t*)vm->method_cache; + RavaMethod_t *cached = rava_methodcache_lookup(cache, class_name, method_name); + if (cached) return cached; + RavaMethod_t *method = _rava_vm_find_method(vm, class_name, method_name); + if (method) { + rava_methodcache_insert(cache, class_name, method_name, method); + } + return method; +} + +#ifndef __GNUC__ +static bool _rava_vm_execute_instruction(RavaVM_t *vm, RavaCallFrame_t *frame, RavaInstruction_t *instr) { + RavaStack_t *stack = frame->operand_stack; + + switch (instr->opcode) { + case RAVA_OP_NOP: + break; + + case RAVA_OP_LOAD_CONST: + rava_stack_push(stack, rava_value_int((int32_t)instr->operand.int_value)); + break; + + case RAVA_OP_LOAD_LONG: + rava_stack_push(stack, rava_value_long(instr->operand.int_value)); + break; + + case RAVA_OP_LOAD_DOUBLE: + rava_stack_push(stack, rava_value_double(instr->operand.float_value)); + break; + + case RAVA_OP_LOAD_STRING: + rava_stack_push(stack, rava_value_string(instr->operand.string_value)); + break; + + case RAVA_OP_LOAD_LOCAL: + if ((size_t)instr->operand.var.index < frame->local_count) { + rava_stack_push(stack, frame->locals[instr->operand.var.index]); + } + break; + + case RAVA_OP_LOAD_THIS: + if (frame->has_this) { + rava_stack_push(stack, frame->this_ref); + } else { + rava_stack_push(stack, rava_value_null()); + } + break; + + case RAVA_OP_STORE_LOCAL: + if ((size_t)instr->operand.var.index < frame->local_count) { + frame->locals[instr->operand.var.index] = rava_stack_pop(stack); + } + break; + + case RAVA_OP_LOAD_STATIC: { + RavaValue_t *field_val = rava_static_field_table_get( + vm->static_fields, + instr->operand.field.class_name, + instr->operand.field.field_name + ); + if (field_val) { + rava_stack_push(stack, *field_val); + } else { + rava_stack_push(stack, rava_value_int(0)); + } + break; + } + + case RAVA_OP_STORE_STATIC: { + RavaValue_t value = rava_stack_pop(stack); + rava_static_field_table_set( + vm->static_fields, + instr->operand.field.class_name, + instr->operand.field.field_name, + value + ); + break; + } + + case RAVA_OP_ADD: { + RavaValue_t b = rava_stack_pop(stack); + RavaValue_t a = rava_stack_pop(stack); + if (a.type == RAVA_VAL_STRING || b.type == RAVA_VAL_STRING) { + char buf_a[64], buf_b[64]; + const char *str_a, *str_b; + if (a.type == RAVA_VAL_STRING) { + str_a = a.data.string_val ? a.data.string_val : "null"; + } else if (a.type == RAVA_VAL_LONG) { + snprintf(buf_a, sizeof(buf_a), "%ld", (long)rava_value_as_long(a)); + str_a = buf_a; + } else { + snprintf(buf_a, sizeof(buf_a), "%d", rava_value_as_int(a)); + str_a = buf_a; + } + if (b.type == RAVA_VAL_STRING) { + str_b = b.data.string_val ? b.data.string_val : "null"; + } else if (b.type == RAVA_VAL_LONG) { + snprintf(buf_b, sizeof(buf_b), "%ld", (long)rava_value_as_long(b)); + str_b = buf_b; + } else { + snprintf(buf_b, sizeof(buf_b), "%d", rava_value_as_int(b)); + str_b = buf_b; + } + size_t len = strlen(str_a) + strlen(str_b) + 1; + char *result = malloc(len); + strcpy(result, str_a); + strcat(result, str_b); + rava_stack_push(stack, rava_value_string(result)); + free(result); + } else if (a.type == RAVA_VAL_LONG || b.type == RAVA_VAL_LONG) { + rava_stack_push(stack, rava_value_long(rava_value_as_long(a) + rava_value_as_long(b))); + } else { + rava_stack_push(stack, rava_value_int(rava_value_as_int(a) + rava_value_as_int(b))); + } + break; + } + + case RAVA_OP_SUB: { + RavaValue_t b = rava_stack_pop(stack); + RavaValue_t a = rava_stack_pop(stack); + if (a.type == RAVA_VAL_LONG || b.type == RAVA_VAL_LONG) { + rava_stack_push(stack, rava_value_long(rava_value_as_long(a) - rava_value_as_long(b))); + } else { + rava_stack_push(stack, rava_value_int(rava_value_as_int(a) - rava_value_as_int(b))); + } + break; + } + + case RAVA_OP_MUL: { + RavaValue_t b = rava_stack_pop(stack); + RavaValue_t a = rava_stack_pop(stack); + if (a.type == RAVA_VAL_LONG || b.type == RAVA_VAL_LONG) { + rava_stack_push(stack, rava_value_long(rava_value_as_long(a) * rava_value_as_long(b))); + } else { + rava_stack_push(stack, rava_value_int(rava_value_as_int(a) * rava_value_as_int(b))); + } + break; + } + + case RAVA_OP_DIV: { + RavaValue_t b = rava_stack_pop(stack); + RavaValue_t a = rava_stack_pop(stack); + if (a.type == RAVA_VAL_LONG || b.type == RAVA_VAL_LONG) { + int64_t divisor = rava_value_as_long(b); + if (divisor == 0) { + vm->had_error = true; + vm->error_message = strdup("Division by zero"); + return false; + } + rava_stack_push(stack, rava_value_long(rava_value_as_long(a) / divisor)); + } else { + int32_t divisor = rava_value_as_int(b); + if (divisor == 0) { + vm->had_error = true; + vm->error_message = strdup("Division by zero"); + return false; + } + rava_stack_push(stack, rava_value_int(rava_value_as_int(a) / divisor)); + } + break; + } + + case RAVA_OP_MOD: { + RavaValue_t b = rava_stack_pop(stack); + RavaValue_t a = rava_stack_pop(stack); + if (a.type == RAVA_VAL_LONG || b.type == RAVA_VAL_LONG) { + rava_stack_push(stack, rava_value_long(rava_value_as_long(a) % rava_value_as_long(b))); + } else { + rava_stack_push(stack, rava_value_int(rava_value_as_int(a) % rava_value_as_int(b))); + } + break; + } + + case RAVA_OP_NEG: { + RavaValue_t a = rava_stack_pop(stack); + rava_stack_push(stack, rava_value_int(-rava_value_as_int(a))); + break; + } + + case RAVA_OP_EQ: { + RavaValue_t b = rava_stack_pop(stack); + RavaValue_t a = rava_stack_pop(stack); + rava_stack_push(stack, rava_value_boolean(rava_value_as_int(a) == rava_value_as_int(b))); + break; + } + + case RAVA_OP_NE: { + RavaValue_t b = rava_stack_pop(stack); + RavaValue_t a = rava_stack_pop(stack); + rava_stack_push(stack, rava_value_boolean(rava_value_as_int(a) != rava_value_as_int(b))); + break; + } + + case RAVA_OP_LT: { + RavaValue_t b = rava_stack_pop(stack); + RavaValue_t a = rava_stack_pop(stack); + rava_stack_push(stack, rava_value_boolean(rava_value_as_int(a) < rava_value_as_int(b))); + break; + } + + case RAVA_OP_LE: { + RavaValue_t b = rava_stack_pop(stack); + RavaValue_t a = rava_stack_pop(stack); + rava_stack_push(stack, rava_value_boolean(rava_value_as_int(a) <= rava_value_as_int(b))); + break; + } + + case RAVA_OP_GT: { + RavaValue_t b = rava_stack_pop(stack); + RavaValue_t a = rava_stack_pop(stack); + rava_stack_push(stack, rava_value_boolean(rava_value_as_int(a) > rava_value_as_int(b))); + break; + } + + case RAVA_OP_GE: { + RavaValue_t b = rava_stack_pop(stack); + RavaValue_t a = rava_stack_pop(stack); + rava_stack_push(stack, rava_value_boolean(rava_value_as_int(a) >= rava_value_as_int(b))); + break; + } + + case RAVA_OP_AND: { + RavaValue_t b = rava_stack_pop(stack); + RavaValue_t a = rava_stack_pop(stack); + rava_stack_push(stack, rava_value_boolean(rava_value_as_boolean(a) && rava_value_as_boolean(b))); + break; + } + + case RAVA_OP_OR: { + RavaValue_t b = rava_stack_pop(stack); + RavaValue_t a = rava_stack_pop(stack); + rava_stack_push(stack, rava_value_boolean(rava_value_as_boolean(a) || rava_value_as_boolean(b))); + break; + } + + case RAVA_OP_NOT: { + RavaValue_t a = rava_stack_pop(stack); + rava_stack_push(stack, rava_value_boolean(!rava_value_as_boolean(a))); + break; + } + + case RAVA_OP_JUMP: { + if (!frame->method->label_table) { + frame->method->label_table = rava_labeltable_create(frame->method->instructions); + } + size_t target_pc = rava_labeltable_lookup((LabelTable_t*)frame->method->label_table, instr->operand.label_id); + if (target_pc > 0) { + frame->pc = target_pc - 1; + return true; + } + break; + } + + case RAVA_OP_JUMP_IF_TRUE: { + RavaValue_t cond = rava_stack_pop(stack); + if (rava_value_as_boolean(cond)) { + if (!frame->method->label_table) { + frame->method->label_table = rava_labeltable_create(frame->method->instructions); + } + size_t target_pc = rava_labeltable_lookup((LabelTable_t*)frame->method->label_table, instr->operand.label_id); + if (target_pc > 0) { + frame->pc = target_pc - 1; + return true; + } + } + break; + } + + case RAVA_OP_JUMP_IF_FALSE: { + RavaValue_t cond = rava_stack_pop(stack); + if (!rava_value_as_boolean(cond)) { + if (!frame->method->label_table) { + frame->method->label_table = rava_labeltable_create(frame->method->instructions); + } + size_t target_pc = rava_labeltable_lookup((LabelTable_t*)frame->method->label_table, instr->operand.label_id); + if (target_pc > 0) { + frame->pc = target_pc - 1; + return true; + } + } + break; + } + + case RAVA_OP_LABEL: + break; + + case RAVA_OP_CALL_STATIC: { + RavaMethod_t *method = _rava_vm_find_method_cached(vm, + instr->operand.call.class_name, + instr->operand.call.method_name); + if (!method) { + vm->had_error = true; + vm->error_message = malloc(256); + snprintf(vm->error_message, 256, "Method not found: %s.%s", + instr->operand.call.class_name, + instr->operand.call.method_name); + return false; + } + + RavaCallFrame_t *new_frame = rava_call_frame_create(method); + + for (int i = instr->operand.call.arg_count - 1; i >= 0; i--) { + new_frame->locals[i] = rava_stack_pop(stack); + } + + rava_call_stack_push(vm->call_stack, new_frame); + + while (new_frame->pc < new_frame->method->instructions->count) { + RavaInstruction_t *next_instr = &new_frame->method->instructions->instructions[new_frame->pc]; + + if (next_instr->opcode == RAVA_OP_RETURN || next_instr->opcode == RAVA_OP_RETURN_VOID) { + RavaValue_t result = rava_value_int(0); + if (next_instr->opcode == RAVA_OP_RETURN && !rava_stack_is_empty(new_frame->operand_stack)) { + result = rava_stack_pop(new_frame->operand_stack); + } + + rava_call_stack_pop(vm->call_stack); + rava_call_frame_destroy(new_frame); + + if (next_instr->opcode == RAVA_OP_RETURN) { + rava_stack_push(stack, result); + } + break; + } + + new_frame->pc++; + if (!_rava_vm_execute_instruction(vm, new_frame, next_instr)) { + return false; + } + } + break; + } + + case RAVA_OP_CALL_VIRTUAL: { + RavaValue_t args[16]; + for (int i = instr->operand.call.arg_count - 1; i >= 0; i--) { + args[i] = rava_stack_pop(stack); + } + RavaValue_t obj_val = rava_stack_pop(stack); + const char *class_name = instr->operand.call.class_name; + if (obj_val.type == RAVA_VAL_OBJECT && obj_val.data.object_val) { + class_name = obj_val.data.object_val->class_name; + } + + RavaMethod_t *method = _rava_vm_find_method_cached(vm, class_name, instr->operand.call.method_name); + if (!method) { + if (strcmp(instr->operand.call.method_name, "") == 0 && instr->operand.call.arg_count == 0) { + break; + } + vm->had_error = true; + vm->error_message = malloc(256); + snprintf(vm->error_message, 256, "Method not found: %s.%s", + class_name, instr->operand.call.method_name); + return false; + } + + RavaCallFrame_t *new_frame = rava_call_frame_create(method); + new_frame->has_this = true; + new_frame->this_ref = obj_val; + + for (int i = 0; i < instr->operand.call.arg_count; i++) { + new_frame->locals[i] = args[i]; + } + + rava_call_stack_push(vm->call_stack, new_frame); + + while (new_frame->pc < new_frame->method->instructions->count) { + RavaInstruction_t *next_instr = &new_frame->method->instructions->instructions[new_frame->pc]; + + if (next_instr->opcode == RAVA_OP_RETURN || next_instr->opcode == RAVA_OP_RETURN_VOID) { + RavaValue_t result = rava_value_int(0); + if (next_instr->opcode == RAVA_OP_RETURN && !rava_stack_is_empty(new_frame->operand_stack)) { + result = rava_stack_pop(new_frame->operand_stack); + } + + rava_call_stack_pop(vm->call_stack); + rava_call_frame_destroy(new_frame); + + if (next_instr->opcode == RAVA_OP_RETURN) { + rava_stack_push(stack, result); + } + break; + } + + new_frame->pc++; + if (!_rava_vm_execute_instruction(vm, new_frame, next_instr)) { + return false; + } + } + break; + } + + case RAVA_OP_RETURN: + case RAVA_OP_RETURN_VOID: + return true; + + case RAVA_OP_POP: + rava_stack_pop(stack); + break; + + case RAVA_OP_DUP: + rava_stack_push(stack, rava_stack_peek(stack)); + break; + + case RAVA_OP_PRINT: { + RavaValue_t value = rava_stack_pop(stack); + switch (value.type) { + case RAVA_VAL_STRING: + printf("%s", value.data.string_val ? value.data.string_val : "null"); + break; + case RAVA_VAL_BOOLEAN: + printf("%s", value.data.bool_val ? "true" : "false"); + break; + case RAVA_VAL_DOUBLE: + printf("%g", value.data.double_val); + break; + case RAVA_VAL_FLOAT: + printf("%g", (double)value.data.float_val); + break; + case RAVA_VAL_LONG: + printf("%ld", (long)value.data.long_val); + break; + case RAVA_VAL_NULL: + printf("null"); + break; + case RAVA_VAL_ARRAY: + printf("[array@%p]", (void*)value.data.array_val); + break; + default: + printf("%d", rava_value_as_int(value)); + break; + } + fflush(stdout); + break; + } + + case RAVA_OP_PRINTLN: { + RavaValue_t value = rava_stack_pop(stack); + switch (value.type) { + case RAVA_VAL_STRING: + printf("%s\n", value.data.string_val ? value.data.string_val : "null"); + break; + case RAVA_VAL_BOOLEAN: + printf("%s\n", value.data.bool_val ? "true" : "false"); + break; + case RAVA_VAL_DOUBLE: + printf("%g\n", value.data.double_val); + break; + case RAVA_VAL_FLOAT: + printf("%g\n", (double)value.data.float_val); + break; + case RAVA_VAL_LONG: + printf("%ld\n", (long)value.data.long_val); + break; + case RAVA_VAL_NULL: + printf("null\n"); + break; + case RAVA_VAL_ARRAY: + printf("[array@%p]\n", (void*)value.data.array_val); + break; + default: + printf("%d\n", rava_value_as_int(value)); + break; + } + fflush(stdout); + break; + } + + case RAVA_OP_NEW: { + const char *class_name = instr->operand.call.class_name; + RavaObject_t *obj = rava_object_create(class_name); + if (obj) { + rava_stack_push(stack, rava_value_object(obj)); + } else { + rava_stack_push(stack, rava_value_null()); + } + break; + } + + case RAVA_OP_NEW_ARRAY: { + RavaValue_t length_val = rava_stack_pop(stack); + int32_t length = rava_value_as_int(length_val); + if (length < 0) length = 0; + RavaArray_t *array = rava_array_create(RAVA_VAL_INT, (size_t)length); + if (array) { + rava_stack_push(stack, rava_value_array(array)); + } else { + rava_stack_push(stack, rava_value_null()); + } + break; + } + + case RAVA_OP_GET_FIELD: { + RavaValue_t obj_val = rava_stack_pop(stack); + if (obj_val.type == RAVA_VAL_OBJECT && obj_val.data.object_val) { + RavaValue_t field_val = rava_object_get_field(obj_val.data.object_val, instr->operand.field.field_name); + rava_stack_push(stack, field_val); + } else { + rava_stack_push(stack, rava_value_null()); + } + break; + } + + case RAVA_OP_PUT_FIELD: { + RavaValue_t value = rava_stack_pop(stack); + RavaValue_t obj_val = rava_stack_pop(stack); + if (obj_val.type == RAVA_VAL_OBJECT && obj_val.data.object_val) { + rava_object_set_field(obj_val.data.object_val, instr->operand.field.field_name, value); + } + break; + } + + case RAVA_OP_ARRAY_LENGTH: { + RavaValue_t array_val = rava_stack_pop(stack); + if (array_val.type == RAVA_VAL_ARRAY) { + size_t length = rava_array_length(array_val.data.array_val); + rava_stack_push(stack, rava_value_int((int32_t)length)); + } else { + rava_stack_push(stack, rava_value_int(0)); + } + break; + } + + case RAVA_OP_LOAD_ARRAY: { + RavaValue_t index_val = rava_stack_pop(stack); + RavaValue_t array_val = rava_stack_pop(stack); + if (array_val.type == RAVA_VAL_ARRAY) { + int32_t index = rava_value_as_int(index_val); + int32_t value = rava_array_get_int(array_val.data.array_val, (size_t)index); + rava_stack_push(stack, rava_value_int(value)); + } else { + rava_stack_push(stack, rava_value_int(0)); + } + break; + } + + case RAVA_OP_STORE_ARRAY: { + RavaValue_t value_val = rava_stack_pop(stack); + RavaValue_t index_val = rava_stack_pop(stack); + RavaValue_t array_val = rava_stack_pop(stack); + if (array_val.type == RAVA_VAL_ARRAY) { + int32_t index = rava_value_as_int(index_val); + int32_t value = rava_value_as_int(value_val); + rava_array_set_int(array_val.data.array_val, (size_t)index, value); + } + break; + } + + case RAVA_OP_STRING_LENGTH: { + RavaValue_t str_val = rava_stack_pop(stack); + if (str_val.type == RAVA_VAL_STRING && str_val.data.string_val) { + rava_stack_push(stack, rava_value_int((int32_t)strlen(str_val.data.string_val))); + } else { + rava_stack_push(stack, rava_value_int(0)); + } + break; + } + + case RAVA_OP_STRING_CHARAT: { + RavaValue_t index_val = rava_stack_pop(stack); + RavaValue_t str_val = rava_stack_pop(stack); + int32_t index = rava_value_as_int(index_val); + if (str_val.type == RAVA_VAL_STRING && str_val.data.string_val) { + size_t len = strlen(str_val.data.string_val); + if (index >= 0 && (size_t)index < len) { + RavaValue_t char_val; + char_val.type = RAVA_VAL_CHAR; + char_val.data.char_val = str_val.data.string_val[index]; + rava_stack_push(stack, char_val); + } else { + rava_stack_push(stack, rava_value_int(0)); + } + } else { + rava_stack_push(stack, rava_value_int(0)); + } + break; + } + + case RAVA_OP_STRING_EQUALS: { + RavaValue_t b = rava_stack_pop(stack); + RavaValue_t a = rava_stack_pop(stack); + bool result = false; + if (a.type == RAVA_VAL_STRING && b.type == RAVA_VAL_STRING) { + if (a.data.string_val && b.data.string_val) { + result = strcmp(a.data.string_val, b.data.string_val) == 0; + } else { + result = (a.data.string_val == b.data.string_val); + } + } + rava_stack_push(stack, rava_value_boolean(result)); + break; + } + + case RAVA_OP_STRING_SUBSTRING: + case RAVA_OP_STRING_COMPARETO: + break; + + case RAVA_OP_FILE_READ: { + RavaValue_t path_val = rava_stack_pop(stack); + if (path_val.type == RAVA_VAL_STRING && path_val.data.string_val) { + FILE *file = fopen(path_val.data.string_val, "r"); + if (file) { + fseek(file, 0, SEEK_END); + long size = ftell(file); + fseek(file, 0, SEEK_SET); + char *content = malloc(size + 1); + if (content) { + size_t read = fread(content, 1, size, file); + content[read] = '\0'; + rava_stack_push(stack, rava_value_string(content)); + free(content); + } else { + rava_stack_push(stack, rava_value_null()); + } + fclose(file); + } else { + rava_stack_push(stack, rava_value_null()); + } + } else { + rava_stack_push(stack, rava_value_null()); + } + break; + } + + case RAVA_OP_FILE_WRITE: { + RavaValue_t content_val = rava_stack_pop(stack); + RavaValue_t path_val = rava_stack_pop(stack); + if (path_val.type == RAVA_VAL_STRING && path_val.data.string_val && + content_val.type == RAVA_VAL_STRING && content_val.data.string_val) { + FILE *file = fopen(path_val.data.string_val, "w"); + if (file) { + fputs(content_val.data.string_val, file); + fclose(file); + rava_stack_push(stack, rava_value_boolean(true)); + } else { + rava_stack_push(stack, rava_value_boolean(false)); + } + } else { + rava_stack_push(stack, rava_value_boolean(false)); + } + break; + } + + case RAVA_OP_FILE_EXISTS: { + RavaValue_t path_val = rava_stack_pop(stack); + if (path_val.type == RAVA_VAL_STRING && path_val.data.string_val) { + FILE *file = fopen(path_val.data.string_val, "r"); + if (file) { + fclose(file); + rava_stack_push(stack, rava_value_boolean(true)); + } else { + rava_stack_push(stack, rava_value_boolean(false)); + } + } else { + rava_stack_push(stack, rava_value_boolean(false)); + } + break; + } + + case RAVA_OP_FILE_DELETE: { + RavaValue_t path_val = rava_stack_pop(stack); + if (path_val.type == RAVA_VAL_STRING && path_val.data.string_val) { + int result = remove(path_val.data.string_val); + rava_stack_push(stack, rava_value_boolean(result == 0)); + } else { + rava_stack_push(stack, rava_value_boolean(false)); + } + break; + } + + case RAVA_OP_CURRENT_TIME_MILLIS: { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + int64_t millis = (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + rava_stack_push(stack, rava_value_long(millis)); + break; + } + + case RAVA_OP_NANO_TIME: { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + int64_t nanos = (int64_t)ts.tv_sec * 1000000000LL + ts.tv_nsec; + rava_stack_push(stack, rava_value_long(nanos)); + break; + } + + default: + break; + } + + return true; +} +#endif + +#ifdef __GNUC__ +__attribute__((unused)) +static bool _rava_vm_execute_fast(RavaVM_t *vm, RavaCallFrame_t *frame) { + static const void *dispatch_table[] = { + &&op_nop, + &&op_load_const, &&op_load_long, &&op_load_double, &&op_load_string, + &&op_load_local, &&op_load_field, &&op_load_static, &&op_load_array, + &&op_store_local, &&op_store_field, &&op_store_static, &&op_store_array, + &&op_add, &&op_sub, &&op_mul, &&op_div, &&op_mod, &&op_neg, + &&op_and, &&op_or, &&op_xor, &&op_not, &&op_shl, &&op_shr, &&op_ushr, + &&op_eq, &&op_ne, &&op_lt, &&op_le, &&op_gt, &&op_ge, + &&op_jump, &&op_jump_if_true, &&op_jump_if_false, &&op_label, + &&op_call, &&op_call_static, &&op_call_virtual, &&op_call_native, + &&op_return, &&op_return_void, + &&op_new, &&op_new_array, &&op_array_length, &&op_get_field, &&op_put_field, + &&op_cast, &&op_instanceof, + &&op_throw, &&op_pop, &&op_dup, + &&op_print, &&op_println, + &&op_string_length, &&op_string_charat, &&op_string_substring, + &&op_string_equals, &&op_string_compareto, + &&op_load_this, + &&op_file_read, &&op_file_write, &&op_file_exists, &&op_file_delete, + &&op_current_time_millis, &&op_nano_time, + &&op_inc_local, &&op_dec_local, + &&op_load_local_const_add, &&op_load_local_const_lt_jumpfalse, + &&op_load_local_const_le_jumpfalse, &&op_load_two_locals, + &&op_add_local_to_local, &&op_load_local_lt_local_jumpfalse + }; + + RavaStack_t *stack = frame->operand_stack; + RavaInstruction_t *instructions = frame->method->instructions->instructions; + size_t instr_count = frame->method->instructions->count; + register size_t pc = frame->pc; + RavaInstruction_t *instr; + + if (!frame->method->label_table) { + frame->method->label_table = rava_labeltable_create(frame->method->instructions); + } + LabelTable_t *label_table = (LabelTable_t*)frame->method->label_table; + +#define DISPATCH() do { \ + if (pc >= instr_count) goto done; \ + instr = &instructions[pc++]; \ + if (instr->opcode >= sizeof(dispatch_table)/sizeof(dispatch_table[0])) { \ + fprintf(stderr, "Invalid opcode: %d at pc=%zu\n", instr->opcode, pc-1); \ + vm->had_error = true; \ + goto done; \ + } \ + goto *dispatch_table[instr->opcode]; \ +} while(0) + +#define DISPATCH_NEXT() do { \ + instr = &instructions[pc++]; \ + goto *dispatch_table[instr->opcode]; \ +} while(0) + + DISPATCH(); + +op_nop: + DISPATCH(); + +op_load_const: + STACK_PUSH_INT(stack, (int32_t)instr->operand.int_value); + DISPATCH(); + +op_load_long: + STACK_PUSH_LONG(stack, instr->operand.int_value); + DISPATCH(); + +op_load_double: { + RavaValue_t v; v.type = RAVA_VAL_DOUBLE; v.data.double_val = instr->operand.float_value; + STACK_PUSH(stack, v); + DISPATCH(); +} + +op_load_string: + STACK_PUSH(stack, rava_value_string(instr->operand.string_value)); + DISPATCH(); + +op_load_local: + STACK_PUSH(stack, frame->locals[instr->operand.var.index]); + DISPATCH(); + +op_load_field: { + RavaValue_t obj_val = STACK_POP(stack); + if (obj_val.type == RAVA_VAL_OBJECT && obj_val.data.object_val) { + STACK_PUSH(stack, rava_object_get_field(obj_val.data.object_val, instr->operand.field.field_name)); + } else { + STACK_PUSH_INT(stack, 0); + } + DISPATCH(); +} + +op_load_static: { + RavaValue_t *field_val = rava_static_field_table_get(vm->static_fields, + instr->operand.field.class_name, instr->operand.field.field_name); + STACK_PUSH(stack, field_val ? *field_val : rava_value_int(0)); + DISPATCH(); +} + +op_load_array: { + RavaValue_t idx = STACK_POP(stack); + RavaValue_t arr = STACK_POP(stack); + if (arr.type == RAVA_VAL_ARRAY && arr.data.array_val) { + size_t index = (size_t)rava_value_as_int(idx); + if (index < arr.data.array_val->length) { + switch (arr.data.array_val->element_type) { + case RAVA_VAL_INT: + STACK_PUSH_INT(stack, ((int32_t*)arr.data.array_val->data)[index]); + break; + case RAVA_VAL_LONG: + STACK_PUSH_LONG(stack, ((int64_t*)arr.data.array_val->data)[index]); + break; + default: + STACK_PUSH_INT(stack, ((int32_t*)arr.data.array_val->data)[index]); + break; + } + } else { + STACK_PUSH_INT(stack, 0); + } + } else { + STACK_PUSH_INT(stack, 0); + } + DISPATCH(); +} + +op_store_local: + frame->locals[instr->operand.var.index] = STACK_POP(stack); + DISPATCH(); + +op_store_field: { + RavaValue_t value = STACK_POP(stack); + RavaValue_t obj_val = STACK_POP(stack); + if (obj_val.type == RAVA_VAL_OBJECT && obj_val.data.object_val) { + rava_object_set_field(obj_val.data.object_val, instr->operand.field.field_name, value); + } + DISPATCH(); +} + +op_store_static: { + RavaValue_t value = STACK_POP(stack); + rava_static_field_table_set(vm->static_fields, instr->operand.field.class_name, + instr->operand.field.field_name, value); + DISPATCH(); +} + +op_store_array: { + RavaValue_t value = STACK_POP(stack); + RavaValue_t idx = STACK_POP(stack); + RavaValue_t arr = STACK_POP(stack); + if (arr.type == RAVA_VAL_ARRAY && arr.data.array_val) { + size_t index = (size_t)rava_value_as_int(idx); + if (index < arr.data.array_val->length) { + switch (arr.data.array_val->element_type) { + case RAVA_VAL_INT: + ((int32_t*)arr.data.array_val->data)[index] = rava_value_as_int(value); + break; + case RAVA_VAL_LONG: + ((int64_t*)arr.data.array_val->data)[index] = rava_value_as_long(value); + break; + default: + ((int32_t*)arr.data.array_val->data)[index] = rava_value_as_int(value); + break; + } + } + } + DISPATCH(); +} + +op_add: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + if (VALUE_IS_INT(a) && VALUE_IS_INT(b)) { + STACK_PUSH_INT(stack, VALUE_AS_INT_FAST(a) + VALUE_AS_INT_FAST(b)); + } else if (a.type == RAVA_VAL_STRING || b.type == RAVA_VAL_STRING) { + char buf_a[64], buf_b[64]; + const char *str_a, *str_b; + if (a.type == RAVA_VAL_STRING) str_a = a.data.string_val ? a.data.string_val : "null"; + else if (VALUE_IS_LONG(a)) { snprintf(buf_a, 64, "%ld", (long)VALUE_AS_LONG_FAST(a)); str_a = buf_a; } + else { snprintf(buf_a, 64, "%d", rava_value_as_int(a)); str_a = buf_a; } + if (b.type == RAVA_VAL_STRING) str_b = b.data.string_val ? b.data.string_val : "null"; + else if (VALUE_IS_LONG(b)) { snprintf(buf_b, 64, "%ld", (long)VALUE_AS_LONG_FAST(b)); str_b = buf_b; } + else { snprintf(buf_b, 64, "%d", rava_value_as_int(b)); str_b = buf_b; } + size_t len = strlen(str_a) + strlen(str_b) + 1; + char *result = malloc(len); + strcpy(result, str_a); strcat(result, str_b); + STACK_PUSH(stack, rava_value_string(result)); + free(result); + } else if (VALUE_IS_LONG(a) || VALUE_IS_LONG(b)) { + STACK_PUSH_LONG(stack, rava_value_as_long(a) + rava_value_as_long(b)); + } else { + STACK_PUSH_INT(stack, rava_value_as_int(a) + rava_value_as_int(b)); + } + DISPATCH(); +} + +op_sub: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + if (VALUE_IS_INT(a) && VALUE_IS_INT(b)) { + STACK_PUSH_INT(stack, VALUE_AS_INT_FAST(a) - VALUE_AS_INT_FAST(b)); + } else if (VALUE_IS_LONG(a) || VALUE_IS_LONG(b)) { + STACK_PUSH_LONG(stack, rava_value_as_long(a) - rava_value_as_long(b)); + } else { + STACK_PUSH_INT(stack, rava_value_as_int(a) - rava_value_as_int(b)); + } + DISPATCH(); +} + +op_mul: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + if (VALUE_IS_INT(a) && VALUE_IS_INT(b)) { + STACK_PUSH_INT(stack, VALUE_AS_INT_FAST(a) * VALUE_AS_INT_FAST(b)); + } else if (VALUE_IS_LONG(a) || VALUE_IS_LONG(b)) { + STACK_PUSH_LONG(stack, rava_value_as_long(a) * rava_value_as_long(b)); + } else { + STACK_PUSH_INT(stack, rava_value_as_int(a) * rava_value_as_int(b)); + } + DISPATCH(); +} + +op_div: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + if (VALUE_IS_INT(a) && VALUE_IS_INT(b)) { + int32_t divisor = VALUE_AS_INT_FAST(b); + if (divisor == 0) { vm->had_error = true; goto done; } + STACK_PUSH_INT(stack, VALUE_AS_INT_FAST(a) / divisor); + } else if (VALUE_IS_LONG(a) || VALUE_IS_LONG(b)) { + int64_t divisor = rava_value_as_long(b); + if (divisor == 0) { vm->had_error = true; goto done; } + STACK_PUSH_LONG(stack, rava_value_as_long(a) / divisor); + } else { + int32_t divisor = rava_value_as_int(b); + if (divisor == 0) { vm->had_error = true; goto done; } + STACK_PUSH_INT(stack, rava_value_as_int(a) / divisor); + } + DISPATCH(); +} + +op_mod: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + if (VALUE_IS_INT(a) && VALUE_IS_INT(b)) { + int32_t divisor = VALUE_AS_INT_FAST(b); + if (divisor == 0) { vm->had_error = true; goto done; } + STACK_PUSH_INT(stack, VALUE_AS_INT_FAST(a) % divisor); + } else if (VALUE_IS_LONG(a) || VALUE_IS_LONG(b)) { + int64_t divisor = rava_value_as_long(b); + if (divisor == 0) { vm->had_error = true; goto done; } + STACK_PUSH_LONG(stack, rava_value_as_long(a) % divisor); + } else { + int32_t divisor = rava_value_as_int(b); + if (divisor == 0) { vm->had_error = true; goto done; } + STACK_PUSH_INT(stack, rava_value_as_int(a) % divisor); + } + DISPATCH(); +} + +op_neg: { + RavaValue_t a = STACK_POP(stack); + if (VALUE_IS_INT(a)) { + STACK_PUSH_INT(stack, -VALUE_AS_INT_FAST(a)); + } else if (VALUE_IS_LONG(a)) { + STACK_PUSH_LONG(stack, -VALUE_AS_LONG_FAST(a)); + } else { + STACK_PUSH_INT(stack, -rava_value_as_int(a)); + } + DISPATCH(); +} + +op_and: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + STACK_PUSH_INT(stack, rava_value_as_int(a) & rava_value_as_int(b)); + DISPATCH(); +} + +op_or: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + STACK_PUSH_INT(stack, rava_value_as_int(a) | rava_value_as_int(b)); + DISPATCH(); +} + +op_xor: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + STACK_PUSH_INT(stack, rava_value_as_int(a) ^ rava_value_as_int(b)); + DISPATCH(); +} + +op_not: { + RavaValue_t a = STACK_POP(stack); + STACK_PUSH_BOOL(stack, !rava_value_as_boolean(a)); + DISPATCH(); +} + +op_shl: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + STACK_PUSH_INT(stack, rava_value_as_int(a) << rava_value_as_int(b)); + DISPATCH(); +} + +op_shr: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + STACK_PUSH_INT(stack, rava_value_as_int(a) >> rava_value_as_int(b)); + DISPATCH(); +} + +op_ushr: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + STACK_PUSH_INT(stack, (uint32_t)rava_value_as_int(a) >> rava_value_as_int(b)); + DISPATCH(); +} + +op_eq: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + if (VALUE_IS_INT(a) && VALUE_IS_INT(b)) { + STACK_PUSH_BOOL(stack, VALUE_AS_INT_FAST(a) == VALUE_AS_INT_FAST(b)); + } else if (VALUE_IS_LONG(a) || VALUE_IS_LONG(b)) { + STACK_PUSH_BOOL(stack, rava_value_as_long(a) == rava_value_as_long(b)); + } else { + STACK_PUSH_BOOL(stack, rava_value_as_int(a) == rava_value_as_int(b)); + } + DISPATCH(); +} + +op_ne: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + if (VALUE_IS_INT(a) && VALUE_IS_INT(b)) { + STACK_PUSH_BOOL(stack, VALUE_AS_INT_FAST(a) != VALUE_AS_INT_FAST(b)); + } else if (VALUE_IS_LONG(a) || VALUE_IS_LONG(b)) { + STACK_PUSH_BOOL(stack, rava_value_as_long(a) != rava_value_as_long(b)); + } else { + STACK_PUSH_BOOL(stack, rava_value_as_int(a) != rava_value_as_int(b)); + } + DISPATCH(); +} + +op_lt: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + if (VALUE_IS_INT(a) && VALUE_IS_INT(b)) { + STACK_PUSH_BOOL(stack, VALUE_AS_INT_FAST(a) < VALUE_AS_INT_FAST(b)); + } else if (VALUE_IS_LONG(a) || VALUE_IS_LONG(b)) { + STACK_PUSH_BOOL(stack, rava_value_as_long(a) < rava_value_as_long(b)); + } else { + STACK_PUSH_BOOL(stack, rava_value_as_int(a) < rava_value_as_int(b)); + } + DISPATCH(); +} + +op_le: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + if (VALUE_IS_INT(a) && VALUE_IS_INT(b)) { + STACK_PUSH_BOOL(stack, VALUE_AS_INT_FAST(a) <= VALUE_AS_INT_FAST(b)); + } else if (VALUE_IS_LONG(a) || VALUE_IS_LONG(b)) { + STACK_PUSH_BOOL(stack, rava_value_as_long(a) <= rava_value_as_long(b)); + } else { + STACK_PUSH_BOOL(stack, rava_value_as_int(a) <= rava_value_as_int(b)); + } + DISPATCH(); +} + +op_gt: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + if (VALUE_IS_INT(a) && VALUE_IS_INT(b)) { + STACK_PUSH_BOOL(stack, VALUE_AS_INT_FAST(a) > VALUE_AS_INT_FAST(b)); + } else if (VALUE_IS_LONG(a) || VALUE_IS_LONG(b)) { + STACK_PUSH_BOOL(stack, rava_value_as_long(a) > rava_value_as_long(b)); + } else { + STACK_PUSH_BOOL(stack, rava_value_as_int(a) > rava_value_as_int(b)); + } + DISPATCH(); +} + +op_ge: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + if (VALUE_IS_INT(a) && VALUE_IS_INT(b)) { + STACK_PUSH_BOOL(stack, VALUE_AS_INT_FAST(a) >= VALUE_AS_INT_FAST(b)); + } else if (VALUE_IS_LONG(a) || VALUE_IS_LONG(b)) { + STACK_PUSH_BOOL(stack, rava_value_as_long(a) >= rava_value_as_long(b)); + } else { + STACK_PUSH_BOOL(stack, rava_value_as_int(a) >= rava_value_as_int(b)); + } + DISPATCH(); +} + +op_jump: { + pc = rava_labeltable_lookup(label_table, instr->operand.label_id); + DISPATCH(); +} + +op_jump_if_true: { + RavaValue_t cond = STACK_POP(stack); + if (rava_value_as_boolean(cond)) { + pc = rava_labeltable_lookup(label_table, instr->operand.label_id); + } + DISPATCH(); +} + +op_jump_if_false: { + RavaValue_t cond = STACK_POP(stack); + if (!rava_value_as_boolean(cond)) { + pc = rava_labeltable_lookup(label_table, instr->operand.label_id); + } + DISPATCH(); +} + +op_label: + DISPATCH(); + +op_call: +op_call_static: { + frame->pc = pc; + RavaMethod_t *target_method = _rava_vm_find_method_cached(vm, + instr->operand.call.class_name, instr->operand.call.method_name); + if (!target_method) { + vm->had_error = true; + vm->error_message = malloc(256); + snprintf(vm->error_message, 256, "Method not found: %s.%s", + instr->operand.call.class_name, instr->operand.call.method_name); + goto done; + } + RavaCallFrame_t *new_frame = rava_call_frame_create(target_method); + for (int i = instr->operand.call.arg_count - 1; i >= 0; i--) { + new_frame->locals[i] = STACK_POP(stack); + } + rava_call_stack_push(vm->call_stack, new_frame); + if (!_rava_vm_execute_fast(vm, new_frame)) { + goto done; + } + if (!rava_stack_is_empty(new_frame->operand_stack)) { + STACK_PUSH(stack, rava_stack_pop(new_frame->operand_stack)); + } + rava_call_stack_pop(vm->call_stack); + rava_call_frame_destroy(new_frame); + DISPATCH(); +} + +op_call_virtual: { + frame->pc = pc; + RavaValue_t this_val = stack->values[stack->top - instr->operand.call.arg_count - 1]; + const char *target_class = instr->operand.call.class_name; + if (this_val.type == RAVA_VAL_OBJECT && this_val.data.object_val) { + target_class = this_val.data.object_val->class_name; + } + RavaMethod_t *target_method = _rava_vm_find_method_cached(vm, target_class, instr->operand.call.method_name); + if (!target_method) { + if (strcmp(instr->operand.call.method_name, "") == 0 && instr->operand.call.arg_count == 0) { + STACK_POP(stack); + DISPATCH(); + } + vm->had_error = true; + vm->error_message = malloc(256); + snprintf(vm->error_message, 256, "Method not found: %s.%s", + target_class, instr->operand.call.method_name); + goto done; + } + RavaCallFrame_t *new_frame = rava_call_frame_create(target_method); + new_frame->has_this = true; + RavaValue_t args[16]; + for (int i = instr->operand.call.arg_count - 1; i >= 0; i--) { + args[i] = STACK_POP(stack); + } + new_frame->this_ref = STACK_POP(stack); + for (int i = 0; i < instr->operand.call.arg_count; i++) { + new_frame->locals[i] = args[i]; + } + rava_call_stack_push(vm->call_stack, new_frame); + if (!_rava_vm_execute_fast(vm, new_frame)) { + goto done; + } + if (!rava_stack_is_empty(new_frame->operand_stack)) { + STACK_PUSH(stack, rava_stack_pop(new_frame->operand_stack)); + } + rava_call_stack_pop(vm->call_stack); + rava_call_frame_destroy(new_frame); + DISPATCH(); +} + +op_call_native: + DISPATCH(); + +op_return: + goto done; + +op_return_void: + goto done; + +op_new: { + RavaObject_t *obj = rava_object_create(instr->operand.call.class_name); + STACK_PUSH(stack, rava_value_object(obj)); + DISPATCH(); +} + +op_new_array: { + RavaValue_t size_val = STACK_POP(stack); + size_t size = (size_t)rava_value_as_int(size_val); + RavaArray_t *array = rava_array_create(RAVA_VAL_INT, size); + STACK_PUSH(stack, rava_value_array(array)); + DISPATCH(); +} + +op_array_length: { + RavaValue_t arr = STACK_POP(stack); + if (arr.type == RAVA_VAL_ARRAY && arr.data.array_val) { + STACK_PUSH_INT(stack, (int32_t)arr.data.array_val->length); + } else { + STACK_PUSH_INT(stack, 0); + } + DISPATCH(); +} + +op_get_field: { + RavaValue_t obj_val = STACK_POP(stack); + if (obj_val.type == RAVA_VAL_OBJECT && obj_val.data.object_val) { + STACK_PUSH(stack, rava_object_get_field(obj_val.data.object_val, instr->operand.field.field_name)); + } else { + STACK_PUSH_INT(stack, 0); + } + DISPATCH(); +} + +op_put_field: { + RavaValue_t value = STACK_POP(stack); + RavaValue_t obj_val = STACK_POP(stack); + if (obj_val.type == RAVA_VAL_OBJECT && obj_val.data.object_val) { + rava_object_set_field(obj_val.data.object_val, instr->operand.field.field_name, value); + } + DISPATCH(); +} + +op_cast: +op_instanceof: +op_throw: + DISPATCH(); + +op_pop: + (void)STACK_POP(stack); + DISPATCH(); + +op_dup: { + RavaValue_t top = STACK_PEEK(stack); + STACK_PUSH(stack, top); + DISPATCH(); +} + +op_print: { + RavaValue_t value = STACK_POP(stack); + switch (value.type) { + case RAVA_VAL_STRING: printf("%s", value.data.string_val ? value.data.string_val : "null"); break; + case RAVA_VAL_BOOLEAN: printf("%s", value.data.bool_val ? "true" : "false"); break; + case RAVA_VAL_DOUBLE: printf("%g", value.data.double_val); break; + case RAVA_VAL_LONG: printf("%ld", (long)value.data.long_val); break; + case RAVA_VAL_NULL: printf("null"); break; + default: printf("%d", rava_value_as_int(value)); break; + } + fflush(stdout); + DISPATCH(); +} + +op_println: { + RavaValue_t value = STACK_POP(stack); + switch (value.type) { + case RAVA_VAL_STRING: printf("%s\n", value.data.string_val ? value.data.string_val : "null"); break; + case RAVA_VAL_BOOLEAN: printf("%s\n", value.data.bool_val ? "true" : "false"); break; + case RAVA_VAL_DOUBLE: printf("%g\n", value.data.double_val); break; + case RAVA_VAL_LONG: printf("%ld\n", (long)value.data.long_val); break; + case RAVA_VAL_NULL: printf("null\n"); break; + default: printf("%d\n", rava_value_as_int(value)); break; + } + DISPATCH(); +} + +op_string_length: { + RavaValue_t str = STACK_POP(stack); + if (str.type == RAVA_VAL_STRING && str.data.string_val) { + STACK_PUSH_INT(stack, (int32_t)strlen(str.data.string_val)); + } else { + STACK_PUSH_INT(stack, 0); + } + DISPATCH(); +} + +op_string_charat: { + RavaValue_t idx = STACK_POP(stack); + RavaValue_t str = STACK_POP(stack); + if (str.type == RAVA_VAL_STRING && str.data.string_val) { + size_t index = (size_t)rava_value_as_int(idx); + if (index < strlen(str.data.string_val)) { + RavaValue_t v; v.type = RAVA_VAL_CHAR; v.data.char_val = str.data.string_val[index]; + STACK_PUSH(stack, v); + } else { + RavaValue_t v; v.type = RAVA_VAL_CHAR; v.data.char_val = 0; + STACK_PUSH(stack, v); + } + } else { + RavaValue_t v; v.type = RAVA_VAL_CHAR; v.data.char_val = 0; + STACK_PUSH(stack, v); + } + DISPATCH(); +} + +op_string_substring: { + RavaValue_t end_val = STACK_POP(stack); + RavaValue_t start_val = STACK_POP(stack); + RavaValue_t str = STACK_POP(stack); + if (str.type == RAVA_VAL_STRING && str.data.string_val) { + size_t len = strlen(str.data.string_val); + size_t start = (size_t)rava_value_as_int(start_val); + size_t end = (size_t)rava_value_as_int(end_val); + if (start > len) start = len; + if (end > len) end = len; + if (start > end) start = end; + size_t sub_len = end - start; + char *sub = malloc(sub_len + 1); + strncpy(sub, str.data.string_val + start, sub_len); + sub[sub_len] = '\0'; + STACK_PUSH(stack, rava_value_string(sub)); + free(sub); + } else { + STACK_PUSH(stack, rava_value_string("")); + } + DISPATCH(); +} + +op_string_equals: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + if (a.type == RAVA_VAL_STRING && b.type == RAVA_VAL_STRING) { + if (a.data.string_val && b.data.string_val) { + STACK_PUSH_BOOL(stack, strcmp(a.data.string_val, b.data.string_val) == 0); + } else { + STACK_PUSH_BOOL(stack, a.data.string_val == b.data.string_val); + } + } else { + STACK_PUSH_BOOL(stack, false); + } + DISPATCH(); +} + +op_string_compareto: { + RavaValue_t b = STACK_POP(stack); + RavaValue_t a = STACK_POP(stack); + if (a.type == RAVA_VAL_STRING && b.type == RAVA_VAL_STRING && + a.data.string_val && b.data.string_val) { + STACK_PUSH_INT(stack, strcmp(a.data.string_val, b.data.string_val)); + } else { + STACK_PUSH_INT(stack, 0); + } + DISPATCH(); +} + +op_load_this: + if (frame->has_this) { + STACK_PUSH(stack, frame->this_ref); + } else { + STACK_PUSH(stack, rava_value_null()); + } + DISPATCH(); + +op_file_read: { + RavaValue_t path_val = STACK_POP(stack); + if (path_val.type == RAVA_VAL_STRING && path_val.data.string_val) { + FILE *file = fopen(path_val.data.string_val, "r"); + if (file) { + fseek(file, 0, SEEK_END); + long size = ftell(file); + fseek(file, 0, SEEK_SET); + char *content = malloc(size + 1); + if (content) { + size_t rd = fread(content, 1, size, file); + content[rd] = '\0'; + STACK_PUSH(stack, rava_value_string(content)); + free(content); + } else { + STACK_PUSH(stack, rava_value_null()); + } + fclose(file); + } else { + STACK_PUSH(stack, rava_value_null()); + } + } else { + STACK_PUSH(stack, rava_value_null()); + } + DISPATCH(); +} + +op_file_write: { + RavaValue_t content_val = STACK_POP(stack); + RavaValue_t path_val = STACK_POP(stack); + if (path_val.type == RAVA_VAL_STRING && path_val.data.string_val && + content_val.type == RAVA_VAL_STRING && content_val.data.string_val) { + FILE *file = fopen(path_val.data.string_val, "w"); + if (file) { + fputs(content_val.data.string_val, file); + fclose(file); + STACK_PUSH_BOOL(stack, true); + } else { + STACK_PUSH_BOOL(stack, false); + } + } else { + STACK_PUSH_BOOL(stack, false); + } + DISPATCH(); +} + +op_file_exists: { + RavaValue_t path_val = STACK_POP(stack); + if (path_val.type == RAVA_VAL_STRING && path_val.data.string_val) { + FILE *file = fopen(path_val.data.string_val, "r"); + if (file) { + fclose(file); + STACK_PUSH_BOOL(stack, true); + } else { + STACK_PUSH_BOOL(stack, false); + } + } else { + STACK_PUSH_BOOL(stack, false); + } + DISPATCH(); +} + +op_file_delete: { + RavaValue_t path_val = STACK_POP(stack); + if (path_val.type == RAVA_VAL_STRING && path_val.data.string_val) { + int result = remove(path_val.data.string_val); + STACK_PUSH_BOOL(stack, result == 0); + } else { + STACK_PUSH_BOOL(stack, false); + } + DISPATCH(); +} + +op_current_time_millis: { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + int64_t millis = (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + STACK_PUSH_LONG(stack, millis); + DISPATCH(); +} + +op_nano_time: { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + int64_t nanos = (int64_t)ts.tv_sec * 1000000000LL + ts.tv_nsec; + STACK_PUSH_LONG(stack, nanos); + DISPATCH(); +} + +op_inc_local: { + size_t idx = (size_t)instr->operand.var.index; + if (VALUE_IS_INT(frame->locals[idx])) { + frame->locals[idx].data.int_val++; + } else { + frame->locals[idx].data.long_val++; + } + DISPATCH(); +} + +op_dec_local: { + size_t idx = (size_t)instr->operand.var.index; + if (VALUE_IS_INT(frame->locals[idx])) { + frame->locals[idx].data.int_val--; + } else { + frame->locals[idx].data.long_val--; + } + DISPATCH(); +} + +op_load_local_const_add: { + size_t idx = (size_t)instr->operand.super.local_index; + int64_t c = instr->operand.super.const_value; + RavaValue_t v = frame->locals[idx]; + if (VALUE_IS_INT(v)) { + STACK_PUSH_INT(stack, VALUE_AS_INT_FAST(v) + (int32_t)c); + } else { + STACK_PUSH_LONG(stack, rava_value_as_long(v) + c); + } + DISPATCH(); +} + +op_load_local_const_lt_jumpfalse: { + size_t idx = (size_t)instr->operand.super.local_index; + int64_t c = instr->operand.super.const_value; + int label = instr->operand.super.label_id; + int64_t local_val = rava_value_as_long(frame->locals[idx]); + if (!(local_val < c)) { + pc = rava_labeltable_lookup(label_table, label); + } + DISPATCH(); +} + +op_load_local_const_le_jumpfalse: { + size_t idx = (size_t)instr->operand.super.local_index; + int64_t c = instr->operand.super.const_value; + int label = instr->operand.super.label_id; + int64_t local_val = rava_value_as_long(frame->locals[idx]); + if (!(local_val <= c)) { + pc = rava_labeltable_lookup(label_table, label); + } + DISPATCH(); +} + +op_load_two_locals: { + size_t idx1 = (size_t)instr->operand.two_locals.index1; + size_t idx2 = (size_t)instr->operand.two_locals.index2; + STACK_PUSH(stack, frame->locals[idx1]); + STACK_PUSH(stack, frame->locals[idx2]); + DISPATCH(); +} + +op_add_local_to_local: { + size_t dest = (size_t)instr->operand.add_locals.dest_index; + size_t src = (size_t)instr->operand.add_locals.src_index; + if (VALUE_IS_INT(frame->locals[dest]) && VALUE_IS_INT(frame->locals[src])) { + frame->locals[dest].data.int_val += frame->locals[src].data.int_val; + } else { + frame->locals[dest].data.long_val = + rava_value_as_long(frame->locals[dest]) + rava_value_as_long(frame->locals[src]); + frame->locals[dest].type = RAVA_VAL_LONG; + } + DISPATCH(); +} + +op_load_local_lt_local_jumpfalse: { + size_t local1 = (size_t)instr->operand.cmp_locals.local1; + size_t local2 = (size_t)instr->operand.cmp_locals.local2; + int label = instr->operand.cmp_locals.label_id; + int64_t v1 = rava_value_as_long(frame->locals[local1]); + int64_t v2 = rava_value_as_long(frame->locals[local2]); + if (!(v1 < v2)) { + pc = rava_labeltable_lookup(label_table, label); + } + DISPATCH(); +} + +done: + frame->pc = pc; + return !vm->had_error; + +#undef DISPATCH +#undef DISPATCH_NEXT +} + +static bool _rava_vm_execute_ultrafast(RavaVM_t *vm, RavaMethod_t *entry_method) { + static const void *dispatch_table[] = { + &&uf_nop, + &&uf_load_const, &&uf_load_long, &&uf_load_double, &&uf_load_string, + &&uf_load_local, &&uf_load_field, &&uf_load_static, &&uf_load_array, + &&uf_store_local, &&uf_store_field, &&uf_store_static, &&uf_store_array, + &&uf_add, &&uf_sub, &&uf_mul, &&uf_div, &&uf_mod, &&uf_neg, + &&uf_and, &&uf_or, &&uf_xor, &&uf_not, &&uf_shl, &&uf_shr, &&uf_ushr, + &&uf_eq, &&uf_ne, &&uf_lt, &&uf_le, &&uf_gt, &&uf_ge, + &&uf_jump, &&uf_jump_if_true, &&uf_jump_if_false, &&uf_label, + &&uf_call, &&uf_call_static, &&uf_call_virtual, &&uf_call_native, + &&uf_return, &&uf_return_void, + &&uf_new, &&uf_new_array, &&uf_array_length, &&uf_get_field, &&uf_put_field, + &&uf_cast, &&uf_instanceof, + &&uf_throw, &&uf_pop, &&uf_dup, + &&uf_print, &&uf_println, + &&uf_string_length, &&uf_string_charat, &&uf_string_substring, + &&uf_string_equals, &&uf_string_compareto, + &&uf_load_this, + &&uf_file_read, &&uf_file_write, &&uf_file_exists, &&uf_file_delete, + &&uf_current_time_millis, &&uf_nano_time, + &&uf_inc_local, &&uf_dec_local, + &&uf_load_local_const_add, &&uf_load_local_const_lt_jumpfalse, + &&uf_load_local_const_le_jumpfalse, &&uf_load_two_locals, + &&uf_add_local_to_local, &&uf_load_local_lt_local_jumpfalse + }; + + rava_fastframe_reset(); + FastFrame_t *frame = rava_fastframe_push(entry_method, entry_method->local_count); + if (!frame) return false; + + if (!entry_method->label_table) { + rava_optimize_superinstructions(entry_method); + entry_method->label_table = rava_labeltable_create(entry_method->instructions); + } + + RavaInstruction_t *instructions = entry_method->instructions->instructions; + size_t instr_count = entry_method->instructions->count; + register size_t pc = 0; + RavaInstruction_t *instr; + RavaNanboxValue_t result = rava_nanbox_null(); + +#define UF_DISPATCH() do { \ + if (pc >= instr_count) goto uf_done; \ + instr = &instructions[pc++]; \ + goto *dispatch_table[instr->opcode]; \ +} while(0) + +#define UF_PUSH(v) rava_fastframe_stack_push(frame, v) +#define UF_POP() rava_fastframe_stack_pop(frame) +#define UF_PEEK() rava_fastframe_stack_peek(frame) + + UF_DISPATCH(); + +uf_nop: + UF_DISPATCH(); + +uf_load_const: + UF_PUSH(rava_nanbox_int((int32_t)instr->operand.int_value)); + UF_DISPATCH(); + +uf_load_long: + UF_PUSH(rava_nanbox_long(instr->operand.int_value)); + UF_DISPATCH(); + +uf_load_double: + UF_PUSH(rava_nanbox_double(instr->operand.float_value)); + UF_DISPATCH(); + +uf_load_string: + UF_PUSH(rava_nanbox_string(instr->operand.string_value)); + UF_DISPATCH(); + +uf_load_local: + UF_PUSH(frame->locals[instr->operand.var.index]); + UF_DISPATCH(); + +uf_store_local: + frame->locals[instr->operand.var.index] = UF_POP(); + UF_DISPATCH(); + +uf_add: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + if (rava_nanbox_is_int(a) && rava_nanbox_is_int(b)) { + UF_PUSH(rava_nanbox_int(rava_nanbox_as_int(a) + rava_nanbox_as_int(b))); + } else if (rava_nanbox_is_long(a) || rava_nanbox_is_long(b)) { + UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) + rava_nanbox_to_long(b))); + } else if (rava_nanbox_is_double(a) || rava_nanbox_is_double(b)) { + UF_PUSH(rava_nanbox_double(rava_nanbox_to_double(a) + rava_nanbox_to_double(b))); + } else { + UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) + rava_nanbox_to_long(b))); + } + UF_DISPATCH(); +} + +uf_sub: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + if (rava_nanbox_is_int(a) && rava_nanbox_is_int(b)) { + UF_PUSH(rava_nanbox_int(rava_nanbox_as_int(a) - rava_nanbox_as_int(b))); + } else if (rava_nanbox_is_long(a) || rava_nanbox_is_long(b)) { + UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) - rava_nanbox_to_long(b))); + } else { + UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) - rava_nanbox_to_long(b))); + } + UF_DISPATCH(); +} + +uf_mul: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + if (rava_nanbox_is_int(a) && rava_nanbox_is_int(b)) { + UF_PUSH(rava_nanbox_int(rava_nanbox_as_int(a) * rava_nanbox_as_int(b))); + } else if (rava_nanbox_is_long(a) || rava_nanbox_is_long(b)) { + UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) * rava_nanbox_to_long(b))); + } else { + UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) * rava_nanbox_to_long(b))); + } + UF_DISPATCH(); +} + +uf_div: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + int64_t bv = rava_nanbox_to_long(b); + if (bv == 0) { UF_PUSH(rava_nanbox_int(0)); UF_DISPATCH(); } + if (rava_nanbox_is_int(a) && rava_nanbox_is_int(b)) { + UF_PUSH(rava_nanbox_int(rava_nanbox_as_int(a) / rava_nanbox_as_int(b))); + } else { + UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) / bv)); + } + UF_DISPATCH(); +} + +uf_mod: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + int64_t bv = rava_nanbox_to_long(b); + if (bv == 0) { UF_PUSH(rava_nanbox_int(0)); UF_DISPATCH(); } + if (rava_nanbox_is_int(a) && rava_nanbox_is_int(b)) { + UF_PUSH(rava_nanbox_int(rava_nanbox_as_int(a) % rava_nanbox_as_int(b))); + } else { + UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) % bv)); + } + UF_DISPATCH(); +} + +uf_neg: { + RavaNanboxValue_t a = UF_POP(); + if (rava_nanbox_is_int(a)) { + UF_PUSH(rava_nanbox_int(-rava_nanbox_as_int(a))); + } else { + UF_PUSH(rava_nanbox_long(-rava_nanbox_to_long(a))); + } + UF_DISPATCH(); +} + +uf_and: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + UF_PUSH(rava_nanbox_bool(rava_nanbox_to_bool(a) && rava_nanbox_to_bool(b))); + UF_DISPATCH(); +} + +uf_or: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + UF_PUSH(rava_nanbox_bool(rava_nanbox_to_bool(a) || rava_nanbox_to_bool(b))); + UF_DISPATCH(); +} + +uf_xor: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) ^ rava_nanbox_to_long(b))); + UF_DISPATCH(); +} + +uf_not: { + RavaNanboxValue_t a = UF_POP(); + UF_PUSH(rava_nanbox_bool(!rava_nanbox_to_bool(a))); + UF_DISPATCH(); +} + +uf_shl: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) << (rava_nanbox_to_int(b) & 63))); + UF_DISPATCH(); +} + +uf_shr: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) >> (rava_nanbox_to_int(b) & 63))); + UF_DISPATCH(); +} + +uf_ushr: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + UF_PUSH(rava_nanbox_long((uint64_t)rava_nanbox_to_long(a) >> (rava_nanbox_to_int(b) & 63))); + UF_DISPATCH(); +} + +uf_eq: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + UF_PUSH(rava_nanbox_bool(rava_nanbox_to_long(a) == rava_nanbox_to_long(b))); + UF_DISPATCH(); +} + +uf_ne: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + UF_PUSH(rava_nanbox_bool(rava_nanbox_to_long(a) != rava_nanbox_to_long(b))); + UF_DISPATCH(); +} + +uf_lt: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + UF_PUSH(rava_nanbox_bool(rava_nanbox_to_long(a) < rava_nanbox_to_long(b))); + UF_DISPATCH(); +} + +uf_le: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + UF_PUSH(rava_nanbox_bool(rava_nanbox_to_long(a) <= rava_nanbox_to_long(b))); + UF_DISPATCH(); +} + +uf_gt: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + UF_PUSH(rava_nanbox_bool(rava_nanbox_to_long(a) > rava_nanbox_to_long(b))); + UF_DISPATCH(); +} + +uf_ge: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + UF_PUSH(rava_nanbox_bool(rava_nanbox_to_long(a) >= rava_nanbox_to_long(b))); + UF_DISPATCH(); +} + +uf_jump: { + LabelTable_t *lt = (LabelTable_t*)frame->method->label_table; + pc = rava_labeltable_lookup(lt, instr->operand.label_id); + UF_DISPATCH(); +} + +uf_jump_if_true: { + RavaNanboxValue_t cond = UF_POP(); + if (rava_nanbox_to_bool(cond)) { + LabelTable_t *lt = (LabelTable_t*)frame->method->label_table; + pc = rava_labeltable_lookup(lt, instr->operand.label_id); + } + UF_DISPATCH(); +} + +uf_jump_if_false: { + RavaNanboxValue_t cond = UF_POP(); + if (!rava_nanbox_to_bool(cond)) { + LabelTable_t *lt = (LabelTable_t*)frame->method->label_table; + pc = rava_labeltable_lookup(lt, instr->operand.label_id); + } + UF_DISPATCH(); +} + +uf_label: + UF_DISPATCH(); + +uf_call: +uf_call_static: { + RavaMethod_t *target = _rava_vm_find_method_cached(vm, + instr->operand.call.class_name, instr->operand.call.method_name); + if (!target) { + vm->had_error = true; + goto uf_done; + } + if (!target->label_table) { + rava_optimize_superinstructions(target); + target->label_table = rava_labeltable_create(target->instructions); + } + RavaNanboxValue_t args[16]; + for (int i = instr->operand.call.arg_count - 1; i >= 0; i--) { + args[i] = UF_POP(); + } + frame->pc = pc; + FastFrame_t *new_frame = rava_fastframe_push(target, target->local_count); + if (!new_frame) { + vm->had_error = true; + goto uf_done; + } + for (int i = 0; i < instr->operand.call.arg_count; i++) { + new_frame->locals[i] = args[i]; + } + frame = new_frame; + instructions = target->instructions->instructions; + instr_count = target->instructions->count; + pc = 0; + UF_DISPATCH(); +} + +uf_call_virtual: { + int arg_count = instr->operand.call.arg_count; + RavaNanboxValue_t args[16]; + for (int i = arg_count - 1; i >= 0; i--) { + args[i] = UF_POP(); + } + RavaNanboxValue_t obj_val = UF_POP(); + if (rava_nanbox_is_null(obj_val)) { + vm->had_error = true; + goto uf_done; + } + RavaObject_t *obj = rava_nanbox_as_object(obj_val); + const char *class_name = obj->class_name; + RavaMethod_t *target = _rava_vm_find_method_cached(vm, class_name, instr->operand.call.method_name); + if (!target) { + if (strcmp(instr->operand.call.method_name, "") == 0 && arg_count == 0) { + UF_DISPATCH(); + } + vm->had_error = true; + goto uf_done; + } + if (!target->label_table) { + rava_optimize_superinstructions(target); + target->label_table = rava_labeltable_create(target->instructions); + } + frame->pc = pc; + FastFrame_t *new_frame = rava_fastframe_push(target, target->local_count); + if (!new_frame) { + vm->had_error = true; + goto uf_done; + } + new_frame->has_this = true; + new_frame->this_ref = obj_val; + for (int i = 0; i < arg_count; i++) { + new_frame->locals[i] = args[i]; + } + frame = new_frame; + instructions = target->instructions->instructions; + instr_count = target->instructions->count; + pc = 0; + UF_DISPATCH(); +} + +uf_call_native: + UF_DISPATCH(); + +uf_return: { + result = UF_POP(); + rava_fastframe_pop(); + frame = rava_fastframe_current(); + if (!frame) { + RavaCallFrame_t *old_frame = rava_call_stack_current(vm->call_stack); + if (old_frame) { + rava_stack_push(old_frame->operand_stack, rava_nanbox_to_value(result)); + } + goto uf_done; + } + UF_PUSH(result); + pc = frame->pc; + instructions = frame->method->instructions->instructions; + instr_count = frame->method->instructions->count; + UF_DISPATCH(); +} + +uf_return_void: { + rava_fastframe_pop(); + frame = rava_fastframe_current(); + if (!frame) goto uf_done; + pc = frame->pc; + instructions = frame->method->instructions->instructions; + instr_count = frame->method->instructions->count; + UF_DISPATCH(); +} + +uf_new: { + RavaObject_t *obj = rava_object_create(instr->operand.call.class_name); + UF_PUSH(rava_nanbox_object(obj)); + UF_DISPATCH(); +} + +uf_new_array: { + RavaNanboxValue_t size_val = UF_POP(); + int32_t size = rava_nanbox_to_int(size_val); + RavaArray_t *arr = rava_array_create(RAVA_VAL_INT, size > 0 ? (size_t)size : 0); + UF_PUSH(rava_nanbox_array(arr)); + UF_DISPATCH(); +} + +uf_array_length: { + RavaNanboxValue_t arr_val = UF_POP(); + RavaArray_t *arr = rava_nanbox_as_array(arr_val); + UF_PUSH(rava_nanbox_int(arr ? (int32_t)arr->length : 0)); + UF_DISPATCH(); +} + +uf_load_array: { + RavaNanboxValue_t idx = UF_POP(); + RavaNanboxValue_t arr_val = UF_POP(); + RavaArray_t *arr = rava_nanbox_as_array(arr_val); + int32_t i = rava_nanbox_to_int(idx); + if (arr && i >= 0 && (size_t)i < arr->length) { + int32_t val = rava_array_get_int(arr, (size_t)i); + UF_PUSH(rava_nanbox_int(val)); + } else { + UF_PUSH(rava_nanbox_int(0)); + } + UF_DISPATCH(); +} + +uf_store_array: { + RavaNanboxValue_t val = UF_POP(); + RavaNanboxValue_t idx = UF_POP(); + RavaNanboxValue_t arr_val = UF_POP(); + RavaArray_t *arr = rava_nanbox_as_array(arr_val); + int32_t i = rava_nanbox_to_int(idx); + if (arr && i >= 0 && (size_t)i < arr->length) { + rava_array_set_int(arr, (size_t)i, rava_nanbox_to_int(val)); + } + UF_DISPATCH(); +} + +uf_get_field: +uf_load_field: { + RavaNanboxValue_t obj_val = UF_POP(); + RavaObject_t *obj = rava_nanbox_as_object(obj_val); + if (obj) { + RavaValue_t v = rava_object_get_field(obj, instr->operand.field.field_name); + UF_PUSH(rava_value_to_nanbox(v)); + } else { + UF_PUSH(rava_nanbox_null()); + } + UF_DISPATCH(); +} + +uf_put_field: +uf_store_field: { + RavaNanboxValue_t val = UF_POP(); + RavaNanboxValue_t obj_val = UF_POP(); + RavaObject_t *obj = rava_nanbox_as_object(obj_val); + if (obj) { + rava_object_set_field(obj, instr->operand.field.field_name, rava_nanbox_to_value(val)); + } + UF_DISPATCH(); +} + +uf_load_static: { + RavaValue_t *v = rava_static_field_table_get(vm->static_fields, instr->operand.field.class_name, instr->operand.field.field_name); + UF_PUSH(v ? rava_value_to_nanbox(*v) : rava_nanbox_null()); + UF_DISPATCH(); +} + +uf_store_static: { + RavaNanboxValue_t val = UF_POP(); + rava_static_field_table_set(vm->static_fields, instr->operand.field.class_name, instr->operand.field.field_name, rava_nanbox_to_value(val)); + UF_DISPATCH(); +} + +uf_load_this: + if (frame->has_this) { + UF_PUSH(frame->this_ref); + } else { + UF_PUSH(rava_nanbox_null()); + } + UF_DISPATCH(); + +uf_pop: + UF_POP(); + UF_DISPATCH(); + +uf_dup: { + RavaNanboxValue_t v = UF_PEEK(); + UF_PUSH(v); + UF_DISPATCH(); +} + +uf_print: { + RavaNanboxValue_t v = UF_POP(); + if (rava_nanbox_is_int(v)) { + printf("%d", rava_nanbox_as_int(v)); + } else if (rava_nanbox_is_long(v)) { + printf("%ld", rava_nanbox_as_long(v)); + } else if (rava_nanbox_is_double(v)) { + printf("%g", rava_nanbox_as_double(v)); + } else if (rava_nanbox_is_bool(v)) { + printf("%s", rava_nanbox_as_bool(v) ? "true" : "false"); + } else if (rava_nanbox_is_string(v)) { + printf("%s", rava_nanbox_as_string(v)); + } else if (rava_nanbox_is_null(v)) { + printf("null"); + } + UF_DISPATCH(); +} + +uf_println: { + RavaNanboxValue_t v = UF_POP(); + if (rava_nanbox_is_int(v)) { + printf("%d\n", rava_nanbox_as_int(v)); + } else if (rava_nanbox_is_long(v)) { + printf("%ld\n", rava_nanbox_as_long(v)); + } else if (rava_nanbox_is_double(v)) { + printf("%g\n", rava_nanbox_as_double(v)); + } else if (rava_nanbox_is_bool(v)) { + printf("%s\n", rava_nanbox_as_bool(v) ? "true" : "false"); + } else if (rava_nanbox_is_string(v)) { + printf("%s\n", rava_nanbox_as_string(v)); + } else if (rava_nanbox_is_null(v)) { + printf("null\n"); + } + UF_DISPATCH(); +} + +uf_string_length: { + RavaNanboxValue_t s = UF_POP(); + const char *str = rava_nanbox_as_string(s); + UF_PUSH(rava_nanbox_int(str ? (int32_t)strlen(str) : 0)); + UF_DISPATCH(); +} + +uf_string_charat: { + RavaNanboxValue_t idx = UF_POP(); + RavaNanboxValue_t s = UF_POP(); + const char *str = rava_nanbox_as_string(s); + int32_t i = rava_nanbox_to_int(idx); + if (str && i >= 0 && (size_t)i < strlen(str)) { + UF_PUSH(rava_nanbox_int((int32_t)str[i])); + } else { + UF_PUSH(rava_nanbox_int(0)); + } + UF_DISPATCH(); +} + +uf_string_substring: { + RavaNanboxValue_t end_val = UF_POP(); + RavaNanboxValue_t start_val = UF_POP(); + RavaNanboxValue_t s = UF_POP(); + const char *str = rava_nanbox_as_string(s); + int32_t start = rava_nanbox_to_int(start_val); + int32_t end = rava_nanbox_to_int(end_val); + if (str && start >= 0 && end >= start && (size_t)end <= strlen(str)) { + size_t len = (size_t)(end - start); + char *sub = malloc(len + 1); + strncpy(sub, str + start, len); + sub[len] = '\0'; + UF_PUSH(rava_nanbox_string(sub)); + } else { + UF_PUSH(rava_nanbox_string("")); + } + UF_DISPATCH(); +} + +uf_string_equals: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + const char *sa = rava_nanbox_as_string(a); + const char *sb = rava_nanbox_as_string(b); + UF_PUSH(rava_nanbox_bool(sa && sb && strcmp(sa, sb) == 0)); + UF_DISPATCH(); +} + +uf_string_compareto: { + RavaNanboxValue_t b = UF_POP(); + RavaNanboxValue_t a = UF_POP(); + const char *sa = rava_nanbox_as_string(a); + const char *sb = rava_nanbox_as_string(b); + if (sa && sb) { + UF_PUSH(rava_nanbox_int(strcmp(sa, sb))); + } else { + UF_PUSH(rava_nanbox_int(0)); + } + UF_DISPATCH(); +} + +uf_cast: +uf_instanceof: +uf_throw: + UF_DISPATCH(); + +uf_file_read: +uf_file_write: +uf_file_exists: +uf_file_delete: + UF_DISPATCH(); + +uf_current_time_millis: { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + int64_t ms = (int64_t)ts.tv_sec * 1000 + (int64_t)ts.tv_nsec / 1000000; + UF_PUSH(rava_nanbox_long(ms)); + UF_DISPATCH(); +} + +uf_nano_time: { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + int64_t ns = (int64_t)ts.tv_sec * 1000000000LL + (int64_t)ts.tv_nsec; + UF_PUSH(rava_nanbox_long(ns)); + UF_DISPATCH(); +} + +uf_inc_local: { + int idx = instr->operand.var.index; + RavaNanboxValue_t v = frame->locals[idx]; + if (rava_nanbox_is_int(v)) { + frame->locals[idx] = rava_nanbox_int(rava_nanbox_as_int(v) + 1); + } else { + frame->locals[idx] = rava_nanbox_long(rava_nanbox_to_long(v) + 1); + } + UF_DISPATCH(); +} + +uf_dec_local: { + int idx = instr->operand.var.index; + RavaNanboxValue_t v = frame->locals[idx]; + if (rava_nanbox_is_int(v)) { + frame->locals[idx] = rava_nanbox_int(rava_nanbox_as_int(v) - 1); + } else { + frame->locals[idx] = rava_nanbox_long(rava_nanbox_to_long(v) - 1); + } + UF_DISPATCH(); +} + +uf_load_local_const_add: { + int idx = instr->operand.super.local_index; + int64_t c = instr->operand.super.const_value; + RavaNanboxValue_t v = frame->locals[idx]; + if (rava_nanbox_is_int(v)) { + UF_PUSH(rava_nanbox_int(rava_nanbox_as_int(v) + (int32_t)c)); + } else { + UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(v) + c)); + } + UF_DISPATCH(); +} + +uf_load_local_const_lt_jumpfalse: { + int idx = instr->operand.super.local_index; + int64_t c = instr->operand.super.const_value; + int label = instr->operand.super.label_id; + RavaNanboxValue_t v = frame->locals[idx]; + int64_t local_val = rava_nanbox_to_long(v); + if (!(local_val < c)) { + LabelTable_t *lt = (LabelTable_t*)frame->method->label_table; + pc = rava_labeltable_lookup(lt, label); + } + UF_DISPATCH(); +} + +uf_load_local_const_le_jumpfalse: { + int idx = instr->operand.super.local_index; + int64_t c = instr->operand.super.const_value; + int label = instr->operand.super.label_id; + RavaNanboxValue_t v = frame->locals[idx]; + int64_t local_val = rava_nanbox_to_long(v); + if (!(local_val <= c)) { + LabelTable_t *lt = (LabelTable_t*)frame->method->label_table; + pc = rava_labeltable_lookup(lt, label); + } + UF_DISPATCH(); +} + +uf_load_two_locals: { + int idx1 = instr->operand.two_locals.index1; + int idx2 = instr->operand.two_locals.index2; + UF_PUSH(frame->locals[idx1]); + UF_PUSH(frame->locals[idx2]); + UF_DISPATCH(); +} + +uf_add_local_to_local: { + int dest = instr->operand.add_locals.dest_index; + int src = instr->operand.add_locals.src_index; + RavaNanboxValue_t d = frame->locals[dest]; + RavaNanboxValue_t s = frame->locals[src]; + if (rava_nanbox_is_int(d) && rava_nanbox_is_int(s)) { + frame->locals[dest] = rava_nanbox_int(rava_nanbox_as_int(d) + rava_nanbox_as_int(s)); + } else { + frame->locals[dest] = rava_nanbox_long(rava_nanbox_to_long(d) + rava_nanbox_to_long(s)); + } + UF_DISPATCH(); +} + +uf_load_local_lt_local_jumpfalse: { + int local1 = instr->operand.cmp_locals.local1; + int local2 = instr->operand.cmp_locals.local2; + int label = instr->operand.cmp_locals.label_id; + int64_t v1 = rava_nanbox_to_long(frame->locals[local1]); + int64_t v2 = rava_nanbox_to_long(frame->locals[local2]); + if (!(v1 < v2)) { + LabelTable_t *lt = (LabelTable_t*)frame->method->label_table; + pc = rava_labeltable_lookup(lt, label); + } + UF_DISPATCH(); +} + +uf_done: + rava_fastframe_reset(); + return !vm->had_error; + +#undef UF_DISPATCH +#undef UF_PUSH +#undef UF_POP +#undef UF_PEEK +} +#endif + +bool rava_vm_execute(RavaVM_t *vm, const char *class_name, const char *method_name) { + RavaMethod_t *method = _rava_vm_find_method_cached(vm, class_name, method_name); + if (!method) { + vm->had_error = true; + vm->error_message = malloc(256); + snprintf(vm->error_message, 256, "Method not found: %s.%s", class_name, method_name); + return false; + } + + RavaCallFrame_t *frame = rava_call_frame_create(method); + rava_call_stack_push(vm->call_stack, frame); + +#ifdef __GNUC__ + return _rava_vm_execute_ultrafast(vm, method); +#else + while (frame->pc < frame->method->instructions->count) { + RavaInstruction_t *instr = &frame->method->instructions->instructions[frame->pc]; + + if (instr->opcode == RAVA_OP_RETURN || instr->opcode == RAVA_OP_RETURN_VOID) { + break; + } + + frame->pc++; + if (!_rava_vm_execute_instruction(vm, frame, instr)) { + return false; + } + } + + return !vm->had_error; +#endif +} + +RavaValue_t rava_vm_get_result(RavaVM_t *vm) { + RavaCallFrame_t *frame = rava_call_stack_current(vm->call_stack); + if (frame && !rava_stack_is_empty(frame->operand_stack)) { + return rava_stack_peek(frame->operand_stack); + } + return rava_value_int(0); +} diff --git a/runtime/runtime.h b/runtime/runtime.h new file mode 100644 index 0000000..1782c39 --- /dev/null +++ b/runtime/runtime.h @@ -0,0 +1,149 @@ +#ifndef RAVA_RUNTIME_H +#define RAVA_RUNTIME_H + +#include "../ir/ir.h" +#include "../types/types.h" +#include +#include +#include + +typedef enum { + RAVA_VAL_INT, + RAVA_VAL_LONG, + RAVA_VAL_FLOAT, + RAVA_VAL_DOUBLE, + RAVA_VAL_BOOLEAN, + RAVA_VAL_CHAR, + RAVA_VAL_NULL, + RAVA_VAL_OBJECT, + RAVA_VAL_ARRAY, + RAVA_VAL_STRING +} RavaValueType_e; + +typedef struct { + RavaValueType_e element_type; + size_t length; + void *data; +} RavaArray_t; + +typedef struct RavaObject_t RavaObject_t; +typedef struct RavaValue_t RavaValue_t; + +struct RavaObject_t { + char *class_name; + char **field_names; + RavaValue_t *field_values; + size_t field_count; + size_t field_capacity; +}; + +struct RavaValue_t { + RavaValueType_e type; + union { + int32_t int_val; + int64_t long_val; + float float_val; + double double_val; + bool bool_val; + char char_val; + RavaObject_t *object_val; + RavaArray_t *array_val; + char *string_val; + } data; +}; + +typedef struct { + RavaValue_t *values; + size_t capacity; + size_t top; +} RavaStack_t; + +typedef struct { + RavaMethod_t *method; + RavaValue_t *locals; + size_t local_count; + size_t pc; + RavaStack_t *operand_stack; + RavaValue_t this_ref; + bool has_this; +} RavaCallFrame_t; + +typedef struct { + RavaCallFrame_t **frames; + size_t capacity; + size_t count; +} RavaCallStack_t; + +typedef struct { + char *class_name; + char *field_name; + RavaValue_t value; +} RavaStaticField_t; + +typedef struct { + RavaStaticField_t *fields; + size_t count; + size_t capacity; +} RavaStaticFieldTable_t; + +struct MethodCache_s; + +typedef struct { + RavaProgram_t *program; + RavaCallStack_t *call_stack; + RavaStaticFieldTable_t *static_fields; + struct MethodCache_s *method_cache; + char *error_message; + bool had_error; +} RavaVM_t; + +RavaValue_t rava_value_int(int32_t value); +RavaValue_t rava_value_long(int64_t value); +RavaValue_t rava_value_float(float value); +RavaValue_t rava_value_double(double value); +RavaValue_t rava_value_boolean(bool value); +RavaValue_t rava_value_null(); +RavaValue_t rava_value_array(RavaArray_t *array); +RavaValue_t rava_value_string(const char *str); + +int32_t rava_value_as_int(RavaValue_t value); +int64_t rava_value_as_long(RavaValue_t value); +double rava_value_as_double(RavaValue_t value); +bool rava_value_as_boolean(RavaValue_t value); +const char* rava_value_as_string(RavaValue_t value); + +RavaArray_t* rava_array_create(RavaValueType_e element_type, size_t length); +void rava_array_destroy(RavaArray_t *array); +void rava_array_set_int(RavaArray_t *array, size_t index, int32_t value); +int32_t rava_array_get_int(RavaArray_t *array, size_t index); +size_t rava_array_length(RavaArray_t *array); + +RavaObject_t* rava_object_create(const char *class_name); +void rava_object_destroy(RavaObject_t *obj); +void rava_object_set_field(RavaObject_t *obj, const char *name, RavaValue_t value); +RavaValue_t rava_object_get_field(RavaObject_t *obj, const char *name); +RavaValue_t rava_value_object(RavaObject_t *obj); + +RavaStack_t* rava_stack_create(size_t capacity); +void rava_stack_destroy(RavaStack_t *stack); +void rava_stack_push(RavaStack_t *stack, RavaValue_t value); +RavaValue_t rava_stack_pop(RavaStack_t *stack); +RavaValue_t rava_stack_peek(RavaStack_t *stack); +bool rava_stack_is_empty(RavaStack_t *stack); + +RavaCallFrame_t* rava_call_frame_create(RavaMethod_t *method); +void rava_call_frame_destroy(RavaCallFrame_t *frame); + +RavaCallStack_t* rava_call_stack_create(); +void rava_call_stack_destroy(RavaCallStack_t *stack); +void rava_call_stack_push(RavaCallStack_t *stack, RavaCallFrame_t *frame); +RavaCallFrame_t* rava_call_stack_pop(RavaCallStack_t *stack); +RavaCallFrame_t* rava_call_stack_current(RavaCallStack_t *stack); + +RavaVM_t* rava_vm_create(RavaProgram_t *program); +void rava_vm_destroy(RavaVM_t *vm); + +bool rava_vm_execute(RavaVM_t *vm, const char *class_name, const char *method_name); +RavaValue_t rava_vm_get_result(RavaVM_t *vm); + +#endif diff --git a/runtime/superinst.c b/runtime/superinst.c new file mode 100644 index 0000000..4633946 --- /dev/null +++ b/runtime/superinst.c @@ -0,0 +1,149 @@ +#include "superinst.h" +#include + +static void optimize_inc_dec_local(RavaInstruction_t *instrs, size_t count) { + for (size_t i = 0; i + 3 < count; i++) { + if (instrs[i].opcode == RAVA_OP_LOAD_LOCAL && + instrs[i+1].opcode == RAVA_OP_LOAD_CONST && + instrs[i+1].operand.int_value == 1 && + instrs[i+2].opcode == RAVA_OP_ADD && + instrs[i+3].opcode == RAVA_OP_STORE_LOCAL && + instrs[i].operand.var.index == instrs[i+3].operand.var.index) { + + instrs[i].opcode = RAVA_OP_INC_LOCAL; + instrs[i+1].opcode = RAVA_OP_NOP; + instrs[i+2].opcode = RAVA_OP_NOP; + instrs[i+3].opcode = RAVA_OP_NOP; + } + + if (instrs[i].opcode == RAVA_OP_LOAD_LOCAL && + instrs[i+1].opcode == RAVA_OP_LOAD_CONST && + instrs[i+1].operand.int_value == 1 && + instrs[i+2].opcode == RAVA_OP_SUB && + instrs[i+3].opcode == RAVA_OP_STORE_LOCAL && + instrs[i].operand.var.index == instrs[i+3].operand.var.index) { + + instrs[i].opcode = RAVA_OP_DEC_LOCAL; + instrs[i+1].opcode = RAVA_OP_NOP; + instrs[i+2].opcode = RAVA_OP_NOP; + instrs[i+3].opcode = RAVA_OP_NOP; + } + } +} + +static void optimize_loop_comparison(RavaInstruction_t *instrs, size_t count) { + for (size_t i = 0; i + 3 < count; i++) { + if (instrs[i].opcode == RAVA_OP_LOAD_LOCAL && + instrs[i+1].opcode == RAVA_OP_LOAD_CONST && + instrs[i+2].opcode == RAVA_OP_LT && + instrs[i+3].opcode == RAVA_OP_JUMP_IF_FALSE) { + + int local_idx = instrs[i].operand.var.index; + int64_t const_val = instrs[i+1].operand.int_value; + int label = instrs[i+3].operand.label_id; + + instrs[i].opcode = RAVA_OP_LOAD_LOCAL_CONST_LT_JUMPFALSE; + instrs[i].operand.super.local_index = local_idx; + instrs[i].operand.super.const_value = const_val; + instrs[i].operand.super.label_id = label; + + instrs[i+1].opcode = RAVA_OP_NOP; + instrs[i+2].opcode = RAVA_OP_NOP; + instrs[i+3].opcode = RAVA_OP_NOP; + } + + if (instrs[i].opcode == RAVA_OP_LOAD_LOCAL && + instrs[i+1].opcode == RAVA_OP_LOAD_CONST && + instrs[i+2].opcode == RAVA_OP_LE && + instrs[i+3].opcode == RAVA_OP_JUMP_IF_FALSE) { + + int local_idx = instrs[i].operand.var.index; + int64_t const_val = instrs[i+1].operand.int_value; + int label = instrs[i+3].operand.label_id; + + instrs[i].opcode = RAVA_OP_LOAD_LOCAL_CONST_LE_JUMPFALSE; + instrs[i].operand.super.local_index = local_idx; + instrs[i].operand.super.const_value = const_val; + instrs[i].operand.super.label_id = label; + + instrs[i+1].opcode = RAVA_OP_NOP; + instrs[i+2].opcode = RAVA_OP_NOP; + instrs[i+3].opcode = RAVA_OP_NOP; + } + } +} + +static void optimize_two_locals(RavaInstruction_t *instrs, size_t count) { + for (size_t i = 0; i + 1 < count; i++) { + if (instrs[i].opcode == RAVA_OP_LOAD_LOCAL && + instrs[i+1].opcode == RAVA_OP_LOAD_LOCAL) { + + int idx1 = instrs[i].operand.var.index; + int idx2 = instrs[i+1].operand.var.index; + + instrs[i].opcode = RAVA_OP_LOAD_TWO_LOCALS; + instrs[i].operand.two_locals.index1 = idx1; + instrs[i].operand.two_locals.index2 = idx2; + + instrs[i+1].opcode = RAVA_OP_NOP; + } + } +} + +static void optimize_add_local_to_local(RavaInstruction_t *instrs, size_t count) { + for (size_t i = 0; i + 3 < count; i++) { + if (instrs[i].opcode == RAVA_OP_LOAD_LOCAL && + instrs[i+1].opcode == RAVA_OP_LOAD_LOCAL && + instrs[i+2].opcode == RAVA_OP_ADD && + instrs[i+3].opcode == RAVA_OP_STORE_LOCAL && + instrs[i].operand.var.index == instrs[i+3].operand.var.index) { + + int dest_idx = instrs[i].operand.var.index; + int src_idx = instrs[i+1].operand.var.index; + + instrs[i].opcode = RAVA_OP_ADD_LOCAL_TO_LOCAL; + instrs[i].operand.add_locals.dest_index = dest_idx; + instrs[i].operand.add_locals.src_index = src_idx; + + instrs[i+1].opcode = RAVA_OP_NOP; + instrs[i+2].opcode = RAVA_OP_NOP; + instrs[i+3].opcode = RAVA_OP_NOP; + } + } +} + +static void optimize_local_lt_local_jump(RavaInstruction_t *instrs, size_t count) { + for (size_t i = 0; i + 3 < count; i++) { + if (instrs[i].opcode == RAVA_OP_LOAD_LOCAL && + instrs[i+1].opcode == RAVA_OP_LOAD_LOCAL && + instrs[i+2].opcode == RAVA_OP_LT && + instrs[i+3].opcode == RAVA_OP_JUMP_IF_FALSE) { + + int local1 = instrs[i].operand.var.index; + int local2 = instrs[i+1].operand.var.index; + int label = instrs[i+3].operand.label_id; + + instrs[i].opcode = RAVA_OP_LOAD_LOCAL_LT_LOCAL_JUMPFALSE; + instrs[i].operand.cmp_locals.local1 = local1; + instrs[i].operand.cmp_locals.local2 = local2; + instrs[i].operand.cmp_locals.label_id = label; + + instrs[i+1].opcode = RAVA_OP_NOP; + instrs[i+2].opcode = RAVA_OP_NOP; + instrs[i+3].opcode = RAVA_OP_NOP; + } + } +} + +void rava_optimize_superinstructions(RavaMethod_t* method) { + if (!method || !method->instructions) return; + + RavaInstruction_t *instrs = method->instructions->instructions; + size_t count = method->instructions->count; + + optimize_add_local_to_local(instrs, count); + optimize_local_lt_local_jump(instrs, count); + optimize_inc_dec_local(instrs, count); + optimize_loop_comparison(instrs, count); + optimize_two_locals(instrs, count); +} diff --git a/runtime/superinst.h b/runtime/superinst.h new file mode 100644 index 0000000..03fa489 --- /dev/null +++ b/runtime/superinst.h @@ -0,0 +1,8 @@ +#ifndef RAVA_SUPERINST_H +#define RAVA_SUPERINST_H + +#include "../ir/ir.h" + +void rava_optimize_superinstructions(RavaMethod_t* method); + +#endif diff --git a/semantic/semantic.c b/semantic/semantic.c new file mode 100644 index 0000000..14f3500 --- /dev/null +++ b/semantic/semantic.c @@ -0,0 +1,462 @@ +#define _POSIX_C_SOURCE 200809L +#include "semantic.h" +#include +#include +#include + +static void _rava_semantic_error(RavaSemanticAnalyzer_t *analyzer, const char *message, int line, int col); +static bool _rava_semantic_analyze_node(RavaSemanticAnalyzer_t *analyzer, RavaASTNode_t *node); +static RavaType_t* _rava_semantic_check_expression(RavaSemanticAnalyzer_t *analyzer, RavaASTNode_t *expr); + +RavaSemanticAnalyzer_t* rava_semantic_analyzer_create() { + RavaSemanticAnalyzer_t *analyzer = calloc(1, sizeof(RavaSemanticAnalyzer_t)); + analyzer->symbol_table = rava_symbol_table_create(); + analyzer->error_message = NULL; + analyzer->had_error = false; + analyzer->error_count = 0; + return analyzer; +} + +void rava_semantic_analyzer_destroy(RavaSemanticAnalyzer_t *analyzer) { + if (!analyzer) return; + rava_symbol_table_destroy(analyzer->symbol_table); + free(analyzer->error_message); + free(analyzer); +} + +static void _rava_semantic_error(RavaSemanticAnalyzer_t *analyzer, const char *message, int line, int col) { + analyzer->had_error = true; + analyzer->error_count++; + + if (analyzer->error_message) free(analyzer->error_message); + analyzer->error_message = malloc(512); + snprintf(analyzer->error_message, 512, "Semantic error at line %d, column %d: %s", + line, col, message); +} + +static RavaType_t* _rava_semantic_resolve_type(RavaSemanticAnalyzer_t *analyzer, RavaASTNode_t *type_node) { + (void)analyzer; + if (!type_node || type_node->type != RAVA_AST_TYPE) return NULL; + + RavaType_t *base_type = rava_type_from_name(type_node->data.type.type_name); + + if (type_node->data.type.is_array) { + return rava_type_create_array(base_type, type_node->data.type.array_dimensions); + } + + return base_type; +} + +static bool _rava_semantic_analyze_class(RavaSemanticAnalyzer_t *analyzer, RavaASTNode_t *class_node) { + if (class_node->type != RAVA_AST_CLASS_DECL) return false; + + char *class_name = class_node->data.class_decl.name; + RavaType_t *class_type = rava_type_create_class(class_name); + + RavaSymbol_t *class_symbol = rava_symbol_create(RAVA_SYMBOL_CLASS, class_name, class_type); + class_symbol->modifiers = class_node->data.class_decl.modifiers; + class_symbol->modifiers_count = class_node->data.class_decl.modifiers_count; + class_symbol->declaration = class_node; + + if (!rava_symbol_table_define(analyzer->symbol_table, class_symbol)) { + _rava_semantic_error(analyzer, "Class already defined", class_node->line, class_node->column); + rava_symbol_destroy(class_symbol); + return false; + } + + rava_symbol_table_enter_scope(analyzer->symbol_table, class_name); + + for (size_t i = 0; i < class_node->children_count; i++) { + _rava_semantic_analyze_node(analyzer, class_node->children[i]); + } + + rava_symbol_table_exit_scope(analyzer->symbol_table); + + return !analyzer->had_error; +} + +static bool _rava_semantic_analyze_method(RavaSemanticAnalyzer_t *analyzer, RavaASTNode_t *method_node) { + if (method_node->type != RAVA_AST_METHOD_DECL) return false; + + char *method_name = method_node->data.method_decl.name; + RavaType_t *return_type = _rava_semantic_resolve_type(analyzer, method_node->data.method_decl.return_type); + + RavaSymbol_t *method_symbol = rava_symbol_create(RAVA_SYMBOL_METHOD, method_name, return_type); + method_symbol->modifiers = method_node->data.method_decl.modifiers; + method_symbol->modifiers_count = method_node->data.method_decl.modifiers_count; + method_symbol->declaration = method_node; + + if (!rava_symbol_table_define(analyzer->symbol_table, method_symbol)) { + char error_msg[256]; + snprintf(error_msg, sizeof(error_msg), "Method '%s' already defined", method_name); + _rava_semantic_error(analyzer, error_msg, method_node->line, method_node->column); + rava_symbol_destroy(method_symbol); + return false; + } + + rava_symbol_table_enter_scope(analyzer->symbol_table, method_name); + + for (size_t i = 0; i < method_node->children_count; i++) { + RavaASTNode_t *child = method_node->children[i]; + + if (child->type == RAVA_AST_PARAM_DECL) { + char *param_name = child->data.var_decl.name; + RavaType_t *param_type = _rava_semantic_resolve_type(analyzer, child->data.var_decl.type); + + RavaSymbol_t *param_symbol = rava_symbol_create(RAVA_SYMBOL_PARAMETER, param_name, param_type); + param_symbol->declaration = child; + + if (!rava_symbol_table_define(analyzer->symbol_table, param_symbol)) { + char error_msg[256]; + snprintf(error_msg, sizeof(error_msg), "Parameter '%s' already defined", param_name); + _rava_semantic_error(analyzer, error_msg, child->line, child->column); + rava_symbol_destroy(param_symbol); + } + } else if (child->type == RAVA_AST_BLOCK_STMT) { + _rava_semantic_analyze_node(analyzer, child); + } + } + + rava_symbol_table_exit_scope(analyzer->symbol_table); + + return !analyzer->had_error; +} + +static bool _rava_semantic_analyze_field(RavaSemanticAnalyzer_t *analyzer, RavaASTNode_t *field_node) { + if (field_node->type != RAVA_AST_FIELD_DECL) return false; + + char *field_name = field_node->data.var_decl.name; + RavaType_t *field_type = _rava_semantic_resolve_type(analyzer, field_node->data.var_decl.type); + + RavaSymbol_t *field_symbol = rava_symbol_create(RAVA_SYMBOL_FIELD, field_name, field_type); + field_symbol->declaration = field_node; + + if (!rava_symbol_table_define(analyzer->symbol_table, field_symbol)) { + char error_msg[256]; + snprintf(error_msg, sizeof(error_msg), "Field '%s' already defined", field_name); + _rava_semantic_error(analyzer, error_msg, field_node->line, field_node->column); + rava_symbol_destroy(field_symbol); + return false; + } + + if (field_node->data.var_decl.initializer) { + RavaType_t *init_type = _rava_semantic_check_expression(analyzer, field_node->data.var_decl.initializer); + if (init_type && !rava_type_is_assignable_to(init_type, field_type)) { + char error_msg[256]; + snprintf(error_msg, sizeof(error_msg), + "Cannot assign '%s' to field of type '%s'", + rava_type_to_string(init_type), + rava_type_to_string(field_type)); + _rava_semantic_error(analyzer, error_msg, field_node->line, field_node->column); + } + } + + return !analyzer->had_error; +} + +static bool _rava_semantic_analyze_var_decl(RavaSemanticAnalyzer_t *analyzer, RavaASTNode_t *var_node) { + if (var_node->type != RAVA_AST_VAR_DECL) return false; + + char *var_name = var_node->data.var_decl.name; + RavaType_t *var_type = _rava_semantic_resolve_type(analyzer, var_node->data.var_decl.type); + + RavaSymbol_t *var_symbol = rava_symbol_create(RAVA_SYMBOL_VARIABLE, var_name, var_type); + var_symbol->declaration = var_node; + + if (!rava_symbol_table_define(analyzer->symbol_table, var_symbol)) { + char error_msg[256]; + snprintf(error_msg, sizeof(error_msg), "Variable '%s' already defined", var_name); + _rava_semantic_error(analyzer, error_msg, var_node->line, var_node->column); + rava_symbol_destroy(var_symbol); + return false; + } + + if (var_node->data.var_decl.initializer) { + RavaType_t *init_type = _rava_semantic_check_expression(analyzer, var_node->data.var_decl.initializer); + if (init_type && !rava_type_is_assignable_to(init_type, var_type)) { + char error_msg[256]; + snprintf(error_msg, sizeof(error_msg), + "Cannot assign '%s' to variable of type '%s'", + rava_type_to_string(init_type), + rava_type_to_string(var_type)); + _rava_semantic_error(analyzer, error_msg, var_node->line, var_node->column); + } + } + + return !analyzer->had_error; +} + +static RavaType_t* _rava_semantic_check_expression(RavaSemanticAnalyzer_t *analyzer, RavaASTNode_t *expr) { + if (!expr) return NULL; + + switch (expr->type) { + case RAVA_AST_LITERAL_EXPR: { + switch (expr->data.literal.literal_type) { + case RAVA_TOKEN_LITERAL_INTEGER: + return rava_type_create_primitive(RAVA_TYPE_INT); + case RAVA_TOKEN_LITERAL_LONG: + return rava_type_create_primitive(RAVA_TYPE_LONG); + case RAVA_TOKEN_LITERAL_FLOAT: + return rava_type_create_primitive(RAVA_TYPE_FLOAT); + case RAVA_TOKEN_LITERAL_DOUBLE: + return rava_type_create_primitive(RAVA_TYPE_DOUBLE); + case RAVA_TOKEN_LITERAL_STRING: + return rava_type_create_class("String"); + case RAVA_TOKEN_LITERAL_CHARACTER: + return rava_type_create_primitive(RAVA_TYPE_CHAR); + case RAVA_TOKEN_LITERAL_TRUE: + case RAVA_TOKEN_LITERAL_FALSE: + return rava_type_create_primitive(RAVA_TYPE_BOOLEAN); + case RAVA_TOKEN_LITERAL_NULL: + return rava_type_create_primitive(RAVA_TYPE_NULL); + default: + return NULL; + } + } + + case RAVA_AST_IDENTIFIER_EXPR: { + char *name = expr->data.identifier.name; + + if (strcmp(name, "System") == 0) { + return rava_type_from_name("System"); + } + if (strcmp(name, "Files") == 0) { + return rava_type_from_name("Files"); + } + + RavaSymbol_t *symbol = rava_symbol_table_resolve(analyzer->symbol_table, name); + if (!symbol) { + char error_msg[256]; + snprintf(error_msg, sizeof(error_msg), "Undefined identifier '%s'", name); + _rava_semantic_error(analyzer, error_msg, expr->line, expr->column); + return NULL; + } + return symbol->type; + } + + case RAVA_AST_BINARY_EXPR: { + RavaType_t *left_type = _rava_semantic_check_expression(analyzer, expr->data.binary.left); + RavaType_t *right_type = _rava_semantic_check_expression(analyzer, expr->data.binary.right); + + if (!left_type || !right_type) return NULL; + + if (expr->data.binary.op >= RAVA_BINOP_EQ && expr->data.binary.op <= RAVA_BINOP_GE) { + return rava_type_create_primitive(RAVA_TYPE_BOOLEAN); + } + + if (expr->data.binary.op == RAVA_BINOP_AND || expr->data.binary.op == RAVA_BINOP_OR) { + if (left_type->kind != RAVA_TYPE_BOOLEAN || right_type->kind != RAVA_TYPE_BOOLEAN) { + _rava_semantic_error(analyzer, "Logical operators require boolean operands", + expr->line, expr->column); + return NULL; + } + return rava_type_create_primitive(RAVA_TYPE_BOOLEAN); + } + + return rava_type_binary_result(left_type, right_type); + } + + case RAVA_AST_UNARY_EXPR: { + return _rava_semantic_check_expression(analyzer, expr->data.unary.operand); + } + + case RAVA_AST_ASSIGN_EXPR: { + RavaType_t *target_type = _rava_semantic_check_expression(analyzer, expr->data.assign.target); + RavaType_t *value_type = _rava_semantic_check_expression(analyzer, expr->data.assign.value); + + if (target_type && value_type && !rava_type_is_assignable_to(value_type, target_type)) { + char error_msg[256]; + snprintf(error_msg, sizeof(error_msg), + "Cannot assign '%s' to '%s'", + rava_type_to_string(value_type), + rava_type_to_string(target_type)); + _rava_semantic_error(analyzer, error_msg, expr->line, expr->column); + } + + return target_type; + } + + case RAVA_AST_CALL_EXPR: { + _rava_semantic_check_expression(analyzer, expr->data.call.callee); + for (size_t i = 0; i < expr->data.call.arguments_count; i++) { + _rava_semantic_check_expression(analyzer, expr->data.call.arguments[i]); + } + if (expr->data.call.callee->type == RAVA_AST_MEMBER_ACCESS_EXPR) { + RavaASTNode_t *member = expr->data.call.callee; + const char *method = member->data.member_access.member; + if (member->data.member_access.object->type == RAVA_AST_IDENTIFIER_EXPR && + strcmp(member->data.member_access.object->data.identifier.name, "Files") == 0) { + if (strcmp(method, "read") == 0) { + return rava_type_create_class("String"); + } else if (strcmp(method, "write") == 0 || strcmp(method, "exists") == 0 || + strcmp(method, "delete") == 0) { + return rava_type_create_primitive(RAVA_TYPE_BOOLEAN); + } + } + if (member->data.member_access.object->type == RAVA_AST_IDENTIFIER_EXPR && + strcmp(member->data.member_access.object->data.identifier.name, "System") == 0) { + if (strcmp(method, "currentTimeMillis") == 0 || strcmp(method, "nanoTime") == 0) { + return rava_type_create_primitive(RAVA_TYPE_LONG); + } + } + if (strcmp(method, "length") == 0) { + return rava_type_create_primitive(RAVA_TYPE_INT); + } else if (strcmp(method, "charAt") == 0) { + return rava_type_create_primitive(RAVA_TYPE_CHAR); + } else if (strcmp(method, "equals") == 0) { + return rava_type_create_primitive(RAVA_TYPE_BOOLEAN); + } else if (strcmp(method, "compareTo") == 0) { + return rava_type_create_primitive(RAVA_TYPE_INT); + } + } + return rava_type_create_primitive(RAVA_TYPE_INT); + } + + case RAVA_AST_MEMBER_ACCESS_EXPR: { + _rava_semantic_check_expression(analyzer, expr->data.member_access.object); + return rava_type_create_primitive(RAVA_TYPE_INT); + } + + case RAVA_AST_ARRAY_ACCESS_EXPR: { + RavaType_t *array_type = _rava_semantic_check_expression(analyzer, expr->data.array_access.array); + RavaType_t *index_type = _rava_semantic_check_expression(analyzer, expr->data.array_access.index); + + if (index_type && index_type->kind != RAVA_TYPE_INT && index_type->kind != RAVA_TYPE_LONG) { + _rava_semantic_error(analyzer, "Array index must be an integer", expr->line, expr->column); + } + + if (array_type && array_type->kind == RAVA_TYPE_ARRAY) { + return array_type->data.array.element_type; + } + return rava_type_create_primitive(RAVA_TYPE_INT); + } + + case RAVA_AST_NEW_EXPR: { + RavaType_t *type = _rava_semantic_resolve_type(analyzer, expr->data.new_expr.type); + + for (size_t i = 0; i < expr->data.new_expr.arguments_count; i++) { + RavaType_t *arg_type = _rava_semantic_check_expression(analyzer, expr->data.new_expr.arguments[i]); + if (type && type->kind == RAVA_TYPE_ARRAY && arg_type) { + if (arg_type->kind != RAVA_TYPE_INT && arg_type->kind != RAVA_TYPE_LONG) { + _rava_semantic_error(analyzer, "Array size must be an integer", expr->line, expr->column); + } + } + } + + return type; + } + + default: + return NULL; + } +} + +static bool _rava_semantic_analyze_statement(RavaSemanticAnalyzer_t *analyzer, RavaASTNode_t *stmt) { + if (!stmt) return true; + + switch (stmt->type) { + case RAVA_AST_BLOCK_STMT: + for (size_t i = 0; i < stmt->children_count; i++) { + _rava_semantic_analyze_node(analyzer, stmt->children[i]); + } + return true; + + case RAVA_AST_IF_STMT: { + RavaType_t *cond_type = _rava_semantic_check_expression(analyzer, stmt->data.if_stmt.condition); + if (cond_type && cond_type->kind != RAVA_TYPE_BOOLEAN) { + _rava_semantic_error(analyzer, "If condition must be boolean", stmt->line, stmt->column); + } + _rava_semantic_analyze_node(analyzer, stmt->data.if_stmt.then_stmt); + if (stmt->data.if_stmt.else_stmt) { + _rava_semantic_analyze_node(analyzer, stmt->data.if_stmt.else_stmt); + } + return true; + } + + case RAVA_AST_WHILE_STMT: { + RavaType_t *cond_type = _rava_semantic_check_expression(analyzer, stmt->data.while_stmt.condition); + if (cond_type && cond_type->kind != RAVA_TYPE_BOOLEAN) { + _rava_semantic_error(analyzer, "While condition must be boolean", stmt->line, stmt->column); + } + _rava_semantic_analyze_node(analyzer, stmt->data.while_stmt.body); + return true; + } + + case RAVA_AST_FOR_STMT: + if (stmt->data.for_stmt.init) { + if (stmt->data.for_stmt.init->type == RAVA_AST_VAR_DECL) { + _rava_semantic_analyze_node(analyzer, stmt->data.for_stmt.init); + } else { + _rava_semantic_check_expression(analyzer, stmt->data.for_stmt.init); + } + } + if (stmt->data.for_stmt.condition) { + RavaType_t *cond_type = _rava_semantic_check_expression(analyzer, stmt->data.for_stmt.condition); + if (cond_type && cond_type->kind != RAVA_TYPE_BOOLEAN) { + _rava_semantic_error(analyzer, "For condition must be boolean", stmt->line, stmt->column); + } + } + if (stmt->data.for_stmt.update) { + _rava_semantic_check_expression(analyzer, stmt->data.for_stmt.update); + } + _rava_semantic_analyze_node(analyzer, stmt->data.for_stmt.body); + return true; + + case RAVA_AST_RETURN_STMT: + if (stmt->data.return_stmt.value) { + _rava_semantic_check_expression(analyzer, stmt->data.return_stmt.value); + } + return true; + + case RAVA_AST_EXPR_STMT: + if (stmt->children_count > 0) { + _rava_semantic_check_expression(analyzer, stmt->children[0]); + } + return true; + + case RAVA_AST_VAR_DECL: + return _rava_semantic_analyze_var_decl(analyzer, stmt); + + default: + return true; + } +} + +static bool _rava_semantic_analyze_node(RavaSemanticAnalyzer_t *analyzer, RavaASTNode_t *node) { + if (!node) return true; + + switch (node->type) { + case RAVA_AST_COMPILATION_UNIT: + for (size_t i = 0; i < node->children_count; i++) { + _rava_semantic_analyze_node(analyzer, node->children[i]); + } + return true; + + case RAVA_AST_CLASS_DECL: + return _rava_semantic_analyze_class(analyzer, node); + + case RAVA_AST_METHOD_DECL: + return _rava_semantic_analyze_method(analyzer, node); + + case RAVA_AST_FIELD_DECL: + return _rava_semantic_analyze_field(analyzer, node); + + case RAVA_AST_CONSTRUCTOR_DECL: + return true; + + default: + return _rava_semantic_analyze_statement(analyzer, node); + } +} + +bool rava_semantic_analyze(RavaSemanticAnalyzer_t *analyzer, RavaASTNode_t *root) { + if (!analyzer || !root) return false; + + _rava_semantic_analyze_node(analyzer, root); + + return !analyzer->had_error; +} + +RavaType_t* rava_semantic_get_expression_type(RavaSemanticAnalyzer_t *analyzer, RavaASTNode_t *expr) { + return _rava_semantic_check_expression(analyzer, expr); +} diff --git a/semantic/semantic.h b/semantic/semantic.h new file mode 100644 index 0000000..cc47d38 --- /dev/null +++ b/semantic/semantic.h @@ -0,0 +1,23 @@ +#ifndef RAVA_SEMANTIC_H +#define RAVA_SEMANTIC_H + +#include "../parser/parser.h" +#include "../types/types.h" +#include "symbol_table.h" +#include + +typedef struct { + RavaSymbolTable_t *symbol_table; + char *error_message; + bool had_error; + int error_count; +} RavaSemanticAnalyzer_t; + +RavaSemanticAnalyzer_t* rava_semantic_analyzer_create(); +void rava_semantic_analyzer_destroy(RavaSemanticAnalyzer_t *analyzer); + +bool rava_semantic_analyze(RavaSemanticAnalyzer_t *analyzer, RavaASTNode_t *root); + +RavaType_t* rava_semantic_get_expression_type(RavaSemanticAnalyzer_t *analyzer, RavaASTNode_t *expr); + +#endif diff --git a/semantic/symbol_table.c b/semantic/symbol_table.c new file mode 100644 index 0000000..0e26d9e --- /dev/null +++ b/semantic/symbol_table.c @@ -0,0 +1,139 @@ +#define _POSIX_C_SOURCE 200809L +#include "symbol_table.h" +#include +#include + +RavaSymbolTable_t* rava_symbol_table_create() { + RavaSymbolTable_t *table = malloc(sizeof(RavaSymbolTable_t)); + table->global_scope = rava_scope_create("global", NULL); + table->current_scope = table->global_scope; + return table; +} + +void rava_symbol_table_destroy(RavaSymbolTable_t *table) { + if (!table) return; + rava_scope_destroy(table->global_scope); + free(table); +} + +RavaScope_t* rava_scope_create(const char *name, RavaScope_t *parent) { + RavaScope_t *scope = calloc(1, sizeof(RavaScope_t)); + scope->name = strdup(name); + scope->symbols = NULL; + scope->parent = parent; + scope->children = NULL; + scope->children_count = 0; + scope->children_capacity = 0; + return scope; +} + +void rava_scope_destroy(RavaScope_t *scope) { + if (!scope) return; + + free(scope->name); + + RavaSymbol_t *symbol = scope->symbols; + while (symbol) { + RavaSymbol_t *next = symbol->next; + rava_symbol_destroy(symbol); + symbol = next; + } + + for (size_t i = 0; i < scope->children_count; i++) { + rava_scope_destroy(scope->children[i]); + } + free(scope->children); + + free(scope); +} + +void rava_symbol_table_enter_scope(RavaSymbolTable_t *table, const char *scope_name) { + RavaScope_t *new_scope = rava_scope_create(scope_name, table->current_scope); + + if (table->current_scope->children_count >= table->current_scope->children_capacity) { + size_t new_capacity = table->current_scope->children_capacity == 0 ? 4 : + table->current_scope->children_capacity * 2; + table->current_scope->children = realloc(table->current_scope->children, + sizeof(RavaScope_t*) * new_capacity); + table->current_scope->children_capacity = new_capacity; + } + + table->current_scope->children[table->current_scope->children_count++] = new_scope; + table->current_scope = new_scope; +} + +void rava_symbol_table_exit_scope(RavaSymbolTable_t *table) { + if (table->current_scope->parent) { + table->current_scope = table->current_scope->parent; + } +} + +RavaSymbol_t* rava_symbol_create(RavaSymbolKind_e kind, const char *name, RavaType_t *type) { + RavaSymbol_t *symbol = calloc(1, sizeof(RavaSymbol_t)); + symbol->kind = kind; + symbol->name = strdup(name); + symbol->type = type; + symbol->modifiers = NULL; + symbol->modifiers_count = 0; + symbol->declaration = NULL; + symbol->next = NULL; + return symbol; +} + +void rava_symbol_destroy(RavaSymbol_t *symbol) { + if (!symbol) return; + free(symbol->name); + rava_type_destroy(symbol->type); + free(symbol); +} + +bool rava_symbol_table_define(RavaSymbolTable_t *table, RavaSymbol_t *symbol) { + if (!table || !symbol) return false; + + if (rava_symbol_table_resolve_in_scope(table->current_scope, symbol->name)) { + return false; + } + + symbol->next = table->current_scope->symbols; + table->current_scope->symbols = symbol; + return true; +} + +RavaSymbol_t* rava_symbol_table_resolve(RavaSymbolTable_t *table, const char *name) { + if (!table || !name) return NULL; + + RavaScope_t *scope = table->current_scope; + while (scope) { + RavaSymbol_t *symbol = rava_symbol_table_resolve_in_scope(scope, name); + if (symbol) return symbol; + scope = scope->parent; + } + + return NULL; +} + +RavaSymbol_t* rava_symbol_table_resolve_in_scope(RavaScope_t *scope, const char *name) { + if (!scope || !name) return NULL; + + RavaSymbol_t *symbol = scope->symbols; + while (symbol) { + if (strcmp(symbol->name, name) == 0) { + return symbol; + } + symbol = symbol->next; + } + + return NULL; +} + +bool rava_symbol_has_modifier(RavaSymbol_t *symbol, RavaModifier_e modifier) { + if (!symbol) return false; + + for (size_t i = 0; i < symbol->modifiers_count; i++) { + if (symbol->modifiers[i] == modifier) { + return true; + } + } + + return false; +} diff --git a/semantic/symbol_table.h b/semantic/symbol_table.h new file mode 100644 index 0000000..ce6dfb5 --- /dev/null +++ b/semantic/symbol_table.h @@ -0,0 +1,61 @@ +#ifndef RAVA_SYMBOL_TABLE_H +#define RAVA_SYMBOL_TABLE_H + +#include "../types/types.h" +#include "../parser/parser.h" +#include +#include + +typedef enum { + RAVA_SYMBOL_CLASS, + RAVA_SYMBOL_METHOD, + RAVA_SYMBOL_FIELD, + RAVA_SYMBOL_VARIABLE, + RAVA_SYMBOL_PARAMETER +} RavaSymbolKind_e; + +typedef struct RavaSymbol_t { + RavaSymbolKind_e kind; + char *name; + RavaType_t *type; + RavaModifier_e *modifiers; + size_t modifiers_count; + RavaASTNode_t *declaration; + size_t local_index; + + struct RavaSymbol_t *next; +} RavaSymbol_t; + +typedef struct RavaScope_t { + char *name; + RavaSymbol_t *symbols; + struct RavaScope_t *parent; + struct RavaScope_t **children; + size_t children_count; + size_t children_capacity; +} RavaScope_t; + +typedef struct { + RavaScope_t *global_scope; + RavaScope_t *current_scope; +} RavaSymbolTable_t; + +RavaSymbolTable_t* rava_symbol_table_create(); +void rava_symbol_table_destroy(RavaSymbolTable_t *table); + +RavaScope_t* rava_scope_create(const char *name, RavaScope_t *parent); +void rava_scope_destroy(RavaScope_t *scope); + +void rava_symbol_table_enter_scope(RavaSymbolTable_t *table, const char *scope_name); +void rava_symbol_table_exit_scope(RavaSymbolTable_t *table); + +RavaSymbol_t* rava_symbol_create(RavaSymbolKind_e kind, const char *name, RavaType_t *type); +void rava_symbol_destroy(RavaSymbol_t *symbol); + +bool rava_symbol_table_define(RavaSymbolTable_t *table, RavaSymbol_t *symbol); +RavaSymbol_t* rava_symbol_table_resolve(RavaSymbolTable_t *table, const char *name); +RavaSymbol_t* rava_symbol_table_resolve_in_scope(RavaScope_t *scope, const char *name); + +bool rava_symbol_has_modifier(RavaSymbol_t *symbol, RavaModifier_e modifier); + +#endif diff --git a/tests/run_examples.c b/tests/run_examples.c new file mode 100644 index 0000000..f0ee5d0 --- /dev/null +++ b/tests/run_examples.c @@ -0,0 +1,130 @@ +#include "../lexer/lexer.h" +#include "../parser/parser.h" +#include "../semantic/semantic.h" +#include "../ir/ir.h" +#include "../ir/ir_gen.h" +#include "../runtime/runtime.h" +#include +#include +#include + +static char* read_file(const char *filename) { + FILE *file = fopen(filename, "r"); + if (!file) { + printf("Error: Could not open file %s\n", filename); + return NULL; + } + + fseek(file, 0, SEEK_END); + long size = ftell(file); + fseek(file, 0, SEEK_SET); + + char *content = malloc(size + 1); + size_t read_bytes = fread(content, 1, size, file); (void)read_bytes; + content[size] = '\0'; + + fclose(file); + return content; +} + +static bool run_example(const char *filename, const char *class_name) { + printf("\n"); + printf("================================================================================\n"); + printf("Running: %s\n", filename); + printf("================================================================================\n"); + + char *source = read_file(filename); + if (!source) return false; + + RavaLexer_t *lexer = rava_lexer_create(source); + RavaParser_t *parser = rava_parser_create(lexer); + RavaASTNode_t *ast = rava_parser_parse(parser); + + if (parser->had_error) { + printf("❌ Parse error: %s\n", parser->error_message); + free(source); + return false; + } + + RavaSemanticAnalyzer_t *analyzer = rava_semantic_analyzer_create(); + if (!rava_semantic_analyze(analyzer, ast)) { + printf("❌ Semantic error: %s\n", analyzer->error_message); + free(source); + return false; + } + + RavaIRGenerator_t *ir_gen = rava_ir_generator_create(analyzer); + RavaProgram_t *program = rava_ir_generate(ir_gen, ast); + + if (!program) { + printf("❌ IR generation failed\n"); + free(source); + return false; + } + + RavaVM_t *vm = rava_vm_create(program); + printf("\nOutput:\n"); + + if (!rava_vm_execute(vm, class_name, "main")) { + printf("\n❌ Runtime error: %s\n", vm->error_message); + rava_vm_destroy(vm); + free(source); + return false; + } + + printf("\n✅ Execution completed successfully!\n"); + + rava_vm_destroy(vm); + rava_program_destroy(program); + rava_ir_generator_destroy(ir_gen); + rava_semantic_analyzer_destroy(analyzer); + rava_ast_node_destroy(ast); + rava_parser_destroy(parser); + rava_lexer_destroy(lexer); + free(source); + + return true; +} + +int main() { + printf("================================================================================\n"); + printf("RAVA JAVA INTERPRETER - UNIVERSITY-LEVEL DEMONSTRATION SUITE\n"); + printf("================================================================================\n"); + + int passed = 0; + int total = 0; + + const char *examples[][2] = { + {"examples/01_Fibonacci.java", "Fibonacci"}, + {"examples/02_PrimeNumbers.java", "PrimeNumbers"}, + {"examples/03_FactorialVariations.java", "FactorialVariations"}, + {"examples/04_GCD_LCM.java", "GCD_LCM"}, + {"examples/05_PowerFunction.java", "PowerFunction"}, + {"examples/06_BubbleSort.java", "BubbleSort"}, + {"examples/07_CollatzConjecture.java", "CollatzConjecture"}, + {"examples/08_PascalTriangle.java", "PascalTriangle"}, + {"examples/09_TowerOfHanoi.java", "TowerOfHanoi"}, + {"examples/10_AckermannFunction.java", "AckermannFunction"}, + }; + + for (size_t i = 0; i < 10; i++) { + total++; + if (run_example(examples[i][0], examples[i][1])) { + passed++; + } + } + + printf("\n"); + printf("================================================================================\n"); + printf("DEMONSTRATION SUMMARY\n"); + printf("================================================================================\n"); + printf("Passed: %d/%d\n", passed, total); + printf("Failed: %d/%d\n", total - passed, total); + + if (passed == total) { + printf("\n🎉 ALL DEMONSTRATIONS PASSED!\n"); + printf("The Rava interpreter successfully executed university-level Java programs!\n"); + } + + return (passed == total) ? 0 : 1; +} diff --git a/types/types.c b/types/types.c new file mode 100644 index 0000000..6477f4a --- /dev/null +++ b/types/types.c @@ -0,0 +1,200 @@ +#define _POSIX_C_SOURCE 200809L +#include "types.h" +#include +#include +#include + +RavaType_t* rava_type_create_primitive(RavaTypeKind_e kind) { + RavaType_t *type = calloc(1, sizeof(RavaType_t)); + type->kind = kind; + + switch (kind) { + case RAVA_TYPE_VOID: type->name = strdup("void"); break; + case RAVA_TYPE_BOOLEAN: type->name = strdup("boolean"); break; + case RAVA_TYPE_BYTE: type->name = strdup("byte"); break; + case RAVA_TYPE_SHORT: type->name = strdup("short"); break; + case RAVA_TYPE_INT: type->name = strdup("int"); break; + case RAVA_TYPE_LONG: type->name = strdup("long"); break; + case RAVA_TYPE_CHAR: type->name = strdup("char"); break; + case RAVA_TYPE_FLOAT: type->name = strdup("float"); break; + case RAVA_TYPE_DOUBLE: type->name = strdup("double"); break; + default: type->name = strdup("unknown"); break; + } + + return type; +} + +RavaType_t* rava_type_create_class(const char *class_name) { + RavaType_t *type = calloc(1, sizeof(RavaType_t)); + type->kind = RAVA_TYPE_CLASS; + type->name = strdup(class_name); + type->data.class_type.class_name = strdup(class_name); + return type; +} + +RavaType_t* rava_type_create_array(RavaType_t *element_type, int dimensions) { + RavaType_t *type = calloc(1, sizeof(RavaType_t)); + type->kind = RAVA_TYPE_ARRAY; + type->data.array.element_type = element_type; + type->data.array.dimensions = dimensions; + + char buffer[256]; + snprintf(buffer, sizeof(buffer), "%s", rava_type_to_string(element_type)); + for (int i = 0; i < dimensions; i++) { + strcat(buffer, "[]"); + } + type->name = strdup(buffer); + + return type; +} + +void rava_type_destroy(RavaType_t *type) { + if (!type) return; + + free(type->name); + + if (type->kind == RAVA_TYPE_ARRAY) { + rava_type_destroy(type->data.array.element_type); + } else if (type->kind == RAVA_TYPE_CLASS) { + free(type->data.class_type.class_name); + } + + free(type); +} + +RavaType_t* rava_type_from_name(const char *type_name) { + if (strcmp(type_name, "void") == 0) return rava_type_create_primitive(RAVA_TYPE_VOID); + if (strcmp(type_name, "boolean") == 0) return rava_type_create_primitive(RAVA_TYPE_BOOLEAN); + if (strcmp(type_name, "byte") == 0) return rava_type_create_primitive(RAVA_TYPE_BYTE); + if (strcmp(type_name, "short") == 0) return rava_type_create_primitive(RAVA_TYPE_SHORT); + if (strcmp(type_name, "int") == 0) return rava_type_create_primitive(RAVA_TYPE_INT); + if (strcmp(type_name, "long") == 0) return rava_type_create_primitive(RAVA_TYPE_LONG); + if (strcmp(type_name, "char") == 0) return rava_type_create_primitive(RAVA_TYPE_CHAR); + if (strcmp(type_name, "float") == 0) return rava_type_create_primitive(RAVA_TYPE_FLOAT); + if (strcmp(type_name, "double") == 0) return rava_type_create_primitive(RAVA_TYPE_DOUBLE); + + return rava_type_create_class(type_name); +} + +bool rava_type_is_primitive(RavaType_t *type) { + return type->kind >= RAVA_TYPE_VOID && type->kind <= RAVA_TYPE_DOUBLE; +} + +bool rava_type_is_numeric(RavaType_t *type) { + return type->kind >= RAVA_TYPE_BYTE && type->kind <= RAVA_TYPE_DOUBLE; +} + +bool rava_type_is_integral(RavaType_t *type) { + return type->kind >= RAVA_TYPE_BYTE && type->kind <= RAVA_TYPE_CHAR; +} + +bool rava_type_is_floating(RavaType_t *type) { + return type->kind == RAVA_TYPE_FLOAT || type->kind == RAVA_TYPE_DOUBLE; +} + +bool rava_type_is_reference(RavaType_t *type) { + return type->kind == RAVA_TYPE_CLASS || + type->kind == RAVA_TYPE_ARRAY || + type->kind == RAVA_TYPE_NULL; +} + +bool rava_type_equals(RavaType_t *a, RavaType_t *b) { + if (!a || !b) return false; + if (a->kind != b->kind) return false; + + if (a->kind == RAVA_TYPE_ARRAY) { + return a->data.array.dimensions == b->data.array.dimensions && + rava_type_equals(a->data.array.element_type, b->data.array.element_type); + } + + if (a->kind == RAVA_TYPE_CLASS) { + return strcmp(a->data.class_type.class_name, b->data.class_type.class_name) == 0; + } + + return true; +} + +bool rava_type_is_assignable_to(RavaType_t *from, RavaType_t *to) { + if (!from || !to) return false; + + if (rava_type_equals(from, to)) return true; + + if (from->kind == RAVA_TYPE_NULL && rava_type_is_reference(to)) { + return true; + } + + if (rava_type_is_numeric(from) && rava_type_is_numeric(to)) { + int from_rank = from->kind - RAVA_TYPE_BYTE; + int to_rank = to->kind - RAVA_TYPE_BYTE; + return from_rank <= to_rank; + } + + return false; +} + +bool rava_type_is_castable_to(RavaType_t *from, RavaType_t *to) { + if (rava_type_is_assignable_to(from, to)) return true; + + if (rava_type_is_numeric(from) && rava_type_is_numeric(to)) { + return true; + } + + if (rava_type_is_reference(from) && rava_type_is_reference(to)) { + return true; + } + + return false; +} + +static bool _is_string_type(RavaType_t *type) { + return type->kind == RAVA_TYPE_CLASS && + type->data.class_type.class_name && + strcmp(type->data.class_type.class_name, "String") == 0; +} + +RavaType_t* rava_type_binary_result(RavaType_t *left, RavaType_t *right) { + if (!left || !right) return NULL; + + if (_is_string_type(left) || _is_string_type(right)) { + return rava_type_create_class("String"); + } + + if (!rava_type_is_numeric(left) || !rava_type_is_numeric(right)) { + return NULL; + } + + if (left->kind == RAVA_TYPE_DOUBLE || right->kind == RAVA_TYPE_DOUBLE) { + return rava_type_create_primitive(RAVA_TYPE_DOUBLE); + } + + if (left->kind == RAVA_TYPE_FLOAT || right->kind == RAVA_TYPE_FLOAT) { + return rava_type_create_primitive(RAVA_TYPE_FLOAT); + } + + if (left->kind == RAVA_TYPE_LONG || right->kind == RAVA_TYPE_LONG) { + return rava_type_create_primitive(RAVA_TYPE_LONG); + } + + return rava_type_create_primitive(RAVA_TYPE_INT); +} + +RavaType_t* rava_type_unary_result(RavaType_t *operand) { + if (!operand) return NULL; + + if (!rava_type_is_numeric(operand)) { + return NULL; + } + + if (operand->kind == RAVA_TYPE_BYTE || + operand->kind == RAVA_TYPE_SHORT || + operand->kind == RAVA_TYPE_CHAR) { + return rava_type_create_primitive(RAVA_TYPE_INT); + } + + return rava_type_create_primitive(operand->kind); +} + +const char* rava_type_to_string(RavaType_t *type) { + if (!type) return "null"; + return type->name; +} diff --git a/types/types.h b/types/types.h new file mode 100644 index 0000000..6f1a2aa --- /dev/null +++ b/types/types.h @@ -0,0 +1,63 @@ +#ifndef RAVA_TYPES_H +#define RAVA_TYPES_H + +#include +#include + +typedef enum { + RAVA_TYPE_VOID, + RAVA_TYPE_BOOLEAN, + RAVA_TYPE_BYTE, + RAVA_TYPE_SHORT, + RAVA_TYPE_INT, + RAVA_TYPE_LONG, + RAVA_TYPE_CHAR, + RAVA_TYPE_FLOAT, + RAVA_TYPE_DOUBLE, + RAVA_TYPE_CLASS, + RAVA_TYPE_INTERFACE, + RAVA_TYPE_ARRAY, + RAVA_TYPE_NULL, + RAVA_TYPE_UNKNOWN +} RavaTypeKind_e; + +typedef struct RavaType_t RavaType_t; + +struct RavaType_t { + RavaTypeKind_e kind; + char *name; + + union { + struct { + RavaType_t *element_type; + int dimensions; + } array; + + struct { + char *class_name; + } class_type; + } data; +}; + +RavaType_t* rava_type_create_primitive(RavaTypeKind_e kind); +RavaType_t* rava_type_create_class(const char *class_name); +RavaType_t* rava_type_create_array(RavaType_t *element_type, int dimensions); +void rava_type_destroy(RavaType_t *type); + +RavaType_t* rava_type_from_name(const char *type_name); +bool rava_type_is_primitive(RavaType_t *type); +bool rava_type_is_numeric(RavaType_t *type); +bool rava_type_is_integral(RavaType_t *type); +bool rava_type_is_floating(RavaType_t *type); +bool rava_type_is_reference(RavaType_t *type); + +bool rava_type_equals(RavaType_t *a, RavaType_t *b); +bool rava_type_is_assignable_to(RavaType_t *from, RavaType_t *to); +bool rava_type_is_castable_to(RavaType_t *from, RavaType_t *to); + +RavaType_t* rava_type_binary_result(RavaType_t *left, RavaType_t *right); +RavaType_t* rava_type_unary_result(RavaType_t *operand); + +const char* rava_type_to_string(RavaType_t *type); + +#endif