Scripting: add 'elif' command

This commit is contained in:
d0k3 2017-12-19 02:58:58 +01:00
parent 32b9675c49
commit 75448571ea

View File

@ -21,13 +21,18 @@
#define _ERR_STR_LEN 32 #define _ERR_STR_LEN 32
#define _CMD_IF "if" #define _CMD_IF "if"
#define _CMD_ELIF "elif"
#define _CMD_ELSE "else" #define _CMD_ELSE "else"
#define _CMD_END "end" #define _CMD_END "end"
#define _CMD_FOR "for" #define _CMD_FOR "for"
#define _CMD_NEXT "next" #define _CMD_NEXT "next"
#define _ARG_TRUE "TRUE" #define _ARG_TRUE "TRUE"
#define _ARG_FALSE "FALSE" #define _ARG_FALSE "FALSE"
#define _SKIP_BLOCK 1
#define _SKIP_TILL_END 2
#define VAR_BUFFER (SCRIPT_BUFFER + SCRIPT_BUFFER_SIZE - VAR_BUFFER_SIZE) #define VAR_BUFFER (SCRIPT_BUFFER + SCRIPT_BUFFER_SIZE - VAR_BUFFER_SIZE)
// macros for textviewer // macros for textviewer
@ -47,6 +52,7 @@
typedef enum { typedef enum {
CMD_ID_NONE = 0, CMD_ID_NONE = 0,
CMD_ID_IF, CMD_ID_IF,
CMD_ID_ELIF,
CMD_ID_ELSE, CMD_ID_ELSE,
CMD_ID_END, CMD_ID_END,
CMD_ID_GOTO, CMD_ID_GOTO,
@ -97,6 +103,7 @@ typedef struct {
Gm9ScriptCmd cmd_list[] = { Gm9ScriptCmd cmd_list[] = {
{ CMD_ID_NONE , "#" , 0, 0 }, // dummy entry { CMD_ID_NONE , "#" , 0, 0 }, // dummy entry
{ CMD_ID_IF , _CMD_IF , 1, 0 }, // control flow commands at the top of the list { CMD_ID_IF , _CMD_IF , 1, 0 }, // control flow commands at the top of the list
{ CMD_ID_ELIF , _CMD_ELIF , 1, 0 },
{ CMD_ID_ELSE , _CMD_ELSE , 0, 0 }, { CMD_ID_ELSE , _CMD_ELSE , 0, 0 },
{ CMD_ID_END , _CMD_END , 0, 0 }, { CMD_ID_END , _CMD_END , 0, 0 },
{ CMD_ID_GOTO , "goto" , 1, 0 }, { CMD_ID_GOTO , "goto" , 1, 0 },
@ -140,8 +147,8 @@ 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 bool skip_state = false; // if true, skip the block that comes next
static char* jump_ptr = NULL; // next position after a jump static char* jump_ptr = NULL; // next position after a jump
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
@ -512,6 +519,8 @@ char* skip_block(char* ptr, bool ignore_else, bool stop_after_end) {
return line_start; // end of block found return line_start; // end of block found
} else if (!ignore_else && MATCH_STR(str, str_len, _CMD_ELSE)) { // stop at else } else if (!ignore_else && MATCH_STR(str, str_len, _CMD_ELSE)) { // stop at else
return line_start; // end of block found return line_start; // end of block found
} else if (!ignore_else && MATCH_STR(str, str_len, _CMD_ELIF)) { // stop at elif
return line_start; // end of block found
} else if (MATCH_STR(str, str_len, _CMD_IF)) { } else if (MATCH_STR(str, str_len, _CMD_IF)) {
ptr = line_start = skip_block(line_end + 1, true, false); ptr = line_start = skip_block(line_end + 1, true, false);
if (ptr == NULL) return NULL; if (ptr == NULL) return NULL;
@ -573,7 +582,7 @@ 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);
} // irrelevant line } // otherwise: irrelevant line
} }
return NULL; return NULL;
@ -595,10 +604,13 @@ bool parse_line(const char* line_start, const char* line_end, cmd_id* cmdid, u32
if (!(cmd = get_string(ptr, line_end, &cmd_len, &ptr, err_str))) return false; // string error if (!(cmd = get_string(ptr, line_end, &cmd_len, &ptr, err_str))) return false; // string error
if ((cmd >= line_end) || (*cmd == '#') || (*cmd == '@')) return true; // empty line or comment or label if ((cmd >= line_end) || (*cmd == '#') || (*cmd == '@')) return true; // empty line or comment or label
// special handling for "if" // special handling for "if" and "elif"
if (MATCH_STR(cmd, cmd_len, _CMD_IF)) { if (MATCH_STR(cmd, cmd_len, _CMD_IF)) {
*cmdid = CMD_ID_IF; *cmdid = CMD_ID_IF;
return true; return true;
} else if (MATCH_STR(cmd, cmd_len, _CMD_ELIF)) {
*cmdid = CMD_ID_ELIF;
return true;
} }
// got cmd, now parse flags & args // got cmd, now parse flags & args
@ -642,13 +654,31 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
// perform command // perform command
if (id == CMD_ID_IF) { if (id == CMD_ID_IF) {
// check the argument // check the argument
skip_state = (strncmp(argv[0], _ARG_TRUE, _ARG_MAX_LEN) != 0); // "if true" or "if false" // "if true" or "if false"
skip_state = (strncmp(argv[0], _ARG_TRUE, _ARG_MAX_LEN) == 0) ? 0 : _SKIP_BLOCK;
ifcnt++; ifcnt++;
if (syntax_error && err_str) if (syntax_error && err_str)
snprintf(err_str, _ERR_STR_LEN, "syntax error after 'if'"); snprintf(err_str, _ERR_STR_LEN, "syntax error after 'if'");
ret = !syntax_error; ret = !syntax_error;
} }
else if (id == CMD_ID_ELIF) {
// check syntax errors
if (ifcnt == 0) {
if (err_str) snprintf(err_str, _ERR_STR_LEN, "'elif' without 'if'");
syntax_error = true;
return false;
}
// skip state handling, check the argument if required
// "if true" or "if false"
skip_state = !skip_state ? _SKIP_TILL_END :
((strncmp(argv[0], _ARG_TRUE, _ARG_MAX_LEN) == 0) ? 0 : _SKIP_BLOCK);
if (syntax_error && err_str)
snprintf(err_str, _ERR_STR_LEN, "syntax error after 'elif'");
ret = !syntax_error;
}
else if (id == CMD_ID_ELSE) { else if (id == CMD_ID_ELSE) {
// check syntax errors // check syntax errors
if (ifcnt == 0) { if (ifcnt == 0) {
@ -658,7 +688,7 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
} }
// turn the skip state // turn the skip state
skip_state = !skip_state; skip_state = skip_state ? 0 : _SKIP_TILL_END;
ret = true; ret = true;
} }
@ -671,7 +701,7 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
} }
// close last "if" // close last "if"
skip_state = false; skip_state = 0;
ifcnt--; ifcnt--;
ret = true; ret = true;
@ -969,25 +999,32 @@ bool run_line(const char* line_start, const char* line_end, u32* flags, char* er
} }
// block out of control flow commands // block out of control flow commands
if (if_cond && ((cmdid == CMD_ID_IF) || (cmdid == CMD_ID_ELSE) || (cmdid == CMD_ID_END) || (cmdid == CMD_ID_GOTO))) { if (if_cond && ((cmdid == CMD_ID_IF) || (cmdid == CMD_ID_ELIF) || (cmdid == CMD_ID_ELSE) || (cmdid == CMD_ID_END) ||
(cmdid == CMD_ID_GOTO))) {
if (err_str) snprintf(err_str, _ERR_STR_LEN, "control flow error"); if (err_str) snprintf(err_str, _ERR_STR_LEN, "control flow error");
syntax_error = true; syntax_error = true;
return false; return false;
} }
// handle "if" // handle "if" / elif
if (cmdid == CMD_ID_IF) { if ((cmdid == CMD_ID_IF) || (cmdid == CMD_ID_ELIF)) {
// set defaults // set defaults
argc = 1; argc = 1;
strncpy(argv[0], _ARG_FALSE, _ARG_MAX_LEN); strncpy(argv[0], _ARG_FALSE, _ARG_MAX_LEN);
// skip to behind the "if" command // elif handling
if ((cmdid == CMD_ID_ELIF) && !skip_state) {
skip_state = _SKIP_TO_END;
return true;
}
// skip to behind the "if"/"elif" command
char* line_start_next = (char*) line_start; char* line_start_next = (char*) line_start;
for (; IS_WHITESPACE(*line_start_next); line_start_next++); for (; IS_WHITESPACE(*line_start_next); line_start_next++);
line_start_next += strlen(_CMD_IF); line_start_next += strlen((cmdid == CMD_ID_IF) ? _CMD_IF : _CMD_ELIF);
// run condition, take over result // run condition, take over result
if (run_line(line_start_next, line_end, flags, err_str, true)) if (run_line(line_start_next, line_end, flags, err_str, true)))
strncpy(argv[0], _ARG_TRUE, _ARG_MAX_LEN); strncpy(argv[0], _ARG_TRUE, _ARG_MAX_LEN);
} }
@ -1256,7 +1293,7 @@ bool ExecuteGM9Script(const char* path_script) {
// reset control flow global vars // reset control flow global vars
ifcnt = 0; ifcnt = 0;
skip_state = false; skip_state = 0;
syntax_error = false; syntax_error = false;
// fetch script - if no path is given, assume script already in script buffer // fetch script - if no path is given, assume script already in script buffer
@ -1326,7 +1363,7 @@ bool ExecuteGM9Script(const char* path_script) {
// skip state handling // skip state handling
char* skip_ptr = ptr; char* skip_ptr = ptr;
if (skip_state) { if (skip_state) {
skip_ptr = skip_block(line_end + 1, false, false); skip_ptr = skip_block(line_end + 1, (skip_state == _SKIP_TILL_END), false);
if (!skip_ptr) snprintf(err_str, _ERR_STR_LEN, "unclosed conditional"); if (!skip_ptr) snprintf(err_str, _ERR_STR_LEN, "unclosed conditional");
} }