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

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

  ViewVC Help
Powered by ViewVC