Repository: nltk/nltk

Issue:
start state for pcfg seems to be ignored
The start state for pcfg grammars seems to be ignored by the parser and instead used the first rule.  Perhaps I am misunderstanding start state.

Here is an example:

> > > tree= " ( Trip straightenterscene  ( TripSegment  ( TripSegment  ( TripSegment straight )  ( TripSegment  ( Avoiding starboardturnapproachingship straight portturn straight portturn straight starboardturn ) ) )  ( TripSegment straight ) ) straightleavescene ) "
> > > tree=nltk.Tree(tree)
> > > grammarTest=nltk.induce_pcfg('Trip',tree.productions())
> > > grammarTest
> > > <Grammar with 5 productions>
> > > print grammarTest
> > > Grammar with 5 productions (start state = 'Trip')
> > >     TripSegment -> Avoiding [0.2]
> > >     Trip -> 'straightenterscene' TripSegment 'straightleavescene' [1.0]
> > >     Avoiding -> 'starboardturnapproachingship' 'straight' 'portturn' 'straight' 'portturn' 'straight' 'starboardturn' [1.0]
> > >     TripSegment -> TripSegment TripSegment [0.4]
> > >     TripSegment -> 'straight' [0.4]

parsing this grammar with the following sequence yields no results.
['straightenterscene', 'straight', 'starboardturnapproachingship', 'straight', 'portturn', 'straight', 'portturn', 'straight', 'starboardturn', 'straight', 'straightleavescene']

however reordering the rules to putting the start state at the top fixes it.

grammarTest=grammar2=nltk.parse_pcfg("""Trip -> 'straightenterscene' TripSegment 'straightleavescene' [1.0]
TripSegment -> Avoiding [0.2]  
    Avoiding -> 'starboardturnapproachingship' 'straight' 'portturn' 'straight' 'portturn' 'straight' 'starboardturn' [1.0]
    TripSegment -> TripSegment TripSegment [0.4]
    TripSegment -> 'straight' [0.4]"""



Discussion:
Having had a look at this, the bug seems to be cropping up because `induce_pcfg()` expects the `start` argument to be a nonterminal, but then happily passes it along even if it's a string. I've attached some testing code that briefly demonstrates the issue below. From `induce_pcfg()`, `start` gets passed to `WeightedGrammar`'s constructor, and then to `ContextFreeGrammar`, and there doesn't seem to be any type-checking at any point.

One possible fix would be to add something like:

```
if not is_nonterminal(start):
    raise TypeError
```

to either `induce_pcfg()`, `WeightedGrammar`, or `ContextFreeGrammar`. I suppose you could also silently convert `start` to a `Nonterminal` if it gets passed as a string?

I'm not exactly sure what the best fix would be, but I'm happy to submit a pull request if I get some advice on the best way to deal with the issue.

Testing code:

```
import nltk

tree = " ( Trip straightenterscene ( TripSegment ( TripSegment ( TripSegment straight ) ( TripSegment ( Avoiding starboardturnapproachingship straight portturn straight portturn straight starboardturn ) ) ) ( TripSegment straight ) ) straightleavescene ) "
tree = nltk.Tree(tree)
grammar1 = nltk.induce_pcfg('Trip', tree.productions())
grammar2 = nltk.parse_pcfg(
    """Trip -> 'straightenterscene' TripSegment 'straightleavescene' [1.0]
TripSegment -> Avoiding [0.2]
Avoiding -> 'starboardturnapproachingship' 'straight' 'portturn' 'straight' 'portturn' 'straight' 'starboardturn' [1.0]
TripSegment -> TripSegment TripSegment [0.4]
TripSegment -> 'straight' [0.4]"""
)
sentence1 = ['straightenterscene', 'straight',
             'starboardturnapproachingship', 'straight',
             'portturn', 'straight', 'portturn', 'straight',
             'starboardturn', 'straight', 'straightleavescene']


grammar1_fixed = nltk.induce_pcfg("Trip", tree.productions())
grammar1_fixed._start = nltk.Nonterminal("Trip")

parser1 = nltk.parse.ChartParser(grammar1)
parser1_fixed = nltk.parse.ChartParser(grammar1_fixed)
parser2 = nltk.parse.ChartParser(grammar2)

parse1 = parser1.nbest_parse(sentence1)
parse1_fixed = parser1_fixed.nbest_parse(sentence1)
parse2 = parser2.nbest_parse(sentence1)

# No results
print "Parser 1:", parse1
# Both of these return the parse correctly
print "Parser 1 fixed:", parse1_fixed
print "Parser 2:", parse2
```


Resolving patch:
diff --git a/nltk/grammar.py b/nltk/grammar.py
--- a/nltk/grammar.py
+++ b/nltk/grammar.py
@@ -439,6 +439,10 @@ def __init__(self, start, productions, calculate_leftcorners=True):
             leftcorner relation. In that case, some optimized chart parsers won't work.
         :type calculate_leftcorners: bool
         """
+        if not is_nonterminal(start):
+            raise TypeError("start should be a Nonterminal object,"
+                            " not a %s" % type(start).__name__)
+
         self._start = start
         self._productions = productions
         self._categories = set(prod.lhs() for prod in productions)


Test patch: