From e9becf553ad86685e79d52c4317aeb4441b91525 Mon Sep 17 00:00:00 2001 From: Jan Breuer <jan.breuer@jaybee.cz> Date: 周一, 19 1月 2015 02:46:22 +0800 Subject: [PATCH] Refactoring of arbitrary data, special numbers --- libscpi/src/lexer.c | 30 + libscpi/inc/scpi/types.h | 19 libscpi/inc/scpi/units.h | 6 libscpi/src/parser.c | 431 ++++++++++++++++++++++++--------- libscpi/src/units.c | 132 ++++----- libscpi/inc/scpi/parser.h | 15 libscpi/test/test_parser.c | 1 examples/common/scpi-def.c | 56 +++- README.md | 43 ++ 9 files changed, 488 insertions(+), 245 deletions(-) diff --git a/README.md b/README.md index d284238..3f3971c 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,9 @@ Usage --------------- Download source package or clone repository + - v2.0_beta1 - https://github.com/j123b567/scpi-parser/archive/v2.0_beta1.zip + - v1.2 - https://github.com/j123b567/scpi-parser/archive/v1.2.zip + - v1.1 - https://github.com/j123b567/scpi-parser/archive/v1.1.zip - v1.0 - https://github.com/j123b567/scpi-parser/archive/v1.0.zip - git clone https://github.com/j123b567/scpi-parser.git @@ -27,15 +30,20 @@ Version history ---------------- -Version v1.0 released 24.6.2013 +Version v2.0_beta1 2015-01-18 + - parsing more compliant with SCPI-1999 + - support all parameter types defined in the spec - separate them and identifie them + - support for Arbitrary program data + - support for tagging command patterns (useful for common handler) + - support for extending any parameter type using SCPI_Parameter + - general support for number or text value (e.g. 1, 6, DEF, INF) not limited to one array of special numbers + - support for optional command headers (Richard.hmm) + + +Version v1.0 released 2013-06-24 - support basic command pattern matching (no optional keywoards) - support basic data types (no expressions, no nondecimal numbers, no arbitrary program data, ...) - last version before refactoring of the parser and before extending parameter handling - -Future Version v2.0 released ???????? - - parsing more compliant with SCPI-1999 - - support all parameter types defined in the spec - separate them and identifie them - - support for optional command headers (Richard.hmm) Command pattern definition @@ -65,7 +73,7 @@ The command callback can use predefined function to parse input parameters and to write output. -Reading input parameter is done by functions `SCPI_ParamInt`, `SCPI_ParamDouble`, `SCPI_ParamString` adn `SCPI_ParamNumber`. +Reading input parameter is done by functions `SCPI_ParamInt`, `SCPI_ParamDouble`, `SCPI_ParamString`, `SCPI_ParamNumber`, `SCPI_ParamArbitraryBlock`, `SCPI_ParamCopyText`, `SCPI_ParamBool` and `SCPI_ParamChoice` Writing output is done by functions `SCPI_ResultInt`, `SCPI_ResultDouble`, `SCPI_ResultString`, `SCPI_ResultText`. You can write multiple output variables. They are automaticcaly separated by coma ",". @@ -136,7 +144,6 @@ .interface = &scpi_interface, .registers = scpi_regs, .units = scpi_units_def, - .special_numbers = scpi_special_numbers_def, }; ``` @@ -196,17 +203,29 @@ If you discard some parameters, there is no way to recover them. +Specifying `mandatory` parameter will introduce SCPI Error -109 "Missing parameter" + These are the functions, you can use to read parameters + - `SCPI_Parameter` - read parameter to scpi_parameter_t. This structure contains pointer to buffer and type of the parameter (program mnemonic, hex number, ....) - `SCPI_ParamInt` - read signed 32bit integer value (dec or hex with 0x prefix) - `SCPI_ParamDouble` - read double value - `SCPI_ParamNumber` - read double value with or without units or represented by special number (DEF, MIN, MAX, ...). This function is more universal then SCPI_ParamDouble. - - `SCPI_ParamText` - read text value - may be encapsuled in "" - - `SCPI_ParamString` - read unspecified parameter not encapsulated in "" + - `SCPI_ParamCopyText` - read text value - must be encapsuled in "" + - `SCPI_ParamBool` - read boolean value (0, 1, on, off) + - `SCPI_ParamChoice` - read value from predefined constants + These are the functions, you can use to write results - `SCPI_ResultInt` - write integer value - `SCPI_ResultDouble` - write double value - `SCPI_ResultText` - write text value encapsulated in "" - - `SCPI_ResultString` - directly write string value + - `SCPI_ResultMnemonic` - directly write string value + - `SCPI_ResultArbitraryBlock` - result arbitrary data + - `SCPI_ResultIntBase` - write integer in special base + - `SCPI_ResultBool` - write boolean value -You can use function `SCPI_NumberToStr` to convert number with units to textual representation and then use `SCPI_ResultString` to write this to the user. +You can use function `SCPI_NumberToStr` to convert number with units to textual representation and then use `SCPI_ResultMnemonic` to write this to the user. + +You can use `SCPI_Parameter` in conjuction with `SCPI_ParamIsNumber`, `SCPI_ParamToInt`, `SCPI_ParamToDouble`, `SCPI_ParamToChoice` in your own parameter type handlers. + +`SCPI_ParamNumber` is now more universal. It can handle number with units, it can handle special numbers like `DEF`, `INF`, ... These special numbers are now defined in parameter and not in context. It is possible to define more general usage with different special numbers for different commands, diff --git a/examples/common/scpi-def.c b/examples/common/scpi-def.c index 74db607..562b67b 100644 --- a/examples/common/scpi-def.c +++ b/examples/common/scpi-def.c @@ -46,21 +46,21 @@ fprintf(stderr, "meas:volt:dc\r\n"); // debug command name // read first parameter if present - if (!SCPI_ParamNumber(context, ¶m1, false)) { + if (!SCPI_ParamNumber(context, scpi_special_numbers_def, ¶m1, false)) { // do something, if parameter not present } // read second paraeter if present - if (!SCPI_ParamNumber(context, ¶m2, false)) { + if (!SCPI_ParamNumber(context, scpi_special_numbers_def, ¶m2, false)) { // do something, if parameter not present } - SCPI_NumberToStr(context, ¶m1, bf, 15); + SCPI_NumberToStr(context, scpi_special_numbers_def, ¶m1, bf, 15); fprintf(stderr, "\tP1=%s\r\n", bf); - SCPI_NumberToStr(context, ¶m2, bf, 15); + SCPI_NumberToStr(context, scpi_special_numbers_def, ¶m2, bf, 15); fprintf(stderr, "\tP2=%s\r\n", bf); SCPI_ResultDouble(context, 0); @@ -75,21 +75,21 @@ fprintf(stderr, "meas:volt:ac\r\n"); // debug command name // read first parameter if present - if (!SCPI_ParamNumber(context, ¶m1, false)) { + if (!SCPI_ParamNumber(context, scpi_special_numbers_def, ¶m1, false)) { // do something, if parameter not present } // read second paraeter if present - if (!SCPI_ParamNumber(context, ¶m2, false)) { + if (!SCPI_ParamNumber(context, scpi_special_numbers_def, ¶m2, false)) { // do something, if parameter not present } - SCPI_NumberToStr(context, ¶m1, bf, 15); + SCPI_NumberToStr(context, scpi_special_numbers_def, ¶m1, bf, 15); fprintf(stderr, "\tP1=%s\r\n", bf); - SCPI_NumberToStr(context, ¶m2, bf, 15); + SCPI_NumberToStr(context, scpi_special_numbers_def, ¶m2, bf, 15); fprintf(stderr, "\tP2=%s\r\n", bf); SCPI_ResultDouble(context, 0); @@ -131,23 +131,25 @@ return SCPI_RES_OK; } -const char * trigger_source[] = { - "BUS", - "IMMediate", - "EXTernal", - NULL /* termination of option list */ +scpi_choice_def_t trigger_source[] = { + {"BUS", 5}, + {"IMMediate", 6}, + {"EXTernal", 7}, + SCPI_CHOICE_LIST_END /* termination of option list */ }; scpi_result_t TEST_ChoiceQ(scpi_t * context) { int32_t param; + const char * name; if (!SCPI_ParamChoice(context, trigger_source, ¶m, true)) { return SCPI_RES_ERR; } - fprintf(stderr, "\tP1=%s (%d)\r\n", trigger_source[param], param); + SCPI_ChoiceToName(trigger_source, param, &name); + fprintf(stderr, "\tP1=%s (%d)\r\n", name, param); SCPI_ResultInt(context, param); @@ -157,6 +159,29 @@ scpi_result_t TEST_Numbers(scpi_t * context) { fprintf(stderr, "RAW CMD %.*s\r\n", (int)context->param_list.cmd_raw.length, context->param_list.cmd_raw.data); + + return SCPI_RES_OK; +} + +scpi_result_t TEST_Text(scpi_t * context) { + char buffer[100]; + size_t copy_len; + + buffer[0] = 0; + SCPI_ParamCopyText(context, buffer, 100, ©_len, false); + + fprintf(stderr, "TEXT: ***%s***\r\n", buffer); + + return SCPI_RES_OK; +} + +scpi_result_t TEST_ArbQ(scpi_t * context) { + const char * data; + size_t len; + + SCPI_ParamArbitraryBlock(context, &data, &len, false); + + SCPI_ResultArbitraryBlock(context, data, len); return SCPI_RES_OK; } @@ -212,6 +237,8 @@ {.pattern = "TEST:BOOL", .callback = TEST_Bool,}, {.pattern = "TEST:CHOice?", .callback = TEST_ChoiceQ,}, {.pattern = "TEST#:NUMbers#", .callback = TEST_Numbers,}, + {.pattern = "TEST:TEXT", .callback = TEST_Text,}, + {.pattern = "TEST:ARBitrary?", .callback = TEST_ArbQ,}, SCPI_CMD_LIST_END }; @@ -240,6 +267,5 @@ .interface = &scpi_interface, .registers = scpi_regs, .units = scpi_units_def, - .special_numbers = scpi_special_numbers_def, .idn = {"MANUFACTURE", "INSTR2013", NULL, "01-02"}, }; diff --git a/libscpi/inc/scpi/parser.h b/libscpi/inc/scpi/parser.h index 834a12f..76c8713 100644 --- a/libscpi/inc/scpi/parser.h +++ b/libscpi/inc/scpi/parser.h @@ -58,21 +58,26 @@ size_t SCPI_ResultText(scpi_t * context, const char * data); size_t SCPI_ResultArbitraryBlock(scpi_t * context, const char * data, size_t len); size_t SCPI_ResultBool(scpi_t * context, scpi_bool_t val); - scpi_bool_t SCPI_Parameter(scpi_t * context, scpi_parameter_t * parameter, scpi_bool_t mandatory); scpi_bool_t SCPI_ParamIsNumber(scpi_parameter_t * parameter, scpi_bool_t suffixAllowed); + scpi_bool_t SCPI_ParamToInt(scpi_t * context, scpi_parameter_t * parameter, int32_t * value); + scpi_bool_t SCPI_ParamToDouble(scpi_t * context, scpi_parameter_t * parameter, double * value); + scpi_bool_t SCPI_ParamToChoice(scpi_t * context, scpi_parameter_t * parameter, const scpi_choice_def_t * options, int32_t * value); + scpi_bool_t SCPI_ChoiceToName(const scpi_choice_def_t * options, int32_t tag, const char ** text); + scpi_bool_t SCPI_ParamInt(scpi_t * context, int32_t * value, scpi_bool_t mandatory); scpi_bool_t SCPI_ParamDouble(scpi_t * context, double * value, scpi_bool_t mandatory); -// scpi_bool_t SCPI_ParamString(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory); scpi_bool_t SCPI_ParamCharacters(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory); -#define SCPI_ParamArbitraryBlock SCPI_ParamCharacters -// scpi_bool_t SCPI_ParamText(scpi_t * context, const char ** value, size_t * len, int * type, scpi_bool_t mandatory); + scpi_bool_t SCPI_ParamArbitraryBlock(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory); + scpi_bool_t SCPI_ParamCopyText(scpi_t * context, char * buffer, size_t buffer_len, size_t * copy_len, scpi_bool_t mandatory); scpi_bool_t SCPI_ParamBool(scpi_t * context, scpi_bool_t * value, scpi_bool_t mandatory); - scpi_bool_t SCPI_ParamChoice(scpi_t * context, const char * options[], int32_t * value, scpi_bool_t mandatory); + scpi_bool_t SCPI_ParamChoice(scpi_t * context, const scpi_choice_def_t * options, int32_t * value, scpi_bool_t mandatory); + scpi_bool_t SCPI_IsCmd(scpi_t * context, const char * cmd); + int32_t SCPI_CmdTag(scpi_t * context); #ifdef __cplusplus } diff --git a/libscpi/inc/scpi/types.h b/libscpi/inc/scpi/types.h index 3a6d507..ade80cf 100644 --- a/libscpi/inc/scpi/types.h +++ b/libscpi/inc/scpi/types.h @@ -105,7 +105,7 @@ typedef struct _scpi_command_t scpi_command_t; -#define SCPI_CMD_LIST_END {NULL, NULL, } +#define SCPI_CMD_LIST_END {NULL, NULL, 0} /* scpi interface */ typedef struct _scpi_t scpi_t; @@ -229,12 +229,12 @@ }; typedef enum _scpi_special_number_t scpi_special_number_t; - struct _scpi_special_number_def_t { + struct _scpi_choice_def_t { const char * name; - scpi_special_number_t type; + int32_t tag; }; -#define SCPI_SPECIAL_NUMBERS_LIST_END {NULL, SCPI_NUM_NUMBER} - typedef struct _scpi_special_number_def_t scpi_special_number_def_t; +#define SCPI_CHOICE_LIST_END {NULL, -1} + typedef struct _scpi_choice_def_t scpi_choice_def_t; struct _scpi_param_list_t { const scpi_command_t * cmd; @@ -257,16 +257,12 @@ }; typedef struct _scpi_data_parameter_t scpi_data_parameter_t; - struct _scpi_parameter_t { - scpi_token_type_t type; - scpi_data_parameter_t data; - scpi_number_t number; - }; - typedef struct _scpi_parameter_t scpi_parameter_t; + typedef scpi_token_t scpi_parameter_t; struct _scpi_command_t { const char * pattern; scpi_command_callback_t callback; + int32_t tag; }; struct _scpi_interface_t { @@ -289,7 +285,6 @@ scpi_error_queue_t error_queue; scpi_reg_val_t * registers; const scpi_unit_def_t * units; - const scpi_special_number_def_t * special_numbers; void * user_context; scpi_parser_state_t parser_state; const char * idn[4]; diff --git a/libscpi/inc/scpi/units.h b/libscpi/inc/scpi/units.h index a004135..975a644 100644 --- a/libscpi/inc/scpi/units.h +++ b/libscpi/inc/scpi/units.h @@ -44,12 +44,12 @@ #endif extern const scpi_unit_def_t scpi_units_def[]; - extern const scpi_special_number_def_t scpi_special_numbers_def[]; + extern const scpi_choice_def_t scpi_special_numbers_def[]; - scpi_bool_t SCPI_ParamNumber(scpi_t * context, scpi_number_t * value, scpi_bool_t mandatory); + scpi_bool_t SCPI_ParamNumber(scpi_t * context, const scpi_choice_def_t * special, scpi_number_t * value, scpi_bool_t mandatory); scpi_bool_t SCPI_ParamTranslateNumberVal(scpi_t * context, scpi_parameter_t * parameter); - size_t SCPI_NumberToStr(scpi_t * context, scpi_number_t * value, char * str, size_t len); + size_t SCPI_NumberToStr(scpi_t * context, const scpi_choice_def_t * special, scpi_number_t * value, char * str, size_t len); #ifdef __cplusplus } diff --git a/libscpi/src/lexer.c b/libscpi/src/lexer.c index 349bcc5..e7dc48d 100644 --- a/libscpi/src/lexer.c +++ b/libscpi/src/lexer.c @@ -297,6 +297,7 @@ } /* 7.6.1.2 <COMMAND PROGRAM HEADER> */ + /** * Skip program mnemonic [a-z][a-z0-9_]* * @param state @@ -319,6 +320,7 @@ } /* tokens */ + /** * Detect token white space * @param state @@ -342,6 +344,7 @@ } /* 7.6.1 <COMMAND PROGRAM HEADER> */ + /** * Skip command program header \*<PROGRAM MNEMONIC> * @param state @@ -424,7 +427,7 @@ } } else if (res <= SKIP_INCOMPLETE) { token->type = SCPI_TOKEN_INCOMPLETE_COMPOUND_PROGRAM_HEADER; - } + } } if (token->type != SCPI_TOKEN_UNKNOWN) { @@ -438,6 +441,7 @@ } /* 7.7.1 <CHARACTER PROGRAM DATA> */ + /** * Detect token "Character program data" * @param state @@ -713,6 +717,7 @@ int arbitraryBlockLength = 0; const char * ptr = state->pos; token->ptr = state->pos; + int validData = -1; if (skipChr(state, '#')) { if (!iseos(state) && isNonzeroDigit(state->pos[0])) { @@ -732,23 +737,29 @@ if (i == 0) { state->pos += arbitraryBlockLength; - if ((state->buffer + state->len) < (state->pos)) { - token->len = 0; - } else { + if ((state->buffer + state->len) >= (state->pos)) { token->ptr = state->pos - arbitraryBlockLength; token->len = arbitraryBlockLength; + validData = 1; } - } else { - token->len = 0; + } else if (iseos(state)) { + validData = 0; } - } else { - token->len = 0; + } else if (iseos(state)) { + validData = 0; } } - if ((token->len > 0)) { + if (validData == 1) { + // valid token->type = SCPI_TOKEN_ARBITRARY_BLOCK_PROGRAM_DATA; + } else if (validData == 0) { + // incomplete + token->type = SCPI_TOKEN_UNKNOWN; + token->len = 0; + state->pos = state->buffer + state->len; } else { + // invalid token->type = SCPI_TOKEN_UNKNOWN; state->pos = token->ptr; token->len = 0; @@ -780,6 +791,7 @@ } // TODO: 7.7.7.2-2 recursive - any program data + /** * Detect token Expression * @param state diff --git a/libscpi/src/parser.c b/libscpi/src/parser.c index 43f5b98..d7f5eee 100644 --- a/libscpi/src/parser.c +++ b/libscpi/src/parser.c @@ -373,6 +373,30 @@ } /** + * Write arbitrary block program data to the result + * @param context + * @param data + * @param len + * @return + */ +size_t SCPI_ResultArbitraryBlock(scpi_t * context, const char * data, size_t len) { + size_t result = 0; + char block_header[12]; + size_t header_len; + block_header[0] = '#'; + longToStr(len, block_header + 2, 10, 10); + + header_len = strlen(block_header + 2); + block_header[1] = header_len + '0'; + + result += writeData(context, block_header, header_len + 2); + result += writeData(context, data, len); + + context->output_count++; + return result; +} + +/** * Write boolean value to the result * @param context * @param val @@ -382,8 +406,19 @@ return SCPI_ResultIntBase(context, val ? 1 : 0, 10); } - /* parsing parameters */ + +/** + * Invalidate token + * @param token + * @param ptr + */ +static void invalidateToken(scpi_token_t * token, const char * ptr) { + token->len = 0; + token->ptr = ptr; + token->type = SCPI_TOKEN_UNKNOWN; +} + /** * Get one parameter from command line * @param context @@ -392,17 +427,14 @@ * @return */ scpi_bool_t SCPI_Parameter(scpi_t * context, scpi_parameter_t * parameter, scpi_bool_t mandatory) { - scpi_token_t token; lex_state_t * state; - int32_t value; - parameter->data.ptr = NULL; - parameter->data.len = 0; - parameter->number.value = 0; - parameter->number.base = 10; - parameter->number.unit = SCPI_UNIT_NONE; - parameter->number.type = SCPI_NUM_NUMBER; - parameter->type = SCPI_TOKEN_UNKNOWN; + if (!parameter) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + invalidateToken(parameter, NULL); state = &context->param_list.lex_state; @@ -410,14 +442,14 @@ if (mandatory) { SCPI_ErrorPush(context, SCPI_ERROR_MISSING_PARAMETER); } else { - parameter->number.type = SCPI_NUM_DEF; parameter->type = SCPI_TOKEN_PROGRAM_MNEMONIC; // TODO: select something different } return FALSE; } if (context->input_count != 0) { - scpiLex_Comma(state, &token); - if (token.type != SCPI_TOKEN_COMMA) { + scpiLex_Comma(state, parameter); + if (parameter->type != SCPI_TOKEN_COMMA) { + invalidateToken(parameter, NULL); SCPI_ErrorPush(context, SCPI_ERROR_INVALID_SEPARATOR); return FALSE; } @@ -425,50 +457,22 @@ context->input_count++; - scpiParser_parseProgramData(&context->param_list.lex_state, &token); + scpiParser_parseProgramData(&context->param_list.lex_state, parameter); - parameter->type = token.type; - parameter->data.ptr = token.ptr; - parameter->data.len = token.len; - - switch (token.type) { + switch (parameter->type) { case SCPI_TOKEN_HEXNUM: - parameter->number.base = 16; - strToLong(token.ptr, &value, 16); - parameter->number.value = value; - return TRUE; case SCPI_TOKEN_OCTNUM: - parameter->number.base = 8; - strToLong(token.ptr, &value, 8); - parameter->number.value = value; - return TRUE; case SCPI_TOKEN_BINNUM: - parameter->number.base = 2; - strToLong(token.ptr, &value, 2); - parameter->number.value = value; - return TRUE; case SCPI_TOKEN_PROGRAM_MNEMONIC: - return TRUE; case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: - strToDouble(token.ptr, ¶meter->number.value); - return TRUE; case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX: - strToDouble(token.ptr, ¶meter->number.value); - return TRUE; case SCPI_TOKEN_ARBITRARY_BLOCK_PROGRAM_DATA: - return TRUE; case SCPI_TOKEN_SINGLE_QUOTE_PROGRAM_DATA: - // TODO: replace double "single qoute" - return TRUE; case SCPI_TOKEN_DOUBLE_QUOTE_PROGRAM_DATA: - // TODO: replace double "double qoute" - return TRUE; case SCPI_TOKEN_PROGRAM_EXPRESSION: return TRUE; default: - parameter->type = SCPI_TOKEN_UNKNOWN; - parameter->data.ptr = NULL; - parameter->data.len = 0; + invalidateToken(parameter, NULL); SCPI_ErrorPush(context, SCPI_ERROR_INVALID_STRING_DATA); return FALSE; } @@ -491,7 +495,66 @@ return suffixAllowed; default: return FALSE; - } + } +} + +/** + * Convert parameter to integer + * @param context + * @param parameter + * @param value result + * @return true if succesful + */ +scpi_bool_t SCPI_ParamToInt(scpi_t * context, scpi_parameter_t * parameter, int32_t * value) { + + if (!value) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + switch (parameter->type) { + case SCPI_TOKEN_HEXNUM: + return strToLong(parameter->ptr, value, 16) > 0 ? TRUE : FALSE; + case SCPI_TOKEN_OCTNUM: + return strToLong(parameter->ptr, value, 8) > 0 ? TRUE : FALSE; + case SCPI_TOKEN_BINNUM: + return strToLong(parameter->ptr, value, 2) > 0 ? TRUE : FALSE; + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX: + return strToLong(parameter->ptr, value, 10) > 0 ? TRUE : FALSE; + } + return FALSE; +} + +/** + * Convert parameter to double + * @param context + * @param parameter + * @param value result + * @return true if succesful + */ +scpi_bool_t SCPI_ParamToDouble(scpi_t * context, scpi_parameter_t * parameter, double * value) { + scpi_bool_t result = FALSE; + int32_t valint; + + if (!value) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + switch (parameter->type) { + case SCPI_TOKEN_HEXNUM: + case SCPI_TOKEN_OCTNUM: + case SCPI_TOKEN_BINNUM: + result = SCPI_ParamToInt(context, parameter, &valint); + *value = valint; + break; + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX: + result = strToDouble(parameter->ptr, value) > 0 ? TRUE : FALSE; + break; + } + return result; } /** @@ -501,20 +564,19 @@ * @param mandatory * @return */ -scpi_bool_t SCPI_ParamDouble(scpi_t * context, double * value, scpi_bool_t mandatory) -{ +scpi_bool_t SCPI_ParamDouble(scpi_t * context, double * value, scpi_bool_t mandatory) { scpi_bool_t result; scpi_parameter_t param; - + if (!value) { - SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); return FALSE; } - + result = SCPI_Parameter(context, ¶m, mandatory); if (result) { if (SCPI_ParamIsNumber(¶m, FALSE)) { - *value = param.number.value; + SCPI_ParamToDouble(context, ¶m, value); } else if (SCPI_ParamIsNumber(¶m, TRUE)) { SCPI_ErrorPush(context, SCPI_ERROR_SUFFIX_NOT_ALLOWED); result = FALSE; @@ -523,7 +585,7 @@ result = FALSE; } } - return result; + return result; } /** @@ -533,22 +595,27 @@ * @param mandatory * @return */ -scpi_bool_t SCPI_ParamInt(scpi_t * context, int32_t * value, scpi_bool_t mandatory) -{ - // TODO: remove dependency on double - double tmpVal; +scpi_bool_t SCPI_ParamInt(scpi_t * context, int32_t * value, scpi_bool_t mandatory) { scpi_bool_t result; + scpi_parameter_t param; if (!value) { SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); return FALSE; } - - result = SCPI_ParamDouble(context, &tmpVal, mandatory); + + result = SCPI_Parameter(context, ¶m, mandatory); if (result) { - *value = tmpVal; + if (SCPI_ParamIsNumber(¶m, FALSE)) { + SCPI_ParamToInt(context, ¶m, value); + } else if (SCPI_ParamIsNumber(¶m, TRUE)) { + SCPI_ErrorPush(context, SCPI_ERROR_SUFFIX_NOT_ALLOWED); + result = FALSE; + } else { + SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); + result = FALSE; + } } - return result; } @@ -560,25 +627,150 @@ * @param mandatory * @return */ -scpi_bool_t SCPI_ParamCharacters(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory) -{ +scpi_bool_t SCPI_ParamCharacters(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory) { scpi_bool_t result; scpi_parameter_t param; - + if (!value || !len) { SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); return FALSE; } - + result = SCPI_Parameter(context, ¶m, mandatory); - if (result) { - *value = param.data.ptr; - *len = param.data.len; + if (result) { + *value = param.ptr; + *len = param.len; // TODO: return also parameter type (ProgramMnemonic, ArbitraryBlockProgramData, SingleQuoteProgramData, DoubleQuoteProgramData } - + return result; +} + +/** + * Get arbitrary block program data and returns pointer to data + * @param context + * @param value result pointer to data + * @param len result length of data + * @param mandatory + * @return + */ +scpi_bool_t SCPI_ParamArbitraryBlock(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory) { + scpi_bool_t result; + scpi_parameter_t param; + + if (!value || !len) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + result = SCPI_Parameter(context, ¶m, mandatory); + if (result) { + if (param.type == SCPI_TOKEN_ARBITRARY_BLOCK_PROGRAM_DATA) { + *value = param.ptr; + *len = param.len; + } else { + SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); + result = FALSE; + } + } + + return result; +} + +scpi_bool_t SCPI_ParamCopyText(scpi_t * context, char * buffer, size_t buffer_len, size_t * copy_len, scpi_bool_t mandatory) { + scpi_bool_t result; + scpi_parameter_t param; + size_t i_from; + size_t i_to; + char quote; + + if (!buffer || !copy_len) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + result = SCPI_Parameter(context, ¶m, mandatory); + if (result) { + + switch (param.type) { + case SCPI_TOKEN_SINGLE_QUOTE_PROGRAM_DATA: + case SCPI_TOKEN_DOUBLE_QUOTE_PROGRAM_DATA: + quote = param.type == SCPI_TOKEN_SINGLE_QUOTE_PROGRAM_DATA ? '\'' : '"'; + for (i_from = 0, i_to = 0; i_from < (size_t) param.len; i_from++) { + if (i_from >= buffer_len) { + break; + } + buffer[i_to] = param.ptr[i_from]; + i_to++; + if (param.ptr[i_from] == quote) { + i_from++; + } + } + break; + default: + SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); + result = FALSE; + } + } + + return result; +} + +/** + * Convert parameter to choice + * @param context + * @param parameter - should be PROGRAM_MNEMONIC + * @param options - NULL terminated list of choices + * @param value - index to options + * @return + */ +scpi_bool_t SCPI_ParamToChoice(scpi_t * context, scpi_parameter_t * parameter, const scpi_choice_def_t * options, int32_t * value) { + size_t res; + scpi_bool_t result = FALSE; + + if (!options || !value) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + if (parameter->type == SCPI_TOKEN_PROGRAM_MNEMONIC) { + for (res = 0; options[res].name; ++res) { + if (matchPattern(options[res].name, strlen(options[res].name), parameter->ptr, parameter->len)) { + *value = options[res].tag; + result = TRUE; + break; + } + } + + if (!result) { + SCPI_ErrorPush(context, SCPI_ERROR_ILLEGAL_PARAMETER_VALUE); + } + } else { + SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); + } + + return result; +} + +/** + * Find tag in choices and returns its first textual representation + * @param options specifications of choices numbers (patterns) + * @param tag numerical representatio of choice + * @param text result text + * @return true if succesfule, else false + */ +scpi_bool_t SCPI_ChoiceToName(const scpi_choice_def_t * options, int32_t tag, const char ** text) { + int i; + + for (i = 0; options[i].name != NULL; i++) { + if (options[i].tag == tag) { + *text = options[i].name; + return TRUE; + } + } + + return FALSE; } /** @@ -588,39 +780,36 @@ * @param mandatory * @return */ -scpi_bool_t SCPI_ParamBool(scpi_t * context, scpi_bool_t * value, scpi_bool_t mandatory) -{ +scpi_bool_t SCPI_ParamBool(scpi_t * context, scpi_bool_t * value, scpi_bool_t mandatory) { scpi_bool_t result; scpi_parameter_t param; + int32_t intval; + + scpi_choice_def_t bool_options[] = { + {"OFF", 0}, + {"ON", 1}, + SCPI_CHOICE_LIST_END /* termination of option list */ + }; if (!value) { SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); return FALSE; } - + result = SCPI_Parameter(context, ¶m, mandatory); - + if (result) { - switch (param.type) { - case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: - *value = param.number.value ? 1 : 0; - break; - case SCPI_TOKEN_PROGRAM_MNEMONIC: - if (compareStr("ON", 2, param.data.ptr, param.data.len)) { - *value = TRUE; - } else if (compareStr("OFF", 3, param.data.ptr, param.data.len)) { - *value = FALSE; - } else { - SCPI_ErrorPush(context, SCPI_ERROR_ILLEGAL_PARAMETER_VALUE); - result = FALSE; - } - break; - default: - SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); - result = FALSE; - } + if (param.type == SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA) { + SCPI_ParamToInt(context, ¶m, &intval); + *value = intval ? TRUE : FALSE; + } else { + result = SCPI_ParamToChoice(context, ¶m, bool_options, &intval); + if (result) { + *value = intval ? TRUE : FALSE; + } + } } - + return result; } @@ -632,35 +821,18 @@ * @param mandatory * @return */ -scpi_bool_t SCPI_ParamChoice(scpi_t * context, const char * options[], int32_t * value, scpi_bool_t mandatory) -{ - size_t res; +scpi_bool_t SCPI_ParamChoice(scpi_t * context, const scpi_choice_def_t * options, int32_t * value, scpi_bool_t mandatory) { scpi_bool_t result; scpi_parameter_t param; - + if (!options || !value) { SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); return FALSE; } - + result = SCPI_Parameter(context, ¶m, mandatory); if (result) { - if (param.type == SCPI_TOKEN_PROGRAM_MNEMONIC) { - for (res = 0; options[res]; ++res) { - if (matchPattern(options[res], strlen(options[res]), param.data.ptr, param.data.len)) { - *value = res; - break; - } - } - - if (!options[res]) { - SCPI_ErrorPush(context, SCPI_ERROR_ILLEGAL_PARAMETER_VALUE); - result = FALSE; - } - } else { - SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); - result = FALSE; - } + result = SCPI_ParamToChoice(context, ¶m, options, value); } return result; @@ -754,12 +926,6 @@ return token->len; } -static void invalidateToken(scpi_token_t * token, const char * ptr) { - token->len = 0; - token->ptr = ptr; - token->type = SCPI_TOKEN_UNKNOWN; -} - /** * Skip complete command line - program header and parameters * @param state @@ -799,7 +965,7 @@ state->programHeader.len = 1; state->programHeader.type = SCPI_TOKEN_INVALID; - invalidateToken(&state->programData, lex_state.buffer); + invalidateToken(&state->programData, lex_state.buffer); } if (SCPI_TOKEN_SEMICOLON == tmp.type) { @@ -813,7 +979,34 @@ return lex_state.pos - lex_state.buffer; } +/** + * Check current command + * - suitable for one handle to multiple commands + * @param context + * @param cmd + * @return + */ +scpi_bool_t SCPI_IsCmd(scpi_t * context, const char * cmd) { + if (!context->param_list.cmd) { + return FALSE; + } + const char * pattern = context->param_list.cmd->pattern; + return matchCommand(pattern, cmd, strlen(cmd)); +} + +/** + * Return the .tag field of the matching scpi_command_t + * @param context + * @return + */ +int32_t SCPI_CmdTag(scpi_t * context) { + if (context->param_list.cmd) { + return context->param_list.cmd->tag; + } else { + return 0; + } +} diff --git a/libscpi/src/units.c b/libscpi/src/units.c index 3b4cae2..792df76 100644 --- a/libscpi/src/units.c +++ b/libscpi/src/units.c @@ -103,7 +103,7 @@ /* * Special number values definition */ -const scpi_special_number_def_t scpi_special_numbers_def[] = { +const scpi_choice_def_t scpi_special_numbers_def[] = { {/* name */ "MINimum", /* type */ SCPI_NUM_MIN}, {/* name */ "MAXimum", /* type */ SCPI_NUM_MAX}, {/* name */ "DEFault", /* type */ SCPI_NUM_DEF}, @@ -112,59 +112,8 @@ {/* name */ "NAN", /* type */ SCPI_NUM_NAN}, {/* name */ "INFinity", /* type */ SCPI_NUM_INF}, {/* name */ "NINF", /* type */ SCPI_NUM_NINF}, - SCPI_SPECIAL_NUMBERS_LIST_END, + SCPI_CHOICE_LIST_END, }; - -/** - * Match string constant to one of special number values - * @param specs specifications of special numbers (patterns) - * @param str string to be recognised - * @param len length of string - * @param value resultin value - * @return TRUE if str matches one of specs patterns - */ -static scpi_bool_t translateSpecialNumber(const scpi_special_number_def_t * specs, const char * str, size_t len, scpi_number_t * value) { - int i; - - value->value = 0.0; - value->unit = SCPI_UNIT_NONE; - value->type = SCPI_NUM_NUMBER; - - if (specs == NULL) { - return FALSE; - } - - for (i = 0; specs[i].name != NULL; i++) { - if (matchPattern(specs[i].name, strlen(specs[i].name), str, len)) { - value->type = specs[i].type; - return TRUE; - } - } - - return FALSE; -} - -/** - * Convert special number type to its string representation - * @param specs specifications of special numbers (patterns) - * @param type type of special number - * @return String representing special number or NULL - */ -static const char * translateSpecialNumberInverse(const scpi_special_number_def_t * specs, scpi_special_number_t type) { - int i; - - if (specs == NULL) { - return NULL; - } - - for (i = 0; specs[i].name != NULL; i++) { - if (specs[i].type == type) { - return specs[i].name; - } - } - - return NULL; -} /** * Convert string describing unit to its representation @@ -249,47 +198,94 @@ * @param mandatory if the parameter is mandatory * @return */ -scpi_bool_t SCPI_ParamNumber(scpi_t * context, scpi_number_t * value, scpi_bool_t mandatory) +scpi_bool_t SCPI_ParamNumber(scpi_t * context, const scpi_choice_def_t * special, scpi_number_t * value, scpi_bool_t mandatory) { scpi_token_t token; lex_state_t state; scpi_parameter_t param; scpi_bool_t result; - - result = SCPI_Parameter(context, ¶m, mandatory); - + int32_t intval; - state.buffer = param.data.ptr; + if (!value) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + result = SCPI_Parameter(context, ¶m, mandatory); + + if (!result) { + return result; + } + + state.buffer = param.ptr; state.pos = state.buffer; - state.len = param.data.len; + state.len = param.len; switch(param.type) { case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: case SCPI_TOKEN_HEXNUM: case SCPI_TOKEN_OCTNUM: case SCPI_TOKEN_BINNUM: + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX: + case SCPI_TOKEN_PROGRAM_MNEMONIC: + value->unit = SCPI_UNIT_NONE; + value->type = SCPI_NUM_NUMBER; result = TRUE; + break; + } + + switch(param.type) { + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX: + case SCPI_TOKEN_PROGRAM_MNEMONIC: + value->base = 10; + break; + case SCPI_TOKEN_BINNUM: + value->base = 2; + break; + case SCPI_TOKEN_HEXNUM: + value->base = 16; + break; + case SCPI_TOKEN_OCTNUM: + value->base = 8; + break; + } + + switch(param.type) { + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: + SCPI_ParamToDouble(context, ¶m, &(value->value)); + break; + case SCPI_TOKEN_HEXNUM: + SCPI_ParamToDouble(context, ¶m, &(value->value)); + break; + case SCPI_TOKEN_OCTNUM: + SCPI_ParamToDouble(context, ¶m, &(value->value)); + break; + case SCPI_TOKEN_BINNUM: + SCPI_ParamToDouble(context, ¶m, &(value->value)); break; case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX: scpiLex_DecimalNumericProgramData(&state, &token); scpiLex_WhiteSpace(&state, &token); scpiLex_SuffixProgramData(&state, &token); - result = transformNumber(context, token.ptr, token.len, ¶m.number); + SCPI_ParamToDouble(context, ¶m, &(value->value)); + + result = transformNumber(context, token.ptr, token.len, value); break; case SCPI_TOKEN_PROGRAM_MNEMONIC: scpiLex_WhiteSpace(&state, &token); scpiLex_CharacterProgramData(&state, &token); /* convert string to special number type */ - result = translateSpecialNumber(context->special_numbers, token.ptr, token.len, ¶m.number); + SCPI_ParamToChoice(context, &token, special, &intval); + + value->type = intval; + value->value = 0; + break; default: result = FALSE; - } - - if (result) { - memcpy(value, ¶m.number, sizeof(scpi_number_t)); } return result; @@ -303,7 +299,7 @@ * @param len max length of string * @return number of chars written to string */ -size_t SCPI_NumberToStr(scpi_t * context, scpi_number_t * value, char * str, size_t len) { +size_t SCPI_NumberToStr(scpi_t * context, const scpi_choice_def_t * special, scpi_number_t * value, char * str, size_t len) { const char * type; const char * unit; size_t result; @@ -312,9 +308,7 @@ return 0; } - type = translateSpecialNumberInverse(context->special_numbers, value->type); - - if (type) { + if (SCPI_ChoiceToName(special, value->type, &type)) { strncpy(str, type, len); return min(strlen(type), len); } diff --git a/libscpi/test/test_parser.c b/libscpi/test/test_parser.c index 1c1faac..7abb3ce 100644 --- a/libscpi/test/test_parser.c +++ b/libscpi/test/test_parser.c @@ -104,7 +104,6 @@ .interface = &scpi_interface, .registers = scpi_regs, .units = scpi_units_def, - .special_numbers = scpi_special_numbers_def, .idn = {"MANUFACTURE", "INSTR2013", NULL, "01-02"}, }; -- Gitblit v1.9.1