Code Coverage for nltk.draw.rdparser
Untested Functions
- demo()
- RecursiveDescentDemo: __init__(), _animate_backtrack(), _animate_backtrack_frame(), _animate_expand(), _animate_expand_frame(), _animate_match(), _animate_match_backtrack(), _animate_match_backtrack_frame(), _animate_match_frame(), _backtrack(), _configure(), _expand(), _get(), _highlight_nodes(), _highlight_prodlist(), _init_bindings(), _init_buttons(), _init_canvas(), _init_feedback(), _init_fonts(), _init_grammar(), _init_menubar(), _makeroom(), _match(), _position_text(), _prodlist_select(), _redraw(), _redraw_quick(), _step(), _toggle_grammar(), _tree_leaves(), about(), autostep(), cancel_autostep(), destroy(), edit_grammar(), edit_sentence(), help(), mainloop(), postscript(), reset(), resize(), set_grammar(), set_sentence()
"""
A graphical tool for exploring the recursive descent parser.
The recursive descent parser maintains a tree, which records the
structure of the portion of the text that has been parsed. It uses
CFG productions to expand the fringe of the tree, and matches its
leaves against the text. Initially, the tree contains the start
symbol ("S"). It is shown in the main canvas, to the right of the
list of available expansions.
The parser builds up a tree structure for the text using three
operations:
- "expand" uses a CFG production to add children to a node on the
fringe of the tree.
- "match" compares a leaf in the tree to a text token.
- "backtrack" returns the tree to its state before the most recent
expand or match operation.
The parser maintains a list of tree locations called a "frontier" to
remember which nodes have not yet been expanded and which leaves have
not yet been matched against the text. The leftmost frontier node is
shown in green, and the other frontier nodes are shown in blue. The
parser always performs expand and match operations on the leftmost
element of the frontier.
You can control the parser's operation by using the "expand," "match,"
and "backtrack" buttons; or you can use the "step" button to let the
parser automatically decide which operation to apply. The parser uses
the following rules to decide which operation to apply:
- If the leftmost frontier element is a token, try matching it.
- If the leftmost frontier element is a node, try expanding it with
the first untried expansion.
- Otherwise, backtrack.
The "expand" button applies the untried expansion whose CFG production
is listed earliest in the grammar. To manually choose which expansion
to apply, click on a CFG production from the list of available
expansions, on the left side of the main window.
The "autostep" button will let the parser continue applying
applications to the tree until it reaches a complete parse. You can
cancel an autostep in progress at any time by clicking on the
"autostep" button again.
Keyboard Shortcuts::
[Space]\t Perform the next expand, match, or backtrack operation
[a]\t Step through operations until the next complete parse
[e]\t Perform an expand operation
[m]\t Perform a match operation
[b]\t Perform a backtrack operation
[Delete]\t Reset the parser
[g]\t Show/hide available expansions list
[h]\t Help
[Ctrl-p]\t Print
[q]\t Quit
"""
import string
import tkFont
from Tkinter import *
from nltk import parse, tokenize
from nltk.draw.tree import *
from nltk.draw import *
from nltk.draw.cfg import *
class RecursiveDescentDemo(object):
"""
A graphical tool for exploring the recursive descent parser. The tool
displays the parser's tree and the remaining text, and allows the
user to control the parser's operation. In particular, the user
can expand subtrees on the frontier, match tokens on the frontier
against the text, and backtrack. A "step" button simply steps
through the parsing process, performing the operations that
C{RecursiveDescentParser} would use.
"""
def __init__(self, grammar, sent, trace=0):
self._sent = sent
self._parser = parse.SteppingRecursiveDescentParser(grammar, trace)
self._top = Tk()
self._top.title('Recursive Descent Parser Demo')
self._init_bindings()
self._init_fonts(self._top)
self._animation_frames = IntVar(self._top)
self._animation_frames.set(5)
self._animating_lock = 0
self._autostep = 0
self._show_grammar = IntVar(self._top)
self._show_grammar.set(1)
self._init_menubar(self._top)
self._init_buttons(self._top)
self._init_feedback(self._top)
self._init_grammar(self._top)
self._init_canvas(self._top)
self._parser.initialize(self._sent)
self._canvas.bind('<Configure>', self._configure)
def _init_fonts(self, root):
self._sysfont = tkFont.Font(font=Button()["font"])
root.option_add("*Font", self._sysfont)
self._size = IntVar(root)
self._size.set(self._sysfont.cget('size'))
self._boldfont = tkFont.Font(family='helvetica', weight='bold',
size=self._size.get())
self._font = tkFont.Font(family='helvetica',
size=self._size.get())
if self._size.get() < 0: big = self._size.get()-2
else: big = self._size.get()+2
self._bigfont = tkFont.Font(family='helvetica', weight='bold',
size=big)
def _init_grammar(self, parent):
self._prodframe = listframe = Frame(parent)
self._prodframe.pack(fill='both', side='left', padx=2)
self._prodlist_label = Label(self._prodframe, font=self._boldfont,
text='Available Expansions')
self._prodlist_label.pack()
self._prodlist = Listbox(self._prodframe, selectmode='single',
relief='groove', background='white',
foreground='#909090', font=self._font,
selectforeground='#004040',
selectbackground='#c0f0c0')
self._prodlist.pack(side='right', fill='both', expand=1)
self._productions = list(self._parser.grammar().productions())
for production in self._productions:
self._prodlist.insert('end', (' %s' % production))
self._prodlist.config(height=min(len(self._productions), 25))
if len(self._productions) > 25:
listscroll = Scrollbar(self._prodframe,
orient='vertical')
self._prodlist.config(yscrollcommand = listscroll.set)
listscroll.config(command=self._prodlist.yview)
listscroll.pack(side='left', fill='y')
self._prodlist.bind('<<ListboxSelect>>', self._prodlist_select)
def _init_bindings(self):
self._top.bind('<Control-q>', self.destroy)
self._top.bind('<Control-x>', self.destroy)
self._top.bind('<Escape>', self.destroy)
self._top.bind('e', self.expand)
self._top.bind('m', self.match)
self._top.bind('<Alt-m>', self.match)
self._top.bind('<Control-m>', self.match)
self._top.bind('b', self.backtrack)
self._top.bind('<Alt-b>', self.backtrack)
self._top.bind('<Control-b>', self.backtrack)
self._top.bind('<Control-z>', self.backtrack)
self._top.bind('<BackSpace>', self.backtrack)
self._top.bind('a', self.autostep)
self._top.bind('<Control-space>', self.autostep)
self._top.bind('<Control-c>', self.cancel_autostep)
self._top.bind('<space>', self.step)
self._top.bind('<Delete>', self.reset)
self._top.bind('<Control-p>', self.postscript)
self._top.bind('<Control-h>', self.help)
self._top.bind('<F1>', self.help)
self._top.bind('<Control-g>', self.edit_grammar)
self._top.bind('<Control-t>', self.edit_sentence)
def _init_buttons(self, parent):
self._buttonframe = buttonframe = Frame(parent)
buttonframe.pack(fill='none', side='bottom', padx=3, pady=2)
Button(buttonframe, text='Step',
background='#90c0d0', foreground='black',
command=self.step,).pack(side='left')
Button(buttonframe, text='Autostep',
background='#90c0d0', foreground='black',
command=self.autostep,).pack(side='left')
Button(buttonframe, text='Expand', underline=0,
background='#90f090', foreground='black',
command=self.expand).pack(side='left')
Button(buttonframe, text='Match', underline=0,
background='#90f090', foreground='black',
command=self.match).pack(side='left')
Button(buttonframe, text='Backtrack', underline=0,
background='#f0a0a0', foreground='black',
command=self.backtrack).pack(side='left')
def _configure(self, event):
self._autostep = 0
(x1, y1, x2, y2) = self._cframe.scrollregion()
y2 = event.height - 6
self._canvas['scrollregion'] = '%d %d %d %d' % (x1,y1,x2,y2)
self._redraw()
def _init_feedback(self, parent):
self._feedbackframe = feedbackframe = Frame(parent)
feedbackframe.pack(fill='x', side='bottom', padx=3, pady=3)
self._lastoper_label = Label(feedbackframe, text='Last Operation:',
font=self._font)
self._lastoper_label.pack(side='left')
lastoperframe = Frame(feedbackframe, relief='sunken', border=1)
lastoperframe.pack(fill='x', side='right', expand=1, padx=5)
self._lastoper1 = Label(lastoperframe, foreground='#007070',
background='#f0f0f0', font=self._font)
self._lastoper2 = Label(lastoperframe, anchor='w', width=30,
foreground='#004040', background='#f0f0f0',
font=self._font)
self._lastoper1.pack(side='left')
self._lastoper2.pack(side='left', fill='x', expand=1)
def _init_canvas(self, parent):
self._cframe = CanvasFrame(parent, background='white',
closeenough=10,
border=2, relief='sunken')
self._cframe.pack(expand=1, fill='both', side='top', pady=2)
canvas = self._canvas = self._cframe.canvas()
self._tree = None
self._textwidgets = []
self._textline = None
def _init_menubar(self, parent):
menubar = Menu(parent)
filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label='Reset Parser', underline=0,
command=self.reset, accelerator='Del')
filemenu.add_command(label='Print to Postscript', underline=0,
command=self.postscript, accelerator='Ctrl-p')
filemenu.add_command(label='Exit', underline=1,
command=self.destroy, accelerator='Ctrl-x')
menubar.add_cascade(label='File', underline=0, menu=filemenu)
editmenu = Menu(menubar, tearoff=0)
editmenu.add_command(label='Edit Grammar', underline=5,
command=self.edit_grammar,
accelerator='Ctrl-g')
editmenu.add_command(label='Edit Text', underline=5,
command=self.edit_sentence,
accelerator='Ctrl-t')
menubar.add_cascade(label='Edit', underline=0, menu=editmenu)
rulemenu = Menu(menubar, tearoff=0)
rulemenu.add_command(label='Step', underline=1,
command=self.step, accelerator='Space')
rulemenu.add_separator()
rulemenu.add_command(label='Match', underline=0,
command=self.match, accelerator='Ctrl-m')
rulemenu.add_command(label='Expand', underline=0,
command=self.expand, accelerator='Ctrl-e')
rulemenu.add_separator()
rulemenu.add_command(label='Backtrack', underline=0,
command=self.backtrack, accelerator='Ctrl-b')
menubar.add_cascade(label='Apply', underline=0, menu=rulemenu)
viewmenu = Menu(menubar, tearoff=0)
viewmenu.add_checkbutton(label="Show Grammar", underline=0,
variable=self._show_grammar,
command=self._toggle_grammar)
viewmenu.add_separator()
viewmenu.add_radiobutton(label='Tiny', variable=self._size,
underline=0, value=10, command=self.resize)
viewmenu.add_radiobutton(label='Small', variable=self._size,
underline=0, value=12, command=self.resize)
viewmenu.add_radiobutton(label='Medium', variable=self._size,
underline=0, value=14, command=self.resize)
viewmenu.add_radiobutton(label='Large', variable=self._size,
underline=0, value=18, command=self.resize)
viewmenu.add_radiobutton(label='Huge', variable=self._size,
underline=0, value=24, command=self.resize)
menubar.add_cascade(label='View', underline=0, menu=viewmenu)
animatemenu = Menu(menubar, tearoff=0)
animatemenu.add_radiobutton(label="No Animation", underline=0,
variable=self._animation_frames,
value=0)
animatemenu.add_radiobutton(label="Slow Animation", underline=0,
variable=self._animation_frames,
value=10, accelerator='-')
animatemenu.add_radiobutton(label="Normal Animation", underline=0,
variable=self._animation_frames,
value=5, accelerator='=')
animatemenu.add_radiobutton(label="Fast Animation", underline=0,
variable=self._animation_frames,
value=2, accelerator='+')
menubar.add_cascade(label="Animate", underline=1, menu=animatemenu)
helpmenu = Menu(menubar, tearoff=0)
helpmenu.add_command(label='About', underline=0,
command=self.about)
helpmenu.add_command(label='Instructions', underline=0,
command=self.help, accelerator='F1')
menubar.add_cascade(label='Help', underline=0, menu=helpmenu)
parent.config(menu=menubar)
def _get(self, widget, treeloc):
for i in treeloc: widget = widget.subtrees()[i]
if isinstance(widget, TreeSegmentWidget):
widget = widget.node()
return widget
def _redraw(self):
canvas = self._canvas
if self._tree is not None:
self._cframe.destroy_widget(self._tree)
for twidget in self._textwidgets:
self._cframe.destroy_widget(twidget)
if self._textline is not None:
self._canvas.delete(self._textline)
helv = ('helvetica', -self._size.get())
bold = ('helvetica', -self._size.get(), 'bold')
attribs = {'tree_color': '#000000', 'tree_width': 2,
'node_font': bold, 'leaf_font': helv,}
tree = self._parser.tree()
self._tree = tree_to_treesegment(canvas, tree, **attribs)
self._cframe.add_widget(self._tree, 30, 5)
helv = ('helvetica', -self._size.get())
bottom = y = self._cframe.scrollregion()[3]
self._textwidgets = [TextWidget(canvas, word, font=self._font)
for word in self._sent]
for twidget in self._textwidgets:
self._cframe.add_widget(twidget, 0, 0)
twidget.move(0, bottom-twidget.bbox()[3]-5)
y = min(y, twidget.bbox()[1])
self._textline = canvas.create_line(-5000, y-5, 5000, y-5, dash='.')
self._highlight_nodes()
self._highlight_prodlist()
self._position_text()
def _redraw_quick(self):
self._highlight_nodes()
self._highlight_prodlist()
self._position_text()
def _highlight_nodes(self):
bold = ('helvetica', -self._size.get(), 'bold')
for treeloc in self._parser.frontier()[:1]:
self._get(self._tree, treeloc)['color'] = '#20a050'
self._get(self._tree, treeloc)['font'] = bold
for treeloc in self._parser.frontier()[1:]:
self._get(self._tree, treeloc)['color'] = '#008080'
def _highlight_prodlist(self):
self._prodlist.delete(0, 'end')
expandable = self._parser.expandable_productions()
untried = self._parser.untried_expandable_productions()
productions = self._productions
for index in range(len(productions)):
if productions[index] in expandable:
if productions[index] in untried:
self._prodlist.insert(index, ' %s' % productions[index])
else:
self._prodlist.insert(index, ' %s (TRIED)' %
productions[index])
self._prodlist.selection_set(index)
else:
self._prodlist.insert(index, ' %s' % productions[index])
def _position_text(self):
numwords = len(self._sent)
num_matched = numwords - len(self._parser.remaining_text())
leaves = self._tree_leaves()[:num_matched]
xmax = self._tree.bbox()[0]
for i in range(0, len(leaves)):
widget = self._textwidgets[i]
leaf = leaves[i]
widget['color'] = '#006040'
leaf['color'] = '#006040'
widget.move(leaf.bbox()[0] - widget.bbox()[0], 0)
xmax = widget.bbox()[2] + 10
for i in range(len(leaves), numwords):
widget = self._textwidgets[i]
widget['color'] = '#a0a0a0'
widget.move(xmax - widget.bbox()[0], 0)
xmax = widget.bbox()[2] + 10
if self._parser.currently_complete():
for twidget in self._textwidgets:
twidget['color'] = '#00a000'
for i in range(0, len(leaves)):
widget = self._textwidgets[i]
leaf = leaves[i]
dy = widget.bbox()[1] - leaf.bbox()[3] - 10.0
dy = max(dy, leaf.parent().node().bbox()[3] - leaf.bbox()[3] + 10)
leaf.move(0, dy)
def _tree_leaves(self, tree=None):
if tree is None: tree = self._tree
if isinstance(tree, TreeSegmentWidget):
leaves = []
for child in tree.subtrees(): leaves += self._tree_leaves(child)
return leaves
else:
return [tree]
def destroy(self, *e):
self._autostep = 0
if self._top is None: return
self._top.destroy()
self._top = None
def reset(self, *e):
self._autostep = 0
self._parser.initialize(self._sent)
self._lastoper1['text'] = 'Reset Demo'
self._lastoper2['text'] = ''
self._redraw()
def autostep(self, *e):
if self._animation_frames.get() == 0:
self._animation_frames.set(2)
if self._autostep:
self._autostep = 0
else:
self._autostep = 1
self._step()
def cancel_autostep(self, *e):
self._autostep = 0
def step(self, *e): self._autostep = 0; self._step()
def match(self, *e): self._autostep = 0; self._match()
def expand(self, *e): self._autostep = 0; self._expand()
def backtrack(self, *e): self._autostep = 0; self._backtrack()
def _step(self):
if self._animating_lock: return
if self._expand(): pass
elif self._parser.untried_match() and self._match(): pass
elif self._backtrack(): pass
else:
self._lastoper1['text'] = 'Finished'
self._lastoper2['text'] = ''
self._autostep = 0
if self._parser.currently_complete():
self._autostep = 0
self._lastoper2['text'] += ' [COMPLETE PARSE]'
def _expand(self, *e):
if self._animating_lock: return
old_frontier = self._parser.frontier()
rv = self._parser.expand()
if rv is not None:
self._lastoper1['text'] = 'Expand:'
self._lastoper2['text'] = rv
self._prodlist.selection_clear(0, 'end')
index = self._productions.index(rv)
self._prodlist.selection_set(index)
self._animate_expand(old_frontier[0])
return 1
else:
self._lastoper1['text'] = 'Expand:'
self._lastoper2['text'] = '(all expansions tried)'
return 0
def _match(self, *e):
if self._animating_lock: return
old_frontier = self._parser.frontier()
rv = self._parser.match()
if rv is not None:
self._lastoper1['text'] = 'Match:'
self._lastoper2['text'] = rv
self._animate_match(old_frontier[0])
return 1
else:
self._lastoper1['text'] = 'Match:'
self._lastoper2['text'] = '(failed)'
return 0
def _backtrack(self, *e):
if self._animating_lock: return
if self._parser.backtrack():
elt = self._parser.tree()
for i in self._parser.frontier()[0]:
elt = elt[i]
self._lastoper1['text'] = 'Backtrack'
self._lastoper2['text'] = ''
if isinstance(elt, Tree):
self._animate_backtrack(self._parser.frontier()[0])
else:
self._animate_match_backtrack(self._parser.frontier()[0])
return 1
else:
self._autostep = 0
self._lastoper1['text'] = 'Finished'
self._lastoper2['text'] = ''
return 0
def about(self, *e):
ABOUT = ("NLTK Recursive Descent Parser Demo\n"+
"Written by Edward Loper")
TITLE = 'About: Recursive Descent Parser Demo'
try:
from tkMessageBox import Message
Message(message=ABOUT, title=TITLE).show()
except:
ShowText(self._top, TITLE, ABOUT)
def help(self, *e):
self._autostep = 0
try:
ShowText(self._top, 'Help: Recursive Descent Parser Demo',
(__doc__).strip(), width=75, font='fixed')
except:
ShowText(self._top, 'Help: Recursive Descent Parser Demo',
(__doc__).strip(), width=75)
def postscript(self, *e):
self._autostep = 0
self._cframe.print_to_file()
def mainloop(self, *args, **kwargs):
"""
Enter the Tkinter mainloop. This function must be called if
this demo is created from a non-interactive program (e.g.
from a secript); otherwise, the demo will close as soon as
the script completes.
"""
if in_idle(): return
self._top.mainloop(*args, **kwargs)
def resize(self, size=None):
if size is not None: self._size.set(size)
size = self._size.get()
self._font.configure(size=-(abs(size)))
self._boldfont.configure(size=-(abs(size)))
self._sysfont.configure(size=-(abs(size)))
self._bigfont.configure(size=-(abs(size+2)))
self._redraw()
def _toggle_grammar(self, *e):
if self._show_grammar.get():
self._prodframe.pack(fill='both', side='left', padx=2,
after=self._feedbackframe)
self._lastoper1['text'] = 'Show Grammar'
else:
self._prodframe.pack_forget()
self._lastoper1['text'] = 'Hide Grammar'
self._lastoper2['text'] = ''
def _prodlist_select(self, event):
selection = self._prodlist.curselection()
if len(selection) != 1: return
index = int(selection[0])
old_frontier = self._parser.frontier()
production = self._parser.expand(self._productions[index])
if production:
self._lastoper1['text'] = 'Expand:'
self._lastoper2['text'] = production
self._prodlist.selection_clear(0, 'end')
self._prodlist.selection_set(index)
self._animate_expand(old_frontier[0])
else:
self._prodlist.selection_clear(0, 'end')
for prod in self._parser.expandable_productions():
index = self._productions.index(prod)
self._prodlist.selection_set(index)
def _animate_expand(self, treeloc):
oldwidget = self._get(self._tree, treeloc)
oldtree = oldwidget.parent()
top = not isinstance(oldtree.parent(), TreeSegmentWidget)
tree = self._parser.tree()
for i in treeloc:
tree = tree[i]
widget = tree_to_treesegment(self._canvas, tree,
node_font=self._boldfont,
leaf_color='white',
tree_width=2, tree_color='white',
node_color='white',
leaf_font=self._font)
widget.node()['color'] = '#20a050'
(oldx, oldy) = oldtree.node().bbox()[:2]
(newx, newy) = widget.node().bbox()[:2]
widget.move(oldx-newx, oldy-newy)
if top:
self._cframe.add_widget(widget, 0, 5)
widget.move(30-widget.node().bbox()[0], 0)
self._tree = widget
else:
oldtree.parent().replace_child(oldtree, widget)
if widget.subtrees():
dx = (oldx + widget.node().width()/2 -
widget.subtrees()[0].bbox()[0]/2 -
widget.subtrees()[0].bbox()[2]/2)
for subtree in widget.subtrees(): subtree.move(dx, 0)
self._makeroom(widget)
if top:
self._cframe.destroy_widget(oldtree)
else:
oldtree.destroy()
colors = ['gray%d' % (10*int(10*x/self._animation_frames.get()))
for x in range(self._animation_frames.get(),0,-1)]
dy = widget.bbox()[3] + 30 - self._canvas.coords(self._textline)[1]
if dy > 0:
for twidget in self._textwidgets: twidget.move(0, dy)
self._canvas.move(self._textline, 0, dy)
self._animate_expand_frame(widget, colors)
def _makeroom(self, treeseg):
"""
Make sure that no sibling tree bbox's overlap.
"""
parent = treeseg.parent()
if not isinstance(parent, TreeSegmentWidget): return
index = parent.subtrees().index(treeseg)
rsiblings = parent.subtrees()[index+1:]
if rsiblings:
dx = treeseg.bbox()[2] - rsiblings[0].bbox()[0] + 10
for sibling in rsiblings: sibling.move(dx, 0)
if index > 0:
lsibling = parent.subtrees()[index-1]
dx = max(0, lsibling.bbox()[2] - treeseg.bbox()[0] + 10)
treeseg.move(dx, 0)
self._makeroom(parent)
def _animate_expand_frame(self, widget, colors):
if len(colors) > 0:
self._animating_lock = 1
widget['color'] = colors[0]
for subtree in widget.subtrees():
if isinstance(subtree, TreeSegmentWidget):
subtree.node()['color'] = colors[0]
else:
subtree['color'] = colors[0]
self._top.after(50, self._animate_expand_frame,
widget, colors[1:])
else:
widget['color'] = 'black'
for subtree in widget.subtrees():
if isinstance(subtree, TreeSegmentWidget):
subtree.node()['color'] = 'black'
else:
subtree['color'] = 'black'
self._redraw_quick()
widget.node()['color'] = 'black'
self._animating_lock = 0
if self._autostep: self._step()
def _animate_backtrack(self, treeloc):
if self._animation_frames.get() == 0: colors = []
else: colors = ['#a00000', '#000000', '#a00000']
colors += ['gray%d' % (10*int(10*x/(self._animation_frames.get())))
for x in range(1, self._animation_frames.get()+1)]
widgets = [self._get(self._tree, treeloc).parent()]
for subtree in widgets[0].subtrees():
if isinstance(subtree, TreeSegmentWidget):
widgets.append(subtree.node())
else:
widgets.append(subtree)
self._animate_backtrack_frame(widgets, colors)
def _animate_backtrack_frame(self, widgets, colors):
if len(colors) > 0:
self._animating_lock = 1
for widget in widgets: widget['color'] = colors[0]
self._top.after(50, self._animate_backtrack_frame,
widgets, colors[1:])
else:
for widget in widgets[0].subtrees():
widgets[0].remove_child(widget)
widget.destroy()
self._redraw_quick()
self._animating_lock = 0
if self._autostep: self._step()
def _animate_match_backtrack(self, treeloc):
widget = self._get(self._tree, treeloc)
node = widget.parent().node()
dy = (1.0 * (node.bbox()[3] - widget.bbox()[1] + 14) /
max(1, self._animation_frames.get()))
self._animate_match_backtrack_frame(self._animation_frames.get(),
widget, dy)
def _animate_match(self, treeloc):
widget = self._get(self._tree, treeloc)
dy = ((self._textwidgets[0].bbox()[1] - widget.bbox()[3] - 10.0) /
max(1, self._animation_frames.get()))
self._animate_match_frame(self._animation_frames.get(), widget, dy)
def _animate_match_frame(self, frame, widget, dy):
if frame > 0:
self._animating_lock = 1
widget.move(0, dy)
self._top.after(10, self._animate_match_frame,
frame-1, widget, dy)
else:
widget['color'] = '#006040'
self._redraw_quick()
self._animating_lock = 0
if self._autostep: self._step()
def _animate_match_backtrack_frame(self, frame, widget, dy):
if frame > 0:
self._animating_lock = 1
widget.move(0, dy)
self._top.after(10, self._animate_match_backtrack_frame,
frame-1, widget, dy)
else:
widget.parent().remove_child(widget)
widget.destroy()
self._animating_lock = 0
if self._autostep: self._step()
def edit_grammar(self, *e):
CFGEditor(self._top, self._parser.grammar(), self.set_grammar)
def set_grammar(self, grammar):
self._parser.set_grammar(grammar)
self._productions = list(grammar.productions())
self._prodlist.delete(0, 'end')
for production in self._productions:
self._prodlist.insert('end', (' %s' % production))
def edit_sentence(self, *e):
sentence = string.join(self._sent)
title = 'Edit Text'
instr = 'Enter a new sentence to parse.'
EntryDialog(self._top, sentence, instr, self.set_sentence, title)
def set_sentence(self, sentence):
self._sent = sentence.split()
self.reset()
def demo():
"""
Create a recursive descent parser demo, using a simple grammar and
text.
"""
from nltk import cfg
grammar = cfg.parse_cfg("""
# Grammatical productions.
S -> NP VP
NP -> Det N PP | Det N
VP -> V NP PP | V NP | V
PP -> P NP
# Lexical productions.
NP -> 'I'
Det -> 'the' | 'a'
N -> 'man' | 'park' | 'dog' | 'telescope'
V -> 'ate' | 'saw'
P -> 'in' | 'under' | 'with'
""")
sent = 'the dog saw a man in the park'.split()
RecursiveDescentDemo(grammar, sent).mainloop()
if __name__ == '__main__': demo()