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,1910 +1,1910 @@
1
- # Natural Language Toolkit: Logic
2
- #
3
- # Author: Dan Garrette <dhgarrette@gmail.com>
4
- #
5
- # Copyright (C) 2001-2015 NLTK Project
6
- # URL: <http://nltk.org>
7
- # For license information, see LICENSE.TXT
8
-
9
- """
10
- A version of first order predicate logic, built on
11
- top of the typed lambda calculus.
12
- """
13
- from __future__ import print_function, unicode_literals
14
-
15
- import re
16
- import operator
17
- from collections import defaultdict
18
- from functools import reduce
19
-
20
- from nltk.util import Trie
21
- from nltk.internals import Counter
22
- from nltk.compat import (total_ordering, string_types,
23
- python_2_unicode_compatible)
24
-
25
- APP = 'APP'
26
-
27
- _counter = Counter()
28
-
29
- class Tokens(object):
30
- LAMBDA = '\\'; LAMBDA_LIST = ['\\']
31
-
32
- #Quantifiers
33
- EXISTS = 'exists'; EXISTS_LIST = ['some', 'exists', 'exist']
34
- ALL = 'all'; ALL_LIST = ['all', 'forall']
35
-
36
- #Punctuation
37
- DOT = '.'
38
- OPEN = '('
39
- CLOSE = ')'
40
- COMMA = ','
41
-
42
- #Operations
43
- NOT = '-'; NOT_LIST = ['not', '-', '!']
44
- AND = '&'; AND_LIST = ['and', '&', '^']
45
- OR = '|'; OR_LIST = ['or', '|']
46
- IMP = '->'; IMP_LIST = ['implies', '->', '=>']
47
- IFF = '<->'; IFF_LIST = ['iff', '<->', '<=>']
48
- EQ = '='; EQ_LIST = ['=', '==']
49
- NEQ = '!='; NEQ_LIST = ['!=']
50
-
51
- #Collections of tokens
52
- BINOPS = AND_LIST + OR_LIST + IMP_LIST + IFF_LIST
53
- QUANTS = EXISTS_LIST + ALL_LIST
54
- PUNCT = [DOT, OPEN, CLOSE, COMMA]
55
-
56
- TOKENS = BINOPS + EQ_LIST + NEQ_LIST + QUANTS + LAMBDA_LIST + PUNCT + NOT_LIST
57
-
58
- #Special
59
- SYMBOLS = [x for x in TOKENS if re.match(r'^[-\\.(),!&^|>=<]*$', x)]
60
-
61
-
62
- def boolean_ops():
63
- """
64
- Boolean operators
65
- """
66
- names = ["negation", "conjunction", "disjunction", "implication", "equivalence"]
67
- for pair in zip(names, [Tokens.NOT, Tokens.AND, Tokens.OR, Tokens.IMP, Tokens.IFF]):
68
- print("%-15s\t%s" % pair)
69
-
70
- def equality_preds():
71
- """
72
- Equality predicates
73
- """
74
- names = ["equality", "inequality"]
75
- for pair in zip(names, [Tokens.EQ, Tokens.NEQ]):
76
- print("%-15s\t%s" % pair)
77
-
78
- def binding_ops():
79
- """
80
- Binding operators
81
- """
82
- names = ["existential", "universal", "lambda"]
83
- for pair in zip(names, [Tokens.EXISTS, Tokens.ALL, Tokens.LAMBDA]):
84
- print("%-15s\t%s" % pair)
85
-
86
-
87
- @python_2_unicode_compatible
88
- class LogicParser(object):
89
- """A lambda calculus expression parser."""
90
-
91
- def __init__(self, type_check=False):
92
- """
93
- :param type_check: bool should type checking be performed?
94
- to their types.
95
- """
96
- assert isinstance(type_check, bool)
97
-
98
- self._currentIndex = 0
99
- self._buffer = []
100
- self.type_check = type_check
101
-
102
- """A list of tuples of quote characters. The 4-tuple is comprised
103
- of the start character, the end character, the escape character, and
104
- a boolean indicating whether the quotes should be included in the
105
- result. Quotes are used to signify that a token should be treated as
106
- atomic, ignoring any special characters within the token. The escape
107
- character allows the quote end character to be used within the quote.
108
- If True, the boolean indicates that the final token should contain the
109
- quote and escape characters.
110
- This method exists to be overridden"""
111
- self.quote_chars = []
112
-
113
- self.operator_precedence = dict(
114
- [(x,1) for x in Tokens.LAMBDA_LIST] + \
115
- [(x,2) for x in Tokens.NOT_LIST] + \
116
- [(APP,3)] + \
117
- [(x,4) for x in Tokens.EQ_LIST+Tokens.NEQ_LIST] + \
118
- [(x,5) for x in Tokens.QUANTS] + \
119
- [(x,6) for x in Tokens.AND_LIST] + \
120
- [(x,7) for x in Tokens.OR_LIST] + \
121
- [(x,8) for x in Tokens.IMP_LIST] + \
122
- [(x,9) for x in Tokens.IFF_LIST] + \
123
- [(None,10)])
124
- self.right_associated_operations = [APP]
125
-
126
- def parse(self, data, signature=None):
127
- """
128
- Parse the expression.
129
-
130
- :param data: str for the input to be parsed
131
- :param signature: ``dict<str, str>`` that maps variable names to type
132
- strings
133
- :returns: a parsed Expression
134
- """
135
- data = data.rstrip()
136
-
137
- self._currentIndex = 0
138
- self._buffer, mapping = self.process(data)
139
-
140
- try:
141
- result = self.process_next_expression(None)
142
- if self.inRange(0):
143
- raise UnexpectedTokenException(self._currentIndex+1, self.token(0))
144
- except LogicalExpressionException as e:
145
- msg = '%s\n%s\n%s^' % (e, data, ' '*mapping[e.index-1])
146
- raise LogicalExpressionException(None, msg)
147
-
148
- if self.type_check:
149
- result.typecheck(signature)
150
-
151
- return result
152
-
153
- def process(self, data):
154
- """Split the data into tokens"""
155
- out = []
156
- mapping = {}
157
- tokenTrie = Trie(self.get_all_symbols())
158
- token = ''
159
- data_idx = 0
160
- token_start_idx = data_idx
161
- while data_idx < len(data):
162
- cur_data_idx = data_idx
163
- quoted_token, data_idx = self.process_quoted_token(data_idx, data)
164
- if quoted_token:
165
- if not token:
166
- token_start_idx = cur_data_idx
167
- token += quoted_token
168
- continue
169
-
170
- st = tokenTrie
171
- c = data[data_idx]
172
- symbol = ''
173
- while c in st:
174
- symbol += c
175
- st = st[c]
176
- if len(data)-data_idx > len(symbol):
177
- c = data[data_idx+len(symbol)]
178
- else:
179
- break
180
- if Trie.LEAF in st:
181
- #token is a complete symbol
182
- if token:
183
- mapping[len(out)] = token_start_idx
184
- out.append(token)
185
- token = ''
186
- mapping[len(out)] = data_idx
187
- out.append(symbol)
188
- data_idx += len(symbol)
189
- else:
190
- if data[data_idx] in ' \t\n': #any whitespace
191
- if token:
192
- mapping[len(out)] = token_start_idx
193
- out.append(token)
194
- token = ''
195
- else:
196
- if not token:
197
- token_start_idx = data_idx
198
- token += data[data_idx]
199
- data_idx += 1
200
- if token:
201
- mapping[len(out)] = token_start_idx
202
- out.append(token)
203
- mapping[len(out)] = len(data)
204
- mapping[len(out)+1] = len(data)+1
205
- return out, mapping
206
-
207
- def process_quoted_token(self, data_idx, data):
208
- token = ''
209
- c = data[data_idx]
210
- i = data_idx
211
- for start, end, escape, incl_quotes in self.quote_chars:
212
- if c == start:
213
- if incl_quotes:
214
- token += c
215
- i += 1
216
- while data[i] != end:
217
- if data[i] == escape:
218
- if incl_quotes:
219
- token += data[i]
220
- i += 1
221
- if len(data) == i: #if there are no more chars
222
- raise LogicalExpressionException(None, "End of input reached. "
223
- "Escape character [%s] found at end."
224
- % escape)
225
- token += data[i]
226
- else:
227
- token += data[i]
228
- i += 1
229
- if len(data) == i:
230
- raise LogicalExpressionException(None, "End of input reached. "
231
- "Expected: [%s]" % end)
232
- if incl_quotes:
233
- token += data[i]
234
- i += 1
235
- if not token:
236
- raise LogicalExpressionException(None, 'Empty quoted token found')
237
- break
238
- return token, i
239
-
240
- def get_all_symbols(self):
241
- """This method exists to be overridden"""
242
- return Tokens.SYMBOLS
243
-
244
- def inRange(self, location):
245
- """Return TRUE if the given location is within the buffer"""
246
- return self._currentIndex+location < len(self._buffer)
247
-
248
- def token(self, location=None):
249
- """Get the next waiting token. If a location is given, then
250
- return the token at currentIndex+location without advancing
251
- currentIndex; setting it gives lookahead/lookback capability."""
252
- try:
253
- if location is None:
254
- tok = self._buffer[self._currentIndex]
255
- self._currentIndex += 1
256
- else:
257
- tok = self._buffer[self._currentIndex+location]
258
- return tok
259
- except IndexError:
260
- raise ExpectedMoreTokensException(self._currentIndex+1)
261
-
262
- def isvariable(self, tok):
263
- return tok not in Tokens.TOKENS
264
-
265
- def process_next_expression(self, context):
266
- """Parse the next complete expression from the stream and return it."""
267
- try:
268
- tok = self.token()
269
- except ExpectedMoreTokensException:
270
- raise ExpectedMoreTokensException(self._currentIndex+1, message='Expression expected.')
271
-
272
- accum = self.handle(tok, context)
273
-
274
- if not accum:
275
- raise UnexpectedTokenException(self._currentIndex, tok, message='Expression expected.')
276
-
277
- return self.attempt_adjuncts(accum, context)
278
-
279
- def handle(self, tok, context):
280
- """This method is intended to be overridden for logics that
281
- use different operators or expressions"""
282
- if self.isvariable(tok):
283
- return self.handle_variable(tok, context)
284
-
285
- elif tok in Tokens.NOT_LIST:
286
- return self.handle_negation(tok, context)
287
-
288
- elif tok in Tokens.LAMBDA_LIST:
289
- return self.handle_lambda(tok, context)
290
-
291
- elif tok in Tokens.QUANTS:
292
- return self.handle_quant(tok, context)
293
-
294
- elif tok == Tokens.OPEN:
295
- return self.handle_open(tok, context)
296
-
297
- def attempt_adjuncts(self, expression, context):
298
- cur_idx = None
299
- while cur_idx != self._currentIndex: #while adjuncts are added
300
- cur_idx = self._currentIndex
301
- expression = self.attempt_EqualityExpression(expression, context)
302
- expression = self.attempt_ApplicationExpression(expression, context)
303
- expression = self.attempt_BooleanExpression(expression, context)
304
- return expression
305
-
306
- def handle_negation(self, tok, context):
307
- return self.make_NegatedExpression(self.process_next_expression(Tokens.NOT))
308
-
309
- def make_NegatedExpression(self, expression):
310
- return NegatedExpression(expression)
311
-
312
- def handle_variable(self, tok, context):
313
- #It's either: 1) a predicate expression: sees(x,y)
314
- # 2) an application expression: P(x)
315
- # 3) a solo variable: john OR x
316
- accum = self.make_VariableExpression(tok)
317
- if self.inRange(0) and self.token(0) == Tokens.OPEN:
318
- #The predicate has arguments
319
- if not isinstance(accum, FunctionVariableExpression) and \
320
- not isinstance(accum, ConstantExpression):
321
- raise LogicalExpressionException(self._currentIndex,
322
- "'%s' is an illegal predicate name. "
323
- "Individual variables may not be used as "
324
- "predicates." % tok)
325
- self.token() #swallow the Open Paren
326
-
327
- #curry the arguments
328
- accum = self.make_ApplicationExpression(accum, self.process_next_expression(APP))
329
- while self.inRange(0) and self.token(0) == Tokens.COMMA:
330
- self.token() #swallow the comma
331
- accum = self.make_ApplicationExpression(accum, self.process_next_expression(APP))
332
- self.assertNextToken(Tokens.CLOSE)
333
- return accum
334
-
335
- def get_next_token_variable(self, description):
336
- try:
337
- tok = self.token()
338
- except ExpectedMoreTokensException as e:
339
- raise ExpectedMoreTokensException(e.index, 'Variable expected.')
340
- if isinstance(self.make_VariableExpression(tok), ConstantExpression):
341
- raise LogicalExpressionException(self._currentIndex,
342
- "'%s' is an illegal variable name. "
343
- "Constants may not be %s." % (tok, description))
344
- return Variable(tok)
345
-
346
- def handle_lambda(self, tok, context):
347
- # Expression is a lambda expression
348
- if not self.inRange(0):
349
- raise ExpectedMoreTokensException(self._currentIndex+2,
350
- message="Variable and Expression expected following lambda operator.")
351
- vars = [self.get_next_token_variable('abstracted')]
352
- while True:
353
- if not self.inRange(0) or (self.token(0) == Tokens.DOT and not self.inRange(1)):
354
- raise ExpectedMoreTokensException(self._currentIndex+2, message="Expression expected.")
355
- if not self.isvariable(self.token(0)):
356
- break
357
- # Support expressions like: \x y.M == \x.\y.M
358
- vars.append(self.get_next_token_variable('abstracted'))
359
- if self.inRange(0) and self.token(0) == Tokens.DOT:
360
- self.token() #swallow the dot
361
-
362
- accum = self.process_next_expression(tok)
363
- while vars:
364
- accum = self.make_LambdaExpression(vars.pop(), accum)
365
- return accum
366
-
367
- def handle_quant(self, tok, context):
368
- # Expression is a quantified expression: some x.M
369
- factory = self.get_QuantifiedExpression_factory(tok)
370
-
371
- if not self.inRange(0):
372
- raise ExpectedMoreTokensException(self._currentIndex+2,
373
- message="Variable and Expression expected following quantifier '%s'." % tok)
374
- vars = [self.get_next_token_variable('quantified')]
375
- while True:
376
- if not self.inRange(0) or (self.token(0) == Tokens.DOT and not self.inRange(1)):
377
- raise ExpectedMoreTokensException(self._currentIndex+2, message="Expression expected.")
378
- if not self.isvariable(self.token(0)):
379
- break
380
- # Support expressions like: some x y.M == some x.some y.M
381
- vars.append(self.get_next_token_variable('quantified'))
382
- if self.inRange(0) and self.token(0) == Tokens.DOT:
383
- self.token() #swallow the dot
384
-
385
- accum = self.process_next_expression(tok)
386
- while vars:
387
- accum = self.make_QuanifiedExpression(factory, vars.pop(), accum)
388
- return accum
389
-
390
- def get_QuantifiedExpression_factory(self, tok):
391
- """This method serves as a hook for other logic parsers that
392
- have different quantifiers"""
393
- if tok in Tokens.EXISTS_LIST:
394
- return ExistsExpression
395
- elif tok in Tokens.ALL_LIST:
396
- return AllExpression
397
- else:
398
- self.assertToken(tok, Tokens.QUANTS)
399
-
400
- def make_QuanifiedExpression(self, factory, variable, term):
401
- return factory(variable, term)
402
-
403
- def handle_open(self, tok, context):
404
- #Expression is in parens
405
- accum = self.process_next_expression(None)
406
- self.assertNextToken(Tokens.CLOSE)
407
- return accum
408
-
409
- def attempt_EqualityExpression(self, expression, context):
410
- """Attempt to make an equality expression. If the next token is an
411
- equality operator, then an EqualityExpression will be returned.
412
- Otherwise, the parameter will be returned."""
413
- if self.inRange(0):
414
- tok = self.token(0)
415
- if tok in Tokens.EQ_LIST + Tokens.NEQ_LIST and self.has_priority(tok, context):
416
- self.token() #swallow the "=" or "!="
417
- expression = self.make_EqualityExpression(expression, self.process_next_expression(tok))
418
- if tok in Tokens.NEQ_LIST:
419
- expression = self.make_NegatedExpression(expression)
420
- return expression
421
-
422
- def make_EqualityExpression(self, first, second):
423
- """This method serves as a hook for other logic parsers that
424
- have different equality expression classes"""
425
- return EqualityExpression(first, second)
426
-
427
- def attempt_BooleanExpression(self, expression, context):
428
- """Attempt to make a boolean expression. If the next token is a boolean
429
- operator, then a BooleanExpression will be returned. Otherwise, the
430
- parameter will be returned."""
431
- while self.inRange(0):
432
- tok = self.token(0)
433
- factory = self.get_BooleanExpression_factory(tok)
434
- if factory and self.has_priority(tok, context):
435
- self.token() #swallow the operator
436
- expression = self.make_BooleanExpression(factory, expression,
437
- self.process_next_expression(tok))
438
- else:
439
- break
440
- return expression
441
-
442
- def get_BooleanExpression_factory(self, tok):
443
- """This method serves as a hook for other logic parsers that
444
- have different boolean operators"""
445
- if tok in Tokens.AND_LIST:
446
- return AndExpression
447
- elif tok in Tokens.OR_LIST:
448
- return OrExpression
449
- elif tok in Tokens.IMP_LIST:
450
- return ImpExpression
451
- elif tok in Tokens.IFF_LIST:
452
- return IffExpression
453
- else:
454
- return None
455
-
456
- def make_BooleanExpression(self, factory, first, second):
457
- return factory(first, second)
458
-
459
- def attempt_ApplicationExpression(self, expression, context):
460
- """Attempt to make an application expression. The next tokens are
461
- a list of arguments in parens, then the argument expression is a
462
- function being applied to the arguments. Otherwise, return the
463
- argument expression."""
464
- if self.has_priority(APP, context):
465
- if self.inRange(0) and self.token(0) == Tokens.OPEN:
466
- if not isinstance(expression, LambdaExpression) and \
467
- not isinstance(expression, ApplicationExpression) and \
468
- not isinstance(expression, FunctionVariableExpression) and \
469
- not isinstance(expression, ConstantExpression):
470
- raise LogicalExpressionException(self._currentIndex,
471
- ("The function '%s" % expression) +
472
- "' is not a Lambda Expression, an "
473
- "Application Expression, or a "
474
- "functional predicate, so it may "
475
- "not take arguments.")
476
- self.token() #swallow then open paren
477
- #curry the arguments
478
- accum = self.make_ApplicationExpression(expression, self.process_next_expression(APP))
479
- while self.inRange(0) and self.token(0) == Tokens.COMMA:
480
- self.token() #swallow the comma
481
- accum = self.make_ApplicationExpression(accum, self.process_next_expression(APP))
482
- self.assertNextToken(Tokens.CLOSE)
483
- return accum
484
- return expression
485
-
486
- def make_ApplicationExpression(self, function, argument):
487
- return ApplicationExpression(function, argument)
488
-
489
- def make_VariableExpression(self, name):
490
- return VariableExpression(Variable(name))
491
-
492
- def make_LambdaExpression(self, variable, term):
493
- return LambdaExpression(variable, term)
494
-
495
- def has_priority(self, operation, context):
496
- return self.operator_precedence[operation] < self.operator_precedence[context] or \
497
- (operation in self.right_associated_operations and \
498
- self.operator_precedence[operation] == self.operator_precedence[context])
499
-
500
- def assertNextToken(self, expected):
501
- try:
502
- tok = self.token()
503
- except ExpectedMoreTokensException as e:
504
- raise ExpectedMoreTokensException(e.index, message="Expected token '%s'." % expected)
505
-
506
- if isinstance(expected, list):
507
- if tok not in expected:
508
- raise UnexpectedTokenException(self._currentIndex, tok, expected)
509
- else:
510
- if tok != expected:
511
- raise UnexpectedTokenException(self._currentIndex, tok, expected)
512
-
513
- def assertToken(self, tok, expected):
514
- if isinstance(expected, list):
515
- if tok not in expected:
516
- raise UnexpectedTokenException(self._currentIndex, tok, expected)
517
- else:
518
- if tok != expected:
519
- raise UnexpectedTokenException(self._currentIndex, tok, expected)
520
-
521
- def __repr__(self):
522
- if self.inRange(0):
523
- msg = 'Next token: ' + self.token(0)
524
- else:
525
- msg = 'No more tokens'
526
- return '<' + self.__class__.__name__ + ': ' + msg + '>'
527
-
528
-
529
- def read_logic(s, logic_parser=None, encoding=None):
530
- """
531
- Convert a file of First Order Formulas into a list of {Expression}s.
532
-
533
- :param s: the contents of the file
534
- :type s: str
535
- :param logic_parser: The parser to be used to parse the logical expression
536
- :type logic_parser: LogicParser
537
- :param encoding: the encoding of the input string, if it is binary
538
- :type encoding: str
539
- :return: a list of parsed formulas.
540
- :rtype: list(Expression)
541
- """
542
- if encoding is not None:
543
- s = s.decode(encoding)
544
- if logic_parser is None:
545
- logic_parser = LogicParser()
546
-
547
- statements = []
548
- for linenum, line in enumerate(s.splitlines()):
549
- line = line.strip()
550
- if line.startswith('#') or line=='': continue
551
- try:
552
- statements.append(logic_parser.parse(line))
553
- except LogicalExpressionException:
554
- raise ValueError('Unable to parse line %s: %s' % (linenum, line))
555
- return statements
556
-
557
-
558
- @total_ordering
559
- @python_2_unicode_compatible
560
- class Variable(object):
561
- def __init__(self, name):
562
- """
563
- :param name: the name of the variable
564
- """
565
- assert isinstance(name, string_types), "%s is not a string" % name
566
- self.name = name
567
-
568
- def __eq__(self, other):
569
- return isinstance(other, Variable) and self.name == other.name
570
-
571
- def __ne__(self, other):
572
- return not self == other
573
-
574
- def __lt__(self, other):
575
- if not isinstance(other, Variable):
576
- raise TypeError
577
- return self.name < other.name
578
-
579
- def substitute_bindings(self, bindings):
580
- return bindings.get(self, self)
581
-
582
- def __hash__(self):
583
- return hash(self.name)
584
-
585
- def __str__(self):
586
- return self.name
587
-
588
- def __repr__(self):
589
- return "Variable('%s')" % self.name
590
-
591
-
592
- def unique_variable(pattern=None, ignore=None):
593
- """
594
- Return a new, unique variable.
595
-
596
- :param pattern: ``Variable`` that is being replaced. The new variable must
597
- be the same type.
598
- :param term: a set of ``Variable`` objects that should not be returned from
599
- this function.
600
- :rtype: Variable
601
- """
602
- if pattern is not None:
603
- if is_indvar(pattern.name):
604
- prefix = 'z'
605
- elif is_funcvar(pattern.name):
606
- prefix = 'F'
607
- elif is_eventvar(pattern.name):
608
- prefix = 'e0'
609
- else:
610
- assert False, "Cannot generate a unique constant"
611
- else:
612
- prefix = 'z'
613
-
614
- v = Variable("%s%s" % (prefix, _counter.get()))
615
- while ignore is not None and v in ignore:
616
- v = Variable("%s%s" % (prefix, _counter.get()))
617
- return v
618
-
619
- def skolem_function(univ_scope=None):
620
- """
621
- Return a skolem function over the variables in univ_scope
622
- param univ_scope
623
- """
624
- skolem = VariableExpression(Variable('F%s' % _counter.get()))
625
- if univ_scope:
626
- for v in list(univ_scope):
627
- skolem = skolem(VariableExpression(v))
628
- return skolem
629
-
630
-
631
- @python_2_unicode_compatible
632
- class Type(object):
633
- def __repr__(self):
634
- return "%s" % self
635
-
636
- def __hash__(self):
637
- return hash("%s" % self)
638
-
639
- @classmethod
640
- def fromstring(cls, s):
641
- return read_type(s)
642
-
643
- @python_2_unicode_compatible
644
- class ComplexType(Type):
645
- def __init__(self, first, second):
646
- assert(isinstance(first, Type)), "%s is not a Type" % first
647
- assert(isinstance(second, Type)), "%s is not a Type" % second
648
- self.first = first
649
- self.second = second
650
-
651
- def __eq__(self, other):
652
- return isinstance(other, ComplexType) and \
653
- self.first == other.first and \
654
- self.second == other.second
655
-
656
- def __ne__(self, other):
657
- return not self == other
658
-
659
- __hash__ = Type.__hash__
660
-
661
- def matches(self, other):
662
- if isinstance(other, ComplexType):
663
- return self.first.matches(other.first) and \
664
- self.second.matches(other.second)
665
- else:
666
- return self == ANY_TYPE
667
-
668
- def resolve(self, other):
669
- if other == ANY_TYPE:
670
- return self
671
- elif isinstance(other, ComplexType):
672
- f = self.first.resolve(other.first)
673
- s = self.second.resolve(other.second)
674
- if f and s:
675
- return ComplexType(f,s)
676
- else:
677
- return None
678
- elif self == ANY_TYPE:
679
- return other
680
- else:
681
- return None
682
-
683
- def __str__(self):
684
- if self == ANY_TYPE:
685
- return "%s" % ANY_TYPE
686
- else:
687
- return '<%s,%s>' % (self.first, self.second)
688
-
689
- def str(self):
690
- if self == ANY_TYPE:
691
- return ANY_TYPE.str()
692
- else:
693
- return '(%s -> %s)' % (self.first.str(), self.second.str())
694
-
695
- class BasicType(Type):
696
- def __eq__(self, other):
697
- return isinstance(other, BasicType) and ("%s" % self) == ("%s" % other)
698
-
699
- def __ne__(self, other):
700
- return not self == other
701
-
702
- __hash__ = Type.__hash__
703
-
704
- def matches(self, other):
705
- return other == ANY_TYPE or self == other
706
-
707
- def resolve(self, other):
708
- if self.matches(other):
709
- return self
710
- else:
711
- return None
712
-
713
- @python_2_unicode_compatible
714
- class EntityType(BasicType):
715
- def __str__(self):
716
- return 'e'
717
-
718
- def str(self):
719
- return 'IND'
720
-
721
- @python_2_unicode_compatible
722
- class TruthValueType(BasicType):
723
- def __str__(self):
724
- return 't'
725
-
726
- def str(self):
727
- return 'BOOL'
728
-
729
- @python_2_unicode_compatible
730
- class EventType(BasicType):
731
- def __str__(self):
732
- return 'v'
733
-
734
- def str(self):
735
- return 'EVENT'
736
-
737
- @python_2_unicode_compatible
738
- class AnyType(BasicType, ComplexType):
739
- def __init__(self):
740
- pass
741
-
742
- @property
743
- def first(self): return self
744
-
745
- @property
746
- def second(self): return self
747
-
748
- def __eq__(self, other):
749
- return isinstance(other, AnyType) or other.__eq__(self)
750
-
751
- def __ne__(self, other):
752
- return not self == other
753
-
754
- __hash__ = Type.__hash__
755
-
756
- def matches(self, other):
757
- return True
758
-
759
- def resolve(self, other):
760
- return other
761
-
762
- def __str__(self):
763
- return '?'
764
-
765
- def str(self):
766
- return 'ANY'
767
-
768
-
769
- TRUTH_TYPE = TruthValueType()
770
- ENTITY_TYPE = EntityType()
771
- EVENT_TYPE = EventType()
772
- ANY_TYPE = AnyType()
773
-
774
-
775
- def read_type(type_string):
776
- assert isinstance(type_string, string_types)
777
- type_string = type_string.replace(' ', '') #remove spaces
778
-
779
- if type_string[0] == '<':
780
- assert type_string[-1] == '>'
781
- paren_count = 0
782
- for i,char in enumerate(type_string):
783
- if char == '<':
784
- paren_count += 1
785
- elif char == '>':
786
- paren_count -= 1
787
- assert paren_count > 0
788
- elif char == ',':
789
- if paren_count == 1:
790
- break
791
- return ComplexType(read_type(type_string[1 :i ]),
792
- read_type(type_string[i+1:-1]))
793
- elif type_string[0] == "%s" % ENTITY_TYPE:
794
- return ENTITY_TYPE
795
- elif type_string[0] == "%s" % TRUTH_TYPE:
796
- return TRUTH_TYPE
797
- elif type_string[0] == "%s" % ANY_TYPE:
798
- return ANY_TYPE
799
- else:
800
- raise LogicalExpressionException("Unexpected character: '%s'." % type_string[0])
801
-
802
-
803
- class TypeException(Exception):
804
- def __init__(self, msg):
805
- Exception.__init__(self, msg)
806
-
807
- class InconsistentTypeHierarchyException(TypeException):
808
- def __init__(self, variable, expression=None):
809
- if expression:
810
- msg = "The variable '%s' was found in multiple places with different"\
811
- " types in '%s'." % (variable, expression)
812
- else:
813
- msg = "The variable '%s' was found in multiple places with different"\
814
- " types." % (variable)
815
- Exception.__init__(self, msg)
816
-
817
- class TypeResolutionException(TypeException):
818
- def __init__(self, expression, other_type):
819
- Exception.__init__(self, "The type of '%s', '%s', cannot be "
820
- "resolved with type '%s'" % \
821
- (expression, expression.type, other_type))
822
-
823
- class IllegalTypeException(TypeException):
824
- def __init__(self, expression, other_type, allowed_type):
825
- Exception.__init__(self, "Cannot set type of %s '%s' to '%s'; "
826
- "must match type '%s'." %
827
- (expression.__class__.__name__, expression,
828
- other_type, allowed_type))
829
-
830
-
831
- def typecheck(expressions, signature=None):
832
- """
833
- Ensure correct typing across a collection of ``Expression`` objects.
834
- :param expressions: a collection of expressions
835
- :param signature: dict that maps variable names to types (or string
836
- representations of types)
837
- """
838
- #typecheck and create master signature
839
- for expression in expressions:
840
- signature = expression.typecheck(signature)
841
- #apply master signature to all expressions
842
- for expression in expressions[:-1]:
843
- expression.typecheck(signature)
844
- return signature
845
-
846
-
847
- class SubstituteBindingsI(object):
848
- """
849
- An interface for classes that can perform substitutions for
850
- variables.
851
- """
852
- def substitute_bindings(self, bindings):
853
- """
854
- :return: The object that is obtained by replacing
855
- each variable bound by ``bindings`` with its values.
856
- Aliases are already resolved. (maybe?)
857
- :rtype: (any)
858
- """
859
- raise NotImplementedError()
860
-
861
- def variables(self):
862
- """
863
- :return: A list of all variables in this object.
864
- """
865
- raise NotImplementedError()
866
-
867
-
868
- @python_2_unicode_compatible
869
- class Expression(SubstituteBindingsI):
870
- """This is the base abstract object for all logical expressions"""
871
-
872
- _logic_parser = LogicParser()
873
- _type_checking_logic_parser = LogicParser(type_check=True)
874
-
875
- @classmethod
876
- def fromstring(cls, s, type_check=False, signature=None):
877
- if type_check:
878
- return cls._type_checking_logic_parser.parse(s, signature)
879
- else:
880
- return cls._logic_parser.parse(s, signature)
881
-
882
- def __call__(self, other, *additional):
883
- accum = self.applyto(other)
884
- for a in additional:
885
- accum = accum(a)
886
- return accum
887
-
888
- def applyto(self, other):
889
- assert isinstance(other, Expression), "%s is not an Expression" % other
890
- return ApplicationExpression(self, other)
891
-
892
- def __neg__(self):
893
- return NegatedExpression(self)
894
-
895
- def negate(self):
896
- """If this is a negated expression, remove the negation.
897
- Otherwise add a negation."""
898
- return -self
899
-
900
- def __and__(self, other):
901
- if not isinstance(other, Expression):
902
- raise TypeError("%s is not an Expression" % other)
903
- return AndExpression(self, other)
904
-
905
- def __or__(self, other):
906
- if not isinstance(other, Expression):
907
- raise TypeError("%s is not an Expression" % other)
908
- return OrExpression(self, other)
909
-
910
- def __gt__(self, other):
911
- if not isinstance(other, Expression):
912
- raise TypeError("%s is not an Expression" % other)
913
- return ImpExpression(self, other)
914
-
915
- def __lt__(self, other):
916
- if not isinstance(other, Expression):
917
- raise TypeError("%s is not an Expression" % other)
918
- return IffExpression(self, other)
919
-
920
- def __eq__(self, other):
921
- raise NotImplementedError()
922
-
923
- def __ne__(self, other):
924
- return not self == other
925
-
926
- def equiv(self, other, prover=None):
927
- """
928
- Check for logical equivalence.
929
- Pass the expression (self <-> other) to the theorem prover.
930
- If the prover says it is valid, then the self and other are equal.
931
-
932
- :param other: an ``Expression`` to check equality against
933
- :param prover: a ``nltk.inference.api.Prover``
934
- """
935
- assert isinstance(other, Expression), "%s is not an Expression" % other
936
-
937
- if prover is None:
938
- from nltk.inference import Prover9
939
- prover = Prover9()
940
- bicond = IffExpression(self.simplify(), other.simplify())
941
- return prover.prove(bicond)
942
-
943
- def __hash__(self):
944
- return hash(repr(self))
945
-
946
- def substitute_bindings(self, bindings):
947
- expr = self
948
- for var in expr.variables():
949
- if var in bindings:
950
- val = bindings[var]
951
- if isinstance(val, Variable):
952
- val = self.make_VariableExpression(val)
953
- elif not isinstance(val, Expression):
954
- raise ValueError('Can not substitute a non-expression '
955
- 'value into an expression: %r' % (val,))
956
- # Substitute bindings in the target value.
957
- val = val.substitute_bindings(bindings)
958
- # Replace var w/ the target value.
959
- expr = expr.replace(var, val)
960
- return expr.simplify()
961
-
962
- def typecheck(self, signature=None):
963
- """
964
- Infer and check types. Raise exceptions if necessary.
965
-
966
- :param signature: dict that maps variable names to types (or string
967
- representations of types)
968
- :return: the signature, plus any additional type mappings
969
- """
970
- sig = defaultdict(list)
971
- if signature:
972
- for key in signature:
973
- val = signature[key]
974
- varEx = VariableExpression(Variable(key))
975
- if isinstance(val, Type):
976
- varEx.type = val
977
- else:
978
- varEx.type = read_type(val)
979
- sig[key].append(varEx)
980
-
981
- self._set_type(signature=sig)
982
-
983
- return dict((key, sig[key][0].type) for key in sig)
984
-
985
- def findtype(self, variable):
986
- """
987
- Find the type of the given variable as it is used in this expression.
988
- For example, finding the type of "P" in "P(x) & Q(x,y)" yields "<e,t>"
989
-
990
- :param variable: Variable
991
- """
992
- raise NotImplementedError()
993
-
994
- def _set_type(self, other_type=ANY_TYPE, signature=None):
995
- """
996
- Set the type of this expression to be the given type. Raise type
997
- exceptions where applicable.
998
-
999
- :param other_type: Type
1000
- :param signature: dict(str -> list(AbstractVariableExpression))
1001
- """
1002
- raise NotImplementedError()
1003
-
1004
- def replace(self, variable, expression, replace_bound=False, alpha_convert=True):
1005
- """
1006
- Replace every instance of 'variable' with 'expression'
1007
- :param variable: ``Variable`` The variable to replace
1008
- :param expression: ``Expression`` The expression with which to replace it
1009
- :param replace_bound: bool Should bound variables be replaced?
1010
- :param alpha_convert: bool Alpha convert automatically to avoid name clashes?
1011
- """
1012
- assert isinstance(variable, Variable), "%s is not a Variable" % variable
1013
- assert isinstance(expression, Expression), "%s is not an Expression" % expression
1014
-
1015
- return self.visit_structured(lambda e: e.replace(variable, expression,
1016
- replace_bound, alpha_convert),
1017
- self.__class__)
1018
-
1019
- def normalize(self, newvars=None):
1020
- """Rename auto-generated unique variables"""
1021
- def get_indiv_vars(e):
1022
- if isinstance(e, IndividualVariableExpression):
1023
- return set([e])
1024
- elif isinstance(e, AbstractVariableExpression):
1025
- return set()
1026
- else:
1027
- return e.visit(get_indiv_vars,
1028
- lambda parts: reduce(operator.or_, parts, set()))
1029
-
1030
- result = self
1031
- for i,e in enumerate(sorted(get_indiv_vars(self), key=lambda e: e.variable)):
1032
- if isinstance(e,EventVariableExpression):
1033
- newVar = e.__class__(Variable('e0%s' % (i+1)))
1034
- elif isinstance(e,IndividualVariableExpression):
1035
- newVar = e.__class__(Variable('z%s' % (i+1)))
1036
- else:
1037
- newVar = e
1038
- result = result.replace(e.variable, newVar, True)
1039
- return result
1040
-
1041
- def visit(self, function, combinator):
1042
- """
1043
- Recursively visit subexpressions. Apply 'function' to each
1044
- subexpression and pass the result of each function application
1045
- to the 'combinator' for aggregation:
1046
-
1047
- return combinator(map(function, self.subexpressions))
1048
-
1049
- Bound variables are neither applied upon by the function nor given to
1050
- the combinator.
1051
- :param function: ``Function<Expression,T>`` to call on each subexpression
1052
- :param combinator: ``Function<list<T>,R>`` to combine the results of the
1053
- function calls
1054
- :return: result of combination ``R``
1055
- """
1056
- raise NotImplementedError()
1057
-
1058
- def visit_structured(self, function, combinator):
1059
- """
1060
- Recursively visit subexpressions. Apply 'function' to each
1061
- subexpression and pass the result of each function application
1062
- to the 'combinator' for aggregation. The combinator must have
1063
- the same signature as the constructor. The function is not
1064
- applied to bound variables, but they are passed to the
1065
- combinator.
1066
- :param function: ``Function`` to call on each subexpression
1067
- :param combinator: ``Function`` with the same signature as the
1068
- constructor, to combine the results of the function calls
1069
- :return: result of combination
1070
- """
1071
- return self.visit(function, lambda parts: combinator(*parts))
1072
-
1073
- def __repr__(self):
1074
- return '<%s %s>' % (self.__class__.__name__, self)
1075
-
1076
- def __str__(self):
1077
- return self.str()
1078
-
1079
- def variables(self):
1080
- """
1081
- Return a set of all the variables for binding substitution.
1082
- The variables returned include all free (non-bound) individual
1083
- variables and any variable starting with '?' or '@'.
1084
- :return: set of ``Variable`` objects
1085
- """
1086
- return self.free() | set(p for p in self.predicates()|self.constants()
1087
- if re.match('^[?@]', p.name))
1088
-
1089
- def free(self):
1090
- """
1091
- Return a set of all the free (non-bound) variables. This includes
1092
- both individual and predicate variables, but not constants.
1093
- :return: set of ``Variable`` objects
1094
- """
1095
- return self.visit(lambda e: e.free(),
1096
- lambda parts: reduce(operator.or_, parts, set()))
1097
-
1098
- def constants(self):
1099
- """
1100
- Return a set of individual constants (non-predicates).
1101
- :return: set of ``Variable`` objects
1102
- """
1103
- return self.visit(lambda e: e.constants(),
1104
- lambda parts: reduce(operator.or_, parts, set()))
1105
-
1106
- def predicates(self):
1107
- """
1108
- Return a set of predicates (constants, not variables).
1109
- :return: set of ``Variable`` objects
1110
- """
1111
- return self.visit(lambda e: e.predicates(),
1112
- lambda parts: reduce(operator.or_, parts, set()))
1113
-
1114
- def simplify(self):
1115
- """
1116
- :return: beta-converted version of this expression
1117
- """
1118
- return self.visit_structured(lambda e: e.simplify(), self.__class__)
1119
-
1120
- def make_VariableExpression(self, variable):
1121
- return VariableExpression(variable)
1122
-
1123
-
1124
- @python_2_unicode_compatible
1125
- class ApplicationExpression(Expression):
1126
- r"""
1127
- This class is used to represent two related types of logical expressions.
1128
-
1129
- The first is a Predicate Expression, such as "P(x,y)". A predicate
1130
- expression is comprised of a ``FunctionVariableExpression`` or
1131
- ``ConstantExpression`` as the predicate and a list of Expressions as the
1132
- arguments.
1133
-
1134
- The second is a an application of one expression to another, such as
1135
- "(\x.dog(x))(fido)".
1136
-
1137
- The reason Predicate Expressions are treated as Application Expressions is
1138
- that the Variable Expression predicate of the expression may be replaced
1139
- with another Expression, such as a LambdaExpression, which would mean that
1140
- the Predicate should be thought of as being applied to the arguments.
1141
-
1142
- The logical expression reader will always curry arguments in a application expression.
1143
- So, "\x y.see(x,y)(john,mary)" will be represented internally as
1144
- "((\x y.(see(x))(y))(john))(mary)". This simplifies the internals since
1145
- there will always be exactly one argument in an application.
1146
-
1147
- The str() method will usually print the curried forms of application
1148
- expressions. The one exception is when the the application expression is
1149
- really a predicate expression (ie, underlying function is an
1150
- ``AbstractVariableExpression``). This means that the example from above
1151
- will be returned as "(\x y.see(x,y)(john))(mary)".
1152
- """
1153
- def __init__(self, function, argument):
1154
- """
1155
- :param function: ``Expression``, for the function expression
1156
- :param argument: ``Expression``, for the argument
1157
- """
1158
- assert isinstance(function, Expression), "%s is not an Expression" % function
1159
- assert isinstance(argument, Expression), "%s is not an Expression" % argument
1160
- self.function = function
1161
- self.argument = argument
1162
-
1163
- def simplify(self):
1164
- function = self.function.simplify()
1165
- argument = self.argument.simplify()
1166
- if isinstance(function, LambdaExpression):
1167
- return function.term.replace(function.variable, argument).simplify()
1168
- else:
1169
- return self.__class__(function, argument)
1170
-
1171
- @property
1172
- def type(self):
1173
- if isinstance(self.function.type, ComplexType):
1174
- return self.function.type.second
1175
- else:
1176
- return ANY_TYPE
1177
-
1178
- def _set_type(self, other_type=ANY_TYPE, signature=None):
1179
- """:see Expression._set_type()"""
1180
- assert isinstance(other_type, Type)
1181
-
1182
- if signature is None:
1183
- signature = defaultdict(list)
1184
-
1185
- self.argument._set_type(ANY_TYPE, signature)
1186
- try:
1187
- self.function._set_type(ComplexType(self.argument.type, other_type), signature)
1188
- except TypeResolutionException:
1189
- raise TypeException(
1190
- "The function '%s' is of type '%s' and cannot be applied "
1191
- "to '%s' of type '%s'. Its argument must match type '%s'."
1192
- % (self.function, self.function.type, self.argument,
1193
- self.argument.type, self.function.type.first))
1194
-
1195
- def findtype(self, variable):
1196
- """:see Expression.findtype()"""
1197
- assert isinstance(variable, Variable), "%s is not a Variable" % variable
1198
- if self.is_atom():
1199
- function, args = self.uncurry()
1200
- else:
1201
- #It's not a predicate expression ("P(x,y)"), so leave args curried
1202
- function = self.function
1203
- args = [self.argument]
1204
-
1205
- found = [arg.findtype(variable) for arg in [function]+args]
1206
-
1207
- unique = []
1208
- for f in found:
1209
- if f != ANY_TYPE:
1210
- if unique:
1211
- for u in unique:
1212
- if f.matches(u):
1213
- break
1214
- else:
1215
- unique.append(f)
1216
-
1217
- if len(unique) == 1:
1218
- return list(unique)[0]
1219
- else:
1220
- return ANY_TYPE
1221
-
1222
- def constants(self):
1223
- """:see: Expression.constants()"""
1224
- if isinstance(self.function, AbstractVariableExpression):
1225
- function_constants = set()
1226
- else:
1227
- function_constants = self.function.constants()
1228
- return function_constants | self.argument.constants()
1229
-
1230
- def predicates(self):
1231
- """:see: Expression.predicates()"""
1232
- if isinstance(self.function, ConstantExpression):
1233
- function_preds = set([self.function.variable])
1234
- else:
1235
- function_preds = self.function.predicates()
1236
- return function_preds | self.argument.predicates()
1237
-
1238
- def visit(self, function, combinator):
1239
- """:see: Expression.visit()"""
1240
- return combinator([function(self.function), function(self.argument)])
1241
-
1242
- def __eq__(self, other):
1243
- return isinstance(other, ApplicationExpression) and \
1244
- self.function == other.function and \
1245
- self.argument == other.argument
1246
-
1247
- def __ne__(self, other):
1248
- return not self == other
1249
-
1250
- __hash__ = Expression.__hash__
1251
-
1252
- def __str__(self):
1253
- # uncurry the arguments and find the base function
1254
- if self.is_atom():
1255
- function, args = self.uncurry()
1256
- arg_str = ','.join("%s" % arg for arg in args)
1257
- else:
1258
- #Leave arguments curried
1259
- function = self.function
1260
- arg_str = "%s" % self.argument
1261
-
1262
- function_str = "%s" % function
1263
- parenthesize_function = False
1264
- if isinstance(function, LambdaExpression):
1265
- if isinstance(function.term, ApplicationExpression):
1266
- if not isinstance(function.term.function,
1267
- AbstractVariableExpression):
1268
- parenthesize_function = True
1269
- elif not isinstance(function.term, BooleanExpression):
1270
- parenthesize_function = True
1271
- elif isinstance(function, ApplicationExpression):
1272
- parenthesize_function = True
1273
-
1274
- if parenthesize_function:
1275
- function_str = Tokens.OPEN + function_str + Tokens.CLOSE
1276
-
1277
- return function_str + Tokens.OPEN + arg_str + Tokens.CLOSE
1278
-
1279
- def uncurry(self):
1280
- """
1281
- Uncurry this application expression
1282
-
1283
- return: A tuple (base-function, arg-list)
1284
- """
1285
- function = self.function
1286
- args = [self.argument]
1287
- while isinstance(function, ApplicationExpression):
1288
- #(\x.\y.sees(x,y)(john))(mary)
1289
- args.insert(0, function.argument)
1290
- function = function.function
1291
- return (function, args)
1292
-
1293
- @property
1294
- def pred(self):
1295
- """
1296
- Return uncurried base-function.
1297
- If this is an atom, then the result will be a variable expression.
1298
- Otherwise, it will be a lambda expression.
1299
- """
1300
- return self.uncurry()[0]
1301
-
1302
- @property
1303
- def args(self):
1304
- """
1305
- Return uncurried arg-list
1306
- """
1307
- return self.uncurry()[1]
1308
-
1309
- def is_atom(self):
1310
- """
1311
- Is this expression an atom (as opposed to a lambda expression applied
1312
- to a term)?
1313
- """
1314
- return isinstance(self.pred, AbstractVariableExpression)
1315
-
1316
-
1317
- @total_ordering
1318
- @python_2_unicode_compatible
1319
- class AbstractVariableExpression(Expression):
1320
- """This class represents a variable to be used as a predicate or entity"""
1321
- def __init__(self, variable):
1322
- """
1323
- :param variable: ``Variable``, for the variable
1324
- """
1325
- assert isinstance(variable, Variable), "%s is not a Variable" % variable
1326
- self.variable = variable
1327
-
1328
- def simplify(self):
1329
- return self
1330
-
1331
- def replace(self, variable, expression, replace_bound=False, alpha_convert=True):
1332
- """:see: Expression.replace()"""
1333
- assert isinstance(variable, Variable), "%s is not an Variable" % variable
1334
- assert isinstance(expression, Expression), "%s is not an Expression" % expression
1335
- if self.variable == variable:
1336
- return expression
1337
- else:
1338
- return self
1339
-
1340
- def _set_type(self, other_type=ANY_TYPE, signature=None):
1341
- """:see Expression._set_type()"""
1342
- assert isinstance(other_type, Type)
1343
-
1344
- if signature is None:
1345
- signature = defaultdict(list)
1346
-
1347
- resolution = other_type
1348
- for varEx in signature[self.variable.name]:
1349
- resolution = varEx.type.resolve(resolution)
1350
- if not resolution:
1351
- raise InconsistentTypeHierarchyException(self)
1352
-
1353
- signature[self.variable.name].append(self)
1354
- for varEx in signature[self.variable.name]:
1355
- varEx.type = resolution
1356
-
1357
- def findtype(self, variable):
1358
- """:see Expression.findtype()"""
1359
- assert isinstance(variable, Variable), "%s is not a Variable" % variable
1360
- if self.variable == variable:
1361
- return self.type
1362
- else:
1363
- return ANY_TYPE
1364
-
1365
- def predicates(self):
1366
- """:see: Expression.predicates()"""
1367
- return set()
1368
-
1369
- def __eq__(self, other):
1370
- """Allow equality between instances of ``AbstractVariableExpression``
1371
- subtypes."""
1372
- return isinstance(other, AbstractVariableExpression) and \
1373
- self.variable == other.variable
1374
-
1375
- def __ne__(self, other):
1376
- return not self == other
1377
-
1378
- def __lt__(self, other):
1379
- if not isinstance(other, AbstractVariableExpression):
1380
- raise TypeError
1381
- return self.variable < other.variable
1382
-
1383
- __hash__ = Expression.__hash__
1384
-
1385
- def __str__(self):
1386
- return "%s" % self.variable
1387
-
1388
- class IndividualVariableExpression(AbstractVariableExpression):
1389
- """This class represents variables that take the form of a single lowercase
1390
- character (other than 'e') followed by zero or more digits."""
1391
- def _set_type(self, other_type=ANY_TYPE, signature=None):
1392
- """:see Expression._set_type()"""
1393
- assert isinstance(other_type, Type)
1394
-
1395
- if signature is None:
1396
- signature = defaultdict(list)
1397
-
1398
- if not other_type.matches(ENTITY_TYPE):
1399
- raise IllegalTypeException(self, other_type, ENTITY_TYPE)
1400
-
1401
- signature[self.variable.name].append(self)
1402
-
1403
- def _get_type(self): return ENTITY_TYPE
1404
- type = property(_get_type, _set_type)
1405
-
1406
- def free(self):
1407
- """:see: Expression.free()"""
1408
- return set([self.variable])
1409
-
1410
- def constants(self):
1411
- """:see: Expression.constants()"""
1412
- return set()
1413
-
1414
- class FunctionVariableExpression(AbstractVariableExpression):
1415
- """This class represents variables that take the form of a single uppercase
1416
- character followed by zero or more digits."""
1417
- type = ANY_TYPE
1418
-
1419
- def free(self):
1420
- """:see: Expression.free()"""
1421
- return set([self.variable])
1422
-
1423
- def constants(self):
1424
- """:see: Expression.constants()"""
1425
- return set()
1426
-
1427
- class EventVariableExpression(IndividualVariableExpression):
1428
- """This class represents variables that take the form of a single lowercase
1429
- 'e' character followed by zero or more digits."""
1430
- type = EVENT_TYPE
1431
-
1432
- class ConstantExpression(AbstractVariableExpression):
1433
- """This class represents variables that do not take the form of a single
1434
- character followed by zero or more digits."""
1435
- type = ENTITY_TYPE
1436
-
1437
- def _set_type(self, other_type=ANY_TYPE, signature=None):
1438
- """:see Expression._set_type()"""
1439
- assert isinstance(other_type, Type)
1440
-
1441
- if signature is None:
1442
- signature = defaultdict(list)
1443
-
1444
- if other_type == ANY_TYPE:
1445
- #entity type by default, for individuals
1446
- resolution = ENTITY_TYPE
1447
- else:
1448
- resolution = other_type
1449
- if self.type != ENTITY_TYPE:
1450
- resolution = resolution.resolve(self.type)
1451
-
1452
- for varEx in signature[self.variable.name]:
1453
- resolution = varEx.type.resolve(resolution)
1454
- if not resolution:
1455
- raise InconsistentTypeHierarchyException(self)
1456
-
1457
- signature[self.variable.name].append(self)
1458
- for varEx in signature[self.variable.name]:
1459
- varEx.type = resolution
1460
-
1461
- def free(self):
1462
- """:see: Expression.free()"""
1463
- return set()
1464
-
1465
- def constants(self):
1466
- """:see: Expression.constants()"""
1467
- return set([self.variable])
1468
-
1469
-
1470
- def VariableExpression(variable):
1471
- """
1472
- This is a factory method that instantiates and returns a subtype of
1473
- ``AbstractVariableExpression`` appropriate for the given variable.
1474
- """
1475
- assert isinstance(variable, Variable), "%s is not a Variable" % variable
1476
- if is_indvar(variable.name):
1477
- return IndividualVariableExpression(variable)
1478
- elif is_funcvar(variable.name):
1479
- return FunctionVariableExpression(variable)
1480
- elif is_eventvar(variable.name):
1481
- return EventVariableExpression(variable)
1482
- else:
1483
- return ConstantExpression(variable)
1484
-
1485
-
1486
- class VariableBinderExpression(Expression):
1487
- """This an abstract class for any Expression that binds a variable in an
1488
- Expression. This includes LambdaExpressions and Quantified Expressions"""
1489
- def __init__(self, variable, term):
1490
- """
1491
- :param variable: ``Variable``, for the variable
1492
- :param term: ``Expression``, for the term
1493
- """
1494
- assert isinstance(variable, Variable), "%s is not a Variable" % variable
1495
- assert isinstance(term, Expression), "%s is not an Expression" % term
1496
- self.variable = variable
1497
- self.term = term
1498
-
1499
- def replace(self, variable, expression, replace_bound=False, alpha_convert=True):
1500
- """:see: Expression.replace()"""
1501
- assert isinstance(variable, Variable), "%s is not a Variable" % variable
1502
- assert isinstance(expression, Expression), "%s is not an Expression" % expression
1503
- #if the bound variable is the thing being replaced
1504
- if self.variable == variable:
1505
- if replace_bound:
1506
- assert isinstance(expression, AbstractVariableExpression),\
1507
- "%s is not a AbstractVariableExpression" % expression
1508
- return self.__class__(expression.variable,
1509
- self.term.replace(variable, expression, True, alpha_convert))
1510
- else:
1511
- return self
1512
- else:
1513
- # if the bound variable appears in the expression, then it must
1514
- # be alpha converted to avoid a conflict
1515
- if alpha_convert and self.variable in expression.free():
1516
- self = self.alpha_convert(unique_variable(pattern=self.variable))
1517
-
1518
- #replace in the term
1519
- return self.__class__(self.variable,
1520
- self.term.replace(variable, expression, replace_bound, alpha_convert))
1521
-
1522
- def alpha_convert(self, newvar):
1523
- """Rename all occurrences of the variable introduced by this variable
1524
- binder in the expression to ``newvar``.
1525
- :param newvar: ``Variable``, for the new variable
1526
- """
1527
- assert isinstance(newvar, Variable), "%s is not a Variable" % newvar
1528
- return self.__class__(newvar,
1529
- self.term.replace(self.variable,
1530
- VariableExpression(newvar),
1531
- True))
1532
-
1533
- def free(self):
1534
- """:see: Expression.free()"""
1535
- return self.term.free() - set([self.variable])
1536
-
1537
- def findtype(self, variable):
1538
- """:see Expression.findtype()"""
1539
- assert isinstance(variable, Variable), "%s is not a Variable" % variable
1540
- if variable == self.variable:
1541
- return ANY_TYPE
1542
- else:
1543
- return self.term.findtype(variable)
1544
-
1545
- def visit(self, function, combinator):
1546
- """:see: Expression.visit()"""
1547
- return combinator([function(self.term)])
1548
-
1549
- def visit_structured(self, function, combinator):
1550
- """:see: Expression.visit_structured()"""
1551
- return combinator(self.variable, function(self.term))
1552
-
1553
- def __eq__(self, other):
1554
- r"""Defines equality modulo alphabetic variance. If we are comparing
1555
- \x.M and \y.N, then check equality of M and N[x/y]."""
1556
- if isinstance(self, other.__class__) or \
1557
- isinstance(other, self.__class__):
1558
- if self.variable == other.variable:
1559
- return self.term == other.term
1560
- else:
1561
- # Comparing \x.M and \y.N. Relabel y in N with x and continue.
1562
- varex = VariableExpression(self.variable)
1563
- return self.term == other.term.replace(other.variable, varex)
1564
- else:
1565
- return False
1566
-
1567
- def __ne__(self, other):
1568
- return not self == other
1569
-
1570
- __hash__ = Expression.__hash__
1571
-
1572
-
1573
- @python_2_unicode_compatible
1574
- class LambdaExpression(VariableBinderExpression):
1575
- @property
1576
- def type(self):
1577
- return ComplexType(self.term.findtype(self.variable),
1578
- self.term.type)
1579
-
1580
- def _set_type(self, other_type=ANY_TYPE, signature=None):
1581
- """:see Expression._set_type()"""
1582
- assert isinstance(other_type, Type)
1583
-
1584
- if signature is None:
1585
- signature = defaultdict(list)
1586
-
1587
- self.term._set_type(other_type.second, signature)
1588
- if not self.type.resolve(other_type):
1589
- raise TypeResolutionException(self, other_type)
1590
-
1591
- def __str__(self):
1592
- variables = [self.variable]
1593
- term = self.term
1594
- while term.__class__ == self.__class__:
1595
- variables.append(term.variable)
1596
- term = term.term
1597
- return Tokens.LAMBDA + ' '.join("%s" % v for v in variables) + \
1598
- Tokens.DOT + "%s" % term
1599
-
1600
-
1601
- @python_2_unicode_compatible
1602
- class QuantifiedExpression(VariableBinderExpression):
1603
- @property
1604
- def type(self): return TRUTH_TYPE
1605
-
1606
- def _set_type(self, other_type=ANY_TYPE, signature=None):
1607
- """:see Expression._set_type()"""
1608
- assert isinstance(other_type, Type)
1609
-
1610
- if signature is None:
1611
- signature = defaultdict(list)
1612
-
1613
- if not other_type.matches(TRUTH_TYPE):
1614
- raise IllegalTypeException(self, other_type, TRUTH_TYPE)
1615
- self.term._set_type(TRUTH_TYPE, signature)
1616
-
1617
- def __str__(self):
1618
- variables = [self.variable]
1619
- term = self.term
1620
- while term.__class__ == self.__class__:
1621
- variables.append(term.variable)
1622
- term = term.term
1623
- return self.getQuantifier() + ' ' + ' '.join("%s" % v for v in variables) + \
1624
- Tokens.DOT + "%s" % term
1625
-
1626
- class ExistsExpression(QuantifiedExpression):
1627
- def getQuantifier(self):
1628
- return Tokens.EXISTS
1629
-
1630
- class AllExpression(QuantifiedExpression):
1631
- def getQuantifier(self):
1632
- return Tokens.ALL
1633
-
1634
-
1635
- @python_2_unicode_compatible
1636
- class NegatedExpression(Expression):
1637
- def __init__(self, term):
1638
- assert isinstance(term, Expression), "%s is not an Expression" % term
1639
- self.term = term
1640
-
1641
- @property
1642
- def type(self): return TRUTH_TYPE
1643
-
1644
- def _set_type(self, other_type=ANY_TYPE, signature=None):
1645
- """:see Expression._set_type()"""
1646
- assert isinstance(other_type, Type)
1647
-
1648
- if signature is None:
1649
- signature = defaultdict(list)
1650
-
1651
- if not other_type.matches(TRUTH_TYPE):
1652
- raise IllegalTypeException(self, other_type, TRUTH_TYPE)
1653
- self.term._set_type(TRUTH_TYPE, signature)
1654
-
1655
- def findtype(self, variable):
1656
- assert isinstance(variable, Variable), "%s is not a Variable" % variable
1657
- return self.term.findtype(variable)
1658
-
1659
- def visit(self, function, combinator):
1660
- """:see: Expression.visit()"""
1661
- return combinator([function(self.term)])
1662
-
1663
- def negate(self):
1664
- """:see: Expression.negate()"""
1665
- return self.term
1666
-
1667
- def __eq__(self, other):
1668
- return isinstance(other, NegatedExpression) and self.term == other.term
1669
-
1670
- def __ne__(self, other):
1671
- return not self == other
1672
-
1673
- __hash__ = Expression.__hash__
1674
-
1675
- def __str__(self):
1676
- return Tokens.NOT + "%s" % self.term
1677
-
1678
-
1679
- @python_2_unicode_compatible
1680
- class BinaryExpression(Expression):
1681
- def __init__(self, first, second):
1682
- assert isinstance(first, Expression), "%s is not an Expression" % first
1683
- assert isinstance(second, Expression), "%s is not an Expression" % second
1684
- self.first = first
1685
- self.second = second
1686
-
1687
- @property
1688
- def type(self): return TRUTH_TYPE
1689
-
1690
- def findtype(self, variable):
1691
- """:see Expression.findtype()"""
1692
- assert isinstance(variable, Variable), "%s is not a Variable" % variable
1693
- f = self.first.findtype(variable)
1694
- s = self.second.findtype(variable)
1695
- if f == s or s == ANY_TYPE:
1696
- return f
1697
- elif f == ANY_TYPE:
1698
- return s
1699
- else:
1700
- return ANY_TYPE
1701
-
1702
- def visit(self, function, combinator):
1703
- """:see: Expression.visit()"""
1704
- return combinator([function(self.first), function(self.second)])
1705
-
1706
- def __eq__(self, other):
1707
- return (isinstance(self, other.__class__) or \
1708
- isinstance(other, self.__class__)) and \
1709
- self.first == other.first and self.second == other.second
1710
-
1711
- def __ne__(self, other):
1712
- return not self == other
1713
-
1714
- __hash__ = Expression.__hash__
1715
-
1716
- def __str__(self):
1717
- first = self._str_subex(self.first)
1718
- second = self._str_subex(self.second)
1719
- return Tokens.OPEN + first + ' ' + self.getOp() \
1720
- + ' ' + second + Tokens.CLOSE
1721
-
1722
- def _str_subex(self, subex):
1723
- return "%s" % subex
1724
-
1725
-
1726
- class BooleanExpression(BinaryExpression):
1727
- def _set_type(self, other_type=ANY_TYPE, signature=None):
1728
- """:see Expression._set_type()"""
1729
- assert isinstance(other_type, Type)
1730
-
1731
- if signature is None:
1732
- signature = defaultdict(list)
1733
-
1734
- if not other_type.matches(TRUTH_TYPE):
1735
- raise IllegalTypeException(self, other_type, TRUTH_TYPE)
1736
- self.first._set_type(TRUTH_TYPE, signature)
1737
- self.second._set_type(TRUTH_TYPE, signature)
1738
-
1739
- class AndExpression(BooleanExpression):
1740
- """This class represents conjunctions"""
1741
- def getOp(self):
1742
- return Tokens.AND
1743
-
1744
- def _str_subex(self, subex):
1745
- s = "%s" % subex
1746
- if isinstance(subex, AndExpression):
1747
- return s[1:-1]
1748
- return s
1749
-
1750
- class OrExpression(BooleanExpression):
1751
- """This class represents disjunctions"""
1752
- def getOp(self):
1753
- return Tokens.OR
1754
-
1755
- def _str_subex(self, subex):
1756
- s = "%s" % subex
1757
- if isinstance(subex, OrExpression):
1758
- return s[1:-1]
1759
- return s
1760
-
1761
- class ImpExpression(BooleanExpression):
1762
- """This class represents implications"""
1763
- def getOp(self):
1764
- return Tokens.IMP
1765
-
1766
- class IffExpression(BooleanExpression):
1767
- """This class represents biconditionals"""
1768
- def getOp(self):
1769
- return Tokens.IFF
1770
-
1771
-
1772
- class EqualityExpression(BinaryExpression):
1773
- """This class represents equality expressions like "(x = y)"."""
1774
- def _set_type(self, other_type=ANY_TYPE, signature=None):
1775
- """:see Expression._set_type()"""
1776
- assert isinstance(other_type, Type)
1777
-
1778
- if signature is None:
1779
- signature = defaultdict(list)
1780
-
1781
- if not other_type.matches(TRUTH_TYPE):
1782
- raise IllegalTypeException(self, other_type, TRUTH_TYPE)
1783
- self.first._set_type(ENTITY_TYPE, signature)
1784
- self.second._set_type(ENTITY_TYPE, signature)
1785
-
1786
- def getOp(self):
1787
- return Tokens.EQ
1788
-
1789
-
1790
- ### Utilities
1791
-
1792
- class LogicalExpressionException(Exception):
1793
- def __init__(self, index, message):
1794
- self.index = index
1795
- Exception.__init__(self, message)
1796
-
1797
- class UnexpectedTokenException(LogicalExpressionException):
1798
- def __init__(self, index, unexpected=None, expected=None, message=None):
1799
- if unexpected and expected:
1800
- msg = "Unexpected token: '%s'. " \
1801
- "Expected token '%s'." % (unexpected, expected)
1802
- elif unexpected:
1803
- msg = "Unexpected token: '%s'." % unexpected
1804
- if message:
1805
- msg += ' '+message
1806
- else:
1807
- msg = "Expected token '%s'." % expected
1808
- LogicalExpressionException.__init__(self, index, msg)
1809
-
1810
- class ExpectedMoreTokensException(LogicalExpressionException):
1811
- def __init__(self, index, message=None):
1812
- if not message:
1813
- message = 'More tokens expected.'
1814
- LogicalExpressionException.__init__(self, index, 'End of input found. ' + message)
1815
-
1816
-
1817
- def is_indvar(expr):
1818
- """
1819
- An individual variable must be a single lowercase character other than 'e',
1820
- followed by zero or more digits.
1821
-
1822
- :param expr: str
1823
- :return: bool True if expr is of the correct form
1824
- """
1825
- assert isinstance(expr, string_types), "%s is not a string" % expr
1826
- return re.match(r'^[a-df-z]\d*$', expr) is not None
1827
-
1828
- def is_funcvar(expr):
1829
- """
1830
- A function variable must be a single uppercase character followed by
1831
- zero or more digits.
1832
-
1833
- :param expr: str
1834
- :return: bool True if expr is of the correct form
1835
- """
1836
- assert isinstance(expr, string_types), "%s is not a string" % expr
1837
- return re.match(r'^[A-Z]\d*$', expr) is not None
1838
-
1839
- def is_eventvar(expr):
1840
- """
1841
- An event variable must be a single lowercase 'e' character followed by
1842
- zero or more digits.
1843
-
1844
- :param expr: str
1845
- :return: bool True if expr is of the correct form
1846
- """
1847
- assert isinstance(expr, string_types), "%s is not a string" % expr
1848
- return re.match(r'^e\d*$', expr) is not None
1849
-
1850
-
1851
- def demo():
1852
- lexpr = Expression.fromstring
1853
- print('='*20 + 'Test reader' + '='*20)
1854
- print(lexpr(r'john'))
1855
- print(lexpr(r'man(x)'))
1856
- print(lexpr(r'-man(x)'))
1857
- print(lexpr(r'(man(x) & tall(x) & walks(x))'))
1858
- print(lexpr(r'exists x.(man(x) & tall(x) & walks(x))'))
1859
- print(lexpr(r'\x.man(x)'))
1860
- print(lexpr(r'\x.man(x)(john)'))
1861
- print(lexpr(r'\x y.sees(x,y)'))
1862
- print(lexpr(r'\x y.sees(x,y)(a,b)'))
1863
- print(lexpr(r'(\x.exists y.walks(x,y))(x)'))
1864
- print(lexpr(r'exists x.x = y'))
1865
- print(lexpr(r'exists x.(x = y)'))
1866
- print(lexpr('P(x) & x=y & P(y)'))
1867
- print(lexpr(r'\P Q.exists x.(P(x) & Q(x))'))
1868
- print(lexpr(r'man(x) <-> tall(x)'))
1869
-
1870
- print('='*20 + 'Test simplify' + '='*20)
1871
- print(lexpr(r'\x.\y.sees(x,y)(john)(mary)').simplify())
1872
- print(lexpr(r'\x.\y.sees(x,y)(john, mary)').simplify())
1873
- print(lexpr(r'all x.(man(x) & (\x.exists y.walks(x,y))(x))').simplify())
1874
- print(lexpr(r'(\P.\Q.exists x.(P(x) & Q(x)))(\x.dog(x))(\x.bark(x))').simplify())
1875
-
1876
- print('='*20 + 'Test alpha conversion and binder expression equality' + '='*20)
1877
- e1 = lexpr('exists x.P(x)')
1878
- print(e1)
1879
- e2 = e1.alpha_convert(Variable('z'))
1880
- print(e2)
1881
- print(e1 == e2)
1882
-
1883
- def demo_errors():
1884
- print('='*20 + 'Test reader errors' + '='*20)
1885
- demoException('(P(x) & Q(x)')
1886
- demoException('((P(x) &) & Q(x))')
1887
- demoException('P(x) -> ')
1888
- demoException('P(x')
1889
- demoException('P(x,')
1890
- demoException('P(x,)')
1891
- demoException('exists')
1892
- demoException('exists x.')
1893
- demoException('\\')
1894
- demoException('\\ x y.')
1895
- demoException('P(x)Q(x)')
1896
- demoException('(P(x)Q(x)')
1897
- demoException('exists x -> y')
1898
-
1899
- def demoException(s):
1900
- try:
1901
- Expression.fromstring(s)
1902
- except LogicalExpressionException as e:
1903
- print("%s: %s" % (e.__class__.__name__, e))
1904
-
1905
- def printtype(ex):
1906
- print("%s : %s" % (ex.str(), ex.type))
1907
-
1908
- if __name__ == '__main__':
1909
- demo()
1910
- # demo_errors()
1
+ # Natural Language Toolkit: Logic
2
+ #
3
+ # Author: Dan Garrette <dhgarrette@gmail.com>
4
+ #
5
+ # Copyright (C) 2001-2018 NLTK Project
6
+ # URL: <http://nltk.org>
7
+ # For license information, see LICENSE.TXT
8
+
9
+ """
10
+ A version of first order predicate logic, built on
11
+ top of the typed lambda calculus.
12
+ """
13
+ from __future__ import print_function, unicode_literals
14
+
15
+ import re
16
+ import operator
17
+ from collections import defaultdict
18
+ from functools import reduce, total_ordering
19
+
20
+ from six import string_types
21
+
22
+ from nltk.util import Trie
23
+ from nltk.internals import Counter
24
+ from nltk.compat import python_2_unicode_compatible
25
+
26
+ APP = 'APP'
27
+
28
+ _counter = Counter()
29
+
30
+ class Tokens(object):
31
+ LAMBDA = '\\'; LAMBDA_LIST = ['\\']
32
+
33
+ #Quantifiers
34
+ EXISTS = 'exists'; EXISTS_LIST = ['some', 'exists', 'exist']
35
+ ALL = 'all'; ALL_LIST = ['all', 'forall']
36
+
37
+ #Punctuation
38
+ DOT = '.'
39
+ OPEN = '('
40
+ CLOSE = ')'
41
+ COMMA = ','
42
+
43
+ #Operations
44
+ NOT = '-'; NOT_LIST = ['not', '-', '!']
45
+ AND = '&'; AND_LIST = ['and', '&', '^']
46
+ OR = '|'; OR_LIST = ['or', '|']
47
+ IMP = '->'; IMP_LIST = ['implies', '->', '=>']
48
+ IFF = '<->'; IFF_LIST = ['iff', '<->', '<=>']
49
+ EQ = '='; EQ_LIST = ['=', '==']
50
+ NEQ = '!='; NEQ_LIST = ['!=']
51
+
52
+ #Collections of tokens
53
+ BINOPS = AND_LIST + OR_LIST + IMP_LIST + IFF_LIST
54
+ QUANTS = EXISTS_LIST + ALL_LIST
55
+ PUNCT = [DOT, OPEN, CLOSE, COMMA]
56
+
57
+ TOKENS = BINOPS + EQ_LIST + NEQ_LIST + QUANTS + LAMBDA_LIST + PUNCT + NOT_LIST
58
+
59
+ #Special
60
+ SYMBOLS = [x for x in TOKENS if re.match(r'^[-\\.(),!&^|>=<]*$', x)]
61
+
62
+
63
+ def boolean_ops():
64
+ """
65
+ Boolean operators
66
+ """
67
+ names = ["negation", "conjunction", "disjunction", "implication", "equivalence"]
68
+ for pair in zip(names, [Tokens.NOT, Tokens.AND, Tokens.OR, Tokens.IMP, Tokens.IFF]):
69
+ print("%-15s\t%s" % pair)
70
+
71
+ def equality_preds():
72
+ """
73
+ Equality predicates
74
+ """
75
+ names = ["equality", "inequality"]
76
+ for pair in zip(names, [Tokens.EQ, Tokens.NEQ]):
77
+ print("%-15s\t%s" % pair)
78
+
79
+ def binding_ops():
80
+ """
81
+ Binding operators
82
+ """
83
+ names = ["existential", "universal", "lambda"]
84
+ for pair in zip(names, [Tokens.EXISTS, Tokens.ALL, Tokens.LAMBDA]):
85
+ print("%-15s\t%s" % pair)
86
+
87
+
88
+ @python_2_unicode_compatible
89
+ class LogicParser(object):
90
+ """A lambda calculus expression parser."""
91
+
92
+ def __init__(self, type_check=False):
93
+ """
94
+ :param type_check: bool should type checking be performed?
95
+ to their types.
96
+ """
97
+ assert isinstance(type_check, bool)
98
+
99
+ self._currentIndex = 0
100
+ self._buffer = []
101
+ self.type_check = type_check
102
+
103
+ """A list of tuples of quote characters. The 4-tuple is comprised
104
+ of the start character, the end character, the escape character, and
105
+ a boolean indicating whether the quotes should be included in the
106
+ result. Quotes are used to signify that a token should be treated as
107
+ atomic, ignoring any special characters within the token. The escape
108
+ character allows the quote end character to be used within the quote.
109
+ If True, the boolean indicates that the final token should contain the
110
+ quote and escape characters.
111
+ This method exists to be overridden"""
112
+ self.quote_chars = []
113
+
114
+ self.operator_precedence = dict(
115
+ [(x,1) for x in Tokens.LAMBDA_LIST] + \
116
+ [(x,2) for x in Tokens.NOT_LIST] + \
117
+ [(APP,3)] + \
118
+ [(x,4) for x in Tokens.EQ_LIST+Tokens.NEQ_LIST] + \
119
+ [(x,5) for x in Tokens.QUANTS] + \
120
+ [(x,6) for x in Tokens.AND_LIST] + \
121
+ [(x,7) for x in Tokens.OR_LIST] + \
122
+ [(x,8) for x in Tokens.IMP_LIST] + \
123
+ [(x,9) for x in Tokens.IFF_LIST] + \
124
+ [(None,10)])
125
+ self.right_associated_operations = [APP]
126
+
127
+ def parse(self, data, signature=None):
128
+ """
129
+ Parse the expression.
130
+
131
+ :param data: str for the input to be parsed
132
+ :param signature: ``dict<str, str>`` that maps variable names to type
133
+ strings
134
+ :returns: a parsed Expression
135
+ """
136
+ data = data.rstrip()
137
+
138
+ self._currentIndex = 0
139
+ self._buffer, mapping = self.process(data)
140
+
141
+ try:
142
+ result = self.process_next_expression(None)
143
+ if self.inRange(0):
144
+ raise UnexpectedTokenException(self._currentIndex+1, self.token(0))
145
+ except LogicalExpressionException as e:
146
+ msg = '%s\n%s\n%s^' % (e, data, ' '*mapping[e.index-1])
147
+ raise LogicalExpressionException(None, msg)
148
+
149
+ if self.type_check:
150
+ result.typecheck(signature)
151
+
152
+ return result
153
+
154
+ def process(self, data):
155
+ """Split the data into tokens"""
156
+ out = []
157
+ mapping = {}
158
+ tokenTrie = Trie(self.get_all_symbols())
159
+ token = ''
160
+ data_idx = 0
161
+ token_start_idx = data_idx
162
+ while data_idx < len(data):
163
+ cur_data_idx = data_idx
164
+ quoted_token, data_idx = self.process_quoted_token(data_idx, data)
165
+ if quoted_token:
166
+ if not token:
167
+ token_start_idx = cur_data_idx
168
+ token += quoted_token
169
+ continue
170
+
171
+ st = tokenTrie
172
+ c = data[data_idx]
173
+ symbol = ''
174
+ while c in st:
175
+ symbol += c
176
+ st = st[c]
177
+ if len(data)-data_idx > len(symbol):
178
+ c = data[data_idx+len(symbol)]
179
+ else:
180
+ break
181
+ if Trie.LEAF in st:
182
+ #token is a complete symbol
183
+ if token:
184
+ mapping[len(out)] = token_start_idx
185
+ out.append(token)
186
+ token = ''
187
+ mapping[len(out)] = data_idx
188
+ out.append(symbol)
189
+ data_idx += len(symbol)
190
+ else:
191
+ if data[data_idx] in ' \t\n': #any whitespace
192
+ if token:
193
+ mapping[len(out)] = token_start_idx
194
+ out.append(token)
195
+ token = ''
196
+ else:
197
+ if not token:
198
+ token_start_idx = data_idx
199
+ token += data[data_idx]
200
+ data_idx += 1
201
+ if token:
202
+ mapping[len(out)] = token_start_idx
203
+ out.append(token)
204
+ mapping[len(out)] = len(data)
205
+ mapping[len(out)+1] = len(data)+1
206
+ return out, mapping
207
+
208
+ def process_quoted_token(self, data_idx, data):
209
+ token = ''
210
+ c = data[data_idx]
211
+ i = data_idx
212
+ for start, end, escape, incl_quotes in self.quote_chars:
213
+ if c == start:
214
+ if incl_quotes:
215
+ token += c
216
+ i += 1
217
+ while data[i] != end:
218
+ if data[i] == escape:
219
+ if incl_quotes:
220
+ token += data[i]
221
+ i += 1
222
+ if len(data) == i: #if there are no more chars
223
+ raise LogicalExpressionException(None, "End of input reached. "
224
+ "Escape character [%s] found at end."
225
+ % escape)
226
+ token += data[i]
227
+ else:
228
+ token += data[i]
229
+ i += 1
230
+ if len(data) == i:
231
+ raise LogicalExpressionException(None, "End of input reached. "
232
+ "Expected: [%s]" % end)
233
+ if incl_quotes:
234
+ token += data[i]
235
+ i += 1
236
+ if not token:
237
+ raise LogicalExpressionException(None, 'Empty quoted token found')
238
+ break
239
+ return token, i
240
+
241
+ def get_all_symbols(self):
242
+ """This method exists to be overridden"""
243
+ return Tokens.SYMBOLS
244
+
245
+ def inRange(self, location):
246
+ """Return TRUE if the given location is within the buffer"""
247
+ return self._currentIndex+location < len(self._buffer)
248
+
249
+ def token(self, location=None):
250
+ """Get the next waiting token. If a location is given, then
251
+ return the token at currentIndex+location without advancing
252
+ currentIndex; setting it gives lookahead/lookback capability."""
253
+ try:
254
+ if location is None:
255
+ tok = self._buffer[self._currentIndex]
256
+ self._currentIndex += 1
257
+ else:
258
+ tok = self._buffer[self._currentIndex+location]
259
+ return tok
260
+ except IndexError:
261
+ raise ExpectedMoreTokensException(self._currentIndex+1)
262
+
263
+ def isvariable(self, tok):
264
+ return tok not in Tokens.TOKENS
265
+
266
+ def process_next_expression(self, context):
267
+ """Parse the next complete expression from the stream and return it."""
268
+ try:
269
+ tok = self.token()
270
+ except ExpectedMoreTokensException:
271
+ raise ExpectedMoreTokensException(self._currentIndex+1, message='Expression expected.')
272
+
273
+ accum = self.handle(tok, context)
274
+
275
+ if not accum:
276
+ raise UnexpectedTokenException(self._currentIndex, tok, message='Expression expected.')
277
+
278
+ return self.attempt_adjuncts(accum, context)
279
+
280
+ def handle(self, tok, context):
281
+ """This method is intended to be overridden for logics that
282
+ use different operators or expressions"""
283
+ if self.isvariable(tok):
284
+ return self.handle_variable(tok, context)
285
+
286
+ elif tok in Tokens.NOT_LIST:
287
+ return self.handle_negation(tok, context)
288
+
289
+ elif tok in Tokens.LAMBDA_LIST:
290
+ return self.handle_lambda(tok, context)
291
+
292
+ elif tok in Tokens.QUANTS:
293
+ return self.handle_quant(tok, context)
294
+
295
+ elif tok == Tokens.OPEN:
296
+ return self.handle_open(tok, context)
297
+
298
+ def attempt_adjuncts(self, expression, context):
299
+ cur_idx = None
300
+ while cur_idx != self._currentIndex: #while adjuncts are added
301
+ cur_idx = self._currentIndex
302
+ expression = self.attempt_EqualityExpression(expression, context)
303
+ expression = self.attempt_ApplicationExpression(expression, context)
304
+ expression = self.attempt_BooleanExpression(expression, context)
305
+ return expression
306
+
307
+ def handle_negation(self, tok, context):
308
+ return self.make_NegatedExpression(self.process_next_expression(Tokens.NOT))
309
+
310
+ def make_NegatedExpression(self, expression):
311
+ return NegatedExpression(expression)
312
+
313
+ def handle_variable(self, tok, context):
314
+ #It's either: 1) a predicate expression: sees(x,y)
315
+ # 2) an application expression: P(x)
316
+ # 3) a solo variable: john OR x
317
+ accum = self.make_VariableExpression(tok)
318
+ if self.inRange(0) and self.token(0) == Tokens.OPEN:
319
+ #The predicate has arguments
320
+ if not isinstance(accum, FunctionVariableExpression) and \
321
+ not isinstance(accum, ConstantExpression):
322
+ raise LogicalExpressionException(self._currentIndex,
323
+ "'%s' is an illegal predicate name. "
324
+ "Individual variables may not be used as "
325
+ "predicates." % tok)
326
+ self.token() #swallow the Open Paren
327
+
328
+ #curry the arguments
329
+ accum = self.make_ApplicationExpression(accum, self.process_next_expression(APP))
330
+ while self.inRange(0) and self.token(0) == Tokens.COMMA:
331
+ self.token() #swallow the comma
332
+ accum = self.make_ApplicationExpression(accum, self.process_next_expression(APP))
333
+ self.assertNextToken(Tokens.CLOSE)
334
+ return accum
335
+
336
+ def get_next_token_variable(self, description):
337
+ try:
338
+ tok = self.token()
339
+ except ExpectedMoreTokensException as e:
340
+ raise ExpectedMoreTokensException(e.index, 'Variable expected.')
341
+ if isinstance(self.make_VariableExpression(tok), ConstantExpression):
342
+ raise LogicalExpressionException(self._currentIndex,
343
+ "'%s' is an illegal variable name. "
344
+ "Constants may not be %s." % (tok, description))
345
+ return Variable(tok)
346
+
347
+ def handle_lambda(self, tok, context):
348
+ # Expression is a lambda expression
349
+ if not self.inRange(0):
350
+ raise ExpectedMoreTokensException(self._currentIndex+2,
351
+ message="Variable and Expression expected following lambda operator.")
352
+ vars = [self.get_next_token_variable('abstracted')]
353
+ while True:
354
+ if not self.inRange(0) or (self.token(0) == Tokens.DOT and not self.inRange(1)):
355
+ raise ExpectedMoreTokensException(self._currentIndex+2, message="Expression expected.")
356
+ if not self.isvariable(self.token(0)):
357
+ break
358
+ # Support expressions like: \x y.M == \x.\y.M
359
+ vars.append(self.get_next_token_variable('abstracted'))
360
+ if self.inRange(0) and self.token(0) == Tokens.DOT:
361
+ self.token() #swallow the dot
362
+
363
+ accum = self.process_next_expression(tok)
364
+ while vars:
365
+ accum = self.make_LambdaExpression(vars.pop(), accum)
366
+ return accum
367
+
368
+ def handle_quant(self, tok, context):
369
+ # Expression is a quantified expression: some x.M
370
+ factory = self.get_QuantifiedExpression_factory(tok)
371
+
372
+ if not self.inRange(0):
373
+ raise ExpectedMoreTokensException(self._currentIndex+2,
374
+ message="Variable and Expression expected following quantifier '%s'." % tok)
375
+ vars = [self.get_next_token_variable('quantified')]
376
+ while True:
377
+ if not self.inRange(0) or (self.token(0) == Tokens.DOT and not self.inRange(1)):
378
+ raise ExpectedMoreTokensException(self._currentIndex+2, message="Expression expected.")
379
+ if not self.isvariable(self.token(0)):
380
+ break
381
+ # Support expressions like: some x y.M == some x.some y.M
382
+ vars.append(self.get_next_token_variable('quantified'))
383
+ if self.inRange(0) and self.token(0) == Tokens.DOT:
384
+ self.token() #swallow the dot
385
+
386
+ accum = self.process_next_expression(tok)
387
+ while vars:
388
+ accum = self.make_QuanifiedExpression(factory, vars.pop(), accum)
389
+ return accum
390
+
391
+ def get_QuantifiedExpression_factory(self, tok):
392
+ """This method serves as a hook for other logic parsers that
393
+ have different quantifiers"""
394
+ if tok in Tokens.EXISTS_LIST:
395
+ return ExistsExpression
396
+ elif tok in Tokens.ALL_LIST:
397
+ return AllExpression
398
+ else:
399
+ self.assertToken(tok, Tokens.QUANTS)
400
+
401
+ def make_QuanifiedExpression(self, factory, variable, term):
402
+ return factory(variable, term)
403
+
404
+ def handle_open(self, tok, context):
405
+ #Expression is in parens
406
+ accum = self.process_next_expression(None)
407
+ self.assertNextToken(Tokens.CLOSE)
408
+ return accum
409
+
410
+ def attempt_EqualityExpression(self, expression, context):
411
+ """Attempt to make an equality expression. If the next token is an
412
+ equality operator, then an EqualityExpression will be returned.
413
+ Otherwise, the parameter will be returned."""
414
+ if self.inRange(0):
415
+ tok = self.token(0)
416
+ if tok in Tokens.EQ_LIST + Tokens.NEQ_LIST and self.has_priority(tok, context):
417
+ self.token() #swallow the "=" or "!="
418
+ expression = self.make_EqualityExpression(expression, self.process_next_expression(tok))
419
+ if tok in Tokens.NEQ_LIST:
420
+ expression = self.make_NegatedExpression(expression)
421
+ return expression
422
+
423
+ def make_EqualityExpression(self, first, second):
424
+ """This method serves as a hook for other logic parsers that
425
+ have different equality expression classes"""
426
+ return EqualityExpression(first, second)
427
+
428
+ def attempt_BooleanExpression(self, expression, context):
429
+ """Attempt to make a boolean expression. If the next token is a boolean
430
+ operator, then a BooleanExpression will be returned. Otherwise, the
431
+ parameter will be returned."""
432
+ while self.inRange(0):
433
+ tok = self.token(0)
434
+ factory = self.get_BooleanExpression_factory(tok)
435
+ if factory and self.has_priority(tok, context):
436
+ self.token() #swallow the operator
437
+ expression = self.make_BooleanExpression(factory, expression,
438
+ self.process_next_expression(tok))
439
+ else:
440
+ break
441
+ return expression
442
+
443
+ def get_BooleanExpression_factory(self, tok):
444
+ """This method serves as a hook for other logic parsers that
445
+ have different boolean operators"""
446
+ if tok in Tokens.AND_LIST:
447
+ return AndExpression
448
+ elif tok in Tokens.OR_LIST:
449
+ return OrExpression
450
+ elif tok in Tokens.IMP_LIST:
451
+ return ImpExpression
452
+ elif tok in Tokens.IFF_LIST:
453
+ return IffExpression
454
+ else:
455
+ return None
456
+
457
+ def make_BooleanExpression(self, factory, first, second):
458
+ return factory(first, second)
459
+
460
+ def attempt_ApplicationExpression(self, expression, context):
461
+ """Attempt to make an application expression. The next tokens are
462
+ a list of arguments in parens, then the argument expression is a
463
+ function being applied to the arguments. Otherwise, return the
464
+ argument expression."""
465
+ if self.has_priority(APP, context):
466
+ if self.inRange(0) and self.token(0) == Tokens.OPEN:
467
+ if not isinstance(expression, LambdaExpression) and \
468
+ not isinstance(expression, ApplicationExpression) and \
469
+ not isinstance(expression, FunctionVariableExpression) and \
470
+ not isinstance(expression, ConstantExpression):
471
+ raise LogicalExpressionException(self._currentIndex,
472
+ ("The function '%s" % expression) +
473
+ "' is not a Lambda Expression, an "
474
+ "Application Expression, or a "
475
+ "functional predicate, so it may "
476
+ "not take arguments.")
477
+ self.token() #swallow then open paren
478
+ #curry the arguments
479
+ accum = self.make_ApplicationExpression(expression, self.process_next_expression(APP))
480
+ while self.inRange(0) and self.token(0) == Tokens.COMMA:
481
+ self.token() #swallow the comma
482
+ accum = self.make_ApplicationExpression(accum, self.process_next_expression(APP))
483
+ self.assertNextToken(Tokens.CLOSE)
484
+ return accum
485
+ return expression
486
+
487
+ def make_ApplicationExpression(self, function, argument):
488
+ return ApplicationExpression(function, argument)
489
+
490
+ def make_VariableExpression(self, name):
491
+ return VariableExpression(Variable(name))
492
+
493
+ def make_LambdaExpression(self, variable, term):
494
+ return LambdaExpression(variable, term)
495
+
496
+ def has_priority(self, operation, context):
497
+ return self.operator_precedence[operation] < self.operator_precedence[context] or \
498
+ (operation in self.right_associated_operations and \
499
+ self.operator_precedence[operation] == self.operator_precedence[context])
500
+
501
+ def assertNextToken(self, expected):
502
+ try:
503
+ tok = self.token()
504
+ except ExpectedMoreTokensException as e:
505
+ raise ExpectedMoreTokensException(e.index, message="Expected token '%s'." % expected)
506
+
507
+ if isinstance(expected, list):
508
+ if tok not in expected:
509
+ raise UnexpectedTokenException(self._currentIndex, tok, expected)
510
+ else:
511
+ if tok != expected:
512
+ raise UnexpectedTokenException(self._currentIndex, tok, expected)
513
+
514
+ def assertToken(self, tok, expected):
515
+ if isinstance(expected, list):
516
+ if tok not in expected:
517
+ raise UnexpectedTokenException(self._currentIndex, tok, expected)
518
+ else:
519
+ if tok != expected:
520
+ raise UnexpectedTokenException(self._currentIndex, tok, expected)
521
+
522
+ def __repr__(self):
523
+ if self.inRange(0):
524
+ msg = 'Next token: ' + self.token(0)
525
+ else:
526
+ msg = 'No more tokens'
527
+ return '<' + self.__class__.__name__ + ': ' + msg + '>'
528
+
529
+
530
+ def read_logic(s, logic_parser=None, encoding=None):
531
+ """
532
+ Convert a file of First Order Formulas into a list of {Expression}s.
533
+
534
+ :param s: the contents of the file
535
+ :type s: str
536
+ :param logic_parser: The parser to be used to parse the logical expression
537
+ :type logic_parser: LogicParser
538
+ :param encoding: the encoding of the input string, if it is binary
539
+ :type encoding: str
540
+ :return: a list of parsed formulas.
541
+ :rtype: list(Expression)
542
+ """
543
+ if encoding is not None:
544
+ s = s.decode(encoding)
545
+ if logic_parser is None:
546
+ logic_parser = LogicParser()
547
+
548
+ statements = []
549
+ for linenum, line in enumerate(s.splitlines()):
550
+ line = line.strip()
551
+ if line.startswith('#') or line=='': continue
552
+ try:
553
+ statements.append(logic_parser.parse(line))
554
+ except LogicalExpressionException:
555
+ raise ValueError('Unable to parse line %s: %s' % (linenum, line))
556
+ return statements
557
+
558
+
559
+ @total_ordering
560
+ @python_2_unicode_compatible
561
+ class Variable(object):
562
+ def __init__(self, name):
563
+ """
564
+ :param name: the name of the variable
565
+ """
566
+ assert isinstance(name, string_types), "%s is not a string" % name
567
+ self.name = name
568
+
569
+ def __eq__(self, other):
570
+ return isinstance(other, Variable) and self.name == other.name
571
+
572
+ def __ne__(self, other):
573
+ return not self == other
574
+
575
+ def __lt__(self, other):
576
+ if not isinstance(other, Variable):
577
+ raise TypeError
578
+ return self.name < other.name
579
+
580
+ def substitute_bindings(self, bindings):
581
+ return bindings.get(self, self)
582
+
583
+ def __hash__(self):
584
+ return hash(self.name)
585
+
586
+ def __str__(self):
587
+ return self.name
588
+
589
+ def __repr__(self):
590
+ return "Variable('%s')" % self.name
591
+
592
+
593
+ def unique_variable(pattern=None, ignore=None):
594
+ """
595
+ Return a new, unique variable.
596
+
597
+ :param pattern: ``Variable`` that is being replaced. The new variable must
598
+ be the same type.
599
+ :param term: a set of ``Variable`` objects that should not be returned from
600
+ this function.
601
+ :rtype: Variable
602
+ """
603
+ if pattern is not None:
604
+ if is_indvar(pattern.name):
605
+ prefix = 'z'
606
+ elif is_funcvar(pattern.name):
607
+ prefix = 'F'
608
+ elif is_eventvar(pattern.name):
609
+ prefix = 'e0'
610
+ else:
611
+ assert False, "Cannot generate a unique constant"
612
+ else:
613
+ prefix = 'z'
614
+
615
+ v = Variable("%s%s" % (prefix, _counter.get()))
616
+ while ignore is not None and v in ignore:
617
+ v = Variable("%s%s" % (prefix, _counter.get()))
618
+ return v
619
+
620
+ def skolem_function(univ_scope=None):
621
+ """
622
+ Return a skolem function over the variables in univ_scope
623
+ param univ_scope
624
+ """
625
+ skolem = VariableExpression(Variable('F%s' % _counter.get()))
626
+ if univ_scope:
627
+ for v in list(univ_scope):
628
+ skolem = skolem(VariableExpression(v))
629
+ return skolem
630
+
631
+
632
+ @python_2_unicode_compatible
633
+ class Type(object):
634
+ def __repr__(self):
635
+ return "%s" % self
636
+
637
+ def __hash__(self):
638
+ return hash("%s" % self)
639
+
640
+ @classmethod
641
+ def fromstring(cls, s):
642
+ return read_type(s)
643
+
644
+ @python_2_unicode_compatible
645
+ class ComplexType(Type):
646
+ def __init__(self, first, second):
647
+ assert(isinstance(first, Type)), "%s is not a Type" % first
648
+ assert(isinstance(second, Type)), "%s is not a Type" % second
649
+ self.first = first
650
+ self.second = second
651
+
652
+ def __eq__(self, other):
653
+ return isinstance(other, ComplexType) and \
654
+ self.first == other.first and \
655
+ self.second == other.second
656
+
657
+ def __ne__(self, other):
658
+ return not self == other
659
+
660
+ __hash__ = Type.__hash__
661
+
662
+ def matches(self, other):
663
+ if isinstance(other, ComplexType):
664
+ return self.first.matches(other.first) and \
665
+ self.second.matches(other.second)
666
+ else:
667
+ return self == ANY_TYPE
668
+
669
+ def resolve(self, other):
670
+ if other == ANY_TYPE:
671
+ return self
672
+ elif isinstance(other, ComplexType):
673
+ f = self.first.resolve(other.first)
674
+ s = self.second.resolve(other.second)
675
+ if f and s:
676
+ return ComplexType(f,s)
677
+ else:
678
+ return None
679
+ elif self == ANY_TYPE:
680
+ return other
681
+ else:
682
+ return None
683
+
684
+ def __str__(self):
685
+ if self == ANY_TYPE:
686
+ return "%s" % ANY_TYPE
687
+ else:
688
+ return '<%s,%s>' % (self.first, self.second)
689
+
690
+ def str(self):
691
+ if self == ANY_TYPE:
692
+ return ANY_TYPE.str()
693
+ else:
694
+ return '(%s -> %s)' % (self.first.str(), self.second.str())
695
+
696
+ class BasicType(Type):
697
+ def __eq__(self, other):
698
+ return isinstance(other, BasicType) and ("%s" % self) == ("%s" % other)
699
+
700
+ def __ne__(self, other):
701
+ return not self == other
702
+
703
+ __hash__ = Type.__hash__
704
+
705
+ def matches(self, other):
706
+ return other == ANY_TYPE or self == other
707
+
708
+ def resolve(self, other):
709
+ if self.matches(other):
710
+ return self
711
+ else:
712
+ return None
713
+
714
+ @python_2_unicode_compatible
715
+ class EntityType(BasicType):
716
+ def __str__(self):
717
+ return 'e'
718
+
719
+ def str(self):
720
+ return 'IND'
721
+
722
+ @python_2_unicode_compatible
723
+ class TruthValueType(BasicType):
724
+ def __str__(self):
725
+ return 't'
726
+
727
+ def str(self):
728
+ return 'BOOL'
729
+
730
+ @python_2_unicode_compatible
731
+ class EventType(BasicType):
732
+ def __str__(self):
733
+ return 'v'
734
+
735
+ def str(self):
736
+ return 'EVENT'
737
+
738
+ @python_2_unicode_compatible
739
+ class AnyType(BasicType, ComplexType):
740
+ def __init__(self):
741
+ pass
742
+
743
+ @property
744
+ def first(self): return self
745
+
746
+ @property
747
+ def second(self): return self
748
+
749
+ def __eq__(self, other):
750
+ return isinstance(other, AnyType) or other.__eq__(self)
751
+
752
+ def __ne__(self, other):
753
+ return not self == other
754
+
755
+ __hash__ = Type.__hash__
756
+
757
+ def matches(self, other):
758
+ return True
759
+
760
+ def resolve(self, other):
761
+ return other
762
+
763
+ def __str__(self):
764
+ return '?'
765
+
766
+ def str(self):
767
+ return 'ANY'
768
+
769
+
770
+ TRUTH_TYPE = TruthValueType()
771
+ ENTITY_TYPE = EntityType()
772
+ EVENT_TYPE = EventType()
773
+ ANY_TYPE = AnyType()
774
+
775
+
776
+ def read_type(type_string):
777
+ assert isinstance(type_string, string_types)
778
+ type_string = type_string.replace(' ', '') #remove spaces
779
+
780
+ if type_string[0] == '<':
781
+ assert type_string[-1] == '>'
782
+ paren_count = 0
783
+ for i,char in enumerate(type_string):
784
+ if char == '<':
785
+ paren_count += 1
786
+ elif char == '>':
787
+ paren_count -= 1
788
+ assert paren_count > 0
789
+ elif char == ',':
790
+ if paren_count == 1:
791
+ break
792
+ return ComplexType(read_type(type_string[1 :i ]),
793
+ read_type(type_string[i+1:-1]))
794
+ elif type_string[0] == "%s" % ENTITY_TYPE:
795
+ return ENTITY_TYPE
796
+ elif type_string[0] == "%s" % TRUTH_TYPE:
797
+ return TRUTH_TYPE
798
+ elif type_string[0] == "%s" % ANY_TYPE:
799
+ return ANY_TYPE
800
+ else:
801
+ raise LogicalExpressionException("Unexpected character: '%s'." % type_string[0])
802
+
803
+
804
+ class TypeException(Exception):
805
+ def __init__(self, msg):
806
+ super(TypeException, self).__init__(msg)
807
+
808
+ class InconsistentTypeHierarchyException(TypeException):
809
+ def __init__(self, variable, expression=None):
810
+ if expression:
811
+ msg = "The variable '%s' was found in multiple places with different"\
812
+ " types in '%s'." % (variable, expression)
813
+ else:
814
+ msg = "The variable '%s' was found in multiple places with different"\
815
+ " types." % (variable)
816
+ super(InconsistentTypeHierarchyException, self).__init__(msg)
817
+
818
+ class TypeResolutionException(TypeException):
819
+ def __init__(self, expression, other_type):
820
+ super(TypeResolutionException, self).__init__(
821
+ "The type of '%s', '%s', cannot be resolved with type '%s'" %
822
+ (expression, expression.type, other_type))
823
+
824
+ class IllegalTypeException(TypeException):
825
+ def __init__(self, expression, other_type, allowed_type):
826
+ super(IllegalTypeException, self).__init__(
827
+ "Cannot set type of %s '%s' to '%s'; must match type '%s'." %
828
+ (expression.__class__.__name__, expression, other_type,
829
+ allowed_type))
830
+
831
+ def typecheck(expressions, signature=None):
832
+ """
833
+ Ensure correct typing across a collection of ``Expression`` objects.
834
+ :param expressions: a collection of expressions
835
+ :param signature: dict that maps variable names to types (or string
836
+ representations of types)
837
+ """
838
+ #typecheck and create master signature
839
+ for expression in expressions:
840
+ signature = expression.typecheck(signature)
841
+ #apply master signature to all expressions
842
+ for expression in expressions[:-1]:
843
+ expression.typecheck(signature)
844
+ return signature
845
+
846
+
847
+ class SubstituteBindingsI(object):
848
+ """
849
+ An interface for classes that can perform substitutions for
850
+ variables.
851
+ """
852
+ def substitute_bindings(self, bindings):
853
+ """
854
+ :return: The object that is obtained by replacing
855
+ each variable bound by ``bindings`` with its values.
856
+ Aliases are already resolved. (maybe?)
857
+ :rtype: (any)
858
+ """
859
+ raise NotImplementedError()
860
+
861
+ def variables(self):
862
+ """
863
+ :return: A list of all variables in this object.
864
+ """
865
+ raise NotImplementedError()
866
+
867
+
868
+ @python_2_unicode_compatible
869
+ class Expression(SubstituteBindingsI):
870
+ """This is the base abstract object for all logical expressions"""
871
+
872
+ _logic_parser = LogicParser()
873
+ _type_checking_logic_parser = LogicParser(type_check=True)
874
+
875
+ @classmethod
876
+ def fromstring(cls, s, type_check=False, signature=None):
877
+ if type_check:
878
+ return cls._type_checking_logic_parser.parse(s, signature)
879
+ else:
880
+ return cls._logic_parser.parse(s, signature)
881
+
882
+ def __call__(self, other, *additional):
883
+ accum = self.applyto(other)
884
+ for a in additional:
885
+ accum = accum(a)
886
+ return accum
887
+
888
+ def applyto(self, other):
889
+ assert isinstance(other, Expression), "%s is not an Expression" % other
890
+ return ApplicationExpression(self, other)
891
+
892
+ def __neg__(self):
893
+ return NegatedExpression(self)
894
+
895
+ def negate(self):
896
+ """If this is a negated expression, remove the negation.
897
+ Otherwise add a negation."""
898
+ return -self
899
+
900
+ def __and__(self, other):
901
+ if not isinstance(other, Expression):
902
+ raise TypeError("%s is not an Expression" % other)
903
+ return AndExpression(self, other)
904
+
905
+ def __or__(self, other):
906
+ if not isinstance(other, Expression):
907
+ raise TypeError("%s is not an Expression" % other)
908
+ return OrExpression(self, other)
909
+
910
+ def __gt__(self, other):
911
+ if not isinstance(other, Expression):
912
+ raise TypeError("%s is not an Expression" % other)
913
+ return ImpExpression(self, other)
914
+
915
+ def __lt__(self, other):
916
+ if not isinstance(other, Expression):
917
+ raise TypeError("%s is not an Expression" % other)
918
+ return IffExpression(self, other)
919
+
920
+ def __eq__(self, other):
921
+ raise NotImplementedError()
922
+
923
+ def __ne__(self, other):
924
+ return not self == other
925
+
926
+ def equiv(self, other, prover=None):
927
+ """
928
+ Check for logical equivalence.
929
+ Pass the expression (self <-> other) to the theorem prover.
930
+ If the prover says it is valid, then the self and other are equal.
931
+
932
+ :param other: an ``Expression`` to check equality against
933
+ :param prover: a ``nltk.inference.api.Prover``
934
+ """
935
+ assert isinstance(other, Expression), "%s is not an Expression" % other
936
+
937
+ if prover is None:
938
+ from nltk.inference import Prover9
939
+ prover = Prover9()
940
+ bicond = IffExpression(self.simplify(), other.simplify())
941
+ return prover.prove(bicond)
942
+
943
+ def __hash__(self):
944
+ return hash(repr(self))
945
+
946
+ def substitute_bindings(self, bindings):
947
+ expr = self
948
+ for var in expr.variables():
949
+ if var in bindings:
950
+ val = bindings[var]
951
+ if isinstance(val, Variable):
952
+ val = self.make_VariableExpression(val)
953
+ elif not isinstance(val, Expression):
954
+ raise ValueError('Can not substitute a non-expression '
955
+ 'value into an expression: %r' % (val,))
956
+ # Substitute bindings in the target value.
957
+ val = val.substitute_bindings(bindings)
958
+ # Replace var w/ the target value.
959
+ expr = expr.replace(var, val)
960
+ return expr.simplify()
961
+
962
+ def typecheck(self, signature=None):
963
+ """
964
+ Infer and check types. Raise exceptions if necessary.
965
+
966
+ :param signature: dict that maps variable names to types (or string
967
+ representations of types)
968
+ :return: the signature, plus any additional type mappings
969
+ """
970
+ sig = defaultdict(list)
971
+ if signature:
972
+ for key in signature:
973
+ val = signature[key]
974
+ varEx = VariableExpression(Variable(key))
975
+ if isinstance(val, Type):
976
+ varEx.type = val
977
+ else:
978
+ varEx.type = read_type(val)
979
+ sig[key].append(varEx)
980
+
981
+ self._set_type(signature=sig)
982
+
983
+ return dict((key, sig[key][0].type) for key in sig)
984
+
985
+ def findtype(self, variable):
986
+ """
987
+ Find the type of the given variable as it is used in this expression.
988
+ For example, finding the type of "P" in "P(x) & Q(x,y)" yields "<e,t>"
989
+
990
+ :param variable: Variable
991
+ """
992
+ raise NotImplementedError()
993
+
994
+ def _set_type(self, other_type=ANY_TYPE, signature=None):
995
+ """
996
+ Set the type of this expression to be the given type. Raise type
997
+ exceptions where applicable.
998
+
999
+ :param other_type: Type
1000
+ :param signature: dict(str -> list(AbstractVariableExpression))
1001
+ """
1002
+ raise NotImplementedError()
1003
+
1004
+ def replace(self, variable, expression, replace_bound=False, alpha_convert=True):
1005
+ """
1006
+ Replace every instance of 'variable' with 'expression'
1007
+ :param variable: ``Variable`` The variable to replace
1008
+ :param expression: ``Expression`` The expression with which to replace it
1009
+ :param replace_bound: bool Should bound variables be replaced?
1010
+ :param alpha_convert: bool Alpha convert automatically to avoid name clashes?
1011
+ """
1012
+ assert isinstance(variable, Variable), "%s is not a Variable" % variable
1013
+ assert isinstance(expression, Expression), "%s is not an Expression" % expression
1014
+
1015
+ return self.visit_structured(lambda e: e.replace(variable, expression,
1016
+ replace_bound, alpha_convert),
1017
+ self.__class__)
1018
+
1019
+ def normalize(self, newvars=None):
1020
+ """Rename auto-generated unique variables"""
1021
+ def get_indiv_vars(e):
1022
+ if isinstance(e, IndividualVariableExpression):
1023
+ return set([e])
1024
+ elif isinstance(e, AbstractVariableExpression):
1025
+ return set()
1026
+ else:
1027
+ return e.visit(get_indiv_vars,
1028
+ lambda parts: reduce(operator.or_, parts, set()))
1029
+
1030
+ result = self
1031
+ for i,e in enumerate(sorted(get_indiv_vars(self), key=lambda e: e.variable)):
1032
+ if isinstance(e,EventVariableExpression):
1033
+ newVar = e.__class__(Variable('e0%s' % (i+1)))
1034
+ elif isinstance(e,IndividualVariableExpression):
1035
+ newVar = e.__class__(Variable('z%s' % (i+1)))
1036
+ else:
1037
+ newVar = e
1038
+ result = result.replace(e.variable, newVar, True)
1039
+ return result
1040
+
1041
+ def visit(self, function, combinator):
1042
+ """
1043
+ Recursively visit subexpressions. Apply 'function' to each
1044
+ subexpression and pass the result of each function application
1045
+ to the 'combinator' for aggregation:
1046
+
1047
+ return combinator(map(function, self.subexpressions))
1048
+
1049
+ Bound variables are neither applied upon by the function nor given to
1050
+ the combinator.
1051
+ :param function: ``Function<Expression,T>`` to call on each subexpression
1052
+ :param combinator: ``Function<list<T>,R>`` to combine the results of the
1053
+ function calls
1054
+ :return: result of combination ``R``
1055
+ """
1056
+ raise NotImplementedError()
1057
+
1058
+ def visit_structured(self, function, combinator):
1059
+ """
1060
+ Recursively visit subexpressions. Apply 'function' to each
1061
+ subexpression and pass the result of each function application
1062
+ to the 'combinator' for aggregation. The combinator must have
1063
+ the same signature as the constructor. The function is not
1064
+ applied to bound variables, but they are passed to the
1065
+ combinator.
1066
+ :param function: ``Function`` to call on each subexpression
1067
+ :param combinator: ``Function`` with the same signature as the
1068
+ constructor, to combine the results of the function calls
1069
+ :return: result of combination
1070
+ """
1071
+ return self.visit(function, lambda parts: combinator(*parts))
1072
+
1073
+ def __repr__(self):
1074
+ return '<%s %s>' % (self.__class__.__name__, self)
1075
+
1076
+ def __str__(self):
1077
+ return self.str()
1078
+
1079
+ def variables(self):
1080
+ """
1081
+ Return a set of all the variables for binding substitution.
1082
+ The variables returned include all free (non-bound) individual
1083
+ variables and any variable starting with '?' or '@'.
1084
+ :return: set of ``Variable`` objects
1085
+ """
1086
+ return self.free() | set(p for p in self.predicates()|self.constants()
1087
+ if re.match('^[?@]', p.name))
1088
+
1089
+ def free(self):
1090
+ """
1091
+ Return a set of all the free (non-bound) variables. This includes
1092
+ both individual and predicate variables, but not constants.
1093
+ :return: set of ``Variable`` objects
1094
+ """
1095
+ return self.visit(lambda e: e.free(),
1096
+ lambda parts: reduce(operator.or_, parts, set()))
1097
+
1098
+ def constants(self):
1099
+ """
1100
+ Return a set of individual constants (non-predicates).
1101
+ :return: set of ``Variable`` objects
1102
+ """
1103
+ return self.visit(lambda e: e.constants(),
1104
+ lambda parts: reduce(operator.or_, parts, set()))
1105
+
1106
+ def predicates(self):
1107
+ """
1108
+ Return a set of predicates (constants, not variables).
1109
+ :return: set of ``Variable`` objects
1110
+ """
1111
+ return self.visit(lambda e: e.predicates(),
1112
+ lambda parts: reduce(operator.or_, parts, set()))
1113
+
1114
+ def simplify(self):
1115
+ """
1116
+ :return: beta-converted version of this expression
1117
+ """
1118
+ return self.visit_structured(lambda e: e.simplify(), self.__class__)
1119
+
1120
+ def make_VariableExpression(self, variable):
1121
+ return VariableExpression(variable)
1122
+
1123
+
1124
+ @python_2_unicode_compatible
1125
+ class ApplicationExpression(Expression):
1126
+ r"""
1127
+ This class is used to represent two related types of logical expressions.
1128
+
1129
+ The first is a Predicate Expression, such as "P(x,y)". A predicate
1130
+ expression is comprised of a ``FunctionVariableExpression`` or
1131
+ ``ConstantExpression`` as the predicate and a list of Expressions as the
1132
+ arguments.
1133
+
1134
+ The second is a an application of one expression to another, such as
1135
+ "(\x.dog(x))(fido)".
1136
+
1137
+ The reason Predicate Expressions are treated as Application Expressions is
1138
+ that the Variable Expression predicate of the expression may be replaced
1139
+ with another Expression, such as a LambdaExpression, which would mean that
1140
+ the Predicate should be thought of as being applied to the arguments.
1141
+
1142
+ The logical expression reader will always curry arguments in a application expression.
1143
+ So, "\x y.see(x,y)(john,mary)" will be represented internally as
1144
+ "((\x y.(see(x))(y))(john))(mary)". This simplifies the internals since
1145
+ there will always be exactly one argument in an application.
1146
+
1147
+ The str() method will usually print the curried forms of application
1148
+ expressions. The one exception is when the the application expression is
1149
+ really a predicate expression (ie, underlying function is an
1150
+ ``AbstractVariableExpression``). This means that the example from above
1151
+ will be returned as "(\x y.see(x,y)(john))(mary)".
1152
+ """
1153
+ def __init__(self, function, argument):
1154
+ """
1155
+ :param function: ``Expression``, for the function expression
1156
+ :param argument: ``Expression``, for the argument
1157
+ """
1158
+ assert isinstance(function, Expression), "%s is not an Expression" % function
1159
+ assert isinstance(argument, Expression), "%s is not an Expression" % argument
1160
+ self.function = function
1161
+ self.argument = argument
1162
+
1163
+ def simplify(self):
1164
+ function = self.function.simplify()
1165
+ argument = self.argument.simplify()
1166
+ if isinstance(function, LambdaExpression):
1167
+ return function.term.replace(function.variable, argument).simplify()
1168
+ else:
1169
+ return self.__class__(function, argument)
1170
+
1171
+ @property
1172
+ def type(self):
1173
+ if isinstance(self.function.type, ComplexType):
1174
+ return self.function.type.second
1175
+ else:
1176
+ return ANY_TYPE
1177
+
1178
+ def _set_type(self, other_type=ANY_TYPE, signature=None):
1179
+ """:see Expression._set_type()"""
1180
+ assert isinstance(other_type, Type)
1181
+
1182
+ if signature is None:
1183
+ signature = defaultdict(list)
1184
+
1185
+ self.argument._set_type(ANY_TYPE, signature)
1186
+ try:
1187
+ self.function._set_type(ComplexType(self.argument.type, other_type), signature)
1188
+ except TypeResolutionException:
1189
+ raise TypeException(
1190
+ "The function '%s' is of type '%s' and cannot be applied "
1191
+ "to '%s' of type '%s'. Its argument must match type '%s'."
1192
+ % (self.function, self.function.type, self.argument,
1193
+ self.argument.type, self.function.type.first))
1194
+
1195
+ def findtype(self, variable):
1196
+ """:see Expression.findtype()"""
1197
+ assert isinstance(variable, Variable), "%s is not a Variable" % variable
1198
+ if self.is_atom():
1199
+ function, args = self.uncurry()
1200
+ else:
1201
+ #It's not a predicate expression ("P(x,y)"), so leave args curried
1202
+ function = self.function
1203
+ args = [self.argument]
1204
+
1205
+ found = [arg.findtype(variable) for arg in [function]+args]
1206
+
1207
+ unique = []
1208
+ for f in found:
1209
+ if f != ANY_TYPE:
1210
+ if unique:
1211
+ for u in unique:
1212
+ if f.matches(u):
1213
+ break
1214
+ else:
1215
+ unique.append(f)
1216
+
1217
+ if len(unique) == 1:
1218
+ return list(unique)[0]
1219
+ else:
1220
+ return ANY_TYPE
1221
+
1222
+ def constants(self):
1223
+ """:see: Expression.constants()"""
1224
+ if isinstance(self.function, AbstractVariableExpression):
1225
+ function_constants = set()
1226
+ else:
1227
+ function_constants = self.function.constants()
1228
+ return function_constants | self.argument.constants()
1229
+
1230
+ def predicates(self):
1231
+ """:see: Expression.predicates()"""
1232
+ if isinstance(self.function, ConstantExpression):
1233
+ function_preds = set([self.function.variable])
1234
+ else:
1235
+ function_preds = self.function.predicates()
1236
+ return function_preds | self.argument.predicates()
1237
+
1238
+ def visit(self, function, combinator):
1239
+ """:see: Expression.visit()"""
1240
+ return combinator([function(self.function), function(self.argument)])
1241
+
1242
+ def __eq__(self, other):
1243
+ return isinstance(other, ApplicationExpression) and \
1244
+ self.function == other.function and \
1245
+ self.argument == other.argument
1246
+
1247
+ def __ne__(self, other):
1248
+ return not self == other
1249
+
1250
+ __hash__ = Expression.__hash__
1251
+
1252
+ def __str__(self):
1253
+ # uncurry the arguments and find the base function
1254
+ if self.is_atom():
1255
+ function, args = self.uncurry()
1256
+ arg_str = ','.join("%s" % arg for arg in args)
1257
+ else:
1258
+ #Leave arguments curried
1259
+ function = self.function
1260
+ arg_str = "%s" % self.argument
1261
+
1262
+ function_str = "%s" % function
1263
+ parenthesize_function = False
1264
+ if isinstance(function, LambdaExpression):
1265
+ if isinstance(function.term, ApplicationExpression):
1266
+ if not isinstance(function.term.function,
1267
+ AbstractVariableExpression):
1268
+ parenthesize_function = True
1269
+ elif not isinstance(function.term, BooleanExpression):
1270
+ parenthesize_function = True
1271
+ elif isinstance(function, ApplicationExpression):
1272
+ parenthesize_function = True
1273
+
1274
+ if parenthesize_function:
1275
+ function_str = Tokens.OPEN + function_str + Tokens.CLOSE
1276
+
1277
+ return function_str + Tokens.OPEN + arg_str + Tokens.CLOSE
1278
+
1279
+ def uncurry(self):
1280
+ """
1281
+ Uncurry this application expression
1282
+
1283
+ return: A tuple (base-function, arg-list)
1284
+ """
1285
+ function = self.function
1286
+ args = [self.argument]
1287
+ while isinstance(function, ApplicationExpression):
1288
+ #(\x.\y.sees(x,y)(john))(mary)
1289
+ args.insert(0, function.argument)
1290
+ function = function.function
1291
+ return (function, args)
1292
+
1293
+ @property
1294
+ def pred(self):
1295
+ """
1296
+ Return uncurried base-function.
1297
+ If this is an atom, then the result will be a variable expression.
1298
+ Otherwise, it will be a lambda expression.
1299
+ """
1300
+ return self.uncurry()[0]
1301
+
1302
+ @property
1303
+ def args(self):
1304
+ """
1305
+ Return uncurried arg-list
1306
+ """
1307
+ return self.uncurry()[1]
1308
+
1309
+ def is_atom(self):
1310
+ """
1311
+ Is this expression an atom (as opposed to a lambda expression applied
1312
+ to a term)?
1313
+ """
1314
+ return isinstance(self.pred, AbstractVariableExpression)
1315
+
1316
+
1317
+ @total_ordering
1318
+ @python_2_unicode_compatible
1319
+ class AbstractVariableExpression(Expression):
1320
+ """This class represents a variable to be used as a predicate or entity"""
1321
+ def __init__(self, variable):
1322
+ """
1323
+ :param variable: ``Variable``, for the variable
1324
+ """
1325
+ assert isinstance(variable, Variable), "%s is not a Variable" % variable
1326
+ self.variable = variable
1327
+
1328
+ def simplify(self):
1329
+ return self
1330
+
1331
+ def replace(self, variable, expression, replace_bound=False, alpha_convert=True):
1332
+ """:see: Expression.replace()"""
1333
+ assert isinstance(variable, Variable), "%s is not an Variable" % variable
1334
+ assert isinstance(expression, Expression), "%s is not an Expression" % expression
1335
+ if self.variable == variable:
1336
+ return expression
1337
+ else:
1338
+ return self
1339
+
1340
+ def _set_type(self, other_type=ANY_TYPE, signature=None):
1341
+ """:see Expression._set_type()"""
1342
+ assert isinstance(other_type, Type)
1343
+
1344
+ if signature is None:
1345
+ signature = defaultdict(list)
1346
+
1347
+ resolution = other_type
1348
+ for varEx in signature[self.variable.name]:
1349
+ resolution = varEx.type.resolve(resolution)
1350
+ if not resolution:
1351
+ raise InconsistentTypeHierarchyException(self)
1352
+
1353
+ signature[self.variable.name].append(self)
1354
+ for varEx in signature[self.variable.name]:
1355
+ varEx.type = resolution
1356
+
1357
+ def findtype(self, variable):
1358
+ """:see Expression.findtype()"""
1359
+ assert isinstance(variable, Variable), "%s is not a Variable" % variable
1360
+ if self.variable == variable:
1361
+ return self.type
1362
+ else:
1363
+ return ANY_TYPE
1364
+
1365
+ def predicates(self):
1366
+ """:see: Expression.predicates()"""
1367
+ return set()
1368
+
1369
+ def __eq__(self, other):
1370
+ """Allow equality between instances of ``AbstractVariableExpression``
1371
+ subtypes."""
1372
+ return isinstance(other, AbstractVariableExpression) and \
1373
+ self.variable == other.variable
1374
+
1375
+ def __ne__(self, other):
1376
+ return not self == other
1377
+
1378
+ def __lt__(self, other):
1379
+ if not isinstance(other, AbstractVariableExpression):
1380
+ raise TypeError
1381
+ return self.variable < other.variable
1382
+
1383
+ __hash__ = Expression.__hash__
1384
+
1385
+ def __str__(self):
1386
+ return "%s" % self.variable
1387
+
1388
+ class IndividualVariableExpression(AbstractVariableExpression):
1389
+ """This class represents variables that take the form of a single lowercase
1390
+ character (other than 'e') followed by zero or more digits."""
1391
+ def _set_type(self, other_type=ANY_TYPE, signature=None):
1392
+ """:see Expression._set_type()"""
1393
+ assert isinstance(other_type, Type)
1394
+
1395
+ if signature is None:
1396
+ signature = defaultdict(list)
1397
+
1398
+ if not other_type.matches(ENTITY_TYPE):
1399
+ raise IllegalTypeException(self, other_type, ENTITY_TYPE)
1400
+
1401
+ signature[self.variable.name].append(self)
1402
+
1403
+ def _get_type(self): return ENTITY_TYPE
1404
+ type = property(_get_type, _set_type)
1405
+
1406
+ def free(self):
1407
+ """:see: Expression.free()"""
1408
+ return set([self.variable])
1409
+
1410
+ def constants(self):
1411
+ """:see: Expression.constants()"""
1412
+ return set()
1413
+
1414
+ class FunctionVariableExpression(AbstractVariableExpression):
1415
+ """This class represents variables that take the form of a single uppercase
1416
+ character followed by zero or more digits."""
1417
+ type = ANY_TYPE
1418
+
1419
+ def free(self):
1420
+ """:see: Expression.free()"""
1421
+ return set([self.variable])
1422
+
1423
+ def constants(self):
1424
+ """:see: Expression.constants()"""
1425
+ return set()
1426
+
1427
+ class EventVariableExpression(IndividualVariableExpression):
1428
+ """This class represents variables that take the form of a single lowercase
1429
+ 'e' character followed by zero or more digits."""
1430
+ type = EVENT_TYPE
1431
+
1432
+ class ConstantExpression(AbstractVariableExpression):
1433
+ """This class represents variables that do not take the form of a single
1434
+ character followed by zero or more digits."""
1435
+ type = ENTITY_TYPE
1436
+
1437
+ def _set_type(self, other_type=ANY_TYPE, signature=None):
1438
+ """:see Expression._set_type()"""
1439
+ assert isinstance(other_type, Type)
1440
+
1441
+ if signature is None:
1442
+ signature = defaultdict(list)
1443
+
1444
+ if other_type == ANY_TYPE:
1445
+ #entity type by default, for individuals
1446
+ resolution = ENTITY_TYPE
1447
+ else:
1448
+ resolution = other_type
1449
+ if self.type != ENTITY_TYPE:
1450
+ resolution = resolution.resolve(self.type)
1451
+
1452
+ for varEx in signature[self.variable.name]:
1453
+ resolution = varEx.type.resolve(resolution)
1454
+ if not resolution:
1455
+ raise InconsistentTypeHierarchyException(self)
1456
+
1457
+ signature[self.variable.name].append(self)
1458
+ for varEx in signature[self.variable.name]:
1459
+ varEx.type = resolution
1460
+
1461
+ def free(self):
1462
+ """:see: Expression.free()"""
1463
+ return set()
1464
+
1465
+ def constants(self):
1466
+ """:see: Expression.constants()"""
1467
+ return set([self.variable])
1468
+
1469
+
1470
+ def VariableExpression(variable):
1471
+ """
1472
+ This is a factory method that instantiates and returns a subtype of
1473
+ ``AbstractVariableExpression`` appropriate for the given variable.
1474
+ """
1475
+ assert isinstance(variable, Variable), "%s is not a Variable" % variable
1476
+ if is_indvar(variable.name):
1477
+ return IndividualVariableExpression(variable)
1478
+ elif is_funcvar(variable.name):
1479
+ return FunctionVariableExpression(variable)
1480
+ elif is_eventvar(variable.name):
1481
+ return EventVariableExpression(variable)
1482
+ else:
1483
+ return ConstantExpression(variable)
1484
+
1485
+
1486
+ class VariableBinderExpression(Expression):
1487
+ """This an abstract class for any Expression that binds a variable in an
1488
+ Expression. This includes LambdaExpressions and Quantified Expressions"""
1489
+ def __init__(self, variable, term):
1490
+ """
1491
+ :param variable: ``Variable``, for the variable
1492
+ :param term: ``Expression``, for the term
1493
+ """
1494
+ assert isinstance(variable, Variable), "%s is not a Variable" % variable
1495
+ assert isinstance(term, Expression), "%s is not an Expression" % term
1496
+ self.variable = variable
1497
+ self.term = term
1498
+
1499
+ def replace(self, variable, expression, replace_bound=False, alpha_convert=True):
1500
+ """:see: Expression.replace()"""
1501
+ assert isinstance(variable, Variable), "%s is not a Variable" % variable
1502
+ assert isinstance(expression, Expression), "%s is not an Expression" % expression
1503
+ #if the bound variable is the thing being replaced
1504
+ if self.variable == variable:
1505
+ if replace_bound:
1506
+ assert isinstance(expression, AbstractVariableExpression),\
1507
+ "%s is not a AbstractVariableExpression" % expression
1508
+ return self.__class__(expression.variable,
1509
+ self.term.replace(variable, expression, True, alpha_convert))
1510
+ else:
1511
+ return self
1512
+ else:
1513
+ # if the bound variable appears in the expression, then it must
1514
+ # be alpha converted to avoid a conflict
1515
+ if alpha_convert and self.variable in expression.free():
1516
+ self = self.alpha_convert(unique_variable(pattern=self.variable))
1517
+
1518
+ #replace in the term
1519
+ return self.__class__(self.variable,
1520
+ self.term.replace(variable, expression, replace_bound, alpha_convert))
1521
+
1522
+ def alpha_convert(self, newvar):
1523
+ """Rename all occurrences of the variable introduced by this variable
1524
+ binder in the expression to ``newvar``.
1525
+ :param newvar: ``Variable``, for the new variable
1526
+ """
1527
+ assert isinstance(newvar, Variable), "%s is not a Variable" % newvar
1528
+ return self.__class__(newvar,
1529
+ self.term.replace(self.variable,
1530
+ VariableExpression(newvar),
1531
+ True))
1532
+
1533
+ def free(self):
1534
+ """:see: Expression.free()"""
1535
+ return self.term.free() - set([self.variable])
1536
+
1537
+ def findtype(self, variable):
1538
+ """:see Expression.findtype()"""
1539
+ assert isinstance(variable, Variable), "%s is not a Variable" % variable
1540
+ if variable == self.variable:
1541
+ return ANY_TYPE
1542
+ else:
1543
+ return self.term.findtype(variable)
1544
+
1545
+ def visit(self, function, combinator):
1546
+ """:see: Expression.visit()"""
1547
+ return combinator([function(self.term)])
1548
+
1549
+ def visit_structured(self, function, combinator):
1550
+ """:see: Expression.visit_structured()"""
1551
+ return combinator(self.variable, function(self.term))
1552
+
1553
+ def __eq__(self, other):
1554
+ r"""Defines equality modulo alphabetic variance. If we are comparing
1555
+ \x.M and \y.N, then check equality of M and N[x/y]."""
1556
+ if isinstance(self, other.__class__) or \
1557
+ isinstance(other, self.__class__):
1558
+ if self.variable == other.variable:
1559
+ return self.term == other.term
1560
+ else:
1561
+ # Comparing \x.M and \y.N. Relabel y in N with x and continue.
1562
+ varex = VariableExpression(self.variable)
1563
+ return self.term == other.term.replace(other.variable, varex)
1564
+ else:
1565
+ return False
1566
+
1567
+ def __ne__(self, other):
1568
+ return not self == other
1569
+
1570
+ __hash__ = Expression.__hash__
1571
+
1572
+
1573
+ @python_2_unicode_compatible
1574
+ class LambdaExpression(VariableBinderExpression):
1575
+ @property
1576
+ def type(self):
1577
+ return ComplexType(self.term.findtype(self.variable),
1578
+ self.term.type)
1579
+
1580
+ def _set_type(self, other_type=ANY_TYPE, signature=None):
1581
+ """:see Expression._set_type()"""
1582
+ assert isinstance(other_type, Type)
1583
+
1584
+ if signature is None:
1585
+ signature = defaultdict(list)
1586
+
1587
+ self.term._set_type(other_type.second, signature)
1588
+ if not self.type.resolve(other_type):
1589
+ raise TypeResolutionException(self, other_type)
1590
+
1591
+ def __str__(self):
1592
+ variables = [self.variable]
1593
+ term = self.term
1594
+ while term.__class__ == self.__class__:
1595
+ variables.append(term.variable)
1596
+ term = term.term
1597
+ return Tokens.LAMBDA + ' '.join("%s" % v for v in variables) + \
1598
+ Tokens.DOT + "%s" % term
1599
+
1600
+
1601
+ @python_2_unicode_compatible
1602
+ class QuantifiedExpression(VariableBinderExpression):
1603
+ @property
1604
+ def type(self): return TRUTH_TYPE
1605
+
1606
+ def _set_type(self, other_type=ANY_TYPE, signature=None):
1607
+ """:see Expression._set_type()"""
1608
+ assert isinstance(other_type, Type)
1609
+
1610
+ if signature is None:
1611
+ signature = defaultdict(list)
1612
+
1613
+ if not other_type.matches(TRUTH_TYPE):
1614
+ raise IllegalTypeException(self, other_type, TRUTH_TYPE)
1615
+ self.term._set_type(TRUTH_TYPE, signature)
1616
+
1617
+ def __str__(self):
1618
+ variables = [self.variable]
1619
+ term = self.term
1620
+ while term.__class__ == self.__class__:
1621
+ variables.append(term.variable)
1622
+ term = term.term
1623
+ return self.getQuantifier() + ' ' + ' '.join("%s" % v for v in variables) + \
1624
+ Tokens.DOT + "%s" % term
1625
+
1626
+ class ExistsExpression(QuantifiedExpression):
1627
+ def getQuantifier(self):
1628
+ return Tokens.EXISTS
1629
+
1630
+ class AllExpression(QuantifiedExpression):
1631
+ def getQuantifier(self):
1632
+ return Tokens.ALL
1633
+
1634
+
1635
+ @python_2_unicode_compatible
1636
+ class NegatedExpression(Expression):
1637
+ def __init__(self, term):
1638
+ assert isinstance(term, Expression), "%s is not an Expression" % term
1639
+ self.term = term
1640
+
1641
+ @property
1642
+ def type(self): return TRUTH_TYPE
1643
+
1644
+ def _set_type(self, other_type=ANY_TYPE, signature=None):
1645
+ """:see Expression._set_type()"""
1646
+ assert isinstance(other_type, Type)
1647
+
1648
+ if signature is None:
1649
+ signature = defaultdict(list)
1650
+
1651
+ if not other_type.matches(TRUTH_TYPE):
1652
+ raise IllegalTypeException(self, other_type, TRUTH_TYPE)
1653
+ self.term._set_type(TRUTH_TYPE, signature)
1654
+
1655
+ def findtype(self, variable):
1656
+ assert isinstance(variable, Variable), "%s is not a Variable" % variable
1657
+ return self.term.findtype(variable)
1658
+
1659
+ def visit(self, function, combinator):
1660
+ """:see: Expression.visit()"""
1661
+ return combinator([function(self.term)])
1662
+
1663
+ def negate(self):
1664
+ """:see: Expression.negate()"""
1665
+ return self.term
1666
+
1667
+ def __eq__(self, other):
1668
+ return isinstance(other, NegatedExpression) and self.term == other.term
1669
+
1670
+ def __ne__(self, other):
1671
+ return not self == other
1672
+
1673
+ __hash__ = Expression.__hash__
1674
+
1675
+ def __str__(self):
1676
+ return Tokens.NOT + "%s" % self.term
1677
+
1678
+
1679
+ @python_2_unicode_compatible
1680
+ class BinaryExpression(Expression):
1681
+ def __init__(self, first, second):
1682
+ assert isinstance(first, Expression), "%s is not an Expression" % first
1683
+ assert isinstance(second, Expression), "%s is not an Expression" % second
1684
+ self.first = first
1685
+ self.second = second
1686
+
1687
+ @property
1688
+ def type(self): return TRUTH_TYPE
1689
+
1690
+ def findtype(self, variable):
1691
+ """:see Expression.findtype()"""
1692
+ assert isinstance(variable, Variable), "%s is not a Variable" % variable
1693
+ f = self.first.findtype(variable)
1694
+ s = self.second.findtype(variable)
1695
+ if f == s or s == ANY_TYPE:
1696
+ return f
1697
+ elif f == ANY_TYPE:
1698
+ return s
1699
+ else:
1700
+ return ANY_TYPE
1701
+
1702
+ def visit(self, function, combinator):
1703
+ """:see: Expression.visit()"""
1704
+ return combinator([function(self.first), function(self.second)])
1705
+
1706
+ def __eq__(self, other):
1707
+ return (isinstance(self, other.__class__) or \
1708
+ isinstance(other, self.__class__)) and \
1709
+ self.first == other.first and self.second == other.second
1710
+
1711
+ def __ne__(self, other):
1712
+ return not self == other
1713
+
1714
+ __hash__ = Expression.__hash__
1715
+
1716
+ def __str__(self):
1717
+ first = self._str_subex(self.first)
1718
+ second = self._str_subex(self.second)
1719
+ return Tokens.OPEN + first + ' ' + self.getOp() \
1720
+ + ' ' + second + Tokens.CLOSE
1721
+
1722
+ def _str_subex(self, subex):
1723
+ return "%s" % subex
1724
+
1725
+
1726
+ class BooleanExpression(BinaryExpression):
1727
+ def _set_type(self, other_type=ANY_TYPE, signature=None):
1728
+ """:see Expression._set_type()"""
1729
+ assert isinstance(other_type, Type)
1730
+
1731
+ if signature is None:
1732
+ signature = defaultdict(list)
1733
+
1734
+ if not other_type.matches(TRUTH_TYPE):
1735
+ raise IllegalTypeException(self, other_type, TRUTH_TYPE)
1736
+ self.first._set_type(TRUTH_TYPE, signature)
1737
+ self.second._set_type(TRUTH_TYPE, signature)
1738
+
1739
+ class AndExpression(BooleanExpression):
1740
+ """This class represents conjunctions"""
1741
+ def getOp(self):
1742
+ return Tokens.AND
1743
+
1744
+ def _str_subex(self, subex):
1745
+ s = "%s" % subex
1746
+ if isinstance(subex, AndExpression):
1747
+ return s[1:-1]
1748
+ return s
1749
+
1750
+ class OrExpression(BooleanExpression):
1751
+ """This class represents disjunctions"""
1752
+ def getOp(self):
1753
+ return Tokens.OR
1754
+
1755
+ def _str_subex(self, subex):
1756
+ s = "%s" % subex
1757
+ if isinstance(subex, OrExpression):
1758
+ return s[1:-1]
1759
+ return s
1760
+
1761
+ class ImpExpression(BooleanExpression):
1762
+ """This class represents implications"""
1763
+ def getOp(self):
1764
+ return Tokens.IMP
1765
+
1766
+ class IffExpression(BooleanExpression):
1767
+ """This class represents biconditionals"""
1768
+ def getOp(self):
1769
+ return Tokens.IFF
1770
+
1771
+
1772
+ class EqualityExpression(BinaryExpression):
1773
+ """This class represents equality expressions like "(x = y)"."""
1774
+ def _set_type(self, other_type=ANY_TYPE, signature=None):
1775
+ """:see Expression._set_type()"""
1776
+ assert isinstance(other_type, Type)
1777
+
1778
+ if signature is None:
1779
+ signature = defaultdict(list)
1780
+
1781
+ if not other_type.matches(TRUTH_TYPE):
1782
+ raise IllegalTypeException(self, other_type, TRUTH_TYPE)
1783
+ self.first._set_type(ENTITY_TYPE, signature)
1784
+ self.second._set_type(ENTITY_TYPE, signature)
1785
+
1786
+ def getOp(self):
1787
+ return Tokens.EQ
1788
+
1789
+
1790
+ ### Utilities
1791
+
1792
+ class LogicalExpressionException(Exception):
1793
+ def __init__(self, index, message):
1794
+ self.index = index
1795
+ Exception.__init__(self, message)
1796
+
1797
+ class UnexpectedTokenException(LogicalExpressionException):
1798
+ def __init__(self, index, unexpected=None, expected=None, message=None):
1799
+ if unexpected and expected:
1800
+ msg = "Unexpected token: '%s'. " \
1801
+ "Expected token '%s'." % (unexpected, expected)
1802
+ elif unexpected:
1803
+ msg = "Unexpected token: '%s'." % unexpected
1804
+ if message:
1805
+ msg += ' '+message
1806
+ else:
1807
+ msg = "Expected token '%s'." % expected
1808
+ LogicalExpressionException.__init__(self, index, msg)
1809
+
1810
+ class ExpectedMoreTokensException(LogicalExpressionException):
1811
+ def __init__(self, index, message=None):
1812
+ if not message:
1813
+ message = 'More tokens expected.'
1814
+ LogicalExpressionException.__init__(self, index, 'End of input found. ' + message)
1815
+
1816
+
1817
+ def is_indvar(expr):
1818
+ """
1819
+ An individual variable must be a single lowercase character other than 'e',
1820
+ followed by zero or more digits.
1821
+
1822
+ :param expr: str
1823
+ :return: bool True if expr is of the correct form
1824
+ """
1825
+ assert isinstance(expr, string_types), "%s is not a string" % expr
1826
+ return re.match(r'^[a-df-z]\d*$', expr) is not None
1827
+
1828
+ def is_funcvar(expr):
1829
+ """
1830
+ A function variable must be a single uppercase character followed by
1831
+ zero or more digits.
1832
+
1833
+ :param expr: str
1834
+ :return: bool True if expr is of the correct form
1835
+ """
1836
+ assert isinstance(expr, string_types), "%s is not a string" % expr
1837
+ return re.match(r'^[A-Z]\d*$', expr) is not None
1838
+
1839
+ def is_eventvar(expr):
1840
+ """
1841
+ An event variable must be a single lowercase 'e' character followed by
1842
+ zero or more digits.
1843
+
1844
+ :param expr: str
1845
+ :return: bool True if expr is of the correct form
1846
+ """
1847
+ assert isinstance(expr, string_types), "%s is not a string" % expr
1848
+ return re.match(r'^e\d*$', expr) is not None
1849
+
1850
+
1851
+ def demo():
1852
+ lexpr = Expression.fromstring
1853
+ print('='*20 + 'Test reader' + '='*20)
1854
+ print(lexpr(r'john'))
1855
+ print(lexpr(r'man(x)'))
1856
+ print(lexpr(r'-man(x)'))
1857
+ print(lexpr(r'(man(x) & tall(x) & walks(x))'))
1858
+ print(lexpr(r'exists x.(man(x) & tall(x) & walks(x))'))
1859
+ print(lexpr(r'\x.man(x)'))
1860
+ print(lexpr(r'\x.man(x)(john)'))
1861
+ print(lexpr(r'\x y.sees(x,y)'))
1862
+ print(lexpr(r'\x y.sees(x,y)(a,b)'))
1863
+ print(lexpr(r'(\x.exists y.walks(x,y))(x)'))
1864
+ print(lexpr(r'exists x.x = y'))
1865
+ print(lexpr(r'exists x.(x = y)'))
1866
+ print(lexpr('P(x) & x=y & P(y)'))
1867
+ print(lexpr(r'\P Q.exists x.(P(x) & Q(x))'))
1868
+ print(lexpr(r'man(x) <-> tall(x)'))
1869
+
1870
+ print('='*20 + 'Test simplify' + '='*20)
1871
+ print(lexpr(r'\x.\y.sees(x,y)(john)(mary)').simplify())
1872
+ print(lexpr(r'\x.\y.sees(x,y)(john, mary)').simplify())
1873
+ print(lexpr(r'all x.(man(x) & (\x.exists y.walks(x,y))(x))').simplify())
1874
+ print(lexpr(r'(\P.\Q.exists x.(P(x) & Q(x)))(\x.dog(x))(\x.bark(x))').simplify())
1875
+
1876
+ print('='*20 + 'Test alpha conversion and binder expression equality' + '='*20)
1877
+ e1 = lexpr('exists x.P(x)')
1878
+ print(e1)
1879
+ e2 = e1.alpha_convert(Variable('z'))
1880
+ print(e2)
1881
+ print(e1 == e2)
1882
+
1883
+ def demo_errors():
1884
+ print('='*20 + 'Test reader errors' + '='*20)
1885
+ demoException('(P(x) & Q(x)')
1886
+ demoException('((P(x) &) & Q(x))')
1887
+ demoException('P(x) -> ')
1888
+ demoException('P(x')
1889
+ demoException('P(x,')
1890
+ demoException('P(x,)')
1891
+ demoException('exists')
1892
+ demoException('exists x.')
1893
+ demoException('\\')
1894
+ demoException('\\ x y.')
1895
+ demoException('P(x)Q(x)')
1896
+ demoException('(P(x)Q(x)')
1897
+ demoException('exists x -> y')
1898
+
1899
+ def demoException(s):
1900
+ try:
1901
+ Expression.fromstring(s)
1902
+ except LogicalExpressionException as e:
1903
+ print("%s: %s" % (e.__class__.__name__, e))
1904
+
1905
+ def printtype(ex):
1906
+ print("%s : %s" % (ex.str(), ex.type))
1907
+
1908
+ if __name__ == '__main__':
1909
+ demo()
1910
+ # demo_errors()