mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Scripting: handling for 'for'
This commit is contained in:
parent
239faede0f
commit
e9c1faa319
@ -34,9 +34,12 @@
|
|||||||
|
|
||||||
#define _ARG_TRUE "TRUE"
|
#define _ARG_TRUE "TRUE"
|
||||||
#define _ARG_FALSE "FALSE"
|
#define _ARG_FALSE "FALSE"
|
||||||
|
#define _VAR_FORPATH "FORPATH"
|
||||||
|
|
||||||
#define _SKIP_BLOCK 1
|
#define _SKIP_BLOCK 1
|
||||||
#define _SKIP_TILL_END 2
|
#define _SKIP_TILL_END 2
|
||||||
|
#define _SKIP_TO_NEXT 3
|
||||||
|
#define _SKIP_TO_FOR 4
|
||||||
|
|
||||||
#define VAR_BUFFER (SCRIPT_BUFFER + SCRIPT_BUFFER_SIZE - VAR_BUFFER_SIZE)
|
#define VAR_BUFFER (SCRIPT_BUFFER + SCRIPT_BUFFER_SIZE - VAR_BUFFER_SIZE)
|
||||||
|
|
||||||
@ -179,6 +182,7 @@ static u32 script_color_code = 0;
|
|||||||
// global vars for control flow
|
// global vars for control flow
|
||||||
static bool syntax_error = false; // if true, severe error, script has to stop
|
static bool syntax_error = false; // if true, severe error, script has to stop
|
||||||
static char* jump_ptr = NULL; // next position after a jump
|
static char* jump_ptr = NULL; // next position after a jump
|
||||||
|
static char* for_ptr = NULL; // position of the active 'for' command
|
||||||
static u32 skip_state = 0; // zero, _SKIP_BLOCK, _SKIP_TILL_END
|
static u32 skip_state = 0; // zero, _SKIP_BLOCK, _SKIP_TILL_END
|
||||||
static u32 ifcnt = 0; // current # of 'if' nesting
|
static u32 ifcnt = 0; // current # of 'if' nesting
|
||||||
|
|
||||||
@ -585,6 +589,39 @@ char* skip_block(char* ptr, bool ignore_else, bool stop_after_end) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char* find_next(char* ptr) {
|
||||||
|
while (ptr && *ptr) {
|
||||||
|
// store line start / line end
|
||||||
|
char* line_start = ptr;
|
||||||
|
char* line_end = strchr(ptr, '\n');
|
||||||
|
if (!line_end) line_end = ptr + strlen(ptr);
|
||||||
|
|
||||||
|
// grab first string
|
||||||
|
char* str = NULL;
|
||||||
|
u32 str_len = 0;
|
||||||
|
if (!(str = get_string(ptr, line_end, &str_len, &ptr, NULL)) || (str >= line_end)) {
|
||||||
|
// string error or empty line
|
||||||
|
ptr = line_end + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check string
|
||||||
|
if (MATCH_STR(str, str_len, _CMD_IF)) { // skip 'if' blocks
|
||||||
|
ptr = skip_block(ptr, true, true);
|
||||||
|
} else if (MATCH_STR(str, str_len, _CMD_END) || MATCH_STR(str, str_len, _CMD_FOR)) {
|
||||||
|
ptr = NULL; // this should not happen here
|
||||||
|
} else if (MATCH_STR(str, str_len, _CMD_NEXT)) {
|
||||||
|
return line_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move on to the next line
|
||||||
|
ptr = line_end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'next' not found
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
char* find_label(const char* label, const char* last_found) {
|
char* find_label(const char* label, const char* last_found) {
|
||||||
char* script = (char*) SCRIPT_BUFFER; // equals global, not a good solution
|
char* script = (char*) SCRIPT_BUFFER; // equals global, not a good solution
|
||||||
char* ptr = script;
|
char* ptr = script;
|
||||||
@ -626,12 +663,42 @@ char* find_label(const char* label, const char* last_found) {
|
|||||||
return line_start; // match found
|
return line_start; // match found
|
||||||
} else if (MATCH_STR(str, str_len, _CMD_IF)) {
|
} else if (MATCH_STR(str, str_len, _CMD_IF)) {
|
||||||
next = skip_block(line_start, true, true);
|
next = skip_block(line_start, true, true);
|
||||||
|
} else if (MATCH_STR(str, str_len, _CMD_FOR)) {
|
||||||
|
next = find_next(line_start);
|
||||||
} // otherwise: irrelevant line
|
} // otherwise: irrelevant line
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool for_handler(char* path, const char* dir, const char* pattern) {
|
||||||
|
static DIR fdir;
|
||||||
|
static DIR* dp = NULL;
|
||||||
|
static char ldir[256];
|
||||||
|
static char lpattern[64];
|
||||||
|
|
||||||
|
if (!path && !dir && !pattern) {
|
||||||
|
if (dp) fvx_closedir(dp);
|
||||||
|
dp = NULL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir) {
|
||||||
|
snprintf(lpattern, 64, pattern);
|
||||||
|
snprintf(ldir, 256, dir);
|
||||||
|
if (dp) return false; // <- this should never happen
|
||||||
|
if (fvx_opendir(&fdir, dir) != FR_OK)
|
||||||
|
return false;
|
||||||
|
dp = &fdir;
|
||||||
|
} else if (dp) {
|
||||||
|
FILINFO fno;
|
||||||
|
if ((fvx_preaddir(dp, &fno, lpattern) != FR_OK) || !*(fno.fname)) *path = '\0';
|
||||||
|
else snprintf(path, 256, "%s/%s", ldir, fno.fname);
|
||||||
|
} else return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool parse_line(const char* line_start, const char* line_end, cmd_id* cmdid, u32* flags, u32* argc, char** argv, char* err_str) {
|
bool parse_line(const char* line_start, const char* line_end, cmd_id* cmdid, u32* flags, u32* argc, char** argv, char* err_str) {
|
||||||
char* ptr = (char*) line_start;
|
char* ptr = (char*) line_start;
|
||||||
char* str;
|
char* str;
|
||||||
@ -759,6 +826,45 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
|
|||||||
|
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
|
else if (id == CMD_ID_FOR) {
|
||||||
|
// cheating alert(!): actually this does nothing much
|
||||||
|
// just sets up the for_handler and skips to 'next'
|
||||||
|
if (for_ptr) {
|
||||||
|
if (err_str) snprintf(err_str, _ERR_STR_LEN, "'for' inside 'for'");
|
||||||
|
syntax_error = true;
|
||||||
|
return false;
|
||||||
|
} else if (!for_handler(NULL, argv[0], argv[1])) {
|
||||||
|
if (err_str) snprintf(err_str, _ERR_STR_LEN, "dir not found");
|
||||||
|
skip_state = _SKIP_TO_NEXT;
|
||||||
|
ret = false;
|
||||||
|
} else {
|
||||||
|
skip_state = _SKIP_TO_NEXT;
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (id == CMD_ID_NEXT) {
|
||||||
|
// actual work is done here
|
||||||
|
char* var = set_var(_VAR_FORPATH, "");
|
||||||
|
ret = true;
|
||||||
|
if (!for_ptr) {
|
||||||
|
if (err_str) snprintf(err_str, _ERR_STR_LEN, "'next' without 'for'");
|
||||||
|
syntax_error = true;
|
||||||
|
return false;
|
||||||
|
} else if (!var) {
|
||||||
|
if (err_str) snprintf(err_str, _ERR_STR_LEN, "forpath error");
|
||||||
|
ret = false;
|
||||||
|
} else {
|
||||||
|
if (!for_handler(var, NULL, NULL)) *var = '\0';
|
||||||
|
if (!*var) {
|
||||||
|
for_handler(NULL, NULL, NULL); // finish for_handler
|
||||||
|
for_ptr = NULL;
|
||||||
|
skip_state = 0;
|
||||||
|
} else {
|
||||||
|
skip_state = _SKIP_TO_FOR;
|
||||||
|
}
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (id == CMD_ID_GOTO) {
|
else if (id == CMD_ID_GOTO) {
|
||||||
jump_ptr = find_label(argv[0], NULL);
|
jump_ptr = find_label(argv[0], NULL);
|
||||||
if (!jump_ptr) {
|
if (!jump_ptr) {
|
||||||
@ -1458,6 +1564,8 @@ bool ExecuteGM9Script(const char* path_script) {
|
|||||||
|
|
||||||
// reset control flow global vars
|
// reset control flow global vars
|
||||||
ifcnt = 0;
|
ifcnt = 0;
|
||||||
|
jump_ptr = NULL;
|
||||||
|
for_ptr = NULL;
|
||||||
skip_state = 0;
|
skip_state = 0;
|
||||||
syntax_error = false;
|
syntax_error = false;
|
||||||
|
|
||||||
@ -1537,15 +1645,32 @@ bool ExecuteGM9Script(const char* path_script) {
|
|||||||
char err_str[_ERR_STR_LEN+1] = { 0 };
|
char err_str[_ERR_STR_LEN+1] = { 0 };
|
||||||
bool result = run_line(ptr, line_end, &flags, err_str, false);
|
bool result = run_line(ptr, line_end, &flags, err_str, false);
|
||||||
|
|
||||||
|
|
||||||
// skip state handling
|
// skip state handling
|
||||||
char* skip_ptr = ptr;
|
char* skip_ptr = ptr;
|
||||||
if (skip_state) {
|
if ((skip_state == _SKIP_BLOCK) || (skip_state == _SKIP_TILL_END)) {
|
||||||
skip_ptr = skip_block(line_end + 1, (skip_state == _SKIP_TILL_END), false);
|
skip_ptr = skip_block(line_end + 1, (skip_state == _SKIP_TILL_END), false);
|
||||||
if (!skip_ptr) {
|
if (!skip_ptr) {
|
||||||
snprintf(err_str, _ERR_STR_LEN, "unclosed conditional");
|
snprintf(err_str, _ERR_STR_LEN, "unclosed conditional");
|
||||||
result = false;
|
result = false;
|
||||||
syntax_error = true;
|
syntax_error = true;
|
||||||
}
|
}
|
||||||
|
} else if (skip_state == _SKIP_TO_NEXT) {
|
||||||
|
skip_ptr = find_next(ptr);
|
||||||
|
if (!skip_ptr) {
|
||||||
|
snprintf(err_str, _ERR_STR_LEN, "'for' without 'next'");
|
||||||
|
result = false;
|
||||||
|
syntax_error = true;
|
||||||
|
}
|
||||||
|
for_ptr = (char*) line_end + 1;
|
||||||
|
} else if (skip_state == _SKIP_TO_FOR) {
|
||||||
|
skip_ptr = for_ptr;
|
||||||
|
if (!skip_ptr) {
|
||||||
|
snprintf(err_str, _ERR_STR_LEN, "'next' without 'for'");
|
||||||
|
result = false;
|
||||||
|
syntax_error = true;
|
||||||
|
}
|
||||||
|
skip_state = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1582,6 +1707,8 @@ bool ExecuteGM9Script(const char* path_script) {
|
|||||||
lno = get_lno(script, script_size, ptr);
|
lno = get_lno(script, script_size, ptr);
|
||||||
ifcnt = 0; // jumping into conditional block is unexpected/unsupported
|
ifcnt = 0; // jumping into conditional block is unexpected/unsupported
|
||||||
jump_ptr = NULL;
|
jump_ptr = NULL;
|
||||||
|
for_ptr = NULL;
|
||||||
|
for_handler(NULL, NULL, NULL);
|
||||||
} else {
|
} else {
|
||||||
ptr = line_end + 1;
|
ptr = line_end + 1;
|
||||||
lno++;
|
lno++;
|
||||||
@ -1592,6 +1719,10 @@ bool ExecuteGM9Script(const char* path_script) {
|
|||||||
if (ifcnt) {
|
if (ifcnt) {
|
||||||
ShowPrompt(false, "%s\nend of script: unresolved 'if'", path_str);
|
ShowPrompt(false, "%s\nend of script: unresolved 'if'", path_str);
|
||||||
return false;
|
return false;
|
||||||
|
} else if (for_ptr) {
|
||||||
|
ShowPrompt(false, "%s\nend of script: unresolved 'for'", path_str);
|
||||||
|
for_handler(NULL, NULL, NULL);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// success message if applicable
|
// success message if applicable
|
||||||
|
Loading…
x
Reference in New Issue
Block a user