Jan Breuer
2013-06-19 1ea396b98ee06b0ec56403b70d553ccc5b379b9a
Parse program data
6个文件已修改
1个文件已添加
1个文件已删除
715 ■■■■■ 已修改文件
libscpi/Makefile 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
libscpi/inc/scpi/lexer.h 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libscpi/inc/scpi/parser.h 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libscpi/inc/scpi/types.h 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libscpi/src/lexer.c 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libscpi/src/parser.c 99 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libscpi/test/test_lexer.c 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libscpi/test/test_lexer_parser.c 302 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libscpi/Makefile
@@ -36,7 +36,7 @@
TESTS = $(addprefix test/, \
    test_fifo.c test_scpi_utils.c test_lexer.c \
    test_fifo.c test_scpi_utils.c test_lexer_parser.c\
    )
TESTS_OBJS = $(TESTS:.c=.o)
libscpi/inc/scpi/lexer.h
@@ -43,45 +43,6 @@
extern "C" {
#endif
enum _token_type_t {
    TokComma,
    TokSemicolon,
    TokQuiestion,
    TokNewLine,
    TokHexnum,
    TokOctnum,
    TokBinnum,
    TokProgramMnemonic,
    TokDecimalNumericProgramData,
    TokSuffixProgramData,
    TokArbitraryBlockProgramData,
    TokSingleQuoteProgramData,
    TokDoubleQuoteProgramData,
    TokProgramExpression,
    TokCompoundProgramHeader,
    TokCommonProgramHeader,
    TokCompoundQueryProgramHeader,
    TokCommonQueryProgramHeader,
    TokWhiteSpace,
    TokUnknown,
};
typedef enum _token_type_t token_type_t;
struct _token_t {
    token_type_t type;
    const char * ptr;
    int len;
};
typedef struct _token_t token_t;
struct _lex_state_t {
    const char * buffer;
    const char * pos;
    int len;
};
typedef struct _lex_state_t lex_state_t;
int SCPI_LexWhiteSpace(lex_state_t * state, token_t * token);
int SCPI_LexProgramHeader(lex_state_t * state,  token_t * token);
int SCPI_LexQuestion(lex_state_t * state, token_t * token);
libscpi/inc/scpi/parser.h
@@ -39,11 +39,11 @@
#include "scpi/types.h"
#include "scpi/debug.h"
#include "scpi/lexer.h"
#ifdef    __cplusplus
extern "C" {
#endif
    void SCPI_Init(scpi_t * context);
    int SCPI_Input(scpi_t * context, const char * data, size_t len);
@@ -61,6 +61,10 @@
    bool_t SCPI_ParamText(scpi_t * context, const char ** value, size_t * len, bool_t mandatory);    
    int SCPI_ParseProgramData(lex_state_t * state, token_t * token);
    int SCPI_ParseAllProgramData(lex_state_t * state, token_t * token, int * numberOfParameters);
    int SCPI_DetectProgramMessageUnit(scpi_t * context);
#ifdef    __cplusplus
}
#endif
libscpi/inc/scpi/types.h
@@ -124,6 +124,63 @@
    typedef scpi_result_t(*scpi_write_control_t)(scpi_t * context, scpi_ctrl_name_t ctrl, scpi_reg_val_t val);
    typedef int (*scpi_error_callback_t)(scpi_t * context, int_fast16_t error);
    /* scpi lexer */
    enum _token_type_t {
        TokComma,
        TokSemicolon,
        TokQuiestion,
        TokNewLine,
        TokHexnum,
        TokOctnum,
        TokBinnum,
        TokProgramMnemonic,
        TokDecimalNumericProgramData,
        TokDecimalNumericProgramDataWithSuffix,
        TokSuffixProgramData,
        TokArbitraryBlockProgramData,
        TokSingleQuoteProgramData,
        TokDoubleQuoteProgramData,
        TokProgramExpression,
        TokCompoundProgramHeader,
        TokCommonProgramHeader,
        TokCompoundQueryProgramHeader,
        TokCommonQueryProgramHeader,
        TokWhiteSpace,
        TokAllProgramData,
        TokUnknown,
    };
    typedef enum _token_type_t token_type_t;
    struct _token_t {
        token_type_t type;
        const char * ptr;
        int len;
    };
    typedef struct _token_t token_t;
    struct _lex_state_t {
        const char * buffer;
        const char * pos;
        int len;
    };
    typedef struct _lex_state_t lex_state_t;
    /* scpi parser */
    enum _message_termination_t {
        PmutNone,
        PmutNewLine,
        PmutSemicolon,
    };
    typedef enum _message_termination_t message_termination_t;
    struct _scpi_parser_state_t {
        token_t programHeader;
        token_t programData;
        int numberOfParameters;
        message_termination_t termination;
    };
    typedef struct _scpi_parser_state_t scpi_parser_state_t;
    typedef scpi_result_t(*scpi_command_callback_t)(scpi_t *);
    /* scpi error queue */
@@ -204,6 +261,7 @@
        const scpi_unit_def_t * units;
        const scpi_special_number_def_t * special_numbers;
        void * user_context;
        scpi_parser_state_t parser_state;
    };
#ifdef  __cplusplus
libscpi/src/lexer.c
@@ -437,8 +437,9 @@
    } else {
        token->type = TokUnknown;
        state->pos = token->ptr;
        token->len = 0;
    }
    return token->len;
    return token->len > 0 ? token->len + 2 : 0;
}
@@ -510,7 +511,7 @@
        token->len = 0;
    }
    
    return token->len;
    return token->len > 0 ? token->len + 2 : 0;
}
/* 7.7.6 <ARBITRARY BLOCK PROGRAM DATA> */
@@ -521,7 +522,7 @@
int SCPI_LexArbitraryBlockProgramData(lex_state_t * state, token_t * token) {
    int i;
    int j = 0;
    const char * ptr = state->pos;
    token->ptr = state->pos;
    if (skipChr(state, '#')) {
@@ -542,11 +543,11 @@
            
            if(i == 0) {
                state->pos += j;
                if (!iseos(state)) {
                if ((state->buffer + state->len) < (state->pos)) {
                    token->len = 0;
                } else {
                    token->ptr = state->pos - j;
                    token->len = j;
                } else {
                    token->len = 0;
                }
            } else {
                token->len = 0;
@@ -564,7 +565,7 @@
        token->len = 0;
    }
    
    return token->len;
    return token->len + (token->ptr - ptr);
}
/* 7.7.7 <EXPRESSION PROGRAM DATA> */
libscpi/src/parser.c
@@ -568,13 +568,83 @@
    return FALSE;
}
int SCPI_ParseProgramData(lex_state_t * state, token_t * token) {
    token_t tmp;
    int result = 0;
    int wsLen;
    int suffixLen;
    int realLen = 0;
    realLen += SCPI_LexWhiteSpace(state, &tmp);
    if (result == 0) result = SCPI_LexNondecimalNumericData(state, token);
    if (result == 0) result = SCPI_LexCharacterProgramData(state, token);
    if (result == 0) {
        result = SCPI_LexDecimalNumericProgramData(state, token);
        if (result != 0) {
            wsLen = SCPI_LexWhiteSpace(state, &tmp);
            suffixLen = SCPI_LexSuffixProgramData(state, &tmp);
            if (suffixLen > 0) {
                token->len += wsLen + suffixLen;
                token->type = TokDecimalNumericProgramDataWithSuffix;
                result = token->len;
            }
        }
    }
    if (result == 0) result = SCPI_LexStringProgramData(state, token);
    if (result == 0) result = SCPI_LexArbitraryBlockProgramData(state, token);
    if (result == 0) result = SCPI_LexProgramExpression(state, token);
    realLen += SCPI_LexWhiteSpace(state, &tmp);
    return result + realLen;
}
int SCPI_ParseAllProgramData(lex_state_t * state, token_t * token, int * numberOfParameters) {
    int result;
    token_t tmp;
    int paramCount = 0;
    token->len = -1;
    token->type = TokAllProgramData;
    token->ptr = state->pos;
void SCPI_ProgramMessageUnit(scpi_t * context) {
    for (result = 1; result != 0; result = SCPI_LexComma(state, &tmp)) {
        token->len += result;
        if (result == 0) {
            token->type = TokUnknown;
            token->len = 0;
            break;
        }
        result = SCPI_ParseProgramData(state, &tmp);
        if (tmp.type != TokUnknown) {
            token->len += result;
        } else {
            token->type = TokUnknown;
            token->len = 0;
            break;
        }
        paramCount++;
    }
    if (token->len == -1) {
        token->len = 0;
    }
    if (numberOfParameters != NULL) {
        *numberOfParameters = paramCount;
    }
    return token->len;
}
int SCPI_DetectProgramMessageUnit(scpi_t * context) {
    lex_state_t state;
    token_t tmp;
    token_t header;
    token_t token;
    int result = 0;
    
    state.buffer = state.pos = context->buffer.data;
    state.len = context->buffer.position;
@@ -582,25 +652,22 @@
    /* ignore whitespace at the begginig */
    SCPI_LexWhiteSpace(&state, &tmp);
    
    SCPI_LexProgramHeader(&state, &header);
    SCPI_LexProgramHeader(&state, &context->parser_state.programHeader);
    
    SCPI_LexWhiteSpace(&state, &tmp);
    
    SCPI_ParseProgramDate(context, &state, &token);
    SCPI_ParseAllProgramData(&state, &context->parser_state.programData, &context->parser_state.numberOfParameters);
    
    SCPI_LexWhiteSpace(&state, &tmp);
    if (result == 0) result = SCPI_LexNewLine(&state, &tmp);
    if (result == 0) result = SCPI_LexSemicolon(&state, &tmp);
    
    {
        SCPI_LexComma(&state, &token);
        SCPI_LexWhiteSpace(&state, &tmp);
        SCPI_ParseProgramDate(context, &state);
        SCPI_LexWhiteSpace(&state, &tmp);
    if (TokSemicolon == tmp.type) {
        context->parser_state.termination = PmutSemicolon;
    } else if (TokNewLine == tmp.type) {
        context->parser_state.termination = PmutNewLine;
    } else {
        context->parser_state.termination = PmutNone;
    }
    SCPI_LexNewLine(&state, &tmp);
}
libscpi/test/test_lexer.c
File was deleted
libscpi/test/test_lexer_parser.c
New file
@@ -0,0 +1,302 @@
/*
 * File:   test_lexer.c
 * Author: jaybee
 *
 * Created on Thu Mar 21 14:39:03 UTC 2013
 */
#include <stdio.h>
#include <stdlib.h>
#include "CUnit/Basic.h"
#include "scpi/lexer.h"
#include "scpi/parser.h"
/*
 * CUnit Test Suite
 */
int init_suite(void) {
    return 0;
}
int clean_suite(void) {
    return 0;
}
typedef int (*lexfn_t)(lex_state_t * state, token_t * token);
typedef int (*lexfn2_t)(lex_state_t * state, token_t * token, int * cnt);
const char * typeToStr(token_type_t type) {
    switch (type) {
        case TokComma: return "TokComma";
        case TokSemicolon: return "TokSemicolon";
        case TokQuiestion: return "TokQuiestion";
        case TokNewLine: return "TokNewLine";
        case TokHexnum: return "TokHexnum";
        case TokOctnum: return "TokOctnum";
        case TokBinnum: return "TokBinnum";
        case TokProgramMnemonic: return "TokProgramMnemonic";
        case TokDecimalNumericProgramData: return "TokDecimalNumericProgramData";
        case TokDecimalNumericProgramDataWithSuffix: return "TokDecimalNumericProgramDataWithSuffix";
        case TokSuffixProgramData: return "TokSuffixProgramData";
        case TokArbitraryBlockProgramData: return "TokArbitraryBlockProgramData";
        case TokSingleQuoteProgramData: return "TokSingleQuoteProgramData";
        case TokDoubleQuoteProgramData: return "TokDoubleQuoteProgramData";
        case TokProgramExpression: return "TokProgramExpression";
        case TokCompoundProgramHeader: return "TokCompoundProgramHeader";
        case TokCommonProgramHeader: return "TokCommonProgramHeader";
        case TokCompoundQueryProgramHeader: return "TokCompoundQueryProgramHeader";
        case TokCommonQueryProgramHeader: return "TokCommonQueryProgramHeader";
        case TokWhiteSpace: return "TokWhiteSpace";
        case TokAllProgramData: return "TokAllProgramData";
        default: return "TokUnknown";
    }
}
void printToken(token_t * token) {
    printf("Token:\r\n");
    printf("\t->type = %s\r\n", typeToStr(token->type));
    printf("\t->ptr = %p (\"%.*s\")\r\n", token->ptr, token->len, token->ptr);
    printf("\t->len = %d\r\n", token->len);
}
#if 0
static void TEST_TOKEN(const char * str, lexfn_t fn, int offset, int len, token_type_t tp) {
    lex_state_t state;
    token_t token;
    state.buffer = state.pos = str;
    state.len = strlen(str);
    fn(&state, &token);
    CU_ASSERT_EQUAL(str + offset, token.ptr);
    CU_ASSERT_EQUAL(len, token.len);
    CU_ASSERT_EQUAL(tp, token.type);
}
#endif
#define TEST_TOKEN(s, f, o, l, t) do {          \
    const char * str = s;                       \
    lexfn_t fn = f;                             \
    int offset = o;                             \
    int len = l;                                \
    token_type_t tp = t;                        \
    lex_state_t state;                          \
    token_t token;                              \
                                                \
    state.buffer = state.pos = str;             \
    state.len = strlen(str);                    \
    fn(&state, &token);                         \
    CU_ASSERT_EQUAL(str + offset, token.ptr);   \
    CU_ASSERT_EQUAL(len, token.len);            \
    CU_ASSERT_EQUAL(tp, token.type);            \
    if (tp != token.type) printToken(&token);   \
    else                                        \
    if (len != token.len) printToken(&token);   \
} while(0)
void testWhiteSpace(void) {
    TEST_TOKEN("  \t MEAS", SCPI_LexWhiteSpace, 0, 4, TokWhiteSpace);
    TEST_TOKEN("MEAS", SCPI_LexWhiteSpace, 0, 0, TokUnknown);
}
void testNondecimal(void) {
    TEST_TOKEN("#H123fe5A", SCPI_LexNondecimalNumericData, 2, 7, TokHexnum);
    TEST_TOKEN("#B0111010101", SCPI_LexNondecimalNumericData, 2, 10, TokBinnum);
    TEST_TOKEN("#Q125725433", SCPI_LexNondecimalNumericData, 2, 9, TokOctnum);
}
void testCharacterProgramData(void) {
    TEST_TOKEN("abc_213as564", SCPI_LexCharacterProgramData, 0, 12, TokProgramMnemonic);
    TEST_TOKEN("abc_213as564 , ", SCPI_LexCharacterProgramData, 0, 12, TokProgramMnemonic);
}
void testDecimal(void) {
    TEST_TOKEN("10", SCPI_LexDecimalNumericProgramData, 0, 2, TokDecimalNumericProgramData);
    TEST_TOKEN("10 , ", SCPI_LexDecimalNumericProgramData, 0, 2, TokDecimalNumericProgramData);
    TEST_TOKEN("-10.5 , ", SCPI_LexDecimalNumericProgramData, 0, 5, TokDecimalNumericProgramData);
    TEST_TOKEN("+.5 , ", SCPI_LexDecimalNumericProgramData, 0, 3, TokDecimalNumericProgramData);
    TEST_TOKEN("-. , ", SCPI_LexDecimalNumericProgramData, 0, 0, TokUnknown);
    TEST_TOKEN("-1 e , ", SCPI_LexDecimalNumericProgramData, 0, 2, TokDecimalNumericProgramData);
    TEST_TOKEN("-1 e 3, ", SCPI_LexDecimalNumericProgramData, 0, 6, TokDecimalNumericProgramData);
    TEST_TOKEN("1.5E12", SCPI_LexDecimalNumericProgramData, 0, 6, TokDecimalNumericProgramData);
}
void testSuffix(void) {
    TEST_TOKEN("A/V , ", SCPI_LexSuffixProgramData, 0, 3, TokSuffixProgramData);
    TEST_TOKEN("mA.h", SCPI_LexSuffixProgramData, 0, 4, TokSuffixProgramData);
}
void testProgramHeader(void) {
    TEST_TOKEN("*IDN? ", SCPI_LexProgramHeader, 0, 5, TokCommonQueryProgramHeader);
    TEST_TOKEN("*RST ", SCPI_LexProgramHeader, 0, 4, TokCommonProgramHeader);
    TEST_TOKEN("*?; ", SCPI_LexProgramHeader, 0, 0, TokUnknown);
    TEST_TOKEN(":*IDN?; ", SCPI_LexProgramHeader, 0, 0, TokUnknown);
    TEST_TOKEN("MEAS:VOLT:DC? ", SCPI_LexProgramHeader, 0, 13, TokCompoundQueryProgramHeader);
    TEST_TOKEN("CONF:VOLT:DC ", SCPI_LexProgramHeader, 0, 12, TokCompoundProgramHeader);
    TEST_TOKEN(":MEAS:VOLT:DC? ", SCPI_LexProgramHeader, 0, 14, TokCompoundQueryProgramHeader);
    TEST_TOKEN(":MEAS::VOLT:DC? ", SCPI_LexProgramHeader, 0, 0, TokUnknown);
    TEST_TOKEN("*IDN?", SCPI_LexProgramHeader, 0, 5, TokCommonQueryProgramHeader);
    TEST_TOKEN("*RST", SCPI_LexProgramHeader, 0, 4, TokCommonProgramHeader);
}
void testArbitraryBlock(void) {
    TEST_TOKEN("#12AB", SCPI_LexArbitraryBlockProgramData, 3, 2, TokArbitraryBlockProgramData);
    TEST_TOKEN("#12AB, ", SCPI_LexArbitraryBlockProgramData, 3, 2, TokArbitraryBlockProgramData);
    TEST_TOKEN("#13AB", SCPI_LexArbitraryBlockProgramData, 0, 0, TokUnknown);
    TEST_TOKEN("#12\r\n, ", SCPI_LexArbitraryBlockProgramData, 3, 2, TokArbitraryBlockProgramData);
    TEST_TOKEN("#02AB, ", SCPI_LexArbitraryBlockProgramData, 0, 0, TokUnknown);
}
void testExpression(void) {
    TEST_TOKEN("( 1 + 2 )", SCPI_LexProgramExpression, 0, 9, TokProgramExpression);
    TEST_TOKEN("( 1 + 2 ) , ", SCPI_LexProgramExpression, 0, 9, TokProgramExpression);
    TEST_TOKEN("( 1 + 2  , ", SCPI_LexProgramExpression, 0, 0, TokUnknown);
}
void testString(void) {
    TEST_TOKEN("\"ahoj\"", SCPI_LexStringProgramData, 1, 4, TokDoubleQuoteProgramData);
    TEST_TOKEN("\"ahoj\" ", SCPI_LexStringProgramData, 1, 4, TokDoubleQuoteProgramData);
    TEST_TOKEN("'ahoj' ", SCPI_LexStringProgramData, 1, 4, TokSingleQuoteProgramData);
    TEST_TOKEN("'ahoj ", SCPI_LexStringProgramData, 0, 0, TokUnknown);
    TEST_TOKEN("'ah''oj' ", SCPI_LexStringProgramData, 1, 6, TokSingleQuoteProgramData);
    TEST_TOKEN("'ah\"oj' ", SCPI_LexStringProgramData, 1, 5, TokSingleQuoteProgramData);
    TEST_TOKEN("\"ah\"\"oj\" ", SCPI_LexStringProgramData, 1, 6, TokDoubleQuoteProgramData);
}
void testProgramData(void) {
    TEST_TOKEN("#H123fe5A", SCPI_ParseProgramData, 2, 7, TokHexnum);
    TEST_TOKEN("  #H123fe5A ", SCPI_ParseProgramData, 4, 7, TokHexnum);
    TEST_TOKEN("#B0111010101", SCPI_ParseProgramData, 2, 10, TokBinnum);
    TEST_TOKEN("#Q125725433", SCPI_ParseProgramData, 2, 9, TokOctnum);
    TEST_TOKEN("10", SCPI_ParseProgramData, 0, 2, TokDecimalNumericProgramData);
    TEST_TOKEN("10 , ", SCPI_ParseProgramData, 0, 2, TokDecimalNumericProgramData);
    TEST_TOKEN("-10.5 , ", SCPI_ParseProgramData, 0, 5, TokDecimalNumericProgramData);
    TEST_TOKEN("+.5 , ", SCPI_ParseProgramData, 0, 3, TokDecimalNumericProgramData);
    TEST_TOKEN("-. , ", SCPI_ParseProgramData, 0, 0, TokUnknown);
    TEST_TOKEN("-1 e , ", SCPI_ParseProgramData, 0, 4, TokDecimalNumericProgramDataWithSuffix);
    TEST_TOKEN("-1 e 3, ", SCPI_ParseProgramData, 0, 6, TokDecimalNumericProgramData);
    TEST_TOKEN("1.5E12", SCPI_ParseProgramData, 0, 6, TokDecimalNumericProgramData);
    TEST_TOKEN("#12AB", SCPI_ParseProgramData, 3, 2, TokArbitraryBlockProgramData);
    TEST_TOKEN("#12AB, ", SCPI_ParseProgramData, 3, 2, TokArbitraryBlockProgramData);
    TEST_TOKEN("#13AB", SCPI_ParseProgramData, 0, 0, TokUnknown);
    TEST_TOKEN("#12\r\n, ", SCPI_ParseProgramData, 3, 2, TokArbitraryBlockProgramData);
    TEST_TOKEN("#02AB, ", SCPI_ParseProgramData, 0, 0, TokUnknown);
    TEST_TOKEN("( 1 + 2 ) , ", SCPI_ParseProgramData, 0, 9, TokProgramExpression);
    TEST_TOKEN("( 1 + 2  , ", SCPI_ParseProgramData, 0, 0, TokUnknown);
    TEST_TOKEN("\"ahoj\" ", SCPI_ParseProgramData, 1, 4, TokDoubleQuoteProgramData);
    TEST_TOKEN("'ahoj' ", SCPI_ParseProgramData, 1, 4, TokSingleQuoteProgramData);
    TEST_TOKEN("'ahoj ", SCPI_ParseProgramData, 0, 0, TokUnknown);
    TEST_TOKEN("'ah''oj' ", SCPI_ParseProgramData, 1, 6, TokSingleQuoteProgramData);
    TEST_TOKEN("'ah\"oj' ", SCPI_ParseProgramData, 1, 5, TokSingleQuoteProgramData);
    TEST_TOKEN("\"ah\"\"oj\" ", SCPI_ParseProgramData, 1, 6, TokDoubleQuoteProgramData);
    TEST_TOKEN("abc_213as564 , ", SCPI_LexCharacterProgramData, 0, 12, TokProgramMnemonic);
    TEST_TOKEN("1.5E12 V", SCPI_ParseProgramData, 0, 8, TokDecimalNumericProgramDataWithSuffix);
}
#define TEST_ALL_TOKEN(s, f, o, l, t, c) do {   \
    const char * str = s;                       \
    lexfn2_t fn = f;                            \
    int offset = o;                             \
    int len = l;                                \
    token_type_t tp = t;                        \
    lex_state_t state;                          \
    token_t token;                              \
    int count;                                  \
                                                \
    state.buffer = state.pos = str;             \
    state.len = strlen(str);                    \
    fn(&state, &token, &count);                 \
    CU_ASSERT_EQUAL(str + offset, token.ptr);   \
    CU_ASSERT_EQUAL(len, token.len);            \
    CU_ASSERT_EQUAL(tp, token.type);            \
    CU_ASSERT_EQUAL(count, c);                  \
    if (tp != token.type) printToken(&token);   \
    else                                        \
    if (len != token.len) printToken(&token);   \
} while(0)
void testAllProgramData(void) {
    TEST_ALL_TOKEN("1.5E12 V", SCPI_ParseAllProgramData, 0, 8, TokAllProgramData, 1);
    TEST_ALL_TOKEN("1.5E12 V, abc_213as564, 10, #H123fe5A", SCPI_ParseAllProgramData, 0, 37, TokAllProgramData, 4);
    TEST_ALL_TOKEN("1.5E12 V, ", SCPI_ParseAllProgramData, 0, 0, TokUnknown, 1);
    TEST_ALL_TOKEN("#12\r\n, 1.5E12 V", SCPI_ParseAllProgramData, 0, 15, TokAllProgramData, 2);
    TEST_ALL_TOKEN(" ( 1 + 2 ) ,#12\r\n, 1.5E12 V", SCPI_ParseAllProgramData, 0, 27, TokAllProgramData, 3);
    TEST_ALL_TOKEN("\"ahoj\" , #12AB", SCPI_ParseAllProgramData, 0, 14, TokAllProgramData, 2);
}
#define TEST_DETECT(s, h, ht, d, dc, t) do {                                    \
    const char * str = s;                                                       \
    scpi_t context;                                                             \
    int result;                                                                 \
    context.buffer.data = s;                                                    \
    context.buffer.length = strlen(s);                                          \
    context.buffer.position = context.buffer.length;                            \
    result = SCPI_DetectProgramMessageUnit(&context);                           \
    CU_ASSERT_EQUAL(context.parser_state.programHeader.ptr, str+h);             \
    CU_ASSERT_EQUAL(context.parser_state.programHeader.type, ht);               \
    CU_ASSERT_EQUAL(context.parser_state.programData.ptr, str+d);               \
    CU_ASSERT_EQUAL(context.parser_state.numberOfParameters, dc);               \
    CU_ASSERT_EQUAL(context.parser_state.termination, t);                       \
} while(0)
void testDetectProgramMessageUnit(void) {
    TEST_DETECT("*IDN?\r\n", 0, TokCommonQueryProgramHeader, 5, 0, PmutNewLine);
    TEST_DETECT(" MEAS:VOLT:DC?\r\n", 1, TokCompoundQueryProgramHeader, 14, 0, PmutNewLine);
    TEST_DETECT(" MEAS:VOLT:DC? 1.2 V\r\n", 1, TokCompoundQueryProgramHeader, 15, 1, PmutNewLine);
    TEST_DETECT(" CONF:VOLT:DC 1.2 V, 100mv;", 1, TokCompoundProgramHeader, 14, 2, PmutSemicolon);
    TEST_DETECT(" CONF:VOLT:DC 1.2 V, 100mv", 1, TokCompoundProgramHeader, 14, 2, PmutNone);
    TEST_DETECT(" CONF:VOLT:DC 1.2 V, \r\n", 1, TokCompoundProgramHeader, 14, 1, PmutNewLine);
}
int main() {
    CU_pSuite pSuite = NULL;
    /* Initialize the CUnit test registry */
    if (CUE_SUCCESS != CU_initialize_registry())
        return CU_get_error();
    /* Add a suite to the registry */
    pSuite = CU_add_suite("Lexer", init_suite, clean_suite);
    if (NULL == pSuite) {
        CU_cleanup_registry();
        return CU_get_error();
    }
    /* Add the tests to the suite */
    if ((NULL == CU_add_test(pSuite, "WhiteSpace", testWhiteSpace))
            || (NULL == CU_add_test(pSuite, "Nondecimal", testNondecimal))
            || (NULL == CU_add_test(pSuite, "CharacterProgramData", testCharacterProgramData))
            || (NULL == CU_add_test(pSuite, "Decimal", testDecimal))
            || (NULL == CU_add_test(pSuite, "Suffix", testSuffix))
            || (NULL == CU_add_test(pSuite, "ProgramHeader", testProgramHeader))
            || (NULL == CU_add_test(pSuite, "ArbitraryBlock", testArbitraryBlock))
            || (NULL == CU_add_test(pSuite, "Expression", testExpression))
            || (NULL == CU_add_test(pSuite, "String", testString))
            || (NULL == CU_add_test(pSuite, "ProgramData", testProgramData))
            || (NULL == CU_add_test(pSuite, "AllProgramData", testAllProgramData))
            || (NULL == CU_add_test(pSuite, "DetectProgramMessageUnit", testDetectProgramMessageUnit))
            ) {
        CU_cleanup_registry();
        return CU_get_error();
    }
    /* Run all tests using the CUnit Basic interface */
    CU_basic_set_mode(CU_BRM_VERBOSE);
    CU_basic_run_tests();
    CU_cleanup_registry();
    return CU_get_error();
}