Initial commit of dem beast./plot.py
This commit is contained in:
commit
c9cf356cbd
157
Makefile
Normal file
157
Makefile
Normal file
@ -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
|
||||
23
examples/01_Fibonacci.java
Normal file
23
examples/01_Fibonacci.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
31
examples/02_PrimeNumbers.java
Normal file
31
examples/02_PrimeNumbers.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
28
examples/03_FactorialVariations.java
Normal file
28
examples/03_FactorialVariations.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
25
examples/04_GCD_LCM.java
Normal file
25
examples/04_GCD_LCM.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
26
examples/05_PowerFunction.java
Normal file
26
examples/05_PowerFunction.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
55
examples/06_ArrayOperations.java
Normal file
55
examples/06_ArrayOperations.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
64
examples/06_BubbleSort.java
Normal file
64
examples/06_BubbleSort.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
25
examples/07_CollatzConjecture.java
Normal file
25
examples/07_CollatzConjecture.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
41
examples/08_PascalTriangle.java
Normal file
41
examples/08_PascalTriangle.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
19
examples/09_TowerOfHanoi.java
Normal file
19
examples/09_TowerOfHanoi.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
22
examples/10_AckermannFunction.java
Normal file
22
examples/10_AckermannFunction.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
68
examples/11_ArrayOperations.java
Normal file
68
examples/11_ArrayOperations.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
14
examples/11_SimpleArray.java
Normal file
14
examples/11_SimpleArray.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
30
examples/11_TowerOfHanoiStatic.java
Normal file
30
examples/11_TowerOfHanoiStatic.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
44
examples/12_StringBasics.java
Normal file
44
examples/12_StringBasics.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
22
examples/13_SimpleObject.java
Normal file
22
examples/13_SimpleObject.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
23
examples/14_InstanceMethods.java
Normal file
23
examples/14_InstanceMethods.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
32
examples/15_FileIO.java
Normal file
32
examples/15_FileIO.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
21
examples/16_ForLoop.java
Normal file
21
examples/16_ForLoop.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
29
examples/17_Inheritance.java
Normal file
29
examples/17_Inheritance.java
Normal file
@ -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!");
|
||||
}
|
||||
}
|
||||
28
examples/18_ElseIf.java
Normal file
28
examples/18_ElseIf.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
42
examples/19_BreakContinue.java
Normal file
42
examples/19_BreakContinue.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
94
examples/20_Benchmark.java
Normal file
94
examples/20_Benchmark.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
10
examples/HelloWorld.java
Normal file
10
examples/HelloWorld.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
71
examples/benchmark.py
Normal file
71
examples/benchmark.py
Normal file
@ -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()
|
||||
200
ir/ir.c
Normal file
200
ir/ir.c
Normal file
@ -0,0 +1,200 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "ir.h"
|
||||
#include "../runtime/labeltable.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
197
ir/ir.h
Normal file
197
ir/ir.h
Normal file
@ -0,0 +1,197 @@
|
||||
#ifndef RAVA_IR_H
|
||||
#define RAVA_IR_H
|
||||
|
||||
#include "../types/types.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
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
|
||||
669
ir/ir_gen.c
Normal file
669
ir/ir_gen.c
Normal file
@ -0,0 +1,669 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "ir_gen.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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("<init>");
|
||||
_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("<init>", 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;
|
||||
}
|
||||
37
ir/ir_gen.h
Normal file
37
ir/ir_gen.h
Normal file
@ -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
|
||||
160
lexer/lexer.h
Normal file
160
lexer/lexer.h
Normal file
@ -0,0 +1,160 @@
|
||||
#ifndef RAVA_LEXER_H
|
||||
#define RAVA_LEXER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
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
|
||||
74
lexer/lexer_keywords.c
Normal file
74
lexer/lexer_keywords.c
Normal file
@ -0,0 +1,74 @@
|
||||
#include "lexer.h"
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
216
lexer/lexer_literals.c
Normal file
216
lexer/lexer_literals.c
Normal file
@ -0,0 +1,216 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "lexer.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
||||
319
lexer/lexer_tokenizer.c
Normal file
319
lexer/lexer_tokenizer.c
Normal file
@ -0,0 +1,319 @@
|
||||
#include "lexer.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
236
parser/parser.c
Normal file
236
parser/parser.c
Normal file
@ -0,0 +1,236 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "parser.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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);
|
||||
251
parser/parser.h
Normal file
251
parser/parser.h
Normal file
@ -0,0 +1,251 @@
|
||||
#ifndef RAVA_PARSER_H
|
||||
#define RAVA_PARSER_H
|
||||
|
||||
#include "../lexer/lexer.h"
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
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
|
||||
196
parser/parser_declarations.c
Normal file
196
parser/parser_declarations.c
Normal file
@ -0,0 +1,196 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "parser.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
433
parser/parser_expressions.c
Normal file
433
parser/parser_expressions.c
Normal file
@ -0,0 +1,433 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "parser.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
216
parser/parser_printer.c
Normal file
216
parser/parser_printer.c
Normal file
@ -0,0 +1,216 @@
|
||||
#include "parser.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
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("<null>\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 : "<unnamed>");
|
||||
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);
|
||||
}
|
||||
}
|
||||
216
parser/parser_statements.c
Normal file
216
parser/parser_statements.c
Normal file
@ -0,0 +1,216 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "parser.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
48
runtime/fastframe.c
Normal file
48
runtime/fastframe.c
Normal file
@ -0,0 +1,48 @@
|
||||
#include "fastframe.h"
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
63
runtime/fastframe.h
Normal file
63
runtime/fastframe.h
Normal file
@ -0,0 +1,63 @@
|
||||
#ifndef RAVA_FASTFRAME_H
|
||||
#define RAVA_FASTFRAME_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#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
|
||||
52
runtime/labeltable.c
Normal file
52
runtime/labeltable.c
Normal file
@ -0,0 +1,52 @@
|
||||
#include "labeltable.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
17
runtime/labeltable.h
Normal file
17
runtime/labeltable.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef RAVA_LABELTABLE_H
|
||||
#define RAVA_LABELTABLE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#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
|
||||
57
runtime/methodcache.c
Normal file
57
runtime/methodcache.c
Normal file
@ -0,0 +1,57 @@
|
||||
#include "methodcache.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
41
runtime/methodcache.h
Normal file
41
runtime/methodcache.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef RAVA_METHODCACHE_H
|
||||
#define RAVA_METHODCACHE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#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
|
||||
156
runtime/nanbox.h
Normal file
156
runtime/nanbox.h
Normal file
@ -0,0 +1,156 @@
|
||||
#ifndef RAVA_NANBOX_H
|
||||
#define RAVA_NANBOX_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
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
|
||||
2810
runtime/runtime.c
Normal file
2810
runtime/runtime.c
Normal file
File diff suppressed because it is too large
Load Diff
149
runtime/runtime.h
Normal file
149
runtime/runtime.h
Normal file
@ -0,0 +1,149 @@
|
||||
#ifndef RAVA_RUNTIME_H
|
||||
#define RAVA_RUNTIME_H
|
||||
|
||||
#include "../ir/ir.h"
|
||||
#include "../types/types.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
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
|
||||
149
runtime/superinst.c
Normal file
149
runtime/superinst.c
Normal file
@ -0,0 +1,149 @@
|
||||
#include "superinst.h"
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
8
runtime/superinst.h
Normal file
8
runtime/superinst.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef RAVA_SUPERINST_H
|
||||
#define RAVA_SUPERINST_H
|
||||
|
||||
#include "../ir/ir.h"
|
||||
|
||||
void rava_optimize_superinstructions(RavaMethod_t* method);
|
||||
|
||||
#endif
|
||||
462
semantic/semantic.c
Normal file
462
semantic/semantic.c
Normal file
@ -0,0 +1,462 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "semantic.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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);
|
||||
}
|
||||
23
semantic/semantic.h
Normal file
23
semantic/semantic.h
Normal file
@ -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 <stdbool.h>
|
||||
|
||||
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
|
||||
139
semantic/symbol_table.c
Normal file
139
semantic/symbol_table.c
Normal file
@ -0,0 +1,139 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "symbol_table.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
61
semantic/symbol_table.h
Normal file
61
semantic/symbol_table.h
Normal file
@ -0,0 +1,61 @@
|
||||
#ifndef RAVA_SYMBOL_TABLE_H
|
||||
#define RAVA_SYMBOL_TABLE_H
|
||||
|
||||
#include "../types/types.h"
|
||||
#include "../parser/parser.h"
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
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
|
||||
130
tests/run_examples.c
Normal file
130
tests/run_examples.c
Normal file
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
200
types/types.c
Normal file
200
types/types.c
Normal file
@ -0,0 +1,200 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "types.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
||||
63
types/types.h
Normal file
63
types/types.h
Normal file
@ -0,0 +1,63 @@
|
||||
#ifndef RAVA_TYPES_H
|
||||
#define RAVA_TYPES_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
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
|
||||
Loading…
Reference in New Issue
Block a user