1
2
3
4
5
6
7
8
9
10
11
12 """
13 A graphical tool for exploring chart parsing.
14
15 Chart parsing is a flexible parsing algorithm that uses a data
16 structure called a "chart" to record hypotheses about syntactic
17 constituents. Each hypothesis is represented by a single "edge" on
18 the chart. A set of "chart rules" determine when new edges can be
19 added to the chart. This set of rules controls the overall behavior
20 of the parser (e.g., whether it parses top-down or bottom-up).
21
22 The chart parsing tool demonstrates the process of parsing a single
23 sentence, with a given grammar and lexicon. Its display is divided
24 into three sections: the bottom section displays the chart; the middle
25 section displays the sentence; and the top section displays the
26 partial syntax tree corresponding to the selected edge. Buttons along
27 the bottom of the window are used to control the execution of the
28 algorithm.
29
30 The chart parsing tool allows for flexible control of the parsing
31 algorithm. At each step of the algorithm, you can select which rule
32 or strategy you wish to apply. This allows you to experiment with
33 mixing different strategies (e.g., top-down and bottom-up). You can
34 exercise fine-grained control over the algorithm by selecting which
35 edge you wish to apply a rule to.
36 """
37
38
39
40
41 import pickle
42 from tkFileDialog import asksaveasfilename, askopenfilename
43 import Tkinter, tkFont, tkMessageBox
44 import math
45 import string
46 import os.path
47
48 from nltk.parse.chart import *
49 from nltk import tokenize, Tree, cfg
50 from nltk.draw import ShowText, EntryDialog, in_idle
51 from nltk.draw import MutableOptionMenu
52 from nltk.draw import ColorizedList, SymbolWidget, CanvasFrame
53 from nltk.draw.cfg import CFGEditor
54 from nltk.draw.tree import tree_to_treesegment, TreeSegmentWidget
55
56
57
58
59
60
61
62
87
88
89
90
91
93 """
94 A view of a chart that displays the contents of the corresponding matrix.
95 """
96 - def __init__(self, parent, chart, toplevel=True, title='Chart Matrix',
97 show_numedges=False):
98 self._chart = chart
99 self._cells = []
100 self._marks = []
101
102 self._selected_cell = None
103
104 if toplevel:
105 self._root = Tkinter.Toplevel(parent)
106 self._root.title(title)
107 self._root.bind('<Control-q>', self.destroy)
108 self._init_quit(self._root)
109 else:
110 self._root = Tkinter.Frame(parent)
111
112 self._init_matrix(self._root)
113 self._init_list(self._root)
114 if show_numedges:
115 self._init_numedges(self._root)
116 else:
117 self._numedges_label = None
118
119 self._callbacks = {}
120
121 self._num_edges = 0
122
123 self.draw()
124
128
130 cframe = Tkinter.Frame(root, border=2, relief='sunken')
131 cframe.pack(expand=0, fill='none', padx=1, pady=3, side='top')
132 self._canvas = Tkinter.Canvas(cframe, width=200, height=200,
133 background='white')
134 self._canvas.pack(expand=0, fill='none')
135
137 self._numedges_label = Tkinter.Label(root, text='0 edges')
138 self._numedges_label.pack(expand=0, fill='none', side='top')
139
146
148 if self._root is None: return
149 try: self._root.destroy()
150 except: pass
151 self._root = None
152
154 if chart is not self._chart:
155 self._chart = chart
156 self._num_edges = 0
157 self.draw()
158
160 if self._root is None: return
161
162
163 N = len(self._cells)
164 cell_edges = [[0 for i in range(N)] for j in range(N)]
165 for edge in self._chart:
166 cell_edges[edge.start()][edge.end()] += 1
167
168
169 for i in range(N):
170 for j in range(i, N):
171 if cell_edges[i][j] == 0:
172 color = 'gray20'
173 else:
174 color = ('#00%02x%02x' %
175 (min(255, 50+128*cell_edges[i][j]/10),
176 max(0, 128-128*cell_edges[i][j]/10)))
177 cell_tag = self._cells[i][j]
178 self._canvas.itemconfig(cell_tag, fill=color)
179 if (i,j) == self._selected_cell:
180 self._canvas.itemconfig(cell_tag, outline='#00ffff',
181 width=3)
182 self._canvas.tag_raise(cell_tag)
183 else:
184 self._canvas.itemconfig(cell_tag, outline='black',
185 width=1)
186
187
188 edges = list(self._chart.select(span=self._selected_cell))
189 self._list.set(edges)
190
191
192 self._num_edges = self._chart.num_edges()
193 if self._numedges_label is not None:
194 self._numedges_label['text'] = '%d edges' % self._num_edges
195
197 self._canvas.itemconfig('inactivebox', state='hidden')
198 self.update()
199
201 self._canvas.itemconfig('inactivebox', state='normal')
202 self.update()
203
205 self._callbacks.setdefault(event,{})[func] = 1
206
208 if func is None: del self._callbacks[event]
209 else:
210 try: del self._callbacks[event][func]
211 except: pass
212
214 if not self._callbacks.has_key(event): return
215 for cb_func in self._callbacks[event].keys(): cb_func(*args)
216
218 if self._root is None: return
219
220
221
222 if ((i,j) == self._selected_cell and
223 self._chart.num_edges() == self._num_edges): return
224
225 self._selected_cell = (i,j)
226 self.update()
227
228
229 self._fire_callbacks('select_cell', i, j)
230
232 if self._root is None: return
233 self._selected_cell = None
234 self._list.set([])
235 self.update()
236
242
246
251
253 if self._root is None: return
254 self._list.unmark(edge)
255
260
262 if self._root is None: return
263 LEFT_MARGIN = BOT_MARGIN = 15
264 TOP_MARGIN = 5
265 c = self._canvas
266 c.delete('all')
267 N = self._chart.num_leaves()+1
268 dx = (int(c['width'])-LEFT_MARGIN)/N
269 dy = (int(c['height'])-TOP_MARGIN-BOT_MARGIN)/N
270
271 c.delete('all')
272
273
274 for i in range(N):
275 c.create_text(LEFT_MARGIN-2, i*dy+dy/2+TOP_MARGIN,
276 text=`i`, anchor='e')
277 c.create_text(i*dx+dx/2+LEFT_MARGIN, N*dy+TOP_MARGIN+1,
278 text=`i`, anchor='n')
279 c.create_line(LEFT_MARGIN, dy*(i+1)+TOP_MARGIN,
280 dx*N+LEFT_MARGIN, dy*(i+1)+TOP_MARGIN, dash='.')
281 c.create_line(dx*i+LEFT_MARGIN, TOP_MARGIN,
282 dx*i+LEFT_MARGIN, dy*N+TOP_MARGIN, dash='.')
283
284
285 c.create_rectangle(LEFT_MARGIN, TOP_MARGIN,
286 LEFT_MARGIN+dx*N, dy*N+TOP_MARGIN,
287 width=2)
288
289
290 self._cells = [[None for i in range(N)] for j in range(N)]
291 for i in range(N):
292 for j in range(i, N):
293 t = c.create_rectangle(j*dx+LEFT_MARGIN, i*dy+TOP_MARGIN,
294 (j+1)*dx+LEFT_MARGIN,
295 (i+1)*dy+TOP_MARGIN,
296 fill='gray20')
297 self._cells[i][j] = t
298 def cb(event, self=self, i=i, j=j): self._click_cell(i,j)
299 c.tag_bind(t, '<Button-1>', cb)
300
301
302 xmax, ymax = int(c['width']), int(c['height'])
303 t = c.create_rectangle(-100, -100, xmax+100, ymax+100,
304 fill='gray50', state='hidden',
305 tag='inactivebox')
306 c.tag_lower(t)
307
308
309 self.update()
310
311 - def pack(self, *args, **kwargs):
312 self._root.pack(*args, **kwargs)
313
314
315
316
317
319 - def __init__(self, parent, chart, grammar, toplevel=True):
320 self._chart = chart
321 self._grammar = grammar
322 self._trees = []
323 self._y = 10
324 self._treewidgets = []
325 self._selection = None
326 self._selectbox = None
327
328 if toplevel:
329 self._root = Tkinter.Toplevel(parent)
330 self._root.title('Chart Parsing Demo: Results')
331 self._root.bind('<Control-q>', self.destroy)
332 else:
333 self._root = Tkinter.Frame(parent)
334
335
336 if toplevel:
337 buttons = Tkinter.Frame(self._root)
338 buttons.pack(side='bottom', expand=0, fill='x')
339 Tkinter.Button(buttons, text='Quit',
340 command=self.destroy).pack(side='right')
341 Tkinter.Button(buttons, text='Print All',
342 command=self.print_all).pack(side='left')
343 Tkinter.Button(buttons, text='Print Selection',
344 command=self.print_selection).pack(side='left')
345
346
347 self._cframe = CanvasFrame(self._root, closeenough=20)
348 self._cframe.pack(side='top', expand=1, fill='both')
349
350
351 self.update()
352
354 if self._root is None: return
355
356 if edge is not None:
357 if edge.lhs() != self._grammar.start(): return
358 if edge.span() != (0, self._chart.num_leaves()): return
359
360 for parse in self._chart.parses(self._grammar.start()):
361 if parse not in self._trees:
362 self._add(parse)
363
364 - def _add(self, parse):
381
383 c = self._cframe.canvas()
384 if self._selection is not None:
385 c.delete(self._selectbox)
386 self._selection = widget
387 (x1, y1, x2, y2) = widget.bbox()
388 self._selectbox = c.create_rectangle(x1, y1, x2, y2,
389 width=2, outline='#088')
390
391 - def _color(self, treewidget, color):
398
400 if self._root is None: return
401 self._cframe.print_to_file()
402
404 if self._root is None: return
405 if self._selection is None:
406 tkMessageBox.showerror('Print Error', 'No tree selected')
407 else:
408 c = self._cframe.canvas()
409 for widget in self._treewidgets:
410 if widget is not self._selection:
411 self._cframe.destroy_widget(widget)
412 c.delete(self._selectbox)
413 (x1,y1,x2,y2) = self._selection.bbox()
414 self._selection.move(10-x1,10-y1)
415 c['scrollregion'] = '0 0 %s %s' % (x2-x1+20, y2-y1+20)
416 self._cframe.print_to_file()
417
418
419 self._treewidgets = [self._selection]
420 self.clear()
421 self.update()
422
424 if self._root is None: return
425 for treewidget in self._treewidgets:
426 self._cframe.destroy_widget(treewidget)
427 self._trees = []
428 self._treewidgets = []
429 if self._selection is not None:
430 self._cframe.canvas().delete(self._selectbox)
431 self._selection = None
432 self._y = 10
433
438
443
445 if self._root is None: return
446 try: self._root.destroy()
447 except: pass
448 self._root = None
449
450 - def pack(self, *args, **kwargs):
451 self._root.pack(*args, **kwargs)
452
453
454
455
456
458 """
459
460 @ivar _root: The root window
461
462 @ivar _charts: A dictionary mapping names to charts. When
463 charts are loaded, they are added to this dictionary.
464
465 @ivar _left_chart: The left L{Chart}.
466 @ivar _left_name: The name C{_left_chart} (derived from filename)
467 @ivar _left_matrix: The L{ChartMatrixView} for C{_left_chart}
468 @ivar _left_selector: The drop-down C{MutableOptionsMenu} used
469 to select C{_left_chart}.
470
471 @ivar _right_chart: The right L{Chart}.
472 @ivar _right_name: The name C{_right_chart} (derived from filename)
473 @ivar _right_matrix: The L{ChartMatrixView} for C{_right_chart}
474 @ivar _right_selector: The drop-down C{MutableOptionsMenu} used
475 to select C{_right_chart}.
476
477 @ivar _out_chart: The out L{Chart}.
478 @ivar _out_name: The name C{_out_chart} (derived from filename)
479 @ivar _out_matrix: The L{ChartMatrixView} for C{_out_chart}
480 @ivar _out_label: The label for C{_out_chart}.
481
482 @ivar _op_label: A Label containing the most recent operation.
483 """
484
485 _OPSYMBOL = {'-': '-',
486 'and': SymbolWidget.SYMBOLS['intersection'],
487 'or': SymbolWidget.SYMBOLS['union']}
488
490
491
492 faketok = [''] * 8
493 self._emptychart = Chart(faketok)
494
495
496 self._left_name = 'None'
497 self._right_name = 'None'
498 self._left_chart = self._emptychart
499 self._right_chart = self._emptychart
500
501
502 self._charts = {'None': self._emptychart}
503
504
505 self._out_chart = self._emptychart
506
507
508 self._operator = None
509
510
511 self._root = Tkinter.Tk()
512 self._root.title('Chart Comparison')
513 self._root.bind('<Control-q>', self.destroy)
514 self._root.bind('<Control-x>', self.destroy)
515
516
517 self._init_menubar(self._root)
518 self._init_chartviews(self._root)
519 self._init_divider(self._root)
520 self._init_buttons(self._root)
521 self._init_bindings(self._root)
522
523
524 for filename in chart_filenames:
525 self.load_chart(filename)
526
528 if self._root is None: return
529 try: self._root.destroy()
530 except: pass
531 self._root = None
532
533 - def mainloop(self, *args, **kwargs):
534 return
535 self._root.mainloop(*args, **kwargs)
536
537
538
539
540
542 menubar = Tkinter.Menu(root)
543
544
545 filemenu = Tkinter.Menu(menubar, tearoff=0)
546 filemenu.add_command(label='Load Chart', accelerator='Ctrl-o',
547 underline=0, command=self.load_chart_dialog)
548 filemenu.add_command(label='Save Output', accelerator='Ctrl-s',
549 underline=0, command=self.save_chart_dialog)
550 filemenu.add_separator()
551 filemenu.add_command(label='Exit', underline=1,
552 command=self.destroy, accelerator='Ctrl-x')
553 menubar.add_cascade(label='File', underline=0, menu=filemenu)
554
555
556 opmenu = Tkinter.Menu(menubar, tearoff=0)
557 opmenu.add_command(label='Intersection',
558 command=self._intersection,
559 accelerator='+')
560 opmenu.add_command(label='Union',
561 command=self._union,
562 accelerator='*')
563 opmenu.add_command(label='Difference',
564 command=self._difference,
565 accelerator='-')
566 opmenu.add_separator()
567 opmenu.add_command(label='Swap Charts',
568 command=self._swapcharts)
569 menubar.add_cascade(label='Compare', underline=0, menu=opmenu)
570
571
572 self._root.config(menu=menubar)
573
575 divider = Tkinter.Frame(root, border=2, relief='sunken')
576 divider.pack(side='top', fill='x', ipady=2)
577
579 opfont=('symbol', -36)
580 eqfont=('helvetica', -36)
581
582 frame = Tkinter.Frame(root, background='#c0c0c0')
583 frame.pack(side='top', expand=1, fill='both')
584
585
586 cv1_frame = Tkinter.Frame(frame, border=3, relief='groove')
587 cv1_frame.pack(side='left', padx=8, pady=7, expand=1, fill='both')
588 self._left_selector = MutableOptionMenu(
589 cv1_frame, self._charts.keys(), command=self._select_left)
590 self._left_selector.pack(side='top', pady=5, fill='x')
591 self._left_matrix = ChartMatrixView(cv1_frame, self._emptychart,
592 toplevel=False,
593 show_numedges=True)
594 self._left_matrix.pack(side='bottom', padx=5, pady=5,
595 expand=1, fill='both')
596 self._left_matrix.add_callback('select', self.select_edge)
597 self._left_matrix.add_callback('select_cell', self.select_cell)
598 self._left_matrix.inactivate()
599
600
601 self._op_label = Tkinter.Label(frame, text=' ', width=3,
602 background='#c0c0c0', font=opfont)
603 self._op_label.pack(side='left', padx=5, pady=5)
604
605
606 cv2_frame = Tkinter.Frame(frame, border=3, relief='groove')
607 cv2_frame.pack(side='left', padx=8, pady=7, expand=1, fill='both')
608 self._right_selector = MutableOptionMenu(
609 cv2_frame, self._charts.keys(), command=self._select_right)
610 self._right_selector.pack(side='top', pady=5, fill='x')
611 self._right_matrix = ChartMatrixView(cv2_frame, self._emptychart,
612 toplevel=False,
613 show_numedges=True)
614 self._right_matrix.pack(side='bottom', padx=5, pady=5,
615 expand=1, fill='both')
616 self._right_matrix.add_callback('select', self.select_edge)
617 self._right_matrix.add_callback('select_cell', self.select_cell)
618 self._right_matrix.inactivate()
619
620
621 Tkinter.Label(frame, text='=', width=3, background='#c0c0c0',
622 font=eqfont).pack(side='left', padx=5, pady=5)
623
624
625 out_frame = Tkinter.Frame(frame, border=3, relief='groove')
626 out_frame.pack(side='left', padx=8, pady=7, expand=1, fill='both')
627 self._out_label = Tkinter.Label(out_frame, text='Output')
628 self._out_label.pack(side='top', pady=9)
629 self._out_matrix = ChartMatrixView(out_frame, self._emptychart,
630 toplevel=False,
631 show_numedges=True)
632 self._out_matrix.pack(side='bottom', padx=5, pady=5,
633 expand=1, fill='both')
634 self._out_matrix.add_callback('select', self.select_edge)
635 self._out_matrix.add_callback('select_cell', self.select_cell)
636 self._out_matrix.inactivate()
637
653
657
658
659
660
661
662
669
676
678 if self._operator == '-': self._difference()
679 elif self._operator == 'or': self._union()
680 elif self._operator == 'and': self._intersection()
681
682
683
684
685
686 CHART_FILE_TYPES = [('Pickle file', '.pickle'),
687 ('All files', '*')]
688
690 filename = asksaveasfilename(filetypes=self.CHART_FILE_TYPES,
691 defaultextension='.pickle')
692 if not filename: return
693 try: pickle.dump((self._out_chart), open(filename, 'w'))
694 except Exception, e:
695 tkMessageBox.showerror('Error Saving Chart',
696 'Unable to open file: %r\n%s' %
697 (filename, e))
698
708
724
726 self._left_matrix.update()
727 self._right_matrix.update()
728 self._out_matrix.update()
729
730
731
732
733
735 if edge in self._left_chart:
736 self._left_matrix.markonly_edge(edge)
737 else:
738 self._left_matrix.unmark_edge()
739 if edge in self._right_chart:
740 self._right_matrix.markonly_edge(edge)
741 else:
742 self._right_matrix.unmark_edge()
743 if edge in self._out_chart:
744 self._out_matrix.markonly_edge(edge)
745 else:
746 self._out_matrix.unmark_edge()
747
752
753
754
755
756
758 if not self._checkcompat(): return
759
760 out_chart = Chart(self._left_chart.tokens())
761 for edge in self._left_chart:
762 if edge not in self._right_chart:
763 out_chart.insert(edge, [])
764
765 self._update('-', out_chart)
766
768 if not self._checkcompat(): return
769
770 out_chart = Chart(self._left_chart.tokens())
771 for edge in self._left_chart:
772 if edge in self._right_chart:
773 out_chart.insert(edge, [])
774
775 self._update('and', out_chart)
776
778 if not self._checkcompat(): return
779
780 out_chart = Chart(self._left_chart.tokens())
781 for edge in self._left_chart:
782 out_chart.insert(edge, [])
783 for edge in self._right_chart:
784 out_chart.insert(edge, [])
785
786 self._update('or', out_chart)
787
789 left, right = self._left_name, self._right_name
790 self._left_selector.set(right)
791 self._right_selector.set(left)
792
794 if (self._left_chart.tokens() != self._right_chart.tokens() or
795 self._left_chart.property_names() !=
796 self._right_chart.property_names() or
797 self._left_chart == self._emptychart or
798 self._right_chart == self._emptychart):
799
800 self._out_chart = self._emptychart
801 self._out_matrix.set_chart(self._out_chart)
802 self._out_matrix.inactivate()
803 self._out_label['text'] = 'Output'
804
805 return False
806 else:
807 return True
808
809 - def _update(self, operator, out_chart):
810 self._operator = operator
811 self._op_label['text'] = self._OPSYMBOL[operator]
812 self._out_chart = out_chart
813 self._out_matrix.set_chart(out_chart)
814 self._out_label['text'] = '%s %s %s' % (self._left_name,
815 self._operator,
816 self._right_name)
817
819 self._out_chart = self._emptychart
820 self._out_matrix.set_chart(self._out_chart)
821 self._op_label['text'] = ' '
822 self._out_matrix.inactivate()
823
827
828
829
830
831
832
833
834
835
836
837
838
840 """
841 A component for viewing charts. This is used by C{ChartDemo} to
842 allow students to interactively experiment with various chart
843 parsing techniques. It is also used by C{Chart.draw()}.
844
845 @ivar _chart: The chart that we are giving a view of. This chart
846 may be modified; after it is modified, you should call
847 C{update}.
848 @ivar _sentence: The list of tokens that the chart spans.
849
850 @ivar _root: The root window.
851 @ivar _chart_canvas: The canvas we're using to display the chart
852 itself.
853 @ivar _tree_canvas: The canvas we're using to display the tree
854 that each edge spans. May be None, if we're not displaying
855 trees.
856 @ivar _sentence_canvas: The canvas we're using to display the sentence
857 text. May be None, if we're not displaying the sentence text.
858 @ivar _edgetags: A dictionary mapping from edges to the tags of
859 the canvas elements (lines, etc) used to display that edge.
860 The values of this dictionary have the form
861 C{(linetag, rhstag1, dottag, rhstag2, lhstag)}.
862 @ivar _treetags: A list of all the tags that make up the tree;
863 used to erase the tree (without erasing the loclines).
864 @ivar _chart_height: The height of the chart canvas.
865 @ivar _sentence_height: The height of the sentence canvas.
866 @ivar _tree_height: The height of the tree
867
868 @ivar _text_height: The height of a text string (in the normal
869 font).
870
871 @ivar _edgelevels: A list of edges at each level of the chart (the
872 top level is the 0th element). This list is used to remember
873 where edges should be drawn; and to make sure that no edges
874 are overlapping on the chart view.
875
876 @ivar _unitsize: Pixel size of one unit (from the location). This
877 is determined by the span of the chart's location, and the
878 width of the chart display canvas.
879
880 @ivar _fontsize: The current font size
881
882 @ivar _marks: A dictionary from edges to marks. Marks are
883 strings, specifying colors (e.g. 'green').
884 """
885
886 _LEAF_SPACING = 10
887 _MARGIN = 10
888 _TREE_LEVEL_SIZE = 12
889 _CHART_LEVEL_SIZE = 40
890
891 - def __init__(self, chart, root=None, **kw):
892 """
893 Construct a new C{Chart} display.
894 """
895
896 draw_tree = kw.get('draw_tree', 0)
897 draw_sentence = kw.get('draw_sentence', 1)
898 self._fontsize = kw.get('fontsize', -12)
899
900
901 self._chart = chart
902
903
904 self._callbacks = {}
905
906
907 self._edgelevels = []
908 self._edgetags = {}
909
910
911 self._marks = {}
912
913
914
915 self._treetoks = []
916 self._treetoks_edge = None
917 self._treetoks_index = 0
918
919
920 self._tree_tags = []
921
922
923 self._compact = 0
924
925
926 if root is None:
927 top = Tkinter.Tk()
928 top.title('Chart View')
929 def destroy1(e, top=top): top.destroy()
930 def destroy2(top=top): top.destroy()
931 top.bind('q', destroy1)
932 b = Tkinter.Button(top, text='Done', command=destroy2)
933 b.pack(side='bottom')
934 self._root = top
935 else:
936 self._root = root
937
938
939 self._init_fonts(root)
940
941
942 (self._chart_sb, self._chart_canvas) = self._sb_canvas(self._root)
943 self._chart_canvas['height'] = 300
944 self._chart_canvas['closeenough'] = 15
945
946
947 if draw_sentence:
948 cframe = Tkinter.Frame(self._root, relief='sunk', border=2)
949 cframe.pack(fill='both', side='bottom')
950 self._sentence_canvas = Tkinter.Canvas(cframe, height=50)
951 self._sentence_canvas['background'] = '#e0e0e0'
952 self._sentence_canvas.pack(fill='both')
953
954 else:
955 self._sentence_canvas = None
956
957
958 if draw_tree:
959 (sb, canvas) = self._sb_canvas(self._root, 'n', 'x')
960 (self._tree_sb, self._tree_canvas) = (sb, canvas)
961 self._tree_canvas['height'] = 200
962 else:
963 self._tree_canvas = None
964
965
966 self._analyze()
967 self.draw()
968 self._resize()
969 self._grow()
970
971
972
973 self._chart_canvas.bind('<Configure>', self._configure)
974
976 self._boldfont = tkFont.Font(family='helvetica', weight='bold',
977 size=self._fontsize)
978 self._font = tkFont.Font(family='helvetica',
979 size=self._fontsize)
980
981 self._sysfont = tkFont.Font(font=Tkinter.Button()["font"])
982 root.option_add("*Font", self._sysfont)
983
984 - def _sb_canvas(self, root, expand='y',
985 fill='both', side='bottom'):
986 """
987 Helper for __init__: construct a canvas with a scrollbar.
988 """
989 cframe =Tkinter.Frame(root, relief='sunk', border=2)
990 cframe.pack(fill=fill, expand=expand, side=side)
991 canvas = Tkinter.Canvas(cframe, background='#e0e0e0')
992
993
994 sb = Tkinter.Scrollbar(cframe, orient='vertical')
995 sb.pack(side='right', fill='y')
996 canvas.pack(side='left', fill=fill, expand='yes')
997
998
999 sb['command']= canvas.yview
1000 canvas['yscrollcommand'] = sb.set
1001
1002 return (sb, canvas)
1003
1006
1009
1010 - def page_up(self, *e):
1011 self._chart_canvas.yview('scroll', -1, 'pages')
1012
1013 - def page_down(self, *e):
1014 self._chart_canvas.yview('scroll', 1, 'pages')
1015
1017 """
1018 Grow the window, if necessary
1019 """
1020
1021 N = self._chart.num_leaves()
1022 width = max(int(self._chart_canvas['width']),
1023 N * self._unitsize + ChartView._MARGIN * 2 )
1024
1025
1026
1027 self._chart_canvas.configure(width=width)
1028 self._chart_canvas.configure(height=self._chart_canvas['height'])
1029
1030 self._unitsize = (width - 2*ChartView._MARGIN) / N
1031
1032
1033 if self._sentence_canvas is not None:
1034 self._sentence_canvas['height'] = self._sentence_height
1035
1043
1045 return abs(self._fontsize)
1046
1057
1058 - def update(self, chart=None):
1059 """
1060 Draw any edges that have not been drawn. This is typically
1061 called when a after modifies the canvas that a CanvasView is
1062 displaying. C{update} will cause any edges that have been
1063 added to the chart to be drawn.
1064
1065 If update is given a C{chart} argument, then it will replace
1066 the current chart with the given chart.
1067 """
1068 if chart is not None:
1069 self._chart = chart
1070 self._edgelevels = []
1071 self._marks = {}
1072 self._analyze()
1073 self._grow()
1074 self.draw()
1075 self.erase_tree()
1076 self._resize()
1077 else:
1078 for edge in self._chart:
1079 if not self._edgetags.has_key(edge):
1080 self._add_edge(edge)
1081 self._resize()
1082
1083
1085 """
1086 Return 1 if the given edge overlaps with any edge on the given
1087 level. This is used by _add_edge to figure out what level a
1088 new edge should be added to.
1089 """
1090 (s1, e1) = edge.span()
1091 for otheredge in self._edgelevels[lvl]:
1092 (s2, e2) = otheredge.span()
1093 if (s1 <= s2 < e1) or (s2 <= s1 < e2) or (s1==s2==e1==e2):
1094 return 1
1095 return 0
1096
1098 """
1099 Given a new edge, recalculate:
1100
1101 - _text_height
1102 - _unitsize (if the edge text is too big for the current
1103 _unitsize, then increase _unitsize)
1104 """
1105 c = self._chart_canvas
1106
1107 if isinstance(edge, TreeEdge):
1108 lhs = edge.lhs()
1109 rhselts = []
1110 for elt in edge.rhs():
1111 if isinstance(elt, cfg.Nonterminal):
1112 rhselts.append(str(elt.symbol()))
1113 else:
1114 rhselts.append(repr(elt))
1115 rhs = string.join(rhselts)
1116 else:
1117 lhs = edge.lhs()
1118 rhs = ''
1119
1120 for s in (lhs, rhs):
1121 tag = c.create_text(0,0, text=s,
1122 font=self._boldfont,
1123 anchor='nw', justify='left')
1124 bbox = c.bbox(tag)
1125 c.delete(tag)
1126 width = bbox[2]
1127 edgelen = max(edge.length(), 1)
1128 self._unitsize = max(self._unitsize, width/edgelen)
1129 self._text_height = max(self._text_height, bbox[3] - bbox[1])
1130
1132 """
1133 Add a single edge to the ChartView:
1134
1135 - Call analyze_edge to recalculate display parameters
1136 - Find an available level
1137 - Call _draw_edge
1138 """
1139 if self._edgetags.has_key(edge): return
1140 self._analyze_edge(edge)
1141 self._grow()
1142
1143 if not self._compact:
1144 self._edgelevels.append([edge])
1145 lvl = len(self._edgelevels)-1
1146 self._draw_edge(edge, lvl)
1147 self._resize()
1148 return
1149
1150
1151 lvl = 0
1152 while 1:
1153
1154 while lvl >= len(self._edgelevels):
1155 self._edgelevels.append([])
1156 self._resize()
1157
1158
1159 if lvl>=minlvl and not self._edge_conflict(edge, lvl):
1160
1161 self._edgelevels[lvl].append(edge)
1162 break
1163
1164
1165 lvl += 1
1166
1167 self._draw_edge(edge, lvl)
1168
1170 level = None
1171 for i in range(len(self._edgelevels)):
1172 if edge in self._edgelevels[i]:
1173 level = i
1174 break
1175 if level == None: return
1176
1177 y = (level+1) * self._chart_level_size
1178 dy = self._text_height + 10
1179 self._chart_canvas.yview('moveto', 1.0)
1180 if self._chart_height != 0:
1181 self._chart_canvas.yview('moveto',
1182 float(y-dy)/self._chart_height)
1183
1185 """
1186 Draw a single edge on the ChartView.
1187 """
1188 c = self._chart_canvas
1189
1190
1191 x1 = (edge.start() * self._unitsize + ChartView._MARGIN)
1192 x2 = (edge.end() * self._unitsize + ChartView._MARGIN)
1193 if x2 == x1: x2 += max(4, self._unitsize/5)
1194 y = (lvl+1) * self._chart_level_size
1195 linetag = c.create_line(x1, y, x2, y, arrow='last', width=3)
1196
1197
1198 if isinstance(edge, TreeEdge):
1199 rhs = []
1200 for elt in edge.rhs():
1201 if isinstance(elt, cfg.Nonterminal):
1202 rhs.append(str(elt.symbol()))
1203 else:
1204 rhs.append(repr(elt))
1205 pos = edge.dot()
1206 else:
1207 rhs = []
1208 pos = 0
1209
1210 rhs1 = string.join(rhs[:pos])
1211 rhs2 = string.join(rhs[pos:])
1212 rhstag1 = c.create_text(x1+3, y, text=rhs1,
1213 font=self._font,
1214 anchor='nw')
1215 dotx = c.bbox(rhstag1)[2] + 6
1216 doty = (c.bbox(rhstag1)[1]+c.bbox(rhstag1)[3])/2
1217 dottag = c.create_oval(dotx-2, doty-2, dotx+2, doty+2)
1218 rhstag2 = c.create_text(dotx+6, y, text=rhs2,
1219 font=self._font,
1220 anchor='nw')
1221 lhstag = c.create_text((x1+x2)/2, y, text=str(edge.lhs()),
1222 anchor='s',
1223 font=self._boldfont)
1224
1225
1226 self._edgetags[edge] = (linetag, rhstag1,
1227 dottag, rhstag2, lhstag)
1228
1229
1230 def cb(event, self=self, edge=edge):
1231 self._fire_callbacks('select', edge)
1232 c.tag_bind(rhstag1, '<Button-1>', cb)
1233 c.tag_bind(rhstag2, '<Button-1>', cb)
1234 c.tag_bind(linetag, '<Button-1>', cb)
1235 c.tag_bind(dottag, '<Button-1>', cb)
1236 c.tag_bind(lhstag, '<Button-1>', cb)
1237
1238 self._color_edge(edge)
1239
1240 - def _color_edge(self, edge, linecolor=None, textcolor=None):
1241 """
1242 Color in an edge with the given colors.
1243 If no colors are specified, use intelligent defaults
1244 (dependant on selection, etc.)
1245 """
1246 if not self._edgetags.has_key(edge): return
1247 c = self._chart_canvas
1248
1249 if linecolor is not None and textcolor is not None:
1250 if self._marks.has_key(edge):
1251 linecolor = self._marks[edge]
1252 tags = self._edgetags[edge]
1253 c.itemconfig(tags[0], fill=linecolor)
1254 c.itemconfig(tags[1], fill=textcolor)
1255 c.itemconfig(tags[2], fill=textcolor,
1256 outline=textcolor)
1257 c.itemconfig(tags[3], fill=textcolor)
1258 c.itemconfig(tags[4], fill=textcolor)
1259 return
1260 else:
1261 N = self._chart.num_leaves()
1262 if self._marks.has_key(edge):
1263 self._color_edge(self._marks[edge])
1264 if (edge.is_complete() and edge.span() == (0, N)):
1265 self._color_edge(edge, '#084', '#042')
1266 elif isinstance(edge, LeafEdge):
1267 self._color_edge(edge, '#48c', '#246')
1268 else:
1269 self._color_edge(edge, '#00f', '#008')
1270
1272 """
1273 Mark an edge
1274 """
1275 self._marks[edge] = mark
1276 self._color_edge(edge)
1277
1279 """
1280 Unmark an edge (or all edges)
1281 """
1282 if edge == None:
1283 old_marked_edges = self._marks.keys()
1284 self._marks = {}
1285 for edge in old_marked_edges:
1286 self._color_edge(edge)
1287 else:
1288 del self._marks[edge]
1289 self._color_edge(edge)
1290
1294
1296 """
1297 Analyze the sentence string, to figure out how big a unit needs
1298 to be, How big the tree should be, etc.
1299 """
1300
1301 unitsize = 70
1302 text_height = 0
1303 c = self._chart_canvas
1304
1305
1306 for leaf in self._chart.leaves():
1307 tag = c.create_text(0,0, text=repr(leaf),
1308 font=self._font,
1309 anchor='nw', justify='left')
1310 bbox = c.bbox(tag)
1311 c.delete(tag)
1312 width = bbox[2] + ChartView._LEAF_SPACING
1313 unitsize = max(width, unitsize)
1314 text_height = max(text_height, bbox[3] - bbox[1])
1315
1316 self._unitsize = unitsize
1317 self._text_height = text_height
1318 self._sentence_height = (self._text_height +
1319 2*ChartView._MARGIN)
1320
1321
1322 for edge in self._chart.edges():
1323 self._analyze_edge(edge)
1324
1325
1326 self._chart_level_size = self._text_height * 2.5
1327
1328
1329 self._tree_height = (3 * (ChartView._TREE_LEVEL_SIZE +
1330 self._text_height))
1331
1332
1333 self._resize()
1334
1336 """
1337 Update the scroll-regions for each canvas. This ensures that
1338 everything is within a scroll-region, so the user can use the
1339 scrollbars to view the entire display. This does I{not}
1340 resize the window.
1341 """
1342 c = self._chart_canvas
1343
1344
1345 width = ( self._chart.num_leaves() * self._unitsize +
1346 ChartView._MARGIN * 2 )
1347
1348 levels = len(self._edgelevels)
1349 self._chart_height = (levels+2)*self._chart_level_size
1350 c['scrollregion']=(0,0,width,self._chart_height)
1351
1352
1353 if self._tree_canvas:
1354 self._tree_canvas['scrollregion'] = (0, 0, width,
1355 self._tree_height)
1356
1358 """
1359 Draw location lines. These are vertical gridlines used to
1360 show where each location unit is.
1361 """
1362 BOTTOM = 50000
1363 c1 = self._tree_canvas
1364 c2 = self._sentence_canvas
1365 c3 = self._chart_canvas
1366 margin = ChartView._MARGIN
1367 self._loclines = []
1368 for i in range(0, self._chart.num_leaves()+1):
1369 x = i*self._unitsize + margin
1370
1371 if c1:
1372 t1=c1.create_line(x, 0, x, BOTTOM)
1373 c1.tag_lower(t1)
1374 if c2:
1375 t2=c2.create_line(x, 0, x, self._sentence_height)
1376 c2.tag_lower(t2)
1377 t3=c3.create_line(x, 0, x, BOTTOM)
1378 c3.tag_lower(t3)
1379 t4=c3.create_text(x+2, 0, text=`i`, anchor='nw',
1380 font=self._font)
1381 c3.tag_lower(t4)
1382
1383
1384
1385
1386 if i % 2 == 0:
1387 if c1: c1.itemconfig(t1, fill='gray60')
1388 if c2: c2.itemconfig(t2, fill='gray60')
1389 c3.itemconfig(t3, fill='gray60')
1390 else:
1391 if c1: c1.itemconfig(t1, fill='gray80')
1392 if c2: c2.itemconfig(t2, fill='gray80')
1393 c3.itemconfig(t3, fill='gray80')
1394
1396 """Draw the sentence string."""
1397 if self._chart.num_leaves() == 0: return
1398 c = self._sentence_canvas
1399 margin = ChartView._MARGIN
1400 y = ChartView._MARGIN
1401
1402 for i, leaf in enumerate(self._chart.leaves()):
1403 x1 = i * self._unitsize + margin
1404 x2 = x1 + self._unitsize
1405 x = (x1+x2)/2
1406 tag = c.create_text(x, y, text=repr(leaf),
1407 font=self._font,
1408 anchor='n', justify='left')
1409 bbox = c.bbox(tag)
1410 rt=c.create_rectangle(x1+2, bbox[1]-(ChartView._LEAF_SPACING/2),
1411 x2-2, bbox[3]+(ChartView._LEAF_SPACING/2),
1412 fill='#f0f0f0', outline='#f0f0f0')
1413 c.tag_lower(rt)
1414
1416 for tag in self._tree_tags: self._tree_canvas.delete(tag)
1417 self._treetoks = []
1418 self._treetoks_edge = None
1419 self._treetoks_index = 0
1420
1422 if edge is None and self._treetoks_edge is None: return
1423 if edge is None: edge = self._treetoks_edge
1424
1425
1426 if self._treetoks_edge != edge:
1427 self._treetoks = [t for t in self._chart.trees(edge)
1428 if isinstance(t, Tree)]
1429 self._treetoks_edge = edge
1430 self._treetoks_index = 0
1431
1432
1433 if len(self._treetoks) == 0: return
1434
1435
1436 for tag in self._tree_tags: self._tree_canvas.delete(tag)
1437
1438
1439 tree = self._treetoks[self._treetoks_index]
1440 self._draw_treetok(tree, edge.start())
1441
1442
1443 self._draw_treecycle()
1444
1445
1446 w = self._chart.num_leaves()*self._unitsize+2*ChartView._MARGIN
1447 h = tree.height() * (ChartView._TREE_LEVEL_SIZE+self._text_height)
1448 self._tree_canvas['scrollregion'] = (0, 0, w, h)
1449
1451 self._treetoks_index = (self._treetoks_index+1)%len(self._treetoks)
1452 self.draw_tree(self._treetoks_edge)
1453
1455 if len(self._treetoks) <= 1: return
1456
1457
1458 label = '%d Trees' % len(self._treetoks)
1459 c = self._tree_canvas
1460 margin = ChartView._MARGIN
1461 right = self._chart.num_leaves()*self._unitsize+margin-2
1462 tag = c.create_text(right, 2, anchor='ne', text=label,
1463 font=self._boldfont)
1464 self._tree_tags.append(tag)
1465 _, _, _, y = c.bbox(tag)
1466
1467
1468 for i in range(len(self._treetoks)):
1469 x = right - 20*(len(self._treetoks)-i-1)
1470 if i == self._treetoks_index: fill = '#084'
1471 else: fill = '#fff'
1472 tag = c.create_polygon(x, y+10, x-5, y, x-10, y+10,
1473 fill=fill, outline='black')
1474 self._tree_tags.append(tag)
1475
1476
1477
1478 def cb(event, self=self, i=i):
1479 self._treetoks_index = i
1480 self.draw_tree()
1481 c.tag_bind(tag, '<Button-1>', cb)
1482
1484 """
1485 @param index: The index of the first leaf in the tree.
1486 @return: The index of the first leaf after the tree.
1487 """
1488 c = self._tree_canvas
1489 margin = ChartView._MARGIN
1490
1491
1492 child_xs = []
1493 for child in treetok:
1494 if isinstance(child, Tree):
1495 child_x, index = self._draw_treetok(child, index, depth+1)
1496 child_xs.append(child_x)
1497 else:
1498 child_xs.append((2*index+1)*self._unitsize/2 + margin)
1499 index += 1
1500
1501
1502
1503 if child_xs:
1504 nodex = sum(child_xs)/len(child_xs)
1505 else:
1506
1507 nodex = (2*index+1)*self._unitsize/2 + margin
1508 index += 1
1509
1510
1511 nodey = depth * (ChartView._TREE_LEVEL_SIZE + self._text_height)
1512 tag = c.create_text(nodex, nodey, anchor='n', justify='center',
1513 text=str(treetok.node), fill='#042',
1514 font=self._boldfont)
1515 self._tree_tags.append(tag)
1516
1517
1518 childy = nodey + ChartView._TREE_LEVEL_SIZE + self._text_height
1519 for childx, child in zip(child_xs, treetok):
1520 if isinstance(child, Tree) and child:
1521
1522 tag = c.create_line(nodex, nodey + self._text_height,
1523 childx, childy, width=2, fill='#084')
1524 self._tree_tags.append(tag)
1525 if isinstance(child, Tree) and not child:
1526
1527 tag = c.create_line(nodex, nodey + self._text_height,
1528 childx, childy, width=2,
1529 fill='#048', dash='2 3')
1530 self._tree_tags.append(tag)
1531 if not isinstance(child, Tree):
1532
1533 tag = c.create_line(nodex, nodey + self._text_height,
1534 childx, 10000, width=2, fill='#084')
1535 self._tree_tags.append(tag)
1536
1537 return nodex, index
1538
1540 """
1541 Draw everything (from scratch).
1542 """
1543 if self._tree_canvas:
1544 self._tree_canvas.delete('all')
1545 self.draw_tree()
1546
1547 if self._sentence_canvas:
1548 self._sentence_canvas.delete('all')
1549 self._draw_sentence()
1550
1551 self._chart_canvas.delete('all')
1552 self._edgetags = {}
1553
1554
1555 for lvl in range(len(self._edgelevels)):
1556 for edge in self._edgelevels[lvl]:
1557 self._draw_edge(edge, lvl)
1558
1559 for edge in self._chart:
1560 self._add_edge(edge)
1561
1562 self._draw_loclines()
1563
1565 self._callbacks.setdefault(event,{})[func] = 1
1566
1568 if func is None: del self._callbacks[event]
1569 else:
1570 try: del self._callbacks[event][func]
1571 except: pass
1572
1574 if not self._callbacks.has_key(event): return
1575 for cb_func in self._callbacks[event].keys(): cb_func(*args)
1576
1577
1578
1579
1580
1581
1582
1583
1610
1613 return 'Predictor Rule (aka Top Down Expand Rule)'
1614
1615
1616
1617
1618
1619
1620
1622 """
1623 To create an edge rule, make an empty base class that uses
1624 EdgeRule as the first base class, and the basic rule as the
1625 second base class. (Order matters!)
1626 """
1628 super = self.__class__.__bases__[1]
1629 self._edge = edge
1630 self.NUM_EDGES = super.NUM_EDGES-1
1636 super = self.__class__.__bases__[1]
1637 return super.__str__(self)
1638
1645
1646
1647
1648
1649
1651 - def __init__(self, grammar, tokens, title='Chart Parsing Demo'):
1652
1653 self._init_parser(grammar, tokens)
1654
1655 self._root = None
1656 try:
1657
1658 self._root = Tkinter.Tk()
1659 self._root.title(title)
1660 self._root.bind('<Control-q>', self.destroy)
1661
1662
1663 frame3 = Tkinter.Frame(self._root)
1664 frame2 = Tkinter.Frame(self._root)
1665 frame1 = Tkinter.Frame(self._root)
1666 frame3.pack(side='bottom', fill='none')
1667 frame2.pack(side='bottom', fill='x')
1668 frame1.pack(side='bottom', fill='both', expand=1)
1669
1670 self._init_fonts(self._root)
1671 self._init_animation()
1672 self._init_chartview(frame1)
1673 self._init_rulelabel(frame2)
1674 self._init_buttons(frame3)
1675 self._init_menubar()
1676
1677 self._matrix = None
1678 self._results = None
1679
1680
1681 self._init_bindings()
1682
1683 except:
1684 print 'Error creating Tree View'
1685 self.destroy()
1686 raise
1687
1689 if self._root is None: return
1690 self._root.destroy()
1691 self._root = None
1692
1693 - def mainloop(self, *args, **kwargs):
1694 """
1695 Enter the Tkinter mainloop. This function must be called if
1696 this demo is created from a non-interactive program (e.g.
1697 from a secript); otherwise, the demo will close as soon as
1698 the script completes.
1699 """
1700 if in_idle(): return
1701 self._root.mainloop(*args, **kwargs)
1702
1703
1704
1705
1706
1708 self._grammar = grammar
1709 self._tokens = tokens
1710 self._cp = SteppingChartParser(self._grammar)
1711 self._cp.initialize(self._tokens)
1712 self._chart = self._cp.chart()
1713
1714
1715 self._cpstep = self._cp.step()
1716
1717
1718 self._selection = None
1719
1721
1722 self._sysfont = tkFont.Font(font=Tkinter.Button()["font"])
1723 root.option_add("*Font", self._sysfont)
1724
1725
1726 self._size = Tkinter.IntVar(root)
1727 self._size.set(self._sysfont.cget('size'))
1728
1729 self._boldfont = tkFont.Font(family='helvetica', weight='bold',
1730 size=self._size.get())
1731 self._font = tkFont.Font(family='helvetica',
1732 size=self._size.get())
1733
1735
1736 self._step = Tkinter.IntVar(self._root)
1737 self._step.set(1)
1738
1739
1740 self._animate = Tkinter.IntVar(self._root)
1741 self._animate.set(3)
1742
1743
1744 self._animating = 0
1745
1750
1752 ruletxt = 'Last edge generated by:'
1753
1754 self._rulelabel1 = Tkinter.Label(parent,text=ruletxt,
1755 font=self._boldfont)
1756 self._rulelabel2 = Tkinter.Label(parent, width=40,
1757 relief='groove', anchor='w',
1758 font=self._boldfont)
1759 self._rulelabel1.pack(side='left')
1760 self._rulelabel2.pack(side='left')
1761 step = Tkinter.Checkbutton(parent, variable=self._step,
1762 text='Step')
1763 step.pack(side='right')
1764
1810
1812 self._root.bind('<Up>', self._cv.scroll_up)
1813 self._root.bind('<Down>', self._cv.scroll_down)
1814 self._root.bind('<Prior>', self._cv.page_up)
1815 self._root.bind('<Next>', self._cv.page_down)
1816 self._root.bind('<Control-q>', self.destroy)
1817 self._root.bind('<Control-x>', self.destroy)
1818 self._root.bind('<F1>', self.help)
1819
1820 self._root.bind('<Control-s>', self.save_chart)
1821 self._root.bind('<Control-o>', self.load_chart)
1822 self._root.bind('<Control-r>', self.reset)
1823
1824 self._root.bind('t', self.top_down_strategy)
1825 self._root.bind('b', self.bottom_up_strategy)
1826 self._root.bind('e', self.earley_algorithm)
1827 self._root.bind('<space>', self._stop_animation)
1828
1829 self._root.bind('<Control-g>', self.edit_grammar)
1830 self._root.bind('<Control-t>', self.edit_sentence)
1831
1832
1833 self._root.bind('-', lambda e,a=self._animate:a.set(1))
1834 self._root.bind('=', lambda e,a=self._animate:a.set(2))
1835 self._root.bind('+', lambda e,a=self._animate:a.set(3))
1836
1837
1838 self._root.bind('s', lambda e,s=self._step:s.set(not s.get()))
1839
1841 menubar = Tkinter.Menu(self._root)
1842
1843 filemenu = Tkinter.Menu(menubar, tearoff=0)
1844 filemenu.add_command(label='Save Chart', underline=0,
1845 command=self.save_chart, accelerator='Ctrl-s')
1846 filemenu.add_command(label='Load Chart', underline=0,
1847 command=self.load_chart, accelerator='Ctrl-o')
1848 filemenu.add_command(label='Reset Chart', underline=0,
1849 command=self.reset, accelerator='Ctrl-r')
1850 filemenu.add_separator()
1851 filemenu.add_command(label='Save Grammar',
1852 command=self.save_grammar)
1853 filemenu.add_command(label='Load Grammar',
1854 command=self.load_grammar)
1855 filemenu.add_separator()
1856 filemenu.add_command(label='Exit', underline=1,
1857 command=self.destroy, accelerator='Ctrl-x')
1858 menubar.add_cascade(label='File', underline=0, menu=filemenu)
1859
1860 editmenu = Tkinter.Menu(menubar, tearoff=0)
1861 editmenu.add_command(label='Edit Grammar', underline=5,
1862 command=self.edit_grammar,
1863 accelerator='Ctrl-g')
1864 editmenu.add_command(label='Edit Text', underline=5,
1865 command=self.edit_sentence,
1866 accelerator='Ctrl-t')
1867 menubar.add_cascade(label='Edit', underline=0, menu=editmenu)
1868
1869 viewmenu = Tkinter.Menu(menubar, tearoff=0)
1870 viewmenu.add_command(label='Chart Matrix', underline=6,
1871 command=self.view_matrix)
1872 viewmenu.add_command(label='Results', underline=0,
1873 command=self.view_results)
1874 menubar.add_cascade(label='View', underline=0, menu=viewmenu)
1875
1876 rulemenu = Tkinter.Menu(menubar, tearoff=0)
1877 rulemenu.add_command(label='Top Down Strategy', underline=0,
1878 command=self.top_down_strategy,
1879 accelerator='t')
1880 rulemenu.add_command(label='Bottom Up Strategy', underline=0,
1881 command=self.bottom_up_strategy,
1882 accelerator='b')
1883 rulemenu.add_command(label='Earley Algorithm', underline=0,
1884 command=self.bottom_up_strategy,
1885 accelerator='e')
1886 rulemenu.add_separator()
1887 rulemenu.add_command(label='Bottom Up Init Rule',
1888 command=self.bottom_up_init)
1889 rulemenu.add_command(label='Bottom Up Rule',
1890 command=self.bottom_up)
1891 rulemenu.add_command(label='Top Down Init Rule',
1892 command=self.top_down_init)
1893 rulemenu.add_command(label='Top Down Expand Rule',
1894 command=self.top_down_expand)
1895 rulemenu.add_command(label='Top Down Match Rule',
1896 command=self.top_down_match)
1897 rulemenu.add_command(label='Fundamental Rule',
1898 command=self.fundamental)
1899 menubar.add_cascade(label='Apply', underline=0, menu=rulemenu)
1900
1901 animatemenu = Tkinter.Menu(menubar, tearoff=0)
1902 animatemenu.add_checkbutton(label="Step", underline=0,
1903 variable=self._step,
1904 accelerator='s')
1905 animatemenu.add_separator()
1906 animatemenu.add_radiobutton(label="No Animation", underline=0,
1907 variable=self._animate, value=0)
1908 animatemenu.add_radiobutton(label="Slow Animation", underline=0,
1909 variable=self._animate, value=1,
1910 accelerator='-')
1911 animatemenu.add_radiobutton(label="Normal Animation", underline=0,
1912 variable=self._animate, value=2,
1913 accelerator='=')
1914 animatemenu.add_radiobutton(label="Fast Animation", underline=0,
1915 variable=self._animate, value=3,
1916 accelerator='+')
1917 menubar.add_cascade(label="Animate", underline=1, menu=animatemenu)
1918
1919 zoommenu = Tkinter.Menu(menubar, tearoff=0)
1920 zoommenu.add_radiobutton(label='Tiny', variable=self._size,
1921 underline=0, value=10, command=self.resize)
1922 zoommenu.add_radiobutton(label='Small', variable=self._size,
1923 underline=0, value=12, command=self.resize)
1924 zoommenu.add_radiobutton(label='Medium', variable=self._size,
1925 underline=0, value=14, command=self.resize)
1926 zoommenu.add_radiobutton(label='Large', variable=self._size,
1927 underline=0, value=18, command=self.resize)
1928 zoommenu.add_radiobutton(label='Huge', variable=self._size,
1929 underline=0, value=24, command=self.resize)
1930 menubar.add_cascade(label='Zoom', underline=0, menu=zoommenu)
1931
1932 helpmenu = Tkinter.Menu(menubar, tearoff=0)
1933 helpmenu.add_command(label='About', underline=0,
1934 command=self.about)
1935 helpmenu.add_command(label='Instructions', underline=0,
1936 command=self.help, accelerator='F1')
1937 menubar.add_cascade(label='Help', underline=0, menu=helpmenu)
1938
1939 self._root.config(menu=menubar)
1940
1941
1942
1943
1944
1952
1953
1954
1958
1967
1975
1989
1990
1991
1992
1993
1994 - def help(self, *e):
1995 self._animating = 0
1996
1997 try:
1998 ShowText(self._root, 'Help: Chart Parser Demo',
1999 (__doc__).strip(), width=75, font='fixed')
2000 except:
2001 ShowText(self._root, 'Help: Chart Parser Demo',
2002 (__doc__).strip(), width=75)
2003
2005 ABOUT = ("NLTK Chart Parser Demo\n"+
2006 "Written by Edward Loper")
2007 tkMessageBox.showinfo('About: Chart Parser Demo', ABOUT)
2008
2009
2010
2011
2012
2013 CHART_FILE_TYPES = [('Pickle file', '.pickle'),
2014 ('All files', '*')]
2015 GRAMMAR_FILE_TYPES = [('Plaintext grammar file', '.cfg'),
2016 ('Pickle file', '.pickle'),
2017 ('All files', '*')]
2018
2036
2038 "Save a chart to a pickle file"
2039 filename = asksaveasfilename(filetypes=self.CHART_FILE_TYPES,
2040 defaultextension='.pickle')
2041 if not filename: return
2042 try:
2043 pickle.dump(self._chart, open(filename, 'w'))
2044 except Exception, e:
2045 raise
2046 tkMessageBox.showerror('Error Saving Chart',
2047 'Unable to open file: %r' % filename)
2048
2063
2065 filename = asksaveasfilename(filetypes=self.GRAMMAR_FILE_TYPES,
2066 defaultextension='.cfg')
2067 if not filename: return
2068 try:
2069 if filename.endswith('.pickle'):
2070 pickle.dump((self._chart, self._tokens), open(filename, 'w'))
2071 else:
2072 file = open(filename, 'w')
2073 prods = self._grammar.productions()
2074 start = [p for p in prods if p.lhs() == self._grammar.start()]
2075 rest = [p for p in prods if p.lhs() != self._grammar.start()]
2076 for prod in start: file.write('%s\n' % prod)
2077 for prod in rest: file.write('%s\n' % prod)
2078 file.close()
2079 except Exception, e:
2080 tkMessageBox.showerror('Error Saving Grammar',
2081 'Unable to open file: %r' % filename)
2082
2083 - def reset(self, *args):
2084 self._animating = 0
2085 self._cp = SteppingChartParser(self._grammar)
2086 self._cp.initialize(self._tokens)
2087 self._chart = self._cp.chart()
2088 self._cv.update(self._chart)
2089 if self._matrix: self._matrix.set_chart(self._chart)
2090 if self._matrix: self._matrix.deselect_cell()
2091 if self._results: self._results.set_chart(self._chart)
2092 self._cpstep = self._cp.step()
2093
2094
2095
2096
2097
2100
2105
2111
2113 self._tokens = list(sentence.split())
2114 self.reset()
2115
2116
2117
2118
2119
2124
2126 if self._results is not None: self._results.destroy()
2127 self._results = ChartResultsView(self._root, self._chart,
2128 self._grammar)
2129
2130
2131
2132
2133
2137
2143
2145 return abs(self._size.get())
2146
2147
2148
2149
2150
2187
2190
2202
2204 new_edge = self._cpstep.next()
2205
2206 if new_edge is not None:
2207 self._show_new_edge(new_edge)
2208 return new_edge
2209
2211 if rule == None:
2212 self._rulelabel2['text'] = ''
2213 else:
2214 name = str(rule)
2215 self._rulelabel2['text'] = name
2216 size = self._cv.get_font_size()
2217
2218
2219
2220
2221
2222
2223 _TD_INIT = [TopDownInitRule()]
2224 _TD_EXPAND = [TopDownExpandRule()]
2225 _TD_MATCH = [TopDownMatchRule()]
2226 _BU_INIT = [BottomUpInitRule()]
2227 _BU_RULE = [BottomUpPredictRule()]
2228 _FUNDAMENTAL = [SingleEdgeFundamentalRule()]
2229 _EARLEY = [PseudoEarleyRule()]
2230 _EARLEY_INIT = [PseudoEarleyInitRule()]
2231
2232
2233 _TD_STRATEGY = _TD_INIT + _TD_EXPAND + _TD_MATCH + _FUNDAMENTAL
2234 _BU_STRATEGY = _BU_INIT + _BU_RULE + _FUNDAMENTAL
2235 _EARLEY = _EARLEY_INIT + _EARLEY
2236
2237
2256
2258 grammar = cfg.parse_cfg("""
2259 # Grammatical productions.
2260 S -> NP VP
2261 VP -> VP PP | V NP | V
2262 NP -> Det N | NP PP
2263 PP -> P NP
2264 # Lexical productions.
2265 NP -> 'John' | 'I'
2266 Det -> 'the' | 'my' | 'a'
2267 N -> 'dog' | 'cookie' | 'table' | 'cake' | 'fork'
2268 V -> 'ate' | 'saw'
2269 P -> 'on' | 'under' | 'with'
2270 """)
2271
2272 sent = 'John ate the cake on the table with a fork'
2273 sent = 'John ate the cake on the table'
2274 tokens = list(sent.split())
2275
2276 print 'grammar= ('
2277 for rule in grammar.productions():
2278 print ' ', repr(rule)+','
2279 print ')'
2280 print 'tokens = %r' % tokens
2281 print 'Calling "ChartDemo(grammar, tokens)"...'
2282 ChartDemo(grammar, tokens).mainloop()
2283
2284 if __name__ == '__main__':
2285 demo()
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299