diff --git a/main.c b/main.c index 40ddcfd..d11339a 100644 --- a/main.c +++ b/main.c @@ -3,13 +3,47 @@ #include #include "scpi_parser.h" -void cmd_aIDNq_cb(const SCPI_argval_t *args); -void cmd_APPL_SIN_cb(const SCPI_argval_t *args); -void cmd_APPL_TRI_cb(const SCPI_argval_t *args); -void cmd_FREQ_cb(const SCPI_argval_t *args); -void cmd_DISP_TEXT_cb(const SCPI_argval_t *args); -void cmd_DATA_BLOB_cb(const SCPI_argval_t *args); -void cmd_DATA_BLOB_data(const uint8_t *bytes); + + +void cmd_aIDNq_cb(const SCPI_argval_t *args) +{ + printf("cb *IDN?\n"); +} + + +void cmd_APPL_SIN_cb(const SCPI_argval_t *args) +{ + printf("cb APPLy:SINe\n"); +} + + +void cmd_APPL_TRI_cb(const SCPI_argval_t *args) +{ + printf("cb APPLy:TRIangle\n"); +} + + +void cmd_FREQ_cb(const SCPI_argval_t *args) +{ + printf("cb FREQuency %d\n", args[0].INT); +} + +void cmd_DISP_TEXT_cb(const SCPI_argval_t *args) +{ + printf("cb DISPlay:TEXT %s, %d\n", args[0].STRING, args[1].BOOL); +} + + +void cmd_DATA_BLOB_cb(const SCPI_argval_t *args) +{ + printf("cb DATA:BLOB %f, <%d>\n", args[0].FLOAT, args[1].BLOB_LEN); +} + + +void cmd_DATA_BLOB_data(const uint8_t *bytes) +{ + printf("blob item: %s\n", bytes); +} void cmd_STQENq_cb(const SCPI_argval_t *args) { @@ -76,73 +110,16 @@ const SCPI_command_t scpi_cmd_lang[] = { }; -void cmd_aIDNq_cb(const SCPI_argval_t *args) -{ - printf("cb *IDN?\n"); -} - - -void cmd_APPL_SIN_cb(const SCPI_argval_t *args) -{ - printf("cb APPLy:SINe\n"); -} - - -void cmd_APPL_TRI_cb(const SCPI_argval_t *args) -{ - printf("cb APPLy:TRIangle\n"); -} - - -void cmd_FREQ_cb(const SCPI_argval_t *args) -{ - printf("cb FREQuency %d\n", args[0].INT); -} - -void cmd_DISP_TEXT_cb(const SCPI_argval_t *args) -{ - printf("cb DISPlay:TEXT %s, %d\n", args[0].STRING, args[1].BOOL); -} - - -void cmd_DATA_BLOB_cb(const SCPI_argval_t *args) -{ - printf("cb DATA:BLOB %f, <%d>\n", args[0].FLOAT, args[1].BLOB_LEN); -} - - -void cmd_DATA_BLOB_data(const uint8_t *bytes) -{ - printf("blob item: %s\n", bytes); -} - - int main() { // const char *inp = "*IDN?\n"; // const char *inp = "FREQ 50\n"; - //const char *inp = "DISPlay:TEXT 'banana', OFF\nDISP:TEXT \"dblquot!\", 1\r\nFREQ 50\r\n"; + const char *inp = "DISPlay:TEXT 'ban\\\\'ana', OFF\nDISP:TEXT \"dblquot!\", 1\r\nFREQ 50\r\n"; - //const char *inp = "DATA:BLOB 13.456, #216AbcdEfghIjklMnop\nFREQ 50\r\n"; - const char *inp = "STAT:QUE:ENAB?;ENAB 0;ENAB?;:*IDN?\n"; +// const char *inp = "DATA:BLOB 13.456, #216AbcdEfghIjklMnop\nFREQ 50\r\n"; +// const char *inp = "STAT:QUE:ENAB?;ENAB \t 1;ENAB?;:*IDN?\n"; for (int i = 0; i < strlen(inp); i++) { scpi_handle_byte(inp[i]); } - - -// printf("%d\n", char_equals_ci('a','A')); -// printf("%d\n", char_equals_ci('z','z')); -// printf("%d\n", char_equals_ci('z','Z')); -// printf("%d\n", char_equals_ci('M','M')); -// printf("%d\n", char_equals_ci('M','q')); -// printf("%d\n", char_equals_ci('*','*')); -// printf("%d\n", char_equals_ci('a',' ')); - -// printf("%d\n", level_str_matches("A","A")); -// printf("%d\n", level_str_matches("AbCdE","ABCDE")); -// printf("%d\n", level_str_matches("*IDN?","*IDN?")); -// printf("%d\n", level_str_matches("*IDN?","*IDN")); -// printf("%d\n", level_str_matches("MEAS","MEASure")); - //printf("%d\n", level_str_matches("*FBAZ?","*FxyzBAZ?")); } diff --git a/scpi_parser.c b/scpi_parser.c index 0523aa8..bc155a9 100644 --- a/scpi_parser.c +++ b/scpi_parser.c @@ -7,18 +7,16 @@ #include typedef enum { - PARS_COMMAND, + PARS_COMMAND = 0, // collect generic arg, terminated with comma or newline. Leading and trailing whitespace ignored. - PARS_ARG, - PARS_ARG_STR_APOS, // collect arg - string with single quotes - PARS_ARG_STR_QUOT, // collect arg - string with double quotes - PARS_ARG_BLOB_PREAMBLE, + PARS_ARG, // generic argument (bool, float...) + PARS_ARG_STRING, // collect arg - string (special treatment for quotes) + PARS_ARG_BLOB_PREAMBLE, // #nDDD + PARS_ARG_BLOB_DISCARD, // discard blob - same as BLOB_BODY, but no callback or buffering PARS_ARG_BLOB_BODY, // blob body, callback for each group - // command with no args terminated by whitespace, discard whitespace until newline and then run callback. - // error on non-whitespace - PARS_TRAILING_WHITE, - PARS_TRAILING_WHITE_NOCB, // no callback. - PARS_DISCARD_LINE, // used after detecting error + PARS_TRAILING_WHITE, // command ready to run, waiting for end, only whitespace allowed + PARS_TRAILING_WHITE_NOCB, // discard whitespace until end of line, don't run callback on success + PARS_DISCARD_LINE, // used after detecting error - drop all chars until \n } parser_state_t; #define MAX_CHARBUF_LEN 256 @@ -37,6 +35,9 @@ static struct { int32_t blob_cnt; // preamble counter, if 0, was just #, must read count. Used also for blob body. int32_t blob_len; // total blob length to read + char string_quote; // symbol used to quote string + bool string_escape; // last char was backslash, next quote is literal + // recognized complete command level strings (FUNCtion) - exact copy from command struct char cur_levels[MAX_LEVEL_COUNT][MAX_CMD_LEN]; uint8_t cur_level_i; // next free level slot index @@ -47,18 +48,8 @@ static struct { SCPI_argval_t args[MAX_PARAM_COUNT]; uint8_t arg_i; // next free argument slot index -} pst = { - // defaults - .err_queue_i = 0, - .state = PARS_COMMAND, - .charbuf_i = 0, - .cur_level_i = 0, - .cmdbuf_kept = false, - .matched_cmd = NULL, - .blob_cnt = 0, - .blob_len = 0, - .arg_i = 0 -}; + +} pst; // initialized by all zeros static void pars_cmd_colon(void); // colon starting a command sub-segment @@ -107,8 +98,7 @@ static void pars_reset_cmd(void) pst.cmdbuf_kept = false; pst.matched_cmd = NULL; pst.arg_i = 0; - pst.blob_cnt = 0; - pst.blob_len = 0; + pst.string_escape = false; } @@ -117,18 +107,16 @@ static void pars_reset_cmd_keeplevel(void) { pst.state = PARS_COMMAND; pst.charbuf_i = 0; - // rewind to last colon + // rewind to last colon if (pst.cur_level_i > 0) { pst.cur_level_i--; // keep prev levels } pst.cmdbuf_kept = true; - pst.matched_cmd = NULL; pst.arg_i = 0; - pst.blob_cnt = 0; - pst.blob_len = 0; + pst.string_escape = false; } @@ -185,6 +173,12 @@ static void pars_run_callback(void) } +void scpi_discard_blob(void) +{ + pst.state = PARS_ARG_BLOB_DISCARD; +} + + void scpi_handle_byte(const uint8_t b) { // TODO handle blob here @@ -277,29 +271,26 @@ void scpi_handle_byte(const uint8_t b) } break; - // TODO escape sequence in string + case PARS_ARG_STRING: + // string - case PARS_ARG_STR_APOS: - if (c == '\'') { + if (c == pst.string_quote && !pst.string_escape) { // end of string pst.state = PARS_ARG; // next will be newline or comma (or ignored spaces) } else if (c == '\n') { printf("ERROR string literal not terminated.\n");//TODO error pst.state = PARS_DISCARD_LINE; } 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 if (c == '\n') { - printf("ERROR string literal not terminated.\n");//TODO error - pst.state = PARS_DISCARD_LINE; - } else { - charbuf_append(c); + if (pst.string_escape) { + charbuf_append(c); + pst.string_escape = false; + } else { + if (c == '\\') { + pst.string_escape = true; + } else { + charbuf_append(c); + } + } } break; @@ -309,6 +300,8 @@ void scpi_handle_byte(const uint8_t b) break; case PARS_ARG_BLOB_BODY: + // binary blob body with callback on buffer full + charbuf_append(c); pst.blob_cnt++; @@ -324,6 +317,17 @@ void scpi_handle_byte(const uint8_t b) pst.state = PARS_TRAILING_WHITE_NOCB; // discard trailing whitespace until newline } + break; + + case PARS_ARG_BLOB_DISCARD: + // binary blob, discard incoming data + + pst.blob_cnt++; + + if (pst.blob_cnt == pst.blob_len) { + pst.state = PARS_DISCARD_LINE; + } + break; } } @@ -557,27 +561,6 @@ static bool try_match_cmd(const SCPI_command_t *cmd, bool partial) 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_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 @@ -596,6 +579,27 @@ static void pars_arg_char(char c) } break; + case SCPI_DT_STRING: + if (c == '\'' || c == '"') { + pst.state = PARS_ARG_STRING; + pst.string_quote = c; + pst.string_escape = false; + } else { + printf("ERROR invalid string quote, or chars after string.\n");//TODO error + pst.state = PARS_DISCARD_LINE; + } + break; + + case SCPI_DT_BLOB: + if (c == '#') { + pst.state = PARS_ARG_BLOB_PREAMBLE; + pst.blob_cnt = 0; + } else { + printf("ERROR unexpected char '%c', binary block should start with #\n", c);//TODO error + pst.state = PARS_DISCARD_LINE; + } + break; + default: charbuf_append(c); break; diff --git a/scpi_parser.h b/scpi_parser.h index feac81a..e822a4c 100644 --- a/scpi_parser.h +++ b/scpi_parser.h @@ -57,5 +57,12 @@ typedef struct { // Zero terminated command struct array extern const SCPI_command_t scpi_cmd_lang[]; + // --------------- functions -------------------- + +/** + * SCPI parser entry point. + * All incoming bytes should be sent to this function. + */ void scpi_handle_byte(const uint8_t b); +