| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- #define SCPI_TLST_EX_C
- #include "app/scpi/scpi_tlst_ex.h"
- #include "app/scpi/scpi_parser.h"
- #include <ctype.h> // toupper, islower
- // Author: Sychov A., Jan 2021
- // Extension module to adopt the Tree-List algorithms to the SCPI module.
- //----------------------------------------------------------------
- // 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"
- //----------------------------------------------------------------
- #if !TREE_LIST_DEEP_ENABLE || !TREE_LIST_DEEP_PARENT_SEARCH_ENABLE
- #error Tree List deep search is required or parent search feature must be enabled
- #else
- // @scpi_commands_tlds - functional set for @tree_list_search_deep
- const sTreeListSearchDeep_FSet_t scpi_commands_tlds = {
- .fnext = treelist_getnext_key,
- .fcmp = treelist_header_compare,
- .fisdef = treelist_is_default_node,
- #if TREE_LIST_RECURSIVE_LIMIT_ENABLE
- .freclim = treelist_recursive_limiter,
- #endif
- .fagree = treelist_result_agree,
- };
- #endif
- // ========================================================================================================
- // @scpi_is_default_node
- // Checks if the passed string is the default (optional) SCPI command mnemonic
- // Is used to determine the default node in the tree-list.
- // References:
- // "5.1 Interpreting Command Tables", [2]
- // "7.6.1 <COMMAND PROGPRAM HEADER>", [1]
- // "7.6.1.5 Header Compounding Rules", [1]
- // @commandString - SCPI command simple mnemonic;
- // Note: compound mnemonics are not supported;
- // Note: the passed mnemonic must be valid, the mnemonic will not be validated.
- bool scpi_is_default_node( const char * commandString )
- {
- my_assert( commandString );
- if( NULL == commandString ) return false;
- bool optional_open = false;
- bool optional_close = false;
- while( '\0' != *commandString )
- {
- if( scpi_isopt_open( *commandString ) )
- {
- if( !optional_open )
- {
- optional_open = true;
- ++commandString;
- continue; // skip character
- }
- return false; // invalid mnemonic: extra optional sign '['
- }
- if( scpi_isopt_close( *commandString ) )
- {
- if( optional_open && !optional_close )
- {
- optional_close = true;
- ++commandString;
- continue; // skip character
- }
- return false; // invalid mnemonic: invalid syntax (optional sign ']')
- }
- ++commandString;
- }
- my_assert( !(optional_open ^ optional_close) ); // must be 'open===close'
-
- // if both @optional_open and @optional_close signs are present, this mnemonic is considered as default
- return ( optional_open && optional_close );
- }
- // ========================================================================================================
- // @scpi_command_header_compare
- // Compares the program mnemonic of registered command (@command) with
- // ... the program mnemonic of received command header (@src_key).
- // Function ignores the tailing question mark of the command header mnemonic.
- // References:
- // "7.6 Program Header Functional Elements", [1]
- // "5.1 Interpreting Command Tables", [2]
- // @commandString - the registered command header part, null-term string,
- // ... attached to the node using @nodekey_ parameter of DECLARE_TREE_LIST_LEAF.
- // @comparingToken - the searching command header part, a pair of (@shead, @stail)
- // ... pointers grouped into the @sStrToken_t.
- // Note: the @sStrToken_t structure referred by @src_key shall be writable.
- // Note: @command can points to the part of the original command string.
- // Returns: The function returns true in case the part specified by @src_key
- // ... matchs to the part of the command specified by @command and nearest
- // ... mnemonic separator (colon).
- bool scpi_command_header_compare( const char * commandString,
- const sStrToken_t * comparingToken )
- {
- bool result = true; // forward set, true by default.
- bool longform = false;
-
- const char * shead = comparingToken->shead;
- const char * stail = comparingToken->stail;
- my_assert( shead );
- my_assert( stail );
- my_assert( commandString );
- if( scpi_isnl( *(stail-1) )) --stail;
- if( scpi_queryind( *(stail-1) )) --stail;
-
- // skip white characters before comparing
- while( scpi_iswhite( *shead ) )
- shead++;
- if( shead >= stail )
- {
- return false; // invalid mnemonic
- }
- // Optional mnemonic indicators (e.g., "[:SYSTem]:ERRor"):
- bool optional_open = false;
- bool optional_close = false;
- // Until the end of the searching mnemonic is reached:
- for( ;shead != stail; ++commandString )
- {
- if( scpi_isopt_open( *commandString ) )
- {
- if( !optional_open )
- {
- optional_open = true;
- continue; // skip character
- }
- return false; // error: extra optional sign '['
- }
- if( scpi_isopt_close( *commandString ) )
- {
- if( optional_open && !optional_close )
- {
- optional_close = true;
- continue; // skip character
- }
- return false; // error: invalid syntax (optional sign ']')
- }
- if( '\0' == *commandString )
- {
- return false; // end of source mnemonic when @shead != @stail
- }
- if( toupper(*shead) != toupper(*commandString) )
- {
- return false; // case insensetive characters mismatch
- }
- if( islower(*commandString) )
- {
- longform = true; // long form found while @shead != @stail
- }
- ++shead;
- }
- // check if long form of mnemonic is found:
- if( longform )
- {
- // the searching mnemonic shall have the same length as
- // ... long form of source mnemonic.
- // Check if the end of long form reached: NULL-char or ']' is in the end of @commandString
- result = ('\0' == *commandString || scpi_isopt_close( *commandString ) );
- }
- else
- {
- // the searching mnemonic shall have the same length as
- // ... short form of source mnemonic.
-
- // Note: @commandString has been incremented at least once.
- // Check if the end of short form reached, check the character's
- // ... case at @commandString position and @commandString-1 position, the
- // ... register of characters must differ:
- // (the same character's register -> error)
- result = ( islower(*(commandString-1)) != islower(*commandString) || '\0' == *commandString );
- }
- // if short form of command is received, it is required
- // to validate the passed registered command til the end.
- // Fix: if long form is received, it is requried to process the @commandString til the end
- if( optional_open && !optional_close )
- {
- while( '\0' != *commandString )
- {
- if( scpi_isopt_close( *commandString ) )
- {
- if( optional_close )
- {
- return false; // error: invalid syntax (optional sign ']')
- }
- optional_close = true;
- }
- ++commandString;
- }
- }
- my_assert( !(optional_open ^ optional_close) ); // must be 'open===close'
- return result;
- }
- #if TREE_LIST_RECURSIVE_LIMIT_ENABLE
- // @scpi_search_recursive_limiter
- // Limits the depth of recursive calls of @tree_list_search_deep
- // Is used to prevent stack overflow.
- // @dir - direction: true - enter recursive call, false - leave call;
- // @ctx - mandatory user dependent context.
- // Returns, if @dir is 'true':
- // - true: recursive call is possible;
- // - false: recursive call is impossible;
- // Returns, if @dir is 'false': does not matter;
- bool scpi_search_recursive_limiter( bool dir, void * _ctx )
- {
- my_assert( _ctx );
- sTreeListSearchCtx_t * ctx = (sTreeListSearchCtx_t*)_ctx;
-
- if( dir )
- if( ctx->call_depth >= SCPI_MAX_CMD_SEARCH_DEPTH )
- {
- my_assert( ctx->call_depth < SCPI_MAX_CMD_SEARCH_DEPTH );
- return false; // limit is reached
- }
- return true;
- }
- #endif
- // @scpi_command_getnext_prepare
- // Prepare context for @scpi_command_getnext_key before calling @tree_list_search_deep
- // @ctx - search key context, is used to iterate upon the command mnemonic chain (compound command)
- // @cmd_shead - compound SCPI command mnemonic head
- // @cmd_stail - compound SCPI command mnemonic tail
- void scpi_command_getnext_prepare( sScpiCmdGetNextKey_t * ctx, const char * cmd_shead, const char * cmd_stail )
- {
- my_assert( ctx );
- my_assert( cmd_shead );
- my_assert( cmd_stail );
- my_assert( cmd_shead <= cmd_stail );
- ctx->full_token.shead = cmd_shead;
- ctx->full_token.stail = cmd_stail;
- ctx->found_token.shead = NULL;
- ctx->found_token.stail = NULL;
- }
- // @scpi_command_search_prepare
- // Prepare the SCPI Command search context before calling @tree_list_search_deep
- // @ctx - search context, is used in call of @tree_list_search_deep
- // @cmd_shead - compound SCPI command mnemonic head
- // @cmd_stail - compound SCPI command mnemonic tail
- void scpi_command_search_prepare( sTreeListSearchCtx_t * ctx, const char * cmd_shead, const char * cmd_stail )
- {
- my_assert( ctx );
- scpi_command_getnext_prepare( &ctx->cmdKey, cmd_shead, cmd_stail );
- #if TREE_LIST_RECURSIVE_LIMIT_ENABLE
- ctx->call_depth = 0;
- #endif
- }
- // @scpi_command_getnext_key
- // Get next command mnemonic token in the chain specified in @ctx
- // @ctx - rotine context;
- // Returns:
- // NULL - no key found / end of the command line;
- // NON NULL: string token containing command mnemonic (key)
- const sStrToken_t* scpi_command_getnext_key( sScpiCmdGetNextKey_t * ctx )
- {
- my_assert( ctx->full_token.shead );
- my_assert( ctx->full_token.stail );
- my_assert( ctx->full_token.shead <= ctx->full_token.stail );
- my_assert( NULL == ctx->found_token.shead || ctx->found_token.shead >= ctx->full_token.shead );
- my_assert( NULL == ctx->found_token.shead || ctx->found_token.shead <= ctx->full_token.stail );
- my_assert( NULL == ctx->found_token.stail || ctx->found_token.stail >= ctx->full_token.shead );
- my_assert( NULL == ctx->found_token.stail || ctx->found_token.stail <= ctx->full_token.stail );
- if( NULL == ctx->found_token.shead )
- {
- ctx->found_token.shead = ctx->full_token.shead;
- ctx->found_token.stail = scpi_cmdsep( ctx->full_token.shead );
- my_assert( ctx->found_token.stail ); // @tail never will be NULL
- return &ctx->found_token;
- }
- if( ctx->found_token.shead >= ctx->full_token.stail
- && ctx->found_token.stail >= ctx->full_token.stail )
- {
- return NULL; // end-of-command line
- }
- // all the command mnemonics are separated with SCPI_CHARACTER_COMPOUNDCOMMAND_SEPARATOR
- if( scpi_iscmdsep( *ctx->found_token.stail ) )
- {
- ctx->found_token.shead = (ctx->found_token.stail + 1); // skip separator
- ctx->found_token.stail = scpi_cmdsep( ctx->found_token.shead );
- my_assert( ctx->found_token.stail ); // @tail never will be NULL
- return &ctx->found_token;
- }
- return NULL; // invalid command chain
- }
- // @scpi_command_command_found
- // SCPI search command result notifee.
- void scpi_command_command_found( sTreeListSearchCtx_t * ctx, const sTreeList_t * found, const sTreeList_t * parent )
- {
- ctx->parent = parent; // pass the parent node pointer to the caller (@scpiSearchCommandHandler)
- }
|