/[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 2509 by schoenebeck, Fri Jan 10 12:20:05 2014 UTC revision 2510 by schoenebeck, Thu Jan 23 04:00:26 2014 UTC
# Line 35  Line 35 
35  #include "lscpserver.h"  #include "lscpserver.h"
36  #include "lscpevent.h"  #include "lscpevent.h"
37  #include "lscpsymbols.h"  #include "lscpsymbols.h"
38    #include <algorithm>
39    
40  namespace LinuxSampler {  namespace LinuxSampler {
41    
42  // to save us typing work in the rules action definitions  // to save us typing work in the rules action definitions
43  #define LSCPSERVER ((yyparse_param_t*) yyparse_param)->pServer  #define LSCPSERVER ((yyparse_param_t*) yyparse_param)->pServer
44  #define SESSION_PARAM ((yyparse_param_t*) yyparse_param)  #define SESSION_PARAM ((yyparse_param_t*) yyparse_param)
45  #define INCREMENT_LINE { SESSION_PARAM->iLine++; SESSION_PARAM->iColumn = 0; }  #define INCREMENT_LINE { SESSION_PARAM->iLine++; SESSION_PARAM->iColumn = 0; sParsed.clear(); }
46    
47  // clears input buffer  // clears input buffer
48  void restart(yyparse_param_t* pparam, int& yychar);  void restart(yyparse_param_t* pparam, int& yychar);
# Line 51  static char buf[1024]; // input buffer t Line 52  static char buf[1024]; // input buffer t
52  static int bytes = 0;  // current number of characters in the input buffer  static int bytes = 0;  // current number of characters in the input buffer
53  static int ptr   = 0;  // current position in the input buffer  static int ptr   = 0;  // current position in the input buffer
54  static String sLastError; // error message of the last error occured  static String sLastError; // error message of the last error occured
55    static String sParsed; ///< Characters of current line which have already been shifted (consumed/parsed) by the parser.
56    
57  // external reference to the function which actually reads from the socket  // external reference to the function which actually reads from the socket
58  extern int GetLSCPCommand( void *buf, int max_size);  extern int GetLSCPCommand( void *buf, int max_size);
# Line 81  int yylex(YYSTYPE* yylval) { Line 83  int yylex(YYSTYPE* yylval) {
83      const char c = buf[ptr++];      const char c = buf[ptr++];
84      // increment current reading position (just for verbosity / messages)      // increment current reading position (just for verbosity / messages)
85      GetCurrentYaccSession()->iColumn++;      GetCurrentYaccSession()->iColumn++;
86        sParsed += c;
87      // we have to handle "normal" and "extended" ASCII characters separately      // we have to handle "normal" and "extended" ASCII characters separately
88      if (isExtendedAsciiChar(c)) {      if (isExtendedAsciiChar(c)) {
89          // workaround for characters with ASCII code higher than 127          // workaround for characters with ASCII code higher than 127
# Line 103  int octalsToNumber(char oct_digit0, char Line 106  int octalsToNumber(char oct_digit0, char
106    
107  }  }
108    
 // we provide our own version of yyerror() so we don't have to link against the yacc library  
 void yyerror(const char* s);  
 void yyerror(void* x, const char* s) { yyerror(s); }  
   
109  using namespace LinuxSampler;  using namespace LinuxSampler;
110    
111    static std::set<String> yyExpectedSymbols();
112    
113    /**
114     * Will be called when an error occured (usually syntax error).
115     *
116     * We provide our own version of yyerror() so we a) don't have to link against
117     * the yacc library and b) can render more helpful syntax error messages.
118     */
119    void yyerror(void* x, const char* s) {
120        yyparse_param_t* param = GetCurrentYaccSession();
121    
122        // get the text part already parsed (of current line)
123        const bool bContainsLineFeed =
124            sParsed.find('\r') != std::string::npos ||
125            sParsed.find('\n') != std::string::npos;
126        // remove potential line feed characters
127        if (bContainsLineFeed) {
128            for (size_t p = sParsed.find('\r'); p != std::string::npos;
129                 p = sParsed.find('\r')) sParsed.erase(p);
130            for (size_t p = sParsed.find('\n'); p != std::string::npos;
131                 p = sParsed.find('\n')) sParsed.erase(p);
132        }
133    
134        // start assembling the error message with Bison's own message
135        String txt = s;
136    
137        // append exact position info of syntax error
138        txt += (" (line:"  + ToString(param->iLine+1)) +
139               (",column:" + ToString(param->iColumn)) + ")";
140    
141        // append the part of the lined that has already been parsed
142        txt += ". Context: \"" + sParsed;
143        if (txt.empty() || bContainsLineFeed)
144            txt += "^";
145        else
146            txt.insert(txt.size() - 1, "^");
147        txt += "...\"";
148    
149        // append the non-terminal symbols expected now/next
150        std::set<String> expectedSymbols = yyExpectedSymbols();
151        for (std::set<String>::const_iterator it = expectedSymbols.begin();
152             it != expectedSymbols.end(); ++it)
153        {
154            if (it == expectedSymbols.begin())
155                txt += " -> Should be: " + *it;
156            else
157                txt += " | " + *it;
158        }
159    
160        dmsg(2,("LSCPParser: %s\n", txt.c_str()));
161        sLastError = txt;
162    }
163    
164  %}  %}
165    
166  // reentrant parser  // reentrant parser
# Line 116  using namespace LinuxSampler; Line 168  using namespace LinuxSampler;
168    
169  %parse-param {void* yyparse_param}  %parse-param {void* yyparse_param}
170    
171    // After entering the yyparse() function, store references to the parser's
172    // state stack, so that we can create more helpful syntax error messages than
173    // Bison (2.x) could do.
174    %initial-action {
175        yyparse_param_t* p = (yyparse_param_t*) yyparse_param;
176        p->ppStackBottom = &yyss;
177        p->ppStackTop    = &yyssp;
178    }
179    
180  // tell bison to spit out verbose syntax error messages  // tell bison to spit out verbose syntax error messages
181  %error-verbose  %error-verbose
182    
# Line 1200  QUIT                  :  'Q''U''I''T' Line 1261  QUIT                  :  'Q''U''I''T'
1261    
1262  %%  %%
1263    
1264    #define DEBUG_BISON_SYNTAX_ERROR_WALKER 0
1265    
1266  /**  /**
1267   * Will be called when an error occured (usually syntax error).   * Internal function, only called by yyExpectedSymbols(). It is given a Bison
1268     * parser state stack, reflecting the parser's entire state at a certain point,
1269     * i.e. when a syntax error occured. This function will then walk ahead the
1270     * potential parse tree starting from the current head of the given state
1271     * stack. This function will call itself recursively to scan the individual
1272     * parse tree branches. As soon as it hits on the next non-terminal grammar
1273     * symbol in one parse tree branch, it adds the found non-terminal symbol to
1274     * @a expectedSymbols and aborts scanning the respective tree branch further.
1275     * If any local parser state is reached a second time, the respective parse
1276     * tree is aborted to avoid any endless recursion.
1277     *
1278     * @param stack - Bison (yacc) state stack
1279     * @param expectedSymbols - will be filled with next expected grammar symbols
1280     * @param depth - just for internal debugging purposes
1281   */   */
1282  void yyerror(const char* s) {  static void walkAndFillExpectedSymbols(std::vector<YYTYPE_INT16>& stack, std::set<String>& expectedSymbols, int depth = 0) {
1283    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1284        printf("\n");
1285        for (int i = 0; i < depth; ++i) printf("\t");
1286        printf("State stack:");
1287        for (int i = 0; i < stack.size(); ++i) {
1288            printf(" %d", stack[i]);
1289        }
1290        printf("\n");
1291    #endif
1292    
1293        if (stack.empty()) return;
1294    
1295        int state = stack[stack.size() - 1];
1296        int n = yypact[state];
1297        if (n == YYPACT_NINF) { // default reduction required ...
1298            // get default reduction rule for this state
1299            n = yydefact[state];
1300            if (n <= 0 || n >= YYNRULES) return; // no rule, something is wrong
1301            // return the new resolved expected symbol (left-hand symbol of grammar
1302            // rule), then we're done in this state
1303            expectedSymbols.insert(yytname[yyr1[n]]);
1304            return;
1305        }
1306        if (!(YYPACT_NINF < n && n <= YYLAST)) return;
1307    
1308    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1309        for (int i = 0; i < depth; ++i) printf("\t");
1310        printf("Expected tokens:");
1311    #endif
1312        int begin = n < 0 ? -n : 0;
1313        int checklim = YYLAST - n + 1;
1314        int end = checklim < YYNTOKENS ? checklim : YYNTOKENS;
1315        int rule, action, stackSize;
1316        for (int token = begin; token < end; ++token) {
1317            if (token == YYTERROR || yycheck[n + token] != token) continue;
1318    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1319            printf(" %s", yytname[token]);
1320    #endif
1321    
1322            //if (yycheck[n + token] != token) goto default_reduction;
1323    
1324            action = yytable[n + token];
1325            if (action == 0 || action == YYTABLE_NINF) {
1326    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1327                printf(" (invalid action) "); fflush(stdout);
1328    #endif
1329                continue; // error, ignore
1330            }
1331            if (action < 0) { // reduction with rule -action required ...
1332    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1333                printf(" (reduction) "); fflush(stdout);
1334    #endif
1335                rule = -action;
1336                goto reduce;
1337            }
1338            if (action == YYFINAL) continue; // "accept" state, we don't care about it here
1339    
1340            // "shift" required ...
1341    
1342            if (std::find(stack.begin(), stack.end(), action) != stack.end())
1343                continue; // duplicate state, ignore it to avoid endless recursions
1344    
1345            // "shift" / push the new state on the state stack and call this
1346            // function recursively, and restore the stack after the recurse return
1347            stackSize = stack.size();
1348            stack.push_back(action);
1349            walkAndFillExpectedSymbols( //FIXME: could cause stack overflow (should be a loop instead), is probably fine with our current grammar though
1350                stack, expectedSymbols, depth + 1
1351            );
1352            stack.resize(stackSize); // restore stack
1353            continue;
1354    
1355        //default_reduction: // resolve default reduction for this state
1356        //    printf(" (default red.) "); fflush(stdout);
1357        //    rule = yydefact[state];
1358    
1359        reduce: // "reduce" required
1360    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1361            printf(" (reduce by %d) ", rule); fflush(stdout);
1362    #endif
1363            if (rule == 0 || rule >= YYNRULES) continue; // invalid rule, something is wrong
1364            // store the left-hand symbol of the grammar rule
1365            expectedSymbols.insert(yytname[yyr1[rule]]);
1366    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1367            printf(" (SYM %s) ", yytname[yyr1[rule]]); fflush(stdout);
1368    #endif
1369        }
1370    #if DEBUG_BISON_SYNTAX_ERROR_WALKER
1371        printf("\n");
1372    #endif
1373    }
1374    
1375    /**
1376     * Should only be called on syntax errors: returns a set of non-terminal
1377     * symbols expected to appear now/next, just at the point where the syntax
1378     * error appeared.
1379     */
1380    static std::set<String> yyExpectedSymbols() {
1381        std::set<String> result;
1382      yyparse_param_t* param = GetCurrentYaccSession();      yyparse_param_t* param = GetCurrentYaccSession();
1383      String msg = s      YYTYPE_INT16* ss = (*param->ppStackBottom);
1384          + (" (line:"   + ToString(param->iLine+1))      YYTYPE_INT16* sp = (*param->ppStackTop);
1385          + ( ",column:" + ToString(param->iColumn))      int iStackSize   = sp - ss + 1;
1386          + ")";      // copy and wrap parser's state stack into a convenient STL container
1387      dmsg(2,("LSCPParser: %s\n", msg.c_str()));      std::vector<YYTYPE_INT16> stack;
1388      sLastError = msg;      for (int i = 0; i < iStackSize; ++i) {
1389            stack.push_back(ss[i]);
1390        }
1391        // do the actual parser work
1392        walkAndFillExpectedSymbols(stack, result);
1393        return result;
1394  }  }
1395    
1396  namespace LinuxSampler {  namespace LinuxSampler {
# Line 1222  void restart(yyparse_param_t* pparam, in Line 1402  void restart(yyparse_param_t* pparam, in
1402      bytes = 0;      bytes = 0;
1403      ptr   = 0;      ptr   = 0;
1404      sLastError = "";      sLastError = "";
1405        sParsed = "";
1406  }  }
1407    
1408  }  }

Legend:
Removed from v.2509  
changed lines
  Added in v.2510

  ViewVC Help
Powered by ViewVC