| | |
| | | * Copyright (c) 2012-2013 Jan Breuer, |
| | | * |
| | | * All Rights Reserved |
| | | * |
| | | * |
| | | * Redistribution and use in source and binary forms, with or without |
| | | * modification, are permitted provided that the following conditions are |
| | | * met: |
| | |
| | | * 2. Redistributions in binary form must reproduce the above copyright |
| | | * notice, this list of conditions and the following disclaimer in the |
| | | * documentation and/or other materials provided with the distribution. |
| | | * |
| | | * |
| | | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR |
| | | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| | | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| | |
| | | /** |
| | | * @file scpi_parser.c |
| | | * @date Thu Nov 15 10:58:45 UTC 2012 |
| | | * |
| | | * |
| | | * @brief SCPI parser implementation |
| | | * |
| | | * |
| | | * |
| | | * |
| | | */ |
| | | |
| | | #include <ctype.h> |
| | | #include <string.h> |
| | | |
| | | #include "scpi/config.h" |
| | | #include "scpi/parser.h" |
| | | #include "utils.h" |
| | | #include "parser_private.h" |
| | | #include "lexer_private.h" |
| | | #include "scpi/error.h" |
| | | |
| | | |
| | | static size_t patternSeparatorPos(const char * pattern, size_t len); |
| | | static size_t cmdSeparatorPos(const char * cmd, size_t len); |
| | | static size_t cmdTerminatorPos(const char * cmd, size_t len); |
| | | static size_t cmdlineSeparatorPos(const char * cmd, size_t len); |
| | | static char * cmdlineSeparator(const char * cmd, size_t len); |
| | | static char * cmdlineTerminator(const char * cmd, size_t len); |
| | | static const char * cmdlineNext(const char * cmd, size_t len); |
| | | static bool_t cmdMatch(const char * pattern, const char * cmd, size_t len); |
| | | |
| | | static void paramSkipBytes(scpi_t * context, size_t num); |
| | | static void paramSkipWhitespace(scpi_t * context); |
| | | static bool_t paramNext(scpi_t * context, bool_t mandatory); |
| | | |
| | | /* |
| | | int _strnicmp(const char* s1, const char* s2, size_t len) { |
| | | int result = 0; |
| | | int i; |
| | | |
| | | for (i = 0; i < len && s1[i] && s2[i]; i++) { |
| | | char c1 = tolower(s1[i]); |
| | | char c2 = tolower(s2[i]); |
| | | if (c1 != c2) { |
| | | result = (int) c1 - (int) c2; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | */ |
| | | |
| | | /** |
| | | * Find pattern separator position |
| | | * @param pattern |
| | | * @param len - max search length |
| | | * @return position of separator or len |
| | | */ |
| | | size_t patternSeparatorPos(const char * pattern, size_t len) { |
| | | |
| | | char * separator = strnpbrk(pattern, len, "?:[]"); |
| | | if (separator == NULL) { |
| | | return len; |
| | | } else { |
| | | return separator - pattern; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Find command separator position |
| | | * @param cmd - input command |
| | | * @param len - max search length |
| | | * @return position of separator or len |
| | | */ |
| | | size_t cmdSeparatorPos(const char * cmd, size_t len) { |
| | | char * separator = strnpbrk(cmd, len, ":?"); |
| | | size_t result; |
| | | if (separator == NULL) { |
| | | result = len; |
| | | } else { |
| | | result = separator - cmd; |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Find command termination character |
| | | * @param cmd - input command |
| | | * @param len - max search length |
| | | * @return position of terminator or len |
| | | */ |
| | | size_t cmdTerminatorPos(const char * cmd, size_t len) { |
| | | char * terminator = strnpbrk(cmd, len, "; \r\n\t"); |
| | | if (terminator == NULL) { |
| | | return len; |
| | | } else { |
| | | return terminator - cmd; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Find command line separator |
| | | * @param cmd - input command |
| | | * @param len - max search length |
| | | * @return pointer to line separator or NULL |
| | | */ |
| | | char * cmdlineSeparator(const char * cmd, size_t len) { |
| | | return strnpbrk(cmd, len, ";\r\n"); |
| | | } |
| | | |
| | | /** |
| | | * Find command line terminator |
| | | * @param cmd - input command |
| | | * @param len - max search length |
| | | * @return pointer to command line terminator or NULL |
| | | */ |
| | | char * cmdlineTerminator(const char * cmd, size_t len) { |
| | | return strnpbrk(cmd, len, "\r\n"); |
| | | } |
| | | |
| | | /** |
| | | * Find command line separator position |
| | | * @param cmd - input command |
| | | * @param len - max search length |
| | | * @return position of line separator or len |
| | | */ |
| | | size_t cmdlineSeparatorPos(const char * cmd, size_t len) { |
| | | char * separator = cmdlineSeparator(cmd, len); |
| | | if (separator == NULL) { |
| | | return len; |
| | | } else { |
| | | return separator - cmd; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Find next part of command |
| | | * @param cmd - input command |
| | | * @param len - max search length |
| | | * @return Pointer to next part of command |
| | | */ |
| | | const char * cmdlineNext(const char * cmd, size_t len) { |
| | | const char * separator = cmdlineSeparator(cmd, len); |
| | | if (separator == NULL) { |
| | | return cmd + len; |
| | | } else { |
| | | return separator + 1; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Compare pattern and command |
| | | * @param pattern |
| | | * @param cmd - command |
| | | * @param len - max search length |
| | | * @return TRUE if pattern matches, FALSE otherwise |
| | | */ |
| | | bool_t cmdMatch(const char * pattern, const char * cmd, size_t len) { |
| | | int result = FALSE; |
| | | |
| | | const char * pattern_ptr = pattern; |
| | | int pattern_len = strlen(pattern); |
| | | const char * pattern_end = pattern + pattern_len; |
| | | |
| | | const char * cmd_ptr = cmd; |
| | | size_t cmd_len = strnlen(cmd, len); |
| | | const char * cmd_end = cmd + cmd_len; |
| | | |
| | | while (1) { |
| | | int pattern_sep_pos = patternSeparatorPos(pattern_ptr, pattern_end - pattern_ptr); |
| | | int cmd_sep_pos = cmdSeparatorPos(cmd_ptr, cmd_end - cmd_ptr); |
| | | |
| | | if (matchPattern(pattern_ptr, pattern_sep_pos, cmd_ptr, cmd_sep_pos)) { |
| | | pattern_ptr = pattern_ptr + pattern_sep_pos; |
| | | cmd_ptr = cmd_ptr + cmd_sep_pos; |
| | | result = TRUE; |
| | | |
| | | // command is complete |
| | | if ((pattern_ptr == pattern_end) && (cmd_ptr >= cmd_end)) { |
| | | break; |
| | | } |
| | | |
| | | // pattern complete, but command not |
| | | if ((pattern_ptr == pattern_end) && (cmd_ptr < cmd_end)) { |
| | | result = FALSE; |
| | | break; |
| | | } |
| | | |
| | | // command complete, but pattern not |
| | | if (cmd_ptr >= cmd_end) { |
| | | result = FALSE; |
| | | break; |
| | | } |
| | | |
| | | // both command and patter contains command separator at this position |
| | | if ((pattern_ptr[0] == cmd_ptr[0]) && ((pattern_ptr[0] == ':') || (pattern_ptr[0] == '?'))) { |
| | | pattern_ptr = pattern_ptr + 1; |
| | | cmd_ptr = cmd_ptr + 1; |
| | | } else { |
| | | result = FALSE; |
| | | break; |
| | | } |
| | | } else { |
| | | result = FALSE; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | #include "scpi/constants.h" |
| | | |
| | | /** |
| | | * Write data to SCPI output |
| | |
| | | * @param len - lenght of data to be written |
| | | * @return number of bytes written |
| | | */ |
| | | static inline size_t writeData(scpi_t * context, const char * data, size_t len) { |
| | | static size_t writeData(scpi_t * context, const char * data, size_t len) { |
| | | return context->interface->write(context, data, len); |
| | | } |
| | | |
| | |
| | | * @param context |
| | | * @return |
| | | */ |
| | | static inline int flushData(scpi_t * context) { |
| | | static int flushData(scpi_t * context) { |
| | | if (context && context->interface && context->interface->flush) { |
| | | return context->interface->flush(context); |
| | | } else { |
| | |
| | | * @param context |
| | | * @return number of bytes written |
| | | */ |
| | | static inline size_t writeDelimiter(scpi_t * context) { |
| | | static size_t writeDelimiter(scpi_t * context) { |
| | | if (context->output_count > 0) { |
| | | return writeData(context, ", ", 2); |
| | | return writeData(context, ",", 2); |
| | | } else { |
| | | return 0; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Zapis nove radky na SCPI vystup |
| | | * Conditionaly write "New Line" |
| | | * @param context |
| | | * @return pocet zapsanych znaku |
| | | * @return number of characters written |
| | | */ |
| | | static inline size_t writeNewLine(scpi_t * context) { |
| | | size_t len; |
| | | static size_t writeNewLine(scpi_t * context) { |
| | | if (context->output_count > 0) { |
| | | size_t len; |
| | | len = writeData(context, "\r\n", 2); |
| | | flushData(context); |
| | | return len; |
| | |
| | | } |
| | | |
| | | /** |
| | | * Process command |
| | | * @param context |
| | | */ |
| | | static void processCommand(scpi_t * context) { |
| | | const scpi_command_t * cmd = context->param_list.cmd; |
| | | lex_state_t * state = &context->param_list.lex_state; |
| | | |
| | | context->cmd_error = FALSE; |
| | | context->output_count = 0; |
| | | context->input_count = 0; |
| | | |
| | | SCPI_DEBUG_COMMAND(context); |
| | | /* if callback exists - call command callback */ |
| | | if (cmd->callback != NULL) { |
| | | if ((cmd->callback(context) != SCPI_RES_OK) && !context->cmd_error) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_EXECUTION_ERROR); |
| | | } |
| | | } |
| | | |
| | | /* conditionaly write new line */ |
| | | writeNewLine(context); |
| | | |
| | | /* set error if command callback did not read all parameters */ |
| | | if (state->pos < (state->buffer + state->len) && !context->cmd_error) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_PARAMETER_NOT_ALLOWED); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Cycle all patterns and search matching pattern. Execute command callback. |
| | | * @param context |
| | | * @result TRUE if context->paramlist is filled with correct values |
| | | */ |
| | | static scpi_bool_t findCommandHeader(scpi_t * context, const char * header, int len) { |
| | | int32_t i; |
| | | const scpi_command_t * cmd; |
| | | |
| | | for (i = 0; context->cmdlist[i].pattern != NULL; i++) { |
| | | cmd = &context->cmdlist[i]; |
| | | if (matchCommand(cmd->pattern, header, len)) { |
| | | context->param_list.cmd = cmd; |
| | | return TRUE; |
| | | } |
| | | } |
| | | return FALSE; |
| | | } |
| | | |
| | | /** |
| | | * Parse one command line |
| | | * @param context |
| | | * @param data - complete command line |
| | | * @param len - command line length |
| | | * @return 1 if the last evaluated command was found |
| | | */ |
| | | int SCPI_Parse(scpi_t * context, const char * data, size_t len) { |
| | | int32_t i; |
| | | int SCPI_Parse(scpi_t * context, const char * data, int len) { |
| | | int result = 0; |
| | | const char * cmdline_end = data + len; |
| | | const char * cmdline_ptr = data; |
| | | size_t cmd_len; |
| | | scpi_parser_state_t * state; |
| | | int r; |
| | | |
| | | if (context == NULL) { |
| | | return -1; |
| | | } |
| | | |
| | | while (cmdline_ptr < cmdline_end) { |
| | | state = &context->parser_state; |
| | | |
| | | while (1) { |
| | | result = 0; |
| | | cmd_len = cmdTerminatorPos(cmdline_ptr, cmdline_end - cmdline_ptr); |
| | | if (cmd_len > 0) { |
| | | for (i = 0; context->cmdlist[i].pattern != NULL; i++) { |
| | | if (cmdMatch(context->cmdlist[i].pattern, cmdline_ptr, cmd_len)) { |
| | | if (context->cmdlist[i].callback != NULL) { |
| | | context->cmd_error = FALSE; |
| | | context->paramlist.cmd = &context->cmdlist[i]; |
| | | context->paramlist.parameters = cmdline_ptr + cmd_len; |
| | | context->paramlist.length = cmdlineSeparatorPos(context->paramlist.parameters, cmdline_end - context->paramlist.parameters); |
| | | context->output_count = 0; |
| | | context->input_count = 0; |
| | | |
| | | SCPI_DEBUG_COMMAND(context); |
| | | if ((context->cmdlist[i].callback(context) != SCPI_RES_OK) && !context->cmd_error) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_EXECUTION_ERROR); |
| | | } |
| | | r = scpiParser_detectProgramMessageUnit(state, data, len); |
| | | |
| | | writeNewLine(context); // conditionaly write new line |
| | | if (state->programHeader.type == SCPI_TOKEN_INVALID) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_INVALID_CHARACTER); |
| | | } else if (state->programHeader.len > 0) { |
| | | if (findCommandHeader(context, state->programHeader.ptr, state->programHeader.len)) { |
| | | |
| | | paramSkipWhitespace(context); |
| | | if (context->paramlist.length != 0 && !context->cmd_error) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_PARAMETER_NOT_ALLOWED); |
| | | } |
| | | context->param_list.lex_state.buffer = state->programData.ptr; |
| | | context->param_list.lex_state.pos = context->param_list.lex_state.buffer; |
| | | context->param_list.lex_state.len = state->programData.len; |
| | | context->param_list.cmd_raw.data = state->programHeader.ptr; |
| | | context->param_list.cmd_raw.position = 0; |
| | | context->param_list.cmd_raw.length = state->programHeader.len; |
| | | |
| | | result = 1; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | if (result == 0) { |
| | | processCommand(context); |
| | | |
| | | result = 1; |
| | | } else { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_UNDEFINED_HEADER); |
| | | } |
| | | } |
| | | cmdline_ptr = cmdlineNext(cmdline_ptr, cmdline_end - cmdline_ptr); |
| | | |
| | | if (r < len) { |
| | | data += r; |
| | | len -= r; |
| | | } else { |
| | | break; |
| | | } |
| | | |
| | | } |
| | | return result; |
| | |
| | | * @param interface |
| | | */ |
| | | void SCPI_Init(scpi_t * context) { |
| | | if (context->idn[0] == NULL) { |
| | | context->idn[0] = SCPI_DEFAULT_1_MANUFACTURE; |
| | | } |
| | | if (context->idn[1] == NULL) { |
| | | context->idn[1] = SCPI_DEFAULT_2_MODEL; |
| | | } |
| | | if (context->idn[2] == NULL) { |
| | | context->idn[2] = SCPI_DEFAULT_3; |
| | | } |
| | | if (context->idn[3] == NULL) { |
| | | context->idn[3] = SCPI_DEFAULT_4_REVISION; |
| | | } |
| | | |
| | | context->buffer.position = 0; |
| | | SCPI_ErrorInit(context); |
| | | } |
| | |
| | | * Interface to the application. Adds data to system buffer and try to search |
| | | * command line termination. If the termination is found or if len=0, command |
| | | * parser is called. |
| | | * |
| | | * |
| | | * @param context |
| | | * @param data - data to process |
| | | * @param len - length of data |
| | | * @return |
| | | * @return |
| | | */ |
| | | int SCPI_Input(scpi_t * context, const char * data, size_t len) { |
| | | int SCPI_Input(scpi_t * context, const char * data, int len) { |
| | | int result = 0; |
| | | size_t buffer_free; |
| | | char * cmd_term; |
| | | int ws; |
| | | size_t totcmdlen = 0; |
| | | int cmdlen = 0; |
| | | |
| | | if (len == 0) { |
| | | context->buffer.data[context->buffer.position] = 0; |
| | | result = SCPI_Parse(context, context->buffer.data, context->buffer.position); |
| | | context->buffer.position = 0; |
| | | } else { |
| | | int buffer_free; |
| | | |
| | | buffer_free = context->buffer.length - context->buffer.position; |
| | | if (len > (buffer_free - 1)) { |
| | | return -1; |
| | |
| | | context->buffer.position += len; |
| | | context->buffer.data[context->buffer.position] = 0; |
| | | |
| | | ws = skipWhitespace(context->buffer.data, context->buffer.position); |
| | | cmd_term = cmdlineTerminator(context->buffer.data + ws, context->buffer.position - ws); |
| | | if (cmd_term != NULL) { |
| | | int curr_len = cmd_term - context->buffer.data; |
| | | result = SCPI_Parse(context, context->buffer.data + ws, curr_len - ws); |
| | | memmove(context->buffer.data, cmd_term, context->buffer.position - curr_len); |
| | | context->buffer.position -= curr_len; |
| | | |
| | | while (1) { |
| | | cmdlen = scpiParser_detectProgramMessageUnit(&context->parser_state, context->buffer.data + totcmdlen, context->buffer.position - totcmdlen); |
| | | totcmdlen += cmdlen; |
| | | if (context->parser_state.termination == SCPI_MESSAGE_TERMINATION_NL) break; |
| | | if (context->parser_state.programHeader.type == SCPI_TOKEN_UNKNOWN) break; |
| | | if (totcmdlen >= context->buffer.position) break; |
| | | } |
| | | |
| | | if (context->parser_state.termination == SCPI_MESSAGE_TERMINATION_NL) { |
| | | result = SCPI_Parse(context, context->buffer.data, totcmdlen); |
| | | memmove(context->buffer.data, context->buffer.data + totcmdlen, context->buffer.position - totcmdlen); |
| | | context->buffer.position -= totcmdlen; |
| | | } |
| | | } |
| | | |
| | |
| | | * Write raw string result to the output |
| | | * @param context |
| | | * @param data |
| | | * @return |
| | | * @return |
| | | */ |
| | | size_t SCPI_ResultString(scpi_t * context, const char * data) { |
| | | size_t len = strlen(data); |
| | | size_t SCPI_ResultCharacters(scpi_t * context, const char * data, size_t len) { |
| | | size_t result = 0; |
| | | result += writeDelimiter(context); |
| | | result += writeData(context, data, len); |
| | |
| | | * Write integer value to the result |
| | | * @param context |
| | | * @param val |
| | | * @return |
| | | * @return |
| | | */ |
| | | size_t SCPI_ResultInt(scpi_t * context, int32_t val) { |
| | | char buffer[12]; |
| | | return SCPI_ResultIntBase(context, val, 10); |
| | | } |
| | | |
| | | /** |
| | | * Return prefix of nondecimal base |
| | | * @param base |
| | | * @return |
| | | */ |
| | | static const char * getBasePrefix(int8_t base) { |
| | | switch (base) { |
| | | case 2: return "#B"; |
| | | case 8: return "#Q"; |
| | | case 16: return "#H"; |
| | | default: return NULL; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Write integer value in specific base to the result |
| | | * @param context |
| | | * @param val |
| | | * @param base |
| | | * @return |
| | | */ |
| | | size_t SCPI_ResultIntBase(scpi_t * context, int32_t val, int8_t base) { |
| | | char buffer[33]; |
| | | const char * basePrefix; |
| | | size_t result = 0; |
| | | size_t len = longToStr(val, buffer, sizeof (buffer)); |
| | | size_t len; |
| | | |
| | | len = longToStr(val, buffer, sizeof (buffer), base); |
| | | basePrefix = getBasePrefix(base); |
| | | |
| | | result += writeDelimiter(context); |
| | | if (basePrefix != NULL) { |
| | | result += writeData(context, basePrefix, 2); |
| | | } |
| | | result += writeData(context, buffer, len); |
| | | context->output_count++; |
| | | return result; |
| | |
| | | * Write double walue to the result |
| | | * @param context |
| | | * @param val |
| | | * @return |
| | | * @return |
| | | */ |
| | | size_t SCPI_ResultDouble(scpi_t * context, double val) { |
| | | char buffer[32]; |
| | |
| | | * Write string withn " to the result |
| | | * @param context |
| | | * @param data |
| | | * @return |
| | | * @return |
| | | */ |
| | | size_t SCPI_ResultText(scpi_t * context, const char * data) { |
| | | size_t result = 0; |
| | | result += writeDelimiter(context); |
| | | result += writeData(context, "\"", 1); |
| | | // TODO: convert " to "" |
| | | result += writeData(context, data, strlen(data)); |
| | | result += writeData(context, "\"", 1); |
| | | context->output_count++; |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Write boolean value to the result |
| | | * @param context |
| | | * @param val |
| | | * @return |
| | | */ |
| | | size_t SCPI_ResultBool(scpi_t * context, scpi_bool_t val) { |
| | | return SCPI_ResultIntBase(context, val ? 1 : 0, 10); |
| | | } |
| | | |
| | | |
| | | /* parsing parameters */ |
| | | |
| | | /** |
| | | * Skip num bytes from the begginig of parameters |
| | | * Get one parameter from command line |
| | | * @param context |
| | | * @param num |
| | | */ |
| | | void paramSkipBytes(scpi_t * context, size_t num) { |
| | | if (context->paramlist.length < num) { |
| | | num = context->paramlist.length; |
| | | } |
| | | context->paramlist.parameters += num; |
| | | context->paramlist.length -= num; |
| | | } |
| | | |
| | | /** |
| | | * Skip white spaces from the beggining of parameters |
| | | * @param context |
| | | */ |
| | | void paramSkipWhitespace(scpi_t * context) { |
| | | size_t ws = skipWhitespace(context->paramlist.parameters, context->paramlist.length); |
| | | paramSkipBytes(context, ws); |
| | | } |
| | | |
| | | /** |
| | | * Find next parameter |
| | | * @param context |
| | | * @param parameter |
| | | * @param mandatory |
| | | * @return |
| | | */ |
| | | bool_t paramNext(scpi_t * context, bool_t mandatory) { |
| | | paramSkipWhitespace(context); |
| | | if (context->paramlist.length == 0) { |
| | | 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; |
| | | |
| | | state = &context->param_list.lex_state; |
| | | |
| | | if (state->pos >= (state->buffer + state->len)) { |
| | | 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) { |
| | | if (context->paramlist.parameters[0] == ',') { |
| | | paramSkipBytes(context, 1); |
| | | paramSkipWhitespace(context); |
| | | } else { |
| | | scpiLex_Comma(state, &token); |
| | | if (token.type != SCPI_TOKEN_COMMA) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_INVALID_SEPARATOR); |
| | | return FALSE; |
| | | } |
| | | } |
| | | |
| | | context->input_count++; |
| | | return TRUE; |
| | | |
| | | scpiParser_parseProgramData(&context->param_list.lex_state, &token); |
| | | |
| | | parameter->type = token.type; |
| | | parameter->data.ptr = token.ptr; |
| | | parameter->data.len = token.len; |
| | | |
| | | switch (token.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; |
| | | SCPI_ErrorPush(context, SCPI_ERROR_INVALID_STRING_DATA); |
| | | return FALSE; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Parse integer parameter |
| | | * Detect if parameter is number |
| | | * @param parameter |
| | | * @param suffixAllowed |
| | | * @return |
| | | */ |
| | | scpi_bool_t SCPI_ParamIsNumber(scpi_parameter_t * parameter, scpi_bool_t suffixAllowed) { |
| | | switch (parameter->type) { |
| | | case SCPI_TOKEN_HEXNUM: |
| | | case SCPI_TOKEN_OCTNUM: |
| | | case SCPI_TOKEN_BINNUM: |
| | | case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: |
| | | return TRUE; |
| | | case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX: |
| | | return suffixAllowed; |
| | | default: |
| | | return FALSE; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Read floating point parameter |
| | | * @param context |
| | | * @param value |
| | | * @param mandatory |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamInt(scpi_t * context, int32_t * value, bool_t mandatory) { |
| | | char * param; |
| | | size_t param_len; |
| | | size_t num_len; |
| | | |
| | | 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); |
| | | return FALSE; |
| | | } |
| | | |
| | | if (!SCPI_ParamString(context, ¶m, ¶m_len, mandatory)) { |
| | | return FALSE; |
| | | |
| | | result = SCPI_Parameter(context, ¶m, mandatory); |
| | | if (result) { |
| | | if (SCPI_ParamIsNumber(¶m, FALSE)) { |
| | | *value = param.number.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; |
| | | } |
| | | } |
| | | |
| | | num_len = strToLong(param, value); |
| | | |
| | | if (num_len != param_len) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_SUFFIX_NOT_ALLOWED); |
| | | return FALSE; |
| | | } |
| | | |
| | | return TRUE; |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Parse double parameter |
| | | * Read integer parameter |
| | | * @param context |
| | | * @param value |
| | | * @param mandatory |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamDouble(scpi_t * context, double * value, bool_t mandatory) { |
| | | char * param; |
| | | size_t param_len; |
| | | size_t num_len; |
| | | 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 result; |
| | | |
| | | if (!value) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); |
| | | return FALSE; |
| | | } |
| | | |
| | | if (!SCPI_ParamString(context, ¶m, ¶m_len, mandatory)) { |
| | | return FALSE; |
| | | |
| | | result = SCPI_ParamDouble(context, &tmpVal, mandatory); |
| | | if (result) { |
| | | *value = tmpVal; |
| | | } |
| | | |
| | | num_len = strToDouble(param, value); |
| | | |
| | | if (num_len != param_len) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_SUFFIX_NOT_ALLOWED); |
| | | return FALSE; |
| | | } |
| | | |
| | | return TRUE; |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Parse string parameter |
| | | * Read character parameter |
| | | * @param context |
| | | * @param value |
| | | * @param len |
| | | * @param mandatory |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamString(scpi_t * context, char ** value, size_t * len, bool_t mandatory) { |
| | | size_t length; |
| | | |
| | | 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 (!paramNext(context, mandatory)) { |
| | | return FALSE; |
| | | // TODO: return also parameter type (ProgramMnemonic, ArbitraryBlockProgramData, SingleQuoteProgramData, DoubleQuoteProgramData |
| | | } |
| | | |
| | | if (locateStr(context->paramlist.parameters, context->paramlist.length, value, &length)) { |
| | | paramSkipBytes(context, length); |
| | | paramSkipWhitespace(context); |
| | | if (len) { |
| | | *len = length; |
| | | } |
| | | return TRUE; |
| | | } |
| | | |
| | | return FALSE; |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Parse text parameter (can be inside "") |
| | | * Read BOOL parameter (0,1,ON,OFF) |
| | | * @param context |
| | | * @param value |
| | | * @param len |
| | | * @param mandatory |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamText(scpi_t * context, char ** value, size_t * len, bool_t mandatory) { |
| | | size_t length; |
| | | scpi_bool_t SCPI_ParamBool(scpi_t * context, scpi_bool_t * value, scpi_bool_t mandatory) |
| | | { |
| | | scpi_bool_t result; |
| | | scpi_parameter_t param; |
| | | |
| | | if (!value || !len) { |
| | | if (!value) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); |
| | | return FALSE; |
| | | } |
| | | |
| | | if (!paramNext(context, mandatory)) { |
| | | 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 (locateText(context->paramlist.parameters, context->paramlist.length, value, &length)) { |
| | | paramSkipBytes(context, length); |
| | | if (len) { |
| | | *len = length; |
| | | } |
| | | return TRUE; |
| | | } |
| | | |
| | | return FALSE; |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Read value from list of options |
| | | * @param context |
| | | * @param options |
| | | * @param value |
| | | * @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 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; |
| | | } |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Parse one parameter and detect type |
| | | * @param state |
| | | * @param token |
| | | * @return |
| | | */ |
| | | int scpiParser_parseProgramData(lex_state_t * state, scpi_token_t * token) { |
| | | scpi_token_t tmp; |
| | | int result = 0; |
| | | int wsLen; |
| | | int suffixLen; |
| | | int realLen = 0; |
| | | realLen += scpiLex_WhiteSpace(state, &tmp); |
| | | |
| | | if (result == 0) result = scpiLex_NondecimalNumericData(state, token); |
| | | if (result == 0) result = scpiLex_CharacterProgramData(state, token); |
| | | if (result == 0) { |
| | | result = scpiLex_DecimalNumericProgramData(state, token); |
| | | if (result != 0) { |
| | | wsLen = scpiLex_WhiteSpace(state, &tmp); |
| | | suffixLen = scpiLex_SuffixProgramData(state, &tmp); |
| | | if (suffixLen > 0) { |
| | | token->len += wsLen + suffixLen; |
| | | token->type = SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX; |
| | | result = token->len; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (result == 0) result = scpiLex_StringProgramData(state, token); |
| | | if (result == 0) result = scpiLex_ArbitraryBlockProgramData(state, token); |
| | | if (result == 0) result = scpiLex_ProgramExpression(state, token); |
| | | |
| | | realLen += scpiLex_WhiteSpace(state, &tmp); |
| | | |
| | | return result + realLen; |
| | | } |
| | | |
| | | /** |
| | | * Skip all parameters to correctly detect end of command line. |
| | | * @param state |
| | | * @param token |
| | | * @param numberOfParameters |
| | | * @return |
| | | */ |
| | | int scpiParser_parseAllProgramData(lex_state_t * state, scpi_token_t * token, int * numberOfParameters) { |
| | | |
| | | int result; |
| | | scpi_token_t tmp; |
| | | int paramCount = 0; |
| | | |
| | | token->len = -1; |
| | | token->type = SCPI_TOKEN_ALL_PROGRAM_DATA; |
| | | token->ptr = state->pos; |
| | | |
| | | |
| | | for (result = 1; result != 0; result = scpiLex_Comma(state, &tmp)) { |
| | | token->len += result; |
| | | |
| | | if (result == 0) { |
| | | token->type = SCPI_TOKEN_UNKNOWN; |
| | | token->len = 0; |
| | | paramCount = -1; |
| | | break; |
| | | } |
| | | |
| | | result = scpiParser_parseProgramData(state, &tmp); |
| | | if (tmp.type != SCPI_TOKEN_UNKNOWN) { |
| | | token->len += result; |
| | | } else { |
| | | token->type = SCPI_TOKEN_UNKNOWN; |
| | | token->len = 0; |
| | | paramCount = -1; |
| | | break; |
| | | } |
| | | paramCount++; |
| | | } |
| | | |
| | | if (token->len == -1) { |
| | | token->len = 0; |
| | | } |
| | | |
| | | if (numberOfParameters != NULL) { |
| | | *numberOfParameters = paramCount; |
| | | } |
| | | 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 |
| | | * @param buffer |
| | | * @param len |
| | | * @return |
| | | */ |
| | | int scpiParser_detectProgramMessageUnit(scpi_parser_state_t * state, const char * buffer, int len) { |
| | | lex_state_t lex_state; |
| | | scpi_token_t tmp; |
| | | int result = 0; |
| | | |
| | | lex_state.buffer = lex_state.pos = buffer; |
| | | lex_state.len = len; |
| | | state->numberOfParameters = 0; |
| | | |
| | | /* ignore whitespace at the begginig */ |
| | | scpiLex_WhiteSpace(&lex_state, &tmp); |
| | | |
| | | if (scpiLex_ProgramHeader(&lex_state, &state->programHeader) >= 0) { |
| | | if (scpiLex_WhiteSpace(&lex_state, &tmp) > 0) { |
| | | scpiParser_parseAllProgramData(&lex_state, &state->programData, &state->numberOfParameters); |
| | | } else { |
| | | invalidateToken(&state->programData, lex_state.pos); |
| | | } |
| | | } else { |
| | | invalidateToken(&state->programHeader, lex_state.buffer); |
| | | invalidateToken(&state->programData, lex_state.buffer); |
| | | } |
| | | |
| | | if (result == 0) result = scpiLex_NewLine(&lex_state, &tmp); |
| | | if (result == 0) result = scpiLex_Semicolon(&lex_state, &tmp); |
| | | |
| | | if (!scpiLex_IsEos(&lex_state) && (result == 0)) { |
| | | lex_state.pos++; |
| | | |
| | | state->programHeader.len = 1; |
| | | state->programHeader.type = SCPI_TOKEN_INVALID; |
| | | |
| | | invalidateToken(&state->programData, lex_state.buffer); |
| | | } |
| | | |
| | | if (SCPI_TOKEN_SEMICOLON == tmp.type) { |
| | | state->termination = SCPI_MESSAGE_TERMINATION_SEMICOLON; |
| | | } else if (SCPI_TOKEN_NL == tmp.type) { |
| | | state->termination = SCPI_MESSAGE_TERMINATION_NL; |
| | | } else { |
| | | state->termination = SCPI_MESSAGE_TERMINATION_NONE; |
| | | } |
| | | |
| | | return lex_state.pos - lex_state.buffer; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |