/[svn]/linuxsampler/trunk/src/network/lscp.y
ViewVC logotype

Diff of /linuxsampler/trunk/src/network/lscp.y

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 2461 by persson, Sun Aug 25 06:09:11 2013 UTC revision 3787 by schoenebeck, Mon Jun 8 11:55:18 2020 UTC
# Line 3  Line 3 
3   *   LinuxSampler - modular, streaming capable sampler                     *   *   LinuxSampler - modular, streaming capable sampler                     *
4   *                                                                         *   *                                                                         *
5   *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *   *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *
6   *   Copyright (C) 2005 - 2013 Christian Schoenebeck                       *   *   Copyright (C) 2005 - 2020 Christian Schoenebeck                       *
7   *                                                                         *   *                                                                         *
8   *   This program is free software; you can redistribute it and/or modify  *   *   This program is free software; you can redistribute it and/or modify  *
9   *   it under the terms of the GNU General Public License as published by  *   *   it under the terms of the GNU General Public License as published by  *
# Line 34  Line 34 
34  #include "lscpparser.h"  #include "lscpparser.h"
35  #include "lscpserver.h"  #include "lscpserver.h"
36  #include "lscpevent.h"  #include "lscpevent.h"
37  #include "lscpsymbols.h"  
38    #if AC_APPLE_UNIVERSAL_BUILD
39    # include "lscp.tab.h"
40    #else
41    # include "lscpsymbols.h"
42    #endif
43    
44    #include <algorithm>
45    #include "lscp.h"
46    
47  namespace LinuxSampler {  namespace LinuxSampler {
48    
49  // to save us typing work in the rules action definitions  // to save us typing work in the rules action definitions
50  #define LSCPSERVER ((yyparse_param_t*) yyparse_param)->pServer  #define LSCPSERVER ((yyparse_param_t*) yyparse_param)->pServer
51  #define SESSION_PARAM ((yyparse_param_t*) yyparse_param)  #define SESSION_PARAM ((yyparse_param_t*) yyparse_param)
52  #define INCREMENT_LINE { SESSION_PARAM->iLine++; SESSION_PARAM->iColumn = 0; }  #define INCREMENT_LINE { SESSION_PARAM->onNextLine(); sParsed.clear(); }
53    
54  // clears input buffer  // clears input buffer
55  void restart(yyparse_param_t* pparam, int& yychar);  void restart(yyparse_param_t* pparam, int& yychar);
# Line 51  static char buf[1024]; // input buffer t Line 59  static char buf[1024]; // input buffer t
59  static int bytes = 0;  // current number of characters in the input buffer  static int bytes = 0;  // current number of characters in the input buffer
60  static int ptr   = 0;  // current position in the input buffer  static int ptr   = 0;  // current position in the input buffer
61  static String sLastError; // error message of the last error occured  static String sLastError; // error message of the last error occured
62    static String sParsed; ///< Characters of current line which have already been shifted (consumed/parsed) by the parser.
63    
64  // external reference to the function which actually reads from the socket  // external reference to the function which actually reads from the socket
65  extern int GetLSCPCommand( void *buf, int max_size);  extern int GetLSCPCommand( void *buf, int max_size);
# Line 64  inline bool isExtendedAsciiChar(const ch Line 73  inline bool isExtendedAsciiChar(const ch
73      return (c < 0);      return (c < 0);
74  }  }
75    
76    // returns true if the given character is between between a to z.
77    inline bool isLowerCaseAlphaChar(const char c) {
78        return c >= 'a' && c <= 'z';
79    }
80    
81    // converts the given (expected) lower case character to upper case
82    inline char alphaCharToUpperCase(const char c) {
83        return (c - 'a') + 'A';
84    }
85    
86  // custom scanner function which reads from the socket  // custom scanner function which reads from the socket
87  // (bison expects it to return the numerical ID of the next  // (bison expects it to return the numerical ID of the next
88  // "recognized token" from the input stream)  // "recognized token" from the input stream)
# Line 81  int yylex(YYSTYPE* yylval) { Line 100  int yylex(YYSTYPE* yylval) {
100      const char c = buf[ptr++];      const char c = buf[ptr++];
101      // increment current reading position (just for verbosity / messages)      // increment current reading position (just for verbosity / messages)
102      GetCurrentYaccSession()->iColumn++;      GetCurrentYaccSession()->iColumn++;
103        sParsed += c;
104      // we have to handle "normal" and "extended" ASCII characters separately      // we have to handle "normal" and "extended" ASCII characters separately
105      if (isExtendedAsciiChar(c)) {      if (isExtendedAsciiChar(c)) {
106          // workaround for characters with ASCII code higher than 127          // workaround for characters with ASCII code higher than 127
# Line 103  int octalsToNumber(char oct_digit0, char Line 123  int octalsToNumber(char oct_digit0, char
123    
124  }  }
125    
 // we provide our own version of yyerror() so we don't have to link against the yacc library  
 void yyerror(const char* s);  
 void yyerror(void* x, const char* s) { yyerror(s); }  
   
126  using namespace LinuxSampler;  using namespace LinuxSampler;
127    
128    static std::set<String> yyExpectedSymbols();
129    
130    /**
131     * Will be called when an error occured (usually syntax error).
132     *
133     * We provide our own version of yyerror() so we a) don't have to link against
134     * the yacc library and b) can render more helpful syntax error messages.
135     */
136    void yyerror(void* x, const char* s) {
137        yyparse_param_t* param = GetCurrentYaccSession();
138    
139        // get the text part already parsed (of current line)
140        const bool bContainsLineFeed =
141            sParsed.find('\r') != std::string::npos ||
142            sParsed.find('\n') != std::string::npos;
143        // remove potential line feed characters
144        if (bContainsLineFeed) {
145            for (size_t p = sParsed.find('\r'); p != std::string::npos;
146                 p = sParsed.find('\r')) sParsed.erase(p);
147            for (size_t p = sParsed.find('\n'); p != std::string::npos;
148                 p = sParsed.find('\n')) sParsed.erase(p);
149        }
150    
151        // start assembling the error message with Bison's own message
152        String txt = s;
153    
154        // append exact position info of syntax error
155        txt += (" (line:"  + ToString(param->iLine+1)) +
156               (",column:" + ToString(param->iColumn)) + ")";
157    
158        // append the part of the lined that has already been parsed
159        txt += ". Context: \"" + sParsed;
160        if (txt.empty() || bContainsLineFeed)
161            txt += "^";
162        else
163            txt.insert(txt.size() - 1, "^");
164        txt += "...\"";
165    
166        // append the non-terminal symbols expected now/next
167        std::set<String> expectedSymbols = yyExpectedSymbols();
168        for (std::set<String>::const_iterator it = expectedSymbols.begin();
169             it != expectedSymbols.end(); ++it)
170        {
171            if (it == expectedSymbols.begin())
172                txt += " -> Should be: " + *it;
173            else
174                txt += " | " + *it;
175        }
176    
177        dmsg(2,("LSCPParser: %s\n", txt.c_str()));
178        sLastError = txt;
179    }
180    
181  %}  %}
182    
183  // reentrant parser  // reentrant parser
# Line 116  using namespace LinuxSampler; Line 185  using namespace LinuxSampler;
185    
186  %parse-param {void* yyparse_param}  %parse-param {void* yyparse_param}
187    
188    // After entering the yyparse() function, store references to the parser's
189    // symbol stack, so that we can create more helpful syntax error messages than
190    // Bison (2.x) could do.
191    %initial-action {
192        yyparse_param_t* p = (yyparse_param_t*) yyparse_param;
193        p->ppStackBottom = &yyss;
194        p->ppStackTop    = &yyssp;
195    }
196    
197  // tell bison to spit out verbose syntax error messages  // tell bison to spit out verbose syntax error messages
198  %error-verbose  %error-verbose
199    
# Line 124  using namespace LinuxSampler; Line 202  using namespace LinuxSampler;
202  %type <Char> char char_base alpha_char digit digit_oct digit_hex escape_seq escape_seq_octal escape_seq_hex  %type <Char> char char_base alpha_char digit digit_oct digit_hex escape_seq escape_seq_octal escape_seq_hex
203  %type <Dotnum> real dotnum volume_value boolean control_value  %type <Dotnum> real dotnum volume_value boolean control_value
204  %type <Number> number sampler_channel instrument_index fx_send_id audio_channel_index device_index effect_index effect_instance effect_chain chain_pos input_control midi_input_channel_index midi_input_port_index midi_map midi_bank midi_prog midi_ctrl  %type <Number> number sampler_channel instrument_index fx_send_id audio_channel_index device_index effect_index effect_instance effect_chain chain_pos input_control midi_input_channel_index midi_input_port_index midi_map midi_bank midi_prog midi_ctrl
205  %type <String> string string_escaped text text_escaped text_escaped_base stringval stringval_escaped digits param_val_list param_val query_val filename module effect_system db_path map_name entry_name fx_send_name effect_name engine_name command add_instruction create_instruction destroy_instruction get_instruction list_instruction load_instruction send_instruction set_chan_instruction load_instr_args load_engine_args audio_output_type_name midi_input_type_name remove_instruction unmap_instruction set_instruction subscribe_event unsubscribe_event map_instruction reset_instruction clear_instruction find_instruction move_instruction copy_instruction scan_mode edit_instruction format_instruction append_instruction insert_instruction  %type <String> string string_escaped text text_escaped text_escaped_base stringval stringval_escaped digits param_val_list param_val query_val filename module effect_system db_path map_name entry_name fx_send_name effect_name engine_name line statement command add_instruction create_instruction destroy_instruction get_instruction list_instruction load_instruction send_instruction set_chan_instruction load_instr_args load_engine_args audio_output_type_name midi_input_type_name remove_instruction unmap_instruction set_instruction subscribe_event unsubscribe_event map_instruction reset_instruction clear_instruction find_instruction move_instruction copy_instruction scan_mode edit_instruction format_instruction append_instruction insert_instruction
206  %type <FillResponse> buffer_size_type  %type <FillResponse> buffer_size_type
207  %type <KeyValList> key_val_list query_val_list  %type <KeyValList> key_val_list query_val_list
208  %type <LoadMode> instr_load_mode  %type <LoadMode> instr_load_mode
# Line 146  using namespace LinuxSampler; Line 224  using namespace LinuxSampler;
224    
225  // GRAMMAR_BNF_BEGIN - do NOT delete or modify this line !!!  // GRAMMAR_BNF_BEGIN - do NOT delete or modify this line !!!
226    
227  input                 : line LF  input                 :  line   { INCREMENT_LINE; if (!$1.empty()) LSCPSERVER->AnswerClient($1); return LSCP_DONE; }
228                        | line CR LF                        |  error  { INCREMENT_LINE; LSCPSERVER->AnswerClient("ERR:0:" + sLastError + "\r\n"); RESTART; return LSCP_SYNTAX_ERROR; }
229                          ;
230    
231    line                  :  statement LF     { $$ = $1; }
232                          |  statement CR LF  { $$ = $1; }
233                        ;                        ;
234    
235  line                  :  /* epsilon (empty line ignored) */ { INCREMENT_LINE; return LSCP_DONE; }  statement             :  /* epsilon (empty statement/line ignored) */  { $$ = ""; }
236                        |  comment  { INCREMENT_LINE; return LSCP_DONE; }                        |  comment  { $$ = ""; }
237                        |  command  { INCREMENT_LINE; LSCPSERVER->AnswerClient($1); return LSCP_DONE; }                        |  command
                       |  error    { INCREMENT_LINE; LSCPSERVER->AnswerClient("ERR:0:" + sLastError + "\r\n"); RESTART; return LSCP_SYNTAX_ERROR; }  
238                        ;                        ;
239    
240  comment               :  '#'  comment               :  '#'
# Line 190  command               :  ADD SP add_inst Line 271  command               :  ADD SP add_inst
271                        ;                        ;
272    
273  add_instruction       :  CHANNEL                               { $$ = LSCPSERVER->AddChannel();                  }  add_instruction       :  CHANNEL                               { $$ = LSCPSERVER->AddChannel();                  }
274                          |  CHANNEL SP MIDI_INPUT SP sampler_channel SP device_index                           { $$ = LSCPSERVER->AddChannelMidiInput($5,$7);    }
275                          |  CHANNEL SP MIDI_INPUT SP sampler_channel SP device_index SP midi_input_port_index  { $$ = LSCPSERVER->AddChannelMidiInput($5,$7,$9); }
276                        |  DB_INSTRUMENT_DIRECTORY SP db_path    { $$ = LSCPSERVER->AddDbInstrumentDirectory($3);  }                        |  DB_INSTRUMENT_DIRECTORY SP db_path    { $$ = LSCPSERVER->AddDbInstrumentDirectory($3);  }
277                        |  DB_INSTRUMENTS SP NON_MODAL SP scan_mode SP db_path SP filename                        { $$ = LSCPSERVER->AddDbInstruments($5,$7,$9, true);        }                        |  DB_INSTRUMENTS SP NON_MODAL SP scan_mode SP db_path SP filename                        { $$ = LSCPSERVER->AddDbInstruments($5,$7,$9, true);        }
278                        |  DB_INSTRUMENTS SP NON_MODAL SP scan_mode SP FILE_AS_DIR SP db_path SP filename         { $$ = LSCPSERVER->AddDbInstruments($5,$9,$11, true, true); }                        |  DB_INSTRUMENTS SP NON_MODAL SP scan_mode SP FILE_AS_DIR SP db_path SP filename         { $$ = LSCPSERVER->AddDbInstruments($5,$9,$11, true, true); }
# Line 278  unmap_instruction     :  MIDI_INSTRUMENT Line 361  unmap_instruction     :  MIDI_INSTRUMENT
361                        ;                        ;
362    
363  remove_instruction    :  CHANNEL SP sampler_channel                   { $$ = LSCPSERVER->RemoveChannel($3);                      }  remove_instruction    :  CHANNEL SP sampler_channel                   { $$ = LSCPSERVER->RemoveChannel($3);                      }
364                          |  CHANNEL SP MIDI_INPUT SP sampler_channel                                           { $$ = LSCPSERVER->RemoveChannelMidiInput($5);       }
365                          |  CHANNEL SP MIDI_INPUT SP sampler_channel SP device_index                           { $$ = LSCPSERVER->RemoveChannelMidiInput($5,$7);    }
366                          |  CHANNEL SP MIDI_INPUT SP sampler_channel SP device_index SP midi_input_port_index  { $$ = LSCPSERVER->RemoveChannelMidiInput($5,$7,$9); }
367                        |  MIDI_INSTRUMENT_MAP SP midi_map              { $$ = LSCPSERVER->RemoveMidiInstrumentMap($3);            }                        |  MIDI_INSTRUMENT_MAP SP midi_map              { $$ = LSCPSERVER->RemoveMidiInstrumentMap($3);            }
368                        |  MIDI_INSTRUMENT_MAP SP ALL                   { $$ = LSCPSERVER->RemoveAllMidiInstrumentMaps();          }                        |  MIDI_INSTRUMENT_MAP SP ALL                   { $$ = LSCPSERVER->RemoveAllMidiInstrumentMaps();          }
369                        |  SEND_EFFECT_CHAIN SP device_index SP effect_chain  { $$ = LSCPSERVER->RemoveSendEffectChain($3,$5);     }                        |  SEND_EFFECT_CHAIN SP device_index SP effect_chain  { $$ = LSCPSERVER->RemoveSendEffectChain($3,$5);     }
# Line 362  set_instruction       :  AUDIO_OUTPUT_DE Line 448  set_instruction       :  AUDIO_OUTPUT_DE
448                        |  DB_INSTRUMENT SP DESCRIPTION SP db_path SP stringval_escaped                     { $$ = LSCPSERVER->SetDbInstrumentDescription($5,$7);              }                        |  DB_INSTRUMENT SP DESCRIPTION SP db_path SP stringval_escaped                     { $$ = LSCPSERVER->SetDbInstrumentDescription($5,$7);              }
449                        |  DB_INSTRUMENT SP FILE_PATH SP filename SP filename                               { $$ = LSCPSERVER->SetDbInstrumentFilePath($5,$7);                 }                        |  DB_INSTRUMENT SP FILE_PATH SP filename SP filename                               { $$ = LSCPSERVER->SetDbInstrumentFilePath($5,$7);                 }
450                        |  ECHO SP boolean                                                                  { $$ = LSCPSERVER->SetEcho((yyparse_param_t*) yyparse_param, $3);  }                        |  ECHO SP boolean                                                                  { $$ = LSCPSERVER->SetEcho((yyparse_param_t*) yyparse_param, $3);  }
451                          |  SHELL SP INTERACT SP boolean                                                     { $$ = LSCPSERVER->SetShellInteract((yyparse_param_t*) yyparse_param, $5); }
452                          |  SHELL SP AUTO_CORRECT SP boolean                                                 { $$ = LSCPSERVER->SetShellAutoCorrect((yyparse_param_t*) yyparse_param, $5); }
453                          |  SHELL SP DOC SP boolean                                                          { $$ = LSCPSERVER->SetShellDoc((yyparse_param_t*) yyparse_param, $5); }
454                        |  VOLUME SP volume_value                                                           { $$ = LSCPSERVER->SetGlobalVolume($3);                            }                        |  VOLUME SP volume_value                                                           { $$ = LSCPSERVER->SetGlobalVolume($3);                            }
455                        |  VOICES SP number                                                                 { $$ = LSCPSERVER->SetGlobalMaxVoices($3);                         }                        |  VOICES SP number                                                                 { $$ = LSCPSERVER->SetGlobalMaxVoices($3);                         }
456                        |  STREAMS SP number                                                                { $$ = LSCPSERVER->SetGlobalMaxStreams($3);                        }                        |  STREAMS SP number                                                                { $$ = LSCPSERVER->SetGlobalMaxStreams($3);                        }
# Line 452  buffer_size_type      :  BYTES       { $ Line 541  buffer_size_type      :  BYTES       { $
541  list_instruction      :  AUDIO_OUTPUT_DEVICES                               { $$ = LSCPSERVER->GetAudioOutputDevices();              }  list_instruction      :  AUDIO_OUTPUT_DEVICES                               { $$ = LSCPSERVER->GetAudioOutputDevices();              }
542                        |  MIDI_INPUT_DEVICES                                 { $$ = LSCPSERVER->GetMidiInputDevices();                }                        |  MIDI_INPUT_DEVICES                                 { $$ = LSCPSERVER->GetMidiInputDevices();                }
543                        |  CHANNELS                                           { $$ = LSCPSERVER->ListChannels();                       }                        |  CHANNELS                                           { $$ = LSCPSERVER->ListChannels();                       }
544                          |  CHANNEL SP MIDI_INPUTS SP sampler_channel          { $$ = LSCPSERVER->ListChannelMidiInputs($5);            }
545                        |  AVAILABLE_ENGINES                                  { $$ = LSCPSERVER->ListAvailableEngines();               }                        |  AVAILABLE_ENGINES                                  { $$ = LSCPSERVER->ListAvailableEngines();               }
546                        |  AVAILABLE_EFFECTS                                  { $$ = LSCPSERVER->ListAvailableEffects();               }                        |  AVAILABLE_EFFECTS                                  { $$ = LSCPSERVER->ListAvailableEffects();               }
547                        |  EFFECT_INSTANCES                                   { $$ = LSCPSERVER->ListEffectInstances();                }                        |  EFFECT_INSTANCES                                   { $$ = LSCPSERVER->ListEffectInstances();                }
# Line 853  REMOVE                :  'R''E''M''O''V' Line 943  REMOVE                :  'R''E''M''O''V'
943  SET                   :  'S''E''T'  SET                   :  'S''E''T'
944                        ;                        ;
945    
946    SHELL                 :  'S''H''E''L''L'
947                          ;
948    
949    INTERACT              :  'I''N''T''E''R''A''C''T'
950                          ;
951    
952    AUTO_CORRECT          :  'A''U''T''O''_''C''O''R''R''E''C''T'
953                          ;
954    
955  APPEND                :  'A''P''P''E''N''D'  APPEND                :  'A''P''P''E''N''D'
956                        ;                        ;
957    
# Line 1081  MIDI_INPUT_TYPE       :  'M''I''D''I''_' Line 1180  MIDI_INPUT_TYPE       :  'M''I''D''I''_'
1180  MIDI_INPUT            :  'M''I''D''I''_''I''N''P''U''T'  MIDI_INPUT            :  'M''I''D''I''_''I''N''P''U''T'
1181                        ;                        ;
1182    
1183    MIDI_INPUTS           :  'M''I''D''I''_''I''N''P''U''T''S'
1184                          ;
1185    
1186  MIDI_CONTROLLER       :  'M''I''D''I''_''C''O''N''T''R''O''L''L''E''R'  MIDI_CONTROLLER       :  'M''I''D''I''_''C''O''N''T''R''O''L''L''E''R'
1187                        ;                        ;
1188    
# Line 1186  NAME                  :  'N''A''M''E' Line 1288  NAME                  :  'N''A''M''E'
1288  ECHO                  :  'E''C''H''O'  ECHO                  :  'E''C''H''O'
1289                        ;                        ;
1290    
1291    DOC                   :  'D''O''C'
1292                          ;
1293    
1294  QUIT                  :  'Q''U''I''T'  QUIT                  :  'Q''U''I''T'
1295                        ;                        ;
1296    
1297  %%  %%
1298    
1299    // TODO: actually would be fine to have the following bunch of source code in a separate file, however those functions are a) accessing private Bison tables like yytable and b) including the functions from another file here would make the line numbers incorrect on compile errors in auto generated lscpparser.cpp
1300    
1301  /**  /**
1302   * Will be called when an error occured (usually syntax error).   * Additional informations of a grammar symbol.
1303     */
1304    struct BisonSymbolInfo {
1305        bool isTerminalSymbol; ///< Whether the symbol is a terminal or non-termianl symbol. NOTE: Read comment regarding this in _isRuleTerminalSymbol() !!
1306        String nextExpectedChars; ///< According to current parser position: sequence of characters expected next for satisfying this grammar symbol.
1307    };
1308    
1309    #if HAVE_BISON_MAJ >= 3 // Bison 3.x or younger ...
1310    
1311    /**
1312     * Must ONLY be called just before a so called "reduce" parser action:
1313     * Returns true if the grammar rule, which is just about to be "reduced", is a
1314     * terminal symbol (in *our* terms).
1315     *
1316     * Please note that the term "terminal symbol" is a bit confusingly used in
1317     * this source code here around. In Bison's terms, "terminal symbols" are (more
1318     * or less) just the numbers returned by the YYLEX function. Since we decided
1319     * though to use a convenient solution without a separate lexer, and all its
1320     * caveats, all numbers by the yylex() function here are just the ASCII
1321     * numbers of the individual characters received. Based on that however, one
1322     * single character is not what one would intuitively expect of being a
1323     * "terminal symbol", because it is simply too primitive.
1324     *
1325     * So in this LSCP parser source code a "terminal symbol" rather means a
1326     * keyword like "CREATE" or "GET". In the grammal definition above, those are
1327     * however defined as grammar rules (non-terminals in Bison's terms). So this
1328     * function decides like this: if the given grammar rule just contains
1329     * individual characters on the right side of its grammar rule, then it is a
1330     * "terminal symbol" in *our* terms.
1331     *
1332     * @param rule - Bison grammar rule number
1333     * @param stack - reflecting current Bison parser state
1334     */
1335    inline static bool _isRuleTerminalSymbol(int rule, const std::vector<YYTYPE_INT16>& stack) {
1336        int nrhs = yyr2[rule];
1337        for (int i = 0; i < nrhs; ++i)
1338            if (yystos[*(stack.end() - nrhs + i)] >= YYNTOKENS) return false;
1339        return true;
1340    }
1341    
1342    /**
1343     * Must ONLY be called just before a so called "reduce" parser action: Returns
1344     * additional informations to the given grammar rule that is about to be
1345     * "reduced".
1346     *
1347     * @param rule - Bison grammar rule number
1348     * @param stack - reflecting current Bison parser state
1349     * @param nextExpectedChars - must already be filled with the characters
1350     *                            expected to be coming next
1351     */
1352    inline static BisonSymbolInfo _symbolInfoForRule(int rule, const std::vector<YYTYPE_INT16>& stack, const String& nextExpectedChars) {
1353        BisonSymbolInfo info;
1354        info.isTerminalSymbol = _isRuleTerminalSymbol(rule, stack);
1355        if (info.isTerminalSymbol) info.nextExpectedChars  = nextExpectedChars;
1356        return info;
1357    }
1358    
1359    #else // Bison 2.x or older ...
1360    
1361    //TODO: The Bison 2.x code below can probably soon just be deleted. Most Bisonx 2.x versions should be able to compile successfully with the Bison 3.x code above as well (just requires the existence of table yystos[] in the auto generated lscpparser.cpp).
1362    
1363    /**
1364     * Returns true if the given grammar @a rule is a terminal symbol (in *our*
1365     * terms).
1366     *
1367     * Please note that the term "terminal symbol" is a bit confusingly used in
1368     * this source code here around. In Bison's terms, "terminal symbols" are (more
1369     * or less) just the numbers returned by the YYLEX function. Since we decided
1370     * though to use a convenient solution without a separate lexer, and all its
1371     * caveats, all numbers by the yylex() function here are just the ASCII
1372     * numbers of the individual characters received. Based on that however, one
1373     * single character is not what one would intuitively expect of being a
1374     * "terminal symbol", because it is simply too primitive.
1375     *
1376     * So in this LSCP parser source code a "terminal symbol" rather means a
1377     * keyword like "CREATE" or "GET". In the grammal definition above, those are
1378     * however defined as grammar rules (non-terminals in Bison's terms). So this
1379     * function decides like this: if the given grammar rule just contains
1380     * individual characters on the right side of its grammar rule, then it is a
1381     * "terminal symbol" in *our* terms.
1382     *
1383     * @param rule - Bison grammar rule number
1384     */
1385    inline static bool _isRuleTerminalSymbol(int rule) {
1386        for (int i = yyprhs[rule]; yyrhs[i] != -1; ++i)
1387            if (yyrhs[i] >= YYNTOKENS) return false;
1388        return true;
1389    }
1390    
1391    /**
1392     * Returns additional informations to the given grammar @a rule.
1393     *
1394     * @param rule - grammar rule index to retrieve informations about
1395     * @param nextExpectedChars - must already be filled with the characters
1396     *                            expected to be coming next
1397     */
1398    inline static BisonSymbolInfo _symbolInfoForRule(int rule, const String& nextExpectedChars) {
1399        BisonSymbolInfo info;
1400        info.isTerminalSymbol = _isRuleTerminalSymbol(rule);
1401        if (info.isTerminalSymbol) info.nextExpectedChars  = nextExpectedChars;
1402        return info;
1403    }
1404    
1405    #endif // HAVE_BISON_MAJ >= 3
1406    
1407    /**
1408     * Returns the human readable name of the given @a token.
1409     */
1410    inline static String _tokenName(int token) {
1411        String s = yytname[token];
1412        // remove leading and trailing apostrophes that Bison usually adds to
1413        // ASCII characters used directly in grammar rules
1414        if (s.empty()) return s;
1415        if (s[0] == '\'') s.erase(0, 1);
1416        if (s.empty()) return s;
1417        if (s[s.size() - 1] == '\'') s.erase(s.size() - 1);
1418        return s;
1419    }
1420    
1421    /**
1422     * Assumes the given @a token is exactly one character and returns that
1423     * character. This must be changed in future, i.e. in case Unicode characters
1424     * will be introduced in the LSCP grammar one day.
1425     */
1426    inline static char _tokenChar(int token) {
1427        String s = _tokenName(token);
1428        if (s == "\\n") return '\n';
1429        if (s == "\\r") return '\r';
1430        return _tokenName(token)[0];
1431    }
1432    
1433    /**
1434     * Implements Bison's so called "reduce" action, according to Bison's LALR(1)
1435     * parser algorithm.
1436     */
1437    inline static int _yyReduce(std::vector<YYTYPE_INT16>& stack, const int& rule) {
1438        if (stack.empty()) throw 1; // severe error
1439        const int len = yyr2[rule];
1440        stack.resize(stack.size() - len);
1441        YYTYPE_INT16 newState = yypgoto[yyr1[rule] - YYNTOKENS] + stack.back();
1442        if (0 <= newState && newState <= YYLAST && yycheck[newState] == stack.back())
1443            newState = yytable[newState];
1444        else
1445            newState = yydefgoto[yyr1[rule] - YYNTOKENS];
1446        stack.push_back(newState);
1447        return newState;
1448    }
1449    
1450    /**
1451     * Implements Bison's so called "default reduce" action, according to Bison's
1452     * LALR(1) parser algorithm.
1453     */
1454    inline static int _yyDefaultReduce(std::vector<YYTYPE_INT16>& stack) {
1455        if (stack.empty()) throw 2; // severe error
1456        int rule = yydefact[stack.back()];
1457        if (rule <= 0 || rule >= YYNRULES) throw 3; // no rule, something is wrong
1458        return _yyReduce(stack, rule);
1459    }
1460    
1461    static bool yyValid(std::vector<YYTYPE_INT16>& stack, char ch);
1462    
1463    /**
1464     * A set of parser symbol stacks. This type is used for the recursive algorithms
1465     * in a) yyAutoComplete() and b) walkAndFillExpectedSymbols() for detecting
1466     * endless recursions.
1467     *
1468     * This unique container is used to keep track of all previous parser states
1469     * (stacks), for detecting a parser symbol stack that has already been
1470     * encountered before. Because if yyAutoComplete() or
1471     * walkAndFillExpectedSymbols() reach the exactly same parser symbol stack
1472     * again, that means there is an endless recursion in that part of the grammar
1473     * tree branch and shall not be evaluated any further, since it would end up in
1474     * an endless loop of the algorithm otherwise.
1475     *
1476     * This solution consumes a lot of memory, but unfortunately there is no other
1477     * easy way to solve it. With our grammar and today's usual memory heap size &
1478     * memory stack size in modern devices, it should be fine though.
1479     */
1480    typedef std::set< std::vector<YYTYPE_INT16> > YYStackHistory;
1481    
1482    /*
1483     * YYTERROR macro was removed in Bison 3.6.0, we need it in function below.
1484     */
1485    #ifndef YYTERROR
1486    # define YYTERROR YYSYMBOL_YYerror
1487    #endif
1488    
1489    #define DEBUG_BISON_SYNTAX_ERROR_WALKER 0
1490    
1491    /**
1492     * Tries to find the next expected grammar symbols according to the given
1493     * precise parse position & state represented by @a stack, according to Bison's
1494     * LALR(1) parser algorithm.
1495     *
1496     * This function is given a Bison parser symbol stack, reflecting the parser's
1497     * entire state at a certain point, i.e. when a syntax error occured. This
1498     * function will then walk ahead the potential parse tree starting from the
1499     * current head of the given symbol stack. This function will call itself
1500     * recursively to scan the individual parse tree branches. As soon as it hits
1501     * on the next non-terminal grammar symbol in one parse tree branch, it adds the
1502     * found non-terminal symbol to @a expectedSymbols and aborts scanning the
1503     * respective tree branch further. If any local parser state is reached a second
1504     * time, the respective parse tree is aborted to avoid any endless recursion.
1505     *
1506     * @param stack - current Bison (yacc) symbol stack to be examined
1507     * @param expectedSymbols - will be filled with next expected grammar symbols
1508     * @param nextExpectedChars - just for internal purpose, due to the recursive
1509     *                            implementation of this function, do supply an
1510     *                            empty string for this argument
1511     * @param history - only for internal purpose, keeps a history of all previous
1512     *                  parser symbol stacks (just for avoiding endless recursion in
1513     *                  this recursive algorithm), do supply an empty history
1514     * @param depth - just for internal debugging purposes, do not supply it
1515   */   */
1516  void yyerror(const char* s) {  static void walkAndFillExpectedSymbols(
1517        std::vector<YYTYPE_INT16>& stack,
1518        std::map<String,BisonSymbolInfo>& expectedSymbols,
1519        String& nextExpectedChars, YYStackHistory& history, int depth = 0)
1520    {
1521    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1522        printf("\n");
1523        for (int i = 0; i < depth; ++i) printf("\t");
1524        printf("Symbol stack:");
1525        for (int i = 0; i < stack.size(); ++i) {
1526            printf(" %d", stack[i]);
1527        }
1528        printf("\n");
1529    #endif
1530        startLabel:
1531    
1532        // detect endless recursion
1533        if (history.count(stack)) return;
1534        history.insert(stack);
1535    
1536        if (stack.empty()) {
1537    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1538            for (int i = 0; i < depth; ++i) printf("\t");
1539            printf("(EMPTY STACK)\n");
1540    #endif
1541            return;
1542        }
1543    
1544        int state = stack[stack.size() - 1];
1545        int n = yypact[state];
1546        if (n == YYPACT_NINF) { // default reduction required ...
1547            // get default reduction rule for this state
1548            n = yydefact[state];
1549            if (n <= 0 || n >= YYNRULES) {
1550    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1551                for (int i = 0; i < depth; ++i) printf("\t");
1552                printf("(EMPTY RULE)\n");
1553    #endif
1554                return; // no rule, something is wrong
1555            }
1556    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1557            for (int i = 0; i < depth; ++i) printf("\t");
1558            printf("(default reduction)\n");
1559    #endif
1560            #if HAVE_BISON_MAJ >= 3
1561            if (!nextExpectedChars.empty() || !_isRuleTerminalSymbol(n, stack)) {
1562            #else
1563            if (!nextExpectedChars.empty() || !_isRuleTerminalSymbol(n)) {
1564            #endif
1565                // Return the new resolved expected symbol (left-hand symbol of grammar
1566                // rule), then we're done in this state. (If the same symbol can be
1567                // matched on different ways, then it is non-terminal symbol.)
1568                bool ambigious =
1569                    expectedSymbols.count(yytname[yyr1[n]]) &&
1570                    expectedSymbols[yytname[yyr1[n]]].nextExpectedChars != nextExpectedChars;
1571                #if HAVE_BISON_MAJ >= 3
1572                expectedSymbols[yytname[yyr1[n]]] = _symbolInfoForRule(n, stack, nextExpectedChars);
1573                #else
1574                expectedSymbols[yytname[yyr1[n]]] = _symbolInfoForRule(n, nextExpectedChars);
1575                #endif
1576                if (ambigious)
1577                    expectedSymbols[yytname[yyr1[n]]].isTerminalSymbol = false;
1578    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1579                for (int i = 0; i < depth; ++i) printf("\t");
1580                printf("(empty expectedChars. sym = %s)\n", yytname[yyr1[n]]);
1581    #endif
1582                return;
1583            }
1584            _yyReduce(stack, n);
1585            goto startLabel;
1586        }
1587        if (!(YYPACT_NINF < n && n <= YYLAST)) {
1588    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1589            for (int i = 0; i < depth; ++i) printf("\t");
1590            printf("(invalid action B)\n");
1591    #endif
1592            return;
1593        }
1594    
1595        // Check for duplicate states, if duplicates exist return
1596        // (this check is necessary since the introduction of the yyValid() call
1597        // below, which does not care about duplicates).
1598        for (int i = 0; i < stack.size(); ++i)
1599            for (int k = i + 1; k < stack.size(); ++k)
1600                if (stack[i] == stack[k])
1601                    return;
1602    
1603    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1604        for (int i = 0; i < depth; ++i) printf("\t");
1605        printf("Expected tokens:");
1606    #endif
1607        int begin = n < 0 ? -n : 0;
1608        //int checklim = YYLAST - n + 1;
1609        int end = YYNTOKENS;//checklim < YYNTOKENS ? checklim : YYNTOKENS;
1610        int rule, action, stackSize, nextExpectedCharsLen;
1611        for (int token = begin; token < end; ++token) {
1612            if (token <= YYTERROR) continue;
1613            if (yytname[token] == String("$undefined")) continue;
1614            if (yytname[token] == String("EXT_ASCII_CHAR")) continue;
1615            //if (yycheck[n + token] != token) goto default_reduction;
1616            if (yycheck[n + token] != token) { // default reduction suggested ...
1617                // If we are here, it means the current token in the loop would not
1618                // cause a "shift", however we don't already know whether this token
1619                // is valid or not. Because there might be several reductions
1620                // involved until one can determine whether the token causes an
1621                // error or is valid. So we use this heavy check instead:
1622                std::vector<YYTYPE_INT16> stackCopy = stack; // copy required, since reduction will take place
1623                if (!yyValid(stackCopy, _tokenChar(token))) continue; // invalid token
1624    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1625                printf(" ETdr(%s)", yytname[token]);
1626    #endif
1627                // the token is valid, "stackCopy" has been reduced accordingly
1628                // and now do recurse ...
1629                nextExpectedChars += _tokenName(token);
1630                nextExpectedCharsLen = (int)nextExpectedChars.size();
1631                walkAndFillExpectedSymbols( //FIXME: could cause stack overflow (should be a loop instead), is probably fine with our current grammar though
1632                    stackCopy, expectedSymbols, nextExpectedChars, history, depth + 1
1633                );
1634                nextExpectedChars.resize(nextExpectedCharsLen); // restore 'nextExpectedChars'
1635                continue;
1636            }
1637    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1638            printf(" ET(%s)", yytname[token]);
1639    #endif
1640    
1641            action = yytable[n + token];
1642            if (action == 0 || action == YYTABLE_NINF) {
1643    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1644                printf(" (invalid action A) "); fflush(stdout);
1645    #endif
1646                continue; // error, ignore
1647            }
1648            if (action < 0) { // reduction with rule -action required ...
1649    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1650                printf(" (reduction) "); fflush(stdout);
1651    #endif
1652                rule = -action;
1653                goto reduce;
1654            }
1655            if (action == YYFINAL) {
1656    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1657                printf(" (ACCEPT) "); fflush(stdout);
1658    #endif
1659                continue; // "accept" state, we don't care about it here
1660            }
1661    
1662            // "shift" required ...
1663    
1664            if (std::find(stack.begin(), stack.end(), action) != stack.end()) {
1665    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1666                printf(" (duplicate state %d) ", action); fflush(stdout);
1667    #endif
1668                continue; // duplicate state, ignore it to avoid endless recursions
1669            }
1670    
1671            // "shift" / push the new state on the symbol stack and call this
1672            // function recursively, and restore the stack after the recurse return
1673            stackSize = (int)stack.size();
1674            nextExpectedCharsLen = (int)nextExpectedChars.size();
1675            stack.push_back(action);
1676            nextExpectedChars += _tokenName(token);
1677            walkAndFillExpectedSymbols( //FIXME: could cause stack overflow (should be a loop instead), is probably fine with our current grammar though
1678                stack, expectedSymbols, nextExpectedChars, history, depth + 1
1679            );
1680            stack.resize(stackSize); // restore stack
1681            nextExpectedChars.resize(nextExpectedCharsLen); // restore 'nextExpectedChars'
1682            continue;
1683    
1684        //default_reduction: // resolve default reduction for this state
1685        //    printf(" (default red.) "); fflush(stdout);
1686        //    rule = yydefact[state];
1687    
1688        reduce: // "reduce" required
1689    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1690            printf(" (reduce by %d) ", rule); fflush(stdout);
1691    #endif
1692            if (rule == 0 || rule >= YYNRULES) {
1693    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1694                printf(" (invalid rule) "); fflush(stdout);
1695    #endif
1696                continue; // invalid rule, something is wrong
1697            }
1698            // Store the left-hand symbol of the grammar rule. (If the same symbol
1699            // can be matched on different ways, then it is non-terminal symbol.)
1700            bool ambigious =
1701                expectedSymbols.count(yytname[yyr1[rule]]) &&
1702                expectedSymbols[yytname[yyr1[rule]]].nextExpectedChars != nextExpectedChars;
1703            #if HAVE_BISON_MAJ >= 3
1704            expectedSymbols[yytname[yyr1[rule]]] = _symbolInfoForRule(rule, stack, nextExpectedChars);
1705            #else
1706            expectedSymbols[yytname[yyr1[rule]]] = _symbolInfoForRule(rule, nextExpectedChars);
1707            #endif
1708            if (ambigious)
1709                expectedSymbols[yytname[yyr1[n]]].isTerminalSymbol = false;
1710    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1711            printf(" (SYM %s) ", yytname[yyr1[rule]]); fflush(stdout);
1712    #endif
1713        }
1714    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1715        printf("\n");
1716    #endif
1717    }
1718    
1719    /**
1720     * Just a convenience wrapper on top of the actual walkAndFillExpectedSymbols()
1721     * implementation above, which can be called with less parameters than the
1722     * implementing function above actually requires.
1723     */
1724    static void walkAndFillExpectedSymbols(
1725        std::vector<YYTYPE_INT16>& stack,
1726        std::map<String,BisonSymbolInfo>& expectedSymbols)
1727    {
1728        String nextExpectedChars;
1729        YYStackHistory history;
1730    
1731        walkAndFillExpectedSymbols(
1732            stack, expectedSymbols, nextExpectedChars, history
1733        );
1734    }
1735    
1736    #define DEBUG_PUSH_PARSE 0
1737    
1738    /**
1739     * Implements parsing exactly one character (given by @a ch), continueing at the
1740     * parser position reflected by @a stack. The @a stack will hold the new parser
1741     * state after this call.
1742     *
1743     * This function is implemented according to Bison's LALR(1) parser algorithm.
1744     */
1745    static bool yyPushParse(std::vector<YYTYPE_INT16>& stack, char ch) {
1746        startLabel:
1747    
1748    #if DEBUG_PUSH_PARSE
1749        //printf("\n");
1750        //for (int i = 0; i < depth; ++i) printf("\t");
1751        printf("Symbol stack:");
1752        for (int i = 0; i < stack.size(); ++i) {
1753            printf(" %d", stack[i]);
1754        }
1755        printf(" char='%c'(%d)\n", ch, (int)ch);
1756    #endif
1757    
1758        if (stack.empty()) return false;
1759    
1760        int state = stack.back();
1761        int n = yypact[state];
1762        if (n == YYPACT_NINF) { // default reduction required ...
1763    #if DEBUG_PUSH_PARSE
1764            printf("(def reduce 1)\n");
1765    #endif
1766            state = _yyDefaultReduce(stack);
1767            goto startLabel;
1768        }
1769        if (!(YYPACT_NINF < n && n <= YYLAST)) return false;
1770    
1771        YYTYPE_INT16 token = (ch == YYEOF) ? YYEOF : yytranslate[ch];
1772        n += token;
1773        if (n < 0 || YYLAST < n || yycheck[n] != token) {
1774    #if DEBUG_PUSH_PARSE
1775            printf("(def reduce 2) n=%d token=%d\n", n, token);
1776    #endif
1777            state = _yyDefaultReduce(stack);
1778            goto startLabel;
1779        }
1780        int action = yytable[n]; // yytable[yypact[state] + token]
1781        if (action == 0 || action == YYTABLE_NINF) throw 4;
1782        if (action < 0) {
1783    #if DEBUG_PUSH_PARSE
1784            printf("(reduce)\n");
1785    #endif
1786            int rule = -action;
1787            state = _yyReduce(stack, rule);
1788            goto startLabel;
1789        }
1790        if (action == YYFINAL) return true; // final state reached
1791    
1792    #if DEBUG_PUSH_PARSE
1793        printf("(push)\n");
1794    #endif
1795        // push new state
1796        state = action;
1797        stack.push_back(state);
1798        return true;
1799    }
1800    
1801    /**
1802     * Returns true if parsing ahead with given character @a ch is syntactically
1803     * valid according to the LSCP grammar, it returns false if it would create a
1804     * parse error.
1805     *
1806     * The @a stack will reflect the new parser state after this call.
1807     *
1808     * This is just a wrapper ontop of yyPushParse() which converts parser
1809     * exceptions thrown by yyPushParse() into @c false return value.
1810     */
1811    static bool yyValid(std::vector<YYTYPE_INT16>& stack, char ch) {
1812        try {
1813            return yyPushParse(stack, ch);
1814        } catch (int i) {
1815    #if DEBUG_PUSH_PARSE
1816            printf("exception %d\n", i);
1817    #endif
1818            return false;
1819        } catch (...) {
1820            return false;
1821        }
1822    }
1823    
1824    /**
1825     * Returns the amount of correct characters of given @a line from the left,
1826     * according to the LSCP grammar.
1827     *
1828     * @param stack - a Bison symbol stack to work with
1829     * @param line  - the input line to check
1830     * @param bAutoCorrect - if true: try to correct obvious, trivial syntax errors
1831     */
1832    static int yyValidCharacters(std::vector<YYTYPE_INT16>& stack, String& line, bool bAutoCorrect) {
1833        int i;
1834        for (i = 0; i < line.size(); ++i) {
1835            // since we might check the same parser state twice against the current
1836            // char here below, and since the symbol stack might be altered
1837            // (i.e. shifted or reduced) on syntax errors, we have to backup the
1838            // current symbol stack and restore it on syntax errors below
1839            std::vector<YYTYPE_INT16> stackCopy = stack;
1840            if (yyValid(stackCopy, line[i])) {
1841                stack = stackCopy;
1842                continue;
1843            }
1844            if (bAutoCorrect) {
1845                // try trivial corrections, i.e. upper case character instead of
1846                // lower case, subline instead of space and vice versa
1847                char c;
1848                if      (line[i] == ' ') c = '_';
1849                else if (line[i] == '_') c = ' ';
1850                else if (isLowerCaseAlphaChar(line[i]))
1851                    c = alphaCharToUpperCase(line[i]);
1852                else return i;
1853                if (yyValid(stack, c)) {
1854                    line[i] = c;
1855                    continue;
1856                }
1857            }
1858            return i;
1859        }
1860        return i;
1861    }
1862    
1863    /**
1864     * Should only be called on syntax errors: returns a set of non-terminal
1865     * symbols expected to appear now/next, just at the point where the syntax
1866     * error appeared.
1867     *
1868     * @returns names of the non-terminal symbols expected at this parse position
1869     */
1870    static std::set<String> yyExpectedSymbols() {
1871        std::map<String,BisonSymbolInfo> expectedSymbols;
1872      yyparse_param_t* param = GetCurrentYaccSession();      yyparse_param_t* param = GetCurrentYaccSession();
1873      String msg = s      YYTYPE_INT16* ss = (*param->ppStackBottom);
1874          + (" (line:"   + ToString(param->iLine+1))      YYTYPE_INT16* sp = (*param->ppStackTop);
1875          + ( ",column:" + ToString(param->iColumn))      int iStackSize   = int(sp - ss + 1);
1876          + ")";      // copy and wrap parser's symbol stack into a convenient STL container
1877      dmsg(2,("LSCPParser: %s\n", msg.c_str()));      std::vector<YYTYPE_INT16> stack;
1878      sLastError = msg;      for (int i = 0; i < iStackSize; ++i) {
1879            stack.push_back(ss[i]);
1880        }
1881        // do the actual parser work
1882        walkAndFillExpectedSymbols(stack, expectedSymbols);
1883    
1884        // convert expectedSymbols to the result set
1885        std::set<String> result;
1886        for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin();
1887             it != expectedSymbols.end(); ++it) result.insert(it->first);
1888        return result;
1889    }
1890    
1891    #define DEBUG_YY_AUTO_COMPLETE 0
1892    
1893    /**
1894     * Generates and returns an auto completion string for the current parser
1895     * state given by @a stack. That means, this function will return the longest
1896     * sequence of characters that is uniqueley expected to be sent next by the LSCP
1897     * client. Or in other words, if the LSCP client would send any other
1898     * character(s) than returned here, it would result in a syntax error.
1899     *
1900     * This function takes a Bison symbol @a stack as argument, reflecting the
1901     * current Bison parser state, and evaluates the individual grammar tree
1902     * branches starting from that particular position. It walks along the grammar
1903     * tree as long as there is only one possible tree branch and assembles a string
1904     * of input characters that would lead to that walk through the grammar tree. As
1905     * soon as a position in the grammar tree is reached where there are multiple
1906     * possible tree branches, this algorithm will stop, since the user could have
1907     * multiple possible valid characters he could type at that point, thus auto
1908     * completion would no longer be unique at that point.
1909     *
1910     * Regarding @a history argument: read the description on YYStackHistory for the
1911     * purpose behind this argument.
1912     *
1913     * @param stack - current Bison (yacc) symbol stack to create auto completion for
1914     * @param history - only for internal purpose, keeps a history of all previous
1915     *                  parser symbol stacks (just for avoiding endless recursion in
1916     *                  this auto completion algorithm), do supply an empty history
1917     * @param depth - just for internal debugging purposes, do not supply anything
1918     * @returns auto completion for current, given parser state
1919     */
1920    static String yyAutoComplete(std::vector<YYTYPE_INT16>& stack, YYStackHistory& history, int depth = 0) {
1921        std::map<String,BisonSymbolInfo> expectedSymbols;
1922        walkAndFillExpectedSymbols(stack, expectedSymbols);
1923        if (expectedSymbols.size() == 1) {
1924            String name          = expectedSymbols.begin()->first;
1925            BisonSymbolInfo info = expectedSymbols.begin()->second;
1926    #if DEBUG_YY_AUTO_COMPLETE
1927            for (int q = 0; q < depth; ++q) printf("  ");
1928            printf("(%d) Suggested Sub Completion (sz=%d): type=%s %s -> '%s'\n", depth, expectedSymbols.size(), (info.isTerminalSymbol) ? "T" : "NT", name.c_str(), info.nextExpectedChars.c_str());
1929    #endif
1930            if (info.nextExpectedChars.empty() || !info.isTerminalSymbol) return "";
1931            // parse forward with the suggested auto completion
1932            std::vector<YYTYPE_INT16> stackCopy = stack;
1933            yyValidCharacters(stackCopy, info.nextExpectedChars, false);
1934            // detect endless recursion
1935            if (history.count(stackCopy)) return "";
1936            history.insert(stackCopy);
1937            // recurse and return the expanded auto completion with maximum length
1938            return info.nextExpectedChars + yyAutoComplete(stackCopy, history, depth + 1);
1939        } else if (expectedSymbols.size() == 0) {
1940    #if DEBUG_YY_AUTO_COMPLETE
1941            for (int q = 0; q < depth; ++q) printf("  ");
1942            printf("(%d) No sub suggestion.\n", depth);
1943    #endif
1944            return "";
1945        } else if (expectedSymbols.size() > 1) {
1946    #if DEBUG_YY_AUTO_COMPLETE
1947            for (int q = 0; q < depth; ++q) printf("  ");
1948            printf("(%d) Multiple sub possibilities (before expansion):", depth);
1949            for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin();
1950                 it != expectedSymbols.end(); ++it)
1951            {
1952                printf(" %s (..%s)", it->first.c_str(), it->second.nextExpectedChars.c_str());
1953            }
1954            printf("\n");
1955    #endif
1956            // check if any of the possibilites is a non-terminal symbol, if so, we
1957            // have no way for auto completion at this point
1958            for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin();
1959                 it != expectedSymbols.end(); ++it)
1960            {
1961                if (!it->second.isTerminalSymbol) {
1962    #if DEBUG_YY_AUTO_COMPLETE
1963                    for (int q = 0; q < depth; ++q) printf("  ");
1964                    printf("(%d) Non-terminal exists. Stop.", depth);
1965    #endif
1966                    return "";
1967                }
1968            }
1969    #if 0 // commented out for now, since practically irrelevant and VERY slow ...
1970            // all possibilities are terminal symbols, so expand all possiblities to
1971            // maximum length with a recursive call for each possibility
1972            for (std::map<String,BisonSymbolInfo>::iterator it = expectedSymbols.begin();
1973                 it != expectedSymbols.end(); ++it)
1974            {
1975                if (it->second.nextExpectedChars.empty() || !it->second.isTerminalSymbol) continue;
1976                // parse forward with this particular suggested auto completion
1977                std::vector<YYTYPE_INT16> stackCopy = stack;
1978                yyValidCharacters(stackCopy, it->second.nextExpectedChars, false);
1979                // detect endless recursion
1980                if (history.count(stackCopy)) continue;
1981                history.insert(stackCopy);
1982                // recurse and return the total possible auto completion for this
1983                // grammar tree branch
1984                it->second.nextExpectedChars += yyAutoComplete(stackCopy, history, depth + 1);
1985            }
1986    #endif
1987            // try to find the longest common string all possibilities start with
1988            // (from the left)
1989            String sCommon;
1990            for (int i = 0; true; ++i) {
1991                char c = '\0';
1992                for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin();
1993                     it != expectedSymbols.end(); ++it)
1994                {
1995                    if (i >= it->second.nextExpectedChars.size())
1996                        goto commonSearchEndLabel;
1997                    if (it == expectedSymbols.begin())
1998                        c = it->second.nextExpectedChars[i];
1999                    if (c != it->second.nextExpectedChars[i])
2000                        goto commonSearchEndLabel;
2001                    if (it == --expectedSymbols.end())
2002                        sCommon += c;
2003                }
2004            }
2005            commonSearchEndLabel:
2006    #if DEBUG_YY_AUTO_COMPLETE
2007            for (int q = 0; q < depth; ++q) printf("  ");
2008            printf("(%d) Multiple sub possibilities (after expansion):", depth);
2009            for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin();
2010                 it != expectedSymbols.end(); ++it)
2011            {
2012                printf(" %s (..%s)", it->first.c_str(), it->second.nextExpectedChars.c_str());
2013            }
2014            printf("\n");
2015            for (int q = 0; q < depth; ++q) printf("  ");
2016            printf("(%d) Common sub possibility: '%s'\n", depth, sCommon.c_str());
2017    #endif
2018            return sCommon;
2019        }
2020        return ""; // just pro forma, should never happen though
2021    }
2022    
2023    /**
2024     * Just a convenience wrapper on top of the actual yyAutoComplete()
2025     * implementation. See its description above for details.
2026     */
2027    static String yyAutoComplete(std::vector<YYTYPE_INT16>& stack) {
2028        YYStackHistory history;
2029        return yyAutoComplete(stack, history);
2030  }  }
2031    
2032  namespace LinuxSampler {  namespace LinuxSampler {
2033    
2034    #define DEBUG_SHELL_INTERACTION 0
2035    
2036    /**
2037     * If LSCP shell mode is enabled for the respective LSCP client connection, then
2038     * this function is called on every new byte received from that client. It will
2039     * check the current total input line and reply to the LSCP shell with a
2040     * specially crafted string, which allows the shell to provide colored syntax
2041     * highlighting and potential auto completion in the shell.
2042     *
2043     * It also performs auto correction of obvious & trivial syntax mistakes if
2044     * requested.
2045     *
2046     * The return value of this function will be sent to the client. It contains one
2047     * line specially formatted for the LSCP shell application, which can easily be
2048     * processed by the client/shell for extracting its necessary informations like
2049     * which part of the current command line is syntactically correct, which part
2050     * is incorrect, what could be auto completed right now, etc. So all the heavy
2051     * grammar evaluation tasks are peformed by the LSCP server for the LSCP shell
2052     * application (which is desgined as a thin client), so the LSCP shell
2053     * application will only have to show the results of the LSCP server's
2054     * evaluation to the user on the screen.
2055     *
2056     * @param line - the current command line to be evaluated by LSCP parser
2057     * @param param = reentrant parser session parameters
2058     * @param possibilities - whether all possibilities shall be shown
2059     * @returns LSCP shell response line to be returned to the client
2060     */
2061    String lscpParserProcessShellInteraction(String& line, yyparse_param_t* param, bool possibilities) {
2062        // first, determine how many characters (starting from the left) of the
2063        // given input line are already syntactically correct
2064        std::vector<YYTYPE_INT16> stack;
2065        stack.push_back(0); // every Bison symbol stack starts with state zero
2066        String l = line + '\n'; // '\n' to pretend ENTER as if the line was now complete
2067        int n = yyValidCharacters(stack, l, param->bShellAutoCorrect);
2068    
2069        // if auto correction is enabled, apply the auto corrected string to
2070        // intput/output string 'line'
2071        if (param->bShellAutoCorrect) {
2072            int nMin = int( (n < line.length()) ? n : line.length() );
2073            line.replace(0, nMin, l.substr(0, nMin));
2074        }
2075    
2076        ssize_t cursorPos = line.size() + param->iCursorOffset;
2077        if (cursorPos < 0) cursorPos = 0;
2078    
2079        // generate an info string that will be sent to the LSCP shell for letting
2080        // it know which part is correct, which one is wrong, where is the cursor, etc.
2081        String result = line;
2082        result.insert(n <= result.length() ? n : result.length(), LSCP_SHK_GOOD_FRONT);
2083        result.insert(cursorPos <= n ? cursorPos : cursorPos + String(LSCP_SHK_GOOD_FRONT).length(), LSCP_SHK_CURSOR);
2084        int code = (n > line.length()) ? LSCP_SHU_COMPLETE : (n < line.length()) ?
2085                   LSCP_SHU_SYNTAX_ERR : LSCP_SHU_INCOMPLETE;
2086        result = "SHU:" + ToString(code) + ":" + result;
2087        //if (n > line.length()) result += " [OK]";
2088    
2089        // get a clean parser stack to the last valid parse position
2090        // (due to the appended '\n' character above, and on syntax errors, the
2091        // symbol stack might be in undesired, i.e. reduced state)
2092        stack.clear();
2093        stack.push_back(0); // every Bison symbol stack starts with state zero
2094        l = line.substr(0, n);
2095        if (!l.empty()) yyValidCharacters(stack, l, param->bShellAutoCorrect);
2096    
2097        // generate auto completion suggestion (based on the current parser stack)
2098        std::vector<YYTYPE_INT16> stackCopy = stack; // make a copy, since yyAutoComplete() might alter the stack
2099        String sSuggestion = yyAutoComplete(stackCopy);
2100        if (!sSuggestion.empty()) result += LSCP_SHK_SUGGEST_BACK + sSuggestion;
2101    
2102        if (possibilities) {
2103            // append all possible terminals and non-terminals according to
2104            // current parser state
2105            std::map<String,BisonSymbolInfo> expectedSymbols;
2106            walkAndFillExpectedSymbols(stack, expectedSymbols);
2107    
2108            // pretend to LSCP shell that the following terminal symbols were
2109            // non-terminal symbols (since they are not human visible for auto
2110            // completion on the shell's screen)
2111            std::set<String> specialNonTerminals;
2112            specialNonTerminals.insert("SP");
2113            specialNonTerminals.insert("CR");
2114            specialNonTerminals.insert("LF");
2115    
2116            String sPossibilities;
2117            int iNonTerminals = 0;
2118            int iTerminals    = 0;
2119            for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin();
2120                 it != expectedSymbols.end(); ++it)
2121            {
2122                if (!sPossibilities.empty()) sPossibilities += " | ";
2123                if (it->second.isTerminalSymbol && !specialNonTerminals.count(it->first)) {
2124                    sPossibilities += it->first;
2125                    iTerminals++;
2126                } else {
2127                    sPossibilities += "<" + it->first + ">";
2128                    iNonTerminals++;
2129                }
2130            }
2131            if (!sPossibilities.empty() && (iNonTerminals || iTerminals > 1)) {
2132                result += LSCP_SHK_POSSIBILITIES_BACK + sPossibilities;
2133            }
2134        }
2135    
2136    #if DEBUG_SHELL_INTERACTION
2137        printf("%s\n", result.c_str());
2138    #endif
2139    
2140        return result;
2141    }
2142    
2143  /**  /**
2144   * Clears input buffer.   * Clears input buffer.
2145   */   */
# Line 1213  void restart(yyparse_param_t* pparam, in Line 2147  void restart(yyparse_param_t* pparam, in
2147      bytes = 0;      bytes = 0;
2148      ptr   = 0;      ptr   = 0;
2149      sLastError = "";      sLastError = "";
2150        sParsed = "";
2151  }  }
2152    
2153  }  }

Legend:
Removed from v.2461  
changed lines
  Added in v.3787

  ViewVC Help
Powered by ViewVC