mirror of
https://github.com/Ed94/metadesk.git
synced 2026-06-13 07:52:22 -07:00
1137 lines
34 KiB
C
1137 lines
34 KiB
C
// Copyright (c) 2024 Epic Games Tools
|
|
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
|
|
|
////////////////////////////////
|
|
//~ rjf: String Expression Operator Tables
|
|
|
|
read_only global String8 mg_str_expr_op_symbol_string_table[MG_StrExprOp_COUNT] =
|
|
{
|
|
str8_lit_comp(""),
|
|
str8_lit_comp("."), // MG_StrExprOp_Dot
|
|
str8_lit_comp("->"), // MG_StrExprOp_ExpandIfTrue
|
|
str8_lit_comp(".."), // MG_StrExprOp_Concat
|
|
str8_lit_comp("=>"), // MG_StrExprOp_BumpToColumn
|
|
str8_lit_comp("+"), // MG_StrExprOp_Add
|
|
str8_lit_comp("-"), // MG_StrExprOp_Subtract
|
|
str8_lit_comp("*"), // MG_StrExprOp_Multiply
|
|
str8_lit_comp("/"), // MG_StrExprOp_Divide
|
|
str8_lit_comp("%"), // MG_StrExprOp_Modulo
|
|
str8_lit_comp("<<"), // MG_StrExprOp_LeftShift
|
|
str8_lit_comp(">>"), // MG_StrExprOp_RightShift
|
|
str8_lit_comp("&"), // MG_StrExprOp_BitwiseAnd
|
|
str8_lit_comp("|"), // MG_StrExprOp_BitwiseOr
|
|
str8_lit_comp("^"), // MG_StrExprOp_BitwiseXor
|
|
str8_lit_comp("~"), // MG_StrExprOp_BitwiseNegate
|
|
str8_lit_comp("&&"), // MG_StrExprOp_BooleanAnd
|
|
str8_lit_comp("||"), // MG_StrExprOp_BooleanOr
|
|
str8_lit_comp("!"), // MG_StrExprOp_BooleanNot
|
|
str8_lit_comp("=="), // MG_StrExprOp_Equals
|
|
str8_lit_comp("!="), // MG_StrExprOp_DoesNotEqual
|
|
};
|
|
|
|
read_only global S8 mg_str_expr_op_precedence_table[MG_StrExprOp_COUNT] =
|
|
{
|
|
0,
|
|
20, // MG_StrExprOp_Dot
|
|
1, // MG_StrExprOp_ExpandIfTrue
|
|
2, // MG_StrExprOp_Concat
|
|
12, // MG_StrExprOp_BumpToColumn
|
|
5, // MG_StrExprOp_Add
|
|
5, // MG_StrExprOp_Subtract
|
|
6, // MG_StrExprOp_Multiply
|
|
6, // MG_StrExprOp_Divide
|
|
6, // MG_StrExprOp_Modulo
|
|
7, // MG_StrExprOp_LeftShift
|
|
7, // MG_StrExprOp_RightShift
|
|
8, // MG_StrExprOp_BitwiseAnd
|
|
10, // MG_StrExprOp_BitwiseOr
|
|
9, // MG_StrExprOp_BitwiseXor
|
|
11, // MG_StrExprOp_BitwiseNegate
|
|
3, // MG_StrExprOp_BooleanAnd
|
|
3, // MG_StrExprOp_BooleanOr
|
|
11, // MG_StrExprOp_BooleanNot
|
|
4, // MG_StrExprOp_Equals
|
|
4, // MG_StrExprOp_DoesNotEqual
|
|
};
|
|
|
|
read_only global MG_StrExprOpKind mg_str_expr_op_kind_table[MG_StrExprOp_COUNT] =
|
|
{
|
|
MG_StrExprOpKind_Null,
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_Dot
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_ExpandIfTrue
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_Concat
|
|
MG_StrExprOpKind_Prefix, // MG_StrExprOp_BumpToColumn
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_Add
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_Subtract
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_Multiply
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_Divide
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_Modulo
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_LeftShift
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_RightShift
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_BitwiseAnd
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_BitwiseOr
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_BitwiseXor
|
|
MG_StrExprOpKind_Prefix, // MG_StrExprOp_BitwiseNegate
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_BooleanAnd
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_BooleanOr
|
|
MG_StrExprOpKind_Prefix, // MG_StrExprOp_BooleanNot
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_Equals
|
|
MG_StrExprOpKind_Binary, // MG_StrExprOp_DoesNotEqual
|
|
};
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Basic Helpers
|
|
|
|
internal U64
|
|
mg_hash_from_string(String8 string)
|
|
{
|
|
U64 result = 5381;
|
|
for(U64 i = 0; i < string.size; i += 1)
|
|
{
|
|
result = ((result << 5) + result) + string.str[i];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal TxtPt
|
|
mg_txt_pt_from_string_off(String8 string, U64 off)
|
|
{
|
|
TxtPt pt = txt_pt(1, 1);
|
|
for(U64 idx = 0; idx < string.size && idx < off; idx += 1)
|
|
{
|
|
if(string.str[idx] == '\n')
|
|
{
|
|
pt.line += 1;
|
|
pt.column = 1;
|
|
}
|
|
else
|
|
{
|
|
pt.column += 1;
|
|
}
|
|
}
|
|
return pt;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Message Lists
|
|
|
|
internal void
|
|
mg_msg_list_push(Arena *arena, MG_MsgList *msgs, MG_Msg *msg)
|
|
{
|
|
MG_MsgNode *n = push_array(arena, MG_MsgNode, 1);
|
|
MemoryCopyStruct(&n->v, msg);
|
|
SLLQueuePush(msgs->first, msgs->last, n);
|
|
msgs->count += 1;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: String Escaping
|
|
|
|
internal String8
|
|
mg_escaped_from_str8(Arena *arena, String8 string)
|
|
{
|
|
// NOTE(rjf): This doesn't handle hex/octal/unicode escape sequences right
|
|
// now, just the simple stuff.
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
String8List strs = {0};
|
|
U64 start = 0;
|
|
for(U64 idx = 0; idx <= string.size; idx += 1)
|
|
{
|
|
if(idx == string.size || string.str[idx] == '\\' || string.str[idx] == '\r')
|
|
{
|
|
String8 str = str8_substr(string, r1u64(start, idx));
|
|
if(str.size != 0)
|
|
{
|
|
str8_list_push(scratch.arena, &strs, str);
|
|
}
|
|
start = idx+1;
|
|
}
|
|
if(idx < string.size && string.str[idx] == '\\')
|
|
{
|
|
U8 next_char = string.str[idx+1];
|
|
U8 replace_byte = 0;
|
|
switch(next_char)
|
|
{
|
|
default:{}break;
|
|
case 'a': replace_byte = 0x07; break;
|
|
case 'b': replace_byte = 0x08; break;
|
|
case 'e': replace_byte = 0x1b; break;
|
|
case 'f': replace_byte = 0x0c; break;
|
|
case 'n': replace_byte = 0x0a; break;
|
|
case 'r': replace_byte = 0x0d; break;
|
|
case 't': replace_byte = 0x09; break;
|
|
case 'v': replace_byte = 0x0b; break;
|
|
case '\\':replace_byte = '\\'; break;
|
|
case '\'':replace_byte = '\''; break;
|
|
case '"': replace_byte = '"'; break;
|
|
case '?': replace_byte = '?'; break;
|
|
}
|
|
String8 replace_string = push_str8_copy(scratch.arena, str8(&replace_byte, 1));
|
|
str8_list_push(scratch.arena, &strs, replace_string);
|
|
if(replace_byte == '\\' || replace_byte == '"' || replace_byte == '\'')
|
|
{
|
|
idx += 1;
|
|
start += 1;
|
|
}
|
|
}
|
|
}
|
|
String8 result = str8_list_join(arena, &strs, 0);
|
|
scratch_end(scratch);
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: String Wrapping
|
|
|
|
internal String8List
|
|
mg_wrapped_lines_from_string(Arena *arena, String8 string, U64 first_line_max_width, U64 max_width, U64 wrap_indent)
|
|
{
|
|
String8List list = {0};
|
|
Rng1U64 line_range = r1u64(0, 0);
|
|
U64 wrapped_indent_level = 0;
|
|
static char *spaces = " ";
|
|
for (U64 idx = 0; idx <= string.size; idx += 1){
|
|
U8 chr = idx < string.size ? string.str[idx] : 0;
|
|
if (chr == '\n'){
|
|
Rng1U64 candidate_line_range = line_range;
|
|
candidate_line_range.max = idx;
|
|
// NOTE(nick): when wrapping is interrupted with \n we emit a string without including \n
|
|
// because later tool_fprint_list inserts separator after each node
|
|
// except for last node, so don't strip last \n.
|
|
if (idx + 1 == string.size){
|
|
candidate_line_range.max += 1;
|
|
}
|
|
String8 substr = str8_substr(string, candidate_line_range);
|
|
str8_list_push(arena, &list, substr);
|
|
line_range = r1u64(idx+1,idx+1);
|
|
}
|
|
else
|
|
if (char_is_space(chr) || chr == 0){
|
|
Rng1U64 candidate_line_range = line_range;
|
|
candidate_line_range.max = idx;
|
|
String8 substr = str8_substr(string, candidate_line_range);
|
|
U64 width_this_line = max_width-wrapped_indent_level;
|
|
if (list.node_count == 0){
|
|
width_this_line = first_line_max_width;
|
|
}
|
|
if (substr.size > width_this_line){
|
|
String8 line = str8_substr(string, line_range);
|
|
if (wrapped_indent_level > 0){
|
|
line = push_str8f(arena, "%.*s%S", wrapped_indent_level, spaces, line);
|
|
}
|
|
str8_list_push(arena, &list, line);
|
|
line_range = r1u64(line_range.max+1, candidate_line_range.max);
|
|
wrapped_indent_level = ClampTop(64, wrap_indent);
|
|
}
|
|
else{
|
|
line_range = candidate_line_range;
|
|
}
|
|
}
|
|
}
|
|
if (line_range.min < string.size && line_range.max > line_range.min){
|
|
String8 line = str8_substr(string, line_range);
|
|
if (wrapped_indent_level > 0){
|
|
line = push_str8f(arena, "%.*s%S", wrapped_indent_level, spaces, line);
|
|
}
|
|
str8_list_push(arena, &list, line);
|
|
}
|
|
return list;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: C-String-Izing
|
|
|
|
internal String8
|
|
mg_c_string_literal_from_multiline_string(String8 string)
|
|
{
|
|
String8List strings = {0};
|
|
{
|
|
str8_list_push(mg_arena, &strings, str8_lit("\"\"\n"));
|
|
U64 active_line_start_off = 0;
|
|
for(U64 off = 0; off <= string.size; off += 1)
|
|
{
|
|
B32 is_newline = (off < string.size && (string.str[off] == '\n' || string.str[off] == '\r'));
|
|
B32 is_ender = (off >= string.size || is_newline);
|
|
if(is_ender)
|
|
{
|
|
String8 line = str8_substr(string, r1u64(active_line_start_off, off));
|
|
str8_list_push(mg_arena, &strings, str8_lit("\""));
|
|
str8_list_push(mg_arena, &strings, line);
|
|
if(is_newline)
|
|
{
|
|
str8_list_push(mg_arena, &strings, str8_lit("\\n\"\n"));
|
|
}
|
|
else
|
|
{
|
|
str8_list_push(mg_arena, &strings, str8_lit("\"\n"));
|
|
}
|
|
active_line_start_off = off+1;
|
|
}
|
|
if(is_newline && string.str[off] == '\r')
|
|
{
|
|
active_line_start_off += 1;
|
|
off += 1;
|
|
}
|
|
}
|
|
}
|
|
String8 result = str8_list_join(mg_arena, &strings, 0);
|
|
return result;
|
|
}
|
|
|
|
internal String8
|
|
mg_c_array_literal_contents_from_data(String8 data)
|
|
{
|
|
Temp scratch = scratch_begin(0, 0);
|
|
String8List strings = {0};
|
|
{
|
|
for(U64 off = 0; off < data.size;)
|
|
{
|
|
U64 chunk_size = Min(data.size-off, 64);
|
|
U8 *chunk_bytes = data.str+off;
|
|
String8 chunk_text_string = {0};
|
|
chunk_text_string.size = chunk_size*5;
|
|
chunk_text_string.str = push_array(mg_arena, U8, chunk_text_string.size);
|
|
for(U64 byte_idx = 0; byte_idx < chunk_size; byte_idx += 1)
|
|
{
|
|
String8 byte_str = push_str8f(scratch.arena, "0x%02x,", chunk_bytes[byte_idx]);
|
|
MemoryCopy(chunk_text_string.str+byte_idx*5, byte_str.str, byte_str.size);
|
|
}
|
|
off += chunk_size;
|
|
str8_list_push(mg_arena, &strings, chunk_text_string);
|
|
str8_list_push(mg_arena, &strings, str8_lit("\n"));
|
|
}
|
|
}
|
|
String8 result = str8_list_join(mg_arena, &strings, 0);
|
|
scratch_end(scratch);
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Map Functions
|
|
|
|
internal MG_Map
|
|
mg_push_map(Arena *arena, U64 slot_count)
|
|
{
|
|
MG_Map map = {0};
|
|
map.slots_count = slot_count;
|
|
map.slots = push_array(arena, MG_MapSlot, map.slots_count);
|
|
return map;
|
|
}
|
|
|
|
internal void *
|
|
mg_map_ptr_from_string(MG_Map *map, String8 string)
|
|
{
|
|
void *result = 0;
|
|
{
|
|
U64 hash = mg_hash_from_string(string);
|
|
U64 slot_idx = hash%map->slots_count;
|
|
MG_MapSlot *slot = &map->slots[slot_idx];
|
|
for(MG_MapNode *n = slot->first; n != 0; n = n->next)
|
|
{
|
|
if(str8_match(n->key, string, 0))
|
|
{
|
|
result = n->val;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal void
|
|
mg_map_insert_ptr(Arena *arena, MG_Map *map, String8 string, void *val)
|
|
{
|
|
U64 hash = mg_hash_from_string(string);
|
|
U64 slot_idx = hash%map->slots_count;
|
|
MG_MapSlot *slot = &map->slots[slot_idx];
|
|
MG_MapNode *n = push_array(arena, MG_MapNode, 1);
|
|
n->key = push_str8_copy(arena, string);
|
|
n->val = val;
|
|
SLLQueuePush(slot->first, slot->last, n);
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: String Expression Parsing
|
|
|
|
internal MG_StrExpr *
|
|
mg_push_str_expr(Arena *arena, MG_StrExprOp op, MD_Node *node)
|
|
{
|
|
MG_StrExpr *expr = push_array(arena, MG_StrExpr, 1);
|
|
MemoryCopyStruct(expr, &mg_str_expr_nil);
|
|
expr->op = op;
|
|
expr->node = node;
|
|
return expr;
|
|
}
|
|
|
|
internal MG_StrExprParseResult
|
|
mg_str_expr_parse_from_first_opl__min_prec(Arena *arena, MD_Node *first, MD_Node *opl, S8 min_prec)
|
|
{
|
|
MG_StrExprParseResult parse = {&mg_str_expr_nil};
|
|
{
|
|
MD_Node *it = first;
|
|
|
|
//- rjf: consume prefix operators
|
|
MG_StrExpr *leafmost_op = &mg_str_expr_nil;
|
|
for(;it != opl && !md_node_is_nil(it);)
|
|
{
|
|
MG_StrExprOp found_op = MG_StrExprOp_Null;
|
|
for(MG_StrExprOp op = (MG_StrExprOp)(MG_StrExprOp_Null+1);
|
|
op < MG_StrExprOp_COUNT;
|
|
op = (MG_StrExprOp)(op+1))
|
|
{
|
|
if(mg_str_expr_op_kind_table[op] == MG_StrExprOpKind_Prefix &&
|
|
str8_match(it->string, mg_str_expr_op_symbol_string_table[op], 0) &&
|
|
mg_str_expr_op_precedence_table[op] >= min_prec)
|
|
{
|
|
found_op = op;
|
|
break;
|
|
}
|
|
}
|
|
if(found_op != MG_StrExprOp_Null)
|
|
{
|
|
MG_StrExpr *op_expr = mg_push_str_expr(arena, found_op, it);
|
|
if(leafmost_op == &mg_str_expr_nil)
|
|
{
|
|
leafmost_op = op_expr;
|
|
}
|
|
op_expr->left = parse.root;
|
|
parse.root = op_expr;
|
|
it = it->next;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//- rjf: parse atom
|
|
{
|
|
MG_StrExpr *atom = &mg_str_expr_nil;
|
|
if(it->flags & (MD_NodeFlag_Identifier|MD_NodeFlag_Numeric|MD_NodeFlag_StringLiteral) &&
|
|
md_node_is_nil(it->first))
|
|
{
|
|
atom = mg_push_str_expr(arena, MG_StrExprOp_Null, it);
|
|
it = it->next;
|
|
}
|
|
else if(!md_node_is_nil(it->first))
|
|
{
|
|
MG_StrExprParseResult subparse = mg_str_expr_parse_from_first_opl__min_prec(arena, it->first, &md_nil_node, 0);
|
|
atom = subparse.root;
|
|
md_msg_list_concat_in_place(&parse.msgs, &subparse.msgs);
|
|
it = it->next;
|
|
}
|
|
if(leafmost_op != &mg_str_expr_nil)
|
|
{
|
|
leafmost_op->left = atom;
|
|
}
|
|
else
|
|
{
|
|
parse.root = atom;
|
|
}
|
|
}
|
|
|
|
//- rjf: parse binary operator extensions at this precedence level
|
|
for(;it != opl && !md_node_is_nil(it);)
|
|
{
|
|
// rjf: find binary op kind of `it`
|
|
MG_StrExprOp found_op = MG_StrExprOp_Null;
|
|
for(MG_StrExprOp op = (MG_StrExprOp)(MG_StrExprOp_Null+1);
|
|
op < MG_StrExprOp_COUNT;
|
|
op = (MG_StrExprOp)(op+1))
|
|
{
|
|
if(mg_str_expr_op_kind_table[op] == MG_StrExprOpKind_Binary &&
|
|
str8_match(it->string, mg_str_expr_op_symbol_string_table[op], 0) &&
|
|
mg_str_expr_op_precedence_table[op] >= min_prec)
|
|
{
|
|
found_op = op;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// rjf: good found_op -> build binary expr
|
|
if(found_op != MG_StrExprOp_Null)
|
|
{
|
|
MG_StrExpr *op_expr = mg_push_str_expr(arena, found_op, it);
|
|
if(leafmost_op == &mg_str_expr_nil)
|
|
{
|
|
leafmost_op = op_expr;
|
|
}
|
|
op_expr->left = parse.root;
|
|
parse.root = op_expr;
|
|
it = it->next;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
// rjf: parse right hand side of binary operator
|
|
MG_StrExprParseResult subparse = mg_str_expr_parse_from_first_opl__min_prec(arena, it, opl, mg_str_expr_op_precedence_table[found_op]+1);
|
|
parse.root->right = subparse.root;
|
|
md_msg_list_concat_in_place(&parse.msgs, &subparse.msgs);
|
|
if(subparse.root == &mg_str_expr_nil)
|
|
{
|
|
md_msg_list_pushf(arena, &parse.msgs, it, MD_MsgKind_Error, "Missing right-hand-side of '%S'.", mg_str_expr_op_symbol_string_table[found_op]);
|
|
}
|
|
it = subparse.next_node;
|
|
}
|
|
|
|
// rjf: store next node for more caller-side parsing
|
|
parse.next_node = it;
|
|
}
|
|
return parse;
|
|
}
|
|
|
|
internal MG_StrExprParseResult
|
|
mg_str_expr_parse_from_first_opl(Arena *arena, MD_Node *first, MD_Node *opl)
|
|
{
|
|
MG_StrExprParseResult parse = mg_str_expr_parse_from_first_opl__min_prec(arena, first, opl, 0);
|
|
return parse;
|
|
}
|
|
|
|
internal MG_StrExprParseResult
|
|
mg_str_expr_parse_from_root(Arena *arena, MD_Node *root)
|
|
{
|
|
MG_StrExprParseResult parse = mg_str_expr_parse_from_first_opl__min_prec(arena, root->first, &md_nil_node, 0);
|
|
return parse;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Table Generation Functions
|
|
|
|
internal MG_NodeArray
|
|
mg_node_array_make(Arena *arena, U64 count)
|
|
{
|
|
MG_NodeArray result = {0};
|
|
result.count = count;
|
|
result.v = push_array(arena, MD_Node *, result.count);
|
|
for(U64 idx = 0; idx < result.count; idx += 1)
|
|
{
|
|
result.v[idx] = &md_nil_node;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal MG_NodeArray
|
|
mg_child_array_from_node(Arena *arena, MD_Node *node)
|
|
{
|
|
MG_NodeArray children = mg_node_array_make(arena, md_child_count_from_node(node));
|
|
U64 idx = 0;
|
|
for MD_EachNode(child, node->first)
|
|
{
|
|
children.v[idx] = child;
|
|
idx += 1;
|
|
}
|
|
return children;
|
|
}
|
|
|
|
internal MG_NodeGrid
|
|
mg_node_grid_make_from_node(Arena *arena, MD_Node *root)
|
|
{
|
|
MG_NodeGrid grid = {0};
|
|
|
|
// rjf: determine dimensions
|
|
U64 row_count = md_child_count_from_node(root);
|
|
U64 column_count = 0;
|
|
for MD_EachNode(row, root->first)
|
|
{
|
|
U64 cell_count_this_row = md_child_count_from_node(row);
|
|
column_count = Max(column_count, cell_count_this_row);
|
|
}
|
|
|
|
// rjf: fill grid
|
|
grid.x_stride = 1;
|
|
grid.y_stride = column_count;
|
|
grid.cells = mg_node_array_make(arena, row_count*column_count);
|
|
grid.row_parents = mg_node_array_make(arena, row_count);
|
|
|
|
// rjf: fill nodes
|
|
{
|
|
U64 y = 0;
|
|
for MD_EachNode(row, root->first)
|
|
{
|
|
U64 x = 0;
|
|
grid.row_parents.v[y] = row;
|
|
for MD_EachNode(cell, row->first)
|
|
{
|
|
grid.cells.v[x*grid.x_stride + y*grid.y_stride] = cell;
|
|
x += 1;
|
|
}
|
|
y += 1;
|
|
}
|
|
}
|
|
|
|
return grid;
|
|
}
|
|
|
|
internal MG_NodeArray
|
|
mg_row_from_index(MG_NodeGrid grid, U64 index)
|
|
{
|
|
MG_NodeArray result = {0};
|
|
if(0 <= index && index < grid.cells.count / grid.x_stride)
|
|
{
|
|
result.count = grid.y_stride;
|
|
result.v = &grid.cells.v[index*grid.y_stride];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal MG_NodeArray
|
|
mg_column_from_index(Arena *arena, MG_NodeGrid grid, U64 index)
|
|
{
|
|
MG_NodeArray result = {0};
|
|
if(0 <= index && index < grid.y_stride)
|
|
{
|
|
U64 row_count = grid.cells.count / grid.y_stride;
|
|
result = mg_node_array_make(arena, row_count);
|
|
U64 idx = 0;
|
|
for(U64 row_idx = 0; row_idx < row_count; row_idx += 1, idx += 1)
|
|
{
|
|
result.v[idx] = grid.cells.v[index*grid.x_stride + row_idx*grid.y_stride];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal MD_Node *
|
|
mg_node_from_grid_xy(MG_NodeGrid grid, U64 x, U64 y)
|
|
{
|
|
MD_Node *result = &md_nil_node;
|
|
U64 idx = x*grid.x_stride + y*grid.y_stride;
|
|
if(0 <= idx && idx < grid.cells.count)
|
|
{
|
|
result = grid.cells.v[idx];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal MG_ColumnDescArray
|
|
mg_column_desc_array_make(Arena *arena, U64 count, MG_ColumnDesc *descs)
|
|
{
|
|
MG_ColumnDescArray result = {0};
|
|
result.count = count;
|
|
result.v = push_array(arena, MG_ColumnDesc, result.count);
|
|
MemoryCopy(result.v, descs, sizeof(*result.v)*result.count);
|
|
return result;
|
|
}
|
|
|
|
internal MG_ColumnDescArray
|
|
mg_column_desc_array_from_tag(Arena *arena, MD_Node *tag)
|
|
{
|
|
MG_ColumnDescArray result = {0};
|
|
result.count = md_child_count_from_node(tag);
|
|
result.v = push_array(arena, MG_ColumnDesc, result.count);
|
|
U64 idx = 0;
|
|
for MD_EachNode(hdr, tag->first)
|
|
{
|
|
result.v[idx].name = push_str8_copy(arena, hdr->string);
|
|
result.v[idx].kind = MG_ColumnKind_DirectCell;
|
|
if(md_node_has_tag(hdr, str8_lit("tag_check"), 0))
|
|
{
|
|
result.v[idx].kind = MG_ColumnKind_CheckForTag;
|
|
}
|
|
if(md_node_has_tag(hdr, str8_lit("tag_child"), 0))
|
|
{
|
|
String8 tag_name = md_tag_from_string(hdr, str8_lit("tag_child"), 0)->first->string;
|
|
result.v[idx].kind = MG_ColumnKind_TagChild;
|
|
result.v[idx].tag_name = tag_name;
|
|
}
|
|
idx += 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal U64
|
|
mg_column_index_from_name(MG_ColumnDescArray descs, String8 name)
|
|
{
|
|
U64 result = 0;
|
|
for(U64 idx = 0; idx < descs.count; idx += 1)
|
|
{
|
|
if(str8_match(descs.v[idx].name, name, 0))
|
|
{
|
|
result = idx;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal String8
|
|
mg_string_from_row_desc_idx(MD_Node *row_parent, MG_ColumnDescArray descs, U64 idx)
|
|
{
|
|
String8 result = {0};
|
|
|
|
// rjf: grab relevant column description
|
|
MG_ColumnDesc *desc = 0;
|
|
if(0 <= idx && idx < descs.count)
|
|
{
|
|
desc = descs.v + idx;
|
|
}
|
|
|
|
// rjf: grab node
|
|
if(desc != 0)
|
|
{
|
|
switch(desc->kind)
|
|
{
|
|
default: break;
|
|
|
|
case MG_ColumnKind_DirectCell:
|
|
{
|
|
// rjf: determine grid idx (shifted by synthetic columns)
|
|
U64 cell_idx = idx;
|
|
for(U64 col_idx = 0; col_idx < descs.count && col_idx < idx; col_idx += 1)
|
|
{
|
|
if(descs.v[col_idx].kind != MG_ColumnKind_DirectCell)
|
|
{
|
|
cell_idx -= 1;
|
|
}
|
|
}
|
|
MD_Node *node = md_child_from_index(row_parent, cell_idx);
|
|
result = node->string;
|
|
}break;
|
|
|
|
case MG_ColumnKind_CheckForTag:
|
|
{
|
|
String8 tag_name = desc->name;
|
|
MD_Node *tag = md_tag_from_string(row_parent, tag_name, 0);
|
|
result = md_node_is_nil(tag) ? str8_lit("0") : str8_lit("1");
|
|
}break;
|
|
|
|
case MG_ColumnKind_TagChild:
|
|
{
|
|
String8 tag_name = desc->tag_name;
|
|
MD_Node *tag = md_tag_from_string(row_parent, tag_name, 0);
|
|
result = tag->first->string;
|
|
}break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
internal S64
|
|
mg_eval_table_expand_expr__numeric(MG_StrExpr *expr, MG_TableExpandInfo *info)
|
|
{
|
|
S64 result = 0;
|
|
MG_StrExprOp op = expr->op;
|
|
|
|
switch(op)
|
|
{
|
|
default:
|
|
{
|
|
if(MG_StrExprOp_FirstString <= op && op <= MG_StrExprOp_LastString)
|
|
{
|
|
Temp scratch = scratch_begin(0, 0);
|
|
String8List result_strs = {0};
|
|
mg_eval_table_expand_expr__string(scratch.arena, expr, info, &result_strs);
|
|
String8 result_str = str8_list_join(scratch.arena, &result_strs, 0);
|
|
try_s64_from_str8_c_rules(result_str, &result);
|
|
scratch_end(scratch);
|
|
}
|
|
}break;
|
|
|
|
case MG_StrExprOp_Null:
|
|
{
|
|
try_s64_from_str8_c_rules(expr->node->string, &result);
|
|
}break;
|
|
|
|
//- rjf: numeric arithmetic binary ops
|
|
case MG_StrExprOp_Add:
|
|
case MG_StrExprOp_Subtract:
|
|
case MG_StrExprOp_Multiply:
|
|
case MG_StrExprOp_Divide:
|
|
case MG_StrExprOp_Modulo:
|
|
case MG_StrExprOp_LeftShift:
|
|
case MG_StrExprOp_RightShift:
|
|
case MG_StrExprOp_BitwiseAnd:
|
|
case MG_StrExprOp_BitwiseOr:
|
|
case MG_StrExprOp_BitwiseXor:
|
|
case MG_StrExprOp_BooleanAnd:
|
|
case MG_StrExprOp_BooleanOr:
|
|
{
|
|
S64 left_val = mg_eval_table_expand_expr__numeric(expr->left, info);
|
|
S64 right_val = mg_eval_table_expand_expr__numeric(expr->right, info);
|
|
switch(op)
|
|
{
|
|
default:break;
|
|
case MG_StrExprOp_Add: result = left_val+right_val; break;
|
|
case MG_StrExprOp_Subtract: result = left_val-right_val; break;
|
|
case MG_StrExprOp_Multiply: result = left_val*right_val; break;
|
|
case MG_StrExprOp_Divide: result = left_val/right_val; break;
|
|
case MG_StrExprOp_Modulo: result = left_val%right_val; break;
|
|
case MG_StrExprOp_LeftShift: result = left_val<<right_val; break;
|
|
case MG_StrExprOp_RightShift: result = left_val>>right_val; break;
|
|
case MG_StrExprOp_BitwiseAnd: result = left_val&right_val; break;
|
|
case MG_StrExprOp_BitwiseOr: result = left_val|right_val; break;
|
|
case MG_StrExprOp_BitwiseXor: result = left_val^right_val; break;
|
|
case MG_StrExprOp_BooleanAnd: result = left_val&&right_val; break;
|
|
case MG_StrExprOp_BooleanOr: result = left_val||right_val; break;
|
|
}
|
|
}break;
|
|
|
|
//- rjf: prefix unary ops
|
|
case MG_StrExprOp_BitwiseNegate:
|
|
case MG_StrExprOp_BooleanNot:
|
|
{
|
|
S64 right_val = mg_eval_table_expand_expr__numeric(expr->left, info);
|
|
switch(op)
|
|
{
|
|
default:break;
|
|
case MG_StrExprOp_BitwiseNegate: result = (S64)(~((U64)right_val)); break;
|
|
case MG_StrExprOp_BooleanNot: result = !right_val;
|
|
}
|
|
}break;
|
|
|
|
//- rjf: comparisons
|
|
case MG_StrExprOp_Equals:
|
|
case MG_StrExprOp_DoesNotEqual:
|
|
{
|
|
Temp scratch = scratch_begin(0, 0);
|
|
String8List left_strs = {0};
|
|
String8List right_strs = {0};
|
|
mg_eval_table_expand_expr__string(scratch.arena, expr->left, info, &left_strs);
|
|
mg_eval_table_expand_expr__string(scratch.arena, expr->right, info, &right_strs);
|
|
String8 left_str = str8_list_join(scratch.arena, &left_strs, 0);
|
|
String8 right_str = str8_list_join(scratch.arena, &right_strs, 0);
|
|
B32 match = str8_match(left_str, right_str, 0);
|
|
result = (op == MG_StrExprOp_Equals ? match : !match);
|
|
scratch_end(scratch);
|
|
}break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
internal void
|
|
mg_eval_table_expand_expr__string(Arena *arena, MG_StrExpr *expr, MG_TableExpandInfo *info, String8List *out)
|
|
{
|
|
MG_StrExprOp op = expr->op;
|
|
|
|
switch(op)
|
|
{
|
|
default:
|
|
{
|
|
if(MG_StrExprOp_FirstNumeric <= op && op <= MG_StrExprOp_LastNumeric)
|
|
{
|
|
S64 numeric_eval = mg_eval_table_expand_expr__numeric(expr, info);
|
|
String8 numeric_eval_stringized = {0};
|
|
if(md_node_has_tag(md_root_from_node(expr->node), str8_lit("hex"), 0))
|
|
{
|
|
numeric_eval_stringized = push_str8f(arena, "0x%I64x", numeric_eval);
|
|
}
|
|
else
|
|
{
|
|
numeric_eval_stringized = push_str8f(arena, "%I64d", numeric_eval);
|
|
}
|
|
str8_list_push(arena, out, numeric_eval_stringized);
|
|
}
|
|
}break;
|
|
|
|
case MG_StrExprOp_Null:
|
|
{
|
|
str8_list_push(arena, out, expr->node->string);
|
|
}break;
|
|
|
|
case MG_StrExprOp_Dot:
|
|
{
|
|
// rjf: grab left/right
|
|
MG_StrExpr *left_expr = expr->left;
|
|
MD_Node *left_node = left_expr->node;
|
|
MG_StrExpr *right_expr = expr->right;
|
|
MD_Node *right_node = right_expr->node;
|
|
|
|
// rjf: grab table name (LHS of .) and column lookup string (RHS of .)
|
|
String8 expand_label = left_node->string;
|
|
String8 column_lookup = right_node->string;
|
|
|
|
// rjf: find which task corresponds to this table
|
|
U64 row_idx = 0;
|
|
MG_NodeGrid *grid = 0;
|
|
MG_ColumnDescArray column_descs = {0};
|
|
{
|
|
for(MG_TableExpandTask *task = info->first_expand_task; task != 0; task = task->next)
|
|
{
|
|
if(str8_match(expand_label, task->expansion_label, 0))
|
|
{
|
|
row_idx = task->idx;
|
|
grid = task->grid;
|
|
column_descs = task->column_descs;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// rjf: grab row parent
|
|
MD_Node *row_parent = &md_nil_node;
|
|
if(grid && (0 <= row_idx && row_idx < grid->row_parents.count))
|
|
{
|
|
row_parent = grid->row_parents.v[row_idx];
|
|
}
|
|
|
|
// rjf: get string for this table lookup
|
|
String8 lookup_string = {0};
|
|
{
|
|
U64 column_idx = 0;
|
|
|
|
if(str8_match(column_lookup, str8_lit("_it"), 0))
|
|
{
|
|
lookup_string = push_str8f(arena, "%I64u", row_idx);
|
|
}
|
|
else
|
|
{
|
|
// NOTE(rjf): numeric column lookup (column index)
|
|
if(right_node->flags & MD_NodeFlag_Numeric)
|
|
{
|
|
try_u64_from_str8_c_rules(column_lookup, &column_idx);
|
|
}
|
|
|
|
// NOTE(rjf): string column lookup (column name)
|
|
if(right_node->flags & (MD_NodeFlag_Identifier|MD_NodeFlag_StringLiteral))
|
|
{
|
|
column_idx = mg_column_index_from_name(column_descs, column_lookup);
|
|
}
|
|
|
|
lookup_string = mg_string_from_row_desc_idx(row_parent, column_descs, column_idx);
|
|
if(str8_match(lookup_string, str8_lit("--"), 0))
|
|
{
|
|
lookup_string = info->missing_value_fallback;
|
|
}
|
|
}
|
|
}
|
|
|
|
// rjf: push lookup string
|
|
{
|
|
str8_list_push(arena, out, lookup_string);
|
|
}
|
|
}break;
|
|
|
|
case MG_StrExprOp_ExpandIfTrue:
|
|
{
|
|
S64 bool_value = mg_eval_table_expand_expr__numeric(expr->left, info);
|
|
if(bool_value)
|
|
{
|
|
mg_eval_table_expand_expr__string(arena, expr->right, info, out);
|
|
}
|
|
}break;
|
|
|
|
case MG_StrExprOp_Concat:
|
|
{
|
|
mg_eval_table_expand_expr__string(arena, expr->left, info, out);
|
|
mg_eval_table_expand_expr__string(arena, expr->right, info, out);
|
|
}break;
|
|
|
|
case MG_StrExprOp_BumpToColumn:
|
|
{
|
|
S64 column = mg_eval_table_expand_expr__numeric(expr->left, info);
|
|
S64 current_column = out->total_size;
|
|
S64 spaces_to_push = column - current_column;
|
|
if(spaces_to_push > 0)
|
|
{
|
|
String8 str = {0};
|
|
str.size = spaces_to_push;
|
|
str.str = push_array(arena, U8, spaces_to_push);
|
|
for(S64 idx = 0; idx < spaces_to_push; idx += 1)
|
|
{
|
|
str.str[idx] = ' ';
|
|
}
|
|
str8_list_push(arena, out, str);
|
|
}
|
|
}break;
|
|
}
|
|
}
|
|
|
|
internal void
|
|
mg_loop_table_column_expansion(Arena *arena, String8 strexpr, MG_TableExpandInfo *info, MG_TableExpandTask *task, String8List *out)
|
|
{
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
for(U64 it_idx = 0; it_idx < task->count; it_idx += 1)
|
|
{
|
|
task->idx = it_idx;
|
|
|
|
//- rjf: iterate all further dimensions, if there's left in the chain
|
|
if(task->next)
|
|
{
|
|
mg_loop_table_column_expansion(arena, strexpr, info, task->next, out);
|
|
}
|
|
|
|
//- rjf: if this is the last task in the chain, perform expansion
|
|
else
|
|
{
|
|
String8List expansion_strs = {0};
|
|
U64 start = 0;
|
|
for(U64 char_idx = 0; char_idx <= strexpr.size;)
|
|
{
|
|
// rjf: push plain text parts of strexpr
|
|
if(char_idx == strexpr.size || strexpr.str[char_idx] == '$')
|
|
{
|
|
String8 plain_text_substr = str8_substr(strexpr, r1u64(start, char_idx));
|
|
start = char_idx;
|
|
if(plain_text_substr.size != 0)
|
|
{
|
|
str8_list_push(arena, &expansion_strs, plain_text_substr);
|
|
}
|
|
}
|
|
|
|
// rjf: handle expansion expression
|
|
if(strexpr.str[char_idx] == '$')
|
|
{
|
|
String8 string = str8_skip(strexpr, char_idx+1);
|
|
Rng1U64 expr_range = {0};
|
|
S64 paren_nest = 0;
|
|
for(U64 idx = 0; idx < string.size; idx += 1)
|
|
{
|
|
if(string.str[idx] == '(')
|
|
{
|
|
paren_nest += 1;
|
|
if(paren_nest == 1)
|
|
{
|
|
expr_range.min = idx;
|
|
}
|
|
}
|
|
if(string.str[idx] == ')')
|
|
{
|
|
paren_nest -= 1;
|
|
if(paren_nest == 0)
|
|
{
|
|
expr_range.max = idx+1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
String8 expr_string = str8_substr(string, expr_range);
|
|
MD_TokenizeResult expr_tokenize = md_tokenize_from_text(scratch.arena, expr_string);
|
|
MD_ParseResult expr_base_parse = md_parse_from_text_tokens(scratch.arena, str8_lit(""), expr_string, expr_tokenize.tokens);
|
|
MG_StrExprParseResult expr_parse = mg_str_expr_parse_from_root(scratch.arena, expr_base_parse.root->first);
|
|
mg_eval_table_expand_expr__string(arena, expr_parse.root, info, &expansion_strs);
|
|
char_idx = start = char_idx + 1 + expr_range.max;
|
|
}
|
|
else
|
|
{
|
|
char_idx += 1;
|
|
}
|
|
}
|
|
String8 expansion_str = str8_list_join(arena, &expansion_strs, 0);
|
|
if(expansion_str.size != 0)
|
|
{
|
|
str8_list_push(arena, out, expansion_str);
|
|
}
|
|
}
|
|
}
|
|
|
|
scratch_end(scratch);
|
|
}
|
|
|
|
internal String8List
|
|
mg_string_list_from_table_gen(Arena *arena, MG_Map grid_name_map, MG_Map grid_column_desc_map, String8 fallback, MD_Node *gen)
|
|
{
|
|
String8List result = {0};
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
if(md_node_is_nil(gen->first) && gen->string.size != 0)
|
|
{
|
|
str8_list_push(arena, &result, gen->string);
|
|
str8_list_push(arena, &result, str8_lit("\n"));
|
|
}
|
|
else for MD_EachNode(strexpr_node, gen->first)
|
|
{
|
|
// rjf: build task list
|
|
MG_TableExpandTask *first_task = 0;
|
|
MG_TableExpandTask *last_task = 0;
|
|
for MD_EachNode(tag, strexpr_node->first_tag)
|
|
{
|
|
if(str8_match(tag->string, str8_lit("expand"), 0))
|
|
{
|
|
// rjf: grab args for this expansion
|
|
MD_Node *table_name_node = md_child_from_index(tag, 0);
|
|
MD_Node *expand_label_node = md_child_from_index(tag, 1);
|
|
String8 table_name = table_name_node->string;
|
|
String8 expand_label = expand_label_node->string;
|
|
|
|
// rjf: lookup table / column descriptions
|
|
MG_NodeGrid *grid = mg_map_ptr_from_string(&grid_name_map, table_name);
|
|
MG_ColumnDescArray *column_descs = mg_map_ptr_from_string(&grid_column_desc_map, table_name);
|
|
|
|
// rjf: figure out row count
|
|
U64 grid_row_count = 0;
|
|
if(grid != 0)
|
|
{
|
|
grid_row_count = grid->cells.count / grid->y_stride;
|
|
}
|
|
|
|
// rjf: push task for this expansion
|
|
if(grid != 0)
|
|
{
|
|
MG_TableExpandTask *task = push_array(scratch.arena, MG_TableExpandTask, 1);
|
|
task->expansion_label = expand_label;
|
|
task->grid = grid;
|
|
task->column_descs = *column_descs;
|
|
task->count = grid_row_count;
|
|
task->idx = 0;
|
|
SLLQueuePush(first_task, last_task, task);
|
|
}
|
|
}
|
|
}
|
|
|
|
// rjf: do expansion generation, OR just push this string if we have no expansions
|
|
{
|
|
MG_TableExpandInfo info = {first_task, fallback};
|
|
if(first_task != 0)
|
|
{
|
|
mg_loop_table_column_expansion(arena, strexpr_node->string, &info, first_task, &result);
|
|
}
|
|
else
|
|
{
|
|
str8_list_push(arena, &result, strexpr_node->string);
|
|
}
|
|
}
|
|
}
|
|
scratch_end(scratch);
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Layer Lookup Functions
|
|
|
|
internal String8
|
|
mg_layer_key_from_path(String8 path)
|
|
{
|
|
Temp scratch = scratch_begin(0, 0);
|
|
U64 src_folder_pos = 0;
|
|
for(U64 next_src_folder_pos = 0;
|
|
next_src_folder_pos < path.size;
|
|
next_src_folder_pos = str8_find_needle(path, next_src_folder_pos+1, str8_lit("src"), 0))
|
|
{
|
|
src_folder_pos = next_src_folder_pos;
|
|
}
|
|
String8List path_parts = str8_split_path(scratch.arena, str8_chop_last_slash(str8_skip(path, src_folder_pos+4)));
|
|
StringJoin join = {0};
|
|
join.sep = str8_lit("/");
|
|
String8 key = str8_list_join(mg_arena, &path_parts, &join);
|
|
scratch_end(scratch);
|
|
return key;
|
|
}
|
|
|
|
internal MG_Layer *
|
|
mg_layer_from_key(String8 key)
|
|
{
|
|
U64 hash = mg_hash_from_string(key);
|
|
U64 slot_idx = hash%mg_state->slots_count;
|
|
MG_LayerSlot *slot = &mg_state->slots[slot_idx];
|
|
MG_Layer *layer = 0;
|
|
for(MG_LayerNode *n = slot->first; n != 0; n = n->next)
|
|
{
|
|
if(str8_match(n->v.key, key, 0))
|
|
{
|
|
layer = &n->v;
|
|
break;
|
|
}
|
|
}
|
|
if(layer == 0)
|
|
{
|
|
MG_LayerNode *n = push_array(mg_arena, MG_LayerNode, 1);
|
|
SLLQueuePush(slot->first, slot->last, n);
|
|
n->v.key = push_str8_copy(mg_arena, key);
|
|
layer = &n->v;
|
|
}
|
|
return layer;
|
|
}
|