Răsfoiți Sursa

Implemented syntax error location tracking and fixed compiler warnings.

Sander Mathijs van Veen 14 ani în urmă
părinte
comite
930b44e002
2 a modificat fișierele cu 95 adăugiri și 56 ștergeri
  1. 65 36
      src/pyrex/bison_.pyx
  2. 30 20
      src/python/bison.py

+ 65 - 36
src/pyrex/bison_.pyx

@@ -278,8 +278,10 @@ cdef class ParserEngine:
 
         s += '          {\n'
         s += '            PyObject* obj = PyErr_Occurred();\n'
-        s += '            if (obj)\n'
+        s += '            if (obj) {\n'
+        s += '              //yyerror("exception raised");\n'
         s += '              YYERROR;\n'
+        s += '            }\n'
         s += '          }\n'
 
         return s
@@ -333,27 +335,46 @@ cdef class ParserEngine:
         #writelines = f.writelines
 
         # grammar file prologue
-        write("\n".join([
-            "%{",
+        write('\n'.join([
+            '%code top {',
             '',
             '#include "Python.h"',
-            "extern FILE *yyin;",
-            "extern int yylineno;"
-            "extern char *yytext;",
-            "#define YYSTYPE void*",
+            'extern FILE *yyin;',
+            #'extern int yylineno;'
+            'extern char *yytext;',
+            '#define YYSTYPE void*',
             #'extern void *py_callback(void *, char *, int, void*, ...);',
             'void *(*py_callback)(void *, char *, int, int, ...);',
             'void (*py_input)(void *, char *, int *, int);',
             'void *py_parser;',
             'char *rules_hash = "%s";' % self.parserHash,
+            '#define YYERROR_VERBOSE 1',
             '',
-            "%}",
+            '}',
+            '',
+            '%code requires {',
+            '',
+            '#define YYLTYPE YYLTYPE',
+            'typedef struct YYLTYPE',
+            '{',
+            '  int first_line;',
+            '  int first_column;',
+            '  int last_line;',
+            '  int last_column;',
+            '  char *filename;',
+            '} YYLTYPE;',
+            #'',
+            #'YYLTYPE yylloc; /* location data */'
+            '',
+            '}',
+            '',
+            '%locations',
             '',
             ]))
 
         # write out tokens and start target dec
-        write("%%token %s\n\n" % " ".join(gTokens))
-        write("%%start %s\n\n" % gStart)
+        write('%%token %s\n\n' % ' '.join(gTokens))
+        write('%%start %s\n\n' % gStart)
 
         # write out precedences
         for p in gPrecedences:
@@ -448,44 +469,52 @@ cdef class ParserEngine:
             except:
                 traceback.print_exc()
 
-        write("\n\n%%\n\n")
+        write('\n\n%%\n\n')
 
         # now generate C code
-        epilogue = "\n".join([
+        epilogue = '\n'.join([
             'void do_parse(void *parser1,',
-            '              void *(*cb)(void *, char *, int, int, void *, ...),',
+            '              void *(*cb)(void *, char *, int, int, ...),',
             '              void (*in)(void *, char*, int *, int),',
             '              int debug',
             '              )',
             '{',
-            '   //printf("Not calling yyparse\\n");',
-            '   //return;',
             '   py_callback = cb;',
             '   py_input = in;',
-            "   py_parser = parser1;",
-            "   yydebug = debug;",
-            "   //yyin = stdin;",
-            '   //printf("calling yyparse(), in=0x%lx\\n", py_input);',
-            "   yyparse();",
-            '   //printf("Back from parser\\n");',
-            "}",
-            "int yyerror(char *mesg)",
-            "{",
-            '  //printf("yytext=0x%lx\\n", yytext);',
-            '  PyObject *args = PyTuple_New(3);',
-            '  int ret;',
+            '   py_parser = parser1;',
+            '   yydebug = debug;',
+            '   yyparse();',
+            '}',
+            '',
+            'int yyerror(char *msg)',
+            '{',
+            '  PyObject *fn = PyObject_GetAttrString((PyObject *)py_parser,',
+            '                                        "report_syntax_error");',
+            '  if (!fn)',
+            '      return 1;',
+            '',
+            '  PyObject *args;',
+            '  args = Py_BuildValue("(s,s,i,i,i,i)", msg, yytext,',
+            '                       yylloc.first_line, yylloc.first_column,',
+            '                       yylloc.last_line, yylloc.last_column);',
+            '',
+            '  if (!args)',
+            '      return 1;',
+            #'',
+            #'  fprintf(stderr, "%d.%d-%d.%d: error: \'%s\' before \'%s\'.",',
+            #'          yylloc.first_line, yylloc.first_column,',
+            #'          yylloc.last_line, yylloc.last_column, msg, yytext);',
             '',
-            '  PyTuple_SetItem(args, 0, PyInt_FromLong(yylineno+1));',
-            '  PyTuple_SetItem(args, 1, PyString_FromString(mesg));',
-            '  PyTuple_SetItem(args, 2, PyString_FromString(yytext));',
+            '  PyObject *res = PyObject_CallObject(fn, args);',
+            '  Py_DECREF(args);',
             '',
-            '  ret = PyObject_SetAttrString((PyObject *)py_parser, "last_error", args);',
-            '  //printf("PyObject_SetAttrString: %d\\n", ret);',
+            '  if (!res)',
+            '      return 1;',
             '',
-            '  //printf("line %d: %s before %s\\n", yylineno+1, mesg, yytext);',
-            "  //exit(0);",
-            "}",
-            ]) + "\n"
+            '  Py_DECREF(res);',
+            '  return 0;',
+            '}',
+            ]) + '\n'
         write(epilogue)
 
         # done with grammar file

+ 30 - 20
src/python/bison.py

@@ -24,8 +24,11 @@ from bison_ import ParserEngine
 from .node import BisonNode
 
 class BisonSyntaxError(Exception):
-    pass
+    def __init__(self, msg, args):
+        super(BisonSyntaxError, self).__init__(msg)
 
+        self.first_line, self.first_col, self.last_line, self.last_col, \
+                self.message, self.token_value = args
 
 class TimeoutError(Exception):
     pass
@@ -184,8 +187,9 @@ class BisonParser(object):
             if self.verbose:
                 print 'no handler for %s, using default' % targetname
 
-            self.last = self.default_node_class(targetname, option=option,
-                                                names=names, values=values)
+            cls = self.default_node_class
+            self.last = cls(target=targetname, option=option, names=names,
+                            values=values)
 
         # assumedly the last thing parsed is at the top of the tree
         return self.last
@@ -299,26 +303,32 @@ class BisonParser(object):
 
         """
 
-        if filename != None:
-            msg = '%s:%d: "%s" near "%s"' \
-                    % ((filename,) + error)
+        #if filename != None:
+        #    msg = '%s:%d: "%s" near "%s"' \
+        #            % ((filename,) + error)
 
-            if not self.interactive:
-                raise BisonSyntaxError(msg)
+        #    if not self.interactive:
+        #        raise BisonSyntaxError(msg)
 
-            print >>sys.stderr, msg
-        elif hasattr(error, '__getitem__') and isinstance(error[0], int):
-            msg = 'Line %d: "%s" near "%s"' % error
+        #    print >>sys.stderr, msg
+        #elif hasattr(error, '__getitem__') and isinstance(error[0], int):
+        #    msg = 'Line %d: "%s" near "%s"' % error
 
-            if not self.interactive:
-                raise BisonSyntaxError(msg)
+        #    if not self.interactive:
+        #        raise BisonSyntaxError(msg)
 
-            print >>sys.stderr, msg
-        else:
-            if not self.interactive:
-                raise
+        #    print >>sys.stderr, msg
+        #else:
+        if not self.interactive:
+            raise
 
-            if self.verbose:
-                traceback.print_exc()
+        if self.verbose:
+            traceback.print_exc()
+
+        print 'ERROR:', error
 
-            print 'ERROR:', error
+    def report_syntax_error(self, msg, yytext, first_line, first_col,
+            last_line, last_col):
+        yytext = yytext.replace('\n', '\\n')
+        args = (first_line, first_col, last_line, last_col, msg, yytext)
+        raise BisonSyntaxError('%d.%d-%d.%d: "%s" near "%s".' % args, args)