# HG changeset patch # User Michael Pavone # Date 1702526958 28800 # Node ID ebca8ab027019d3a3c5917d83bbdb96586f1a416 # Parent 340299a76db73177c9f9aeebf92987e401388510 Basic string support in debugger language diff -r 340299a76db7 -r ebca8ab02701 debug.c --- a/debug.c Wed Dec 06 22:25:49 2023 -0800 +++ b/debug.c Wed Dec 13 20:09:18 2023 -0800 @@ -165,6 +165,45 @@ return arrays + val.v.u32; } +static debug_string **debug_strings; +static uint32_t num_debug_strings; +static uint32_t debug_string_storage; +static debug_val new_debug_string(char *str) +{ + if (num_debug_strings == debug_string_storage) { + debug_string_storage = debug_string_storage ? 2 * debug_string_storage : 4; + debug_strings = realloc(debug_strings, debug_string_storage * sizeof(debug_string*)); + } + debug_string *string = calloc(1, sizeof(debug_string)); + string->size = string->storage = strlen(str); + string->buffer = calloc(1, string->size + 1); + memcpy(string->buffer, str, string->size + 1); + debug_strings[num_debug_strings] = string; + return (debug_val){ + .type = DBG_VAL_STRING, + .v = { + .u32 = num_debug_strings++ + } + }; +} + +static debug_string* get_string(debug_val val) +{ + if (val.type != DBG_VAL_STRING) { + return NULL; + } + return debug_strings[val.v.u32]; +} + +static char* get_cstring(debug_val val) +{ + debug_string *str = get_string(val); + if (!str) { + return NULL; + } + return str->buffer; +} + static uint8_t debug_cast_int(debug_val val, uint32_t *out) { if (val.type == DBG_VAL_U32) { @@ -373,9 +412,74 @@ "TOKEN_LBRACKET", "TOKEN_RBRACKET", "TOKEN_LPAREN", - "TOKEN_RPAREN" + "TOKEN_RPAREN", + "TOKEN_STRING" }; +static char *parse_string_literal(char *start, char **end) +{ + uint32_t length = 0; + uint8_t is_escape = 0; + char *cur; + for (cur = start; *cur && *cur != '"'; cur++) + { + if (is_escape) { + switch (*cur) + { + case 't': + case 'n': + case 'r': + case '\\': + break; + default: + fprintf(stderr, "Unsupported escape character %c\n", *cur); + return NULL; + } + is_escape = 0; + } else if (*cur == '\\') { + is_escape = 1; + continue; + } + length++; + } + if (!*cur) { + fprintf(stderr, "Unterminated string literal: %s\n", start); + return NULL; + } + *end = cur + 1; + char *ret = calloc(1, length + 1); + char *dst = ret; + is_escape = 0; + for (cur = start; *cur != '"'; ++cur) + { + if (is_escape) { + switch (*cur) + { + case 't': + *(dst++) = '\t'; + break; + case 'n': + *(dst++) = '\n'; + break; + case 'r': + *(dst++) = '\r'; + break; + case '\\': + *(dst++) = '\\'; + break; + } + is_escape = 0; + } else if (*cur == '\\') { + is_escape = 1; + continue; + } else { + *(dst++) = *cur; + } + } + *dst = 0; + return ret; +} + static token parse_token(char *start, char **end) { while(*start && isblank(*start) && *start != '\n' && *start != '\r') @@ -383,10 +487,10 @@ ++start; } if (!*start || *start == '\n' || *start == '\r') { + *end = start; return (token){ .type = TOKEN_NONE }; - *end = start; } if (*start == '$' || (*start == '0' && start[1] == 'x')) { return (token) { @@ -396,6 +500,21 @@ } }; } + if (*start == '"') { + char *str = parse_string_literal(start + 1, end); + if (!str) { + *end = start; + return (token){ + .type = TOKEN_NONE + }; + } + return (token){ + .type = TOKEN_STRING, + .v = { + .str = str + } + }; + } if (isdigit(*start)) { uint32_t ipart = strtol(start, end, 10); if (**end == '.') { @@ -543,7 +662,7 @@ } else { free_expr(e->right); } - if (e->op.type == TOKEN_NAME || e->op.type == TOKEN_ARRAY) { + if (e->op.type == TOKEN_NAME || e->op.type == TOKEN_ARRAY || e->op.type == TOKEN_STRING) { free(e->op.v.str); } } @@ -697,7 +816,7 @@ } return ret; } - if (first.type != TOKEN_INT && first.type != TOKEN_DECIMAL && first.type != TOKEN_NAME) { + if (first.type != TOKEN_INT && first.type != TOKEN_DECIMAL && first.type != TOKEN_NAME && first.type != TOKEN_STRING) { fprintf(stderr, "Unexpected token %s\n", token_type_names[first.type]); return NULL; } @@ -889,7 +1008,7 @@ } return maybe_muldiv(ret, *end, end); } - if (first.type != TOKEN_INT && first.type != TOKEN_DECIMAL && first.type != TOKEN_NAME) { + if (first.type != TOKEN_INT && first.type != TOKEN_DECIMAL && first.type != TOKEN_NAME && first.type != TOKEN_STRING) { fprintf(stderr, "Unexpected token %s\n", token_type_names[first.type]); return NULL; } @@ -1055,7 +1174,7 @@ } return maybe_binary(ret, *end, end); } - if (first.type != TOKEN_INT && first.type != TOKEN_DECIMAL && first.type != TOKEN_NAME) { + if (first.type != TOKEN_INT && first.type != TOKEN_DECIMAL && first.type != TOKEN_NAME && first.type != TOKEN_STRING) { fprintf(stderr, "Unexpected token %s\n", token_type_names[first.type]); return NULL; } @@ -1151,8 +1270,11 @@ } else if (e->op.type == TOKEN_INT) { *out = debug_int(e->op.v.num); return 1; + } else if (e->op.type == TOKEN_DECIMAL){ + *out = debug_float(e->op.v.f); + return 1; } else { - *out = debug_float(e->op.v.f); + *out = new_debug_string(e->op.v.str); return 1; } case EXPR_UNARY: @@ -2390,54 +2512,14 @@ static uint8_t cmd_printf(debug_root *root, parsed_command *cmd) { - char *param = cmd->raw; - if (!param) { - fputs("printf requires at least one parameter\n", stderr); - return 1; - } - while (isblank(*param)) - { - ++param; - } - if (*param != '"') { - fprintf(stderr, "First parameter to printf must be a string, found '%s'\n", param); + char *fmt = get_cstring(cmd->args[0].value); + if (!fmt) { + fprintf(stderr, "First parameter to printf must be a string\n"); return 1; } - ++param; - char *fmt = strdup(param); - char *cur = param, *out = fmt; - while (*cur && *cur != '"') - { - if (*cur == '\\') { - switch (cur[1]) - { - case 't': - *(out++) = '\t'; - break; - case 'n': - *(out++) = '\n'; - break; - case 'r': - *(out++) = '\r'; - break; - case '\\': - *(out++) = '\\'; - break; - default: - fprintf(stderr, "Unsupported escape character %c in string %s\n", cur[1], fmt); - free(fmt); - return 1; - } - cur += 2; - } else { - *(out++) = *(cur++); - } - } - *out = 0; - ++cur; - param = cur; - cur = fmt; + char *cur = fmt; char format_str[3] = {'%', 'd', 0}; + uint32_t cur_param = 1; while (*cur) { if (*cur == '%') { @@ -2452,29 +2534,22 @@ break; default: fprintf(stderr, "Unsupported format character %c\n", cur[1]); - free(fmt); return 1; } format_str[1] = cur[1]; - expr *arg = parse_expression(param, ¶m); - if (!arg) { - free(fmt); + if (cur_param == cmd->num_args) { + fprintf(stderr, "Not enough arguments for format char %c\n", *cur); return 1; } - debug_val val; - if (!eval_expr(root, arg, &val)) { - free(fmt); - return 1; - } + debug_val val = cmd->args[cur_param++].value; if (cur[1] == 's') { if (val.type == DBG_VAL_STRING) { - //TODO: implement me + printf(format_str, get_cstring(val)); } else { char tmp[128]; uint32_t address; if (!debug_cast_int(val, &address)) { fprintf(stderr, "Format char 's' accepts only integers and strings\n"); - free(fmt); return 1; } int j; @@ -2495,7 +2570,6 @@ float fval; if (!debug_cast_float(val, &fval)) { fprintf(stderr, "Format char '%c' only accepts floats\n", cur[1]); - free(fmt); return 1; } printf(format_str, fval); @@ -2503,7 +2577,6 @@ uint32_t ival; if (!debug_cast_int(val, &ival)) { fprintf(stderr, "Format char '%c' only accepts integers\n", cur[1]); - free(fmt); return 1; } printf(format_str, ival); @@ -2906,16 +2979,26 @@ static uint8_t cmd_bindup(debug_root *root, parsed_command *cmd) { - if (!bind_up(cmd->raw)) { - fprintf(stderr, "%s is not a valid binding name\n", cmd->raw); + char *bind = get_cstring(cmd->args[0].value); + if (!bind) { + fprintf(stderr, "Argument to bindup must be a string\n"); + return 1; + } + if (!bind_up(bind)) { + fprintf(stderr, "%s is not a valid binding name\n", bind); } return 1; } static uint8_t cmd_binddown(debug_root *root, parsed_command *cmd) { - if (!bind_down(cmd->raw)) { - fprintf(stderr, "%s is not a valid binding name\n", cmd->raw); + char *bind = get_cstring(cmd->args[0].value); + if (!bind) { + fprintf(stderr, "Argument to binddown must be a string\n"); + return 1; + } + if (!bind_down(bind)) { + fprintf(stderr, "%s is not a valid binding name\n", bind); } return 1; } @@ -2970,8 +3053,12 @@ static uint8_t cmd_symbols(debug_root *root, parsed_command *cmd) { - char *filename = cmd->raw ? strip_ws(cmd->raw) : NULL; - if (filename && *filename) { + if (cmd->num_args) { + char *filename = get_cstring(cmd->args[0].value); + if (!filename) { + fprintf(stderr, "Argument to symbols must be a string if provided\n"); + return 1; + } FILE *f = fopen(filename, "r"); if (!f) { fprintf(stderr, "Failed to open %s for reading\n", filename); @@ -3009,9 +3096,18 @@ fprintf(stderr, "Invalid size %s\n", cmd->format); return 1; } - FILE *f = fopen(cmd->args[0].raw, "wb"); + if (!eval_expr(root, cmd->args[0].parsed, &cmd->args[0].value)) { + fprintf(stderr, "Failed to eval %s\n", cmd->args[0].raw); + return 1; + } + char *fname = get_cstring(cmd->args[0].value); + if (!fname) { + fprintf(stderr, "First argument to save must be a string\n"); + return 1; + } + FILE *f = fopen(fname, "wb"); if (!f) { - fprintf(stderr, "Failed to open %s for writing\n", cmd->args[0].raw); + fprintf(stderr, "Failed to open %s for writing\n", fname); return 1; } uint32_t start = 0; @@ -3163,9 +3259,18 @@ fprintf(stderr, "Invalid size %s\n", cmd->format); return 1; } - FILE *f = fopen(cmd->args[0].raw, "rb"); + if (!eval_expr(root, cmd->args[0].parsed, &cmd->args[0].value)) { + fprintf(stderr, "Failed to eval %s\n", cmd->args[0].raw); + return 1; + } + char *fname = get_cstring(cmd->args[0].value); + if (!fname) { + fprintf(stderr, "First argument to load must be a string\n"); + return 1; + } + FILE *f = fopen(fname, "rb"); if (!f) { - fprintf(stderr, "Failed to open %s for reading\n", cmd->args[0].raw); + fprintf(stderr, "Failed to open %s for reading\n", fname); return 1; } uint32_t start = 0; @@ -3776,8 +3881,7 @@ .desc = "Print a string with C-style formatting specifiers replaced with the value of the remaining arguments", .impl = cmd_printf, .min_args = 1, - .max_args = -1, - .raw_args = 1 + .max_args = -1 }, { .names = (const char *[]){ @@ -3903,8 +4007,7 @@ .desc = "Simulate a keyup for binding NAME", .impl = cmd_bindup, .min_args = 1, - .max_args = 1, - .raw_args = 1 + .max_args = 1 }, { .names = (const char *[]){ @@ -3914,8 +4017,7 @@ .desc = "Simulate a keydown for binding NAME", .impl = cmd_binddown, .min_args = 1, - .max_args = 1, - .raw_args = 1 + .max_args = 1 }, { .names = (const char *[]){ @@ -3960,8 +4062,7 @@ .desc = "Loads a list of symbols from the file indicated by FILENAME or lists currently loaded symbols if FILENAME is omitted", .impl = cmd_symbols, .min_args = 0, - .max_args = 1, - .raw_args = 1 + .max_args = 1 }, { .names = (const char *[]){ diff -r 340299a76db7 -r ebca8ab02701 debug.h --- a/debug.h Wed Dec 06 22:25:49 2023 -0800 +++ b/debug.h Wed Dec 13 20:09:18 2023 -0800 @@ -23,7 +23,8 @@ TOKEN_LBRACKET, TOKEN_RBRACKET, TOKEN_LPAREN, - TOKEN_RPAREN + TOKEN_RPAREN, + TOKEN_STRING } token_type; typedef struct { @@ -163,6 +164,12 @@ uint8_t is_native; } debug_func; +typedef struct { + char *buffer; + uint32_t size; + uint32_t storage; +} debug_string; + typedef struct debug_var debug_var; typedef debug_val (*debug_var_get)(debug_var *var); typedef void (*debug_var_set)(debug_var *var, debug_val val);