Compare commits

...

4 commits

Author SHA1 Message Date
6f198b17a9 unsafe 2025-12-20 04:00:59 +05:30
a00e8690fd unsafe 2025-12-20 03:18:31 +05:30
a222b3807e unsafe 2025-12-20 03:16:16 +05:30
e0277a8807 HMMMMMMMMMMMMMMMMMMMM 2025-12-20 03:10:01 +05:30
12 changed files with 2052 additions and 437 deletions

Binary file not shown.

File diff suppressed because it is too large Load diff

549
game/client_backup.sui Normal file
View file

@ -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

View file

@ -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

View file

@ -81,6 +81,8 @@ struct ParsedFile {
nodes: Vec<ASTNode>,
/// 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<String>,
/// Dependency graph: file -> list of files it depends on
dependency_graph: HashMap<String, HashSet<String>>,
/// 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<Vec<ASTNode>, 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
}
}

View file

@ -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<TypedASTNode> {
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

View file

@ -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);

View file

@ -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);

View file

@ -1,4 +1,4 @@
#include "../libsuicmez/libsuicmez.h"
#include "libsuicmez/libsuicmez.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
@ -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);

View file

@ -1,4 +1,4 @@
#include "../libsuicmez/libsuicmez.h"
#include "libsuicmez/libsuicmez.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
@ -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);

View file

@ -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;
}

Binary file not shown.