/*- * Copyright (c) 2012-2015 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: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 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 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file expression.c * * @brief Expressions handling * * */ #include "scpi/expression.h" #include "scpi/error.h" #include "scpi/parser.h" #include "lexer_private.h" /** * Parse one range or single value * @param state lexer state * @param isRange return true if parsed expression is range * @param valueFrom return parsed value from * @param valueTo return parsed value to * @return SCPI_EXPR_OK - parsing was succesful * SCPI_EXPR_ERROR - parser error * SCPI_EXPR_NO_MORE - no more data */ static scpi_expr_result_t numericRange(lex_state_t * state, scpi_bool_t * isRange, scpi_token_t * valueFrom, scpi_token_t * valueTo) { if (scpiLex_DecimalNumericProgramData(state, valueFrom)) { if (scpiLex_Colon(state, valueTo)) { *isRange = TRUE; if (scpiLex_DecimalNumericProgramData(state, valueTo)) { return SCPI_EXPR_OK; } else { return SCPI_EXPR_ERROR; } } else { *isRange = FALSE; return SCPI_EXPR_OK; } } return SCPI_EXPR_NO_MORE; } /** * Parse entry on specified position * @param context scpi context * @param param input parameter * @param index index of position (start from 0) * @param isRange return true if expression at index was range * @param valueFrom return value from * @param valueTo return value to * @return SCPI_EXPR_OK - parsing was succesful * SCPI_EXPR_ERROR - parser error * SCPI_EXPR_NO_MORE - no more data * @see SCPI_ExprNumericListEntryInt, SCPI_ExprNumericListEntryDouble */ scpi_expr_result_t SCPI_ExprNumericListEntry(scpi_t * context, scpi_parameter_t * param, int index, scpi_bool_t * isRange, scpi_parameter_t * valueFrom, scpi_parameter_t * valueTo) { lex_state_t lex; int i; scpi_expr_result_t res = SCPI_EXPR_OK; if (!isRange || !valueFrom || !valueTo || !param) { SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); return SCPI_EXPR_ERROR; } if (param->type != SCPI_TOKEN_PROGRAM_EXPRESSION) { SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); return SCPI_EXPR_ERROR; } lex.buffer = param->ptr + 1; lex.pos = lex.buffer; lex.len = param->len - 2; for (i = 0; i <= index; i++) { res = numericRange(&lex, isRange, valueFrom, valueTo); if (res != SCPI_EXPR_OK) { break; } if (i != index) { if (!scpiLex_Comma(&lex, valueFrom)) { res = scpiLex_IsEos(&lex) ? SCPI_EXPR_NO_MORE : SCPI_EXPR_ERROR; break; } } } if (res == SCPI_EXPR_ERROR) { SCPI_ErrorPush(context, SCPI_ERROR_EXPRESSION_PARSING_ERROR); } return res; } /** * Parse entry on specified position and convert result to int32_t * @param context scpi context * @param param input parameter * @param index index of position (start from 0) * @param isRange return true if expression at index was range * @param valueFrom return value from * @param valueTo return value to * @return SCPI_EXPR_OK - parsing was succesful * SCPI_EXPR_ERROR - parser error * SCPI_EXPR_NO_MORE - no more data * @see SCPI_ExprNumericListEntry, SCPI_ExprNumericListEntryDouble */ scpi_expr_result_t SCPI_ExprNumericListEntryInt(scpi_t * context, scpi_parameter_t * param, int index, scpi_bool_t * isRange, int32_t * valueFrom, int32_t * valueTo) { scpi_expr_result_t res; scpi_bool_t range = FALSE; scpi_parameter_t paramFrom; scpi_parameter_t paramTo; res = SCPI_ExprNumericListEntry(context, param, index, &range, ¶mFrom, ¶mTo); if (res == SCPI_EXPR_OK) { *isRange = range; SCPI_ParamToInt(context, ¶mFrom, valueFrom); if (range) { SCPI_ParamToInt(context, ¶mTo, valueTo); } } return res; } /** * Parse entry on specified position and convert result to double * @param context scpi context * @param param input parameter * @param index index of position (start from 0) * @param isRange return true if expression at index was range * @param valueFrom return value from * @param valueTo return value to * @return SCPI_EXPR_OK - parsing was succesful * SCPI_EXPR_ERROR - parser error * SCPI_EXPR_NO_MORE - no more data * @see SCPI_ExprNumericListEntry, SCPI_ExprNumericListEntryInt */ scpi_expr_result_t SCPI_ExprNumericListEntryDouble(scpi_t * context, scpi_parameter_t * param, int index, scpi_bool_t * isRange, double * valueFrom, double * valueTo) { scpi_expr_result_t res; scpi_bool_t range = FALSE; scpi_parameter_t paramFrom; scpi_parameter_t paramTo; res = SCPI_ExprNumericListEntry(context, param, index, &range, ¶mFrom, ¶mTo); if (res == SCPI_EXPR_OK) { *isRange = range; SCPI_ParamToDouble(context, ¶mFrom, valueFrom); if (range) { SCPI_ParamToDouble(context, ¶mTo, valueTo); } } return res; } /** * Parse one channel_spec e.g. "1!5!8" * @param context * @param state lexer state * @param values range values * @param length length of values array * @param dimensions real number of dimensions */ static scpi_expr_result_t channelSpec(scpi_t * context, lex_state_t * state, int32_t * values, size_t length, size_t * dimensions) { scpi_parameter_t param; size_t i = 0; while(scpiLex_DecimalNumericProgramData(state, ¶m)) { if (i < length) { SCPI_ParamToInt(context, ¶m, &values[i]); } if (scpiLex_SpecificCharacter(state, ¶m, '!')) { i++; } else { *dimensions = i + 1; return SCPI_EXPR_OK; } } if (i == 0) { return SCPI_EXPR_NO_MORE; } else { // there was at least one number followed by !, but after ! was not another number return SCPI_EXPR_ERROR; } } /** * Parse channel_range e.g. "1!2:5!6" * @param context * @param state lexer state * @param isRange return true if it is range * @param valuesFrom return array of values from * @param valuesTo return array of values to * @param length length of values arrays * @param dimensions real number of dimensions */ static scpi_expr_result_t channelRange(scpi_t * context, lex_state_t * state, scpi_bool_t * isRange, int32_t * valuesFrom, int32_t * valuesTo, size_t length, size_t * dimensions) { scpi_token_t token; scpi_expr_result_t err; size_t fromDimensions; size_t toDimensions; err = channelSpec(context, state, valuesFrom, length, &fromDimensions); if (err == SCPI_EXPR_OK) { if (scpiLex_Colon(state, &token)) { *isRange = TRUE; err = channelSpec(context, state, valuesTo, length, &toDimensions); if (err != SCPI_EXPR_OK) { return SCPI_EXPR_ERROR; } if (fromDimensions != toDimensions) { return SCPI_EXPR_ERROR; } *dimensions = fromDimensions; } else { *isRange = FALSE; return SCPI_EXPR_OK; } } return err; } /** * Parse one list entry at specific position e.g. "1!2:5!6" * @param context * @param param * @param index * @param isRange return true if it is range * @param valuesFrom return array of values from * @param valuesTo return array of values to * @param length length of values arrays * @param dimensions real number of dimensions */ scpi_expr_result_t SCPI_ExprChannelListEntry(scpi_t * context, scpi_parameter_t * param, int index, scpi_bool_t * isRange, int32_t * valuesFrom, int32_t * valuesTo, size_t length, size_t * dimensions) { lex_state_t lex; int i; scpi_expr_result_t res = SCPI_EXPR_OK; scpi_token_t token; if (!isRange || !param || !dimensions || (length && (!valuesFrom || !valuesTo))) { SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); return SCPI_EXPR_ERROR; } if (param->type != SCPI_TOKEN_PROGRAM_EXPRESSION) { SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); return SCPI_EXPR_ERROR; } lex.buffer = param->ptr + 1; lex.pos = lex.buffer; lex.len = param->len - 2; // detect channel list expression if (!scpiLex_SpecificCharacter(&lex, &token, '@')) { SCPI_ErrorPush(context, SCPI_ERROR_EXPRESSION_PARSING_ERROR); return SCPI_EXPR_ERROR; } for (i = 0; i <= index; i++) { res = channelRange(context, &lex, isRange, valuesFrom, valuesTo, (i == index) ? length : 0, dimensions); if (res != SCPI_EXPR_OK) { break; } if (i != index) { if (!scpiLex_Comma(&lex, &token)) { res = scpiLex_IsEos(&lex) ? SCPI_EXPR_NO_MORE : SCPI_EXPR_ERROR; break; } } } if (res == SCPI_EXPR_ERROR) { SCPI_ErrorPush(context, SCPI_ERROR_EXPRESSION_PARSING_ERROR); } return res; }