Jan Breuer
2015-01-19 e9becf553ad86685e79d52c4317aeb4441b91525
Refactoring of arbitrary data, special numbers

- refactoring of handling arbitrary program data
- refactoring of handling special numbers and bool as choice
9个文件已修改
733 ■■■■■ 已修改文件
README.md 43 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/common/scpi-def.c 56 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libscpi/inc/scpi/parser.h 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libscpi/inc/scpi/types.h 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libscpi/inc/scpi/units.h 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libscpi/src/lexer.c 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libscpi/src/parser.c 431 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libscpi/src/units.c 132 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libscpi/test/test_parser.c 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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,
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, &param1, false)) {
    if (!SCPI_ParamNumber(context, scpi_special_numbers_def, &param1, false)) {
        // do something, if parameter not present
    }
    // read second paraeter if present
    if (!SCPI_ParamNumber(context, &param2, false)) {
    if (!SCPI_ParamNumber(context, scpi_special_numbers_def, &param2, false)) {
        // do something, if parameter not present
    }
    
    SCPI_NumberToStr(context, &param1, bf, 15);
    SCPI_NumberToStr(context, scpi_special_numbers_def, &param1, bf, 15);
    fprintf(stderr, "\tP1=%s\r\n", bf);
    
    SCPI_NumberToStr(context, &param2, bf, 15);
    SCPI_NumberToStr(context, scpi_special_numbers_def, &param2, 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, &param1, false)) {
    if (!SCPI_ParamNumber(context, scpi_special_numbers_def, &param1, false)) {
        // do something, if parameter not present
    }
    // read second paraeter if present
    if (!SCPI_ParamNumber(context, &param2, false)) {
    if (!SCPI_ParamNumber(context, scpi_special_numbers_def, &param2, false)) {
        // do something, if parameter not present
    }
    
    SCPI_NumberToStr(context, &param1, bf, 15);
    SCPI_NumberToStr(context, scpi_special_numbers_def, &param1, bf, 15);
    fprintf(stderr, "\tP1=%s\r\n", bf);
    
    SCPI_NumberToStr(context, &param2, bf, 15);
    SCPI_NumberToStr(context, scpi_special_numbers_def, &param2, 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, &param, 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, &copy_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"},
};
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
}
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];
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
}
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
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, &parameter->number.value);
            return TRUE;
        case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX:
            strToDouble(token.ptr, &parameter->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, &param, mandatory);
    if (result) {
        if (SCPI_ParamIsNumber(&param, FALSE)) {
           *value = param.number.value;
            SCPI_ParamToDouble(context, &param, value);
        } else if (SCPI_ParamIsNumber(&param, 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, &param, mandatory);
    if (result) {
        *value = tmpVal;
        if (SCPI_ParamIsNumber(&param, FALSE)) {
            SCPI_ParamToInt(context, &param, value);
        } else if (SCPI_ParamIsNumber(&param, 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, &param, 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, &param, 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, &param, 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, &param, 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, &param, &intval);
            *value = intval ? TRUE : FALSE;
        } else {
            result = SCPI_ParamToChoice(context, &param, 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, &param, 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, &param, 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;
    }
}
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, &param, mandatory);
    int32_t intval;
    state.buffer = param.data.ptr;
    if (!value) {
        SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR);
        return FALSE;
    }
    result = SCPI_Parameter(context, &param, 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, &param, &(value->value));
            break;
        case SCPI_TOKEN_HEXNUM:
            SCPI_ParamToDouble(context, &param, &(value->value));
            break;
        case SCPI_TOKEN_OCTNUM:
            SCPI_ParamToDouble(context, &param, &(value->value));
            break;
        case SCPI_TOKEN_BINNUM:
            SCPI_ParamToDouble(context, &param, &(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, &param.number);
            SCPI_ParamToDouble(context, &param, &(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, &param.number);
            SCPI_ParamToChoice(context, &token, special, &intval);
            value->type = intval;
            value->value = 0;
            break;
        default:
            result = FALSE;
    }
    if (result) {
        memcpy(value, &param.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);
    }
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"},
};