Hello community,

here is the log from the commit of package python3-spark_parser for 
openSUSE:Factory checked in at 2017-02-22 13:56:35
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python3-spark_parser (Old)
 and      /work/SRC/openSUSE:Factory/.python3-spark_parser.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python3-spark_parser"

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python3-spark_parser/python3-spark_parser.changes    
    2016-12-06 14:27:17.000000000 +0100
+++ 
/work/SRC/openSUSE:Factory/.python3-spark_parser.new/python3-spark_parser.changes
   2017-02-22 13:56:37.513932318 +0100
@@ -1,0 +2,17 @@
+Tue Feb 21 05:10:05 UTC 2017 - [email protected]
+
+- specfile:
+  * update copyright year
+  * require click
+  * added binary spark-parser-coverage
+
+- update to version 1.6.0:
+  * Add ability to track grammar coverage
+  * Add ability to remove grammar rules
+
+- changes from version 1.5.2:
+  * Fix bug in dumpGrammar()
+  * print routines take an optional I/O parameter
+  * Correct Jay Earley's name
+
+-------------------------------------------------------------------

Old:
----
  spark_parser-1.5.1.tar.gz

New:
----
  spark_parser-1.6.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python3-spark_parser.spec ++++++
--- /var/tmp/diff_new_pack.1E8rZZ/_old  2017-02-22 13:56:38.117846406 +0100
+++ /var/tmp/diff_new_pack.1E8rZZ/_new  2017-02-22 13:56:38.121845838 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python3-spark_parser
 #
-# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,16 +17,18 @@
 
 
 Name:           python3-spark_parser
-Version:        1.5.1
+Version:        1.6.0
 Release:        0
 Summary:        An Early-Algorithm Context-free grammar Parser
 License:        MIT
 Group:          Development/Languages/Python
 Url:            https://github.com/rocky/python3-spark/
 Source:         
https://files.pythonhosted.org/packages/source/s/spark_parser/spark_parser-%{version}.tar.gz
+BuildRequires:  python3-click
 BuildRequires:  python3-devel
 BuildRequires:  python3-nose
 BuildRequires:  python3-setuptools
+Requires:       python3-click
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 BuildArch:      noarch
 
@@ -58,6 +60,7 @@
 %files
 %defattr(-,root,root,-)
 %doc LICENSE ChangeLog README.rst
+%{_bindir}/spark-parser-coverage
 %{python3_sitelib}/*
 
 %changelog

++++++ spark_parser-1.5.1.tar.gz -> spark_parser-1.6.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/ChangeLog 
new/spark_parser-1.6.0/ChangeLog
--- old/spark_parser-1.5.1/ChangeLog    2016-11-28 13:09:23.000000000 +0100
+++ new/spark_parser-1.6.0/ChangeLog    2017-02-01 00:58:47.000000000 +0100
@@ -1,6 +1,76 @@
+2017-01-31  rocky <[email protected]>
+
+       * ChangeLog: Get ready for release
+
+2017-01-30  rocky <[email protected]>
+
+       * NEW-FEATURES.rst, NEWS, spark_parser/spark.py,
+       spark_parser/version.py: Add the ability to remove a rule Go over new 
features and over NEWS
+
+2017-01-29  rocky <[email protected]>
+
+       * spark_parser/spark.py: Reinstate extra debug_reduce parameters
+
+2017-01-29  rocky <[email protected]>
+
+       * spark_parser/spark.py: function parameter mismatch
+
+2017-01-29  rocky <[email protected]>
+
+       * bin/spark-parser-coverage, spark_parser/spark.py: Fix bugs in
+       accumulating coverage counts
+
+2017-01-29  rocky <[email protected]>
+
+       * bin/spark-parser-coverage, setup.py, spark_parser/spark.py,
+       test/test_spark.py: More coverage code
+
+2017-01-28  rocky <[email protected]>
+
+       * spark_parser/spark.py, test/test_spark.py: Banc on coverage a
+       little more
+
+2017-01-28  rocky <[email protected]>
+
+       * spark_parser/__init__.py, spark_parser/spark.py,
+       test/test_spark.py: Start tracking grammar coverage
+
+2017-01-27  rocky <[email protected]>
+
+       * setup.py: Works on 3.6.0
+
+2017-01-27  rocky <[email protected]>
+
+       * ChangeLog, NEWS, example/expr2/eval.py,
+       example/python2/test/format/if.py,
+       example/python2/test/parse/if.py,
+       example/python2/test/parse/if.right, spark_parser/version.py: Get
+       ready for release 1.5.2 Some lint changes
+
+2017-01-27  rocky <[email protected]>
+
+       * spark_parser/spark.py, test/test_misc.py: Fix bug in dumpGrammar()
+
+2016-12-08  R. Bernstein <[email protected]>
+
+       * NEW-FEATURES.rst: Update NEW-FEATURES.rst
+
+2016-12-08  R. Bernstein <[email protected]>
+
+       * NEW-FEATURES.rst: Update NEW-FEATURES.rst
+
+2016-12-08  rocky <[email protected]>
+
+       * README.rst, setup.py: Correct Jay EarlEy's name
+
 2016-11-28  rocky <[email protected]>
 
-       * setup.py, spark_parser/version.py: Get ready for release 1.5.1
+       * ChangeLog, NEWS, example/expr2/eval.py,
+       example/python2/py2_format.py, example/python2/py2_parser.py,
+       example/python2/test/scan/indent1.py,
+       example/python2/test/scan/indent1.right, setup.py,
+       spark_parser/version.py, test/test_misc.py: Get ready for release
+       1.5.1 Lint some files
 
 2016-11-28  R. Bernstein <[email protected]>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/NEW-FEATURES.rst 
new/spark_parser-1.6.0/NEW-FEATURES.rst
--- old/spark_parser-1.5.1/NEW-FEATURES.rst     2016-11-28 12:49:08.000000000 
+0100
+++ new/spark_parser-1.6.0/NEW-FEATURES.rst     2017-02-01 01:11:28.000000000 
+0100
@@ -1,4 +1,7 @@
-The original version of this Early parser, circa 2000, was pretty awesome for 
its
+Introduction
+============
+
+The original version of this Earley parser, circa 2000, was pretty awesome for 
its
 age.  It was remarkably fast, and small: one Python file. Some care
 was put into making it run fast.
 
@@ -13,6 +16,9 @@
 Many of the changes I've added come from using the program. I list
 features aside from the packaging, tests, and examples mentioned above.
 
+Comments in Grammar
+===================
+
 The first thing I desired when working with a large complex grammar
 and then sets of grammars that I found desirable was the ability to
 comment the grammar. For example:
@@ -23,6 +29,9 @@
     term := term MULT_OP atom
 
 
+AST Tree Display
+================
+
 Next, I needed better AST tree display routines. For example:
 
 .. code-block::
@@ -43,19 +52,32 @@
         2. atom
           type: NUMBER, value: '2'
 
-After that it was useful to have grammar checking routines,
-specifically, the ability to find unused left-hand-side nonterminals
-that are not either the start symbol or used on the right-hand side.
-Likewise unused nonterminals (lower-case symbols) that appear on the
-right-hand side that are not defined as a rule. Of course, tokens or
-upper-case symbols are ok.
+Grammar Checking
+================
+
+In a large project like uncompyle6_ there are lots of grammar rules:
+over 600 rules for each of the 13 or so versions of Python.
+
+It is very easy to create nonsensical grammar rules, so we need to
+have a way to check the grammar.  Partuclarly useful is the ability to
+find unused left-hand-side nonterminals that are not either the start
+symbol or used on the right-hand side.  Likewise unused nonterminals
+(lower-case symbols) that appear on the right-hand side that are not
+defined as a rule. Of course, tokens or upper-case symbols are ok.
+
+Checking for duplicate rules is also handy. Also finding immediate
+recursion rules. e.g. `expr ::= expr`.
 
-Checking for duplicate rules was also handy.
+Parser Error State
+==================
 
 The original code showed you the how far you had parsed and that was
 useful. But in production code you often want more. So I added the
 list of rule states of the current state. I won't show that here.
 
+Reduce Rule Tracing
+===================
+
 However also added was the ability to dump rules as reductions
 occurred. Here is an example of that from uncompyle6:
 
@@ -107,12 +129,38 @@
 printing program. The same can be done for duplicate-rule printing
 and other things like that.
 
+Custom Additional Reduction Rule Checks
+=======================================
+
 More recently, I the ability to callback before each reduction so
 additional checks can be peformed before a reduction. In an ambiguous
 grammar useful as it helps distinguish which rule should be used among
 many.
 
-Lastly, I've added a little syntactic sugar for the Kleene closure
+Here are some little examples from the project *uncompyle6* which
+deparses Python bytecode. There is a rule in the grammar for a keyword
+argument that's used in a parameter list of a function.
+for example the `path=` in `os.path.exists(path='/etc/hosts')`
+
+This grammar rule is:
+
+.. code-block::
+
+   kwarg ::= LOAD_CONST expr
+
+
+But there is an additional restriction that the value in the
+`LOAD_CONST` can't be any old value; it must be a "string" (which
+would have the value "path") in the previous example.
+
+The reduction rule checking can work at a strickly token level, or it
+can work on and AST tree that would be generated if the reduction were done.
+
+
+Limited Grammar Shorthands: \+, \*, ?
+=====================================
+
+I also added a little syntactic sugar for the Kleene closure
 operators `+`, `*` and optional suffix `?`. It is limited to only one
 nonterminal on the right-hand side, but that does come up often and
 helps a little. So you can now do things like:
@@ -143,6 +191,27 @@
 .. code-block::
 
      opt_comma ::= COMMA
-     ratings ::=
+     opt_comma ::=
 
 respectively.
+
+Tracking Grammar Coverage
+==========================
+
+Again in *uncompyle6* there are lots of grammar rules, so it is very 
+easy to have dead grammar rules that never get used. And
+grammar constructs from one version of Python can easily bleed into
+another version. By looking at grammar coverage over a large set of
+parses, I can prune grammar rules or segregate them. I can also craft
+smaller parse tests which cover more of the grammar in fewer Python
+statements
+
+Removing Grammar Rules
+======================
+
+This may sound like a weird thing to want. But in a program like
+*uncompyle6* where there is a lot of grammar sharing via inheritance
+sometimes the grammar inherited is too large. This gives me a way
+to prune the grammar back down.
+
+.. _uncompyle6: https://pypi.python.org/pypi/uncompyle6/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/PKG-INFO 
new/spark_parser-1.6.0/PKG-INFO
--- old/spark_parser-1.5.1/PKG-INFO     2016-11-28 13:30:14.000000000 +0100
+++ new/spark_parser-1.6.0/PKG-INFO     2017-02-01 01:13:19.000000000 +0100
@@ -1,7 +1,7 @@
 Metadata-Version: 1.1
 Name: spark_parser
-Version: 1.5.1
-Summary: An Early-Algorithm Context-free grammar Parser Toolkit
+Version: 1.6.0
+Summary: An Earley-Algorithm Context-free grammar Parser Toolkit
 Home-page: https://github.com/rocky/python-spark/
 Author: Rocky Bernstein
 Author-email: [email protected]
@@ -12,7 +12,7 @@
         =====
         
         SPARK stands for Scanning, Parsing, and Rewriting Kit. It uses Jay
-        Early's algorithm for parsing context free grammars, and comes with
+        Earley's algorithm for parsing context free grammars, and comes with
         some generic Abstract Syntax Tree routines. There is also a prototype
         scanner which does its job by combining Python regular expressions.
         
@@ -22,7 +22,7 @@
         current incarnation of this code is maintained (or not) by Rocky
         Bernstein.
         
-        Note: Early algorithm parsers are almost linear when given an LR 
grammar.
+        Note: Earley algorithm parsers are almost linear when given an LR 
grammar.
         These are grammars which are left-recursive.
         
         Installation
@@ -71,5 +71,6 @@
 Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
 Classifier: Topic :: Software Development :: Code Generators
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/README.rst 
new/spark_parser-1.6.0/README.rst
--- old/spark_parser-1.5.1/README.rst   2016-11-28 13:06:42.000000000 +0100
+++ new/spark_parser-1.6.0/README.rst   2017-01-30 07:40:22.000000000 +0100
@@ -4,7 +4,7 @@
 =====
 
 SPARK stands for Scanning, Parsing, and Rewriting Kit. It uses Jay
-Early's algorithm for parsing context free grammars, and comes with
+Earley's algorithm for parsing context free grammars, and comes with
 some generic Abstract Syntax Tree routines. There is also a prototype
 scanner which does its job by combining Python regular expressions.
 
@@ -14,7 +14,7 @@
 current incarnation of this code is maintained (or not) by Rocky
 Bernstein.
 
-Note: Early algorithm parsers are almost linear when given an LR grammar.
+Note: Earley algorithm parsers are almost linear when given an LR grammar.
 These are grammars which are left-recursive.
 
 Installation
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/bin/spark-parser-coverage 
new/spark_parser-1.6.0/bin/spark-parser-coverage
--- old/spark_parser-1.5.1/bin/spark-parser-coverage    1970-01-01 
01:00:00.000000000 +0100
+++ new/spark_parser-1.6.0/bin/spark-parser-coverage    2017-01-30 
07:40:22.000000000 +0100
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+from __future__ import print_function
+import click
+import pickle
+
+def sort_profile_info(path, max_count=1000):
+    profile_info = pickle.load(open(path, "rb"))
+    items = sorted(profile_info.items(),
+                   key=lambda kv: kv[1],
+                   reverse=False)
+    return [item for item in items if item[1] <= max_count]
+
+DEFAULT_COVERAGE_FILE = "/tmp/spark-grammar.cover",
+DEFAULT_COUNT = 100
[email protected]()
[email protected]('--path', type=str, default=DEFAULT_COVERAGE_FILE,
+              help=("grammar coverage file (default %s)" % 
DEFAULT_COVERAGE_FILE))
[email protected]('--max-count', type=int, default=DEFAULT_COUNT,
+              help=("limit output to rules having no more than this many hits 
(default %d)" % DEFAULT_COUNT))
+def run(path, max_count):
+    """Print grammar reduce statistics for a series of spark-parser parses
+    """
+    for rule, count in sort_profile_info(path, max_count):
+        print("%d: %s" % (count, rule))
+        pass
+    return
+
+if __name__ == '__main__':
+    run()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/example/expr2/eval.py 
new/spark_parser-1.6.0/example/expr2/eval.py
--- old/spark_parser-1.5.1/example/expr2/eval.py        2016-11-28 
13:24:41.000000000 +0100
+++ new/spark_parser-1.6.0/example/expr2/eval.py        2017-01-30 
07:40:22.000000000 +0100
@@ -91,7 +91,7 @@
     parsed = parse_expr(expr_str, show_tokens=show_tokens,
                         parser_debug=parser_debug)
     if showast:
-        print (parsed)
+        print(parsed)
 
     assert parsed == 'expr', 'Should have parsed grammar start'
 
Binary files 
old/spark_parser-1.5.1/example/python2/__pycache__/py2_format.cpython-33.pyc 
and 
new/spark_parser-1.6.0/example/python2/__pycache__/py2_format.cpython-33.pyc 
differ
Binary files 
old/spark_parser-1.5.1/example/python2/__pycache__/py2_format.cpython-34.pyc 
and 
new/spark_parser-1.6.0/example/python2/__pycache__/py2_format.cpython-34.pyc 
differ
Binary files 
old/spark_parser-1.5.1/example/python2/__pycache__/py2_format.cpython-35.pyc 
and 
new/spark_parser-1.6.0/example/python2/__pycache__/py2_format.cpython-35.pyc 
differ
Binary files 
old/spark_parser-1.5.1/example/python2/__pycache__/py2_format.cpython-36.pyc 
and 
new/spark_parser-1.6.0/example/python2/__pycache__/py2_format.cpython-36.pyc 
differ
Binary files 
old/spark_parser-1.5.1/example/python2/__pycache__/py2_parser.cpython-33.pyc 
and 
new/spark_parser-1.6.0/example/python2/__pycache__/py2_parser.cpython-33.pyc 
differ
Binary files 
old/spark_parser-1.5.1/example/python2/__pycache__/py2_parser.cpython-34.pyc 
and 
new/spark_parser-1.6.0/example/python2/__pycache__/py2_parser.cpython-34.pyc 
differ
Binary files 
old/spark_parser-1.5.1/example/python2/__pycache__/py2_parser.cpython-35.pyc 
and 
new/spark_parser-1.6.0/example/python2/__pycache__/py2_parser.cpython-35.pyc 
differ
Binary files 
old/spark_parser-1.5.1/example/python2/__pycache__/py2_parser.cpython-36.pyc 
and 
new/spark_parser-1.6.0/example/python2/__pycache__/py2_parser.cpython-36.pyc 
differ
Binary files 
old/spark_parser-1.5.1/example/python2/__pycache__/py2_scan.cpython-33.pyc and 
new/spark_parser-1.6.0/example/python2/__pycache__/py2_scan.cpython-33.pyc 
differ
Binary files 
old/spark_parser-1.5.1/example/python2/__pycache__/py2_scan.cpython-34.pyc and 
new/spark_parser-1.6.0/example/python2/__pycache__/py2_scan.cpython-34.pyc 
differ
Binary files 
old/spark_parser-1.5.1/example/python2/__pycache__/py2_scan.cpython-35.pyc and 
new/spark_parser-1.6.0/example/python2/__pycache__/py2_scan.cpython-35.pyc 
differ
Binary files 
old/spark_parser-1.5.1/example/python2/__pycache__/py2_scan.cpython-36.pyc and 
new/spark_parser-1.6.0/example/python2/__pycache__/py2_scan.cpython-36.pyc 
differ
Binary files 
old/spark_parser-1.5.1/example/python2/__pycache__/py2_token.cpython-36.pyc and 
new/spark_parser-1.6.0/example/python2/__pycache__/py2_token.cpython-36.pyc 
differ
Binary files old/spark_parser-1.5.1/example/python2/py2_format.pyc and 
new/spark_parser-1.6.0/example/python2/py2_format.pyc differ
Binary files old/spark_parser-1.5.1/example/python2/py2_parser.pyc and 
new/spark_parser-1.6.0/example/python2/py2_parser.pyc differ
Binary files old/spark_parser-1.5.1/example/python2/py2_scan.pyc and 
new/spark_parser-1.6.0/example/python2/py2_scan.pyc differ
Binary files 
old/spark_parser-1.5.1/example/python2/test/__pycache__/helper.cpython-33.pyc 
and 
new/spark_parser-1.6.0/example/python2/test/__pycache__/helper.cpython-33.pyc 
differ
Binary files 
old/spark_parser-1.5.1/example/python2/test/__pycache__/helper.cpython-34.pyc 
and 
new/spark_parser-1.6.0/example/python2/test/__pycache__/helper.cpython-34.pyc 
differ
Binary files 
old/spark_parser-1.5.1/example/python2/test/__pycache__/helper.cpython-35.pyc 
and 
new/spark_parser-1.6.0/example/python2/test/__pycache__/helper.cpython-35.pyc 
differ
Binary files 
old/spark_parser-1.5.1/example/python2/test/__pycache__/helper.cpython-36.pyc 
and 
new/spark_parser-1.6.0/example/python2/test/__pycache__/helper.cpython-36.pyc 
differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/example/python2/test/format/if.py 
new/spark_parser-1.6.0/example/python2/test/format/if.py
--- old/spark_parser-1.5.1/example/python2/test/format/if.py    2016-11-28 
12:49:08.000000000 +0100
+++ new/spark_parser-1.6.0/example/python2/test/format/if.py    2017-01-30 
07:40:22.000000000 +0100
@@ -12,13 +12,13 @@
 pass
 
 if True:
-   pass
+    pass
 elif False:
-   pass
+    pass
 
 if True:
-   pass
+    pass
 elif False:
-   pass
+    pass
 else:
-   pass
+    pass
Binary files old/spark_parser-1.5.1/example/python2/test/helper.pyc and 
new/spark_parser-1.6.0/example/python2/test/helper.pyc differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/example/python2/test/parse/if.py 
new/spark_parser-1.6.0/example/python2/test/parse/if.py
--- old/spark_parser-1.5.1/example/python2/test/parse/if.py     2016-11-28 
12:49:08.000000000 +0100
+++ new/spark_parser-1.6.0/example/python2/test/parse/if.py     2017-01-30 
07:40:22.000000000 +0100
@@ -10,13 +10,13 @@
 pass
 
 if True:
-   pass
+    pass
 elif False:
-   pass
+    pass
 
 if True:
-   pass
+    pass
 elif False:
-   pass
+    pass
 else:
-   pass
+    pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spark_parser-1.5.1/example/python2/test/parse/if.right 
new/spark_parser-1.6.0/example/python2/test/parse/if.right
--- old/spark_parser-1.5.1/example/python2/test/parse/if.right  2016-11-28 
12:49:08.000000000 +0100
+++ new/spark_parser-1.6.0/example/python2/test/parse/if.right  2017-01-30 
07:40:22.000000000 +0100
@@ -105,11 +105,11 @@
         3. suite (5)
           0. L12.8: NEWLINE: '\n'
           1. indent
-            L13.3: INDENT: '   '
+            L13.4: INDENT: '    '
           2. stmt_plus
             pass_stmt
-              L13.7: PASS: 'pass'
-          3. L13.8: NEWLINE: '\n'
+              L13.8: PASS: 'pass'
+          3. L13.9: NEWLINE: '\n'
           4. L14.4: DEDENT: ''
         4. elif_suites (5)
           0. elif_suites
@@ -124,11 +124,11 @@
           4. suite (5)
             0. L14.11: NEWLINE: '\n'
             1. indent
-              L15.3: INDENT: '   '
+              L15.4: INDENT: '    '
             2. stmt_plus
               pass_stmt
-                L15.7: PASS: 'pass'
-            3. L15.8: NEWLINE: '\n'
+                L15.8: PASS: 'pass'
+            3. L15.9: NEWLINE: '\n'
             4. L17.0: DEDENT: ''
         5. else_suite_opt
     12. newline_or_stmt
@@ -146,11 +146,11 @@
         3. suite (5)
           0. L17.8: NEWLINE: '\n'
           1. indent
-            L18.3: INDENT: '   '
+            L18.4: INDENT: '    '
           2. stmt_plus
             pass_stmt
-              L18.7: PASS: 'pass'
-          3. L18.8: NEWLINE: '\n'
+              L18.8: PASS: 'pass'
+          3. L18.9: NEWLINE: '\n'
           4. L19.4: DEDENT: ''
         4. elif_suites (5)
           0. elif_suites
@@ -165,11 +165,11 @@
           4. suite (5)
             0. L19.11: NEWLINE: '\n'
             1. indent
-              L20.3: INDENT: '   '
+              L20.4: INDENT: '    '
             2. stmt_plus
               pass_stmt
-                L20.7: PASS: 'pass'
-            3. L20.8: NEWLINE: '\n'
+                L20.8: PASS: 'pass'
+            3. L20.9: NEWLINE: '\n'
             4. L21.4: DEDENT: ''
         5. else_suite_opt (3)
           0. L21.4: ELSE: 'else'
@@ -177,10 +177,10 @@
           2. suite (5)
             0. L21.6: NEWLINE: '\n'
             1. indent
-              L22.3: INDENT: '   '
+              L22.4: INDENT: '    '
             2. stmt_plus
               pass_stmt
-                L22.7: PASS: 'pass'
-            3. L22.8: NEWLINE: '\n'
+                L22.8: PASS: 'pass'
+            3. L22.9: NEWLINE: '\n'
             4. L23.1: DEDENT: ''
   1. L23.1: ENDMARKER: '\x04'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/setup.cfg 
new/spark_parser-1.6.0/setup.cfg
--- old/spark_parser-1.5.1/setup.cfg    2016-11-28 13:30:14.000000000 +0100
+++ new/spark_parser-1.6.0/setup.cfg    2017-02-01 01:13:19.000000000 +0100
@@ -1,5 +1,5 @@
 [egg_info]
+tag_svn_revision = 0
 tag_build = 
 tag_date = 0
-tag_svn_revision = 0
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/setup.py 
new/spark_parser-1.6.0/setup.py
--- old/spark_parser-1.5.1/setup.py     2016-11-28 13:08:46.000000000 +0100
+++ new/spark_parser-1.6.0/setup.py     2017-01-30 07:40:22.000000000 +0100
@@ -26,6 +26,7 @@
                 'Programming Language :: Python :: 3.3',
                 'Programming Language :: Python :: 3.4',
                 'Programming Language :: Python :: 3.5',
+                'Programming Language :: Python :: 3.6',
                 'Topic :: Software Development :: Code Generators',
                 'Topic :: Software Development :: Libraries :: Python Modules',
                 ]
@@ -39,7 +40,7 @@
 modname            = 'spark_parser'
 name               = 'spark_parser'
 py_modules         = None
-short_desc         = 'An Early-Algorithm Context-free grammar Parser Toolkit'
+short_desc         = 'An Earley-Algorithm Context-free grammar Parser Toolkit'
 web                = 'https://github.com/rocky/python-spark/'
 
 # tracebacks in zip files are funky and not debuggable
@@ -63,7 +64,7 @@
 setup(
        classifiers        = classifiers,
        description        = short_desc,
-       # install_requires   = install_requires,
+       install_requires   = ['click'],
        license            = license,
        long_description   = long_description,
        maintainer         = maintainer,
@@ -71,6 +72,7 @@
        packages           = find_packages(),
        py_modules         = py_modules,
        name               = name,
+       scripts            = ['bin/spark-parser-coverage'],
        test_suite         = 'nose.collector',
        url                = web,
        tests_require     = ['nose>=1.0'],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/spark_parser/__init__.py 
new/spark_parser-1.6.0/spark_parser/__init__.py
--- old/spark_parser-1.5.1/spark_parser/__init__.py     2016-06-11 
14:14:15.000000000 +0200
+++ new/spark_parser-1.6.0/spark_parser/__init__.py     2017-01-30 
07:40:22.000000000 +0100
@@ -6,21 +6,11 @@
 
 PYTHON3 = (sys.version_info >= (3, 0))
 
-if PYTHON3:
-    from spark_parser.ast import AST as AST
-    from spark_parser.ast import GenericASTTraversal as GenericASTTraversal
-    from spark_parser.ast import GenericASTTraversalPruningException as 
GenericASTTraversalPruningException
-    from spark_parser.spark import DEFAULT_DEBUG
-    from spark_parser.spark import GenericParser as GenericParser
-    from spark_parser.spark import GenericASTBuilder as GenericASTBuilder
-    from spark_parser.scanner import GenericScanner as GenericScanner
-    from spark_parser.scanner import GenericToken as GenericToken
-else:
-    from ast import AST as AST
-    from ast import GenericASTTraversal as GenericASTTraversal
-    from ast import GenericASTTraversalPruningException as 
GenericASTTraversalPruningException
-    from spark import DEFAULT_DEBUG
-    from spark import GenericParser as GenericParser
-    from spark import GenericASTBuilder as GenericASTBuilder
-    from scanner import GenericScanner as GenericScanner
-    from scanner import GenericToken as GenericToken
+from spark_parser.ast import AST as AST
+from spark_parser.ast import GenericASTTraversal as GenericASTTraversal
+from spark_parser.ast import GenericASTTraversalPruningException as 
GenericASTTraversalPruningException
+from spark_parser.spark import DEFAULT_DEBUG
+from spark_parser.spark import GenericParser as GenericParser
+from spark_parser.spark import GenericASTBuilder as GenericASTBuilder
+from spark_parser.scanner import GenericScanner as GenericScanner
+from spark_parser.scanner import GenericToken as GenericToken
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/spark_parser/spark.py 
new/spark_parser-1.6.0/spark_parser/spark.py
--- old/spark_parser-1.5.1/spark_parser/spark.py        2016-11-28 
13:06:42.000000000 +0100
+++ new/spark_parser-1.6.0/spark_parser/spark.py        2017-01-30 
07:41:02.000000000 +0100
@@ -1,5 +1,5 @@
 """
-Copyright (c) 2015-2016 Rocky Bernstein
+Copyright (c) 2015-2017 Rocky Bernstein
 Copyright (c) 1998-2002 John Aycock
 
   Permission is hereby granted, free of charge, to any person obtaining
@@ -22,7 +22,7 @@
   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 """
 
-import os, re, sys
+import os, pickle, re, sys
 
 if sys.version[0:3] <= '2.3':
     from sets import Set as set
@@ -44,7 +44,7 @@
     return namelist
 
 def rule2str(rule):
-    return "%s ::= %s" % (rule[0], ' '.join(rule[1]))
+    return ("%s ::= %s" % (rule[0], ' '.join(rule[1]))).rstrip()
 
 class _State:
     '''
@@ -73,11 +73,28 @@
     Parsing", unpublished paper, 2001.
     '''
 
-    def __init__(self, start, debug=DEFAULT_DEBUG):
+    def __init__(self, start, debug=DEFAULT_DEBUG,
+                 coverage_path=None):
+        """_start_ : grammar start symbol;
+           _debug_ : produce optional parsing debug information
+           _profile_ : if not None should be a file path to open
+           with where to store profile is stored
+        """
+
         self.rules = {}
         self.rule2func = {}
         self.rule2name = {}
 
+        # grammar coverage information
+        self.coverage_path = coverage_path
+        if coverage_path:
+            self.profile_info = {}
+            if isinstance(coverage_path, str):
+                if os.path.exists(coverage_path):
+                    self.profile_info = pickle.load(open(coverage_path, "rb"))
+        else:
+            self.profile_info = None
+
         # When set, shows additional debug output
         self.debug = debug
 
@@ -121,15 +138,15 @@
         #
         #  XXX - should find a better way to do this..
         #
-        changes = 1
+        changes = True
         while changes:
-            changes = 0
+            changes = False
             for k, v in list(self.edges.items()):
                 if v is None:
                     state, sym = k
                     if state in self.states:
                         self.goto(state, sym)
-                        changes = 1
+                        changes = True
         rv = self.__dict__.copy()
         for s in list(self.states.values()):
             del s.items
@@ -222,9 +239,39 @@
             self.rule2func[rule] = fn
             self.rule2name[rule] = func.__name__[2:]
             self.ruleschanged = True
+
+            if self.profile_info is not None:
+                rule_str = self.reduce_string(rule)
+                if rule_str not in self.profile_info:
+                    self.profile_info[rule_str] = 0
             pass
         return
 
+    def remove_rule(self, doc):
+        """Remove a grammar rules from  _self.rules_, _self.rule2func_,
+            and _self.rule2name_
+        """
+        rules = doc.split()
+        index = []
+        for i in range(len(rules)):
+            if rules[i] == '::=':
+                index.append(i-1)
+        index.append(len(rules))
+        for i in range(len(index)-1):
+            lhs = rules[index[i]]
+            rhs = rules[index[i]+2:index[i+1]]
+            rule = (lhs, tuple(rhs))
+
+            if lhs not in self.rules:
+                return
+
+            self.rules[lhs].remove(rule)
+            del self.rule2func[rule]
+            del self.rule2name[rule]
+            self.ruleschanged = True
+        return
+
+
     def collectRules(self):
         for name in _namelist(self):
             if name[:2] == 'p_':
@@ -407,6 +454,9 @@
             else:
                 self.error(None, None)
 
+        if self.profile_info is not None:
+            self.dump_profile_info()
+
         return self.buildTree(self._START, finalitem,
                     tokens, len(sets)-2)
 
@@ -575,6 +625,8 @@
                 lhs, rhs = rule
                 if self.debug['reduce']:
                     self.debug_reduce(rule, tokens, parent, i)
+                if self.profile_info is not None:
+                    self.profile_rule(rule)
                 if lhs in self.check_reduce and tokens:
                     if self.check_reduce[lhs] == 'AST':
                         ast = self.reduce_ast(rule, tokens, item, i, sets)
@@ -767,29 +819,29 @@
         '''
         return list[0]
 
-    def dumpGrammar(self):
+    def dumpGrammar(self, out=sys.stdout):
         """
         Print grammar rules
         """
         for rule in sorted(self.rule2name.items()):
-            print("%s" % rule2str(rule))
+            out.write("%s\n" % rule2str(rule[0]))
         return
 
-    def checkGrammar(self):
+    def checkGrammar(self, out=sys.stderr):
         '''
         Check grammar
         '''
         lhs, rhs, tokens, right_recursive = self.checkSets()
         if len(lhs) > 0:
-            print("LHS symbols not used on the RHS:")
-            print(sorted(lhs))
+            out.write("LHS symbols not used on the RHS:\n")
+            out.write(sorted(lhs), "\n")
         if len(rhs) > 0:
-            print("RHS symbols not used on the LHS:")
-            print(sorted(rhs))
+            out.write("RHS symbols not used on the LHS:\n")
+            out.write(sorted(rhs, "\n"))
         if len(right_recursive) > 0:
-            print("Right recursive rules:")
+            out.write("Right recursive rules:\n")
             for rule in right_recursive:
-                print("%s ::= %s" % (rule[0], ' '.join(rule[1])))
+                out.write("%s ::= %s\n" % (rule[0], ' '.join(rule[1])))
                 pass
             pass
 
@@ -824,8 +876,38 @@
         missing_rhs = rhs_set - lhs_set
         return (missing_lhs, missing_rhs, token_set, right_recursive)
 
+    def reduce_string(self, rule):
+        return "%s ::= %s" % (rule[0], ' '.join(rule[1]))
+
+    # Note the unused parameters here are used in subclassed
+    # routines that need more information
     def debug_reduce(self, rule, tokens, parent, i):
-        print("%s ::= %s" % (rule[0], ' '.join(rule[1])))
+        print(self.reduce_string(rule))
+
+    def profile_rule(self, rule):
+        """Bump count of the number of times _rule_ was used"""
+        rule_str = self.reduce_string(rule)
+        if rule_str not in self.profile_info:
+            self.profile_info[rule_str] = 1
+        else:
+            self.profile_info[rule_str] += 1
+
+    def get_profile_info(self):
+        """Show the accumulated results of how many times each rule was used"""
+        return sorted(self.profile_info.items(),
+                      key=lambda kv: kv[1],
+                      reverse=False)
+        return
+
+    def dump_profile_info(self):
+        if isinstance(self.coverage_path, str):
+            with open(self.coverage_path, 'wb') as fp:
+                pickle.dump(self.profile_info, fp)
+        else:
+            for rule, count in self.get_profile_info():
+                self.coverage_path.write("%s -- %d\n" % (rule, count))
+                pass
+            self.coverage_path.write("-" * 40 + "\n")
 
     def reduce_ast(self, rule, tokens, item, k, sets):
         rhs = rule[1]
@@ -860,7 +942,12 @@
 
 class GenericASTBuilder(GenericParser):
     def __init__(self, AST, start, debug=DEFAULT_DEBUG):
-        GenericParser.__init__(self, start, debug=debug)
+        if 'SPARK_PARSER_COVERAGE' in os.environ:
+            coverage_path = os.environ['SPARK_PARSER_COVERAGE']
+        else:
+            coverage_path = None
+        GenericParser.__init__(self, start, debug=debug,
+                               coverage_path=coverage_path)
         self.AST = AST
 
     def preprocess(self, rule, func):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/spark_parser/version.py 
new/spark_parser-1.6.0/spark_parser/version.py
--- old/spark_parser-1.5.1/spark_parser/version.py      2016-11-28 
13:07:14.000000000 +0100
+++ new/spark_parser-1.6.0/spark_parser/version.py      2017-01-30 
08:15:15.000000000 +0100
@@ -1,3 +1,3 @@
 # This file is suitable for sourcing inside bash as
 # well as importing into Python
-VERSION='1.5.1'
+VERSION='1.6.0'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/spark_parser.egg-info/PKG-INFO 
new/spark_parser-1.6.0/spark_parser.egg-info/PKG-INFO
--- old/spark_parser-1.5.1/spark_parser.egg-info/PKG-INFO       2016-11-28 
13:30:14.000000000 +0100
+++ new/spark_parser-1.6.0/spark_parser.egg-info/PKG-INFO       2017-02-01 
01:13:19.000000000 +0100
@@ -1,7 +1,7 @@
 Metadata-Version: 1.1
 Name: spark-parser
-Version: 1.5.1
-Summary: An Early-Algorithm Context-free grammar Parser Toolkit
+Version: 1.6.0
+Summary: An Earley-Algorithm Context-free grammar Parser Toolkit
 Home-page: https://github.com/rocky/python-spark/
 Author: Rocky Bernstein
 Author-email: [email protected]
@@ -12,7 +12,7 @@
         =====
         
         SPARK stands for Scanning, Parsing, and Rewriting Kit. It uses Jay
-        Early's algorithm for parsing context free grammars, and comes with
+        Earley's algorithm for parsing context free grammars, and comes with
         some generic Abstract Syntax Tree routines. There is also a prototype
         scanner which does its job by combining Python regular expressions.
         
@@ -22,7 +22,7 @@
         current incarnation of this code is maintained (or not) by Rocky
         Bernstein.
         
-        Note: Early algorithm parsers are almost linear when given an LR 
grammar.
+        Note: Earley algorithm parsers are almost linear when given an LR 
grammar.
         These are grammars which are left-recursive.
         
         Installation
@@ -71,5 +71,6 @@
 Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
 Classifier: Topic :: Software Development :: Code Generators
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/spark_parser.egg-info/SOURCES.txt 
new/spark_parser-1.6.0/spark_parser.egg-info/SOURCES.txt
--- old/spark_parser-1.5.1/spark_parser.egg-info/SOURCES.txt    2016-11-28 
13:30:14.000000000 +0100
+++ new/spark_parser-1.6.0/spark_parser.egg-info/SOURCES.txt    2017-02-01 
01:13:19.000000000 +0100
@@ -6,6 +6,7 @@
 README.rst
 TODO.rst
 setup.py
+bin/spark-parser-coverage
 example/README.md
 example/expr/README.md
 example/expr/expr.py
@@ -40,15 +41,19 @@
 example/python2/__pycache__/py2_format.cpython-33.pyc
 example/python2/__pycache__/py2_format.cpython-34.pyc
 example/python2/__pycache__/py2_format.cpython-35.pyc
+example/python2/__pycache__/py2_format.cpython-36.pyc
 example/python2/__pycache__/py2_parser.cpython-33.pyc
 example/python2/__pycache__/py2_parser.cpython-34.pyc
 example/python2/__pycache__/py2_parser.cpython-35.pyc
+example/python2/__pycache__/py2_parser.cpython-36.pyc
 example/python2/__pycache__/py2_scan.cpython-33.pyc
 example/python2/__pycache__/py2_scan.cpython-34.pyc
 example/python2/__pycache__/py2_scan.cpython-35.pyc
+example/python2/__pycache__/py2_scan.cpython-36.pyc
 example/python2/__pycache__/py2_token.cpython-33.pyc
 example/python2/__pycache__/py2_token.cpython-34.pyc
 example/python2/__pycache__/py2_token.cpython-35.pyc
+example/python2/__pycache__/py2_token.cpython-36.pyc
 example/python2/test/Makefile
 example/python2/test/helper.py
 example/python2/test/helper.pyc
@@ -63,6 +68,7 @@
 example/python2/test/__pycache__/helper.cpython-33.pyc
 example/python2/test/__pycache__/helper.cpython-34.pyc
 example/python2/test/__pycache__/helper.cpython-35.pyc
+example/python2/test/__pycache__/helper.cpython-36.pyc
 example/python2/test/format/assert.py
 example/python2/test/format/assert.right
 example/python2/test/format/def-bug.py-notyet
@@ -114,10 +120,14 @@
 spark_parser.egg-info/PKG-INFO
 spark_parser.egg-info/SOURCES.txt
 spark_parser.egg-info/dependency_links.txt
+spark_parser.egg-info/requires.txt
 spark_parser.egg-info/top_level.txt
 spark_parser.egg-info/zip-safe
 test/test_checker.py
 test/test_checker.pyc
+test/test_grammar.py
+test/test_grammar.pyc
+test/test_grammar.py~
 test/test_misc.py
 test/test_misc.pyc
 test/test_spark.py
@@ -125,9 +135,15 @@
 test/__pycache__/test_checker.cpython-33.pyc
 test/__pycache__/test_checker.cpython-34.pyc
 test/__pycache__/test_checker.cpython-35.pyc
+test/__pycache__/test_checker.cpython-36.pyc
+test/__pycache__/test_grammar.cpython-33.pyc
+test/__pycache__/test_grammar.cpython-34.pyc
+test/__pycache__/test_grammar.cpython-35.pyc
 test/__pycache__/test_misc.cpython-33.pyc
 test/__pycache__/test_misc.cpython-34.pyc
 test/__pycache__/test_misc.cpython-35.pyc
+test/__pycache__/test_misc.cpython-36.pyc
 test/__pycache__/test_spark.cpython-33.pyc
 test/__pycache__/test_spark.cpython-34.pyc
-test/__pycache__/test_spark.cpython-35.pyc
\ No newline at end of file
+test/__pycache__/test_spark.cpython-35.pyc
+test/__pycache__/test_spark.cpython-36.pyc
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spark_parser-1.5.1/spark_parser.egg-info/requires.txt 
new/spark_parser-1.6.0/spark_parser.egg-info/requires.txt
--- old/spark_parser-1.5.1/spark_parser.egg-info/requires.txt   1970-01-01 
01:00:00.000000000 +0100
+++ new/spark_parser-1.6.0/spark_parser.egg-info/requires.txt   2017-02-01 
01:13:19.000000000 +0100
@@ -0,0 +1 @@
+click
Binary files 
old/spark_parser-1.5.1/test/__pycache__/test_checker.cpython-36.pyc and 
new/spark_parser-1.6.0/test/__pycache__/test_checker.cpython-36.pyc differ
Binary files 
old/spark_parser-1.5.1/test/__pycache__/test_grammar.cpython-33.pyc and 
new/spark_parser-1.6.0/test/__pycache__/test_grammar.cpython-33.pyc differ
Binary files 
old/spark_parser-1.5.1/test/__pycache__/test_grammar.cpython-34.pyc and 
new/spark_parser-1.6.0/test/__pycache__/test_grammar.cpython-34.pyc differ
Binary files 
old/spark_parser-1.5.1/test/__pycache__/test_grammar.cpython-35.pyc and 
new/spark_parser-1.6.0/test/__pycache__/test_grammar.cpython-35.pyc differ
Binary files old/spark_parser-1.5.1/test/__pycache__/test_misc.cpython-33.pyc 
and new/spark_parser-1.6.0/test/__pycache__/test_misc.cpython-33.pyc differ
Binary files old/spark_parser-1.5.1/test/__pycache__/test_misc.cpython-34.pyc 
and new/spark_parser-1.6.0/test/__pycache__/test_misc.cpython-34.pyc differ
Binary files old/spark_parser-1.5.1/test/__pycache__/test_misc.cpython-35.pyc 
and new/spark_parser-1.6.0/test/__pycache__/test_misc.cpython-35.pyc differ
Binary files old/spark_parser-1.5.1/test/__pycache__/test_misc.cpython-36.pyc 
and new/spark_parser-1.6.0/test/__pycache__/test_misc.cpython-36.pyc differ
Binary files old/spark_parser-1.5.1/test/__pycache__/test_spark.cpython-33.pyc 
and new/spark_parser-1.6.0/test/__pycache__/test_spark.cpython-33.pyc differ
Binary files old/spark_parser-1.5.1/test/__pycache__/test_spark.cpython-34.pyc 
and new/spark_parser-1.6.0/test/__pycache__/test_spark.cpython-34.pyc differ
Binary files old/spark_parser-1.5.1/test/__pycache__/test_spark.cpython-35.pyc 
and new/spark_parser-1.6.0/test/__pycache__/test_spark.cpython-35.pyc differ
Binary files old/spark_parser-1.5.1/test/__pycache__/test_spark.cpython-36.pyc 
and new/spark_parser-1.6.0/test/__pycache__/test_spark.cpython-36.pyc differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/test/test_grammar.py 
new/spark_parser-1.6.0/test/test_grammar.py
--- old/spark_parser-1.5.1/test/test_grammar.py 1970-01-01 01:00:00.000000000 
+0100
+++ new/spark_parser-1.6.0/test/test_grammar.py 2017-02-01 01:06:03.000000000 
+0100
@@ -0,0 +1,67 @@
+import unittest
+from spark_parser import (GenericParser, PYTHON3)
+
+if PYTHON3:
+    from io import StringIO
+else:
+    from StringIO import StringIO
+
+
+class Expr(GenericParser):
+    """Testing DumpGrammar, adding a rule and removing a rule"""
+    def p_rules(self, args):
+        """
+        expr ::= expr ADD_OP term
+        expr ::= term
+        term ::= term MULT_OP factor
+        term ::= factor
+        factor ::= INTEGER
+        """
+        return
+    pass
+
+nop_func = lambda self, args: None
+
+class TestGrammar(unittest.TestCase):
+
+    def test_basic(self):
+        parser = Expr('expr')
+        f = StringIO()
+        parser.dumpGrammar(f)
+        expect = """START ::= |- expr
+expr ::= expr ADD_OP term
+expr ::= term
+factor ::= INTEGER
+term ::= factor
+term ::= term MULT_OP factor
+"""
+        self.assertEqual(expect, f.getvalue())
+
+        parser.addRule("expr ::= expr SUB_OP term", nop_func)
+        expect = """START ::= |- expr
+expr ::= expr ADD_OP term
+expr ::= expr SUB_OP term
+expr ::= term
+factor ::= INTEGER
+term ::= factor
+term ::= term MULT_OP factor
+"""
+        f = StringIO()
+        parser.dumpGrammar(f)
+        self.assertEqual(expect, f.getvalue())
+
+        parser.remove_rule("expr ::= expr ADD_OP term")
+        expect = """START ::= |- expr
+expr ::= expr SUB_OP term
+expr ::= term
+factor ::= INTEGER
+term ::= factor
+term ::= term MULT_OP factor
+"""
+        f = StringIO()
+        parser.dumpGrammar(f)
+        self.assertEqual(expect, f.getvalue())
+
+
+if __name__ == '__main__':
+    unittest.main()
Binary files old/spark_parser-1.5.1/test/test_grammar.pyc and 
new/spark_parser-1.6.0/test/test_grammar.pyc differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/test/test_grammar.py~ 
new/spark_parser-1.6.0/test/test_grammar.py~
--- old/spark_parser-1.5.1/test/test_grammar.py~        1970-01-01 
01:00:00.000000000 +0100
+++ new/spark_parser-1.6.0/test/test_grammar.py~        2017-01-30 
07:41:02.000000000 +0100
@@ -0,0 +1,64 @@
+import unittest
+from spark_parser.spark import GenericParser
+
+from StringIO import StringIO
+
+
+class Expr(GenericParser):
+    """Testing DumpGrammar, adding a rule and removing a rule"""
+    def p_rules(self, args):
+        """
+        expr ::= expr ADD_OP term
+        expr ::= term
+        term ::= term MULT_OP factor
+        term ::= factor
+        factor ::= INTEGER
+        """
+        return
+    pass
+
+nop_func = lambda self, args: None
+
+class TestGrammar(unittest.TestCase):
+
+    def test_basic(self):
+        parser = Expr('expr')
+        f = StringIO()
+        parser.dumpGrammar(f)
+        expect = """START ::= |- expr
+expr ::= expr ADD_OP term
+expr ::= term
+factor ::= INTEGER
+term ::= factor
+term ::= term MULT_OP factor
+"""
+        self.assertEqual(expect, f.getvalue())
+
+        parser.addRule("expr ::= expr SUB_OP term", nop_func)
+        expect = """START ::= |- expr
+expr ::= expr ADD_OP term
+expr ::= expr SUB_OP term
+expr ::= term
+factor ::= INTEGER
+term ::= factor
+term ::= term MULT_OP factor
+"""
+        f = StringIO()
+        parser.dumpGrammar(f)
+        self.assertEqual(expect, f.getvalue())
+
+        parser.remove_rule("expr ::= expr ADD_OP term")
+        expect = """START ::= |- expr
+expr ::= expr SUB_OP term
+expr ::= term
+factor ::= INTEGER
+term ::= factor
+term ::= term MULT_OP factor
+"""
+        f = StringIO()
+        parser.dumpGrammar(f)
+        self.assertEqual(expect, f.getvalue())
+
+
+if __name__ == '__main__':
+    unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/test/test_misc.py 
new/spark_parser-1.6.0/test/test_misc.py
--- old/spark_parser-1.5.1/test/test_misc.py    2016-11-28 13:14:51.000000000 
+0100
+++ new/spark_parser-1.6.0/test/test_misc.py    2017-01-30 07:40:22.000000000 
+0100
@@ -1,5 +1,11 @@
 import unittest
 from spark_parser.spark import GenericParser
+from spark_parser import PYTHON3
+
+if PYTHON3:
+    from io import StringIO
+else:
+    from StringIO import StringIO
 
 class Rules(GenericParser):
     """Testing duplicate rules"""
@@ -54,6 +60,17 @@
                           (('stmts', ('stmt',)), 'rules'),
                           (('stmts', ('stmts', 'stmt')), 'rules'),
                           (('x', ('TOKEN',)), 'rules')])
+        f = StringIO()
+        expect = \
+"""START ::= |- x
+ratings ::=
+ratings ::= ratings STARS
+stmts ::= stmt
+stmts ::= stmts stmt
+x ::= TOKEN
+"""
+        parser.dumpGrammar(f)
+        self.assertEqual(f.getvalue(), expect)
 
         # Check Invalid rule
         try:
@@ -79,6 +96,5 @@
                           (('opt_period', ()), 'rules'),
                           (('opt_period', ('PERIOD',)), 'rules'), ])
 
-
 if __name__ == '__main__':
     unittest.main()
Binary files old/spark_parser-1.5.1/test/test_misc.pyc and 
new/spark_parser-1.6.0/test/test_misc.pyc differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spark_parser-1.5.1/test/test_spark.py 
new/spark_parser-1.6.0/test/test_spark.py
--- old/spark_parser-1.5.1/test/test_spark.py   2016-06-21 06:51:23.000000000 
+0200
+++ new/spark_parser-1.6.0/test/test_spark.py   2017-01-30 07:40:22.000000000 
+0100
@@ -10,7 +10,7 @@
 
 class ExprScanner(GenericScanner):
 
-    def __init__(self):
+    def __init__(self, coverage_path=None):
         GenericScanner.__init__(self)
 
     def tokenize(self, input):
@@ -61,8 +61,8 @@
     by SPARK.
     """
 
-    def __init__(self, start='expr', debug=DEFAULT_DEBUG):
-        GenericParser.__init__(self, start, debug)
+    def __init__(self, start='expr', debug=DEFAULT_DEBUG, coverage_path=None):
+        GenericParser.__init__(self, start, debug=debug, 
coverage_path=coverage_path)
 
     def p_expr_add_term(self, args):
         ' expr ::= expr ADD_OP term '
@@ -110,7 +110,11 @@
         test_term6 = AST('multiply', [test_term_to_factor2, 
test_factor_to_integer3])
         test_expr7 = AST('add', [test_expr_to_term1, test_term6])
 
+        # import sys
+        # parser = ExprParser(coverage_path=sys.stdout)
+        # parser = ExprParser(coverage_path="/tmp/spark-grammar.cover")
         parser = ExprParser()
+
         lhs, rhs, tokens, right_recursive = parser.checkSets()
         self.assertEqual(len(lhs), 0)
         self.assertEqual(len(rhs), 0)
Binary files old/spark_parser-1.5.1/test/test_spark.pyc and 
new/spark_parser-1.6.0/test/test_spark.pyc differ


Reply via email to