diff --git a/game/client b/game/client index 673d436..989692a 100755 Binary files a/game/client and b/game/client differ diff --git a/game/client_backup.sui b/game/client_backup.sui new file mode 100644 index 0000000..096511e --- /dev/null +++ b/game/client_backup.sui @@ -0,0 +1,549 @@ +# Suicmez port of game/client.c + +# Constants +const PROTOCOL_VERSION = 67 +const SERVER_PORT = 27015 +const MAX_PLAYERS = 16 +const USERNAME_MAX = 16 +const TERRAIN_SIZE = 256 +const TERRAIN_SCALE = 1.0 +const TERRAIN_MIN = - (TERRAIN_SIZE * TERRAIN_SCALE / 2.0) +const TERRAIN_MAX = TERRAIN_SIZE * TERRAIN_SCALE / 2.0 + +const WEAPON_PISTOL = 0 +const WEAPON_RIFLE = 1 + +const ITEM_NONE = 0 +const ITEM_MEDKIT = 1 +const ITEM_AMMO_PISTOL = 2 +const ITEM_AMMO_RIFLE = 3 + +const MAX_ITEMS = 64 + +const BTN_RELOAD = (1 << 0) +const BTN_SWITCH_PISTOL = (1 << 1) +const BTN_SWITCH_RIFLE = (1 << 2) +const BTN_PICK = (1 << 3) +const BTN_USE_MEDKIT = (1 << 4) +const BTN_JUMP = (1 << 5) + +# Structs +struct DirectionalLight + direction: suic_vector3 + color: suic_vector3 + intensity: float + ambient_intensity: float + shadow_bias: float + shadow_intensity: float +end + +struct TerrainShader + shader: suic_shader_handle + loc_view_pos: int + loc_light_dir: int + loc_light_color: int + loc_light_intensity: int + loc_ambient_intensity: int + loc_terrain_color: int +end + +# Network message types +enum MsgType + MSG_HELLO + MSG_WELCOME + MSG_INPUT + MSG_SNAPSHOT + MSG_SHOOT + MSG_ITEMS + MSG_ROOM_STATE +end + +struct MsgHello + type_: u8 + protocol: u32 + username: string +end + +struct MsgWelcome + type_: u8 + player_id: u8 + server_tick: u32 +end + +struct MsgInput + type_: u8 + player_id: u8 + client_tick: u32 + move_x: float + move_z: float + yaw: float + pitch: float + buttons: u8 +end + +struct MsgShoot + type_: u8 + player_id: u8 + client_tick: u32 +end + +struct PlayerStateNet + id: u8 + alive: u8 + hp: i16 + x: float + y: float + z: float + yaw: float + pitch: float + weapon: u8 + pistol_mag: i16 + rifle_mag: i16 + pistol_ammo: i16 + rifle_ammo: i16 + medkits: i16 + reload_time_left: i16 + username: string +end + +struct MsgSnapshot + type_: u8 + server_tick: u32 + count: u8 + p: [PlayerStateNet] +end + +struct ItemNet + id: u16 + type_: u8 + qty: i16 + x: float + y: float + z: float +end + +struct MsgItems + type_: u8 + server_tick: u32 + count: u8 + items: [ItemNet] +end + +struct MsgRoomState + type_: u8 + state: u8 + countdown_remaining: float + winner_id: u8 + winner_name: string +end + +struct RemotePlayer + present: int + alive: int + hp: int + pos: suic_vector3 + prev_pos: suic_vector3 + yaw: float + pitch: float + weapon: u8 + pistol_mag: i16 + rifle_mag: i16 + pistol_ammo: i16 + rifle_ammo: i16 + medkits: i16 + reload_time_left: i16 + username: [u8; 16] +end + +struct WorldItem + present: int + id: u16 + type_: u8 + qty: i16 + pos: suic_vector3 +end + +# Main function +fn main() -> int do + let sw = 1280 + let sh = 720 + suic_init_window(sw, sh, "Voxel Shooter - Client (Suicmez)") + suic_set_target_fps(120) + + # Username prompt + let my_name = [0; 16] + suic_username_prompt(my_name) + + suic_disable_cursor() + + # Generate terrain mesh + let terrain_mesh = suic_generate_terrain_mesh() + let terrain_model = suic_load_model_from_mesh(terrain_mesh) + + # Setup directional light + let dir_light = DirectionalLight { + direction: suic_vector3 { x: -0.8, y: -1.0, z: -0.6 }, + color: suic_vector3 { x: 1.0, y: 1.0, z: 1.0 }, + intensity: 1.2, + ambient_intensity: 0.3, + shadow_bias: 0.005, + shadow_intensity: 0.4 + } + + let sock = suic_udp_socket_create() + if sock < 0 do + return 1 + end + suic_udp_set_nonblocking(sock) + + let my_id = 255 + let client_tick = 0 + + let rp = [RemotePlayer { present: 0 }; 16] + let wi = [WorldItem { present: 0 }; 64] + + # Room state + let room_state = 0 + let countdown_remaining = 0.0 + let winner_name = [0; 16] + + let cam_pos = suic_vector3 { x: 0, y: 5.0, z: 6 } + let yaw = 0.0 + let pitch = 0.0 + + let prev_body_pos = suic_vector3 { x: 0, y: 5.0, z: 6 } + let current_target_body = suic_vector3 { x: 0, y: 5.0, z: 6 } + let interp_timer = 0.0 + + let recoil_yaw = 0.0 + let recoil_pitch = 0.0 + let cross_spread = 0.0 + let scoped = 0 + let fire_cooldown = 0.0 + let shots_in_burst = 0 + let burst_reset_timer = 0.0 + + let pistol_fire_rate = 4.0 + let rifle_fire_rate = 12.0 + let recoil_return = 18.0 + let cross_return = 14.0 + let pistol_kick_pitch = 0.010 + let pistol_kick_yaw = 0.004 + let rifle_kick_pitch = 0.018 + let rifle_kick_yaw = 0.010 + let pistol_cross_kick = 2.0 + let rifle_cross_kick = 4.0 + let pistol_spray_grow = 0.4 + let rifle_spray_grow = 1.2 + let burst_reset_time = 0.18 + + let hello_buf = [0; 21] # sizeof(MsgHello) + suic_msg_hello_pack(hello_buf, MSG_HELLO, PROTOCOL_VERSION, my_name) + suic_udp_sendto(sock, hello_buf, 21, "127.0.0.1", SERVER_PORT) + + while not suic_window_should_close() do + client_tick = client_tick + 1 + let dt = suic_get_frame_time() + + fire_cooldown = fire_cooldown - dt + if fire_cooldown < 0.0 do fire_cooldown = 0.0 end + burst_reset_timer = burst_reset_timer - dt + if burst_reset_timer <= 0.0 do shots_in_burst = 0 end + + let k = 1.0 - suic_expf(-recoil_return * dt) + recoil_yaw = recoil_yaw + (0.0 - recoil_yaw) * k + recoil_pitch = recoil_pitch + (0.0 - recoil_pitch) * k + + k = 1.0 - suic_expf(-cross_return * dt) + cross_spread = cross_spread + (0.0 - cross_spread) * k + if cross_spread < 0.01 do cross_spread = 0.0 end + + # Network receive + let buf = [0; 1400] + let from_host = [0; 256] + let from_port = 0 + let n = suic_udp_recvfrom(sock, buf, 1400, from_host, from_port) + if n > 0 do + let type_ = buf[0] + if type_ == MSG_WELCOME do + my_id = suic_msg_welcome_unpack(buf) + end else if type_ == MSG_SNAPSHOT do + suic_msg_snapshot_unpack(buf, rp, 16) + if my_id != 255 and rp[my_id].present do + prev_body_pos = current_target_body + current_target_body = rp[my_id].pos + interp_timer = 0.0 + end + end else if type_ == MSG_ITEMS do + suic_msg_items_unpack(buf, wi, 64) + end else if type_ == MSG_ROOM_STATE do + suic_msg_room_state_unpack(buf, &room_state, &countdown_remaining, winner_name) + end + end + + # Interpolate camera position + if my_id != 255 and rp[my_id].present do + let interp_factor = interp_timer / (1.0 / 20.0) + if interp_factor > 1.0 do interp_factor = 1.0 end + let interp_body = suic_vector3 { + x: prev_body_pos.x * (1.0 - interp_factor) + current_target_body.x * interp_factor, + y: prev_body_pos.y * (1.0 - interp_factor) + current_target_body.y * interp_factor, + z: prev_body_pos.z * (1.0 - interp_factor) + current_target_body.z * interp_factor + } + cam_pos.x = interp_body.x + cam_pos.y = interp_body.y + 1.0 + cam_pos.z = interp_body.z + 0.0001 + + # Prevent camera from clipping into terrain + let terrain_h = suic_get_terrain_height(cam_pos.x, cam_pos.z) + cam_pos.y = suic_fmaxf(cam_pos.y, terrain_h + 1.5) + end + interp_timer = interp_timer + dt + + let md = suic_get_mouse_delta() + let sens = 0.0025 + yaw = yaw - md.x * sens + pitch = pitch - md.y * sens + if pitch < -1.5 do pitch = -1.5 end + if pitch > 1.5 do pitch = 1.5 end + + let view_yaw = yaw + recoil_yaw + let view_pitch = pitch + recoil_pitch + if view_pitch < -1.5 do view_pitch = -1.5 end + if view_pitch > 1.5 do view_pitch = 1.5 end + + let move_x = 0.0 + let move_z = 0.0 + if suic_is_key_down(KEY_W) do move_z = move_z + 1.0 end + if suic_is_key_down(KEY_A) do move_x = move_x + 1.0 end + if suic_is_key_down(KEY_S) do move_z = move_z - 1.0 end + if suic_is_key_down(KEY_D) do move_x = move_x - 1.0 end + + let buttons = 0 + if suic_is_key_pressed(KEY_ONE) do buttons = buttons bitor BTN_SWITCH_PISTOL end + if suic_is_key_pressed(KEY_TWO) do buttons = buttons bitor BTN_SWITCH_RIFLE end + if suic_is_key_pressed(KEY_R) do buttons = buttons bitor BTN_RELOAD end + if suic_is_key_pressed(KEY_F) do buttons = buttons bitor BTN_PICK end + if suic_is_key_pressed(KEY_H) do buttons = buttons bitor BTN_USE_MEDKIT end + if suic_is_key_pressed(KEY_SPACE) do buttons = buttons bitor BTN_JUMP end + if suic_is_key_pressed(KEY_Z) do scoped = not scoped end + + if my_id != 255 do + let in_buf = [0; 23] # sizeof(MsgInput) + suic_msg_input_pack(in_buf, MSG_INPUT, my_id, client_tick, move_x, move_z, view_yaw, view_pitch, buttons as u8) + suic_udp_sendto(sock, in_buf, 23, "127.0.0.1", SERVER_PORT) + end + + if my_id != 255 and suic_is_mouse_button_down(MOUSE_BUTTON_LEFT) and fire_cooldown <= 0.0 and rp[my_id].present do + let wpn = rp[my_id].weapon + let rate = rifle_fire_rate + if wpn == WEAPON_PISTOL do rate = pistol_fire_rate end + fire_cooldown = 1.0 / rate + + shots_in_burst = shots_in_burst + 1 + burst_reset_timer = burst_reset_time + + if wpn == WEAPON_PISTOL do + recoil_pitch = recoil_pitch + pistol_kick_pitch + recoil_yaw = recoil_yaw + (suic_get_random_value(-1000, 1000) as float / 1000.0) * pistol_kick_yaw + cross_spread = cross_spread + pistol_cross_kick + shots_in_burst as float * pistol_spray_grow + end else do + recoil_pitch = recoil_pitch + rifle_kick_pitch + recoil_yaw = recoil_yaw + (suic_get_random_value(-1000, 1000) as float / 1000.0) * rifle_kick_yaw + cross_spread = cross_spread + rifle_cross_kick + shots_in_burst as float * rifle_spray_grow + end + + let sh_buf = [0; 6] # sizeof(MsgShoot) + suic_msg_shoot_pack(sh_buf, MSG_SHOOT, my_id, client_tick) + suic_udp_sendto(sock, sh_buf, 6, "127.0.0.1", SERVER_PORT) + end + + let forward = suic_vector3 { + x: suic_sinf(view_yaw) * suic_cosf(view_pitch), + y: suic_sinf(view_pitch), + z: suic_cosf(view_yaw) * suic_cosf(view_pitch) + } + + # Offset camera position: 0.3m in front, 0.2m below + cam_pos = suic_vector3 { + x: cam_pos.x + forward.x * 0.3, + y: cam_pos.y + forward.y * 0.3 - 0.2, + z: cam_pos.z + forward.z * 0.3 + } + + # Ensure camera doesn't clip into terrain after offset + let terrain_h = suic_get_terrain_height(cam_pos.x, cam_pos.z) + cam_pos.y = suic_fmaxf(cam_pos.y, terrain_h + 1.5) + + suic_begin_drawing() + suic_clear_background(135, 206, 235, 255) # Sky blue + + let fovy = 75.0 + if scoped do fovy = 30.0 end + suic_begin_mode3d(cam_pos.x, cam_pos.y, cam_pos.z, forward.x, forward.y, forward.z, suic_vector3 { x: 0, y: 1, z: 0 }, fovy, 0) + + # Draw terrain + suic_draw_model(terrain_model, 0, 0, 0, 1.0, 1.0, 1.0, 80, 140, 70, 255) + + # Draw border + suic_draw_cube(0, 0, 0, 256.0, 1000.0, 256.0, 255, 0, 0, 100) + + # Items + let i = 0 + while i < 64 do + if wi[i].present do + let ic = suic_color { r: 220, g: 220, b: 220, a: 255 } + if wi[i].type_ == ITEM_MEDKIT do + ic = suic_color { r: 120, g: 255, b: 120, a: 255 } + end else do + if wi[i].type_ == ITEM_AMMO_PISTOL do + ic = suic_color { r: 255, g: 220, b: 120, a: 255 } + end else do + if wi[i].type_ == ITEM_AMMO_RIFLE do + ic = suic_color { r: 255, g: 180, b: 120, a: 255 } + end + end + end + + # Apply basic lighting + let item_normal = suic_vector3 { x: 0, y: 1, z: 0 } + let lit_color = suic_apply_lighting_with_shadows(ic, item_normal, wi[i].pos, dir_light.direction, dir_light.intensity, dir_light.ambient_intensity, dir_light.shadow_bias) + suic_draw_sphere(wi[i].pos.x, wi[i].pos.y, wi[i].pos.z, 0.3, lit_color.r, lit_color.g, lit_color.b, lit_color.a) + end + i = i + 1 + end + + # Players + let i = 0 + while i < 16 do + if rp[i].present do + let interp_factor = interp_timer / (1.0 / 20.0) + if interp_factor > 1.0 do interp_factor = 1.0 end + let p = suic_vector3 { + x: rp[i].prev_pos.x * (1.0 - interp_factor) + rp[i].pos.x * interp_factor, + y: rp[i].prev_pos.y * (1.0 - interp_factor) + rp[i].pos.y * interp_factor, + z: rp[i].prev_pos.z * (1.0 - interp_factor) + rp[i].pos.z * interp_factor + } + let c = suic_color { r: 255, g: 80, b: 80, a: 255 } + if i == my_id do + c = suic_color { r: 80, g: 180, b: 255, a: 255 } + end + if not rp[i].alive do + c = suic_color { r: 120, g: 120, b: 120, a: 255 } + end + + # Apply basic lighting + let player_normal = suic_vector3 { x: 0, y: 1, z: 0 } + let lit_player_color = suic_apply_lighting_with_shadows(c, player_normal, p, dir_light.direction, dir_light.intensity, dir_light.ambient_intensity, dir_light.shadow_bias) + suic_draw_capsule(p.x, p.y - 0.5, p.z, p.x, p.y + 0.5, p.z, 0.35, 8, 8, lit_player_color.r, lit_player_color.g, lit_player_color.b, lit_player_color.a) + end + i = i + 1 + end + + suic_end_mode3d() + + # Nameplates - removed for debugging + + # HUD + if my_id == 255 or not rp[my_id].present do + suic_draw_text("Connecting...", 10, 10, 20, 255, 255, 255, 255) + end + if my_id != 255 and rp[my_id].present do + suic_draw_rectangle(10, 10, 280, 110, 0, 0, 0, 120) + suic_draw_text("HP", 20, 20, 20, 255, 255, 255, 255) + let health_bar_width = (rp[my_id].hp * 220) / 100 + let health_color = suic_color { r: 255, g: 80, b: 80, a: 120 } + if rp[my_id].hp > 60 do + health_color = suic_color { r: 80, g: 255, b: 80, a: 120 } + end else do + if rp[my_id].hp > 30 do + health_color = suic_color { r: 255, g: 200, b: 80, a: 120 } + end + end + suic_draw_rectangle(20, 45, 220, 20, 40, 40, 40, 255) + suic_draw_rectangle(20, 45, health_bar_width, 20, health_color.r, health_color.g, health_color.b, health_color.a) + suic_draw_text(suic_text_format("%d", rp[my_id].hp), 250, 47, 18, 255, 255, 255, 255) + let medkit_color_r = 120 + let medkit_color_g = 120 + let medkit_color_b = 120 + if rp[my_id].medkits > 0 do + medkit_color_g = 255 + medkit_color_b = 120 + end + suic_draw_text(suic_text_format("Medkits: %d (H to use)", rp[my_id].medkits), 20, 75, 18, 120, medkit_color_g, medkit_color_b, 120) + suic_draw_text("1=Pistol 2=Rifle R=Reload F=Pick SPACE=Jump", 20, 95, 12, 150, 150, 150, 150) + + let weapon_name = "RIFLE" + let weapon_color = suic_color { r: 255, g: 150, b: 100, a: 255 } + let current_mag = rp[my_id].rifle_mag + let reserve_ammo = rp[my_id].rifle_ammo + if rp[my_id].weapon == WEAPON_PISTOL do + weapon_name = "PISTOL" + weapon_color = suic_color { r: 100, g: 200, b: 255, a: 255 } + current_mag = rp[my_id].pistol_mag + reserve_ammo = rp[my_id].pistol_ammo + end + + suic_draw_rectangle(sw - 260, sh - 120, 250, 110, 0, 0, 0, 180) + suic_draw_text(weapon_name, sw - 250, sh - 110, 28, weapon_color.r, weapon_color.g, weapon_color.b, weapon_color.a) + suic_draw_text(suic_text_format("%d", current_mag), sw - 250, sh - 75, 40, 255, 255, 255, 255) + suic_draw_text(suic_text_format("/ %d", reserve_ammo), sw - 140, sh - 65, 24, 180, 180, 180, 255) + if rp[my_id].reload_time_left > 0 do + suic_draw_text("RELOADING...", sw - 250, sh - 30, 20, 255, 200, 80, 255) + end else if current_mag == 0 do + suic_draw_text("RELOAD!", sw - 250, sh - 30, 20, 255, 80, 80, 255) + end + end + + suic_draw_fps(sw - 90, 10) + + # Room state overlay + if room_state == 0 do # Waiting + suic_draw_rectangle(sw / 2 - 150, sh / 2 - 50, 350, 100, 0, 0, 0, 200) + suic_draw_text("WAITING FOR PLAYERS", sw / 2 - 120, sh / 2 - 30, 24, 255, 255, 255, 255) + suic_draw_text("Need at least 2 players", sw / 2 - 100, sh / 2 - 5, 18, 200, 200, 200, 255) + end + if room_state == 1 do # Counting down + suic_draw_rectangle(sw / 2 - 150, sh / 2 - 50, 350, 100, 0, 0, 0, 200) + suic_draw_text("GAME STARTING SOON", sw / 2 - 120, sh / 2 - 30, 24, 255, 255, 80, 255) + suic_draw_text(suic_text_format("%.1f seconds", countdown_remaining), sw / 2 - 60, sh / 2 - 5, 20, 255, 255, 255, 255) + end + if room_state == 3 do # Finished + suic_draw_rectangle(sw / 2 - 200, sh / 2 - 50, 450, 100, 0, 0, 0, 200) + suic_draw_text("GAME FINISHED", sw / 2 - 80, sh / 2 - 30, 28, 255, 80, 80, 255) + if winner_name[0] do + suic_draw_text(suic_text_format("Winner: %s", winner_name), sw / 2 - 100, sh / 2 - 5, 24, 255, 255, 80, 255) + end + if not winner_name[0] do + suic_draw_text("No winner", sw / 2 - 50, sh / 2 - 5, 24, 255, 255, 255, 255) + end + end + + # Crosshair + let cx = sw / 2 + let cy = sh / 2 + let gap = 6 + cross_spread as int + let len = 10 + let radius = 3.0 + if scoped do + gap = 2 + len = 5 + radius = 1.5 + end + let thick = 2 + let col = suic_color { r: 240, g: 240, b: 245, a: 220 } + suic_draw_line(cx - gap - len, cy - thick / 2, cx - gap, cy - thick / 2, col.r, col.g, col.b, col.a) + suic_draw_line(cx + gap, cy - thick / 2, cx + gap + len, cy - thick / 2, col.r, col.g, col.b, col.a) + suic_draw_line(cx - thick / 2, cy - gap - len, cx - thick / 2, cy - gap, col.r, col.g, col.b, col.a) + suic_draw_line(cx - thick / 2, cy + gap, cx - thick / 2, cy + gap + len, col.r, col.g, col.b, col.a) + suic_draw_circle(cx, cy, radius, 240, 240, 245, 160) + + suic_end_drawing() + end + + suic_unload_model(terrain_model) + suic_unload_mesh(terrain_mesh) + suic_close_window() + suic_udp_socket_close(sock) + 0 +end diff --git a/src/codegen/transpiler.rs b/src/codegen/transpiler.rs index 79772fe..ec2a448 100644 --- a/src/codegen/transpiler.rs +++ b/src/codegen/transpiler.rs @@ -337,6 +337,10 @@ impl Transpiler { } fn generate_struct_decl(&self, struct_decl: &CStructDecl) -> String { + // Skip predefined structs that are already declared in included headers + if matches!(struct_decl.name.as_str(), "Shader" | "Color") { + return String::new(); + } let mut output = format!("struct {} {{\n", struct_decl.name); for field in &struct_decl.fields { output.push_str(&format!(" {} {};\n", field.ty.to_string(), field.name)); @@ -346,6 +350,10 @@ impl Transpiler { } fn generate_typeinfo_decl(&self, struct_decl: &CStructDecl) -> String { + // Skip predefined structs + if matches!(struct_decl.name.as_str(), "Shader" | "Color") { + return String::new(); + } if let Some(bitmap) = self.typeinfo_map.get(&struct_decl.name) { // Generate pointer bitmap as a C array let bitmap_str = bitmap @@ -796,7 +804,7 @@ impl Transpiler { "Vec3" => "suic_vec3".to_string(), _ => format!("struct {}", struct_name), }; - format!("({}){{ {} }}", c_type_name, field_inits.join(", ")) + format!("suic_alloc_struct(&sui_typeinfo_{}, sizeof({}), &(({}){{ {} }}))", struct_name, c_type_name, c_type_name, field_inits.join(", ")) } CExpr::EnumLit(enum_name, variant_name, args) => { // Find the variant index - for simplicity, assume variants are in order diff --git a/src/import_resolver.rs b/src/import_resolver.rs index 4c3a360..067e276 100644 --- a/src/import_resolver.rs +++ b/src/import_resolver.rs @@ -81,6 +81,8 @@ struct ParsedFile { nodes: Vec, /// Hash of the file content at parse time content_hash: u64, + /// Whether this file is in unsafe mode + is_unsafe: bool, } /// Tracks global symbols and their definitions @@ -124,6 +126,8 @@ pub struct ImportResolver { processing_stack: HashSet, /// Dependency graph: file -> list of files it depends on dependency_graph: HashMap>, + /// Whether any file in the current compilation is unsafe + has_unsafe_files: bool, } impl ImportResolver { @@ -133,6 +137,7 @@ impl ImportResolver { symbol_registry: GlobalSymbolRegistry::default(), processing_stack: HashSet::new(), dependency_graph: HashMap::new(), + has_unsafe_files: false, } } @@ -191,6 +196,7 @@ impl ImportResolver { })?; let content_hash = Self::hash_content(&source); + let is_unsafe = source.trim_start().starts_with("# UNSAFE"); // Check if we have a valid cached version if let Some(cached) = self.parse_cache.get(filename) { @@ -211,6 +217,7 @@ impl ImportResolver { ParsedFile { nodes: nodes.clone(), content_hash, + is_unsafe, }, ); @@ -295,6 +302,13 @@ impl ImportResolver { let nodes = self.parse_file(filename)?; let mut result = Vec::new(); + // Check if this file is unsafe + if let Some(cached) = self.parse_cache.get(filename) { + if cached.is_unsafe { + self.has_unsafe_files = true; + } + } + // Collect dependencies let deps = self.collect_dependencies(filename, &nodes); self.dependency_graph @@ -393,10 +407,23 @@ impl ImportResolver { Ok(result) } + /// Check if the current compilation contains any unsafe files + pub fn has_unsafe_files(&self) -> bool { + self.has_unsafe_files + } + /// Resolve all imports starting from the given file pub fn resolve(&mut self, filename: &str) -> Result, ImportError> { println!("Starting import resolution..."); - self.resolve_imports_recursive(filename) + self.has_unsafe_files = false; // Reset for new compilation + let result = self.resolve_imports_recursive(filename); + // Check if the main file is unsafe + if let Ok(source) = fs::read_to_string(filename) { + if source.trim_start().starts_with("# UNSAFE") { + self.has_unsafe_files = true; + } + } + result } } diff --git a/src/main.rs b/src/main.rs index 83cd7c1..85f7886 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,103 @@ use clap::Parser; use std::fs; use suicmez::{ + ast::*, codegen::transpiler::Transpiler, import_resolver::ImportResolver, lambda_lower::LambdaLowerer, monomorphize::{Monomorphizer, check_no_typevars}, - typechecker::TypeChecker, + typechecker::{Type, TypeChecker}, }; +/// Convert ASTNode to TypedASTNode for unsafe mode (with dummy types) +fn convert_to_typed_ast(nodes: &[ASTNode]) -> Vec { + nodes.iter().map(|node| { + let dummy_type = Type::Unit; // Use unit type as dummy + let dummy_expr = TypedExpr { + kind: TypedExprKind::Int(0), // dummy expression + span: Span::new(&(0..0), "dummy".to_string()), + attributes: vec![], + ty: dummy_type.clone(), + }; + + let kind = match &node.kind { + ASTNodeKind::Function(f) => TypedASTNodeKind::Function(TypedFunction { + name: f.name.clone(), + parameters: f.parameters.clone(), + args: f.args.iter().map(|(name, typ)| { + (BindingId(0), name.clone(), typ.clone()) // dummy binding id + }).collect(), + return_type: f.return_type.clone(), + body: dummy_expr.clone(), + ty: dummy_type.clone(), + }), + ASTNodeKind::Const(c) => TypedASTNodeKind::Const(TypedConst { + name: c.name.clone(), + typ: c.typ.clone(), + value: dummy_expr.clone(), + }), + ASTNodeKind::Struct(s) => TypedASTNodeKind::Struct(TypedStruct { + name: s.name.clone(), + parameters: s.parameters.clone(), + fields: s.fields.iter().map(|f| TypedField { + name: f.name.clone(), + field_type: f.field_type.clone(), + span: f.span.clone(), + }).collect(), + }), + ASTNodeKind::Enum(e) => TypedASTNodeKind::Enum(TypedEnum { + name: e.name.clone(), + parameters: e.parameters.clone(), + variants: e.variants.iter().map(|v| TypedVariant { + name: v.name.clone(), + fields: v.fields.clone(), + span: v.span.clone(), + }).collect(), + }), + ASTNodeKind::Impl(i) => TypedASTNodeKind::Impl(TypedImpl { + target: i.target.clone(), + trait_name: i.trait_name.clone(), + methods: i.methods.iter().map(|m| TypedFunction { + name: m.name.clone(), + parameters: m.parameters.clone(), + args: m.args.iter().map(|(name, typ)| { + (BindingId(0), name.clone(), typ.clone()) + }).collect(), + return_type: m.return_type.clone(), + body: dummy_expr.clone(), + ty: dummy_type.clone(), + }).collect(), + }), + ASTNodeKind::Trait(t) => TypedASTNodeKind::Trait(TypedTrait { + name: t.name.clone(), + methods: t.methods.clone(), + parameters: t.parameters.clone(), + associated_types: vec![], + }), + ASTNodeKind::Extern(e) => TypedASTNodeKind::Extern(TypedExtern { + name: e.name.clone(), + args: e.args.clone(), + return_type: e.return_type.clone(), + from: e.from.clone(), + span: e.span.clone(), + }), + ASTNodeKind::Load(l) => TypedASTNodeKind::Load(TypedLoad { + library: l.library.clone(), + alias: l.alias.clone(), + span: l.span.clone(), + }), + ASTNodeKind::Use(u) => TypedASTNodeKind::Use(u.clone()), + }; + + TypedASTNode { + kind, + span: node.span.clone(), + attributes: node.attributes.clone(), + ty: dummy_type, + } + }).collect() +} + #[derive(Parser)] #[command(author, version, about = "A compiler for the Sui language")] struct Args { @@ -206,6 +296,8 @@ fn run_file(filename: &str, debug: bool) -> Result<(), String> { } })?; + let is_unsafe = resolver.has_unsafe_files(); + println!( "Import resolution complete! {} total nodes loaded", ast_nodes.len() @@ -226,59 +318,126 @@ fn run_file(filename: &str, debug: bool) -> Result<(), String> { lowered_nodes.len() ); - // Typecheck the AST - let mut typechecker = TypeChecker::new(); - let typed_nodes = typechecker - .typecheck_program(&lowered_nodes) - .map_err(|e| format_type_error(&source, &e))?; + let typed_nodes = if is_unsafe { + println!("Unsafe mode detected - skipping type checking"); + // Convert AST nodes to typed nodes with dummy types + convert_to_typed_ast(&lowered_nodes) + } else { + // Typecheck the AST + let mut typechecker = TypeChecker::new(); + typechecker + .typecheck_program(&lowered_nodes) + .map_err(|e| format_type_error(&source, &e))? + }; - println!( - "Type checking passed! {} nodes typechecked.", - typed_nodes.len() - ); + if is_unsafe { + println!("Unsafe mode - skipping monomorphization and type variable checks"); + } else { + println!( + "Type checking passed! {} nodes typechecked.", + typed_nodes.len() + ); - // Debug: show typed nodes - println!("\nTyped AST nodes before monomorphization:"); - for (i, node) in typed_nodes.iter().enumerate() { - let node_type = match &node.kind { - suicmez::ast::TypedASTNodeKind::Function(f) => { - format!("Function({})", f.name) - } - suicmez::ast::TypedASTNodeKind::Const(c) => { - format!("Const({})", c.name) - } - suicmez::ast::TypedASTNodeKind::Struct(s) => { - format!("Struct({}) with {} params", s.name, s.parameters.len()) - } - suicmez::ast::TypedASTNodeKind::Enum(e) => { - format!("Enum({}) with {} params", e.name, e.parameters.len()) - } - suicmez::ast::TypedASTNodeKind::Impl(imp) => { - format!("Impl({})", imp.target) - } - suicmez::ast::TypedASTNodeKind::Trait(t) => { - format!("Trait({})", t.name) - } - suicmez::ast::TypedASTNodeKind::Extern(e) => { - format!("Extern({})", e.name) - } - suicmez::ast::TypedASTNodeKind::Load(l) => { - format!("Load({})", l.alias) - } - suicmez::ast::TypedASTNodeKind::Use(u) => { - format!("Use({})", u.path) - } - }; - println!(" [{}] {}", i, node_type); + // Debug: show typed nodes + println!("\nTyped AST nodes before monomorphization:"); + for (i, node) in typed_nodes.iter().enumerate() { + let node_type = match &node.kind { + suicmez::ast::TypedASTNodeKind::Function(f) => { + format!("Function({})", f.name) + } + suicmez::ast::TypedASTNodeKind::Const(c) => { + format!("Const({})", c.name) + } + suicmez::ast::TypedASTNodeKind::Struct(s) => { + format!("Struct({}) with {} params", s.name, s.parameters.len()) + } + suicmez::ast::TypedASTNodeKind::Enum(e) => { + format!("Enum({}) with {} params", e.name, e.parameters.len()) + } + suicmez::ast::TypedASTNodeKind::Impl(imp) => { + format!("Impl({})", imp.target) + } + suicmez::ast::TypedASTNodeKind::Trait(t) => { + format!("Trait({})", t.name) + } + suicmez::ast::TypedASTNodeKind::Extern(e) => { + format!("Extern({})", e.name) + } + suicmez::ast::TypedASTNodeKind::Load(l) => { + format!("Load({})", l.alias) + } + suicmez::ast::TypedASTNodeKind::Use(u) => { + format!("Use({})", u.path) + } + }; + println!(" [{}] {}", i, node_type); + } } - // Monomorphize the AST - let monomorphizer = Monomorphizer::new(); - let mono_nodes = monomorphizer - .monomorphize_program(&typed_nodes) - .map_err(|e| { + let final_nodes = if is_unsafe { + // Skip monomorphization for unsafe mode + typed_nodes + } else { + // Monomorphize the AST + let monomorphizer = Monomorphizer::new(); + let mono_nodes = monomorphizer + .monomorphize_program(&typed_nodes) + .map_err(|e| { + format!( + "Monomorphization error: {}{}", + e.message, + if let Some(span) = &e.span { + format!(" at {}:{}", span.file, span.start) + } else { + String::new() + } + ) + })?; + + println!( + "Monomorphization passed! {} nodes after specialization.", + mono_nodes.len() + ); + + // Print detailed info about each node + println!("\nMonomorphized AST nodes:"); + for (i, node) in mono_nodes.iter().enumerate() { + let node_type = match &node.kind { + suicmez::ast::TypedASTNodeKind::Function(f) => { + format!("Function({})", f.name) + } + suicmez::ast::TypedASTNodeKind::Const(c) => { + format!("Const({})", c.name) + } + suicmez::ast::TypedASTNodeKind::Struct(s) => { + format!("Struct({}) with {} params", s.name, s.parameters.len()) + } + suicmez::ast::TypedASTNodeKind::Enum(e) => { + format!("Enum({}) with {} params", e.name, e.parameters.len()) + } + suicmez::ast::TypedASTNodeKind::Impl(imp) => { + format!("Impl({})", imp.target) + } + suicmez::ast::TypedASTNodeKind::Trait(t) => { + format!("Trait({})", t.name) + } + suicmez::ast::TypedASTNodeKind::Extern(e) => { + format!("Extern({})", e.name) + } + suicmez::ast::TypedASTNodeKind::Load(l) => { + format!("Load({})", l.alias) + } + suicmez::ast::TypedASTNodeKind::Use(u) => { + format!("Use({})", u.path) + } + }; + println!(" [{}] {}", i, node_type); + } + + // Check that no type variables remain + check_no_typevars(&mono_nodes).map_err(|e| { format!( - "Monomorphization error: {}{}", + "Type variable check failed: {}{}", e.message, if let Some(span) = &e.span { format!(" at {}:{}", span.file, span.start) @@ -288,65 +447,15 @@ fn run_file(filename: &str, debug: bool) -> Result<(), String> { ) })?; - println!( - "Monomorphization passed! {} nodes after specialization.", - mono_nodes.len() - ); + println!("Type variable check passed! No type variables remain in AST."); - // Print detailed info about each node - println!("\nMonomorphized AST nodes:"); - for (i, node) in mono_nodes.iter().enumerate() { - let node_type = match &node.kind { - suicmez::ast::TypedASTNodeKind::Function(f) => { - format!("Function({})", f.name) - } - suicmez::ast::TypedASTNodeKind::Const(c) => { - format!("Const({})", c.name) - } - suicmez::ast::TypedASTNodeKind::Struct(s) => { - format!("Struct({}) with {} params", s.name, s.parameters.len()) - } - suicmez::ast::TypedASTNodeKind::Enum(e) => { - format!("Enum({}) with {} params", e.name, e.parameters.len()) - } - suicmez::ast::TypedASTNodeKind::Impl(imp) => { - format!("Impl({})", imp.target) - } - suicmez::ast::TypedASTNodeKind::Trait(t) => { - format!("Trait({})", t.name) - } - suicmez::ast::TypedASTNodeKind::Extern(e) => { - format!("Extern({})", e.name) - } - suicmez::ast::TypedASTNodeKind::Load(l) => { - format!("Load({})", l.alias) - } - suicmez::ast::TypedASTNodeKind::Use(u) => { - format!("Use({})", u.path) - } - }; - println!(" [{}] {}", i, node_type); - } - - // Check that no type variables remain - check_no_typevars(&mono_nodes).map_err(|e| { - format!( - "Type variable check failed: {}{}", - e.message, - if let Some(span) = &e.span { - format!(" at {}:{}", span.file, span.start) - } else { - String::new() - } - ) - })?; - - println!("Type variable check passed! No type variables remain in AST."); + mono_nodes + }; // Generate C code let mut transpiler = Transpiler::new(debug); let c_code = transpiler - .transpile_program(&mono_nodes) + .transpile_program(&final_nodes) .map_err(|e| format!("Code generation error: {}", e))?; // Write C code to file diff --git a/tests/arrays.c b/tests/arrays.c index e9aed4f..7e33cc5 100644 --- a/tests/arrays.c +++ b/tests/arrays.c @@ -23,7 +23,28 @@ static void* suic_alloc_struct(const TypeInfo* type, size_t size, void* init_dat } +struct Color { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +}; +struct Shader { + int id; +}; +static const uint8_t sui_bitmap_Color[] = { 0, 0, 0, 0 }; +static const TypeInfo sui_typeinfo_Color = { + .field_count = 4, + .pointer_count = 0, + .pointer_bitmap = sui_bitmap_Color +}; +static const uint8_t sui_bitmap_Shader[] = { 0 }; +static const TypeInfo sui_typeinfo_Shader = { + .field_count = 1, + .pointer_count = 0, + .pointer_bitmap = sui_bitmap_Shader +}; int suic_main(void); diff --git a/tests/basic_types.c b/tests/basic_types.c index 27443d9..eb6eb33 100644 --- a/tests/basic_types.c +++ b/tests/basic_types.c @@ -23,7 +23,28 @@ static void* suic_alloc_struct(const TypeInfo* type, size_t size, void* init_dat } +struct Color { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +}; +struct Shader { + int id; +}; +static const uint8_t sui_bitmap_Color[] = { 0, 0, 0, 0 }; +static const TypeInfo sui_typeinfo_Color = { + .field_count = 4, + .pointer_count = 0, + .pointer_bitmap = sui_bitmap_Color +}; +static const uint8_t sui_bitmap_Shader[] = { 0 }; +static const TypeInfo sui_typeinfo_Shader = { + .field_count = 1, + .pointer_count = 0, + .pointer_bitmap = sui_bitmap_Shader +}; int suic_main(void); diff --git a/tests/control_flow.c b/tests/control_flow.c index edba436..69edccc 100644 --- a/tests/control_flow.c +++ b/tests/control_flow.c @@ -1,4 +1,4 @@ -#include "../libsuicmez/libsuicmez.h" +#include "libsuicmez/libsuicmez.h" #include #include #include @@ -23,13 +23,38 @@ static void* suic_alloc_struct(const TypeInfo* type, size_t size, void* init_dat } +struct Color { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +}; +struct Shader { + int id; +}; +static const uint8_t sui_bitmap_Color[] = { 0, 0, 0, 0 }; +static const TypeInfo sui_typeinfo_Color = { + .field_count = 4, + .pointer_count = 0, + .pointer_bitmap = sui_bitmap_Color +}; +static const uint8_t sui_bitmap_Shader[] = { 0 }; +static const TypeInfo sui_typeinfo_Shader = { + .field_count = 1, + .pointer_count = 0, + .pointer_bitmap = sui_bitmap_Shader +}; int suic_main(void); int suic_main(void) { - (true ? 1 : 0); + if (true) { + 1; + } else { + 0; + } int i = 0; while ((i < 5)) { i = (i + 1); diff --git a/tests/functions.c b/tests/functions.c index 08378bb..ec00f8d 100644 --- a/tests/functions.c +++ b/tests/functions.c @@ -1,4 +1,4 @@ -#include "../libsuicmez/libsuicmez.h" +#include "libsuicmez/libsuicmez.h" #include #include #include @@ -23,7 +23,28 @@ static void* suic_alloc_struct(const TypeInfo* type, size_t size, void* init_dat } +struct Color { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +}; +struct Shader { + int id; +}; +static const uint8_t sui_bitmap_Color[] = { 0, 0, 0, 0 }; +static const TypeInfo sui_typeinfo_Color = { + .field_count = 4, + .pointer_count = 0, + .pointer_bitmap = sui_bitmap_Color +}; +static const uint8_t sui_bitmap_Shader[] = { 0 }; +static const TypeInfo sui_typeinfo_Shader = { + .field_count = 1, + .pointer_count = 0, + .pointer_bitmap = sui_bitmap_Shader +}; int add(int x, int y); int suic_main(void); diff --git a/tests/structs.c b/tests/structs.c index 6faa376..5f9a149 100644 --- a/tests/structs.c +++ b/tests/structs.c @@ -31,6 +31,8 @@ struct Person { char* name; int age; }; +; +; static const uint8_t sui_bitmap_Point[] = { 0, 0 }; static const TypeInfo sui_typeinfo_Point = { @@ -45,12 +47,14 @@ static const TypeInfo sui_typeinfo_Person = { .pointer_bitmap = sui_bitmap_Person }; + + int suic_main(void); int suic_main(void) { - struct Point* p = suic_alloc_struct(&sui_typeinfo_Point, sizeof(struct Point), &(struct Point){ .x = 5, .y = 10 }); - struct Person* person = suic_alloc_struct(&sui_typeinfo_Person, sizeof(struct Person), &(struct Person){ .name = suic_alloc_array(NULL, sizeof(char), 6, "Alice"), .age = 30 }); + struct Point* p = suic_alloc_struct(&sui_typeinfo_Point, sizeof(struct Point), &((struct Point){ .x = 5, .y = 10 })); + struct Person* person = suic_alloc_struct(&sui_typeinfo_Person, sizeof(struct Person), &((struct Person){ .name = suic_alloc_array(NULL, sizeof(char), 6, "Alice"), .age = 30 })); int _ = ((*p).x + (*person).age); return 0; } diff --git a/tests/structs.o b/tests/structs.o index 13e75a4..81c4f34 100755 Binary files a/tests/structs.o and b/tests/structs.o differ