nltk 3.2__zip → 3.3__zip

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (406) hide show
  1. {nltk-3.2 → nltk-3.3}/INSTALL.txt +6 -6
  2. {nltk-3.2 → nltk-3.3}/LICENSE.txt +13 -13
  3. {nltk-3.2 → nltk-3.3}/MANIFEST.in +6 -6
  4. {nltk-3.2/nltk.egg-info → nltk-3.3}/PKG-INFO +13 -5
  5. nltk-3.2/README.txt → nltk-3.3/README.md +49 -48
  6. nltk-3.3/nltk/VERSION +1 -0
  7. {nltk-3.2 → nltk-3.3}/nltk/__init__.py +185 -185
  8. {nltk-3.2 → nltk-3.3}/nltk/app/__init__.py +52 -53
  9. {nltk-3.2 → nltk-3.3}/nltk/app/chartparser_app.py +2276 -2276
  10. {nltk-3.2 → nltk-3.3}/nltk/app/chunkparser_app.py +1262 -1264
  11. {nltk-3.2 → nltk-3.3}/nltk/app/collocations_app.py +347 -350
  12. {nltk-3.2 → nltk-3.3}/nltk/app/concordance_app.py +568 -570
  13. {nltk-3.2 → nltk-3.3}/nltk/app/nemo_app.py +156 -155
  14. {nltk-3.2 → nltk-3.3}/nltk/app/rdparser_app.py +892 -894
  15. {nltk-3.2 → nltk-3.3}/nltk/app/srparser_app.py +809 -809
  16. {nltk-3.2 → nltk-3.3}/nltk/app/wordfreq_app.py +32 -32
  17. {nltk-3.2 → nltk-3.3}/nltk/app/wordnet_app.py +968 -968
  18. {nltk-3.2 → nltk-3.3}/nltk/book.py +91 -90
  19. {nltk-3.2 → nltk-3.3}/nltk/ccg/__init__.py +22 -22
  20. {nltk-3.2 → nltk-3.3}/nltk/ccg/api.py +359 -335
  21. {nltk-3.2 → nltk-3.3}/nltk/ccg/chart.py +394 -361
  22. {nltk-3.2 → nltk-3.3}/nltk/ccg/combinator.py +343 -312
  23. {nltk-3.2 → nltk-3.3}/nltk/ccg/lexicon.py +328 -290
  24. nltk-3.3/nltk/ccg/logic.py +46 -0
  25. {nltk-3.2 → nltk-3.3}/nltk/chat/__init__.py +49 -49
  26. {nltk-3.2 → nltk-3.3}/nltk/chat/eliza.py +244 -244
  27. {nltk-3.2 → nltk-3.3}/nltk/chat/iesha.py +140 -140
  28. {nltk-3.2 → nltk-3.3}/nltk/chat/rude.py +92 -92
  29. {nltk-3.2 → nltk-3.3}/nltk/chat/suntsu.py +117 -117
  30. {nltk-3.2 → nltk-3.3}/nltk/chat/util.py +122 -120
  31. {nltk-3.2 → nltk-3.3}/nltk/chat/zen.py +282 -282
  32. {nltk-3.2 → nltk-3.3}/nltk/chunk/__init__.py +190 -190
  33. {nltk-3.2 → nltk-3.3}/nltk/chunk/api.py +51 -51
  34. {nltk-3.2 → nltk-3.3}/nltk/chunk/named_entity.py +331 -331
  35. {nltk-3.2 → nltk-3.3}/nltk/chunk/regexp.py +1390 -1384
  36. {nltk-3.2 → nltk-3.3}/nltk/chunk/util.py +600 -600
  37. {nltk-3.2 → nltk-3.3}/nltk/classify/__init__.py +98 -98
  38. {nltk-3.2 → nltk-3.3}/nltk/classify/api.py +193 -193
  39. {nltk-3.2 → nltk-3.3}/nltk/classify/decisiontree.py +295 -295
  40. {nltk-3.2 → nltk-3.3}/nltk/classify/maxent.py +1510 -1492
  41. {nltk-3.2 → nltk-3.3}/nltk/classify/megam.py +179 -178
  42. {nltk-3.2 → nltk-3.3}/nltk/classify/naivebayes.py +245 -242
  43. {nltk-3.2 → nltk-3.3}/nltk/classify/positivenaivebayes.py +170 -170
  44. {nltk-3.2 → nltk-3.3}/nltk/classify/rte_classify.py +160 -191
  45. {nltk-3.2 → nltk-3.3}/nltk/classify/scikitlearn.py +153 -151
  46. {nltk-3.2 → nltk-3.3}/nltk/classify/senna.py +183 -181
  47. {nltk-3.2 → nltk-3.3}/nltk/classify/svm.py +15 -15
  48. {nltk-3.2 → nltk-3.3}/nltk/classify/tadm.py +113 -112
  49. {nltk-3.2 → nltk-3.3}/nltk/classify/textcat.py +193 -193
  50. {nltk-3.2 → nltk-3.3}/nltk/classify/util.py +324 -312
  51. {nltk-3.2 → nltk-3.3}/nltk/classify/weka.py +346 -345
  52. {nltk-3.2 → nltk-3.3}/nltk/cluster/__init__.py +86 -86
  53. {nltk-3.2 → nltk-3.3}/nltk/cluster/api.py +74 -70
  54. {nltk-3.2 → nltk-3.3}/nltk/cluster/em.py +247 -247
  55. {nltk-3.2 → nltk-3.3}/nltk/cluster/gaac.py +168 -168
  56. {nltk-3.2 → nltk-3.3}/nltk/cluster/kmeans.py +221 -221
  57. {nltk-3.2 → nltk-3.3}/nltk/cluster/util.py +304 -289
  58. nltk-3.3/nltk/collections.py +640 -0
  59. {nltk-3.2 → nltk-3.3}/nltk/collocations.py +388 -388
  60. nltk-3.3/nltk/compat.py +365 -0
  61. {nltk-3.2 → nltk-3.3}/nltk/corpus/__init__.py +341 -320
  62. {nltk-3.2 → nltk-3.3}/nltk/corpus/europarl_raw.py +44 -44
  63. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/__init__.py +147 -145
  64. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/aligned.py +115 -114
  65. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/api.py +448 -446
  66. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/bnc.py +252 -252
  67. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/bracket_parse.py +227 -227
  68. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/categorized_sents.py +179 -178
  69. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/chasen.py +140 -139
  70. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/childes.py +522 -494
  71. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/chunked.py +212 -212
  72. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/cmudict.py +97 -95
  73. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/comparative_sents.py +280 -278
  74. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/conll.py +528 -521
  75. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/crubadan.py +116 -116
  76. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/dependency.py +101 -101
  77. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/framenet.py +3059 -2093
  78. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ieer.py +112 -111
  79. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/indian.py +85 -86
  80. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ipipan.py +332 -331
  81. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/knbc.py +187 -187
  82. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/lin.py +156 -156
  83. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/mte.py +311 -310
  84. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nkjp.py +429 -428
  85. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nombank.py +421 -421
  86. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nps_chat.py +73 -73
  87. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/opinion_lexicon.py +115 -115
  88. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/panlex_lite.py +165 -165
  89. nltk-3.3/nltk/corpus/reader/pl196x.py +292 -0
  90. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/plaintext.py +232 -233
  91. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ppattach.py +96 -95
  92. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/propbank.py +479 -481
  93. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/pros_cons.py +128 -126
  94. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/reviews.py +330 -328
  95. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/rte.py +144 -146
  96. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/semcor.py +256 -256
  97. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/senseval.py +203 -201
  98. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/sentiwordnet.py +137 -137
  99. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/sinica_treebank.py +75 -75
  100. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/string_category.py +60 -59
  101. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/switchboard.py +119 -119
  102. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/tagged.py +295 -294
  103. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/timit.py +452 -450
  104. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/toolbox.py +68 -68
  105. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/twitter.py +155 -157
  106. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/udhr.py +80 -80
  107. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/util.py +804 -800
  108. nltk-3.3/nltk/corpus/reader/verbnet.py +595 -0
  109. nltk-3.3/nltk/corpus/reader/wordlist.py +134 -0
  110. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/wordnet.py +2108 -2036
  111. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/xmldocs.py +392 -387
  112. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ycoe.py +243 -242
  113. {nltk-3.2 → nltk-3.3}/nltk/corpus/util.py +144 -127
  114. {nltk-3.2 → nltk-3.3}/nltk/data.py +1487 -1451
  115. {nltk-3.2 → nltk-3.3}/nltk/decorators.py +219 -215
  116. {nltk-3.2 → nltk-3.3}/nltk/downloader.py +2278 -2274
  117. {nltk-3.2 → nltk-3.3}/nltk/draw/__init__.py +27 -27
  118. {nltk-3.2 → nltk-3.3}/nltk/draw/cfg.py +774 -777
  119. {nltk-3.2 → nltk-3.3}/nltk/draw/dispersion.py +58 -58
  120. {nltk-3.2 → nltk-3.3}/nltk/draw/table.py +1098 -1098
  121. {nltk-3.2 → nltk-3.3}/nltk/draw/tree.py +963 -966
  122. {nltk-3.2 → nltk-3.3}/nltk/draw/util.py +2356 -2356
  123. {nltk-3.2 → nltk-3.3}/nltk/featstruct.py +2617 -2500
  124. {nltk-3.2 → nltk-3.3}/nltk/grammar.py +1559 -1516
  125. {nltk-3.2 → nltk-3.3}/nltk/help.py +56 -56
  126. {nltk-3.2 → nltk-3.3}/nltk/inference/__init__.py +20 -20
  127. {nltk-3.2 → nltk-3.3}/nltk/inference/api.py +597 -589
  128. {nltk-3.2 → nltk-3.3}/nltk/inference/discourse.py +612 -608
  129. {nltk-3.2 → nltk-3.3}/nltk/inference/mace.py +311 -311
  130. {nltk-3.2 → nltk-3.3}/nltk/inference/nonmonotonic.py +509 -509
  131. {nltk-3.2 → nltk-3.3}/nltk/inference/prover9.py +431 -431
  132. {nltk-3.2 → nltk-3.3}/nltk/inference/resolution.py +687 -687
  133. {nltk-3.2 → nltk-3.3}/nltk/inference/tableau.py +607 -607
  134. {nltk-3.2 → nltk-3.3}/nltk/internals.py +982 -982
  135. {nltk-3.2 → nltk-3.3}/nltk/jsontags.py +63 -63
  136. {nltk-3.2 → nltk-3.3}/nltk/lazyimport.py +142 -142
  137. {nltk-3.2 → nltk-3.3}/nltk/metrics/__init__.py +30 -29
  138. {nltk-3.2 → nltk-3.3}/nltk/metrics/agreement.py +421 -401
  139. nltk-3.3/nltk/metrics/aline.py +607 -0
  140. {nltk-3.2 → nltk-3.3}/nltk/metrics/association.py +414 -410
  141. {nltk-3.2 → nltk-3.3}/nltk/metrics/confusionmatrix.py +206 -206
  142. {nltk-3.2 → nltk-3.3}/nltk/metrics/distance.py +207 -202
  143. {nltk-3.2 → nltk-3.3}/nltk/metrics/paice.py +381 -381
  144. {nltk-3.2 → nltk-3.3}/nltk/metrics/scores.py +228 -228
  145. {nltk-3.2 → nltk-3.3}/nltk/metrics/segmentation.py +231 -232
  146. {nltk-3.2 → nltk-3.3}/nltk/metrics/spearman.py +69 -69
  147. {nltk-3.2 → nltk-3.3}/nltk/misc/__init__.py +11 -11
  148. {nltk-3.2 → nltk-3.3}/nltk/misc/babelfish.py +10 -10
  149. {nltk-3.2 → nltk-3.3}/nltk/misc/chomsky.py +135 -133
  150. {nltk-3.2 → nltk-3.3}/nltk/misc/minimalset.py +83 -83
  151. {nltk-3.2 → nltk-3.3}/nltk/misc/sort.py +157 -157
  152. {nltk-3.2 → nltk-3.3}/nltk/misc/wordfinder.py +129 -129
  153. {nltk-3.2 → nltk-3.3}/nltk/parse/__init__.py +81 -80
  154. {nltk-3.2 → nltk-3.3}/nltk/parse/api.py +66 -66
  155. {nltk-3.2 → nltk-3.3}/nltk/parse/bllip.py +282 -282
  156. {nltk-3.2 → nltk-3.3}/nltk/parse/chart.py +1682 -1681
  157. nltk-3.3/nltk/parse/corenlp.py +780 -0
  158. {nltk-3.2 → nltk-3.3}/nltk/parse/dependencygraph.py +760 -758
  159. {nltk-3.2 → nltk-3.3}/nltk/parse/earleychart.py +452 -451
  160. {nltk-3.2 → nltk-3.3}/nltk/parse/evaluate.py +129 -129
  161. {nltk-3.2 → nltk-3.3}/nltk/parse/featurechart.py +580 -578
  162. {nltk-3.2 → nltk-3.3}/nltk/parse/generate.py +87 -76
  163. {nltk-3.2 → nltk-3.3}/nltk/parse/malt.py +356 -356
  164. {nltk-3.2 → nltk-3.3}/nltk/parse/nonprojectivedependencyparser.py +783 -783
  165. {nltk-3.2 → nltk-3.3}/nltk/parse/pchart.py +522 -522
  166. {nltk-3.2 → nltk-3.3}/nltk/parse/projectivedependencyparser.py +579 -578
  167. {nltk-3.2 → nltk-3.3}/nltk/parse/recursivedescent.py +655 -656
  168. {nltk-3.2 → nltk-3.3}/nltk/parse/shiftreduce.py +458 -459
  169. {nltk-3.2 → nltk-3.3}/nltk/parse/stanford.py +431 -406
  170. {nltk-3.2 → nltk-3.3}/nltk/parse/transitionparser.py +774 -772
  171. {nltk-3.2 → nltk-3.3}/nltk/parse/util.py +231 -231
  172. {nltk-3.2 → nltk-3.3}/nltk/parse/viterbi.py +401 -401
  173. {nltk-3.2 → nltk-3.3}/nltk/probability.py +2410 -2355
  174. {nltk-3.2 → nltk-3.3}/nltk/sem/__init__.py +61 -61
  175. {nltk-3.2 → nltk-3.3}/nltk/sem/boxer.py +1261 -1261
  176. {nltk-3.2 → nltk-3.3}/nltk/sem/chat80.py +782 -782
  177. {nltk-3.2 → nltk-3.3}/nltk/sem/cooper_storage.py +118 -118
  178. {nltk-3.2 → nltk-3.3}/nltk/sem/drt.py +1258 -1257
  179. {nltk-3.2 → nltk-3.3}/nltk/sem/drt_glue_demo.py +485 -489
  180. {nltk-3.2 → nltk-3.3}/nltk/sem/evaluate.py +790 -784
  181. {nltk-3.2 → nltk-3.3}/nltk/sem/glue.py +696 -694
  182. {nltk-3.2 → nltk-3.3}/nltk/sem/hole.py +386 -384
  183. {nltk-3.2 → nltk-3.3}/nltk/sem/lfg.py +210 -208
  184. {nltk-3.2 → nltk-3.3}/nltk/sem/linearlogic.py +451 -449
  185. {nltk-3.2 → nltk-3.3}/nltk/sem/logic.py +1910 -1910
  186. {nltk-3.2 → nltk-3.3}/nltk/sem/relextract.py +476 -483
  187. {nltk-3.2 → nltk-3.3}/nltk/sem/skolemize.py +101 -101
  188. {nltk-3.2 → nltk-3.3}/nltk/sem/util.py +249 -249
  189. {nltk-3.2 → nltk-3.3}/nltk/sentiment/__init__.py +14 -14
  190. {nltk-3.2 → nltk-3.3}/nltk/sentiment/sentiment_analyzer.py +229 -229
  191. {nltk-3.2 → nltk-3.3}/nltk/sentiment/util.py +762 -752
  192. {nltk-3.2 → nltk-3.3}/nltk/sentiment/vader.py +443 -445
  193. {nltk-3.2 → nltk-3.3}/nltk/stem/__init__.py +31 -33
  194. {nltk-3.2 → nltk-3.3}/nltk/stem/api.py +28 -25
  195. nltk-3.3/nltk/stem/arlstem.py +369 -0
  196. {nltk-3.2 → nltk-3.3}/nltk/stem/isri.py +345 -345
  197. {nltk-3.2 → nltk-3.3}/nltk/stem/lancaster.py +337 -310
  198. nltk-3.3/nltk/stem/porter.py +710 -0
  199. {nltk-3.2 → nltk-3.3}/nltk/stem/regexp.py +61 -61
  200. {nltk-3.2 → nltk-3.3}/nltk/stem/rslp.py +142 -142
  201. {nltk-3.2 → nltk-3.3}/nltk/stem/snowball.py +4236 -3712
  202. {nltk-3.2 → nltk-3.3}/nltk/stem/util.py +22 -12
  203. {nltk-3.2 → nltk-3.3}/nltk/stem/wordnet.py +51 -51
  204. {nltk-3.2 → nltk-3.3}/nltk/tag/__init__.py +152 -127
  205. {nltk-3.2 → nltk-3.3}/nltk/tag/api.py +86 -81
  206. {nltk-3.2 → nltk-3.3}/nltk/tag/brill.py +424 -426
  207. {nltk-3.2 → nltk-3.3}/nltk/tag/brill_trainer.py +608 -608
  208. {nltk-3.2 → nltk-3.3}/nltk/tag/crf.py +204 -200
  209. {nltk-3.2 → nltk-3.3}/nltk/tag/hmm.py +1277 -1277
  210. {nltk-3.2 → nltk-3.3}/nltk/tag/hunpos.py +132 -132
  211. {nltk-3.2 → nltk-3.3}/nltk/tag/mapping.py +101 -101
  212. {nltk-3.2 → nltk-3.3}/nltk/tag/perceptron.py +329 -314
  213. {nltk-3.2 → nltk-3.3}/nltk/tag/senna.py +147 -145
  214. {nltk-3.2 → nltk-3.3}/nltk/tag/sequential.py +740 -736
  215. {nltk-3.2 → nltk-3.3}/nltk/tag/stanford.py +219 -196
  216. {nltk-3.2 → nltk-3.3}/nltk/tag/tnt.py +595 -604
  217. {nltk-3.2 → nltk-3.3}/nltk/tag/util.py +72 -72
  218. {nltk-3.2 → nltk-3.3}/nltk/tbl/__init__.py +28 -28
  219. {nltk-3.2 → nltk-3.3}/nltk/tbl/api.py +1 -1
  220. {nltk-3.2 → nltk-3.3}/nltk/tbl/demo.py +366 -366
  221. {nltk-3.2 → nltk-3.3}/nltk/tbl/erroranalysis.py +39 -39
  222. {nltk-3.2 → nltk-3.3}/nltk/tbl/feature.py +259 -261
  223. {nltk-3.2 → nltk-3.3}/nltk/tbl/rule.py +321 -321
  224. {nltk-3.2 → nltk-3.3}/nltk/tbl/template.py +303 -308
  225. {nltk-3.2 → nltk-3.3}/nltk/test/__init__.py +18 -18
  226. {nltk-3.2 → nltk-3.3}/nltk/test/all.py +24 -23
  227. {nltk-3.2 → nltk-3.3}/nltk/test/bleu.doctest +14 -14
  228. {nltk-3.2 → nltk-3.3}/nltk/test/bnc.doctest +60 -60
  229. {nltk-3.2 → nltk-3.3}/nltk/test/ccg.doctest +376 -395
  230. nltk-3.3/nltk/test/ccg_semantics.doctest +553 -0
  231. {nltk-3.2 → nltk-3.3}/nltk/test/chat80.doctest +234 -234
  232. {nltk-3.2 → nltk-3.3}/nltk/test/childes.doctest +184 -184
  233. {nltk-3.2 → nltk-3.3}/nltk/test/childes_fixt.py +14 -13
  234. {nltk-3.2 → nltk-3.3}/nltk/test/chunk.doctest +373 -373
  235. {nltk-3.2 → nltk-3.3}/nltk/test/classify.doctest +185 -185
  236. {nltk-3.2 → nltk-3.3}/nltk/test/classify_fixt.py +11 -10
  237. nltk-3.3/nltk/test/collections.doctest +20 -0
  238. {nltk-3.2 → nltk-3.3}/nltk/test/collocations.doctest +276 -276
  239. {nltk-3.2 → nltk-3.3}/nltk/test/compat.doctest +134 -138
  240. {nltk-3.2 → nltk-3.3}/nltk/test/compat_fixt.py +9 -8
  241. nltk-3.3/nltk/test/concordance.doctest +55 -0
  242. {nltk-3.2 → nltk-3.3}/nltk/test/corpus.doctest +2200 -2212
  243. {nltk-3.2 → nltk-3.3}/nltk/test/corpus_fixt.py +3 -3
  244. {nltk-3.2 → nltk-3.3}/nltk/test/crubadan.doctest +65 -65
  245. {nltk-3.2 → nltk-3.3}/nltk/test/data.doctest +379 -379
  246. {nltk-3.2 → nltk-3.3}/nltk/test/dependency.doctest +241 -241
  247. {nltk-3.2 → nltk-3.3}/nltk/test/discourse.doctest +546 -546
  248. {nltk-3.2 → nltk-3.3}/nltk/test/discourse_fixt.py +14 -13
  249. {nltk-3.2 → nltk-3.3}/nltk/test/doctest_nose_plugin.py +159 -156
  250. {nltk-3.2 → nltk-3.3}/nltk/test/drt.doctest +517 -517
  251. {nltk-3.2 → nltk-3.3}/nltk/test/featgram.doctest +607 -607
  252. {nltk-3.2 → nltk-3.3}/nltk/test/featstruct.doctest +1229 -1230
  253. {nltk-3.2 → nltk-3.3}/nltk/test/framenet.doctest +288 -239
  254. {nltk-3.2 → nltk-3.3}/nltk/test/generate.doctest +67 -67
  255. {nltk-3.2 → nltk-3.3}/nltk/test/gensim.doctest +140 -141
  256. {nltk-3.2 → nltk-3.3}/nltk/test/gensim_fixt.py +10 -9
  257. {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics.doctest +384 -386
  258. {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics_malt.doctest +68 -68
  259. {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics_malt_fixt.py +12 -11
  260. {nltk-3.2 → nltk-3.3}/nltk/test/grammar.doctest +48 -48
  261. {nltk-3.2 → nltk-3.3}/nltk/test/grammartestsuites.doctest +109 -109
  262. {nltk-3.2 → nltk-3.3}/nltk/test/index.doctest +100 -100
  263. {nltk-3.2 → nltk-3.3}/nltk/test/inference.doctest +534 -534
  264. {nltk-3.2 → nltk-3.3}/nltk/test/inference_fixt.py +12 -11
  265. {nltk-3.2 → nltk-3.3}/nltk/test/internals.doctest +140 -140
  266. {nltk-3.2 → nltk-3.3}/nltk/test/japanese.doctest +48 -48
  267. {nltk-3.2 → nltk-3.3}/nltk/test/logic.doctest +1098 -1098
  268. {nltk-3.2 → nltk-3.3}/nltk/test/metrics.doctest +270 -270
  269. {nltk-3.2 → nltk-3.3}/nltk/test/misc.doctest +118 -118
  270. {nltk-3.2 → nltk-3.3}/nltk/test/nonmonotonic.doctest +286 -286
  271. {nltk-3.2 → nltk-3.3}/nltk/test/nonmonotonic_fixt.py +12 -11
  272. {nltk-3.2 → nltk-3.3}/nltk/test/paice.doctest +35 -35
  273. {nltk-3.2 → nltk-3.3}/nltk/test/parse.doctest +884 -884
  274. {nltk-3.2 → nltk-3.3}/nltk/test/portuguese_en.doctest +565 -565
  275. {nltk-3.2 → nltk-3.3}/nltk/test/portuguese_en_fixt.py +16 -16
  276. {nltk-3.2 → nltk-3.3}/nltk/test/probability.doctest +304 -304
  277. {nltk-3.2 → nltk-3.3}/nltk/test/probability_fixt.py +13 -12
  278. {nltk-3.2 → nltk-3.3}/nltk/test/propbank.doctest +176 -176
  279. {nltk-3.2 → nltk-3.3}/nltk/test/relextract.doctest +263 -263
  280. {nltk-3.2 → nltk-3.3}/nltk/test/resolution.doctest +221 -221
  281. {nltk-3.2 → nltk-3.3}/nltk/test/runtests.py +86 -64
  282. {nltk-3.2 → nltk-3.3}/nltk/test/segmentation_fixt.py +10 -10
  283. {nltk-3.2 → nltk-3.3}/nltk/test/semantics.doctest +665 -665
  284. {nltk-3.2 → nltk-3.3}/nltk/test/semantics_fixt.py +7 -7
  285. {nltk-3.2 → nltk-3.3}/nltk/test/sentiment.doctest +233 -233
  286. {nltk-3.2 → nltk-3.3}/nltk/test/sentiwordnet.doctest +41 -41
  287. {nltk-3.2 → nltk-3.3}/nltk/test/simple.doctest +85 -85
  288. {nltk-3.2 → nltk-3.3}/nltk/test/stem.doctest +78 -78
  289. {nltk-3.2 → nltk-3.3}/nltk/test/tag.doctest +33 -33
  290. {nltk-3.2 → nltk-3.3}/nltk/test/tokenize.doctest +273 -166
  291. {nltk-3.2 → nltk-3.3}/nltk/test/toolbox.doctest +307 -307
  292. {nltk-3.2 → nltk-3.3}/nltk/test/translate.doctest +242 -242
  293. {nltk-3.2 → nltk-3.3}/nltk/test/translate_fixt.py +3 -3
  294. {nltk-3.2 → nltk-3.3}/nltk/test/tree.doctest +1101 -1101
  295. {nltk-3.2 → nltk-3.3}/nltk/test/treeprettyprinter.doctest +127 -127
  296. {nltk-3.2 → nltk-3.3}/nltk/test/treetransforms.doctest +156 -156
  297. nltk-3.3/nltk/test/unit/test_2x_compat.py +63 -0
  298. nltk-3.3/nltk/test/unit/test_aline.py +52 -0
  299. nltk-3.3/nltk/test/unit/test_chunk.py +56 -0
  300. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_classify.py +55 -53
  301. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_collocations.py +88 -79
  302. nltk-3.3/nltk/test/unit/test_concordance.py +98 -0
  303. nltk-3.3/nltk/test/unit/test_corenlp.py +1000 -0
  304. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_corpora.py +200 -183
  305. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_corpus_views.py +45 -45
  306. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_hmm.py +88 -88
  307. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_json2csv_corpus.py +188 -187
  308. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_naivebayes.py +26 -26
  309. nltk-3.3/nltk/test/unit/test_rte_classify.py +86 -0
  310. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_seekable_unicode_stream_reader.py +124 -124
  311. nltk-3.3/nltk/test/unit/test_senna.py +74 -0
  312. nltk-3.3/nltk/test/unit/test_stem.py +121 -0
  313. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_tag.py +20 -20
  314. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_tgrep.py +628 -626
  315. nltk-3.3/nltk/test/unit/test_tokenize.py +168 -0
  316. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_twitter_auth.py +185 -185
  317. nltk-3.3/nltk/test/unit/test_wordnet.py +134 -0
  318. nltk-3.3/nltk/test/unit/translate/test_bleu.py +244 -0
  319. nltk-3.3/nltk/test/unit/translate/test_gdfa.py +57 -0
  320. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm1.py +75 -75
  321. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm2.py +87 -87
  322. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm3.py +105 -105
  323. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm4.py +126 -126
  324. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm5.py +166 -166
  325. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm_model.py +270 -270
  326. nltk-3.3/nltk/test/unit/translate/test_nist.py +37 -0
  327. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_stack_decoder.py +300 -300
  328. {nltk-3.2 → nltk-3.3}/nltk/test/unit/utils.py +41 -41
  329. {nltk-3.2 → nltk-3.3}/nltk/test/util.doctest +48 -48
  330. {nltk-3.2 → nltk-3.3}/nltk/test/wordnet.doctest +595 -581
  331. {nltk-3.2 → nltk-3.3}/nltk/test/wordnet_fixt.py +7 -6
  332. {nltk-3.2 → nltk-3.3}/nltk/test/wordnet_lch.doctest +53 -53
  333. {nltk-3.2 → nltk-3.3}/nltk/test/wsd.doctest +68 -68
  334. {nltk-3.2 → nltk-3.3}/nltk/text.py +663 -610
  335. {nltk-3.2 → nltk-3.3}/nltk/tgrep.py +938 -937
  336. {nltk-3.2 → nltk-3.3}/nltk/tokenize/__init__.py +130 -107
  337. {nltk-3.2 → nltk-3.3}/nltk/tokenize/api.py +77 -78
  338. {nltk-3.2 → nltk-3.3}/nltk/tokenize/casual.py +343 -342
  339. {nltk-3.2 → nltk-3.3}/nltk/tokenize/mwe.py +120 -120
  340. nltk-3.3/nltk/tokenize/nist.py +173 -0
  341. {nltk-3.2 → nltk-3.3}/nltk/tokenize/punkt.py +1620 -1601
  342. {nltk-3.2 → nltk-3.3}/nltk/tokenize/regexp.py +208 -202
  343. nltk-3.3/nltk/tokenize/repp.py +151 -0
  344. {nltk-3.2 → nltk-3.3}/nltk/tokenize/sexpr.py +140 -142
  345. {nltk-3.2 → nltk-3.3}/nltk/tokenize/simple.py +138 -136
  346. {nltk-3.2 → nltk-3.3}/nltk/tokenize/stanford.py +117 -113
  347. nltk-3.3/nltk/tokenize/stanford_segmenter.py +246 -0
  348. {nltk-3.2 → nltk-3.3}/nltk/tokenize/texttiling.py +458 -460
  349. nltk-3.3/nltk/tokenize/toktok.py +155 -0
  350. nltk-3.3/nltk/tokenize/treebank.py +363 -0
  351. nltk-3.3/nltk/tokenize/util.py +261 -0
  352. {nltk-3.2 → nltk-3.3}/nltk/toolbox.py +499 -497
  353. {nltk-3.2 → nltk-3.3}/nltk/translate/__init__.py +24 -24
  354. {nltk-3.2 → nltk-3.3}/nltk/translate/api.py +331 -321
  355. {nltk-3.2 → nltk-3.3}/nltk/translate/bleu_score.py +615 -556
  356. nltk-3.3/nltk/translate/chrf_score.py +137 -0
  357. {nltk-3.2 → nltk-3.3}/nltk/translate/gale_church.py +248 -235
  358. {nltk-3.2 → nltk-3.3}/nltk/translate/gdfa.py +135 -131
  359. nltk-3.3/nltk/translate/gleu_score.py +193 -0
  360. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm1.py +250 -241
  361. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm2.py +308 -308
  362. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm3.py +337 -337
  363. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm4.py +467 -467
  364. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm5.py +639 -639
  365. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm_model.py +536 -533
  366. {nltk-3.2 → nltk-3.3}/nltk/translate/metrics.py +40 -40
  367. nltk-3.3/nltk/translate/nist_score.py +178 -0
  368. {nltk-3.2 → nltk-3.3}/nltk/translate/phrase_based.py +175 -175
  369. {nltk-3.2 → nltk-3.3}/nltk/translate/ribes_score.py +325 -325
  370. {nltk-3.2 → nltk-3.3}/nltk/translate/stack_decoder.py +499 -495
  371. {nltk-3.2 → nltk-3.3}/nltk/tree.py +1605 -1603
  372. {nltk-3.2 → nltk-3.3}/nltk/treeprettyprinter.py +564 -564
  373. {nltk-3.2 → nltk-3.3}/nltk/treetransforms.py +309 -309
  374. {nltk-3.2 → nltk-3.3}/nltk/twitter/__init__.py +28 -28
  375. {nltk-3.2 → nltk-3.3}/nltk/twitter/api.py +141 -136
  376. {nltk-3.2 → nltk-3.3}/nltk/twitter/common.py +264 -258
  377. {nltk-3.2 → nltk-3.3}/nltk/twitter/twitter_demo.py +288 -288
  378. {nltk-3.2 → nltk-3.3}/nltk/twitter/twitterclient.py +530 -530
  379. {nltk-3.2 → nltk-3.3}/nltk/twitter/util.py +147 -146
  380. nltk-3.3/nltk/util.py +751 -0
  381. {nltk-3.2 → nltk-3.3}/nltk/wsd.py +53 -53
  382. {nltk-3.2 → nltk-3.3/nltk.egg-info}/PKG-INFO +13 -5
  383. {nltk-3.2 → nltk-3.3}/nltk.egg-info/SOURCES.txt +26 -3
  384. nltk-3.3/nltk.egg-info/requires.txt +31 -0
  385. {nltk-3.2 → nltk-3.3}/setup.cfg +0 -1
  386. {nltk-3.2 → nltk-3.3}/setup.py +102 -75
  387. nltk-3.2/nltk/VERSION +0 -1
  388. nltk-3.2/nltk/compat.py +0 -680
  389. nltk-3.2/nltk/corpus/reader/pl196x.py +0 -278
  390. nltk-3.2/nltk/corpus/reader/verbnet.py +0 -408
  391. nltk-3.2/nltk/corpus/reader/wordlist.py +0 -37
  392. nltk-3.2/nltk/sentiment/vader_lexicon.txt +0 -7517
  393. nltk-3.2/nltk/six.py +0 -868
  394. nltk-3.2/nltk/stem/porter.py +0 -692
  395. nltk-3.2/nltk/test/unit/test_2x_compat.py +0 -26
  396. nltk-3.2/nltk/test/unit/test_stem.py +0 -36
  397. nltk-3.2/nltk/test/unit/test_tokenize.py +0 -23
  398. nltk-3.2/nltk/test/unit/translate/test_bleu.py +0 -84
  399. nltk-3.2/nltk/tokenize/treebank.py +0 -124
  400. nltk-3.2/nltk/tokenize/util.py +0 -90
  401. nltk-3.2/nltk/util.py +0 -1364
  402. {nltk-3.2 → nltk-3.3}/nltk/test/unit/__init__.py +0 -0
  403. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/__init__.py +0 -0
  404. {nltk-3.2 → nltk-3.3}/nltk.egg-info/dependency_links.txt +0 -0
  405. {nltk-3.2 → nltk-3.3}/nltk.egg-info/not-zip-safe +0 -0
  406. {nltk-3.2 → nltk-3.3}/nltk.egg-info/top_level.txt +0 -0
@@ -1,2276 +1,2276 @@
1
- # Natural Language Toolkit: Chart Parser Application
2
- #
3
- # Copyright (C) 2001-2015 NLTK Project
4
- # Author: Edward Loper <edloper@gmail.com>
5
- # Jean Mark Gawron <gawron@mail.sdsu.edu>
6
- # Steven Bird <stevenbird1@gmail.com>
7
- # URL: <http://nltk.org/>
8
- # For license information, see LICENSE.TXT
9
-
10
- """
11
- A graphical tool for exploring chart parsing.
12
-
13
- Chart parsing is a flexible parsing algorithm that uses a data
14
- structure called a "chart" to record hypotheses about syntactic
15
- constituents. Each hypothesis is represented by a single "edge" on
16
- the chart. A set of "chart rules" determine when new edges can be
17
- added to the chart. This set of rules controls the overall behavior
18
- of the parser (e.g. whether it parses top-down or bottom-up).
19
-
20
- The chart parsing tool demonstrates the process of parsing a single
21
- sentence, with a given grammar and lexicon. Its display is divided
22
- into three sections: the bottom section displays the chart; the middle
23
- section displays the sentence; and the top section displays the
24
- partial syntax tree corresponding to the selected edge. Buttons along
25
- the bottom of the window are used to control the execution of the
26
- algorithm.
27
-
28
- The chart parsing tool allows for flexible control of the parsing
29
- algorithm. At each step of the algorithm, you can select which rule
30
- or strategy you wish to apply. This allows you to experiment with
31
- mixing different strategies (e.g. top-down and bottom-up). You can
32
- exercise fine-grained control over the algorithm by selecting which
33
- edge you wish to apply a rule to.
34
- """
35
-
36
- # At some point, we should rewrite this tool to use the new canvas
37
- # widget system.
38
-
39
-
40
- from __future__ import division
41
- import nltk.compat
42
- import pickle
43
- from tkinter.filedialog import asksaveasfilename, askopenfilename
44
- import tkinter
45
- import math
46
- import os.path
47
- import tkinter.font, tkinter.messagebox
48
-
49
- from nltk.parse.chart import (BottomUpPredictCombineRule, BottomUpPredictRule,
50
- Chart, LeafEdge, LeafInitRule, SingleEdgeFundamentalRule,
51
- SteppingChartParser, TopDownInitRule, TopDownPredictRule,
52
- TreeEdge)
53
- from nltk.tree import Tree
54
- from nltk.grammar import Nonterminal, CFG
55
- from nltk.util import in_idle
56
- from nltk.draw.util import (CanvasFrame, ColorizedList,
57
- EntryDialog, MutableOptionMenu,
58
- ShowText, SymbolWidget)
59
- from nltk.draw import CFGEditor, tree_to_treesegment, TreeSegmentWidget
60
-
61
- # Known bug: ChartView doesn't handle edges generated by epsilon
62
- # productions (e.g., [Production: PP -> ]) very well.
63
-
64
- #######################################################################
65
- # Edge List
66
- #######################################################################
67
-
68
- class EdgeList(ColorizedList):
69
- ARROW = SymbolWidget.SYMBOLS['rightarrow']
70
-
71
- def _init_colortags(self, textwidget, options):
72
- textwidget.tag_config('terminal', foreground='#006000')
73
- textwidget.tag_config('arrow', font='symbol', underline='0')
74
- textwidget.tag_config('dot', foreground = '#000000')
75
- textwidget.tag_config('nonterminal', foreground='blue',
76
- font=('helvetica', -12, 'bold'))
77
-
78
- def _item_repr(self, item):
79
- contents = []
80
- contents.append(('%s\t' % item.lhs(), 'nonterminal'))
81
- contents.append((self.ARROW, 'arrow'))
82
- for i, elt in enumerate(item.rhs()):
83
- if i == item.dot():
84
- contents.append((' *', 'dot'))
85
- if isinstance(elt, Nonterminal):
86
- contents.append((' %s' % elt.symbol(), 'nonterminal'))
87
- else:
88
- contents.append((' %r' % elt, 'terminal'))
89
- if item.is_complete():
90
- contents.append((' *', 'dot'))
91
- return contents
92
-
93
- #######################################################################
94
- # Chart Matrix View
95
- #######################################################################
96
-
97
- class ChartMatrixView(object):
98
- """
99
- A view of a chart that displays the contents of the corresponding matrix.
100
- """
101
- def __init__(self, parent, chart, toplevel=True, title='Chart Matrix',
102
- show_numedges=False):
103
- self._chart = chart
104
- self._cells = []
105
- self._marks = []
106
-
107
- self._selected_cell = None
108
-
109
- if toplevel:
110
- self._root = tkinter.Toplevel(parent)
111
- self._root.title(title)
112
- self._root.bind('<Control-q>', self.destroy)
113
- self._init_quit(self._root)
114
- else:
115
- self._root = tkinter.Frame(parent)
116
-
117
- self._init_matrix(self._root)
118
- self._init_list(self._root)
119
- if show_numedges:
120
- self._init_numedges(self._root)
121
- else:
122
- self._numedges_label = None
123
-
124
- self._callbacks = {}
125
-
126
- self._num_edges = 0
127
-
128
- self.draw()
129
-
130
- def _init_quit(self, root):
131
- quit = tkinter.Button(root, text='Quit', command=self.destroy)
132
- quit.pack(side='bottom', expand=0, fill='none')
133
-
134
- def _init_matrix(self, root):
135
- cframe = tkinter.Frame(root, border=2, relief='sunken')
136
- cframe.pack(expand=0, fill='none', padx=1, pady=3, side='top')
137
- self._canvas = tkinter.Canvas(cframe, width=200, height=200,
138
- background='white')
139
- self._canvas.pack(expand=0, fill='none')
140
-
141
- def _init_numedges(self, root):
142
- self._numedges_label = tkinter.Label(root, text='0 edges')
143
- self._numedges_label.pack(expand=0, fill='none', side='top')
144
-
145
- def _init_list(self, root):
146
- self._list = EdgeList(root, [], width=20, height=5)
147
- self._list.pack(side='top', expand=1, fill='both', pady=3)
148
- def cb(edge, self=self): self._fire_callbacks('select', edge)
149
- self._list.add_callback('select', cb)
150
- self._list.focus()
151
-
152
- def destroy(self, *e):
153
- if self._root is None: return
154
- try: self._root.destroy()
155
- except: pass
156
- self._root = None
157
-
158
- def set_chart(self, chart):
159
- if chart is not self._chart:
160
- self._chart = chart
161
- self._num_edges = 0
162
- self.draw()
163
-
164
- def update(self):
165
- if self._root is None: return
166
-
167
- # Count the edges in each cell
168
- N = len(self._cells)
169
- cell_edges = [[0 for i in range(N)] for j in range(N)]
170
- for edge in self._chart:
171
- cell_edges[edge.start()][edge.end()] += 1
172
-
173
- # Color the cells correspondingly.
174
- for i in range(N):
175
- for j in range(i, N):
176
- if cell_edges[i][j] == 0:
177
- color = 'gray20'
178
- else:
179
- color = ('#00%02x%02x' %
180
- (min(255, 50+128*cell_edges[i][j]/10),
181
- max(0, 128-128*cell_edges[i][j]/10)))
182
- cell_tag = self._cells[i][j]
183
- self._canvas.itemconfig(cell_tag, fill=color)
184
- if (i,j) == self._selected_cell:
185
- self._canvas.itemconfig(cell_tag, outline='#00ffff',
186
- width=3)
187
- self._canvas.tag_raise(cell_tag)
188
- else:
189
- self._canvas.itemconfig(cell_tag, outline='black',
190
- width=1)
191
-
192
- # Update the edge list.
193
- edges = list(self._chart.select(span=self._selected_cell))
194
- self._list.set(edges)
195
-
196
- # Update our edge count.
197
- self._num_edges = self._chart.num_edges()
198
- if self._numedges_label is not None:
199
- self._numedges_label['text'] = '%d edges' % self._num_edges
200
-
201
- def activate(self):
202
- self._canvas.itemconfig('inactivebox', state='hidden')
203
- self.update()
204
-
205
- def inactivate(self):
206
- self._canvas.itemconfig('inactivebox', state='normal')
207
- self.update()
208
-
209
- def add_callback(self, event, func):
210
- self._callbacks.setdefault(event,{})[func] = 1
211
-
212
- def remove_callback(self, event, func=None):
213
- if func is None: del self._callbacks[event]
214
- else:
215
- try: del self._callbacks[event][func]
216
- except: pass
217
-
218
- def _fire_callbacks(self, event, *args):
219
- if event not in self._callbacks: return
220
- for cb_func in list(self._callbacks[event].keys()): cb_func(*args)
221
-
222
- def select_cell(self, i, j):
223
- if self._root is None: return
224
-
225
- # If the cell is already selected (and the chart contents
226
- # haven't changed), then do nothing.
227
- if ((i,j) == self._selected_cell and
228
- self._chart.num_edges() == self._num_edges): return
229
-
230
- self._selected_cell = (i,j)
231
- self.update()
232
-
233
- # Fire the callback.
234
- self._fire_callbacks('select_cell', i, j)
235
-
236
- def deselect_cell(self):
237
- if self._root is None: return
238
- self._selected_cell = None
239
- self._list.set([])
240
- self.update()
241
-
242
- def _click_cell(self, i, j):
243
- if self._selected_cell == (i,j):
244
- self.deselect_cell()
245
- else:
246
- self.select_cell(i, j)
247
-
248
- def view_edge(self, edge):
249
- self.select_cell(*edge.span())
250
- self._list.view(edge)
251
-
252
- def mark_edge(self, edge):
253
- if self._root is None: return
254
- self.select_cell(*edge.span())
255
- self._list.mark(edge)
256
-
257
- def unmark_edge(self, edge=None):
258
- if self._root is None: return
259
- self._list.unmark(edge)
260
-
261
- def markonly_edge(self, edge):
262
- if self._root is None: return
263
- self.select_cell(*edge.span())
264
- self._list.markonly(edge)
265
-
266
- def draw(self):
267
- if self._root is None: return
268
- LEFT_MARGIN = BOT_MARGIN = 15
269
- TOP_MARGIN = 5
270
- c = self._canvas
271
- c.delete('all')
272
- N = self._chart.num_leaves()+1
273
- dx = (int(c['width'])-LEFT_MARGIN)/N
274
- dy = (int(c['height'])-TOP_MARGIN-BOT_MARGIN)/N
275
-
276
- c.delete('all')
277
-
278
- # Labels and dotted lines
279
- for i in range(N):
280
- c.create_text(LEFT_MARGIN-2, i*dy+dy/2+TOP_MARGIN,
281
- text=repr(i), anchor='e')
282
- c.create_text(i*dx+dx/2+LEFT_MARGIN, N*dy+TOP_MARGIN+1,
283
- text=repr(i), anchor='n')
284
- c.create_line(LEFT_MARGIN, dy*(i+1)+TOP_MARGIN,
285
- dx*N+LEFT_MARGIN, dy*(i+1)+TOP_MARGIN, dash='.')
286
- c.create_line(dx*i+LEFT_MARGIN, TOP_MARGIN,
287
- dx*i+LEFT_MARGIN, dy*N+TOP_MARGIN, dash='.')
288
-
289
- # A box around the whole thing
290
- c.create_rectangle(LEFT_MARGIN, TOP_MARGIN,
291
- LEFT_MARGIN+dx*N, dy*N+TOP_MARGIN,
292
- width=2)
293
-
294
- # Cells
295
- self._cells = [[None for i in range(N)] for j in range(N)]
296
- for i in range(N):
297
- for j in range(i, N):
298
- t = c.create_rectangle(j*dx+LEFT_MARGIN, i*dy+TOP_MARGIN,
299
- (j+1)*dx+LEFT_MARGIN,
300
- (i+1)*dy+TOP_MARGIN,
301
- fill='gray20')
302
- self._cells[i][j] = t
303
- def cb(event, self=self, i=i, j=j): self._click_cell(i,j)
304
- c.tag_bind(t, '<Button-1>', cb)
305
-
306
- # Inactive box
307
- xmax, ymax = int(c['width']), int(c['height'])
308
- t = c.create_rectangle(-100, -100, xmax+100, ymax+100,
309
- fill='gray50', state='hidden',
310
- tag='inactivebox')
311
- c.tag_lower(t)
312
-
313
- # Update the cells.
314
- self.update()
315
-
316
- def pack(self, *args, **kwargs):
317
- self._root.pack(*args, **kwargs)
318
-
319
- #######################################################################
320
- # Chart Results View
321
- #######################################################################
322
-
323
- class ChartResultsView(object):
324
- def __init__(self, parent, chart, grammar, toplevel=True):
325
- self._chart = chart
326
- self._grammar = grammar
327
- self._trees = []
328
- self._y = 10
329
- self._treewidgets = []
330
- self._selection = None
331
- self._selectbox = None
332
-
333
- if toplevel:
334
- self._root = tkinter.Toplevel(parent)
335
- self._root.title('Chart Parser Application: Results')
336
- self._root.bind('<Control-q>', self.destroy)
337
- else:
338
- self._root = tkinter.Frame(parent)
339
-
340
- # Buttons
341
- if toplevel:
342
- buttons = tkinter.Frame(self._root)
343
- buttons.pack(side='bottom', expand=0, fill='x')
344
- tkinter.Button(buttons, text='Quit',
345
- command=self.destroy).pack(side='right')
346
- tkinter.Button(buttons, text='Print All',
347
- command=self.print_all).pack(side='left')
348
- tkinter.Button(buttons, text='Print Selection',
349
- command=self.print_selection).pack(side='left')
350
-
351
- # Canvas frame.
352
- self._cframe = CanvasFrame(self._root, closeenough=20)
353
- self._cframe.pack(side='top', expand=1, fill='both')
354
-
355
- # Initial update
356
- self.update()
357
-
358
- def update(self, edge=None):
359
- if self._root is None: return
360
- # If the edge isn't a parse edge, do nothing.
361
- if edge is not None:
362
- if edge.lhs() != self._grammar.start(): return
363
- if edge.span() != (0, self._chart.num_leaves()): return
364
-
365
- for parse in self._chart.parses(self._grammar.start()):
366
- if parse not in self._trees:
367
- self._add(parse)
368
-
369
- def _add(self, parse):
370
- # Add it to self._trees.
371
- self._trees.append(parse)
372
-
373
- # Create a widget for it.
374
- c = self._cframe.canvas()
375
- treewidget = tree_to_treesegment(c, parse)
376
-
377
- # Add it to the canvas frame.
378
- self._treewidgets.append(treewidget)
379
- self._cframe.add_widget(treewidget, 10, self._y)
380
-
381
- # Register callbacks.
382
- treewidget.bind_click(self._click)
383
-
384
- # Update y.
385
- self._y = treewidget.bbox()[3] + 10
386
-
387
- def _click(self, widget):
388
- c = self._cframe.canvas()
389
- if self._selection is not None:
390
- c.delete(self._selectbox)
391
- self._selection = widget
392
- (x1, y1, x2, y2) = widget.bbox()
393
- self._selectbox = c.create_rectangle(x1, y1, x2, y2,
394
- width=2, outline='#088')
395
-
396
- def _color(self, treewidget, color):
397
- treewidget.label()['color'] = color
398
- for child in treewidget.subtrees():
399
- if isinstance(child, TreeSegmentWidget):
400
- self._color(child, color)
401
- else:
402
- child['color'] = color
403
-
404
- def print_all(self, *e):
405
- if self._root is None: return
406
- self._cframe.print_to_file()
407
-
408
- def print_selection(self, *e):
409
- if self._root is None: return
410
- if self._selection is None:
411
- tkinter.messagebox.showerror('Print Error', 'No tree selected')
412
- else:
413
- c = self._cframe.canvas()
414
- for widget in self._treewidgets:
415
- if widget is not self._selection:
416
- self._cframe.destroy_widget(widget)
417
- c.delete(self._selectbox)
418
- (x1,y1,x2,y2) = self._selection.bbox()
419
- self._selection.move(10-x1,10-y1)
420
- c['scrollregion'] = '0 0 %s %s' % (x2-x1+20, y2-y1+20)
421
- self._cframe.print_to_file()
422
-
423
- # Restore our state.
424
- self._treewidgets = [self._selection]
425
- self.clear()
426
- self.update()
427
-
428
- def clear(self):
429
- if self._root is None: return
430
- for treewidget in self._treewidgets:
431
- self._cframe.destroy_widget(treewidget)
432
- self._trees = []
433
- self._treewidgets = []
434
- if self._selection is not None:
435
- self._cframe.canvas().delete(self._selectbox)
436
- self._selection = None
437
- self._y = 10
438
-
439
- def set_chart(self, chart):
440
- self.clear()
441
- self._chart = chart
442
- self.update()
443
-
444
- def set_grammar(self, grammar):
445
- self.clear()
446
- self._grammar = grammar
447
- self.update()
448
-
449
- def destroy(self, *e):
450
- if self._root is None: return
451
- try: self._root.destroy()
452
- except: pass
453
- self._root = None
454
-
455
- def pack(self, *args, **kwargs):
456
- self._root.pack(*args, **kwargs)
457
-
458
- #######################################################################
459
- # Chart Comparer
460
- #######################################################################
461
-
462
- class ChartComparer(object):
463
- """
464
-
465
- :ivar _root: The root window
466
-
467
- :ivar _charts: A dictionary mapping names to charts. When
468
- charts are loaded, they are added to this dictionary.
469
-
470
- :ivar _left_chart: The left ``Chart``.
471
- :ivar _left_name: The name ``_left_chart`` (derived from filename)
472
- :ivar _left_matrix: The ``ChartMatrixView`` for ``_left_chart``
473
- :ivar _left_selector: The drop-down ``MutableOptionsMenu`` used
474
- to select ``_left_chart``.
475
-
476
- :ivar _right_chart: The right ``Chart``.
477
- :ivar _right_name: The name ``_right_chart`` (derived from filename)
478
- :ivar _right_matrix: The ``ChartMatrixView`` for ``_right_chart``
479
- :ivar _right_selector: The drop-down ``MutableOptionsMenu`` used
480
- to select ``_right_chart``.
481
-
482
- :ivar _out_chart: The out ``Chart``.
483
- :ivar _out_name: The name ``_out_chart`` (derived from filename)
484
- :ivar _out_matrix: The ``ChartMatrixView`` for ``_out_chart``
485
- :ivar _out_label: The label for ``_out_chart``.
486
-
487
- :ivar _op_label: A Label containing the most recent operation.
488
- """
489
-
490
- _OPSYMBOL = {'-': '-',
491
- 'and': SymbolWidget.SYMBOLS['intersection'],
492
- 'or': SymbolWidget.SYMBOLS['union']}
493
-
494
- def __init__(self, *chart_filenames):
495
- # This chart is displayed when we don't have a value (eg
496
- # before any chart is loaded).
497
- faketok = [''] * 8
498
- self._emptychart = Chart(faketok)
499
-
500
- # The left & right charts start out empty.
501
- self._left_name = 'None'
502
- self._right_name = 'None'
503
- self._left_chart = self._emptychart
504
- self._right_chart = self._emptychart
505
-
506
- # The charts that have been loaded.
507
- self._charts = {'None': self._emptychart}
508
-
509
- # The output chart.
510
- self._out_chart = self._emptychart
511
-
512
- # The most recent operation
513
- self._operator = None
514
-
515
- # Set up the root window.
516
- self._root = tkinter.Tk()
517
- self._root.title('Chart Comparison')
518
- self._root.bind('<Control-q>', self.destroy)
519
- self._root.bind('<Control-x>', self.destroy)
520
-
521
- # Initialize all widgets, etc.
522
- self._init_menubar(self._root)
523
- self._init_chartviews(self._root)
524
- self._init_divider(self._root)
525
- self._init_buttons(self._root)
526
- self._init_bindings(self._root)
527
-
528
- # Load any specified charts.
529
- for filename in chart_filenames:
530
- self.load_chart(filename)
531
-
532
- def destroy(self, *e):
533
- if self._root is None: return
534
- try: self._root.destroy()
535
- except: pass
536
- self._root = None
537
-
538
- def mainloop(self, *args, **kwargs):
539
- return
540
- self._root.mainloop(*args, **kwargs)
541
-
542
- #////////////////////////////////////////////////////////////
543
- # Initialization
544
- #////////////////////////////////////////////////////////////
545
-
546
- def _init_menubar(self, root):
547
- menubar = tkinter.Menu(root)
548
-
549
- # File menu
550
- filemenu = tkinter.Menu(menubar, tearoff=0)
551
- filemenu.add_command(label='Load Chart', accelerator='Ctrl-o',
552
- underline=0, command=self.load_chart_dialog)
553
- filemenu.add_command(label='Save Output', accelerator='Ctrl-s',
554
- underline=0, command=self.save_chart_dialog)
555
- filemenu.add_separator()
556
- filemenu.add_command(label='Exit', underline=1,
557
- command=self.destroy, accelerator='Ctrl-x')
558
- menubar.add_cascade(label='File', underline=0, menu=filemenu)
559
-
560
- # Compare menu
561
- opmenu = tkinter.Menu(menubar, tearoff=0)
562
- opmenu.add_command(label='Intersection',
563
- command=self._intersection,
564
- accelerator='+')
565
- opmenu.add_command(label='Union',
566
- command=self._union,
567
- accelerator='*')
568
- opmenu.add_command(label='Difference',
569
- command=self._difference,
570
- accelerator='-')
571
- opmenu.add_separator()
572
- opmenu.add_command(label='Swap Charts',
573
- command=self._swapcharts)
574
- menubar.add_cascade(label='Compare', underline=0, menu=opmenu)
575
-
576
- # Add the menu
577
- self._root.config(menu=menubar)
578
-
579
- def _init_divider(self, root):
580
- divider = tkinter.Frame(root, border=2, relief='sunken')
581
- divider.pack(side='top', fill='x', ipady=2)
582
-
583
- def _init_chartviews(self, root):
584
- opfont=('symbol', -36) # Font for operator.
585
- eqfont=('helvetica', -36) # Font for equals sign.
586
-
587
- frame = tkinter.Frame(root, background='#c0c0c0')
588
- frame.pack(side='top', expand=1, fill='both')
589
-
590
- # The left matrix.
591
- cv1_frame = tkinter.Frame(frame, border=3, relief='groove')
592
- cv1_frame.pack(side='left', padx=8, pady=7, expand=1, fill='both')
593
- self._left_selector = MutableOptionMenu(
594
- cv1_frame, list(self._charts.keys()), command=self._select_left)
595
- self._left_selector.pack(side='top', pady=5, fill='x')
596
- self._left_matrix = ChartMatrixView(cv1_frame, self._emptychart,
597
- toplevel=False,
598
- show_numedges=True)
599
- self._left_matrix.pack(side='bottom', padx=5, pady=5,
600
- expand=1, fill='both')
601
- self._left_matrix.add_callback('select', self.select_edge)
602
- self._left_matrix.add_callback('select_cell', self.select_cell)
603
- self._left_matrix.inactivate()
604
-
605
- # The operator.
606
- self._op_label = tkinter.Label(frame, text=' ', width=3,
607
- background='#c0c0c0', font=opfont)
608
- self._op_label.pack(side='left', padx=5, pady=5)
609
-
610
- # The right matrix.
611
- cv2_frame = tkinter.Frame(frame, border=3, relief='groove')
612
- cv2_frame.pack(side='left', padx=8, pady=7, expand=1, fill='both')
613
- self._right_selector = MutableOptionMenu(
614
- cv2_frame, list(self._charts.keys()), command=self._select_right)
615
- self._right_selector.pack(side='top', pady=5, fill='x')
616
- self._right_matrix = ChartMatrixView(cv2_frame, self._emptychart,
617
- toplevel=False,
618
- show_numedges=True)
619
- self._right_matrix.pack(side='bottom', padx=5, pady=5,
620
- expand=1, fill='both')
621
- self._right_matrix.add_callback('select', self.select_edge)
622
- self._right_matrix.add_callback('select_cell', self.select_cell)
623
- self._right_matrix.inactivate()
624
-
625
- # The equals sign
626
- tkinter.Label(frame, text='=', width=3, background='#c0c0c0',
627
- font=eqfont).pack(side='left', padx=5, pady=5)
628
-
629
- # The output matrix.
630
- out_frame = tkinter.Frame(frame, border=3, relief='groove')
631
- out_frame.pack(side='left', padx=8, pady=7, expand=1, fill='both')
632
- self._out_label = tkinter.Label(out_frame, text='Output')
633
- self._out_label.pack(side='top', pady=9)
634
- self._out_matrix = ChartMatrixView(out_frame, self._emptychart,
635
- toplevel=False,
636
- show_numedges=True)
637
- self._out_matrix.pack(side='bottom', padx=5, pady=5,
638
- expand=1, fill='both')
639
- self._out_matrix.add_callback('select', self.select_edge)
640
- self._out_matrix.add_callback('select_cell', self.select_cell)
641
- self._out_matrix.inactivate()
642
-
643
- def _init_buttons(self, root):
644
- buttons = tkinter.Frame(root)
645
- buttons.pack(side='bottom', pady=5, fill='x', expand=0)
646
- tkinter.Button(buttons, text='Intersection',
647
- command=self._intersection).pack(side='left')
648
- tkinter.Button(buttons, text='Union',
649
- command=self._union).pack(side='left')
650
- tkinter.Button(buttons, text='Difference',
651
- command=self._difference).pack(side='left')
652
- tkinter.Frame(buttons, width=20).pack(side='left')
653
- tkinter.Button(buttons, text='Swap Charts',
654
- command=self._swapcharts).pack(side='left')
655
-
656
- tkinter.Button(buttons, text='Detatch Output',
657
- command=self._detatch_out).pack(side='right')
658
-
659
- def _init_bindings(self, root):
660
- #root.bind('<Control-s>', self.save_chart)
661
- root.bind('<Control-o>', self.load_chart_dialog)
662
- #root.bind('<Control-r>', self.reset)
663
-
664
- #////////////////////////////////////////////////////////////
665
- # Input Handling
666
- #////////////////////////////////////////////////////////////
667
-
668
- def _select_left(self, name):
669
- self._left_name = name
670
- self._left_chart = self._charts[name]
671
- self._left_matrix.set_chart(self._left_chart)
672
- if name == 'None': self._left_matrix.inactivate()
673
- self._apply_op()
674
-
675
- def _select_right(self, name):
676
- self._right_name = name
677
- self._right_chart = self._charts[name]
678
- self._right_matrix.set_chart(self._right_chart)
679
- if name == 'None': self._right_matrix.inactivate()
680
- self._apply_op()
681
-
682
- def _apply_op(self):
683
- if self._operator == '-': self._difference()
684
- elif self._operator == 'or': self._union()
685
- elif self._operator == 'and': self._intersection()
686
-
687
-
688
- #////////////////////////////////////////////////////////////
689
- # File
690
- #////////////////////////////////////////////////////////////
691
- CHART_FILE_TYPES = [('Pickle file', '.pickle'),
692
- ('All files', '*')]
693
-
694
- def save_chart_dialog(self, *args):
695
- filename = asksaveasfilename(filetypes=self.CHART_FILE_TYPES,
696
- defaultextension='.pickle')
697
- if not filename: return
698
- try:
699
- with open(filename, 'wb') as outfile:
700
- pickle.dump(self._out_chart, outfile)
701
- except Exception as e:
702
- tkinter.messagebox.showerror('Error Saving Chart',
703
- 'Unable to open file: %r\n%s' %
704
- (filename, e))
705
-
706
- def load_chart_dialog(self, *args):
707
- filename = askopenfilename(filetypes=self.CHART_FILE_TYPES,
708
- defaultextension='.pickle')
709
- if not filename: return
710
- try: self.load_chart(filename)
711
- except Exception as e:
712
- tkinter.messagebox.showerror('Error Loading Chart',
713
- 'Unable to open file: %r\n%s' %
714
- (filename, e))
715
-
716
- def load_chart(self, filename):
717
- with open(filename, 'rb') as infile:
718
- chart = pickle.load(infile)
719
- name = os.path.basename(filename)
720
- if name.endswith('.pickle'): name = name[:-7]
721
- if name.endswith('.chart'): name = name[:-6]
722
- self._charts[name] = chart
723
- self._left_selector.add(name)
724
- self._right_selector.add(name)
725
-
726
- # If either left_matrix or right_matrix is empty, then
727
- # display the new chart.
728
- if self._left_chart is self._emptychart:
729
- self._left_selector.set(name)
730
- elif self._right_chart is self._emptychart:
731
- self._right_selector.set(name)
732
-
733
- def _update_chartviews(self):
734
- self._left_matrix.update()
735
- self._right_matrix.update()
736
- self._out_matrix.update()
737
-
738
- #////////////////////////////////////////////////////////////
739
- # Selection
740
- #////////////////////////////////////////////////////////////
741
-
742
- def select_edge(self, edge):
743
- if edge in self._left_chart:
744
- self._left_matrix.markonly_edge(edge)
745
- else:
746
- self._left_matrix.unmark_edge()
747
- if edge in self._right_chart:
748
- self._right_matrix.markonly_edge(edge)
749
- else:
750
- self._right_matrix.unmark_edge()
751
- if edge in self._out_chart:
752
- self._out_matrix.markonly_edge(edge)
753
- else:
754
- self._out_matrix.unmark_edge()
755
-
756
- def select_cell(self, i, j):
757
- self._left_matrix.select_cell(i, j)
758
- self._right_matrix.select_cell(i, j)
759
- self._out_matrix.select_cell(i, j)
760
-
761
- #////////////////////////////////////////////////////////////
762
- # Operations
763
- #////////////////////////////////////////////////////////////
764
-
765
- def _difference(self):
766
- if not self._checkcompat(): return
767
-
768
- out_chart = Chart(self._left_chart.tokens())
769
- for edge in self._left_chart:
770
- if edge not in self._right_chart:
771
- out_chart.insert(edge, [])
772
-
773
- self._update('-', out_chart)
774
-
775
- def _intersection(self):
776
- if not self._checkcompat(): return
777
-
778
- out_chart = Chart(self._left_chart.tokens())
779
- for edge in self._left_chart:
780
- if edge in self._right_chart:
781
- out_chart.insert(edge, [])
782
-
783
- self._update('and', out_chart)
784
-
785
- def _union(self):
786
- if not self._checkcompat(): return
787
-
788
- out_chart = Chart(self._left_chart.tokens())
789
- for edge in self._left_chart:
790
- out_chart.insert(edge, [])
791
- for edge in self._right_chart:
792
- out_chart.insert(edge, [])
793
-
794
- self._update('or', out_chart)
795
-
796
- def _swapcharts(self):
797
- left, right = self._left_name, self._right_name
798
- self._left_selector.set(right)
799
- self._right_selector.set(left)
800
-
801
- def _checkcompat(self):
802
- if (self._left_chart.tokens() != self._right_chart.tokens() or
803
- self._left_chart.property_names() !=
804
- self._right_chart.property_names() or
805
- self._left_chart == self._emptychart or
806
- self._right_chart == self._emptychart):
807
- # Clear & inactivate the output chart.
808
- self._out_chart = self._emptychart
809
- self._out_matrix.set_chart(self._out_chart)
810
- self._out_matrix.inactivate()
811
- self._out_label['text'] = 'Output'
812
- # Issue some other warning?
813
- return False
814
- else:
815
- return True
816
-
817
- def _update(self, operator, out_chart):
818
- self._operator = operator
819
- self._op_label['text'] = self._OPSYMBOL[operator]
820
- self._out_chart = out_chart
821
- self._out_matrix.set_chart(out_chart)
822
- self._out_label['text'] = '%s %s %s' % (self._left_name,
823
- self._operator,
824
- self._right_name)
825
-
826
- def _clear_out_chart(self):
827
- self._out_chart = self._emptychart
828
- self._out_matrix.set_chart(self._out_chart)
829
- self._op_label['text'] = ' '
830
- self._out_matrix.inactivate()
831
-
832
- def _detatch_out(self):
833
- ChartMatrixView(self._root, self._out_chart,
834
- title=self._out_label['text'])
835
-
836
-
837
-
838
-
839
-
840
-
841
-
842
-
843
- #######################################################################
844
- # Chart View
845
- #######################################################################
846
-
847
- class ChartView(object):
848
- """
849
- A component for viewing charts. This is used by ``ChartParserApp`` to
850
- allow students to interactively experiment with various chart
851
- parsing techniques. It is also used by ``Chart.draw()``.
852
-
853
- :ivar _chart: The chart that we are giving a view of. This chart
854
- may be modified; after it is modified, you should call
855
- ``update``.
856
- :ivar _sentence: The list of tokens that the chart spans.
857
-
858
- :ivar _root: The root window.
859
- :ivar _chart_canvas: The canvas we're using to display the chart
860
- itself.
861
- :ivar _tree_canvas: The canvas we're using to display the tree
862
- that each edge spans. May be None, if we're not displaying
863
- trees.
864
- :ivar _sentence_canvas: The canvas we're using to display the sentence
865
- text. May be None, if we're not displaying the sentence text.
866
- :ivar _edgetags: A dictionary mapping from edges to the tags of
867
- the canvas elements (lines, etc) used to display that edge.
868
- The values of this dictionary have the form
869
- ``(linetag, rhstag1, dottag, rhstag2, lhstag)``.
870
- :ivar _treetags: A list of all the tags that make up the tree;
871
- used to erase the tree (without erasing the loclines).
872
- :ivar _chart_height: The height of the chart canvas.
873
- :ivar _sentence_height: The height of the sentence canvas.
874
- :ivar _tree_height: The height of the tree
875
-
876
- :ivar _text_height: The height of a text string (in the normal
877
- font).
878
-
879
- :ivar _edgelevels: A list of edges at each level of the chart (the
880
- top level is the 0th element). This list is used to remember
881
- where edges should be drawn; and to make sure that no edges
882
- are overlapping on the chart view.
883
-
884
- :ivar _unitsize: Pixel size of one unit (from the location). This
885
- is determined by the span of the chart's location, and the
886
- width of the chart display canvas.
887
-
888
- :ivar _fontsize: The current font size
889
-
890
- :ivar _marks: A dictionary from edges to marks. Marks are
891
- strings, specifying colors (e.g. 'green').
892
- """
893
-
894
- _LEAF_SPACING = 10
895
- _MARGIN = 10
896
- _TREE_LEVEL_SIZE = 12
897
- _CHART_LEVEL_SIZE = 40
898
-
899
- def __init__(self, chart, root=None, **kw):
900
- """
901
- Construct a new ``Chart`` display.
902
- """
903
- # Process keyword args.
904
- draw_tree = kw.get('draw_tree', 0)
905
- draw_sentence = kw.get('draw_sentence', 1)
906
- self._fontsize = kw.get('fontsize', -12)
907
-
908
- # The chart!
909
- self._chart = chart
910
-
911
- # Callback functions
912
- self._callbacks = {}
913
-
914
- # Keep track of drawn edges
915
- self._edgelevels = []
916
- self._edgetags = {}
917
-
918
- # Keep track of which edges are marked.
919
- self._marks = {}
920
-
921
- # These are used to keep track of the set of tree tokens
922
- # currently displayed in the tree canvas.
923
- self._treetoks = []
924
- self._treetoks_edge = None
925
- self._treetoks_index = 0
926
-
927
- # Keep track of the tags used to draw the tree
928
- self._tree_tags = []
929
-
930
- # Put multiple edges on each level?
931
- self._compact = 0
932
-
933
- # If they didn't provide a main window, then set one up.
934
- if root is None:
935
- top = tkinter.Tk()
936
- top.title('Chart View')
937
- def destroy1(e, top=top): top.destroy()
938
- def destroy2(top=top): top.destroy()
939
- top.bind('q', destroy1)
940
- b = tkinter.Button(top, text='Done', command=destroy2)
941
- b.pack(side='bottom')
942
- self._root = top
943
- else:
944
- self._root = root
945
-
946
- # Create some fonts.
947
- self._init_fonts(root)
948
-
949
- # Create the chart canvas.
950
- (self._chart_sb, self._chart_canvas) = self._sb_canvas(self._root)
951
- self._chart_canvas['height'] = 300
952
- self._chart_canvas['closeenough'] = 15
953
-
954
- # Create the sentence canvas.
955
- if draw_sentence:
956
- cframe = tkinter.Frame(self._root, relief='sunk', border=2)
957
- cframe.pack(fill='both', side='bottom')
958
- self._sentence_canvas = tkinter.Canvas(cframe, height=50)
959
- self._sentence_canvas['background'] = '#e0e0e0'
960
- self._sentence_canvas.pack(fill='both')
961
- #self._sentence_canvas['height'] = self._sentence_height
962
- else:
963
- self._sentence_canvas = None
964
-
965
- # Create the tree canvas.
966
- if draw_tree:
967
- (sb, canvas) = self._sb_canvas(self._root, 'n', 'x')
968
- (self._tree_sb, self._tree_canvas) = (sb, canvas)
969
- self._tree_canvas['height'] = 200
970
- else:
971
- self._tree_canvas = None
972
-
973
- # Do some analysis to figure out how big the window should be
974
- self._analyze()
975
- self.draw()
976
- self._resize()
977
- self._grow()
978
-
979
- # Set up the configure callback, which will be called whenever
980
- # the window is resized.
981
- self._chart_canvas.bind('<Configure>', self._configure)
982
-
983
- def _init_fonts(self, root):
984
- self._boldfont = tkinter.font.Font(family='helvetica', weight='bold',
985
- size=self._fontsize)
986
- self._font = tkinter.font.Font(family='helvetica',
987
- size=self._fontsize)
988
- # See: <http://www.astro.washington.edu/owen/ROTKFolklore.html>
989
- self._sysfont = tkinter.font.Font(font=tkinter.Button()["font"])
990
- root.option_add("*Font", self._sysfont)
991
-
992
- def _sb_canvas(self, root, expand='y',
993
- fill='both', side='bottom'):
994
- """
995
- Helper for __init__: construct a canvas with a scrollbar.
996
- """
997
- cframe =tkinter.Frame(root, relief='sunk', border=2)
998
- cframe.pack(fill=fill, expand=expand, side=side)
999
- canvas = tkinter.Canvas(cframe, background='#e0e0e0')
1000
-
1001
- # Give the canvas a scrollbar.
1002
- sb = tkinter.Scrollbar(cframe, orient='vertical')
1003
- sb.pack(side='right', fill='y')
1004
- canvas.pack(side='left', fill=fill, expand='yes')
1005
-
1006
- # Connect the scrollbars to the canvas.
1007
- sb['command']= canvas.yview
1008
- canvas['yscrollcommand'] = sb.set
1009
-
1010
- return (sb, canvas)
1011
-
1012
- def scroll_up(self, *e):
1013
- self._chart_canvas.yview('scroll', -1, 'units')
1014
-
1015
- def scroll_down(self, *e):
1016
- self._chart_canvas.yview('scroll', 1, 'units')
1017
-
1018
- def page_up(self, *e):
1019
- self._chart_canvas.yview('scroll', -1, 'pages')
1020
-
1021
- def page_down(self, *e):
1022
- self._chart_canvas.yview('scroll', 1, 'pages')
1023
-
1024
- def _grow(self):
1025
- """
1026
- Grow the window, if necessary
1027
- """
1028
- # Grow, if need-be
1029
- N = self._chart.num_leaves()
1030
- width = max(int(self._chart_canvas['width']),
1031
- N * self._unitsize + ChartView._MARGIN * 2 )
1032
-
1033
- # It won't resize without the second (height) line, but I
1034
- # don't understand why not.
1035
- self._chart_canvas.configure(width=width)
1036
- self._chart_canvas.configure(height=self._chart_canvas['height'])
1037
-
1038
- self._unitsize = (width - 2*ChartView._MARGIN) / N
1039
-
1040
- # Reset the height for the sentence window.
1041
- if self._sentence_canvas is not None:
1042
- self._sentence_canvas['height'] = self._sentence_height
1043
-
1044
- def set_font_size(self, size):
1045
- self._font.configure(size=-abs(size))
1046
- self._boldfont.configure(size=-abs(size))
1047
- self._sysfont.configure(size=-abs(size))
1048
- self._analyze()
1049
- self._grow()
1050
- self.draw()
1051
-
1052
- def get_font_size(self):
1053
- return abs(self._fontsize)
1054
-
1055
- def _configure(self, e):
1056
- """
1057
- The configure callback. This is called whenever the window is
1058
- resized. It is also called when the window is first mapped.
1059
- It figures out the unit size, and redraws the contents of each
1060
- canvas.
1061
- """
1062
- N = self._chart.num_leaves()
1063
- self._unitsize = (e.width - 2*ChartView._MARGIN) / N
1064
- self.draw()
1065
-
1066
- def update(self, chart=None):
1067
- """
1068
- Draw any edges that have not been drawn. This is typically
1069
- called when a after modifies the canvas that a CanvasView is
1070
- displaying. ``update`` will cause any edges that have been
1071
- added to the chart to be drawn.
1072
-
1073
- If update is given a ``chart`` argument, then it will replace
1074
- the current chart with the given chart.
1075
- """
1076
- if chart is not None:
1077
- self._chart = chart
1078
- self._edgelevels = []
1079
- self._marks = {}
1080
- self._analyze()
1081
- self._grow()
1082
- self.draw()
1083
- self.erase_tree()
1084
- self._resize()
1085
- else:
1086
- for edge in self._chart:
1087
- if edge not in self._edgetags:
1088
- self._add_edge(edge)
1089
- self._resize()
1090
-
1091
-
1092
- def _edge_conflict(self, edge, lvl):
1093
- """
1094
- Return True if the given edge overlaps with any edge on the given
1095
- level. This is used by _add_edge to figure out what level a
1096
- new edge should be added to.
1097
- """
1098
- (s1, e1) = edge.span()
1099
- for otheredge in self._edgelevels[lvl]:
1100
- (s2, e2) = otheredge.span()
1101
- if (s1 <= s2 < e1) or (s2 <= s1 < e2) or (s1==s2==e1==e2):
1102
- return True
1103
- return False
1104
-
1105
- def _analyze_edge(self, edge):
1106
- """
1107
- Given a new edge, recalculate:
1108
-
1109
- - _text_height
1110
- - _unitsize (if the edge text is too big for the current
1111
- _unitsize, then increase _unitsize)
1112
- """
1113
- c = self._chart_canvas
1114
-
1115
- if isinstance(edge, TreeEdge):
1116
- lhs = edge.lhs()
1117
- rhselts = []
1118
- for elt in edge.rhs():
1119
- if isinstance(elt, Nonterminal):
1120
- rhselts.append(str(elt.symbol()))
1121
- else:
1122
- rhselts.append(repr(elt))
1123
- rhs = " ".join(rhselts)
1124
- else:
1125
- lhs = edge.lhs()
1126
- rhs = ''
1127
-
1128
- for s in (lhs, rhs):
1129
- tag = c.create_text(0,0, text=s,
1130
- font=self._boldfont,
1131
- anchor='nw', justify='left')
1132
- bbox = c.bbox(tag)
1133
- c.delete(tag)
1134
- width = bbox[2] #+ ChartView._LEAF_SPACING
1135
- edgelen = max(edge.length(), 1)
1136
- self._unitsize = max(self._unitsize, width/edgelen)
1137
- self._text_height = max(self._text_height, bbox[3] - bbox[1])
1138
-
1139
- def _add_edge(self, edge, minlvl=0):
1140
- """
1141
- Add a single edge to the ChartView:
1142
-
1143
- - Call analyze_edge to recalculate display parameters
1144
- - Find an available level
1145
- - Call _draw_edge
1146
- """
1147
- # Do NOT show leaf edges in the chart.
1148
- if isinstance(edge, LeafEdge): return
1149
-
1150
- if edge in self._edgetags: return
1151
- self._analyze_edge(edge)
1152
- self._grow()
1153
-
1154
- if not self._compact:
1155
- self._edgelevels.append([edge])
1156
- lvl = len(self._edgelevels)-1
1157
- self._draw_edge(edge, lvl)
1158
- self._resize()
1159
- return
1160
-
1161
- # Figure out what level to draw the edge on.
1162
- lvl = 0
1163
- while True:
1164
- # If this level doesn't exist yet, create it.
1165
- while lvl >= len(self._edgelevels):
1166
- self._edgelevels.append([])
1167
- self._resize()
1168
-
1169
- # Check if we can fit the edge in this level.
1170
- if lvl>=minlvl and not self._edge_conflict(edge, lvl):
1171
- # Go ahead and draw it.
1172
- self._edgelevels[lvl].append(edge)
1173
- break
1174
-
1175
- # Try the next level.
1176
- lvl += 1
1177
-
1178
- self._draw_edge(edge, lvl)
1179
-
1180
- def view_edge(self, edge):
1181
- level = None
1182
- for i in range(len(self._edgelevels)):
1183
- if edge in self._edgelevels[i]:
1184
- level = i
1185
- break
1186
- if level is None: return
1187
- # Try to view the new edge..
1188
- y = (level+1) * self._chart_level_size
1189
- dy = self._text_height + 10
1190
- self._chart_canvas.yview('moveto', 1.0)
1191
- if self._chart_height != 0:
1192
- self._chart_canvas.yview('moveto',
1193
- (y-dy)/self._chart_height)
1194
-
1195
- def _draw_edge(self, edge, lvl):
1196
- """
1197
- Draw a single edge on the ChartView.
1198
- """
1199
- c = self._chart_canvas
1200
-
1201
- # Draw the arrow.
1202
- x1 = (edge.start() * self._unitsize + ChartView._MARGIN)
1203
- x2 = (edge.end() * self._unitsize + ChartView._MARGIN)
1204
- if x2 == x1: x2 += max(4, self._unitsize/5)
1205
- y = (lvl+1) * self._chart_level_size
1206
- linetag = c.create_line(x1, y, x2, y, arrow='last', width=3)
1207
-
1208
- # Draw a label for the edge.
1209
- if isinstance(edge, TreeEdge):
1210
- rhs = []
1211
- for elt in edge.rhs():
1212
- if isinstance(elt, Nonterminal):
1213
- rhs.append(str(elt.symbol()))
1214
- else:
1215
- rhs.append(repr(elt))
1216
- pos = edge.dot()
1217
- else:
1218
- rhs = []
1219
- pos = 0
1220
-
1221
- rhs1 = " ".join(rhs[:pos])
1222
- rhs2 = " ".join(rhs[pos:])
1223
- rhstag1 = c.create_text(x1+3, y, text=rhs1,
1224
- font=self._font,
1225
- anchor='nw')
1226
- dotx = c.bbox(rhstag1)[2] + 6
1227
- doty = (c.bbox(rhstag1)[1]+c.bbox(rhstag1)[3])/2
1228
- dottag = c.create_oval(dotx-2, doty-2, dotx+2, doty+2)
1229
- rhstag2 = c.create_text(dotx+6, y, text=rhs2,
1230
- font=self._font,
1231
- anchor='nw')
1232
- lhstag = c.create_text((x1+x2)/2, y, text=str(edge.lhs()),
1233
- anchor='s',
1234
- font=self._boldfont)
1235
-
1236
- # Keep track of the edge's tags.
1237
- self._edgetags[edge] = (linetag, rhstag1,
1238
- dottag, rhstag2, lhstag)
1239
-
1240
- # Register a callback for clicking on the edge.
1241
- def cb(event, self=self, edge=edge):
1242
- self._fire_callbacks('select', edge)
1243
- c.tag_bind(rhstag1, '<Button-1>', cb)
1244
- c.tag_bind(rhstag2, '<Button-1>', cb)
1245
- c.tag_bind(linetag, '<Button-1>', cb)
1246
- c.tag_bind(dottag, '<Button-1>', cb)
1247
- c.tag_bind(lhstag, '<Button-1>', cb)
1248
-
1249
- self._color_edge(edge)
1250
-
1251
- def _color_edge(self, edge, linecolor=None, textcolor=None):
1252
- """
1253
- Color in an edge with the given colors.
1254
- If no colors are specified, use intelligent defaults
1255
- (dependent on selection, etc.)
1256
- """
1257
- if edge not in self._edgetags: return
1258
- c = self._chart_canvas
1259
-
1260
- if linecolor is not None and textcolor is not None:
1261
- if edge in self._marks:
1262
- linecolor = self._marks[edge]
1263
- tags = self._edgetags[edge]
1264
- c.itemconfig(tags[0], fill=linecolor)
1265
- c.itemconfig(tags[1], fill=textcolor)
1266
- c.itemconfig(tags[2], fill=textcolor,
1267
- outline=textcolor)
1268
- c.itemconfig(tags[3], fill=textcolor)
1269
- c.itemconfig(tags[4], fill=textcolor)
1270
- return
1271
- else:
1272
- N = self._chart.num_leaves()
1273
- if edge in self._marks:
1274
- self._color_edge(self._marks[edge])
1275
- if (edge.is_complete() and edge.span() == (0, N)):
1276
- self._color_edge(edge, '#084', '#042')
1277
- elif isinstance(edge, LeafEdge):
1278
- self._color_edge(edge, '#48c', '#246')
1279
- else:
1280
- self._color_edge(edge, '#00f', '#008')
1281
-
1282
- def mark_edge(self, edge, mark='#0df'):
1283
- """
1284
- Mark an edge
1285
- """
1286
- self._marks[edge] = mark
1287
- self._color_edge(edge)
1288
-
1289
- def unmark_edge(self, edge=None):
1290
- """
1291
- Unmark an edge (or all edges)
1292
- """
1293
- if edge is None:
1294
- old_marked_edges = list(self._marks.keys())
1295
- self._marks = {}
1296
- for edge in old_marked_edges:
1297
- self._color_edge(edge)
1298
- else:
1299
- del self._marks[edge]
1300
- self._color_edge(edge)
1301
-
1302
- def markonly_edge(self, edge, mark='#0df'):
1303
- self.unmark_edge()
1304
- self.mark_edge(edge, mark)
1305
-
1306
- def _analyze(self):
1307
- """
1308
- Analyze the sentence string, to figure out how big a unit needs
1309
- to be, How big the tree should be, etc.
1310
- """
1311
- # Figure out the text height and the unit size.
1312
- unitsize = 70 # min unitsize
1313
- text_height = 0
1314
- c = self._chart_canvas
1315
-
1316
- # Check against all tokens
1317
- for leaf in self._chart.leaves():
1318
- tag = c.create_text(0,0, text=repr(leaf),
1319
- font=self._font,
1320
- anchor='nw', justify='left')
1321
- bbox = c.bbox(tag)
1322
- c.delete(tag)
1323
- width = bbox[2] + ChartView._LEAF_SPACING
1324
- unitsize = max(width, unitsize)
1325
- text_height = max(text_height, bbox[3] - bbox[1])
1326
-
1327
- self._unitsize = unitsize
1328
- self._text_height = text_height
1329
- self._sentence_height = (self._text_height +
1330
- 2*ChartView._MARGIN)
1331
-
1332
- # Check against edges.
1333
- for edge in self._chart.edges():
1334
- self._analyze_edge(edge)
1335
-
1336
- # Size of chart levels
1337
- self._chart_level_size = self._text_height * 2
1338
-
1339
- # Default tree size..
1340
- self._tree_height = (3 * (ChartView._TREE_LEVEL_SIZE +
1341
- self._text_height))
1342
-
1343
- # Resize the scrollregions.
1344
- self._resize()
1345
-
1346
- def _resize(self):
1347
- """
1348
- Update the scroll-regions for each canvas. This ensures that
1349
- everything is within a scroll-region, so the user can use the
1350
- scrollbars to view the entire display. This does *not*
1351
- resize the window.
1352
- """
1353
- c = self._chart_canvas
1354
-
1355
- # Reset the chart scroll region
1356
- width = ( self._chart.num_leaves() * self._unitsize +
1357
- ChartView._MARGIN * 2 )
1358
-
1359
- levels = len(self._edgelevels)
1360
- self._chart_height = (levels+2)*self._chart_level_size
1361
- c['scrollregion']=(0,0,width,self._chart_height)
1362
-
1363
- # Reset the tree scroll region
1364
- if self._tree_canvas:
1365
- self._tree_canvas['scrollregion'] = (0, 0, width,
1366
- self._tree_height)
1367
-
1368
- def _draw_loclines(self):
1369
- """
1370
- Draw location lines. These are vertical gridlines used to
1371
- show where each location unit is.
1372
- """
1373
- BOTTOM = 50000
1374
- c1 = self._tree_canvas
1375
- c2 = self._sentence_canvas
1376
- c3 = self._chart_canvas
1377
- margin = ChartView._MARGIN
1378
- self._loclines = []
1379
- for i in range(0, self._chart.num_leaves()+1):
1380
- x = i*self._unitsize + margin
1381
-
1382
- if c1:
1383
- t1=c1.create_line(x, 0, x, BOTTOM)
1384
- c1.tag_lower(t1)
1385
- if c2:
1386
- t2=c2.create_line(x, 0, x, self._sentence_height)
1387
- c2.tag_lower(t2)
1388
- t3=c3.create_line(x, 0, x, BOTTOM)
1389
- c3.tag_lower(t3)
1390
- t4=c3.create_text(x+2, 0, text=repr(i), anchor='nw',
1391
- font=self._font)
1392
- c3.tag_lower(t4)
1393
- #if i % 4 == 0:
1394
- # if c1: c1.itemconfig(t1, width=2, fill='gray60')
1395
- # if c2: c2.itemconfig(t2, width=2, fill='gray60')
1396
- # c3.itemconfig(t3, width=2, fill='gray60')
1397
- if i % 2 == 0:
1398
- if c1: c1.itemconfig(t1, fill='gray60')
1399
- if c2: c2.itemconfig(t2, fill='gray60')
1400
- c3.itemconfig(t3, fill='gray60')
1401
- else:
1402
- if c1: c1.itemconfig(t1, fill='gray80')
1403
- if c2: c2.itemconfig(t2, fill='gray80')
1404
- c3.itemconfig(t3, fill='gray80')
1405
-
1406
- def _draw_sentence(self):
1407
- """Draw the sentence string."""
1408
- if self._chart.num_leaves() == 0: return
1409
- c = self._sentence_canvas
1410
- margin = ChartView._MARGIN
1411
- y = ChartView._MARGIN
1412
-
1413
- for i, leaf in enumerate(self._chart.leaves()):
1414
- x1 = i * self._unitsize + margin
1415
- x2 = x1 + self._unitsize
1416
- x = (x1+x2)/2
1417
- tag = c.create_text(x, y, text=repr(leaf),
1418
- font=self._font,
1419
- anchor='n', justify='left')
1420
- bbox = c.bbox(tag)
1421
- rt=c.create_rectangle(x1+2, bbox[1]-(ChartView._LEAF_SPACING/2),
1422
- x2-2, bbox[3]+(ChartView._LEAF_SPACING/2),
1423
- fill='#f0f0f0', outline='#f0f0f0')
1424
- c.tag_lower(rt)
1425
-
1426
- def erase_tree(self):
1427
- for tag in self._tree_tags: self._tree_canvas.delete(tag)
1428
- self._treetoks = []
1429
- self._treetoks_edge = None
1430
- self._treetoks_index = 0
1431
-
1432
- def draw_tree(self, edge=None):
1433
- if edge is None and self._treetoks_edge is None: return
1434
- if edge is None: edge = self._treetoks_edge
1435
-
1436
- # If it's a new edge, then get a new list of treetoks.
1437
- if self._treetoks_edge != edge:
1438
- self._treetoks = [t for t in self._chart.trees(edge)
1439
- if isinstance(t, Tree)]
1440
- self._treetoks_edge = edge
1441
- self._treetoks_index = 0
1442
-
1443
- # Make sure there's something to draw.
1444
- if len(self._treetoks) == 0: return
1445
-
1446
- # Erase the old tree.
1447
- for tag in self._tree_tags: self._tree_canvas.delete(tag)
1448
-
1449
- # Draw the new tree.
1450
- tree = self._treetoks[self._treetoks_index]
1451
- self._draw_treetok(tree, edge.start())
1452
-
1453
- # Show how many trees are available for the edge.
1454
- self._draw_treecycle()
1455
-
1456
- # Update the scroll region.
1457
- w = self._chart.num_leaves()*self._unitsize+2*ChartView._MARGIN
1458
- h = tree.height() * (ChartView._TREE_LEVEL_SIZE+self._text_height)
1459
- self._tree_canvas['scrollregion'] = (0, 0, w, h)
1460
-
1461
- def cycle_tree(self):
1462
- self._treetoks_index = (self._treetoks_index+1)%len(self._treetoks)
1463
- self.draw_tree(self._treetoks_edge)
1464
-
1465
- def _draw_treecycle(self):
1466
- if len(self._treetoks) <= 1: return
1467
-
1468
- # Draw the label.
1469
- label = '%d Trees' % len(self._treetoks)
1470
- c = self._tree_canvas
1471
- margin = ChartView._MARGIN
1472
- right = self._chart.num_leaves()*self._unitsize+margin-2
1473
- tag = c.create_text(right, 2, anchor='ne', text=label,
1474
- font=self._boldfont)
1475
- self._tree_tags.append(tag)
1476
- _, _, _, y = c.bbox(tag)
1477
-
1478
- # Draw the triangles.
1479
- for i in range(len(self._treetoks)):
1480
- x = right - 20*(len(self._treetoks)-i-1)
1481
- if i == self._treetoks_index: fill = '#084'
1482
- else: fill = '#fff'
1483
- tag = c.create_polygon(x, y+10, x-5, y, x-10, y+10,
1484
- fill=fill, outline='black')
1485
- self._tree_tags.append(tag)
1486
-
1487
- # Set up a callback: show the tree if they click on its
1488
- # triangle.
1489
- def cb(event, self=self, i=i):
1490
- self._treetoks_index = i
1491
- self.draw_tree()
1492
- c.tag_bind(tag, '<Button-1>', cb)
1493
-
1494
- def _draw_treetok(self, treetok, index, depth=0):
1495
- """
1496
- :param index: The index of the first leaf in the tree.
1497
- :return: The index of the first leaf after the tree.
1498
- """
1499
- c = self._tree_canvas
1500
- margin = ChartView._MARGIN
1501
-
1502
- # Draw the children
1503
- child_xs = []
1504
- for child in treetok:
1505
- if isinstance(child, Tree):
1506
- child_x, index = self._draw_treetok(child, index, depth+1)
1507
- child_xs.append(child_x)
1508
- else:
1509
- child_xs.append((2*index+1)*self._unitsize/2 + margin)
1510
- index += 1
1511
-
1512
- # If we have children, then get the node's x by averaging their
1513
- # node x's. Otherwise, make room for ourselves.
1514
- if child_xs:
1515
- nodex = sum(child_xs)/len(child_xs)
1516
- else:
1517
- # [XX] breaks for null productions.
1518
- nodex = (2*index+1)*self._unitsize/2 + margin
1519
- index += 1
1520
-
1521
- # Draw the node
1522
- nodey = depth * (ChartView._TREE_LEVEL_SIZE + self._text_height)
1523
- tag = c.create_text(nodex, nodey, anchor='n', justify='center',
1524
- text=str(treetok.label()), fill='#042',
1525
- font=self._boldfont)
1526
- self._tree_tags.append(tag)
1527
-
1528
- # Draw lines to the children.
1529
- childy = nodey + ChartView._TREE_LEVEL_SIZE + self._text_height
1530
- for childx, child in zip(child_xs, treetok):
1531
- if isinstance(child, Tree) and child:
1532
- # A "real" tree token:
1533
- tag = c.create_line(nodex, nodey + self._text_height,
1534
- childx, childy, width=2, fill='#084')
1535
- self._tree_tags.append(tag)
1536
- if isinstance(child, Tree) and not child:
1537
- # An unexpanded tree token:
1538
- tag = c.create_line(nodex, nodey + self._text_height,
1539
- childx, childy, width=2,
1540
- fill='#048', dash='2 3')
1541
- self._tree_tags.append(tag)
1542
- if not isinstance(child, Tree):
1543
- # A leaf:
1544
- tag = c.create_line(nodex, nodey + self._text_height,
1545
- childx, 10000, width=2, fill='#084')
1546
- self._tree_tags.append(tag)
1547
-
1548
- return nodex, index
1549
-
1550
- def draw(self):
1551
- """
1552
- Draw everything (from scratch).
1553
- """
1554
- if self._tree_canvas:
1555
- self._tree_canvas.delete('all')
1556
- self.draw_tree()
1557
-
1558
- if self._sentence_canvas:
1559
- self._sentence_canvas.delete('all')
1560
- self._draw_sentence()
1561
-
1562
- self._chart_canvas.delete('all')
1563
- self._edgetags = {}
1564
-
1565
- # Redraw any edges we erased.
1566
- for lvl in range(len(self._edgelevels)):
1567
- for edge in self._edgelevels[lvl]:
1568
- self._draw_edge(edge, lvl)
1569
-
1570
- for edge in self._chart:
1571
- self._add_edge(edge)
1572
-
1573
- self._draw_loclines()
1574
-
1575
- def add_callback(self, event, func):
1576
- self._callbacks.setdefault(event,{})[func] = 1
1577
-
1578
- def remove_callback(self, event, func=None):
1579
- if func is None: del self._callbacks[event]
1580
- else:
1581
- try: del self._callbacks[event][func]
1582
- except: pass
1583
-
1584
- def _fire_callbacks(self, event, *args):
1585
- if event not in self._callbacks: return
1586
- for cb_func in list(self._callbacks[event].keys()): cb_func(*args)
1587
-
1588
- #######################################################################
1589
- # Edge Rules
1590
- #######################################################################
1591
- # These version of the chart rules only apply to a specific edge.
1592
- # This lets the user select an edge, and then apply a rule.
1593
-
1594
- class EdgeRule(object):
1595
- """
1596
- To create an edge rule, make an empty base class that uses
1597
- EdgeRule as the first base class, and the basic rule as the
1598
- second base class. (Order matters!)
1599
- """
1600
- def __init__(self, edge):
1601
- super = self.__class__.__bases__[1]
1602
- self._edge = edge
1603
- self.NUM_EDGES = super.NUM_EDGES-1
1604
- def apply(self, chart, grammar, *edges):
1605
- super = self.__class__.__bases__[1]
1606
- edges += (self._edge,)
1607
- for e in super.apply(self, chart, grammar, *edges): yield e
1608
- def __str__(self):
1609
- super = self.__class__.__bases__[1]
1610
- return super.__str__(self)
1611
-
1612
- class TopDownPredictEdgeRule(EdgeRule, TopDownPredictRule):
1613
- pass
1614
- class BottomUpEdgeRule(EdgeRule, BottomUpPredictRule):
1615
- pass
1616
- class BottomUpLeftCornerEdgeRule(EdgeRule, BottomUpPredictCombineRule):
1617
- pass
1618
- class FundamentalEdgeRule(EdgeRule, SingleEdgeFundamentalRule):
1619
- pass
1620
-
1621
- #######################################################################
1622
- # Chart Parser Application
1623
- #######################################################################
1624
-
1625
- class ChartParserApp(object):
1626
- def __init__(self, grammar, tokens, title='Chart Parser Application'):
1627
- # Initialize the parser
1628
- self._init_parser(grammar, tokens)
1629
-
1630
- self._root = None
1631
- try:
1632
- # Create the root window.
1633
- self._root = tkinter.Tk()
1634
- self._root.title(title)
1635
- self._root.bind('<Control-q>', self.destroy)
1636
-
1637
- # Set up some frames.
1638
- frame3 = tkinter.Frame(self._root)
1639
- frame2 = tkinter.Frame(self._root)
1640
- frame1 = tkinter.Frame(self._root)
1641
- frame3.pack(side='bottom', fill='none')
1642
- frame2.pack(side='bottom', fill='x')
1643
- frame1.pack(side='bottom', fill='both', expand=1)
1644
-
1645
- self._init_fonts(self._root)
1646
- self._init_animation()
1647
- self._init_chartview(frame1)
1648
- self._init_rulelabel(frame2)
1649
- self._init_buttons(frame3)
1650
- self._init_menubar()
1651
-
1652
- self._matrix = None
1653
- self._results = None
1654
-
1655
- # Set up keyboard bindings.
1656
- self._init_bindings()
1657
-
1658
- except:
1659
- print('Error creating Tree View')
1660
- self.destroy()
1661
- raise
1662
-
1663
- def destroy(self, *args):
1664
- if self._root is None: return
1665
- self._root.destroy()
1666
- self._root = None
1667
-
1668
- def mainloop(self, *args, **kwargs):
1669
- """
1670
- Enter the Tkinter mainloop. This function must be called if
1671
- this demo is created from a non-interactive program (e.g.
1672
- from a secript); otherwise, the demo will close as soon as
1673
- the script completes.
1674
- """
1675
- if in_idle(): return
1676
- self._root.mainloop(*args, **kwargs)
1677
-
1678
- #////////////////////////////////////////////////////////////
1679
- # Initialization Helpers
1680
- #////////////////////////////////////////////////////////////
1681
-
1682
- def _init_parser(self, grammar, tokens):
1683
- self._grammar = grammar
1684
- self._tokens = tokens
1685
- self._reset_parser()
1686
-
1687
- def _reset_parser(self):
1688
- self._cp = SteppingChartParser(self._grammar)
1689
- self._cp.initialize(self._tokens)
1690
- self._chart = self._cp.chart()
1691
-
1692
- # Insert LeafEdges before the parsing starts.
1693
- for _new_edge in LeafInitRule().apply(self._chart, self._grammar):
1694
- pass
1695
-
1696
- # The step iterator -- use this to generate new edges
1697
- self._cpstep = self._cp.step()
1698
-
1699
- # The currently selected edge
1700
- self._selection = None
1701
-
1702
- def _init_fonts(self, root):
1703
- # See: <http://www.astro.washington.edu/owen/ROTKFolklore.html>
1704
- self._sysfont = tkinter.font.Font(font=tkinter.Button()["font"])
1705
- root.option_add("*Font", self._sysfont)
1706
-
1707
- # TWhat's our font size (default=same as sysfont)
1708
- self._size = tkinter.IntVar(root)
1709
- self._size.set(self._sysfont.cget('size'))
1710
-
1711
- self._boldfont = tkinter.font.Font(family='helvetica', weight='bold',
1712
- size=self._size.get())
1713
- self._font = tkinter.font.Font(family='helvetica',
1714
- size=self._size.get())
1715
-
1716
- def _init_animation(self):
1717
- # Are we stepping? (default=yes)
1718
- self._step = tkinter.IntVar(self._root)
1719
- self._step.set(1)
1720
-
1721
- # What's our animation speed (default=fast)
1722
- self._animate = tkinter.IntVar(self._root)
1723
- self._animate.set(3) # Default speed = fast
1724
-
1725
- # Are we currently animating?
1726
- self._animating = 0
1727
-
1728
- def _init_chartview(self, parent):
1729
- self._cv = ChartView(self._chart, parent,
1730
- draw_tree=1, draw_sentence=1)
1731
- self._cv.add_callback('select', self._click_cv_edge)
1732
-
1733
- def _init_rulelabel(self, parent):
1734
- ruletxt = 'Last edge generated by:'
1735
-
1736
- self._rulelabel1 = tkinter.Label(parent,text=ruletxt,
1737
- font=self._boldfont)
1738
- self._rulelabel2 = tkinter.Label(parent, width=40,
1739
- relief='groove', anchor='w',
1740
- font=self._boldfont)
1741
- self._rulelabel1.pack(side='left')
1742
- self._rulelabel2.pack(side='left')
1743
- step = tkinter.Checkbutton(parent, variable=self._step,
1744
- text='Step')
1745
- step.pack(side='right')
1746
-
1747
- def _init_buttons(self, parent):
1748
- frame1 = tkinter.Frame(parent)
1749
- frame2 = tkinter.Frame(parent)
1750
- frame1.pack(side='bottom', fill='x')
1751
- frame2.pack(side='top', fill='none')
1752
-
1753
- tkinter.Button(frame1, text='Reset\nParser',
1754
- background='#90c0d0', foreground='black',
1755
- command=self.reset).pack(side='right')
1756
- #Tkinter.Button(frame1, text='Pause',
1757
- # background='#90c0d0', foreground='black',
1758
- # command=self.pause).pack(side='left')
1759
-
1760
- tkinter.Button(frame1, text='Top Down\nStrategy',
1761
- background='#90c0d0', foreground='black',
1762
- command=self.top_down_strategy).pack(side='left')
1763
- tkinter.Button(frame1, text='Bottom Up\nStrategy',
1764
- background='#90c0d0', foreground='black',
1765
- command=self.bottom_up_strategy).pack(side='left')
1766
- tkinter.Button(frame1, text='Bottom Up\nLeft-Corner Strategy',
1767
- background='#90c0d0', foreground='black',
1768
- command=self.bottom_up_leftcorner_strategy).pack(side='left')
1769
-
1770
- tkinter.Button(frame2, text='Top Down Init\nRule',
1771
- background='#90f090', foreground='black',
1772
- command=self.top_down_init).pack(side='left')
1773
- tkinter.Button(frame2, text='Top Down Predict\nRule',
1774
- background='#90f090', foreground='black',
1775
- command=self.top_down_predict).pack(side='left')
1776
- tkinter.Frame(frame2, width=20).pack(side='left')
1777
-
1778
- tkinter.Button(frame2, text='Bottom Up Predict\nRule',
1779
- background='#90f090', foreground='black',
1780
- command=self.bottom_up).pack(side='left')
1781
- tkinter.Frame(frame2, width=20).pack(side='left')
1782
-
1783
- tkinter.Button(frame2, text='Bottom Up Left-Corner\nPredict Rule',
1784
- background='#90f090', foreground='black',
1785
- command=self.bottom_up_leftcorner).pack(side='left')
1786
- tkinter.Frame(frame2, width=20).pack(side='left')
1787
-
1788
- tkinter.Button(frame2, text='Fundamental\nRule',
1789
- background='#90f090', foreground='black',
1790
- command=self.fundamental).pack(side='left')
1791
-
1792
- def _init_bindings(self):
1793
- self._root.bind('<Up>', self._cv.scroll_up)
1794
- self._root.bind('<Down>', self._cv.scroll_down)
1795
- self._root.bind('<Prior>', self._cv.page_up)
1796
- self._root.bind('<Next>', self._cv.page_down)
1797
- self._root.bind('<Control-q>', self.destroy)
1798
- self._root.bind('<Control-x>', self.destroy)
1799
- self._root.bind('<F1>', self.help)
1800
-
1801
- self._root.bind('<Control-s>', self.save_chart)
1802
- self._root.bind('<Control-o>', self.load_chart)
1803
- self._root.bind('<Control-r>', self.reset)
1804
-
1805
- self._root.bind('t', self.top_down_strategy)
1806
- self._root.bind('b', self.bottom_up_strategy)
1807
- self._root.bind('c', self.bottom_up_leftcorner_strategy)
1808
- self._root.bind('<space>', self._stop_animation)
1809
-
1810
- self._root.bind('<Control-g>', self.edit_grammar)
1811
- self._root.bind('<Control-t>', self.edit_sentence)
1812
-
1813
- # Animation speed control
1814
- self._root.bind('-', lambda e,a=self._animate:a.set(1))
1815
- self._root.bind('=', lambda e,a=self._animate:a.set(2))
1816
- self._root.bind('+', lambda e,a=self._animate:a.set(3))
1817
-
1818
- # Step control
1819
- self._root.bind('s', lambda e,s=self._step:s.set(not s.get()))
1820
-
1821
- def _init_menubar(self):
1822
- menubar = tkinter.Menu(self._root)
1823
-
1824
- filemenu = tkinter.Menu(menubar, tearoff=0)
1825
- filemenu.add_command(label='Save Chart', underline=0,
1826
- command=self.save_chart, accelerator='Ctrl-s')
1827
- filemenu.add_command(label='Load Chart', underline=0,
1828
- command=self.load_chart, accelerator='Ctrl-o')
1829
- filemenu.add_command(label='Reset Chart', underline=0,
1830
- command=self.reset, accelerator='Ctrl-r')
1831
- filemenu.add_separator()
1832
- filemenu.add_command(label='Save Grammar',
1833
- command=self.save_grammar)
1834
- filemenu.add_command(label='Load Grammar',
1835
- command=self.load_grammar)
1836
- filemenu.add_separator()
1837
- filemenu.add_command(label='Exit', underline=1,
1838
- command=self.destroy, accelerator='Ctrl-x')
1839
- menubar.add_cascade(label='File', underline=0, menu=filemenu)
1840
-
1841
- editmenu = tkinter.Menu(menubar, tearoff=0)
1842
- editmenu.add_command(label='Edit Grammar', underline=5,
1843
- command=self.edit_grammar,
1844
- accelerator='Ctrl-g')
1845
- editmenu.add_command(label='Edit Text', underline=5,
1846
- command=self.edit_sentence,
1847
- accelerator='Ctrl-t')
1848
- menubar.add_cascade(label='Edit', underline=0, menu=editmenu)
1849
-
1850
- viewmenu = tkinter.Menu(menubar, tearoff=0)
1851
- viewmenu.add_command(label='Chart Matrix', underline=6,
1852
- command=self.view_matrix)
1853
- viewmenu.add_command(label='Results', underline=0,
1854
- command=self.view_results)
1855
- menubar.add_cascade(label='View', underline=0, menu=viewmenu)
1856
-
1857
- rulemenu = tkinter.Menu(menubar, tearoff=0)
1858
- rulemenu.add_command(label='Top Down Strategy', underline=0,
1859
- command=self.top_down_strategy,
1860
- accelerator='t')
1861
- rulemenu.add_command(label='Bottom Up Strategy', underline=0,
1862
- command=self.bottom_up_strategy,
1863
- accelerator='b')
1864
- rulemenu.add_command(label='Bottom Up Left-Corner Strategy', underline=0,
1865
- command=self.bottom_up_leftcorner_strategy,
1866
- accelerator='c')
1867
- rulemenu.add_separator()
1868
- rulemenu.add_command(label='Bottom Up Rule',
1869
- command=self.bottom_up)
1870
- rulemenu.add_command(label='Bottom Up Left-Corner Rule',
1871
- command=self.bottom_up_leftcorner)
1872
- rulemenu.add_command(label='Top Down Init Rule',
1873
- command=self.top_down_init)
1874
- rulemenu.add_command(label='Top Down Predict Rule',
1875
- command=self.top_down_predict)
1876
- rulemenu.add_command(label='Fundamental Rule',
1877
- command=self.fundamental)
1878
- menubar.add_cascade(label='Apply', underline=0, menu=rulemenu)
1879
-
1880
- animatemenu = tkinter.Menu(menubar, tearoff=0)
1881
- animatemenu.add_checkbutton(label="Step", underline=0,
1882
- variable=self._step,
1883
- accelerator='s')
1884
- animatemenu.add_separator()
1885
- animatemenu.add_radiobutton(label="No Animation", underline=0,
1886
- variable=self._animate, value=0)
1887
- animatemenu.add_radiobutton(label="Slow Animation", underline=0,
1888
- variable=self._animate, value=1,
1889
- accelerator='-')
1890
- animatemenu.add_radiobutton(label="Normal Animation", underline=0,
1891
- variable=self._animate, value=2,
1892
- accelerator='=')
1893
- animatemenu.add_radiobutton(label="Fast Animation", underline=0,
1894
- variable=self._animate, value=3,
1895
- accelerator='+')
1896
- menubar.add_cascade(label="Animate", underline=1, menu=animatemenu)
1897
-
1898
- zoommenu = tkinter.Menu(menubar, tearoff=0)
1899
- zoommenu.add_radiobutton(label='Tiny', variable=self._size,
1900
- underline=0, value=10, command=self.resize)
1901
- zoommenu.add_radiobutton(label='Small', variable=self._size,
1902
- underline=0, value=12, command=self.resize)
1903
- zoommenu.add_radiobutton(label='Medium', variable=self._size,
1904
- underline=0, value=14, command=self.resize)
1905
- zoommenu.add_radiobutton(label='Large', variable=self._size,
1906
- underline=0, value=18, command=self.resize)
1907
- zoommenu.add_radiobutton(label='Huge', variable=self._size,
1908
- underline=0, value=24, command=self.resize)
1909
- menubar.add_cascade(label='Zoom', underline=0, menu=zoommenu)
1910
-
1911
- helpmenu = tkinter.Menu(menubar, tearoff=0)
1912
- helpmenu.add_command(label='About', underline=0,
1913
- command=self.about)
1914
- helpmenu.add_command(label='Instructions', underline=0,
1915
- command=self.help, accelerator='F1')
1916
- menubar.add_cascade(label='Help', underline=0, menu=helpmenu)
1917
-
1918
- self._root.config(menu=menubar)
1919
-
1920
- #////////////////////////////////////////////////////////////
1921
- # Selection Handling
1922
- #////////////////////////////////////////////////////////////
1923
-
1924
- def _click_cv_edge(self, edge):
1925
- if edge != self._selection:
1926
- # Clicking on a new edge selects it.
1927
- self._select_edge(edge)
1928
- else:
1929
- # Repeated clicks on one edge cycle its trees.
1930
- self._cv.cycle_tree()
1931
- # [XX] this can get confused if animation is running
1932
- # faster than the callbacks...
1933
-
1934
- def _select_matrix_edge(self, edge):
1935
- self._select_edge(edge)
1936
- self._cv.view_edge(edge)
1937
-
1938
- def _select_edge(self, edge):
1939
- self._selection = edge
1940
- # Update the chart view.
1941
- self._cv.markonly_edge(edge, '#f00')
1942
- self._cv.draw_tree(edge)
1943
- # Update the matrix view.
1944
- if self._matrix: self._matrix.markonly_edge(edge)
1945
- if self._matrix: self._matrix.view_edge(edge)
1946
-
1947
- def _deselect_edge(self):
1948
- self._selection = None
1949
- # Update the chart view.
1950
- self._cv.unmark_edge()
1951
- self._cv.erase_tree()
1952
- # Update the matrix view
1953
- if self._matrix: self._matrix.unmark_edge()
1954
-
1955
- def _show_new_edge(self, edge):
1956
- self._display_rule(self._cp.current_chartrule())
1957
- # Update the chart view.
1958
- self._cv.update()
1959
- self._cv.draw_tree(edge)
1960
- self._cv.markonly_edge(edge, '#0df')
1961
- self._cv.view_edge(edge)
1962
- # Update the matrix view.
1963
- if self._matrix: self._matrix.update()
1964
- if self._matrix: self._matrix.markonly_edge(edge)
1965
- if self._matrix: self._matrix.view_edge(edge)
1966
- # Update the results view.
1967
- if self._results: self._results.update(edge)
1968
-
1969
- #////////////////////////////////////////////////////////////
1970
- # Help/usage
1971
- #////////////////////////////////////////////////////////////
1972
-
1973
- def help(self, *e):
1974
- self._animating = 0
1975
- # The default font's not very legible; try using 'fixed' instead.
1976
- try:
1977
- ShowText(self._root, 'Help: Chart Parser Application',
1978
- (__doc__ or '').strip(), width=75, font='fixed')
1979
- except:
1980
- ShowText(self._root, 'Help: Chart Parser Application',
1981
- (__doc__ or '').strip(), width=75)
1982
-
1983
- def about(self, *e):
1984
- ABOUT = ("NLTK Chart Parser Application\n"+
1985
- "Written by Edward Loper")
1986
- tkinter.messagebox.showinfo('About: Chart Parser Application', ABOUT)
1987
-
1988
- #////////////////////////////////////////////////////////////
1989
- # File Menu
1990
- #////////////////////////////////////////////////////////////
1991
-
1992
- CHART_FILE_TYPES = [('Pickle file', '.pickle'),
1993
- ('All files', '*')]
1994
- GRAMMAR_FILE_TYPES = [('Plaintext grammar file', '.cfg'),
1995
- ('Pickle file', '.pickle'),
1996
- ('All files', '*')]
1997
-
1998
- def load_chart(self, *args):
1999
- "Load a chart from a pickle file"
2000
- filename = askopenfilename(filetypes=self.CHART_FILE_TYPES,
2001
- defaultextension='.pickle')
2002
- if not filename: return
2003
- try:
2004
- with open(filename, 'rb') as infile:
2005
- chart = pickle.load(infile)
2006
- self._chart = chart
2007
- self._cv.update(chart)
2008
- if self._matrix: self._matrix.set_chart(chart)
2009
- if self._matrix: self._matrix.deselect_cell()
2010
- if self._results: self._results.set_chart(chart)
2011
- self._cp.set_chart(chart)
2012
- except Exception as e:
2013
- raise
2014
- tkinter.messagebox.showerror('Error Loading Chart',
2015
- 'Unable to open file: %r' % filename)
2016
-
2017
- def save_chart(self, *args):
2018
- "Save a chart to a pickle file"
2019
- filename = asksaveasfilename(filetypes=self.CHART_FILE_TYPES,
2020
- defaultextension='.pickle')
2021
- if not filename: return
2022
- try:
2023
- with open(filename, 'wb') as outfile:
2024
- pickle.dump(self._chart, outfile)
2025
- except Exception as e:
2026
- raise
2027
- tkinter.messagebox.showerror('Error Saving Chart',
2028
- 'Unable to open file: %r' % filename)
2029
-
2030
- def load_grammar(self, *args):
2031
- "Load a grammar from a pickle file"
2032
- filename = askopenfilename(filetypes=self.GRAMMAR_FILE_TYPES,
2033
- defaultextension='.cfg')
2034
- if not filename: return
2035
- try:
2036
- if filename.endswith('.pickle'):
2037
- with open(filename, 'rb') as infile:
2038
- grammar = pickle.load(infile)
2039
- else:
2040
- with open(filename, 'r') as infile:
2041
- grammar = CFG.fromstring(infile.read())
2042
- self.set_grammar(grammar)
2043
- except Exception as e:
2044
- tkinter.messagebox.showerror('Error Loading Grammar',
2045
- 'Unable to open file: %r' % filename)
2046
-
2047
- def save_grammar(self, *args):
2048
- filename = asksaveasfilename(filetypes=self.GRAMMAR_FILE_TYPES,
2049
- defaultextension='.cfg')
2050
- if not filename: return
2051
- try:
2052
- if filename.endswith('.pickle'):
2053
- with open(filename, 'wb') as outfile:
2054
- pickle.dump((self._chart, self._tokens), outfile)
2055
- else:
2056
- with open(filename, 'w') as outfile:
2057
- prods = self._grammar.productions()
2058
- start = [p for p in prods if p.lhs() == self._grammar.start()]
2059
- rest = [p for p in prods if p.lhs() != self._grammar.start()]
2060
- for prod in start: outfile.write('%s\n' % prod)
2061
- for prod in rest: outfile.write('%s\n' % prod)
2062
- except Exception as e:
2063
- tkinter.messagebox.showerror('Error Saving Grammar',
2064
- 'Unable to open file: %r' % filename)
2065
-
2066
- def reset(self, *args):
2067
- self._animating = 0
2068
- self._reset_parser()
2069
- self._cv.update(self._chart)
2070
- if self._matrix: self._matrix.set_chart(self._chart)
2071
- if self._matrix: self._matrix.deselect_cell()
2072
- if self._results: self._results.set_chart(self._chart)
2073
-
2074
- #////////////////////////////////////////////////////////////
2075
- # Edit
2076
- #////////////////////////////////////////////////////////////
2077
-
2078
- def edit_grammar(self, *e):
2079
- CFGEditor(self._root, self._grammar, self.set_grammar)
2080
-
2081
- def set_grammar(self, grammar):
2082
- self._grammar = grammar
2083
- self._cp.set_grammar(grammar)
2084
- if self._results: self._results.set_grammar(grammar)
2085
-
2086
- def edit_sentence(self, *e):
2087
- sentence = " ".join(self._tokens)
2088
- title = 'Edit Text'
2089
- instr = 'Enter a new sentence to parse.'
2090
- EntryDialog(self._root, sentence, instr, self.set_sentence, title)
2091
-
2092
- def set_sentence(self, sentence):
2093
- self._tokens = list(sentence.split())
2094
- self.reset()
2095
-
2096
- #////////////////////////////////////////////////////////////
2097
- # View Menu
2098
- #////////////////////////////////////////////////////////////
2099
-
2100
- def view_matrix(self, *e):
2101
- if self._matrix is not None: self._matrix.destroy()
2102
- self._matrix = ChartMatrixView(self._root, self._chart)
2103
- self._matrix.add_callback('select', self._select_matrix_edge)
2104
-
2105
- def view_results(self, *e):
2106
- if self._results is not None: self._results.destroy()
2107
- self._results = ChartResultsView(self._root, self._chart,
2108
- self._grammar)
2109
-
2110
- #////////////////////////////////////////////////////////////
2111
- # Zoom Menu
2112
- #////////////////////////////////////////////////////////////
2113
-
2114
- def resize(self):
2115
- self._animating = 0
2116
- self.set_font_size(self._size.get())
2117
-
2118
- def set_font_size(self, size):
2119
- self._cv.set_font_size(size)
2120
- self._font.configure(size=-abs(size))
2121
- self._boldfont.configure(size=-abs(size))
2122
- self._sysfont.configure(size=-abs(size))
2123
-
2124
- def get_font_size(self):
2125
- return abs(self._size.get())
2126
-
2127
- #////////////////////////////////////////////////////////////
2128
- # Parsing
2129
- #////////////////////////////////////////////////////////////
2130
-
2131
- def apply_strategy(self, strategy, edge_strategy=None):
2132
- # If we're animating, then stop.
2133
- if self._animating:
2134
- self._animating = 0
2135
- return
2136
-
2137
- # Clear the rule display & mark.
2138
- self._display_rule(None)
2139
- #self._cv.unmark_edge()
2140
-
2141
- if self._step.get():
2142
- selection = self._selection
2143
- if (selection is not None) and (edge_strategy is not None):
2144
- # Apply the given strategy to the selected edge.
2145
- self._cp.set_strategy([edge_strategy(selection)])
2146
- newedge = self._apply_strategy()
2147
-
2148
- # If it failed, then clear the selection.
2149
- if newedge is None:
2150
- self._cv.unmark_edge()
2151
- self._selection = None
2152
- else:
2153
- self._cp.set_strategy(strategy)
2154
- self._apply_strategy()
2155
-
2156
- else:
2157
- self._cp.set_strategy(strategy)
2158
- if self._animate.get():
2159
- self._animating = 1
2160
- self._animate_strategy()
2161
- else:
2162
- for edge in self._cpstep:
2163
- if edge is None: break
2164
- self._cv.update()
2165
- if self._matrix: self._matrix.update()
2166
- if self._results: self._results.update()
2167
-
2168
- def _stop_animation(self, *e):
2169
- self._animating = 0
2170
-
2171
- def _animate_strategy(self, speed=1):
2172
- if self._animating == 0: return
2173
- if self._apply_strategy() is not None:
2174
- if self._animate.get() == 0 or self._step.get() == 1:
2175
- return
2176
- if self._animate.get() == 1:
2177
- self._root.after(3000, self._animate_strategy)
2178
- elif self._animate.get() == 2:
2179
- self._root.after(1000, self._animate_strategy)
2180
- else:
2181
- self._root.after(20, self._animate_strategy)
2182
-
2183
- def _apply_strategy(self):
2184
- new_edge = next(self._cpstep)
2185
-
2186
- if new_edge is not None:
2187
- self._show_new_edge(new_edge)
2188
- return new_edge
2189
-
2190
- def _display_rule(self, rule):
2191
- if rule is None:
2192
- self._rulelabel2['text'] = ''
2193
- else:
2194
- name = str(rule)
2195
- self._rulelabel2['text'] = name
2196
- size = self._cv.get_font_size()
2197
-
2198
- #////////////////////////////////////////////////////////////
2199
- # Parsing Strategies
2200
- #////////////////////////////////////////////////////////////
2201
-
2202
- # Basic rules:
2203
- _TD_INIT = [TopDownInitRule()]
2204
- _TD_PREDICT = [TopDownPredictRule()]
2205
- _BU_RULE = [BottomUpPredictRule()]
2206
- _BU_LC_RULE = [BottomUpPredictCombineRule()]
2207
- _FUNDAMENTAL = [SingleEdgeFundamentalRule()]
2208
-
2209
- # Complete strategies:
2210
- _TD_STRATEGY = _TD_INIT + _TD_PREDICT + _FUNDAMENTAL
2211
- _BU_STRATEGY = _BU_RULE + _FUNDAMENTAL
2212
- _BU_LC_STRATEGY = _BU_LC_RULE + _FUNDAMENTAL
2213
-
2214
- # Button callback functions:
2215
- def top_down_init(self, *e):
2216
- self.apply_strategy(self._TD_INIT, None)
2217
- def top_down_predict(self, *e):
2218
- self.apply_strategy(self._TD_PREDICT, TopDownPredictEdgeRule)
2219
- def bottom_up(self, *e):
2220
- self.apply_strategy(self._BU_RULE, BottomUpEdgeRule)
2221
- def bottom_up_leftcorner(self, *e):
2222
- self.apply_strategy(self._BU_LC_RULE, BottomUpLeftCornerEdgeRule)
2223
- def fundamental(self, *e):
2224
- self.apply_strategy(self._FUNDAMENTAL, FundamentalEdgeRule)
2225
- def bottom_up_strategy(self, *e):
2226
- self.apply_strategy(self._BU_STRATEGY, BottomUpEdgeRule)
2227
- def bottom_up_leftcorner_strategy(self, *e):
2228
- self.apply_strategy(self._BU_LC_STRATEGY, BottomUpLeftCornerEdgeRule)
2229
- def top_down_strategy(self, *e):
2230
- self.apply_strategy(self._TD_STRATEGY, TopDownPredictEdgeRule)
2231
-
2232
- def app():
2233
- grammar = CFG.fromstring("""
2234
- # Grammatical productions.
2235
- S -> NP VP
2236
- VP -> VP PP | V NP | V
2237
- NP -> Det N | NP PP
2238
- PP -> P NP
2239
- # Lexical productions.
2240
- NP -> 'John' | 'I'
2241
- Det -> 'the' | 'my' | 'a'
2242
- N -> 'dog' | 'cookie' | 'table' | 'cake' | 'fork'
2243
- V -> 'ate' | 'saw'
2244
- P -> 'on' | 'under' | 'with'
2245
- """)
2246
-
2247
- sent = 'John ate the cake on the table with a fork'
2248
- sent = 'John ate the cake on the table'
2249
- tokens = list(sent.split())
2250
-
2251
- print('grammar= (')
2252
- for rule in grammar.productions():
2253
- print((' ', repr(rule)+','))
2254
- print(')')
2255
- print(('tokens = %r' % tokens))
2256
- print('Calling "ChartParserApp(grammar, tokens)"...')
2257
- ChartParserApp(grammar, tokens).mainloop()
2258
-
2259
- if __name__ == '__main__':
2260
- app()
2261
-
2262
- # Chart comparer:
2263
- #charts = ['/tmp/earley.pickle',
2264
- # '/tmp/topdown.pickle',
2265
- # '/tmp/bottomup.pickle']
2266
- #ChartComparer(*charts).mainloop()
2267
-
2268
- #import profile
2269
- #profile.run('demo2()', '/tmp/profile.out')
2270
- #import pstats
2271
- #p = pstats.Stats('/tmp/profile.out')
2272
- #p.strip_dirs().sort_stats('time', 'cum').print_stats(60)
2273
- #p.strip_dirs().sort_stats('cum', 'time').print_stats(60)
2274
-
2275
- __all__ = ['app']
2276
-
1
+ # Natural Language Toolkit: Chart Parser Application
2
+ #
3
+ # Copyright (C) 2001-2018 NLTK Project
4
+ # Author: Edward Loper <edloper@gmail.com>
5
+ # Jean Mark Gawron <gawron@mail.sdsu.edu>
6
+ # Steven Bird <stevenbird1@gmail.com>
7
+ # URL: <http://nltk.org/>
8
+ # For license information, see LICENSE.TXT
9
+
10
+ """
11
+ A graphical tool for exploring chart parsing.
12
+
13
+ Chart parsing is a flexible parsing algorithm that uses a data
14
+ structure called a "chart" to record hypotheses about syntactic
15
+ constituents. Each hypothesis is represented by a single "edge" on
16
+ the chart. A set of "chart rules" determine when new edges can be
17
+ added to the chart. This set of rules controls the overall behavior
18
+ of the parser (e.g. whether it parses top-down or bottom-up).
19
+
20
+ The chart parsing tool demonstrates the process of parsing a single
21
+ sentence, with a given grammar and lexicon. Its display is divided
22
+ into three sections: the bottom section displays the chart; the middle
23
+ section displays the sentence; and the top section displays the
24
+ partial syntax tree corresponding to the selected edge. Buttons along
25
+ the bottom of the window are used to control the execution of the
26
+ algorithm.
27
+
28
+ The chart parsing tool allows for flexible control of the parsing
29
+ algorithm. At each step of the algorithm, you can select which rule
30
+ or strategy you wish to apply. This allows you to experiment with
31
+ mixing different strategies (e.g. top-down and bottom-up). You can
32
+ exercise fine-grained control over the algorithm by selecting which
33
+ edge you wish to apply a rule to.
34
+ """
35
+
36
+ # At some point, we should rewrite this tool to use the new canvas
37
+ # widget system.
38
+
39
+
40
+ from __future__ import division
41
+ import pickle
42
+ import os.path
43
+
44
+ from six.moves.tkinter import (Button, Canvas, Checkbutton, Frame, IntVar,
45
+ Label, Menu, Scrollbar, Tk, Toplevel)
46
+ from six.moves.tkinter_font import Font
47
+ from six.moves.tkinter_messagebox import showerror, showinfo
48
+ from six.moves.tkinter_tkfiledialog import asksaveasfilename, askopenfilename
49
+
50
+ from nltk.parse.chart import (BottomUpPredictCombineRule, BottomUpPredictRule,
51
+ Chart, LeafEdge, LeafInitRule, SingleEdgeFundamentalRule,
52
+ SteppingChartParser, TopDownInitRule, TopDownPredictRule,
53
+ TreeEdge)
54
+ from nltk.tree import Tree
55
+ from nltk.grammar import Nonterminal, CFG
56
+ from nltk.util import in_idle
57
+ from nltk.draw.util import (CanvasFrame, ColorizedList,
58
+ EntryDialog, MutableOptionMenu,
59
+ ShowText, SymbolWidget)
60
+ from nltk.draw import CFGEditor, tree_to_treesegment, TreeSegmentWidget
61
+
62
+ # Known bug: ChartView doesn't handle edges generated by epsilon
63
+ # productions (e.g., [Production: PP -> ]) very well.
64
+
65
+ #######################################################################
66
+ # Edge List
67
+ #######################################################################
68
+
69
+ class EdgeList(ColorizedList):
70
+ ARROW = SymbolWidget.SYMBOLS['rightarrow']
71
+
72
+ def _init_colortags(self, textwidget, options):
73
+ textwidget.tag_config('terminal', foreground='#006000')
74
+ textwidget.tag_config('arrow', font='symbol', underline='0')
75
+ textwidget.tag_config('dot', foreground = '#000000')
76
+ textwidget.tag_config('nonterminal', foreground='blue',
77
+ font=('helvetica', -12, 'bold'))
78
+
79
+ def _item_repr(self, item):
80
+ contents = []
81
+ contents.append(('%s\t' % item.lhs(), 'nonterminal'))
82
+ contents.append((self.ARROW, 'arrow'))
83
+ for i, elt in enumerate(item.rhs()):
84
+ if i == item.dot():
85
+ contents.append((' *', 'dot'))
86
+ if isinstance(elt, Nonterminal):
87
+ contents.append((' %s' % elt.symbol(), 'nonterminal'))
88
+ else:
89
+ contents.append((' %r' % elt, 'terminal'))
90
+ if item.is_complete():
91
+ contents.append((' *', 'dot'))
92
+ return contents
93
+
94
+ #######################################################################
95
+ # Chart Matrix View
96
+ #######################################################################
97
+
98
+ class ChartMatrixView(object):
99
+ """
100
+ A view of a chart that displays the contents of the corresponding matrix.
101
+ """
102
+ def __init__(self, parent, chart, toplevel=True, title='Chart Matrix',
103
+ show_numedges=False):
104
+ self._chart = chart
105
+ self._cells = []
106
+ self._marks = []
107
+
108
+ self._selected_cell = None
109
+
110
+ if toplevel:
111
+ self._root = Toplevel(parent)
112
+ self._root.title(title)
113
+ self._root.bind('<Control-q>', self.destroy)
114
+ self._init_quit(self._root)
115
+ else:
116
+ self._root = Frame(parent)
117
+
118
+ self._init_matrix(self._root)
119
+ self._init_list(self._root)
120
+ if show_numedges:
121
+ self._init_numedges(self._root)
122
+ else:
123
+ self._numedges_label = None
124
+
125
+ self._callbacks = {}
126
+
127
+ self._num_edges = 0
128
+
129
+ self.draw()
130
+
131
+ def _init_quit(self, root):
132
+ quit = Button(root, text='Quit', command=self.destroy)
133
+ quit.pack(side='bottom', expand=0, fill='none')
134
+
135
+ def _init_matrix(self, root):
136
+ cframe = Frame(root, border=2, relief='sunken')
137
+ cframe.pack(expand=0, fill='none', padx=1, pady=3, side='top')
138
+ self._canvas = Canvas(cframe, width=200, height=200,
139
+ background='white')
140
+ self._canvas.pack(expand=0, fill='none')
141
+
142
+ def _init_numedges(self, root):
143
+ self._numedges_label = Label(root, text='0 edges')
144
+ self._numedges_label.pack(expand=0, fill='none', side='top')
145
+
146
+ def _init_list(self, root):
147
+ self._list = EdgeList(root, [], width=20, height=5)
148
+ self._list.pack(side='top', expand=1, fill='both', pady=3)
149
+ def cb(edge, self=self): self._fire_callbacks('select', edge)
150
+ self._list.add_callback('select', cb)
151
+ self._list.focus()
152
+
153
+ def destroy(self, *e):
154
+ if self._root is None: return
155
+ try: self._root.destroy()
156
+ except: pass
157
+ self._root = None
158
+
159
+ def set_chart(self, chart):
160
+ if chart is not self._chart:
161
+ self._chart = chart
162
+ self._num_edges = 0
163
+ self.draw()
164
+
165
+ def update(self):
166
+ if self._root is None: return
167
+
168
+ # Count the edges in each cell
169
+ N = len(self._cells)
170
+ cell_edges = [[0 for i in range(N)] for j in range(N)]
171
+ for edge in self._chart:
172
+ cell_edges[edge.start()][edge.end()] += 1
173
+
174
+ # Color the cells correspondingly.
175
+ for i in range(N):
176
+ for j in range(i, N):
177
+ if cell_edges[i][j] == 0:
178
+ color = 'gray20'
179
+ else:
180
+ color = ('#00%02x%02x' %
181
+ (min(255, 50+128*cell_edges[i][j]/10),
182
+ max(0, 128-128*cell_edges[i][j]/10)))
183
+ cell_tag = self._cells[i][j]
184
+ self._canvas.itemconfig(cell_tag, fill=color)
185
+ if (i,j) == self._selected_cell:
186
+ self._canvas.itemconfig(cell_tag, outline='#00ffff',
187
+ width=3)
188
+ self._canvas.tag_raise(cell_tag)
189
+ else:
190
+ self._canvas.itemconfig(cell_tag, outline='black',
191
+ width=1)
192
+
193
+ # Update the edge list.
194
+ edges = list(self._chart.select(span=self._selected_cell))
195
+ self._list.set(edges)
196
+
197
+ # Update our edge count.
198
+ self._num_edges = self._chart.num_edges()
199
+ if self._numedges_label is not None:
200
+ self._numedges_label['text'] = '%d edges' % self._num_edges
201
+
202
+ def activate(self):
203
+ self._canvas.itemconfig('inactivebox', state='hidden')
204
+ self.update()
205
+
206
+ def inactivate(self):
207
+ self._canvas.itemconfig('inactivebox', state='normal')
208
+ self.update()
209
+
210
+ def add_callback(self, event, func):
211
+ self._callbacks.setdefault(event,{})[func] = 1
212
+
213
+ def remove_callback(self, event, func=None):
214
+ if func is None: del self._callbacks[event]
215
+ else:
216
+ try: del self._callbacks[event][func]
217
+ except: pass
218
+
219
+ def _fire_callbacks(self, event, *args):
220
+ if event not in self._callbacks: return
221
+ for cb_func in list(self._callbacks[event].keys()): cb_func(*args)
222
+
223
+ def select_cell(self, i, j):
224
+ if self._root is None: return
225
+
226
+ # If the cell is already selected (and the chart contents
227
+ # haven't changed), then do nothing.
228
+ if ((i,j) == self._selected_cell and
229
+ self._chart.num_edges() == self._num_edges): return
230
+
231
+ self._selected_cell = (i,j)
232
+ self.update()
233
+
234
+ # Fire the callback.
235
+ self._fire_callbacks('select_cell', i, j)
236
+
237
+ def deselect_cell(self):
238
+ if self._root is None: return
239
+ self._selected_cell = None
240
+ self._list.set([])
241
+ self.update()
242
+
243
+ def _click_cell(self, i, j):
244
+ if self._selected_cell == (i,j):
245
+ self.deselect_cell()
246
+ else:
247
+ self.select_cell(i, j)
248
+
249
+ def view_edge(self, edge):
250
+ self.select_cell(*edge.span())
251
+ self._list.view(edge)
252
+
253
+ def mark_edge(self, edge):
254
+ if self._root is None: return
255
+ self.select_cell(*edge.span())
256
+ self._list.mark(edge)
257
+
258
+ def unmark_edge(self, edge=None):
259
+ if self._root is None: return
260
+ self._list.unmark(edge)
261
+
262
+ def markonly_edge(self, edge):
263
+ if self._root is None: return
264
+ self.select_cell(*edge.span())
265
+ self._list.markonly(edge)
266
+
267
+ def draw(self):
268
+ if self._root is None: return
269
+ LEFT_MARGIN = BOT_MARGIN = 15
270
+ TOP_MARGIN = 5
271
+ c = self._canvas
272
+ c.delete('all')
273
+ N = self._chart.num_leaves()+1
274
+ dx = (int(c['width'])-LEFT_MARGIN)/N
275
+ dy = (int(c['height'])-TOP_MARGIN-BOT_MARGIN)/N
276
+
277
+ c.delete('all')
278
+
279
+ # Labels and dotted lines
280
+ for i in range(N):
281
+ c.create_text(LEFT_MARGIN-2, i*dy+dy/2+TOP_MARGIN,
282
+ text=repr(i), anchor='e')
283
+ c.create_text(i*dx+dx/2+LEFT_MARGIN, N*dy+TOP_MARGIN+1,
284
+ text=repr(i), anchor='n')
285
+ c.create_line(LEFT_MARGIN, dy*(i+1)+TOP_MARGIN,
286
+ dx*N+LEFT_MARGIN, dy*(i+1)+TOP_MARGIN, dash='.')
287
+ c.create_line(dx*i+LEFT_MARGIN, TOP_MARGIN,
288
+ dx*i+LEFT_MARGIN, dy*N+TOP_MARGIN, dash='.')
289
+
290
+ # A box around the whole thing
291
+ c.create_rectangle(LEFT_MARGIN, TOP_MARGIN,
292
+ LEFT_MARGIN+dx*N, dy*N+TOP_MARGIN,
293
+ width=2)
294
+
295
+ # Cells
296
+ self._cells = [[None for i in range(N)] for j in range(N)]
297
+ for i in range(N):
298
+ for j in range(i, N):
299
+ t = c.create_rectangle(j*dx+LEFT_MARGIN, i*dy+TOP_MARGIN,
300
+ (j+1)*dx+LEFT_MARGIN,
301
+ (i+1)*dy+TOP_MARGIN,
302
+ fill='gray20')
303
+ self._cells[i][j] = t
304
+ def cb(event, self=self, i=i, j=j): self._click_cell(i,j)
305
+ c.tag_bind(t, '<Button-1>', cb)
306
+
307
+ # Inactive box
308
+ xmax, ymax = int(c['width']), int(c['height'])
309
+ t = c.create_rectangle(-100, -100, xmax+100, ymax+100,
310
+ fill='gray50', state='hidden',
311
+ tag='inactivebox')
312
+ c.tag_lower(t)
313
+
314
+ # Update the cells.
315
+ self.update()
316
+
317
+ def pack(self, *args, **kwargs):
318
+ self._root.pack(*args, **kwargs)
319
+
320
+ #######################################################################
321
+ # Chart Results View
322
+ #######################################################################
323
+
324
+ class ChartResultsView(object):
325
+ def __init__(self, parent, chart, grammar, toplevel=True):
326
+ self._chart = chart
327
+ self._grammar = grammar
328
+ self._trees = []
329
+ self._y = 10
330
+ self._treewidgets = []
331
+ self._selection = None
332
+ self._selectbox = None
333
+
334
+ if toplevel:
335
+ self._root = Toplevel(parent)
336
+ self._root.title('Chart Parser Application: Results')
337
+ self._root.bind('<Control-q>', self.destroy)
338
+ else:
339
+ self._root = Frame(parent)
340
+
341
+ # Buttons
342
+ if toplevel:
343
+ buttons = Frame(self._root)
344
+ buttons.pack(side='bottom', expand=0, fill='x')
345
+ Button(buttons, text='Quit',
346
+ command=self.destroy).pack(side='right')
347
+ Button(buttons, text='Print All',
348
+ command=self.print_all).pack(side='left')
349
+ Button(buttons, text='Print Selection',
350
+ command=self.print_selection).pack(side='left')
351
+
352
+ # Canvas frame.
353
+ self._cframe = CanvasFrame(self._root, closeenough=20)
354
+ self._cframe.pack(side='top', expand=1, fill='both')
355
+
356
+ # Initial update
357
+ self.update()
358
+
359
+ def update(self, edge=None):
360
+ if self._root is None: return
361
+ # If the edge isn't a parse edge, do nothing.
362
+ if edge is not None:
363
+ if edge.lhs() != self._grammar.start(): return
364
+ if edge.span() != (0, self._chart.num_leaves()): return
365
+
366
+ for parse in self._chart.parses(self._grammar.start()):
367
+ if parse not in self._trees:
368
+ self._add(parse)
369
+
370
+ def _add(self, parse):
371
+ # Add it to self._trees.
372
+ self._trees.append(parse)
373
+
374
+ # Create a widget for it.
375
+ c = self._cframe.canvas()
376
+ treewidget = tree_to_treesegment(c, parse)
377
+
378
+ # Add it to the canvas frame.
379
+ self._treewidgets.append(treewidget)
380
+ self._cframe.add_widget(treewidget, 10, self._y)
381
+
382
+ # Register callbacks.
383
+ treewidget.bind_click(self._click)
384
+
385
+ # Update y.
386
+ self._y = treewidget.bbox()[3] + 10
387
+
388
+ def _click(self, widget):
389
+ c = self._cframe.canvas()
390
+ if self._selection is not None:
391
+ c.delete(self._selectbox)
392
+ self._selection = widget
393
+ (x1, y1, x2, y2) = widget.bbox()
394
+ self._selectbox = c.create_rectangle(x1, y1, x2, y2,
395
+ width=2, outline='#088')
396
+
397
+ def _color(self, treewidget, color):
398
+ treewidget.label()['color'] = color
399
+ for child in treewidget.subtrees():
400
+ if isinstance(child, TreeSegmentWidget):
401
+ self._color(child, color)
402
+ else:
403
+ child['color'] = color
404
+
405
+ def print_all(self, *e):
406
+ if self._root is None: return
407
+ self._cframe.print_to_file()
408
+
409
+ def print_selection(self, *e):
410
+ if self._root is None: return
411
+ if self._selection is None:
412
+ showerror('Print Error', 'No tree selected')
413
+ else:
414
+ c = self._cframe.canvas()
415
+ for widget in self._treewidgets:
416
+ if widget is not self._selection:
417
+ self._cframe.destroy_widget(widget)
418
+ c.delete(self._selectbox)
419
+ (x1,y1,x2,y2) = self._selection.bbox()
420
+ self._selection.move(10-x1,10-y1)
421
+ c['scrollregion'] = '0 0 %s %s' % (x2-x1+20, y2-y1+20)
422
+ self._cframe.print_to_file()
423
+
424
+ # Restore our state.
425
+ self._treewidgets = [self._selection]
426
+ self.clear()
427
+ self.update()
428
+
429
+ def clear(self):
430
+ if self._root is None: return
431
+ for treewidget in self._treewidgets:
432
+ self._cframe.destroy_widget(treewidget)
433
+ self._trees = []
434
+ self._treewidgets = []
435
+ if self._selection is not None:
436
+ self._cframe.canvas().delete(self._selectbox)
437
+ self._selection = None
438
+ self._y = 10
439
+
440
+ def set_chart(self, chart):
441
+ self.clear()
442
+ self._chart = chart
443
+ self.update()
444
+
445
+ def set_grammar(self, grammar):
446
+ self.clear()
447
+ self._grammar = grammar
448
+ self.update()
449
+
450
+ def destroy(self, *e):
451
+ if self._root is None: return
452
+ try: self._root.destroy()
453
+ except: pass
454
+ self._root = None
455
+
456
+ def pack(self, *args, **kwargs):
457
+ self._root.pack(*args, **kwargs)
458
+
459
+ #######################################################################
460
+ # Chart Comparer
461
+ #######################################################################
462
+
463
+ class ChartComparer(object):
464
+ """
465
+
466
+ :ivar _root: The root window
467
+
468
+ :ivar _charts: A dictionary mapping names to charts. When
469
+ charts are loaded, they are added to this dictionary.
470
+
471
+ :ivar _left_chart: The left ``Chart``.
472
+ :ivar _left_name: The name ``_left_chart`` (derived from filename)
473
+ :ivar _left_matrix: The ``ChartMatrixView`` for ``_left_chart``
474
+ :ivar _left_selector: The drop-down ``MutableOptionsMenu`` used
475
+ to select ``_left_chart``.
476
+
477
+ :ivar _right_chart: The right ``Chart``.
478
+ :ivar _right_name: The name ``_right_chart`` (derived from filename)
479
+ :ivar _right_matrix: The ``ChartMatrixView`` for ``_right_chart``
480
+ :ivar _right_selector: The drop-down ``MutableOptionsMenu`` used
481
+ to select ``_right_chart``.
482
+
483
+ :ivar _out_chart: The out ``Chart``.
484
+ :ivar _out_name: The name ``_out_chart`` (derived from filename)
485
+ :ivar _out_matrix: The ``ChartMatrixView`` for ``_out_chart``
486
+ :ivar _out_label: The label for ``_out_chart``.
487
+
488
+ :ivar _op_label: A Label containing the most recent operation.
489
+ """
490
+
491
+ _OPSYMBOL = {'-': '-',
492
+ 'and': SymbolWidget.SYMBOLS['intersection'],
493
+ 'or': SymbolWidget.SYMBOLS['union']}
494
+
495
+ def __init__(self, *chart_filenames):
496
+ # This chart is displayed when we don't have a value (eg
497
+ # before any chart is loaded).
498
+ faketok = [''] * 8
499
+ self._emptychart = Chart(faketok)
500
+
501
+ # The left & right charts start out empty.
502
+ self._left_name = 'None'
503
+ self._right_name = 'None'
504
+ self._left_chart = self._emptychart
505
+ self._right_chart = self._emptychart
506
+
507
+ # The charts that have been loaded.
508
+ self._charts = {'None': self._emptychart}
509
+
510
+ # The output chart.
511
+ self._out_chart = self._emptychart
512
+
513
+ # The most recent operation
514
+ self._operator = None
515
+
516
+ # Set up the root window.
517
+ self._root = Tk()
518
+ self._root.title('Chart Comparison')
519
+ self._root.bind('<Control-q>', self.destroy)
520
+ self._root.bind('<Control-x>', self.destroy)
521
+
522
+ # Initialize all widgets, etc.
523
+ self._init_menubar(self._root)
524
+ self._init_chartviews(self._root)
525
+ self._init_divider(self._root)
526
+ self._init_buttons(self._root)
527
+ self._init_bindings(self._root)
528
+
529
+ # Load any specified charts.
530
+ for filename in chart_filenames:
531
+ self.load_chart(filename)
532
+
533
+ def destroy(self, *e):
534
+ if self._root is None: return
535
+ try: self._root.destroy()
536
+ except: pass
537
+ self._root = None
538
+
539
+ def mainloop(self, *args, **kwargs):
540
+ return
541
+ self._root.mainloop(*args, **kwargs)
542
+
543
+ #////////////////////////////////////////////////////////////
544
+ # Initialization
545
+ #////////////////////////////////////////////////////////////
546
+
547
+ def _init_menubar(self, root):
548
+ menubar = Menu(root)
549
+
550
+ # File menu
551
+ filemenu = Menu(menubar, tearoff=0)
552
+ filemenu.add_command(label='Load Chart', accelerator='Ctrl-o',
553
+ underline=0, command=self.load_chart_dialog)
554
+ filemenu.add_command(label='Save Output', accelerator='Ctrl-s',
555
+ underline=0, command=self.save_chart_dialog)
556
+ filemenu.add_separator()
557
+ filemenu.add_command(label='Exit', underline=1,
558
+ command=self.destroy, accelerator='Ctrl-x')
559
+ menubar.add_cascade(label='File', underline=0, menu=filemenu)
560
+
561
+ # Compare menu
562
+ opmenu = Menu(menubar, tearoff=0)
563
+ opmenu.add_command(label='Intersection',
564
+ command=self._intersection,
565
+ accelerator='+')
566
+ opmenu.add_command(label='Union',
567
+ command=self._union,
568
+ accelerator='*')
569
+ opmenu.add_command(label='Difference',
570
+ command=self._difference,
571
+ accelerator='-')
572
+ opmenu.add_separator()
573
+ opmenu.add_command(label='Swap Charts',
574
+ command=self._swapcharts)
575
+ menubar.add_cascade(label='Compare', underline=0, menu=opmenu)
576
+
577
+ # Add the menu
578
+ self._root.config(menu=menubar)
579
+
580
+ def _init_divider(self, root):
581
+ divider = Frame(root, border=2, relief='sunken')
582
+ divider.pack(side='top', fill='x', ipady=2)
583
+
584
+ def _init_chartviews(self, root):
585
+ opfont=('symbol', -36) # Font for operator.
586
+ eqfont=('helvetica', -36) # Font for equals sign.
587
+
588
+ frame = Frame(root, background='#c0c0c0')
589
+ frame.pack(side='top', expand=1, fill='both')
590
+
591
+ # The left matrix.
592
+ cv1_frame = Frame(frame, border=3, relief='groove')
593
+ cv1_frame.pack(side='left', padx=8, pady=7, expand=1, fill='both')
594
+ self._left_selector = MutableOptionMenu(
595
+ cv1_frame, list(self._charts.keys()), command=self._select_left)
596
+ self._left_selector.pack(side='top', pady=5, fill='x')
597
+ self._left_matrix = ChartMatrixView(cv1_frame, self._emptychart,
598
+ toplevel=False,
599
+ show_numedges=True)
600
+ self._left_matrix.pack(side='bottom', padx=5, pady=5,
601
+ expand=1, fill='both')
602
+ self._left_matrix.add_callback('select', self.select_edge)
603
+ self._left_matrix.add_callback('select_cell', self.select_cell)
604
+ self._left_matrix.inactivate()
605
+
606
+ # The operator.
607
+ self._op_label = Label(frame, text=' ', width=3,
608
+ background='#c0c0c0', font=opfont)
609
+ self._op_label.pack(side='left', padx=5, pady=5)
610
+
611
+ # The right matrix.
612
+ cv2_frame = Frame(frame, border=3, relief='groove')
613
+ cv2_frame.pack(side='left', padx=8, pady=7, expand=1, fill='both')
614
+ self._right_selector = MutableOptionMenu(
615
+ cv2_frame, list(self._charts.keys()), command=self._select_right)
616
+ self._right_selector.pack(side='top', pady=5, fill='x')
617
+ self._right_matrix = ChartMatrixView(cv2_frame, self._emptychart,
618
+ toplevel=False,
619
+ show_numedges=True)
620
+ self._right_matrix.pack(side='bottom', padx=5, pady=5,
621
+ expand=1, fill='both')
622
+ self._right_matrix.add_callback('select', self.select_edge)
623
+ self._right_matrix.add_callback('select_cell', self.select_cell)
624
+ self._right_matrix.inactivate()
625
+
626
+ # The equals sign
627
+ Label(frame, text='=', width=3, background='#c0c0c0',
628
+ font=eqfont).pack(side='left', padx=5, pady=5)
629
+
630
+ # The output matrix.
631
+ out_frame = Frame(frame, border=3, relief='groove')
632
+ out_frame.pack(side='left', padx=8, pady=7, expand=1, fill='both')
633
+ self._out_label = Label(out_frame, text='Output')
634
+ self._out_label.pack(side='top', pady=9)
635
+ self._out_matrix = ChartMatrixView(out_frame, self._emptychart,
636
+ toplevel=False,
637
+ show_numedges=True)
638
+ self._out_matrix.pack(side='bottom', padx=5, pady=5,
639
+ expand=1, fill='both')
640
+ self._out_matrix.add_callback('select', self.select_edge)
641
+ self._out_matrix.add_callback('select_cell', self.select_cell)
642
+ self._out_matrix.inactivate()
643
+
644
+ def _init_buttons(self, root):
645
+ buttons = Frame(root)
646
+ buttons.pack(side='bottom', pady=5, fill='x', expand=0)
647
+ Button(buttons, text='Intersection',
648
+ command=self._intersection).pack(side='left')
649
+ Button(buttons, text='Union',
650
+ command=self._union).pack(side='left')
651
+ Button(buttons, text='Difference',
652
+ command=self._difference).pack(side='left')
653
+ Frame(buttons, width=20).pack(side='left')
654
+ Button(buttons, text='Swap Charts',
655
+ command=self._swapcharts).pack(side='left')
656
+
657
+ Button(buttons, text='Detatch Output',
658
+ command=self._detatch_out).pack(side='right')
659
+
660
+ def _init_bindings(self, root):
661
+ #root.bind('<Control-s>', self.save_chart)
662
+ root.bind('<Control-o>', self.load_chart_dialog)
663
+ #root.bind('<Control-r>', self.reset)
664
+
665
+ #////////////////////////////////////////////////////////////
666
+ # Input Handling
667
+ #////////////////////////////////////////////////////////////
668
+
669
+ def _select_left(self, name):
670
+ self._left_name = name
671
+ self._left_chart = self._charts[name]
672
+ self._left_matrix.set_chart(self._left_chart)
673
+ if name == 'None': self._left_matrix.inactivate()
674
+ self._apply_op()
675
+
676
+ def _select_right(self, name):
677
+ self._right_name = name
678
+ self._right_chart = self._charts[name]
679
+ self._right_matrix.set_chart(self._right_chart)
680
+ if name == 'None': self._right_matrix.inactivate()
681
+ self._apply_op()
682
+
683
+ def _apply_op(self):
684
+ if self._operator == '-': self._difference()
685
+ elif self._operator == 'or': self._union()
686
+ elif self._operator == 'and': self._intersection()
687
+
688
+
689
+ #////////////////////////////////////////////////////////////
690
+ # File
691
+ #////////////////////////////////////////////////////////////
692
+ CHART_FILE_TYPES = [('Pickle file', '.pickle'),
693
+ ('All files', '*')]
694
+
695
+ def save_chart_dialog(self, *args):
696
+ filename = asksaveasfilename(filetypes=self.CHART_FILE_TYPES,
697
+ defaultextension='.pickle')
698
+ if not filename: return
699
+ try:
700
+ with open(filename, 'wb') as outfile:
701
+ pickle.dump(self._out_chart, outfile)
702
+ except Exception as e:
703
+ showerror('Error Saving Chart',
704
+ 'Unable to open file: %r\n%s' %
705
+ (filename, e))
706
+
707
+ def load_chart_dialog(self, *args):
708
+ filename = askopenfilename(filetypes=self.CHART_FILE_TYPES,
709
+ defaultextension='.pickle')
710
+ if not filename: return
711
+ try: self.load_chart(filename)
712
+ except Exception as e:
713
+ showerror('Error Loading Chart',
714
+ 'Unable to open file: %r\n%s' %
715
+ (filename, e))
716
+
717
+ def load_chart(self, filename):
718
+ with open(filename, 'rb') as infile:
719
+ chart = pickle.load(infile)
720
+ name = os.path.basename(filename)
721
+ if name.endswith('.pickle'): name = name[:-7]
722
+ if name.endswith('.chart'): name = name[:-6]
723
+ self._charts[name] = chart
724
+ self._left_selector.add(name)
725
+ self._right_selector.add(name)
726
+
727
+ # If either left_matrix or right_matrix is empty, then
728
+ # display the new chart.
729
+ if self._left_chart is self._emptychart:
730
+ self._left_selector.set(name)
731
+ elif self._right_chart is self._emptychart:
732
+ self._right_selector.set(name)
733
+
734
+ def _update_chartviews(self):
735
+ self._left_matrix.update()
736
+ self._right_matrix.update()
737
+ self._out_matrix.update()
738
+
739
+ #////////////////////////////////////////////////////////////
740
+ # Selection
741
+ #////////////////////////////////////////////////////////////
742
+
743
+ def select_edge(self, edge):
744
+ if edge in self._left_chart:
745
+ self._left_matrix.markonly_edge(edge)
746
+ else:
747
+ self._left_matrix.unmark_edge()
748
+ if edge in self._right_chart:
749
+ self._right_matrix.markonly_edge(edge)
750
+ else:
751
+ self._right_matrix.unmark_edge()
752
+ if edge in self._out_chart:
753
+ self._out_matrix.markonly_edge(edge)
754
+ else:
755
+ self._out_matrix.unmark_edge()
756
+
757
+ def select_cell(self, i, j):
758
+ self._left_matrix.select_cell(i, j)
759
+ self._right_matrix.select_cell(i, j)
760
+ self._out_matrix.select_cell(i, j)
761
+
762
+ #////////////////////////////////////////////////////////////
763
+ # Operations
764
+ #////////////////////////////////////////////////////////////
765
+
766
+ def _difference(self):
767
+ if not self._checkcompat(): return
768
+
769
+ out_chart = Chart(self._left_chart.tokens())
770
+ for edge in self._left_chart:
771
+ if edge not in self._right_chart:
772
+ out_chart.insert(edge, [])
773
+
774
+ self._update('-', out_chart)
775
+
776
+ def _intersection(self):
777
+ if not self._checkcompat(): return
778
+
779
+ out_chart = Chart(self._left_chart.tokens())
780
+ for edge in self._left_chart:
781
+ if edge in self._right_chart:
782
+ out_chart.insert(edge, [])
783
+
784
+ self._update('and', out_chart)
785
+
786
+ def _union(self):
787
+ if not self._checkcompat(): return
788
+
789
+ out_chart = Chart(self._left_chart.tokens())
790
+ for edge in self._left_chart:
791
+ out_chart.insert(edge, [])
792
+ for edge in self._right_chart:
793
+ out_chart.insert(edge, [])
794
+
795
+ self._update('or', out_chart)
796
+
797
+ def _swapcharts(self):
798
+ left, right = self._left_name, self._right_name
799
+ self._left_selector.set(right)
800
+ self._right_selector.set(left)
801
+
802
+ def _checkcompat(self):
803
+ if (self._left_chart.tokens() != self._right_chart.tokens() or
804
+ self._left_chart.property_names() !=
805
+ self._right_chart.property_names() or
806
+ self._left_chart == self._emptychart or
807
+ self._right_chart == self._emptychart):
808
+ # Clear & inactivate the output chart.
809
+ self._out_chart = self._emptychart
810
+ self._out_matrix.set_chart(self._out_chart)
811
+ self._out_matrix.inactivate()
812
+ self._out_label['text'] = 'Output'
813
+ # Issue some other warning?
814
+ return False
815
+ else:
816
+ return True
817
+
818
+ def _update(self, operator, out_chart):
819
+ self._operator = operator
820
+ self._op_label['text'] = self._OPSYMBOL[operator]
821
+ self._out_chart = out_chart
822
+ self._out_matrix.set_chart(out_chart)
823
+ self._out_label['text'] = '%s %s %s' % (self._left_name,
824
+ self._operator,
825
+ self._right_name)
826
+
827
+ def _clear_out_chart(self):
828
+ self._out_chart = self._emptychart
829
+ self._out_matrix.set_chart(self._out_chart)
830
+ self._op_label['text'] = ' '
831
+ self._out_matrix.inactivate()
832
+
833
+ def _detatch_out(self):
834
+ ChartMatrixView(self._root, self._out_chart,
835
+ title=self._out_label['text'])
836
+
837
+
838
+
839
+
840
+
841
+
842
+
843
+
844
+ #######################################################################
845
+ # Chart View
846
+ #######################################################################
847
+
848
+ class ChartView(object):
849
+ """
850
+ A component for viewing charts. This is used by ``ChartParserApp`` to
851
+ allow students to interactively experiment with various chart
852
+ parsing techniques. It is also used by ``Chart.draw()``.
853
+
854
+ :ivar _chart: The chart that we are giving a view of. This chart
855
+ may be modified; after it is modified, you should call
856
+ ``update``.
857
+ :ivar _sentence: The list of tokens that the chart spans.
858
+
859
+ :ivar _root: The root window.
860
+ :ivar _chart_canvas: The canvas we're using to display the chart
861
+ itself.
862
+ :ivar _tree_canvas: The canvas we're using to display the tree
863
+ that each edge spans. May be None, if we're not displaying
864
+ trees.
865
+ :ivar _sentence_canvas: The canvas we're using to display the sentence
866
+ text. May be None, if we're not displaying the sentence text.
867
+ :ivar _edgetags: A dictionary mapping from edges to the tags of
868
+ the canvas elements (lines, etc) used to display that edge.
869
+ The values of this dictionary have the form
870
+ ``(linetag, rhstag1, dottag, rhstag2, lhstag)``.
871
+ :ivar _treetags: A list of all the tags that make up the tree;
872
+ used to erase the tree (without erasing the loclines).
873
+ :ivar _chart_height: The height of the chart canvas.
874
+ :ivar _sentence_height: The height of the sentence canvas.
875
+ :ivar _tree_height: The height of the tree
876
+
877
+ :ivar _text_height: The height of a text string (in the normal
878
+ font).
879
+
880
+ :ivar _edgelevels: A list of edges at each level of the chart (the
881
+ top level is the 0th element). This list is used to remember
882
+ where edges should be drawn; and to make sure that no edges
883
+ are overlapping on the chart view.
884
+
885
+ :ivar _unitsize: Pixel size of one unit (from the location). This
886
+ is determined by the span of the chart's location, and the
887
+ width of the chart display canvas.
888
+
889
+ :ivar _fontsize: The current font size
890
+
891
+ :ivar _marks: A dictionary from edges to marks. Marks are
892
+ strings, specifying colors (e.g. 'green').
893
+ """
894
+
895
+ _LEAF_SPACING = 10
896
+ _MARGIN = 10
897
+ _TREE_LEVEL_SIZE = 12
898
+ _CHART_LEVEL_SIZE = 40
899
+
900
+ def __init__(self, chart, root=None, **kw):
901
+ """
902
+ Construct a new ``Chart`` display.
903
+ """
904
+ # Process keyword args.
905
+ draw_tree = kw.get('draw_tree', 0)
906
+ draw_sentence = kw.get('draw_sentence', 1)
907
+ self._fontsize = kw.get('fontsize', -12)
908
+
909
+ # The chart!
910
+ self._chart = chart
911
+
912
+ # Callback functions
913
+ self._callbacks = {}
914
+
915
+ # Keep track of drawn edges
916
+ self._edgelevels = []
917
+ self._edgetags = {}
918
+
919
+ # Keep track of which edges are marked.
920
+ self._marks = {}
921
+
922
+ # These are used to keep track of the set of tree tokens
923
+ # currently displayed in the tree canvas.
924
+ self._treetoks = []
925
+ self._treetoks_edge = None
926
+ self._treetoks_index = 0
927
+
928
+ # Keep track of the tags used to draw the tree
929
+ self._tree_tags = []
930
+
931
+ # Put multiple edges on each level?
932
+ self._compact = 0
933
+
934
+ # If they didn't provide a main window, then set one up.
935
+ if root is None:
936
+ top = Tk()
937
+ top.title('Chart View')
938
+ def destroy1(e, top=top): top.destroy()
939
+ def destroy2(top=top): top.destroy()
940
+ top.bind('q', destroy1)
941
+ b = Button(top, text='Done', command=destroy2)
942
+ b.pack(side='bottom')
943
+ self._root = top
944
+ else:
945
+ self._root = root
946
+
947
+ # Create some fonts.
948
+ self._init_fonts(root)
949
+
950
+ # Create the chart canvas.
951
+ (self._chart_sb, self._chart_canvas) = self._sb_canvas(self._root)
952
+ self._chart_canvas['height'] = 300
953
+ self._chart_canvas['closeenough'] = 15
954
+
955
+ # Create the sentence canvas.
956
+ if draw_sentence:
957
+ cframe = Frame(self._root, relief='sunk', border=2)
958
+ cframe.pack(fill='both', side='bottom')
959
+ self._sentence_canvas = Canvas(cframe, height=50)
960
+ self._sentence_canvas['background'] = '#e0e0e0'
961
+ self._sentence_canvas.pack(fill='both')
962
+ #self._sentence_canvas['height'] = self._sentence_height
963
+ else:
964
+ self._sentence_canvas = None
965
+
966
+ # Create the tree canvas.
967
+ if draw_tree:
968
+ (sb, canvas) = self._sb_canvas(self._root, 'n', 'x')
969
+ (self._tree_sb, self._tree_canvas) = (sb, canvas)
970
+ self._tree_canvas['height'] = 200
971
+ else:
972
+ self._tree_canvas = None
973
+
974
+ # Do some analysis to figure out how big the window should be
975
+ self._analyze()
976
+ self.draw()
977
+ self._resize()
978
+ self._grow()
979
+
980
+ # Set up the configure callback, which will be called whenever
981
+ # the window is resized.
982
+ self._chart_canvas.bind('<Configure>', self._configure)
983
+
984
+ def _init_fonts(self, root):
985
+ self._boldfont = Font(family='helvetica', weight='bold',
986
+ size=self._fontsize)
987
+ self._font = Font(family='helvetica',
988
+ size=self._fontsize)
989
+ # See: <http://www.astro.washington.edu/owen/ROTKFolklore.html>
990
+ self._sysfont = Font(font=Button()["font"])
991
+ root.option_add("*Font", self._sysfont)
992
+
993
+ def _sb_canvas(self, root, expand='y',
994
+ fill='both', side='bottom'):
995
+ """
996
+ Helper for __init__: construct a canvas with a scrollbar.
997
+ """
998
+ cframe = Frame(root, relief='sunk', border=2)
999
+ cframe.pack(fill=fill, expand=expand, side=side)
1000
+ canvas = Canvas(cframe, background='#e0e0e0')
1001
+
1002
+ # Give the canvas a scrollbar.
1003
+ sb = Scrollbar(cframe, orient='vertical')
1004
+ sb.pack(side='right', fill='y')
1005
+ canvas.pack(side='left', fill=fill, expand='yes')
1006
+
1007
+ # Connect the scrollbars to the canvas.
1008
+ sb['command']= canvas.yview
1009
+ canvas['yscrollcommand'] = sb.set
1010
+
1011
+ return (sb, canvas)
1012
+
1013
+ def scroll_up(self, *e):
1014
+ self._chart_canvas.yview('scroll', -1, 'units')
1015
+
1016
+ def scroll_down(self, *e):
1017
+ self._chart_canvas.yview('scroll', 1, 'units')
1018
+
1019
+ def page_up(self, *e):
1020
+ self._chart_canvas.yview('scroll', -1, 'pages')
1021
+
1022
+ def page_down(self, *e):
1023
+ self._chart_canvas.yview('scroll', 1, 'pages')
1024
+
1025
+ def _grow(self):
1026
+ """
1027
+ Grow the window, if necessary
1028
+ """
1029
+ # Grow, if need-be
1030
+ N = self._chart.num_leaves()
1031
+ width = max(int(self._chart_canvas['width']),
1032
+ N * self._unitsize + ChartView._MARGIN * 2 )
1033
+
1034
+ # It won't resize without the second (height) line, but I
1035
+ # don't understand why not.
1036
+ self._chart_canvas.configure(width=width)
1037
+ self._chart_canvas.configure(height=self._chart_canvas['height'])
1038
+
1039
+ self._unitsize = (width - 2*ChartView._MARGIN) / N
1040
+
1041
+ # Reset the height for the sentence window.
1042
+ if self._sentence_canvas is not None:
1043
+ self._sentence_canvas['height'] = self._sentence_height
1044
+
1045
+ def set_font_size(self, size):
1046
+ self._font.configure(size=-abs(size))
1047
+ self._boldfont.configure(size=-abs(size))
1048
+ self._sysfont.configure(size=-abs(size))
1049
+ self._analyze()
1050
+ self._grow()
1051
+ self.draw()
1052
+
1053
+ def get_font_size(self):
1054
+ return abs(self._fontsize)
1055
+
1056
+ def _configure(self, e):
1057
+ """
1058
+ The configure callback. This is called whenever the window is
1059
+ resized. It is also called when the window is first mapped.
1060
+ It figures out the unit size, and redraws the contents of each
1061
+ canvas.
1062
+ """
1063
+ N = self._chart.num_leaves()
1064
+ self._unitsize = (e.width - 2*ChartView._MARGIN) / N
1065
+ self.draw()
1066
+
1067
+ def update(self, chart=None):
1068
+ """
1069
+ Draw any edges that have not been drawn. This is typically
1070
+ called when a after modifies the canvas that a CanvasView is
1071
+ displaying. ``update`` will cause any edges that have been
1072
+ added to the chart to be drawn.
1073
+
1074
+ If update is given a ``chart`` argument, then it will replace
1075
+ the current chart with the given chart.
1076
+ """
1077
+ if chart is not None:
1078
+ self._chart = chart
1079
+ self._edgelevels = []
1080
+ self._marks = {}
1081
+ self._analyze()
1082
+ self._grow()
1083
+ self.draw()
1084
+ self.erase_tree()
1085
+ self._resize()
1086
+ else:
1087
+ for edge in self._chart:
1088
+ if edge not in self._edgetags:
1089
+ self._add_edge(edge)
1090
+ self._resize()
1091
+
1092
+
1093
+ def _edge_conflict(self, edge, lvl):
1094
+ """
1095
+ Return True if the given edge overlaps with any edge on the given
1096
+ level. This is used by _add_edge to figure out what level a
1097
+ new edge should be added to.
1098
+ """
1099
+ (s1, e1) = edge.span()
1100
+ for otheredge in self._edgelevels[lvl]:
1101
+ (s2, e2) = otheredge.span()
1102
+ if (s1 <= s2 < e1) or (s2 <= s1 < e2) or (s1==s2==e1==e2):
1103
+ return True
1104
+ return False
1105
+
1106
+ def _analyze_edge(self, edge):
1107
+ """
1108
+ Given a new edge, recalculate:
1109
+
1110
+ - _text_height
1111
+ - _unitsize (if the edge text is too big for the current
1112
+ _unitsize, then increase _unitsize)
1113
+ """
1114
+ c = self._chart_canvas
1115
+
1116
+ if isinstance(edge, TreeEdge):
1117
+ lhs = edge.lhs()
1118
+ rhselts = []
1119
+ for elt in edge.rhs():
1120
+ if isinstance(elt, Nonterminal):
1121
+ rhselts.append(str(elt.symbol()))
1122
+ else:
1123
+ rhselts.append(repr(elt))
1124
+ rhs = " ".join(rhselts)
1125
+ else:
1126
+ lhs = edge.lhs()
1127
+ rhs = ''
1128
+
1129
+ for s in (lhs, rhs):
1130
+ tag = c.create_text(0,0, text=s,
1131
+ font=self._boldfont,
1132
+ anchor='nw', justify='left')
1133
+ bbox = c.bbox(tag)
1134
+ c.delete(tag)
1135
+ width = bbox[2] #+ ChartView._LEAF_SPACING
1136
+ edgelen = max(edge.length(), 1)
1137
+ self._unitsize = max(self._unitsize, width/edgelen)
1138
+ self._text_height = max(self._text_height, bbox[3] - bbox[1])
1139
+
1140
+ def _add_edge(self, edge, minlvl=0):
1141
+ """
1142
+ Add a single edge to the ChartView:
1143
+
1144
+ - Call analyze_edge to recalculate display parameters
1145
+ - Find an available level
1146
+ - Call _draw_edge
1147
+ """
1148
+ # Do NOT show leaf edges in the chart.
1149
+ if isinstance(edge, LeafEdge): return
1150
+
1151
+ if edge in self._edgetags: return
1152
+ self._analyze_edge(edge)
1153
+ self._grow()
1154
+
1155
+ if not self._compact:
1156
+ self._edgelevels.append([edge])
1157
+ lvl = len(self._edgelevels)-1
1158
+ self._draw_edge(edge, lvl)
1159
+ self._resize()
1160
+ return
1161
+
1162
+ # Figure out what level to draw the edge on.
1163
+ lvl = 0
1164
+ while True:
1165
+ # If this level doesn't exist yet, create it.
1166
+ while lvl >= len(self._edgelevels):
1167
+ self._edgelevels.append([])
1168
+ self._resize()
1169
+
1170
+ # Check if we can fit the edge in this level.
1171
+ if lvl>=minlvl and not self._edge_conflict(edge, lvl):
1172
+ # Go ahead and draw it.
1173
+ self._edgelevels[lvl].append(edge)
1174
+ break
1175
+
1176
+ # Try the next level.
1177
+ lvl += 1
1178
+
1179
+ self._draw_edge(edge, lvl)
1180
+
1181
+ def view_edge(self, edge):
1182
+ level = None
1183
+ for i in range(len(self._edgelevels)):
1184
+ if edge in self._edgelevels[i]:
1185
+ level = i
1186
+ break
1187
+ if level is None: return
1188
+ # Try to view the new edge..
1189
+ y = (level+1) * self._chart_level_size
1190
+ dy = self._text_height + 10
1191
+ self._chart_canvas.yview('moveto', 1.0)
1192
+ if self._chart_height != 0:
1193
+ self._chart_canvas.yview('moveto',
1194
+ (y-dy)/self._chart_height)
1195
+
1196
+ def _draw_edge(self, edge, lvl):
1197
+ """
1198
+ Draw a single edge on the ChartView.
1199
+ """
1200
+ c = self._chart_canvas
1201
+
1202
+ # Draw the arrow.
1203
+ x1 = (edge.start() * self._unitsize + ChartView._MARGIN)
1204
+ x2 = (edge.end() * self._unitsize + ChartView._MARGIN)
1205
+ if x2 == x1: x2 += max(4, self._unitsize/5)
1206
+ y = (lvl+1) * self._chart_level_size
1207
+ linetag = c.create_line(x1, y, x2, y, arrow='last', width=3)
1208
+
1209
+ # Draw a label for the edge.
1210
+ if isinstance(edge, TreeEdge):
1211
+ rhs = []
1212
+ for elt in edge.rhs():
1213
+ if isinstance(elt, Nonterminal):
1214
+ rhs.append(str(elt.symbol()))
1215
+ else:
1216
+ rhs.append(repr(elt))
1217
+ pos = edge.dot()
1218
+ else:
1219
+ rhs = []
1220
+ pos = 0
1221
+
1222
+ rhs1 = " ".join(rhs[:pos])
1223
+ rhs2 = " ".join(rhs[pos:])
1224
+ rhstag1 = c.create_text(x1+3, y, text=rhs1,
1225
+ font=self._font,
1226
+ anchor='nw')
1227
+ dotx = c.bbox(rhstag1)[2] + 6
1228
+ doty = (c.bbox(rhstag1)[1]+c.bbox(rhstag1)[3])/2
1229
+ dottag = c.create_oval(dotx-2, doty-2, dotx+2, doty+2)
1230
+ rhstag2 = c.create_text(dotx+6, y, text=rhs2,
1231
+ font=self._font,
1232
+ anchor='nw')
1233
+ lhstag = c.create_text((x1+x2)/2, y, text=str(edge.lhs()),
1234
+ anchor='s',
1235
+ font=self._boldfont)
1236
+
1237
+ # Keep track of the edge's tags.
1238
+ self._edgetags[edge] = (linetag, rhstag1,
1239
+ dottag, rhstag2, lhstag)
1240
+
1241
+ # Register a callback for clicking on the edge.
1242
+ def cb(event, self=self, edge=edge):
1243
+ self._fire_callbacks('select', edge)
1244
+ c.tag_bind(rhstag1, '<Button-1>', cb)
1245
+ c.tag_bind(rhstag2, '<Button-1>', cb)
1246
+ c.tag_bind(linetag, '<Button-1>', cb)
1247
+ c.tag_bind(dottag, '<Button-1>', cb)
1248
+ c.tag_bind(lhstag, '<Button-1>', cb)
1249
+
1250
+ self._color_edge(edge)
1251
+
1252
+ def _color_edge(self, edge, linecolor=None, textcolor=None):
1253
+ """
1254
+ Color in an edge with the given colors.
1255
+ If no colors are specified, use intelligent defaults
1256
+ (dependent on selection, etc.)
1257
+ """
1258
+ if edge not in self._edgetags: return
1259
+ c = self._chart_canvas
1260
+
1261
+ if linecolor is not None and textcolor is not None:
1262
+ if edge in self._marks:
1263
+ linecolor = self._marks[edge]
1264
+ tags = self._edgetags[edge]
1265
+ c.itemconfig(tags[0], fill=linecolor)
1266
+ c.itemconfig(tags[1], fill=textcolor)
1267
+ c.itemconfig(tags[2], fill=textcolor,
1268
+ outline=textcolor)
1269
+ c.itemconfig(tags[3], fill=textcolor)
1270
+ c.itemconfig(tags[4], fill=textcolor)
1271
+ return
1272
+ else:
1273
+ N = self._chart.num_leaves()
1274
+ if edge in self._marks:
1275
+ self._color_edge(self._marks[edge])
1276
+ if (edge.is_complete() and edge.span() == (0, N)):
1277
+ self._color_edge(edge, '#084', '#042')
1278
+ elif isinstance(edge, LeafEdge):
1279
+ self._color_edge(edge, '#48c', '#246')
1280
+ else:
1281
+ self._color_edge(edge, '#00f', '#008')
1282
+
1283
+ def mark_edge(self, edge, mark='#0df'):
1284
+ """
1285
+ Mark an edge
1286
+ """
1287
+ self._marks[edge] = mark
1288
+ self._color_edge(edge)
1289
+
1290
+ def unmark_edge(self, edge=None):
1291
+ """
1292
+ Unmark an edge (or all edges)
1293
+ """
1294
+ if edge is None:
1295
+ old_marked_edges = list(self._marks.keys())
1296
+ self._marks = {}
1297
+ for edge in old_marked_edges:
1298
+ self._color_edge(edge)
1299
+ else:
1300
+ del self._marks[edge]
1301
+ self._color_edge(edge)
1302
+
1303
+ def markonly_edge(self, edge, mark='#0df'):
1304
+ self.unmark_edge()
1305
+ self.mark_edge(edge, mark)
1306
+
1307
+ def _analyze(self):
1308
+ """
1309
+ Analyze the sentence string, to figure out how big a unit needs
1310
+ to be, How big the tree should be, etc.
1311
+ """
1312
+ # Figure out the text height and the unit size.
1313
+ unitsize = 70 # min unitsize
1314
+ text_height = 0
1315
+ c = self._chart_canvas
1316
+
1317
+ # Check against all tokens
1318
+ for leaf in self._chart.leaves():
1319
+ tag = c.create_text(0,0, text=repr(leaf),
1320
+ font=self._font,
1321
+ anchor='nw', justify='left')
1322
+ bbox = c.bbox(tag)
1323
+ c.delete(tag)
1324
+ width = bbox[2] + ChartView._LEAF_SPACING
1325
+ unitsize = max(width, unitsize)
1326
+ text_height = max(text_height, bbox[3] - bbox[1])
1327
+
1328
+ self._unitsize = unitsize
1329
+ self._text_height = text_height
1330
+ self._sentence_height = (self._text_height +
1331
+ 2*ChartView._MARGIN)
1332
+
1333
+ # Check against edges.
1334
+ for edge in self._chart.edges():
1335
+ self._analyze_edge(edge)
1336
+
1337
+ # Size of chart levels
1338
+ self._chart_level_size = self._text_height * 2
1339
+
1340
+ # Default tree size..
1341
+ self._tree_height = (3 * (ChartView._TREE_LEVEL_SIZE +
1342
+ self._text_height))
1343
+
1344
+ # Resize the scrollregions.
1345
+ self._resize()
1346
+
1347
+ def _resize(self):
1348
+ """
1349
+ Update the scroll-regions for each canvas. This ensures that
1350
+ everything is within a scroll-region, so the user can use the
1351
+ scrollbars to view the entire display. This does *not*
1352
+ resize the window.
1353
+ """
1354
+ c = self._chart_canvas
1355
+
1356
+ # Reset the chart scroll region
1357
+ width = ( self._chart.num_leaves() * self._unitsize +
1358
+ ChartView._MARGIN * 2 )
1359
+
1360
+ levels = len(self._edgelevels)
1361
+ self._chart_height = (levels+2)*self._chart_level_size
1362
+ c['scrollregion']=(0,0,width,self._chart_height)
1363
+
1364
+ # Reset the tree scroll region
1365
+ if self._tree_canvas:
1366
+ self._tree_canvas['scrollregion'] = (0, 0, width,
1367
+ self._tree_height)
1368
+
1369
+ def _draw_loclines(self):
1370
+ """
1371
+ Draw location lines. These are vertical gridlines used to
1372
+ show where each location unit is.
1373
+ """
1374
+ BOTTOM = 50000
1375
+ c1 = self._tree_canvas
1376
+ c2 = self._sentence_canvas
1377
+ c3 = self._chart_canvas
1378
+ margin = ChartView._MARGIN
1379
+ self._loclines = []
1380
+ for i in range(0, self._chart.num_leaves()+1):
1381
+ x = i*self._unitsize + margin
1382
+
1383
+ if c1:
1384
+ t1=c1.create_line(x, 0, x, BOTTOM)
1385
+ c1.tag_lower(t1)
1386
+ if c2:
1387
+ t2=c2.create_line(x, 0, x, self._sentence_height)
1388
+ c2.tag_lower(t2)
1389
+ t3=c3.create_line(x, 0, x, BOTTOM)
1390
+ c3.tag_lower(t3)
1391
+ t4=c3.create_text(x+2, 0, text=repr(i), anchor='nw',
1392
+ font=self._font)
1393
+ c3.tag_lower(t4)
1394
+ #if i % 4 == 0:
1395
+ # if c1: c1.itemconfig(t1, width=2, fill='gray60')
1396
+ # if c2: c2.itemconfig(t2, width=2, fill='gray60')
1397
+ # c3.itemconfig(t3, width=2, fill='gray60')
1398
+ if i % 2 == 0:
1399
+ if c1: c1.itemconfig(t1, fill='gray60')
1400
+ if c2: c2.itemconfig(t2, fill='gray60')
1401
+ c3.itemconfig(t3, fill='gray60')
1402
+ else:
1403
+ if c1: c1.itemconfig(t1, fill='gray80')
1404
+ if c2: c2.itemconfig(t2, fill='gray80')
1405
+ c3.itemconfig(t3, fill='gray80')
1406
+
1407
+ def _draw_sentence(self):
1408
+ """Draw the sentence string."""
1409
+ if self._chart.num_leaves() == 0: return
1410
+ c = self._sentence_canvas
1411
+ margin = ChartView._MARGIN
1412
+ y = ChartView._MARGIN
1413
+
1414
+ for i, leaf in enumerate(self._chart.leaves()):
1415
+ x1 = i * self._unitsize + margin
1416
+ x2 = x1 + self._unitsize
1417
+ x = (x1+x2)/2
1418
+ tag = c.create_text(x, y, text=repr(leaf),
1419
+ font=self._font,
1420
+ anchor='n', justify='left')
1421
+ bbox = c.bbox(tag)
1422
+ rt=c.create_rectangle(x1+2, bbox[1]-(ChartView._LEAF_SPACING/2),
1423
+ x2-2, bbox[3]+(ChartView._LEAF_SPACING/2),
1424
+ fill='#f0f0f0', outline='#f0f0f0')
1425
+ c.tag_lower(rt)
1426
+
1427
+ def erase_tree(self):
1428
+ for tag in self._tree_tags: self._tree_canvas.delete(tag)
1429
+ self._treetoks = []
1430
+ self._treetoks_edge = None
1431
+ self._treetoks_index = 0
1432
+
1433
+ def draw_tree(self, edge=None):
1434
+ if edge is None and self._treetoks_edge is None: return
1435
+ if edge is None: edge = self._treetoks_edge
1436
+
1437
+ # If it's a new edge, then get a new list of treetoks.
1438
+ if self._treetoks_edge != edge:
1439
+ self._treetoks = [t for t in self._chart.trees(edge)
1440
+ if isinstance(t, Tree)]
1441
+ self._treetoks_edge = edge
1442
+ self._treetoks_index = 0
1443
+
1444
+ # Make sure there's something to draw.
1445
+ if len(self._treetoks) == 0: return
1446
+
1447
+ # Erase the old tree.
1448
+ for tag in self._tree_tags: self._tree_canvas.delete(tag)
1449
+
1450
+ # Draw the new tree.
1451
+ tree = self._treetoks[self._treetoks_index]
1452
+ self._draw_treetok(tree, edge.start())
1453
+
1454
+ # Show how many trees are available for the edge.
1455
+ self._draw_treecycle()
1456
+
1457
+ # Update the scroll region.
1458
+ w = self._chart.num_leaves()*self._unitsize+2*ChartView._MARGIN
1459
+ h = tree.height() * (ChartView._TREE_LEVEL_SIZE+self._text_height)
1460
+ self._tree_canvas['scrollregion'] = (0, 0, w, h)
1461
+
1462
+ def cycle_tree(self):
1463
+ self._treetoks_index = (self._treetoks_index+1)%len(self._treetoks)
1464
+ self.draw_tree(self._treetoks_edge)
1465
+
1466
+ def _draw_treecycle(self):
1467
+ if len(self._treetoks) <= 1: return
1468
+
1469
+ # Draw the label.
1470
+ label = '%d Trees' % len(self._treetoks)
1471
+ c = self._tree_canvas
1472
+ margin = ChartView._MARGIN
1473
+ right = self._chart.num_leaves()*self._unitsize+margin-2
1474
+ tag = c.create_text(right, 2, anchor='ne', text=label,
1475
+ font=self._boldfont)
1476
+ self._tree_tags.append(tag)
1477
+ _, _, _, y = c.bbox(tag)
1478
+
1479
+ # Draw the triangles.
1480
+ for i in range(len(self._treetoks)):
1481
+ x = right - 20*(len(self._treetoks)-i-1)
1482
+ if i == self._treetoks_index: fill = '#084'
1483
+ else: fill = '#fff'
1484
+ tag = c.create_polygon(x, y+10, x-5, y, x-10, y+10,
1485
+ fill=fill, outline='black')
1486
+ self._tree_tags.append(tag)
1487
+
1488
+ # Set up a callback: show the tree if they click on its
1489
+ # triangle.
1490
+ def cb(event, self=self, i=i):
1491
+ self._treetoks_index = i
1492
+ self.draw_tree()
1493
+ c.tag_bind(tag, '<Button-1>', cb)
1494
+
1495
+ def _draw_treetok(self, treetok, index, depth=0):
1496
+ """
1497
+ :param index: The index of the first leaf in the tree.
1498
+ :return: The index of the first leaf after the tree.
1499
+ """
1500
+ c = self._tree_canvas
1501
+ margin = ChartView._MARGIN
1502
+
1503
+ # Draw the children
1504
+ child_xs = []
1505
+ for child in treetok:
1506
+ if isinstance(child, Tree):
1507
+ child_x, index = self._draw_treetok(child, index, depth+1)
1508
+ child_xs.append(child_x)
1509
+ else:
1510
+ child_xs.append((2*index+1)*self._unitsize/2 + margin)
1511
+ index += 1
1512
+
1513
+ # If we have children, then get the node's x by averaging their
1514
+ # node x's. Otherwise, make room for ourselves.
1515
+ if child_xs:
1516
+ nodex = sum(child_xs)/len(child_xs)
1517
+ else:
1518
+ # [XX] breaks for null productions.
1519
+ nodex = (2*index+1)*self._unitsize/2 + margin
1520
+ index += 1
1521
+
1522
+ # Draw the node
1523
+ nodey = depth * (ChartView._TREE_LEVEL_SIZE + self._text_height)
1524
+ tag = c.create_text(nodex, nodey, anchor='n', justify='center',
1525
+ text=str(treetok.label()), fill='#042',
1526
+ font=self._boldfont)
1527
+ self._tree_tags.append(tag)
1528
+
1529
+ # Draw lines to the children.
1530
+ childy = nodey + ChartView._TREE_LEVEL_SIZE + self._text_height
1531
+ for childx, child in zip(child_xs, treetok):
1532
+ if isinstance(child, Tree) and child:
1533
+ # A "real" tree token:
1534
+ tag = c.create_line(nodex, nodey + self._text_height,
1535
+ childx, childy, width=2, fill='#084')
1536
+ self._tree_tags.append(tag)
1537
+ if isinstance(child, Tree) and not child:
1538
+ # An unexpanded tree token:
1539
+ tag = c.create_line(nodex, nodey + self._text_height,
1540
+ childx, childy, width=2,
1541
+ fill='#048', dash='2 3')
1542
+ self._tree_tags.append(tag)
1543
+ if not isinstance(child, Tree):
1544
+ # A leaf:
1545
+ tag = c.create_line(nodex, nodey + self._text_height,
1546
+ childx, 10000, width=2, fill='#084')
1547
+ self._tree_tags.append(tag)
1548
+
1549
+ return nodex, index
1550
+
1551
+ def draw(self):
1552
+ """
1553
+ Draw everything (from scratch).
1554
+ """
1555
+ if self._tree_canvas:
1556
+ self._tree_canvas.delete('all')
1557
+ self.draw_tree()
1558
+
1559
+ if self._sentence_canvas:
1560
+ self._sentence_canvas.delete('all')
1561
+ self._draw_sentence()
1562
+
1563
+ self._chart_canvas.delete('all')
1564
+ self._edgetags = {}
1565
+
1566
+ # Redraw any edges we erased.
1567
+ for lvl in range(len(self._edgelevels)):
1568
+ for edge in self._edgelevels[lvl]:
1569
+ self._draw_edge(edge, lvl)
1570
+
1571
+ for edge in self._chart:
1572
+ self._add_edge(edge)
1573
+
1574
+ self._draw_loclines()
1575
+
1576
+ def add_callback(self, event, func):
1577
+ self._callbacks.setdefault(event,{})[func] = 1
1578
+
1579
+ def remove_callback(self, event, func=None):
1580
+ if func is None: del self._callbacks[event]
1581
+ else:
1582
+ try: del self._callbacks[event][func]
1583
+ except: pass
1584
+
1585
+ def _fire_callbacks(self, event, *args):
1586
+ if event not in self._callbacks: return
1587
+ for cb_func in list(self._callbacks[event].keys()): cb_func(*args)
1588
+
1589
+ #######################################################################
1590
+ # Edge Rules
1591
+ #######################################################################
1592
+ # These version of the chart rules only apply to a specific edge.
1593
+ # This lets the user select an edge, and then apply a rule.
1594
+
1595
+ class EdgeRule(object):
1596
+ """
1597
+ To create an edge rule, make an empty base class that uses
1598
+ EdgeRule as the first base class, and the basic rule as the
1599
+ second base class. (Order matters!)
1600
+ """
1601
+ def __init__(self, edge):
1602
+ super = self.__class__.__bases__[1]
1603
+ self._edge = edge
1604
+ self.NUM_EDGES = super.NUM_EDGES-1
1605
+ def apply(self, chart, grammar, *edges):
1606
+ super = self.__class__.__bases__[1]
1607
+ edges += (self._edge,)
1608
+ for e in super.apply(self, chart, grammar, *edges): yield e
1609
+ def __str__(self):
1610
+ super = self.__class__.__bases__[1]
1611
+ return super.__str__(self)
1612
+
1613
+ class TopDownPredictEdgeRule(EdgeRule, TopDownPredictRule):
1614
+ pass
1615
+ class BottomUpEdgeRule(EdgeRule, BottomUpPredictRule):
1616
+ pass
1617
+ class BottomUpLeftCornerEdgeRule(EdgeRule, BottomUpPredictCombineRule):
1618
+ pass
1619
+ class FundamentalEdgeRule(EdgeRule, SingleEdgeFundamentalRule):
1620
+ pass
1621
+
1622
+ #######################################################################
1623
+ # Chart Parser Application
1624
+ #######################################################################
1625
+
1626
+ class ChartParserApp(object):
1627
+ def __init__(self, grammar, tokens, title='Chart Parser Application'):
1628
+ # Initialize the parser
1629
+ self._init_parser(grammar, tokens)
1630
+
1631
+ self._root = None
1632
+ try:
1633
+ # Create the root window.
1634
+ self._root = Tk()
1635
+ self._root.title(title)
1636
+ self._root.bind('<Control-q>', self.destroy)
1637
+
1638
+ # Set up some frames.
1639
+ frame3 = Frame(self._root)
1640
+ frame2 = Frame(self._root)
1641
+ frame1 = Frame(self._root)
1642
+ frame3.pack(side='bottom', fill='none')
1643
+ frame2.pack(side='bottom', fill='x')
1644
+ frame1.pack(side='bottom', fill='both', expand=1)
1645
+
1646
+ self._init_fonts(self._root)
1647
+ self._init_animation()
1648
+ self._init_chartview(frame1)
1649
+ self._init_rulelabel(frame2)
1650
+ self._init_buttons(frame3)
1651
+ self._init_menubar()
1652
+
1653
+ self._matrix = None
1654
+ self._results = None
1655
+
1656
+ # Set up keyboard bindings.
1657
+ self._init_bindings()
1658
+
1659
+ except:
1660
+ print('Error creating Tree View')
1661
+ self.destroy()
1662
+ raise
1663
+
1664
+ def destroy(self, *args):
1665
+ if self._root is None: return
1666
+ self._root.destroy()
1667
+ self._root = None
1668
+
1669
+ def mainloop(self, *args, **kwargs):
1670
+ """
1671
+ Enter the Tkinter mainloop. This function must be called if
1672
+ this demo is created from a non-interactive program (e.g.
1673
+ from a secript); otherwise, the demo will close as soon as
1674
+ the script completes.
1675
+ """
1676
+ if in_idle(): return
1677
+ self._root.mainloop(*args, **kwargs)
1678
+
1679
+ #////////////////////////////////////////////////////////////
1680
+ # Initialization Helpers
1681
+ #////////////////////////////////////////////////////////////
1682
+
1683
+ def _init_parser(self, grammar, tokens):
1684
+ self._grammar = grammar
1685
+ self._tokens = tokens
1686
+ self._reset_parser()
1687
+
1688
+ def _reset_parser(self):
1689
+ self._cp = SteppingChartParser(self._grammar)
1690
+ self._cp.initialize(self._tokens)
1691
+ self._chart = self._cp.chart()
1692
+
1693
+ # Insert LeafEdges before the parsing starts.
1694
+ for _new_edge in LeafInitRule().apply(self._chart, self._grammar):
1695
+ pass
1696
+
1697
+ # The step iterator -- use this to generate new edges
1698
+ self._cpstep = self._cp.step()
1699
+
1700
+ # The currently selected edge
1701
+ self._selection = None
1702
+
1703
+ def _init_fonts(self, root):
1704
+ # See: <http://www.astro.washington.edu/owen/ROTKFolklore.html>
1705
+ self._sysfont = Font(font=Button()["font"])
1706
+ root.option_add("*Font", self._sysfont)
1707
+
1708
+ # TWhat's our font size (default=same as sysfont)
1709
+ self._size = IntVar(root)
1710
+ self._size.set(self._sysfont.cget('size'))
1711
+
1712
+ self._boldfont = Font(family='helvetica', weight='bold',
1713
+ size=self._size.get())
1714
+ self._font = Font(family='helvetica',
1715
+ size=self._size.get())
1716
+
1717
+ def _init_animation(self):
1718
+ # Are we stepping? (default=yes)
1719
+ self._step = IntVar(self._root)
1720
+ self._step.set(1)
1721
+
1722
+ # What's our animation speed (default=fast)
1723
+ self._animate = IntVar(self._root)
1724
+ self._animate.set(3) # Default speed = fast
1725
+
1726
+ # Are we currently animating?
1727
+ self._animating = 0
1728
+
1729
+ def _init_chartview(self, parent):
1730
+ self._cv = ChartView(self._chart, parent,
1731
+ draw_tree=1, draw_sentence=1)
1732
+ self._cv.add_callback('select', self._click_cv_edge)
1733
+
1734
+ def _init_rulelabel(self, parent):
1735
+ ruletxt = 'Last edge generated by:'
1736
+
1737
+ self._rulelabel1 = Label(parent,text=ruletxt,
1738
+ font=self._boldfont)
1739
+ self._rulelabel2 = Label(parent, width=40,
1740
+ relief='groove', anchor='w',
1741
+ font=self._boldfont)
1742
+ self._rulelabel1.pack(side='left')
1743
+ self._rulelabel2.pack(side='left')
1744
+ step = Checkbutton(parent, variable=self._step,
1745
+ text='Step')
1746
+ step.pack(side='right')
1747
+
1748
+ def _init_buttons(self, parent):
1749
+ frame1 = Frame(parent)
1750
+ frame2 = Frame(parent)
1751
+ frame1.pack(side='bottom', fill='x')
1752
+ frame2.pack(side='top', fill='none')
1753
+
1754
+ Button(frame1, text='Reset\nParser',
1755
+ background='#90c0d0', foreground='black',
1756
+ command=self.reset).pack(side='right')
1757
+ # Button(frame1, text='Pause',
1758
+ # background='#90c0d0', foreground='black',
1759
+ # command=self.pause).pack(side='left')
1760
+
1761
+ Button(frame1, text='Top Down\nStrategy',
1762
+ background='#90c0d0', foreground='black',
1763
+ command=self.top_down_strategy).pack(side='left')
1764
+ Button(frame1, text='Bottom Up\nStrategy',
1765
+ background='#90c0d0', foreground='black',
1766
+ command=self.bottom_up_strategy).pack(side='left')
1767
+ Button(frame1, text='Bottom Up\nLeft-Corner Strategy',
1768
+ background='#90c0d0', foreground='black',
1769
+ command=self.bottom_up_leftcorner_strategy).pack(side='left')
1770
+
1771
+ Button(frame2, text='Top Down Init\nRule',
1772
+ background='#90f090', foreground='black',
1773
+ command=self.top_down_init).pack(side='left')
1774
+ Button(frame2, text='Top Down Predict\nRule',
1775
+ background='#90f090', foreground='black',
1776
+ command=self.top_down_predict).pack(side='left')
1777
+ Frame(frame2, width=20).pack(side='left')
1778
+
1779
+ Button(frame2, text='Bottom Up Predict\nRule',
1780
+ background='#90f090', foreground='black',
1781
+ command=self.bottom_up).pack(side='left')
1782
+ Frame(frame2, width=20).pack(side='left')
1783
+
1784
+ Button(frame2, text='Bottom Up Left-Corner\nPredict Rule',
1785
+ background='#90f090', foreground='black',
1786
+ command=self.bottom_up_leftcorner).pack(side='left')
1787
+ Frame(frame2, width=20).pack(side='left')
1788
+
1789
+ Button(frame2, text='Fundamental\nRule',
1790
+ background='#90f090', foreground='black',
1791
+ command=self.fundamental).pack(side='left')
1792
+
1793
+ def _init_bindings(self):
1794
+ self._root.bind('<Up>', self._cv.scroll_up)
1795
+ self._root.bind('<Down>', self._cv.scroll_down)
1796
+ self._root.bind('<Prior>', self._cv.page_up)
1797
+ self._root.bind('<Next>', self._cv.page_down)
1798
+ self._root.bind('<Control-q>', self.destroy)
1799
+ self._root.bind('<Control-x>', self.destroy)
1800
+ self._root.bind('<F1>', self.help)
1801
+
1802
+ self._root.bind('<Control-s>', self.save_chart)
1803
+ self._root.bind('<Control-o>', self.load_chart)
1804
+ self._root.bind('<Control-r>', self.reset)
1805
+
1806
+ self._root.bind('t', self.top_down_strategy)
1807
+ self._root.bind('b', self.bottom_up_strategy)
1808
+ self._root.bind('c', self.bottom_up_leftcorner_strategy)
1809
+ self._root.bind('<space>', self._stop_animation)
1810
+
1811
+ self._root.bind('<Control-g>', self.edit_grammar)
1812
+ self._root.bind('<Control-t>', self.edit_sentence)
1813
+
1814
+ # Animation speed control
1815
+ self._root.bind('-', lambda e,a=self._animate:a.set(1))
1816
+ self._root.bind('=', lambda e,a=self._animate:a.set(2))
1817
+ self._root.bind('+', lambda e,a=self._animate:a.set(3))
1818
+
1819
+ # Step control
1820
+ self._root.bind('s', lambda e,s=self._step:s.set(not s.get()))
1821
+
1822
+ def _init_menubar(self):
1823
+ menubar = Menu(self._root)
1824
+
1825
+ filemenu = Menu(menubar, tearoff=0)
1826
+ filemenu.add_command(label='Save Chart', underline=0,
1827
+ command=self.save_chart, accelerator='Ctrl-s')
1828
+ filemenu.add_command(label='Load Chart', underline=0,
1829
+ command=self.load_chart, accelerator='Ctrl-o')
1830
+ filemenu.add_command(label='Reset Chart', underline=0,
1831
+ command=self.reset, accelerator='Ctrl-r')
1832
+ filemenu.add_separator()
1833
+ filemenu.add_command(label='Save Grammar',
1834
+ command=self.save_grammar)
1835
+ filemenu.add_command(label='Load Grammar',
1836
+ command=self.load_grammar)
1837
+ filemenu.add_separator()
1838
+ filemenu.add_command(label='Exit', underline=1,
1839
+ command=self.destroy, accelerator='Ctrl-x')
1840
+ menubar.add_cascade(label='File', underline=0, menu=filemenu)
1841
+
1842
+ editmenu = Menu(menubar, tearoff=0)
1843
+ editmenu.add_command(label='Edit Grammar', underline=5,
1844
+ command=self.edit_grammar,
1845
+ accelerator='Ctrl-g')
1846
+ editmenu.add_command(label='Edit Text', underline=5,
1847
+ command=self.edit_sentence,
1848
+ accelerator='Ctrl-t')
1849
+ menubar.add_cascade(label='Edit', underline=0, menu=editmenu)
1850
+
1851
+ viewmenu = Menu(menubar, tearoff=0)
1852
+ viewmenu.add_command(label='Chart Matrix', underline=6,
1853
+ command=self.view_matrix)
1854
+ viewmenu.add_command(label='Results', underline=0,
1855
+ command=self.view_results)
1856
+ menubar.add_cascade(label='View', underline=0, menu=viewmenu)
1857
+
1858
+ rulemenu = Menu(menubar, tearoff=0)
1859
+ rulemenu.add_command(label='Top Down Strategy', underline=0,
1860
+ command=self.top_down_strategy,
1861
+ accelerator='t')
1862
+ rulemenu.add_command(label='Bottom Up Strategy', underline=0,
1863
+ command=self.bottom_up_strategy,
1864
+ accelerator='b')
1865
+ rulemenu.add_command(label='Bottom Up Left-Corner Strategy', underline=0,
1866
+ command=self.bottom_up_leftcorner_strategy,
1867
+ accelerator='c')
1868
+ rulemenu.add_separator()
1869
+ rulemenu.add_command(label='Bottom Up Rule',
1870
+ command=self.bottom_up)
1871
+ rulemenu.add_command(label='Bottom Up Left-Corner Rule',
1872
+ command=self.bottom_up_leftcorner)
1873
+ rulemenu.add_command(label='Top Down Init Rule',
1874
+ command=self.top_down_init)
1875
+ rulemenu.add_command(label='Top Down Predict Rule',
1876
+ command=self.top_down_predict)
1877
+ rulemenu.add_command(label='Fundamental Rule',
1878
+ command=self.fundamental)
1879
+ menubar.add_cascade(label='Apply', underline=0, menu=rulemenu)
1880
+
1881
+ animatemenu = Menu(menubar, tearoff=0)
1882
+ animatemenu.add_checkbutton(label="Step", underline=0,
1883
+ variable=self._step,
1884
+ accelerator='s')
1885
+ animatemenu.add_separator()
1886
+ animatemenu.add_radiobutton(label="No Animation", underline=0,
1887
+ variable=self._animate, value=0)
1888
+ animatemenu.add_radiobutton(label="Slow Animation", underline=0,
1889
+ variable=self._animate, value=1,
1890
+ accelerator='-')
1891
+ animatemenu.add_radiobutton(label="Normal Animation", underline=0,
1892
+ variable=self._animate, value=2,
1893
+ accelerator='=')
1894
+ animatemenu.add_radiobutton(label="Fast Animation", underline=0,
1895
+ variable=self._animate, value=3,
1896
+ accelerator='+')
1897
+ menubar.add_cascade(label="Animate", underline=1, menu=animatemenu)
1898
+
1899
+ zoommenu = Menu(menubar, tearoff=0)
1900
+ zoommenu.add_radiobutton(label='Tiny', variable=self._size,
1901
+ underline=0, value=10, command=self.resize)
1902
+ zoommenu.add_radiobutton(label='Small', variable=self._size,
1903
+ underline=0, value=12, command=self.resize)
1904
+ zoommenu.add_radiobutton(label='Medium', variable=self._size,
1905
+ underline=0, value=14, command=self.resize)
1906
+ zoommenu.add_radiobutton(label='Large', variable=self._size,
1907
+ underline=0, value=18, command=self.resize)
1908
+ zoommenu.add_radiobutton(label='Huge', variable=self._size,
1909
+ underline=0, value=24, command=self.resize)
1910
+ menubar.add_cascade(label='Zoom', underline=0, menu=zoommenu)
1911
+
1912
+ helpmenu = Menu(menubar, tearoff=0)
1913
+ helpmenu.add_command(label='About', underline=0,
1914
+ command=self.about)
1915
+ helpmenu.add_command(label='Instructions', underline=0,
1916
+ command=self.help, accelerator='F1')
1917
+ menubar.add_cascade(label='Help', underline=0, menu=helpmenu)
1918
+
1919
+ self._root.config(menu=menubar)
1920
+
1921
+ #////////////////////////////////////////////////////////////
1922
+ # Selection Handling
1923
+ #////////////////////////////////////////////////////////////
1924
+
1925
+ def _click_cv_edge(self, edge):
1926
+ if edge != self._selection:
1927
+ # Clicking on a new edge selects it.
1928
+ self._select_edge(edge)
1929
+ else:
1930
+ # Repeated clicks on one edge cycle its trees.
1931
+ self._cv.cycle_tree()
1932
+ # [XX] this can get confused if animation is running
1933
+ # faster than the callbacks...
1934
+
1935
+ def _select_matrix_edge(self, edge):
1936
+ self._select_edge(edge)
1937
+ self._cv.view_edge(edge)
1938
+
1939
+ def _select_edge(self, edge):
1940
+ self._selection = edge
1941
+ # Update the chart view.
1942
+ self._cv.markonly_edge(edge, '#f00')
1943
+ self._cv.draw_tree(edge)
1944
+ # Update the matrix view.
1945
+ if self._matrix: self._matrix.markonly_edge(edge)
1946
+ if self._matrix: self._matrix.view_edge(edge)
1947
+
1948
+ def _deselect_edge(self):
1949
+ self._selection = None
1950
+ # Update the chart view.
1951
+ self._cv.unmark_edge()
1952
+ self._cv.erase_tree()
1953
+ # Update the matrix view
1954
+ if self._matrix: self._matrix.unmark_edge()
1955
+
1956
+ def _show_new_edge(self, edge):
1957
+ self._display_rule(self._cp.current_chartrule())
1958
+ # Update the chart view.
1959
+ self._cv.update()
1960
+ self._cv.draw_tree(edge)
1961
+ self._cv.markonly_edge(edge, '#0df')
1962
+ self._cv.view_edge(edge)
1963
+ # Update the matrix view.
1964
+ if self._matrix: self._matrix.update()
1965
+ if self._matrix: self._matrix.markonly_edge(edge)
1966
+ if self._matrix: self._matrix.view_edge(edge)
1967
+ # Update the results view.
1968
+ if self._results: self._results.update(edge)
1969
+
1970
+ #////////////////////////////////////////////////////////////
1971
+ # Help/usage
1972
+ #////////////////////////////////////////////////////////////
1973
+
1974
+ def help(self, *e):
1975
+ self._animating = 0
1976
+ # The default font's not very legible; try using 'fixed' instead.
1977
+ try:
1978
+ ShowText(self._root, 'Help: Chart Parser Application',
1979
+ (__doc__ or '').strip(), width=75, font='fixed')
1980
+ except:
1981
+ ShowText(self._root, 'Help: Chart Parser Application',
1982
+ (__doc__ or '').strip(), width=75)
1983
+
1984
+ def about(self, *e):
1985
+ ABOUT = ("NLTK Chart Parser Application\n"+
1986
+ "Written by Edward Loper")
1987
+ showinfo('About: Chart Parser Application', ABOUT)
1988
+
1989
+ #////////////////////////////////////////////////////////////
1990
+ # File Menu
1991
+ #////////////////////////////////////////////////////////////
1992
+
1993
+ CHART_FILE_TYPES = [('Pickle file', '.pickle'),
1994
+ ('All files', '*')]
1995
+ GRAMMAR_FILE_TYPES = [('Plaintext grammar file', '.cfg'),
1996
+ ('Pickle file', '.pickle'),
1997
+ ('All files', '*')]
1998
+
1999
+ def load_chart(self, *args):
2000
+ "Load a chart from a pickle file"
2001
+ filename = askopenfilename(filetypes=self.CHART_FILE_TYPES,
2002
+ defaultextension='.pickle')
2003
+ if not filename: return
2004
+ try:
2005
+ with open(filename, 'rb') as infile:
2006
+ chart = pickle.load(infile)
2007
+ self._chart = chart
2008
+ self._cv.update(chart)
2009
+ if self._matrix: self._matrix.set_chart(chart)
2010
+ if self._matrix: self._matrix.deselect_cell()
2011
+ if self._results: self._results.set_chart(chart)
2012
+ self._cp.set_chart(chart)
2013
+ except Exception as e:
2014
+ raise
2015
+ showerror('Error Loading Chart',
2016
+ 'Unable to open file: %r' % filename)
2017
+
2018
+ def save_chart(self, *args):
2019
+ "Save a chart to a pickle file"
2020
+ filename = asksaveasfilename(filetypes=self.CHART_FILE_TYPES,
2021
+ defaultextension='.pickle')
2022
+ if not filename: return
2023
+ try:
2024
+ with open(filename, 'wb') as outfile:
2025
+ pickle.dump(self._chart, outfile)
2026
+ except Exception as e:
2027
+ raise
2028
+ showerror('Error Saving Chart',
2029
+ 'Unable to open file: %r' % filename)
2030
+
2031
+ def load_grammar(self, *args):
2032
+ "Load a grammar from a pickle file"
2033
+ filename = askopenfilename(filetypes=self.GRAMMAR_FILE_TYPES,
2034
+ defaultextension='.cfg')
2035
+ if not filename: return
2036
+ try:
2037
+ if filename.endswith('.pickle'):
2038
+ with open(filename, 'rb') as infile:
2039
+ grammar = pickle.load(infile)
2040
+ else:
2041
+ with open(filename, 'r') as infile:
2042
+ grammar = CFG.fromstring(infile.read())
2043
+ self.set_grammar(grammar)
2044
+ except Exception as e:
2045
+ showerror('Error Loading Grammar',
2046
+ 'Unable to open file: %r' % filename)
2047
+
2048
+ def save_grammar(self, *args):
2049
+ filename = asksaveasfilename(filetypes=self.GRAMMAR_FILE_TYPES,
2050
+ defaultextension='.cfg')
2051
+ if not filename: return
2052
+ try:
2053
+ if filename.endswith('.pickle'):
2054
+ with open(filename, 'wb') as outfile:
2055
+ pickle.dump((self._chart, self._tokens), outfile)
2056
+ else:
2057
+ with open(filename, 'w') as outfile:
2058
+ prods = self._grammar.productions()
2059
+ start = [p for p in prods if p.lhs() == self._grammar.start()]
2060
+ rest = [p for p in prods if p.lhs() != self._grammar.start()]
2061
+ for prod in start: outfile.write('%s\n' % prod)
2062
+ for prod in rest: outfile.write('%s\n' % prod)
2063
+ except Exception as e:
2064
+ showerror('Error Saving Grammar',
2065
+ 'Unable to open file: %r' % filename)
2066
+
2067
+ def reset(self, *args):
2068
+ self._animating = 0
2069
+ self._reset_parser()
2070
+ self._cv.update(self._chart)
2071
+ if self._matrix: self._matrix.set_chart(self._chart)
2072
+ if self._matrix: self._matrix.deselect_cell()
2073
+ if self._results: self._results.set_chart(self._chart)
2074
+
2075
+ #////////////////////////////////////////////////////////////
2076
+ # Edit
2077
+ #////////////////////////////////////////////////////////////
2078
+
2079
+ def edit_grammar(self, *e):
2080
+ CFGEditor(self._root, self._grammar, self.set_grammar)
2081
+
2082
+ def set_grammar(self, grammar):
2083
+ self._grammar = grammar
2084
+ self._cp.set_grammar(grammar)
2085
+ if self._results: self._results.set_grammar(grammar)
2086
+
2087
+ def edit_sentence(self, *e):
2088
+ sentence = " ".join(self._tokens)
2089
+ title = 'Edit Text'
2090
+ instr = 'Enter a new sentence to parse.'
2091
+ EntryDialog(self._root, sentence, instr, self.set_sentence, title)
2092
+
2093
+ def set_sentence(self, sentence):
2094
+ self._tokens = list(sentence.split())
2095
+ self.reset()
2096
+
2097
+ #////////////////////////////////////////////////////////////
2098
+ # View Menu
2099
+ #////////////////////////////////////////////////////////////
2100
+
2101
+ def view_matrix(self, *e):
2102
+ if self._matrix is not None: self._matrix.destroy()
2103
+ self._matrix = ChartMatrixView(self._root, self._chart)
2104
+ self._matrix.add_callback('select', self._select_matrix_edge)
2105
+
2106
+ def view_results(self, *e):
2107
+ if self._results is not None: self._results.destroy()
2108
+ self._results = ChartResultsView(self._root, self._chart,
2109
+ self._grammar)
2110
+
2111
+ #////////////////////////////////////////////////////////////
2112
+ # Zoom Menu
2113
+ #////////////////////////////////////////////////////////////
2114
+
2115
+ def resize(self):
2116
+ self._animating = 0
2117
+ self.set_font_size(self._size.get())
2118
+
2119
+ def set_font_size(self, size):
2120
+ self._cv.set_font_size(size)
2121
+ self._font.configure(size=-abs(size))
2122
+ self._boldfont.configure(size=-abs(size))
2123
+ self._sysfont.configure(size=-abs(size))
2124
+
2125
+ def get_font_size(self):
2126
+ return abs(self._size.get())
2127
+
2128
+ #////////////////////////////////////////////////////////////
2129
+ # Parsing
2130
+ #////////////////////////////////////////////////////////////
2131
+
2132
+ def apply_strategy(self, strategy, edge_strategy=None):
2133
+ # If we're animating, then stop.
2134
+ if self._animating:
2135
+ self._animating = 0
2136
+ return
2137
+
2138
+ # Clear the rule display & mark.
2139
+ self._display_rule(None)
2140
+ #self._cv.unmark_edge()
2141
+
2142
+ if self._step.get():
2143
+ selection = self._selection
2144
+ if (selection is not None) and (edge_strategy is not None):
2145
+ # Apply the given strategy to the selected edge.
2146
+ self._cp.set_strategy([edge_strategy(selection)])
2147
+ newedge = self._apply_strategy()
2148
+
2149
+ # If it failed, then clear the selection.
2150
+ if newedge is None:
2151
+ self._cv.unmark_edge()
2152
+ self._selection = None
2153
+ else:
2154
+ self._cp.set_strategy(strategy)
2155
+ self._apply_strategy()
2156
+
2157
+ else:
2158
+ self._cp.set_strategy(strategy)
2159
+ if self._animate.get():
2160
+ self._animating = 1
2161
+ self._animate_strategy()
2162
+ else:
2163
+ for edge in self._cpstep:
2164
+ if edge is None: break
2165
+ self._cv.update()
2166
+ if self._matrix: self._matrix.update()
2167
+ if self._results: self._results.update()
2168
+
2169
+ def _stop_animation(self, *e):
2170
+ self._animating = 0
2171
+
2172
+ def _animate_strategy(self, speed=1):
2173
+ if self._animating == 0: return
2174
+ if self._apply_strategy() is not None:
2175
+ if self._animate.get() == 0 or self._step.get() == 1:
2176
+ return
2177
+ if self._animate.get() == 1:
2178
+ self._root.after(3000, self._animate_strategy)
2179
+ elif self._animate.get() == 2:
2180
+ self._root.after(1000, self._animate_strategy)
2181
+ else:
2182
+ self._root.after(20, self._animate_strategy)
2183
+
2184
+ def _apply_strategy(self):
2185
+ new_edge = next(self._cpstep)
2186
+
2187
+ if new_edge is not None:
2188
+ self._show_new_edge(new_edge)
2189
+ return new_edge
2190
+
2191
+ def _display_rule(self, rule):
2192
+ if rule is None:
2193
+ self._rulelabel2['text'] = ''
2194
+ else:
2195
+ name = str(rule)
2196
+ self._rulelabel2['text'] = name
2197
+ size = self._cv.get_font_size()
2198
+
2199
+ #////////////////////////////////////////////////////////////
2200
+ # Parsing Strategies
2201
+ #////////////////////////////////////////////////////////////
2202
+
2203
+ # Basic rules:
2204
+ _TD_INIT = [TopDownInitRule()]
2205
+ _TD_PREDICT = [TopDownPredictRule()]
2206
+ _BU_RULE = [BottomUpPredictRule()]
2207
+ _BU_LC_RULE = [BottomUpPredictCombineRule()]
2208
+ _FUNDAMENTAL = [SingleEdgeFundamentalRule()]
2209
+
2210
+ # Complete strategies:
2211
+ _TD_STRATEGY = _TD_INIT + _TD_PREDICT + _FUNDAMENTAL
2212
+ _BU_STRATEGY = _BU_RULE + _FUNDAMENTAL
2213
+ _BU_LC_STRATEGY = _BU_LC_RULE + _FUNDAMENTAL
2214
+
2215
+ # Button callback functions:
2216
+ def top_down_init(self, *e):
2217
+ self.apply_strategy(self._TD_INIT, None)
2218
+ def top_down_predict(self, *e):
2219
+ self.apply_strategy(self._TD_PREDICT, TopDownPredictEdgeRule)
2220
+ def bottom_up(self, *e):
2221
+ self.apply_strategy(self._BU_RULE, BottomUpEdgeRule)
2222
+ def bottom_up_leftcorner(self, *e):
2223
+ self.apply_strategy(self._BU_LC_RULE, BottomUpLeftCornerEdgeRule)
2224
+ def fundamental(self, *e):
2225
+ self.apply_strategy(self._FUNDAMENTAL, FundamentalEdgeRule)
2226
+ def bottom_up_strategy(self, *e):
2227
+ self.apply_strategy(self._BU_STRATEGY, BottomUpEdgeRule)
2228
+ def bottom_up_leftcorner_strategy(self, *e):
2229
+ self.apply_strategy(self._BU_LC_STRATEGY, BottomUpLeftCornerEdgeRule)
2230
+ def top_down_strategy(self, *e):
2231
+ self.apply_strategy(self._TD_STRATEGY, TopDownPredictEdgeRule)
2232
+
2233
+ def app():
2234
+ grammar = CFG.fromstring("""
2235
+ # Grammatical productions.
2236
+ S -> NP VP
2237
+ VP -> VP PP | V NP | V
2238
+ NP -> Det N | NP PP
2239
+ PP -> P NP
2240
+ # Lexical productions.
2241
+ NP -> 'John' | 'I'
2242
+ Det -> 'the' | 'my' | 'a'
2243
+ N -> 'dog' | 'cookie' | 'table' | 'cake' | 'fork'
2244
+ V -> 'ate' | 'saw'
2245
+ P -> 'on' | 'under' | 'with'
2246
+ """)
2247
+
2248
+ sent = 'John ate the cake on the table with a fork'
2249
+ sent = 'John ate the cake on the table'
2250
+ tokens = list(sent.split())
2251
+
2252
+ print('grammar= (')
2253
+ for rule in grammar.productions():
2254
+ print((' ', repr(rule)+','))
2255
+ print(')')
2256
+ print(('tokens = %r' % tokens))
2257
+ print('Calling "ChartParserApp(grammar, tokens)"...')
2258
+ ChartParserApp(grammar, tokens).mainloop()
2259
+
2260
+ if __name__ == '__main__':
2261
+ app()
2262
+
2263
+ # Chart comparer:
2264
+ #charts = ['/tmp/earley.pickle',
2265
+ # '/tmp/topdown.pickle',
2266
+ # '/tmp/bottomup.pickle']
2267
+ #ChartComparer(*charts).mainloop()
2268
+
2269
+ #import profile
2270
+ #profile.run('demo2()', '/tmp/profile.out')
2271
+ #import pstats
2272
+ #p = pstats.Stats('/tmp/profile.out')
2273
+ #p.strip_dirs().sort_stats('time', 'cum').print_stats(60)
2274
+ #p.strip_dirs().sort_stats('cum', 'time').print_stats(60)
2275
+
2276
+ __all__ = ['app']