/[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 2522 by schoenebeck, Sat Feb 8 00:49:30 2014 UTC revision 2523 by schoenebeck, Sun Feb 23 19:10:36 2014 UTC
# Line 196  void yyerror(void* x, const char* s) { Line 196  void yyerror(void* x, const char* s) {
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 218  void yyerror(void* x, const char* s) { Line 218  void yyerror(void* x, const char* s) {
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 1403  inline static String _tokenName(int toke Line 1406  inline static String _tokenName(int toke
1406      return s;      return s;
1407  }  }
1408    
1409    /**
1410     * Assumes the given @a token is exactly one character and returns that
1411     * character. This must be changed in future, i.e. in case Unicode characters
1412     * will be introduced in the LSCP grammar one day.
1413     */
1414    inline static char _tokenChar(int token) {
1415        String s = _tokenName(token);
1416        if (s == "\\n") return '\n';
1417        if (s == "\\r") return '\r';
1418        return _tokenName(token)[0];
1419    }
1420    
1421    /**
1422     * Implements Bison's so called "reduce" action, according to Bison's LALR(1)
1423     * parser algorithm.
1424     */
1425    inline static int _yyReduce(std::vector<YYTYPE_INT16>& stack, const int& rule) {
1426        if (stack.empty()) throw 1; // severe error
1427        const int len = yyr2[rule];
1428        stack.resize(stack.size() - len);
1429        YYTYPE_INT16 newState = yypgoto[yyr1[rule] - YYNTOKENS] + stack.back();
1430        if (0 <= newState && newState <= YYLAST && yycheck[newState] == stack.back())
1431            newState = yytable[newState];
1432        else
1433            newState = yydefgoto[yyr1[rule] - YYNTOKENS];
1434        stack.push_back(newState);
1435        return newState;
1436    }
1437    
1438    /**
1439     * Implements Bison's so called "default reduce" action, according to Bison's
1440     * LALR(1) parser algorithm.
1441     */
1442    inline static int _yyDefaultReduce(std::vector<YYTYPE_INT16>& stack) {
1443        if (stack.empty()) throw 2; // severe error
1444        int rule = yydefact[stack.back()];
1445        if (rule <= 0 || rule >= YYNRULES) throw 3; // no rule, something is wrong
1446        return _yyReduce(stack, rule);
1447    }
1448    
1449    static bool yyValid(std::vector<YYTYPE_INT16>& stack, char ch);
1450    
1451  #define DEBUG_BISON_SYNTAX_ERROR_WALKER 0  #define DEBUG_BISON_SYNTAX_ERROR_WALKER 0
1452    
1453  /**  /**
# Line 1441  static void walkAndFillExpectedSymbols( Line 1486  static void walkAndFillExpectedSymbols(
1486      }      }
1487      printf("\n");      printf("\n");
1488  #endif  #endif
1489        startLabel:
1490    
1491      if (stack.empty()) return;      if (stack.empty()) {
1492    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1493            for (int i = 0; i < depth; ++i) printf("\t");
1494            printf("(EMPTY STACK)\n");
1495    #endif
1496            return;
1497        }
1498    
1499      int state = stack[stack.size() - 1];      int state = stack[stack.size() - 1];
1500      int n = yypact[state];      int n = yypact[state];
1501      if (n == YYPACT_NINF) { // default reduction required ...      if (n == YYPACT_NINF) { // default reduction required ...
1502          // get default reduction rule for this state          // get default reduction rule for this state
1503          n = yydefact[state];          n = yydefact[state];
1504          if (n <= 0 || n >= YYNRULES) return; // no rule, something is wrong          if (n <= 0 || n >= YYNRULES) {
1505          // return the new resolved expected symbol (left-hand symbol of grammar  #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1506          // rule), then we're done in this state              for (int i = 0; i < depth; ++i) printf("\t");
1507          #if HAVE_BISON_MAJ >= 3              printf("(EMPTY RULE)\n");
1508          expectedSymbols[yytname[yyr1[n]]] = _symbolInfoForRule(n, stack, nextExpectedChars);  #endif
1509          #else              return; // no rule, something is wrong
1510          expectedSymbols[yytname[yyr1[n]]] = _symbolInfoForRule(n, nextExpectedChars);          }
1511          #endif  #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1512            for (int i = 0; i < depth; ++i) printf("\t");
1513            printf("(default reduction)\n");
1514    #endif
1515            if (!nextExpectedChars.empty() || !_isRuleTerminalSymbol(n, stack)) {
1516                // Return the new resolved expected symbol (left-hand symbol of grammar
1517                // rule), then we're done in this state. (If the same symbol can be
1518                // matched on different ways, then it is non-terminal symbol.)
1519                bool ambigious =
1520                    expectedSymbols.count(yytname[yyr1[n]]) &&
1521                    expectedSymbols[yytname[yyr1[n]]].nextExpectedChars != nextExpectedChars;
1522                #if HAVE_BISON_MAJ >= 3
1523                expectedSymbols[yytname[yyr1[n]]] = _symbolInfoForRule(n, stack, nextExpectedChars);
1524                #else
1525                expectedSymbols[yytname[yyr1[n]]] = _symbolInfoForRule(n, nextExpectedChars);
1526                #endif
1527                if (ambigious)
1528                    expectedSymbols[yytname[yyr1[n]]].isTerminalSymbol = false;
1529    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1530                for (int i = 0; i < depth; ++i) printf("\t");
1531                printf("(empty expectedChars. sym = %s)\n", yytname[yyr1[n]]);
1532    #endif
1533                return;
1534            }
1535            _yyReduce(stack, n);
1536            goto startLabel;
1537        }
1538        if (!(YYPACT_NINF < n && n <= YYLAST)) {
1539    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1540            for (int i = 0; i < depth; ++i) printf("\t");
1541            printf("(invalid action B)\n");
1542    #endif
1543          return;          return;
1544      }      }
1545      if (!(YYPACT_NINF < n && n <= YYLAST)) return;  
1546        // Check for duplicate states, if duplicates exist return
1547        // (this check is necessary since the introduction of the yyValid() call
1548        // below, which does not care about duplicates).
1549        for (int i = 0; i < stack.size(); ++i)
1550            for (int k = i + 1; k < stack.size(); ++k)
1551                if (stack[i] == stack[k])
1552                    return;
1553    
1554  #if DEBUG_BISON_SYNTAX_ERROR_WALKER  #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1555      for (int i = 0; i < depth; ++i) printf("\t");      for (int i = 0; i < depth; ++i) printf("\t");
1556      printf("Expected tokens:");      printf("Expected tokens:");
1557  #endif  #endif
1558      int begin = n < 0 ? -n : 0;      int begin = n < 0 ? -n : 0;
1559      int checklim = YYLAST - n + 1;      //int checklim = YYLAST - n + 1;
1560      int end = checklim < YYNTOKENS ? checklim : YYNTOKENS;      int end = YYNTOKENS;//checklim < YYNTOKENS ? checklim : YYNTOKENS;
1561      int rule, action, stackSize, nextExpectedCharsLen;      int rule, action, stackSize, nextExpectedCharsLen;
1562      for (int token = begin; token < end; ++token) {      for (int token = begin; token < end; ++token) {
1563          if (token == YYTERROR || yycheck[n + token] != token) continue;          if (token <= YYTERROR) continue;
1564            if (yytname[token] == String("$undefined")) continue;
1565            if (yytname[token] == String("EXT_ASCII_CHAR")) continue;
1566            //if (yycheck[n + token] != token) goto default_reduction;
1567            if (yycheck[n + token] != token) { // default reduction suggested ...
1568                // If we are here, it means the current token in the loop would not
1569                // cause a "shift", however we don't already know whether this token
1570                // is valid or not. Because there might be several reductions
1571                // involved until one can determine whether the token causes an
1572                // error or is valid. So we use this heavy check instead:
1573                std::vector<YYTYPE_INT16> stackCopy = stack; // copy required, since reduction will take place
1574                if (!yyValid(stackCopy, _tokenChar(token))) continue; // invalid token
1575  #if DEBUG_BISON_SYNTAX_ERROR_WALKER  #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1576          printf(" %s", yytname[token]);              printf(" ETdr(%s)", yytname[token]);
1577    #endif
1578                // the token is valid, "stackCopy" has been reduced accordingly
1579                // and now do recurse ...
1580                nextExpectedChars += _tokenName(token);
1581                nextExpectedCharsLen = nextExpectedChars.size();
1582                walkAndFillExpectedSymbols( //FIXME: could cause stack overflow (should be a loop instead), is probably fine with our current grammar though
1583                    stackCopy, expectedSymbols, nextExpectedChars, depth + 1
1584                );
1585                nextExpectedChars.resize(nextExpectedCharsLen); // restore 'nextExpectedChars'
1586                continue;
1587            }
1588    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1589            printf(" ET(%s)", yytname[token]);
1590  #endif  #endif
   
         //if (yycheck[n + token] != token) goto default_reduction;  
1591    
1592          action = yytable[n + token];          action = yytable[n + token];
1593          if (action == 0 || action == YYTABLE_NINF) {          if (action == 0 || action == YYTABLE_NINF) {
1594  #if DEBUG_BISON_SYNTAX_ERROR_WALKER  #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1595              printf(" (invalid action) "); fflush(stdout);              printf(" (invalid action A) "); fflush(stdout);
1596  #endif  #endif
1597              continue; // error, ignore              continue; // error, ignore
1598          }          }
# Line 1491  static void walkAndFillExpectedSymbols( Line 1603  static void walkAndFillExpectedSymbols(
1603              rule = -action;              rule = -action;
1604              goto reduce;              goto reduce;
1605          }          }
1606          if (action == YYFINAL) continue; // "accept" state, we don't care about it here          if (action == YYFINAL) {
1607    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1608                printf(" (ACCEPT) "); fflush(stdout);
1609    #endif
1610                continue; // "accept" state, we don't care about it here
1611            }
1612    
1613          // "shift" required ...          // "shift" required ...
1614    
1615          if (std::find(stack.begin(), stack.end(), action) != stack.end())          if (std::find(stack.begin(), stack.end(), action) != stack.end()) {
1616    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1617                printf(" (duplicate state %d) ", action); fflush(stdout);
1618    #endif
1619              continue; // duplicate state, ignore it to avoid endless recursions              continue; // duplicate state, ignore it to avoid endless recursions
1620            }
1621    
1622          // "shift" / push the new state on the state stack and call this          // "shift" / push the new state on the state stack and call this
1623          // function recursively, and restore the stack after the recurse return          // function recursively, and restore the stack after the recurse return
# Line 1519  static void walkAndFillExpectedSymbols( Line 1640  static void walkAndFillExpectedSymbols(
1640  #if DEBUG_BISON_SYNTAX_ERROR_WALKER  #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1641          printf(" (reduce by %d) ", rule); fflush(stdout);          printf(" (reduce by %d) ", rule); fflush(stdout);
1642  #endif  #endif
1643          if (rule == 0 || rule >= YYNRULES) continue; // invalid rule, something is wrong          if (rule == 0 || rule >= YYNRULES) {
1644          // store the left-hand symbol of the grammar rule  #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1645                printf(" (invalid rule) "); fflush(stdout);
1646    #endif
1647                continue; // invalid rule, something is wrong
1648            }
1649            // Store the left-hand symbol of the grammar rule. (If the same symbol
1650            // can be matched on different ways, then it is non-terminal symbol.)
1651            bool ambigious =
1652                expectedSymbols.count(yytname[yyr1[rule]]) &&
1653                expectedSymbols[yytname[yyr1[rule]]].nextExpectedChars != nextExpectedChars;
1654          #if HAVE_BISON_MAJ >= 3          #if HAVE_BISON_MAJ >= 3
1655          expectedSymbols[yytname[yyr1[rule]]] = _symbolInfoForRule(rule, stack, nextExpectedChars);          expectedSymbols[yytname[yyr1[rule]]] = _symbolInfoForRule(rule, stack, nextExpectedChars);
1656          #else          #else
1657          expectedSymbols[yytname[yyr1[rule]]] = _symbolInfoForRule(rule, nextExpectedChars);          expectedSymbols[yytname[yyr1[rule]]] = _symbolInfoForRule(rule, nextExpectedChars);
1658          #endif          #endif
1659            if (ambigious)
1660                expectedSymbols[yytname[yyr1[n]]].isTerminalSymbol = false;
1661  #if DEBUG_BISON_SYNTAX_ERROR_WALKER  #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1662          printf(" (SYM %s) ", yytname[yyr1[rule]]); fflush(stdout);          printf(" (SYM %s) ", yytname[yyr1[rule]]); fflush(stdout);
1663  #endif  #endif
# Line 1535  static void walkAndFillExpectedSymbols( Line 1667  static void walkAndFillExpectedSymbols(
1667  #endif  #endif
1668  }  }
1669    
 /**  
  * Implements Bison's so called "reduce" action, according to Bison's LALR(1)  
  * parser algorithm.  
  */  
 inline static int _yyReduce(std::vector<YYTYPE_INT16>& stack, const int& rule) {  
     if (stack.empty()) throw 1; // severe error  
     const int len = yyr2[rule];  
     stack.resize(stack.size() - len);  
     YYTYPE_INT16 newState = yypgoto[yyr1[rule] - YYNTOKENS] + stack.back();  
     if (0 <= newState && newState <= YYLAST && yycheck[newState] == stack.back())  
         newState = yytable[newState];  
     else  
         newState = yydefgoto[yyr1[rule] - YYNTOKENS];  
     stack.push_back(newState);  
     return newState;  
 }  
   
 /**  
  * Implements Bison's so called "default reduce" action, according to Bison's  
  * LALR(1) parser algorithm.  
  */  
 inline static int _yyDefaultReduce(std::vector<YYTYPE_INT16>& stack) {  
     if (stack.empty()) throw 2; // severe error  
     int rule = yydefact[stack.back()];  
     if (rule <= 0 || rule >= YYNRULES) throw 3; // no rule, something is wrong  
     return _yyReduce(stack, rule);  
 }  
   
1670  #define DEBUG_PUSH_PARSE 0  #define DEBUG_PUSH_PARSE 0
1671    
1672  /**  /**
# Line 1629  static bool yyPushParse(std::vector<YYTY Line 1733  static bool yyPushParse(std::vector<YYTY
1733  }  }
1734    
1735  /**  /**
1736   * Returns true if parsing ahead with given character @a ch is syntactially   * Returns true if parsing ahead with given character @a ch is syntactically
1737   * valid according to the LSCP grammar, it returns false if it would create a   * valid according to the LSCP grammar, it returns false if it would create a
1738   * parse error.   * parse error.
1739   *   *
1740     * The @a stack will reflect the new parser state after this call.
1741     *
1742   * This is just a wrapper ontop of yyPushParse() which converts parser   * This is just a wrapper ontop of yyPushParse() which converts parser
1743   * exceptions thrown by yyPushParse() into negative return value.   * exceptions thrown by yyPushParse() into negative return value.
1744   */   */
# Line 1664  static int yyValidCharacters(std::vector Line 1770  static int yyValidCharacters(std::vector
1770          // char here below, and since the state stack might be altered          // char here below, and since the state stack might be altered
1771          // (i.e. shifted or reduced) on syntax errors, we have to backup the          // (i.e. shifted or reduced) on syntax errors, we have to backup the
1772          // current state stack and restore it on syntax errors below          // current state stack and restore it on syntax errors below
1773          std::vector<YYTYPE_INT16> stackBackup = stack;          std::vector<YYTYPE_INT16> stackCopy = stack;
1774          if (yyValid(stackBackup, line[i])) {          if (yyValid(stackCopy, line[i])) {
1775              stack = stackBackup;              stack = stackCopy;
1776              continue;              continue;
1777          }          }
1778          if (bAutoCorrect) {          if (bAutoCorrect) {
# Line 1692  static int yyValidCharacters(std::vector Line 1798  static int yyValidCharacters(std::vector
1798   * Should only be called on syntax errors: returns a set of non-terminal   * Should only be called on syntax errors: returns a set of non-terminal
1799   * symbols expected to appear now/next, just at the point where the syntax   * symbols expected to appear now/next, just at the point where the syntax
1800   * error appeared.   * error appeared.
1801     *
1802     * @returns names of the non-terminal symbols expected at this parse position
1803   */   */
1804  static std::set<String> yyExpectedSymbols() {  static std::set<String> yyExpectedSymbols() {
1805      std::map<String,BisonSymbolInfo> expectedSymbols;      std::map<String,BisonSymbolInfo> expectedSymbols;
# Line 1715  static std::set<String> yyExpectedSymbol Line 1823  static std::set<String> yyExpectedSymbol
1823      return result;      return result;
1824  }  }
1825    
1826    #define DEBUG_YY_AUTO_COMPLETE 0
1827    
1828    /**
1829     * A set of parser state stacks. This type is used in yyAutoComplete() to keep
1830     * track of all previous parser states, for detecting a parser state stack that
1831     * has already been before. Because if yyAutoComplete() reaches the exactly same
1832     * parser state stack again, it means there is an endless recursion in that
1833     * part of the grammar tree branch and shall not be evaluated any further,
1834     * because it would end up in an endless loop otherwise.
1835     *
1836     * This solution consumes a lot of memory, but unfortunately there is no other
1837     * easy way to solve it. With our grammar and today's memory heap & memory stack
1838     * it should be fine though.
1839     */
1840    typedef std::set< std::vector<YYTYPE_INT16> > YYStackHistory;
1841    
1842    /**
1843     * Generates and returns an auto completion string for the current parser
1844     * state given by @a stack.
1845     *
1846     * Regarding @a history argument: read the description on YYStackHistory for the
1847     * purpose behind this argument.
1848     *
1849     * @param stack - current Bison (yacc) state stack to create auto completion for
1850     * @param history - only for internal purpose, keeps a history of all previous parser state stacks
1851     * @param depth - just for internal debugging purposes
1852     * @returns auto completion for current, given parser state
1853     */
1854    static String yyAutoComplete(std::vector<YYTYPE_INT16>& stack, YYStackHistory& history, int depth = 0) {
1855        std::map<String,BisonSymbolInfo> expectedSymbols;
1856        String notUsedHere;
1857        walkAndFillExpectedSymbols(stack, expectedSymbols, notUsedHere);
1858        if (expectedSymbols.size() == 1) {
1859            String name          = expectedSymbols.begin()->first;
1860            BisonSymbolInfo info = expectedSymbols.begin()->second;
1861    #if DEBUG_YY_AUTO_COMPLETE
1862            for (int q = 0; q < depth; ++q) printf("  ");
1863            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());
1864    #endif
1865            if (info.nextExpectedChars.empty() || !info.isTerminalSymbol) return "";
1866            // parse forward with the suggested auto completion
1867            std::vector<YYTYPE_INT16> stackCopy = stack;
1868            yyValidCharacters(stackCopy, info.nextExpectedChars, false);
1869            // detect endless recursion
1870            if (history.count(stackCopy)) return "";
1871            history.insert(stackCopy);
1872            // recurse and return the expanded auto completion with maximum length
1873            return info.nextExpectedChars + yyAutoComplete(stackCopy, history, depth + 1);
1874        } else if (expectedSymbols.size() == 0) {
1875    #if DEBUG_YY_AUTO_COMPLETE
1876            for (int q = 0; q < depth; ++q) printf("  ");
1877            printf("(%d) No sub suggestion.\n", depth);
1878    #endif
1879            return "";
1880        } else if (expectedSymbols.size() > 1) {
1881    #if DEBUG_YY_AUTO_COMPLETE
1882            for (int q = 0; q < depth; ++q) printf("  ");
1883            printf("(%d) Multiple sub possibilities (before expansion):", depth);
1884            for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin();
1885                 it != expectedSymbols.end(); ++it)
1886            {
1887                printf(" %s (..%s)", it->first.c_str(), it->second.nextExpectedChars.c_str());
1888            }
1889            printf("\n");
1890    #endif
1891            // check if any of the possibilites is a non-terminal symbol, if so, we
1892            // have no way for auto completion at this point
1893            for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin();
1894                 it != expectedSymbols.end(); ++it)
1895            {
1896                if (!it->second.isTerminalSymbol) {
1897    #if DEBUG_YY_AUTO_COMPLETE
1898                    for (int q = 0; q < depth; ++q) printf("  ");
1899                    printf("(%d) Non-terminal exists. Stop.", depth);
1900    #endif
1901                    return "";
1902                }
1903            }
1904    #if 0 // commented out for now, since practically irrelevant and VERY slow ...
1905            // all possibilities are terminal symbols, so expand all possiblities to
1906            // maximum length with a recursive call for each possibility
1907            for (std::map<String,BisonSymbolInfo>::iterator it = expectedSymbols.begin();
1908                 it != expectedSymbols.end(); ++it)
1909            {
1910                if (it->second.nextExpectedChars.empty() || !it->second.isTerminalSymbol) continue;
1911                // parse forward with this particular suggested auto completion
1912                std::vector<YYTYPE_INT16> stackCopy = stack;
1913                yyValidCharacters(stackCopy, it->second.nextExpectedChars, false);
1914                // detect endless recursion
1915                if (history.count(stackCopy)) continue;
1916                history.insert(stackCopy);
1917                // recurse and return the total possible auto completion for this
1918                // grammar tree branch
1919                it->second.nextExpectedChars += yyAutoComplete(stackCopy, history, depth + 1);
1920            }
1921    #endif
1922            // try to find the longest common string all possibilities start with
1923            // (from the left)
1924            String sCommon;
1925            for (int i = 0; true; ++i) {
1926                char c;
1927                for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin();
1928                     it != expectedSymbols.end(); ++it)
1929                {
1930                    if (i >= it->second.nextExpectedChars.size())
1931                        goto commonSearchEndLabel;
1932                    if (it == expectedSymbols.begin())
1933                        c = it->second.nextExpectedChars[i];
1934                    if (c != it->second.nextExpectedChars[i])
1935                        goto commonSearchEndLabel;
1936                    if (it == --expectedSymbols.end())
1937                        sCommon += c;
1938                }
1939            }
1940            commonSearchEndLabel:
1941    #if DEBUG_YY_AUTO_COMPLETE
1942            for (int q = 0; q < depth; ++q) printf("  ");
1943            printf("(%d) Multiple sub possibilities (after expansion):", depth);
1944            for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin();
1945                 it != expectedSymbols.end(); ++it)
1946            {
1947                printf(" %s (..%s)", it->first.c_str(), it->second.nextExpectedChars.c_str());
1948            }
1949            printf("\n");
1950            for (int q = 0; q < depth; ++q) printf("  ");
1951            printf("(%d) Common sub possibility: '%s'\n", depth, sCommon.c_str());
1952    #endif
1953            return sCommon;
1954        }
1955        return ""; // just pro forma, should never happen though
1956    }
1957    
1958    /**
1959     * Just a convenience wrapper on top of the actual yyAutoComplete()
1960     * implementation. See description above for details.
1961     */
1962    static String yyAutoComplete(std::vector<YYTYPE_INT16>& stack) {
1963        YYStackHistory history;
1964        return yyAutoComplete(stack, history);
1965    }
1966    
1967  namespace LinuxSampler {  namespace LinuxSampler {
1968    
1969  #define DEBUG_SHELL_INTERACTION 0  #define DEBUG_SHELL_INTERACTION 0
1970    
1971  /**  /**
1972   * If LSP shell mode is enabled, then this function is called on every new   * If LSP shell mode is enabled for the respective LSCP client connection, then
1973   * received from client. It will check the current total input line and reply   * this function is called on every new byte received from that client. It will
1974   * to the LSCP shell for providing colored syntax highlighting and potential   * check the current total input line and reply to the LSCP shell for providing
1975   * auto completion in the shell.   * colored syntax highlighting and potential auto completion in the shell.
1976   *   *
1977   * It also performs auto correction of obvious & trivial syntax mistakes if   * It also performs auto correction of obvious & trivial syntax mistakes if
1978   * requested.   * requested.
1979     *
1980     * The return value of this function will be sent to the client. It contains one
1981     * line specially formatted for the LSCP shell, which can easily be processed by
1982     * the client/shell for gettings its necessary informations like which part of
1983     * the current command line is syntactically correct, which part is incorrect,
1984     * what could be auto completed right now, etc.
1985     *
1986     * @returns LSCP shell response line to be returned to the client
1987   */   */
1988  String lscpParserProcessShellInteraction(String& line, yyparse_param_t* param) {  String lscpParserProcessShellInteraction(String& line, yyparse_param_t* param) {
1989      // first, determine how many characters (starting from the left) of the      // first, determine how many characters (starting from the left) of the
1990      // given input line are already syntactially correct      // given input line are already syntactically correct
1991      std::vector<YYTYPE_INT16> stack;      std::vector<YYTYPE_INT16> stack;
1992      stack.push_back(0); // every Bison symbol stack starts with state zero      stack.push_back(0); // every Bison symbol stack starts with state zero
1993      String l = line + '\n'; // '\n' to pretend ENTER as if the line was now complete      String l = line + '\n'; // '\n' to pretend ENTER as if the line was now complete
# Line 1761  String lscpParserProcessShellInteraction Line 2018  String lscpParserProcessShellInteraction
2018      if (!l.empty()) yyValidCharacters(stack, l, param->bShellAutoCorrect);      if (!l.empty()) yyValidCharacters(stack, l, param->bShellAutoCorrect);
2019    
2020      // generate auto completion suggestion (based on the current parser stack)      // generate auto completion suggestion (based on the current parser stack)
2021      std::map<String,BisonSymbolInfo> expectedSymbols;      String sSuggestion = yyAutoComplete(stack);
2022      String notUsedHere;      if (!sSuggestion.empty()) result += LSCP_SHK_SUGGEST_BACK + sSuggestion;
     walkAndFillExpectedSymbols(stack, expectedSymbols, notUsedHere);  
     if (expectedSymbols.size() == 1) {  
         String name          = expectedSymbols.begin()->first;  
         BisonSymbolInfo info = expectedSymbols.begin()->second;  
 #if DEBUG_SHELL_INTERACTION  
         printf("Suggested Completion (%d): %s '%s'\n", expectedSymbols.size(), (info.isTerminalSymbol) ? "T:" : "NT:", (name + " (" + info.nextExpectedChars + ")").c_str());  
 #endif  
         result += LSCP_SHK_SUGGEST_BACK + info.nextExpectedChars;  
     } else if (expectedSymbols.size() == 0) {  
 #if DEBUG_SHELL_INTERACTION  
         printf("No suggestion.\n");  
 #endif  
     } else if (expectedSymbols.size() > 1) {  
 #if DEBUG_SHELL_INTERACTION  
         printf("Multiple possibilities:");  
         for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin();  
              it != expectedSymbols.end(); ++it)  
         {  
             printf(" %s (..%s)", it->first.c_str(), it->second.nextExpectedChars.c_str());  
         }  
         printf("\n");  
 #endif  
         // check if any of the possibilites is a non-terminal symbol, if so, we  
         // have no way for auto completion at this point  
         bool bNonTerminalExists = false;  
         for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin();  
              it != expectedSymbols.end(); ++it) if (!it->second.isTerminalSymbol) { bNonTerminalExists = true; break; };  
         if (!bNonTerminalExists) {  
             // all possibilites are terminal symbaols, so try to find the least  
             // common string all possibilites start with from the left  
             String sCommon;  
             for (int i = 0; true; ++i) {  
                 char c;  
                 for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin();  
                      it != expectedSymbols.end(); ++it)  
                 {  
                     if (i >= it->second.nextExpectedChars.size())  
                         goto commonSearchEndLabel;  
                     if (it == expectedSymbols.begin())  
                         c = it->second.nextExpectedChars[i];  
                     if (c != it->second.nextExpectedChars[i])  
                         goto commonSearchEndLabel;  
                     if (it == --expectedSymbols.end())  
                         sCommon += c;  
                 }  
             }  
             commonSearchEndLabel:  
             if (!sCommon.empty()) result += LSCP_SHK_SUGGEST_BACK + sCommon;  
 #if DEBUG_SHELL_INTERACTION  
             printf("Common possibility: '%s'\n", sCommon.c_str());  
 #endif  
         }  
     }  
2023    
2024  #if DEBUG_SHELL_INTERACTION  #if DEBUG_SHELL_INTERACTION
2025      printf("%s\n", result.c_str());      printf("%s\n", result.c_str());

Legend:
Removed from v.2522  
changed lines
  Added in v.2523

  ViewVC Help
Powered by ViewVC