|
|
@ -15,10 +15,11 @@ typedef enum { |
|
|
|
PARS_ARG_BLOB_PREAMBLE, |
|
|
|
PARS_ARG_BLOB_PREAMBLE, |
|
|
|
// command with no args terminated by whitespace, discard whitespace until newline and then run callback.
|
|
|
|
// command with no args terminated by whitespace, discard whitespace until newline and then run callback.
|
|
|
|
// error on non-whitespace
|
|
|
|
// error on non-whitespace
|
|
|
|
PARS_DISCARD_WHITESPACE_ENDL, |
|
|
|
PARS_TRAILING_WHITE, |
|
|
|
PARS_DISCARD_LINE, // used after detecting error
|
|
|
|
PARS_DISCARD_LINE, // used after detecting error
|
|
|
|
} parser_state_t; |
|
|
|
} parser_state_t; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define MAX_CHARBUF_LEN 256 |
|
|
|
|
|
|
|
|
|
|
|
/** parser internal state struct */ |
|
|
|
/** parser internal state struct */ |
|
|
|
static struct { |
|
|
|
static struct { |
|
|
@ -28,9 +29,11 @@ static struct { |
|
|
|
parser_state_t state; // current parser internal state
|
|
|
|
parser_state_t state; // current parser internal state
|
|
|
|
|
|
|
|
|
|
|
|
// string buffer, chars collected here until recognized
|
|
|
|
// string buffer, chars collected here until recognized
|
|
|
|
char charbuf[256]; |
|
|
|
char charbuf[MAX_CHARBUF_LEN]; |
|
|
|
uint16_t charbuf_i; |
|
|
|
uint16_t charbuf_i; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int8_t blob_preamble_cnt; // preamble counter, if 0, was just #, must read count
|
|
|
|
|
|
|
|
|
|
|
|
// recognized complete command level strings (FUNCtion) - exact copy from command struct
|
|
|
|
// recognized complete command level strings (FUNCtion) - exact copy from command struct
|
|
|
|
char cur_levels[MAX_LEVEL_COUNT][MAX_CMD_LEN]; |
|
|
|
char cur_levels[MAX_LEVEL_COUNT][MAX_CMD_LEN]; |
|
|
|
uint8_t cur_level_i; // next free level slot index
|
|
|
|
uint8_t cur_level_i; // next free level slot index
|
|
|
@ -41,7 +44,7 @@ static struct { |
|
|
|
|
|
|
|
|
|
|
|
SCPI_argval_t args[MAX_PARAM_COUNT]; |
|
|
|
SCPI_argval_t args[MAX_PARAM_COUNT]; |
|
|
|
uint8_t arg_i; // next free argument slot index
|
|
|
|
uint8_t arg_i; // next free argument slot index
|
|
|
|
} pstate = { |
|
|
|
} pst = { |
|
|
|
// defaults
|
|
|
|
// defaults
|
|
|
|
.err_queue_i = 0, |
|
|
|
.err_queue_i = 0, |
|
|
|
.state = PARS_COMMAND, |
|
|
|
.state = PARS_COMMAND, |
|
|
@ -58,7 +61,15 @@ static void pars_cmd_space(void); // space ending a command |
|
|
|
static void pars_cmd_newline(void); // LF
|
|
|
|
static void pars_cmd_newline(void); // LF
|
|
|
|
static void pars_cmd_semicolon(void); // semicolon right after a command
|
|
|
|
static void pars_cmd_semicolon(void); // semicolon right after a command
|
|
|
|
static bool pars_match_cmd(bool partial); |
|
|
|
static bool pars_match_cmd(bool partial); |
|
|
|
|
|
|
|
static void pars_arg_char(char c); |
|
|
|
|
|
|
|
static void pars_arg_comma(void); |
|
|
|
|
|
|
|
static void pars_arg_newline(void); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool try_match_cmd(const SCPI_command_t *cmd, bool partial); |
|
|
|
|
|
|
|
static void charbuf_terminate(void); |
|
|
|
|
|
|
|
static void charbuf_append(char c); |
|
|
|
|
|
|
|
static void pars_run_callback(void); |
|
|
|
|
|
|
|
static void arg_convert_value(void); |
|
|
|
|
|
|
|
|
|
|
|
// char matching
|
|
|
|
// char matching
|
|
|
|
#define INRANGE(c, a, b) ((c) >= (a) && (c) <= (b)) |
|
|
|
#define INRANGE(c, a, b) ((c) >= (a) && (c) <= (b)) |
|
|
@ -70,38 +81,69 @@ static bool pars_match_cmd(bool partial); |
|
|
|
|
|
|
|
|
|
|
|
#define IS_IDENT_CHAR(c) (IS_LCASE_CHAR((c)) || IS_UCASE_CHAR((c)) || IS_NUMBER_CHAR((c)) || (c) == '_' || (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_INT_CHAR(c) IS_NUMBER_CHAR((c)) |
|
|
|
#define IS_FLOAT_CHAR(c) (IS_NUMBER_CHAR((c)) || (c) == '.' || (c) == 'e' || (c) == 'E' || (e) == '+' || (e) == '-') |
|
|
|
#define IS_FLOAT_CHAR(c) (IS_NUMBER_CHAR((c)) || (c) == '.' || (c) == 'e' || (c) == 'E' || (c) == '+' || (c) == '-') |
|
|
|
|
|
|
|
|
|
|
|
#define CHAR_TO_LOWER(ucase) ((ucase) + 32) |
|
|
|
#define CHAR_TO_LOWER(ucase) ((ucase) + 32) |
|
|
|
#define CHAR_TO_UPPER(lcase) ((lcase) - 32) |
|
|
|
#define CHAR_TO_UPPER(lcase) ((lcase) - 32) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Reset parser state. */ |
|
|
|
static void pars_reset_cmd(void) |
|
|
|
static void pars_reset_cmd(void) |
|
|
|
{ |
|
|
|
{ |
|
|
|
pstate.state = PARS_COMMAND; |
|
|
|
pst.state = PARS_COMMAND; |
|
|
|
pstate.charbuf_i = 0; |
|
|
|
pst.charbuf_i = 0; |
|
|
|
pstate.cur_level_i = 0; |
|
|
|
pst.cur_level_i = 0; |
|
|
|
pstate.cmdbuf_kept = false; |
|
|
|
pst.cmdbuf_kept = false; |
|
|
|
pstate.matched_cmd = NULL; |
|
|
|
pst.matched_cmd = NULL; |
|
|
|
pstate.arg_i = 0; |
|
|
|
pst.arg_i = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Reset parser state, keep level (semicolon) */ |
|
|
|
static void pars_reset_cmd_keeplevel(void) |
|
|
|
static void pars_reset_cmd_keeplevel(void) |
|
|
|
{ |
|
|
|
{ |
|
|
|
pstate.state = PARS_COMMAND; |
|
|
|
pst.state = PARS_COMMAND; |
|
|
|
pstate.charbuf_i = 0; |
|
|
|
pst.charbuf_i = 0; |
|
|
|
// rewind to last colon
|
|
|
|
// rewind to last colon
|
|
|
|
|
|
|
|
|
|
|
|
if (pstate.cur_level_i > 0) { |
|
|
|
if (pst.cur_level_i > 0) { |
|
|
|
pstate.cur_level_i--; // keep prev levels
|
|
|
|
pst.cur_level_i--; // keep prev levels
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pst.cmdbuf_kept = true; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pst.matched_cmd = NULL; |
|
|
|
|
|
|
|
pst.arg_i = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pstate.cmdbuf_kept = true; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pstate.matched_cmd = NULL; |
|
|
|
/** Add a byte to charbuf, error on overflow */ |
|
|
|
pstate.arg_i = 0; |
|
|
|
static void charbuf_append(char c) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (pst.charbuf_i >= MAX_CHARBUF_LEN) { |
|
|
|
|
|
|
|
printf("ERROR string buffer overflow.\n");//TODO error
|
|
|
|
|
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pst.charbuf[pst.charbuf_i++] = c; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Terminate charbuf and rewind the pointer to start */ |
|
|
|
|
|
|
|
static void charbuf_terminate(void) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
pst.charbuf[pst.charbuf_i] = '\0'; |
|
|
|
|
|
|
|
pst.charbuf_i = 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Run the matched command's callback with the arguments */ |
|
|
|
|
|
|
|
static void pars_run_callback(void) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (pst.matched_cmd != NULL) { |
|
|
|
|
|
|
|
pst.matched_cmd->callback(pst.args); // run
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -110,26 +152,18 @@ void scpi_handle_byte(const uint8_t b) |
|
|
|
// TODO handle blob here
|
|
|
|
// TODO handle blob here
|
|
|
|
const char c = (char) b; |
|
|
|
const char c = (char) b; |
|
|
|
|
|
|
|
|
|
|
|
switch (pstate.state) { |
|
|
|
switch (pst.state) { |
|
|
|
case PARS_COMMAND: |
|
|
|
case PARS_COMMAND: |
|
|
|
// Collecting command
|
|
|
|
// Collecting command
|
|
|
|
|
|
|
|
|
|
|
|
if (pstate.charbuf_i == 0 && pstate.cur_level_i == 0) { |
|
|
|
|
|
|
|
if (IS_WHITESPACE(c)) { |
|
|
|
|
|
|
|
// leading whitespace is ignored
|
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (IS_IDENT_CHAR(c)) { |
|
|
|
if (IS_IDENT_CHAR(c)) { |
|
|
|
// valid command char
|
|
|
|
// valid command char
|
|
|
|
|
|
|
|
|
|
|
|
if (pstate.charbuf_i < MAX_CMD_LEN) { |
|
|
|
if (pst.charbuf_i < MAX_CMD_LEN) { |
|
|
|
pstate.charbuf[pstate.charbuf_i++] = c; |
|
|
|
charbuf_append(c); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
printf("ERROR command part too long.\n");//TODO error
|
|
|
|
printf("ERROR command part too long.\n");//TODO error
|
|
|
|
pstate.state = PARS_DISCARD_LINE; |
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -155,7 +189,7 @@ void scpi_handle_byte(const uint8_t b) |
|
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
default: |
|
|
|
printf("ERROR unexpected char '%c' in command.\n", c);//TODO error
|
|
|
|
printf("ERROR unexpected char '%c' in command.\n", c);//TODO error
|
|
|
|
pstate.state = PARS_DISCARD_LINE; |
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
break; |
|
|
|
break; |
|
|
@ -167,41 +201,225 @@ void scpi_handle_byte(const uint8_t b) |
|
|
|
} |
|
|
|
} |
|
|
|
break; |
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
case PARS_TRAILING_WHITE: |
|
|
|
|
|
|
|
if (IS_WHITESPACE(c)) break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (c == '\n') { |
|
|
|
|
|
|
|
pars_run_callback(); |
|
|
|
|
|
|
|
pars_reset_cmd(); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
printf("ERROR unexpected char '%c' in trailing whitespace.\n", c);//TODO error
|
|
|
|
|
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
break; // whitespace discarded
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case PARS_ARG: |
|
|
|
|
|
|
|
if (IS_WHITESPACE(c)) break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (c) { |
|
|
|
|
|
|
|
case ',': |
|
|
|
|
|
|
|
pars_arg_comma(); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case '\n': |
|
|
|
|
|
|
|
pars_arg_newline(); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
pars_arg_char(c); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case PARS_ARG_STR_APOS: |
|
|
|
|
|
|
|
if (c == '\'') { |
|
|
|
|
|
|
|
// end of string
|
|
|
|
|
|
|
|
pst.state = PARS_ARG; // next will be newline or comma (or ignored spaces)
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
charbuf_append(c); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case PARS_ARG_STR_QUOT: |
|
|
|
|
|
|
|
if (c == '"') { |
|
|
|
|
|
|
|
// end of string
|
|
|
|
|
|
|
|
pst.state = PARS_ARG; // next will be newline or comma (or ignored spaces)
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
charbuf_append(c); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO more states
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Non-whitespace and non-comma char received in arg. */ |
|
|
|
|
|
|
|
static void pars_arg_char(char c) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
switch (pst.matched_cmd->params[pst.arg_i]) { |
|
|
|
|
|
|
|
case SCPI_DT_STRING: |
|
|
|
|
|
|
|
if (c == '\'') { |
|
|
|
|
|
|
|
pst.state = PARS_ARG_STR_APOS; |
|
|
|
|
|
|
|
} else if (c == '"') { |
|
|
|
|
|
|
|
pst.state = PARS_ARG_STR_QUOT; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
printf("ERROR unexpected char '%c', should be ' or \"\n", c);//TODO error
|
|
|
|
|
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case SCPI_DT_BLOB: |
|
|
|
|
|
|
|
if (c == '#') { |
|
|
|
|
|
|
|
pst.state = PARS_ARG_BLOB_PREAMBLE; |
|
|
|
|
|
|
|
pst.blob_preamble_cnt = 0; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
printf("ERROR unexpected char '%c', binary block should start with #\n", c);//TODO error
|
|
|
|
|
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case SCPI_DT_FLOAT: |
|
|
|
|
|
|
|
if (!IS_FLOAT_CHAR(c)) { |
|
|
|
|
|
|
|
printf("ERROR unexpected char '%c' in float.\n", c);//TODO error
|
|
|
|
|
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
charbuf_append(c); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case SCPI_DT_INT: |
|
|
|
|
|
|
|
if (!IS_FLOAT_CHAR(c)) { |
|
|
|
|
|
|
|
printf("ERROR unexpected char '%d' in int.\n", c);//TODO error
|
|
|
|
|
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
charbuf_append(c); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
charbuf_append(c); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Received a comma while collecting an arg */ |
|
|
|
|
|
|
|
static void pars_arg_comma(void) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (pst.arg_i == pst.matched_cmd->param_cnt - 1) { |
|
|
|
|
|
|
|
// it was the last argument
|
|
|
|
|
|
|
|
// comma illegal
|
|
|
|
|
|
|
|
printf("ERROR unexpected comma after the last argument\n");//TODO error
|
|
|
|
|
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Convert to the right type
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
arg_convert_value(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void pars_arg_newline(void) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (pst.arg_i < pst.matched_cmd->param_cnt - 1) { |
|
|
|
|
|
|
|
// not the last arg yet - fail
|
|
|
|
|
|
|
|
printf("ERROR not enough arguments!\n");//TODO error
|
|
|
|
|
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
arg_convert_value(); |
|
|
|
|
|
|
|
pars_run_callback(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pars_reset_cmd(); // start a new command
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Convert BOOL, FLOAT or INT char to arg type and advance to next */ |
|
|
|
|
|
|
|
static void arg_convert_value(void) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
charbuf_terminate(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SCPI_argval_t *dest = &pst.args[pst.arg_i]; |
|
|
|
|
|
|
|
int j; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (pst.matched_cmd->params[pst.arg_i]) { |
|
|
|
|
|
|
|
case SCPI_DT_BOOL: |
|
|
|
|
|
|
|
if (strcasecmp(pst.charbuf, "1") == 0) { |
|
|
|
|
|
|
|
dest->BOOL = 1; |
|
|
|
|
|
|
|
} else if (strcasecmp(pst.charbuf, "0") == 0) { |
|
|
|
|
|
|
|
dest->BOOL = 0; |
|
|
|
|
|
|
|
} else if (strcasecmp(pst.charbuf, "ON") == 0) { |
|
|
|
|
|
|
|
dest->BOOL = 1; |
|
|
|
|
|
|
|
} else if (strcasecmp(pst.charbuf, "OFF") == 0) { |
|
|
|
|
|
|
|
dest->BOOL = 0; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
printf("ERROR argument mismatch for type BOOL\n");//TODO error
|
|
|
|
|
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case SCPI_DT_FLOAT: |
|
|
|
|
|
|
|
j = sscanf(pst.charbuf, "%f", &dest->FLOAT); |
|
|
|
|
|
|
|
if (j == 0) { |
|
|
|
|
|
|
|
printf("ERROR failed to convert %s to FLOAT\n", pst.charbuf);//TODO error
|
|
|
|
|
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case SCPI_DT_INT: |
|
|
|
|
|
|
|
j = sscanf(pst.charbuf, "%d", &dest->INT); |
|
|
|
|
|
|
|
if (j == 0) { |
|
|
|
|
|
|
|
printf("ERROR failed to convert %s to INT\n", pst.charbuf);//TODO error
|
|
|
|
|
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO string
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
// impossible
|
|
|
|
|
|
|
|
printf("ERROR unexpected data type\n");//TODO error
|
|
|
|
|
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// proceed to next argument
|
|
|
|
|
|
|
|
pst.arg_i++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Colon received when collecting command parts */ |
|
|
|
static void pars_cmd_colon(void) |
|
|
|
static void pars_cmd_colon(void) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (pstate.charbuf_i == 0) { |
|
|
|
if (pst.charbuf_i == 0) { |
|
|
|
// No command text before colon
|
|
|
|
// No command text before colon
|
|
|
|
|
|
|
|
|
|
|
|
if (pstate.cur_level_i == 0 || pstate.cmdbuf_kept) { |
|
|
|
if (pst.cur_level_i == 0 || pst.cmdbuf_kept) { |
|
|
|
// top level command starts with colon (or after semicolon - reset level)
|
|
|
|
// top level command starts with colon (or after semicolon - reset level)
|
|
|
|
pars_reset_cmd(); |
|
|
|
pars_reset_cmd(); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
// colon after nothing - error
|
|
|
|
// colon after nothing - error
|
|
|
|
printf("ERROR unexpected colon in command.\n");//TODO error
|
|
|
|
printf("ERROR unexpected colon in command.\n");//TODO error
|
|
|
|
pstate.state = PARS_DISCARD_LINE; |
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
// internal colon - partial match
|
|
|
|
// internal colon - partial match
|
|
|
|
if (pars_match_cmd(true)) { |
|
|
|
if (pars_match_cmd(true)) { |
|
|
|
printf("OK partial cmd, last segment = %s\n", pstate.cur_levels[pstate.cur_level_i - 1]); |
|
|
|
printf("OK partial cmd, last segment = %s\n", pst.cur_levels[pst.cur_level_i - 1]); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
printf("ERROR no such command (colon).\n");//TODO error
|
|
|
|
printf("ERROR no such command (colon).\n");//TODO error
|
|
|
|
pstate.state = PARS_DISCARD_LINE; |
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Newline received when collecting command - end command and execute. */ |
|
|
|
static void pars_cmd_newline(void) |
|
|
|
static void pars_cmd_newline(void) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (pstate.cur_level_i == 0 && pstate.charbuf_i == 0) { |
|
|
|
if (pst.cur_level_i == 0 && pst.charbuf_i == 0) { |
|
|
|
// nothing before newline
|
|
|
|
// nothing before newline
|
|
|
|
pars_reset_cmd(); |
|
|
|
pars_reset_cmd(); |
|
|
|
return; |
|
|
|
return; |
|
|
@ -209,20 +427,41 @@ static void pars_cmd_newline(void) |
|
|
|
|
|
|
|
|
|
|
|
// complete match
|
|
|
|
// complete match
|
|
|
|
if (pars_match_cmd(false)) { |
|
|
|
if (pars_match_cmd(false)) { |
|
|
|
if (pstate.matched_cmd->param_cnt == 0) { |
|
|
|
if (pst.matched_cmd->param_cnt == 0) { |
|
|
|
// no param command - OK
|
|
|
|
// no param command - OK
|
|
|
|
pstate.matched_cmd->callback(pstate.args); // args are empty
|
|
|
|
pst.matched_cmd->callback(pst.args); // args are empty
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
printf("ERROR command missing arguments.\n");//TODO error
|
|
|
|
printf("ERROR command missing arguments.\n");//TODO error
|
|
|
|
pars_reset_cmd(); |
|
|
|
pars_reset_cmd(); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
printf("ERROR no such command (newline) %s.\n", pstate.charbuf);//TODO error
|
|
|
|
printf("ERROR no such command (newline) %s.\n", pst.charbuf);//TODO error
|
|
|
|
pstate.state = PARS_DISCARD_LINE; |
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Whitespace received when collecting command parts */ |
|
|
|
|
|
|
|
static void pars_cmd_space(void) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (pst.cur_level_i == 0 && pst.charbuf_i == 0) { |
|
|
|
|
|
|
|
// leading whitespace, ignore
|
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (pars_match_cmd(false)) { |
|
|
|
|
|
|
|
if (pst.matched_cmd->param_cnt == 0) { |
|
|
|
|
|
|
|
// no commands
|
|
|
|
|
|
|
|
pst.state = PARS_TRAILING_WHITE; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
pst.state = PARS_ARG; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
printf("ERROR no such command (space) %s.\n", pst.charbuf);//TODO error
|
|
|
|
|
|
|
|
pst.state = PARS_DISCARD_LINE; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Check if chars equal, ignore case */ |
|
|
|
/** Check if chars equal, ignore case */ |
|
|
|
static bool char_equals_ci(char a, char b) |
|
|
|
static bool char_equals_ci(char a, char b) |
|
|
@ -285,18 +524,14 @@ 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 bool pars_match_cmd(bool partial) |
|
|
|
static bool pars_match_cmd(bool partial) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// terminate segment
|
|
|
|
charbuf_terminate(); // zero-end and rewind index
|
|
|
|
pstate.charbuf[pstate.charbuf_i] = '\0'; |
|
|
|
|
|
|
|
pstate.charbuf_i = 0; // rewind
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char *dest = pstate.cur_levels[pstate.cur_level_i++]; |
|
|
|
// copy to level table
|
|
|
|
strcpy(dest, pstate.charbuf); // copy to level table
|
|
|
|
char *dest = pst.cur_levels[pst.cur_level_i++]; |
|
|
|
|
|
|
|
strcpy(dest, pst.charbuf); |
|
|
|
|
|
|
|
|
|
|
|
for (uint16_t i = 0; i < scpi_cmd_lang_len; i++) { |
|
|
|
for (uint16_t i = 0; i < scpi_cmd_lang_len; i++) { |
|
|
|
|
|
|
|
|
|
|
@ -313,7 +548,7 @@ static bool pars_match_cmd(bool partial) |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
// exact match found
|
|
|
|
// exact match found
|
|
|
|
pstate.matched_cmd = cmd; |
|
|
|
pst.matched_cmd = cmd; |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -326,22 +561,22 @@ static bool pars_match_cmd(bool partial) |
|
|
|
/** Try to match current state to a given command */ |
|
|
|
/** Try to match current state to a given command */ |
|
|
|
static bool try_match_cmd(const SCPI_command_t *cmd, bool partial) |
|
|
|
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 (pst.cur_level_i > cmd->level_cnt) return false; // command too short
|
|
|
|
if (pstate.cur_level_i == 0) return false; // nothing to match
|
|
|
|
if (pst.cur_level_i == 0) return false; // nothing to match
|
|
|
|
|
|
|
|
|
|
|
|
if (partial) { |
|
|
|
if (partial) { |
|
|
|
if (pstate.cur_level_i == cmd->level_cnt) { |
|
|
|
if (pst.cur_level_i == cmd->level_cnt) { |
|
|
|
return false; // would be exact match
|
|
|
|
return false; // would be exact match
|
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
if (pstate.cur_level_i != cmd->level_cnt) { |
|
|
|
if (pst.cur_level_i != cmd->level_cnt) { |
|
|
|
return false; // can be only partial match
|
|
|
|
return false; // can be only partial match
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// check for match up to current index
|
|
|
|
// check for match up to current index
|
|
|
|
for (uint8_t j = 0; j < pstate.cur_level_i; j++) { |
|
|
|
for (uint8_t j = 0; j < pst.cur_level_i; j++) { |
|
|
|
if (!level_str_matches(pstate.cur_levels[j], cmd->levels[j])) { |
|
|
|
if (!level_str_matches(pst.cur_levels[j], cmd->levels[j])) { |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|