#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 bool _rava_has_modifier(RavaModifier_e *mods, size_t count, RavaModifier_e mod) {
for (size_t i = 0; i < count; i++) {
if (mods[i] == mod) return true;
}
return false;
}
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;
}
static bool _rava_ir_is_class_name(RavaIRGenerator_t *gen, const char *name) {
for (size_t i = 0; i < gen->program->class_count; i++) {
if (strcmp(gen->program->classes[i]->name, name) == 0) {
return true;
}
}
return false;
}
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;
case RAVA_BINOP_BITAND: instr.opcode = RAVA_OP_AND; break;
case RAVA_BINOP_BITOR: instr.opcode = RAVA_OP_OR; break;
case RAVA_BINOP_BITXOR: instr.opcode = RAVA_OP_XOR; break;
case RAVA_BINOP_LSHIFT: instr.opcode = RAVA_OP_SHL; break;
case RAVA_BINOP_RSHIFT: instr.opcode = RAVA_OP_SHR; break;
case RAVA_BINOP_URSHIFT: instr.opcode = RAVA_OP_USHR; 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:
if (expr->data.binary.op == RAVA_BINOP_AND) {
_rava_ir_gen_expression(gen, expr->data.binary.left);
instr.opcode = RAVA_OP_DUP;
_rava_ir_emit(gen, instr);
int end_label = rava_instruction_list_new_label(gen->current_method->instructions);
instr.opcode = RAVA_OP_JUMP_IF_FALSE;
instr.operand.label_id = end_label;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_POP;
_rava_ir_emit(gen, instr);
_rava_ir_gen_expression(gen, expr->data.binary.right);
instr.opcode = RAVA_OP_LABEL;
instr.operand.label_id = end_label;
_rava_ir_emit(gen, instr);
} else if (expr->data.binary.op == RAVA_BINOP_OR) {
_rava_ir_gen_expression(gen, expr->data.binary.left);
instr.opcode = RAVA_OP_DUP;
_rava_ir_emit(gen, instr);
int end_label = rava_instruction_list_new_label(gen->current_method->instructions);
instr.opcode = RAVA_OP_JUMP_IF_TRUE;
instr.operand.label_id = end_label;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_POP;
_rava_ir_emit(gen, instr);
_rava_ir_gen_expression(gen, expr->data.binary.right);
instr.opcode = RAVA_OP_LABEL;
instr.operand.label_id = end_label;
_rava_ir_emit(gen, instr);
} else {
_rava_ir_gen_binary_expr(gen, expr);
}
break;
case RAVA_AST_UNARY_EXPR:
if (expr->data.unary.op == RAVA_UNOP_PLUS) {
_rava_ir_gen_expression(gen, expr->data.unary.operand);
} else 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_BITNOT) {
_rava_ir_gen_expression(gen, expr->data.unary.operand);
instr.opcode = RAVA_OP_BITNOT;
_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) {
RavaASTNode_t *target_member = expr->data.assign.target;
if (target_member->data.member_access.object->type == RAVA_AST_IDENTIFIER_EXPR) {
const char *obj_name = target_member->data.member_access.object->data.identifier.name;
if (_rava_ir_is_class_name(gen, obj_name) ||
strcmp(obj_name, gen->current_class->name) == 0) {
_rava_ir_gen_expression(gen, expr->data.assign.value);
instr.opcode = RAVA_OP_STORE_STATIC;
instr.operand.field.class_name = strdup(obj_name);
instr.operand.field.field_name = strdup(target_member->data.member_access.member);
_rava_ir_emit(gen, instr);
break;
}
}
_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:
if (expr->data.member_access.object->type == RAVA_AST_IDENTIFIER_EXPR) {
const char *obj_name = expr->data.member_access.object->data.identifier.name;
if (_rava_ir_is_class_name(gen, obj_name) ||
strcmp(obj_name, gen->current_class->name) == 0) {
instr.opcode = RAVA_OP_LOAD_STATIC;
instr.operand.field.class_name = strdup(obj_name);
instr.operand.field.field_name = strdup(expr->data.member_access.member);
_rava_ir_emit(gen, instr);
break;
}
}
_rava_ir_gen_expression(gen, expr->data.member_access.object);
if (strcmp(expr->data.member_access.member, "length") == 0) {
instr.opcode = RAVA_OP_ARRAY_LENGTH;
} else {
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 == 1) {
_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.arguments_count >= 2) {
int saved_next_local = gen->next_local;
_rava_ir_gen_expression(gen, expr->data.new_expr.arguments[0]);
instr.opcode = RAVA_OP_NEW_ARRAY_OF_ARRAYS;
_rava_ir_emit(gen, instr);
int outer_var = gen->next_local++;
instr.opcode = RAVA_OP_STORE_LOCAL;
instr.operand.var.index = outer_var;
_rava_ir_emit(gen, instr);
int loop_var = gen->next_local++;
instr.opcode = RAVA_OP_LOAD_CONST;
instr.operand.int_value = 0;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_STORE_LOCAL;
instr.operand.var.index = loop_var;
_rava_ir_emit(gen, instr);
int loop_start = rava_instruction_list_new_label(gen->current_method->instructions);
int loop_end = rava_instruction_list_new_label(gen->current_method->instructions);
instr.opcode = RAVA_OP_LABEL;
instr.operand.label_id = loop_start;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_LOAD_LOCAL;
instr.operand.var.index = loop_var;
_rava_ir_emit(gen, instr);
_rava_ir_gen_expression(gen, expr->data.new_expr.arguments[0]);
instr.opcode = RAVA_OP_LT;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_JUMP_IF_FALSE;
instr.operand.label_id = loop_end;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_LOAD_LOCAL;
instr.operand.var.index = outer_var;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_LOAD_LOCAL;
instr.operand.var.index = loop_var;
_rava_ir_emit(gen, instr);
_rava_ir_gen_expression(gen, expr->data.new_expr.arguments[1]);
instr.opcode = RAVA_OP_NEW_ARRAY;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_STORE_ARRAY;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_LOAD_LOCAL;
instr.operand.var.index = loop_var;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_LOAD_CONST;
instr.operand.int_value = 1;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_ADD;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_STORE_LOCAL;
instr.operand.var.index = loop_var;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_JUMP;
instr.operand.label_id = loop_start;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_LABEL;
instr.operand.label_id = loop_end;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_LOAD_LOCAL;
instr.operand.var.index = outer_var;
_rava_ir_emit(gen, instr);
if (gen->next_local > gen->current_method->local_count) {
gen->current_method->local_count = gen->next_local;
}
gen->next_local = saved_next_local;
} else {
instr.opcode = RAVA_OP_LOAD_CONST;
instr.operand.int_value = 0;
_rava_ir_emit(gen, instr);
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_substring = false;
bool is_string_equals = false;
bool is_string_compareto = false;
bool is_string_indexof = false;
bool is_string_contains = false;
bool is_string_startswith = false;
bool is_string_endswith = false;
bool is_string_tolowercase = false;
bool is_string_touppercase = false;
bool is_string_trim = 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;
bool is_math_abs = false;
bool is_math_sqrt = false;
bool is_math_pow = false;
bool is_math_min = false;
bool is_math_max = false;
bool is_math_floor = false;
bool is_math_ceil = false;
bool is_math_round = false;
bool is_math_sin = false;
bool is_math_cos = false;
bool is_math_tan = false;
bool is_math_log = false;
bool is_math_exp = false;
bool is_math_random = 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 (member->data.member_access.object->type == RAVA_AST_IDENTIFIER_EXPR &&
strcmp(member->data.member_access.object->data.identifier.name, "Math") == 0) {
const char *method = member->data.member_access.member;
if (strcmp(method, "abs") == 0 && expr->data.call.arguments_count == 1) {
is_math_abs = true;
} else if (strcmp(method, "sqrt") == 0 && expr->data.call.arguments_count == 1) {
is_math_sqrt = true;
} else if (strcmp(method, "pow") == 0 && expr->data.call.arguments_count == 2) {
is_math_pow = true;
} else if (strcmp(method, "min") == 0 && expr->data.call.arguments_count == 2) {
is_math_min = true;
} else if (strcmp(method, "max") == 0 && expr->data.call.arguments_count == 2) {
is_math_max = true;
} else if (strcmp(method, "floor") == 0 && expr->data.call.arguments_count == 1) {
is_math_floor = true;
} else if (strcmp(method, "ceil") == 0 && expr->data.call.arguments_count == 1) {
is_math_ceil = true;
} else if (strcmp(method, "round") == 0 && expr->data.call.arguments_count == 1) {
is_math_round = true;
} else if (strcmp(method, "sin") == 0 && expr->data.call.arguments_count == 1) {
is_math_sin = true;
} else if (strcmp(method, "cos") == 0 && expr->data.call.arguments_count == 1) {
is_math_cos = true;
} else if (strcmp(method, "tan") == 0 && expr->data.call.arguments_count == 1) {
is_math_tan = true;
} else if (strcmp(method, "log") == 0 && expr->data.call.arguments_count == 1) {
is_math_log = true;
} else if (strcmp(method, "exp") == 0 && expr->data.call.arguments_count == 1) {
is_math_exp = true;
} else if (strcmp(method, "random") == 0 && expr->data.call.arguments_count == 0) {
is_math_random = 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;
} else if (strcmp(member->data.member_access.member, "substring") == 0 && expr->data.call.arguments_count == 2) {
is_string_substring = true;
string_object = member->data.member_access.object;
} else if (strcmp(member->data.member_access.member, "compareTo") == 0 && expr->data.call.arguments_count == 1) {
is_string_compareto = true;
string_object = member->data.member_access.object;
} else if (strcmp(member->data.member_access.member, "indexOf") == 0 && expr->data.call.arguments_count == 1) {
is_string_indexof = true;
string_object = member->data.member_access.object;
} else if (strcmp(member->data.member_access.member, "contains") == 0 && expr->data.call.arguments_count == 1) {
is_string_contains = true;
string_object = member->data.member_access.object;
} else if (strcmp(member->data.member_access.member, "startsWith") == 0 && expr->data.call.arguments_count == 1) {
is_string_startswith = true;
string_object = member->data.member_access.object;
} else if (strcmp(member->data.member_access.member, "endsWith") == 0 && expr->data.call.arguments_count == 1) {
is_string_endswith = true;
string_object = member->data.member_access.object;
} else if (strcmp(member->data.member_access.member, "toLowerCase") == 0 && expr->data.call.arguments_count == 0) {
is_string_tolowercase = true;
string_object = member->data.member_access.object;
} else if (strcmp(member->data.member_access.member, "toUpperCase") == 0 && expr->data.call.arguments_count == 0) {
is_string_touppercase = true;
string_object = member->data.member_access.object;
} else if (strcmp(member->data.member_access.member, "trim") == 0 && expr->data.call.arguments_count == 0) {
is_string_trim = 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_string_substring) {
_rava_ir_gen_expression(gen, string_object);
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
_rava_ir_gen_expression(gen, expr->data.call.arguments[1]);
instr.opcode = RAVA_OP_STRING_SUBSTRING;
_rava_ir_emit(gen, instr);
} else if (is_string_compareto) {
_rava_ir_gen_expression(gen, string_object);
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
instr.opcode = RAVA_OP_STRING_COMPARETO;
_rava_ir_emit(gen, instr);
} else if (is_string_indexof) {
_rava_ir_gen_expression(gen, string_object);
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
instr.opcode = RAVA_OP_STRING_INDEXOF;
_rava_ir_emit(gen, instr);
} else if (is_string_contains) {
_rava_ir_gen_expression(gen, string_object);
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
instr.opcode = RAVA_OP_STRING_CONTAINS;
_rava_ir_emit(gen, instr);
} else if (is_string_startswith) {
_rava_ir_gen_expression(gen, string_object);
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
instr.opcode = RAVA_OP_STRING_STARTSWITH;
_rava_ir_emit(gen, instr);
} else if (is_string_endswith) {
_rava_ir_gen_expression(gen, string_object);
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
instr.opcode = RAVA_OP_STRING_ENDSWITH;
_rava_ir_emit(gen, instr);
} else if (is_string_tolowercase) {
_rava_ir_gen_expression(gen, string_object);
instr.opcode = RAVA_OP_STRING_TOLOWERCASE;
_rava_ir_emit(gen, instr);
} else if (is_string_touppercase) {
_rava_ir_gen_expression(gen, string_object);
instr.opcode = RAVA_OP_STRING_TOUPPERCASE;
_rava_ir_emit(gen, instr);
} else if (is_string_trim) {
_rava_ir_gen_expression(gen, string_object);
instr.opcode = RAVA_OP_STRING_TRIM;
_rava_ir_emit(gen, instr);
} else if (is_math_abs) {
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
instr.opcode = RAVA_OP_MATH_ABS;
_rava_ir_emit(gen, instr);
} else if (is_math_sqrt) {
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
instr.opcode = RAVA_OP_MATH_SQRT;
_rava_ir_emit(gen, instr);
} else if (is_math_pow) {
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
_rava_ir_gen_expression(gen, expr->data.call.arguments[1]);
instr.opcode = RAVA_OP_MATH_POW;
_rava_ir_emit(gen, instr);
} else if (is_math_min) {
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
_rava_ir_gen_expression(gen, expr->data.call.arguments[1]);
instr.opcode = RAVA_OP_MATH_MIN;
_rava_ir_emit(gen, instr);
} else if (is_math_max) {
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
_rava_ir_gen_expression(gen, expr->data.call.arguments[1]);
instr.opcode = RAVA_OP_MATH_MAX;
_rava_ir_emit(gen, instr);
} else if (is_math_floor) {
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
instr.opcode = RAVA_OP_MATH_FLOOR;
_rava_ir_emit(gen, instr);
} else if (is_math_ceil) {
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
instr.opcode = RAVA_OP_MATH_CEIL;
_rava_ir_emit(gen, instr);
} else if (is_math_round) {
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
instr.opcode = RAVA_OP_MATH_ROUND;
_rava_ir_emit(gen, instr);
} else if (is_math_sin) {
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
instr.opcode = RAVA_OP_MATH_SIN;
_rava_ir_emit(gen, instr);
} else if (is_math_cos) {
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
instr.opcode = RAVA_OP_MATH_COS;
_rava_ir_emit(gen, instr);
} else if (is_math_tan) {
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
instr.opcode = RAVA_OP_MATH_TAN;
_rava_ir_emit(gen, instr);
} else if (is_math_log) {
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
instr.opcode = RAVA_OP_MATH_LOG;
_rava_ir_emit(gen, instr);
} else if (is_math_exp) {
_rava_ir_gen_expression(gen, expr->data.call.arguments[0]);
instr.opcode = RAVA_OP_MATH_EXP;
_rava_ir_emit(gen, instr);
} else if (is_math_random) {
instr.opcode = RAVA_OP_MATH_RANDOM;
_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_SUPER_EXPR) {
instr.opcode = RAVA_OP_LOAD_THIS;
_rava_ir_emit(gen, instr);
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_SUPER;
instr.operand.call.class_name = gen->current_class->superclass ? strdup(gen->current_class->superclass) : NULL;
instr.operand.call.method_name = strdup("<init>");
instr.operand.call.arg_count = expr->data.call.arguments_count;
_rava_ir_emit(gen, instr);
} else 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_SUPER_EXPR) {
instr.opcode = RAVA_OP_LOAD_THIS;
_rava_ir_emit(gen, instr);
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_SUPER;
instr.operand.call.class_name = gen->current_class->superclass ? strdup(gen->current_class->superclass) : NULL;
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 {
_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;
case RAVA_AST_CAST_EXPR:
_rava_ir_gen_expression(gen, expr->data.cast.expression);
instr.opcode = RAVA_OP_CAST;
if (expr->data.cast.type && expr->data.cast.type->data.type.type_name) {
instr.operand.var.type = rava_type_from_name(expr->data.cast.type->data.type.type_name);
}
_rava_ir_emit(gen, instr);
break;
case RAVA_AST_TERNARY_EXPR: {
_rava_ir_gen_expression(gen, expr->data.ternary.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_expression(gen, expr->data.ternary.true_expr);
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);
_rava_ir_gen_expression(gen, expr->data.ternary.false_expr);
instr.opcode = RAVA_OP_LABEL;
instr.operand.label_id = end_label;
_rava_ir_emit(gen, instr);
break;
}
case RAVA_AST_ARRAY_INIT_EXPR: {
instr.opcode = RAVA_OP_LOAD_CONST;
instr.operand.int_value = (int)expr->data.array_init.elements_count;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_NEW_ARRAY;
_rava_ir_emit(gen, instr);
for (size_t i = 0; i < expr->data.array_init.elements_count; i++) {
instr.opcode = RAVA_OP_DUP;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_LOAD_CONST;
instr.operand.int_value = (int)i;
_rava_ir_emit(gen, instr);
_rava_ir_gen_expression(gen, expr->data.array_init.elements[i]);
instr.opcode = RAVA_OP_STORE_ARRAY;
_rava_ir_emit(gen, instr);
}
break;
}
case RAVA_AST_INSTANCEOF_EXPR:
_rava_ir_gen_expression(gen, expr->data.instanceof_expr.expression);
instr.opcode = RAVA_OP_INSTANCEOF;
instr.operand.string_value = strdup(expr->data.instanceof_expr.type_name);
_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_DO_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_statement(gen, stmt->data.while_stmt.body);
_rava_ir_gen_expression(gen, stmt->data.while_stmt.condition);
instr.opcode = RAVA_OP_JUMP_IF_TRUE;
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_ENHANCED_FOR_STMT: {
size_t idx_local = gen->next_local++;
size_t elem_local = gen->next_local++;
gen->current_method->local_count = gen->next_local;
_rava_ir_add_local(gen, stmt->data.enhanced_for.var_name, elem_local);
instr.opcode = RAVA_OP_LOAD_CONST;
instr.operand.int_value = 0;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_STORE_LOCAL;
instr.operand.var.index = idx_local;
_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);
instr.opcode = RAVA_OP_LOAD_LOCAL;
instr.operand.var.index = idx_local;
_rava_ir_emit(gen, instr);
_rava_ir_gen_expression(gen, stmt->data.enhanced_for.iterable);
instr.opcode = RAVA_OP_ARRAY_LENGTH;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_LT;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_JUMP_IF_FALSE;
instr.operand.label_id = end_label;
_rava_ir_emit(gen, instr);
_rava_ir_gen_expression(gen, stmt->data.enhanced_for.iterable);
instr.opcode = RAVA_OP_LOAD_LOCAL;
instr.operand.var.index = idx_local;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_LOAD_ARRAY;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_STORE_LOCAL;
instr.operand.var.index = elem_local;
_rava_ir_emit(gen, instr);
_rava_ir_gen_statement(gen, stmt->data.enhanced_for.body);
instr.opcode = RAVA_OP_LABEL;
instr.operand.label_id = continue_label;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_LOAD_LOCAL;
instr.operand.var.index = idx_local;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_LOAD_CONST;
instr.operand.int_value = 1;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_ADD;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_STORE_LOCAL;
instr.operand.var.index = idx_local;
_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_SWITCH_STMT: {
int end_label = rava_instruction_list_new_label(gen->current_method->instructions);
RavaLoopContext_t switch_ctx = { .break_label = end_label, .continue_label = -1, .parent = gen->loop_context };
gen->loop_context = &switch_ctx;
size_t case_count = stmt->children_count;
int *case_labels = malloc(case_count * sizeof(int));
int default_label = -1;
for (size_t i = 0; i < case_count; i++) {
case_labels[i] = rava_instruction_list_new_label(gen->current_method->instructions);
if (stmt->children[i]->data.case_stmt.is_default) {
default_label = case_labels[i];
}
}
for (size_t i = 0; i < case_count; i++) {
RavaASTNode_t *case_node = stmt->children[i];
if (!case_node->data.case_stmt.is_default) {
_rava_ir_gen_expression(gen, stmt->data.switch_stmt.expression);
_rava_ir_gen_expression(gen, case_node->data.case_stmt.value);
instr.opcode = RAVA_OP_EQ;
_rava_ir_emit(gen, instr);
instr.opcode = RAVA_OP_JUMP_IF_TRUE;
instr.operand.label_id = case_labels[i];
_rava_ir_emit(gen, instr);
}
}
if (default_label >= 0) {
instr.opcode = RAVA_OP_JUMP;
instr.operand.label_id = default_label;
_rava_ir_emit(gen, instr);
} else {
instr.opcode = RAVA_OP_JUMP;
instr.operand.label_id = end_label;
_rava_ir_emit(gen, instr);
}
for (size_t i = 0; i < case_count; i++) {
RavaASTNode_t *case_node = stmt->children[i];
instr.opcode = RAVA_OP_LABEL;
instr.operand.label_id = case_labels[i];
_rava_ir_emit(gen, instr);
for (size_t j = 0; j < case_node->children_count; j++) {
_rava_ir_gen_statement(gen, case_node->children[j]);
}
}
instr.opcode = RAVA_OP_LABEL;
instr.operand.label_id = end_label;
_rava_ir_emit(gen, instr);
free(case_labels);
gen->loop_context = switch_ctx.parent;
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;
case RAVA_AST_THROW_STMT:
if (stmt->data.throw_stmt.expression) {
_rava_ir_gen_expression(gen, stmt->data.throw_stmt.expression);
} else {
instr.opcode = RAVA_OP_LOAD_CONST;
instr.operand.int_value = 1;
_rava_ir_emit(gen, instr);
}
instr.opcode = RAVA_OP_THROW;
_rava_ir_emit(gen, instr);
break;
case RAVA_AST_TRY_STMT: {
int catch_label = rava_instruction_list_new_label(gen->current_method->instructions);
int finally_label = rava_instruction_list_new_label(gen->current_method->instructions);
int end_label = rava_instruction_list_new_label(gen->current_method->instructions);
int exception_local = gen->next_local++;
gen->current_method->local_count = gen->next_local;
instr.opcode = RAVA_OP_TRY_BEGIN;
instr.operand.try_handler.catch_label = catch_label;
instr.operand.try_handler.finally_label = stmt->data.try_stmt.finally_block ? finally_label : -1;
instr.operand.try_handler.end_label = end_label;
instr.operand.try_handler.exception_local = exception_local;
_rava_ir_emit(gen, instr);
_rava_ir_gen_statement(gen, stmt->data.try_stmt.try_block);
instr.opcode = RAVA_OP_TRY_END;
_rava_ir_emit(gen, instr);
if (stmt->data.try_stmt.finally_block) {
instr.opcode = RAVA_OP_JUMP;
instr.operand.label_id = finally_label;
_rava_ir_emit(gen, instr);
} else {
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 = catch_label;
_rava_ir_emit(gen, instr);
for (size_t i = 0; i < stmt->data.try_stmt.catch_count; i++) {
RavaASTNode_t *catch_node = stmt->data.try_stmt.catch_clauses[i];
if (catch_node->data.catch_clause.exception_name) {
_rava_ir_add_local(gen, catch_node->data.catch_clause.exception_name, exception_local);
}
_rava_ir_gen_statement(gen, catch_node->data.catch_clause.body);
}
if (stmt->data.try_stmt.finally_block) {
instr.opcode = RAVA_OP_LABEL;
instr.operand.label_id = finally_label;
_rava_ir_emit(gen, instr);
_rava_ir_gen_statement(gen, stmt->data.try_stmt.finally_block);
}
instr.opcode = RAVA_OP_LABEL;
instr.operand.label_id = end_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);
if (return_type && strcmp(return_type->name, "void") == 0) {
RavaInstruction_t ret_instr = {0};
ret_instr.opcode = RAVA_OP_RETURN_VOID;
_rava_ir_emit(gen, ret_instr);
}
if (gen->next_local > method->local_count) {
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);
if (gen->next_local > method->local_count) {
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);
bool has_static_init = false;
for (size_t i = 0; i < class_node->children_count; i++) {
RavaASTNode_t *child = class_node->children[i];
if (child->type == RAVA_AST_FIELD_DECL &&
child->data.field_decl.initializer &&
_rava_has_modifier(child->data.field_decl.modifiers,
child->data.field_decl.modifiers_count,
RAVA_MODIFIER_STATIC)) {
has_static_init = true;
break;
}
}
if (has_static_init) {
RavaMethod_t *clinit = rava_method_create("<clinit>", NULL);
gen->current_method = clinit;
gen->next_local = 0;
_rava_ir_clear_locals(gen);
RavaInstruction_t instr = {0};
for (size_t i = 0; i < class_node->children_count; i++) {
RavaASTNode_t *child = class_node->children[i];
if (child->type == RAVA_AST_FIELD_DECL &&
child->data.field_decl.initializer &&
_rava_has_modifier(child->data.field_decl.modifiers,
child->data.field_decl.modifiers_count,
RAVA_MODIFIER_STATIC)) {
_rava_ir_gen_expression(gen, child->data.field_decl.initializer);
instr.opcode = RAVA_OP_STORE_STATIC;
instr.operand.field.class_name = strdup(class_node->data.class_decl.name);
instr.operand.field.field_name = strdup(child->data.field_decl.name);
_rava_ir_emit(gen, instr);
}
}
instr.opcode = RAVA_OP_RETURN_VOID;
_rava_ir_emit(gen, instr);
clinit->local_count = gen->next_local;
rava_class_add_method(gen->current_class, clinit);
gen->current_method = NULL;
}
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;
}