vn/code/third_party/codegen/codegen_table.c

582 lines
23 KiB
C
Raw Permalink Normal View History

2023-07-19 15:09:41 +00:00
static MD_Map cg_tbl_top_level_node_grid_map = {0};
static MD_Map cg_tbl_top_level_table_header_map = {0};
static MD_Map cg_tbl_layer_map_gen = {0};
static MD_Map cg_tbl_layer_map_gen_enum = {0};
static MD_Map cg_tbl_layer_map_gen_data = {0};
static MD_String8 cg_tbl_tag__table = MD_S8LitComp("table");
static MD_String8 cg_tbl_tag__table_gen = MD_S8LitComp("table_gen");
static MD_String8 cg_tbl_tag__table_gen_enum = MD_S8LitComp("table_gen_enum");
static MD_String8 cg_tbl_tag__table_gen_data = MD_S8LitComp("table_gen_data");
static CG_NodeArray
CG_NodeArrayMake(MD_u64 count)
{
CG_NodeArray result = {0};
result.count = count;
result.v = MD_PushArrayZero(cg_arena, MD_Node *, result.count);
for(MD_u64 idx = 0; idx < result.count; idx += 1)
{
result.v[idx] = MD_NilNode();
}
return result;
}
static CG_NodeGrid
CG_GridFromNode(MD_Node *node)
{
CG_NodeGrid grid = {0};
//- rjf: determine dimensions
MD_u64 row_count = 0;
MD_u64 column_count = 0;
{
for(MD_EachNode(row, node->first_child))
{
row_count += 1;
MD_u64 cell_count_this_row = MD_ChildCountFromNode(row);
column_count = MD_Max(cell_count_this_row, column_count);
}
}
//- rjf: allocate cells / row parents
{
grid.cells = CG_NodeArrayMake(row_count * column_count);
grid.row_parents = CG_NodeArrayMake(row_count);
}
//- rjf: fill cells
{
MD_u64 row_idx = 0;
for(MD_EachNode(row, node->first_child))
{
MD_u64 col_idx = 0;
grid.row_parents.v[row_idx] = row;
for(MD_EachNode(cell, row->first_child))
{
grid.cells.v[row_idx * column_count + col_idx] = cell;
col_idx += 1;
}
row_idx += 1;
}
}
return grid;
}
static CG_TableHeader
CG_TableHeaderFromTag(MD_Node *tag)
{
CG_TableHeader result = {0};
result.column_count = MD_ChildCountFromNode(tag);
result.column_descs = MD_PushArrayZero(cg_arena, CG_ColumnDesc, result.column_count);
MD_u64 idx = 0;
for(MD_EachNode(column_node, tag->first_child))
{
result.column_descs[idx].kind = CG_ColumnKind_Default;
result.column_descs[idx].name = column_node->string;
MD_Node *check_for_tag = MD_TagFromString(column_node, MD_S8Lit("check_for_tag"), 0);
if(!MD_NodeIsNil(check_for_tag))
{
result.column_descs[idx].kind = CG_ColumnKind_CheckForTag;
result.column_descs[idx].tag_string = check_for_tag->first_child->string;
}
MD_Node *default_value = MD_TagFromString(column_node, MD_S8Lit("default"), 0);
if(!MD_NodeIsNil(default_value))
{
result.column_descs[idx].default_value = default_value->first_child->string;
}
idx += 1;
}
return result;
}
static MD_u64
CG_RowChildIndexFromColumnName(CG_TableHeader *header, MD_String8 column_name)
{
MD_u64 result = 0;
for(MD_u64 idx = 0; idx < header->column_count; idx += 1)
{
if(MD_S8Match(header->column_descs[idx].name, column_name, 0))
{
break;
}
if(header->column_descs[idx].kind == CG_ColumnKind_Default)
{
result += 1;
}
}
return result;
}
static MD_i64
CG_TableExprEvaluate_Numeric(CG_ExpandInfo *info, MD_Expr *expr)
{
MD_i64 result = 0;
CG_TableOp op = expr->op ? expr->op->op_id : CG_TableOp_Null;
switch(op)
{
case CG_TableOp_Equal:
case CG_TableOp_IsNotEqual:
{
MD_ArenaTemp scratch = MD_GetScratch(0, 0);
MD_String8List left_strs = {0};
MD_String8List right_strs = {0};
CG_TableExprEvaluate_String(info, expr->left, &left_strs);
CG_TableExprEvaluate_String(info, expr->right, &right_strs);
MD_String8 left_str = MD_S8ListJoin(scratch.arena, left_strs, 0);
MD_String8 right_str = MD_S8ListJoin(scratch.arena, right_strs, 0);
result = MD_S8Match(left_str, right_str, 0);
if(op == CG_TableOp_IsNotEqual)
{
result = !result;
}
MD_ReleaseScratch(scratch);
}break;
case CG_TableOp_BooleanAnd:
case CG_TableOp_BooleanOr:
{
MD_i64 left = CG_TableExprEvaluate_Numeric(info, expr->left);
MD_i64 right = CG_TableExprEvaluate_Numeric(info, expr->right);
switch(op)
{
case CG_TableOp_BooleanAnd: result = left && right; break;
case CG_TableOp_BooleanOr: result = left || right; break;
}
}break;
}
return result;
}
static void
CG_TableExprEvaluate_String(CG_ExpandInfo *info, MD_Expr *expr, MD_String8List *out)
{
CG_TableOp op = expr->op ? expr->op->op_id : CG_TableOp_Null;
switch(op)
{
default:
case CG_TableOp_Null:
{
MD_S8ListPush(cg_arena, out, expr->md_node->string);
}break;
case CG_TableOp_Dot:
{
MD_Expr *label_expr = expr->left;
MD_Expr *column_query_expr = expr->right;
MD_Node *label_node = label_expr->md_node;
MD_Node *column_query_node = column_query_expr->md_node;
MD_String8 label = label_node->string;
MD_String8 column_query = column_query_node->string;
MD_b32 column_query_is_by_expand_idx = MD_S8Match(column_query_node->string, MD_S8Lit("_it"), 0);
MD_b32 column_query_is_by_name = !column_query_is_by_expand_idx && column_query_node->flags & MD_NodeFlag_Identifier;
MD_b32 column_query_is_by_index = column_query_node->flags & MD_NodeFlag_Numeric;
// rjf: find which expansion this label refers to, grab its iterator
CG_ExpandIter *iter = 0;
for(CG_ExpandIter *it = info->first_expand_iter; it != 0; it = it->next)
{
if(MD_S8Match(it->label, label, 0))
{
iter = it;
break;
}
}
// rjf: error on invalid label
if(iter == 0)
{
MD_PrintMessageFmt(stderr, MD_CodeLocFromNode(label_node), MD_MessageKind_Error, "Expansion label \"%S\" was not found as referring to a valid @expand tag.", label);
}
// rjf: generate strings from iterator's table
if(iter != 0)
{
CG_NodeGrid *grid = iter->grid;
CG_TableHeader *header = iter->header;
MD_Node *row = grid->row_parents.v[iter->idx];
// rjf: grab the cell string given the row & column_query
MD_String8 cell_string = {0};
{
// NOTE(rjf): expansion index counter
if(column_query_is_by_expand_idx)
{
MD_i64 index = iter->idx;
cell_string = MD_S8Fmt(cg_arena, "%" PRIu64 "", index);
}
// NOTE(rjf): by-name index (look into table header)
else if(column_query_is_by_name && header != 0)
{
MD_u64 column_idx = 0;
CG_ColumnDesc *column = 0;
for(MD_u64 col_idx = 0; col_idx < header->column_count; col_idx += 1)
{
if(MD_S8Match(header->column_descs[col_idx].name, column_query, 0))
{
column = &header->column_descs[col_idx];
column_idx = col_idx;
break;
}
}
MD_u64 row_child_idx = CG_RowChildIndexFromColumnName(header, column_query);
// rjf: error on invalid column
if(column == 0)
{
MD_PrintMessageFmt(stderr, MD_CodeLocFromNode(column_query_node), MD_MessageKind_Error, "Column query \"%S\" did not map to a valid column for expansion label \"%S\".", column_query, label);
}
if(column != 0)
{
switch(column->kind)
{
default:
case CG_ColumnKind_Default:
{
MD_Node *cell_node = MD_ChildFromIndex(row, row_child_idx);
cell_string = cell_node->string;
if(MD_S8Match(cell_node->raw_string, MD_S8Lit("."), 0))
{
cell_string = column->default_value;
}
}break;
case CG_ColumnKind_CheckForTag:
{
MD_b32 has_tag = MD_NodeHasTag(row, column->tag_string, 0);
cell_string = has_tag ? MD_S8Lit("1") : MD_S8Lit("0");
}break;
}
}
}
// NOTE(rjf): by-index (grab nth child of row)
else if(column_query_is_by_index)
{
MD_i64 index = MD_CStyleIntFromString(column_query);
cell_string = MD_ChildFromIndex(row, index)->string;
}
}
MD_S8ListPush(cg_arena, out, cell_string);
}
}break;
case CG_TableOp_Bump:
{
MD_u64 dst = MD_CStyleIntFromString(expr->unary_operand->md_node->string);
MD_u64 src = out->total_size;
MD_u64 spaces_to_print = dst - src;
if(dst > src)
{
for(MD_u64 space_idx = 0; space_idx < spaces_to_print; space_idx += 1)
{
MD_S8ListPush(cg_arena, out, MD_S8Lit(" "));
}
}
}break;
case CG_TableOp_CheckIfTrue:
{
MD_i64 check_val = CG_TableExprEvaluate_Numeric(info, expr->left);
if(check_val)
{
CG_TableExprEvaluate_String(info, expr->right, out);
}
}break;
case CG_TableOp_Concat:
{
CG_TableExprEvaluate_String(info, expr->left, out);
CG_TableExprEvaluate_String(info, expr->right, out);
}break;
}
}
static void
CG_LoopExpansionDimension(CG_ExpandIter *it, CG_ExpandInfo *info, MD_String8List *out)
{
if(it->next)
{
for(MD_u64 idx = 0; idx < it->count; idx += 1)
{
it->idx = idx;
CG_LoopExpansionDimension(it->next, info, out);
}
}
else
{
for(MD_u64 idx = 0; idx < it->count; idx += 1)
{
it->idx = idx;
MD_String8List expansion_strs = {0};
MD_u64 start_idx = 0;
for(MD_u64 char_idx = 0; char_idx <= info->strexpr.size; char_idx += 1)
{
MD_b32 is_expr_marker = info->strexpr.str[char_idx] == '$';
// rjf: push regular string contents
if(char_idx == info->strexpr.size || is_expr_marker)
{
MD_String8 normal_string_chunk = MD_S8Substring(info->strexpr, start_idx, char_idx);
MD_String8 escaped = CG_EscapedFromString(cg_arena, normal_string_chunk);
MD_S8ListPush(cg_arena, &expansion_strs, escaped);
}
// rjf: handle expansion
if(is_expr_marker)
{
MD_String8 expr_string = MD_S8Skip(info->strexpr, char_idx+1);
{
MD_i64 paren_nest = 0;
for(MD_u64 expr_str_char_idx = 0; expr_str_char_idx < expr_string.size; expr_str_char_idx += 1)
{
if(expr_string.str[expr_str_char_idx] == '(')
{
paren_nest += 1;
}
else if(expr_string.str[expr_str_char_idx] == ')')
{
paren_nest -= 1;
if(paren_nest == 0)
{
expr_string.size = expr_str_char_idx+1;
break;
}
}
}
}
MD_ParseResult parse = MD_ParseOneNode(cg_arena, expr_string, 0);
MD_Node *node = parse.node;
MD_ExprParseResult expr_parse = MD_ExprParse(cg_arena, &info->expr_op_table, node->first_child, MD_NilNode());
MD_Expr *expr = expr_parse.expr;
CG_TableExprEvaluate_String(info, expr, &expansion_strs);
MD_String8 parsed_string = MD_S8Substring(info->strexpr, char_idx+1, char_idx+1+parse.string_advance);
parsed_string = MD_S8ChopWhitespace(parsed_string);
start_idx = char_idx+1+parsed_string.size;
}
}
// rjf: push expansion string to output list
MD_String8 expansion_str = MD_S8ListJoin(cg_arena, expansion_strs, 0);
MD_S8ListPush(cg_arena, out, expansion_str);
}
}
}
static MD_String8List
CG_GenStringListFromNode(MD_ExprOprTable expr_op_table, MD_Node *gen)
{
MD_String8List result = {0};
MD_ArenaTemp scratch = MD_GetScratch(0, 0);
for(MD_EachNode(strexpr, gen->first_child))
{
//- rjf: build expansion iterator list
CG_ExpandIter *first_iter = 0;
CG_ExpandIter *last_iter = 0;
{
for(MD_EachNode(tag, strexpr->first_tag))
{
if(MD_S8Match(tag->string, MD_S8Lit("expand"), 0))
{
MD_Node *table_name_node = MD_ChildFromIndex(tag, 0);
MD_Node *label_node = MD_ChildFromIndex(tag, 1);
MD_String8 table_name = table_name_node->string;
MD_String8 label = label_node->string;
// rjf: grab the table associated with table_name
CG_NodeGrid *grid = 0;
{
MD_MapSlot *slot = MD_MapLookup(&cg_tbl_top_level_node_grid_map, MD_MapKeyStr(table_name));
if(slot != 0)
{
grid = slot->val;
}
}
// rjf: grab the table header associated with table_name
CG_TableHeader *header = 0;
{
MD_MapSlot *slot = MD_MapLookup(&cg_tbl_top_level_table_header_map, MD_MapKeyStr(table_name));
if(slot != 0)
{
header = slot->val;
}
}
// rjf: make iterator node if we got a grid
if(grid != 0)
{
CG_ExpandIter *iter = MD_PushArrayZero(scratch.arena, CG_ExpandIter, 1);
MD_QueuePush(first_iter, last_iter, iter);
iter->grid = grid;
iter->header = header;
iter->label = label;
iter->count = grid->row_parents.count;
}
// rjf: print out an error if grid is 0
if(grid == 0)
{
MD_PrintMessageFmt(stderr, MD_CodeLocFromNode(tag), MD_MessageKind_Error, "Table \"%S\" was not found.", table_name);
}
}
}
}
//- rjf: generate string list for this strexpr & push to result
if(first_iter != 0)
{
CG_ExpandInfo info = {0};
{
info.strexpr = strexpr->string;
info.first_expand_iter = first_iter;
info.expr_op_table = expr_op_table;
}
CG_LoopExpansionDimension(first_iter, &info, &result);
}
//- rjf: generate non-expansion strings
else
{
MD_String8 escaped = CG_EscapedFromString(cg_arena, strexpr->string);
MD_S8ListPush(cg_arena, &result, escaped);
}
}
MD_ReleaseScratch(scratch);
return result;
}
static void
CG_TBL_Generate(MD_Node *file_list)
{
//- rjf: initialize all maps
cg_tbl_top_level_node_grid_map = MD_MapMake(cg_arena);
cg_tbl_top_level_table_header_map = MD_MapMake(cg_arena);
cg_tbl_layer_map_gen = MD_MapMake(cg_arena);
cg_tbl_layer_map_gen_enum = MD_MapMake(cg_arena);
cg_tbl_layer_map_gen_data = MD_MapMake(cg_arena);
//- rjf: build table expression operator table
MD_ExprOprTable table_expr_op_table = {0};
{
MD_ExprOprList ops_list = {0};
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 10, MD_S8Lit("."), CG_TableOp_Dot, 0);
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Prefix, 9, MD_S8Lit("=>"), CG_TableOp_Bump, 0);
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 6, MD_S8Lit("??"), CG_TableOp_CheckIfTrue, 0);
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 7, MD_S8Lit(".."), CG_TableOp_Concat, 0);
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 8, MD_S8Lit("=="), CG_TableOp_Equal, 0);
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 8, MD_S8Lit("!="), CG_TableOp_IsNotEqual, 0);
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 5, MD_S8Lit("&&"), CG_TableOp_BooleanAnd, 0);
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 4, MD_S8Lit("||"), CG_TableOp_BooleanOr, 0);
table_expr_op_table = MD_ExprBakeOprTableFromList(cg_arena, &ops_list);
}
//- rjf: gather phase
for(MD_EachNode(file_ref, file_list->first_child))
{
MD_Node *file = MD_ResolveNodeFromReference(file_ref);
MD_String8 layer_name = file->string;
MD_MapKey layer_key = MD_MapKeyStr(layer_name);
for(MD_EachNode(node, file->first_child))
{
MD_Node *table_tag = MD_TagFromString(node, cg_tbl_tag__table, 0);
if(!MD_NodeIsNil(table_tag))
{
CG_NodeGrid *grid = MD_PushArrayZero(cg_arena, CG_NodeGrid, 1);
*grid = CG_GridFromNode(node);
MD_MapOverwrite(cg_arena, &cg_tbl_top_level_node_grid_map, MD_MapKeyStr(node->string), grid);
CG_TableHeader *header = MD_PushArrayZero(cg_arena, CG_TableHeader, 1);
*header = CG_TableHeaderFromTag(table_tag);
MD_MapOverwrite(cg_arena, &cg_tbl_top_level_table_header_map, MD_MapKeyStr(node->string), header);
}
if(MD_NodeHasTag(node, cg_tbl_tag__table_gen, 0))
{
MD_MapInsert(cg_arena, &cg_tbl_layer_map_gen, layer_key, node);
}
if(MD_NodeHasTag(node, cg_tbl_tag__table_gen_enum, 0))
{
MD_MapInsert(cg_arena, &cg_tbl_layer_map_gen_enum, layer_key, node);
}
if(MD_NodeHasTag(node, cg_tbl_tag__table_gen_data, 0))
{
MD_MapInsert(cg_arena, &cg_tbl_layer_map_gen_data, layer_key, node);
}
}
}
//- rjf: generation phase
for(MD_EachNode(file_ref, file_list->first_child))
{
MD_Node *file = MD_ResolveNodeFromReference(file_ref);
MD_String8 layer_name = file->string;
MD_MapKey layer_key = MD_MapKeyStr(layer_name);
//- rjf: generate all table enums
for(MD_MapSlot *slot = MD_MapLookup(&cg_tbl_layer_map_gen_enum, layer_key);
slot != 0;
slot = MD_MapScan(slot->next, layer_key))
{
MD_Node *gen = (MD_Node *)slot->val;
CG_FilePair f = CG_FilePairFromNode(gen);
FILE *file = CG_FileFromNodePair(gen, &f);
fprintf(file, "enum %.*s\n{\n", MD_S8VArg(gen->string));
MD_String8List gen_strings = CG_GenStringListFromNode(table_expr_op_table, gen);
MD_StringJoin join = { MD_S8Lit(""), MD_S8Lit("\n"), MD_S8Lit("") };
MD_String8 gen_string = MD_S8ListJoin(cg_arena, gen_strings, &join);
fprintf(file, "%.*s", MD_S8VArg(gen_string));
fprintf(file, "\n};\n\n");
}
//- rjf: generate all data tables
for(MD_MapSlot *slot = MD_MapLookup(&cg_tbl_layer_map_gen_data, layer_key);
slot != 0;
slot = MD_MapScan(slot->next, layer_key))
{
MD_Node *gen = (MD_Node *)slot->val;
MD_Node *tag = MD_TagFromString(gen, cg_tbl_tag__table_gen_data, 0);
MD_Node *data_table_type_node = tag->first_child;
MD_String8 data_table_type = data_table_type_node->string;
MD_String8List gen_strings = CG_GenStringListFromNode(table_expr_op_table, gen);
MD_String8 h_decl_specifier = MD_S8Lit("extern");
CG_FilePair f = CG_FilePairFromNode(gen);
fprintf(f.h, "%.*s %.*s %.*s[%" PRIu64 "];\n\n", MD_S8VArg(h_decl_specifier), MD_S8VArg(data_table_type), MD_S8VArg(gen->string), gen_strings.node_count);
fprintf(f.c, "%.*s %.*s[%" PRIu64 "] =\n{\n", MD_S8VArg(data_table_type), MD_S8VArg(gen->string), gen_strings.node_count);
MD_StringJoin join = { MD_S8Lit(""), MD_S8Lit("\n"), MD_S8Lit("") };
MD_String8 gen_string = MD_S8ListJoin(cg_arena, gen_strings, &join);
fprintf(f.c, "%.*s", MD_S8VArg(gen_string));
fprintf(f.c, "\n};\n");
fprintf(f.c, "\n");
}
//- rjf: generate all general generations
for(MD_MapSlot *slot = MD_MapLookup(&cg_tbl_layer_map_gen, layer_key);
slot != 0;
slot = MD_MapScan(slot->next, layer_key))
{
MD_Node *gen = (MD_Node *)slot->val;
CG_FilePair f = CG_FilePairFromNode(gen);
FILE *file = CG_FileFromNodePair(gen, &f);
MD_String8List gen_strings = CG_GenStringListFromNode(table_expr_op_table, gen);
MD_StringJoin join = { MD_S8Lit(""), MD_S8Lit("\n"), MD_S8Lit("") };
MD_String8 gen_string = MD_S8ListJoin(cg_arena, gen_strings, &join);
fprintf(file, "%.*s", MD_S8VArg(gen_string));
fprintf(file, "\n\n");
}
}
}