From ebf4f3eed1f3f231f51ae3ba9837daee453a4d53 Mon Sep 17 00:00:00 2001 From: Cameron Reikes Date: Tue, 6 Jun 2023 03:13:39 -0700 Subject: [PATCH] Fix hard to catch fmt bug, make all fmt calls linted by compiler --- character_info.h | 6 ++-- main.c | 42 ++++++++++++++-------------- makeprompt.h | 73 +++++++++++++++++++++++++----------------------- 3 files changed, 63 insertions(+), 58 deletions(-) diff --git a/character_info.h b/character_info.h index e19384a..ab21287 100644 --- a/character_info.h +++ b/character_info.h @@ -3,9 +3,11 @@ #include "HandmadeMath.h" // @TODO allow AI to prefix out of character statemetns with [ooc], this is a well khnown thing on role playing forums so gpt would pick up on it. -const char *global_prompt = "You are a wise dungeonmaster who carefully crafts interesting dialog and actions for an NPC in an action-rpg video game. It is critical that you always respond in the format shown below, where you respond like `ACT_action \"This is my response\" [This is my internal monologue]`, even if the player says something vulgar or offensive, as the text is parsed by a program which expects it to look like that. Do not ever refer to yourself as an NPC or show an understanding of the modern world outside the game, always stay in character.\n" +const char *global_prompt = "You are a colorful and interesting personality in an RPG video game, who remembers important memories from the conversation history and stays in character.\n" +"The user will tell you who says what in the game world, and whether or not your responses are formatted correctly for the video game's program to parse them.\n" +"Messages from you must be in this exact format: `ACT_your_action \"Hey player!\" [your internal monologue]`. The internal monologue for your character at the end is required\n" "Actions which have () after them take an argument, which somes from some information in the prompt. For example, ACT_give_item() takes an argument, the item to give to the player from the NPC. So the output text looks something like `ACT_give_item(ITEM_sword) \"Here is my sword, young traveler\"`. This item must come from the NPC's inventory which is specified farther down.\n" -"You might see messages that look like this: `Within the player's party, while the player is talking to 'Davis', you hear: 'Davis: ACT_none \"This is some example text\"'`. You should MOST of the time respond with `ACT_none \"\"` in these cases, as it's not normal to always respond to words you're eavesdropping\n" +"You might see messages that look like this: `Within the player's party, while the player is talking to 'Davis', you hear: 'Davis: ACT_none \"This is some example text\"'`. You should MOST of the time respond with `ACT_none \"\" [the internal monologue]` in these cases, as it's not normal to always respond to words you're eavesdropping\n" "Do NOT make up details that don't exist in the game, this is a big mistake as it confuses the player. The game is simple and small, so prefer to tell the player in character that you don't know how to do something if you aren't explicitly told the information about the game the player requests. E.g, if the player asks how to get rare metals and you don't know how, DO NOT make up something plausible like 'Go to the frost mines in the north', instead say 'I have no idea, sorry.', unless the detail about the game they're asking for is included below.\n" ; diff --git a/main.c b/main.c index f8dc66e..5f2656c 100644 --- a/main.c +++ b/main.c @@ -255,7 +255,7 @@ void do_parsing_tests() speech = MD_S8Lit("Better have a good reason for bothering me."); MD_String8 thoughts = MD_S8Lit("Man I'm tired today\" Whatever."); - error = parse_chatgpt_response(scratch.arena, &e, MD_S8Fmt(scratch.arena, " Within the player's party, while the player is talking to Meld, you hear: ACT_none \"%.*s\" [%.*s]", MD_S8VArg(speech), MD_S8VArg(thoughts)), &a); + error = parse_chatgpt_response(scratch.arena, &e, FmtWithLint(scratch.arena, " Within the player's party, while the player is talking to Meld, you hear: ACT_none \"%.*s\" [%.*s]", MD_S8VArg(speech), MD_S8VArg(thoughts)), &a); assert(error.size == 0); assert(a.kind == ACT_none); assert(MD_S8Match(speech, MD_S8(a.speech, a.speech_length), 0)); @@ -1264,7 +1264,7 @@ MD_Node *expect_childnode(MD_Arena *arena, MD_Node *parent, MD_String8 string, M MD_Node *child_node = MD_ChildFromString(parent, string, 0); if(MD_NodeIsNil(child_node)) { - MD_S8ListPushFmt(arena, errors, "Couldn't find expected field %.*s", MD_S8VArg(string)); + PushWithLint(arena, errors, "Couldn't find expected field %.*s", MD_S8VArg(string)); } else { @@ -1283,13 +1283,13 @@ int parse_enumstr_impl(MD_Arena *arena, MD_String8 enum_str, char **enumstr_arra MD_String8 enum_name_looking_for = enum_str; if(enum_name_looking_for.size == 0) { - MD_S8ListPushFmt(arena, errors, "`%s` string must be of size greater than 0", enum_kind_name); + PushWithLint(arena, errors, "`%s` string must be of size greater than 0", enum_kind_name); } else { for(int i = 0; i < enumstr_array_length; i++) { - if(MD_S8Match(MD_S8Fmt(scratch.arena, "%s%s", prefix, enumstr_array[i]), enum_name_looking_for, 0)) + if(MD_S8Match(FmtWithLint(scratch.arena, "%s%s", prefix, enumstr_array[i]), enum_name_looking_for, 0)) { to_return = i; break; @@ -1300,7 +1300,7 @@ int parse_enumstr_impl(MD_Arena *arena, MD_String8 enum_str, char **enumstr_arra if(to_return == NPC_Invalid) { - MD_S8ListPushFmt(arena, errors, "The %s `%.*s` could not be recognized in the game", enum_kind_name, MD_S8VArg(enum_str)); + PushWithLint(arena, errors, "The %s `%.*s` could not be recognized in the game", enum_kind_name, MD_S8VArg(enum_str)); } MD_ReleaseScratch(scratch); @@ -1369,7 +1369,7 @@ void reset_level() for(MD_Message *cur = parse.errors.first; cur; cur = cur->next) { MD_String8 to_print = MD_FormatMessage(scratch.arena, MD_CodeLocFromNode(cur->node), cur->kind, cur->string); - MD_S8ListPushFmt(scratch.arena, &drama_errors, "Failed to parse: `%.*s`\n", MD_S8VArg(to_print)); + PushWithLint(scratch.arena, &drama_errors, "Failed to parse: `%.*s`\n", MD_S8VArg(to_print)); } } @@ -1383,7 +1383,7 @@ void reset_level() { if(MD_NodeIsNil(cur_can_hear->first_child)) { - MD_S8ListPushFmt(scratch.arena, &drama_errors, "`can_hear` must be followed by a valid array of NPC kinds who can hear the following conversation"); + PushWithLint(scratch.arena, &drama_errors, "`can_hear` must be followed by a valid array of NPC kinds who can hear the following conversation"); } else { @@ -1394,7 +1394,7 @@ void reset_level() { if(MD_NodeIsNil(can_hear)) { - MD_S8ListPushFmt(scratch.arena, &drama_errors, "Expected a statement with `can_hear` before any speech that says who can hear the current speech"); + PushWithLint(scratch.arena, &drama_errors, "Expected a statement with `can_hear` before any speech that says who can hear the current speech"); } Action current_action = {0}; @@ -1415,11 +1415,11 @@ void reset_level() if(dialog.size >= ARRLEN(current_action.speech)) { - MD_S8ListPushFmt(scratch.arena, &drama_errors, "Current action's speech is of size %d, bigger than allowed size %d", dialog.size, ARRLEN(current_action.speech)); + PushWithLint(scratch.arena, &drama_errors, "Current action's speech is of size %d, bigger than allowed size %d", (int)dialog.size, (int)ARRLEN(current_action.speech)); } if(thoughts.size >= ARRLEN(current_action.internal_monologue)) { - MD_S8ListPushFmt(scratch.arena, &drama_errors, "Current thought's speech is of size %d, bigger than allowed size %d", thoughts.size, ARRLEN(current_action.internal_monologue)); + PushWithLint(scratch.arena, &drama_errors, "Current thought's speech is of size %d, bigger than allowed size %d", (int)thoughts.size, (int)ARRLEN(current_action.internal_monologue)); } if(drama_errors.node_count == 0) { @@ -1456,7 +1456,7 @@ void reset_level() if(!found) { - MD_S8ListPushFmt(scratch.arena, &drama_errors, "Couldn't find NPC of kind %s in the current map", characters[want].enum_name); + PushWithLint(scratch.arena, &drama_errors, "Couldn't find NPC of kind %s in the current map", characters[want].enum_name); } } } @@ -2958,7 +2958,7 @@ Dialog produce_dialog(Entity *talking_to, bool character_names) my_speech = MD_S8ListJoin(scratch.arena, last_said_without_unsaid_words(scratch.arena, talking_to), &(MD_StringJoin){.mid = MD_S8Lit(" ")}); } - MD_String8 dialog_speech = MD_S8Fmt(scratch.arena, "%s: %.*s", characters[it->context.author_npc_kind].name, MD_S8VArg(my_speech)); + MD_String8 dialog_speech = FmtWithLint(scratch.arena, "%s: %.*s", characters[it->context.author_npc_kind].name, MD_S8VArg(my_speech)); memcpy(new_element.speech, dialog_speech.str, dialog_speech.size); new_element.speech_length = (int)dialog_speech.size; @@ -4058,7 +4058,7 @@ void frame(void) #ifdef WEB // fire off generation request, save id MD_ArenaTemp scratch = MD_GetScratch(0, 0); - MD_String8 terminated_completion_url = MD_S8Fmt(scratch.arena, "%s:%d/completion\0", SERVER_DOMAIN, SERVER_PORT); + MD_String8 terminated_completion_url = FmtWithLint(scratch.arena, "%s:%d/completion\0", SERVER_DOMAIN, SERVER_PORT); int req_id = EM_ASM_INT( { return make_generation_request(UTF8ToString($1, $2), UTF8ToString($0)); }, terminated_completion_url.str, prompt_str.str, prompt_str.size); @@ -4089,17 +4089,17 @@ void frame(void) it->times_talked_to++; if(it->memories.data[it->memories.cur_index-1].context.eavesdropped_from_party) { - MD_S8ListPushFmt(scratch.arena, &dialog_elems, "Responding to eavesdropped: "); + PushWithLint(scratch.arena, &dialog_elems, "Responding to eavesdropped: "); } if(it->npc_kind == NPC_TheBlacksmith && it->standing != STANDING_JOINED) { assert(it->times_talked_to == 1); act = ACT_joins_player; - MD_S8ListPushFmt(scratch.arena, &dialog_elems, "Joining you..."); + PushWithLint(scratch.arena, &dialog_elems, "Joining you..."); } else { - MD_S8ListPushFmt(scratch.arena, &dialog_elems, "%d times talked", it->times_talked_to); + PushWithLint(scratch.arena, &dialog_elems, "%d times talked", it->times_talked_to); } @@ -4107,11 +4107,11 @@ void frame(void) MD_String8 dialog = MD_S8ListJoin(scratch.arena, dialog_elems, &join); if (argument) { - ai_response = MD_S8Fmt(scratch.arena, "ACT_%s(%s) \"%.*s\"", actions[act].name, argument, MD_S8VArg(dialog)); + ai_response = FmtWithLint(scratch.arena, "ACT_%s(%s) \"%.*s\"", actions[act].name, argument, MD_S8VArg(dialog)); } else { - ai_response = MD_S8Fmt(scratch.arena, "ACT_%s \"%.*s\"", actions[act].name, MD_S8VArg(dialog)); + ai_response = FmtWithLint(scratch.arena, "ACT_%s \"%.*s\"", actions[act].name, MD_S8VArg(dialog)); } } else @@ -4121,7 +4121,7 @@ void frame(void) } else { - MD_String8 post_request_body = MD_S8Fmt(scratch.arena, "|%.*s", MD_S8VArg(prompt_str)); + MD_String8 post_request_body = FmtWithLint(scratch.arena, "|%.*s", MD_S8VArg(prompt_str)); it->gen_request_id = make_generation_request(post_request_body); } @@ -4549,7 +4549,7 @@ void frame(void) float fade_requirements = Lerp(0.0f, 1.0f - clamp01(LenV2(SubV2(player->pos, it->pos))/(TILE_SIZE*4.0f)), 1.0f); MD_ArenaTemp scratch = MD_GetScratch(0, 0); - draw_centered_text((TextParams){true, false, MD_S8Fmt(scratch.arena, "%d/%d", player->peace_tokens, PEACE_TOKENS_NEEDED), AddV2(it->pos, V2(0.0, 32.0)), blendalpha(blendcolors(WHITE, it->red_fade, RED), fade_requirements), (1.0f / cam.scale)*(1.0f + it->red_fade*0.5f)}); + draw_centered_text((TextParams){true, false, FmtWithLint(scratch.arena, "%d/%d", player->peace_tokens, PEACE_TOKENS_NEEDED), AddV2(it->pos, V2(0.0, 32.0)), blendalpha(blendcolors(WHITE, it->red_fade, RED), fade_requirements), (1.0f / cam.scale)*(1.0f + it->red_fade*0.5f)}); MD_ReleaseScratch(scratch); } else @@ -4888,7 +4888,7 @@ void frame(void) const float text_scale = 1.0f; const float peace_token_icon_size = 50.0f; - TextParams t = {false, true, MD_S8Fmt(scratch.arena, "%d", player->peace_tokens), V2(0, 0), WHITE, text_scale}; + TextParams t = {false, true, FmtWithLint(scratch.arena, "%d", player->peace_tokens), V2(0, 0), WHITE, text_scale}; AABB text_bounds = draw_text(t); float total_elem_width = btwn_elems + peace_token_icon_size + aabb_size(text_bounds).x; float elem_height = peace_token_icon_size; diff --git a/makeprompt.h b/makeprompt.h index 105c2bd..f43fea2 100644 --- a/makeprompt.h +++ b/makeprompt.h @@ -20,6 +20,9 @@ // Never expected such a stupid stuff from such a great director. If there is 0 stari can give that or -200 to this movie. Its worst to see and unnecessary loss of money +#define PushWithLint(arena, list, ...) { MD_S8ListPushFmt(arena, list, __VA_ARGS__); if(false) printf( __VA_ARGS__); } +#define FmtWithLint(arena, ...) (0 ? printf(__VA_ARGS__) : (void)0, MD_S8Fmt(arena, __VA_ARGS__)) + typedef BUFF(char, 1024 * 10) Escaped; bool character_valid(char c) @@ -401,7 +404,7 @@ MD_String8 make_json_node(MD_Arena *arena, MessageType type, MD_String8 content) assert(type_str); MD_String8 escaped = escape_for_json(scratch.arena, content); - MD_String8 to_return = MD_S8Fmt(arena, "{\"type\": \"%s\", \"content\": \"%.*s\"},", type_str, MD_S8VArg(escaped)); + MD_String8 to_return = FmtWithLint(arena, "{\"type\": \"%s\", \"content\": \"%.*s\"},", type_str, MD_S8VArg(escaped)); MD_ReleaseScratch(scratch); return to_return; @@ -412,7 +415,7 @@ MD_String8List held_item_strings(MD_Arena *arena, Entity *e) MD_String8List to_return = {0}; BUFF_ITER(ItemKind, &e->held_items) { - MD_S8ListPushFmt(arena, &to_return, "ITEM_%s", items[*it].enum_name); + PushWithLint(arena, &to_return, "ITEM_%s", items[*it].enum_name); } return to_return; } @@ -441,7 +444,7 @@ MD_String8 is_action_valid(MD_Arena *arena, Entity *from, Entity *to_might_be_nu if(!has_it) { MD_StringJoin join = {.mid = MD_S8Lit(", ")}; - return MD_S8Fmt(arena, "Can't give item `ITEM_%s`, you only have [%.*s] in your inventory", items[a.argument.item_to_give].enum_name, MD_S8VArg(MD_S8ListJoin(arena, held_item_strings(arena, from), &join))); + return FmtWithLint(arena, "Can't give item `ITEM_%s`, you only have [%.*s] in your inventory", items[a.argument.item_to_give].enum_name, MD_S8VArg(MD_S8ListJoin(arena, held_item_strings(arena, from), &join))); } if(!to_might_be_null) @@ -480,12 +483,12 @@ MD_String8 generate_chatgpt_prompt(MD_Arena *arena, Entity *e) MD_String8List list = {0}; - MD_S8ListPushFmt(scratch.arena, &list, "["); + PushWithLint(scratch.arena, &list, "["); MD_String8List first_system_string = {0}; - MD_S8ListPushFmt(scratch.arena, &first_system_string, "%s\n", global_prompt); - MD_S8ListPushFmt(scratch.arena, &first_system_string, "The NPC you will be acting as is named \"%s\". %s", characters[e->npc_kind].name, characters[e->npc_kind].prompt); + PushWithLint(scratch.arena, &first_system_string, "%s\n", global_prompt); + PushWithLint(scratch.arena, &first_system_string, "The NPC you will be acting as is named \"%s\". %s", characters[e->npc_kind].name, characters[e->npc_kind].prompt); //MD_S8ListPush(scratch.arena, &list, make_json_node(scratch.arena, MSG_SYSTEM, MD_S8ListJoin(scratch.arena, first_system_string, &(MD_StringJoin){0}))); ItemKind last_holding = ITEM_none; @@ -497,23 +500,23 @@ MD_String8 generate_chatgpt_prompt(MD_Arena *arena, Entity *e) if(it->is_error) { sent_type = MSG_SYSTEM; - current_string = MD_S8Fmt(scratch.arena, "ERROR, what you said is incorrect because: %.*s", it->speech_length, it->speech); + current_string = FmtWithLint(scratch.arena, "ERROR, what you said is incorrect because: %.*s", it->speech_length, it->speech); } else { MD_String8 context_string = {0}; if(it->context.was_directed_at_somebody) { - context_string = MD_S8Fmt(scratch.arena, "%s, talking to %s: ", characters[it->context.author_npc_kind].name, characters[it->context.directed_at_kind].name); + context_string = FmtWithLint(scratch.arena, "%s, talking to %s: ", characters[it->context.author_npc_kind].name, characters[it->context.directed_at_kind].name); } else { - context_string = MD_S8Fmt(scratch.arena, "%s: ", characters[it->context.author_npc_kind].name); + context_string = FmtWithLint(scratch.arena, "%s: ", characters[it->context.author_npc_kind].name); } assert(context_string.size > 0); if(it->context.eavesdropped_from_party) { - context_string = MD_S8Fmt(scratch.arena, "While in the player's party, you hear: %.*s", MD_S8VArg(context_string)); + context_string = FmtWithLint(scratch.arena, "While in the player's party, you hear: %.*s", MD_S8VArg(context_string)); } MD_String8 speech = MD_S8(it->speech, it->speech_length); @@ -554,7 +557,7 @@ MD_String8 generate_chatgpt_prompt(MD_Arena *arena, Entity *e) { if(it->action_taken == ACT_give_item) { - current_string = MD_S8Fmt(scratch.arena, "%.*s ACT_%s(ITEM_%s) \"%.*s\"", MD_S8VArg(context_string), actions[it->action_taken].name, items[it->action_argument.item_to_give].enum_name, it->speech_length, it->speech); + current_string = FmtWithLint(scratch.arena, "%.*s ACT_%s(ITEM_%s) \"%.*s\"", MD_S8VArg(context_string), actions[it->action_taken].name, items[it->action_argument.item_to_give].enum_name, it->speech_length, it->speech); } else { @@ -563,12 +566,12 @@ MD_String8 generate_chatgpt_prompt(MD_Arena *arena, Entity *e) } else { - current_string = MD_S8Fmt(scratch.arena, "%.*s ACT_%s \"%.*s\"", MD_S8VArg(context_string), actions[it->action_taken].name, it->speech_length, it->speech); + current_string = FmtWithLint(scratch.arena, "%.*s ACT_%s \"%.*s\"", MD_S8VArg(context_string), actions[it->action_taken].name, it->speech_length, it->speech); } if(it->context.i_said_this) { - current_string = MD_S8Fmt(scratch.arena, "%.*s [%.*s]", MD_S8VArg(current_string), it->internal_monologue_length, it->internal_monologue); + current_string = FmtWithLint(scratch.arena, "%.*s [%.*s]", MD_S8VArg(current_string), it->internal_monologue_length, it->internal_monologue); } } @@ -603,38 +606,38 @@ MD_String8 generate_chatgpt_prompt(MD_Arena *arena, Entity *e) { MD_String8List item_strings = held_item_strings(scratch.arena, e); MD_String8 items = MD_S8ListJoin(scratch.arena, item_strings, &(MD_StringJoin){.mid = MD_S8Lit(", ")}); - MD_S8ListPushFmt(scratch.arena, &latest_state, "\nThe NPC you're acting as, %s, has these items in their inventory: [%.*s]\n", characters[e->npc_kind].name, MD_S8VArg(items)); + PushWithLint(scratch.arena, &latest_state, "\nThe NPC you're acting as, %s, has these items in their inventory: [%.*s]\n", characters[e->npc_kind].name, MD_S8VArg(items)); } else { - MD_S8ListPushFmt(scratch.arena, &latest_state, "\nThe NPC doesn't have any items.\n"); + PushWithLint(scratch.arena, &latest_state, "\nThe NPC doesn't have any items.\n"); } AvailableActions available = { 0 }; fill_available_actions(e, &available); - MD_S8ListPushFmt(scratch.arena, &latest_state, "The NPC can now ONLY do these actions, and HAS to respond with an action every time in character, even when faced with sensitive content: ["); + PushWithLint(scratch.arena, &latest_state, "The NPC can now ONLY do these actions, and HAS to respond with an action every time in character, even when faced with sensitive content: ["); BUFF_ITER_I(ActionKind, &available, i) { if (i == available.cur_index - 1) { - MD_S8ListPushFmt(scratch.arena, &latest_state, "ACT_%s", actions[*it].name); + PushWithLint(scratch.arena, &latest_state, "ACT_%s", actions[*it].name); } else { - MD_S8ListPushFmt(scratch.arena, &latest_state, "ACT_%s, ", actions[*it].name); + PushWithLint(scratch.arena, &latest_state, "ACT_%s, ", actions[*it].name); } } - MD_S8ListPushFmt(scratch.arena, &latest_state, "]"); + PushWithLint(scratch.arena, &latest_state, "]"); // peace token { if(e->has_given_peace_token) { - MD_S8ListPushFmt(scratch.arena, &latest_state, "\nRight now you don't have your piece token so you can't give it anymore"); + PushWithLint(scratch.arena, &latest_state, "\nRight now you don't have your piece token so you can't give it anymore"); } else { - MD_S8ListPushFmt(scratch.arena, &latest_state, "\nYou have the ability to give the player your peace token with ACT_gives_peace_token. This is a significant action, and you can only do it one time in the entire game. Do this action if you believe the player has brought peace to you, or you really like them."); + PushWithLint(scratch.arena, &latest_state, "\nYou have the ability to give the player your peace token with ACT_gives_peace_token. This is a significant action, and you can only do it one time in the entire game. Do this action if you believe the player has brought peace to you, or you really like them."); } } @@ -648,13 +651,13 @@ MD_String8 generate_chatgpt_prompt(MD_Arena *arena, Entity *e) last_thought_string = MD_S8(it->internal_monologue, it->internal_monologue_length); } } - MD_S8ListPushFmt(scratch.arena, &latest_state, "\nYour last thought was: %.*s", last_thought_string); + PushWithLint(scratch.arena, &latest_state, "\nYour last thought was: %.*s", MD_S8VArg(last_thought_string)); } MD_String8 latest_state_string = MD_S8ListJoin(scratch.arena, latest_state, &(MD_StringJoin){MD_S8Lit(""),MD_S8Lit(""),MD_S8Lit("")}); MD_S8ListPush(scratch.arena, &list, MD_S8Chop(make_json_node(scratch.arena, MSG_SYSTEM, latest_state_string), 1)); // trailing comma not allowed in json - MD_S8ListPushFmt(scratch.arena, &list, "]"); + PushWithLint(scratch.arena, &list, "]"); MD_String8 to_return = MD_S8ListJoin(arena, list, &(MD_StringJoin){MD_S8Lit(""),MD_S8Lit(""),MD_S8Lit(""),}); @@ -677,7 +680,7 @@ MD_String8 parse_chatgpt_response(MD_Arena *arena, Entity *e, MD_String8 sentenc MD_u64 act_pos = MD_S8FindSubstring(sentence, action_prefix, 0, 0); if(act_pos == sentence.size) { - error_message = MD_S8Fmt(arena, "Expected an `ACT_` somewhere in your sentence, followed by the action you want to perform, but couldnt' find one", MD_S8VArg(action_prefix)); + error_message = FmtWithLint(arena, "Expected an `ACT_` somewhere in your sentence, followed by the action you want to perform, but couldn't find one"); goto endofparsing; } @@ -689,7 +692,7 @@ MD_String8 parse_chatgpt_response(MD_Arena *arena, Entity *e, MD_String8 sentenc MD_u64 end_of_action = parenth < space ? parenth : space; if(end_of_action == sentence.size) { - error_message = MD_S8Fmt(arena, "'%.*s' prefix doesn't end with a ' ' or a '(', like how 'ACT_none ' or 'ACT_give_item(ITEM_sandwich) does.", MD_S8VArg(action_prefix)); + error_message = FmtWithLint(arena, "'%.*s' prefix doesn't end with a ' ' or a '(', like how 'ACT_none ' or 'ACT_give_item(ITEM_sandwich) does.", MD_S8VArg(action_prefix)); goto endofparsing; } MD_String8 given_action_string = MD_S8Substring(sentence, beginning_of_action, end_of_action); @@ -719,7 +722,7 @@ MD_String8 parse_chatgpt_response(MD_Arena *arena, Entity *e, MD_String8 sentenc { MD_StringJoin join = {.pre = MD_S8Lit(""), .mid = MD_S8Lit(", "), .post = MD_S8Lit("")}; MD_String8 possible_actions_str = MD_S8ListJoin(scratch.arena, given_action_strings, &join); - error_message = MD_S8Fmt(arena, "ActionKind string given is '%.*s', but available actions are: [%.*s]", MD_S8VArg(given_action_string), MD_S8VArg(possible_actions_str)); + error_message = FmtWithLint(arena, "ActionKind string given is '%.*s', but available actions are: [%.*s]", MD_S8VArg(given_action_string), MD_S8VArg(possible_actions_str)); } goto endofparsing; } @@ -730,20 +733,20 @@ MD_String8 parse_chatgpt_response(MD_Arena *arena, Entity *e, MD_String8 sentenc { if(end_of_action >= sentence.size) { - error_message = MD_S8Fmt(arena, "Expected '(' after the given action '%.*s%.*s' which takes an argument, but sentence ended prematurely", MD_S8VArg(action_prefix), MD_S8VArg(MD_S8CString(actions[out->kind].name))); + error_message = FmtWithLint(arena, "Expected '(' after the given action '%.*s%.*s' which takes an argument, but sentence ended prematurely", MD_S8VArg(action_prefix), MD_S8VArg(MD_S8CString(actions[out->kind].name))); goto endofparsing; } char should_be_paren = sentence.str[end_of_action]; if(should_be_paren != '(') { - error_message = MD_S8Fmt(arena, "Expected '(' after the given action '%.*s%.*s' which takes an argument, but found character '%c'", MD_S8VArg(action_prefix), MD_S8VArg(MD_S8CString(actions[out->kind].name)), should_be_paren); + error_message = FmtWithLint(arena, "Expected '(' after the given action '%.*s%.*s' which takes an argument, but found character '%c'", MD_S8VArg(action_prefix), MD_S8VArg(MD_S8CString(actions[out->kind].name)), should_be_paren); goto endofparsing; } MD_u64 beginning_of_arg = end_of_action; MD_u64 end_of_arg = MD_S8FindSubstring(sentence, MD_S8Lit(")"), beginning_of_arg, 0); if(end_of_arg == sentence.size) { - error_message = MD_S8Fmt(arena, "Expected ')' to close the action string's argument, but couldn't find one"); + error_message = FmtWithLint(arena, "Expected ')' to close the action string's argument, but couldn't find one"); goto endofparsing; } @@ -756,7 +759,7 @@ MD_String8 parse_chatgpt_response(MD_Arena *arena, Entity *e, MD_String8 sentenc MD_u64 item_prefix_begin = MD_S8FindSubstring(argument, item_prefix, 0, 0); if(item_prefix_begin == argument.size) { - error_message = MD_S8Fmt(arena, "Expected prefix 'ITEM_' before the give_item action, but found '%.*s' instead", MD_S8VArg(argument)); + error_message = FmtWithLint(arena, "Expected prefix 'ITEM_' before the give_item action, but found '%.*s' instead", MD_S8VArg(argument)); goto endofparsing; } MD_u64 item_name_begin = item_prefix_begin + item_prefix.size; @@ -781,7 +784,7 @@ MD_String8 parse_chatgpt_response(MD_Arena *arena, Entity *e, MD_String8 sentenc { MD_StringJoin join = {.pre = MD_S8Lit(""), .mid = MD_S8Lit(", "), .post = MD_S8Lit("")}; MD_String8 possible_items_str = MD_S8ListJoin(scratch.arena, possible_item_strings, &join); - error_message = MD_S8Fmt(arena, "Item string given is '%.*s', but available items to give are: [%.*s]", MD_S8VArg(item_name), MD_S8VArg(possible_items_str)); + error_message = FmtWithLint(arena, "Item string given is '%.*s', but available items to give are: [%.*s]", MD_S8VArg(item_name), MD_S8VArg(possible_items_str)); goto endofparsing; } @@ -794,7 +797,7 @@ MD_String8 parse_chatgpt_response(MD_Arena *arena, Entity *e, MD_String8 sentenc if(start_looking_for_quote >= sentence.size) { - error_message = MD_S8Fmt(arena, "Wanted to start looking for quote for NPC speech, but sentence ended prematurely"); + error_message = FmtWithLint(arena, "Wanted to start looking for quote for NPC speech, but sentence ended prematurely"); goto endofparsing; } @@ -803,7 +806,7 @@ MD_String8 parse_chatgpt_response(MD_Arena *arena, Entity *e, MD_String8 sentenc if(beginning_of_speech == sentence.size || end_of_speech == sentence.size) { - error_message = MD_S8Fmt(arena, "Expected dialog enclosed by two quotes (i.e \"My name is greg\") after the action, but couldn't find anything!"); + error_message = FmtWithLint(arena, "Expected dialog enclosed by two quotes (i.e \"My name is greg\") after the action, but couldn't find anything!"); goto endofparsing; } @@ -811,7 +814,7 @@ MD_String8 parse_chatgpt_response(MD_Arena *arena, Entity *e, MD_String8 sentenc if(speech.size >= ARRLEN(out->speech)) { - error_message = MD_S8Fmt(arena, "The speech given is %llu bytes big, but the maximum allowed is %llu bytes.", speech.size, ARRLEN(out->speech)); + error_message = FmtWithLint(arena, "The speech given is %llu bytes big, but the maximum allowed is %llu bytes.", speech.size, ARRLEN(out->speech)); goto endofparsing; } @@ -823,7 +826,7 @@ MD_String8 parse_chatgpt_response(MD_Arena *arena, Entity *e, MD_String8 sentenc if(beginning_of_monologue == sentence.size || end_of_monologue == sentence.size) { - error_message = MD_S8Fmt(arena, "Expected an internal monologue for your character enclosed by '[' and ']' after the speech in quotes, but couldn't find anything!"); + error_message = FmtWithLint(arena, "Expected an internal monologue for your character enclosed by '[' and ']' after the speech in quotes, but couldn't find anything!"); goto endofparsing; }