180 |
%parse-param {void* yyparse_param} |
%parse-param {void* yyparse_param} |
181 |
|
|
182 |
// After entering the yyparse() function, store references to the parser's |
// After entering the yyparse() function, store references to the parser's |
183 |
// state stack, so that we can create more helpful syntax error messages than |
// symbol stack, so that we can create more helpful syntax error messages than |
184 |
// Bison (2.x) could do. |
// Bison (2.x) could do. |
185 |
%initial-action { |
%initial-action { |
186 |
yyparse_param_t* p = (yyparse_param_t*) yyparse_param; |
yyparse_param_t* p = (yyparse_param_t*) yyparse_param; |
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 : '#' |
1348 |
|
|
1349 |
#else // Bison 2.x or older ... |
#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* |
* Returns true if the given grammar @a rule is a terminal symbol (in *our* |
1355 |
* terms). |
* terms). |
1408 |
return s; |
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 |
#define DEBUG_BISON_SYNTAX_ERROR_WALKER 0 |
#define DEBUG_BISON_SYNTAX_ERROR_WALKER 0 |
1454 |
|
|
1455 |
/** |
/** |
1457 |
* precise parse position & state represented by @a stack, according to Bison's |
* precise parse position & state represented by @a stack, according to Bison's |
1458 |
* LALR(1) parser algorithm. |
* LALR(1) parser algorithm. |
1459 |
* |
* |
1460 |
* This function is given a Bison parser state stack, reflecting the parser's |
* This function is given a Bison parser symbol stack, reflecting the parser's |
1461 |
* entire state at a certain point, i.e. when a syntax error occured. This |
* entire state at a certain point, i.e. when a syntax error occured. This |
1462 |
* function will then walk ahead the potential parse tree starting from the |
* function will then walk ahead the potential parse tree starting from the |
1463 |
* current head of the given state stack. This function will call itself |
* current head of the given symbol stack. This function will call itself |
1464 |
* recursively to scan the individual parse tree branches. As soon as it hits |
* recursively to scan the individual parse tree branches. As soon as it hits |
1465 |
* on the next non-terminal grammar symbol in one parse tree branch, it adds the |
* on the next non-terminal grammar symbol in one parse tree branch, it adds the |
1466 |
* found non-terminal symbol to @a expectedSymbols and aborts scanning the |
* found non-terminal symbol to @a expectedSymbols and aborts scanning the |
1467 |
* respective tree branch further. If any local parser state is reached a second |
* respective tree branch further. If any local parser state is reached a second |
1468 |
* time, the respective parse tree is aborted to avoid any endless recursion. |
* time, the respective parse tree is aborted to avoid any endless recursion. |
1469 |
* |
* |
1470 |
* @param stack - current Bison (yacc) state stack to be examined |
* @param stack - current Bison (yacc) symbol stack to be examined |
1471 |
* @param expectedSymbols - will be filled with next expected grammar symbols |
* @param expectedSymbols - will be filled with next expected grammar symbols |
1472 |
* @param nextExpectedChars - just for internal purpose, due to the recursive |
* @param nextExpectedChars - just for internal purpose, due to the recursive |
1473 |
* implementation of this function, do supply an |
* implementation of this function, do supply an |
1482 |
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
1483 |
printf("\n"); |
printf("\n"); |
1484 |
for (int i = 0; i < depth; ++i) printf("\t"); |
for (int i = 0; i < depth; ++i) printf("\t"); |
1485 |
printf("State stack:"); |
printf("Symbol stack:"); |
1486 |
for (int i = 0; i < stack.size(); ++i) { |
for (int i = 0; i < stack.size(); ++i) { |
1487 |
printf(" %d", stack[i]); |
printf(" %d", stack[i]); |
1488 |
} |
} |
1489 |
printf("\n"); |
printf("\n"); |
1490 |
#endif |
#endif |
1491 |
|
startLabel: |
1492 |
|
|
1493 |
if (stack.empty()) return; |
if (stack.empty()) { |
1494 |
|
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
1495 |
|
for (int i = 0; i < depth; ++i) printf("\t"); |
1496 |
|
printf("(EMPTY STACK)\n"); |
1497 |
|
#endif |
1498 |
|
return; |
1499 |
|
} |
1500 |
|
|
1501 |
int state = stack[stack.size() - 1]; |
int state = stack[stack.size() - 1]; |
1502 |
int n = yypact[state]; |
int n = yypact[state]; |
1503 |
if (n == YYPACT_NINF) { // default reduction required ... |
if (n == YYPACT_NINF) { // default reduction required ... |
1504 |
// get default reduction rule for this state |
// get default reduction rule for this state |
1505 |
n = yydefact[state]; |
n = yydefact[state]; |
1506 |
if (n <= 0 || n >= YYNRULES) return; // no rule, something is wrong |
if (n <= 0 || n >= YYNRULES) { |
1507 |
// return the new resolved expected symbol (left-hand symbol of grammar |
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
1508 |
// rule), then we're done in this state |
for (int i = 0; i < depth; ++i) printf("\t"); |
1509 |
|
printf("(EMPTY RULE)\n"); |
1510 |
|
#endif |
1511 |
|
return; // no rule, something is wrong |
1512 |
|
} |
1513 |
|
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
1514 |
|
for (int i = 0; i < depth; ++i) printf("\t"); |
1515 |
|
printf("(default reduction)\n"); |
1516 |
|
#endif |
1517 |
#if HAVE_BISON_MAJ >= 3 |
#if HAVE_BISON_MAJ >= 3 |
1518 |
expectedSymbols[yytname[yyr1[n]]] = _symbolInfoForRule(n, stack, nextExpectedChars); |
if (!nextExpectedChars.empty() || !_isRuleTerminalSymbol(n, stack)) { |
1519 |
#else |
#else |
1520 |
expectedSymbols[yytname[yyr1[n]]] = _symbolInfoForRule(n, nextExpectedChars); |
if (!nextExpectedChars.empty() || !_isRuleTerminalSymbol(n)) { |
1521 |
#endif |
#endif |
1522 |
|
// Return the new resolved expected symbol (left-hand symbol of grammar |
1523 |
|
// rule), then we're done in this state. (If the same symbol can be |
1524 |
|
// matched on different ways, then it is non-terminal symbol.) |
1525 |
|
bool ambigious = |
1526 |
|
expectedSymbols.count(yytname[yyr1[n]]) && |
1527 |
|
expectedSymbols[yytname[yyr1[n]]].nextExpectedChars != nextExpectedChars; |
1528 |
|
#if HAVE_BISON_MAJ >= 3 |
1529 |
|
expectedSymbols[yytname[yyr1[n]]] = _symbolInfoForRule(n, stack, nextExpectedChars); |
1530 |
|
#else |
1531 |
|
expectedSymbols[yytname[yyr1[n]]] = _symbolInfoForRule(n, nextExpectedChars); |
1532 |
|
#endif |
1533 |
|
if (ambigious) |
1534 |
|
expectedSymbols[yytname[yyr1[n]]].isTerminalSymbol = false; |
1535 |
|
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
1536 |
|
for (int i = 0; i < depth; ++i) printf("\t"); |
1537 |
|
printf("(empty expectedChars. sym = %s)\n", yytname[yyr1[n]]); |
1538 |
|
#endif |
1539 |
|
return; |
1540 |
|
} |
1541 |
|
_yyReduce(stack, n); |
1542 |
|
goto startLabel; |
1543 |
|
} |
1544 |
|
if (!(YYPACT_NINF < n && n <= YYLAST)) { |
1545 |
|
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
1546 |
|
for (int i = 0; i < depth; ++i) printf("\t"); |
1547 |
|
printf("(invalid action B)\n"); |
1548 |
|
#endif |
1549 |
return; |
return; |
1550 |
} |
} |
1551 |
if (!(YYPACT_NINF < n && n <= YYLAST)) return; |
|
1552 |
|
// Check for duplicate states, if duplicates exist return |
1553 |
|
// (this check is necessary since the introduction of the yyValid() call |
1554 |
|
// below, which does not care about duplicates). |
1555 |
|
for (int i = 0; i < stack.size(); ++i) |
1556 |
|
for (int k = i + 1; k < stack.size(); ++k) |
1557 |
|
if (stack[i] == stack[k]) |
1558 |
|
return; |
1559 |
|
|
1560 |
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
1561 |
for (int i = 0; i < depth; ++i) printf("\t"); |
for (int i = 0; i < depth; ++i) printf("\t"); |
1562 |
printf("Expected tokens:"); |
printf("Expected tokens:"); |
1563 |
#endif |
#endif |
1564 |
int begin = n < 0 ? -n : 0; |
int begin = n < 0 ? -n : 0; |
1565 |
int checklim = YYLAST - n + 1; |
//int checklim = YYLAST - n + 1; |
1566 |
int end = checklim < YYNTOKENS ? checklim : YYNTOKENS; |
int end = YYNTOKENS;//checklim < YYNTOKENS ? checklim : YYNTOKENS; |
1567 |
int rule, action, stackSize, nextExpectedCharsLen; |
int rule, action, stackSize, nextExpectedCharsLen; |
1568 |
for (int token = begin; token < end; ++token) { |
for (int token = begin; token < end; ++token) { |
1569 |
if (token == YYTERROR || yycheck[n + token] != token) continue; |
if (token <= YYTERROR) continue; |
1570 |
|
if (yytname[token] == String("$undefined")) continue; |
1571 |
|
if (yytname[token] == String("EXT_ASCII_CHAR")) continue; |
1572 |
|
//if (yycheck[n + token] != token) goto default_reduction; |
1573 |
|
if (yycheck[n + token] != token) { // default reduction suggested ... |
1574 |
|
// If we are here, it means the current token in the loop would not |
1575 |
|
// cause a "shift", however we don't already know whether this token |
1576 |
|
// is valid or not. Because there might be several reductions |
1577 |
|
// involved until one can determine whether the token causes an |
1578 |
|
// error or is valid. So we use this heavy check instead: |
1579 |
|
std::vector<YYTYPE_INT16> stackCopy = stack; // copy required, since reduction will take place |
1580 |
|
if (!yyValid(stackCopy, _tokenChar(token))) continue; // invalid token |
1581 |
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
1582 |
printf(" %s", yytname[token]); |
printf(" ETdr(%s)", yytname[token]); |
1583 |
|
#endif |
1584 |
|
// the token is valid, "stackCopy" has been reduced accordingly |
1585 |
|
// and now do recurse ... |
1586 |
|
nextExpectedChars += _tokenName(token); |
1587 |
|
nextExpectedCharsLen = nextExpectedChars.size(); |
1588 |
|
walkAndFillExpectedSymbols( //FIXME: could cause stack overflow (should be a loop instead), is probably fine with our current grammar though |
1589 |
|
stackCopy, expectedSymbols, nextExpectedChars, depth + 1 |
1590 |
|
); |
1591 |
|
nextExpectedChars.resize(nextExpectedCharsLen); // restore 'nextExpectedChars' |
1592 |
|
continue; |
1593 |
|
} |
1594 |
|
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
1595 |
|
printf(" ET(%s)", yytname[token]); |
1596 |
#endif |
#endif |
|
|
|
|
//if (yycheck[n + token] != token) goto default_reduction; |
|
1597 |
|
|
1598 |
action = yytable[n + token]; |
action = yytable[n + token]; |
1599 |
if (action == 0 || action == YYTABLE_NINF) { |
if (action == 0 || action == YYTABLE_NINF) { |
1600 |
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
1601 |
printf(" (invalid action) "); fflush(stdout); |
printf(" (invalid action A) "); fflush(stdout); |
1602 |
#endif |
#endif |
1603 |
continue; // error, ignore |
continue; // error, ignore |
1604 |
} |
} |
1609 |
rule = -action; |
rule = -action; |
1610 |
goto reduce; |
goto reduce; |
1611 |
} |
} |
1612 |
if (action == YYFINAL) continue; // "accept" state, we don't care about it here |
if (action == YYFINAL) { |
1613 |
|
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
1614 |
|
printf(" (ACCEPT) "); fflush(stdout); |
1615 |
|
#endif |
1616 |
|
continue; // "accept" state, we don't care about it here |
1617 |
|
} |
1618 |
|
|
1619 |
// "shift" required ... |
// "shift" required ... |
1620 |
|
|
1621 |
if (std::find(stack.begin(), stack.end(), action) != stack.end()) |
if (std::find(stack.begin(), stack.end(), action) != stack.end()) { |
1622 |
|
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
1623 |
|
printf(" (duplicate state %d) ", action); fflush(stdout); |
1624 |
|
#endif |
1625 |
continue; // duplicate state, ignore it to avoid endless recursions |
continue; // duplicate state, ignore it to avoid endless recursions |
1626 |
|
} |
1627 |
|
|
1628 |
// "shift" / push the new state on the state stack and call this |
// "shift" / push the new state on the symbol stack and call this |
1629 |
// function recursively, and restore the stack after the recurse return |
// function recursively, and restore the stack after the recurse return |
1630 |
stackSize = stack.size(); |
stackSize = stack.size(); |
1631 |
nextExpectedCharsLen = nextExpectedChars.size(); |
nextExpectedCharsLen = nextExpectedChars.size(); |
1646 |
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
1647 |
printf(" (reduce by %d) ", rule); fflush(stdout); |
printf(" (reduce by %d) ", rule); fflush(stdout); |
1648 |
#endif |
#endif |
1649 |
if (rule == 0 || rule >= YYNRULES) continue; // invalid rule, something is wrong |
if (rule == 0 || rule >= YYNRULES) { |
1650 |
// store the left-hand symbol of the grammar rule |
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
1651 |
|
printf(" (invalid rule) "); fflush(stdout); |
1652 |
|
#endif |
1653 |
|
continue; // invalid rule, something is wrong |
1654 |
|
} |
1655 |
|
// Store the left-hand symbol of the grammar rule. (If the same symbol |
1656 |
|
// can be matched on different ways, then it is non-terminal symbol.) |
1657 |
|
bool ambigious = |
1658 |
|
expectedSymbols.count(yytname[yyr1[rule]]) && |
1659 |
|
expectedSymbols[yytname[yyr1[rule]]].nextExpectedChars != nextExpectedChars; |
1660 |
#if HAVE_BISON_MAJ >= 3 |
#if HAVE_BISON_MAJ >= 3 |
1661 |
expectedSymbols[yytname[yyr1[rule]]] = _symbolInfoForRule(rule, stack, nextExpectedChars); |
expectedSymbols[yytname[yyr1[rule]]] = _symbolInfoForRule(rule, stack, nextExpectedChars); |
1662 |
#else |
#else |
1663 |
expectedSymbols[yytname[yyr1[rule]]] = _symbolInfoForRule(rule, nextExpectedChars); |
expectedSymbols[yytname[yyr1[rule]]] = _symbolInfoForRule(rule, nextExpectedChars); |
1664 |
#endif |
#endif |
1665 |
|
if (ambigious) |
1666 |
|
expectedSymbols[yytname[yyr1[n]]].isTerminalSymbol = false; |
1667 |
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
#if DEBUG_BISON_SYNTAX_ERROR_WALKER |
1668 |
printf(" (SYM %s) ", yytname[yyr1[rule]]); fflush(stdout); |
printf(" (SYM %s) ", yytname[yyr1[rule]]); fflush(stdout); |
1669 |
#endif |
#endif |
1673 |
#endif |
#endif |
1674 |
} |
} |
1675 |
|
|
|
/** |
|
|
* 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); |
|
|
} |
|
|
|
|
1676 |
#define DEBUG_PUSH_PARSE 0 |
#define DEBUG_PUSH_PARSE 0 |
1677 |
|
|
1678 |
/** |
/** |
1688 |
#if DEBUG_PUSH_PARSE |
#if DEBUG_PUSH_PARSE |
1689 |
//printf("\n"); |
//printf("\n"); |
1690 |
//for (int i = 0; i < depth; ++i) printf("\t"); |
//for (int i = 0; i < depth; ++i) printf("\t"); |
1691 |
printf("State stack:"); |
printf("Symbol stack:"); |
1692 |
for (int i = 0; i < stack.size(); ++i) { |
for (int i = 0; i < stack.size(); ++i) { |
1693 |
printf(" %d", stack[i]); |
printf(" %d", stack[i]); |
1694 |
} |
} |
1739 |
} |
} |
1740 |
|
|
1741 |
/** |
/** |
1742 |
* Returns true if parsing ahead with given character @a ch is syntactially |
* Returns true if parsing ahead with given character @a ch is syntactically |
1743 |
* 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 |
1744 |
* parse error. |
* parse error. |
1745 |
* |
* |
1746 |
|
* The @a stack will reflect the new parser state after this call. |
1747 |
|
* |
1748 |
* This is just a wrapper ontop of yyPushParse() which converts parser |
* This is just a wrapper ontop of yyPushParse() which converts parser |
1749 |
* exceptions thrown by yyPushParse() into negative return value. |
* exceptions thrown by yyPushParse() into negative return value. |
1750 |
*/ |
*/ |
1765 |
* Returns the amount of correct characters of given @a line from the left, |
* Returns the amount of correct characters of given @a line from the left, |
1766 |
* according to the LSCP grammar. |
* according to the LSCP grammar. |
1767 |
* |
* |
1768 |
* @param stack - a Bison symbol state stack to work with |
* @param stack - a Bison symbol stack to work with |
1769 |
* @param line - the input line to check |
* @param line - the input line to check |
1770 |
* @param bAutoCorrect - if true: try to correct obvious, trivial syntax errors |
* @param bAutoCorrect - if true: try to correct obvious, trivial syntax errors |
1771 |
*/ |
*/ |
1773 |
int i; |
int i; |
1774 |
for (i = 0; i < line.size(); ++i) { |
for (i = 0; i < line.size(); ++i) { |
1775 |
// since we might check the same parser state twice against the current |
// since we might check the same parser state twice against the current |
1776 |
// char here below, and since the state stack might be altered |
// char here below, and since the symbol stack might be altered |
1777 |
// (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 |
1778 |
// current state stack and restore it on syntax errors below |
// current symbol stack and restore it on syntax errors below |
1779 |
std::vector<YYTYPE_INT16> stackBackup = stack; |
std::vector<YYTYPE_INT16> stackCopy = stack; |
1780 |
if (yyValid(stackBackup, line[i])) { |
if (yyValid(stackCopy, line[i])) { |
1781 |
stack = stackBackup; |
stack = stackCopy; |
1782 |
continue; |
continue; |
1783 |
} |
} |
1784 |
if (bAutoCorrect) { |
if (bAutoCorrect) { |
1804 |
* 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 |
1805 |
* 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 |
1806 |
* error appeared. |
* error appeared. |
1807 |
|
* |
1808 |
|
* @returns names of the non-terminal symbols expected at this parse position |
1809 |
*/ |
*/ |
1810 |
static std::set<String> yyExpectedSymbols() { |
static std::set<String> yyExpectedSymbols() { |
1811 |
std::map<String,BisonSymbolInfo> expectedSymbols; |
std::map<String,BisonSymbolInfo> expectedSymbols; |
1813 |
YYTYPE_INT16* ss = (*param->ppStackBottom); |
YYTYPE_INT16* ss = (*param->ppStackBottom); |
1814 |
YYTYPE_INT16* sp = (*param->ppStackTop); |
YYTYPE_INT16* sp = (*param->ppStackTop); |
1815 |
int iStackSize = sp - ss + 1; |
int iStackSize = sp - ss + 1; |
1816 |
// copy and wrap parser's state stack into a convenient STL container |
// copy and wrap parser's symbol stack into a convenient STL container |
1817 |
std::vector<YYTYPE_INT16> stack; |
std::vector<YYTYPE_INT16> stack; |
1818 |
for (int i = 0; i < iStackSize; ++i) { |
for (int i = 0; i < iStackSize; ++i) { |
1819 |
stack.push_back(ss[i]); |
stack.push_back(ss[i]); |
1829 |
return result; |
return result; |
1830 |
} |
} |
1831 |
|
|
1832 |
|
#define DEBUG_YY_AUTO_COMPLETE 0 |
1833 |
|
|
1834 |
|
/** |
1835 |
|
* A set of parser symbol stacks. This type is used in yyAutoComplete() to keep |
1836 |
|
* track of all previous parser states, for detecting a parser symbol stack that |
1837 |
|
* has already been before. Because if yyAutoComplete() reaches the exactly same |
1838 |
|
* parser symbol stack again, it means there is an endless recursion in that |
1839 |
|
* part of the grammar tree branch and shall not be evaluated any further, |
1840 |
|
* because it would end up in an endless loop otherwise. |
1841 |
|
* |
1842 |
|
* This solution consumes a lot of memory, but unfortunately there is no other |
1843 |
|
* easy way to solve it. With our grammar and today's memory heap & memory stack |
1844 |
|
* it should be fine though. |
1845 |
|
*/ |
1846 |
|
typedef std::set< std::vector<YYTYPE_INT16> > YYStackHistory; |
1847 |
|
|
1848 |
|
/** |
1849 |
|
* Generates and returns an auto completion string for the current parser |
1850 |
|
* state given by @a stack. That means, this function will return the longest |
1851 |
|
* sequence of characters that is uniqueley expected to be sent next by the LSCP |
1852 |
|
* client. Or in other words, if the LSCP client would send any other |
1853 |
|
* character(s) than returned here, it would result in a syntax error. |
1854 |
|
* |
1855 |
|
* This function takes a Bison symbol @a stack as argument, reflecting the |
1856 |
|
* current Bison parser state, and evaluates the individual grammar tree |
1857 |
|
* branches starting from that particular position. It walks along the grammar |
1858 |
|
* tree as long as there is only one possible tree branch and assembles a string |
1859 |
|
* of input characters that would lead to that walk through the grammar tree. As |
1860 |
|
* soon as a position in the grammar tree is reached where there are multiple |
1861 |
|
* possible tree branches, this algorithm will stop, since the user could have |
1862 |
|
* multiple possible valid characters he could type at that point, thus auto |
1863 |
|
* completion would no longer be unique at that point. |
1864 |
|
* |
1865 |
|
* Regarding @a history argument: read the description on YYStackHistory for the |
1866 |
|
* purpose behind this argument. |
1867 |
|
* |
1868 |
|
* @param stack - current Bison (yacc) symbol stack to create auto completion for |
1869 |
|
* @param history - only for internal purpose, keeps a history of all previous |
1870 |
|
* parser symbol stacks (just for avoiding endless recursion in |
1871 |
|
* this auto completion algorithm) |
1872 |
|
* @param depth - just for internal debugging purposes |
1873 |
|
* @returns auto completion for current, given parser state |
1874 |
|
*/ |
1875 |
|
static String yyAutoComplete(std::vector<YYTYPE_INT16>& stack, YYStackHistory& history, int depth = 0) { |
1876 |
|
std::map<String,BisonSymbolInfo> expectedSymbols; |
1877 |
|
String notUsedHere; |
1878 |
|
walkAndFillExpectedSymbols(stack, expectedSymbols, notUsedHere); |
1879 |
|
if (expectedSymbols.size() == 1) { |
1880 |
|
String name = expectedSymbols.begin()->first; |
1881 |
|
BisonSymbolInfo info = expectedSymbols.begin()->second; |
1882 |
|
#if DEBUG_YY_AUTO_COMPLETE |
1883 |
|
for (int q = 0; q < depth; ++q) printf(" "); |
1884 |
|
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()); |
1885 |
|
#endif |
1886 |
|
if (info.nextExpectedChars.empty() || !info.isTerminalSymbol) return ""; |
1887 |
|
// parse forward with the suggested auto completion |
1888 |
|
std::vector<YYTYPE_INT16> stackCopy = stack; |
1889 |
|
yyValidCharacters(stackCopy, info.nextExpectedChars, false); |
1890 |
|
// detect endless recursion |
1891 |
|
if (history.count(stackCopy)) return ""; |
1892 |
|
history.insert(stackCopy); |
1893 |
|
// recurse and return the expanded auto completion with maximum length |
1894 |
|
return info.nextExpectedChars + yyAutoComplete(stackCopy, history, depth + 1); |
1895 |
|
} else if (expectedSymbols.size() == 0) { |
1896 |
|
#if DEBUG_YY_AUTO_COMPLETE |
1897 |
|
for (int q = 0; q < depth; ++q) printf(" "); |
1898 |
|
printf("(%d) No sub suggestion.\n", depth); |
1899 |
|
#endif |
1900 |
|
return ""; |
1901 |
|
} else if (expectedSymbols.size() > 1) { |
1902 |
|
#if DEBUG_YY_AUTO_COMPLETE |
1903 |
|
for (int q = 0; q < depth; ++q) printf(" "); |
1904 |
|
printf("(%d) Multiple sub possibilities (before expansion):", depth); |
1905 |
|
for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin(); |
1906 |
|
it != expectedSymbols.end(); ++it) |
1907 |
|
{ |
1908 |
|
printf(" %s (..%s)", it->first.c_str(), it->second.nextExpectedChars.c_str()); |
1909 |
|
} |
1910 |
|
printf("\n"); |
1911 |
|
#endif |
1912 |
|
// check if any of the possibilites is a non-terminal symbol, if so, we |
1913 |
|
// have no way for auto completion at this point |
1914 |
|
for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin(); |
1915 |
|
it != expectedSymbols.end(); ++it) |
1916 |
|
{ |
1917 |
|
if (!it->second.isTerminalSymbol) { |
1918 |
|
#if DEBUG_YY_AUTO_COMPLETE |
1919 |
|
for (int q = 0; q < depth; ++q) printf(" "); |
1920 |
|
printf("(%d) Non-terminal exists. Stop.", depth); |
1921 |
|
#endif |
1922 |
|
return ""; |
1923 |
|
} |
1924 |
|
} |
1925 |
|
#if 0 // commented out for now, since practically irrelevant and VERY slow ... |
1926 |
|
// all possibilities are terminal symbols, so expand all possiblities to |
1927 |
|
// maximum length with a recursive call for each possibility |
1928 |
|
for (std::map<String,BisonSymbolInfo>::iterator it = expectedSymbols.begin(); |
1929 |
|
it != expectedSymbols.end(); ++it) |
1930 |
|
{ |
1931 |
|
if (it->second.nextExpectedChars.empty() || !it->second.isTerminalSymbol) continue; |
1932 |
|
// parse forward with this particular suggested auto completion |
1933 |
|
std::vector<YYTYPE_INT16> stackCopy = stack; |
1934 |
|
yyValidCharacters(stackCopy, it->second.nextExpectedChars, false); |
1935 |
|
// detect endless recursion |
1936 |
|
if (history.count(stackCopy)) continue; |
1937 |
|
history.insert(stackCopy); |
1938 |
|
// recurse and return the total possible auto completion for this |
1939 |
|
// grammar tree branch |
1940 |
|
it->second.nextExpectedChars += yyAutoComplete(stackCopy, history, depth + 1); |
1941 |
|
} |
1942 |
|
#endif |
1943 |
|
// try to find the longest common string all possibilities start with |
1944 |
|
// (from the left) |
1945 |
|
String sCommon; |
1946 |
|
for (int i = 0; true; ++i) { |
1947 |
|
char c; |
1948 |
|
for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin(); |
1949 |
|
it != expectedSymbols.end(); ++it) |
1950 |
|
{ |
1951 |
|
if (i >= it->second.nextExpectedChars.size()) |
1952 |
|
goto commonSearchEndLabel; |
1953 |
|
if (it == expectedSymbols.begin()) |
1954 |
|
c = it->second.nextExpectedChars[i]; |
1955 |
|
if (c != it->second.nextExpectedChars[i]) |
1956 |
|
goto commonSearchEndLabel; |
1957 |
|
if (it == --expectedSymbols.end()) |
1958 |
|
sCommon += c; |
1959 |
|
} |
1960 |
|
} |
1961 |
|
commonSearchEndLabel: |
1962 |
|
#if DEBUG_YY_AUTO_COMPLETE |
1963 |
|
for (int q = 0; q < depth; ++q) printf(" "); |
1964 |
|
printf("(%d) Multiple sub possibilities (after expansion):", depth); |
1965 |
|
for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin(); |
1966 |
|
it != expectedSymbols.end(); ++it) |
1967 |
|
{ |
1968 |
|
printf(" %s (..%s)", it->first.c_str(), it->second.nextExpectedChars.c_str()); |
1969 |
|
} |
1970 |
|
printf("\n"); |
1971 |
|
for (int q = 0; q < depth; ++q) printf(" "); |
1972 |
|
printf("(%d) Common sub possibility: '%s'\n", depth, sCommon.c_str()); |
1973 |
|
#endif |
1974 |
|
return sCommon; |
1975 |
|
} |
1976 |
|
return ""; // just pro forma, should never happen though |
1977 |
|
} |
1978 |
|
|
1979 |
|
/** |
1980 |
|
* Just a convenience wrapper on top of the actual yyAutoComplete() |
1981 |
|
* implementation. See description above for details. |
1982 |
|
*/ |
1983 |
|
static String yyAutoComplete(std::vector<YYTYPE_INT16>& stack) { |
1984 |
|
YYStackHistory history; |
1985 |
|
return yyAutoComplete(stack, history); |
1986 |
|
} |
1987 |
|
|
1988 |
namespace LinuxSampler { |
namespace LinuxSampler { |
1989 |
|
|
1990 |
#define DEBUG_SHELL_INTERACTION 0 |
#define DEBUG_SHELL_INTERACTION 0 |
1991 |
|
|
1992 |
/** |
/** |
1993 |
* If LSP shell mode is enabled, then this function is called on every new |
* If LSCP shell mode is enabled for the respective LSCP client connection, then |
1994 |
* 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 |
1995 |
* to the LSCP shell for providing colored syntax highlighting and potential |
* check the current total input line and reply to the LSCP shell with a |
1996 |
* auto completion in the shell. |
* specially crafted string, which allows the shell to provide colored syntax |
1997 |
|
* highlighting and potential auto completion in the shell. |
1998 |
* |
* |
1999 |
* It also performs auto correction of obvious & trivial syntax mistakes if |
* It also performs auto correction of obvious & trivial syntax mistakes if |
2000 |
* requested. |
* requested. |
2001 |
|
* |
2002 |
|
* The return value of this function will be sent to the client. It contains one |
2003 |
|
* line specially formatted for the LSCP shell application, which can easily be |
2004 |
|
* processed by the client/shell for extracting its necessary informations like |
2005 |
|
* which part of the current command line is syntactically correct, which part |
2006 |
|
* is incorrect, what could be auto completed right now, etc. So all the heavy |
2007 |
|
* grammar evaluation tasks are peformed by the LSCP server for the LSCP shell |
2008 |
|
* application (which is desgined as a thin client), so the LSCP shell |
2009 |
|
* application will only have to show the results of the LSCP server's |
2010 |
|
* evaluation to the user on the screen. |
2011 |
|
* |
2012 |
|
* @returns LSCP shell response line to be returned to the client |
2013 |
*/ |
*/ |
2014 |
String lscpParserProcessShellInteraction(String& line, yyparse_param_t* param) { |
String lscpParserProcessShellInteraction(String& line, yyparse_param_t* param) { |
2015 |
// first, determine how many characters (starting from the left) of the |
// first, determine how many characters (starting from the left) of the |
2016 |
// given input line are already syntactially correct |
// given input line are already syntactically correct |
2017 |
std::vector<YYTYPE_INT16> stack; |
std::vector<YYTYPE_INT16> stack; |
2018 |
stack.push_back(0); // every Bison symbol stack starts with state zero |
stack.push_back(0); // every Bison symbol stack starts with state zero |
2019 |
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 |
2037 |
|
|
2038 |
// get a clean parser stack to the last valid parse position |
// get a clean parser stack to the last valid parse position |
2039 |
// (due to the appended '\n' character above, and on syntax errors, the |
// (due to the appended '\n' character above, and on syntax errors, the |
2040 |
// state stack might be in undesired, i.e. reduced state) |
// symbol stack might be in undesired, i.e. reduced state) |
2041 |
stack.clear(); |
stack.clear(); |
2042 |
stack.push_back(0); // every Bison symbol stack starts with state zero |
stack.push_back(0); // every Bison symbol stack starts with state zero |
2043 |
l = line.substr(0, n); |
l = line.substr(0, n); |
2044 |
if (!l.empty()) yyValidCharacters(stack, l, param->bShellAutoCorrect); |
if (!l.empty()) yyValidCharacters(stack, l, param->bShellAutoCorrect); |
2045 |
|
|
2046 |
// generate auto completion suggestion (based on the current parser stack) |
// generate auto completion suggestion (based on the current parser stack) |
2047 |
std::map<String,BisonSymbolInfo> expectedSymbols; |
String sSuggestion = yyAutoComplete(stack); |
2048 |
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 |
|
|
} |
|
|
} |
|
2049 |
|
|
2050 |
#if DEBUG_SHELL_INTERACTION |
#if DEBUG_SHELL_INTERACTION |
2051 |
printf("%s\n", result.c_str()); |
printf("%s\n", result.c_str()); |