Skip to content

Commit b8bf1c5

Browse files
committed
Add example plug implemented in C3
1 parent c324a39 commit b8bf1c5

File tree

4 files changed

+2378
-0
lines changed

4 files changed

+2378
-0
lines changed

nob.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,22 @@ void libs(Nob_Cmd *cmd)
3535
nob_cmd_append(cmd, "-l:libraylib.so", "-lm", "-ldl", "-lpthread");
3636
}
3737

38+
bool build_plug_c3(bool force, Nob_Cmd *cmd, const char *output_path, const char **source_paths, size_t source_paths_count)
39+
{
40+
int rebuild_is_needed = nob_needs_rebuild(nob_temp_sprintf("%s.so", output_path), source_paths, source_paths_count);
41+
if (rebuild_is_needed < 0) return false;
42+
if (force || rebuild_is_needed) {
43+
cmd->count = 0;
44+
// TODO: check if c3c compile even exists
45+
// otherwise this is not buildable
46+
nob_cmd_append(cmd, "c3c", "dynamic-lib", "-o", output_path);
47+
nob_da_append_many(cmd, source_paths, source_paths_count);
48+
if (!nob_cmd_run_sync_and_reset(cmd)) return false;
49+
}
50+
51+
return true;
52+
}
53+
3854
bool build_plug_c(bool force, Nob_Cmd *cmd, const char *source_path, const char *output_path)
3955
{
4056
int rebuild_is_needed = nob_needs_rebuild1(output_path, source_path);
@@ -117,6 +133,17 @@ int main(int argc, char **argv)
117133
if (!build_plug_c(force, &cmd, PLUGS_DIR"squares/plug.c", BUILD_DIR"libsquare.so")) return 1;
118134
if (!build_plug_c(force, &cmd, PLUGS_DIR"bezier/plug.c", BUILD_DIR"libbezier.so")) return 1;
119135
if (!build_plug_cxx(force, &cmd, PLUGS_DIR"cpp/plug.cpp", BUILD_DIR"libcpp.so")) return 1;
136+
{
137+
const char *output_path = BUILD_DIR"libc3";
138+
const char *source_paths[] = {
139+
PLUGS_DIR"c3/plug.c3",
140+
PLUGS_DIR"c3/raylib.c3i",
141+
PLUGS_DIR"c3/future.c3"
142+
};
143+
size_t source_paths_count = NOB_ARRAY_LEN(source_paths);
144+
145+
if (!build_plug_c3(force, &cmd, output_path, source_paths, source_paths_count)) return 1;
146+
}
120147

121148
{
122149
const char *output_path = BUILD_DIR"panim";

plugs/c3/future.c3

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
module future;
2+
3+
interface Future {
4+
fn any! poll(any data);
5+
}
6+
7+
struct FutureDone(Future) {
8+
any result;
9+
}
10+
11+
fn any! FutureDone.poll(&self, any data) @dynamic {
12+
return self.result;
13+
}
14+
15+
macro Future done(value) {
16+
return @tclone(FutureDone{@tclone(value)});
17+
}
18+
19+
struct FutureReject(Future) {
20+
anyfault excuse;
21+
}
22+
23+
fn any! FutureReject.poll(&self, any data) @dynamic {
24+
return self.excuse?;
25+
}
26+
27+
fn Future reject(anyfault excuse) {
28+
FutureReject r = {};
29+
r.excuse = excuse;
30+
return @tclone(r);
31+
}
32+
33+
def FutureThenFunction = fn Future(any result);
34+
35+
struct FutureThen(Future) {
36+
Future left;
37+
Future right;
38+
FutureThenFunction f;
39+
}
40+
41+
fn any! FutureThen.poll(&self, any data) @dynamic {
42+
if (self.left != null) {
43+
any result = self.left.poll(data)!;
44+
if (result) {
45+
self.right = self.f(result);
46+
self.left = null;
47+
}
48+
return null;
49+
} else {
50+
assert(self.right != null);
51+
return self.right.poll(data);
52+
}
53+
}
54+
55+
macro Future Future.then(Future left, FutureThenFunction f) {
56+
return @tclone(FutureThen {
57+
.left = left,
58+
.f = f
59+
});
60+
}
61+
62+
def FutureCatchFunction = fn Future(anyfault excuse);
63+
64+
struct FutureCatch(Future) {
65+
Future left;
66+
Future right;
67+
FutureCatchFunction f;
68+
}
69+
70+
fn any! FutureCatch.poll(&self, any data) @dynamic {
71+
if (self.left != null) {
72+
any! result = self.left.poll(data);
73+
if (catch excuse = result) {
74+
self.right = self.f(excuse);
75+
self.left = null;
76+
return null;
77+
}
78+
return result;
79+
} else {
80+
assert(self.right != null);
81+
return self.right.poll(data);
82+
}
83+
}
84+
85+
// NOTE: We don't really need to call it @catch (I don't remember what @ does, I think it's an
86+
// inline macro or something?), but since catch is a C3 keyword we decided to call
87+
// @catch. If it causes any problems in the future we should consider a different naming.
88+
macro Future Future.@catch(Future left, FutureCatchFunction f) {
89+
return @tclone(FutureCatch {
90+
.left = left,
91+
.f = f
92+
});
93+
}
94+
95+
// TODO: future::start() should be a method of a single future and we should have a
96+
// future combinator that accepts slice of futures. We should call it something
97+
// like future::parallel(). Or maybe even future::collect() which returns you the
98+
// list of future results.
99+
fn void! start(Future[] futures) {
100+
bool quit = false;
101+
while (!quit) {
102+
quit = true;
103+
foreach (future: &futures) {
104+
if (future.poll(null)! == null) {
105+
quit = false;
106+
}
107+
}
108+
}
109+
}

plugs/c3/plug.c3

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import std::io;
2+
import std::math;
3+
import raylib5::rl;
4+
import future;
5+
6+
// TODO: signature of PlaySoundFunc is incorrect to save time.
7+
def PlaySoundFunc = fn void();
8+
9+
const float CYCLE_DURATION = 3.0f;
10+
11+
struct Env {
12+
float delta_time;
13+
float screen_width;
14+
float screen_height;
15+
bool rendering;
16+
PlaySoundFunc play_sound;
17+
}
18+
19+
struct Lerp(Future) {
20+
float *place;
21+
float a, b, t;
22+
float duration;
23+
}
24+
25+
fn any! Lerp.poll(&self, any env) @dynamic {
26+
switch (env) {
27+
case Env: {
28+
if (self.t >= 1) return self;
29+
self.t = (self.duration*self.t + env.delta_time)/self.duration;
30+
*self.place = (self.b - self.a)*self.t;
31+
if (self.t >= 1) return self;
32+
return null;
33+
}
34+
default: unreachable();
35+
}
36+
}
37+
38+
fn Future lerp(float *place, float a, float b, float duration) {
39+
return @tclone(Lerp {
40+
.place = place,
41+
.a = a,
42+
.b = b,
43+
.duration = duration,
44+
});
45+
}
46+
47+
struct Parallel(Future) {
48+
Future[] futures;
49+
}
50+
51+
fn any! Parallel.poll(&urmom, any env) @dynamic {
52+
bool finished = true;
53+
foreach (future: &urmom.futures) {
54+
if (future.poll(env)! == null) {
55+
finished = false;
56+
}
57+
}
58+
if (finished) return urmom;
59+
return null;
60+
}
61+
62+
fn Future parallel(Future[] futures) {
63+
return @tclone(Parallel {
64+
.futures = futures,
65+
});
66+
}
67+
68+
struct Seq(Future) {
69+
Future[] futures;
70+
usz index;
71+
}
72+
73+
fn bool Seq.finished(&urmom) {
74+
return urmom.index >= urmom.futures.len;
75+
}
76+
77+
fn any! Seq.poll(&urmom, any env) @dynamic {
78+
if (urmom.finished()) return urmom;
79+
if (urmom.futures[urmom.index].poll(env)!) urmom.index += 1;
80+
if (urmom.finished()) return urmom;
81+
return null;
82+
}
83+
84+
fn Future seq(Future[] futures) {
85+
return @tclone(Seq {
86+
.futures = futures,
87+
});
88+
}
89+
90+
struct State {
91+
float t1, t2;
92+
bool finished;
93+
Future anim;
94+
}
95+
96+
State *state = null;
97+
98+
fn void reset_anim()
99+
{
100+
// TODO: clean up allocator::temp() here
101+
state.anim = parallel(@tclone(Future[*] {
102+
// TODO: Tuck the whole @tclone(Future[*]{ ... }) under the future constructors
103+
// See if variadic args can be applied here
104+
seq(@tclone(Future[*] {
105+
lerp(&state.t1, 0, 1, CYCLE_DURATION),
106+
lerp(&state.t1, 1, 0, CYCLE_DURATION/4),
107+
})),
108+
seq(@tclone(Future[*] {
109+
lerp(&state.t2, 0, 1, CYCLE_DURATION + CYCLE_DURATION/4),
110+
}))
111+
}));
112+
}
113+
114+
fn void plug_init() @export("plug_init")
115+
{
116+
state = mem::new(State);
117+
reset_anim();
118+
}
119+
120+
fn void* plug_pre_reload() @export("plug_pre_reload")
121+
{
122+
return state;
123+
}
124+
125+
fn void plug_post_reload(void *old_state) @export("plug_post_reload")
126+
{
127+
state = old_state;
128+
}
129+
130+
fn void orbit_circle(Env env, float t, float radius, float orbit, Color color)
131+
{
132+
float angle = 2*math::PI*t;
133+
float cx = env.screen_width*0.5;
134+
float cy = env.screen_height*0.5;
135+
float px = cx + math::cos(angle)*orbit;
136+
float py = cy + math::sin(angle)*orbit;
137+
rl::drawCircleV({px, py}, radius, color);
138+
}
139+
140+
fn void plug_update(Env env) @export("plug_update")
141+
{
142+
state.finished = state.anim.poll(&env)!! != null;
143+
rl::clearBackground({0x18, 0x18, 0x18, 0xFF});
144+
145+
float radius = env.screen_width*0.04;
146+
float orbit = env.screen_width*0.25;
147+
Color color = rl::BLUE;
148+
orbit_circle(env, state.t1, radius, orbit, color);
149+
150+
radius = env.screen_width*0.02;
151+
orbit = env.screen_width*0.10;
152+
color = rl::RED;
153+
orbit_circle(env, state.t2, radius, orbit, color);
154+
}
155+
156+
fn void plug_reset() @export("plug_reset")
157+
{
158+
state.finished = false;
159+
reset_anim();
160+
}
161+
162+
fn bool plug_finished() @export("plug_finished")
163+
{
164+
return state.finished;
165+
}

0 commit comments

Comments
 (0)