parser now uses error queue

master
Ondřej Hruška 9 years ago
parent 14e9df242d
commit 38aea9635e
  1. 10
      main.c
  2. 2
      scpi_errors.c
  3. 156
      scpi_parser.c

@ -73,7 +73,7 @@ const SCPI_command_t scpi_commands[] = {
}, },
{ {
.levels = {"APPLy", "TRIangle"}, .levels = {"APPLy", "TRIangle"},
.params = {SCPI_DT_INT, SCPI_DT_FLOAT, SCPI_DT_FLOAT}, .params = {SCPI_DT_BOOL, SCPI_DT_FLOAT, SCPI_DT_FLOAT},
.callback = cmd_APPL_TRI_cb .callback = cmd_APPL_TRI_cb
}, },
{ {
@ -118,11 +118,13 @@ int main()
// const char *inp = "*IDN?\n"; // const char *inp = "*IDN?\n";
// const char *inp = "FREQ 50\n"; // const char *inp = "FREQ 50\n";
// const char *inp = "DISP:TEXT 'ban\\\\ana', OFF\nDISP:TEXT \"dblquot!\", 1\r\nFREQ 50\r\n"; const char *inp = "DISP:TEXT 'ban\\\\ana', OFF\nDISP:TEXT \"dblquot!\", 1\r\nFREQ 50\r\n";
const char *inp = "FOO\nDATA:BLOB 13.456, #216AbcdEfghIjklMnop\nFREQ 50\r\n"; //const char *inp = "FOO\nDATA:BLOB 13.456, #216AbcdEfghIjklMnop\nFREQ 50\r\nAPPLY:MOO\n";
//const char *inp = "STAT:QUE:ENAB?;ENAB \t 1;ENAB?;:*IDN?\n"; //const char *inp = "STAT:QUE:ENAB?;ENAB \t 1;ENAB?;:*IDN?\n";
//const char *inp = "*idn?;foo;apply:sin 5,6,\n";
for (int i = 0; i < strlen(inp); i++) { for (int i = 0; i < strlen(inp); i++) {
scpi_handle_byte(inp[i]); scpi_handle_byte(inp[i]);
} }
@ -130,7 +132,7 @@ int main()
scpi_error_string(buf, E_EXE_DATA_QUESTIONABLE, "The data smells fishy"); scpi_error_string(buf, E_EXE_DATA_QUESTIONABLE, "The data smells fishy");
// scpi_read_error(buf), printf("%s\n", buf); scpi_read_error(buf), printf("%s\n", buf);
// scpi_add_error(E_CMD_BLOCK_DATA_ERROR, NULL); // scpi_add_error(E_CMD_BLOCK_DATA_ERROR, NULL);

@ -168,7 +168,7 @@ void scpi_error_string(char *buffer, SCPI_error_t errno, const char *extra)
if (extra != NULL) { if (extra != NULL) {
// extra info // extra info
offs = sprintf(buffer, ";"); // ; offs = sprintf(buffer, "; "); // ;
len += offs; len += offs;
buffer += offs; buffer += offs;

@ -81,6 +81,9 @@ static struct {
} pst = {0}; // initialized by all zeros } pst = {0}; // initialized by all zeros
// buffer for error messages
static char ebuf[256];
// ---------------- PRIVATE PROTOTYPES ------------------ // ---------------- PRIVATE PROTOTYPES ------------------
@ -184,13 +187,33 @@ uint8_t scpi_error_count(void)
return pst.err_queue_used; return pst.err_queue_used;
} }
static void err_no_such_command()
{
char *b = ebuf;
for (int i = 0; i < pst.cur_level_i; i++) {
if (i > 0) b += sprintf(b, ":");
b += sprintf(b, "%s", pst.cur_levels[i]);
}
scpi_add_error(E_CMD_UNDEFINED_HEADER, ebuf);
}
static void err_no_such_command_partial()
{
char *b = ebuf;
for (int i = 0; i < pst.cur_level_i; i++) {
b += sprintf(b, "%s:", pst.cur_levels[i]);
}
scpi_add_error(E_CMD_UNDEFINED_HEADER, ebuf);
}
// ----------------- INPUT PARSING ---------------- // ----------------- INPUT PARSING ----------------
void scpi_handle_byte(const uint8_t b) void scpi_handle_byte(const uint8_t b)
{ {
// TODO handle blob here
const char c = (char) b; const char c = (char) b;
switch (pst.state) { switch (pst.state) {
@ -203,7 +226,7 @@ void scpi_handle_byte(const uint8_t b)
if (pst.charbuf_i < SCPI_MAX_CMD_LEN) { if (pst.charbuf_i < SCPI_MAX_CMD_LEN) {
charbuf_append(c); charbuf_append(c);
} else { } else {
printf("ERROR command part too long.\n");//TODO error scpi_add_error(E_CMD_PROGRAM_MNEMONIC_TOO_LONG, NULL);
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} }
@ -229,7 +252,8 @@ void scpi_handle_byte(const uint8_t b)
break; break;
default: default:
printf("ERROR unexpected char '%c' in command.\n", c);//TODO error sprintf(ebuf, "Unexpected '%c' in command.", c);
scpi_add_error(E_CMD_INVALID_CHARACTER, ebuf);
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} }
} }
@ -253,7 +277,8 @@ void scpi_handle_byte(const uint8_t b)
pars_reset_cmd(); pars_reset_cmd();
} else { } else {
printf("ERROR unexpected char '%c' in trailing whitespace.\n", c);//TODO error sprintf(ebuf, "Unexpected '%c' in trailing whitespace.", c);
scpi_add_error(E_CMD_INVALID_CHARACTER, ebuf);
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} }
@ -287,7 +312,8 @@ void scpi_handle_byte(const uint8_t b)
// end of string // end of string
pst.state = PARS_ARG; // next will be newline or comma (or ignored spaces) pst.state = PARS_ARG; // next will be newline or comma (or ignored spaces)
} else if (c == '\n') { } else if (c == '\n') {
printf("ERROR string literal not terminated.\n");//TODO error scpi_add_error(E_CMD_STRING_DATA_ERROR, "String not terminated (unexpected newline).");
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} else { } else {
if (pst.string_escape) { if (pst.string_escape) {
@ -422,7 +448,7 @@ static uint8_t cmd_level_count(const SCPI_command_t *cmd)
static void charbuf_append(char c) static void charbuf_append(char c)
{ {
if (pst.charbuf_i >= MAX_CHARBUF_LEN) { if (pst.charbuf_i >= MAX_CHARBUF_LEN) {
printf("ERROR string buffer overflow.\n");//TODO error scpi_add_error(E_DEV_INPUT_BUFFER_OVERRUN, NULL);
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} }
@ -452,7 +478,8 @@ static void pars_cmd_colon(void)
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 scpi_add_error(E_CMD_SYNTAX_ERROR, "Unexpected colon.");
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} }
@ -461,7 +488,9 @@ static void pars_cmd_colon(void)
if (match_cmd(true)) { if (match_cmd(true)) {
// ok // ok
} else { } else {
printf("ERROR no such command: %s\n", pst.charbuf);//TODO error // error
err_no_such_command_partial();
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} }
} }
@ -473,22 +502,25 @@ static void pars_cmd_semicolon(void)
{ {
if (pst.cur_level_i == 0 && pst.charbuf_i == 0) { if (pst.cur_level_i == 0 && pst.charbuf_i == 0) {
// nothing before semicolon // nothing before semicolon
printf("ERROR semicolon not allowed here.\n");//TODO error scpi_add_error(E_CMD_SYNTAX_ERROR, "Semicolon not preceded by command.");
pars_reset_cmd(); pars_reset_cmd();
return; return;
} }
if (match_cmd(false)) { if (match_cmd(false)) {
if (cmd_param_count(pst.matched_cmd) == 0) { int req_cnt = cmd_param_count(pst.matched_cmd);
if (req_cnt == 0) {
// no param command - OK // no param command - OK
run_command_callback(); run_command_callback();
pars_reset_cmd_keeplevel(); // keep level - that's what semicolon does pars_reset_cmd_keeplevel(); // keep level - that's what semicolon does
} else { } else {
printf("ERROR command missing arguments.\n");//TODO error sprintf(ebuf, "Required %d, got 0.", req_cnt);
scpi_add_error(E_CMD_MISSING_PARAMETER, ebuf);
pars_reset_cmd(); pars_reset_cmd();
} }
} else { } else {
printf("ERROR no such command %s.\n", pst.charbuf);//TODO error err_no_such_command();
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} }
} }
@ -505,16 +537,22 @@ static void pars_cmd_newline(void)
// complete match // complete match
if (match_cmd(false)) { if (match_cmd(false)) {
if (cmd_param_count(pst.matched_cmd) == 0) { int req_cnt = cmd_param_count(pst.matched_cmd);
if (req_cnt == 0) {
// no param command - OK // no param command - OK
run_command_callback(); run_command_callback();
pars_reset_cmd(); pars_reset_cmd();
} else { } else {
printf("ERROR command missing arguments.\n");//TODO error // error
sprintf(ebuf, "Required %d, got 0.", req_cnt);
scpi_add_error(E_CMD_MISSING_PARAMETER, ebuf);
pars_reset_cmd(); pars_reset_cmd();
} }
} else { } else {
printf("ERROR no such command %s.\n", pst.charbuf);//TODO error err_no_such_command();
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} }
} }
@ -536,7 +574,8 @@ static void pars_cmd_space(void)
pst.state = PARS_ARG; pst.state = PARS_ARG;
} }
} else { } else {
printf("ERROR no such command: %s.\n", pst.charbuf);//TODO error // error
err_no_such_command();
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} }
} }
@ -701,7 +740,9 @@ static void pars_arg_char(char c)
switch (pst.matched_cmd->params[pst.arg_i]) { switch (pst.matched_cmd->params[pst.arg_i]) {
case SCPI_DT_FLOAT: case SCPI_DT_FLOAT:
if (!IS_FLOAT_CHAR(c)) { if (!IS_FLOAT_CHAR(c)) {
printf("ERROR unexpected char '%c' in float.\n", c);//TODO error sprintf(ebuf, "'%c' not allowed in FLOAT.", c);
scpi_add_error(E_CMD_INVALID_CHARACTER_IN_NUMBER, ebuf);
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} else { } else {
charbuf_append(c); charbuf_append(c);
@ -710,7 +751,9 @@ static void pars_arg_char(char c)
case SCPI_DT_INT: case SCPI_DT_INT:
if (!IS_INT_CHAR(c)) { if (!IS_INT_CHAR(c)) {
printf("ERROR unexpected char '%c' in int.\n", c);//TODO error sprintf(ebuf, "'%c' not allowed in INT.", c);
scpi_add_error(E_CMD_INVALID_CHARACTER_IN_NUMBER, ebuf);
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} else { } else {
charbuf_append(c); charbuf_append(c);
@ -723,7 +766,7 @@ static void pars_arg_char(char c)
pst.string_quote = c; pst.string_quote = c;
pst.string_escape = false; pst.string_escape = false;
} else { } else {
printf("ERROR invalid string quote, or chars after string.\n");//TODO error scpi_add_error(E_CMD_INVALID_STRING_DATA, "Invalid quote, or chars after string.");
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} }
break; break;
@ -733,7 +776,7 @@ static void pars_arg_char(char c)
pst.state = PARS_ARG_BLOB_PREAMBLE; pst.state = PARS_ARG_BLOB_PREAMBLE;
pst.blob_cnt = 0; pst.blob_cnt = 0;
} else { } else {
printf("ERROR unexpected char '%c', binary block should start with #\n", c);//TODO error scpi_add_error(E_CMD_INVALID_BLOCK_DATA, "Block data must start with #");
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} }
break; break;
@ -750,14 +793,13 @@ static void pars_arg_comma(void)
{ {
if (pst.arg_i == cmd_param_count(pst.matched_cmd) - 1) { if (pst.arg_i == cmd_param_count(pst.matched_cmd) - 1) {
// it was the last argument // it was the last argument
// comma illegal scpi_add_error(E_CMD_UNEXPECTED_NUMBER_OF_PARAMETERS, "Comma after last argument.");
printf("ERROR unexpected comma after the last argument\n");//TODO error
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
return; return;
} }
if (pst.charbuf_i == 0) { if (pst.charbuf_i == 0) {
printf("ERROR empty argument is not allowed.\n");//TODO error scpi_add_error(E_CMD_SYNTAX_ERROR, "Missing command before comma.");
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
return; return;
} }
@ -768,11 +810,19 @@ static void pars_arg_comma(void)
} }
static void pars_arg_newline(void) // line ended with \n or ;
static void pars_arg_eol_do(bool keep_levels)
{ {
if (pst.arg_i < cmd_param_count(pst.matched_cmd) - 1) { int req_cnt = cmd_param_count(pst.matched_cmd);
if (pst.arg_i < req_cnt - 1) {
// not the last arg yet - fail // not the last arg yet - fail
printf("ERROR not enough arguments!\n");//TODO error
if (pst.charbuf_i > 0) pst.arg_i++; // acknowledge the last arg
sprintf(ebuf, "Required %d, got %d.", req_cnt, pst.arg_i);
scpi_add_error(E_CMD_MISSING_PARAMETER, ebuf);
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
return; return;
} }
@ -780,23 +830,23 @@ static void pars_arg_newline(void)
arg_convert_value(); arg_convert_value();
run_command_callback(); run_command_callback();
if (keep_levels) {
pars_reset_cmd_keeplevel();
} else {
pars_reset_cmd(); // start a new command pars_reset_cmd(); // start a new command
}
} }
static void pars_arg_semicolon(void) static void pars_arg_newline(void)
{ {
if (pst.arg_i < cmd_param_count(pst.matched_cmd) - 1) { pars_arg_eol_do(false);
// not the last arg yet - fail }
printf("ERROR not enough arguments!\n");//TODO error
pst.state = PARS_DISCARD_LINE;
return;
}
arg_convert_value();
run_command_callback();
pars_reset_cmd_keeplevel(); // start a new command, keep level static void pars_arg_semicolon(void)
{
pars_arg_eol_do(true);
} }
@ -819,15 +869,19 @@ static void arg_convert_value(void)
} else if (strcasecmp(pst.charbuf, "OFF") == 0) { } else if (strcasecmp(pst.charbuf, "OFF") == 0) {
dest->BOOL = 0; dest->BOOL = 0;
} else { } else {
printf("ERROR argument mismatch for type BOOL\n");//TODO error sprintf(ebuf, "Invalid BOOL value: '%s'", pst.charbuf);
scpi_add_error(E_CMD_NUMERIC_DATA_ERROR, ebuf);
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} }
break; break;
case SCPI_DT_FLOAT: case SCPI_DT_FLOAT:
j = sscanf(pst.charbuf, "%f", &dest->FLOAT); j = sscanf(pst.charbuf, "%f", &dest->FLOAT);
if (j == 0) { if (j == 0 || pst.charbuf[0] == '\0') { //fail or empty buffer
printf("ERROR failed to convert %s to FLOAT\n", pst.charbuf);//TODO error sprintf(ebuf, "Invalid FLOAT value: '%s'", pst.charbuf);
scpi_add_error(E_CMD_NUMERIC_DATA_ERROR, ebuf);
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} }
break; break;
@ -835,15 +889,18 @@ static void arg_convert_value(void)
case SCPI_DT_INT: case SCPI_DT_INT:
j = sscanf(pst.charbuf, "%d", &dest->INT); j = sscanf(pst.charbuf, "%d", &dest->INT);
if (j == 0) { if (j == 0 || pst.charbuf[0] == '\0') { //fail or empty buffer
printf("ERROR failed to convert %s to INT\n", pst.charbuf);//TODO error sprintf(ebuf, "Invalid INT value: '%s'", pst.charbuf);
scpi_add_error(E_CMD_NUMERIC_DATA_ERROR, ebuf);
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} }
break; break;
case SCPI_DT_STRING: case SCPI_DT_STRING:
if (strlen(pst.charbuf) > SCPI_MAX_STRING_LEN) { if (strlen(pst.charbuf) > SCPI_MAX_STRING_LEN) {
printf("ERROR string too long.\n");//TODO error scpi_add_error(E_CMD_INVALID_STRING_DATA, "String too long.");
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} else { } else {
strcpy(dest->STRING, pst.charbuf); // copy the string strcpy(dest->STRING, pst.charbuf); // copy the string
@ -853,7 +910,7 @@ static void arg_convert_value(void)
default: default:
// impossible // impossible
printf("ERROR unexpected data type\n");//TODO error scpi_add_error(E_DEV_SYSTEM_ERROR, "Unexpected argument data type.");
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
} }
@ -866,21 +923,26 @@ static void pars_blob_preamble_char(uint8_t c)
{ {
if (pst.blob_cnt == 0) { if (pst.blob_cnt == 0) {
if (!INRANGE(c, '1', '9')) { if (!INRANGE(c, '1', '9')) {
printf("ERROR expected ASCII 1-9 after #\n");//TODO error sprintf(ebuf, "Unexpected '%c' in binary data preamble.", c);
pst.state = PARS_DISCARD_LINE;// not enough to remove the blob containing \n scpi_add_error(E_CMD_BLOCK_DATA_ERROR, ebuf);
pst.state = PARS_DISCARD_LINE;// (but not enough to remove the blob containing \n)
return; return;
} }
pst.blob_cnt = c - '0'; // 1-9 pst.blob_cnt = c - '0'; // 1-9
} else { } else {
if (c == '\n') { if (c == '\n') {
printf("ERROR unexpected newline in blob preamble\n");//TODO error scpi_add_error(E_CMD_BLOCK_DATA_ERROR, "Unexpected newline in binary data preamble.");
pars_reset_cmd(); pars_reset_cmd();
return; return;
} }
if (!IS_NUMBER_CHAR(c)) { if (!IS_NUMBER_CHAR(c)) {
printf("ERROR expected ASCII 0-9 after #n\n");//TODO error sprintf(ebuf, "Unexpected '%c' in binary data preamble.", c);
scpi_add_error(E_CMD_BLOCK_DATA_ERROR, ebuf);
pst.state = PARS_DISCARD_LINE; pst.state = PARS_DISCARD_LINE;
return; return;
} }

Loading…
Cancel
Save