gbdb: refactor command parsing
Instead of passing around a string and having each command parse off additional tokens, have a single command to parse all of the tokens, and the commands can pass around a token list. Then each command/subcommand can pop off whatever arguments they need.
This commit is contained in:
207
src/apps/gbdb.c
207
src/apps/gbdb.c
@@ -24,7 +24,7 @@
|
||||
#define INPUT_MAX_LEN 512
|
||||
#define MAX_BREAKPTS 16 /* Should be plenty for anyone */
|
||||
|
||||
typedef void (gbdb_cmd)(char *arg_string);
|
||||
typedef void (gbdb_cmd)(struct tokens **tokens);
|
||||
|
||||
static void breakpoint_addr_hit(uint16_t id);
|
||||
static void breakpoint_cb(struct lr35902_state *lr);
|
||||
@@ -128,37 +128,35 @@ static void show_prompt()
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static void call_next_cmd(char *arg_list,
|
||||
struct tri *commands,
|
||||
const char *cmdname)
|
||||
static void call_next_cmd(struct tokens** tokens,
|
||||
struct tri *commands,
|
||||
const char *cmdname)
|
||||
{
|
||||
char * remainder;
|
||||
gbdb_cmd *cmd;
|
||||
bool ambiguous = false;
|
||||
remainder = strchr(arg_list, ' ');
|
||||
if (remainder != NULL) {
|
||||
remainder[0] = '\0';
|
||||
remainder++;
|
||||
|
||||
char *token = token_next(tokens);
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmd = tri_get_string_autocomplete(commands, arg_list, &ambiguous);
|
||||
|
||||
cmd = tri_get_string_autocomplete(commands, token, &ambiguous);
|
||||
if (ambiguous) {
|
||||
gb_error("ambiguous %s command: '%s'\n", cmdname, arg_list);
|
||||
gb_error("ambiguous %s command: '%s'\n", cmdname, token);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd == NULL) {
|
||||
gb_error("unrecognized %s command: '%s'\n", cmdname, arg_list);
|
||||
gb_error("unrecognized %s command: '%s'\n", cmdname, token);
|
||||
return;
|
||||
}
|
||||
|
||||
cmd(remainder);
|
||||
cmd(tokens);
|
||||
}
|
||||
|
||||
static void set_logfile(char *arglist)
|
||||
static void set_logfile(struct tokens **tokens)
|
||||
{
|
||||
char *token = token = strtok(arglist, " ");
|
||||
char *token = token_next(tokens);
|
||||
int fd;
|
||||
|
||||
if (token != NULL) {
|
||||
@@ -188,17 +186,17 @@ static void set_logfile(char *arglist)
|
||||
config.log_fd = fd;
|
||||
}
|
||||
|
||||
static void set_quiet_on(char *arg_list)
|
||||
static void set_quiet_on(struct tokens **tokens)
|
||||
{
|
||||
config.quiet = true;
|
||||
}
|
||||
|
||||
static void set_quiet_off(char *arg_list)
|
||||
static void set_quiet_off(struct tokens **tokens)
|
||||
{
|
||||
config.quiet = false;
|
||||
}
|
||||
|
||||
static void set_quiet(char *arg_list)
|
||||
static void set_quiet(struct tokens **tokens)
|
||||
{
|
||||
static bool init = false;
|
||||
static struct tri commands;
|
||||
@@ -212,15 +210,15 @@ static void set_quiet(char *arg_list)
|
||||
init = true;
|
||||
}
|
||||
|
||||
call_next_cmd(arg_list, &commands, "set quiet");
|
||||
call_next_cmd(tokens, &commands, "set quiet");
|
||||
}
|
||||
|
||||
static void set_video_debug(char *arg_list)
|
||||
static void set_video_debug(struct tokens **tokens)
|
||||
{
|
||||
video.debug_logging = true;
|
||||
}
|
||||
|
||||
static void set(char *arg_list)
|
||||
static void set(struct tokens **tokens)
|
||||
{
|
||||
static bool init = false;
|
||||
static struct tri commands;
|
||||
@@ -233,16 +231,17 @@ static void set(char *arg_list)
|
||||
init = true;
|
||||
}
|
||||
|
||||
call_next_cmd(arg_list, &commands, "set");
|
||||
call_next_cmd(tokens, &commands, "set");
|
||||
}
|
||||
|
||||
static void echo(char *arg_list)
|
||||
static void echo(struct tokens **tokens)
|
||||
{
|
||||
if (arg_list != NULL) {
|
||||
gb_log("%s\n", arg_list);
|
||||
} else {
|
||||
gb_log("\n");
|
||||
char *token;
|
||||
while ((token = token_next(tokens)) != NULL) {
|
||||
gb_log("%s ", token);
|
||||
}
|
||||
|
||||
gb_log("\n");
|
||||
}
|
||||
|
||||
static void breakpoint_cb(struct lr35902_state *lr)
|
||||
@@ -256,11 +255,11 @@ static void breakpoint_cb(struct lr35902_state *lr)
|
||||
}
|
||||
|
||||
|
||||
static void step(char *arg_list)
|
||||
static void step(struct tokens **tokens)
|
||||
{
|
||||
uint64_t steps, end_steps;
|
||||
char *token;
|
||||
token = strtok(arg_list, " ");
|
||||
token = token_next(tokens);
|
||||
if (token == NULL) {
|
||||
steps = 1;
|
||||
} else {
|
||||
@@ -293,7 +292,7 @@ static void step(char *arg_list)
|
||||
paused_breakpoint = 0;
|
||||
}
|
||||
|
||||
static void regs(char *arg_list)
|
||||
static void regs(struct tokens **tokens)
|
||||
{
|
||||
uint8_t f = (cpu.nf << CPU_F_BIT_POS_N) | (cpu.zf << CPU_F_BIT_POS_Z) |
|
||||
(cpu.cf << CPU_F_BIT_POS_C) | (cpu.hf << CPU_F_BIT_POS_H);
|
||||
@@ -306,21 +305,21 @@ static void regs(char *arg_list)
|
||||
gb_log("SP: 0x%04x\n", cpu.sp);
|
||||
}
|
||||
|
||||
static void peek(char *arg_list)
|
||||
static void peek(struct tokens **tokens)
|
||||
{
|
||||
uint16_t pc = lr35902_get_reg_16(&cpu, LR35902_REG_PC);
|
||||
uint8_t byte = gb_mem_read(memory, pc);
|
||||
gb_log("0x%04x:%s\n", pc, gb_byte_to_opcode(byte));
|
||||
}
|
||||
|
||||
static void vstep(char *arg_list)
|
||||
static void vstep(struct tokens **tokens)
|
||||
{
|
||||
peek(NULL);
|
||||
step(arg_list);
|
||||
step(tokens);
|
||||
regs(NULL);
|
||||
}
|
||||
|
||||
static void stats(char *arg_list)
|
||||
static void stats(struct tokens **tokens)
|
||||
{
|
||||
gb_log("Cycles: %" PRId64 "\n", cpu.metrics.cycles);
|
||||
gb_log("Retired Insructions %" PRId64 "\n", cpu.metrics.retired_instrs);
|
||||
@@ -328,7 +327,13 @@ static void stats(char *arg_list)
|
||||
gb_log("Memory Writes: %" PRId64 "\n", cpu.metrics.mem_writes);
|
||||
}
|
||||
|
||||
static void help(char *arg_list)
|
||||
static void comment(struct tokens **tokens)
|
||||
{
|
||||
/* Gobble up everything */
|
||||
do {} while (token_next(tokens));
|
||||
}
|
||||
|
||||
static void help(struct tokens **tokens)
|
||||
{
|
||||
gb_log(usage);
|
||||
}
|
||||
@@ -376,26 +381,26 @@ static int64_t parse_val(const char *str)
|
||||
return strtoll(str, NULL, 0);
|
||||
}
|
||||
|
||||
static void assert(char *arg_list)
|
||||
static void assert(struct tokens **tokens)
|
||||
{
|
||||
char *val0_str, *val1_str, *operator_str;
|
||||
char *val0_str = NULL, *val1_str = NULL, *operator_str = NULL;
|
||||
const char *usage = "usage: assert <value> <operator> <value>\n";
|
||||
int64_t val0, val1;
|
||||
|
||||
val0_str = strtok(arg_list, " ");
|
||||
val0_str = token_next(tokens);
|
||||
if (val0_str == NULL) {
|
||||
gb_error("%s", usage);
|
||||
return;
|
||||
}
|
||||
val0 = parse_val(val0_str);
|
||||
|
||||
operator_str = strtok(NULL, " ");
|
||||
operator_str = strdup(token_next(tokens));
|
||||
if (operator_str == NULL) {
|
||||
gb_error("%s", usage);
|
||||
return;
|
||||
}
|
||||
|
||||
val1_str = strtok(NULL, " ");
|
||||
val1_str = token_next(tokens);
|
||||
if (val1_str == NULL) {
|
||||
gb_error("%s", usage);
|
||||
return;
|
||||
@@ -434,15 +439,20 @@ static void assert(char *arg_list)
|
||||
fail:
|
||||
gb_error("ASSERT: %s %s %s\n", val0_str, operator_str, val1_str);
|
||||
gb_error("%s=%" PRId64 ", %s=%" PRId64 "\n", val0_str, val0, val1_str, val1);
|
||||
free(val0_str);
|
||||
free(val1_str);
|
||||
free(operator_str);
|
||||
regs(NULL);
|
||||
stats(NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void mem(char *arg_list)
|
||||
static void mem(struct tokens **tokens)
|
||||
{
|
||||
uint16_t addr, bytes, i;
|
||||
char *token = strtok(arg_list, " ");
|
||||
char *token = token_next(tokens);
|
||||
|
||||
gb_log("mem_addr=%s\n", token);
|
||||
|
||||
if (token == NULL) {
|
||||
gb_error("usage: mem <addr> (num bytes) (format)\n");
|
||||
@@ -450,14 +460,16 @@ static void mem(char *arg_list)
|
||||
}
|
||||
|
||||
addr = parse_val(token);
|
||||
token = strtok(NULL, " ");
|
||||
token = token_next(tokens);
|
||||
gb_log("mem_bytes=%s\n", token);
|
||||
if (token == NULL) {
|
||||
bytes = 1;
|
||||
} else {
|
||||
bytes = parse_val(token);
|
||||
}
|
||||
|
||||
token = strtok(NULL, " ");
|
||||
token = token_next(tokens);
|
||||
gb_log("mem_format=%s\n", token);
|
||||
|
||||
for (i = 0; i < bytes; i++) {
|
||||
//TODO: Make sure this has no side effects
|
||||
@@ -474,34 +486,32 @@ static void mem(char *arg_list)
|
||||
|
||||
}
|
||||
|
||||
static void load(char *arg_list)
|
||||
static void load(struct tokens **tokens)
|
||||
{
|
||||
char *token = strtok(arg_list, " ");
|
||||
char *token = token_next(tokens);
|
||||
if (token == NULL) {
|
||||
gb_error("usage: load <file>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
strip_newline(token);
|
||||
gb_log("loading %s\n", token);
|
||||
|
||||
gb_memory_load_file(memory, GB_MEMORY_CART, token);
|
||||
}
|
||||
|
||||
static void load_bootrom(char *arg_list)
|
||||
static void load_bootrom(struct tokens **tokens)
|
||||
{
|
||||
char *token = strtok(arg_list, " ");
|
||||
char *token = token_next(tokens);
|
||||
if (token == NULL) {
|
||||
gb_error("usage: bootrom <file>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
strip_newline(token);
|
||||
|
||||
gb_memory_load_file(memory, GB_MEMORY_BOOTROM, token);
|
||||
}
|
||||
|
||||
|
||||
static void quit(char *arg_list)
|
||||
static void quit(struct tokens **tokens)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
@@ -528,7 +538,7 @@ static __attribute__((hot)) void do_run(void) {
|
||||
paused_signal = 0;
|
||||
}
|
||||
|
||||
static void run(char *arg_list)
|
||||
static void run(struct tokens **tokens )
|
||||
{
|
||||
do_run();
|
||||
}
|
||||
@@ -581,10 +591,10 @@ static int do_set_breakpoint(uint16_t addr, bool temp)
|
||||
}
|
||||
}
|
||||
|
||||
static void set_breakpoint(char *arg_string)
|
||||
static void set_breakpoint(struct tokens **tokens)
|
||||
{
|
||||
uint16_t addr, i;
|
||||
char *token = strtok(arg_string, " ");
|
||||
char *token = token_next(tokens);
|
||||
|
||||
if (token == NULL) {
|
||||
gb_error("usage: breakpoint add <addr>\n");
|
||||
@@ -595,10 +605,10 @@ static void set_breakpoint(char *arg_string)
|
||||
do_set_breakpoint(addr, false);
|
||||
}
|
||||
|
||||
static void runto(char *arg_string)
|
||||
static void runto(struct tokens **tokens)
|
||||
{
|
||||
uint16_t addr, i, rc;
|
||||
char *token = strtok(arg_string, " ");
|
||||
char *token = token_next(tokens);
|
||||
|
||||
if (token == NULL) {
|
||||
gb_error("usage: runto <addr>\n");
|
||||
@@ -657,9 +667,9 @@ static void breakpoint_addr_hit(uint16_t addr)
|
||||
}
|
||||
}
|
||||
|
||||
static void delete_breakpoint(char *arg_string)
|
||||
static void delete_breakpoint(struct tokens **tokens)
|
||||
{
|
||||
char *token = strtok(arg_string, " ");
|
||||
char *token = token_next(tokens);
|
||||
int rc, bkpt;
|
||||
|
||||
if (token == NULL) {
|
||||
@@ -675,7 +685,7 @@ static void delete_breakpoint(char *arg_string)
|
||||
}
|
||||
}
|
||||
|
||||
static void display_breakpoints(char *arg_string)
|
||||
static void display_breakpoints(struct tokens **tokens)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -688,7 +698,7 @@ static void display_breakpoints(char *arg_string)
|
||||
}
|
||||
}
|
||||
|
||||
static void breakpoint(char *arg_list)
|
||||
static void breakpoint(struct tokens **tokens)
|
||||
{
|
||||
static bool init = false;
|
||||
static struct tri commands;
|
||||
@@ -706,7 +716,7 @@ static void breakpoint(char *arg_list)
|
||||
init = true;
|
||||
}
|
||||
|
||||
call_next_cmd(arg_list, &commands, "breakpoint");
|
||||
call_next_cmd(tokens, &commands, "breakpoint");
|
||||
}
|
||||
|
||||
static bool breakpoint_is_at_addr(uint16_t addr) {
|
||||
@@ -723,70 +733,24 @@ static bool breakpoint_is_at_addr(uint16_t addr) {
|
||||
|
||||
static void process_cmd(const char *cmd_str, struct tri *commands)
|
||||
{
|
||||
char cmd_buffer[INPUT_MAX_LEN]; /* Buffer for command parsing */
|
||||
bool ambiguous;
|
||||
gbdb_cmd *cmd;
|
||||
struct tokens *statements;
|
||||
struct tokens *tokens;
|
||||
char *statement, *token;
|
||||
|
||||
/* Current point in the line (there may be more than
|
||||
* one command on a line) */
|
||||
const char *process_str;
|
||||
|
||||
/* String containing the arguments following a command */
|
||||
char *arg_str;
|
||||
|
||||
process_str = cmd_str;
|
||||
|
||||
char *comment;
|
||||
comment = strchr(process_str, '#');
|
||||
if (comment != NULL) {
|
||||
*comment = '\0';
|
||||
statements = tokenize(cmd_str, ";\n");
|
||||
if (!statements) {
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
int i;
|
||||
|
||||
while (process_str[0] == ' ') { process_str++; };
|
||||
for (i = 0;; i++) {
|
||||
if (process_str[i] == '\0' ||
|
||||
process_str[i] == ';') {
|
||||
cmd_buffer[i] = '\0';
|
||||
break;
|
||||
} else {
|
||||
cmd_buffer[i] = process_str[i];
|
||||
}
|
||||
while ((statement = token_next(&statements)) != NULL) {
|
||||
tokens = tokenize(statement, " ");
|
||||
if (!tokens) {
|
||||
continue;
|
||||
}
|
||||
|
||||
strip_newline(cmd_buffer);
|
||||
arg_str = strchr(cmd_buffer, ' ');
|
||||
if (arg_str != NULL) {
|
||||
arg_str[0] = '\0';
|
||||
arg_str++;
|
||||
}
|
||||
call_next_cmd(&tokens, commands, "");
|
||||
}
|
||||
|
||||
if (strlen(cmd_buffer) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
cmd = tri_get_string_autocomplete(commands, cmd_buffer, &ambiguous);
|
||||
|
||||
if (ambiguous) {
|
||||
gb_error("ambiguous command: '%s'\n", cmd_buffer);
|
||||
break;
|
||||
} else if (cmd == NULL) {
|
||||
gb_error("unrecognized command: '%s'\n", cmd_buffer);
|
||||
break;
|
||||
} else {
|
||||
cmd(arg_str);
|
||||
}
|
||||
|
||||
process_str = strchr(process_str, ';');
|
||||
if (process_str != NULL) {
|
||||
process_str++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static int load_initfile(const char *initfile_path, struct tri *commands)
|
||||
@@ -818,6 +782,7 @@ int main(int argc, char **argv)
|
||||
old_buffer[0] = '\0';
|
||||
|
||||
tri_init(&commands);
|
||||
tri_add_string(&commands, "#", comment);
|
||||
tri_add_string(&commands, "step", step);
|
||||
tri_add_string(&commands, "vstep", vstep);
|
||||
tri_add_string(&commands, "run", run);
|
||||
|
||||
Reference in New Issue
Block a user