Jan Breuer
2015-01-18 d6f4a5106bebb23826c0867494ade7f4d7ebd1f3
libscpi/src/parser.c
@@ -39,31 +39,10 @@
#include "scpi/config.h"
#include "scpi/parser.h"
#include "scpi/lexer.h"
#include "utils.h"
#include "parser_private.h"
#include "lexer_private.h"
#include "scpi/error.h"
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, int 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;
}
 */
#include "scpi/constants.h"
/**
 * Write data to SCPI output
@@ -96,7 +75,7 @@
 */
static size_t writeDelimiter(scpi_t * context) {
    if (context->output_count > 0) {
        return writeData(context, ", ", 2);
        return writeData(context, ",", 2);
    } else {
        return 0;
    }
@@ -123,7 +102,8 @@
 * @param context
 */
static void processCommand(scpi_t * context) {
    const scpi_command_t * cmd = context->paramlist.cmd;
    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;
@@ -140,11 +120,8 @@
    /* conditionaly write new line */
    writeNewLine(context);
    /* skip all whitespaces */
    paramSkipWhitespace(context);
    /* set error if command callback did not read all parameters */
    if (context->paramlist.length != 0 && !context->cmd_error) {
    if (state->pos < (state->buffer + state->len) && !context->cmd_error) {
        SCPI_ErrorPush(context, SCPI_ERROR_PARAMETER_NOT_ALLOWED);
    }
}
@@ -154,14 +131,14 @@
 * @param context
 * @result TRUE if context->paramlist is filled with correct values
 */
static bool_t findCommandHeader(scpi_t * context, const char * header, int len) {
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->paramlist.cmd = cmd;
            context->param_list.cmd = cmd;
            return TRUE;
        }
    }
@@ -189,13 +166,19 @@
    while (1) {
        result = 0;
        r = SCPI_DetectProgramMessageUnit(state, data, len);
        r = scpiParser_detectProgramMessageUnit(state, data, len);
        if (state->programHeader.len > 0) {
        if (state->programHeader.type == TokInvalid) {
            SCPI_ErrorPush(context, SCPI_ERROR_INVALID_CHARACTER);
        } else if (state->programHeader.len > 0) {
            if (findCommandHeader(context, state->programHeader.ptr, state->programHeader.len)) {
                context->paramlist.parameters = state->programData.ptr;
                context->paramlist.length = state->programData.len;
                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;
                processCommand(context);
@@ -224,6 +207,19 @@
 * @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);
}
@@ -260,7 +256,7 @@
        while (1) {
            cmdlen = SCPI_DetectProgramMessageUnit(&context->parser_state, context->buffer.data + totcmdlen, context->buffer.position - totcmdlen);
            cmdlen = scpiParser_detectProgramMessageUnit(&context->parser_state, context->buffer.data + totcmdlen, context->buffer.position - totcmdlen);
            totcmdlen += cmdlen;
            if (context->parser_state.termination == PmutNewLine) break;
            if (context->parser_state.programHeader.type == TokUnknown) break;
@@ -285,8 +281,7 @@
 * @param data
 * @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);
@@ -301,10 +296,43 @@
 * @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;
@@ -337,199 +365,328 @@
    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;
}
/* parsing parameters */
/**
 * Skip num bytes from the begginig of parameters
 * Write boolean value to the result
 * @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 mandatory
 * @param val
 * @return
 */
bool_t paramNext(scpi_t * context, bool_t mandatory) {
    paramSkipWhitespace(context);
    if (context->paramlist.length == 0) {
size_t SCPI_ResultBool(scpi_t * context, scpi_bool_t val) {
    return SCPI_ResultIntBase(context, val ? 1 : 0, 10);
}
/* parsing parameters */
/**
 * Get one parameter from command line
 * @param context
 * @param parameter
 * @param mandatory
 * @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 = TokUnknown;
    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 = TokProgramMnemonic; // 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 != TokComma) {
            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 TokHexnum:
            parameter->number.base = 16;
            strToLong(token.ptr, &value, 16);
            parameter->number.value = value;
            return TRUE;
        case TokOctnum:
            parameter->number.base = 8;
            strToLong(token.ptr, &value, 8);
            parameter->number.value = value;
            return TRUE;
        case TokBinnum:
            parameter->number.base = 2;
            strToLong(token.ptr, &value, 2);
            parameter->number.value = value;
            return TRUE;
        case TokProgramMnemonic:
            return TRUE;
        case TokDecimalNumericProgramData:
            strToDouble(token.ptr, &parameter->number.value);
            return TRUE;
        case TokDecimalNumericProgramDataWithSuffix:
            strToDouble(token.ptr, &parameter->number.value);
            return TRUE;
        case TokArbitraryBlockProgramData:
            return TRUE;
        case TokSingleQuoteProgramData:
            // TODO: replace double "single qoute"
            return TRUE;
        case TokDoubleQuoteProgramData:
            // TODO: replace double "double qoute"
            return TRUE;
        case TokProgramExpression:
            return TRUE;
        default:
            parameter->type = TokUnknown;
            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 TokHexnum:
        case TokOctnum:
        case TokBinnum:
        case TokDecimalNumericProgramData:
            return TRUE;
        case TokDecimalNumericProgramDataWithSuffix:
            return suffixAllowed;
        default:
            return FALSE;
    }
}
/**
 * Read floating point parameter
 * @param context
 * @param value
 * @param mandatory
 * @return
 * @return
 */
bool_t SCPI_ParamInt(scpi_t * context, int32_t * value, bool_t mandatory) {
    const 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, &param, &param_len, mandatory)) {
        return FALSE;
    result = SCPI_Parameter(context, &param, mandatory);
    if (result) {
        if (SCPI_ParamIsNumber(&param, FALSE)) {
           *value = param.number.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;
        }
    }
    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
 * @return
 */
bool_t SCPI_ParamDouble(scpi_t * context, double * value, bool_t mandatory) {
    const 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, &param, &param_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
 * @return
 */
bool_t SCPI_ParamString(scpi_t * context, const 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, &param, 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
 * @return
 */
bool_t SCPI_ParamText(scpi_t * context, const 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, &param, mandatory);
    if (result) {
        switch (param.type) {
            case TokDecimalNumericProgramData:
                *value = param.number.value ? 1 : 0;
                break;
            case TokProgramMnemonic:
                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;
}
int SCPI_ParseProgramData(lex_state_t * state, token_t * token) {
    token_t tmp;
/**
 * 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, &param, mandatory);
    if (result) {
        if (param.type == TokProgramMnemonic) {
            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 += SCPI_LexWhiteSpace(state, &tmp);
    realLen += scpiLex_WhiteSpace(state, &tmp);
    if (result == 0) result = SCPI_LexNondecimalNumericData(state, token);
    if (result == 0) result = SCPI_LexCharacterProgramData(state, token);
    if (result == 0) result = scpiLex_NondecimalNumericData(state, token);
    if (result == 0) result = scpiLex_CharacterProgramData(state, token);
    if (result == 0) {
        result = SCPI_LexDecimalNumericProgramData(state, token);
        result = scpiLex_DecimalNumericProgramData(state, token);
        if (result != 0) {
            wsLen = SCPI_LexWhiteSpace(state, &tmp);
            suffixLen = SCPI_LexSuffixProgramData(state, &tmp);
            wsLen = scpiLex_WhiteSpace(state, &tmp);
            suffixLen = scpiLex_SuffixProgramData(state, &tmp);
            if (suffixLen > 0) {
                token->len += wsLen + suffixLen;
                token->type = TokDecimalNumericProgramDataWithSuffix;
@@ -538,19 +695,26 @@
        }
    }
    if (result == 0) result = SCPI_LexStringProgramData(state, token);
    if (result == 0) result = SCPI_LexArbitraryBlockProgramData(state, token);
    if (result == 0) result = SCPI_LexProgramExpression(state, token);
    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 += SCPI_LexWhiteSpace(state, &tmp);
    realLen += scpiLex_WhiteSpace(state, &tmp);
    return result + realLen;
}
int SCPI_ParseAllProgramData(lex_state_t * state, token_t * token, int * numberOfParameters) {
/**
 * 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;
    token_t tmp;
    scpi_token_t tmp;
    int paramCount = 0;
    token->len = -1;
@@ -558,7 +722,7 @@
    token->ptr = state->pos;
    for (result = 1; result != 0; result = SCPI_LexComma(state, &tmp)) {
    for (result = 1; result != 0; result = scpiLex_Comma(state, &tmp)) {
        token->len += result;
        if (result == 0) {
@@ -568,7 +732,7 @@
            break;
        }
        result = SCPI_ParseProgramData(state, &tmp);
        result = scpiParser_parseProgramData(state, &tmp);
        if (tmp.type != TokUnknown) {
            token->len += result;
        } else {
@@ -590,39 +754,53 @@
    return token->len;
}
int SCPI_DetectProgramMessageUnit(scpi_parser_state_t * state, const char * buffer, int len) {
static void invalidateToken(scpi_token_t * token, const char * ptr) {
    token->len = 0;
    token->ptr = ptr;
    token->type = TokUnknown;
}
/**
 * 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;
    token_t tmp;
    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 */
    SCPI_LexWhiteSpace(&lex_state, &tmp);
    scpiLex_WhiteSpace(&lex_state, &tmp);
    if (SCPI_LexProgramHeader(&lex_state, &state->programHeader) >= 0) {
        if (SCPI_LexWhiteSpace(&lex_state, &tmp) > 0) {
            SCPI_ParseAllProgramData(&lex_state, &state->programData, &state->numberOfParameters);
    if (scpiLex_ProgramHeader(&lex_state, &state->programHeader) >= 0) {
        if (scpiLex_WhiteSpace(&lex_state, &tmp) > 0) {
            scpiParser_parseAllProgramData(&lex_state, &state->programData, &state->numberOfParameters);
        } else {
            state->programData.len = 0;
            state->programData.ptr = lex_state.pos;
            state->programData.type = TokUnknown;
            state->numberOfParameters = 0;
            invalidateToken(&state->programData, lex_state.pos);
        }
    } else {
        state->programHeader.len = 0;
        state->programData.ptr = lex_state.buffer;
        state->programHeader.type = TokUnknown;
        state->programData.len = 0;
        state->programData.ptr = lex_state.buffer;
        state->programData.type = TokUnknown;
        state->numberOfParameters = 0;
        invalidateToken(&state->programHeader, lex_state.buffer);
        invalidateToken(&state->programData, lex_state.buffer);
    }
    if (result == 0) result = SCPI_LexNewLine(&lex_state, &tmp);
    if (result == 0) result = SCPI_LexSemicolon(&lex_state, &tmp);
    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 = TokInvalid;
        invalidateToken(&state->programData, lex_state.buffer);
    }
    if (TokSemicolon == tmp.type) {
        state->termination = PmutSemicolon;