power_play/src/base/base_crum.c
2026-02-05 23:51:04 -06:00

486 lines
8.8 KiB
C

////////////////////////////////////////////////////////////
//~ Value conversion utilities
String CR_SanitizeString(Arena *arena, String str)
{
String result = Zi;
result.text = ArenaNext(arena, u8);
for (u64 idx = 0; idx < str.len; ++idx)
{
u8 c = str.text[idx];
String sanitized = Zi;
switch (c)
{
default:
{
sanitized.text = &c;
sanitized.len = 1;
} break;
case '\r':
{
// Ignore
} break;
case '\n':
{
sanitized = Lit("\\n");
} break;
case '\\':
{
sanitized = Lit("\\");
} break;
case '"':
{
sanitized = Lit("\\\"");
} break;
}
result.len += PushString(arena, sanitized).len;
}
return result;
}
i64 CR_IntFromString(String str)
{
i64 result = 0;
b32 ok = 1;
b32 is_hex = 0;
u64 pos = 0;
if (StringBeginsWith(str, Lit("0x")))
{
pos += 2;
is_hex = 1;
}
// Eat sign
i32 sign = 1;
if (ok && !is_hex)
{
if (pos < str.len)
{
if (str.text[pos] == '-')
{
sign = -1;
pos += 1;
}
else if (str.text[pos] == '+')
{
pos += 1;
}
}
}
// Parse number
while (ok && pos < str.len)
{
u8 c = str.text[pos];
u8 digit = 0;
if (c >= '0' && c <= '9')
{
digit = c - '0';
}
else if (is_hex)
{
if (c >= 'a' && c <= 'f')
{
digit = 10 + c - 'a';
}
else if (c >= 'A' && c <= 'F')
{
digit = 10 + c - 'A';
}
else
{
ok = 0;
}
}
else
{
ok = 0;
}
pos += 1;
result += digit * PowU64(10 + is_hex * 6, str.len - pos);
}
if (ok)
{
result *= sign;
}
else
{
result = 0;
}
return result;
}
// TODO: Handle hexadecimal
f64 CR_FloatFromString(String str)
{
f64 result = 0;
b32 ok = 1;
// Eat sign
u64 whole_start_idx = 0;
i32 sign = 1;
if (ok && str.len > 0)
{
if (whole_start_idx < str.len)
{
u8 c = str.text[whole_start_idx];
if (c == '-')
{
sign = -1;
whole_start_idx += 1;
}
else if (c == '+')
{
whole_start_idx += 1;
}
}
}
// Find decimal place
u64 frac_start_idx = whole_start_idx;
for (; ok && frac_start_idx < str.len; ++frac_start_idx)
{
u8 c = str.text[frac_start_idx];
if (c == '.')
{
break;
}
}
// Parse whole part
u64 whole_part = 0;
for (u64 char_idx = whole_start_idx; ok && char_idx < frac_start_idx; ++char_idx)
{
u8 c = str.text[char_idx];
if (c >= '0' && c <= '9')
{
u8 digit = c - '0';
whole_part += digit * PowU64(10, frac_start_idx - (char_idx + 1));
}
else
{
ok = 0;
}
}
// Parse frac part
u64 frac_part = 0;
for (u64 char_idx = frac_start_idx + 1; ok && char_idx < str.len; ++char_idx)
{
u8 c = str.text[char_idx];
if (c >= '0' && c <= '9')
{
u8 digit = c - '0';
frac_part += digit * PowU64(10, str.len - (char_idx + 1));
}
else
{
ok = 0;
}
}
if (ok)
{
if (frac_part != 0)
{
result = ((f64)whole_part + ((f64)frac_part / PowU64(10, str.len - (frac_start_idx + 1)))) * sign;
}
else
{
result = (f64)whole_part * sign;
}
}
else
{
result = 0;
}
return result;
}
b32 CR_BoolFromString(String str)
{
return MatchString(str, Lit("true"));
}
Vec4 CR_Vec4FromString(String str)
{
Vec4 result = Zi;
b32 ok = 1;
// if (StringBeginsWith(str, Lit("(")))
// {
// str.text += 1;
// str.len -= 1;
// }
// if (StringEndsWith(str, Lit(")")))
// {
// str.len -= 1;
// }
u64 pos = 0;
u64 parts_found = 0;
String part = Zi;
part.text = str.text;
if (StringBeginsWith(str, Lit("(")))
{
part.text += 1;
pos += 1;
}
while (pos < str.len && parts_found < 4)
{
u8 c = str.text[pos];
u8 next_c = 0;
if (c == ',' || c == ')')
{
part.len = (str.text + pos) - part.text;
f64 part_float = CR_FloatFromString(part);
result.v[parts_found] = part_float;
parts_found += 1;
part.text = str.text + pos + 2;
pos += 2;
}
else
{
pos += 1;
}
}
return result;
}
Vec3 CR_Vec3FromString(String str)
{
Vec4 v4 = CR_Vec4FromString(str);
Vec3 result = Zi;
result.x = v4.x;
result.y = v4.y;
result.z = v4.z;
return result;
}
Vec2 CR_Vec2FromString(String str)
{
Vec4 v4 = CR_Vec4FromString(str);
Vec2 result = Zi;
result.x = v4.x;
result.y = v4.y;
return result;
}
////////////////////////////////////////////////////////////
//~ Parse
CR_Item *CR_ItemFromString(Arena *arena, String str)
{
CR_Item *result = PushStruct(arena, CR_Item);
TempArena scratch = BeginScratch(arena);
Enum(Mode)
{
Mode_None,
Mode_Text,
Mode_SingleLineComment,
Mode_MultiLineComment,
};
Mode mode = Mode_None;
Struct(ItemNode)
{
ItemNode *next;
CR_Item *item;
};
// TODO: Reuse these
ItemNode *top_item = PushStruct(scratch.arena, ItemNode);
top_item->item = result;
CR_Item *cur_item = 0;
i64 text_start = 0;
b32 text_is_string = 0;
b32 text_has_name = 0;
i64 pos = 0;
b32 is_escaped = 0;
while (pos < (i64)str.len)
{
u8 c = str.text[pos];
u8 next_c = 0;
if (pos + 1 < (i64)str.len)
{
next_c = str.text[pos + 1];
}
b32 is_eol = c == '\n' || c == '\r' || c == 0;
b32 is_whitespace = c == ' ' || c == '\t' || is_eol;
b32 next_is_eol = next_c == '\n' || next_c == '\r' || next_c == 0;
b32 next_is_whitespace = next_c == ' ' || next_c == '\t' || next_is_eol;
switch (mode)
{
case Mode_None:
{
if (c == '/' && next_c == '/')
{
mode = Mode_SingleLineComment;
pos += 2;
}
else if (c == '/' && next_c == '*')
{
mode = Mode_MultiLineComment;
pos += 2;
}
else if (!is_whitespace)
{
mode = Mode_Text;
text_is_string = 0;
text_start = pos;
}
else
{
pos += 1;
}
} break;
case Mode_Text:
{
i64 text_end = -1;
b32 is_name = 0;
if (is_escaped)
{
is_escaped = 0;
}
else
{
if (c == '\\')
{
is_escaped = 1;
}
else if (c == '\"')
{
if (!text_is_string && pos == text_start)
{
text_is_string = 1;
text_start = pos + 1;
}
else
{
text_end = pos;
}
}
else if (next_c == ':')
{
text_end = pos + 1;
is_name = 1;
}
}
if (!text_is_string && next_is_whitespace)
{
text_end = pos + 1;
}
if (text_end != -1)
{
is_escaped = 0;
String src_text = Zi;
if (text_end > text_start)
{
src_text.len = text_end - text_start;
src_text.text = str.text + text_start;
}
if (!cur_item)
{
cur_item = PushStruct(arena, CR_Item);
cur_item->parent = top_item->item;
DllQueuePush(cur_item->parent->first, cur_item->parent->last, cur_item);
cur_item->parent->count += 1;
}
// FIXME: Arrays always have an item even if empty
if (MatchString(src_text, Lit("{")))
{
ItemNode *n = PushStruct(scratch.arena, ItemNode);
n->item = cur_item;
SllStackPush(top_item, n);
pos += 1;
cur_item = 0;
}
else if (MatchString(src_text, Lit("}")))
{
SllStackPop(top_item);
pos += 1;
cur_item = 0;
}
else
{
// FIXME: Resolve escape sequences
String final_text = PushString(arena, src_text);
if (is_name)
{
cur_item->name = final_text;
}
else
{
cur_item->value = final_text;
cur_item = 0;
}
if (is_name)
{
pos += 1;
}
}
mode = Mode_None;
text_is_string = 0;
}
pos += 1;
} break;
case Mode_SingleLineComment:
{
if (is_eol)
{
mode = Mode_None;
}
pos += 1;
} break;
case Mode_MultiLineComment:
{
if (!is_escaped && c == '*' && next_c == '/')
{
mode = Mode_None;
pos += 2;
}
else
{
pos += 1;
}
} break;
}
}
EndScratch(scratch);
return result;
}