Files
metadesk/source/md_impl.c
T
2021-01-20 11:19:34 -07:00

2774 lines
75 KiB
C

#define MD_FUNCTION_IMPL MD_FUNCTION
#define MD_PRIVATE_FUNCTION_IMPL MD_FUNCTION_IMPL
//~
// NOTE(allen): Review @rjf; Building in C++
// While very latest version of C++ have designated initializers
// I would like to be able to build on more simple versions, so I
// ditched the designated initializers in favor of the extra work
// of maintaining order based initializers.
static MD_Node _md_nil_node =
{
&_md_nil_node, // next
&_md_nil_node, // prev
&_md_nil_node, // parent
&_md_nil_node, // first_child
&_md_nil_node, // last_child
&_md_nil_node, // first_tag
&_md_nil_node, // last_tag
MD_NodeKind_Nil, // kind
0, // flags
{0}, // string
{0}, // whole_string
0xdeadffffffffffull,
{(MD_u8*)"`NIL DD NODE`", 13},
0,
0,
};
MD_PRIVATE_FUNCTION_IMPL void
_MD_MemoryZero(void *memory, MD_u64 size)
{
memset(memory, 0, size);
}
MD_PRIVATE_FUNCTION_IMPL void
_MD_MemoryCopy(void *dest, void *src, MD_u64 size)
{
memcpy(dest, src, size);
}
MD_PRIVATE_FUNCTION_IMPL void
_MD_WriteStringToBuffer(MD_String8 string, MD_u64 max, void *dest)
{
MD_u64 write_size = string.size;
if(write_size > max-1) write_size = max-1;
_MD_MemoryCopy(dest, string.str, write_size);
((MD_u8 *)dest)[write_size] = 0;
}
MD_PRIVATE_FUNCTION_IMPL void *
_MD_AllocZero(void *ctx, MD_u64 size)
{
#if !defined(MD_IMPL_Alloc)
# error Missing implementation detail MD_IMPL_Alloc
#else
void *result = MD_IMPL_Alloc(ctx, size);
_MD_MemoryZero(result, size);
return(result);
#endif
}
#define _MD_PushArray(ctx, type, count) (type *)_MD_AllocZero((ctx), sizeof(type)*(count))
MD_PRIVATE_FUNCTION_IMPL void*
_MD_GetCtx(void)
{
#if !defined(MD_IMPL_GetCtx)
return(0);
#else
return(MD_IMPL_GetCtx());
#endif
}
MD_FUNCTION_IMPL MD_b32
MD_CharIsAlpha(MD_u8 c)
{
return MD_CharIsAlphaUpper(c) || MD_CharIsAlphaLower(c);
}
MD_FUNCTION_IMPL MD_b32
MD_CharIsAlphaUpper(MD_u8 c)
{
return c >= 'A' && c <= 'Z';
}
MD_FUNCTION_IMPL MD_b32
MD_CharIsAlphaLower(MD_u8 c)
{
return c >= 'a' && c <= 'z';
}
MD_FUNCTION_IMPL MD_b32
MD_CharIsDigit(MD_u8 c)
{
return (c >= '0' && c <= '9');
}
MD_FUNCTION_IMPL MD_b32
MD_CharIsSymbol(MD_u8 c)
{
return (c == '~' || c == '!' || c == '@' || c == '#' || c == '$' ||
c == '%' || c == '^' || c == '&' || c == '*' || c == '(' ||
c == ')' || c == '-' || c == '=' || c == '+' || c == '[' ||
c == ']' || c == '{' || c == '}' || c == ':' || c == ';' ||
c == ',' || c == '<' || c == '.' || c == '>' || c == '/' ||
c == '?' || c == '|' || c == '\\');
}
MD_FUNCTION_IMPL MD_b32
MD_CharIsSpace(MD_u8 c)
{
return c == ' ' || c == '\r' || c == '\t' || c == '\f' || c == '\v';
}
MD_FUNCTION_IMPL MD_u8
MD_CharToUpper(MD_u8 c)
{
return (c >= 'a' && c <= 'z') ? ('A' + (c - 'a')) : c;
}
MD_FUNCTION_IMPL MD_u8
MD_CharToLower(MD_u8 c)
{
return (c >= 'A' && c <= 'Z') ? ('a' + (c - 'A')) : c;
}
MD_FUNCTION_IMPL MD_u8
MD_CorrectSlash(MD_u8 c)
{
return (c == '\\' ? '/' : c);
}
MD_FUNCTION_IMPL MD_String8
MD_S8(MD_u8 *str, MD_u64 size)
{
MD_String8 string;
string.str = str;
string.size = size;
return string;
}
MD_FUNCTION_IMPL MD_String8
MD_S8Range(MD_u8 *str, MD_u8 *opl)
{
MD_String8 string;
string.str = str;
string.size = (MD_u64)(opl - str);
return string;
}
MD_FUNCTION_IMPL MD_String8
MD_StringSubstring(MD_String8 str, MD_u64 min, MD_u64 max)
{
if(max > str.size)
{
max = str.size;
}
if(min > str.size)
{
min = str.size;
}
if(min > max)
{
MD_u64 swap = min;
min = max;
max = swap;
}
str.size = max - min;
str.str += min;
return str;
}
MD_FUNCTION_IMPL MD_String8
MD_StringSkip(MD_String8 str, MD_u64 min)
{
return MD_StringSubstring(str, min, str.size);
}
MD_FUNCTION_IMPL MD_String8
MD_StringChop(MD_String8 str, MD_u64 nmax)
{
return MD_StringSubstring(str, 0, str.size - nmax);
}
MD_FUNCTION_IMPL MD_String8
MD_StringPrefix(MD_String8 str, MD_u64 size)
{
return MD_StringSubstring(str, 0, size);
}
MD_FUNCTION_IMPL MD_String8
MD_StringSuffix(MD_String8 str, MD_u64 size)
{
return MD_StringSubstring(str, str.size - size, size);
}
MD_FUNCTION_IMPL MD_b32
MD_StringMatch(MD_String8 a, MD_String8 b, MD_StringMatchFlags flags)
{
int result = 0;
if(a.size == b.size || flags & MD_StringMatchFlag_RightSideSloppy)
{
result = 1;
for(MD_u64 i = 0; i < a.size; i += 1)
{
MD_b32 match = (a.str[i] == b.str[i]);
if(flags & MD_StringMatchFlag_CaseInsensitive)
{
match |= (MD_CharToLower(a.str[i]) == MD_CharToLower(b.str[i]));
}
if(flags & MD_StringMatchFlag_SlashInsensitive)
{
match |= (MD_CorrectSlash(a.str[i]) == MD_CorrectSlash(b.str[i]));
}
if(match == 0)
{
result = 0;
break;
}
}
}
return result;
}
MD_FUNCTION_IMPL MD_u64
MD_FindSubstring(MD_String8 str, MD_String8 substring, MD_u64 start_pos, MD_StringMatchFlags flags)
{
MD_b32 found = 0;
MD_u64 found_idx = str.size;
for(MD_u64 i = start_pos; i < str.size; i += 1)
{
if(i + substring.size <= str.size)
{
MD_String8 substr_from_str = MD_StringSubstring(str, i, i+substring.size);
if(MD_StringMatch(substr_from_str, substring, flags))
{
found_idx = i;
found = 1;
if(!(flags & MD_StringMatchFlag_FindLast))
{
break;
}
}
}
}
return found_idx;
}
MD_FUNCTION_IMPL MD_u64
MD_FindLastSubstring(MD_String8 str, MD_String8 substring, MD_StringMatchFlags flags)
{
return MD_FindSubstring(str, substring, 0, flags | MD_StringMatchFlag_FindLast);
}
MD_FUNCTION_IMPL MD_String8
MD_TrimExtension(MD_String8 string)
{
MD_u64 period_pos = MD_FindLastSubstring(string, MD_S8Lit("."), 0);
if(period_pos < string.size)
{
string.size = period_pos;
}
return string;
}
MD_FUNCTION_IMPL MD_String8
MD_TrimFolder(MD_String8 string)
{
MD_u64 slash_pos = MD_FindLastSubstring(string, MD_S8Lit("/"), MD_StringMatchFlag_SlashInsensitive);
if(slash_pos < string.size)
{
string.str += slash_pos+1;
string.size -= slash_pos+1;
}
return string;
}
MD_FUNCTION_IMPL MD_String8
MD_ExtensionFromPath(MD_String8 string)
{
MD_u64 period_pos = MD_FindLastSubstring(string, MD_S8Lit("."), 0);
if(period_pos < string.size)
{
string.str += period_pos+1;
string.size -= period_pos+1;
}
return string;
}
MD_FUNCTION_IMPL MD_String8
MD_FolderFromPath(MD_String8 string)
{
MD_u64 slash_pos = MD_FindLastSubstring(string, MD_S8Lit("/"), MD_StringMatchFlag_SlashInsensitive);
if(slash_pos < string.size)
{
string.size = slash_pos;
}
return string;
}
MD_FUNCTION_IMPL MD_String8
MD_PushStringCopy(MD_String8 string)
{
MD_String8 res;
res.size = string.size;
res.str = _MD_PushArray(_MD_GetCtx(), MD_u8, string.size+1);
_MD_MemoryCopy(res.str, string.str, string.size);
return res;
}
MD_FUNCTION_IMPL MD_String8
MD_PushStringFV(char *fmt, va_list args)
{
MD_String8 result = {0};
va_list args2;
va_copy(args2, args);
MD_u64 needed_bytes = vsnprintf(0, 0, fmt, args)+1;
result.str = _MD_PushArray(_MD_GetCtx(), MD_u8, needed_bytes);
result.size = needed_bytes-1;
vsnprintf((char*)result.str, needed_bytes, fmt, args2);
return result;
}
MD_FUNCTION_IMPL MD_String8
MD_PushStringF(char *fmt, ...)
{
MD_String8 result = {0};
va_list args;
va_start(args, fmt);
result = MD_PushStringFV(fmt, args);
va_end(args);
return result;
}
MD_FUNCTION_IMPL void
MD_PushStringToList(MD_String8List *list, MD_String8 string)
{
list->node_count += 1;
list->total_size += string.size;
MD_String8Node *node = _MD_PushArray(_MD_GetCtx(), MD_String8Node, 1);
node->next = 0;
node->string = string;
if(list->last == 0)
{
list->first = list->last = node;
}
else
{
list->last->next = node;
list->last = list->last->next;
}
}
MD_FUNCTION_IMPL void
MD_PushStringListToList(MD_String8List *list, MD_String8List *to_push)
{
if(to_push->first)
{
list->node_count += to_push->node_count;
list->total_size += to_push->total_size;
if(list->last == 0)
{
*list = *to_push;
}
else
{
list->last->next = to_push->first;
list->last = to_push->last;
}
}
_MD_MemoryZero(to_push, sizeof(*to_push));
}
MD_FUNCTION_IMPL MD_String8List
MD_SplitString(MD_String8 string, int split_count, MD_String8 *splits)
{
MD_String8List list = {0};
MD_u64 split_start = 0;
for(MD_u64 i = 0; i < string.size; i += 1)
{
MD_b32 was_split = 0;
for(int split_idx = 0; split_idx < split_count; split_idx += 1)
{
MD_b32 match = 0;
if(i + splits[split_idx].size <= string.size)
{
match = 1;
for(MD_u64 split_i = 0; split_i < splits[split_idx].size && i + split_i < string.size; split_i += 1)
{
if(splits[split_idx].str[split_i] != string.str[i + split_i])
{
match = 0;
break;
}
}
}
if(match)
{
MD_String8 split_string = MD_S8(string.str + split_start, i - split_start);
MD_PushStringToList(&list, split_string);
split_start = i + splits[split_idx].size;
i += splits[split_idx].size - 1;
was_split = 1;
break;
}
}
if(was_split == 0 && i == string.size - 1)
{
MD_String8 split_string = MD_S8(string.str + split_start, i+1 - split_start);
MD_PushStringToList(&list, split_string);
break;
}
}
return list;
}
MD_FUNCTION_IMPL MD_String8List
MD_SplitStringByString(MD_String8 string, MD_String8 split)
{
return MD_SplitString(string, 1, &split);
}
MD_FUNCTION_IMPL MD_String8List
MD_SplitStringByCharacter(MD_String8 string, MD_u8 character)
{
return MD_SplitStringByString(string, MD_S8(&character, 1));
}
MD_FUNCTION_IMPL MD_String8
MD_JoinStringList(MD_String8List list)
{
MD_String8 string = {0};
string.size = list.total_size;
string.str = _MD_PushArray(_MD_GetCtx(), MD_u8, string.size);
MD_u64 write_pos = 0;
for(MD_String8Node *node = list.first; node; node = node->next)
{
_MD_MemoryCopy(string.str + write_pos, node->string.str, node->string.size);
write_pos += node->string.size;
}
return string;
}
MD_FUNCTION_IMPL MD_String8
MD_JoinStringListWithSeparator(MD_String8List list, MD_String8 separator)
{
if (list.node_count == 0)
{
return MD_S8Lit("");
}
MD_String8 string = {0};
string.size = list.total_size + (list.node_count - 1)*separator.size;
string.str = _MD_PushArray(_MD_GetCtx(), MD_u8, string.size);
MD_u64 write_pos = 0;
for(MD_String8Node *node = list.first; node; node = node->next)
{
_MD_MemoryCopy(string.str + write_pos, node->string.str, node->string.size);
write_pos += node->string.size;
if (node != list.last){
_MD_MemoryCopy(string.str + write_pos, separator.str, separator.size);
write_pos += separator.size;
}
}
return string;
}
MD_FUNCTION_IMPL MD_i64
MD_I64FromString(MD_String8 string)
{
char str[64];
_MD_WriteStringToBuffer(string, sizeof(str), str);
return atoll(str);
}
MD_FUNCTION_IMPL MD_f64
MD_F64FromString(MD_String8 string)
{
char str[64];
_MD_WriteStringToBuffer(string, sizeof(str), str);
return atof(str);
}
MD_FUNCTION_IMPL MD_u64
MD_HashString(MD_String8 string)
{
MD_u64 result = 5381;
for(MD_u64 i = 0; i < string.size; i += 1)
{
result = ((result << 5) + result) + string.str[i];
}
return result;
}
MD_FUNCTION_IMPL MD_u64
MD_CalculateCStringLength(char *cstr)
{
MD_u64 i = 0;
for(; cstr[i]; i += 1);
return i;
}
MD_FUNCTION_IMPL MD_String8
MD_StyledStringFromString(MD_String8 string, MD_WordStyle word_style, MD_String8 separator)
{
MD_String8 result = {0};
MD_String8List words = {0};
MD_b32 break_on_uppercase = 0;
{
break_on_uppercase = 1;
for(MD_u64 i = 0; i < string.size; i += 1)
{
if(!MD_CharIsAlpha(string.str[i]) && !MD_CharIsDigit(string.str[i]))
{
break_on_uppercase = 0;
break;
}
}
}
MD_b32 making_word = 0;
MD_String8 word = {0};
for(MD_u64 i = 0; i < string.size;)
{
if(making_word)
{
if((break_on_uppercase && MD_CharIsAlphaUpper(string.str[i])) ||
string.str[i] == '_' || MD_CharIsSpace(string.str[i]) ||
i == string.size - 1)
{
if(i == string.size - 1)
{
word.size += 1;
}
making_word = 0;
MD_PushStringToList(&words, word);
}
else
{
word.size += 1;
i += 1;
}
}
else
{
if(MD_CharIsAlpha(string.str[i]))
{
making_word = 1;
word.str = string.str + i;
word.size = 1;
}
i += 1;
}
}
result.size = words.total_size;
if(words.node_count > 1)
{
result.size += separator.size*(words.node_count-1);
}
result.str = _MD_PushArray(_MD_GetCtx(), MD_u8, result.size);
{
MD_u64 write_pos = 0;
for(MD_String8Node *node = words.first; node; node = node->next)
{
// NOTE(rjf): Write word string to result.
{
_MD_MemoryCopy(result.str + write_pos, node->string.str, node->string.size);
// NOTE(rjf): Transform string based on word style.
switch(word_style)
{
case MD_WordStyle_UpperCamelCase:
{
result.str[write_pos] = MD_CharToUpper(result.str[write_pos]);
for(MD_u64 i = write_pos+1; i < write_pos + node->string.size; i += 1)
{
result.str[i] = MD_CharToLower(result.str[i]);
}
}break;
case MD_WordStyle_LowerCamelCase:
{
result.str[write_pos] = node == words.first ? MD_CharToLower(result.str[write_pos]) : MD_CharToUpper(result.str[write_pos]);
for(MD_u64 i = write_pos+1; i < write_pos + node->string.size; i += 1)
{
result.str[i] = MD_CharToLower(result.str[i]);
}
}break;
case MD_WordStyle_UpperCase:
{
for(MD_u64 i = write_pos; i < write_pos + node->string.size; i += 1)
{
result.str[i] = MD_CharToUpper(result.str[i]);
}
}break;
case MD_WordStyle_LowerCase:
{
for(MD_u64 i = write_pos; i < write_pos + node->string.size; i += 1)
{
result.str[i] = MD_CharToLower(result.str[i]);
}
}break;
default: break;
}
write_pos += node->string.size;
}
if(node->next)
{
_MD_MemoryCopy(result.str + write_pos, separator.str, separator.size);
write_pos += separator.size;
}
}
}
return result;
}
MD_FUNCTION_IMPL MD_String8
MD_StringFromNodeKind(MD_NodeKind kind)
{
// NOTE(rjf): Must be kept in sync with MD_NodeKind enum.
static char *cstrs[MD_NodeKind_MAX] =
{
"Nil",
"File",
"Label",
"UnnamedSet",
"Tag",
};
return MD_S8CString(cstrs[kind]);
}
MD_FUNCTION_IMPL MD_String8List
MD_StringListFromNodeFlags(MD_NodeFlags flags)
{
// NOTE(rjf): Must be kept in sync with MD_NodeFlags enum.
static char *flag_cstrs[] =
{
"ParenLeft",
"ParenRight",
"BracketLeft",
"BracketRight",
"BraceLeft",
"BraceRight",
"BeforeSemicolon",
"BeforeComma",
"AfterSemicolon",
"AfterComma",
"Numeric",
"Identifier",
"StringLiteral",
"CharLiteral",
};
MD_String8List list = {0};
MD_u64 bits = sizeof(flags) * 8;
for(MD_u64 i = 0; i < bits && i < MD_ArrayCount(flag_cstrs); i += 1)
{
if(flags & (1ull << i))
{
MD_PushStringToList(&list, MD_S8CString(flag_cstrs[i]));
}
}
return list;
}
////////////////////////////////
// NOTE(allen): Unicode
MD_GLOBAL MD_u8 dd_utf8_class[32] = {
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,2,2,2,2,3,3,4,5,
};
MD_FUNCTION_IMPL MD_UnicodeConsume
MD_CodepointFromUtf8(MD_u8 *str, MD_u64 max)
{
#define MD_bitmask1 0x01
#define MD_bitmask2 0x03
#define MD_bitmask3 0x07
#define MD_bitmask4 0x0F
#define MD_bitmask5 0x1F
#define MD_bitmask6 0x3F
#define MD_bitmask7 0x7F
#define MD_bitmask8 0xFF
#define MD_bitmask9 0x01FF
#define MD_bitmask10 0x03FF
MD_UnicodeConsume result = {~((MD_u32)0), 1};
MD_u8 byte = str[0];
MD_u8 byte_class = dd_utf8_class[byte >> 3];
switch (byte_class)
{
case 1:
{
result.codepoint = byte;
}break;
case 2:
{
if (2 <= max)
{
MD_u8 cont_byte = str[1];
if (dd_utf8_class[cont_byte >> 3] == 0)
{
result.codepoint = (byte & MD_bitmask5) << 6;
result.codepoint |= (cont_byte & MD_bitmask6);
result.advance = 2;
}
}
}break;
case 3:
{
if (3 <= max)
{
MD_u8 cont_byte[2] = {str[1], str[2]};
if (dd_utf8_class[cont_byte[0] >> 3] == 0 &&
dd_utf8_class[cont_byte[1] >> 3] == 0)
{
result.codepoint = (byte & MD_bitmask4) << 12;
result.codepoint |= ((cont_byte[0] & MD_bitmask6) << 6);
result.codepoint |= (cont_byte[1] & MD_bitmask6);
result.advance = 3;
}
}
}break;
case 4:
{
if (4 <= max)
{
MD_u8 cont_byte[3] = {str[1], str[2], str[3]};
if (dd_utf8_class[cont_byte[0] >> 3] == 0 &&
dd_utf8_class[cont_byte[1] >> 3] == 0 &&
dd_utf8_class[cont_byte[2] >> 3] == 0)
{
result.codepoint = (byte & MD_bitmask3) << 18;
result.codepoint |= ((cont_byte[0] & MD_bitmask6) << 12);
result.codepoint |= ((cont_byte[1] & MD_bitmask6) << 6);
result.codepoint |= (cont_byte[2] & MD_bitmask6);
result.advance = 4;
}
}
}break;
}
return(result);
}
MD_FUNCTION_IMPL MD_UnicodeConsume
MD_CodepointFromUtf16(MD_u16 *out, MD_u64 max)
{
MD_UnicodeConsume result = {~((MD_u32)0), 1};
result.codepoint = out[0];
result.advance = 1;
if (1 < max && 0xD800 <= out[0] && out[0] < 0xDC00 && 0xDC00 <= out[1] && out[1] < 0xE000)
{
result.codepoint = ((out[0] - 0xD800) << 10) | (out[1] - 0xDC00);
result.advance = 2;
}
return(result);
}
MD_FUNCTION MD_u32
MD_Utf8FromCodepoint(MD_u8 *out, MD_u32 codepoint)
{
#define MD_bit8 0x80
MD_u32 advance = 0;
if (codepoint <= 0x7F)
{
out[0] = (MD_u8)codepoint;
advance = 1;
}
else if (codepoint <= 0x7FF)
{
out[0] = (MD_bitmask2 << 6) | ((codepoint >> 6) & MD_bitmask5);
out[1] = MD_bit8 | (codepoint & MD_bitmask6);
advance = 2;
}
else if (codepoint <= 0xFFFF)
{
out[0] = (MD_bitmask3 << 5) | ((codepoint >> 12) & MD_bitmask4);
out[1] = MD_bit8 | ((codepoint >> 6) & MD_bitmask6);
out[2] = MD_bit8 | ( codepoint & MD_bitmask6);
advance = 3;
}
else if (codepoint <= 0x10FFFF)
{
out[0] = (MD_bitmask4 << 3) | ((codepoint >> 18) & MD_bitmask3);
out[1] = MD_bit8 | ((codepoint >> 12) & MD_bitmask6);
out[2] = MD_bit8 | ((codepoint >> 6) & MD_bitmask6);
out[3] = MD_bit8 | ( codepoint & MD_bitmask6);
advance = 4;
}
else
{
out[0] = '?';
advance = 1;
}
return(advance);
}
MD_FUNCTION MD_u32
MD_Utf16FromCodepoint(MD_u16 *out, MD_u32 codepoint)
{
MD_u32 advance = 1;
if (codepoint == ~((MD_u32)0))
{
out[0] = (MD_u16)'?';
}
else if (codepoint < 0x10000)
{
out[0] = (MD_u16)codepoint;
}
else
{
MD_u64 v = codepoint - 0x10000;
out[0] = 0xD800 + (v >> 10);
out[1] = 0xDC00 + (v & MD_bitmask10);
advance = 2;
}
return(advance);
}
MD_FUNCTION MD_String8
MD_S8FromS16(MD_String16 in)
{
MD_u64 cap = in.size*3;
MD_u8 *str = _MD_PushArray(_MD_GetCtx(), MD_u8, cap + 1);
MD_u16 *ptr = in.str;
MD_u16 *opl = ptr + in.size;
MD_u64 size = 0;
MD_UnicodeConsume consume;
for (;ptr < opl;)
{
consume = MD_CodepointFromUtf16(ptr, opl - ptr);
ptr += consume.advance;
size += MD_Utf8FromCodepoint(str + size, consume.codepoint);
}
str[size] = 0;
return(MD_S8(str, size));
}
MD_FUNCTION MD_String16
MD_S16FromS8(MD_String8 in)
{
MD_u64 cap = in.size*2;
MD_u16 *str = _MD_PushArray(_MD_GetCtx(), MD_u16, (cap + 1));
MD_u8 *ptr = in.str;
MD_u8 *opl = ptr + in.size;
MD_u64 size = 0;
MD_UnicodeConsume consume;
for (;ptr < opl;)
{
consume = MD_CodepointFromUtf8(ptr, opl - ptr);
ptr += consume.advance;
size += MD_Utf16FromCodepoint(str + size, consume.codepoint);
}
str[size] = 0;
MD_String16 result = {str, size};
return(result);
}
MD_FUNCTION MD_String8
MD_S8FromS32(MD_String32 in)
{
MD_u64 cap = in.size*4;
MD_u8 *str = _MD_PushArray(_MD_GetCtx(), MD_u8, cap + 1);
MD_u32 *ptr = in.str;
MD_u32 *opl = ptr + in.size;
MD_u64 size = 0;
MD_UnicodeConsume consume;
for (;ptr < opl; ptr += 1)
{
size += MD_Utf8FromCodepoint(str + size, *ptr);
}
str[size] = 0;
return(MD_S8(str, size));
}
MD_FUNCTION MD_String32
MD_S32FromS8(MD_String8 in)
{
MD_u64 cap = in.size;
MD_u32 *str = _MD_PushArray(_MD_GetCtx(), MD_u32, (cap + 1));
MD_u8 *ptr = in.str;
MD_u8 *opl = ptr + in.size;
MD_u64 size = 0;
MD_UnicodeConsume consume;
for (;ptr < opl;)
{
consume = MD_CodepointFromUtf8(ptr, opl - ptr);
ptr += consume.advance;
str[size] = consume.codepoint;
size += 1;
}
str[size] = 0;
MD_String32 result = {str, size};
return(result);
}
MD_PRIVATE_FUNCTION_IMPL void
_MD_NodeTable_Initialize(MD_NodeTable *table)
{
if(table->table_size == 0)
{
table->table_size = 4096;
table->table = _MD_PushArray(_MD_GetCtx(), MD_NodeTableSlot *, table->table_size);
}
}
MD_FUNCTION_IMPL MD_NodeTableSlot *
MD_NodeTable_Lookup(MD_NodeTable *table, MD_String8 string)
{
_MD_NodeTable_Initialize(table);
MD_NodeTableSlot *slot = 0;
MD_u64 hash = MD_HashString(string);
MD_u64 index = hash % table->table_size;
for(MD_NodeTableSlot *candidate = table->table[index]; candidate; candidate = candidate->next)
{
if(candidate->hash == hash)
{
slot = candidate;
break;
}
}
return slot;
}
MD_FUNCTION_IMPL MD_b32
MD_NodeTable_Insert(MD_NodeTable *table, MD_NodeTableCollisionRule collision_rule, MD_String8 string, MD_Node *node)
{
_MD_NodeTable_Initialize(table);
MD_NodeTableSlot *slot = 0;
MD_u64 hash = MD_HashString(string);
MD_u64 index = hash % table->table_size;
for(MD_NodeTableSlot *candidate = table->table[index]; candidate; candidate = candidate->next)
{
if(candidate->hash == hash)
{
slot = candidate;
break;
}
}
if(slot == 0 || (slot != 0 && collision_rule == MD_NodeTableCollisionRule_Chain))
{
slot = _MD_PushArray(_MD_GetCtx(), MD_NodeTableSlot, 1);
if(slot)
{
slot->next = table->table[index];
table->table[index] = slot;
}
}
if(slot)
{
slot->node = node;
slot->hash = hash;
}
return !!slot;
}
MD_FUNCTION_IMPL MD_Token
MD_ZeroToken(void)
{
MD_Token token = {0};
return token;
}
MD_FUNCTION_IMPL MD_b32
MD_TokenKindIsWhitespace(MD_TokenKind kind)
{
return kind > MD_TokenKind_WhitespaceMin && kind < MD_TokenKind_WhitespaceMax;
}
MD_FUNCTION_IMPL MD_b32
MD_TokenKindIsComment(MD_TokenKind kind)
{
return(kind == MD_TokenKind_Comment);
}
MD_FUNCTION_IMPL MD_b32
MD_TokenKindIsRegular(MD_TokenKind kind)
{
return(kind > MD_TokenKind_RegularMin && kind < MD_TokenKind_RegularMax);
}
MD_FUNCTION_IMPL MD_ParseCtx
MD_Parse_InitializeCtx(MD_String8 filename, MD_String8 contents)
{
MD_ParseCtx ctx = {0};
ctx.first_root = ctx.last_root = MD_NilNode();
ctx.at = contents.str;
ctx.file_contents = contents;
ctx.filename = filename;
return ctx;
}
MD_PRIVATE_FUNCTION_IMPL void
_MD_PushNodeToList(MD_Node **firstp, MD_Node **lastp, MD_Node *parent, MD_Node *node)
{
if(!MD_NodeIsNil(node))
{
MD_Node *first = *firstp;
MD_Node *last = *lastp;
if(MD_NodeIsNil(last))
{
first = last = node;
node->next = node->prev = MD_NilNode();
}
else
{
last->next = node;
node->next = MD_NilNode();
node->prev = last;
last = last->next;
}
*firstp = first;
*lastp = last;
node->parent = parent;
}
}
MD_FUNCTION_IMPL void
MD_Parse_Bump(MD_ParseCtx *ctx, MD_Token token)
{
ctx->at = token.outer_string.str + token.outer_string.size;
}
MD_FUNCTION_IMPL void
MD_Parse_BumpNext(MD_ParseCtx *ctx)
{
MD_Parse_Bump(ctx, MD_Parse_LexNext(ctx));
}
MD_FUNCTION_IMPL MD_Token
MD_Parse_LexNext(MD_ParseCtx *ctx)
{
MD_Token token = {0};
MD_u8 *one_past_last = ctx->file_contents.str + ctx->file_contents.size;
MD_u8 *first = ctx->at;
if (first < one_past_last)
{
MD_u8 *at = first;
MD_u32 skip_chop_n = 0;
#define MD_TokenizerScan(cond) for (; at < one_past_last && (cond); at += 1)
switch (*at)
{
// NOTE(allen): Whitespace parsing
case '\n':
{
token.kind = MD_TokenKind_Newline;
at += 1;
}break;
case ' ': case '\r': case '\t': case '\f': case '\v':
{
token.kind = MD_TokenKind_Whitespace;
at += 1;
MD_TokenizerScan(*at == ' ' || *at == '\r' || *at == '\t' || *at == '\f' || *at == '\v');
}break;
// NOTE(allen): Comment parsing
case '/':
{
if (at + 1 < one_past_last)
{
if (at[1] == '/')
{
at += 2;
token.kind = MD_TokenKind_Comment;
MD_TokenizerScan(*at != '\n');
}
else if (at[1] == '*')
{
at += 2;
token.kind = MD_TokenKind_Comment;
int counter = 1;
for (;at < one_past_last && counter > 0; at += 1)
{
if (at + 1 < one_past_last)
{
if (at[0] == '*' && at[1] == '/')
{
at += 1;
counter -= 1;
}
else if (at[0] == '/' && at[1] == '*')
{
at += 1;
counter += 1;
}
}
}
}
}
if (token.kind == MD_TokenKind_Nil) goto symbol_lex;
}break;
// NOTE(rjf): "Bundle-of-tokens" strings (`stuff` or ```stuff```)
// In practice no different than a regular string, but provides an
// alternate syntax which will allow tools like 4coder to treat the
// contents as regular tokens.
case '`':
{
token.kind = MD_TokenKind_StringLiteral;
if (at + 2 < one_past_last && at[1] == '`' && at[2] == '`')
{
skip_chop_n = 3;
at += 3;
MD_TokenizerScan(!(at + 2 < one_past_last && at[0] == '`' && at[1] == '`' && at[2] == '`'));
at += 3;
}
else
{
// TODO(allen): escape sequences?
skip_chop_n = 1;
at += 1;
MD_TokenizerScan(*at != '\n' && *at != '`');
if (*at == '`') at += 1;
}
}break;
// NOTE(allen): Strings
case '"':
{
token.kind = MD_TokenKind_StringLiteral;
if (at + 2 < one_past_last && at[1] == '"' && at[2] == '"')
{
skip_chop_n = 3;
at += 3;
MD_TokenizerScan(!(at + 2 < one_past_last && at[0] == '"' && at[1] == '"' && at[2] == '"'));
at += 3;
}
else
{
// TODO(allen): escape sequences?
skip_chop_n = 1;
at += 1;
MD_TokenizerScan(*at != '\n' && *at != '"');
if (*at == '"') at += 1;
}
}break;
case '\'':
{
if (at + 2 < one_past_last && at[1] == '\'' && at[2] == '\'')
{
token.kind = MD_TokenKind_StringLiteral;
skip_chop_n = 3;
at += 3;
MD_TokenizerScan(!(at + 2 < one_past_last && at[0] == '\'' && at[1] == '\'' && at[2] == '\''));
at += 3;
}
else
{
token.kind = MD_TokenKind_CharLiteral;
// TODO(allen): escape sequences?
skip_chop_n = 1;
at += 1;
MD_TokenizerScan(*at != '\n' && *at != '\'');
if (*at == '\'') at += 1;
}
}break;
// NOTE(allen): Identifiers, Numbers, Operators
default:
{
if (MD_CharIsAlpha(*at))
{
token.kind = MD_TokenKind_Identifier;
at += 1;
MD_TokenizerScan(MD_CharIsAlpha(*at) || MD_CharIsDigit(*at) || *at == '_');
}
else if (MD_CharIsDigit(*at) ||
(at + 1 < one_past_last && at[0] == '-' && MD_CharIsDigit(at[1])))
{
token.kind = MD_TokenKind_NumericLiteral;
at += 1;
MD_TokenizerScan(MD_CharIsAlpha(*at) || MD_CharIsDigit(*at) || *at == '.');
}
else if (MD_CharIsSymbol(*at))
{
symbol_lex:
token.kind = MD_TokenKind_Symbol;
at += 1;
}
}break;
}
token.outer_string = MD_S8Range(first, at);
token.string = MD_StringSubstring(token.outer_string, skip_chop_n, token.outer_string.size - skip_chop_n);
ctx->at = at;
}
return token;
}
MD_FUNCTION_IMPL MD_Token
MD_Parse_PeekSkipSome(MD_ParseCtx *ctx, MD_TokenGroups skip_groups)
{
MD_ParseCtx ctx_restore = *ctx;
MD_b32 skip_comment = (skip_groups & MD_TokenGroup_Comment);
MD_b32 skip_whitespace = (skip_groups & MD_TokenGroup_Whitespace);
MD_b32 skip_regular = (skip_groups & MD_TokenGroup_Regular);
MD_Token result = {0};
loop:
{
result = MD_Parse_LexNext(ctx);
if ((skip_comment && MD_TokenKindIsComment(result.kind)) ||
(skip_whitespace && MD_TokenKindIsWhitespace(result.kind)) ||
(skip_regular && MD_TokenKindIsRegular(result.kind))){
MD_Parse_Bump(ctx, result);
goto loop;
}
}
{
// TODO(allen): I'm not a fan of what this implies.
*ctx = ctx_restore;
}
return result;
}
MD_FUNCTION_IMPL MD_b32
MD_Parse_TokenMatch(MD_Token token, MD_String8 string, MD_StringMatchFlags flags)
{
return MD_StringMatch(token.string, string, flags);
}
MD_FUNCTION_IMPL MD_b32
MD_Parse_Require(MD_ParseCtx *ctx, MD_String8 string)
{
int result = 0;
MD_Token token_any = MD_Parse_PeekSkipSome(ctx, 0);
if(MD_StringMatch(token_any.string, string, 0))
{
result = 1;
MD_Parse_Bump(ctx, token_any);
goto end;
}
MD_Token token_regular = MD_Parse_PeekSkipSome(ctx, MD_TokenGroup_Comment|MD_TokenGroup_Whitespace);
if(MD_StringMatch(token_regular.string, string, 0))
{
result = 1;
MD_Parse_Bump(ctx, token_regular);
goto end;
}
end:;
return result;
}
MD_FUNCTION_IMPL MD_b32
MD_Parse_RequireKind(MD_ParseCtx *ctx, MD_TokenKind kind, MD_Token *out_token)
{
int result = 0;
MD_TokenGroups skip_groups = MD_TokenGroup_Comment|MD_TokenGroup_Whitespace;
if (MD_TokenKindIsWhitespace(kind))
{
skip_groups &= ~MD_TokenGroup_Whitespace;
}
if (MD_TokenKindIsComment(kind))
{
skip_groups &= ~MD_TokenGroup_Comment;
}
MD_Token token = MD_Parse_PeekSkipSome(ctx, skip_groups);
if(token.kind == kind)
{
result = 1;
MD_Parse_Bump(ctx, token);
if(out_token)
{
*out_token = token;
}
}
return result;
}
MD_PRIVATE_FUNCTION_IMPL void
_MD_Error(MD_ParseCtx *ctx, char *fmt, ...)
{
MD_Error *error = _MD_PushArray(_MD_GetCtx(), MD_Error, 1);
error->filename = ctx->filename;
va_list args;
va_start(args, fmt);
error->string = MD_PushStringFV(fmt, args);
va_end(args);
}
MD_PRIVATE_FUNCTION_IMPL MD_Node *
_MD_MakeNode(MD_NodeKind kind, MD_String8 string, MD_String8 whole_string, MD_String8 filename,
MD_u8 *file_contents, MD_u8 *at)
{
MD_Node *node = _MD_PushArray(_MD_GetCtx(), MD_Node, 1);
if(node)
{
node->kind = kind;
node->string = string;
node->whole_string = whole_string;
node->next = node->prev = node->parent = node->first_child = node->last_child = node->first_tag = node->last_tag = MD_NilNode();
node->filename = filename;
node->file_contents = file_contents;
node->at = at;
}
else
{
node = MD_NilNode();
}
return node;
}
MD_PRIVATE_FUNCTION_IMPL MD_Node *
_MD_MakeNodeFromToken_Ctx(MD_ParseCtx *ctx, MD_NodeKind kind, MD_Token token)
{
return _MD_MakeNode(kind, token.string, token.outer_string, ctx->filename, ctx->file_contents.str, ctx->at);
}
MD_PRIVATE_FUNCTION_IMPL MD_Node *
_MD_MakeNodeFromString_Ctx(MD_ParseCtx *ctx, MD_NodeKind kind, MD_String8 string)
{
return _MD_MakeNode(kind, string, string, ctx->filename, ctx->file_contents.str, ctx->at);
}
typedef MD_u32 _MD_ParseSetFlags;
enum
{
_MD_ParseSetFlag_Paren = (1<<0),
_MD_ParseSetFlag_Brace = (1<<1),
_MD_ParseSetFlag_Bracket = (1<<2),
_MD_ParseSetFlag_Implicit = (1<<3),
};
MD_PRIVATE_FUNCTION_IMPL MD_ParseResult _MD_ParseOneNode(MD_ParseCtx *ctx);
MD_PRIVATE_FUNCTION_IMPL void _MD_ParseSet(MD_ParseCtx *ctx, MD_Node *parent, _MD_ParseSetFlags flags, MD_Node **first_out, MD_Node **last_out);
MD_PRIVATE_FUNCTION_IMPL void _MD_ParseTagList(MD_ParseCtx *ctx, MD_Node **first_out, MD_Node **last_out);
MD_PRIVATE_FUNCTION_IMPL void
_MD_SetNodeFlagsByToken(MD_Node *node, MD_Token token)
{
#define Flag(_kind, _flag) if(token.kind == _kind) { node->flags |= _flag; }
Flag(MD_TokenKind_Identifier, MD_NodeFlag_Identifier);
Flag(MD_TokenKind_NumericLiteral, MD_NodeFlag_Numeric);
Flag(MD_TokenKind_StringLiteral, MD_NodeFlag_StringLiteral);
Flag(MD_TokenKind_CharLiteral, MD_NodeFlag_CharLiteral);
#undef Flag
}
MD_PRIVATE_FUNCTION_IMPL MD_ParseResult
_MD_ParseOneNode(MD_ParseCtx *ctx)
{
MD_u8 *at_first = ctx->at;
MD_ParseResult result = {0};
result.node = MD_NilNode();
MD_Token token = {0};
MD_Node *first_tag = 0;
MD_Node *last_tag = 0;
_MD_ParseTagList(ctx, &first_tag, &last_tag);
// NOTE(rjf): Unnamed Sets
MD_TokenGroups skip_groups = MD_TokenGroup_Whitespace|MD_TokenGroup_Comment;
if(MD_Parse_TokenMatch(MD_Parse_PeekSkipSome(ctx, skip_groups), MD_S8Lit("("), 0) ||
MD_Parse_TokenMatch(MD_Parse_PeekSkipSome(ctx, skip_groups), MD_S8Lit("{"), 0) ||
MD_Parse_TokenMatch(MD_Parse_PeekSkipSome(ctx, skip_groups), MD_S8Lit("["), 0))
{
result.node = _MD_MakeNodeFromString_Ctx(ctx, MD_NodeKind_UnnamedSet, MD_S8Lit(""));
_MD_ParseSet(ctx, result.node,
_MD_ParseSetFlag_Paren |
_MD_ParseSetFlag_Brace |
_MD_ParseSetFlag_Bracket,
&result.node->first_child,
&result.node->last_child);
goto end_parse;
}
// NOTE(rjf): Labels
else if(MD_Parse_RequireKind(ctx, MD_TokenKind_Identifier, &token) ||
MD_Parse_RequireKind(ctx, MD_TokenKind_NumericLiteral, &token) ||
MD_Parse_RequireKind(ctx, MD_TokenKind_StringLiteral, &token) ||
MD_Parse_RequireKind(ctx, MD_TokenKind_CharLiteral, &token) ||
MD_Parse_RequireKind(ctx, MD_TokenKind_Symbol, &token))
{
result.node = MD_MakeNodeFromToken(MD_NodeKind_Label, ctx->filename, ctx->file_contents.str, ctx->at, token);
_MD_SetNodeFlagsByToken(result.node, token);
// NOTE(rjf): Children
if(MD_Parse_Require(ctx, MD_S8Lit(":")))
{
_MD_ParseSet(ctx, result.node,
_MD_ParseSetFlag_Paren |
_MD_ParseSetFlag_Brace |
_MD_ParseSetFlag_Bracket |
_MD_ParseSetFlag_Implicit,
&result.node->first_child,
&result.node->last_child);
}
goto end_parse;
}
end_parse:;
if(!MD_NodeIsNil(result.node))
{
result.bytes_parsed = (MD_u64)(ctx->at - at_first);
result.node->first_tag = first_tag;
result.node->last_tag = last_tag;
for(MD_Node *tag = first_tag; !MD_NodeIsNil(tag); tag = tag->next)
{
tag->parent = result.node;
}
}
return result;
}
MD_PRIVATE_FUNCTION_IMPL void
_MD_ParseSet(MD_ParseCtx *ctx, MD_Node *parent, _MD_ParseSetFlags flags,
MD_Node **first_out, MD_Node **last_out)
{
MD_Node *first = MD_NilNode();
MD_Node *last = MD_NilNode();
MD_b32 brace = 0;
MD_b32 paren = 0;
MD_b32 bracket = 0;
MD_b32 terminate_with_separator = !!(flags & _MD_ParseSetFlag_Implicit);
if(flags & _MD_ParseSetFlag_Brace && MD_Parse_Require(ctx, MD_S8Lit("{")))
{
parent->flags |= MD_NodeFlag_BraceLeft;
brace = 1;
terminate_with_separator = 0;
}
else if(flags & _MD_ParseSetFlag_Paren && MD_Parse_Require(ctx, MD_S8Lit("(")))
{
parent->flags |= MD_NodeFlag_ParenLeft;
paren = 1;
terminate_with_separator = 0;
}
else if(flags & _MD_ParseSetFlag_Bracket && MD_Parse_Require(ctx, MD_S8Lit("[")))
{
parent->flags |= MD_NodeFlag_BracketLeft;
bracket = 1;
terminate_with_separator = 0;
}
// NOTE(rjf): Parse children.
if(brace || paren || bracket || terminate_with_separator)
{
MD_NodeFlags next_child_flags = 0;
for(MD_u64 child_idx = 0;; child_idx += 1)
{
if(brace)
{
if(MD_Parse_Require(ctx, MD_S8Lit("}")))
{
parent->flags |= MD_NodeFlag_BraceRight;
goto end_parse;
}
}
else if(paren || bracket)
{
if(flags & _MD_ParseSetFlag_Paren && MD_Parse_Require(ctx, MD_S8Lit(")")))
{
parent->flags |= MD_NodeFlag_ParenRight;
goto end_parse;
}
else if(flags & _MD_ParseSetFlag_Bracket && MD_Parse_Require(ctx, MD_S8Lit("]")))
{
parent->flags |= MD_NodeFlag_BracketRight;
goto end_parse;
}
}
else
{
MD_Token peek = MD_Parse_PeekSkipSome(ctx, MD_TokenGroup_Whitespace | MD_TokenGroup_Comment);
if(MD_Parse_TokenMatch(peek, MD_S8Lit("}"), 0) ||
MD_Parse_TokenMatch(peek, MD_S8Lit(")"), 0) ||
MD_Parse_TokenMatch(peek, MD_S8Lit("]"), 0))
{
goto end_parse;
}
}
MD_ParseResult parse = _MD_ParseOneNode(ctx);
MD_Node *child = parse.node;
child->flags |= next_child_flags;
next_child_flags = 0;
if(MD_NodeIsNil(child))
{
goto end_parse;
}
else
{
_MD_PushNodeToList(&first, &last, parent, child);
}
// NOTE(rjf): Separators.
{
MD_b32 result = 0;
if(MD_Parse_Require(ctx, MD_S8Lit(",")))
{
result |= 1;
child->flags |= MD_NodeFlag_BeforeComma;
next_child_flags |= MD_NodeFlag_AfterComma;
}
else if(MD_Parse_Require(ctx, MD_S8Lit(";")))
{
result |= 1;
child->flags |= MD_NodeFlag_BeforeSemicolon;
next_child_flags |= MD_NodeFlag_AfterSemicolon;
}
result |= !!MD_Parse_Require(ctx, MD_S8Lit("\n"));
if(result && terminate_with_separator)
{
goto end_parse;
}
}
}
}
end_parse:;
*first_out = first;
*last_out = last;
}
MD_PRIVATE_FUNCTION_IMPL void
_MD_ParseTagList(MD_ParseCtx *ctx, MD_Node **first_out, MD_Node **last_out)
{
MD_Node *first = MD_NilNode();
MD_Node *last = MD_NilNode();
for(;MD_Parse_Require(ctx, MD_S8Lit("@"));)
{
MD_Token name = {0};
if(MD_Parse_RequireKind(ctx, MD_TokenKind_Identifier, &name))
{
MD_Node *tag = _MD_MakeNodeFromToken_Ctx(ctx, MD_NodeKind_Tag, name);
_MD_ParseSet(ctx, tag, _MD_ParseSetFlag_Paren, &tag->first_child, &tag->last_child);
_MD_PushNodeToList(&first, &last, MD_NilNode(), tag);
}
else
{
break;
}
}
*first_out = first;
*last_out = last;
}
MD_FUNCTION_IMPL MD_ParseResult
MD_ParseOneNode(MD_String8 filename, MD_String8 contents)
{
MD_ParseCtx ctx = MD_Parse_InitializeCtx(filename, contents);
return _MD_ParseOneNode(&ctx);
}
MD_FUNCTION MD_Node *
MD_ParseWholeString(MD_String8 filename, MD_String8 contents)
{
MD_Node *root = MD_MakeNodeFromString(MD_NodeKind_File, filename, contents.str, contents.str, MD_PushStringF("`DD Parsed From \"%.*s\"`", MD_StringExpand(filename)));
if(contents.size > 0)
{
MD_ParseCtx ctx = MD_Parse_InitializeCtx(filename, contents);
for(MD_u64 i = 0; i < contents.size;)
{
MD_ParseResult parse = _MD_ParseOneNode(&ctx);
_MD_PushNodeToList(&root->first_child, &root->last_child, root, parse.node);
i += parse.bytes_parsed;
if(parse.bytes_parsed == 0)
{
break;
}
}
}
return root;
}
MD_FUNCTION_IMPL MD_Node *
MD_ParseWholeFile(MD_String8 filename)
{
return MD_ParseWholeString(filename, MD_LoadEntireFile(filename));
}
MD_FUNCTION_IMPL MD_b32
MD_NodeIsNil(MD_Node *node)
{
return node == 0 || node == &_md_nil_node || node->kind == MD_NodeKind_Nil;
}
MD_FUNCTION_IMPL MD_Node *
MD_NilNode(void) { return &_md_nil_node; }
MD_FUNCTION_IMPL MD_Node *
MD_MakeNodeFromToken(MD_NodeKind kind, MD_String8 filename, MD_u8 *file, MD_u8 *at, MD_Token token)
{
return _MD_MakeNode(kind, token.string, token.outer_string, filename, file, at);
}
MD_FUNCTION_IMPL MD_Node *
MD_MakeNodeFromString(MD_NodeKind kind, MD_String8 filename, MD_u8 *file, MD_u8 *at, MD_String8 string)
{
return _MD_MakeNode(kind, string, string, filename, file, at);
}
MD_FUNCTION_IMPL void
MD_PushSibling(MD_Node **first, MD_Node **last, MD_Node *parent, MD_Node *new_sibling)
{
_MD_PushNodeToList(first, last, parent, new_sibling);
}
MD_FUNCTION_IMPL void
MD_PushChild(MD_Node *parent, MD_Node *new_child)
{
_MD_PushNodeToList(&parent->first_child, &parent->last_child, parent, new_child);
}
MD_FUNCTION_IMPL void
MD_PushTag(MD_Node *node, MD_Node *tag)
{
_MD_PushNodeToList(&node->first_tag, &node->last_tag, node, tag);
}
MD_FUNCTION_IMPL MD_b32
MD_NodeMatch(MD_Node *a, MD_Node *b, MD_StringMatchFlags str_flags, MD_NodeMatchFlags node_flags)
{
MD_b32 result = 0;
if(a->kind == b->kind && MD_StringMatch(a->string, b->string, str_flags))
{
result = 1;
if(a->kind != MD_NodeKind_Tag && node_flags & MD_NodeMatchFlag_Tags)
{
for(MD_Node *a_tag = a->first_tag, *b_tag = b->first_tag;
!MD_NodeIsNil(a_tag) || !MD_NodeIsNil(b_tag);
a_tag = a_tag->next, b_tag = b_tag->next)
{
if(MD_NodeMatch(a_tag, b_tag, str_flags, 0))
{
if(node_flags & MD_NodeMatchFlag_TagArguments)
{
for(MD_Node *a_tag_arg = a_tag->first_child, *b_tag_arg = b_tag->first_child;
!MD_NodeIsNil(a_tag_arg) || !MD_NodeIsNil(b_tag_arg);
a_tag_arg = a_tag_arg->next, b_tag_arg = b_tag_arg->next)
{
if(!MD_NodeDeepMatch(a_tag_arg, b_tag_arg, str_flags, node_flags))
{
result = 0;
goto end;
}
}
}
}
else
{
result = 0;
goto end;
}
}
}
}
end:;
return result;
}
MD_FUNCTION_IMPL MD_b32
MD_NodeDeepMatch(MD_Node *a, MD_Node *b, MD_StringMatchFlags str_flags, MD_NodeMatchFlags node_flags)
{
MD_b32 result = MD_NodeMatch(a, b, str_flags, node_flags);
if(result)
{
for(MD_Node *a_child = a->first_child, *b_child = b->first_child;
!MD_NodeIsNil(a_child) || !MD_NodeIsNil(b_child);
a_child = a_child->next, b_child = b_child->next)
{
if(!MD_NodeDeepMatch(a_child, b_child, str_flags, node_flags))
{
result = 0;
goto end;
}
}
}
end:;
return result;
}
MD_FUNCTION_IMPL MD_Node *
MD_NodeFromString(MD_Node *first, MD_Node *last, MD_String8 string)
{
MD_Node *result = MD_NilNode();
for(MD_Node *node = first; !MD_NodeIsNil(node); node = node->next)
{
if(MD_StringMatch(string, node->string, 0))
{
result = node;
break;
}
}
return result;
}
MD_FUNCTION_IMPL MD_Node *
MD_NodeFromIndex(MD_Node *first, MD_Node *last, int n)
{
MD_Node *result = MD_NilNode();
if(n >= 0)
{
int idx = 0;
for(MD_Node *node = first; !MD_NodeIsNil(node); node = node->next, idx += 1)
{
if(idx == n)
{
result = node;
break;
}
}
}
return result;
}
MD_FUNCTION_IMPL int
MD_IndexFromNode(MD_Node *node)
{
int idx = 0;
if(node && !MD_NodeIsNil(node))
{
for(MD_Node *last = node->prev; last; last = last->prev, idx += 1);
}
return idx;
}
MD_FUNCTION_IMPL MD_Node *
MD_NextNodeSibling(MD_Node *last, MD_String8 string)
{
MD_Node *result = MD_NilNode();
if(last)
{
for(MD_Node *node = last->next; node; node = node->next)
{
if(MD_StringMatch(string, node->string, 0))
{
result = node;
break;
}
}
}
return result;
}
MD_FUNCTION_IMPL MD_Node *
MD_ChildFromString(MD_Node *node, MD_String8 child_string)
{
return MD_NodeFromString(node->first_child, node->last_child, child_string);
}
MD_FUNCTION_IMPL MD_Node *
MD_TagFromString(MD_Node *node, MD_String8 tag_string)
{
return MD_NodeFromString(node->first_tag, node->last_tag, tag_string);
}
MD_FUNCTION_IMPL MD_Node *
MD_ChildFromIndex(MD_Node *node, int n)
{
return MD_NodeFromIndex(node->first_child, node->last_child, n);
}
MD_FUNCTION_IMPL MD_Node *
MD_TagFromIndex(MD_Node *node, int n)
{
return MD_NodeFromIndex(node->first_child, node->last_child, n);
}
MD_FUNCTION_IMPL MD_Node *
MD_TagArgFromIndex(MD_Node *node, MD_String8 tag_string, int n)
{
MD_Node *tag = MD_TagFromString(node, tag_string);
return MD_ChildFromIndex(tag, n);
}
MD_FUNCTION_IMPL MD_b32
MD_NodeHasTag(MD_Node *node, MD_String8 tag_string)
{
return !MD_NodeIsNil(MD_TagFromString(node, tag_string));
}
MD_FUNCTION_IMPL MD_CodeLoc
MD_CodeLocFromNode(MD_Node *node)
{
MD_CodeLoc loc;
loc.filename = node->filename;
loc.line = 1;
loc.column = 1;
for(MD_u64 i = 0; node->file_contents[i]; i += 1)
{
if(node->file_contents[i] == '\n')
{
loc.line += 1;
loc.column = 1;
}
else
{
loc.column += 1;
}
if(node->file_contents + i == node->at)
{
break;
}
}
return loc;
}
MD_FUNCTION_IMPL MD_i64
MD_ChildCountFromNode(MD_Node *node)
{
MD_i64 result = 0;
for(MD_EachNode(child, node->first_child))
{
result += 1;
}
return result;
}
MD_FUNCTION_IMPL MD_i64
MD_TagCountFromNode(MD_Node *node)
{
MD_i64 result = 0;
for(MD_EachNode(tag, node->first_tag))
{
result += 1;
}
return result;
}
MD_FUNCTION_IMPL MD_i64
MD_ChildCountFromNodeAndString(MD_Node *node, MD_String8 string, MD_StringMatchFlags flags)
{
MD_i64 result = 0;
for(MD_EachNode(child, node->first_child))
{
if(MD_StringMatch(child->string, string, flags))
{
result += 1;
}
}
return result;
}
MD_FUNCTION_IMPL MD_i64
MD_TagCountFromNodeAndString(MD_Node *node, MD_String8 string, MD_StringMatchFlags flags)
{
MD_i64 result = 0;
for(MD_EachNode(tag, node->first_tag))
{
if(MD_StringMatch(tag->string, string, flags))
{
result += 1;
}
}
return result;
}
MD_FUNCTION_IMPL void
MD_NodeMessage(MD_Node *node, MD_MessageKind kind, MD_String8 str)
{
char *kind_name = kind == MD_MessageKind_Error ? "error" : "warning";
MD_CodeLoc loc = MD_CodeLocFromNode(node);
fprintf(stderr, "%.*s(%i:%i): %s: %.*s\n",
MD_StringExpand(loc.filename),
loc.line, loc.column,
kind_name,
MD_StringExpand(str));
}
MD_FUNCTION_IMPL void
MD_NodeError(MD_Node *node, MD_String8 str)
{
MD_NodeMessage(node, MD_MessageKind_Error, str);
}
MD_FUNCTION_IMPL void
MD_NodeWarning(MD_Node *node, MD_String8 str)
{
MD_NodeMessage(node, MD_MessageKind_Warning, str);
}
MD_FUNCTION_IMPL void
MD_NodeMessageF(MD_Node *node, MD_MessageKind kind, char *fmt, ...)
{
va_list args;
va_start(args, fmt);
MD_NodeMessage(node, kind ,MD_PushStringFV(fmt, args));
va_end(args);
}
MD_FUNCTION_IMPL void
MD_NodeErrorF(MD_Node *node, char *fmt, ...)
{
va_list args;
va_start(args, fmt);
MD_NodeError(node, MD_PushStringFV(fmt, args));
va_end(args);
}
MD_FUNCTION_IMPL void
MD_NodeWarningF(MD_Node *node, char *fmt, ...)
{
va_list args;
va_start(args, fmt);
MD_NodeWarning(node, MD_PushStringFV(fmt, args));
va_end(args);
}
MD_GLOBAL MD_Expr _md_nil_expr =
{
&_md_nil_node,
MD_ExprKind_Nil,
&_md_nil_expr,
{&_md_nil_expr, &_md_nil_expr },
};
MD_FUNCTION_IMPL MD_Expr *
MD_NilExpr(void)
{
return &_md_nil_expr;
}
MD_FUNCTION_IMPL MD_b32
MD_ExprIsNil(MD_Expr *expr)
{
return expr == 0 || expr == &_md_nil_expr || expr->kind == MD_ExprKind_Nil;
}
MD_FUNCTION_IMPL MD_ExprKind
MD_PreUnaryExprKindFromNode(MD_Node *node)
{
MD_ExprKind kind = MD_ExprKind_Nil;
#define MD_ExprStr(str, _kind) if(MD_StringMatch(node->string, MD_S8Lit(str), 0)) {kind = _kind; goto end;}
if(!MD_NodeIsNil(node->first_child))
{
if(node->flags & MD_NodeFlag_ParenLeft &&
node->flags & MD_NodeFlag_ParenRight)
{
kind = MD_ExprKind_Call;
}
else if(node->flags & MD_NodeFlag_BracketLeft &&
node->flags & MD_NodeFlag_BracketRight)
{
kind = MD_ExprKind_Subscript;
}
}
else
{
MD_ExprStr("-", MD_ExprKind_Negative);
MD_ExprStr("~", MD_ExprKind_BitNot);
MD_ExprStr("!", MD_ExprKind_BoolNot);
MD_ExprStr("*", MD_ExprKind_Dereference);
MD_ExprStr("&", MD_ExprKind_Reference);
}
end:;
#undef MD_ExprStr
return kind;
}
MD_FUNCTION_IMPL MD_ExprKind
MD_PostUnaryExprKindFromNode(MD_Node *node)
{
MD_ExprKind kind = MD_ExprKind_Nil;
if(!MD_NodeIsNil(node->first_child))
{
if(node->flags & MD_NodeFlag_ParenLeft &&
node->flags & MD_NodeFlag_ParenRight)
{
kind = MD_ExprKind_Call;
}
else if(node->flags & MD_NodeFlag_BracketLeft &&
node->flags & MD_NodeFlag_BracketRight)
{
kind = MD_ExprKind_Subscript;
}
}
return kind;
}
MD_FUNCTION_IMPL MD_ExprKind
MD_BinaryExprKindFromNode(MD_Node *node)
{
MD_ExprKind kind = MD_ExprKind_Nil;
#define MD_ExprStr(str, _kind) if(MD_StringMatch(node->string, MD_S8Lit(str), 0)) {kind = _kind; goto end;}
if(node->kind == MD_NodeKind_Label && MD_NodeIsNil(node->first_child))
{
MD_ExprStr(".", MD_ExprKind_Dot);
MD_ExprStr("->", MD_ExprKind_Arrow);
MD_ExprStr("+", MD_ExprKind_Add);
MD_ExprStr("-", MD_ExprKind_Subtract);
MD_ExprStr("*", MD_ExprKind_Multiply);
MD_ExprStr("/", MD_ExprKind_Divide);
MD_ExprStr("%", MD_ExprKind_Mod);
MD_ExprStr("==", MD_ExprKind_IsEqual);
MD_ExprStr("!=", MD_ExprKind_IsNotEqual);
MD_ExprStr("<", MD_ExprKind_LessThan);
MD_ExprStr(">", MD_ExprKind_GreaterThan);
MD_ExprStr("<=", MD_ExprKind_LessThanEqualTo);
MD_ExprStr(">=", MD_ExprKind_GreaterThanEqualTo);
MD_ExprStr("&&", MD_ExprKind_BoolAnd);
MD_ExprStr("||", MD_ExprKind_BoolOr);
MD_ExprStr("!", MD_ExprKind_BoolNot);
MD_ExprStr("&", MD_ExprKind_BitAnd);
MD_ExprStr("|", MD_ExprKind_BitOr);
MD_ExprStr("^", MD_ExprKind_BitXor);
MD_ExprStr("<<", MD_ExprKind_LeftShift);
MD_ExprStr(">>", MD_ExprKind_RightShift);
}
end:;
#undef MD_ExprStr
return kind;
}
MD_FUNCTION_IMPL MD_ExprPrec
MD_ExprPrecFromExprKind(MD_ExprKind kind)
{
// 0: Invalid
// 12: (unary) - ~ !
// 11: . -> () []
// 10: * / %
// 9: + -
// 8: << >>
// 7: < <= > >=
// 6: == !=
// 5: (bitwise) &
// 4: ^
// 3: |
// 2: &&
// 1: ||
MD_ExprPrec kind_to_prec[] =
{
+0, // MD_ExprKind_Nil
+0, // MD_ExprKind_Atom
+11, // MD_ExprKind_Dot
+11, // MD_ExprKind_Arrow
+11, // MD_ExprKind_Call
+11, // MD_ExprKind_Subscript
+12, // MD_ExprKind_Dereference
+12, // MD_ExprKind_Reference
+9, // MD_ExprKind_Add
+9, // MD_ExprKind_Subtract
+10, // MD_ExprKind_Multiply
+10, // MD_ExprKind_Divide
+10, // MD_ExprKind_Mod
+6, // MD_ExprKind_IsEqual
+6, // MD_ExprKind_IsNotEqual
+7, // MD_ExprKind_LessThan
+7, // MD_ExprKind_GreaterThan
+7, // MD_ExprKind_LessThanEqualTo
+7, // MD_ExprKind_GreaterThanEqualTo
+2, // MD_ExprKind_BoolAnd
+1, // MD_ExprKind_BoolOr
+12, // MD_ExprKind_BoolNot
+5, // MD_ExprKind_BitAnd
+3, // MD_ExprKind_BitOr
+12, // MD_ExprKind_BitNot
+4, // MD_ExprKind_BitXor
+8, // MD_ExprKind_LeftShift
+8, // MD_ExprKind_RightShift
+12, // MD_ExprKind_Negative
+0, // MD_ExprKind_Pointer
+0, // MD_ExprKind_Array
};
return kind_to_prec[kind];
}
MD_FUNCTION_IMPL MD_Expr *
MD_MakeExpr(MD_Node *node, MD_ExprKind kind, MD_Expr *left, MD_Expr *right)
{
MD_Expr *expr = _MD_PushArray(_MD_GetCtx(), MD_Expr, 1);
if(expr)
{
if(left == 0) left = MD_NilExpr();
if(right == 0) right = MD_NilExpr();
expr->node = node;
expr->kind = kind;
expr->sub[0] = left;
expr->sub[1] = right;
}
else
{
expr = MD_NilExpr();
}
return expr;
}
typedef struct _MD_NodeParseCtx _MD_NodeParseCtx;
struct _MD_NodeParseCtx
{
MD_Node *at;
MD_Node *last;
MD_Node *one_past_last;
};
MD_PRIVATE_FUNCTION_IMPL MD_b32
_MD_NodeParse_ConsumeAtom(_MD_NodeParseCtx *ctx, MD_Node **out)
{
MD_b32 result = 0;
if(ctx->at->kind == MD_NodeKind_Label &&
MD_NodeIsNil(ctx->at->first_child))
{
result = 1;
if(out)
{
*out = ctx->at;
}
ctx->at = ctx->at->next;
}
return result;
}
MD_PRIVATE_FUNCTION_IMPL MD_b32
_MD_NodeParse_ConsumeSet(_MD_NodeParseCtx *ctx, MD_Node **out)
{
MD_b32 result = 0;
if(!MD_NodeIsNil(ctx->at->first_child))
{
if(out)
{
*out = ctx->at;
}
ctx->at = ctx->at->next;
result = 1;
}
return result;
}
MD_PRIVATE_FUNCTION_IMPL MD_b32
_MD_NodeParse_ConsumeLiteral(_MD_NodeParseCtx *ctx, MD_Node **out)
{
MD_b32 result = 0;
if(ctx->at->flags & MD_NodeFlag_StringLiteral ||
ctx->at->flags & MD_NodeFlag_CharLiteral ||
ctx->at->flags & MD_NodeFlag_Numeric)
{
result = 1;
if(out)
{
*out = ctx->at;
}
ctx->at = ctx->at->next;
}
return result;
}
MD_PRIVATE_FUNCTION_IMPL MD_b32
_MD_NodeParse_Consume(_MD_NodeParseCtx *ctx, MD_String8 string, MD_Node **out)
{
MD_b32 result = 0;
if(MD_StringMatch(ctx->at->string, string, 0))
{
result = 1;
if(out)
{
*out = ctx->at;
}
ctx->at = ctx->at->next;
}
return result;
}
MD_PRIVATE_FUNCTION_IMPL void
_MD_NodeParse_Next(_MD_NodeParseCtx *ctx)
{
ctx->at = ctx->at->next;
}
MD_PRIVATE_FUNCTION_IMPL MD_Expr *
_MD_ParseExpr_(_MD_NodeParseCtx *ctx, int precedence_in);
MD_PRIVATE_FUNCTION_IMPL MD_Expr *
_MD_ParseExpr(_MD_NodeParseCtx *ctx);
MD_PRIVATE_FUNCTION_IMPL MD_Expr *
_MD_ParseUnaryExpr(_MD_NodeParseCtx *ctx)
{
MD_Expr *result = MD_NilExpr();
MD_Node *set = 0;
MD_Node *node = 0;
// NOTE(rjf): Sub-expression
if(_MD_NodeParse_ConsumeSet(ctx, &set))
{
result = MD_ParseAsExpr(set->first_child, set->last_child);
}
// NOTE(rjf): Literal
else if(_MD_NodeParse_ConsumeLiteral(ctx, &node))
{
result = MD_MakeExpr(node, MD_ExprKind_Atom, 0, 0);
}
// NOTE(rjf): Negative
else if(_MD_NodeParse_Consume(ctx, MD_S8Lit("-"), &node))
{
result = MD_MakeExpr(node, MD_ExprKind_Negative, 0, _MD_ParseExpr(ctx));
}
// NOTE(rjf): Bitwise Negate
else if(_MD_NodeParse_Consume(ctx, MD_S8Lit("~"), &node))
{
result = MD_MakeExpr(node, MD_ExprKind_BitNot, 0, _MD_ParseExpr(ctx));
}
// NOTE(rjf): Boolean Negate
else if(_MD_NodeParse_Consume(ctx, MD_S8Lit("!"), &node))
{
result = MD_MakeExpr(node, MD_ExprKind_BoolNot, 0, _MD_ParseExpr(ctx));
}
return result;
}
MD_PRIVATE_FUNCTION_IMPL MD_Expr *
_MD_ParseExpr_(_MD_NodeParseCtx *ctx, int precedence_in)
{
MD_Expr *expr = _MD_ParseUnaryExpr(ctx);
if(MD_ExprIsNil(expr))
{
goto end_parse;
}
MD_ExprKind expr_kind = MD_BinaryExprKindFromNode(ctx->at);
if(expr_kind != MD_ExprKind_Nil)
{
for(int precedence = MD_ExprPrecFromExprKind(expr_kind);
precedence >= precedence_in;
precedence -= 1)
{
for(;;)
{
MD_Node *op_node = ctx->at;
expr_kind = MD_BinaryExprKindFromNode(ctx->at);
int operator_precedence = MD_ExprPrecFromExprKind(expr_kind);
if(operator_precedence != precedence)
{
break;
}
if(expr_kind == MD_ExprKind_Nil)
{
break;
}
_MD_NodeParse_Next(ctx);
MD_Expr *right = _MD_ParseExpr_(ctx, precedence+1);
if(MD_ExprIsNil(right))
{
// TODO(rjf): Error: "Expected right-hand-side of binary expression."
goto end_parse;
}
MD_Expr *left = expr;
expr = MD_MakeExpr(op_node, expr_kind, left, right);
expr->sub[0] = left;
expr->sub[1] = right;
}
}
}
end_parse:;
return expr;
}
MD_PRIVATE_FUNCTION_IMPL MD_Expr *
_MD_ParseExpr(_MD_NodeParseCtx *ctx)
{
return _MD_ParseExpr_(ctx, 1);
}
MD_FUNCTION_IMPL MD_Expr *
MD_ParseAsExpr(MD_Node *first, MD_Node *last)
{
_MD_NodeParseCtx ctx_ = { first, last, last->next };
_MD_NodeParseCtx *ctx = &ctx_;
return _MD_ParseExpr(ctx);
}
MD_FUNCTION_IMPL MD_Expr *
MD_ParseAsType(MD_Node *first, MD_Node *last)
{
MD_Expr *expr = MD_NilExpr();
MD_Expr *last_expr = expr;
_MD_NodeParseCtx ctx_ = { first, last, last->next };
_MD_NodeParseCtx *ctx = &ctx_;
#define _MD_PushType(x) if(MD_ExprIsNil(last_expr)) { expr = last_expr = x; } else { last_expr = last_expr->sub[0] = x; }
MD_Node *set = 0;
MD_Node *ptr = 0;
MD_Node *base_type = 0;
for(;;)
{
if(_MD_NodeParse_Consume(ctx, MD_S8Lit("*"), &ptr))
{
MD_Expr *t = MD_MakeExpr(ptr, MD_ExprKind_Pointer, MD_NilExpr(), MD_NilExpr());
_MD_PushType(t);
}
else if(_MD_NodeParse_ConsumeSet(ctx, &set))
{
MD_Expr *t = MD_MakeExpr(set, MD_ExprKind_Array, MD_NilExpr(), MD_NilExpr());
_MD_PushType(t);
}
else if(_MD_NodeParse_ConsumeAtom(ctx, &base_type))
{
MD_Expr *t = MD_MakeExpr(base_type, MD_ExprKind_Atom, MD_NilExpr(), MD_NilExpr());
_MD_PushType(t);
}
else
{
break;
}
}
#undef _MD_PushType
return expr;
}
MD_FUNCTION_IMPL MD_i64
MD_EvaluateExpr_I64(MD_Expr *expr)
{
MD_i64 result = 0;
switch(expr->kind)
{
#define _MD_BinaryOp(name, op) case MD_ExprKind_##name: { result = MD_EvaluateExpr_I64(expr->sub[0]) op MD_EvaluateExpr_I64(expr->sub[1]); }break
_MD_BinaryOp(Add, +);
_MD_BinaryOp(Subtract, -);
_MD_BinaryOp(Multiply, *);
_MD_BinaryOp(Divide, /);
#undef _MD_BinaryOp
case MD_ExprKind_Atom: { result = MD_I64FromString(expr->node->string); }break;
default: break;
}
return result;
}
MD_FUNCTION_IMPL MD_f64
MD_EvaluateExpr_F64(MD_Expr *expr)
{
MD_f64 result = 0;
switch(expr->kind)
{
#define _MD_BinaryOp(name, op) case MD_ExprKind_##name: { result = MD_EvaluateExpr_I64(expr->sub[0]) op MD_EvaluateExpr_F64(expr->sub[1]); }break
_MD_BinaryOp(Add, +);
_MD_BinaryOp(Subtract, -);
_MD_BinaryOp(Multiply, *);
_MD_BinaryOp(Divide, /);
#undef _MD_BinaryOp
case MD_ExprKind_Atom: { result = MD_F64FromString(expr->node->string); }break;
default: break;
}
return result;
}
MD_FUNCTION_IMPL MD_b32
MD_ExprMatch(MD_Expr *a, MD_Expr *b, MD_StringMatchFlags str_flags)
{
MD_b32 result = 0;
if(a->kind == b->kind)
{
result = 1;
if(a->kind == MD_ExprKind_Atom)
{
result = MD_StringMatch(a->node->string, b->node->string, str_flags);
}
}
return result;
}
MD_FUNCTION_IMPL MD_b32
MD_ExprDeepMatch(MD_Expr *a, MD_Expr *b, MD_StringMatchFlags str_flags)
{
MD_b32 result = MD_ExprMatch(a, b, str_flags);
if(result && !MD_ExprIsNil(a) && !MD_ExprIsNil(b))
{
result = (MD_ExprDeepMatch(a->sub[0], b->sub[0], str_flags) &&
MD_ExprDeepMatch(a->sub[1], b->sub[1], str_flags));
}
return result;
}
MD_FUNCTION_IMPL void
MD_OutputTree(FILE *file, MD_Node *node)
{
for(MD_Node *tag = node->first_tag; !MD_NodeIsNil(tag); tag = tag->next)
{
fprintf(file, "@%.*s", MD_StringExpand(tag->string));
if(!MD_NodeIsNil(tag->first_child))
{
fprintf(file, "(");
for(MD_Node *child = tag->first_child; !MD_NodeIsNil(child); child = child->next)
{
MD_OutputTree(file, child);
fprintf(file, ", ");
}
fprintf(file, ")\n");
}
else
{
fprintf(file, " ");
}
}
fprintf(file, "%.*s", MD_StringExpand(node->whole_string));
if(!MD_NodeIsNil(node->first_child))
{
fprintf(file, ":\n{\n");
for(MD_Node *child = node->first_child; !MD_NodeIsNil(child); child = child->next)
{
MD_OutputTree(file, child);
fprintf(file, ",\n");
}
fprintf(file, "}\n");
}
else
{
fprintf(file, " ");
}
}
MD_FUNCTION_IMPL void
MD_OutputExpr(FILE *file, MD_Expr *expr)
{
}
MD_FUNCTION_IMPL void
MD_OutputTree_C_String(FILE *file, MD_Node *node)
{
fprintf(file, "\"");
for(MD_u64 i = 0; i < node->string.size; i += 1)
{
if(node->string.str[i] == '\n')
{
fprintf(file, "\\n\"\n\"");
}
else
{
fprintf(file, "%c", node->string.str[i]);
}
}
fprintf(file, "\"");
}
MD_FUNCTION_IMPL void
MD_OutputTree_C_Struct(FILE *file, MD_Node *node)
{
if(node)
{
fprintf(file, "typedef struct %.*s %.*s;\n",
MD_StringExpand(node->string),
MD_StringExpand(node->string));
fprintf(file, "struct %.*s\n{\n", MD_StringExpand(node->string));
for(MD_Node *child = node->first_child; !MD_NodeIsNil(child); child = child->next)
{
MD_OutputTree_C_Decl(file, child);
fprintf(file, ";\n");
}
fprintf(file, "};\n\n");
}
}
MD_FUNCTION_IMPL void
MD_OutputTree_C_Decl(FILE *file, MD_Node *node)
{
if(node)
{
// TODO(allen): MD_ParseAsType?
MD_Expr *type = MD_ParseAsType(node->first_child, node->last_child);
MD_Output_C_DeclByNameAndType(file, node->string, type);
}
}
MD_FUNCTION_IMPL void
MD_Output_C_DeclByNameAndType(FILE *file, MD_String8 name, MD_Expr *type)
{
MD_OutputType_C_LHS(file, type);
fprintf(file, " %.*s", MD_StringExpand(name));
MD_OutputType_C_RHS(file, type);
}
MD_PRIVATE_FUNCTION_IMPL MD_b32
_MD_OutputType_C_NeedsParens(MD_Expr *type)
{
MD_b32 result = 0;
if (type->kind == MD_ExprKind_Pointer &&
type->sub[0]->kind == MD_ExprKind_Array)
{
result = 1;
}
return(result);
}
MD_FUNCTION_IMPL void
MD_OutputType_C_LHS(FILE *file, MD_Expr *type)
{
switch (type->kind)
{
case MD_ExprKind_Atom:
{
MD_Node *node = type->node;
fprintf(file, "%.*s", MD_StringExpand(node->whole_string));
}break;
case MD_ExprKind_Pointer:
{
MD_OutputType_C_LHS(file, type->sub[0]);
if (_MD_OutputType_C_NeedsParens(type))
{
fprintf(file, "(");
}
fprintf(file, "*");
}break;
case MD_ExprKind_Array:
{
MD_OutputType_C_LHS(file, type->sub[0]);
if (_MD_OutputType_C_NeedsParens(type))
{
fprintf(file, "(");
}
}break;
default:
{
fprintf(file, "{ unexpected MD_ExprKind (%i) in type info for node \"%.*s\" }",
type->kind,
MD_StringExpand(type->node->whole_string));
}break;
}
}
MD_FUNCTION_IMPL void
MD_OutputType_C_RHS(FILE *file, MD_Expr *type)
{
switch (type->kind)
{
case MD_ExprKind_Atom:
{}break;
case MD_ExprKind_Pointer:
{
if (_MD_OutputType_C_NeedsParens(type))
{
fprintf(file, ")");
}
MD_OutputType_C_RHS(file, type->sub[0]);
}break;
case MD_ExprKind_Array:
{
if (_MD_OutputType_C_NeedsParens(type))
{
fprintf(file, ")");
}
fprintf(file, "[");
// TODO(allen): MD_OutputExpr_C(file type->sub[1]);
fprintf(file, "\"C expressions not implemented\"");
fprintf(file, "]");
MD_OutputType_C_RHS(file, type->sub[0]);
}break;
default:
{}break;
}
}
MD_FUNCTION_IMPL MD_CommandLine
MD_CommandLine_Start(int argument_count, char **arguments)
{
MD_CommandLine cmdln = {0};
cmdln.arguments = _MD_PushArray(_MD_GetCtx(), MD_String8, argument_count-1);
for(int i = 1; i < argument_count; i += 1)
{
cmdln.arguments[i-1] = MD_PushStringF("%s", arguments[i]);
}
cmdln.argument_count = argument_count-1;
return cmdln;
}
MD_FUNCTION_IMPL MD_b32
MD_CommandLine_Flag(MD_CommandLine *cmdln, MD_String8 string)
{
MD_b32 result = 0;
for(int i = 0; i < cmdln->argument_count; i += 1)
{
if(MD_StringMatch(string, cmdln->arguments[i], 0))
{
result = 1;
cmdln->arguments[i].str = 0;
cmdln->arguments[i].size = 0;
break;
}
}
return result;
}
MD_FUNCTION_IMPL MD_b32
MD_CommandLine_FlagStrings(MD_CommandLine *cmdln, MD_String8 string, int out_count, MD_String8 *out)
{
MD_b32 result = 0;
for(int i = 0; i < cmdln->argument_count; i += 1)
{
if(MD_StringMatch(string, cmdln->arguments[i], 0))
{
cmdln->arguments[i].str = 0;
cmdln->arguments[i].size = 0;
if(cmdln->argument_count > i + out_count)
{
for(int out_idx = 0; out_idx < out_count; out_idx += 1)
{
out[out_idx] = cmdln->arguments[i+out_idx+1];
cmdln->arguments[i+out_idx+1].str = 0;
cmdln->arguments[i+out_idx+1].size = 0;
}
result = 1;
break;
}
}
}
return result;
}
MD_FUNCTION_IMPL MD_b32
MD_CommandLine_FlagIntegers(MD_CommandLine *cmdln, MD_String8 string, int out_count, MD_i64 *out)
{
MD_b32 result = 0;
for(int i = 0; i < cmdln->argument_count; i += 1)
{
if(MD_StringMatch(string, cmdln->arguments[i], 0))
{
cmdln->arguments[i].str = 0;
cmdln->arguments[i].size = 0;
if(cmdln->argument_count > i + out_count)
{
for(int out_idx = 0; out_idx < out_count; out_idx += 1)
{
out[out_idx] = MD_I64FromString(cmdln->arguments[i+out_idx+1]);
cmdln->arguments[i+out_idx+1].str = 0;
cmdln->arguments[i+out_idx+1].size = 0;
}
result = 1;
break;
}
}
}
return result;
}
MD_FUNCTION_IMPL MD_b32
MD_CommandLine_FlagString(MD_CommandLine *cmdln, MD_String8 string, MD_String8 *out)
{
return MD_CommandLine_FlagStrings(cmdln, string, 1, out);
}
MD_FUNCTION_IMPL MD_b32
MD_CommandLine_FlagInteger(MD_CommandLine *cmdln, MD_String8 string, MD_i64 *out)
{
return MD_CommandLine_FlagIntegers(cmdln, string, 1, out);
}
MD_FUNCTION_IMPL MD_b32
MD_CommandLine_Increment(MD_CommandLine *cmdln, MD_String8 **string_ptr)
{
MD_b32 result = 0;
MD_String8 *string = *string_ptr;
if(string == 0)
{
for(int i = 0; i < cmdln->argument_count; i += 1)
{
if(cmdln->arguments[i].str)
{
string = &cmdln->arguments[i];
break;
}
}
}
else
{
int idx = (int)(string - cmdln->arguments);
string = 0;
for(int i = idx+1; i < cmdln->argument_count; i += 1)
{
if(cmdln->arguments[i].str)
{
string = &cmdln->arguments[i];
break;
}
}
}
*string_ptr = string;
result = !!string;
return result;
}
MD_FUNCTION_IMPL MD_String8
MD_LoadEntireFile(MD_String8 filename)
{
MD_String8 file_contents = {0};
FILE *file = fopen((char*)MD_PushStringCopy(filename).str, "r");
if(file)
{
fseek(file, 0, SEEK_END);
MD_u64 file_size = ftell(file);
fseek(file, 0, SEEK_SET);
file_contents.str = _MD_PushArray(_MD_GetCtx(), MD_u8, file_size+1);
if(file_contents.str)
{
file_contents.size = file_size;
fread(file_contents.str, 1, file_size, file);
}
fclose(file);
}
return file_contents;
}
MD_FUNCTION_IMPL MD_b32
MD_FileIterIncrement(MD_FileIter *it, MD_String8 path, MD_FileInfo *out_info)
{
#if !defined(MD_IMPL_FileIterIncrement)
return(0);
#else
return(MD_IMPL_FileIterIncrement(it, path, out_info));
#endif
}