-
Notifications
You must be signed in to change notification settings - Fork 0
/
utilities.jai
448 lines (371 loc) · 12.6 KB
/
utilities.jai
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
/// Helpful utilities
/// Custom types
byte :: #type u8;
rune :: #type u32;
sint :: #type s64;
uint :: #type u64;
f32 :: #type float32;
f64 :: #type float64;
/// C binding types
/// The names of these types are meant to mirror
/// how they're written in C. That way it cuts
/// down on the number of changes needed when
/// binding code. They've been placed in a
/// struct to namespace the types.
C :: struct {
// @Todo: Should I even handle 32bit since it's not a compilation target?
#if OS == .WINDOWS {
char :: #type u8;
short :: #type s16;
int :: #type s32;
long :: #type s32;
float :: #type float32;
double :: #type float64;
short_int :: #type s16;
long_long :: #type s64;
long_long_int :: #type s64;
unsigned_char :: #type u8;
unsigned_short :: #type u16;
unsigned_short_int :: #type u16;
unsigned :: #type u32;
unsigned_int :: #type u32;
unsigned_long :: #type u32;
unsigned_long_long :: #type u64;
unsigned_long_long_int :: #type u64;
signed_char :: #type s8;
signed_short :: #type s16;
signed_short_int :: #type s16;
signed :: #type s32;
signed_int :: #type s32;
signed_long :: #type s32;
signed_long_long :: #type s64;
signed_long_long_int :: #type s64;
size_t :: #type u64;
intptr_t :: #type s64;
uintptr_t :: #type u64;
ptrdiff_t :: #type u64;
wchar_t :: #type u16;
long_double :: #type float64;
WORD :: #type u16;
DWORD :: #type u32;
}
else #if OS == .LINUX {
char :: #type u8;
short :: #type s16;
int :: #type s32;
long :: #type s64;
float :: #type float32;
double :: #type float64;
short_int :: #type s16;
long_long :: #type s64;
long_long_int :: #type s64;
unsigned_char :: #type u8;
unsigned_short :: #type u16;
unsigned_short_int :: #type u16;
unsigned :: #type u32;
unsigned_int :: #type u32;
unsigned_long :: #type u64;
unsigned_long_long :: #type u64;
unsigned_long_long_int :: #type u64;
signed_char :: #type s8;
signed_short :: #type s16;
signed_short_int :: #type s16;
signed :: #type s32;
signed_int :: #type s32;
signed_long :: #type s64;
signed_long_long :: #type s64;
signed_long_long_int :: #type s64;
size_t :: #type u64;
intptr_t :: #type s64;
uintptr_t :: #type u64;
ptrdiff_t :: #type u64;
wchar_t :: #type u32;
ssize_t :: #type s64;
long_double :: #type float64;
}
else #if OS == .MACOS {
char :: #type u8;
short :: #type s16;
int :: #type s32;
long :: #type s64;
float :: #type float32;
double :: #type float64;
short_int :: #type s16;
long_long :: #type s64;
long_long_int :: #type s64;
unsigned_char :: #type u8;
unsigned_short :: #type u16;
unsigned_short_int :: #type u16;
unsigned :: #type u32;
unsigned_int :: #type u32;
unsigned_long :: #type u64;
unsigned_long_long :: #type u64;
unsigned_long_long_int :: #type u64;
signed_char :: #type s8;
signed_short :: #type s16;
signed_short_int :: #type s16;
signed :: #type s32;
signed_int :: #type s32;
signed_long :: #type s64;
signed_long_long :: #type s64;
signed_long_long_int :: #type s64;
size_t :: #type u64;
intptr_t :: #type s64;
uintptr_t :: #type u64;
ptrdiff_t :: #type u64;
wchar_t :: #type u32;
}
else {
#run compiler_report(sprint("Unknown platform '%'! This may not compile...", OS), mode = .WARNING);
}
}
/// Various utilities
/// Better casting
// @Note(Judah): Because Jai's print_type_to_builder doesn't actually
// output the real type (just an approximation/verbose version), we
// can't rely on it to generate the cast expression (hence the ifs)
as :: ($T: Type, expr: $V, $check := true, $truncate := false) -> T #expand {
#if !check {
#if truncate then return cast,no_check,trunc(T)(expr);
else return cast,no_check(T)(expr);
}
else #if truncate {
return cast,trunc(T)(expr);
}
return cast(T)(expr);
}
as_trunc :: ($T: Type, expr: Code) -> T #expand {
return as(T, expr, truncate = true);
}
as_unsafe :: ($T: Type, expr: Code) -> T #expand {
return as(T, expr, check = false);
}
transmute :: ($T: Type, expr: Code) -> T #expand {
value := expr;
return <<as(*T, *value);
}
/// Make a non-array type
// @Note(Judah): Not sure why making custom_init a partial bake works, bug?
make :: ($T: Type, $$custom_init: (*T) = null) -> T #modify {
info := cast(*Type_Info)T;
if info.type == .ARRAY {
compiler_report(sprint("make_t is not valid on %", T));
return false;
}
return true;
}{
val: T = ---;
ini :: initializer_of(T);
#if ini inline ini(*val);
else memset(*val, 0, size_of(T));
#if custom_init inline custom_init(*val);
return val;
}
/// Make a slice type
make :: ($T: Type, $count := 0, allocator := context.allocator) -> []T #modify {
info := cast(*Type_Info)T;
if info.type != .ARRAY {
compiler_report(sprint("make_slice expects % to be []T", T));
return false;
}
arr := cast(*Type_Info_Array)info;
if arr.array_type != .VIEW {
compiler_report(sprint("make_slice expects % to be []T", T));
return false;
}
T = get_type(arr.element_type);
return true;
}{
arr := NewArray(count, T, allocator = allocator);
return arr;
}
/// Make a dynamic array type
make :: ($T: Type, $count := 0, allocator := context.allocator) -> T #modify {
info := cast(*Type_Info)T;
if info.type != .ARRAY {
compiler_report(sprint("make_dynamic_array expects % to be [..]T", T));
return false;
}
arr := cast(*Type_Info_Array)info;
if arr.array_type != .RESIZABLE {
compiler_report(sprint("make_dynamic_array expects % to be [..]T", T));
return false;
}
return true;
} {
dyn: T;
dyn.allocator = allocator;
#if count > 0 array_reserve(*dyn, count);
return dyn;
}
new :: ($T: Type, allocator := context.allocator) -> *T {
return inline New(T, allocator = allocator);
}
len :: (slice: []$T) -> int #expand { return slice.count; }
cap :: (slice: []$T) -> int #expand { return slice.count; }
len :: (dyn: [..]$T) -> int #expand { return dyn.count; }
cap :: (dyn: [..]$T) -> int #expand { return dyn.allocated; }
kilobyte :: (i: $T) -> T #expand {
return 1024 * i;
}
megabyte :: (i: $T) -> T #expand {
return 1024 * kilobyte(i);
}
gigabyte :: (i: $T) -> T #expand {
return 1024 * megabyte(i);
}
to_array :: inline (args: ..$T) -> []T { return args; }
to_upper :: (r: rune) -> rune {
if r >= #char "a" && r <= #char "z" ||
r >= #char "A" && r <= #char "Z" {
return to_upper(cast(u8)r);
}
return r;
}
/// String_View is a bounds checked string slice
String_View :: struct {
#as value: string;
max_length: int;
}
begin_view :: (str: string) -> String_View {
view: String_View;
view.value = str;
view.value.count = 0;
view.max_length = str.count;
return view;
}
advance_view :: inline (view: *String_View, by := 1) {
assert(view.value.count + by < view.max_length, "Attempt to advance view past maximum length: %", view.max_length);
view.value.count += by;
}
end_view :: inline (view: String_View) -> string {
return view;
}
to_string :: end_view;
operator== :: inline (lhs: String_View, rhs: String_View) -> bool {
return lhs.max_length == rhs.max_length && lhs.value == rhs.value;
}
operator== :: inline (lhs: String_View, rhs: string) -> bool {
return lhs.value == rhs;
}
to_string :: inline (r: rune) -> string {
return character_utf32_to_utf8(r);
}
to_string :: inline (builder: *String_Builder) -> string {
return builder_to_string(builder);
}
to_string :: (loc: Source_Code_Location, $full_path := false) -> string {
#if full_path {
path := loc.fully_pathed_filename;
}
else {
path := path_filename(loc.fully_pathed_filename);
}
return sprint("%:%,%", path, loc.line_number, loc.character_number);
}
to_c_string :: (str: string, allocator: Allocator) -> *u8 {
c_str: *u8 = alloc(str.count + 1, allocator);
memcpy(c_str, str.data, str.count);
c_str[str.count] = 0;
return c_str;
}
join :: (values: ..rune, separator := "", $before_first := false, $after_last := false) -> string {
builder: String_Builder;
#if before_first append(*builder, separator);
for values {
append(*builder, to_string(it));
if it_index append(*builder, separator);
}
#if after_last append(*builder, separator);
return builder_to_string(*builder);
}
pluralize :: (text: string, number: int) -> string {
if number == 1 return text;
ending := ifx text.data[text.count - 1] == #char "s" then "" else "s";
return sprint("%0%", text, ending);
}
contains :: (array: []$T, value: T) -> bool {
for array if it == value return true;
return false;
}
/// Allows a specific block of code to be called with a C context.
c_call :: (block: Code) #expand {
ctx: Context;
push_context ctx {
#insert block;
}
}
/// Equivalent of a 'do {} while(0)' that allows
/// early exiting via 'break' instead of 'return'.
verify :: (block: Code) #expand {
for 0..0 #insert block;
}
get_name_of_current_procedure :: () -> string {
if !context.stack_trace || !context.stack_trace.info return "";
return context.stack_trace.info.name;
}
/// It might seem like these should be in 'lexer.jai',
/// however, that uses its own consume/peek procs, and
/// I don't want that module to be a requirement if you
/// just want basic consume/peek functionality.
/// Unicode character peeking/consuming
// @Todo: Don't rely on character_utf8_to_utf32.
peek_rune :: (src: string) -> rune, bool, int {
if !src.count return 0, false, 0;
rune, width, status := character_utf8_to_utf32(src.data, src.count);
if status != .CONVERSION_OK return 0, false, 0;
return rune, true, width;
}
consume_rune :: (src: *string) -> rune, bool, int {
if !src || !src.count return 0, false, 0;
rune, width, status := character_utf8_to_utf32(src.data, src.count);
if status != .CONVERSION_OK return 0, false, 0;
src.data += width;
src.count -= width;
return rune, true, width;
}
rune_width :: (r: rune) -> int {
if r < 0 return -1;
if r <= 0x7F return 1;
if r <= 0x7FF return 2;
if r >= 0xD800 &&
r <= 0xDFFF return -1;
if r <= 0xFFFF return 3;
if r <= 0x10FFFF return 4;
return -1;
}
// @Todo(Judah): We shouldn't need to call 'to_string' just to append a rune
append :: inline (builder: *String_Builder, r: rune) {
append(builder, to_string(r));
}
/// ASCII character peeking/consuming
peek_char :: (src: string) -> u8, bool {
if !src.count return 0, false;
return src.data[0], true;
}
consume_char :: (src: *string) -> u8, bool {
if !src || !src.count return 0, false;
chr := src.data[0];
src.count -= 1;
src.data += 1;
return chr, true;
}
/// Array peeking
peek_array :: (src: []$T) -> T, bool {
_: T = ---;
if !src.count return _, false;
return src.data[0], true;
}
consume_array :: (src: *[]$T) -> T, bool {
_: T = ---;
if !src || !src.count return _, false;
value := src.data[0];
src.count -= 1;
src.data += 1;
return value, true;
}
#scope_file
#import "Basic";
#import "String";
#import "Unicode";
#import "Compiler";