#ifndef SCPI_PARSER_H
#define SCPI_PARSER_H
    #include <stdint.h>
    #include <stddef.h>
    #include "my_assert.h"
    #include <stdbool.h>
    #include <string.h>

    #include "app/scpi/scpi_base.h" // eScpiParserStatus_t
    #include "app/scpi/scpi_errs.h"

    // SCPI Parser functions
    // Author: Sychov A., 01/aug/2019
    
    //----------------------------------------------------------------
    // Refer to:
    // [1] IEEE 488.2 Standard, revision IEEE Std 488.2-1987 (1992)
    //     "IEEE Standard Codes, Formats, Protocols, and Common Commands for Use With IEEE Std 488.1-1987, IEEE
    //      Standard Digital Interface for Programmable Instrumentation"
    // [2] SCPI Specification, revision 1999.0
    //     "Standard Commands for Programmable Instruments (SCPI), VERSION 1999.0, May 1999"
    //----------------------------------------------------------------

    #define SCPI_CHARACTER_COMMON_HEADER              '*'
    #define SCPI_CHARACTER_MESSAGE_SEPARATOR          ';'
    #define SCPI_CHARACTER_RESPONSE_SEPARATOR         ';'
    #define SCPI_CHARACTER_COMPOUNDCOMMAND_SEPARATOR  ':'
    #define SCPI_CHARACTER_WHITE                      ' '
    #define SCPI_CHARACTER_NL                         '\n'
    #define SCPI_CHARACTER_DATA_SEPARATOR             ','
    #define SCPI_CHARACTER_QUERY                      '?'
    #define SCPI_CHARACTER_DEFNODE_OPEN               '['
    #define SCPI_CHARACTER_DEFNODE_CLOSE              ']'
    #define SCPI_CHARACTER_BRK_OPEN                   '('
    #define SCPI_CHARACTER_BRK_CLOSE                  ')'
    // ==============================================================================================
    // @xParseEntry_t
    // The parser-function context.
    // User shall prepare the context by calling
    // ... @prepareParserContext function before
    // ... calling any parser-function.
    typedef struct
    {       
       const void * private[15];  // private fields, shall not be accessed by user    
    } 
    xParseEntry_t;
    // ==============================================================================================
    // @eScpiParserStatus_t
    // Parser-functions returns statuses:
    typedef enum
    {
        eScpiParserStatus__min = -2147483648,   // don't care, just makes the enum to take 4 bytes
        // ------------------------------------------------------------------------------------------
        // Error statuses:
        eScpiParserStatus_invalid = eScpiStatus_invalid,         
                                                // Invalid argument value: the context have not changed.
                                                // User shall check the input parameters and repeat.
                                                // User shall not prepare the context before repeated call.

        eScpiParserStatus_failed = eScpiStatus_failed,
                                                // Can not parse element, but the context have changed.
                                                // The element can not be parsed due to either wrong 
                                                // ... format or some error during processing.
                                                // User shall not interpretate any results stored in the
                                                // ... context. User shall prepare the context before
                                                // ... next call of any parser-function with specifying
                                                // ... @bKeepContext value as 'false'.
        // Success status:
        eScpiParserStatus_success = eScpiStatus_success,
                                                // The element successfully parsed and the context have changed.
                                                // User can get detailed information from the context.
                                                // User shall prepare the context before next call of any
                                                // ... parser-function with specifying@bKeepContext value 
                                                // ... as 'false'.
        // Warning status:
        eScpiParserStatus_need_data = eScpiStatus_need_data,
                                                // Need more data to continue, the context have changed.
                                                // The function can not process whole element in the buffer
                                                // ... due to not enough data stored in the buffer. User
                                                // ... shall continue processing as soon as get more data.
                                                // Note: even to continue user must prepare context and
                                                // ... specify @bKeepContext as 'true' value.
        // ------------------------------------------------------------------------------------------
        eScpiParserStatus__max = 2147483647     // don't care, just makes the enum to take 4 bytes
    }
    eScpiParserStatus_t;
    // ==============================================================================================
    #pragma pack( push, 1 )
    // @eScpiEntityType_t
    // SCPI PROGRAM MESSAGE element types, refer to "7.3.3 Functional Element Summary", [1]
    typedef enum
    {
        eScpiEntityType__min = 0,
        // ------------------------------
        eScpiEntityTypeInvalid = 0,       // entity type is undefined (default value)
        eScpiEntityTypeString,            // IEEE 488.2 String, "<STRING PROGRAM DATA> 7.7.5", [1]
        eScpiEntityTypeArbitraryBlock,    // IEEE 488.2 Arbitrary Block, "7.7.6 <ARBITRARY BLOCK PROGRAM DATA>", [1]
        eScpiEntityTypeProgramMnemonic,   // IEEE 488.2 Program Mnemonic, "7.6.1.2 Encoding Syntax", [1]
        eScpiEntityTypeCharacter,         // IEEE 488.2 Character, "7.7.1 <CHARACTER PROGRAM DATA>", [1]
        eScpiEntityTypeCmnCommandProgHdr, // IEEE 488.2 Common Command Program Header, "7.6.1 <COMMAND PROGPRAM HEADER>", [1]
        eScpiEntityTypeCmnQueryProgHdr,   // IEEE 488.2 Common Query Program Header, "7.6.2 <QUERY PROGPRAM HEADER>", [1]
        eScpiEntityTypeCmpCommandProgHdr, // IEEE 488.2 Compound/Simple Command Program Header, "7.6.1 <COMMAND PROGPRAM HEADER>", [1]
        eScpiEntityTypeCmpQueryProgHdr,   // IEEE 488.2 Compound/Simple Query Program Header, "7.6.2 <QUERY PROGPRAM HEADER>", [1]
        eScpiEntityTypeDemicalFloatNumber,// IEEE 488.2 Demical Number (float, only mantissa), "7.7.2 <DECIMAL NUMERIC PROGRAM DATA>", [1]
        eScpiEntityTypeDemicalExpNumber,  // IEEE 488.2 Demical Number (scientific, mantissa+exponent), "7.7.2 <DECIMAL NUMERIC PROGRAM DATA>", [1] 
        eScpiEntityTypeHexNumber,         // IEEE 488.2 Hexademical Number, "7.7.4 <NONDECIMAL NUMERIC PROGRAM DATA>", [1]
        eScpiEntityTypeOctNumber,         // IEEE 488.2 Octal Number, "7.7.4 <NONDECIMAL NUMERIC PROGRAM DATA>", [1]
        eScpiEntityTypeBinNumber,         // IEEE 488.2 Binary Number, "7.7.4 <NONDECIMAL NUMERIC PROGRAM DATA>", [1]

        eScpiEntityTypeDemicalIntegerNumber, // Integer number only, extension for IEEE 488.2 Demical Number "7.7.2 <DECIMAL NUMERIC PROGRAM DATA>", [1]
        // ------------------------------
        eScpiEntityType_Service_Invalid = 128,
        // ------------------------------
        eScpiEntityType_Service_MsgTerm,  // IEEE 488.2 Message terminator, "7.5 <PROGRAM MESSAGE TERMINATOR>, [1]"
        eScpiEntityType_Service_MsgSep,   // IEEE 488.2 Message separator, "7.4.1 <PROGRAM MESSAGE UNIT SEPARATOR>, [1]"
        eScpiEntityType_Service_HdrSep,   // IEEE 488.2 Header separator, "7.4.3 <PROGRAM HEADER SEPARATOR>, [1]"
        eScpiEntityType_Service_DataSep,  // IEEE 488.2 Data separator, "7.4.2 <PROGRAM DATA SEPARATOR>, [1]"
        eScpiEntityType_Service_White,    // IEEE 488.2 Data separator, "7.4.1.2 <WHITE CHARACTER>, [1]"
        // ------------------------------
        eScpiEntityType__max = 255,
    }
    eScpiEntityType_t;
    #pragma pack( pop )
    // ==============================================================================================
    #pragma pack( push, 1 )
    // @eScpiArgType_t
    // SCPI command argument type for @parseArgument routine
    typedef enum
    {
       eScpiArg_No_Args,             // Invalid value
       eScpiArg_Character,           // - parseCharacter
       eScpiArg_String,              // - parseString
       eScpiArg_Datablock,           // - parseArbitraryBlock
       eScpiArg_Numeric,             // - parseDemicalNumber + parseNonDemicalNumber (not supported)
       eScpiArg_Numeric_Demical,     // - parseDemicalNumber
       eScpiArg_Numeric_NonDemical,  // - parseNonDemicalNumber
       eScpiArg_Array_Decimal,       // - parseDecimalArray
       // ------------------------------
       eScpiArg__max = 255,
    }
    eScpiArgType_t;
    #pragma pack( pop )
    // ==============================================================================================
    // @prepareParserContext
    // Preapres a parser context @xObj before a parser-function call.
    // The function must be called before each call of any parser-function.
    // If it is required to call the parser function multiple times, user
    // ... shall analyze the last return status and specify the @bKeepContext
    // ... parameter to the corresponding value. See also @eScpiParserStatus_t.
    // Parameters:
    // @xObj - the parse context to prepare;
    // @pBuffer - the memory buffer containing the SCPI element to be parsed;
    // @length - the memory buffer length;
    // @bEndMessage - specifies if the specified buffer holds a complete
    // ... message or not. If the @pBuffer contains a portion of received
    // ... data and there no information about the whole message length,
    // ... the @bEndMessage value must be 'false'. Otherwise, if known that
    // ... the buffer contains the final portion of data (even inclidung the 
    // ... terminating characters), the value must be 'true'.
    // @bKeepContext - specifies if it is required to cleanup the context 
    // ... to restart the parsing ('false'), or it is required to keep the 
    // ... context to continue ('true').
    // Returns: 'true' in success case,
    //          'false' otherwise.
    // Note in case the function returns 'false' user must not use the 
    // ... @xObj context due to it is still not prepared.
    bool prepareParserContext( xParseEntry_t * xObj, const void * pBuffer, size_t length,
                               bool bEndMessage, bool bKeepContext );

    // ==============================================================================================
    // @getParsedEntityDetails
    // Retrieves the parsing results from the parser context.
    // The function shall not be called until either the
    // ... @eScpiParserStatus_success status or @eScpiParserStatus_need_data
    // ... are returned by last parser-function call.
    // Note: take in account, that in case @eScpiParserStatus_need_data had 
    // ... been returned during the last parser-function call, the @eEntityType
    // ... will be set to @eScpiEntityTypeInvalid due to the entity is still
    // ... not identified (need more data).
    // Parameters:
    // @xObj - the parser context to get results from;
    // @pxEntityBegin - pointer to the pointer-variable to store the
    // ... parsed entity beginning, can be NULL;
    // @pxEntityEnd - pointer to the pointer-variable to store the
    // ... parsed entity ending, can be NULL;
    // @peEntityType - pointer to the uint8-variable to store the
    // ... parsed entity type, the type is encoded as described
    // ... in @eScpiEntityType_t enum, can be NULL;
    // Note: in case the parameters described above are NULL an appropriate
    // ... information will not be returned and can be retrieved by
    // ... another call of this function with non-NULL argument until
    // ... the @prepareParserContext is called for the specified context.
    // Returns: 'true' in success case,
    //          'false' otherwise.
    bool getParsedEntityDetails( const xParseEntry_t * xObj,
                                   const void * * pxEntityBegin,
                                   const void * * pxEntityEnd,
                                   uint8_t * peEntityType );
    // ==============================================================================================
    // @getParserError
    // Retrieves the last parser error code and message
    // The function shall not be called until either the @eScpiParserStatus_failed status 
    // is returned by last parser-function call.
    // @xObj - the parser context to get results from;
    // @pErrorCode - optional pointer to the variable to store the error code, can be NULL;
    // Returns:
    //  The parser error message string. Use @getParsedEntityDetails to retrieve more details.
    //  Possible values:
    //  - NULL: no error occurred in specified context or invalid context;
    //  - non-null: valid constant null-terminated string message, no memory free is required;
    const char * getParserError( const xParseEntry_t * xObj,
                                 int32_t * pErrorCode );
    // =================================================================================================================
    // @setParserError
    // Assigne the last parser error code and message
    // The function shall not be called after calling any parser function that return error
    // status code to prevent overwriting actual error code and description. 
    // @xObj - the parser context to get results from;
    // @errorCode - SCPI Error code, [2]
    // @pErrorDesc - Error description, constant null-terminated string;
    // Returns:
    //  true - successful operation,
    //  false - failed
    bool setParserError( const xParseEntry_t * xObj,
                         int32_t errorCode,
                         const char * pErrorDesc  );
    // ==============================================================================================
    // @parseArbitraryBlock
    // "Parser-function", parses an arbitrary block (ARBITRARY BLOCK PROGRAM DATA) 
    // References:
    //     "7.7.6 <ARBITRARY BLOCK PROGRAM DATA>", [1]        
    //     "7.7.6.2 Encoding Syntax", [1]
    // Parameters:
    //     @xObj - parser context, must be prepared by @prepareParserContext
    // ... before each call. If it is needed to continue the processing, the
    // ... @bKeepContext parameter duing the call @prepareParserContext must 
    // ... be set to 'true', and otherwise, to restart parsing the parameter
    // ... must be set to 'false'.
    // Returns:
    //   eScpiParserStatus_failed - error, can not parse data as a string;
    //   eScpiParserStatus_success - success, the string has been successfully parsed;
    //   eScpiParserStatus_need_data - warning, the string can not be parsed due to
    //                             there no enough data to process.
    eScpiParserStatus_t parseArbitraryBlock( xParseEntry_t * xObj );
    // ==============================================================================================
    // @parseString
    // "Parser-function", parses a SCPI string (STRING PROGRAM DATA)
    // References:
    //     "<STRING PROGRAM DATA> 7.7.5", [1]
    //     "7.7.5.2 Encoding Syntax", [1]
    // Parameters:
    //     @xObj - parser context, must be prepared by @prepareParserContext
    // ... before each call. If it is needed to continue the processing, the
    // ... @bKeepContext parameter duing the call @prepareParserContext must 
    // ... be set to 'true', and otherwise, to restart parsing the parameter
    // ... must be set to 'false'.
    // Returns:
    //   eScpiParserStatus_failed - error, can not parse data as a string;
    //   eScpiParserStatus_success - success, the string has been successfully parsed;
    //   eScpiParserStatus_need_data - warning, the string can not be parsed due to
    //                             there no enough data to process.
    eScpiParserStatus_t parseString( xParseEntry_t * xObj );


    // =================================================================================================================
    // @parseCharacter
    // "Parser-function", parses a SCPI character (CHARACTER PROGRAM DATA)
    // References:
    //     "7.7.1 <CHARACTER PROGRAM DATA>", [1]
    //     "7.7.1.2 Encoding Syntax", [1]
    // Parameters:
    //     @xObj - parser context, must be prepared by @prepareParserContext
    // ... before each call. If it is needed to continue the processing, the
    // ... @bKeepContext parameter duing the call @prepareParserContext must 
    // ... be set to 'true', and otherwise, to restart parsing the parameter
    // ... must be set to 'false'.
    // Returns:
    //   eScpiParserStatus_failed - error, can not parse data as a string;
    //   eScpiParserStatus_success - success, the string has been successfully parsed;
    //   eScpiParserStatus_need_data - warning, the string can not be parsed due to
    //                             there no enough data to process.
    eScpiParserStatus_t parseCharacter( xParseEntry_t * xObj );
    // ==============================================================================================
    // @parseProgramMenmonic
    // "Parser-function", parses a SCPI Program Mnemonic (<program mnemonic>)
    // References: 
    //     "<program mnemonic>", "7.6.1.2 Encoding Syntax", [1]
    // Parameters:
    //     @xObj - parser context, must be prepared by @prepareParserContext
    // ... before each call. If it is needed to continue the processing, the
    // ... @bKeepContext parameter duing the call @prepareParserContext must 
    // ... be set to 'true', and otherwise, to restart parsing the parameter
    // ... must be set to 'false'.
    // Returns:
    //   eScpiParserStatus_failed - error, can not parse data as a string;
    //   eScpiParserStatus_success - success, the string has been successfully parsed;
    //   eScpiParserStatus_need_data - warning, the string can not be parsed due to
    //                             there no enough data to process.
    eScpiParserStatus_t parseProgramMenmonic( xParseEntry_t * xObj );
    // ==============================================================================================    
    // @parseCharacter
    // "Parser-function", parses a SCPI character (CHARACTER PROGRAM DATA)
    // References:
    //     "7.7.1 <CHARACTER PROGRAM DATA>", [1]
    //     "7.7.1.2 Encoding Syntax", [1]
    // Parameters:
    //     @xObj - parser context, must be prepared by @prepareParserContext
    // ... before each call. If it is needed to continue the processing, the
    // ... @bKeepContext parameter duing the call @prepareParserContext must 
    // ... be set to 'true', and otherwise, to restart parsing the parameter
    // ... must be set to 'false'.
    // Returns:
    //   eScpiParserStatus_failed - error, can not parse data as a string;
    //   eScpiParserStatus_success - success, the string has been successfully parsed;
    //   eScpiParserStatus_need_data - warning, the string can not be parsed due to
    //                             there no enough data to process.
    eScpiParserStatus_t parseCharacter( xParseEntry_t * xObj );

    // ==============================================================================================
    // @parseCommandProgramHeader
    // "Parser-function", parses a SCPI Command/Query Program Header (<COMMAND PROGRAM HEADER>, <QUERY PROGRMA HEADER>)
    // References:
    //     "7.6.1 <COMMAND PROGPRAM HEADER>", [1]
    //     "7.6.1.2 Encoding Syntax", [1]
    //     "7.6.2 <COMMAND PROGPRAM HEADER>", [1]
    //     "7.6.2.2 Encoding Syntax", [1] 
    // Parameters:
    //     @xObj - parser context, must be prepared by @prepareParserContext
    // ... before each call. If it is needed to continue the processing, the
    // ... @bKeepContext parameter duing the call @prepareParserContext must 
    // ... be set to 'true', and otherwise, to restart parsing the parameter
    // ... must be set to 'false'.
    // Returns:
    //   eScpiParserStatus_failed - error, can not parse data as a string;
    //   eScpiParserStatus_success - success, the string has been successfully parsed;
    //   eScpiParserStatus_need_data - warning, the string can not be parsed due to
    //                         ... there no enough data to process.
    // Notes: All white spaces before the found command program header is skipped,
    // ... the @pxEntityBegin parameter during @getParsedEntityDetails call will refer
    // ... to the command program header beginning.
    eScpiParserStatus_t parseCommandProgramHeader( xParseEntry_t * xObj );
    // =================================================================================================================
    // @parseNumber
    // "Parser-function", parses a SCPI Demical Numeric Program Data and SCPI Hexademical/Octal/Binary Numeric Program Data 
    // References:
    //     "7.7.2 <DECIMAL NUMERIC PROGRAM DATA>", [1]
    //     "7.7.2.2 Encoding Syntax", [1]
    //     "7.7.4 <NONDECIMAL NUMERIC PROGRAM DATA>", [1]
    //     "7.7.4.2 Encoding Syntax", [1]
    // Parameters:
    //     @xObj - parser context, must be prepared by @prepareParserContext
    // ... before each call. If it is needed to continue the processing, the
    // ... @bKeepContext parameter duing the call @prepareParserContext must 
    // ... be set to 'true', and otherwise, to restart parsing the parameter
    // ... must be set to 'false'.
    // Returns:
    //   eScpiParserStatus_failed - error, can not parse data;
    //   eScpiParserStatus_success - success, the string has been successfully parsed;
    //   eScpiParserStatus_need_data - warning, the string can not be parsed due to
    //                             there no enough data to process.
    eScpiParserStatus_t parseNumber( xParseEntry_t * xObj );
    // ==============================================================================================
    
    // @parseProgramDataSeparator
    // "Parser-function", parses a SCPI Program Data Separator
    // References:
    //   "7.4 Separator Functional Elements", [1]
    //   "7.4.2.2 Encoding Syntax", [1] 
    // Parameters:
    //     @xObj - parser context, must be prepared by @prepareParserContext
    // ... before each call. If it is needed to continue the processing, the
    // ... @bKeepContext parameter duing the call @prepareParserContext must 
    // ... be set to 'true', and otherwise, to restart parsing the parameter
    // ... must be set to 'false'.
    // Returns:
    //   eScpiParserStatus_failed - error, can not parse data as a string;
    //   eScpiParserStatus_success - success, the string has been successfully parsed;
    //   eScpiParserStatus_need_data - warning, the string can not be parsed due to
    //                             there no enough data to process.
    eScpiParserStatus_t parseProgramDataSeparator( xParseEntry_t * xObj );

    // ================================================================================================================= 
    // @parseProgramDataSeparator
    // "Parser-function", parses a SCPI Program Header Separator
    // References:
    //   "7.4.3 <PROGRAM HEADER SEPARATOR>, [1]
    //   "7.4.3.2 Encoding Syntax", [1] 
    //   "7.5 <PROGRAM MESSAGE TERMINATOR>", [1]
    // Parameters:
    //     @xObj - parser context, must be prepared by @prepareParserContext
    // ... before each call. If it is needed to continue the processing, the
    // ... @bKeepContext parameter duing the call @prepareParserContext must 
    // ... be set to 'true', and otherwise, to restart parsing the parameter
    // ... must be set to 'false'.
    // Returns:
    //   eScpiParserStatus_failed - error, can not parse data as a string;
    //   eScpiParserStatus_success - success, the string has been successfully parsed;
    //   eScpiParserStatus_need_data - warning, the string can not be parsed due to
    //                             there no enough data to process.
    eScpiParserStatus_t parseProgramHeaderSeparator( xParseEntry_t * xObj );

    // ================================================================================================================= 
    // @parseProgramMessageSeparator
    // "Parser-function", parses a SCPI Program Message Separator
    // References:
    //   "7.4.1 <PROGRAM MESSAGE UNIT SEPARATOR>", [1]
    //   "7.4.1.2 Encoding Syntax", [1]
    //   "7.5 <PROGRAM MESSAGE TERMINATOR>", [1]
    // Parameters:
    //     @xObj - parser context, must be prepared by @prepareParserContext
    // ... before each call. If it is needed to continue the processing, the
    // ... @bKeepContext parameter duing the call @prepareParserContext must 
    // ... be set to 'true', and otherwise, to restart parsing the parameter
    // ... must be set to 'false'.
    // Returns:
    //   eScpiParserStatus_failed - error, can not parse data as a string;
    //   eScpiParserStatus_success - success, the string has been successfully parsed;
    //   eScpiParserStatus_need_data - warning, the string can not be parsed due to
    //                             there no enough data to process.
    eScpiParserStatus_t parseProgramMessageSeparator( xParseEntry_t * xObj );

    // @parseEndOfCommand
    // "Parser-function", searches end of program command unit. 
    // References:
    //   "7.4.1 <PROGRAM MESSAGE UNIT SEPARATOR>", [1]
    //   "7.4.1.2 Encoding Syntax", [1]
    //   "7.5 <PROGRAM MESSAGE TERMINATOR>", [1]
    // Parameters:
    //     @xObj - parser context, must be prepared by @prepareParserContext
    // ... before each call. If it is needed to continue the processing, the
    // ... @bKeepContext parameter duing the call @prepareParserContext must 
    // ... be set to 'true', and otherwise, to restart parsing the parameter
    // ... must be set to 'false'.
    // Returns:
    //   eScpiParserStatus_failed - error, can not parse data as a string;
    //   eScpiParserStatus_success - success, the string has been successfully parsed;
    //   eScpiParserStatus_need_data - warning, the string can not be parsed due to
    //                             there no enough data to process.
    eScpiParserStatus_t parseEndOfCommand( xParseEntry_t * xObj );

    // ==============================================================================================
    // @scpi_iscmdsep
    // Detects if the passed character @ch is a SCPI Program Header Mnemonic Separator
    // Refer to:
    //   "7.6.1 <COMMAND PROGPRAM HEADER>", [1]    
    //   "7.6.1.5 Header Compounding Rules", [1]
    //   "7.6.2 <QUERY PROGRAM HEADER>", [1]
    //   "7.6.2.2 Encoding Syntax", [1] 
    static inline bool scpi_iscmdsep( char ch ) 
    {
        return ( SCPI_CHARACTER_COMPOUNDCOMMAND_SEPARATOR == ch );
    }

    // ==============================================================================================
    // @scpi_iswhite
    // Detects if the passed character @ch is a SCPI White Character
    // Refer to:
    //   "7.4 Separator Functional Elements", [1]
    //   "7.4.1.2 Encoding Syntax", [1] 
    static inline bool scpi_iswhite( char ch ) 
    {
        return ( ch <= SCPI_CHARACTER_WHITE && ch != SCPI_CHARACTER_NL );  // 7.4.1.2 Encoding Syntax, [1]
    }

    // ==============================================================================================
    // @scpi_isdatasep
    // Detects if the passed character @ch is a SCPI Program Data Separator
    // Refer to:
    //   "7.4 Separator Functional Elements", [1]
    //   "7.4.2.2 Encoding Syntax", [1] 
    static inline bool scpi_isdatasep( char ch ) 
    {
        return ( SCPI_CHARACTER_DATA_SEPARATOR == ch );  // 7.4.2.2 Encoding Syntax, [1]
    }

    // ==============================================================================================
    // @scpi_ismsgsep
    // Detects if the passed character @ch is a SCPI Program Message Separator
    // Refer to:
    //   "7.4 Separator Functional Elements", [1]
    //   "7.4.1.2 Encoding Syntax", [1] 
    static inline bool scpi_ismsgsep( char ch ) 
    {
        return ( SCPI_CHARACTER_MESSAGE_SEPARATOR == ch );  // 7.4.1.2 Encoding Syntax, [1]
    }

    // ==============================================================================================
    // @scpi_isnl
    // Detects if the passed character @ch is a SCPI NL Character (New Line)
    // Refer to:
    //   "7.5 <PROGRAM MESSAGE TERMINATOR>", [1]
    //   "7.5.2 Encoding Syntax", [1] 
    static inline bool scpi_isnl( char ch ) 
    {
        return ( ch == SCPI_CHARACTER_NL );  // 7.5.2 Encoding Syntax, [1]
    }

    // ==============================================================================================
    // @scpi_queryind
    // Detects if the passed character @ch is a SCPI Query Indicator 
    // Refer to:
    //   "6.1.6.2.3 Query Detected Message (query)", [1]
    //   "7.6.2.2 Encoding Syntax", [1]
    static inline bool scpi_queryind( char ch ) 
    {
        return ( ch == SCPI_CHARACTER_QUERY );  // 7.6.2.2 Encoding Syntax, [1]
    }

    // ==============================================================================================
    // @scpi_commonhdrind
    // Detects if the passed character @ch is a SCPI Common Program Header indicator
    // Refer to:    
    //   "7.6.1.2 Encoding Syntax", [1]
    static inline bool scpi_commonhdrind( char ch ) 
    {
        return ( ch == SCPI_CHARACTER_COMMON_HEADER );  // 7.6.1.2 Encoding Syntax, [1]
    }

    // ==============================================================================================
    // @scpi_cmdsep
    // Search for the command mnemonic separator
    // @cmdheader - null-terminated string containing simple or
    // ... compound SCPI command program header.
    // See ("7.6.1 <COMMAND PROGPRAM HEADER>", [1])
    // Note: routine modified in order to detect end of line
    static inline const char * scpi_cmdsep( const char * cmdheader )
    {
       assert( cmdheader );
    
       while( '\0' != *cmdheader 
         && !scpi_isnl( *cmdheader) 
         && !scpi_iscmdsep(*cmdheader) 
         && !scpi_iswhite(*cmdheader) 
         && !scpi_ismsgsep(*cmdheader) )
         cmdheader++;
    
       return cmdheader;
    }

    // ==============================================================================================
    // @scpi_isopt_open
    // Detects if the passed character @ch is a SCPI Opening Default Mnemonic Encoding character
    // Refer to:
    //   "5.1 Interpreting Command Tables", [2]    
    static inline bool scpi_isopt_open( char ch ) 
    {
        return ( ch == SCPI_CHARACTER_DEFNODE_OPEN );
    }

    // ==============================================================================================
    // @scpi_isopt_close
    // Detects if the passed character @ch is a SCPI Opening Default Mnemonic Encoding character
    // Refer to:
    //   "5.1 Interpreting Command Tables", [2]    
    static inline bool scpi_isopt_close( char ch ) 
    {
        return ( ch == SCPI_CHARACTER_DEFNODE_CLOSE );
    }

    // =================================================================================================================   
    // @parseWhiteSequence
    // "Parser-function", searches for white characters sequence or NL-terminator
    // References:
    //   "7.4.1.2 Encoding Syntax", [1] 
    //   "7.5 <PROGRAM MESSAGE TERMINATOR>", [1]
    // Parameters:
    //     @xObj - parser context, must be prepared by @prepareParserContext
    // ... before each call. If it is needed to continue the processing, the
    // ... @bKeepContext parameter duing the call @prepareParserContext must 
    // ... be set to 'true', and otherwise, to restart parsing the parameter
    // ... must be set to 'false'.
    // Returns:
    //   eScpiParserStatus_failed - error, can not parse data;
    //   eScpiParserStatus_success - success, the string has been successfully parsed;
    //   eScpiParserStatus_need_data - warning, the string can not be parsed due to
    //                             there no enough data to process.
    eScpiParserStatus_t parseWhiteSequence( xParseEntry_t * xObj );
    // =================================================================================================================   
    // @checkNotEndOfString
    // "Parser-function", checks if the parser context not reached the end of string
    // Parameters:
    //     @xObj - parser context, must be prepared by @prepareParserContext
    // ... before each call. If it is needed to continue the processing, the
    // ... @bKeepContext parameter duing the call @prepareParserContext must 
    // ... be set to 'true', and otherwise, to restart parsing the parameter
    // ... must be set to 'false'. 
    // Returns:
    //   eScpiParserStatus_failed - error, end-of-string is reached
    //   eScpiParserStatus_success - success, end-of-string is not reached
    //   eScpiParserStatus_invalid - error, invalid parameters
    eScpiParserStatus_t checkNotEndOfString( xParseEntry_t * xObj );
    // ==============================================================================================    
    // @parseArguments
    // Common parser function to analyze and recognize command arguments using the
    // template preassigned by @DECLARE_SCPI_ARGS macro.
    // Parameters:
    //     @xObj - parser context, must be prepared by @prepareParserContext
    // ... before each call. If it is needed to continue the processing, the
    // ... @bKeepContext parameter duing the call @prepareParserContext must 
    // ... be set to 'true', and otherwise, to restart parsing the parameter
    // ... must be set to 'false'.
    //     @argTokens - array of argument string tokens to store parameters iterators;
    //     @argTypes - array of argument types [eScpiArgType_t] to set the parameters parsing methods;
    //     @entityTypes - array of entity types [eScpiEntityType_t] to be written after successful call; optional;
    //     @argN - number of arguments (including optional and mandatory ones);
    //     @argOptionalN - number of optional only arguments;
    //     @pArgParsed - pointer to the variable to store the number of successfully 
    // ... parsed arguments, valid only if eScpiParserStatus_success status is returned,
    // ... Otherwise, if the status is eScpiParserStatus_failed this value indicates
    // ... the index of erroneous parameter. Can not be NULL.
    // Returns:
    //   eScpiParserStatus_failed - error, can not parse data;
    //   eScpiParserStatus_invalid - error, invalid parameters;
    //   eScpiParserStatus_success - success, the string has been successfully parsed;
    //   eScpiParserStatus_need_data - warning, the string can not be parsed due to
    //                             there no enough data to process.
    eScpiParserStatus_t parseArguments( xParseEntry_t * xObj,
                                        sStrToken_t * argTokens,
                                        const uint8_t * argTypes,
                                        uint8_t * entityTypes,
                                        size_t argN,
                                        size_t argOptionalN, 
                                        uint8_t * pArgParsed );
    
    eScpiParserStatus_t parseArrayArguments( xParseEntry_t * xObj,
                                             uint8_t * argTokens,
                                             size_t * argNumber,
                                             const uint8_t * argTypes,
                                             uint8_t * entityTypes,
                                             size_t argN,
                                             size_t argOptionalN, 
                                             uint8_t * pArgParsed );
#endif