486 lines
8.8 KiB
C
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;
|
|
}
|