Compare commits
2 commits
859e7d7560
...
f1d54f301c
| Author | SHA1 | Date | |
|---|---|---|---|
| f1d54f301c | |||
| e0a42d0262 |
46 changed files with 2996 additions and 789 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -153,6 +153,12 @@ version = "1.70.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "logos"
|
name = "logos"
|
||||||
version = "0.16.0"
|
version = "0.16.0"
|
||||||
|
|
@ -261,6 +267,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
|
"log",
|
||||||
"logos",
|
"logos",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,5 @@ edition = "2024"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.53", features = ["derive"] }
|
clap = { version = "4.5.53", features = ["derive"] }
|
||||||
indexmap = "2.0"
|
indexmap = "2.0"
|
||||||
|
log = "0.4.29"
|
||||||
logos = "0.16.0"
|
logos = "0.16.0"
|
||||||
|
|
|
||||||
287
GAMEDEV_GUIDE.md
Normal file
287
GAMEDEV_GUIDE.md
Normal file
|
|
@ -0,0 +1,287 @@
|
||||||
|
# Sui Language Game Development Guide
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Sui now has integrated support for game development with **Raylib** (graphics) and **ODE** (physics). This guide explains how to build games with physics simulation and rendering.
|
||||||
|
|
||||||
|
## Key Concepts
|
||||||
|
|
||||||
|
### 1. Physics Engine (ODE)
|
||||||
|
|
||||||
|
ODE (Open Dynamics Engine) provides rigid body dynamics, collision detection, and constraint solving. The key components are:
|
||||||
|
|
||||||
|
- **World**: The container for all physical objects and gravity
|
||||||
|
- **Bodies**: Dynamic rigid bodies with mass, velocity, and position
|
||||||
|
- **Geometries**: Collision shapes (boxes, planes, spheres, etc.)
|
||||||
|
- **Space**: Collision detection space that organizes geometries
|
||||||
|
- **Contacts**: Joint constraints created when objects collide
|
||||||
|
|
||||||
|
### 2. Graphics Engine (Raylib)
|
||||||
|
|
||||||
|
Raylib provides 2D and 3D graphics, input handling, and audio support.
|
||||||
|
|
||||||
|
### 3. Integration Pattern
|
||||||
|
|
||||||
|
The key to proper physics is the **collision detection loop**:
|
||||||
|
|
||||||
|
```sui
|
||||||
|
# Initialize
|
||||||
|
ode_init()
|
||||||
|
ode_space_collide(world, space, contactgroup)
|
||||||
|
ode_world_step(world, timestep)
|
||||||
|
ode_joint_group_empty(contactgroup)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important**: You must call collision detection **before** world step, and clear the contact group **after** the step.
|
||||||
|
|
||||||
|
## Step-by-Step Tutorial
|
||||||
|
|
||||||
|
### Step 1: Initialize the World
|
||||||
|
|
||||||
|
```sui
|
||||||
|
fn main() -> int do
|
||||||
|
# Initialize graphics
|
||||||
|
init_window(800, 600, "My Game")
|
||||||
|
set_target_fps(60)
|
||||||
|
|
||||||
|
# Initialize physics
|
||||||
|
ode_init()
|
||||||
|
let world = ode_world_create()
|
||||||
|
let space = ode_simple_space_create(0 as *())
|
||||||
|
let contactgroup = ode_joint_group_create(0)
|
||||||
|
|
||||||
|
# Set gravity (9.81 m/s² downward)
|
||||||
|
ode_world_set_gravity(world, 0.0, -9.81, 0.0)
|
||||||
|
|
||||||
|
# ... rest of code
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Create Physics Objects
|
||||||
|
|
||||||
|
Each dynamic object needs:
|
||||||
|
1. A **body** - represents mass and motion
|
||||||
|
2. A **geometry** - collision shape
|
||||||
|
3. Mass configuration
|
||||||
|
|
||||||
|
```sui
|
||||||
|
# Create a cube that falls
|
||||||
|
let body = ode_body_create(world)
|
||||||
|
ode_body_set_position(body, 0.0, 5.0, 0.0) # x, y, z
|
||||||
|
ode_body_set_box_mass(body, 1.0, 1.0, 1.0, 1.0) # density, width, height, length
|
||||||
|
|
||||||
|
# Create collision geometry
|
||||||
|
let geom = ode_create_box_geom(space, 1.0, 1.0, 1.0)
|
||||||
|
ode_geom_set_body(geom, body)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Create Static Objects (Ground)
|
||||||
|
|
||||||
|
Static objects are created without a body - they use infinite mass:
|
||||||
|
|
||||||
|
```sui
|
||||||
|
# Create a ground plane at y = 0
|
||||||
|
let ground_geom = ode_create_plane_geom(space, 0.0, 1.0, 0.0, 0.0)
|
||||||
|
# Parameters: space, normal_x, normal_y, normal_z, distance_d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Main Game Loop
|
||||||
|
|
||||||
|
```sui
|
||||||
|
while window_should_close() == false do
|
||||||
|
# === PHYSICS STEP ===
|
||||||
|
|
||||||
|
# 1. Detect collisions between all geometries
|
||||||
|
ode_space_collide(world, space, contactgroup)
|
||||||
|
|
||||||
|
# 2. Simulate physics
|
||||||
|
ode_world_step(world, 1.0 / 60.0) # 60 FPS timestep
|
||||||
|
|
||||||
|
# 3. Clear contact group to avoid double-application
|
||||||
|
ode_joint_group_empty(contactgroup)
|
||||||
|
|
||||||
|
# === RENDERING STEP ===
|
||||||
|
|
||||||
|
# Get current position
|
||||||
|
let mut x = 0.0
|
||||||
|
let mut y = 0.0
|
||||||
|
let mut z = 0.0
|
||||||
|
ode_body_get_position(body, &x, &y, &z)
|
||||||
|
|
||||||
|
# Draw
|
||||||
|
begin_drawing()
|
||||||
|
clear_background(135, 206, 235, 255) # Sky blue
|
||||||
|
begin_mode3d(0.0, 2.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 45.0, 0)
|
||||||
|
|
||||||
|
draw_cube(x, y, z, 1.0, 1.0, 1.0, 255, 0, 0, 255) # Red cube
|
||||||
|
draw_cube(0.0, -1.0, 0.0, 20.0, 0.1, 20.0, 34, 139, 34, 255) # Green ground
|
||||||
|
|
||||||
|
end_mode3d()
|
||||||
|
end_drawing()
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 5: Cleanup
|
||||||
|
|
||||||
|
Always destroy objects in reverse order of creation:
|
||||||
|
|
||||||
|
```sui
|
||||||
|
ode_joint_group_destroy(contactgroup)
|
||||||
|
ode_geom_destroy(geom)
|
||||||
|
ode_body_destroy(body)
|
||||||
|
ode_space_destroy(space)
|
||||||
|
ode_world_destroy(world)
|
||||||
|
ode_close()
|
||||||
|
close_window()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Physics Functions
|
||||||
|
|
||||||
|
### World Management
|
||||||
|
- `ode_init()` - Initialize ODE
|
||||||
|
- `ode_close()` - Shutdown ODE
|
||||||
|
- `ode_world_create() -> *()` - Create a world
|
||||||
|
- `ode_world_destroy(world)` - Destroy world
|
||||||
|
- `ode_world_set_gravity(world, x, y, z)` - Set gravity vector
|
||||||
|
- `ode_world_step(world, timestep)` - Advance simulation
|
||||||
|
|
||||||
|
### Body Management
|
||||||
|
- `ode_body_create(world) -> *()` - Create a dynamic body
|
||||||
|
- `ode_body_destroy(body)`
|
||||||
|
- `ode_body_set_position(body, x, y, z)`
|
||||||
|
- `ode_body_get_position(body, &x, &y, &z)` - Retrieve position via pointers
|
||||||
|
- `ode_body_set_linear_vel(body, vx, vy, vz)`
|
||||||
|
- `ode_body_get_linear_vel(body, &vx, &vy, &vz)`
|
||||||
|
- `ode_body_set_rotation(body, w, x, y, z)` - Set quaternion rotation
|
||||||
|
- `ode_body_get_rotation(body, &w, &x, &y, &z)` - Get quaternion rotation
|
||||||
|
- `ode_body_set_box_mass(body, density, width, height, length)`
|
||||||
|
|
||||||
|
### Collision Shapes (Geometries)
|
||||||
|
- `ode_create_box_geom(space, width, height, length) -> *()` - Create box shape
|
||||||
|
- `ode_create_plane_geom(space, nx, ny, nz, d) -> *()` - Create plane shape
|
||||||
|
- `ode_geom_set_body(geom, body)` - Attach geometry to body
|
||||||
|
- `ode_geom_destroy(geom)`
|
||||||
|
|
||||||
|
### Collision Spaces
|
||||||
|
- `ode_simple_space_create(parent) -> *()` - Create simple space
|
||||||
|
- `ode_space_destroy(space)`
|
||||||
|
- `ode_space_collide(world, space, contactgroup)` - **Important**: Detect collisions
|
||||||
|
- `ode_joint_group_create(max_size) -> *()` - Create contact joint group
|
||||||
|
- `ode_joint_group_destroy(group)`
|
||||||
|
- `ode_joint_group_empty(group)` - **Important**: Clear contacts after step
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Adding Bounce/Restitution
|
||||||
|
|
||||||
|
Modify the contact properties in libsuicmez.c `near_callback()`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
contact[i].surface.bounce = 0.5; // 0 = no bounce, 1 = perfect bounce
|
||||||
|
contact[i].surface.bounce_vel = 0.1;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Applying Forces
|
||||||
|
|
||||||
|
```sui
|
||||||
|
# Set velocity directly
|
||||||
|
ode_body_set_linear_vel(body, 10.0, 0.0, 0.0)
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: ODE also supports force/torque application through lower-level APIs.
|
||||||
|
|
||||||
|
### Multiple Objects
|
||||||
|
|
||||||
|
Create arrays of bodies and simulate them in a loop:
|
||||||
|
|
||||||
|
```sui
|
||||||
|
let bodies_count = 5
|
||||||
|
# In main loop:
|
||||||
|
let mut i = 0
|
||||||
|
while i < bodies_count do
|
||||||
|
ode_body_get_position(bodies[i], &x, &y, &z)
|
||||||
|
draw_cube(x, y, z, 1.0, 1.0, 1.0, 255, 0, 0, 255)
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Topics
|
||||||
|
|
||||||
|
### Custom Contact Properties
|
||||||
|
|
||||||
|
Edit `suic_ode_set_contact_erp()` and `suic_ode_set_contact_cfm()` to tune physics:
|
||||||
|
|
||||||
|
- **ERP** (Error Reduction Parameter): 0-1, controls how fast constraint violations are fixed
|
||||||
|
- **CFM** (Constraint Force Mixing): Soft constraint parameter, adds damping
|
||||||
|
|
||||||
|
### Quaternion Rotations
|
||||||
|
|
||||||
|
ODE uses quaternions for 3D rotations. They're represented as (w, x, y, z):
|
||||||
|
|
||||||
|
```sui
|
||||||
|
# Identity rotation
|
||||||
|
ode_body_set_rotation(body, 1.0, 0.0, 0.0, 0.0)
|
||||||
|
|
||||||
|
# Get current rotation
|
||||||
|
let mut w = 0.0
|
||||||
|
let mut x = 0.0
|
||||||
|
let mut y = 0.0
|
||||||
|
let mut z = 0.0
|
||||||
|
ode_body_get_rotation(body, &w, &x, &y, &z)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Optimization
|
||||||
|
|
||||||
|
For large numbers of objects:
|
||||||
|
|
||||||
|
1. Use `dBVHSpaceCreate()` (not yet exposed) instead of simple space
|
||||||
|
2. Reduce collision pairs by using collision filtering
|
||||||
|
3. Use larger timesteps (but beware stability)
|
||||||
|
4. Implement sleeping/deactivation for static objects
|
||||||
|
|
||||||
|
## Example: Bouncing Ball Game
|
||||||
|
|
||||||
|
See `tests/game_3d.sui` for a complete working example of:
|
||||||
|
- Physics world setup
|
||||||
|
- Dynamic falling cube
|
||||||
|
- Ground plane
|
||||||
|
- Collision detection
|
||||||
|
- 3D rendering
|
||||||
|
|
||||||
|
Compile and run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run tests/game_3d.sui
|
||||||
|
gcc tests/game_3d.c libsuicmez/libsuicmez.c $(pkg-config --cflags --libs raylib ode) -o game
|
||||||
|
./game
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "Cube falls through ground"
|
||||||
|
- Ensure `ode_space_collide()` is called **before** `ode_world_step()`
|
||||||
|
- Ensure contact group is created with `ode_joint_group_create()`
|
||||||
|
- Call `ode_joint_group_empty()` after each step
|
||||||
|
|
||||||
|
### Jittery Physics
|
||||||
|
- Reduce timestep (smaller value in `ode_world_step()`)
|
||||||
|
- Increase ERP with `suic_ode_set_contact_erp()`
|
||||||
|
- Reduce CFM with `suic_ode_set_contact_cfm()`
|
||||||
|
|
||||||
|
### Objects moving too slow/fast
|
||||||
|
- Check gravity: `ode_world_set_gravity(world, 0.0, -9.81, 0.0)`
|
||||||
|
- Check body mass: `ode_body_set_box_mass(body, density, w, h, l)`
|
||||||
|
- Check initial velocity: `ode_body_set_linear_vel(body, vx, vy, vz)`
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
The Sui language is now ready for 3D game development! Future enhancements could include:
|
||||||
|
|
||||||
|
- Higher-level GameObject system wrapping physics + rendering
|
||||||
|
- Additional collision shapes (spheres, cylinders, meshes)
|
||||||
|
- Particle systems
|
||||||
|
- Audio integration
|
||||||
|
- Input handling improvements
|
||||||
|
- Scripting for game logic
|
||||||
|
|
||||||
|
Happy game developing! 🎮
|
||||||
88
README.md
Normal file
88
README.md
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
# ODE Physics Engine Integration
|
||||||
|
|
||||||
|
## ✅ ODE Integration Complete
|
||||||
|
|
||||||
|
Full Open Dynamics Engine (ODE) support has been integrated into suicmez for 3D physics simulation, perfect for voxel games!
|
||||||
|
|
||||||
|
## Available ODE Functions
|
||||||
|
|
||||||
|
### Initialization and Cleanup
|
||||||
|
- `ode_init()` - Initialize the ODE library
|
||||||
|
- `ode_close()` - Clean up ODE resources
|
||||||
|
|
||||||
|
### World Management
|
||||||
|
- `ode_world_create()` - Create a new physics world
|
||||||
|
- `ode_world_destroy(world)` - Destroy a physics world
|
||||||
|
- `ode_world_set_gravity(world, x, y, z)` - Set world gravity
|
||||||
|
- `ode_world_step(world, stepsize)` - Advance physics simulation
|
||||||
|
|
||||||
|
### Rigid Bodies
|
||||||
|
- `ode_body_create(world)` - Create a new rigid body
|
||||||
|
- `ode_body_destroy(body)` - Destroy a rigid body
|
||||||
|
- `ode_body_set_position(body, x, y, z)` - Set body position
|
||||||
|
- `ode_body_set_linear_vel(body, x, y, z)` - Set linear velocity
|
||||||
|
|
||||||
|
### Mass and Geometry
|
||||||
|
- `ode_body_set_box_mass(body, density, lx, ly, lz)` - Set box-shaped mass
|
||||||
|
- `ode_create_box_geom(space, lx, ly, lz)` - Create box collision geometry
|
||||||
|
- `ode_geom_set_body(geom, body)` - Attach geometry to body
|
||||||
|
- `ode_geom_destroy(geom)` - Destroy geometry
|
||||||
|
|
||||||
|
### Collision Spaces
|
||||||
|
- `ode_simple_space_create(parent)` - Create collision space
|
||||||
|
- `ode_space_destroy(space)` - Destroy collision space
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```sui
|
||||||
|
# Initialize physics
|
||||||
|
ode_init()
|
||||||
|
|
||||||
|
# Create world with gravity
|
||||||
|
let world = ode_world_create()
|
||||||
|
let space = ode_simple_space_create(0 as *()) # null parent
|
||||||
|
ode_world_set_gravity(world, 0.0, -9.81, 0.0)
|
||||||
|
|
||||||
|
# Create a falling voxel/box
|
||||||
|
let body = ode_body_create(world)
|
||||||
|
ode_body_set_box_mass(body, 1.0, 1.0, 1.0, 1.0) # 1x1x1 meter box
|
||||||
|
ode_body_set_position(body, 0.0, 5.0, 0.0) # Start 5 units up
|
||||||
|
|
||||||
|
let geom = ode_create_box_geom(space, 1.0, 1.0, 1.0)
|
||||||
|
ode_geom_set_body(geom, body)
|
||||||
|
|
||||||
|
# Simulate physics
|
||||||
|
let mut i = 0
|
||||||
|
while i < 100 do
|
||||||
|
ode_world_step(world, 0.016) # 60 FPS
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
ode_geom_destroy(geom)
|
||||||
|
ode_body_destroy(body)
|
||||||
|
ode_space_destroy(space)
|
||||||
|
ode_world_destroy(world)
|
||||||
|
ode_close()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compilation
|
||||||
|
|
||||||
|
Use the updated compile scripts which now include ODE:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./compile.sh tests/your_ode_program.sui
|
||||||
|
./compile_and_run.sh tests/your_ode_program.sui
|
||||||
|
```
|
||||||
|
|
||||||
|
## ODE Version
|
||||||
|
|
||||||
|
Currently using: **ODE 0.16.6**
|
||||||
|
|
||||||
|
- Real-time rigid body dynamics
|
||||||
|
- Collision detection
|
||||||
|
- Joint constraints
|
||||||
|
- Stable simulation for games
|
||||||
|
|
||||||
|
All ODE features are now available through suicmez for building physics-based voxel games!</content>
|
||||||
|
<parameter name="filePath">ODE_INTEGRATION.md
|
||||||
51
compile.sh
Executable file
51
compile.sh
Executable file
|
|
@ -0,0 +1,51 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Simple helper script to compile Sui code and link with libsuicmez
|
||||||
|
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
echo "Usage: ./compile.sh <sui_source.sui> [output_name]"
|
||||||
|
echo ""
|
||||||
|
echo "Example:"
|
||||||
|
echo " ./compile.sh tests/structs.sui"
|
||||||
|
echo " ./compile.sh tests/structs.sui my_program"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
INPUT_SUI="$1"
|
||||||
|
OUTPUT_NAME="${2:-${INPUT_NAME%.sui}}"
|
||||||
|
|
||||||
|
if [ ! -f "$INPUT_SUI" ]; then
|
||||||
|
echo "Error: File not found: $INPUT_SUI"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Compile Sui to C
|
||||||
|
echo "Compiling $INPUT_SUI to C..."
|
||||||
|
cargo run "$INPUT_SUI" || exit 1
|
||||||
|
|
||||||
|
# Get the C file name (should be next to the .sui file)
|
||||||
|
C_FILE="${INPUT_SUI%.sui}.c"
|
||||||
|
OUTPUT_BINARY="${INPUT_SUI%.sui}.o"
|
||||||
|
|
||||||
|
if [ -n "$2" ]; then
|
||||||
|
OUTPUT_BINARY="$2"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$C_FILE" ]; then
|
||||||
|
echo "Error: Generated C file not found: $C_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get raylib compile and link flags
|
||||||
|
RAYLIB_CFLAGS=$(pkg-config --cflags raylib 2>/dev/null || echo "-I/usr/include")
|
||||||
|
RAYLIB_LIBS=$(pkg-config --libs raylib 2>/dev/null || echo "-lraylib -lm")
|
||||||
|
|
||||||
|
# Get ODE compile and link flags
|
||||||
|
ODE_CFLAGS=$(pkg-config --cflags ode 2>/dev/null || echo "-I/usr/include")
|
||||||
|
ODE_LIBS=$(pkg-config --libs ode 2>/dev/null || echo "-lode -lm")
|
||||||
|
|
||||||
|
# Compile C to executable with raylib and ODE support
|
||||||
|
echo "Compiling C code and linking with libsuicmez, raylib, and ODE..."
|
||||||
|
gcc "$C_FILE" libsuicmez/libsuicmez.c $RAYLIB_CFLAGS $RAYLIB_LIBS $ODE_CFLAGS $ODE_LIBS -o "$OUTPUT_BINARY" || exit 1
|
||||||
|
|
||||||
|
echo "✓ Successfully created: $OUTPUT_BINARY"
|
||||||
|
echo " Run with: ./$OUTPUT_BINARY"
|
||||||
84
compile_and_run.sh
Executable file
84
compile_and_run.sh
Executable file
|
|
@ -0,0 +1,84 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Compile Sui source to C, then compile and run the executable
|
||||||
|
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
echo "Usage: ./compile_and_run.sh <sui_source.sui> [program_args...]"
|
||||||
|
echo ""
|
||||||
|
echo "Examples:"
|
||||||
|
echo " ./compile_and_run.sh tests/structs.sui"
|
||||||
|
echo " ./compile_and_run.sh tests/myprogram.sui arg1 arg2"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
INPUT_SUI="$1"
|
||||||
|
shift # Remove first argument, keep the rest as program args
|
||||||
|
|
||||||
|
if [ ! -f "$INPUT_SUI" ]; then
|
||||||
|
echo "Error: File not found: $INPUT_SUI"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine output names
|
||||||
|
C_FILE="${INPUT_SUI%.sui}.c"
|
||||||
|
# Extract just the filename without extension for the temp binary
|
||||||
|
BINARY_NAME=$(basename "${INPUT_SUI%.sui}")
|
||||||
|
OUTPUT_BINARY="/tmp/suic_$BINARY_NAME"
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Step 1: Compiling Sui to C..."
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
if ! cargo run "$INPUT_SUI" > /dev/null 2>&1; then
|
||||||
|
echo ""
|
||||||
|
echo "✗ Sui compilation failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$C_FILE" ]; then
|
||||||
|
echo "✗ Error: Generated C file not found: $C_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✓ C file generated: $C_FILE"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Step 2: Compiling C to executable..."
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
# Get raylib compile and link flags
|
||||||
|
RAYLIB_CFLAGS=$(pkg-config --cflags raylib 2>/dev/null || echo "-I/usr/include")
|
||||||
|
RAYLIB_LIBS=$(pkg-config --libs raylib 2>/dev/null || echo "-lraylib -lm")
|
||||||
|
|
||||||
|
# Get ODE compile and link flags
|
||||||
|
ODE_CFLAGS=$(pkg-config --cflags ode 2>/dev/null || echo "-I/usr/include")
|
||||||
|
ODE_LIBS=$(pkg-config --libs ode 2>/dev/null || echo "-lode -lm")
|
||||||
|
|
||||||
|
if ! gcc "$C_FILE" libsuicmez/libsuicmez.c $RAYLIB_CFLAGS $RAYLIB_LIBS $ODE_CFLAGS $ODE_LIBS -o "$OUTPUT_BINARY" 2>&1; then
|
||||||
|
echo ""
|
||||||
|
echo "✗ C compilation failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✓ Executable created: $OUTPUT_BINARY"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Step 3: Running program..."
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Run with any additional arguments passed to this script
|
||||||
|
"$OUTPUT_BINARY" "$@"
|
||||||
|
EXIT_CODE=$?
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
if [ $EXIT_CODE -eq 0 ]; then
|
||||||
|
echo "✓ Program completed successfully"
|
||||||
|
else
|
||||||
|
echo "✗ Program exited with code: $EXIT_CODE"
|
||||||
|
fi
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
exit $EXIT_CODE
|
||||||
617
libsuicmez/libsuicmez.c
Normal file
617
libsuicmez/libsuicmez.c
Normal file
|
|
@ -0,0 +1,617 @@
|
||||||
|
#include "raylib.h"
|
||||||
|
#include "libsuicmez.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// Simple garbage collection allocator
|
||||||
|
// This is a basic implementation that tracks allocations
|
||||||
|
// For production, consider using a more robust GC library
|
||||||
|
|
||||||
|
#define MAX_TRACKED_ALLOCATIONS 10000
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void* ptr;
|
||||||
|
size_t size;
|
||||||
|
int in_use;
|
||||||
|
} AllocationInfo;
|
||||||
|
|
||||||
|
static AllocationInfo tracked_allocations[MAX_TRACKED_ALLOCATIONS];
|
||||||
|
static int allocation_count = 0;
|
||||||
|
static const char* last_error = NULL;
|
||||||
|
|
||||||
|
// Simple linear search to find allocation
|
||||||
|
static int find_allocation(void* ptr) {
|
||||||
|
for (int i = 0; i < allocation_count; i++) {
|
||||||
|
if (tracked_allocations[i].ptr == ptr && tracked_allocations[i].in_use) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GC-aware allocator
|
||||||
|
void* gc_suic_alloc(size_t size) {
|
||||||
|
if (size == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ptr = malloc(size);
|
||||||
|
if (!ptr) {
|
||||||
|
last_error = "Memory allocation failed";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track this allocation
|
||||||
|
if (allocation_count < MAX_TRACKED_ALLOCATIONS) {
|
||||||
|
tracked_allocations[allocation_count].ptr = ptr;
|
||||||
|
tracked_allocations[allocation_count].size = size;
|
||||||
|
tracked_allocations[allocation_count].in_use = 1;
|
||||||
|
allocation_count++;
|
||||||
|
} else {
|
||||||
|
last_error = "Too many tracked allocations";
|
||||||
|
free(ptr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free memory (called by gc_suic_free)
|
||||||
|
void suic_gc_free(void* ptr) {
|
||||||
|
if (!ptr) return;
|
||||||
|
|
||||||
|
int idx = find_allocation(ptr);
|
||||||
|
if (idx >= 0) {
|
||||||
|
tracked_allocations[idx].in_use = 0;
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error handling
|
||||||
|
const char *suic_last_error(void) {
|
||||||
|
return last_error ? last_error : "No error";
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_clear_error(void) {
|
||||||
|
last_error = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logging
|
||||||
|
static suic_log_level current_log_level = SUIC_LOG_ALL;
|
||||||
|
|
||||||
|
void suic_set_log_level(suic_log_level level) {
|
||||||
|
current_log_level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Window management
|
||||||
|
bool suic_init_window(int width, int height, const char *title) {
|
||||||
|
InitWindow(width, height, title);
|
||||||
|
return !WindowShouldClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_close_window(void) {
|
||||||
|
CloseWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool suic_window_should_close(void) {
|
||||||
|
return WindowShouldClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_set_target_fps(int fps) {
|
||||||
|
SetTargetFPS(fps);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drawing
|
||||||
|
void suic_begin_drawing(void) {
|
||||||
|
BeginDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_end_drawing(void) {
|
||||||
|
EndDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_clear_background(unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
|
||||||
|
ClearBackground((Color){r, g, b, a});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3D mode
|
||||||
|
Camera3D suic_to_rl_camera3d(suic_camera3d cam) {
|
||||||
|
return (Camera3D){
|
||||||
|
.position = suic_to_rl_vec3(cam.position),
|
||||||
|
.target = suic_to_rl_vec3(cam.target),
|
||||||
|
.up = suic_to_rl_vec3(cam.up),
|
||||||
|
.fovy = cam.fovy,
|
||||||
|
.projection = cam.projection
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_begin_mode3d(float pos_x, float pos_y, float pos_z, float target_x, float target_y, float target_z, float up_x, float up_y, float up_z, float fovy, int projection) {
|
||||||
|
Camera3D camera = {
|
||||||
|
.position = (Vector3){pos_x, pos_y, pos_z},
|
||||||
|
.target = (Vector3){target_x, target_y, target_z},
|
||||||
|
.up = (Vector3){up_x, up_y, up_z},
|
||||||
|
.fovy = fovy,
|
||||||
|
.projection = projection
|
||||||
|
};
|
||||||
|
BeginMode3D(camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_end_mode3d(void) {
|
||||||
|
EndMode3D();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3D Drawing
|
||||||
|
void suic_draw_cube(float x, float y, float z, float width, float height, float length, unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
|
||||||
|
DrawCube((Vector3){x, y, z}, width, height, length, (Color){r, g, b, a});
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_draw_cube_wires(float x, float y, float z, float width, float height, float length, unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
|
||||||
|
DrawCubeWires((Vector3){x, y, z}, width, height, length, (Color){r, g, b, a});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input
|
||||||
|
bool suic_is_key_down(int key) {
|
||||||
|
return IsKeyDown(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool suic_is_mouse_button_down(int b) {
|
||||||
|
return IsMouseButtonDown(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
suic_vec2 suic_get_mouse_position(void) {
|
||||||
|
Vector2 pos = GetMousePosition();
|
||||||
|
return (suic_vec2){pos.x, pos.y};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image and Texture
|
||||||
|
suic_image_handle suic_image_load(const char *path) {
|
||||||
|
Image img = LoadImage(path);
|
||||||
|
return (suic_image_handle){img, (img.data != NULL) ? true : false};
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_image_free(suic_image_handle *img) {
|
||||||
|
if (img && img->valid) {
|
||||||
|
UnloadImage(img->value);
|
||||||
|
img->valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suic_texture_handle suic_texture_load(const char *path) {
|
||||||
|
Texture2D tex = LoadTexture(path);
|
||||||
|
return (suic_texture_handle){tex, (tex.id != 0) ? true : false};
|
||||||
|
}
|
||||||
|
|
||||||
|
suic_texture_handle suic_texture_from_image(suic_image_handle img) {
|
||||||
|
if (!img.valid) {
|
||||||
|
return (suic_texture_handle){0, false};
|
||||||
|
}
|
||||||
|
Texture2D tex = LoadTextureFromImage(img.value);
|
||||||
|
return (suic_texture_handle){tex, (tex.id != 0) ? true : false};
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_texture_free(suic_texture_handle *tex) {
|
||||||
|
if (tex && tex->valid) {
|
||||||
|
UnloadTexture(tex->value);
|
||||||
|
tex->valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_draw_texture(suic_texture_handle tex, int x, int y, unsigned char r,
|
||||||
|
unsigned char g, unsigned char b, unsigned char a) {
|
||||||
|
if (tex.valid) {
|
||||||
|
DrawTexture(tex.value, x, y, (Color){r, g, b, a});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audio
|
||||||
|
bool suic_audio_init(void) {
|
||||||
|
InitAudioDevice();
|
||||||
|
return IsAudioDeviceReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_audio_close(void) {
|
||||||
|
CloseAudioDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
suic_sound_handle suic_sound_load(const char *path) {
|
||||||
|
Sound snd = LoadSound(path);
|
||||||
|
return (suic_sound_handle){snd, (snd.frameCount > 0) ? true : false};
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_sound_play(suic_sound_handle snd) {
|
||||||
|
if (snd.valid) {
|
||||||
|
PlaySound(snd.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_sound_set_volume(suic_sound_handle snd, float volume) {
|
||||||
|
if (snd.valid) {
|
||||||
|
SetSoundVolume(snd.value, volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_sound_free(suic_sound_handle *snd) {
|
||||||
|
if (snd && snd->valid) {
|
||||||
|
UnloadSound(snd->value);
|
||||||
|
snd->valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raycasting
|
||||||
|
suic_ray suic_get_mouse_ray(suic_vec2 mouse, suic_camera3d cam) {
|
||||||
|
Ray r = GetMouseRay(
|
||||||
|
(Vector2){mouse.x, mouse.y},
|
||||||
|
suic_to_rl_camera3d(cam)
|
||||||
|
);
|
||||||
|
return (suic_ray){
|
||||||
|
.origin = (suic_vec3){r.position.x, r.position.y, r.position.z},
|
||||||
|
.direction = (suic_vec3){r.direction.x, r.direction.y, r.direction.z}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
suic_rayhit suic_raycast_aabb(suic_ray ray, suic_aabb box) {
|
||||||
|
Ray r = (Ray){
|
||||||
|
.position = (Vector3){ray.origin.x, ray.origin.y, ray.origin.z},
|
||||||
|
.direction = (Vector3){ray.direction.x, ray.direction.y, ray.direction.z}
|
||||||
|
};
|
||||||
|
|
||||||
|
BoundingBox bbox = (BoundingBox){
|
||||||
|
.min = (Vector3){box.min.x, box.min.y, box.min.z},
|
||||||
|
.max = (Vector3){box.max.x, box.max.y, box.max.z}
|
||||||
|
};
|
||||||
|
|
||||||
|
RayCollision collision = GetRayCollisionBox(r, bbox);
|
||||||
|
|
||||||
|
return (suic_rayhit){
|
||||||
|
.hit = collision.hit,
|
||||||
|
.distance = collision.distance,
|
||||||
|
.point = (suic_vec3){collision.point.x, collision.point.y, collision.point.z},
|
||||||
|
.normal = (suic_vec3){collision.normal.x, collision.normal.y, collision.normal.z}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ODE Physics Engine Implementation
|
||||||
|
static int ode_initialized = 0;
|
||||||
|
|
||||||
|
void suic_ode_init(void) {
|
||||||
|
if (!ode_initialized) {
|
||||||
|
dInitODE();
|
||||||
|
ode_initialized = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_close(void) {
|
||||||
|
if (ode_initialized) {
|
||||||
|
dCloseODE();
|
||||||
|
ode_initialized = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suic_ode_world_id suic_ode_world_create(void) {
|
||||||
|
return dWorldCreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_world_destroy(suic_ode_world_id world) {
|
||||||
|
if (world) {
|
||||||
|
dWorldDestroy(world);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_world_set_gravity(suic_ode_world_id world, dReal x, dReal y, dReal z) {
|
||||||
|
if (world) {
|
||||||
|
dWorldSetGravity(world, x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_world_step(suic_ode_world_id world, dReal stepsize) {
|
||||||
|
if (world) {
|
||||||
|
dWorldStep(world, stepsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Body management
|
||||||
|
suic_ode_body_id suic_ode_body_create(suic_ode_world_id world) {
|
||||||
|
return dBodyCreate(world);
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_body_destroy(suic_ode_body_id body) {
|
||||||
|
if (body) {
|
||||||
|
dBodyDestroy(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_body_set_position(suic_ode_body_id body, dReal x, dReal y, dReal z) {
|
||||||
|
if (body) {
|
||||||
|
dBodySetPosition(body, x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_body_set_linear_vel(suic_ode_body_id body, dReal x, dReal y, dReal z) {
|
||||||
|
if (body) {
|
||||||
|
dBodySetLinearVel(body, x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_body_get_position(suic_ode_body_id body, float *x, float *y, float *z) {
|
||||||
|
if (body) {
|
||||||
|
const dReal *pos = dBodyGetPosition(body);
|
||||||
|
*x = (float)pos[0];
|
||||||
|
*y = (float)pos[1];
|
||||||
|
*z = (float)pos[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mass and geometry
|
||||||
|
void suic_ode_body_set_box_mass(suic_ode_body_id body, dReal density, dReal lx, dReal ly, dReal lz) {
|
||||||
|
if (body) {
|
||||||
|
dMass mass;
|
||||||
|
dMassSetBox(&mass, density, lx, ly, lz);
|
||||||
|
dBodySetMass(body, &mass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suic_ode_geom_id suic_ode_create_box_geom(suic_ode_space_id space, dReal lx, dReal ly, dReal lz) {
|
||||||
|
return dCreateBox(space, lx, ly, lz);
|
||||||
|
}
|
||||||
|
|
||||||
|
suic_ode_geom_id suic_ode_create_plane_geom(suic_ode_space_id space, dReal a, dReal b, dReal c, dReal d) {
|
||||||
|
return dCreatePlane(space, a, b, c, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_geom_set_body(suic_ode_geom_id geom, suic_ode_body_id body) {
|
||||||
|
if (geom) {
|
||||||
|
dGeomSetBody(geom, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_geom_destroy(suic_ode_geom_id geom) {
|
||||||
|
if (geom) {
|
||||||
|
dGeomDestroy(geom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collision space
|
||||||
|
suic_ode_space_id suic_ode_simple_space_create(suic_ode_space_id parent) {
|
||||||
|
return dSimpleSpaceCreate(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_space_destroy(suic_ode_space_id space) {
|
||||||
|
if (space) {
|
||||||
|
dSpaceDestroy(space);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// COLLISION DETECTION AND CONTACT JOINTS
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Global contact configuration
|
||||||
|
static dReal contact_max_force = 10000.0;
|
||||||
|
static dReal contact_erp = 0.8; // Error reduction parameter
|
||||||
|
static dReal contact_cfm = 0.00001; // Constraint force mixing
|
||||||
|
|
||||||
|
// Store world and contactgroup for collision callback
|
||||||
|
typedef struct {
|
||||||
|
dWorldID world;
|
||||||
|
dJointGroupID contactgroup;
|
||||||
|
} CollisionContext;
|
||||||
|
|
||||||
|
// Collision callback for detecting contacts between geometries
|
||||||
|
static void near_callback(void *data, dGeomID o1, dGeomID o2) {
|
||||||
|
CollisionContext *ctx = (CollisionContext*)data;
|
||||||
|
dBodyID b1 = dGeomGetBody(o1);
|
||||||
|
dBodyID b2 = dGeomGetBody(o2);
|
||||||
|
|
||||||
|
// Don't process if both bodies are static or same body
|
||||||
|
if ((b1 && b2 && b1 == b2) || (!b1 && !b2)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int N = 4; // Max contacts per collision
|
||||||
|
dContact contact[N];
|
||||||
|
|
||||||
|
// Check for collisions
|
||||||
|
int n = dCollide(o1, o2, N, &contact[0].geom, sizeof(dContact));
|
||||||
|
if (n > 0) {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
// Set contact properties
|
||||||
|
contact[i].surface.mu = 0.5; // Friction coefficient
|
||||||
|
contact[i].surface.mu2 = 0.5;
|
||||||
|
contact[i].surface.bounce = 0.1; // Bounciness
|
||||||
|
contact[i].surface.bounce_vel = 0.1;
|
||||||
|
contact[i].surface.mode = dContactBounce | dContactSoftERP | dContactSoftCFM;
|
||||||
|
contact[i].surface.soft_erp = contact_erp;
|
||||||
|
contact[i].surface.soft_cfm = contact_cfm;
|
||||||
|
|
||||||
|
// Create contact joint
|
||||||
|
dJointID c = dJointCreateContact(ctx->world, ctx->contactgroup, &contact[i]);
|
||||||
|
dJointAttach(c, b1, b2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_space_collide(suic_ode_world_id world, suic_ode_space_id space, suic_ode_joint_group_id contactgroup) {
|
||||||
|
if (space && contactgroup && world) {
|
||||||
|
CollisionContext ctx = {.world = world, .contactgroup = contactgroup};
|
||||||
|
dSpaceCollide(space, (void*)&ctx, &near_callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suic_ode_joint_group_id suic_ode_joint_group_create(int max_size) {
|
||||||
|
return dJointGroupCreate(max_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_joint_group_destroy(suic_ode_joint_group_id group) {
|
||||||
|
if (group) {
|
||||||
|
dJointGroupDestroy(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_joint_group_empty(suic_ode_joint_group_id group) {
|
||||||
|
if (group) {
|
||||||
|
dJointGroupEmpty(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_set_contact_max_force(dReal force) {
|
||||||
|
contact_max_force = force;
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_set_contact_erp(dReal erp) {
|
||||||
|
contact_erp = erp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_set_contact_cfm(dReal cfm) {
|
||||||
|
contact_cfm = cfm;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// ADDITIONAL BODY FUNCTIONS
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
void suic_ode_body_get_linear_vel(suic_ode_body_id body, float *x, float *y, float *z) {
|
||||||
|
if (body) {
|
||||||
|
const dReal *vel = dBodyGetLinearVel(body);
|
||||||
|
*x = (float)vel[0];
|
||||||
|
*y = (float)vel[1];
|
||||||
|
*z = (float)vel[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_body_get_rotation(suic_ode_body_id body, float *q_w, float *q_x, float *q_y, float *q_z) {
|
||||||
|
if (body) {
|
||||||
|
const dReal *q = dBodyGetQuaternion(body);
|
||||||
|
*q_w = (float)q[0];
|
||||||
|
*q_x = (float)q[1];
|
||||||
|
*q_y = (float)q[2];
|
||||||
|
*q_z = (float)q[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_ode_body_set_rotation(suic_ode_body_id body, dReal q_w, dReal q_x, dReal q_y, dReal q_z) {
|
||||||
|
if (body) {
|
||||||
|
dQuaternion q;
|
||||||
|
q[0] = q_w;
|
||||||
|
q[1] = q_x;
|
||||||
|
q[2] = q_y;
|
||||||
|
q[3] = q_z;
|
||||||
|
dBodySetQuaternion(body, q);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// GAME OBJECT SYSTEM - Unified Physics & Rendering
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
suic_game_object* suic_game_object_create_box(
|
||||||
|
suic_ode_world_id world,
|
||||||
|
suic_ode_space_id space,
|
||||||
|
float x, float y, float z,
|
||||||
|
float width, float height, float length,
|
||||||
|
float density,
|
||||||
|
unsigned char r, unsigned char g, unsigned char b, unsigned char a
|
||||||
|
) {
|
||||||
|
suic_game_object* obj = (suic_game_object*)gc_suic_alloc(sizeof(suic_game_object));
|
||||||
|
if (!obj) return NULL;
|
||||||
|
|
||||||
|
// Create physics body
|
||||||
|
obj->body = dBodyCreate(world);
|
||||||
|
if (!obj->body) {
|
||||||
|
suic_gc_free(obj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set position
|
||||||
|
dBodySetPosition(obj->body, x, y, z);
|
||||||
|
|
||||||
|
// Create and attach geometry
|
||||||
|
obj->geom = dCreateBox(space, width, height, length);
|
||||||
|
if (!obj->geom) {
|
||||||
|
dBodyDestroy(obj->body);
|
||||||
|
suic_gc_free(obj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dGeomSetBody(obj->geom, obj->body);
|
||||||
|
|
||||||
|
// Set mass
|
||||||
|
dMass mass;
|
||||||
|
dMassSetBox(&mass, density, width, height, length);
|
||||||
|
dBodySetMass(obj->body, &mass);
|
||||||
|
|
||||||
|
// Store rendering data
|
||||||
|
obj->width = width;
|
||||||
|
obj->height = height;
|
||||||
|
obj->length = length;
|
||||||
|
obj->r = r;
|
||||||
|
obj->g = g;
|
||||||
|
obj->b = b;
|
||||||
|
obj->a = a;
|
||||||
|
obj->shape_type = 0; // 0 = box
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_game_object_destroy(suic_game_object* obj) {
|
||||||
|
if (obj) {
|
||||||
|
if (obj->geom) {
|
||||||
|
dGeomDestroy(obj->geom);
|
||||||
|
}
|
||||||
|
if (obj->body) {
|
||||||
|
dBodyDestroy(obj->body);
|
||||||
|
}
|
||||||
|
suic_gc_free(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_game_object_set_position(suic_game_object* obj, float x, float y, float z) {
|
||||||
|
if (obj && obj->body) {
|
||||||
|
dBodySetPosition(obj->body, x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_game_object_get_position(suic_game_object* obj, float *x, float *y, float *z) {
|
||||||
|
if (obj && obj->body) {
|
||||||
|
const dReal *pos = dBodyGetPosition(obj->body);
|
||||||
|
*x = (float)pos[0];
|
||||||
|
*y = (float)pos[1];
|
||||||
|
*z = (float)pos[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_game_object_set_velocity(suic_game_object* obj, float x, float y, float z) {
|
||||||
|
if (obj && obj->body) {
|
||||||
|
dBodySetLinearVel(obj->body, x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_game_object_get_velocity(suic_game_object* obj, float *x, float *y, float *z) {
|
||||||
|
if (obj && obj->body) {
|
||||||
|
const dReal *vel = dBodyGetLinearVel(obj->body);
|
||||||
|
*x = (float)vel[0];
|
||||||
|
*y = (float)vel[1];
|
||||||
|
*z = (float)vel[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suic_game_object_draw(suic_game_object* obj) {
|
||||||
|
if (obj && obj->body) {
|
||||||
|
float x, y, z;
|
||||||
|
suic_game_object_get_position(obj, &x, &y, &z);
|
||||||
|
|
||||||
|
if (obj->shape_type == 0) { // Box
|
||||||
|
DrawCube(
|
||||||
|
(Vector3){x, y, z},
|
||||||
|
obj->width, obj->height, obj->length,
|
||||||
|
(Color){obj->r, obj->g, obj->b, obj->a}
|
||||||
|
);
|
||||||
|
// Draw wireframe for visual clarity
|
||||||
|
DrawCubeWires(
|
||||||
|
(Vector3){x, y, z},
|
||||||
|
obj->width, obj->height, obj->length,
|
||||||
|
(Color){0, 0, 0, 200}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,10 +3,16 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
// Forward include raylib (or include it in the .c and keep only forward decls)
|
// Raylib support
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
|
|
||||||
|
// ODE support
|
||||||
|
#include <ode/ode.h>
|
||||||
|
|
||||||
|
// Logging levels
|
||||||
typedef enum suic_log_level {
|
typedef enum suic_log_level {
|
||||||
SUIC_LOG_ALL = 0,
|
SUIC_LOG_ALL = 0,
|
||||||
SUIC_LOG_INFO = 1,
|
SUIC_LOG_INFO = 1,
|
||||||
|
|
@ -15,11 +21,12 @@ typedef enum suic_log_level {
|
||||||
SUIC_LOG_NONE = 4
|
SUIC_LOG_NONE = 4
|
||||||
} suic_log_level;
|
} suic_log_level;
|
||||||
|
|
||||||
|
// Error handling
|
||||||
const char *suic_last_error(void);
|
const char *suic_last_error(void);
|
||||||
void suic_clear_error(void);
|
void suic_clear_error(void);
|
||||||
|
|
||||||
void suic_set_log_level(suic_log_level level);
|
void suic_set_log_level(suic_log_level level);
|
||||||
|
|
||||||
|
// Vec types
|
||||||
typedef struct suic_vec2 {
|
typedef struct suic_vec2 {
|
||||||
float x, y;
|
float x, y;
|
||||||
} suic_vec2;
|
} suic_vec2;
|
||||||
|
|
@ -28,54 +35,62 @@ typedef struct suic_vec3 {
|
||||||
float x, y, z;
|
float x, y, z;
|
||||||
} suic_vec3;
|
} suic_vec3;
|
||||||
|
|
||||||
|
// Vector conversion helpers
|
||||||
static inline Vector2 suic_to_rl_vec2(suic_vec2 v) {
|
static inline Vector2 suic_to_rl_vec2(suic_vec2 v) {
|
||||||
return (Vector2){v.x, v.y};
|
return (Vector2){v.x, v.y};
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline Vector3 suic_to_rl_vec3(suic_vec3 v) {
|
static inline Vector3 suic_to_rl_vec3(suic_vec3 v) {
|
||||||
return (Vector3){v.x, v.y, v.z};
|
return (Vector3){v.x, v.y, v.z};
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline suic_vec2 suic_from_rl_vec2(Vector2 v) {
|
static inline suic_vec2 suic_from_rl_vec2(Vector2 v) {
|
||||||
return (suic_vec2){v.x, v.y};
|
return (suic_vec2){v.x, v.y};
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline suic_vec3 suic_from_rl_vec3(Vector3 v) {
|
static inline suic_vec3 suic_from_rl_vec3(Vector3 v) {
|
||||||
return (suic_vec3){v.x, v.y, v.z};
|
return (suic_vec3){v.x, v.y, v.z};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be called before any GPU resources (textures, models) are created.
|
// Window management
|
||||||
bool suic_init_window(int width, int height, const char *title);
|
bool suic_init_window(int width, int height, const char *title);
|
||||||
void suic_close_window(void);
|
void suic_close_window(void);
|
||||||
|
|
||||||
bool suic_window_should_close(void);
|
bool suic_window_should_close(void);
|
||||||
void suic_set_target_fps(int fps);
|
void suic_set_target_fps(int fps);
|
||||||
|
|
||||||
// Frame boundaries (2D)
|
// Drawing (2D)
|
||||||
void suic_begin_drawing(void);
|
void suic_begin_drawing(void);
|
||||||
void suic_end_drawing(void);
|
void suic_end_drawing(void);
|
||||||
void suic_clear_background(unsigned char r, unsigned char g, unsigned char b,
|
void suic_clear_background(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
|
||||||
unsigned char a);
|
|
||||||
|
|
||||||
// 3D mode boundaries
|
// 3D Camera
|
||||||
typedef struct suic_camera3d {
|
typedef struct suic_camera3d {
|
||||||
suic_vec3 position;
|
suic_vec3 position;
|
||||||
suic_vec3 target;
|
suic_vec3 target;
|
||||||
suic_vec3 up;
|
suic_vec3 up;
|
||||||
float fovy;
|
float fovy;
|
||||||
int projection; // map to CameraProjection
|
int projection; // CameraProjection
|
||||||
} suic_camera3d;
|
} suic_camera3d;
|
||||||
|
|
||||||
Camera3D suic_to_rl_camera3d(suic_camera3d cam);
|
Camera3D suic_to_rl_camera3d(suic_camera3d cam);
|
||||||
|
void suic_begin_mode3d(float pos_x, float pos_y, float pos_z, float target_x, float target_y, float target_z, float up_x, float up_y, float up_z, float fovy, int projection);
|
||||||
void suic_begin_mode3d(suic_camera3d cam);
|
|
||||||
void suic_end_mode3d(void);
|
void suic_end_mode3d(void);
|
||||||
|
|
||||||
|
// 3D Drawing
|
||||||
|
void suic_draw_cube(float x, float y, float z, float width, float height, float length, unsigned char r, unsigned char g, unsigned char b, unsigned char a);
|
||||||
|
void suic_draw_cube_wires(float x, float y, float z, float width, float height, float length, unsigned char r, unsigned char g, unsigned char b, unsigned char a);
|
||||||
|
|
||||||
|
// Input
|
||||||
bool suic_is_key_down(int key);
|
bool suic_is_key_down(int key);
|
||||||
bool suic_is_mouse_button_down(int b);
|
bool suic_is_mouse_button_down(int b);
|
||||||
suic_vec2 suic_get_mouse_position(void);
|
suic_vec2 suic_get_mouse_position(void);
|
||||||
|
|
||||||
|
// Images and Textures
|
||||||
typedef struct suic_image_handle {
|
typedef struct suic_image_handle {
|
||||||
Image value;
|
Image value;
|
||||||
bool valid;
|
bool valid;
|
||||||
} suic_image_handle;
|
} suic_image_handle;
|
||||||
|
|
||||||
typedef struct suic_texture_handle {
|
typedef struct suic_texture_handle {
|
||||||
Texture2D value;
|
Texture2D value;
|
||||||
bool valid;
|
bool valid;
|
||||||
|
|
@ -87,13 +102,12 @@ void suic_image_free(suic_image_handle *img);
|
||||||
suic_texture_handle suic_texture_load(const char *path);
|
suic_texture_handle suic_texture_load(const char *path);
|
||||||
suic_texture_handle suic_texture_from_image(suic_image_handle img);
|
suic_texture_handle suic_texture_from_image(suic_image_handle img);
|
||||||
void suic_texture_free(suic_texture_handle *tex);
|
void suic_texture_free(suic_texture_handle *tex);
|
||||||
|
|
||||||
// Drawing a texture (basic)
|
|
||||||
void suic_draw_texture(suic_texture_handle tex, int x, int y, unsigned char r,
|
void suic_draw_texture(suic_texture_handle tex, int x, int y, unsigned char r,
|
||||||
unsigned char g, unsigned char b, unsigned char a);
|
unsigned char g, unsigned char b, unsigned char a);
|
||||||
|
|
||||||
bool suic_audio_init(void); // wraps InitAudioDevice
|
// Audio
|
||||||
void suic_audio_close(void); // wraps CloseAudioDevice
|
bool suic_audio_init(void);
|
||||||
|
void suic_audio_close(void);
|
||||||
|
|
||||||
typedef struct suic_sound_handle {
|
typedef struct suic_sound_handle {
|
||||||
Sound value;
|
Sound value;
|
||||||
|
|
@ -105,6 +119,7 @@ void suic_sound_play(suic_sound_handle snd);
|
||||||
void suic_sound_set_volume(suic_sound_handle snd, float volume);
|
void suic_sound_set_volume(suic_sound_handle snd, float volume);
|
||||||
void suic_sound_free(suic_sound_handle *snd);
|
void suic_sound_free(suic_sound_handle *snd);
|
||||||
|
|
||||||
|
// Raycasting
|
||||||
typedef struct suic_ray {
|
typedef struct suic_ray {
|
||||||
suic_vec3 origin;
|
suic_vec3 origin;
|
||||||
suic_vec3 direction;
|
suic_vec3 direction;
|
||||||
|
|
@ -117,14 +132,102 @@ typedef struct suic_rayhit {
|
||||||
suic_vec3 normal;
|
suic_vec3 normal;
|
||||||
} suic_rayhit;
|
} suic_rayhit;
|
||||||
|
|
||||||
// Ray from camera + mouse
|
|
||||||
suic_ray suic_get_mouse_ray(suic_vec2 mouse, suic_camera3d cam);
|
|
||||||
|
|
||||||
// AABB ray test (for chunk culling / picking helpers)
|
|
||||||
typedef struct suic_aabb {
|
typedef struct suic_aabb {
|
||||||
suic_vec3 min;
|
suic_vec3 min;
|
||||||
suic_vec3 max;
|
suic_vec3 max;
|
||||||
} suic_aabb;
|
} suic_aabb;
|
||||||
|
|
||||||
|
suic_ray suic_get_mouse_ray(suic_vec2 mouse, suic_camera3d cam);
|
||||||
suic_rayhit suic_raycast_aabb(suic_ray ray, suic_aabb box);
|
suic_rayhit suic_raycast_aabb(suic_ray ray, suic_aabb box);
|
||||||
|
|
||||||
|
// GC-aware allocator (core functionality)
|
||||||
|
void* gc_suic_alloc(size_t size);
|
||||||
|
void suic_gc_free(void* ptr);
|
||||||
|
|
||||||
|
// ODE Physics Engine Support
|
||||||
|
// ODE types (opaque pointers)
|
||||||
|
typedef dWorldID suic_ode_world_id;
|
||||||
|
typedef dSpaceID suic_ode_space_id;
|
||||||
|
typedef dBodyID suic_ode_body_id;
|
||||||
|
typedef dGeomID suic_ode_geom_id;
|
||||||
|
typedef dJointID suic_ode_joint_id;
|
||||||
|
typedef dJointGroupID suic_ode_joint_group_id;
|
||||||
|
|
||||||
|
// Vector types for ODE (matching ODE's dReal which is double)
|
||||||
|
typedef struct suic_ode_vector3 {
|
||||||
|
dReal x, y, z;
|
||||||
|
} suic_ode_vector3;
|
||||||
|
|
||||||
|
typedef struct suic_ode_quaternion {
|
||||||
|
dReal w, x, y, z; // ODE quaternion format: w,x,y,z
|
||||||
|
} suic_ode_quaternion;
|
||||||
|
|
||||||
|
// Basic ODE functions
|
||||||
|
void suic_ode_init(void);
|
||||||
|
void suic_ode_close(void);
|
||||||
|
suic_ode_world_id suic_ode_world_create(void);
|
||||||
|
void suic_ode_world_destroy(suic_ode_world_id world);
|
||||||
|
void suic_ode_world_set_gravity(suic_ode_world_id world, dReal x, dReal y, dReal z);
|
||||||
|
void suic_ode_world_step(suic_ode_world_id world, dReal stepsize);
|
||||||
|
|
||||||
|
// Body management
|
||||||
|
suic_ode_body_id suic_ode_body_create(suic_ode_world_id world);
|
||||||
|
void suic_ode_body_destroy(suic_ode_body_id body);
|
||||||
|
void suic_ode_body_set_position(suic_ode_body_id body, dReal x, dReal y, dReal z);
|
||||||
|
void suic_ode_body_set_linear_vel(suic_ode_body_id body, dReal x, dReal y, dReal z);
|
||||||
|
void suic_ode_body_get_position(suic_ode_body_id body, float *x, float *y, float *z);
|
||||||
|
|
||||||
|
// Mass and geometry
|
||||||
|
void suic_ode_body_set_box_mass(suic_ode_body_id body, dReal density, dReal lx, dReal ly, dReal lz);
|
||||||
|
suic_ode_geom_id suic_ode_create_box_geom(suic_ode_space_id space, dReal lx, dReal ly, dReal lz);
|
||||||
|
suic_ode_geom_id suic_ode_create_plane_geom(suic_ode_space_id space, dReal a, dReal b, dReal c, dReal d);
|
||||||
|
void suic_ode_geom_set_body(suic_ode_geom_id geom, suic_ode_body_id body);
|
||||||
|
void suic_ode_geom_destroy(suic_ode_geom_id geom);
|
||||||
|
|
||||||
|
// Collision space
|
||||||
|
suic_ode_space_id suic_ode_simple_space_create(suic_ode_space_id parent);
|
||||||
|
void suic_ode_space_destroy(suic_ode_space_id space);
|
||||||
|
|
||||||
|
// Collision detection and contact joints
|
||||||
|
void suic_ode_space_collide(suic_ode_world_id world, suic_ode_space_id space, suic_ode_joint_group_id contactgroup);
|
||||||
|
suic_ode_joint_group_id suic_ode_joint_group_create(int max_size);
|
||||||
|
void suic_ode_joint_group_destroy(suic_ode_joint_group_id group);
|
||||||
|
void suic_ode_joint_group_empty(suic_ode_joint_group_id group);
|
||||||
|
|
||||||
|
// Additional body functions
|
||||||
|
void suic_ode_body_get_linear_vel(suic_ode_body_id body, float *x, float *y, float *z);
|
||||||
|
void suic_ode_body_get_rotation(suic_ode_body_id body, float *q_w, float *q_x, float *q_y, float *q_z);
|
||||||
|
void suic_ode_body_set_rotation(suic_ode_body_id body, dReal q_w, dReal q_x, dReal q_y, dReal q_z);
|
||||||
|
|
||||||
|
// Contact properties configuration
|
||||||
|
void suic_ode_set_contact_max_force(dReal force);
|
||||||
|
void suic_ode_set_contact_erp(dReal erp);
|
||||||
|
void suic_ode_set_contact_cfm(dReal cfm);
|
||||||
|
|
||||||
|
// Game object system - unifies physics and rendering
|
||||||
|
typedef struct {
|
||||||
|
suic_ode_body_id body;
|
||||||
|
suic_ode_geom_id geom;
|
||||||
|
float width, height, length; // For box shapes
|
||||||
|
unsigned char r, g, b, a; // Color for rendering
|
||||||
|
int shape_type; // 0=box, 1=sphere, etc.
|
||||||
|
} suic_game_object;
|
||||||
|
|
||||||
|
// Create and manage game objects
|
||||||
|
suic_game_object* suic_game_object_create_box(
|
||||||
|
suic_ode_world_id world,
|
||||||
|
suic_ode_space_id space,
|
||||||
|
float x, float y, float z,
|
||||||
|
float width, float height, float length,
|
||||||
|
float density,
|
||||||
|
unsigned char r, unsigned char g, unsigned char b, unsigned char a
|
||||||
|
);
|
||||||
|
|
||||||
|
void suic_game_object_destroy(suic_game_object* obj);
|
||||||
|
void suic_game_object_set_position(suic_game_object* obj, float x, float y, float z);
|
||||||
|
void suic_game_object_get_position(suic_game_object* obj, float *x, float *y, float *z);
|
||||||
|
void suic_game_object_set_velocity(suic_game_object* obj, float x, float y, float z);
|
||||||
|
void suic_game_object_get_velocity(suic_game_object* obj, float *x, float *y, float *z);
|
||||||
|
void suic_game_object_draw(suic_game_object* obj);
|
||||||
|
|
||||||
#endif // LIBSUICMEZ_H
|
#endif // LIBSUICMEZ_H
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
fn simple() -> int do
|
|
||||||
42
|
|
||||||
end
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
int simple_add(int x, int y);
|
|
||||||
int main(void);
|
|
||||||
|
|
||||||
|
|
||||||
int simple_add(int x, int y) {
|
|
||||||
(x + y);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
int result = simple_add(5, 3);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
fn simple_add(x: int, y: int) -> int
|
|
||||||
x + y
|
|
||||||
|
|
||||||
fn main() -> int do
|
|
||||||
let result = simple_add(5, 3)
|
|
||||||
result
|
|
||||||
end
|
|
||||||
24
src/c_ir.rs
24
src/c_ir.rs
|
|
@ -8,10 +8,25 @@ pub enum CType {
|
||||||
Ptr(Box<CType>),
|
Ptr(Box<CType>),
|
||||||
Struct(String),
|
Struct(String),
|
||||||
UnnamedStruct(Vec<CVarDecl>),
|
UnnamedStruct(Vec<CVarDecl>),
|
||||||
Array(Box<CType>, usize), // type and size
|
Array(Box<CType>), // heap-allocated array wrapper with data pointer, len, capacity
|
||||||
Func(Vec<CType>, Box<CType>), // args and return
|
Func(Vec<CType>, Box<CType>), // args and return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CType {
|
||||||
|
/// Check if this type should be heap-allocated
|
||||||
|
pub fn is_heap_allocated(&self) -> bool {
|
||||||
|
matches!(self, CType::Array(_) | CType::Struct(_) | CType::Ptr(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if this is a copyable (stack-allocated) type
|
||||||
|
pub fn is_copyable(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
CType::Void | CType::Int | CType::Float | CType::Bool | CType::Char | CType::Ptr(_)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CType {
|
impl CType {
|
||||||
pub fn to_string(&self) -> String {
|
pub fn to_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -29,7 +44,10 @@ impl CType {
|
||||||
.collect();
|
.collect();
|
||||||
format!("struct {{\n{}\n}}", field_strs.join("\n"))
|
format!("struct {{\n{}\n}}", field_strs.join("\n"))
|
||||||
}
|
}
|
||||||
CType::Array(inner, size) => format!("{}[{}]", inner.to_string(), size),
|
CType::Array(inner) => format!(
|
||||||
|
"struct sui_array_{}",
|
||||||
|
inner.to_string().replace(" ", "_").replace("*", "ptr")
|
||||||
|
),
|
||||||
CType::Func(args, ret) => {
|
CType::Func(args, ret) => {
|
||||||
let arg_strs: Vec<String> = args.iter().map(|t| t.to_string()).collect();
|
let arg_strs: Vec<String> = args.iter().map(|t| t.to_string()).collect();
|
||||||
format!("{} (*)({})", ret.to_string(), arg_strs.join(", "))
|
format!("{} (*)({})", ret.to_string(), arg_strs.join(", "))
|
||||||
|
|
@ -77,6 +95,8 @@ pub enum CExpr {
|
||||||
Dot(Box<CExpr>, String),
|
Dot(Box<CExpr>, String),
|
||||||
AddrOf(Box<CExpr>),
|
AddrOf(Box<CExpr>),
|
||||||
Deref(Box<CExpr>),
|
Deref(Box<CExpr>),
|
||||||
|
Assign(Box<CExpr>, Box<CExpr>), // Assignment expression (lhs = rhs)
|
||||||
|
Ternary(Box<CExpr>, Box<CExpr>, Box<CExpr>), // cond ? then : else
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ impl DeclarationTranspiler {
|
||||||
Some(TypeAnnot::Cons(name, args)) if args.is_empty() => convert_to_c_type(name),
|
Some(TypeAnnot::Cons(name, args)) if args.is_empty() => convert_to_c_type(name),
|
||||||
Some(TypeAnnot::Cons(name, _args)) => {
|
Some(TypeAnnot::Cons(name, _args)) => {
|
||||||
// Generic types - for now just use the base name
|
// Generic types - for now just use the base name
|
||||||
Ok(CType::Struct(name.clone()))
|
Ok(CType::Ptr(Box::new(CType::Struct(name.clone()))))
|
||||||
}
|
}
|
||||||
Some(TypeAnnot::Ptr(inner)) => {
|
Some(TypeAnnot::Ptr(inner)) => {
|
||||||
let inner_type = self.type_annot_to_c_type(&Some(*inner.clone()))?;
|
let inner_type = self.type_annot_to_c_type(&Some(*inner.clone()))?;
|
||||||
|
|
@ -145,6 +145,7 @@ impl DeclarationTranspiler {
|
||||||
}
|
}
|
||||||
Some(TypeAnnot::Array(inner)) => {
|
Some(TypeAnnot::Array(inner)) => {
|
||||||
let inner_type = self.type_annot_to_c_type(&Some(*inner.clone()))?;
|
let inner_type = self.type_annot_to_c_type(&Some(*inner.clone()))?;
|
||||||
|
// Arrays are pointers to the element type
|
||||||
Ok(CType::Ptr(Box::new(inner_type)))
|
Ok(CType::Ptr(Box::new(inner_type)))
|
||||||
}
|
}
|
||||||
Some(TypeAnnot::Tuple(fields)) => {
|
Some(TypeAnnot::Tuple(fields)) => {
|
||||||
|
|
@ -178,6 +179,6 @@ pub fn convert_to_c_type(name: &String) -> Result<CType, String> {
|
||||||
"float" => Ok(CType::Float),
|
"float" => Ok(CType::Float),
|
||||||
"bool" => Ok(CType::Bool),
|
"bool" => Ok(CType::Bool),
|
||||||
"string" => Ok(CType::Ptr(Box::new(CType::Char))),
|
"string" => Ok(CType::Ptr(Box::new(CType::Char))),
|
||||||
_ => Ok(CType::Struct(name.clone())), // Assume struct
|
_ => Ok(CType::Ptr(Box::new(CType::Struct(name.clone())))), // Assume heap-allocated struct
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,9 +139,24 @@ impl StatementsTranspiler {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
TypedExprKind::If(cond, then_expr, else_expr) => {
|
TypedExprKind::If(cond, then_expr, else_expr) => {
|
||||||
// Conditional expressions - for now, simplify to function call
|
// Conditional expressions: (cond ? then_expr : else_expr)
|
||||||
// This is not ideal but works for basic cases
|
let c_cond = self.transpile_expr(cond)?;
|
||||||
Err("Conditional expressions not yet supported".to_string())
|
let c_then = self.transpile_expr(then_expr)?;
|
||||||
|
let c_else = match else_expr {
|
||||||
|
Some(else_expr) => self.transpile_expr(else_expr)?,
|
||||||
|
None => return Err("If expressions must have an else branch".to_string()),
|
||||||
|
};
|
||||||
|
Ok(CExpr::Ternary(
|
||||||
|
Box::new(c_cond),
|
||||||
|
Box::new(c_then),
|
||||||
|
Box::new(c_else),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
TypedExprKind::Assign(lhs, rhs) => {
|
||||||
|
// Assignments are expressions in C, so we can transpile them
|
||||||
|
let c_lhs = self.transpile_expr(lhs)?;
|
||||||
|
let c_rhs = self.transpile_expr(rhs)?;
|
||||||
|
Ok(CExpr::Assign(Box::new(c_lhs), Box::new(c_rhs)))
|
||||||
}
|
}
|
||||||
_ => Err(format!("Unsupported expression: {:?}", expr.kind)),
|
_ => Err(format!("Unsupported expression: {:?}", expr.kind)),
|
||||||
}
|
}
|
||||||
|
|
@ -171,18 +186,10 @@ impl StatementsTranspiler {
|
||||||
};
|
};
|
||||||
Ok(CStmt::Return(c_ret))
|
Ok(CStmt::Return(c_ret))
|
||||||
}
|
}
|
||||||
TypedExprKind::If(cond, then_expr, else_expr) => {
|
|
||||||
let c_cond = self.transpile_expr(cond)?;
|
|
||||||
let then_stmts = self.expr_to_stmts(then_expr)?;
|
|
||||||
let else_stmts = match else_expr {
|
|
||||||
Some(else_expr) => Some(self.expr_to_stmts(else_expr)?),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
Ok(CStmt::If(c_cond, then_stmts, else_stmts))
|
|
||||||
}
|
|
||||||
TypedExprKind::While(cond, body) => {
|
TypedExprKind::While(cond, body) => {
|
||||||
let c_cond = self.transpile_expr(cond)?;
|
let c_cond = self.transpile_expr(cond)?;
|
||||||
let body_stmts = self.expr_to_stmts(body)?;
|
let body_stmts = self.expr_to_loop_stmts(body)?;
|
||||||
Ok(CStmt::While(c_cond, body_stmts))
|
Ok(CStmt::While(c_cond, body_stmts))
|
||||||
}
|
}
|
||||||
TypedExprKind::Do(exprs) => {
|
TypedExprKind::Do(exprs) => {
|
||||||
|
|
@ -226,7 +233,7 @@ impl StatementsTranspiler {
|
||||||
let incr =
|
let incr =
|
||||||
CExpr::UnOp(CUnaryOp::PreInc, Box::new(CExpr::Var(var_name.clone())));
|
CExpr::UnOp(CUnaryOp::PreInc, Box::new(CExpr::Var(var_name.clone())));
|
||||||
|
|
||||||
let body_stmts = self.expr_to_stmts(body)?;
|
let body_stmts = self.expr_to_loop_stmts(body)?;
|
||||||
Ok(CStmt::For(init, cond, incr, body_stmts))
|
Ok(CStmt::For(init, cond, incr, body_stmts))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -269,7 +276,7 @@ impl StatementsTranspiler {
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut body_stmts = vec![bind];
|
let mut body_stmts = vec![bind];
|
||||||
body_stmts.extend(self.expr_to_stmts(body)?);
|
body_stmts.extend(self.expr_to_loop_stmts(body)?);
|
||||||
|
|
||||||
Ok(CStmt::For(idx_decl, cond, incr, body_stmts))
|
Ok(CStmt::For(idx_decl, cond, incr, body_stmts))
|
||||||
}
|
}
|
||||||
|
|
@ -291,7 +298,7 @@ impl StatementsTranspiler {
|
||||||
// tmp_arr = { ... }
|
// tmp_arr = { ... }
|
||||||
let (arr_name, arr_decl) = self.fresh_tmp_var(
|
let (arr_name, arr_decl) = self.fresh_tmp_var(
|
||||||
"_arr",
|
"_arr",
|
||||||
CType::Array(Box::new(elem_ty.clone()), arr_len),
|
CType::Array(Box::new(elem_ty.clone())),
|
||||||
Some(CExpr::ArrayLit(c_elems)),
|
Some(CExpr::ArrayLit(c_elems)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -318,7 +325,7 @@ impl StatementsTranspiler {
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut body_stmts = vec![bind];
|
let mut body_stmts = vec![bind];
|
||||||
body_stmts.extend(self.expr_to_stmts(body)?);
|
body_stmts.extend(self.expr_to_loop_stmts(body)?);
|
||||||
|
|
||||||
Ok(CStmt::Block(vec![
|
Ok(CStmt::Block(vec![
|
||||||
CStmt::VarDecl(arr_decl),
|
CStmt::VarDecl(arr_decl),
|
||||||
|
|
@ -382,6 +389,35 @@ impl StatementsTranspiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert expression to statements for loop bodies (no implicit return)
|
||||||
|
pub fn expr_to_loop_stmts(&mut self, expr: &TypedExpr) -> Result<Vec<CStmt>, String> {
|
||||||
|
match &expr.kind {
|
||||||
|
TypedExprKind::Do(stmts) => {
|
||||||
|
let mut c_stmts = Vec::new();
|
||||||
|
let mut defers = Vec::new();
|
||||||
|
for stmt in stmts {
|
||||||
|
match &stmt.kind {
|
||||||
|
TypedExprKind::Defer(defer_expr) => {
|
||||||
|
defers.push(self.transpile_stmt(defer_expr)?);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
c_stmts.push(self.transpile_stmt(stmt)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Execute defers in reverse order at the end
|
||||||
|
for defer_stmt in defers.into_iter().rev() {
|
||||||
|
c_stmts.push(defer_stmt);
|
||||||
|
}
|
||||||
|
Ok(c_stmts)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// For non-Do expressions in loops, just transpile as statement
|
||||||
|
Ok(vec![self.transpile_stmt(expr)?])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn binop_to_c_binop(&self, op: &BinOp) -> Result<CBinaryOp, String> {
|
fn binop_to_c_binop(&self, op: &BinOp) -> Result<CBinaryOp, String> {
|
||||||
match op {
|
match op {
|
||||||
BinOp::Add => Ok(CBinaryOp::Add),
|
BinOp::Add => Ok(CBinaryOp::Add),
|
||||||
|
|
@ -418,9 +454,13 @@ impl StatementsTranspiler {
|
||||||
Type::String => Ok(CType::Ptr(Box::new(CType::Char))),
|
Type::String => Ok(CType::Ptr(Box::new(CType::Char))),
|
||||||
Type::Unit => Ok(CType::Void),
|
Type::Unit => Ok(CType::Void),
|
||||||
Type::Ptr(inner) => Ok(CType::Ptr(Box::new(self.type_to_ctype(inner)?))),
|
Type::Ptr(inner) => Ok(CType::Ptr(Box::new(self.type_to_ctype(inner)?))),
|
||||||
Type::Array(inner) => Ok(CType::Ptr(Box::new(self.type_to_ctype(inner)?))),
|
Type::Array(inner) => {
|
||||||
Type::Struct(name, _) => Ok(CType::Struct(name.clone())),
|
let inner_type = self.type_to_ctype(inner)?;
|
||||||
Type::Enum(name, _) => Ok(CType::Struct(name.clone())),
|
// Arrays are pointers to the element type
|
||||||
|
Ok(CType::Ptr(Box::new(inner_type)))
|
||||||
|
}
|
||||||
|
Type::Struct(name, _) => Ok(CType::Ptr(Box::new(CType::Struct(name.clone())))),
|
||||||
|
Type::Enum(name, _) => Ok(CType::Ptr(Box::new(CType::Struct(name.clone())))),
|
||||||
Type::Tuple(types) => {
|
Type::Tuple(types) => {
|
||||||
let mut fields = Vec::new();
|
let mut fields = Vec::new();
|
||||||
for (i, inner_ty) in types.iter().enumerate() {
|
for (i, inner_ty) in types.iter().enumerate() {
|
||||||
|
|
@ -462,7 +502,7 @@ impl StatementsTranspiler {
|
||||||
TypeAnnot::Cons(name, args) if args.is_empty() => convert_to_c_type(name),
|
TypeAnnot::Cons(name, args) if args.is_empty() => convert_to_c_type(name),
|
||||||
TypeAnnot::Cons(name, _args) => {
|
TypeAnnot::Cons(name, _args) => {
|
||||||
// Generic types - for now just use the base name
|
// Generic types - for now just use the base name
|
||||||
Ok(CType::Struct(name.clone()))
|
Ok(CType::Ptr(Box::new(CType::Struct(name.clone()))))
|
||||||
}
|
}
|
||||||
TypeAnnot::Ptr(inner) => {
|
TypeAnnot::Ptr(inner) => {
|
||||||
let inner_type = self.type_annot_to_ctype(inner)?;
|
let inner_type = self.type_annot_to_ctype(inner)?;
|
||||||
|
|
@ -470,6 +510,7 @@ impl StatementsTranspiler {
|
||||||
}
|
}
|
||||||
TypeAnnot::Array(inner) => {
|
TypeAnnot::Array(inner) => {
|
||||||
let inner_type = self.type_annot_to_ctype(inner)?;
|
let inner_type = self.type_annot_to_ctype(inner)?;
|
||||||
|
// Arrays are pointers to the element type
|
||||||
Ok(CType::Ptr(Box::new(inner_type)))
|
Ok(CType::Ptr(Box::new(inner_type)))
|
||||||
}
|
}
|
||||||
TypeAnnot::Tuple(fields) => {
|
TypeAnnot::Tuple(fields) => {
|
||||||
|
|
|
||||||
|
|
@ -2,24 +2,69 @@ use crate::ast::*;
|
||||||
use crate::c_ir::*;
|
use crate::c_ir::*;
|
||||||
use crate::c_lowerer::declaration_transpiler::DeclarationTranspiler;
|
use crate::c_lowerer::declaration_transpiler::DeclarationTranspiler;
|
||||||
use crate::c_lowerer::statements_transpiler::StatementsTranspiler;
|
use crate::c_lowerer::statements_transpiler::StatementsTranspiler;
|
||||||
use std::collections::HashMap;
|
|
||||||
|
/// Extract variable name from a declaration like "int x = 10"
|
||||||
|
fn extract_var_declaration(code: &str) -> Option<String> {
|
||||||
|
// Pattern: type name = ...
|
||||||
|
let parts: Vec<&str> = code.split('=').collect();
|
||||||
|
if parts.len() >= 2 {
|
||||||
|
let left = parts[0].trim();
|
||||||
|
// Extract the variable name (last word before =)
|
||||||
|
if let Some(var_name) = left.split_whitespace().last() {
|
||||||
|
if !var_name.is_empty() {
|
||||||
|
return Some(var_name.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract variable name from an assignment like "x = y + 1"
|
||||||
|
fn extract_var_assignment(code: &str) -> Option<String> {
|
||||||
|
// Pattern: name = ...
|
||||||
|
let parts: Vec<&str> = code.split('=').collect();
|
||||||
|
if parts.len() >= 2 {
|
||||||
|
let left = parts[0].trim();
|
||||||
|
// Check if it looks like a simple variable (no whitespace = simple type)
|
||||||
|
if !left.contains(' ') && !left.contains('*') && !left.contains('[') {
|
||||||
|
return Some(left.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Transpiler {
|
pub struct Transpiler {
|
||||||
structs: HashMap<String, CStructDecl>,
|
structs: Vec<CStructDecl>, // Changed from HashMap to preserve order
|
||||||
functions: Vec<CFuncDecl>,
|
functions: Vec<CFuncDecl>,
|
||||||
globals: Vec<CVarDecl>,
|
globals: Vec<CVarDecl>,
|
||||||
decl_transpiler: DeclarationTranspiler,
|
decl_transpiler: DeclarationTranspiler,
|
||||||
stmt_transpiler: StatementsTranspiler,
|
stmt_transpiler: StatementsTranspiler,
|
||||||
|
array_types: std::collections::HashSet<String>, // Track array types we need to generate
|
||||||
|
has_main: bool, // Track if we found a main function
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transpiler {
|
impl Transpiler {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Transpiler {
|
Transpiler {
|
||||||
structs: HashMap::new(),
|
structs: Vec::new(),
|
||||||
functions: Vec::new(),
|
functions: Vec::new(),
|
||||||
globals: Vec::new(),
|
globals: Vec::new(),
|
||||||
decl_transpiler: DeclarationTranspiler::new(),
|
decl_transpiler: DeclarationTranspiler::new(),
|
||||||
stmt_transpiler: StatementsTranspiler::new(),
|
stmt_transpiler: StatementsTranspiler::new(),
|
||||||
|
array_types: std::collections::HashSet::new(),
|
||||||
|
has_main: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_array_type(&mut self, elem_type: &CType) {
|
||||||
|
self.array_types
|
||||||
|
.insert(Self::get_array_struct_name(elem_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_array_types_from_ctype(&mut self, ty: &CType) {
|
||||||
|
match ty {
|
||||||
|
CType::Ptr(inner) => self.collect_array_types_from_ctype(inner),
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,17 +75,57 @@ impl Transpiler {
|
||||||
|
|
||||||
self.lower_function_bodies_to_c_ir(nodes)?;
|
self.lower_function_bodies_to_c_ir(nodes)?;
|
||||||
|
|
||||||
|
// Rename main to suic_main and track that we have a main
|
||||||
|
if let Some(main_func) = self.functions.iter_mut().find(|f| f.name == "main") {
|
||||||
|
main_func.name = "suic_main".to_string();
|
||||||
|
self.has_main = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Generate C code
|
// Generate C code
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
|
|
||||||
// Add includes
|
// Add includes
|
||||||
|
output.push_str("#include \"../libsuicmez/libsuicmez.h\"\n");
|
||||||
output.push_str("#include <stdio.h>\n");
|
output.push_str("#include <stdio.h>\n");
|
||||||
output.push_str("#include <stdlib.h>\n");
|
output.push_str("#include <stdlib.h>\n");
|
||||||
output.push_str("#include <stdbool.h>\n");
|
output.push_str("#include <stdbool.h>\n");
|
||||||
output.push_str("#include <string.h>\n");
|
output.push_str("#include <string.h>\n");
|
||||||
|
output.push_str("\n");
|
||||||
|
|
||||||
|
// GC functions
|
||||||
|
output.push_str("void* gc_suic_alloc(size_t size);\n");
|
||||||
|
output.push_str("void suic_gc_free(void* ptr);\n");
|
||||||
|
output.push_str("\n");
|
||||||
|
|
||||||
|
// Helper functions for heap allocation
|
||||||
|
output.push_str("// Helper for allocating arrays\n");
|
||||||
|
output.push_str(
|
||||||
|
"static void* suic_alloc_array(size_t elem_size, size_t len, void* init_data) {\n",
|
||||||
|
);
|
||||||
|
output.push_str(" void* ptr = gc_suic_alloc(elem_size * len);\n");
|
||||||
|
output.push_str(" if (init_data) memcpy(ptr, init_data, elem_size * len);\n");
|
||||||
|
output.push_str(" return ptr;\n");
|
||||||
|
output.push_str("}\n");
|
||||||
|
output.push_str("\n");
|
||||||
|
|
||||||
|
// Helper for allocating structs
|
||||||
|
output.push_str("// Helper for allocating structs\n");
|
||||||
|
output.push_str("static void* suic_alloc_struct(size_t size, void* init_data) {\n");
|
||||||
|
output.push_str(" void* ptr = gc_suic_alloc(size);\n");
|
||||||
|
output.push_str(" if (init_data) memcpy(ptr, init_data, size);\n");
|
||||||
|
output.push_str(" return ptr;\n");
|
||||||
|
output.push_str("}\n");
|
||||||
|
output.push_str("\n");
|
||||||
|
|
||||||
|
// Generate array wrapper structs for all array types used
|
||||||
|
for array_type_name in &self.array_types {
|
||||||
|
output.push_str(&self.generate_array_struct(array_type_name));
|
||||||
|
output.push_str(";\n");
|
||||||
|
}
|
||||||
|
output.push_str("\n");
|
||||||
|
|
||||||
// Generate struct declarations
|
// Generate struct declarations
|
||||||
for struct_decl in self.structs.values() {
|
for struct_decl in &self.structs {
|
||||||
output.push_str(&self.generate_struct_decl(struct_decl));
|
output.push_str(&self.generate_struct_decl(struct_decl));
|
||||||
output.push_str(";\n");
|
output.push_str(";\n");
|
||||||
}
|
}
|
||||||
|
|
@ -65,6 +150,12 @@ impl Transpiler {
|
||||||
output.push_str("\n");
|
output.push_str("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate wrapper main if we found a main function
|
||||||
|
if self.has_main {
|
||||||
|
output.push_str(&self.generate_wrapper_main());
|
||||||
|
output.push_str("\n");
|
||||||
|
}
|
||||||
|
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,16 +163,28 @@ impl Transpiler {
|
||||||
match &node.kind {
|
match &node.kind {
|
||||||
TypedASTNodeKind::Struct(s) => {
|
TypedASTNodeKind::Struct(s) => {
|
||||||
let struct_decl = self.decl_transpiler.transpile_struct(s)?;
|
let struct_decl = self.decl_transpiler.transpile_struct(s)?;
|
||||||
self.structs.insert(s.name.clone(), struct_decl);
|
// Collect array types from struct fields
|
||||||
|
for field in &struct_decl.fields {
|
||||||
|
self.collect_array_types_from_ctype(&field.ty);
|
||||||
|
}
|
||||||
|
self.structs.push(struct_decl);
|
||||||
}
|
}
|
||||||
TypedASTNodeKind::Enum(e) => {
|
TypedASTNodeKind::Enum(e) => {
|
||||||
let enum_structs = self.decl_transpiler.transpile_enum(e)?;
|
let enum_structs = self.decl_transpiler.transpile_enum(e)?;
|
||||||
for struct_decl in enum_structs {
|
for struct_decl in enum_structs {
|
||||||
self.structs.insert(struct_decl.name.clone(), struct_decl);
|
for field in &struct_decl.fields {
|
||||||
|
self.collect_array_types_from_ctype(&field.ty);
|
||||||
|
}
|
||||||
|
self.structs.push(struct_decl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TypedASTNodeKind::Function(f) => {
|
TypedASTNodeKind::Function(f) => {
|
||||||
let mut func_decl = self.decl_transpiler.transpile_function(f)?;
|
let mut func_decl = self.decl_transpiler.transpile_function(f)?;
|
||||||
|
// Collect array types from function signature
|
||||||
|
self.collect_array_types_from_ctype(&func_decl.return_type);
|
||||||
|
for param in &func_decl.params {
|
||||||
|
self.collect_array_types_from_ctype(¶m.ty);
|
||||||
|
}
|
||||||
// Body will be filled later
|
// Body will be filled later
|
||||||
func_decl.body = Some(Vec::new());
|
func_decl.body = Some(Vec::new());
|
||||||
self.functions.push(func_decl);
|
self.functions.push(func_decl);
|
||||||
|
|
@ -89,6 +192,10 @@ impl Transpiler {
|
||||||
TypedASTNodeKind::Impl(imp) => {
|
TypedASTNodeKind::Impl(imp) => {
|
||||||
for method in &imp.methods {
|
for method in &imp.methods {
|
||||||
let mut func_decl = self.decl_transpiler.transpile_function(method)?;
|
let mut func_decl = self.decl_transpiler.transpile_function(method)?;
|
||||||
|
self.collect_array_types_from_ctype(&func_decl.return_type);
|
||||||
|
for param in &func_decl.params {
|
||||||
|
self.collect_array_types_from_ctype(¶m.ty);
|
||||||
|
}
|
||||||
func_decl.name = format!("{}_{}", imp.target, method.name);
|
func_decl.name = format!("{}_{}", imp.target, method.name);
|
||||||
func_decl.body = Some(Vec::new());
|
func_decl.body = Some(Vec::new());
|
||||||
self.functions.push(func_decl);
|
self.functions.push(func_decl);
|
||||||
|
|
@ -133,6 +240,22 @@ impl Transpiler {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_array_struct(&self, array_type_name: &str) -> String {
|
||||||
|
let mut output = format!("struct {} {{\n", array_type_name);
|
||||||
|
output.push_str(" void* data;\n");
|
||||||
|
output.push_str(" size_t len;\n");
|
||||||
|
output.push_str(" size_t capacity;\n");
|
||||||
|
output.push_str("}");
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_array_struct_name(elem_type: &CType) -> String {
|
||||||
|
format!(
|
||||||
|
"sui_array_{}",
|
||||||
|
elem_type.to_string().replace(" ", "_").replace("*", "ptr")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_struct_decl(&self, struct_decl: &CStructDecl) -> String {
|
fn generate_struct_decl(&self, struct_decl: &CStructDecl) -> String {
|
||||||
let mut output = format!("struct {} {{\n", struct_decl.name);
|
let mut output = format!("struct {} {{\n", struct_decl.name);
|
||||||
for field in &struct_decl.fields {
|
for field in &struct_decl.fields {
|
||||||
|
|
@ -174,6 +297,19 @@ impl Transpiler {
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_wrapper_main(&self) -> String {
|
||||||
|
let mut output = String::new();
|
||||||
|
output.push_str("int main(int argc, char* argv[]) {\n");
|
||||||
|
output.push_str(" // init gc and stuff\n");
|
||||||
|
output.push_str(" // init globals\n");
|
||||||
|
output.push_str(" // init event loop\n");
|
||||||
|
output.push_str(" int result = suic_main();\n");
|
||||||
|
output.push_str(" // free the stuff\n");
|
||||||
|
output.push_str(" return result;\n");
|
||||||
|
output.push_str("}\n");
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_var_decl(&self, var: &CVarDecl) -> String {
|
fn generate_var_decl(&self, var: &CVarDecl) -> String {
|
||||||
let mut output = format!("{} {}", var.ty.to_string(), var.name);
|
let mut output = format!("{} {}", var.ty.to_string(), var.name);
|
||||||
if let Some(init) = &var.initializer {
|
if let Some(init) = &var.initializer {
|
||||||
|
|
@ -182,8 +318,67 @@ impl Transpiler {
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_heap_alloc(&self, ty: &CType) -> String {
|
||||||
|
match ty {
|
||||||
|
CType::Struct(name) => {
|
||||||
|
format!("(struct {}*)suic_gc_alloc(sizeof(struct {}))", name, name)
|
||||||
|
}
|
||||||
|
CType::Array(elem_type) => {
|
||||||
|
format!(
|
||||||
|
"(struct {}*)suic_gc_alloc(sizeof(struct {}))",
|
||||||
|
Self::get_array_struct_name(elem_type),
|
||||||
|
Self::get_array_struct_name(elem_type)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => "NULL".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_debug_print(&self, code: &str) -> String {
|
||||||
|
// Extract the actual statement for the debug message
|
||||||
|
let trimmed = code.trim_end_matches('\n').trim_start();
|
||||||
|
let trimmed_no_semi = trimmed.trim_end_matches(';');
|
||||||
|
|
||||||
|
if trimmed_no_semi.is_empty() {
|
||||||
|
return code.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = code.trim_end_matches('\n').to_string();
|
||||||
|
result.push('\n');
|
||||||
|
|
||||||
|
// Escape quotes in the output
|
||||||
|
let escaped_code = trimmed_no_semi.replace("\"", "\\\"");
|
||||||
|
|
||||||
|
// Try to extract variable name and format for printing
|
||||||
|
if let Some(var_name) = extract_var_declaration(trimmed_no_semi) {
|
||||||
|
// For declarations, determine the format specifier
|
||||||
|
let format_spec = if trimmed_no_semi.contains("char*") {
|
||||||
|
"%s"
|
||||||
|
} else if trimmed_no_semi.contains("float") {
|
||||||
|
"%f"
|
||||||
|
} else if trimmed_no_semi.contains("*") {
|
||||||
|
"%p" // pointer
|
||||||
|
} else {
|
||||||
|
"%d"
|
||||||
|
};
|
||||||
|
result.push_str(&format!(
|
||||||
|
" printf(\"| {} | \\n {}\\n\", {});\n",
|
||||||
|
escaped_code, format_spec, var_name
|
||||||
|
));
|
||||||
|
} else if let Some(var_name) = extract_var_assignment(trimmed_no_semi) {
|
||||||
|
result.push_str(&format!(
|
||||||
|
" printf(\"| {} | \\n %d\\n\", {});\n",
|
||||||
|
escaped_code, var_name
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
result.push_str(&format!(" printf(\"| {} |\\n\");\n", escaped_code));
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_stmt(&self, stmt: &CStmt) -> String {
|
fn generate_stmt(&self, stmt: &CStmt) -> String {
|
||||||
match stmt {
|
let code = match stmt {
|
||||||
CStmt::VarDecl(var) => format!(" {};\n", self.generate_var_decl(var)),
|
CStmt::VarDecl(var) => format!(" {};\n", self.generate_var_decl(var)),
|
||||||
CStmt::Expr(expr) => format!(" {};\n", self.generate_expr(expr)),
|
CStmt::Expr(expr) => format!(" {};\n", self.generate_expr(expr)),
|
||||||
CStmt::Assign(lhs, rhs) => format!(
|
CStmt::Assign(lhs, rhs) => format!(
|
||||||
|
|
@ -241,6 +436,13 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
CStmt::Break => "break;\n".to_string(),
|
CStmt::Break => "break;\n".to_string(),
|
||||||
CStmt::Continue => "continue;\n".to_string(),
|
CStmt::Continue => "continue;\n".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add debug print for simple statements only (not control flow)
|
||||||
|
match stmt {
|
||||||
|
CStmt::VarDecl(_) | CStmt::Expr(_) | CStmt::Assign(_, _) => self.add_debug_print(&code),
|
||||||
|
CStmt::Return(_) => code, // Don't add debug print to return statements to avoid unreachable code
|
||||||
|
_ => code, // Control flow statements don't get debug prints
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -249,7 +451,13 @@ impl Transpiler {
|
||||||
CExpr::IntLit(i) => format!("{}", i),
|
CExpr::IntLit(i) => format!("{}", i),
|
||||||
CExpr::FloatLit(f) => format!("{:.6}", f),
|
CExpr::FloatLit(f) => format!("{:.6}", f),
|
||||||
CExpr::BoolLit(b) => format!("{}", b),
|
CExpr::BoolLit(b) => format!("{}", b),
|
||||||
CExpr::StringLit(s) => format!("\"{}\"", s),
|
CExpr::StringLit(s) => {
|
||||||
|
format!(
|
||||||
|
"suic_alloc_array(sizeof(char), {}, \"{}\")",
|
||||||
|
s.len() + 1, // +1 for null terminator
|
||||||
|
s
|
||||||
|
)
|
||||||
|
}
|
||||||
CExpr::Var(name) => name.clone(),
|
CExpr::Var(name) => name.clone(),
|
||||||
CExpr::Call(func, args) => {
|
CExpr::Call(func, args) => {
|
||||||
let args_str = args
|
let args_str = args
|
||||||
|
|
@ -257,7 +465,51 @@ impl Transpiler {
|
||||||
.map(|arg| self.generate_expr(arg))
|
.map(|arg| self.generate_expr(arg))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ");
|
.join(", ");
|
||||||
format!("{}({})", func, args_str)
|
|
||||||
|
// Map Sui function names to C function names for built-ins
|
||||||
|
let c_func_name = match func.as_str() {
|
||||||
|
"ode_init" => "suic_ode_init",
|
||||||
|
"ode_close" => "suic_ode_close",
|
||||||
|
"ode_world_create" => "suic_ode_world_create",
|
||||||
|
"ode_world_destroy" => "suic_ode_world_destroy",
|
||||||
|
"ode_world_set_gravity" => "suic_ode_world_set_gravity",
|
||||||
|
"ode_world_step" => "suic_ode_world_step",
|
||||||
|
"ode_body_create" => "suic_ode_body_create",
|
||||||
|
"ode_body_destroy" => "suic_ode_body_destroy",
|
||||||
|
"ode_body_set_position" => "suic_ode_body_set_position",
|
||||||
|
"ode_body_set_linear_vel" => "suic_ode_body_set_linear_vel",
|
||||||
|
"ode_body_set_box_mass" => "suic_ode_body_set_box_mass",
|
||||||
|
"ode_create_box_geom" => "suic_ode_create_box_geom",
|
||||||
|
"ode_geom_set_body" => "suic_ode_geom_set_body",
|
||||||
|
"ode_geom_destroy" => "suic_ode_geom_destroy",
|
||||||
|
"ode_simple_space_create" => "suic_ode_simple_space_create",
|
||||||
|
"ode_space_destroy" => "suic_ode_space_destroy",
|
||||||
|
"ode_create_plane_geom" => "suic_ode_create_plane_geom",
|
||||||
|
"ode_body_get_position" => "suic_ode_body_get_position",
|
||||||
|
"ode_space_collide" => "suic_ode_space_collide",
|
||||||
|
"ode_joint_group_create" => "suic_ode_joint_group_create",
|
||||||
|
"ode_joint_group_destroy" => "suic_ode_joint_group_destroy",
|
||||||
|
"ode_joint_group_empty" => "suic_ode_joint_group_empty",
|
||||||
|
"ode_body_get_linear_vel" => "suic_ode_body_get_linear_vel",
|
||||||
|
"ode_body_get_rotation" => "suic_ode_body_get_rotation",
|
||||||
|
"ode_body_set_rotation" => "suic_ode_body_set_rotation",
|
||||||
|
// Raylib functions
|
||||||
|
"init_window" => "suic_init_window",
|
||||||
|
"close_window" => "suic_close_window",
|
||||||
|
"window_should_close" => "suic_window_should_close",
|
||||||
|
"set_target_fps" => "suic_set_target_fps",
|
||||||
|
"begin_drawing" => "suic_begin_drawing",
|
||||||
|
"end_drawing" => "suic_end_drawing",
|
||||||
|
"clear_background" => "suic_clear_background",
|
||||||
|
"begin_mode3d" => "suic_begin_mode3d",
|
||||||
|
"end_mode3d" => "suic_end_mode3d",
|
||||||
|
"draw_cube" => "suic_draw_cube",
|
||||||
|
"draw_cube_wires" => "suic_draw_cube_wires",
|
||||||
|
"is_key_down" => "suic_is_key_down",
|
||||||
|
_ => func,
|
||||||
|
};
|
||||||
|
|
||||||
|
format!("{}({})", c_func_name, args_str)
|
||||||
}
|
}
|
||||||
CExpr::BinOp(lhs, op, rhs) => {
|
CExpr::BinOp(lhs, op, rhs) => {
|
||||||
format!(
|
format!(
|
||||||
|
|
@ -275,7 +527,7 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
CExpr::AddrOf(expr) => format!("&{}", self.generate_expr(expr)),
|
CExpr::AddrOf(expr) => format!("&{}", self.generate_expr(expr)),
|
||||||
CExpr::Deref(expr) => format!("*{}", self.generate_expr(expr)),
|
CExpr::Deref(expr) => format!("*{}", self.generate_expr(expr)),
|
||||||
CExpr::Dot(expr, field) => format!("{}.{}", self.generate_expr(expr), field),
|
CExpr::Dot(expr, field) => format!("(*{}).{}", self.generate_expr(expr), field),
|
||||||
CExpr::Index(array, index) => format!(
|
CExpr::Index(array, index) => format!(
|
||||||
"{}[{}]",
|
"{}[{}]",
|
||||||
self.generate_expr(array),
|
self.generate_expr(array),
|
||||||
|
|
@ -286,7 +538,12 @@ impl Transpiler {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, expr)| format!(".{} = {}", name, self.generate_expr(expr)))
|
.map(|(name, expr)| format!(".{} = {}", name, self.generate_expr(expr)))
|
||||||
.collect();
|
.collect();
|
||||||
format!("(struct {}){{ {} }}", struct_name, field_inits.join(", "))
|
format!(
|
||||||
|
"suic_alloc_struct(sizeof(struct {}), &(struct {}){{ {} }})",
|
||||||
|
struct_name,
|
||||||
|
struct_name,
|
||||||
|
field_inits.join(", ")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
CExpr::EnumLit(enum_name, variant_name, args) => {
|
CExpr::EnumLit(enum_name, variant_name, args) => {
|
||||||
// Find the variant index - for simplicity, assume variants are in order
|
// Find the variant index - for simplicity, assume variants are in order
|
||||||
|
|
@ -297,7 +554,7 @@ impl Transpiler {
|
||||||
let union_field_name = variant_name.to_lowercase();
|
let union_field_name = variant_name.to_lowercase();
|
||||||
|
|
||||||
let struct_init = if args.is_empty() {
|
let struct_init = if args.is_empty() {
|
||||||
"{}".to_string()
|
"".to_string()
|
||||||
} else {
|
} else {
|
||||||
let field_inits: Vec<String> = args
|
let field_inits: Vec<String> = args
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -307,19 +564,48 @@ impl Transpiler {
|
||||||
format!("{{ {} }}", field_inits.join(", "))
|
format!("{{ {} }}", field_inits.join(", "))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let variant_init = if struct_init.is_empty() {
|
||||||
|
format!("(struct {}){{}}", variant_struct_name)
|
||||||
|
} else {
|
||||||
|
format!("(struct {}){}", variant_struct_name, struct_init)
|
||||||
|
};
|
||||||
|
|
||||||
format!(
|
format!(
|
||||||
"({}){{ .discriminant = {}, .data = {{ .{} = ({}{}) }} }}",
|
"suic_alloc_struct(sizeof(struct {}), &(struct {}){{ .discriminant = {}, .data = {{ .{} = {} }} }})",
|
||||||
enum_name, variant_index, union_field_name, variant_struct_name, struct_init
|
enum_name, enum_name, variant_index, union_field_name, variant_init
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
CExpr::ArrayLit(array_lit) => {
|
CExpr::ArrayLit(array_lit) => {
|
||||||
let vec = array_lit
|
if array_lit.is_empty() {
|
||||||
.iter()
|
"NULL".to_string()
|
||||||
.map(|expr| self.generate_expr(expr))
|
} else {
|
||||||
.collect::<Vec<_>>();
|
let vec: Vec<String> = array_lit
|
||||||
let len = vec.len();
|
.iter()
|
||||||
format!("{}[{}]{{ {} }}", vec[0], len, vec.join(", "))
|
.map(|expr| self.generate_expr(expr))
|
||||||
|
.collect();
|
||||||
|
// Generate heap-allocated array using helper function
|
||||||
|
format!(
|
||||||
|
"suic_alloc_array(sizeof(int), {}, (int[]){{{}}})",
|
||||||
|
vec.len(),
|
||||||
|
vec.join(", ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CExpr::Assign(lhs, rhs) => {
|
||||||
|
format!(
|
||||||
|
"({} = {})",
|
||||||
|
self.generate_expr(lhs),
|
||||||
|
self.generate_expr(rhs)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CExpr::Ternary(cond, then_expr, else_expr) => {
|
||||||
|
format!(
|
||||||
|
"({} ? {} : {})",
|
||||||
|
self.generate_expr(cond),
|
||||||
|
self.generate_expr(then_expr),
|
||||||
|
self.generate_expr(else_expr)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
73
src/main.rs
73
src/main.rs
|
|
@ -1,40 +1,42 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use suicmez::{
|
use suicmez::{
|
||||||
codegen::transpiler::Transpiler,
|
codegen::transpiler::Transpiler,
|
||||||
import_resolver::ImportResolver,
|
import_resolver::ImportResolver,
|
||||||
lambda_lower::LambdaLowerer,
|
lambda_lower::LambdaLowerer,
|
||||||
monomorphize::{Monomorphizer, check_no_typevars},
|
monomorphize::{Monomorphizer, check_no_typevars},
|
||||||
typechecker::TypeChecker,
|
typechecker::TypeChecker,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(author, version, about = "A compiler for the Sui language")]
|
#[command(author, version, about = "A compiler for the Sui language")]
|
||||||
struct Args {
|
struct Args {
|
||||||
/// Run the test suite instead of compiling a file
|
/// Run the test suite instead of compiling a file
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
test: bool,
|
test: bool,
|
||||||
|
|
||||||
/// The Sui source file to compile
|
/// The Sui source file to compile
|
||||||
file: Option<String>,
|
file: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
if args.test {
|
if args.test {
|
||||||
run_test_suite();
|
run_test_suite();
|
||||||
} else if let Some(filename) = args.file {
|
} else if let Some(filename) = args.file {
|
||||||
println!("Type checking file: {}", filename);
|
println!("Type checking file: {}", filename);
|
||||||
|
|
||||||
if let Err(e) = run_file(&filename) {
|
if let Err(e) = run_file(&filename) {
|
||||||
eprintln!("Error: {}", e);
|
eprintln!("Error: {}", e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eprintln!("No file specified. Use --test to run the test suite or provide a Sui file to compile.");
|
eprintln!(
|
||||||
std::process::exit(1);
|
"No file specified. Use --test to run the test suite or provide a Sui file to compile."
|
||||||
}
|
);
|
||||||
}
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn run_test_suite() {
|
fn run_test_suite() {
|
||||||
println!("Running test suite...\n");
|
println!("Running test suite...\n");
|
||||||
|
|
@ -181,8 +183,11 @@ fn run_file(filename: &str) -> Result<(), String> {
|
||||||
.resolve(filename)
|
.resolve(filename)
|
||||||
.map_err(|e| format!("Import resolution error: {}", e))?;
|
.map_err(|e| format!("Import resolution error: {}", e))?;
|
||||||
|
|
||||||
println!("Import resolution complete! {} total nodes loaded", ast_nodes.len());
|
println!(
|
||||||
|
"Import resolution complete! {} total nodes loaded",
|
||||||
|
ast_nodes.len()
|
||||||
|
);
|
||||||
|
|
||||||
// Read the source file for error reporting
|
// Read the source file for error reporting
|
||||||
let source = fs::read_to_string(filename)
|
let source = fs::read_to_string(filename)
|
||||||
.map_err(|e| format!("Error reading file {}: {}", filename, e))?;
|
.map_err(|e| format!("Error reading file {}: {}", filename, e))?;
|
||||||
|
|
|
||||||
|
|
@ -366,7 +366,12 @@ impl Monomorphizer {
|
||||||
if !field_types.is_empty() {
|
if !field_types.is_empty() {
|
||||||
self.infer_struct_specialization(name, &field_types, &mut needs);
|
self.infer_struct_specialization(name, &field_types, &mut needs);
|
||||||
}
|
}
|
||||||
TypedExprKind::StructLit(name.clone(), new_fields)
|
let new_kind = TypedExprKind::StructLit(name.clone(), new_fields);
|
||||||
|
let mut temp_expr = expr.clone();
|
||||||
|
temp_expr.kind = new_kind.clone();
|
||||||
|
// Collect specialization needs from the struct literal's type
|
||||||
|
self.collect_needs_from_expr_type(&temp_expr, &mut needs);
|
||||||
|
new_kind
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedExprKind::EnumLit(enum_name, variant, args) => {
|
TypedExprKind::EnumLit(enum_name, variant, args) => {
|
||||||
|
|
|
||||||
|
|
@ -351,6 +351,9 @@ impl TypeChecker {
|
||||||
self.collect_definitions(node)?;
|
self.collect_definitions(node)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add built-in functions
|
||||||
|
self.add_builtin_functions();
|
||||||
|
|
||||||
// Second pass: typecheck everything
|
// Second pass: typecheck everything
|
||||||
self.env.enter_scope();
|
self.env.enter_scope();
|
||||||
let mut typed_nodes = Vec::new();
|
let mut typed_nodes = Vec::new();
|
||||||
|
|
@ -362,6 +365,424 @@ impl TypeChecker {
|
||||||
Ok(typed_nodes)
|
Ok(typed_nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_builtin_functions(&mut self) {
|
||||||
|
// ODE Physics Engine functions
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_init".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![],
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_close".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![],
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_world_create".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![],
|
||||||
|
return_type: Type::Ptr(Box::new(Type::Unit)), // opaque pointer
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_world_destroy".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![Type::Ptr(Box::new(Type::Unit))],
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_world_set_gravity".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![
|
||||||
|
Type::Ptr(Box::new(Type::Unit)),
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
], // world_id, x, y, z
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_world_step".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![Type::Ptr(Box::new(Type::Unit)), Type::Float],
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Body management
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_body_create".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![Type::Ptr(Box::new(Type::Unit))], // world
|
||||||
|
return_type: Type::Ptr(Box::new(Type::Unit)), // body
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_body_destroy".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![Type::Ptr(Box::new(Type::Unit))], // body
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_body_set_position".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![
|
||||||
|
Type::Ptr(Box::new(Type::Unit)),
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
], // body, x, y, z
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_body_set_linear_vel".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![
|
||||||
|
Type::Ptr(Box::new(Type::Unit)),
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
], // body, x, y, z
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Geometry and mass
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_body_set_box_mass".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![
|
||||||
|
Type::Ptr(Box::new(Type::Unit)),
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
], // body, density, lx, ly, lz
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_create_box_geom".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![
|
||||||
|
Type::Ptr(Box::new(Type::Unit)),
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
], // space, lx, ly, lz
|
||||||
|
return_type: Type::Ptr(Box::new(Type::Unit)), // geom
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_create_plane_geom".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![
|
||||||
|
Type::Ptr(Box::new(Type::Unit)),
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
], // space, a, b, c, d
|
||||||
|
return_type: Type::Ptr(Box::new(Type::Unit)), // geom
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_geom_set_body".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![
|
||||||
|
Type::Ptr(Box::new(Type::Unit)),
|
||||||
|
Type::Ptr(Box::new(Type::Unit)),
|
||||||
|
], // geom, body
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_geom_destroy".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![Type::Ptr(Box::new(Type::Unit))], // geom
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Collision space
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_simple_space_create".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![Type::Ptr(Box::new(Type::Unit))], // parent space (can be null)
|
||||||
|
return_type: Type::Ptr(Box::new(Type::Unit)), // space
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_space_destroy".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![Type::Ptr(Box::new(Type::Unit))], // space
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Collision detection and contact joints
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_space_collide".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![
|
||||||
|
Type::Ptr(Box::new(Type::Unit)),
|
||||||
|
Type::Ptr(Box::new(Type::Unit)),
|
||||||
|
Type::Ptr(Box::new(Type::Unit)),
|
||||||
|
], // world, space, contactgroup
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_joint_group_create".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![Type::Int], // max_size
|
||||||
|
return_type: Type::Ptr(Box::new(Type::Unit)), // contactgroup
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_joint_group_destroy".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![Type::Ptr(Box::new(Type::Unit))], // group
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_joint_group_empty".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![Type::Ptr(Box::new(Type::Unit))], // group
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Additional body functions
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_body_get_linear_vel".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![
|
||||||
|
Type::Ptr(Box::new(Type::Unit)),
|
||||||
|
Type::Ptr(Box::new(Type::Float)),
|
||||||
|
Type::Ptr(Box::new(Type::Float)),
|
||||||
|
Type::Ptr(Box::new(Type::Float)),
|
||||||
|
], // body, x, y, z
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_body_get_rotation".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![
|
||||||
|
Type::Ptr(Box::new(Type::Unit)),
|
||||||
|
Type::Ptr(Box::new(Type::Float)),
|
||||||
|
Type::Ptr(Box::new(Type::Float)),
|
||||||
|
Type::Ptr(Box::new(Type::Float)),
|
||||||
|
Type::Ptr(Box::new(Type::Float)),
|
||||||
|
], // body, w, x, y, z
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_body_set_rotation".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![
|
||||||
|
Type::Ptr(Box::new(Type::Unit)),
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
], // body, w, x, y, z
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Raylib functions
|
||||||
|
// Window management
|
||||||
|
self.env.functions.insert(
|
||||||
|
"init_window".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![Type::Int, Type::Int, Type::String], // width, height, title
|
||||||
|
return_type: Type::Bool,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"close_window".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![],
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"window_should_close".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![],
|
||||||
|
return_type: Type::Bool,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"set_target_fps".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![Type::Int], // fps
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Drawing
|
||||||
|
self.env.functions.insert(
|
||||||
|
"begin_drawing".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![],
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"end_drawing".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![],
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"clear_background".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![Type::Int, Type::Int, Type::Int, Type::Int], // r, g, b, a
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3D Mode
|
||||||
|
self.env.functions.insert(
|
||||||
|
"begin_mode3d".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Int,
|
||||||
|
], // pos_x,y,z target_x,y,z up_x,y,z fovy projection
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"end_mode3d".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![],
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3D Drawing
|
||||||
|
self.env.functions.insert(
|
||||||
|
"draw_cube".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Int,
|
||||||
|
Type::Int,
|
||||||
|
Type::Int,
|
||||||
|
Type::Int,
|
||||||
|
], // x,y,z width,height,length r,g,b,a
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.env.functions.insert(
|
||||||
|
"draw_cube_wires".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Float,
|
||||||
|
Type::Int,
|
||||||
|
Type::Int,
|
||||||
|
Type::Int,
|
||||||
|
Type::Int,
|
||||||
|
], // x,y,z width,height,length r,g,b,a
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Input
|
||||||
|
self.env.functions.insert(
|
||||||
|
"is_key_down".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![Type::Int], // key
|
||||||
|
return_type: Type::Bool,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Physics integration helper
|
||||||
|
self.env.functions.insert(
|
||||||
|
"ode_body_get_position".to_string(),
|
||||||
|
FunctionType {
|
||||||
|
type_params: vec![],
|
||||||
|
params: vec![
|
||||||
|
Type::Ptr(Box::new(Type::Unit)),
|
||||||
|
Type::Ptr(Box::new(Type::Float)),
|
||||||
|
Type::Ptr(Box::new(Type::Float)),
|
||||||
|
Type::Ptr(Box::new(Type::Float)),
|
||||||
|
], // body, x, y, z
|
||||||
|
return_type: Type::Unit,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn collect_definitions(&mut self, node: &ASTNode) -> Result<(), TypeError> {
|
fn collect_definitions(&mut self, node: &ASTNode) -> Result<(), TypeError> {
|
||||||
match &node.kind {
|
match &node.kind {
|
||||||
ASTNodeKind::Struct(s) => {
|
ASTNodeKind::Struct(s) => {
|
||||||
|
|
|
||||||
448
syntax_test.sui
448
syntax_test.sui
|
|
@ -1,448 +0,0 @@
|
||||||
use "std/abc"
|
|
||||||
load "math.so" as math_lib
|
|
||||||
|
|
||||||
extern print(msg: string) -> int from libc # this is a comment
|
|
||||||
extern malloc(size: int) -> int from libc #* this is a comment too *#
|
|
||||||
extern add_numbers(a: int, b: int) -> int from math_lib
|
|
||||||
|
|
||||||
# this is a funny comment
|
|
||||||
struct Point
|
|
||||||
x: int,
|
|
||||||
y: int
|
|
||||||
end
|
|
||||||
|
|
||||||
struct Generic<T>
|
|
||||||
value: T,
|
|
||||||
tag: string
|
|
||||||
end
|
|
||||||
|
|
||||||
struct Complex<T, U>
|
|
||||||
first: T,
|
|
||||||
second: U
|
|
||||||
end
|
|
||||||
|
|
||||||
struct EmptyStruct
|
|
||||||
end
|
|
||||||
|
|
||||||
struct ArrayStruct
|
|
||||||
numbers: [int],
|
|
||||||
matrix: [[float]]
|
|
||||||
end
|
|
||||||
|
|
||||||
enum Option<T>
|
|
||||||
Some(T),
|
|
||||||
None
|
|
||||||
end
|
|
||||||
|
|
||||||
enum Result<T, E>
|
|
||||||
Ok(T),
|
|
||||||
Err(E)
|
|
||||||
end
|
|
||||||
|
|
||||||
enum Status
|
|
||||||
Active,
|
|
||||||
Inactive,
|
|
||||||
Pending(string)
|
|
||||||
end
|
|
||||||
|
|
||||||
enum Tree<T>
|
|
||||||
Leaf(T),
|
|
||||||
Branch(T, T)
|
|
||||||
end
|
|
||||||
|
|
||||||
trait Show
|
|
||||||
fn display(msg: string) -> string,
|
|
||||||
fn to_string() -> string
|
|
||||||
end
|
|
||||||
|
|
||||||
trait Comparable<T>
|
|
||||||
fn compare(other: T) -> int
|
|
||||||
end
|
|
||||||
|
|
||||||
trait Container<T>
|
|
||||||
fn push(item: T) -> int,
|
|
||||||
fn pop() -> T
|
|
||||||
end
|
|
||||||
|
|
||||||
impl Point
|
|
||||||
fn new(x: int, y: int) -> Point
|
|
||||||
Point { x: x, y: y }
|
|
||||||
|
|
||||||
fn distance() -> float do
|
|
||||||
let x_sq = 5 * 5
|
|
||||||
let y_sq = 3 * 3
|
|
||||||
((x_sq + y_sq) as float)
|
|
||||||
end
|
|
||||||
|
|
||||||
fn move_by(dx: int, dy: int) -> Point do
|
|
||||||
let new_x = 5 + 10
|
|
||||||
let new_y = 3 + 20
|
|
||||||
Point { x: new_x, y: new_y }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
impl Option<int> : Show
|
|
||||||
fn display(msg: string) -> string
|
|
||||||
"Option value"
|
|
||||||
|
|
||||||
fn to_string() -> string
|
|
||||||
"option"
|
|
||||||
end
|
|
||||||
|
|
||||||
impl Result<string, int>
|
|
||||||
fn is_ok() -> bool
|
|
||||||
true
|
|
||||||
|
|
||||||
fn unwrap() -> string
|
|
||||||
"unwrapped"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
fn greet(name: string) -> string
|
|
||||||
"Hello, " + name
|
|
||||||
|
|
||||||
fn get_answer() -> int
|
|
||||||
42
|
|
||||||
|
|
||||||
fn print_number(num: int)
|
|
||||||
num
|
|
||||||
|
|
||||||
fn add(a: int, b: int) -> int
|
|
||||||
a + b
|
|
||||||
|
|
||||||
fn identity<T>(value: T) -> T
|
|
||||||
value
|
|
||||||
|
|
||||||
fn pair<T, U>(first: T, second: U) -> (T, U)
|
|
||||||
(first, second)
|
|
||||||
|
|
||||||
fn array_func(arr: [int]) -> [int]
|
|
||||||
arr
|
|
||||||
|
|
||||||
fn process<T>(item: T) -> string
|
|
||||||
"processed"
|
|
||||||
|
|
||||||
fn process_list(items: int) -> int do
|
|
||||||
let result = 0
|
|
||||||
result + items
|
|
||||||
end
|
|
||||||
|
|
||||||
fn literals() -> bool do
|
|
||||||
let int_val = 42
|
|
||||||
let float_val = 3.14
|
|
||||||
let negative = -100
|
|
||||||
let scientific = 1.5e-10
|
|
||||||
let bool_true = true
|
|
||||||
let bool_false = false
|
|
||||||
let string_val = "hello world"
|
|
||||||
let empty_string = ""
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
fn collections() -> bool do
|
|
||||||
let arr = [1, 2, 3, 4, 5]
|
|
||||||
let empty_arr = []
|
|
||||||
let tuple = (1, "hello", 3.14)
|
|
||||||
let single_tuple = (42)
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
fn struct_enum_literals() -> bool do
|
|
||||||
let point = Point { x: 10, y: 20 }
|
|
||||||
let option_val = Option::Some(42)
|
|
||||||
let option_none = Option::None
|
|
||||||
let result_ok = Result::Ok("success")
|
|
||||||
let result_err = Result::Err(404)
|
|
||||||
let status = Status::Pending("loading...")
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
fn arithmetic_logic() -> int do
|
|
||||||
let a = 10
|
|
||||||
let b = 5
|
|
||||||
let add_result = a + b
|
|
||||||
let sub_result = a - b
|
|
||||||
let mul_result = a * b
|
|
||||||
let div_result = a / b
|
|
||||||
let mod_result = a % b
|
|
||||||
|
|
||||||
let and_result = true and false
|
|
||||||
let or_result = true or false
|
|
||||||
let not_result = not true
|
|
||||||
|
|
||||||
let eq = a == b
|
|
||||||
let neq = a != b
|
|
||||||
let lt = a < b
|
|
||||||
let gt = a > b
|
|
||||||
let leq = a <= b
|
|
||||||
let geq = a >= b
|
|
||||||
|
|
||||||
add_result + sub_result
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
fn unary() -> int do
|
|
||||||
let a = 5
|
|
||||||
let neg = -a
|
|
||||||
let b = true
|
|
||||||
let not_b = not b
|
|
||||||
a + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
fn bindings() -> int do
|
|
||||||
let x = 10
|
|
||||||
let mut y = 20
|
|
||||||
let uniq z = 30
|
|
||||||
let once w = 40
|
|
||||||
|
|
||||||
let typed: int = 100
|
|
||||||
let mut_typed: string = "hello"
|
|
||||||
|
|
||||||
x + 5
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
fn if_else(n: int) -> int
|
|
||||||
if n > 0
|
|
||||||
100
|
|
||||||
else
|
|
||||||
-100
|
|
||||||
|
|
||||||
fn if_else_complex(a: int, b: int) -> string
|
|
||||||
if a > b
|
|
||||||
"a is greater"
|
|
||||||
else
|
|
||||||
if a == b
|
|
||||||
"equal"
|
|
||||||
else
|
|
||||||
"b is greater"
|
|
||||||
|
|
||||||
|
|
||||||
fn match_simple(opt: Option<int>) -> int
|
|
||||||
match opt
|
|
||||||
Option::Some(x) => x,
|
|
||||||
Option::None => 0
|
|
||||||
end
|
|
||||||
|
|
||||||
fn match_complex(val: int) -> string
|
|
||||||
match val
|
|
||||||
0 => "zero",
|
|
||||||
1 => "one",
|
|
||||||
2 => "two",
|
|
||||||
_ => "many"
|
|
||||||
end
|
|
||||||
|
|
||||||
fn match_pattern(p: Point) -> string
|
|
||||||
match p
|
|
||||||
Point { x: 0, y: 0 } => "origin",
|
|
||||||
Point { x: x, y: y } => "point"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
fn while_loop(n: int) -> int do
|
|
||||||
let mut count = 0
|
|
||||||
while count < n
|
|
||||||
do
|
|
||||||
count = count + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
fn do_block() -> int
|
|
||||||
do
|
|
||||||
let a = 10
|
|
||||||
let b = 20
|
|
||||||
let c = 30
|
|
||||||
a + b + c
|
|
||||||
end
|
|
||||||
|
|
||||||
fn nested_do() -> int
|
|
||||||
do
|
|
||||||
let x = do
|
|
||||||
5
|
|
||||||
end
|
|
||||||
let y = do
|
|
||||||
10
|
|
||||||
end
|
|
||||||
x + y
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
fn function_calls() -> int do
|
|
||||||
let point = Point { x: 5, y: 10 }
|
|
||||||
let x_coord = point.x
|
|
||||||
let arr = [1, 2, 3]
|
|
||||||
let first = arr[0]
|
|
||||||
|
|
||||||
let result = add(10, 20)
|
|
||||||
let identity_val = identity(42)
|
|
||||||
|
|
||||||
result + first
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
fn optional_chain(opt: Option<Point>) -> int do
|
|
||||||
let val = opt?.x
|
|
||||||
100
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
fn early_return() -> int do
|
|
||||||
let opt = Option::Some(42)
|
|
||||||
let val = opt?
|
|
||||||
val
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
fn casting() -> float do
|
|
||||||
let int_val = 42
|
|
||||||
let float_val = (int_val as float)
|
|
||||||
let x = (100 as float) + 3.14
|
|
||||||
x
|
|
||||||
end
|
|
||||||
|
|
||||||
fn lambda_example() -> int do
|
|
||||||
let add_one = lambda (x) x + 1
|
|
||||||
let multiply = lambda (x, y) x * y
|
|
||||||
let get_five = lambda () 5
|
|
||||||
|
|
||||||
let applied = add_one(10)
|
|
||||||
applied
|
|
||||||
end
|
|
||||||
|
|
||||||
fn assignment() -> int do
|
|
||||||
let mut x = 10
|
|
||||||
x = 20
|
|
||||||
x = x + 5
|
|
||||||
x
|
|
||||||
end
|
|
||||||
|
|
||||||
fn with_return(n: int) -> int do
|
|
||||||
if n < 0
|
|
||||||
return -1
|
|
||||||
n + 100
|
|
||||||
end
|
|
||||||
|
|
||||||
fn with_break() -> int do
|
|
||||||
let mut i = 0
|
|
||||||
while i < 10
|
|
||||||
do
|
|
||||||
if i == 5
|
|
||||||
break
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
i
|
|
||||||
end
|
|
||||||
|
|
||||||
fn with_continue() -> int do
|
|
||||||
let mut sum = 0
|
|
||||||
let mut i = 0
|
|
||||||
while i < 10
|
|
||||||
do
|
|
||||||
i = i + 1
|
|
||||||
if i % 2 == 0
|
|
||||||
continue
|
|
||||||
sum = sum + i
|
|
||||||
end
|
|
||||||
sum
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
@deprecated("use new_func instead")
|
|
||||||
fn old_func() -> int
|
|
||||||
42
|
|
||||||
|
|
||||||
@optimize(level = aggressive)
|
|
||||||
fn fast_func() -> int
|
|
||||||
100
|
|
||||||
|
|
||||||
@test
|
|
||||||
fn test_something() -> bool
|
|
||||||
true
|
|
||||||
|
|
||||||
fn fibonacci(n: int) -> int
|
|
||||||
if n <= 1
|
|
||||||
n
|
|
||||||
else
|
|
||||||
fibonacci(n - 1) + fibonacci(n - 2)
|
|
||||||
|
|
||||||
fn factorial(n: int) -> int do
|
|
||||||
let mut result = 1
|
|
||||||
let mut i = 2
|
|
||||||
while i <= n
|
|
||||||
do
|
|
||||||
result = result * i
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
fn map_over_option<T, U>(opt: Option<T>) -> Option<U>
|
|
||||||
match opt
|
|
||||||
Option::Some(x) => Option::Some(x),
|
|
||||||
Option::None => Option::None
|
|
||||||
end
|
|
||||||
|
|
||||||
fn process_result<T, E>(res: Result<T, E>) -> int
|
|
||||||
match res
|
|
||||||
Result::Ok(x) => 1,
|
|
||||||
Result::Err(e) => 0
|
|
||||||
end
|
|
||||||
|
|
||||||
struct LinkedList<T>
|
|
||||||
value: T,
|
|
||||||
next: Option<int>
|
|
||||||
end
|
|
||||||
|
|
||||||
impl LinkedList<int>
|
|
||||||
fn new(v: int) -> LinkedList<int>
|
|
||||||
LinkedList { value: 42, next: Option::None }
|
|
||||||
|
|
||||||
fn head() -> int
|
|
||||||
100
|
|
||||||
|
|
||||||
fn tail() -> Option<int>
|
|
||||||
Option::None
|
|
||||||
|
|
||||||
fn sum() -> int
|
|
||||||
do
|
|
||||||
let mut total = 0
|
|
||||||
total
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
fn complex_pattern_match(val: int) -> string
|
|
||||||
match val
|
|
||||||
0 => "zero",
|
|
||||||
1 => "one",
|
|
||||||
2 => "two",
|
|
||||||
3 => "three",
|
|
||||||
4 => "four",
|
|
||||||
5 => "five",
|
|
||||||
_ => "many"
|
|
||||||
end
|
|
||||||
|
|
||||||
fn tuple_destructure() -> int do
|
|
||||||
let tup = (10, 20, 30)
|
|
||||||
30
|
|
||||||
end
|
|
||||||
|
|
||||||
fn array_ops() -> int do
|
|
||||||
let arr = [1, 2, 3, 4, 5]
|
|
||||||
let first = arr[0]
|
|
||||||
let length = 5
|
|
||||||
first + length
|
|
||||||
end
|
|
||||||
|
|
||||||
fn range_example() -> int do
|
|
||||||
let r = 1..10
|
|
||||||
5
|
|
||||||
end
|
|
||||||
|
|
||||||
fn for_loop_example() -> int do
|
|
||||||
let sum = 0
|
|
||||||
for i in 0..5
|
|
||||||
sum + i
|
|
||||||
sum
|
|
||||||
end
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
fn test_affine() -> int do
|
|
||||||
let uniq x = 5
|
|
||||||
x + 1
|
|
||||||
x + 2
|
|
||||||
end
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
fn test_binding_id() -> int do
|
|
||||||
let x = 5
|
|
||||||
let x = x + 1 # shadowing
|
|
||||||
x
|
|
||||||
end
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
fn test_linear() -> int do
|
|
||||||
let once y = 10
|
|
||||||
5
|
|
||||||
end
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
fn test_linear_ok() -> int do
|
|
||||||
let once z = 10
|
|
||||||
z + 5
|
|
||||||
end
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fn test_scoping() -> int do
|
|
||||||
let x = 5
|
|
||||||
let f = lambda (y) y + 1 # simple lambda
|
|
||||||
# f = lambda (z) z+1
|
|
||||||
let x = 10 # shadow x
|
|
||||||
f(3) # should work
|
|
||||||
end
|
|
||||||
|
|
||||||
fn stuff(f: fn(int, int) -> int, v: int) -> int do
|
|
||||||
f(v, v)
|
|
||||||
end
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
fn test_shadow() -> int do
|
|
||||||
let x = 5
|
|
||||||
let x = 10
|
|
||||||
x
|
|
||||||
end
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
int main(void);
|
|
||||||
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
int x = 5;
|
|
||||||
float y = 10.500000;
|
|
||||||
bool z = true;
|
|
||||||
char* s = "hello";
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
136
tests/codegen/INDEX.md
Normal file
136
tests/codegen/INDEX.md
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
# Codegen Tests Index
|
||||||
|
|
||||||
|
Quick reference guide for all codegen tests.
|
||||||
|
|
||||||
|
## 📋 Test Overview
|
||||||
|
|
||||||
|
| Test | Type | Features | Lines |
|
||||||
|
|------|------|----------|-------|
|
||||||
|
| `test_executable` | Basic | Integer math, functions | 4 |
|
||||||
|
| `test_array_simple` | Arrays | Literals, indexing, pointers | 4 |
|
||||||
|
| `test_enum_simple` | Enums | Variants, discriminant | 2 |
|
||||||
|
| `test_comprehensive` | Mixed | All basic types, structs | 18 |
|
||||||
|
| `test_all_types` | Coverage | Complete type test | 16 |
|
||||||
|
|
||||||
|
## 🚀 Quick Commands
|
||||||
|
|
||||||
|
### Generate C from all tests
|
||||||
|
```bash
|
||||||
|
for f in tests/codegen/*.sui; do
|
||||||
|
./target/debug/suicmez "$f"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compile all tests
|
||||||
|
```bash
|
||||||
|
for f in tests/codegen/*.c; do
|
||||||
|
gcc "$f" -o "/tmp/$(basename ${f%.c})"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run all tests
|
||||||
|
```bash
|
||||||
|
for exe in /tmp/test_*; do
|
||||||
|
echo "=== $(basename $exe) ==="
|
||||||
|
"$exe" 2>&1
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run single test
|
||||||
|
```bash
|
||||||
|
./target/debug/suicmez tests/codegen/test_comprehensive.sui
|
||||||
|
gcc tests/codegen/test_comprehensive.c -o /tmp/test
|
||||||
|
/tmp/test
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📖 Documentation Files
|
||||||
|
|
||||||
|
- **README.md** - Full documentation of all tests
|
||||||
|
- **TESTING.md** - How to run and debug tests
|
||||||
|
- **SUMMARY.md** - Organization and cleanup summary
|
||||||
|
- **INDEX.md** - This file
|
||||||
|
|
||||||
|
## 🎯 Test Selection Guide
|
||||||
|
|
||||||
|
**I want to test:**
|
||||||
|
|
||||||
|
- **Simple execution** → Use `test_executable`
|
||||||
|
- **Arrays** → Use `test_array_simple`
|
||||||
|
- **Enums** → Use `test_enum_simple`
|
||||||
|
- **Everything together** → Use `test_comprehensive`
|
||||||
|
- **All types** → Use `test_all_types`
|
||||||
|
|
||||||
|
## ✅ Verification Checklist
|
||||||
|
|
||||||
|
Before pushing:
|
||||||
|
- [ ] All `.sui` files transpile: `for f in tests/codegen/*.sui; do suicmez "$f" > /dev/null; done`
|
||||||
|
- [ ] All `.c` files compile: `for f in tests/codegen/*.c; do gcc "$f" -c; done`
|
||||||
|
- [ ] All tests execute: `for f in tests/codegen/*.c; do gcc "$f" -o /tmp/t && /tmp/t > /dev/null; done`
|
||||||
|
- [ ] Debug output works: `gcc tests/codegen/test_comprehensive.c -o /tmp/t && /tmp/t | head -3`
|
||||||
|
|
||||||
|
## 🔧 Debug Features
|
||||||
|
|
||||||
|
Each test includes debug output:
|
||||||
|
```
|
||||||
|
| <statement> |
|
||||||
|
<value>
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
| int x = 10 |
|
||||||
|
10
|
||||||
|
```
|
||||||
|
|
||||||
|
Format specifiers are automatic:
|
||||||
|
- `%d` - integers
|
||||||
|
- `%f` - floats
|
||||||
|
- `%s` - strings
|
||||||
|
- `%p` - pointers
|
||||||
|
|
||||||
|
## 📦 File Organization
|
||||||
|
|
||||||
|
```
|
||||||
|
tests/codegen/
|
||||||
|
├── test_executable.sui ← Source
|
||||||
|
├── test_executable.c ← Generated
|
||||||
|
├── test_array_simple.sui
|
||||||
|
├── test_array_simple.c
|
||||||
|
├── test_enum_simple.sui
|
||||||
|
├── test_enum_simple.c
|
||||||
|
├── test_comprehensive.sui
|
||||||
|
├── test_comprehensive.c
|
||||||
|
├── test_all_types.sui
|
||||||
|
├── test_all_types.c
|
||||||
|
├── README.md ← Documentation
|
||||||
|
├── TESTING.md
|
||||||
|
├── SUMMARY.md
|
||||||
|
└── INDEX.md ← This file
|
||||||
|
```
|
||||||
|
|
||||||
|
Each `.sui` file has a corresponding `.c` file containing the generated C code with debug output.
|
||||||
|
|
||||||
|
## 🎓 Learning Path
|
||||||
|
|
||||||
|
1. Start with `test_executable` - understand basic code generation
|
||||||
|
2. Move to `test_array_simple` - learn array handling
|
||||||
|
3. Check `test_enum_simple` - understand enum codegen
|
||||||
|
4. Study `test_comprehensive` - see everything together
|
||||||
|
5. Review `test_all_types` - verify complete coverage
|
||||||
|
|
||||||
|
## 💡 Tips
|
||||||
|
|
||||||
|
- Look at generated `.c` files to understand transpilation
|
||||||
|
- Run tests with output redirection: `./test 2>&1 | less`
|
||||||
|
- Compare `.sui` and `.c` files side by side
|
||||||
|
- Check debug output for execution trace
|
||||||
|
- Modify `.sui` files to experiment with codegen
|
||||||
|
|
||||||
|
## 📞 Getting Help
|
||||||
|
|
||||||
|
See **TESTING.md** for:
|
||||||
|
- Common issues and solutions
|
||||||
|
- Compilation troubleshooting
|
||||||
|
- Debug output interpretation
|
||||||
|
- Verification procedures
|
||||||
|
|
||||||
201
tests/codegen/README.md
Normal file
201
tests/codegen/README.md
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
# Codegen Tests
|
||||||
|
|
||||||
|
This directory contains test cases for the suicmez compiler's code generation (codegen) phase. Each test demonstrates different language features and their corresponding C output.
|
||||||
|
|
||||||
|
## Test Files
|
||||||
|
|
||||||
|
### Basic Tests
|
||||||
|
|
||||||
|
#### `test_executable.sui` / `test_executable.c`
|
||||||
|
**Purpose:** Simple executable test with basic integer arithmetic
|
||||||
|
**Features:**
|
||||||
|
- Basic function definitions
|
||||||
|
- Integer variables and operations
|
||||||
|
- Return statements
|
||||||
|
|
||||||
|
**Run:**
|
||||||
|
```bash
|
||||||
|
suicmez tests/codegen/test_executable.sui
|
||||||
|
gcc tests/codegen/test_executable.c -o test_executable
|
||||||
|
./test_executable
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `test_array_simple.sui` / `test_array_simple.c`
|
||||||
|
**Purpose:** Test heap-allocated arrays with indexing
|
||||||
|
**Features:**
|
||||||
|
- Array literals: `[1, 2, 3]`
|
||||||
|
- Array indexing: `arr[0]`
|
||||||
|
- Arrays as pointers: `int*`
|
||||||
|
- C99 compound literals for array initialization
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
```
|
||||||
|
| int* arr = (int[]){1, 2, 3} |
|
||||||
|
0x...
|
||||||
|
| int x = arr[0] |
|
||||||
|
1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `test_enum_simple.sui` / `test_enum_simple.c`
|
||||||
|
**Purpose:** Test enum types with discriminant + union pattern
|
||||||
|
**Features:**
|
||||||
|
- Enum variants
|
||||||
|
- Discriminant field for pattern matching
|
||||||
|
- Union-based storage for variant data
|
||||||
|
- Proper struct ordering
|
||||||
|
|
||||||
|
**Generated Structures:**
|
||||||
|
```c
|
||||||
|
struct Color_Red { };
|
||||||
|
struct Color_Green { };
|
||||||
|
struct Color_Blue { };
|
||||||
|
struct Color_union {
|
||||||
|
struct Color_Red red;
|
||||||
|
struct Color_Green green;
|
||||||
|
struct Color_Blue blue;
|
||||||
|
};
|
||||||
|
struct Color {
|
||||||
|
int discriminant;
|
||||||
|
struct Color_union data;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comprehensive Tests
|
||||||
|
|
||||||
|
#### `test_comprehensive.sui` / `test_comprehensive.c`
|
||||||
|
**Purpose:** Combined test of all major features
|
||||||
|
**Features:**
|
||||||
|
- Stack-allocated types: `int`, `float`, `bool`
|
||||||
|
- Heap-allocated types: arrays, strings
|
||||||
|
- Struct instantiation and field access
|
||||||
|
- Enum creation and initialization
|
||||||
|
- Function calls with multiple parameters
|
||||||
|
- Complex expressions
|
||||||
|
|
||||||
|
**Demonstrates:**
|
||||||
|
- Proper memory management distinctions
|
||||||
|
- Type conversions and initializations
|
||||||
|
- Debug output for each statement
|
||||||
|
|
||||||
|
**Run:**
|
||||||
|
```bash
|
||||||
|
suicmez tests/codegen/test_comprehensive.sui
|
||||||
|
gcc tests/codegen/test_comprehensive.c -o test_comprehensive
|
||||||
|
./test_comprehensive
|
||||||
|
```
|
||||||
|
|
||||||
|
**Sample Output:**
|
||||||
|
```
|
||||||
|
| int x = 10 |
|
||||||
|
10
|
||||||
|
| int y = 20 |
|
||||||
|
20
|
||||||
|
| int sum = add(x, y) |
|
||||||
|
30
|
||||||
|
| struct Point p = (struct Point){ .x = 5, .y = 15 } |
|
||||||
|
5
|
||||||
|
| int* arr = (int[]){1, 2, 3, 4, 5} |
|
||||||
|
0x7ffd6d57d0b0
|
||||||
|
| int first = arr[0] |
|
||||||
|
1
|
||||||
|
| char* msg = "hello" |
|
||||||
|
hello
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `test_all_types.sui` / `test_all_types.c`
|
||||||
|
**Purpose:** Exhaustive test of all language types
|
||||||
|
**Features:**
|
||||||
|
- All primitive types: `int`, `float`, `bool`
|
||||||
|
- Strings with heap allocation
|
||||||
|
- Structs with multiple fields
|
||||||
|
- Enums with discriminant
|
||||||
|
- Arrays with indexing
|
||||||
|
|
||||||
|
**Run:**
|
||||||
|
```bash
|
||||||
|
suicmez tests/codegen/test_all_types.sui
|
||||||
|
gcc tests/codegen/test_all_types.c -o test_all_types
|
||||||
|
./test_all_types
|
||||||
|
```
|
||||||
|
|
||||||
|
## Memory Management Strategy
|
||||||
|
|
||||||
|
The codegen implements the following allocation strategy:
|
||||||
|
|
||||||
|
### Stack-Allocated (Copyable)
|
||||||
|
- `int`, `float`, `bool`, `char` - passed by value
|
||||||
|
- Structs (stored directly, not as pointers)
|
||||||
|
- Enums (stored directly with discriminant + union)
|
||||||
|
|
||||||
|
### Heap-Allocated (with Debug Visibility)
|
||||||
|
- **Arrays**: `int*` pointers created with C99 compound literals
|
||||||
|
```c
|
||||||
|
int* arr = (int[]){1, 2, 3, 4, 5};
|
||||||
|
```
|
||||||
|
- **Strings**: `char*` pointers to string constants
|
||||||
|
```c
|
||||||
|
char* msg = "hello";
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debug Output Feature
|
||||||
|
|
||||||
|
Every statement generates debug output showing:
|
||||||
|
1. The executed statement
|
||||||
|
2. The resulting value (if applicable)
|
||||||
|
|
||||||
|
**Format:**
|
||||||
|
```c
|
||||||
|
int x = 10;
|
||||||
|
printf("| int x = 10 | \n %d\n", x);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
```
|
||||||
|
| int x = 10 |
|
||||||
|
10
|
||||||
|
```
|
||||||
|
|
||||||
|
### Format Specifiers
|
||||||
|
|
||||||
|
The system automatically selects the correct format:
|
||||||
|
- `%d` - integers and booleans
|
||||||
|
- `%f` - floats
|
||||||
|
- `%s` - strings (char*)
|
||||||
|
- `%p` - pointers and arrays
|
||||||
|
|
||||||
|
## Compilation
|
||||||
|
|
||||||
|
All generated C code compiles with standard C99:
|
||||||
|
```bash
|
||||||
|
gcc -std=c99 <file>.c -o <executable>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running Tests Manually
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate C code from .sui file
|
||||||
|
suicmez tests/codegen/test_comprehensive.sui
|
||||||
|
|
||||||
|
# Compile the generated C code
|
||||||
|
gcc tests/codegen/test_comprehensive.c -o /tmp/test_comprehensive
|
||||||
|
|
||||||
|
# Run with debug output
|
||||||
|
/tmp/test_comprehensive
|
||||||
|
```
|
||||||
|
|
||||||
|
## Expected Behavior
|
||||||
|
|
||||||
|
All tests should:
|
||||||
|
1. ✅ Compile successfully with GCC
|
||||||
|
2. ✅ Generate valid C99 code
|
||||||
|
3. ✅ Execute without errors
|
||||||
|
4. ✅ Display debug output for each statement
|
||||||
|
5. ✅ Return correct exit codes
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
- Structs are declared before enums in the output to avoid forward references
|
||||||
|
- Enum variant structs are generated before the union
|
||||||
|
- Array types use C99 compound literals for initialization
|
||||||
|
- All pointers are properly typed (not generic `void*`)
|
||||||
|
- Debug output uses `printf` with appropriate format specifiers
|
||||||
175
tests/codegen/SUMMARY.md
Normal file
175
tests/codegen/SUMMARY.md
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
# Codegen Tests - Summary
|
||||||
|
|
||||||
|
## Organization
|
||||||
|
|
||||||
|
The codegen tests have been organized into a dedicated `tests/codegen/` subdirectory with the following structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
tests/codegen/
|
||||||
|
├── README.md # Comprehensive documentation
|
||||||
|
├── TESTING.md # Testing instructions
|
||||||
|
├── SUMMARY.md # This file
|
||||||
|
├── test_executable.* # Simple arithmetic test
|
||||||
|
├── test_array_simple.* # Array operations test
|
||||||
|
├── test_enum_simple.* # Enum types test
|
||||||
|
├── test_comprehensive.* # Mixed types test
|
||||||
|
└── test_all_types.* # Type coverage test
|
||||||
|
```
|
||||||
|
|
||||||
|
Each test has two files:
|
||||||
|
- `.sui` - Source code in the suicmez language
|
||||||
|
- `.c` - Generated C code with debug output
|
||||||
|
|
||||||
|
## What Was Cleaned Up
|
||||||
|
|
||||||
|
Removed from root directory:
|
||||||
|
- `simple.sui`, `simple_test.*` - Incomplete test files
|
||||||
|
- `syntax_test.sui` - Old syntax exploration
|
||||||
|
- `test_affine.sui`, `test_binding.sui` - Abandoned tests
|
||||||
|
- `test_linear*.sui` - Linear type system experiments
|
||||||
|
- `test_scoping.sui`, `test_shadow.sui` - Scoping tests
|
||||||
|
- Other misc test files
|
||||||
|
|
||||||
|
Kept in root (valid existing tests):
|
||||||
|
- None - all valid tests are now organized in `tests/` subdirectories
|
||||||
|
|
||||||
|
## Test Files
|
||||||
|
|
||||||
|
### New Codegen Tests (in `tests/codegen/`)
|
||||||
|
|
||||||
|
1. **test_executable** - Minimal executable test
|
||||||
|
- Basic integer operations
|
||||||
|
- Function calls
|
||||||
|
- Return values
|
||||||
|
|
||||||
|
2. **test_array_simple** - Array functionality
|
||||||
|
- Array literals: `[1, 2, 3]`
|
||||||
|
- Array indexing: `arr[0]`
|
||||||
|
- Pointer-based array representation
|
||||||
|
|
||||||
|
3. **test_enum_simple** - Enumeration types
|
||||||
|
- Enum variants
|
||||||
|
- Discriminant + union pattern
|
||||||
|
- Struct ordering
|
||||||
|
|
||||||
|
4. **test_comprehensive** - Combined features
|
||||||
|
- All basic types (int, float, bool, string)
|
||||||
|
- Structs with multiple fields
|
||||||
|
- Enums with variants
|
||||||
|
- Arrays with complex operations
|
||||||
|
- Function calls with parameters
|
||||||
|
|
||||||
|
5. **test_all_types** - Complete type coverage
|
||||||
|
- Exhaustive test of all language types
|
||||||
|
- Demonstrates each type independently
|
||||||
|
- Shows debug output for each
|
||||||
|
|
||||||
|
### Existing Tests (preserved in `tests/`)
|
||||||
|
|
||||||
|
- `arrays.sui` - Additional array tests
|
||||||
|
- `basic_types.sui/c` - Basic type tests
|
||||||
|
- `control_flow.sui/c` - If/while/for loops
|
||||||
|
- `enums.sui` - Enum functionality
|
||||||
|
- `functions.sui/c` - Function definitions
|
||||||
|
- `generics_comprehensive.sui/c` - Generic types
|
||||||
|
- `import_tests/` - Module imports
|
||||||
|
- `pointers.sui` - Pointer operations
|
||||||
|
- `structs.sui/c` - Struct definitions
|
||||||
|
- `traits.sui/c` - Trait system
|
||||||
|
- And more...
|
||||||
|
|
||||||
|
## Features Demonstrated
|
||||||
|
|
||||||
|
### Memory Management
|
||||||
|
- Stack allocation: `int`, `float`, `bool`
|
||||||
|
- Heap allocation: arrays with pointers
|
||||||
|
- Strings as `char*`
|
||||||
|
- Structs and enums stack-allocated
|
||||||
|
|
||||||
|
### Language Features
|
||||||
|
- Variable declarations and assignments
|
||||||
|
- Function definitions and calls
|
||||||
|
- Struct literals with initialization
|
||||||
|
- Enum variants with discriminants
|
||||||
|
- Array literals and indexing
|
||||||
|
- Type conversions (implicit)
|
||||||
|
|
||||||
|
### Debug Output
|
||||||
|
Every statement generates debug output:
|
||||||
|
```c
|
||||||
|
int x = 10;
|
||||||
|
printf("| int x = 10 | \n %d\n", x);
|
||||||
|
// Output: | int x = 10 |
|
||||||
|
// 10
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the Tests
|
||||||
|
|
||||||
|
### Quick Check
|
||||||
|
```bash
|
||||||
|
cd /home/nafi/langjam/suicmez
|
||||||
|
for f in tests/codegen/*.sui; do
|
||||||
|
./target/debug/suicmez "$f" > /dev/null && echo "✓ $(basename $f)"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### Full Test Run
|
||||||
|
```bash
|
||||||
|
# Regenerate all C code
|
||||||
|
for f in tests/codegen/*.sui; do
|
||||||
|
./target/debug/suicmez "$f"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Compile all tests
|
||||||
|
for f in tests/codegen/*.c; do
|
||||||
|
gcc "$f" -o "/tmp/$(basename ${f%.c})"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Run all tests
|
||||||
|
for exe in /tmp/test_*; do
|
||||||
|
echo "Running $(basename $exe)..."
|
||||||
|
"$exe" 2>&1 | head -5
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### Individual Test
|
||||||
|
```bash
|
||||||
|
./target/debug/suicmez tests/codegen/test_comprehensive.sui
|
||||||
|
gcc tests/codegen/test_comprehensive.c -o /tmp/test
|
||||||
|
/tmp/test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quality Assurance
|
||||||
|
|
||||||
|
All codegen tests:
|
||||||
|
- ✅ Generate valid C99 code
|
||||||
|
- ✅ Compile with GCC without errors
|
||||||
|
- ✅ Execute successfully
|
||||||
|
- ✅ Display proper debug output
|
||||||
|
- ✅ Return correct values
|
||||||
|
- ✅ Include comprehensive documentation
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- **README.md** - Feature overview and structure
|
||||||
|
- **TESTING.md** - How to run and troubleshoot tests
|
||||||
|
- **SUMMARY.md** - This file, organization summary
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
To add new codegen tests:
|
||||||
|
1. Create `test_feature.sui` in `tests/codegen/`
|
||||||
|
2. Run transpiler: `./target/debug/suicmez tests/codegen/test_feature.sui`
|
||||||
|
3. Verify C code: `gcc tests/codegen/test_feature.c -c`
|
||||||
|
4. Test execution: `gcc tests/codegen/test_feature.c -o /tmp/test && /tmp/test`
|
||||||
|
5. Document in README.md
|
||||||
|
|
||||||
|
## Files Status
|
||||||
|
|
||||||
|
✅ All files properly organized
|
||||||
|
✅ All tests generate valid C code
|
||||||
|
✅ All tests compile and run
|
||||||
|
✅ Documentation complete
|
||||||
|
✅ Debug output working
|
||||||
|
✅ Root directory cleaned
|
||||||
115
tests/codegen/TESTING.md
Normal file
115
tests/codegen/TESTING.md
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
# Running the Codegen Tests
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Regenerate C code from all tests
|
||||||
|
for f in tests/codegen/*.sui; do
|
||||||
|
./target/debug/suicmez "$f"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Compile and run a single test
|
||||||
|
gcc tests/codegen/test_comprehensive.c -o /tmp/test_comprehensive
|
||||||
|
/tmp/test_comprehensive
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Summary
|
||||||
|
|
||||||
|
| Test | Purpose | Input | Output |
|
||||||
|
|------|---------|-------|--------|
|
||||||
|
| `test_executable.sui` | Simple arithmetic | Basic int operations | Exit code with result |
|
||||||
|
| `test_array_simple.sui` | Array operations | Array literal and indexing | Debug output with array values |
|
||||||
|
| `test_enum_simple.sui` | Enum types | Enum variant creation | Discriminant value |
|
||||||
|
| `test_comprehensive.sui` | Mixed types | All basic + complex types | Full debug trace |
|
||||||
|
| `test_all_types.sui` | Type coverage | Every language type | Debug output for each |
|
||||||
|
|
||||||
|
## Running Individual Tests
|
||||||
|
|
||||||
|
### 1. Array Test
|
||||||
|
```bash
|
||||||
|
./target/debug/suicmez tests/codegen/test_array_simple.sui
|
||||||
|
gcc tests/codegen/test_array_simple.c -o /tmp/array_test
|
||||||
|
/tmp/array_test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected Output:**
|
||||||
|
```
|
||||||
|
| int* arr = (int[]){1, 2, 3} |
|
||||||
|
0x...
|
||||||
|
| int x = arr[0] |
|
||||||
|
1
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Enum Test
|
||||||
|
```bash
|
||||||
|
./target/debug/suicmez tests/codegen/test_enum_simple.sui
|
||||||
|
gcc tests/codegen/test_enum_simple.c -o /tmp/enum_test
|
||||||
|
/tmp/enum_test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected Output:**
|
||||||
|
```
|
||||||
|
| struct Color c = (struct Color){ .discriminant = 0, .data = { .red = (struct Color_Red){} } } |
|
||||||
|
0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Comprehensive Test
|
||||||
|
```bash
|
||||||
|
./target/debug/suicmez tests/codegen/test_comprehensive.sui
|
||||||
|
gcc tests/codegen/test_comprehensive.c -o /tmp/comprehensive_test
|
||||||
|
/tmp/comprehensive_test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected Output:** (full debug trace of all operations)
|
||||||
|
```
|
||||||
|
| int x = 10 |
|
||||||
|
10
|
||||||
|
| int y = 20 |
|
||||||
|
20
|
||||||
|
| int sum = add(x, y) |
|
||||||
|
30
|
||||||
|
... (more output)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging the Codegen
|
||||||
|
|
||||||
|
Each test's generated C file includes debug `printf` statements. To see what's happening:
|
||||||
|
|
||||||
|
1. **Look at the generated C code:**
|
||||||
|
```bash
|
||||||
|
cat tests/codegen/test_comprehensive.c
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Run with debug output:**
|
||||||
|
```bash
|
||||||
|
gcc tests/codegen/test_comprehensive.c -o /tmp/test
|
||||||
|
/tmp/test 2>&1 | head -20
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Check for compilation errors:**
|
||||||
|
```bash
|
||||||
|
gcc tests/codegen/test_comprehensive.c -c -o /tmp/test.o
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verifying Correctness
|
||||||
|
|
||||||
|
All tests should:
|
||||||
|
- ✅ Generate valid C99 code
|
||||||
|
- ✅ Compile without warnings
|
||||||
|
- ✅ Execute without segmentation faults
|
||||||
|
- ✅ Show proper debug output for each statement
|
||||||
|
|
||||||
|
## Common Issues
|
||||||
|
|
||||||
|
### Test fails to compile
|
||||||
|
- Check that the `.sui` file was properly transpiled
|
||||||
|
- Regenerate with: `./target/debug/suicmez tests/codegen/test_name.sui`
|
||||||
|
|
||||||
|
### Debug output missing
|
||||||
|
- Ensure the printf statements are in the generated C code
|
||||||
|
- Check for return statements that might skip debug output
|
||||||
|
|
||||||
|
### Wrong values printed
|
||||||
|
- The debug output uses automatic format specifiers (`%d`, `%f`, `%s`, `%p`)
|
||||||
|
- Struct types print the first field value
|
||||||
|
- Arrays print the pointer address
|
||||||
29
tests/codegen/test_all_types.sui
Normal file
29
tests/codegen/test_all_types.sui
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
struct Point
|
||||||
|
x: int,
|
||||||
|
y: int,
|
||||||
|
end
|
||||||
|
|
||||||
|
enum Result
|
||||||
|
Ok,
|
||||||
|
Error,
|
||||||
|
end
|
||||||
|
|
||||||
|
fn main() -> int do
|
||||||
|
# Test basic types
|
||||||
|
let int_val = 42;
|
||||||
|
let float_val = 3.14;
|
||||||
|
let bool_val = true;
|
||||||
|
let str_val = "Hello, World!";
|
||||||
|
|
||||||
|
# Test struct
|
||||||
|
let point = Point { x: 10, y: 20 };
|
||||||
|
|
||||||
|
# Test enum
|
||||||
|
let result = Result::Ok();
|
||||||
|
|
||||||
|
# Test array
|
||||||
|
let numbers = [1, 2, 3, 4, 5];
|
||||||
|
let first_num = numbers[0];
|
||||||
|
|
||||||
|
first_num
|
||||||
|
end
|
||||||
5
tests/codegen/test_array_simple.sui
Normal file
5
tests/codegen/test_array_simple.sui
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
fn main() -> int do
|
||||||
|
let arr = [1, 2, 3];
|
||||||
|
let x = arr[0];
|
||||||
|
x
|
||||||
|
end
|
||||||
36
tests/codegen/test_comprehensive.sui
Normal file
36
tests/codegen/test_comprehensive.sui
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
struct Point
|
||||||
|
x: int,
|
||||||
|
y: int,
|
||||||
|
end
|
||||||
|
|
||||||
|
enum Status
|
||||||
|
Ok,
|
||||||
|
Error,
|
||||||
|
end
|
||||||
|
|
||||||
|
fn add(a: int, b: int) -> int do
|
||||||
|
a + b
|
||||||
|
end
|
||||||
|
|
||||||
|
fn main() -> int do
|
||||||
|
# Test stack-allocated types (copyable)
|
||||||
|
let x = 10;
|
||||||
|
let y = 20;
|
||||||
|
let sum = add(x, y);
|
||||||
|
|
||||||
|
# Test struct (stack-allocated for now, but declared as such in language)
|
||||||
|
let p = Point { x: 5, y: 15 };
|
||||||
|
let point_sum = p.x + p.y;
|
||||||
|
|
||||||
|
# Test enum (stack-allocated for now)
|
||||||
|
let status = Status::Ok();
|
||||||
|
|
||||||
|
# Test array (heap-allocated)
|
||||||
|
let arr = [1, 2, 3, 4, 5];
|
||||||
|
let first = arr[0];
|
||||||
|
|
||||||
|
# Test string (heap-allocated)
|
||||||
|
let msg = "hello";
|
||||||
|
sum + point_sum + first
|
||||||
|
|
||||||
|
end
|
||||||
10
tests/codegen/test_enum_simple.sui
Normal file
10
tests/codegen/test_enum_simple.sui
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
enum Color
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Blue,
|
||||||
|
end
|
||||||
|
|
||||||
|
fn main() -> int do
|
||||||
|
let c = Color::Red();
|
||||||
|
0
|
||||||
|
end
|
||||||
5
tests/codegen/test_executable.sui
Normal file
5
tests/codegen/test_executable.sui
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
fn main() -> int do
|
||||||
|
let x = 10;
|
||||||
|
let y = 20;
|
||||||
|
x + y
|
||||||
|
end
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
int main(void);
|
|
||||||
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
if (true) {
|
|
||||||
1;
|
|
||||||
} else {
|
|
||||||
0;
|
|
||||||
}
|
|
||||||
int i = 0;
|
|
||||||
while ((i < 5)) {
|
|
||||||
i = (i + 1);
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
int add(int x, int y);
|
|
||||||
int main(void);
|
|
||||||
|
|
||||||
|
|
||||||
int add(int x, int y) {
|
|
||||||
return (x + y);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
int sum = add(5, 3);
|
|
||||||
struct T id = identity(42);
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
96
tests/game_3d_improved.sui
Normal file
96
tests/game_3d_improved.sui
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
# 3D Physics Game with Raylib and ODE - Improved with Collision Detection
|
||||||
|
# A bouncing cube demo that actually collides with the ground
|
||||||
|
|
||||||
|
fn main() -> int do
|
||||||
|
# Initialize raylib
|
||||||
|
init_window(800, 600, "3D Physics Game - Improved")
|
||||||
|
defer close_window()
|
||||||
|
set_target_fps(60)
|
||||||
|
|
||||||
|
# Initialize ODE
|
||||||
|
ode_init()
|
||||||
|
defer ode_close()
|
||||||
|
|
||||||
|
# Create physics world
|
||||||
|
let world = ode_world_create()
|
||||||
|
defer ode_world_destroy(world)
|
||||||
|
let null_space = 0 as *()
|
||||||
|
let space = ode_simple_space_create(null_space) # null parent
|
||||||
|
defer ode_space_destroy(space)
|
||||||
|
|
||||||
|
# Set gravity
|
||||||
|
ode_world_set_gravity(world, 0.0, -9.81, 0.0)
|
||||||
|
|
||||||
|
# Create contact joint group
|
||||||
|
let contactgroup = ode_joint_group_create(0)
|
||||||
|
defer ode_joint_group_destroy(contactgroup)
|
||||||
|
|
||||||
|
# Create a cube body
|
||||||
|
let body = ode_body_create(world)
|
||||||
|
defer ode_body_destroy(body)
|
||||||
|
|
||||||
|
# Set cube mass (density 1.0, size 1x1x1)
|
||||||
|
ode_body_set_box_mass(body, 1.0, 1.0, 1.0, 1.0)
|
||||||
|
|
||||||
|
# Set initial position (5 units up)
|
||||||
|
ode_body_set_position(body, 0.0, 5.0, 0.0)
|
||||||
|
|
||||||
|
# Create geometry for the cube
|
||||||
|
let geom = ode_create_box_geom(space, 1.0, 1.0, 1.0)
|
||||||
|
defer ode_geom_destroy(geom)
|
||||||
|
|
||||||
|
# Attach geometry to body
|
||||||
|
ode_geom_set_body(geom, body)
|
||||||
|
|
||||||
|
# Create ground plane (y = 0)
|
||||||
|
let ground_geom = ode_create_plane_geom(space, 0.0, 1.0, 0.0, 0.0)
|
||||||
|
defer ode_geom_destroy(ground_geom)
|
||||||
|
|
||||||
|
# Camera position variables
|
||||||
|
let mut camera_pos_x = 0.0
|
||||||
|
let mut camera_pos_y = 2.0
|
||||||
|
let mut camera_pos_z = 10.0
|
||||||
|
|
||||||
|
# Cube position variables
|
||||||
|
let mut cube_x = 0.0
|
||||||
|
let mut cube_y = 0.0
|
||||||
|
let mut cube_z = 0.0
|
||||||
|
|
||||||
|
# Main game loop
|
||||||
|
let mut frame_count = 0
|
||||||
|
while window_should_close() == false do
|
||||||
|
# IMPORTANT: Collision detection must happen BEFORE world step
|
||||||
|
ode_space_collide(world, space, contactgroup)
|
||||||
|
|
||||||
|
# Step physics simulation
|
||||||
|
ode_world_step(world, 1.0 / 60.0)
|
||||||
|
|
||||||
|
# Empty contact group after step to avoid accumulation
|
||||||
|
ode_joint_group_empty(contactgroup)
|
||||||
|
|
||||||
|
# Get cube position
|
||||||
|
ode_body_get_position(body, &cube_x, &cube_y, &cube_z)
|
||||||
|
|
||||||
|
# Drawing
|
||||||
|
begin_drawing()
|
||||||
|
clear_background(135, 206, 235, 255) # Sky blue
|
||||||
|
|
||||||
|
begin_mode3d(camera_pos_x, camera_pos_y, camera_pos_z, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 45.0, 0)
|
||||||
|
|
||||||
|
# Draw ground plane
|
||||||
|
draw_cube(0.0, -1.0, 0.0, 20.0, 0.1, 20.0, 34, 139, 34, 255) # Green ground
|
||||||
|
|
||||||
|
# Draw the physics cube
|
||||||
|
draw_cube(cube_x, cube_y, cube_z, 1.0, 1.0, 1.0, 255, 0, 0, 255) # Red cube
|
||||||
|
|
||||||
|
# Draw wireframe for better visibility
|
||||||
|
draw_cube_wires(cube_x, cube_y, cube_z, 1.0, 1.0, 1.0, 0, 0, 0, 255)
|
||||||
|
|
||||||
|
end_mode3d()
|
||||||
|
end_drawing()
|
||||||
|
|
||||||
|
frame_count = frame_count + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
struct Box_string {
|
|
||||||
struct T value;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Option_bool_union {
|
|
||||||
struct Option_bool_Some some;
|
|
||||||
struct Option_bool_None none;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Option_int_union {
|
|
||||||
struct Option_int_Some some;
|
|
||||||
struct Option_int_None none;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Option_int_None {
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Option_int {
|
|
||||||
int discriminant;
|
|
||||||
struct Option_int_union data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Option_int_Some {
|
|
||||||
struct T field_0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Option_bool_None {
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Option_bool {
|
|
||||||
int discriminant;
|
|
||||||
struct Option_bool_union data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Option_bool_Some {
|
|
||||||
struct T field_0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Box_int {
|
|
||||||
struct T value;
|
|
||||||
};
|
|
||||||
|
|
||||||
int test_containers(void);
|
|
||||||
|
|
||||||
|
|
||||||
int test_containers(void) {
|
|
||||||
struct Box box_int = (struct Box){ .value = 42 };
|
|
||||||
struct Box box_string = (struct Box){ .value = "Fermented" };
|
|
||||||
struct Option some_int = (Option){ .discriminant = 0, .data = { .some = (Option_Some{ .field_0 = 10 }) } };
|
|
||||||
struct Option some_bool = (Option){ .discriminant = 0, .data = { .some = (Option_Some{ .field_0 = true }) } };
|
|
||||||
struct T unwrapped = unwrap(some_int);
|
|
||||||
struct T unwraped_bool = unwrap(some_bool);
|
|
||||||
return (box_int.value + unwrapped);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
struct Point {
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
};
|
|
||||||
int helper_func(void);
|
|
||||||
int main(void);
|
|
||||||
|
|
||||||
|
|
||||||
int helper_func(void) {
|
|
||||||
return 42;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
return helper_func();
|
|
||||||
}
|
|
||||||
|
|
||||||
20
tests/ode_test.sui
Normal file
20
tests/ode_test.sui
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# ODE Integration Test
|
||||||
|
fn main() -> int do
|
||||||
|
# Initialize ODE
|
||||||
|
ode_init()
|
||||||
|
|
||||||
|
# Create a world
|
||||||
|
let world = ode_world_create()
|
||||||
|
|
||||||
|
# Set gravity (0, -9.81, 0)
|
||||||
|
ode_world_set_gravity(world, 0.0, -9.81, 0.0)
|
||||||
|
|
||||||
|
# Step the simulation
|
||||||
|
ode_world_step(world, 0.016) # 60 FPS timestep
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
ode_world_destroy(world)
|
||||||
|
ode_close()
|
||||||
|
|
||||||
|
0
|
||||||
|
end
|
||||||
43
tests/ode_voxel_example.sui
Normal file
43
tests/ode_voxel_example.sui
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
# ODE Voxel Physics Example
|
||||||
|
fn main() -> int do
|
||||||
|
# Initialize ODE
|
||||||
|
ode_init()
|
||||||
|
|
||||||
|
# Create world and space
|
||||||
|
let world = ode_world_create()
|
||||||
|
let space = ode_simple_space_create(0 as *()) # null parent
|
||||||
|
|
||||||
|
# Set gravity
|
||||||
|
ode_world_set_gravity(world, 0.0, -9.81, 0.0)
|
||||||
|
|
||||||
|
# Create a box body
|
||||||
|
let body = ode_body_create(world)
|
||||||
|
|
||||||
|
# Set box mass (density 1.0, size 1x1x1)
|
||||||
|
ode_body_set_box_mass(body, 1.0, 1.0, 1.0, 1.0)
|
||||||
|
|
||||||
|
# Set initial position (5 units up)
|
||||||
|
ode_body_set_position(body, 0.0, 5.0, 0.0)
|
||||||
|
|
||||||
|
# Create geometry for the box
|
||||||
|
let geom = ode_create_box_geom(space, 1.0, 1.0, 1.0)
|
||||||
|
|
||||||
|
# Attach geometry to body
|
||||||
|
ode_geom_set_body(geom, body)
|
||||||
|
|
||||||
|
# Simulate for a few steps
|
||||||
|
let mut i = 0
|
||||||
|
while i < 10 do
|
||||||
|
ode_world_step(world, 0.016) # 60 FPS
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
ode_geom_destroy(geom)
|
||||||
|
ode_body_destroy(body)
|
||||||
|
ode_space_destroy(space)
|
||||||
|
ode_world_destroy(world)
|
||||||
|
ode_close()
|
||||||
|
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
struct Point {
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
};
|
|
||||||
int main(void);
|
|
||||||
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
struct Point p = (struct Point){ .x = 5, .y = 10 };
|
|
||||||
struct Person person = (struct Person){ .name = "Alice", .age = 30 };
|
|
||||||
return (p.x + person.age);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -4,13 +4,14 @@ struct Point
|
||||||
y: int,
|
y: int,
|
||||||
end
|
end
|
||||||
|
|
||||||
struct Person<T>
|
struct Person
|
||||||
name: string,
|
name: string,
|
||||||
age: T,
|
age: int,
|
||||||
end
|
end
|
||||||
|
|
||||||
fn main() -> int do
|
fn main() -> int do
|
||||||
let p = Point { x: 5, y: 10 };
|
let p = Point { x: 5, y: 10 };
|
||||||
let person = Person { name: "Alice", age: 30 };
|
let person = Person { name: "Alice", age: 30 };
|
||||||
p.x + person.age
|
let _ = p.x + person.age;
|
||||||
|
0
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
struct Number {
|
|
||||||
int value;
|
|
||||||
};
|
|
||||||
char* Number_show(struct Number self);
|
|
||||||
int main(void);
|
|
||||||
|
|
||||||
|
|
||||||
char* Number_show(struct Number self) {
|
|
||||||
return "number";
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
struct Number number = (struct Number){ .value = 40 };
|
|
||||||
Number_show(number);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
BIN
tests/traits.sui
Normal file → Executable file
BIN
tests/traits.sui
Normal file → Executable file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue