Implement Traversal of the Header Tree. Solve #22
| | |
| | | void SCPI_Init(scpi_t * context); |
| | | |
| | | int SCPI_Input(scpi_t * context, const char * data, int len); |
| | | int SCPI_Parse(scpi_t * context, const char * data, int len); |
| | | int SCPI_Parse(scpi_t * context, char * data, int len); |
| | | |
| | | |
| | | size_t SCPI_ResultCharacters(scpi_t * context, const char * data, size_t len); |
| | |
| | | |
| | | struct _scpi_token_t { |
| | | scpi_token_type_t type; |
| | | const char * ptr; |
| | | char * ptr; |
| | | int len; |
| | | }; |
| | | typedef struct _scpi_token_t scpi_token_t; |
| | | |
| | | struct _lex_state_t { |
| | | const char * buffer; |
| | | const char * pos; |
| | | char * buffer; |
| | | char * pos; |
| | | int len; |
| | | }; |
| | | typedef struct _lex_state_t lex_state_t; |
| | |
| | | * @return |
| | | */ |
| | | int scpiLex_DecimalNumericProgramData(lex_state_t * state, scpi_token_t * token) { |
| | | const char * rollback; |
| | | char * rollback; |
| | | token->ptr = state->pos; |
| | | |
| | | if (skipMantisa(state)) { |
| | |
| | | * @param len - command line length |
| | | * @return 1 if the last evaluated command was found |
| | | */ |
| | | int SCPI_Parse(scpi_t * context, const char * data, int len) { |
| | | int SCPI_Parse(scpi_t * context, char * data, int len) { |
| | | int result = 0; |
| | | scpi_parser_state_t * state; |
| | | int r; |
| | | scpi_token_t cmd_prev = {SCPI_TOKEN_UNKNOWN, NULL, 0}; |
| | | |
| | | if (context == NULL) { |
| | | return -1; |
| | |
| | | if (state->programHeader.type == SCPI_TOKEN_INVALID) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_INVALID_CHARACTER); |
| | | } else if (state->programHeader.len > 0) { |
| | | |
| | | composeCompoundCommand(&cmd_prev, &state->programHeader); |
| | | |
| | | if (findCommandHeader(context, state->programHeader.ptr, state->programHeader.len)) { |
| | | |
| | | context->param_list.lex_state.buffer = state->programData.ptr; |
| | |
| | | processCommand(context); |
| | | |
| | | result = 1; |
| | | cmd_prev = state->programHeader; |
| | | } else { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_UNDEFINED_HEADER); |
| | | } |
| | |
| | | * @param token |
| | | * @param ptr |
| | | */ |
| | | static void invalidateToken(scpi_token_t * token, const char * ptr) { |
| | | static void invalidateToken(scpi_token_t * token, char * ptr) { |
| | | token->len = 0; |
| | | token->ptr = ptr; |
| | | token->type = SCPI_TOKEN_UNKNOWN; |
| | |
| | | * @param len |
| | | * @return |
| | | */ |
| | | int scpiParser_detectProgramMessageUnit(scpi_parser_state_t * state, const char * buffer, int len) { |
| | | int scpiParser_detectProgramMessageUnit(scpi_parser_state_t * state, char * buffer, int len) { |
| | | lex_state_t lex_state; |
| | | scpi_token_t tmp; |
| | | int result = 0; |
| | |
| | | |
| | | int scpiParser_parseProgramData(lex_state_t * state, scpi_token_t * token) LOCAL; |
| | | int scpiParser_parseAllProgramData(lex_state_t * state, scpi_token_t * token, int * numberOfParameters) LOCAL; |
| | | int scpiParser_detectProgramMessageUnit(scpi_parser_state_t * state, const char * buffer, int len) LOCAL; |
| | | int scpiParser_detectProgramMessageUnit(scpi_parser_state_t * state, char * buffer, int len) LOCAL; |
| | | |
| | | #ifdef __cplusplus |
| | | } |
| | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Compose command from previsou command anc current command |
| | | * |
| | | * @param prev pointer to previous command |
| | | * @param current pointer of current command |
| | | * |
| | | * prev and current should be in the same memory buffer |
| | | */ |
| | | scpi_bool_t composeCompoundCommand(const scpi_token_t * prev, scpi_token_t * current) { |
| | | size_t i; |
| | | |
| | | /* Invalid input */ |
| | | if (current == NULL || current->ptr == NULL || current->len == 0) |
| | | return FALSE; |
| | | |
| | | /* no previous command - nothing to do*/ |
| | | if (prev->ptr == NULL || prev->len == 0) |
| | | return TRUE; |
| | | |
| | | /* Common command or command root - nothing to do */ |
| | | if (current->ptr[0] == '*' || current->ptr[0] == ':') |
| | | return TRUE; |
| | | |
| | | /* Previsou command was common command - nothing to do */ |
| | | if (prev->ptr[0] == '*') |
| | | return TRUE; |
| | | |
| | | /* Find last occurence of ':' */ |
| | | for (i = prev->len; i > 0; i--) { |
| | | if (prev->ptr[i - 1] == ':') { |
| | | break; |
| | | } |
| | | } |
| | | |
| | | /* Previous command was simple command - nothing to do*/ |
| | | if (i == 0) |
| | | return TRUE; |
| | | |
| | | current->ptr -= i; |
| | | current->len += i; |
| | | memmove(current->ptr, prev->ptr, i); |
| | | return TRUE; |
| | | } |
| | | |
| | | |
| | | |
| | | #if !HAVE_STRNLEN |
| | | /* use FreeBSD strnlen */ |
| | |
| | | size_t skipWhitespace(const char * cmd, size_t len) LOCAL; |
| | | scpi_bool_t matchPattern(const char * pattern, size_t pattern_len, const char * str, size_t str_len) LOCAL; |
| | | scpi_bool_t matchCommand(const char * pattern, const char * cmd, size_t len) LOCAL; |
| | | scpi_bool_t composeCompoundCommand(const scpi_token_t * prev, scpi_token_t * current); |
| | | |
| | | #if !HAVE_STRNLEN |
| | | size_t BSD_strnlen(const char *s, size_t maxlen); |
| | |
| | | #endif |
| | | |
| | | #define TEST_TOKEN(s, f, o, l, t) do { \ |
| | | const char * str = s; \ |
| | | char * str = s; \ |
| | | lexfn_t fn = f; \ |
| | | int offset = o; \ |
| | | int len = l; \ |
| | |
| | | |
| | | |
| | | #define TEST_ALL_TOKEN(s, f, o, l, t, c) do { \ |
| | | const char * str = s; \ |
| | | char * str = s; \ |
| | | lexfn2_t fn = f; \ |
| | | int offset = o; \ |
| | | int len = l; \ |
| | |
| | | |
| | | |
| | | #define TEST_DETECT(s, h, hl, ht, d, dc, t) do { \ |
| | | const char * str = s; \ |
| | | char * str = s; \ |
| | | scpi_parser_state_t state; \ |
| | | int result; \ |
| | | result = scpiParser_detectProgramMessageUnit(&state, str, strlen(str)); \ |
| | | result = scpiParser_detectProgramMessageUnit(&state, str, strlen(str)); \ |
| | | CU_ASSERT_EQUAL(state.programHeader.ptr, str + h); \ |
| | | CU_ASSERT_EQUAL(state.programHeader.len, hl); \ |
| | | CU_ASSERT_EQUAL(state.programHeader.type, ht); \ |
| | |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | scpi_result_t test_treeA(scpi_t* context) { |
| | | |
| | | SCPI_ResultInt(context, 10); |
| | | |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | scpi_result_t test_treeB(scpi_t* context) { |
| | | |
| | | SCPI_ResultInt(context, 20); |
| | | |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | static const scpi_command_t scpi_commands[] = { |
| | | /* IEEE Mandated Commands (SCPI std V1999.0 4.1.1) */ |
| | | { .pattern = "*CLS", .callback = SCPI_CoreCls,}, |
| | |
| | | |
| | | { .pattern = "STATus:PRESet", .callback = SCPI_StatusPreset,}, |
| | | |
| | | { .pattern = "TEXTfunction", .callback = text_function,}, |
| | | { .pattern = "TEXTfunction?", .callback = text_function,}, |
| | | |
| | | { .pattern = "TEST:TREEA?", .callback = test_treeA,}, |
| | | { .pattern = "TEST:TREEB?", .callback = test_treeB,}, |
| | | |
| | | SCPI_CMD_LIST_END |
| | | }; |
| | |
| | | TEST_INPUT("", "MA,IN,0,VER\r\n"); |
| | | output_buffer_clear(); |
| | | |
| | | /* Test ctree traversal */ |
| | | TEST_INPUT("TEST:TREEA?;TREEB?\r\n", "10\r\n20\r\n"); |
| | | output_buffer_clear(); |
| | | |
| | | TEST_INPUT("TEST:TREEA?;:TEXT? \"PARAM1\", \"PARAM2\"\r\n", "10\r\n\"PARAM2\"\r\n"); |
| | | output_buffer_clear(); |
| | | |
| | | CU_ASSERT_EQUAL(err_buffer_pos, 0); |
| | | error_buffer_clear(); |
| | | |
| | | // TODO: Compound commands A:B;C -> A:B; A:C |
| | | } |
| | | |
| | | void testErrorHandling(void) { |
| | |
| | | TEST_ERROR("IDN?\r\n", "", SCPI_ERROR_UNDEFINED_HEADER); |
| | | TEST_ERROR("*ESE\r\n", "", SCPI_ERROR_MISSING_PARAMETER); |
| | | TEST_ERROR("*IDN? 12\r\n", "MA,IN,0,VER\r\n", SCPI_ERROR_PARAMETER_NOT_ALLOWED); |
| | | TEST_ERROR("TEXT \"PARAM1\", \"PARAM2\"\r\n", "\"PARAM2\"\r\n", 0); |
| | | TEST_ERROR("TEXT? \"PARAM1\", \"PARAM2\"\r\n", "\"PARAM2\"\r\n", 0); |
| | | |
| | | // TODO: SCPI_ERROR_INVALID_SEPARATOR |
| | | // TODO: SCPI_ERROR_INVALID_SUFFIX |