diff --git a/main.c b/main.c index 83e895a..4a5e0d2 100644 --- a/main.c +++ b/main.c @@ -1,4 +1,5 @@ #include +#include #include #include "scpi_parser.h" @@ -15,7 +16,7 @@ const SCPI_command_t scpi_cmd_lang[4] = { .callback = cmd_aIDNq_cb }, { - .level_cnt = 2, .levels = {"APPLy", "SINe"}, + .level_cnt = 3, .levels = {"APPLy", "SINe"}, .param_cnt = 3, .params = {SCPI_DT_INT, SCPI_DT_FLOAT, SCPI_DT_FLOAT}, .callback = cmd_APPL_SIN_cb }, @@ -57,9 +58,15 @@ void cmd_FREQ_cb(const SCPI_argval_t *args) -int main(int argc, const char**argv) +int main() { - const char *inp = argv[1]; + //const char *inp = "*IDN?\n"; + const char *inp = "FREQ 123\n"; + + for(int i=0;i= (a) && (c) <= (b)) @@ -64,39 +68,44 @@ static void pars_match_level(void); // match charbuf content to a level, advance #define IS_UCASE_CHAR(c) INRANGE((c), 'A', 'Z') #define IS_NUMBER_CHAR(c) INRANGE((c), '0', '9') -#define IS_IDENT_CHAR(c) (IS_LCASE_CHAR((c)) || IS_UCASE_CHAR((c)) || IS_NUMBER_CHAR((c)) || (c) == '_') +#define IS_IDENT_CHAR(c) (IS_LCASE_CHAR((c)) || IS_UCASE_CHAR((c)) || IS_NUMBER_CHAR((c)) || (c) == '_' || (c) == '*' || (c) == '?') #define IS_INT_CHAR(c) IS_NUMBER_CHAR((c)) #define IS_FLOAT_CHAR(c) (IS_NUMBER_CHAR((c)) || (c) == '.' || (c) == 'e' || (c) == 'E' || (e) == '+' || (e) == '-') +#define CHAR_TO_LOWER(ucase) ((ucase) + 32) +#define CHAR_TO_UPPER(lcase) ((lcase) - 32) + + static void pars_reset_cmd(void) { pstate.state = PARS_COMMAND; - pstate.charbuf_ptr = 0; - pstate.cur_level_ptr = 0; + pstate.charbuf_i = 0; + pstate.cur_level_i = 0; pstate.cmdbuf_kept = false; - pstate.detected_cmd = NULL; - pstate.arg_ptr = 0; + pstate.matched_cmd = NULL; + pstate.arg_i = 0; } + static void pars_reset_cmd_keeplevel(void) { pstate.state = PARS_COMMAND; - pstate.charbuf_ptr = 0; + pstate.charbuf_i = 0; // rewind to last colon - if (pstate.cur_level_ptr > 0) { - pstate.cur_level_ptr--; // keep prev levels + if (pstate.cur_level_i > 0) { + pstate.cur_level_i--; // keep prev levels } pstate.cmdbuf_kept = true; - pstate.detected_cmd = NULL; - pstate.arg_ptr = 0; + pstate.matched_cmd = NULL; + pstate.arg_i = 0; } -void scpi_receive_byte(const uint8_t b) +void scpi_handle_byte(const uint8_t b) { // TODO handle blob here const char c = (char) b; @@ -105,7 +114,7 @@ void scpi_receive_byte(const uint8_t b) case PARS_COMMAND: // Collecting command - if (pstate.charbuf_ptr == 0 && pstate.cur_level_ptr == 0) { + if (pstate.charbuf_i == 0 && pstate.cur_level_i == 0) { if (IS_WHITESPACE(c)) { // leading whitespace is ignored break; @@ -116,8 +125,8 @@ void scpi_receive_byte(const uint8_t b) if (IS_IDENT_CHAR(c)) { // valid command char - if (pstate.charbuf_ptr < MAX_CMD_LEN) { - pstate.charbuf[pstate.charbuf_ptr++] = c; + if (pstate.charbuf_i < MAX_CMD_LEN) { + pstate.charbuf[pstate.charbuf_i++] = c; } else { printf("ERROR command part too long.\n");//TODO error pstate.state = PARS_DISCARD_LINE; @@ -127,7 +136,7 @@ void scpi_receive_byte(const uint8_t b) // invalid or delimiter if (IS_WHITESPACE(c)) { -// pars_cmd_space(); // whitespace in command - end of command? + pars_cmd_space(); // whitespace in command - end of command, start of args (?) break; } @@ -137,7 +146,7 @@ void scpi_receive_byte(const uint8_t b) break; case '\n': // line terminator -// pars_cmd_newline(); + pars_cmd_newline(); break; case ';': // ends a command, does not reset cmd path. @@ -159,16 +168,17 @@ void scpi_receive_byte(const uint8_t b) break; // TODO + } } static void pars_cmd_colon(void) { - if (pstate.charbuf_ptr == 0) { + if (pstate.charbuf_i == 0) { // No command text before colon - if (pstate.cur_level_ptr == 0 || pstate.cmdbuf_kept) { + if (pstate.cur_level_i == 0 || pstate.cmdbuf_kept) { // top level command starts with colon (or after semicolon - reset level) pars_reset_cmd(); } else { @@ -178,13 +188,40 @@ static void pars_cmd_colon(void) } } else { - // internal colon - pars_match_level(); + // internal colon - partial match + if (pars_match_cmd(true)) { + printf("OK partial cmd, last segment = %s\n", pstate.cur_levels[pstate.cur_level_i - 1]); + } else { + printf("ERROR no such command (colon).\n");//TODO error + pstate.state = PARS_DISCARD_LINE; + } + } +} + + +static void pars_cmd_newline(void) +{ + if (pstate.cur_level_i == 0 && pstate.charbuf_i == 0) { + // nothing before newline + pars_reset_cmd(); + return; + } + + // complete match + if (pars_match_cmd(false)) { + if (pstate.matched_cmd->param_cnt == 0) { + // no param command - OK + pstate.matched_cmd->callback(pstate.args); // args are empty + } else { + printf("ERROR command missing arguments.\n");//TODO error + pars_reset_cmd(); + } + } else { + printf("ERROR no such command (newline) %s.\n", pstate.charbuf);//TODO error + pstate.state = PARS_DISCARD_LINE; } } -#define CHAR_TO_LOWER(ucase) ((ucase) + 32) -#define CHAR_TO_UPPER(lcase) ((lcase) - 32) /** Check if chars equal, ignore case */ @@ -248,12 +285,66 @@ static bool level_str_matches(const char *test, const char *pattern) } +// proto +static bool try_match_cmd(const SCPI_command_t *cmd, bool partial); -static void pars_match_level(void) + +static bool pars_match_cmd(bool partial) { // terminate segment - pstate.charbuf[pstate.charbuf_ptr] = '\0'; + pstate.charbuf[pstate.charbuf_i] = '\0'; + pstate.charbuf_i = 0; // rewind + + char *dest = pstate.cur_levels[pstate.cur_level_i++]; + strcpy(dest, pstate.charbuf); // copy to level table + + for (uint16_t i = 0; i < scpi_cmd_lang_len; i++) { + + const SCPI_command_t *cmd = &scpi_cmd_lang[i]; + + if (cmd->level_cnt > MAX_LEVEL_COUNT) { + // FAIL, too deep. Bad config + continue; + } - //TODO + if (try_match_cmd(cmd, partial)) { + if (partial) { + // match found, OK + return true; + } else { + // exact match found + pstate.matched_cmd = cmd; + return true; + } + } + } + + return false; } + +/** Try to match current state to a given command */ +static bool try_match_cmd(const SCPI_command_t *cmd, bool partial) +{ + if (pstate.cur_level_i > cmd->level_cnt) return false; // command too short + if (pstate.cur_level_i == 0) return false; // nothing to match + + if (partial) { + if (pstate.cur_level_i == cmd->level_cnt) { + return false; // would be exact match + } + } else { + if (pstate.cur_level_i != cmd->level_cnt) { + return false; // can be only partial match + } + } + + // check for match up to current index + for (uint8_t j = 0; j < pstate.cur_level_i; j++) { + if (!level_str_matches(pstate.cur_levels[j], cmd->levels[j])) { + return false; + } + } + + return true; +} diff --git a/scpi_parser.h b/scpi_parser.h index dce2d7a..76dfa49 100644 --- a/scpi_parser.h +++ b/scpi_parser.h @@ -49,4 +49,4 @@ extern const uint16_t scpi_cmd_lang_len; // number of commands extern const SCPI_command_t scpi_cmd_lang[]; // --------------- functions -------------------- - +void scpi_handle_byte(const uint8_t b);