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 |
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 : '#' |
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 |
/** |
/** |
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 |
} |
} |
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 |
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 |
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 |
/** |
/** |
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 |
*/ |
*/ |
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) { |
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; |
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 |
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()); |