From 1f6e586b2a3fe7f8ca32ce95659032bafef0ad24 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 11 Mar 2025 17:49:04 -0400 Subject: Support external structs with namespaced methods (also C-strings are now `const char*` instead of `char*`) --- compile.c | 4 ++-- examples/game/box.tm | 8 +++---- examples/game/color.tm | 23 ------------------ examples/game/game.tm | 31 ++++++++----------------- examples/game/player.tm | 29 +++++++++++------------ examples/game/raylib.tm | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ examples/game/world.tm | 47 +++++++++++++++++-------------------- stdlib/c_strings.c | 20 ++++++++-------- stdlib/c_strings.h | 2 +- structs.c | 9 +++---- 10 files changed, 125 insertions(+), 110 deletions(-) delete mode 100644 examples/game/color.tm create mode 100644 examples/game/raylib.tm diff --git a/compile.c b/compile.c index 203c44aa..8943dd99 100644 --- a/compile.c +++ b/compile.c @@ -511,7 +511,7 @@ CORD compile_type(type_t *t) case MutexedType: return "MutexedData_t"; case BoolType: return "Bool_t"; case ByteType: return "Byte_t"; - case CStringType: return "char*"; + case CStringType: return "const char*"; case MomentType: return "Moment_t"; case BigIntType: return "Int_t"; case IntType: return CORD_asprintf("Int%ld_t", Match(t, IntType)->bits); @@ -547,7 +547,7 @@ CORD compile_type(type_t *t) case PointerType: return CORD_cat(compile_type(Match(t, PointerType)->pointed), "*"); case StructType: { auto s = Match(t, StructType); - if (s->external) return CORD_all("struct ", s->name); + if (s->external) return s->name; return CORD_all("struct ", namespace_prefix(s->env, s->env->namespace->parent), s->name, "$$struct"); } case EnumType: { diff --git a/examples/game/box.tm b/examples/game/box.tm index acff269e..03294a0a 100644 --- a/examples/game/box.tm +++ b/examples/game/box.tm @@ -1,9 +1,7 @@ # Defines a struct representing boxes on the terrain -use vectors - use ./world.tm -use ./color.tm +use ./raylib.tm -struct Box(pos:Vec2, size=Vec2(50, 50), color=Color.GRAY, blocking=yes): +struct Box(pos:Vector2, size=Vector2(50, 50), color=Color(0x80,0x80,0x80), blocking=yes): func draw(b:Box): - b.color:draw_rectangle(b.pos, b.size) + DrawRectangleRec(Rectangle(b.pos.x, b.pos.y, b.size.x, b.size.y), b.color) diff --git a/examples/game/color.tm b/examples/game/color.tm deleted file mode 100644 index fef12e60..00000000 --- a/examples/game/color.tm +++ /dev/null @@ -1,23 +0,0 @@ -# Defines a struct used to represent colors using 64-bit floats (0.0 - 1.0), -# which can be used to draw colored rectangles in raylib -use -use vectors - -struct Color(r,g,b:Num,a=1.0): - PLAYER := Color(.1,.1,.6,1.) - GRAY := Color(.4,.4,.4) - LIGHT_GRAY := Color(.7,.7,.7) - GOAL := Color(.1,.5,.0) - - func draw_rectangle(c:Color, pos:Vec2, size:Vec2): - inline C { - DrawRectangle( - (int)(_$pos.x), (int)(_$pos.y), (int)(_$size.x), (int)(_$size.y), - ((Color){ - (int8_t)(uint8_t)(255.*_$c.r), - (int8_t)(uint8_t)(255.*_$c.g), - (int8_t)(uint8_t)(255.*_$c.b), - (int8_t)(uint8_t)(255.*_$c.a), - }) - ); - } diff --git a/examples/game/game.tm b/examples/game/game.tm index 86e4e01c..e0bf7b32 100644 --- a/examples/game/game.tm +++ b/examples/game/game.tm @@ -1,40 +1,29 @@ # This game demo uses Raylib to present a simple -use libraylib.so -use -use - +use ./raylib.tm use ./world.tm func main(map=(./map.txt)): - extern InitWindow:func(w:Int32, h:Int32, title:CString) InitWindow(1600, 900, CString("raylib [core] example - 2d camera")) map_contents := map:read() or exit("Could not find the game map: $map") - World.CURRENT:load_map(map_contents) + world := @World( + player=@Player(Vector2(0,0), Vector2(0,0)), + goal=@Box(Vector2(0,0), Vector2(50,50), color=Color(0x10,0xa0,0x10)), + boxes=@[:@Box], + ) + world:load_map(map_contents) - extern SetTargetFPS:func(fps:Int32) SetTargetFPS(60) - extern WindowShouldClose:func(->Bool) - while not WindowShouldClose(): - extern GetFrameTime:func(->Num32) dt := GetFrameTime() - World.CURRENT:update(Num(dt)) + world:update(dt) - extern BeginDrawing:func() BeginDrawing() - - inline C { - ClearBackground((Color){0xCC, 0xCC, 0xCC, 0xFF}); - } - - World.CURRENT:draw() - - extern EndDrawing:func() + ClearBackground(Color(0xCC, 0xCC, 0xCC, 0xFF)) + world:draw() EndDrawing() - extern CloseWindow:func() CloseWindow() diff --git a/examples/game/player.tm b/examples/game/player.tm index a5c39a8d..dac2508a 100644 --- a/examples/game/player.tm +++ b/examples/game/player.tm @@ -1,25 +1,22 @@ # Defines a struct representing the player, which is controlled by WASD keys -use libraylib.so -use -use - -use vectors use ./world.tm +use ./raylib.tm -struct Player(pos,prev_pos:Vec2): - WALK_SPEED := 500. - ACCEL := 0.3 - FRICTION := 0.99 - SIZE := Vec2(30, 30) +struct Player(pos,prev_pos:Vector2): + WALK_SPEED := Num32(500.) + ACCEL := Num32(0.3) + FRICTION := Num32(0.99) + SIZE := Vector2(30, 30) + COLOR := Color(0x60, 0x60, 0xbF) func update(p:@Player): - target_x := inline C:Num { - (Num_t)((IsKeyDown(KEY_A) ? -1 : 0) + (IsKeyDown(KEY_D) ? 1 : 0)) + target_x := inline C:Num32 { + (Num32_t)((IsKeyDown(KEY_A) ? -1 : 0) + (IsKeyDown(KEY_D) ? 1 : 0)) } - target_y := inline C:Num { - (Num_t)((IsKeyDown(KEY_W) ? -1 : 0) + (IsKeyDown(KEY_S) ? 1 : 0)) + target_y := inline C:Num32 { + (Num32_t)((IsKeyDown(KEY_W) ? -1 : 0) + (IsKeyDown(KEY_S) ? 1 : 0)) } - target_vel := Vec2(target_x, target_y):norm() * Player.WALK_SPEED + target_vel := Vector2(target_x, target_y):norm() * Player.WALK_SPEED vel := (p.pos - p.prev_pos)/World.DT vel *= Player.FRICTION @@ -28,4 +25,4 @@ struct Player(pos,prev_pos:Vec2): p.prev_pos, p.pos = p.pos, p.pos + World.DT*vel func draw(p:Player): - Color.PLAYER:draw_rectangle(p.pos, Player.SIZE) + DrawRectangleRec(Rectangle(p.pos.x, p.pos.y, Player.SIZE.x, Player.SIZE.y), Player.COLOR) diff --git a/examples/game/raylib.tm b/examples/game/raylib.tm new file mode 100644 index 00000000..2721d0e1 --- /dev/null +++ b/examples/game/raylib.tm @@ -0,0 +1,62 @@ +# Raylib wrapper for some functions and structs +use libraylib.so +use +use + +struct Color(r,g,b:Byte,a=Byte(255); extern) +struct Rectangle(x,y,width,height:Num32; extern): + func draw(r:Rectangle, color:Color): + DrawRectangleRec(r, color) + +struct Vector2(x,y:Num32; extern): + ZERO := Vector2(0, 0) + func plus(a,b:Vector2->Vector2; inline): + return Vector2(a.x+b.x, a.y+b.y) + func minus(a,b:Vector2->Vector2; inline): + return Vector2(a.x-b.x, a.y-b.y) + func times(a,b:Vector2->Vector2; inline): + return Vector2(a.x*b.x, a.y*b.y) + func negative(v:Vector2->Vector2; inline): + return Vector2(-v.x, -v.y) + func dot(a,b:Vector2->Num32; inline): + return ((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y))! + func cross(a,b:Vector2->Num32; inline): + return a.x*b.y - a.y*b.x + func scaled_by(v:Vector2, k:Num32->Vector2; inline): + return Vector2(v.x*k, v.y*k) + func divided_by(v:Vector2, divisor:Num32->Vector2; inline): + return Vector2(v.x/divisor, v.y/divisor) + func length(v:Vector2->Num32; inline): + return (v.x*v.x + v.y*v.y)!:sqrt() + func dist(a,b:Vector2->Num32; inline): + return a:minus(b):length() + func angle(v:Vector2->Num32; inline): + return Num32.atan2(v.y, v.x) + func norm(v:Vector2->Vector2; inline): + if v.x == 0 and v.y == 0: + return v + len := v:length() + return Vector2(v.x/len, v.y/len) + func rotated(v:Vector2, radians:Num32 -> Vector2): + cos := radians:cos() or return v + sin := radians:sin() or return v + return Vector2(cos*v.x - sin*v.y, sin*v.x + cos*v.y) + func mix(a,b:Vector2, amount:Num32 -> Vector2): + return Vector2( + amount:mix(a.x, b.x), + amount:mix(a.y, b.y), + ) + +extern InitWindow:func(width:Int32, height:Int32, title:CString) +extern SetTargetFPS:func(fps:Int32) +extern WindowShouldClose:func(->Bool) +extern GetFrameTime:func(->Num32) +extern BeginDrawing:func() +extern EndDrawing:func() +extern CloseWindow:func() +extern ClearBackground:func(color:Color) +extern DrawRectangle:func(x,y,width,height:Int32, color:Color) +extern DrawRectangleRec:func(rec:Rectangle, color:Color) +extern DrawText:func(text:CString, x,y:Int32, text_height:Int32, color:Color) +extern GetScreenWidth:func(->Int32) +extern GetScreenHeight:func(->Int32) diff --git a/examples/game/world.tm b/examples/game/world.tm index f1b65a87..864bd8d2 100644 --- a/examples/game/world.tm +++ b/examples/game/world.tm @@ -1,12 +1,10 @@ -use vectors - use ./player.tm -use ./color.tm +use ./raylib.tm use ./box.tm # Return a displacement relative to `a` that will push it out of `b` -func solve_overlap(a_pos:Vec2, a_size:Vec2, b_pos:Vec2, b_size:Vec2 -> Vec2): +func solve_overlap(a_pos:Vector2, a_size:Vector2, b_pos:Vector2, b_size:Vector2 -> Vector2): a_left := a_pos.x a_right := a_pos.x + a_size.x a_top := a_pos.y @@ -23,27 +21,26 @@ func solve_overlap(a_pos:Vec2, a_size:Vec2, b_pos:Vec2, b_size:Vec2 -> Vec2): # If either axis is not overlapping, then there is no collision: if overlap_x <= 0 or overlap_y <= 0: - return Vec2(0, 0) + return Vector2(0, 0) if overlap_x < overlap_y: if a_right > b_left and a_right < b_right: - return Vec2(-(overlap_x), 0) + return Vector2(-(overlap_x), 0) else if a_left < b_right and a_left > b_left: - return Vec2(overlap_x, 0) + return Vector2(overlap_x, 0) else: if a_top < b_bottom and a_top > b_top: - return Vec2(0, overlap_y) + return Vector2(0, overlap_y) else if a_bottom > b_top and a_bottom < b_bottom: - return Vec2(0, -overlap_y) + return Vector2(0, -overlap_y) - return Vec2(0, 0) + return Vector2(0, 0) -struct World(player:@Player, goal:@Box, boxes:@[@Box], dt_accum=0.0, won=no): - DT := 1./60. - CURRENT := @World(@Player(Vec2(0,0), Vec2(0,0)), @Box(Vec2(0,0), Vec2(0,0), Color.GOAL), @[:@Box]) - STIFFNESS := 0.3 +struct World(player:@Player, goal:@Box, boxes:@[@Box], dt_accum=Num32(0.0), won=no): + DT := (Num32(1.)/Num32(60.))! + STIFFNESS := Num32(0.3) - func update(w:@World, dt:Num): + func update(w:@World, dt:Num32): w.dt_accum += dt while w.dt_accum > 0: w:update_once() @@ -52,7 +49,7 @@ struct World(player:@Player, goal:@Box, boxes:@[@Box], dt_accum=0.0, won=no): func update_once(w:@World): w.player:update() - if solve_overlap(w.player.pos, Player.SIZE, w.goal.pos, w.goal.size) != Vec2(0,0): + if solve_overlap(w.player.pos, Player.SIZE, w.goal.pos, w.goal.size) != Vector2(0,0): w.won = yes # Resolve player overlapping with any boxes: @@ -67,26 +64,24 @@ struct World(player:@Player, goal:@Box, boxes:@[@Box], dt_accum=0.0, won=no): w.player:draw() if w.won: - inline C { - DrawText("WINNER", GetScreenWidth()/2-48*3, GetScreenHeight()/2-24, 48, (Color){0,0,0,0xFF}); - } + DrawText(CString("WINNER"), GetScreenWidth()/Int32(2)-Int32(48*3), GetScreenHeight()/Int32(2)-Int32(24), 48, Color(0,0,0)) func load_map(w:@World, map:Text): if map:has($/[]/): map = map:replace_all({$/[]/="#", $/@{1..}/="@", $/ /=" "}) w.boxes = @[:@Box] - box_size := Vec2(50., 50.) + box_size := Vector2(50., 50.) for y,line in map:lines(): for x,cell in line:split(): if cell == "#": - pos := Vec2((Num(x)-1) * box_size.x, (Num(y)-1) * box_size.y) - box := @Box(pos, size=box_size, color=Color.GRAY) + pos := Vector2((Num32(x)-1) * box_size.x, (Num32(y)-1) * box_size.y) + box := @Box(pos, size=box_size, color=Color(0x80,0x80,0x80)) w.boxes:insert(box) else if cell == "@": - pos := Vec2((Num(x)-1) * box_size.x, (Num(y)-1) * box_size.y) - pos += box_size/2. - Player.SIZE/2. + pos := Vector2((Num32(x)-1) * box_size.x, (Num32(y)-1) * box_size.y) + pos += box_size/Num32(2) - Player.SIZE/Num32(2) w.player = @Player(pos,pos) else if cell == "?": - pos := Vec2((Num(x)-1) * box_size.x, (Num(y)-1) * box_size.y) - w.goal = @Box(pos, size=box_size, color=Color.GOAL) + pos := Vector2((Num32(x)-1) * box_size.x, (Num32(y)-1) * box_size.y) + w.goal.pos = pos diff --git a/stdlib/c_strings.c b/stdlib/c_strings.c index 45f69e1a..7987a234 100644 --- a/stdlib/c_strings.c +++ b/stdlib/c_strings.c @@ -15,7 +15,7 @@ public Text_t CString$as_text(const void *c_string, bool colorize, const TypeInf { (void)info; if (!c_string) return Text("CString"); - Text_t text = Text$from_str(*(char**)c_string); + Text_t text = Text$from_str(*(const char**)c_string); return Text$concat(colorize ? Text("\x1b[34mCString\x1b[m(") : Text("CString("), Text$quoted(text, colorize), Text(")")); } @@ -29,10 +29,10 @@ PUREFUNC public int32_t CString$compare(const void *x, const void *y, const Type if (x == y) return 0; - if (!*(char**)x != !*(char**)y) - return (!*(char**)y) - (!*(char**)x); + if (!*(const char**)x != !*(const char**)y) + return (!*(const char**)y) - (!*(const char**)x); - return strcmp(*(char**)x, *(char**)y); + return strcmp(*(const char**)x, *(const char**)y); } PUREFUNC public bool CString$equal(const void *x, const void *y, const TypeInfo_t *type) @@ -42,13 +42,13 @@ PUREFUNC public bool CString$equal(const void *x, const void *y, const TypeInfo_ PUREFUNC public uint64_t CString$hash(const void *c_str, const TypeInfo_t*) { - if (!*(char**)c_str) return 0; - return siphash24(*(void**)c_str, strlen(*(char**)c_str)); + if (!*(const char**)c_str) return 0; + return siphash24(*(void**)c_str, strlen(*(const char**)c_str)); } PUREFUNC public bool CString$is_none(const void *c_str, const TypeInfo_t*) { - return *(char**)c_str == NULL; + return *(const char**)c_str == NULL; } static void CString$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t*) @@ -66,12 +66,12 @@ static void CString$deserialize(FILE *in, void *out, Array_t *pointers, const Ty char *str = GC_MALLOC_ATOMIC((size_t)len+1); fread(str, sizeof(char), (size_t)len, in); str[len+1] = '\0'; - *(char**)out = str; + *(const char**)out = str; } public const TypeInfo_t CString$info = { - .size=sizeof(char*), - .align=__alignof__(char*), + .size=sizeof(const char*), + .align=__alignof__(const char*), .metamethods={ .hash=CString$hash, .compare=CString$compare, diff --git a/stdlib/c_strings.h b/stdlib/c_strings.h index 7b736a0c..24cf99da 100644 --- a/stdlib/c_strings.h +++ b/stdlib/c_strings.h @@ -7,7 +7,7 @@ #include "types.h" -Text_t CString$as_text(char **str, bool colorize, const TypeInfo_t *info); +Text_t CString$as_text(const char **str, bool colorize, const TypeInfo_t *info); Text_t CString$as_text_simple(const char *str); PUREFUNC int CString$compare(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC bool CString$equal(const void *x, const void *y, const TypeInfo_t *type); diff --git a/structs.c b/structs.c index 27f3a295..fe57f472 100644 --- a/structs.c +++ b/structs.c @@ -51,15 +51,12 @@ CORD compile_struct_header(env_t *env, ast_t *ast) type_t *field_t = get_arg_ast_type(env, field); fields = CORD_all(fields, compile_declaration(field_t, field->name), field_t->tag == BoolType ? ":1" : CORD_EMPTY, ";\n"); } - CORD struct_code = CORD_all("struct ", full_name, "$$struct {\n"); - struct_code = CORD_all(struct_code, "};\n"); + CORD struct_code = def->external ? CORD_EMPTY : CORD_all("struct ", full_name, "$$struct {\n", fields, "};\n"); type_t *t = Table$str_get(*env->types, def->name); return CORD_all( - "struct ", full_name, "$$struct {\n", - fields, - "};\n", + struct_code, "DEFINE_OPTIONAL_TYPE(", compile_type(t), ", ", heap_strf("%zu", unpadded_struct_size(t)), - ", ", namespace_prefix(env, env->namespace), "$Optional", def->name, "$$type);\n" + ", ", namespace_prefix(env, env->namespace), "$Optional", def->name, "$$type);\n" "extern const TypeInfo_t ", full_name, "$$info;\n"); } -- cgit v1.2.3