From 3bef04154736645b0a7a742a3c210abc42ba8cb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Wed, 2 Dec 2015 10:23:54 +0100 Subject: [PATCH] reorg, improve --- main.c | 64 ++++++++++++++++ scpi.pro | 6 +- scpi_parser.c | 207 +++++++++++--------------------------------------- scpi_parser.h | 48 ++++++++++++ 4 files changed, 162 insertions(+), 163 deletions(-) create mode 100644 main.c create mode 100644 scpi_parser.h diff --git a/main.c b/main.c new file mode 100644 index 0000000..fdf0261 --- /dev/null +++ b/main.c @@ -0,0 +1,64 @@ +#include +#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); + + +const SCPI_command_t scpi_cmd_lang[] = { + { + .level_cnt = 1, .levels = {"*IDN?"}, + .param_cnt = 0, .params = {}, + .callback = cmd_aIDNq_cb + }, + { + .level_cnt = 2, .levels = {"APPLy", "SINe"}, + .param_cnt = 3, .params = {SCPI_DT_INT, SCPI_DT_FLOAT, SCPI_DT_FLOAT}, + .callback = cmd_APPL_SIN_cb + }, + { + .level_cnt = 2, .levels = {"APPLy", "TRIangle"}, + .param_cnt = 3, .params = {SCPI_DT_INT, SCPI_DT_FLOAT, SCPI_DT_FLOAT}, + .callback = cmd_APPL_TRI_cb + }, + { + .level_cnt = 1, .levels = {"FREQuency"}, + .param_cnt = 1, .params = {SCPI_DT_INT}, + .callback = cmd_FREQ_cb + }, +}; + + +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\n"); +} + + + +int main(int argc, const char**argv) +{ + const char *inp = argv[1]; + printf("cmd lang len = %d\n", (int) sizeof(scpi_cmd_lang)/sizeof(SCPI_command_t)); +} diff --git a/scpi.pro b/scpi.pro index ae59836..a140b24 100644 --- a/scpi.pro +++ b/scpi.pro @@ -4,7 +4,11 @@ CONFIG -= app_bundle CONFIG -= qt SOURCES += \ - scpi_parser.c + scpi_parser.c \ + main.c DISTFILES += \ style.astylerc + +HEADERS += \ + scpi_parser.h diff --git a/scpi_parser.c b/scpi_parser.c index 12e8bbb..e375c66 100644 --- a/scpi_parser.c +++ b/scpi_parser.c @@ -1,53 +1,10 @@ #include #include #include +#include "scpi_parser.h" + #include #include -//#include "scpi_parser.h" - -#define MAX_CMD_LEN 12 -#define MAX_STRING_LEN 12 -#define MAX_LEVEL_COUNT 4 -#define MAX_PARAM_COUNT 4 - -#define ERR_QUEUE_LEN 4 -#define MAX_ERROR_LEN 100 - -/** Argument data types */ -typedef enum { - SCPI_DT_NONE = 0, - SCPI_DT_FLOAT, // float with support for scientific notation - SCPI_DT_INT, // integer (may be signed) - SCPI_DT_BOOL, // 0, 1, ON, OFF - SCPI_DT_STRING, // quoted string, max 12 chars; no escapes. - SCPI_DT_BLOB, // binary block, callback: uint32_t holding number of bytes -} SCPI_datatype_t; - - -/** Arguemnt value (union) */ -typedef union { - float FLOAT; - int32_t INT; - bool BOOL; - char STRING[MAX_STRING_LEN]; - uint32_t BLOB; -} SCPI_argval_t; - - -/** SCPI command preset */ -typedef struct { - const uint8_t level_cnt; // number of used command levels (colons + 1) - const char levels[MAX_LEVEL_COUNT][MAX_CMD_LEN]; // up to 4 parts - - const bool quest; // command ends with a question mark - - const uint8_t param_cnt; // parameter count - const SCPI_datatype_t params[MAX_PARAM_COUNT]; // parameter types (0 for unused) - - // called when the command is completed. BLOB arg must be last in the argument list, - // and only the first part is collected. - void (*callback)(const SCPI_argval_t * args); -} SCPI_command_t; typedef enum { @@ -63,7 +20,7 @@ typedef enum { /** parser internal state struct */ static struct { char err_queue[ERR_QUEUE_LEN][MAX_ERROR_LEN]; - uint8_t err_queue_used; + uint8_t err_queue_ptr; parser_state_t state; // current parser internal state @@ -71,22 +28,25 @@ static struct { char charbuf[256]; uint16_t charbuf_ptr; - // command buffer, built from recognized parts and colons. - char cmdbuf[(MAX_CMD_LEN + 1)*MAX_LEVEL_COUNT + 1]; // :COMMAND for each level + zero terminator - uint16_t cmdbuf_ptr; - bool cmdbuf_kept; // set to 1 after semicolon + // recognized complete command level strings (FUNCtion) - exact copy from command struct + char cur_levels[MAX_LEVEL_COUNT][MAX_CMD_LEN]; + uint8_t cur_level_ptr; // next free level slot index - parser_state_t *detected_cmd; // command is put here after recognition, used as reference for args - uint8_t arg_i; // current argument index (0-based) + bool cmdbuf_kept; // set to 1 after semicolon - cur_levels is kept (removed last part) + + SCPI_command_t *detected_cmd; // command is put here after recognition, used as reference for args + + SCPI_argval_t args[MAX_PARAM_COUNT]; + uint8_t arg_ptr; // next free argument slot index } pstate = { // defaults - .err_queue_used = 0, + .err_queue_ptr = 0, .state = PARS_COMMAND, .charbuf_ptr = 0, - .cmdbuf_ptr = 0, + .cur_level_ptr = 0, .cmdbuf_kept = false, .detected_cmd = NULL, - .arg_i = 0 + .arg_ptr = 0 }; @@ -95,19 +55,24 @@ static void pars_cmd_colon(void); // colon starting a command sub-segment static void pars_cmd_space(void); // space ending a command static void pars_cmd_newline(void); // LF static void pars_cmd_semicolon(void); // semicolon right after a command +static void pars_match_level(void); // match charbuf content to a level, advance level ptr (or fail) -#define INRANGE(x, a, b) ((x) >= (a) && (x) <= (b)) -#define IS_WHITE(x) (INRANGE((x), 0, 9) || INRANGE((x), 11, 32)) +// char matching +#define INRANGE(c, a, b) ((c) >= (a) && (c) <= (b)) +#define IS_WHITESPACE(c) (INRANGE((c), 0, 9) || INRANGE((c), 11, 32)) +#define IS_IDENT_CHAR(c) (INRANGE((c), 'a', 'z') || INRANGE((c), 'A', 'Z') || INRANGE((c), '0', '9') || (c) == '_') +#define IS_INT_CHAR(c) INRANGE((c), '0', '9') +#define IS_FLOAT_CHAR(c) (IS_INT_CHAR((c)) || (c) == '.' || (c) == 'e' || (c) == 'E' || (e) == '+' || (e) == '-') static void pars_reset_cmd(void) { pstate.state = PARS_COMMAND; pstate.charbuf_ptr = 0; - pstate.cmdbuf_ptr = 0; + pstate.cur_level_ptr = 0; pstate.cmdbuf_kept = false; pstate.detected_cmd = NULL; - pstate.arg_i = 0; + pstate.arg_ptr = 0; } static void pars_reset_cmd_keeplevel(void) @@ -116,15 +81,14 @@ static void pars_reset_cmd_keeplevel(void) pstate.charbuf_ptr = 0; // rewind to last colon - pstate.cmdbuf[pstate.cmdbuf_ptr] = 0; // terminate - const char *cp = strrchr(pstate.cmdbuf, ':'); // find last colon - pstate.cmdbuf_ptr = (uint16_t) (cp - pstate.cmdbuf + 1); // location of one char past the colon + if (pstate.cur_level_ptr > 0) { + pstate.cur_level_ptr--; // keep prev levels + } - // ↑ FIXME may be broken pstate.cmdbuf_kept = true; pstate.detected_cmd = NULL; - pstate.arg_i = 0; + pstate.arg_ptr = 0; } @@ -137,23 +101,28 @@ void scpi_receive_byte(const uint8_t b) case PARS_COMMAND: // Collecting command - if (pstate.charbuf_ptr == 0) { - if (IS_WHITE(c)) { + if (pstate.charbuf_ptr == 0 && pstate.cur_level_ptr == 0) { + if (IS_WHITESPACE(c)) { // leading whitespace is ignored break; } } - if (INRANGE(c, 'a', 'z') || INRANGE(c, 'A', 'Z') || INRANGE(c, '0', '9') || c == '_') { + if (IS_IDENT_CHAR(c)) { // valid command char + if (pstate.charbuf_ptr < MAX_CMD_LEN) { pstate.charbuf[pstate.charbuf_ptr++] = c; + } else { + printf("ERROR command part too long.\n");//TODO error + pstate.state = PARS_DISCARD_LINE; } + } else { // invalid or delimiter - if (IS_WHITE(c)) { + if (IS_WHITESPACE(c)) { pars_cmd_space(); // whitespace in command - end of command? break; } @@ -198,10 +167,8 @@ static void pars_cmd_colon(void) if (pstate.charbuf_ptr == 0) { // No command text before colon - // TODO FIXME should keep parsed command parts in array rather than a combined string - - if (pstate.cmdbuf_ptr == 0 || pstate.cmdbuf_kept) { - // top level command starts with optional colon (or after semicolon reset level) + if (pstate.cur_level_ptr == 0 || pstate.cmdbuf_kept) { + // top level command starts with colon (or after semicolon - reset level) pars_reset_cmd(); } else { // colon after nothing - error @@ -210,101 +177,17 @@ static void pars_cmd_colon(void) } } else { - while(pstate.charbuf_ptr > 0 && IS_WHITE(pstate.charbuf[pstate.charbuf_ptr - 1])) { - pstate.charbuf_ptr--; - } - - if (pstate.charbuf_ptr){ - printf("ERROR only whitespace before colon.\n");//TODO error - pstate.state = PARS_DISCARD_LINE; // this error shouldn't happen - return; - } - - pstate.charbuf[pstate.charbuf_ptr] = '\0'; // terminator - + // internal colon + pars_match_level(); } } -static void pars_cmd_space(void); // space ending a command -static void pars_cmd_newline(void); // LF -static void pars_cmd_semicolon(void); // semicolon right after a command - - - - - - - - - - - - - - - -//----------------------- TESTS ---------------------- - -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); - - -const SCPI_command_t lang[] = { - { - .level_cnt = 1, .levels = {"*IDN"}, - .quest = true, - .param_cnt = 0, .params = {}, - .callback = cmd_aIDNq_cb - }, - { - .level_cnt = 2, .levels = {"APPLy", "SINe"}, - .quest = false, - .param_cnt = 3, .params = {SCPI_DT_INT, SCPI_DT_FLOAT, SCPI_DT_FLOAT}, - .callback = cmd_APPL_SIN_cb - }, - { - .level_cnt = 2, .levels = {"APPLy", "TRIangle"}, - .quest = false, - .param_cnt = 3, .params = {SCPI_DT_INT, SCPI_DT_FLOAT, SCPI_DT_FLOAT}, - .callback = cmd_APPL_TRI_cb - }, - { - .level_cnt = 1, .levels = {"FREQuency"}, - .quest = false, - .param_cnt = 1, .params = {SCPI_DT_INT}, - .callback = cmd_FREQ_cb - }, -}; - - -int main(int argc, const char**argv) -{ - const char *inp = argv[1]; -} - - -void cmd_aIDNq_cb(const SCPI_argval_t *args) +static void pars_match_level(void) { - printf("cb *IDN?\n"); -} + // terminate segment + pstate.charbuf[pstate.charbuf_ptr] = '\0'; - -void cmd_APPL_SIN_cb(const SCPI_argval_t *args) -{ - printf("cb APPLy:SINe\n"); + //TODO } - -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\n"); -} diff --git a/scpi_parser.h b/scpi_parser.h new file mode 100644 index 0000000..3aaa856 --- /dev/null +++ b/scpi_parser.h @@ -0,0 +1,48 @@ +#pragma once +#include +#include + +#define MAX_CMD_LEN 12 +#define MAX_STRING_LEN 12 +#define MAX_LEVEL_COUNT 4 +#define MAX_PARAM_COUNT 4 + +#define ERR_QUEUE_LEN 4 +#define MAX_ERROR_LEN 100 + +/** Argument data types */ +typedef enum { + SCPI_DT_NONE = 0, + SCPI_DT_FLOAT, // float with support for scientific notation + SCPI_DT_INT, // integer (may be signed) + SCPI_DT_BOOL, // 0, 1, ON, OFF + SCPI_DT_STRING, // quoted string, max 12 chars; no escapes. + SCPI_DT_BLOB, // binary block, callback: uint32_t holding number of bytes +} SCPI_datatype_t; + + +/** Arguemnt value (union) */ +typedef union { + float FLOAT; + int32_t INT; + bool BOOL; + char STRING[MAX_STRING_LEN]; + uint32_t BLOB; +} SCPI_argval_t; + + +/** SCPI command preset */ +typedef struct { + const uint8_t level_cnt; // number of used command levels (colons + 1) + const char levels[MAX_LEVEL_COUNT][MAX_CMD_LEN]; // up to 4 parts + + const uint8_t param_cnt; // parameter count + const SCPI_datatype_t params[MAX_PARAM_COUNT]; // parameter types (0 for unused) + + // called when the command is completed. BLOB arg must be last in the argument list, + // and only the first part is collected. + void (*callback)(const SCPI_argval_t * args); +} SCPI_command_t; + + +extern const SCPI_command_t scpi_cmd_lang[];