/* 4coder_config.cpp - Parsing *.4coder files. */ // TOP // TODO(allen): Stop handling files this way! My own API should be able to do this!!?!?!?!!?!?!!!!? // NOTE(allen): Actually need binary buffers for some stuff to work, but not this parsing thing here. #include //////////////////////////////// static CString_Array get_code_extensions(Extension_List *list){ CString_Array array = {0}; array.strings = default_extensions; array.count = ArrayCount(default_extensions); if (list->count != 0){ array.strings = list->exts; array.count = list->count; } return(array); } static void parse_extension_line_to_extension_list(String str, Extension_List *list){ int32_t mode = 0; int32_t j = 0, k = 0; for (int32_t i = 0; i < str.size; ++i){ switch (mode){ case 0: { if (str.str[i] == '.'){ mode = 1; list->exts[k++] = &list->space[j]; } }break; case 1: { if (str.str[i] == '.'){ list->space[j++] = 0; list->exts[k++] = &list->space[j]; } else{ list->space[j++] = str.str[i]; } }break; } } list->space[j++] = 0; list->count = k; } //////////////////////////////// static void config_parser__advance_to_next(Config_Parser *ctx){ Cpp_Token *t = ctx->token; Cpp_Token *e = ctx->end; for (t += 1; t < e && t->type == CPP_TOKEN_COMMENT; t += 1); ctx->token = t; } static Config_Parser make_config_parser(Partition *arena, String file_name, String data, Cpp_Token_Array array){ Config_Parser ctx = {0}; ctx.start = array.tokens; ctx.token = ctx.start - 1; ctx.end = ctx.start + array.count; ctx.file_name = file_name; ctx.data = data; ctx.arena = arena; config_parser__advance_to_next(&ctx); return(ctx); } static bool32 config_parser__recognize_token(Config_Parser *ctx, Cpp_Token_Type type){ bool32 result = false; if (ctx->start <= ctx->token && ctx->token < ctx->end){ result = (ctx->token->type == type); } else if (type == CPP_TOKEN_EOF){ result = true; } return(result); } static String config_parser__get_lexeme(Config_Parser *ctx){ String lexeme = {0}; if (ctx->start <= ctx->token && ctx->token < ctx->end){ lexeme = make_string(ctx->data.str + ctx->token->start, ctx->token->size); } return(lexeme); } static Config_Integer config_parser__get_int(Config_Parser *ctx){ Config_Integer config_integer = {0}; String str = config_parser__get_lexeme(ctx); if (match(substr(str, 0, 2), "0x")){ config_integer.is_signed = false; config_integer.uinteger = hexstr_to_int(substr_tail(str, 2)); } else{ config_integer.is_signed = true; config_integer.integer = str_to_int(str); } return(config_integer); } static bool32 config_parser__get_boolean(Config_Parser *ctx){ String str = config_parser__get_lexeme(ctx); return(match(str, "true")); } static bool32 config_parser__recognize_text(Config_Parser *ctx, String text){ bool32 result = false; String lexeme = config_parser__get_lexeme(ctx); if (lexeme.str != 0 && match(lexeme, text)){ result = true; } return(result); } static bool32 config_parser__match_token(Config_Parser *ctx, Cpp_Token_Type type){ bool32 result = config_parser__recognize_token(ctx, type); if (result){ config_parser__advance_to_next(ctx); } return(result); } static bool32 config_parser__match_text(Config_Parser *ctx, String text){ bool32 result = config_parser__recognize_text(ctx, text); if (result){ config_parser__advance_to_next(ctx); } return(result); } static Config *config_parser__config(Config_Parser *ctx); static int32_t *config_parser__version(Config_Parser *ctx); static Config_Assignment *config_parser__assignment(Config_Parser *ctx); static Config_LValue *config_parser__lvalue(Config_Parser *ctx); static Config_RValue *config_parser__rvalue(Config_Parser *ctx); static Config_Compound *config_parser__compound(Config_Parser *ctx); static Config_Compound_Element *config_parser__element(Config_Parser *ctx); static Config* text_data_and_token_array_to_parse_data(Partition *arena, String file_name, String data, Cpp_Token_Array array){ Temp_Memory restore_point = begin_temp_memory(arena); Config_Parser ctx = make_config_parser(arena, file_name, data, array); Config *config = config_parser__config(&ctx); if (config == 0){ end_temp_memory(restore_point); } return(config); } static Config* config_parser__config(Config_Parser *ctx){ int32_t *version = config_parser__version(ctx); Config_Assignment *first = 0; Config_Assignment *last = 0; int32_t count = 0; for (;!config_parser__recognize_token(ctx, CPP_TOKEN_EOF);){ Config_Assignment *assignment = config_parser__assignment(ctx); require(assignment != 0); zdll_push_back(first, last, assignment); count += 1; } Config *config = push_array(ctx->arena, Config, 1); memset(config, 0, sizeof(*config)); config->version = version; config->first = first; config->last = last; config->count = count; return(config); } static int32_t* config_parser__version(Config_Parser *ctx){ require(config_parser__match_text(ctx, make_lit_string("version"))); require(config_parser__match_token(ctx, CPP_TOKEN_PARENTHESE_OPEN)); require(config_parser__recognize_token(ctx, CPP_TOKEN_INTEGER_CONSTANT)); Config_Integer value = config_parser__get_int(ctx); config_parser__advance_to_next(ctx); require(config_parser__match_token(ctx, CPP_TOKEN_PARENTHESE_CLOSE)); int32_t *ptr = push_array(ctx->arena, int32_t, 1); *ptr = value.integer; return(ptr); } static Config_Assignment* config_parser__assignment(Config_Parser *ctx){ Config_LValue *l = config_parser__lvalue(ctx); require(l != 0); require(config_parser__match_token(ctx, CPP_TOKEN_EQ)); Config_RValue *r = config_parser__rvalue(ctx); require(r != 0); require(config_parser__match_token(ctx, CPP_TOKEN_SEMICOLON)); Config_Assignment *assignment = push_array(ctx->arena, Config_Assignment, 1); memset(assignment, 0, sizeof(*assignment)); assignment->l = l; assignment->r = r; return(assignment); } static Config_LValue* config_parser__lvalue(Config_Parser *ctx){ require(config_parser__recognize_token(ctx, CPP_TOKEN_IDENTIFIER)); String identifier = config_parser__get_lexeme(ctx); config_parser__advance_to_next(ctx); int32_t index = 0; if (config_parser__match_token(ctx, CPP_TOKEN_BRACKET_OPEN)){ require(config_parser__recognize_token(ctx, CPP_TOKEN_INTEGER_CONSTANT)); Config_Integer value = config_parser__get_int(ctx); index = value.integer; config_parser__advance_to_next(ctx); require(config_parser__match_token(ctx, CPP_TOKEN_BRACKET_CLOSE)); } Config_LValue *lvalue = push_array(ctx->arena, Config_LValue, 1); memset(lvalue, 0, sizeof(*lvalue)); lvalue->identifier = identifier; lvalue->index = index; return(lvalue); } static Config_RValue* config_parser__rvalue(Config_Parser *ctx){ if (config_parser__recognize_token(ctx, CPP_TOKEN_IDENTIFIER)){ Config_LValue *l = config_parser__lvalue(ctx); require(l != 0); Config_RValue *rvalue = push_array(ctx->arena, Config_RValue, 1); memset(rvalue, 0, sizeof(*rvalue)); rvalue->type = ConfigRValueType_LValue; rvalue->lvalue = l; return(rvalue); } else if (config_parser__recognize_token(ctx, CPP_TOKEN_BRACE_OPEN)){ config_parser__advance_to_next(ctx); Config_Compound *compound = config_parser__compound(ctx); require(compound != 0); Config_RValue *rvalue = push_array(ctx->arena, Config_RValue, 1); memset(rvalue, 0, sizeof(*rvalue)); rvalue->type = ConfigRValueType_Compound; rvalue->compound = compound; return(rvalue); } else if (config_parser__recognize_token(ctx, CPP_TOKEN_BOOLEAN_CONSTANT)){ bool32 b = config_parser__get_boolean(ctx); config_parser__advance_to_next(ctx); Config_RValue *rvalue = push_array(ctx->arena, Config_RValue, 1); memset(rvalue, 0, sizeof(*rvalue)); rvalue->type = ConfigRValueType_Boolean; rvalue->boolean = b; return(rvalue); } else if (config_parser__recognize_token(ctx, CPP_TOKEN_INTEGER_CONSTANT)){ Config_Integer value = config_parser__get_int(ctx); config_parser__advance_to_next(ctx); Config_RValue *rvalue = push_array(ctx->arena, Config_RValue, 1); memset(rvalue, 0, sizeof(*rvalue)); rvalue->type = ConfigRValueType_Integer; if (value.is_signed){ rvalue->integer = value.integer; } else{ rvalue->uinteger = value.uinteger; } return(rvalue); } else if (config_parser__recognize_token(ctx, CPP_TOKEN_STRING_CONSTANT)){ String s = config_parser__get_lexeme(ctx); config_parser__advance_to_next(ctx); char *space = push_array(ctx->arena, char, s.size + 1); push_align(ctx->arena, 8); s = substr(s, 1, s.size - 2); string_interpret_escapes(s, space); Config_RValue *rvalue = push_array(ctx->arena, Config_RValue, 1); memset(rvalue, 0, sizeof(*rvalue)); rvalue->type = ConfigRValueType_String; rvalue->string = make_string_slowly(space); return(rvalue); } else if (config_parser__recognize_token(ctx, CPP_TOKEN_CHARACTER_CONSTANT)){ String s = config_parser__get_lexeme(ctx); config_parser__advance_to_next(ctx); char *space = push_array(ctx->arena, char, s.size + 1); push_align(ctx->arena, 8); s = substr(s, 1, s.size - 2); string_interpret_escapes(s, space); Config_RValue *rvalue = push_array(ctx->arena, Config_RValue, 1); memset(rvalue, 0, sizeof(*rvalue)); rvalue->type = ConfigRValueType_Character; rvalue->character = space[0]; return(rvalue); } return(0); } static Config_Compound* config_parser__compound(Config_Parser *ctx){ Config_Compound_Element *first = 0; Config_Compound_Element *last = 0; int32_t count = 0; Config_Compound_Element *element = config_parser__element(ctx); require(element != 0); zdll_push_back(first, last, element); count += 1; for (;config_parser__match_token(ctx, CPP_TOKEN_COMMA);){ if (config_parser__recognize_token(ctx, CPP_TOKEN_BRACE_CLOSE)){ break; } element = config_parser__element(ctx); require(element != 0); zdll_push_back(first, last, element); count += 1; } require(config_parser__match_token(ctx, CPP_TOKEN_BRACE_CLOSE)); Config_Compound *compound = push_array(ctx->arena, Config_Compound, 1); memset(compound, 0, sizeof(*compound)); compound->first = first; compound->last = last; compound->count = count; return(compound); } static Config_Compound_Element* config_parser__element(Config_Parser *ctx){ Config_Layout layout = {0}; if (config_parser__match_token(ctx, CPP_TOKEN_DOT)){ if (config_parser__recognize_token(ctx, CPP_TOKEN_IDENTIFIER)){ layout.type = ConfigLayoutType_Identifier; layout.identifier = config_parser__get_lexeme(ctx); config_parser__advance_to_next(ctx); } else if (config_parser__recognize_token(ctx, CPP_TOKEN_INTEGER_CONSTANT)){ layout.type = ConfigLayoutType_Integer; Config_Integer value = config_parser__get_int(ctx); layout.integer = value.integer; config_parser__advance_to_next(ctx); } else{ return(0); } require(config_parser__match_token(ctx, CPP_TOKEN_EQ)); } Config_RValue *rvalue = config_parser__rvalue(ctx); require(rvalue != 0); Config_Compound_Element *element = push_array(ctx->arena, Config_Compound_Element, 1); memset(element, 0, sizeof(*element)); element->l = layout; element->r = rvalue; return(element); } //////////////////////////////// static Config_Assignment* config_lookup_assignment(Config *config, String var_name, int32_t subscript){ Config_Assignment *assignment; for (assignment = config->first; assignment != 0; assignment = assignment->next){ Config_LValue *l = assignment->l; if (l != 0 && match(l->identifier, var_name) && l->index == subscript){ break; } } return(assignment); } static bool32 config_var(Config *config, String var_name, int32_t subscript, Config_RValue_Type type, void *out); static bool32 config_evaluate_rvalue(Config *config, Config_Assignment *assignment, Config_RValue *r, Config_RValue_Type type, void *out){ bool32 success = false; if (r != 0 && !assignment->visited){ if (r->type == ConfigRValueType_LValue){ assignment->visited = true; Config_LValue *l = r->lvalue; success = config_var(config, l->identifier, l->index, type, out); assignment->visited = false; } else if (r->type == type){ success = true; switch (type){ case ConfigRValueType_Boolean: { *(bool32*)out = r->boolean; }break; case ConfigRValueType_Integer: { *(int32_t*)out = r->integer; }break; case ConfigRValueType_String: { *(String*)out = r->string; }break; case ConfigRValueType_Character: { *(char*)out = r->character; }break; case ConfigRValueType_Compound: { *(Config_Compound**)out = r->compound; }break; } } } return(success); } static bool32 config_var(Config *config, String var_name, int32_t subscript, Config_RValue_Type type, void *var_out){ Config_Assignment *assignment = config_lookup_assignment(config, var_name, subscript); bool32 success = false; if (assignment != 0){ success = config_evaluate_rvalue(config, assignment, assignment->r, type, var_out); } return(success); } static bool32 config_bool_var(Config *config, String var_name, int32_t subscript, bool32 *var_out){ return(config_var(config, var_name, subscript, ConfigRValueType_Boolean, var_out)); } static bool32 config_bool_var(Config *config, char *var_name, int32_t subscript, bool32 *var_out){ return(config_var(config, make_string_slowly(var_name), subscript, ConfigRValueType_Boolean, var_out)); } static bool32 config_int_var(Config *config, String var_name, int32_t subscript, int32_t *var_out){ return(config_var(config, var_name, subscript, ConfigRValueType_Integer, var_out)); } static bool32 config_int_var(Config *config, char *var_name, int32_t subscript, int32_t *var_out){ return(config_var(config, make_string_slowly(var_name), subscript, ConfigRValueType_Integer, var_out)); } static bool32 config_uint_var(Config *config, String var_name, int32_t subscript, uint32_t *var_out){ return(config_var(config, var_name, subscript, ConfigRValueType_Integer, var_out)); } static bool32 config_uint_var(Config *config, char *var_name, int32_t subscript, uint32_t *var_out){ return(config_var(config, make_string_slowly(var_name), subscript, ConfigRValueType_Integer, var_out)); } static bool32 config_string_var(Config *config, String var_name, int32_t subscript, String *var_out){ return(config_var(config, var_name, subscript, ConfigRValueType_String, var_out)); } static bool32 config_string_var(Config *config, char *var_name, int32_t subscript, String *var_out){ return(config_var(config, make_string_slowly(var_name), subscript, ConfigRValueType_String, var_out)); } static bool32 config_char_var(Config *config, String var_name, int32_t subscript, char *var_out){ return(config_var(config, var_name, subscript, ConfigRValueType_Character, var_out)); } static bool32 config_char_var(Config *config, char *var_name, int32_t subscript, char *var_out){ return(config_var(config, make_string_slowly(var_name), subscript, ConfigRValueType_Character, var_out)); } static bool32 config_compound_var(Config *config, String var_name, int32_t subscript, Config_Compound **var_out){ return(config_var(config, var_name, subscript, ConfigRValueType_Compound, var_out)); } static bool32 config_compound_var(Config *config, char *var_name, int32_t subscript, Config_Compound **var_out){ return(config_var(config, make_string_slowly(var_name), subscript, ConfigRValueType_Compound, var_out)); } static bool32 config_compound_member(Config *config, Config_Compound *compound, String var_name, int32_t index, Config_RValue_Type type, void *var_out){ bool32 success = false; int32_t implicit_index = 0; bool32 implicit_index_is_valid = true; for (Config_Compound_Element *element = compound->first; element != 0; element = element->next, implicit_index += 1){ bool32 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 (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 = {0}; success = config_evaluate_rvalue(config, &dummy_assignment, element->r, type, var_out); } } return(success); } static bool32 config_compound_bool_member(Config *config, Config_Compound *compound, String var_name, int32_t index, bool32 *var_out){ return(config_compound_member(config, compound, var_name, index, ConfigRValueType_Boolean, var_out)); } static bool32 config_compound_bool_member(Config *config, Config_Compound *compound, char *var_name, int32_t index, bool32 *var_out){ return(config_compound_member(config, compound, make_string_slowly(var_name), index, ConfigRValueType_Boolean, var_out)); } static bool32 config_compound_int_member(Config *config, Config_Compound *compound, String var_name, int32_t index, int32_t *var_out){ return(config_compound_member(config, compound, var_name, index, ConfigRValueType_Integer, var_out)); } static bool32 config_compound_int_member(Config *config, Config_Compound *compound, char *var_name, int32_t index, int32_t *var_out){ return(config_compound_member(config, compound, make_string_slowly(var_name), index, ConfigRValueType_Integer, var_out)); } static bool32 config_compound_uint_member(Config *config, Config_Compound *compound, String var_name, int32_t index, uint32_t *var_out){ return(config_compound_member(config, compound, var_name, index, ConfigRValueType_Integer, var_out)); } static bool32 config_compound_uint_member(Config *config, Config_Compound *compound, char *var_name, int32_t index, uint32_t *var_out){ return(config_compound_member(config, compound, make_string_slowly(var_name), index, ConfigRValueType_Integer, var_out)); } static bool32 config_compound_string_member(Config *config, Config_Compound *compound, String var_name, int32_t index, String *var_out){ return(config_compound_member(config, compound, var_name, index, ConfigRValueType_String, var_out)); } static bool32 config_compound_string_member(Config *config, Config_Compound *compound, char *var_name, int32_t index, String *var_out){ return(config_compound_member(config, compound, make_string_slowly(var_name), index, ConfigRValueType_String, var_out)); } static bool32 config_compound_char_member(Config *config, Config_Compound *compound, String var_name, int32_t index, char *var_out){ return(config_compound_member(config, compound, var_name, index, ConfigRValueType_Character, var_out)); } static bool32 config_compound_char_member(Config *config, Config_Compound *compound, char *var_name, int32_t index, char *var_out){ return(config_compound_member(config, compound, make_string_slowly(var_name), index, ConfigRValueType_Character, var_out)); } static bool32 config_compound_compound_member(Config *config, Config_Compound *compound, String var_name, int32_t index, Config_Compound **var_out){ return(config_compound_member(config, compound, var_name, index, ConfigRValueType_Compound, var_out)); } static bool32 config_compound_compound_member(Config *config, Config_Compound *compound, char *var_name, int32_t index, Config_Compound **var_out){ return(config_compound_member(config, compound, make_string_slowly(var_name), index, ConfigRValueType_Compound, var_out)); } //////////////////////////////// static Cpp_Token read_config_token(Cpp_Token_Array array, int32_t *i_ptr){ Cpp_Token token = {0}; int32_t i = *i_ptr; for (; i < array.count; ++i){ Cpp_Token comment_token = array.tokens[i]; if (comment_token.type != CPP_TOKEN_COMMENT){ break; } } if (i < array.count){ token = array.tokens[i]; } *i_ptr = i; return(token); } static Config_Line read_config_line(Cpp_Token_Array array, int32_t *i_ptr, char *text){ Config_Line config_line = {0}; int32_t i = *i_ptr; config_line.id_token = read_config_token(array, &i); int32_t text_index_start = config_line.id_token.start; if (config_line.id_token.type == CPP_TOKEN_IDENTIFIER){ ++i; if (i < array.count){ Cpp_Token token = read_config_token(array, &i); bool32 lvalue_success = true; if (token.type == CPP_TOKEN_BRACKET_OPEN){ lvalue_success = false; ++i; if (i < array.count){ config_line.subscript_token = read_config_token(array, &i); if (config_line.subscript_token.type == CPP_TOKEN_INTEGER_CONSTANT){ ++i; if (i < array.count){ token = read_config_token(array, &i); if (token.type == CPP_TOKEN_BRACKET_CLOSE){ ++i; if (i < array.count){ token = read_config_token(array, &i); lvalue_success = true; } } } } } } if (lvalue_success){ if (token.type == CPP_TOKEN_EQ){ config_line.eq_token = read_config_token(array, &i); ++i; if (i < array.count){ Cpp_Token val_token = read_config_token(array, &i); bool32 rvalue_success = true; if (val_token.type == CPP_TOKEN_BRACE_OPEN){ rvalue_success = false; ++i; if (i < array.count){ config_line.val_array_start = i; bool32 expecting_array_item = 1; for (; i < array.count; ++i){ Cpp_Token array_token = read_config_token(array, &i); if (array_token.size == 0){ break; } if (array_token.type == CPP_TOKEN_BRACE_CLOSE){ config_line.val_array_end = i; rvalue_success = true; break; } else{ if (array_token.type == CPP_TOKEN_COMMA){ if (!expecting_array_item){ expecting_array_item = true; } else{ break; } } else{ if (expecting_array_item){ expecting_array_item = false; ++config_line.val_array_count; } } } } } } if (rvalue_success){ config_line.val_token = val_token; ++i; if (i < array.count){ Cpp_Token semicolon_token = read_config_token(array, &i); if (semicolon_token.type == CPP_TOKEN_SEMICOLON){ config_line.read_success = true; } } } } } } } } if (!config_line.read_success){ Cpp_Token token = {0}; if (i < array.count){ token = array.tokens[i]; } int32_t text_index_current = token.start + token.size; if (text_index_current <= text_index_start){ if (array.count > 0){ token = array.tokens[array.count - 1]; text_index_current = token.start + token.size; } } if (text_index_current > text_index_start){ config_line.error_str = make_string(text + text_index_start, text_index_current - text_index_start); } for (; i < array.count; ++i){ Cpp_Token skip_token = read_config_token(array, &i); if (skip_token.type == CPP_TOKEN_SEMICOLON){ break; } } } *i_ptr = i; return(config_line); } static Config_Item get_config_item(Config_Line line, char *mem, Cpp_Token_Array array){ Config_Item item = {0}; item.line = line; item.array = array; item.mem = mem; if (line.id_token.size != 0){ item.id = make_string(mem + line.id_token.start, line.id_token.size); } if (line.subscript_token.size != 0){ String subscript_str = make_string(mem + line.subscript_token.start,line.subscript_token.size); item.subscript_index = str_to_int_s(subscript_str); item.has_subscript = 1; } return(item); } static bool32 config_var(Config_Item item, char *var_name, int32_t *subscript, uint32_t token_type, void *var_out){ bool32 result = false; bool32 subscript_success = true; if (item.line.val_token.type == token_type){ if ((var_name == 0 && item.id.size == 0) || match(item.id, var_name)){ if (subscript){ if (item.has_subscript){ *subscript = item.subscript_index; } else{ subscript_success = false; } } if (subscript_success){ if (var_out){ switch (token_type){ case CPP_TOKEN_BOOLEAN_CONSTANT: { *(bool32*)var_out = (item.mem[item.line.val_token.start] == 't'); }break; case CPP_TOKEN_INTEGER_CONSTANT: { if (match(make_string(item.mem + item.line.val_token.start, 2), "0x")){ // Hex Integer String val = make_string(item.mem + item.line.val_token.start + 2, item.line.val_token.size - 2); *(uint32_t*)var_out = hexstr_to_int(val); } else{ // Integer String val = make_string(item.mem + item.line.val_token.start, item.line.val_token.size); *(int32_t*)var_out = str_to_int(val); } }break; case CPP_TOKEN_STRING_CONSTANT: { String str = make_string(item.mem + item.line.val_token.start + 1,item.line.val_token.size - 2); copy((String*)var_out, str); }break; case CPP_TOKEN_IDENTIFIER: { String str = make_string(item.mem + item.line.val_token.start,item.line.val_token.size); copy((String*)var_out, str); }break; case CPP_TOKEN_BRACE_OPEN: { Config_Array_Reader *array_reader = (Config_Array_Reader*)var_out; array_reader->array = item.array; array_reader->mem = item.mem; array_reader->i = item.line.val_array_start; array_reader->val_array_end = item.line.val_array_end; array_reader->good = 1; }break; } } result = true; } } } return(result); } static bool32 config_bool_var(Config_Item item, char *var_name, int32_t *subscript, bool32 *var_out){ return(config_var(item, var_name, subscript, CPP_TOKEN_BOOLEAN_CONSTANT, var_out)); } static bool32 config_int_var(Config_Item item, char *var_name, int32_t *subscript, int32_t *var_out){ return(config_var(item, var_name, subscript, CPP_TOKEN_INTEGER_CONSTANT, var_out)); } static bool32 config_uint_var(Config_Item item, char *var_name, int32_t *subscript, uint32_t *var_out){ return(config_var(item, var_name, subscript, CPP_TOKEN_INTEGER_CONSTANT, var_out)); } static bool32 config_string_var(Config_Item item, char *var_name, int32_t *subscript, String *var_out){ return(config_var(item, var_name, subscript, CPP_TOKEN_STRING_CONSTANT, var_out)); } static bool32 config_identifier_var(Config_Item item, char *var_name, int32_t *subscript, String *var_out){ return(config_var(item, var_name, subscript, CPP_TOKEN_IDENTIFIER, var_out)); } static bool32 config_array_var(Config_Item item, char *var_name, int32_t *subscript, Config_Array_Reader *array_reader){ return(config_var(item, var_name, subscript, CPP_TOKEN_BRACE_OPEN, array_reader)); } static bool32 config_array_next_item(Config_Array_Reader *array_reader, Config_Item *item){ bool32 result = false; for (;array_reader->i < array_reader->val_array_end; ++array_reader->i){ Cpp_Token array_token = read_config_token(array_reader->array, &array_reader->i); if (array_token.size == 0 || array_reader->i >= array_reader->val_array_end){ break; } if (array_token.type == CPP_TOKEN_BRACE_CLOSE){ break; } switch (array_token.type){ case CPP_TOKEN_BOOLEAN_CONSTANT: case CPP_TOKEN_INTEGER_CONSTANT: case CPP_TOKEN_STRING_CONSTANT: { Config_Line line = {0}; line.val_token = array_token; line.read_success = 1; *item = get_config_item(line, array_reader->mem, array_reader->array); result = true; ++array_reader->i; goto doublebreak; }break; } } doublebreak:; array_reader->good = result; return(result); } static bool32 config_array_good(Config_Array_Reader *array_reader){ return(array_reader->good); } //////////////////////////////// static void change_mapping(Application_Links *app, String mapping){ bool32 did_remap = false; for (int32_t i = 0; i < named_map_count; ++i){ if (match(mapping, named_maps[i].name)){ did_remap = true; exec_command(app, named_maps[i].remap_command); break; } } if (!did_remap){ print_message(app, literal("Leaving bindings unaltered.\n")); } } //////////////////////////////// static Cpp_Token_Array text_data_to_token_array(Partition *arena, String data){ bool32 success = false; int32_t max_count = (1 << 20)/sizeof(Cpp_Token); Temp_Memory restore_point = begin_temp_memory(arena); Cpp_Token_Array array = {0}; array.tokens = push_array(arena, Cpp_Token, max_count); if (array.tokens != 0){ array.max_count = max_count; Cpp_Keyword_Table kw_table = {0}; Cpp_Keyword_Table pp_table = {0}; if (lexer_keywords_default_init(arena, &kw_table, &pp_table)){ Cpp_Lex_Data S = cpp_lex_data_init(false, kw_table, pp_table); Cpp_Lex_Result result = cpp_lex_step(&S, data.str, data.size + 1, HAS_NULL_TERM, &array, NO_OUT_LIMIT); if (result == LexResult_Finished){ success = true; } } } if (!success){ memset(&array, 0, sizeof(array)); end_temp_memory(restore_point); } return(array); } static Config* text_data_to_parsed_data(Partition *arena, String file_name, String data){ Config *parsed = 0; Temp_Memory restore_point = begin_temp_memory(arena); Cpp_Token_Array array = text_data_to_token_array(arena, data); if (array.tokens != 0){ parsed = text_data_and_token_array_to_parse_data(arena, file_name, data, array); if (parsed == 0){ end_temp_memory(restore_point); } } return(parsed); } //////////////////////////////// static void config_init_default(Config_Data *config){ config->default_wrap_width = 672; config->default_min_base_width = 550; config->enable_code_wrapping = true; config->automatically_adjust_wrapping = true; config->automatically_indent_text_on_save = true; config->automatically_save_changes_on_build = true; config->automatically_load_project = false; config->lalt_lctrl_is_altgr = false; config->default_theme_name = make_fixed_width_string(config->default_theme_name_space); copy(&config->default_theme_name, "4coder"); config->default_font_name = make_fixed_width_string(config->default_font_name_space); copy(&config->default_font_name, ""); config->user_name = make_fixed_width_string(config->user_name_space); copy(&config->user_name, ""); config->default_compiler_bat = make_fixed_width_string(config->default_compiler_bat_space); copy(&config->default_compiler_bat, "cl"); config->default_flags_bat = make_fixed_width_string(config->default_flags_bat_space); copy(&config->default_flags_bat, ""); config->default_compiler_sh = make_fixed_width_string(config->default_compiler_sh_space); copy(&config->default_compiler_sh, "g++"); config->default_flags_sh = make_fixed_width_string(config->default_flags_sh_space); copy(&config->default_flags_sh, ""); config->current_mapping = make_fixed_width_string(config->current_mapping_space); copy(&config->current_mapping, ""); memset(&config->code_exts, 0, sizeof(config->code_exts)); } static void config_parse__data(Partition *scratch, String file_name, String data, Config_Data *config){ config_init_default(config); bool32 success = false; Temp_Memory temp = begin_temp_memory(scratch); Config *parsed = text_data_to_parsed_data(scratch, file_name, data); if (parsed != 0){ success = true; config_bool_var(parsed, "enable_code_wrapping", 0, &config->enable_code_wrapping); config_bool_var(parsed, "automatically_adjust_wrapping", 0, &config->automatically_adjust_wrapping); config_bool_var(parsed, "automatically_indent_text_on_save", 0, &config->automatically_indent_text_on_save); config_bool_var(parsed, "automatically_save_changes_on_build", 0, &config->automatically_save_changes_on_build); config_int_var(parsed, "default_wrap_width", 0, &config->default_wrap_width); config_int_var(parsed, "default_min_base_width", 0, &config->default_min_base_width); config_string_var(parsed, "default_theme_name", 0, &config->default_theme_name); config_string_var(parsed, "default_font_name", 0, &config->default_font_name); config_string_var(parsed, "user_name", 0, &config->user_name); config_string_var(parsed, "default_compiler_bat", 0, &config->default_compiler_bat); config_string_var(parsed, "default_flags_bat", 0, &config->default_flags_bat); config_string_var(parsed, "default_compiler_sh", 0, &config->default_compiler_sh); config_string_var(parsed, "default_flags_sh", 0, &config->default_flags_sh); config_string_var(parsed, "mapping", 0, &config->current_mapping); char space[512]; String str = make_fixed_width_string(space); if (config_string_var(parsed, "treat_as_code", 0, &str)){ parse_extension_line_to_extension_list(str, &config->code_exts); } config_bool_var(parsed, "automatically_load_project", 0, &config->automatically_load_project); config_bool_var(parsed, "lalt_lctrl_is_altgr", 0, &config->lalt_lctrl_is_altgr); } end_temp_memory(temp); if (!success){ config_init_default(config); } } static void config_parse__file_handle(Partition *scratch, String file_name, FILE *file, Config_Data *config){ Temp_Memory temp = begin_temp_memory(scratch); String data = dump_file_handle(scratch, file); if (data.str != 0){ config_parse__data(scratch, file_name, data, config); } else{ config_init_default(config); } end_temp_memory(temp); } static void config_parse__file_name(Application_Links *app, Partition *scratch, char *file_name, Config_Data *config){ bool32 success = false; FILE *file = open_file_try_current_path_then_binary_path(app, file_name); if (file != 0){ Temp_Memory temp = begin_temp_memory(scratch); String data = dump_file_handle(scratch, file); fclose(file); if (data.str != 0){ config_parse__data(scratch, make_string_slowly(file_name), data, config); } end_temp_memory(temp); } if (!success){ config_init_default(config); } } static void init_theme_zero(Theme *theme){ for (int32_t i = 0; i < Stag_COUNT; ++i){ theme->colors[i] = 0; } } static bool32 theme_parse__data(Partition *scratch, String file_name, String data, Theme_Data *theme){ theme->name = make_fixed_width_string(theme->space); copy(&theme->name, "unnamed"); init_theme_zero(&theme->theme); bool32 success = false; Temp_Memory temp = begin_temp_memory(scratch); Config *parsed = text_data_to_parsed_data(scratch, file_name, data); if (parsed != 0){ success = true; String theme_name = {0}; if (config_string_var(parsed, "name", 0, &theme_name)){ copy(&theme->name, theme_name); } for (int32_t i = 0; i < Stag_COUNT; ++i){ char *name = style_tag_names[i]; uint32_t color = 0; if (!config_uint_var(parsed, name, 0, &color)){ color = 0xFFFF00FF; } theme->theme.colors[i] = color; } } end_temp_memory(temp); return(success); } static bool32 theme_parse__file_handle(Partition *scratch, String file_name, FILE *file, Theme_Data *theme){ Temp_Memory temp = begin_temp_memory(scratch); String data = dump_file_handle(scratch, file); bool32 success = false; if (data.str != 0){ success = theme_parse__data(scratch, file_name, data, theme); } end_temp_memory(temp); return(success); } static bool32 theme_parse__file_name(Application_Links *app, Partition *scratch, char *file_name, Theme_Data *theme){ bool32 success = false; FILE *file = open_file_try_current_path_then_binary_path(app, file_name); if (file != 0){ Temp_Memory temp = begin_temp_memory(scratch); String data = dump_file_handle(scratch, file); fclose(file); success = theme_parse__data(scratch, make_string_slowly(file_name), data, theme); end_temp_memory(temp); } if (!success){ char space[256]; String str = make_fixed_width_string(space); append(&str, "Did not find "); append(&str, file_name); append(&str, ", color scheme not loaded"); print_message(app, str.str, str.size); } return(success); } //////////////////////////////// static void load_config_and_apply(Application_Links *app, Partition *scratch, Config_Data *config){ config_parse__file_name(app, scratch, "config.4coder", config); change_mapping(app, config->current_mapping); adjust_all_buffer_wrap_widths(app, config->default_wrap_width, config->default_min_base_width); global_set_setting(app, GlobalSetting_LAltLCtrlIsAltGr, config->lalt_lctrl_is_altgr); } static void load_theme_file_into_live_set(Application_Links *app, Partition *scratch, char *file_name){ Theme_Data theme = {0}; theme_parse__file_name(app, scratch, file_name, &theme); create_theme(app, &theme.theme, theme.name.str, theme.name.size); } static void load_folder_of_themes_into_live_set(Application_Links *app, Partition *scratch, char *folder_name){ char path_space[512]; String path = make_fixed_width_string(path_space); path.size = get_4ed_path(app, path_space, sizeof(path_space)); append(&path, folder_name); if (path.size < path.memory_size){ File_List list = get_file_list(app, path.str, path.size); for (uint32_t i = 0; i < list.count; ++i){ File_Info *info = &list.infos[i]; if (info->folder) continue; String info_file_name = make_string(info->filename, info->filename_len); if (!match(file_extension(info_file_name), "4coder")) continue; char file_name_space[512]; String file_name = make_fixed_width_string(file_name_space); copy(&file_name, path); append(&file_name, "/"); append(&file_name, info_file_name); if (terminate_with_null(&file_name)){ load_theme_file_into_live_set(app, scratch, file_name.str); } } free_file_list(app, list); } } // BOTTOM