init
This commit is contained in:
parent
c216065ae8
commit
c42131d90b
9 changed files with 622 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
.build/
|
||||
96
array.jai
Normal file
96
array.jai
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
Static_Array :: struct(capacity: int, T: Type) {
|
||||
items: [capacity]T;
|
||||
count: int;
|
||||
|
||||
Zero :: #run zero_value(T);
|
||||
}
|
||||
|
||||
operator [] :: inline (a: Static_Array, index: int, loc := #caller_location) -> a.T #no_abc {
|
||||
assert(index >= 0 && index < a.count, "invalid index: % (max: %)", index, a.count - 1, loc = loc);
|
||||
return a.items[index];
|
||||
}
|
||||
|
||||
operator *[] :: inline (a: *Static_Array, index: int, loc := #caller_location) -> *a.T #no_abc {
|
||||
assert(index >= 0 && index < a.count, "invalid index: % (max: %)", index, a.count - 1, loc = loc);
|
||||
return *a.items[index];
|
||||
}
|
||||
|
||||
operator []= :: inline (a: *Static_Array, index: int, value: a.T, loc := #caller_location) #no_abc {
|
||||
assert(index >= 0 && index < a.capacity, "invalid index: % (max: %)", index, a.count - 1, loc = loc);
|
||||
a.items[index] = value;
|
||||
}
|
||||
|
||||
for_expansion :: (a: *Static_Array, body: Code, flags: For_Flags) #expand {
|
||||
view := make_view(a);
|
||||
for *=(flags & .POINTER == .POINTEr) <=(flags & .REVERSE == .REVERSE) `it, `it_index: iter {
|
||||
#insert,scope(body)(break = break it) body;
|
||||
}
|
||||
}
|
||||
|
||||
append :: inline (a: *Static_Array, item: a.T) -> *a.T {
|
||||
ensure_array_has_room(a, 1);
|
||||
ptr := *a[a.count];
|
||||
ptr.* = item;
|
||||
a.count += 1;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
append :: inline (a: *Static_Array) -> *a.T {
|
||||
ensure_array_has_room(a, 1);
|
||||
ptr := *a[a.count];
|
||||
a.count += 1;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
append :: inline (a: *Static_Array, items: ..a.T) -> *a.T {
|
||||
ensure_array_has_room(a, items.count);
|
||||
first := *a.items[a.count];
|
||||
memcpy(a.items.data + a.count, items.data, items.count * size_of(a.T));
|
||||
a.count += items.count;
|
||||
return first;
|
||||
}
|
||||
|
||||
reset :: inline (a: *Static_Array, $keep_memory := true) {
|
||||
#if keep_memory {
|
||||
a.count = 0;
|
||||
}
|
||||
else {
|
||||
for 0..a.count - 1 a.items[it] = a.Zero;
|
||||
a.count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
make_view :: (a: Static_Array) -> []a.T {
|
||||
view := a.items.([]a.T);
|
||||
view.count = a.count;
|
||||
return view;
|
||||
}
|
||||
|
||||
make_dynamic :: (a: *Static_Array) -> [..]a.T {
|
||||
res: [..]a.T;
|
||||
res.count = a.count;
|
||||
res.allocated = a.count;
|
||||
res.data = basic.alloc(a.count * size_of(a.T));
|
||||
memcpy(res.data, a.items.data, a.count * size_of(a.T));
|
||||
}
|
||||
|
||||
|
||||
#scope_file;
|
||||
|
||||
ensure_array_has_room :: (array: *Static_Array, count: int, loc := #caller_location) #expand {
|
||||
basic.assert(array.count + count <= array.capacity, "attempt to add too many elements! want: %, Max: %", array.count + count, array.capacity, loc = loc);
|
||||
}
|
||||
|
||||
#if RUN_TESTS #run {
|
||||
test :: #import,file "./test/module.jai";
|
||||
|
||||
test.run("basic operations", (t) => {
|
||||
a: Static_Array(10, int);
|
||||
|
||||
test.expect(t, a.count == 0);
|
||||
test.expect(t, a.capacity == 10);
|
||||
|
||||
append(*a, 10, 20, 30);
|
||||
test.expect(t, a.count == 3, "count: %", a.count);
|
||||
});
|
||||
}
|
||||
247
encoding/base64.jai
Normal file
247
encoding/base64.jai
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
base64_encode :: (str: string, $for_url := false) -> string, bool {
|
||||
enc, ok := base64_encode(str.([]u8), for_url);
|
||||
return enc.(string), ok;
|
||||
}
|
||||
|
||||
base64_decode :: (str: string) -> string, bool {
|
||||
enc, ok := base64_decode(str.([]u8));
|
||||
return enc.(string), ok;
|
||||
}
|
||||
|
||||
base64_encode :: ($$data: []u8, $for_url := false) -> []u8, bool {
|
||||
if data.count == 0 return .[], false;
|
||||
|
||||
#if for_url {
|
||||
Character_Set :: Encoding_Url;
|
||||
}
|
||||
else {
|
||||
Character_Set :: Encoding_Standard;
|
||||
}
|
||||
|
||||
padded := strings.contains(data.(string), Padding_Character);
|
||||
encoded := basic.NewArray(encoded_length(data.count, padded), u8, true);
|
||||
|
||||
src_idx := 0;
|
||||
dst_idx := 0;
|
||||
src_max := (data.count / 3) * 3;
|
||||
while src_idx < src_max {
|
||||
value: u64;
|
||||
|
||||
parts := (*value).(*u8);
|
||||
(parts + 0).* = xx data[src_idx + 2];
|
||||
(parts + 1).* = xx data[src_idx + 1];
|
||||
(parts + 2).* = xx data[src_idx + 0];
|
||||
|
||||
encoded[dst_idx + 0] = Character_Set[value >> 18 & 0x3F];
|
||||
encoded[dst_idx + 1] = Character_Set[value >> 12 & 0x3F];
|
||||
encoded[dst_idx + 2] = Character_Set[value >> 6 & 0x3F];
|
||||
encoded[dst_idx + 3] = Character_Set[value & 0x3F];
|
||||
|
||||
src_idx += 3;
|
||||
dst_idx += 4;
|
||||
}
|
||||
|
||||
remaining := data.count - src_idx;
|
||||
if !remaining then return encoded, true;
|
||||
|
||||
value := data[src_idx + 0].(u64) << 16;
|
||||
if remaining == 2 {
|
||||
value |= data[src_idx + 1].(u64) << 8;
|
||||
}
|
||||
|
||||
encoded[dst_idx + 0] = Character_Set[value >> 18 & 0x3F];
|
||||
encoded[dst_idx + 1] = Character_Set[value >> 12 & 0x3F];
|
||||
|
||||
if remaining == {
|
||||
case 2;
|
||||
if padded {
|
||||
encoded[dst_idx + 2] = Padding_Character;
|
||||
encoded[dst_idx + 3] = Padding_Character;
|
||||
}
|
||||
case 1;
|
||||
encoded[dst_idx + 2] = Character_Set[value >> 6 & 0x3F];
|
||||
if padded {
|
||||
encoded[dst_idx + 3] = Padding_Character;
|
||||
}
|
||||
}
|
||||
|
||||
return encoded, true;
|
||||
}
|
||||
|
||||
base64_decode :: (data: []u8) -> []u8, bool {
|
||||
if !data.count return .[], false;
|
||||
|
||||
lookup :: (c: u8) -> u8 #expand {
|
||||
if c >= #char "A" && c <= #char "Z" return c - #char "A";
|
||||
if c >= #char "a" && c <= #char "z" return c - 71;
|
||||
if c >= #char "0" && c <= #char "9" return c + 4;
|
||||
if c == #char "+" || c == #char "-" return 62;
|
||||
if c == #char "/" || c == #char "_" return 63;
|
||||
return 255;
|
||||
}
|
||||
|
||||
a4_to_a3 :: () #expand {
|
||||
`a3[0] = (`a4[0] << 2) + ((`a4[1] & 0x30) >> 4);
|
||||
`a3[1] = ((`a4[1] & 0xF) << 4) + ((`a4[2] & 0x3C) >> 2);
|
||||
`a3[2] = ((`a4[2] & 0x3) << 6) + `a4[3];
|
||||
}
|
||||
|
||||
padding := 0;
|
||||
{
|
||||
i := data.count - 1;
|
||||
while i >= 0 {
|
||||
if data[i] == Padding_Character {
|
||||
padding += 1;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
a3: [3]u8;
|
||||
a4: [4]u8;
|
||||
|
||||
i := 0;
|
||||
offset := 0;
|
||||
|
||||
decoded := basic.NewArray(((6 * data.count) / 8) - padding, u8, true);
|
||||
for 0..data.count - 1 {
|
||||
chr := data[it];
|
||||
if chr == Padding_Character break;
|
||||
|
||||
a4[i] = chr;
|
||||
i += 1;
|
||||
|
||||
if i == 4 {
|
||||
for 0..3 a4[it] = lookup(a4[it]);
|
||||
|
||||
a4_to_a3();
|
||||
|
||||
for 0..2 {
|
||||
decoded.data[offset] = a3[it];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
for 0..3 a4[it] = lookup(a4[it]);
|
||||
|
||||
a4_to_a3();
|
||||
|
||||
for 0..i - 1 {
|
||||
(decoded.data + offset + it).* = a3[it];
|
||||
}
|
||||
}
|
||||
|
||||
return decoded, true;
|
||||
}
|
||||
|
||||
#scope_file;
|
||||
|
||||
Padding_Character :: #char "=";
|
||||
Encoding_Url :: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
Encoding_Standard :: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
encoded_length :: (count: int, with_padding := false) -> int {
|
||||
if with_padding then return (count + 2) / 3 * 4;
|
||||
return (count * 8 + 5) / 6;
|
||||
}
|
||||
|
||||
basic :: #import "Basic";
|
||||
strings :: #import "String";
|
||||
|
||||
#if RUN_TESTS #run {
|
||||
test :: #import,file "../test/module.jai";
|
||||
|
||||
test.run("encodes", (t) => {
|
||||
str :: "Hello, World";
|
||||
|
||||
encoded, ok := base64_encode(str);
|
||||
test.expect(t, ok, "'%' did not properly encode!", str);
|
||||
|
||||
expected :: "SGVsbG8sIFdvcmxk";
|
||||
test.expect(t, encoded == expected, "wanted: '%', received: '%'", expected, encoded);
|
||||
});
|
||||
|
||||
test.run("decodes", (t) => {
|
||||
encoded_str :: "SGVsbG8sIFdvcmxk";
|
||||
|
||||
decoded, ok := base64_decode(encoded_str);
|
||||
test.expect(t, ok, "'%' did not properly decode!", encoded_str);
|
||||
|
||||
expected :: "Hello, World";
|
||||
test.expect(t, decoded == expected, "wanted: '%', received: '%'", expected, decoded);
|
||||
});
|
||||
|
||||
test.run("encodes/decodes padding", (t) => {
|
||||
str :: "VkdocGN5QnBjeUJ6YjIxbGRHaHBibWNnYlc5eVpTQmpiMjF3YkdWNExDQnBkQ0J6YUc5MWJHUWdhR0YyWlNCd1lXUmthVzVuUHc9PQ==";
|
||||
|
||||
first_decode, f_ok := base64_decode(str);
|
||||
test.expect(t, f_ok, "first decode failed!");
|
||||
|
||||
re_encode, r_ok := base64_encode(first_decode);
|
||||
test.expect(t, r_ok, "re-encode failed!");
|
||||
|
||||
second_decode, s_ok := base64_decode(re_encode);
|
||||
test.expect(t, s_ok, "second decode failed!");
|
||||
|
||||
for 0..first_decode.count - 1 {
|
||||
test.expect(t, first_decode[it] == second_decode[it], "Decoded byte '%' did not match expected output", it);
|
||||
}
|
||||
});
|
||||
|
||||
test.run("encodes/decodes struct", (t) => {
|
||||
Foo :: struct {
|
||||
first: u64;
|
||||
second: u64;
|
||||
third: u64;
|
||||
forth: u64;
|
||||
}
|
||||
|
||||
foo: Foo;
|
||||
foo.first = 123;
|
||||
foo.second = 456;
|
||||
foo.third = 789;
|
||||
foo.forth = 100;
|
||||
|
||||
enc_builder: basic.String_Builder;
|
||||
basic.print_to_builder(*enc_builder, "%,%,%,%", foo.first, foo.second, foo.third, foo.forth);
|
||||
|
||||
encoded, e_ok := base64_encode(basic.builder_to_string(*enc_builder));
|
||||
test.expect(t, e_ok, "Encode of structure failed!");
|
||||
|
||||
decoded, d_ok := base64_decode(encoded);
|
||||
test.expect(t, d_ok, "Decode of encoded structure failed!");
|
||||
|
||||
parts := strings.split(decoded, ",");
|
||||
test.expect(t, parts.count == 4, "Invalid number of parts after decode: %", parts.count);
|
||||
|
||||
bar: Foo = ---;
|
||||
|
||||
{
|
||||
ptr := (*bar).(*u8);
|
||||
info := Foo.(*Type_Info_Struct);
|
||||
|
||||
for info.members {
|
||||
value, ok := strings.parse_int(*parts[it_index], u64);
|
||||
test.expect(t, ok, "Integer parse of value % ('%') failed!", it_index, parts[it_index]);
|
||||
|
||||
offset := (ptr + it.offset_in_bytes).(*u64);
|
||||
offset.*= value;
|
||||
}
|
||||
}
|
||||
|
||||
test.expect(t, foo.first == bar.first, "Foo.first (%, %) didn't match between encoding/decoding!", foo.first, bar.first);
|
||||
test.expect(t, foo.second == bar.second, "Foo.second (%, %) didn't match between encoding/decoding!", foo.second, bar.second);
|
||||
test.expect(t, foo.third == bar.third, "Foo.third (%, %) didn't match between encoding/decoding!", foo.third, bar.third);
|
||||
test.expect(t, foo.forth == bar.forth, "Foo.forth (%, %) didn't match between encoding/decoding!", foo.forth, bar.forth);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
4
encoding/module.jai
Normal file
4
encoding/module.jai
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#module_parameters(RUN_TESTS := false);
|
||||
|
||||
#load "base64.jai";
|
||||
|
||||
126
macros.jai
Normal file
126
macros.jai
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
verify :: (block: Code) #expand {
|
||||
for 0..0 #insert,scope(block) block;
|
||||
}
|
||||
|
||||
c_call :: (block: Code) #expand {
|
||||
push_context context {
|
||||
#insert,scope() block;
|
||||
}
|
||||
}
|
||||
|
||||
zero_value :: ($T: Type) -> T #expand {
|
||||
zero: T;
|
||||
return zero;
|
||||
}
|
||||
|
||||
// Allows structs to be copy assigned.
|
||||
with :: (old: $T, $new: Code, location := #caller_location) -> T
|
||||
#modify { return T.(*Type_Info).type == .STRUCT, "with can only be used on structs"; }
|
||||
#expand {
|
||||
using compiler;
|
||||
|
||||
ensure :: (cond: bool, message: string, args: ..Any, loc := location) {
|
||||
if !cond compiler_report(basic.tprint(message, ..args), loc);
|
||||
}
|
||||
|
||||
#insert,scope() -> string {
|
||||
b: basic.String_Builder;
|
||||
root := compiler_get_nodes(new).(*Code_Literal);
|
||||
ensure(root.kind == .LITERAL, "argument must be a struct literal");
|
||||
ensure(root.value_type == .STRUCT, "argument must be a struct literal");
|
||||
|
||||
t_info := T.(*Type_Info_Struct);
|
||||
|
||||
// Ensure the literal we were given is of type T so this operator is more predictable.
|
||||
// i.e. disallowing Vector3.{} | SomeRandomType.{ x = 10 };
|
||||
n_type := root.struct_literal_info.type_expression;
|
||||
if n_type != null {
|
||||
tmp: basic.String_Builder;
|
||||
pp.print_expression(*tmp, n_type);
|
||||
n_typename := basic.builder_to_string(*tmp);
|
||||
ensure(n_type.result == t_info, "mismatched types, % vs. %", t_info.name, n_typename);
|
||||
}
|
||||
|
||||
receiver := "old";
|
||||
if is_constant(old) {
|
||||
receiver = "copy";
|
||||
basic.print_to_builder(*b, "% := old;\n", receiver);
|
||||
}
|
||||
|
||||
for root.struct_literal_info.arguments {
|
||||
op := it.(*Code_Binary_Operator);
|
||||
ident := op.left.(*Code_Ident);
|
||||
|
||||
ensure(op.kind == .BINARY_OPERATOR, "copy-assign requires named fields", loc = make_location(op));
|
||||
ensure(op.operator_type == #char "=", "copy-assign requires named field assignment", loc = make_location(op));
|
||||
ensure(ident.kind == .IDENT, "must be an identifier", loc = make_location(op));
|
||||
|
||||
// Catch any incorrect field assignments before the compiler does for better error reporting
|
||||
exists := false;
|
||||
for t_info.members if it.name == ident.name exists = true;
|
||||
ensure(exists, "field % does not exist within %", ident.name, t_info.name, loc = make_location(ident));
|
||||
|
||||
// receiver.field = value;
|
||||
basic.append(*b, receiver);
|
||||
basic.append(*b, ".");
|
||||
pp.print_expression(*b, op);
|
||||
basic.append(*b, ";\n");
|
||||
}
|
||||
|
||||
basic.print_to_builder(*b, "return %;\n", receiver);
|
||||
return basic.builder_to_string(*b);
|
||||
}
|
||||
}
|
||||
|
||||
operator | :: with;
|
||||
|
||||
|
||||
#scope_file;
|
||||
|
||||
test :: #import,file "test/module.jai";
|
||||
|
||||
pp :: #import "Program_Print";
|
||||
compiler :: #import "Compiler";
|
||||
|
||||
#if RUN_TESTS #run {
|
||||
test.run("verify", (t) => {
|
||||
i := 0;
|
||||
verify(#code {
|
||||
i += 1;
|
||||
if i == 1 {
|
||||
break;
|
||||
}
|
||||
|
||||
i += 2;
|
||||
});
|
||||
|
||||
j := 0;
|
||||
verify(#code {
|
||||
for 0..10 {
|
||||
break;
|
||||
}
|
||||
|
||||
j = 1;
|
||||
});
|
||||
|
||||
test.expect(t, i == 1, "i was %", i);
|
||||
test.expect(t, j == 1, "j was %", j);
|
||||
});
|
||||
|
||||
test.run("copy assign", (t) => {
|
||||
Value :: struct {
|
||||
x: float;
|
||||
y: float;
|
||||
z: float;
|
||||
}
|
||||
|
||||
a := Value.{ 10, 20, 30 };
|
||||
b := with(a, .{ x = 1, z = 1 });
|
||||
c := b | .{ y = 500 };
|
||||
|
||||
test.expect(t, b.x == 1, "was %", b.x);
|
||||
test.expect(t, b.z == 1, "was %", b.z);
|
||||
test.expect(t, c.y == 500, "was %", c.y);
|
||||
});
|
||||
}
|
||||
|
||||
32
memory.jai
Normal file
32
memory.jai
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
Kilobyte :: 1024;
|
||||
Megabyte :: 1024 * Kilobyte;
|
||||
Gigabyte :: 1024 * Megabyte;
|
||||
|
||||
Default_Align :: #run 2 * align_of(rawptr);
|
||||
|
||||
align_of :: ($T: Type) -> uint #expand {
|
||||
return #run -> uint {
|
||||
info := type_info(struct{ p: u8; t: T; });
|
||||
return info.members[1].offset_in_bytes.(uint);
|
||||
};
|
||||
}
|
||||
|
||||
bitcast :: ($T: Type, expr: Code) -> T #expand {
|
||||
value := expr;
|
||||
return (*value).(*T).*;
|
||||
}
|
||||
|
||||
power_of_two :: (x: uint) -> bool {
|
||||
if x == 0 return false;
|
||||
return x & (x - 1) == 0;
|
||||
}
|
||||
|
||||
align_forward :: (ptr: uint, align: uint = Default_Align) -> uint {
|
||||
basic.assert(power_of_two(align), "alignment must be a power of two");
|
||||
|
||||
p := ptr;
|
||||
mod := p & (align - 1);
|
||||
if mod != 0 then p += align - mod;
|
||||
return p;
|
||||
}
|
||||
|
||||
26
module.jai
Normal file
26
module.jai
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
uint :: #type u64;
|
||||
rune :: #type u32;
|
||||
rawptr :: #type *void;
|
||||
|
||||
#load "utils.jai";
|
||||
#load "memory.jai";
|
||||
#load "macros.jai";
|
||||
#load "array.jai";
|
||||
|
||||
|
||||
#scope_module;
|
||||
|
||||
basic :: #import "Basic";
|
||||
compiler :: #import "Compiler";
|
||||
|
||||
RUN_TESTS :: true;
|
||||
|
||||
|
||||
#scope_file;
|
||||
|
||||
#if RUN_TESTS {
|
||||
#run compiler.set_build_options_dc(.{ do_output = false });
|
||||
|
||||
#import,file "./encoding/module.jai"(RUN_TESTS);
|
||||
}
|
||||
|
||||
48
test/module.jai
Normal file
48
test/module.jai
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
T :: struct {
|
||||
location: Source_Code_Location;
|
||||
expects_run: int;
|
||||
expects_ok: int;
|
||||
failed: bool;
|
||||
}
|
||||
|
||||
Proc :: #type (t: *T);
|
||||
|
||||
expect :: (t: *T, cond: bool, message := "", args: ..Any, loc := #caller_location) {
|
||||
t.expects_run += 1;
|
||||
if cond {
|
||||
t.expects_ok += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
msg := "expectation failed";
|
||||
if message.count != 0 {
|
||||
msg = basic.tprint(message, ..args);
|
||||
}
|
||||
|
||||
t.failed = true;
|
||||
if #compile_time {
|
||||
compiler.compiler_report(msg, loc = loc, mode = .ERROR_CONTINUABLE);
|
||||
}
|
||||
else {
|
||||
basic.assert(false, msg, loc = loc);
|
||||
}
|
||||
}
|
||||
|
||||
run :: (name: string, proc: Proc, loc := #caller_location) {
|
||||
filename := strings.path_filename(loc.fully_pathed_filename);
|
||||
basic.print("test %,%: %...", filename, loc.line_number, name);
|
||||
|
||||
t: T;
|
||||
proc(*t);
|
||||
|
||||
if t.failed {
|
||||
basic.print(" failed!\n");
|
||||
}
|
||||
else {
|
||||
basic.print(" ok!\n");
|
||||
}
|
||||
}
|
||||
|
||||
basic :: #import "Basic";
|
||||
strings :: #import "String";
|
||||
compiler :: #import "Compiler";
|
||||
42
utils.jai
Normal file
42
utils.jai
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
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;
|
||||
}
|
||||
|
||||
use_pascal_names :: (names: []string) {
|
||||
for name: names {
|
||||
b: basic.String_Builder;
|
||||
|
||||
upper := false;
|
||||
for i: 0..name.count - 1 {
|
||||
c := name[i];
|
||||
if c == #char "_" {
|
||||
upper = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
upper = true;
|
||||
}
|
||||
|
||||
if upper {
|
||||
basic.append(*b, basic.to_upper(c));
|
||||
upper = false;
|
||||
} else {
|
||||
basic.append(*b, c);
|
||||
}
|
||||
}
|
||||
|
||||
names[it_index] = basic.builder_to_string(*b);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue