1652 lines
57 KiB
C++
1652 lines
57 KiB
C++
/*
|
|
4coder_config.cpp - Parsing *.4coder files.
|
|
*/
|
|
|
|
// TOP
|
|
|
|
////////////////////////////////
|
|
// NOTE(allen): Config Search List
|
|
|
|
function void
|
|
def_search_normal_load_list(Arena *arena, List_String_Const_u8 *list){
|
|
Variable_Handle prj_var = vars_read_key(vars_get_root(), vars_save_string_lit("prj_config"));
|
|
String_Const_u8 prj_dir = prj_path_from_project(arena, prj_var);
|
|
if (prj_dir.size > 0){
|
|
string_list_push(arena, list, prj_dir);
|
|
}
|
|
def_search_list_add_system_path(arena, list, SystemPath_UserDirectory);
|
|
def_search_list_add_system_path(arena, list, SystemPath_Binary);
|
|
}
|
|
|
|
function FILE*
|
|
def_search_normal_fopen(Arena *arena, char *file_name, char *opt){
|
|
Temp_Memory_Block block(arena);
|
|
List_String_Const_u8 list = {};
|
|
def_search_normal_load_list(arena, &list);
|
|
FILE *file = def_search_fopen(arena, &list, file_name, opt);
|
|
return(file);
|
|
}
|
|
|
|
////////////////////////////////
|
|
// NOTE(allen): Extension List
|
|
|
|
function String_Const_u8_Array
|
|
parse_extension_line_to_extension_list(Application_Links *app, Arena *arena, String_Const_u8 str){
|
|
ProfileScope(app, "parse extension line to extension list");
|
|
i32 count = 0;
|
|
for (u64 i = 0; i < str.size; i += 1){
|
|
if (str.str[i] == '.'){
|
|
count += 1;
|
|
}
|
|
}
|
|
|
|
String_Const_u8_Array array = {};
|
|
array.count = count;
|
|
array.strings = push_array(arena, String_Const_u8, count);
|
|
|
|
push_align(arena, 1);
|
|
str = string_skip(str, string_find_first(str, '.') + 1);
|
|
for (i32 i = 0; i < count; i += 1){
|
|
u64 next_period = string_find_first(str, '.');
|
|
String_Const_u8 extension = string_prefix(str, next_period);
|
|
str = string_skip(str, next_period + 1);
|
|
array.strings[i] = push_string_copy(arena, extension);
|
|
}
|
|
push_align(arena, 8);
|
|
|
|
return(array);
|
|
}
|
|
|
|
////////////////////////////////
|
|
// NOTE(allen): Token Array
|
|
|
|
function Token_Array
|
|
token_array_from_text(Application_Links *app, Arena *arena, String_Const_u8 data){
|
|
ProfileScope(app, "token array from text");
|
|
Token_List list = lex_full_input_cpp(arena, data);
|
|
return(token_array_from_list(arena, &list));
|
|
}
|
|
|
|
////////////////////////////////
|
|
// NOTE(allen): Built in Mapping
|
|
|
|
function void
|
|
setup_built_in_mapping(Application_Links *app, String_Const_u8 name, Mapping *mapping, i64 global_id, i64 file_id, i64 code_id){
|
|
Thread_Context *tctx = get_thread_context(app);
|
|
if (string_match(name, string_u8_litexpr("default"))){
|
|
mapping_release(tctx, mapping);
|
|
mapping_init(tctx, mapping);
|
|
setup_default_mapping(mapping, global_id, file_id, code_id);
|
|
}
|
|
else if (string_match(name, string_u8_litexpr("mac-default"))){
|
|
mapping_release(tctx, mapping);
|
|
mapping_init(tctx, mapping);
|
|
setup_mac_mapping(mapping, global_id, file_id, code_id);
|
|
}
|
|
else if (string_match(name, string_u8_litexpr("choose"))){
|
|
mapping_release(tctx, mapping);
|
|
mapping_init(tctx, mapping);
|
|
#if OS_MAC
|
|
setup_mac_mapping(mapping, global_id, file_id, code_id);
|
|
#else
|
|
setup_default_mapping(mapping, global_id, file_id, code_id);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
////////////////////////////////
|
|
// NOTE(allen): Errors
|
|
|
|
function Error_Location
|
|
get_error_location(Application_Links *app, u8 *base, u8 *pos){
|
|
ProfileScope(app, "get error location");
|
|
Error_Location location = {};
|
|
location.line_number = 1;
|
|
location.column_number = 1;
|
|
for (u8 *ptr = base;
|
|
ptr < pos;
|
|
ptr += 1){
|
|
if (*ptr == '\n'){
|
|
location.line_number += 1;
|
|
location.column_number = 1;
|
|
}
|
|
else{
|
|
location.column_number += 1;
|
|
}
|
|
}
|
|
return(location);
|
|
}
|
|
|
|
function String_Const_u8
|
|
config_stringize_errors(Application_Links *app, Arena *arena, Config *parsed){
|
|
ProfileScope(app, "stringize errors");
|
|
String_Const_u8 result = {};
|
|
if (parsed->errors.first != 0){
|
|
List_String_Const_u8 list = {};
|
|
for (Config_Error *error = parsed->errors.first;
|
|
error != 0;
|
|
error = error->next){
|
|
Error_Location location = get_error_location(app, parsed->data.str, error->pos);
|
|
string_list_pushf(arena, &list, "%.*s:%d:%d: %.*s\n",
|
|
string_expand(error->file_name), location.line_number, location.column_number, string_expand(error->text));
|
|
}
|
|
result = string_list_flatten(arena, list);
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
////////////////////////////////
|
|
// NOTE(allen): Parser
|
|
|
|
function Config_Parser
|
|
def_config_parser_init(Arena *arena, String_Const_u8 file_name, String_Const_u8 data, Token_Array array){
|
|
Config_Parser ctx = {};
|
|
ctx.token = array.tokens - 1;
|
|
ctx.opl = array.tokens + array.count;
|
|
ctx.file_name = file_name;
|
|
ctx.data = data;
|
|
ctx.arena = arena;
|
|
def_config_parser_inc(&ctx);
|
|
return(ctx);
|
|
}
|
|
|
|
function void
|
|
def_config_parser_inc(Config_Parser *ctx){
|
|
Token *t = ctx->token;
|
|
Token *opl = ctx->opl;
|
|
for (t += 1;
|
|
t < opl && (t->kind == TokenBaseKind_Comment ||
|
|
t->kind == TokenBaseKind_Whitespace);
|
|
t += 1);
|
|
ctx->token = t;
|
|
}
|
|
|
|
function u8*
|
|
def_config_parser_get_pos(Config_Parser *ctx){
|
|
return(ctx->data.str + ctx->token->pos);
|
|
}
|
|
|
|
function b32
|
|
def_config_parser_recognize_base_kind(Config_Parser *ctx, Token_Base_Kind kind){
|
|
b32 result = false;
|
|
if (ctx->token < ctx->opl){
|
|
result = (ctx->token->kind == kind);
|
|
}
|
|
else if (kind == TokenBaseKind_EOF){
|
|
result = true;
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
function b32
|
|
def_config_parser_recognize_cpp_kind(Config_Parser *ctx, Token_Cpp_Kind kind){
|
|
b32 result = false;
|
|
if (ctx->token < ctx->opl){
|
|
result = (ctx->token->sub_kind == kind);
|
|
}
|
|
else if (kind == TokenCppKind_EOF){
|
|
result = true;
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
function b32
|
|
def_config_parser_recognize_boolean(Config_Parser *ctx){
|
|
b32 result = false;
|
|
Token *token = ctx->token;
|
|
if (ctx->token < ctx->opl){
|
|
result = (token->sub_kind == TokenCppKind_LiteralTrue ||
|
|
token->sub_kind == TokenCppKind_LiteralFalse);
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
function b32
|
|
def_config_parser_recognize_text(Config_Parser *ctx, String_Const_u8 text){
|
|
String_Const_u8 lexeme = def_config_parser_get_lexeme(ctx);
|
|
return(lexeme.str != 0 && string_match(lexeme, text));
|
|
}
|
|
|
|
function b32
|
|
def_config_parser_match_cpp_kind(Config_Parser *ctx, Token_Cpp_Kind kind){
|
|
b32 result = def_config_parser_recognize_cpp_kind(ctx, kind);
|
|
if (result){
|
|
def_config_parser_inc(ctx);
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
function b32
|
|
def_config_parser_match_text(Config_Parser *ctx, String_Const_u8 text){
|
|
b32 result = def_config_parser_recognize_text(ctx, text);
|
|
if (result){
|
|
def_config_parser_inc(ctx);
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
function String_Const_u8
|
|
def_config_parser_get_lexeme(Config_Parser *ctx){
|
|
String_Const_u8 lexeme = {};
|
|
Token *token = ctx->token;
|
|
if (token < ctx->opl){
|
|
lexeme = SCu8(ctx->data.str + token->pos, token->size);
|
|
}
|
|
return(lexeme);
|
|
}
|
|
|
|
function Config_Integer
|
|
def_config_parser_get_int(Config_Parser *ctx){
|
|
Config_Integer config_integer = {};
|
|
String_Const_u8 str = def_config_parser_get_lexeme(ctx);
|
|
if (string_match(string_prefix(str, 2), string_u8_litexpr("0x"))){
|
|
config_integer.is_signed = false;
|
|
config_integer.uinteger = (u32)(string_to_integer(string_skip(str, 2), 16));
|
|
}
|
|
else{
|
|
b32 is_negative = (string_get_character(str, 0) == '-');
|
|
if (is_negative){
|
|
str = string_skip(str, 1);
|
|
}
|
|
config_integer.is_signed = true;
|
|
config_integer.integer = (i32)(string_to_integer(str, 10));
|
|
if (is_negative){
|
|
config_integer.integer *= -1;
|
|
}
|
|
}
|
|
return(config_integer);
|
|
}
|
|
|
|
function b32
|
|
def_config_parser_get_boolean(Config_Parser *ctx){
|
|
String_Const_u8 str = def_config_parser_get_lexeme(ctx);
|
|
return(string_match(str, string_u8_litexpr("true")));
|
|
}
|
|
|
|
function Config*
|
|
def_config_parser_top(Config_Parser *ctx){
|
|
i32 *version = def_config_parser_version(ctx);
|
|
|
|
Config_Assignment *first = 0;
|
|
Config_Assignment *last = 0;
|
|
i32 count = 0;
|
|
for (;!def_config_parser_recognize_cpp_kind(ctx, TokenCppKind_EOF);){
|
|
Config_Assignment *assignment = def_config_parser_assignment(ctx);
|
|
if (assignment != 0){
|
|
zdll_push_back(first, last, assignment);
|
|
count += 1;
|
|
}
|
|
}
|
|
|
|
Config *config = push_array(ctx->arena, Config, 1);
|
|
block_zero_struct(config);
|
|
config->version = version;
|
|
config->first = first;
|
|
config->last = last;
|
|
config->count = count;
|
|
config->errors = ctx->errors;
|
|
config->file_name = ctx->file_name;
|
|
config->data = ctx->data;
|
|
return(config);
|
|
}
|
|
|
|
function i32*
|
|
def_config_parser_version(Config_Parser *ctx){
|
|
require(def_config_parser_match_text(ctx, str8_lit("version")));
|
|
|
|
if (!def_config_parser_match_cpp_kind(ctx, TokenCppKind_ParenOp)){
|
|
def_config_parser_push_error_here(ctx, "expected token '(' for version specifier: 'version(#)'");
|
|
def_config_parser_recover(ctx);
|
|
return(0);
|
|
}
|
|
|
|
if (!def_config_parser_recognize_base_kind(ctx, TokenBaseKind_LiteralInteger)){
|
|
def_config_parser_push_error_here(ctx, "expected an integer constant for version specifier: 'version(#)'");
|
|
def_config_parser_recover(ctx);
|
|
return(0);
|
|
}
|
|
|
|
Config_Integer value = def_config_parser_get_int(ctx);
|
|
def_config_parser_inc(ctx);
|
|
|
|
if (!def_config_parser_match_cpp_kind(ctx, TokenCppKind_ParenCl)){
|
|
def_config_parser_push_error_here(ctx, "expected token ')' for version specifier: 'version(#)'");
|
|
def_config_parser_recover(ctx);
|
|
return(0);
|
|
}
|
|
|
|
if (!def_config_parser_match_cpp_kind(ctx, TokenCppKind_Semicolon)){
|
|
def_config_parser_push_error_here(ctx, "expected token ';' for version specifier: 'version(#)'");
|
|
def_config_parser_recover(ctx);
|
|
return(0);
|
|
}
|
|
|
|
i32 *ptr = push_array(ctx->arena, i32, 1);
|
|
*ptr = value.integer;
|
|
return(ptr);
|
|
}
|
|
|
|
function Config_Assignment*
|
|
def_config_parser_assignment(Config_Parser *ctx){
|
|
u8 *pos = def_config_parser_get_pos(ctx);
|
|
|
|
Config_LValue *l = def_config_parser_lvalue(ctx);
|
|
if (l == 0){
|
|
def_config_parser_push_error_here(ctx, "expected an l-value; l-value formats: 'identifier', 'identifier[#]'");
|
|
def_config_parser_recover(ctx);
|
|
return(0);
|
|
}
|
|
|
|
if (!def_config_parser_match_cpp_kind(ctx, TokenCppKind_Eq)){
|
|
def_config_parser_push_error_here(ctx, "expected token '=' for assignment: 'l-value = r-value;'");
|
|
def_config_parser_recover(ctx);
|
|
return(0);
|
|
}
|
|
|
|
Config_RValue *r = def_config_parser_rvalue(ctx);
|
|
if (r == 0){
|
|
def_config_parser_push_error_here(ctx, "expected an r-value; r-value formats:\n"
|
|
"\tconstants (true, false, integers, hexadecimal integers, strings, characters)\n"
|
|
"\tany l-value that is set in the file\n"
|
|
"\tcompound: '{ compound-element, compound-element, compound-element ...}'\n"
|
|
"\ta compound-element is an r-value, and can have a layout specifier\n"
|
|
"\tcompound-element with layout specifier: .name = r-value, .integer = r-value");
|
|
def_config_parser_recover(ctx);
|
|
return(0);
|
|
}
|
|
|
|
if (!def_config_parser_match_cpp_kind(ctx, TokenCppKind_Semicolon)){
|
|
def_config_parser_push_error_here(ctx, "expected token ';' for assignment: 'l-value = r-value;'");
|
|
def_config_parser_recover(ctx);
|
|
return(0);
|
|
}
|
|
|
|
Config_Assignment *assignment = push_array_zero(ctx->arena, Config_Assignment, 1);
|
|
assignment->pos = pos;
|
|
assignment->l = l;
|
|
assignment->r = r;
|
|
return(assignment);
|
|
}
|
|
|
|
function Config_LValue*
|
|
def_config_parser_lvalue(Config_Parser *ctx){
|
|
require(def_config_parser_recognize_cpp_kind(ctx, TokenCppKind_Identifier));
|
|
String_Const_u8 identifier = def_config_parser_get_lexeme(ctx);
|
|
def_config_parser_inc(ctx);
|
|
|
|
i32 index = 0;
|
|
if (def_config_parser_match_cpp_kind(ctx, TokenCppKind_BrackOp)){
|
|
require(def_config_parser_recognize_base_kind(ctx, TokenBaseKind_LiteralInteger));
|
|
Config_Integer value = def_config_parser_get_int(ctx);
|
|
index = value.integer;
|
|
def_config_parser_inc(ctx);
|
|
require(def_config_parser_match_cpp_kind(ctx, TokenCppKind_BrackCl));
|
|
}
|
|
|
|
Config_LValue *lvalue = push_array_zero(ctx->arena, Config_LValue, 1);
|
|
lvalue->identifier = identifier;
|
|
lvalue->index = index;
|
|
return(lvalue);
|
|
}
|
|
|
|
function Config_RValue*
|
|
def_config_parser_rvalue(Config_Parser *ctx){
|
|
Config_RValue *rvalue = 0;
|
|
if (def_config_parser_recognize_cpp_kind(ctx, TokenCppKind_Identifier)){
|
|
Config_LValue *l = def_config_parser_lvalue(ctx);
|
|
require(l != 0);
|
|
rvalue = push_array_zero(ctx->arena, Config_RValue, 1);
|
|
rvalue->type = ConfigRValueType_LValue;
|
|
rvalue->lvalue = l;
|
|
}
|
|
else if (def_config_parser_recognize_cpp_kind(ctx, TokenCppKind_BraceOp)){
|
|
def_config_parser_inc(ctx);
|
|
Config_Compound *compound = def_config_parser_compound(ctx);
|
|
require(compound != 0);
|
|
rvalue = push_array_zero(ctx->arena, Config_RValue, 1);
|
|
rvalue->type = ConfigRValueType_Compound;
|
|
rvalue->compound = compound;
|
|
}
|
|
else if (def_config_parser_recognize_boolean(ctx)){
|
|
b32 b = def_config_parser_get_boolean(ctx);
|
|
def_config_parser_inc(ctx);
|
|
rvalue = push_array_zero(ctx->arena, Config_RValue, 1);
|
|
rvalue->type = ConfigRValueType_Boolean;
|
|
rvalue->boolean = b;
|
|
}
|
|
else if (def_config_parser_recognize_base_kind(ctx, TokenBaseKind_LiteralInteger)){
|
|
Config_Integer value = def_config_parser_get_int(ctx);
|
|
def_config_parser_inc(ctx);
|
|
rvalue = push_array_zero(ctx->arena, Config_RValue, 1);
|
|
rvalue->type = ConfigRValueType_Integer;
|
|
if (value.is_signed){
|
|
rvalue->integer = value.integer;
|
|
}
|
|
else{
|
|
rvalue->uinteger = value.uinteger;
|
|
}
|
|
}
|
|
else if (def_config_parser_recognize_cpp_kind(ctx, TokenCppKind_LiteralString)){
|
|
String_Const_u8 s = def_config_parser_get_lexeme(ctx);
|
|
def_config_parser_inc(ctx);
|
|
s = string_chop(string_skip(s, 1), 1);
|
|
String_Const_u8 interpreted = string_interpret_escapes(ctx->arena, s);
|
|
rvalue = push_array_zero(ctx->arena, Config_RValue, 1);
|
|
rvalue->type = ConfigRValueType_String;
|
|
rvalue->string = interpreted;
|
|
}
|
|
return(rvalue);
|
|
}
|
|
|
|
function void
|
|
config_parser__compound__check(Config_Parser *ctx, Config_Compound *compound){
|
|
b32 implicit_index_allowed = true;
|
|
for (Config_Compound_Element *node = compound->first;
|
|
node != 0;
|
|
node = node->next){
|
|
if (node->l.type != ConfigLayoutType_Unset){
|
|
implicit_index_allowed = false;
|
|
}
|
|
else if (!implicit_index_allowed){
|
|
def_config_parser_push_error(ctx, node->l.pos,
|
|
"encountered unlabeled member after one or more labeled members");
|
|
}
|
|
}
|
|
}
|
|
|
|
function Config_Compound*
|
|
def_config_parser_compound(Config_Parser *ctx){
|
|
Config_Compound_Element *first = 0;
|
|
Config_Compound_Element *last = 0;
|
|
i32 count = 0;
|
|
|
|
Config_Compound_Element *element = def_config_parser_element(ctx);
|
|
require(element != 0);
|
|
zdll_push_back(first, last, element);
|
|
count += 1;
|
|
|
|
for (;def_config_parser_match_cpp_kind(ctx, TokenCppKind_Comma);){
|
|
if (def_config_parser_recognize_cpp_kind(ctx, TokenCppKind_BraceCl)){
|
|
break;
|
|
}
|
|
element = def_config_parser_element(ctx);
|
|
require(element != 0);
|
|
zdll_push_back(first, last, element);
|
|
count += 1;
|
|
}
|
|
|
|
require(def_config_parser_match_cpp_kind(ctx, TokenCppKind_BraceCl));
|
|
|
|
Config_Compound *compound = push_array(ctx->arena, Config_Compound, 1);
|
|
block_zero_struct(compound);
|
|
compound->first = first;
|
|
compound->last = last;
|
|
compound->count = count;
|
|
config_parser__compound__check(ctx, compound);
|
|
return(compound);
|
|
}
|
|
|
|
function Config_Compound_Element*
|
|
def_config_parser_element(Config_Parser *ctx){
|
|
Config_Layout layout = {};
|
|
layout.pos = def_config_parser_get_pos(ctx);
|
|
if (def_config_parser_match_cpp_kind(ctx, TokenCppKind_Dot)){
|
|
if (def_config_parser_recognize_cpp_kind(ctx, TokenCppKind_Identifier)){
|
|
layout.type = ConfigLayoutType_Identifier;
|
|
layout.identifier = def_config_parser_get_lexeme(ctx);
|
|
def_config_parser_inc(ctx);
|
|
}
|
|
else if (def_config_parser_recognize_base_kind(ctx, TokenBaseKind_LiteralInteger)){
|
|
layout.type = ConfigLayoutType_Integer;
|
|
Config_Integer value = def_config_parser_get_int(ctx);
|
|
layout.integer = value.integer;
|
|
def_config_parser_inc(ctx);
|
|
}
|
|
else{
|
|
return(0);
|
|
}
|
|
require(def_config_parser_match_cpp_kind(ctx, TokenCppKind_Eq));
|
|
}
|
|
Config_RValue *rvalue = def_config_parser_rvalue(ctx);
|
|
require(rvalue != 0);
|
|
Config_Compound_Element *element = push_array(ctx->arena, Config_Compound_Element, 1);
|
|
block_zero_struct(element);
|
|
element->l = layout;
|
|
element->r = rvalue;
|
|
return(element);
|
|
}
|
|
|
|
function Config*
|
|
def_config_parse(Application_Links *app, Arena *arena, String_Const_u8 file_name, String_Const_u8 data, Token_Array array){
|
|
ProfileScope(app, "config parse");
|
|
Temp_Memory restore_point = begin_temp(arena);
|
|
Config_Parser ctx = def_config_parser_init(arena, file_name, data, array);
|
|
Config *config = def_config_parser_top(&ctx);
|
|
if (config == 0){
|
|
end_temp(restore_point);
|
|
}
|
|
return(config);
|
|
}
|
|
|
|
function Config*
|
|
def_config_from_text(Application_Links *app, Arena *arena, String_Const_u8 file_name, String_Const_u8 data){
|
|
Config *parsed = 0;
|
|
Temp_Memory restore_point = begin_temp(arena);
|
|
Token_Array array = token_array_from_text(app, arena, data);
|
|
if (array.tokens != 0){
|
|
parsed = def_config_parse(app, arena, file_name, data, array);
|
|
if (parsed == 0){
|
|
end_temp(restore_point);
|
|
}
|
|
}
|
|
return(parsed);
|
|
}
|
|
|
|
function Config_Error*
|
|
def_config_push_error(Arena *arena, Config_Error_List *list, String_Const_u8 file_name, u8 *pos, char *error_text){
|
|
Config_Error *error = push_array(arena, Config_Error, 1);
|
|
zdll_push_back(list->first, list->last, error);
|
|
list->count += 1;
|
|
error->file_name = file_name;
|
|
error->pos = pos;
|
|
error->text = push_string_copy(arena, SCu8(error_text));
|
|
return(error);
|
|
}
|
|
|
|
function Config_Error*
|
|
def_config_push_error(Arena *arena, Config *config, u8 *pos, char *error_text){
|
|
return(def_config_push_error(arena, &config->errors, config->file_name, pos, error_text));
|
|
}
|
|
|
|
function void
|
|
def_config_parser_push_error(Config_Parser *ctx, u8 *pos, char *error_text){
|
|
def_config_push_error(ctx->arena, &ctx->errors, ctx->file_name, pos, error_text);
|
|
}
|
|
|
|
function void
|
|
def_config_parser_push_error_here(Config_Parser *ctx, char *error_text){
|
|
def_config_parser_push_error(ctx, def_config_parser_get_pos(ctx), error_text);
|
|
}
|
|
|
|
function void
|
|
def_config_parser_recover(Config_Parser *ctx){
|
|
for (;;){
|
|
if (def_config_parser_match_cpp_kind(ctx, TokenCppKind_Semicolon)){
|
|
break;
|
|
}
|
|
if (def_config_parser_recognize_cpp_kind(ctx, TokenCppKind_EOF)){
|
|
break;
|
|
}
|
|
def_config_parser_inc(ctx);
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////
|
|
// NOTE(allen): Dump Config to Variables
|
|
|
|
function Config_Get_Result
|
|
config_var(Config *config, String_Const_u8 var_name, i32 subscript);
|
|
|
|
function void
|
|
def_var_dump_rvalue(Application_Links *app, Config *config, Variable_Handle dst, String_ID l_value, Config_RValue *r){
|
|
Scratch_Block scratch(app);
|
|
|
|
b32 *boolean = 0;
|
|
i32 *integer = 0;
|
|
String_Const_u8 *string = 0;
|
|
Config_Compound *compound = 0;
|
|
|
|
Config_Get_Result get_result = {};
|
|
|
|
switch (r->type){
|
|
case ConfigRValueType_LValue:
|
|
{
|
|
Config_LValue *l = r->lvalue;
|
|
if (l != 0){
|
|
get_result = config_var(config, l->identifier, l->index);
|
|
if (get_result.success){
|
|
switch (get_result.type){
|
|
case ConfigRValueType_Boolean:
|
|
{
|
|
boolean = &get_result.boolean;
|
|
}break;
|
|
|
|
case ConfigRValueType_Integer:
|
|
{
|
|
integer = &get_result.integer;
|
|
}break;
|
|
|
|
case ConfigRValueType_String:
|
|
{
|
|
string = &get_result.string;
|
|
}break;
|
|
|
|
case ConfigRValueType_Compound:
|
|
{
|
|
compound = get_result.compound;
|
|
}break;
|
|
}
|
|
}
|
|
}
|
|
}break;
|
|
|
|
case ConfigRValueType_Boolean:
|
|
{
|
|
boolean = &r->boolean;
|
|
}break;
|
|
|
|
case ConfigRValueType_Integer:
|
|
{
|
|
integer = &r->integer;
|
|
}break;
|
|
|
|
case ConfigRValueType_String:
|
|
{
|
|
string = &r->string;
|
|
}break;
|
|
|
|
case ConfigRValueType_Compound:
|
|
{
|
|
compound = r->compound;
|
|
}break;
|
|
}
|
|
|
|
if (boolean != 0){
|
|
String_ID val = 0;
|
|
if (*boolean){
|
|
val = vars_save_string(str8_lit("true"));
|
|
}
|
|
else{
|
|
val = vars_save_string(str8_lit("false"));
|
|
}
|
|
vars_new_variable(dst, l_value, val);
|
|
}
|
|
else if (integer != 0){
|
|
// TODO(allen): signed/unsigned problem
|
|
String_ID val = vars_save_string(push_stringf(scratch, "%d", *integer));
|
|
vars_new_variable(dst, l_value, val);
|
|
}
|
|
else if (string != 0){
|
|
String_ID val = vars_save_string(*string);
|
|
vars_new_variable(dst, l_value, val);
|
|
}
|
|
else if (compound != 0){
|
|
Variable_Handle sub_var = vars_new_variable(dst, l_value);
|
|
|
|
i32 implicit_index = 0;
|
|
b32 implicit_allowed = true;
|
|
|
|
Config_Compound_Element *node = 0;
|
|
if (compound != 0){
|
|
node = compound->first;
|
|
}
|
|
for (; node != 0;
|
|
node = node->next, implicit_index += 1){
|
|
String_ID sub_l_value = 0;
|
|
|
|
switch (node->l.type){
|
|
case ConfigLayoutType_Unset:
|
|
{
|
|
if (implicit_allowed){
|
|
sub_l_value = vars_save_string(push_stringf(scratch, "%d", implicit_index));
|
|
}
|
|
}break;
|
|
|
|
case ConfigLayoutType_Identifier:
|
|
{
|
|
implicit_allowed = false;
|
|
sub_l_value = vars_save_string(node->l.identifier);
|
|
}break;
|
|
|
|
case ConfigLayoutType_Integer:
|
|
{
|
|
implicit_allowed = false;
|
|
sub_l_value = vars_save_string(push_stringf(scratch, "%d", node->l.integer));
|
|
}break;
|
|
}
|
|
|
|
if (sub_l_value != 0){
|
|
Config_RValue *r = node->r;
|
|
if (r != 0){
|
|
def_var_dump_rvalue(app, config, sub_var, sub_l_value, r);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function Variable_Handle
|
|
def_fill_var_from_config(Application_Links *app, Variable_Handle parent, String_ID key, Config *config){
|
|
Variable_Handle result = vars_get_nil();
|
|
|
|
if (key != 0){
|
|
String_ID file_name_id = vars_save_string(config->file_name);
|
|
result = vars_new_variable(parent, key, file_name_id);
|
|
|
|
Variable_Handle var = result;
|
|
|
|
Scratch_Block scratch(app);
|
|
|
|
if (config->version != 0){
|
|
String_ID version_key = vars_save_string(string_u8_litexpr("version"));
|
|
String_ID version_value = vars_save_string(push_stringf(scratch, "%d", *config->version));
|
|
vars_new_variable(parent, version_key, version_value);
|
|
}
|
|
|
|
for (Config_Assignment *node = config->first;
|
|
node != 0;
|
|
node = node->next){
|
|
String_ID l_value = 0;
|
|
Config_LValue *l = node->l;
|
|
if (l != 0){
|
|
String_Const_u8 string = l->identifier;
|
|
if (l->index != 0){
|
|
string = push_stringf(scratch, "%.*s.%d", string_expand(string), l->index);
|
|
}
|
|
l_value = vars_save_string(string);
|
|
}
|
|
|
|
if (l_value != 0){
|
|
Config_RValue *r = node->r;
|
|
if (r != 0){
|
|
def_var_dump_rvalue(app, config, var, l_value, r);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
|
|
////////////////////////////////
|
|
// NOTE(allen): Config Variables Read
|
|
|
|
global const u64 def_config_lookup_count = 4;
|
|
global String_ID def_config_lookup_table[def_config_lookup_count] = {};
|
|
|
|
function void
|
|
_def_config_table_init(void){
|
|
if (def_config_lookup_table[0] == 0){
|
|
def_config_lookup_table[0] = vars_save_string(str8_lit("ses_config"));
|
|
def_config_lookup_table[1] = vars_save_string(str8_lit("prj_config"));
|
|
def_config_lookup_table[2] = vars_save_string(str8_lit("usr_config"));
|
|
def_config_lookup_table[3] = vars_save_string(str8_lit("def_config"));
|
|
}
|
|
}
|
|
|
|
function Variable_Handle
|
|
def_get_config_var(String_ID key){
|
|
_def_config_table_init();
|
|
|
|
Variable_Handle result = vars_get_nil();
|
|
Variable_Handle root = vars_get_root();
|
|
for (u64 i = 0; i < def_config_lookup_count; i += 1){
|
|
String_ID block_key = def_config_lookup_table[i];
|
|
Variable_Handle block_var = vars_read_key(root, block_key);
|
|
Variable_Handle var = vars_read_key(block_var, key);
|
|
if (!vars_is_nil(var)){
|
|
result = var;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
function void
|
|
def_set_config_var(String_ID key, String_ID val){
|
|
_def_config_table_init();
|
|
Variable_Handle root = vars_get_root();
|
|
Variable_Handle block_var = vars_read_key(root, def_config_lookup_table[0]);
|
|
if (vars_is_nil(block_var)){
|
|
block_var = vars_new_variable(root, def_config_lookup_table[0]);
|
|
}
|
|
vars_new_variable(block_var, key, val);
|
|
}
|
|
|
|
function b32
|
|
def_get_config_b32(String_ID key){
|
|
Variable_Handle var = def_get_config_var(key);
|
|
String_ID val = vars_string_id_from_var(var);
|
|
b32 result = (val != 0 && val != vars_save_string_lit("false"));
|
|
return(result);
|
|
}
|
|
|
|
function void
|
|
def_set_config_b32(String_ID key, b32 val){
|
|
String_ID val_id = val?vars_save_string_lit("true"):vars_save_string_lit("false");
|
|
def_set_config_var(key, val_id);
|
|
}
|
|
|
|
function String_Const_u8
|
|
def_get_config_string(Arena *arena, String_ID key){
|
|
Variable_Handle var = def_get_config_var(key);
|
|
String_Const_u8 result = vars_string_from_var(arena, var);
|
|
return(result);
|
|
}
|
|
|
|
function void
|
|
def_set_config_string(String_ID key, String_Const_u8 val){
|
|
def_set_config_var(key, vars_save_string(val));
|
|
}
|
|
|
|
function u64
|
|
def_get_config_u64(Application_Links *app, String_ID key){
|
|
Scratch_Block scratch(app);
|
|
Variable_Handle var = def_get_config_var(key);
|
|
u64 result = vars_u64_from_var(app, var);
|
|
return(result);
|
|
}
|
|
|
|
function void
|
|
def_set_config_u64(Application_Links *app, String_ID key, u64 val){
|
|
Scratch_Block scratch(app);
|
|
String_Const_u8 val_string = push_stringf(scratch, "%llu", val);
|
|
def_set_config_var(key, vars_save_string(val_string));
|
|
}
|
|
|
|
|
|
////////////////////////////////
|
|
// NOTE(allen): Eval
|
|
|
|
function Config_Assignment*
|
|
config_lookup_assignment(Config *config, String_Const_u8 var_name, i32 subscript){
|
|
Config_Assignment *assignment = 0;
|
|
for (assignment = config->first;
|
|
assignment != 0;
|
|
assignment = assignment->next){
|
|
Config_LValue *l = assignment->l;
|
|
if (l != 0 && string_match(l->identifier, var_name) && l->index == subscript){
|
|
break;
|
|
}
|
|
}
|
|
return(assignment);
|
|
}
|
|
|
|
function Config_Get_Result
|
|
config_evaluate_rvalue(Config *config, Config_Assignment *assignment, Config_RValue *r){
|
|
Config_Get_Result result = {};
|
|
if (r != 0 && !assignment->visited){
|
|
if (r->type == ConfigRValueType_LValue){
|
|
assignment->visited = true;
|
|
Config_LValue *l = r->lvalue;
|
|
result = config_var(config, l->identifier, l->index);
|
|
assignment->visited = false;
|
|
}
|
|
else{
|
|
result.success = true;
|
|
result.pos = assignment->pos;
|
|
result.type = r->type;
|
|
switch (r->type){
|
|
case ConfigRValueType_Boolean:
|
|
{
|
|
result.boolean = r->boolean;
|
|
}break;
|
|
|
|
case ConfigRValueType_Integer:
|
|
{
|
|
result.integer = r->integer;
|
|
}break;
|
|
|
|
case ConfigRValueType_String:
|
|
{
|
|
result.string = r->string;
|
|
}break;
|
|
|
|
case ConfigRValueType_Compound:
|
|
{
|
|
result.compound = r->compound;
|
|
}break;
|
|
}
|
|
}
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
function Config_Get_Result
|
|
config_var(Config *config, String_Const_u8 var_name, i32 subscript){
|
|
Config_Get_Result result = {};
|
|
Config_Assignment *assignment = config_lookup_assignment(config, var_name, subscript);
|
|
if (assignment != 0){
|
|
result = config_evaluate_rvalue(config, assignment, assignment->r);
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
|
|
////////////////////////////////
|
|
// NOTE(allen): Nonsense from the old system
|
|
|
|
function Config_Get_Result
|
|
config_compound_member(Config *config, Config_Compound *compound, String_Const_u8 var_name, i32 index){
|
|
Config_Get_Result result = {};
|
|
i32 implicit_index = 0;
|
|
b32 implicit_index_is_valid = true;
|
|
for (Config_Compound_Element *element = compound->first;
|
|
element != 0;
|
|
element = element->next, implicit_index += 1){
|
|
b32 element_matches_query = false;
|
|
switch (element->l.type){
|
|
case ConfigLayoutType_Unset:
|
|
{
|
|
if (implicit_index_is_valid && index == implicit_index){
|
|
element_matches_query = true;
|
|
}
|
|
}break;
|
|
|
|
case ConfigLayoutType_Identifier:
|
|
{
|
|
implicit_index_is_valid = false;
|
|
if (string_match(element->l.identifier, var_name)){
|
|
element_matches_query = true;
|
|
}
|
|
}break;
|
|
|
|
case ConfigLayoutType_Integer:
|
|
{
|
|
implicit_index_is_valid = false;
|
|
if (element->l.integer == index){
|
|
element_matches_query = true;
|
|
}
|
|
}break;
|
|
}
|
|
if (element_matches_query){
|
|
Config_Assignment dummy_assignment = {};
|
|
dummy_assignment.pos = element->l.pos;
|
|
result = config_evaluate_rvalue(config, &dummy_assignment, element->r);
|
|
break;
|
|
}
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
function Config_Iteration_Step_Result
|
|
typed_array_iteration_step(Config *parsed, Config_Compound *compound, Config_RValue_Type type, i32 index);
|
|
|
|
function i32
|
|
typed_array_get_count(Config *parsed, Config_Compound *compound, Config_RValue_Type type);
|
|
|
|
function Config_Get_Result_List
|
|
typed_array_reference_list(Arena *arena, Config *parsed, Config_Compound *compound, Config_RValue_Type type);
|
|
|
|
#define config_fixed_string_var(c,v,s,o,a) config_placed_string_var((c),(v),(s),(o),(a),sizeof(a))
|
|
|
|
////////////////////////////////
|
|
|
|
function b32
|
|
config_bool_var(Config *config, String_Const_u8 var_name, i32 subscript, b32* var_out){
|
|
Config_Get_Result result = config_var(config, var_name, subscript);
|
|
b32 success = (result.success && result.type == ConfigRValueType_Boolean);
|
|
if (success){
|
|
*var_out = result.boolean;
|
|
}
|
|
return(success);
|
|
}
|
|
function b32
|
|
config_bool_var(Config *config, String_Const_u8 var_name, i32 subscript, b8 *var_out){
|
|
b32 temp = false;
|
|
b32 success = config_bool_var(config, var_name, subscript, &temp);
|
|
if (success){
|
|
*var_out = (temp != false);
|
|
}
|
|
return(success);
|
|
}
|
|
function b32
|
|
config_bool_var(Config *config, char *var_name, i32 subscript, b32* var_out){
|
|
return(config_bool_var(config, SCu8(var_name), subscript, var_out));
|
|
}
|
|
function b32
|
|
config_bool_var(Config *config, char* var_name, i32 subscript, b8 *var_out){
|
|
b32 temp = false;
|
|
b32 success = config_bool_var(config, SCu8(var_name), subscript, &temp);
|
|
if (success){
|
|
*var_out = (temp != false);
|
|
}
|
|
return(success);
|
|
}
|
|
|
|
function b32
|
|
config_int_var(Config *config, String_Const_u8 var_name, i32 subscript, i32* var_out){
|
|
Config_Get_Result result = config_var(config, var_name, subscript);
|
|
b32 success = result.success && result.type == ConfigRValueType_Integer;
|
|
if (success){
|
|
*var_out = result.integer;
|
|
}
|
|
return(success);
|
|
}
|
|
|
|
function b32
|
|
config_int_var(Config *config, char *var_name, i32 subscript, i32* var_out){
|
|
return(config_int_var(config, SCu8(var_name), subscript, var_out));
|
|
}
|
|
|
|
function b32
|
|
config_uint_var(Config *config, String_Const_u8 var_name, i32 subscript, u32* var_out){
|
|
Config_Get_Result result = config_var(config, var_name, subscript);
|
|
b32 success = result.success && result.type == ConfigRValueType_Integer;
|
|
if (success){
|
|
*var_out = result.uinteger;
|
|
}
|
|
return(success);
|
|
}
|
|
|
|
function b32
|
|
config_uint_var(Config *config, char *var_name, i32 subscript, u32* var_out){
|
|
return(config_uint_var(config, SCu8(var_name), subscript, var_out));
|
|
}
|
|
|
|
function b32
|
|
config_string_var(Config *config, String_Const_u8 var_name, i32 subscript, String_Const_u8* var_out){
|
|
Config_Get_Result result = config_var(config, var_name, subscript);
|
|
b32 success = result.success && result.type == ConfigRValueType_String;
|
|
if (success){
|
|
*var_out = result.string;
|
|
}
|
|
return(success);
|
|
}
|
|
|
|
function b32
|
|
config_string_var(Config *config, char *var_name, i32 subscript, String_Const_u8* var_out){
|
|
return(config_string_var(config, SCu8(var_name), subscript, var_out));
|
|
}
|
|
|
|
function b32
|
|
config_placed_string_var(Config *config, String_Const_u8 var_name, i32 subscript, String_Const_u8* var_out, u8 *space, u64 space_size){
|
|
Config_Get_Result result = config_var(config, var_name, subscript);
|
|
b32 success = (result.success && result.type == ConfigRValueType_String);
|
|
if (success){
|
|
u64 size = result.string.size;
|
|
size = clamp_top(size, space_size);
|
|
block_copy(space, result.string.str, size);
|
|
*var_out = SCu8(space, size);
|
|
}
|
|
return(success);
|
|
}
|
|
|
|
function b32
|
|
config_placed_string_var(Config *config, char *var_name, i32 subscript, String_Const_u8* var_out, u8 *space, u64 space_size){
|
|
return(config_placed_string_var(config, SCu8(var_name), subscript, var_out, space, space_size));
|
|
}
|
|
|
|
function b32
|
|
config_compound_var(Config *config, String_Const_u8 var_name, i32 subscript, Config_Compound** var_out){
|
|
Config_Get_Result result = config_var(config, var_name, subscript);
|
|
b32 success = (result.success && result.type == ConfigRValueType_Compound);
|
|
if (success){
|
|
*var_out = result.compound;
|
|
}
|
|
return(success);
|
|
}
|
|
|
|
function b32
|
|
config_compound_var(Config *config, char *var_name, i32 subscript, Config_Compound** var_out){
|
|
return(config_compound_var(config, SCu8(var_name), subscript, var_out));
|
|
}
|
|
|
|
function b32
|
|
config_compound_bool_member(Config *config, Config_Compound *compound,
|
|
String_Const_u8 var_name, i32 index, b32* var_out){
|
|
Config_Get_Result result = config_compound_member(config, compound, var_name, index);
|
|
b32 success = result.success && result.type == ConfigRValueType_Boolean;
|
|
if (success){
|
|
*var_out = result.boolean;
|
|
}
|
|
return(success);
|
|
}
|
|
|
|
function b32
|
|
config_compound_bool_member(Config *config, Config_Compound *compound,
|
|
char *var_name, i32 index, b32* var_out){
|
|
return(config_compound_bool_member(config, compound, SCu8(var_name), index, var_out));
|
|
}
|
|
|
|
function b32
|
|
config_compound_int_member(Config *config, Config_Compound *compound,
|
|
String_Const_u8 var_name, i32 index, i32* var_out){
|
|
Config_Get_Result result = config_compound_member(config, compound, var_name, index);
|
|
b32 success = result.success && result.type == ConfigRValueType_Integer;
|
|
if (success){
|
|
*var_out = result.integer;
|
|
}
|
|
return(success);
|
|
}
|
|
|
|
function b32
|
|
config_compound_int_member(Config *config, Config_Compound *compound,
|
|
char *var_name, i32 index, i32* var_out){
|
|
return(config_compound_int_member(config, compound, SCu8(var_name), index, var_out));
|
|
}
|
|
|
|
function b32
|
|
config_compound_uint_member(Config *config, Config_Compound *compound,
|
|
String_Const_u8 var_name, i32 index, u32* var_out){
|
|
Config_Get_Result result = config_compound_member(config, compound, var_name, index);
|
|
b32 success = result.success && result.type == ConfigRValueType_Integer;
|
|
if (success){
|
|
*var_out = result.uinteger;
|
|
}
|
|
return(success);
|
|
}
|
|
|
|
function b32
|
|
config_compound_uint_member(Config *config, Config_Compound *compound,
|
|
char *var_name, i32 index, u32* var_out){
|
|
return(config_compound_uint_member(config, compound, SCu8(var_name), index, var_out));
|
|
}
|
|
|
|
function b32
|
|
config_compound_string_member(Config *config, Config_Compound *compound,
|
|
String_Const_u8 var_name, i32 index, String_Const_u8* var_out){
|
|
Config_Get_Result result = config_compound_member(config, compound, var_name, index);
|
|
b32 success = (result.success && result.type == ConfigRValueType_String);
|
|
if (success){
|
|
*var_out = result.string;
|
|
}
|
|
return(success);
|
|
}
|
|
|
|
function b32
|
|
config_compound_string_member(Config *config, Config_Compound *compound,
|
|
char *var_name, i32 index, String_Const_u8* var_out){
|
|
return(config_compound_string_member(config, compound, SCu8(var_name), index, var_out));
|
|
}
|
|
|
|
function b32
|
|
config_compound_placed_string_member(Config *config, Config_Compound *compound,
|
|
String_Const_u8 var_name, i32 index, String_Const_u8* var_out, u8 *space, u64 space_size){
|
|
Config_Get_Result result = config_compound_member(config, compound, var_name, index);
|
|
b32 success = (result.success && result.type == ConfigRValueType_String);
|
|
if (success){
|
|
u64 size = result.string.size;
|
|
size = clamp_top(size, space_size);
|
|
block_copy(space, result.string.str, size);
|
|
*var_out = SCu8(space, size);
|
|
}
|
|
return(success);
|
|
}
|
|
|
|
function b32
|
|
config_compound_placed_string_member(Config *config, Config_Compound *compound,
|
|
char *var_name, i32 index, String_Const_u8* var_out, u8 *space, u64 space_size){
|
|
return(config_compound_placed_string_member(config, compound, SCu8(var_name), index, var_out, space, space_size));
|
|
}
|
|
|
|
function b32
|
|
config_compound_compound_member(Config *config, Config_Compound *compound,
|
|
String_Const_u8 var_name, i32 index, Config_Compound** var_out){
|
|
Config_Get_Result result = config_compound_member(config, compound, var_name, index);
|
|
b32 success = (result.success && result.type == ConfigRValueType_Compound);
|
|
if (success){
|
|
*var_out = result.compound;
|
|
}
|
|
return(success);
|
|
}
|
|
|
|
function b32
|
|
config_compound_compound_member(Config *config, Config_Compound *compound,
|
|
char *var_name, i32 index, Config_Compound** var_out){
|
|
return(config_compound_compound_member(config, compound, SCu8(var_name), index, var_out));
|
|
}
|
|
|
|
function Iteration_Step_Result
|
|
typed_bool_array_iteration_step(Config *config, Config_Compound *compound, i32 index, b32* var_out){
|
|
Config_Iteration_Step_Result result = typed_array_iteration_step(config, compound, ConfigRValueType_Boolean, index);
|
|
b32 success = (result.step == Iteration_Good);
|
|
if (success){
|
|
*var_out = result.get.boolean;
|
|
}
|
|
return(result.step);
|
|
}
|
|
|
|
function Iteration_Step_Result
|
|
typed_int_array_iteration_step(Config *config, Config_Compound *compound, i32 index, i32* var_out){
|
|
Config_Iteration_Step_Result result = typed_array_iteration_step(config, compound, ConfigRValueType_Integer, index);
|
|
b32 success = (result.step == Iteration_Good);
|
|
if (success){
|
|
*var_out = result.get.integer;
|
|
}
|
|
return(result.step);
|
|
}
|
|
|
|
function Iteration_Step_Result
|
|
typed_uint_array_iteration_step(Config *config, Config_Compound *compound, i32 index, u32* var_out){
|
|
Config_Iteration_Step_Result result = typed_array_iteration_step(config, compound, ConfigRValueType_Integer, index);
|
|
b32 success = (result.step == Iteration_Good);
|
|
if (success){
|
|
*var_out = result.get.uinteger;
|
|
}
|
|
return(result.step);
|
|
}
|
|
|
|
function Iteration_Step_Result
|
|
typed_string_array_iteration_step(Config *config, Config_Compound *compound, i32 index, String_Const_u8* var_out){
|
|
Config_Iteration_Step_Result result = typed_array_iteration_step(config, compound, ConfigRValueType_String, index);
|
|
b32 success = (result.step == Iteration_Good);
|
|
if (success){
|
|
*var_out = result.get.string;
|
|
}
|
|
return(result.step);
|
|
}
|
|
|
|
function Iteration_Step_Result
|
|
typed_placed_string_array_iteration_step(Config *config, Config_Compound *compound, i32 index, String_Const_u8* var_out, u8 *space, u64 space_size){
|
|
Config_Iteration_Step_Result result = typed_array_iteration_step(config, compound, ConfigRValueType_String, index);
|
|
b32 success = (result.step == Iteration_Good);
|
|
if (success){
|
|
u64 size = result.get.string.size;
|
|
size = clamp_top(size, space_size);
|
|
block_copy(space, result.get.string.str, size);
|
|
*var_out = SCu8(space, size);
|
|
}
|
|
return(result.step);
|
|
}
|
|
|
|
function Iteration_Step_Result
|
|
typed_compound_array_iteration_step(Config *config, Config_Compound *compound, i32 index, Config_Compound** var_out){
|
|
Config_Iteration_Step_Result result = typed_array_iteration_step(config, compound, ConfigRValueType_Compound, index);
|
|
b32 success = (result.step == Iteration_Good);
|
|
if (success){
|
|
*var_out = result.get.compound;
|
|
}
|
|
return(result.step);
|
|
}
|
|
|
|
function i32
|
|
typed_bool_array_get_count(Config *config, Config_Compound *compound){
|
|
i32 count = typed_array_get_count(config, compound, ConfigRValueType_Boolean);
|
|
return(count);
|
|
}
|
|
|
|
function i32
|
|
typed_int_array_get_count(Config *config, Config_Compound *compound){
|
|
i32 count = typed_array_get_count(config, compound, ConfigRValueType_Integer);
|
|
return(count);
|
|
}
|
|
|
|
function i32
|
|
typed_string_array_get_count(Config *config, Config_Compound *compound){
|
|
i32 count = typed_array_get_count(config, compound, ConfigRValueType_String);
|
|
return(count);
|
|
}
|
|
|
|
function i32
|
|
typed_compound_array_get_count(Config *config, Config_Compound *compound){
|
|
i32 count = typed_array_get_count(config, compound, ConfigRValueType_Compound);
|
|
return(count);
|
|
}
|
|
|
|
function Config_Get_Result_List
|
|
typed_bool_array_reference_list(Arena *arena, Config *config, Config_Compound *compound){
|
|
Config_Get_Result_List list = typed_array_reference_list(arena, config, compound, ConfigRValueType_Boolean);
|
|
return(list);
|
|
}
|
|
|
|
function Config_Get_Result_List
|
|
typed_int_array_reference_list(Arena *arena, Config *config, Config_Compound *compound){
|
|
Config_Get_Result_List list = typed_array_reference_list(arena, config, compound, ConfigRValueType_Integer);
|
|
return(list);
|
|
}
|
|
|
|
function Config_Get_Result_List
|
|
typed_string_array_reference_list(Arena *arena, Config *config, Config_Compound *compound){
|
|
Config_Get_Result_List list = typed_array_reference_list(arena, config, compound, ConfigRValueType_String);
|
|
return(list);
|
|
}
|
|
|
|
function Config_Get_Result_List
|
|
typed_compound_array_reference_list(Arena *arena, Config *config, Config_Compound *compound){
|
|
Config_Get_Result_List list = typed_array_reference_list(arena, config, compound, ConfigRValueType_Compound);
|
|
return(list);
|
|
}
|
|
|
|
////////////////////////////////
|
|
|
|
function Config_Iteration_Step_Result
|
|
typed_array_iteration_step(Config *parsed, Config_Compound *compound, Config_RValue_Type type, i32 index){
|
|
Config_Iteration_Step_Result result = {};
|
|
result.step = Iteration_Quit;
|
|
Config_Get_Result get_result = config_compound_member(parsed, compound, string_u8_litexpr("~"), index);
|
|
if (get_result.success){
|
|
if (get_result.type == type){
|
|
result.step = Iteration_Good;
|
|
result.get = get_result;
|
|
}
|
|
else{
|
|
result.step = Iteration_Skip;
|
|
}
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
function i32
|
|
typed_array_get_count(Config *parsed, Config_Compound *compound, Config_RValue_Type type){
|
|
i32 count = 0;
|
|
for (i32 i = 0;; ++i){
|
|
Config_Iteration_Step_Result result = typed_array_iteration_step(parsed, compound, type, i);
|
|
if (result.step == Iteration_Skip){
|
|
continue;
|
|
}
|
|
else if (result.step == Iteration_Quit){
|
|
break;
|
|
}
|
|
count += 1;
|
|
}
|
|
return(count);
|
|
}
|
|
|
|
function Config_Get_Result_List
|
|
typed_array_reference_list(Arena *arena, Config *parsed, Config_Compound *compound, Config_RValue_Type type){
|
|
Config_Get_Result_List list = {};
|
|
for (i32 i = 0;; ++i){
|
|
Config_Iteration_Step_Result result = typed_array_iteration_step(parsed, compound, type, i);
|
|
if (result.step == Iteration_Skip){
|
|
continue;
|
|
}
|
|
else if (result.step == Iteration_Quit){
|
|
break;
|
|
}
|
|
Config_Get_Result_Node *node = push_array(arena, Config_Get_Result_Node, 1);
|
|
node->result = result.get;
|
|
zdll_push_back(list.first, list.last, node);
|
|
list.count += 1;
|
|
}
|
|
return(list);
|
|
}
|
|
|
|
////////////////////////////////
|
|
|
|
function void
|
|
change_mode(Application_Links *app, String_Const_u8 mode){
|
|
fcoder_mode = FCoderMode_Original;
|
|
if (string_match(mode, string_u8_litexpr("4coder"))){
|
|
fcoder_mode = FCoderMode_Original;
|
|
}
|
|
else if (string_match(mode, string_u8_litexpr("notepad-like"))){
|
|
begin_notepad_mode(app);
|
|
}
|
|
else{
|
|
print_message(app, string_u8_litexpr("Unknown mode.\n"));
|
|
}
|
|
}
|
|
|
|
////////////////////////////////
|
|
|
|
// TODO(allen): cleanup this mess some more
|
|
|
|
function Config*
|
|
theme_parse__data(Application_Links *app, Arena *arena, String_Const_u8 file_name, String_Const_u8 data, Arena *color_arena, Color_Table *color_table){
|
|
Config *parsed = def_config_from_text(app, arena, file_name, data);
|
|
if (parsed != 0){
|
|
for (Config_Assignment *node = parsed->first;
|
|
node != 0;
|
|
node = node->next){
|
|
Scratch_Block scratch(app, arena);
|
|
Config_LValue *l = node->l;
|
|
String_Const_u8 l_name = push_string_copy(scratch, l->identifier);
|
|
Managed_ID id = managed_id_get(app, string_u8_litexpr("colors"), l_name);
|
|
if (id != 0){
|
|
u32 color = 0;
|
|
if (config_uint_var(parsed, l_name, 0, &color)){
|
|
color_table->arrays[id%color_table->count] = make_colors(color_arena, color);
|
|
}
|
|
else{
|
|
Config_Compound *compound = 0;
|
|
if (config_compound_var(parsed, l_name, 0, &compound)){
|
|
local_persist u32 color_array[256];
|
|
i32 counter = 0;
|
|
for (i32 i = 0;; i += 1){
|
|
Config_Iteration_Step_Result result = typed_array_iteration_step(parsed, compound, ConfigRValueType_Integer, i);
|
|
if (result.step == Iteration_Skip){
|
|
continue;
|
|
}
|
|
else if (result.step == Iteration_Quit){
|
|
break;
|
|
}
|
|
|
|
color_array[counter] = result.get.uinteger;
|
|
counter += 1;
|
|
if (counter == 256){
|
|
break;
|
|
}
|
|
}
|
|
|
|
color_table->arrays[id%color_table->count] = make_colors(color_arena, color_array, counter);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
return(parsed);
|
|
}
|
|
|
|
function Config*
|
|
theme_parse__buffer(Application_Links *app, Arena *arena, Buffer_ID buffer, Arena *color_arena, Color_Table *color_table){
|
|
String_Const_u8 contents = push_whole_buffer(app, arena, buffer);
|
|
Config *parsed = 0;
|
|
if (contents.str != 0){
|
|
String_Const_u8 file_name = push_buffer_file_name(app, arena, buffer);
|
|
parsed = theme_parse__data(app, arena, file_name, contents, color_arena, color_table);
|
|
}
|
|
return(parsed);
|
|
}
|
|
|
|
function Config*
|
|
theme_parse__file_handle(Application_Links *app, Arena *arena, String_Const_u8 file_name, FILE *file, Arena *color_arena, Color_Table *color_table){
|
|
String_Const_u8 data = dump_file_handle(arena, file);
|
|
Config *parsed = 0;
|
|
if (data.str != 0){
|
|
parsed = theme_parse__data(app, arena, file_name, data, color_arena, color_table);
|
|
}
|
|
return(parsed);
|
|
}
|
|
|
|
function Config*
|
|
theme_parse__file_name(Application_Links *app, Arena *arena, char *file_name, Arena *color_arena, Color_Table *color_table){
|
|
Config *parsed = 0;
|
|
FILE* file = fopen(file_name, "rb");
|
|
if (file == 0){
|
|
file = def_search_normal_fopen(arena, file_name, "rb");
|
|
}
|
|
if (file != 0){
|
|
String_Const_u8 data = dump_file_handle(arena, file);
|
|
fclose(file);
|
|
parsed = theme_parse__data(app, arena, SCu8(file_name), data, color_arena, color_table);
|
|
}
|
|
if (parsed == 0){
|
|
Scratch_Block scratch(app, arena);
|
|
String_Const_u8 str = push_u8_stringf(scratch, "Did not find %s, theme not loaded", file_name);
|
|
print_message(app, str);
|
|
}
|
|
return(parsed);
|
|
}
|
|
|
|
////////////////////////////////
|
|
|
|
// TODO(allen): review this function
|
|
function void
|
|
load_config_and_apply(Application_Links *app, Arena *out_arena, i32 override_font_size, b32 override_hinting){
|
|
Scratch_Block scratch(app, out_arena);
|
|
|
|
linalloc_clear(out_arena);
|
|
|
|
Config *parsed = 0;
|
|
FILE *file = def_search_normal_fopen(scratch, "config.4coder", "rb");
|
|
if (file != 0){
|
|
String_Const_u8 data = dump_file_handle(scratch, file);
|
|
fclose(file);
|
|
if (data.str != 0){
|
|
parsed = def_config_from_text(app, scratch, str8_lit("config.4coder"), data);
|
|
}
|
|
}
|
|
|
|
if (parsed != 0){
|
|
// Errors
|
|
String_Const_u8 error_text = config_stringize_errors(app, scratch, parsed);
|
|
if (error_text.str != 0){
|
|
print_message(app, string_u8_litexpr("trying to load config file:\n"));
|
|
print_message(app, error_text);
|
|
}
|
|
|
|
// NOTE(allen): Save As Variables
|
|
if (error_text.str == 0){
|
|
// TODO(allen): this always applies to "def_config" need to get "usr_config" working too
|
|
Variable_Handle config_var = def_fill_var_from_config(app, vars_get_root(), vars_save_string_lit("def_config"), parsed);
|
|
vars_print(app, config_var);
|
|
print_message(app, string_u8_litexpr("\n"));
|
|
}
|
|
}
|
|
else{
|
|
print_message(app, string_u8_litexpr("Using default config:\n"));
|
|
Face_Description description = get_face_description(app, 0);
|
|
if (description.font.file_name.str != 0){
|
|
def_set_config_string(vars_save_string_lit("default_font_name"), description.font.file_name);
|
|
}
|
|
}
|
|
|
|
String_Const_u8 default_font_name = def_get_config_string(scratch, vars_save_string_lit("default_font_name"));
|
|
if (default_font_name.size == 0){
|
|
default_font_name = string_u8_litexpr("liberation-mono.ttf");
|
|
}
|
|
|
|
// TODO(allen): this part seems especially weird now.
|
|
// We want these to be effected by evals of the config system,
|
|
// not by a state that gets evaled and saved *now*!!
|
|
|
|
// Apply config
|
|
String_Const_u8 mode = def_get_config_string(scratch, vars_save_string_lit("mode"));
|
|
change_mode(app, mode);
|
|
|
|
b32 lalt_lctrl_is_altgr = def_get_config_b32(vars_save_string_lit("lalt_lctrl_is_altgr"));
|
|
global_set_setting(app, GlobalSetting_LAltLCtrlIsAltGr, lalt_lctrl_is_altgr);
|
|
|
|
String_Const_u8 default_theme_name = def_get_config_string(scratch, vars_save_string_lit("default_theme_name"));
|
|
Color_Table *colors = get_color_table_by_name(default_theme_name);
|
|
set_active_color(colors);
|
|
|
|
Face_Description description = {};
|
|
if (override_font_size != 0){
|
|
description.parameters.pt_size = override_font_size;
|
|
}
|
|
else{
|
|
description.parameters.pt_size = (i32)def_get_config_u64(app, vars_save_string_lit("default_font_size"));
|
|
}
|
|
|
|
b32 default_font_hinting = def_get_config_b32(vars_save_string_lit("default_font_hinting"));
|
|
description.parameters.hinting = default_font_hinting || override_hinting;
|
|
|
|
description.font.file_name = default_font_name;
|
|
if (!modify_global_face_by_description(app, description)){
|
|
description.font.file_name = get_file_path_in_fonts_folder(scratch, default_font_name);
|
|
modify_global_face_by_description(app, description);
|
|
}
|
|
|
|
b32 bind_by_physical_key = def_get_config_b32(vars_save_string_lit("bind_by_physical_key"));
|
|
if (bind_by_physical_key){
|
|
system_set_key_mode(KeyMode_Physical);
|
|
}
|
|
else{
|
|
system_set_key_mode(KeyMode_LanguageArranged);
|
|
}
|
|
}
|
|
|
|
function void
|
|
load_theme_file_into_live_set(Application_Links *app, char *file_name){
|
|
Arena *arena = &global_theme_arena;
|
|
Color_Table color_table = make_color_table(app, arena);
|
|
Scratch_Block scratch(app, arena);
|
|
Config *config = theme_parse__file_name(app, scratch, file_name, arena, &color_table);
|
|
String_Const_u8 error_text = config_stringize_errors(app, scratch, config);
|
|
print_message(app, error_text);
|
|
|
|
String_Const_u8 name = SCu8(file_name);
|
|
name = string_front_of_path(name);
|
|
if (string_match(string_postfix(name, 7), string_u8_litexpr(".4coder"))){
|
|
name = string_chop(name, 7);
|
|
}
|
|
save_theme(color_table, name);
|
|
}
|
|
|
|
function void
|
|
load_folder_of_themes_into_live_set(Application_Links *app, String_Const_u8 path){
|
|
Scratch_Block scratch(app);
|
|
|
|
File_List list = system_get_file_list(scratch, path);
|
|
for (File_Info **ptr = list.infos, **end = list.infos + list.count;
|
|
ptr < end;
|
|
ptr += 1){
|
|
File_Info *info = *ptr;
|
|
if (!HasFlag(info->attributes.flags, FileAttribute_IsDirectory)){
|
|
String_Const_u8 name = info->file_name;
|
|
Temp_Memory_Block temp(scratch);
|
|
String_Const_u8 full_name = push_u8_stringf(scratch, "%.*s/%.*s",
|
|
string_expand(path),
|
|
string_expand(name));
|
|
load_theme_file_into_live_set(app, (char*)full_name.str);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////
|
|
// NOTE(allen): Commands
|
|
|
|
CUSTOM_COMMAND_SIG(load_theme_current_buffer)
|
|
CUSTOM_DOC("Parse the current buffer as a theme file and add the theme to the theme list. If the buffer has a .4coder postfix in it's name, it is removed when the name is saved.")
|
|
{
|
|
View_ID view = get_active_view(app, Access_ReadVisible);
|
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadVisible);
|
|
|
|
Scratch_Block scratch(app);
|
|
String_Const_u8 file_name = push_buffer_file_name(app, scratch, buffer);
|
|
if (file_name.size > 0){
|
|
Arena *arena = &global_theme_arena;
|
|
Color_Table color_table = make_color_table(app, arena);
|
|
Config *config = theme_parse__buffer(app, scratch, buffer, arena, &color_table);
|
|
String_Const_u8 error_text = config_stringize_errors(app, scratch, config);
|
|
print_message(app, error_text);
|
|
|
|
u64 problem_score = 0;
|
|
if (color_table.count < defcolor_line_numbers_text){
|
|
problem_score = defcolor_line_numbers_text - color_table.count;
|
|
}
|
|
for (i32 i = 0; i < color_table.count; i += 1){
|
|
if (color_table.arrays[i].count == 0){
|
|
problem_score += 1;
|
|
}
|
|
}
|
|
|
|
if (error_text.size > 0 || problem_score >= 10){
|
|
String_Const_u8 string = push_u8_stringf(scratch, "There appears to be a problem parsing %.*s; no theme change applied\n", string_expand(file_name));
|
|
print_message(app, string);
|
|
}
|
|
else{
|
|
String_Const_u8 name = string_front_of_path(file_name);
|
|
if (string_match(string_postfix(name, 7), string_u8_litexpr(".4coder"))){
|
|
name = string_chop(name, 7);
|
|
}
|
|
save_theme(color_table, name);
|
|
|
|
Color_Table_Node *node = global_theme_list.last;
|
|
if (node != 0 && string_match(node->name, name)){
|
|
active_color_table = node->table;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(go_to_user_directory)
|
|
CUSTOM_DOC("Go to the 4coder user directory")
|
|
{
|
|
Scratch_Block scratch(app);
|
|
String_Const_u8 hot = push_hot_directory(app, scratch);
|
|
String8 user_4coder_path = system_get_path(scratch, SystemPath_UserDirectory);
|
|
String8 cmd = push_u8_stringf(scratch, "mkdir \"%.*s\"", string_expand(user_4coder_path));
|
|
exec_system_command(app, 0, buffer_identifier(0), hot, cmd, 0);
|
|
set_hot_directory(app, user_4coder_path);
|
|
}
|
|
|
|
// BOTTOM
|
|
|