drt.py 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455
  1. # Natural Language Toolkit: Discourse Representation Theory (DRT)
  2. #
  3. # Author: Dan Garrette <dhgarrette@gmail.com>
  4. #
  5. # Copyright (C) 2001-2020 NLTK Project
  6. # URL: <http://nltk.org/>
  7. # For license information, see LICENSE.TXT
  8. import operator
  9. from functools import reduce
  10. from itertools import chain
  11. from nltk.sem.logic import (
  12. APP,
  13. AbstractVariableExpression,
  14. AllExpression,
  15. AndExpression,
  16. ApplicationExpression,
  17. BinaryExpression,
  18. BooleanExpression,
  19. ConstantExpression,
  20. EqualityExpression,
  21. EventVariableExpression,
  22. ExistsExpression,
  23. Expression,
  24. FunctionVariableExpression,
  25. ImpExpression,
  26. IndividualVariableExpression,
  27. LambdaExpression,
  28. Tokens,
  29. LogicParser,
  30. NegatedExpression,
  31. OrExpression,
  32. Variable,
  33. is_eventvar,
  34. is_funcvar,
  35. is_indvar,
  36. unique_variable,
  37. )
  38. # Import Tkinter-based modules if they are available
  39. try:
  40. from tkinter import Canvas, Tk
  41. from tkinter.font import Font
  42. from nltk.util import in_idle
  43. except ImportError:
  44. # No need to print a warning here, nltk.draw has already printed one.
  45. pass
  46. class DrtTokens(Tokens):
  47. DRS = "DRS"
  48. DRS_CONC = "+"
  49. PRONOUN = "PRO"
  50. OPEN_BRACKET = "["
  51. CLOSE_BRACKET = "]"
  52. COLON = ":"
  53. PUNCT = [DRS_CONC, OPEN_BRACKET, CLOSE_BRACKET, COLON]
  54. SYMBOLS = Tokens.SYMBOLS + PUNCT
  55. TOKENS = Tokens.TOKENS + [DRS] + PUNCT
  56. class DrtParser(LogicParser):
  57. """A lambda calculus expression parser."""
  58. def __init__(self):
  59. LogicParser.__init__(self)
  60. self.operator_precedence = dict(
  61. [(x, 1) for x in DrtTokens.LAMBDA_LIST]
  62. + [(x, 2) for x in DrtTokens.NOT_LIST]
  63. + [(APP, 3)]
  64. + [(x, 4) for x in DrtTokens.EQ_LIST + Tokens.NEQ_LIST]
  65. + [(DrtTokens.COLON, 5)]
  66. + [(DrtTokens.DRS_CONC, 6)]
  67. + [(x, 7) for x in DrtTokens.OR_LIST]
  68. + [(x, 8) for x in DrtTokens.IMP_LIST]
  69. + [(None, 9)]
  70. )
  71. def get_all_symbols(self):
  72. """This method exists to be overridden"""
  73. return DrtTokens.SYMBOLS
  74. def isvariable(self, tok):
  75. return tok not in DrtTokens.TOKENS
  76. def handle(self, tok, context):
  77. """This method is intended to be overridden for logics that
  78. use different operators or expressions"""
  79. if tok in DrtTokens.NOT_LIST:
  80. return self.handle_negation(tok, context)
  81. elif tok in DrtTokens.LAMBDA_LIST:
  82. return self.handle_lambda(tok, context)
  83. elif tok == DrtTokens.OPEN:
  84. if self.inRange(0) and self.token(0) == DrtTokens.OPEN_BRACKET:
  85. return self.handle_DRS(tok, context)
  86. else:
  87. return self.handle_open(tok, context)
  88. elif tok.upper() == DrtTokens.DRS:
  89. self.assertNextToken(DrtTokens.OPEN)
  90. return self.handle_DRS(tok, context)
  91. elif self.isvariable(tok):
  92. if self.inRange(0) and self.token(0) == DrtTokens.COLON:
  93. return self.handle_prop(tok, context)
  94. else:
  95. return self.handle_variable(tok, context)
  96. def make_NegatedExpression(self, expression):
  97. return DrtNegatedExpression(expression)
  98. def handle_DRS(self, tok, context):
  99. # a DRS
  100. refs = self.handle_refs()
  101. if (
  102. self.inRange(0) and self.token(0) == DrtTokens.COMMA
  103. ): # if there is a comma (it's optional)
  104. self.token() # swallow the comma
  105. conds = self.handle_conds(context)
  106. self.assertNextToken(DrtTokens.CLOSE)
  107. return DRS(refs, conds, None)
  108. def handle_refs(self):
  109. self.assertNextToken(DrtTokens.OPEN_BRACKET)
  110. refs = []
  111. while self.inRange(0) and self.token(0) != DrtTokens.CLOSE_BRACKET:
  112. # Support expressions like: DRS([x y],C) == DRS([x,y],C)
  113. if refs and self.token(0) == DrtTokens.COMMA:
  114. self.token() # swallow the comma
  115. refs.append(self.get_next_token_variable("quantified"))
  116. self.assertNextToken(DrtTokens.CLOSE_BRACKET)
  117. return refs
  118. def handle_conds(self, context):
  119. self.assertNextToken(DrtTokens.OPEN_BRACKET)
  120. conds = []
  121. while self.inRange(0) and self.token(0) != DrtTokens.CLOSE_BRACKET:
  122. # Support expressions like: DRS([x y],C) == DRS([x, y],C)
  123. if conds and self.token(0) == DrtTokens.COMMA:
  124. self.token() # swallow the comma
  125. conds.append(self.process_next_expression(context))
  126. self.assertNextToken(DrtTokens.CLOSE_BRACKET)
  127. return conds
  128. def handle_prop(self, tok, context):
  129. variable = self.make_VariableExpression(tok)
  130. self.assertNextToken(":")
  131. drs = self.process_next_expression(DrtTokens.COLON)
  132. return DrtProposition(variable, drs)
  133. def make_EqualityExpression(self, first, second):
  134. """This method serves as a hook for other logic parsers that
  135. have different equality expression classes"""
  136. return DrtEqualityExpression(first, second)
  137. def get_BooleanExpression_factory(self, tok):
  138. """This method serves as a hook for other logic parsers that
  139. have different boolean operators"""
  140. if tok == DrtTokens.DRS_CONC:
  141. return lambda first, second: DrtConcatenation(first, second, None)
  142. elif tok in DrtTokens.OR_LIST:
  143. return DrtOrExpression
  144. elif tok in DrtTokens.IMP_LIST:
  145. def make_imp_expression(first, second):
  146. if isinstance(first, DRS):
  147. return DRS(first.refs, first.conds, second)
  148. if isinstance(first, DrtConcatenation):
  149. return DrtConcatenation(first.first, first.second, second)
  150. raise Exception("Antecedent of implication must be a DRS")
  151. return make_imp_expression
  152. else:
  153. return None
  154. def make_BooleanExpression(self, factory, first, second):
  155. return factory(first, second)
  156. def make_ApplicationExpression(self, function, argument):
  157. return DrtApplicationExpression(function, argument)
  158. def make_VariableExpression(self, name):
  159. return DrtVariableExpression(Variable(name))
  160. def make_LambdaExpression(self, variables, term):
  161. return DrtLambdaExpression(variables, term)
  162. class DrtExpression(object):
  163. """
  164. This is the base abstract DRT Expression from which every DRT
  165. Expression extends.
  166. """
  167. _drt_parser = DrtParser()
  168. @classmethod
  169. def fromstring(cls, s):
  170. return cls._drt_parser.parse(s)
  171. def applyto(self, other):
  172. return DrtApplicationExpression(self, other)
  173. def __neg__(self):
  174. return DrtNegatedExpression(self)
  175. def __and__(self, other):
  176. raise NotImplementedError()
  177. def __or__(self, other):
  178. assert isinstance(other, DrtExpression)
  179. return DrtOrExpression(self, other)
  180. def __gt__(self, other):
  181. assert isinstance(other, DrtExpression)
  182. if isinstance(self, DRS):
  183. return DRS(self.refs, self.conds, other)
  184. if isinstance(self, DrtConcatenation):
  185. return DrtConcatenation(self.first, self.second, other)
  186. raise Exception("Antecedent of implication must be a DRS")
  187. def equiv(self, other, prover=None):
  188. """
  189. Check for logical equivalence.
  190. Pass the expression (self <-> other) to the theorem prover.
  191. If the prover says it is valid, then the self and other are equal.
  192. :param other: an ``DrtExpression`` to check equality against
  193. :param prover: a ``nltk.inference.api.Prover``
  194. """
  195. assert isinstance(other, DrtExpression)
  196. f1 = self.simplify().fol()
  197. f2 = other.simplify().fol()
  198. return f1.equiv(f2, prover)
  199. @property
  200. def type(self):
  201. raise AttributeError(
  202. "'%s' object has no attribute 'type'" % self.__class__.__name__
  203. )
  204. def typecheck(self, signature=None):
  205. raise NotImplementedError()
  206. def __add__(self, other):
  207. return DrtConcatenation(self, other, None)
  208. def get_refs(self, recursive=False):
  209. """
  210. Return the set of discourse referents in this DRS.
  211. :param recursive: bool Also find discourse referents in subterms?
  212. :return: list of ``Variable`` objects
  213. """
  214. raise NotImplementedError()
  215. def is_pronoun_function(self):
  216. """ Is self of the form "PRO(x)"? """
  217. return (
  218. isinstance(self, DrtApplicationExpression)
  219. and isinstance(self.function, DrtAbstractVariableExpression)
  220. and self.function.variable.name == DrtTokens.PRONOUN
  221. and isinstance(self.argument, DrtIndividualVariableExpression)
  222. )
  223. def make_EqualityExpression(self, first, second):
  224. return DrtEqualityExpression(first, second)
  225. def make_VariableExpression(self, variable):
  226. return DrtVariableExpression(variable)
  227. def resolve_anaphora(self):
  228. return resolve_anaphora(self)
  229. def eliminate_equality(self):
  230. return self.visit_structured(lambda e: e.eliminate_equality(), self.__class__)
  231. def pretty_format(self):
  232. """
  233. Draw the DRS
  234. :return: the pretty print string
  235. """
  236. return "\n".join(self._pretty())
  237. def pretty_print(self):
  238. print(self.pretty_format())
  239. def draw(self):
  240. DrsDrawer(self).draw()
  241. class DRS(DrtExpression, Expression):
  242. """A Discourse Representation Structure."""
  243. def __init__(self, refs, conds, consequent=None):
  244. """
  245. :param refs: list of ``DrtIndividualVariableExpression`` for the
  246. discourse referents
  247. :param conds: list of ``Expression`` for the conditions
  248. """
  249. self.refs = refs
  250. self.conds = conds
  251. self.consequent = consequent
  252. def replace(self, variable, expression, replace_bound=False, alpha_convert=True):
  253. """Replace all instances of variable v with expression E in self,
  254. where v is free in self."""
  255. if variable in self.refs:
  256. # if a bound variable is the thing being replaced
  257. if not replace_bound:
  258. return self
  259. else:
  260. i = self.refs.index(variable)
  261. if self.consequent:
  262. consequent = self.consequent.replace(
  263. variable, expression, True, alpha_convert
  264. )
  265. else:
  266. consequent = None
  267. return DRS(
  268. self.refs[:i] + [expression.variable] + self.refs[i + 1 :],
  269. [
  270. cond.replace(variable, expression, True, alpha_convert)
  271. for cond in self.conds
  272. ],
  273. consequent,
  274. )
  275. else:
  276. if alpha_convert:
  277. # any bound variable that appears in the expression must
  278. # be alpha converted to avoid a conflict
  279. for ref in set(self.refs) & expression.free():
  280. newvar = unique_variable(ref)
  281. newvarex = DrtVariableExpression(newvar)
  282. i = self.refs.index(ref)
  283. if self.consequent:
  284. consequent = self.consequent.replace(
  285. ref, newvarex, True, alpha_convert
  286. )
  287. else:
  288. consequent = None
  289. self = DRS(
  290. self.refs[:i] + [newvar] + self.refs[i + 1 :],
  291. [
  292. cond.replace(ref, newvarex, True, alpha_convert)
  293. for cond in self.conds
  294. ],
  295. consequent,
  296. )
  297. # replace in the conditions
  298. if self.consequent:
  299. consequent = self.consequent.replace(
  300. variable, expression, replace_bound, alpha_convert
  301. )
  302. else:
  303. consequent = None
  304. return DRS(
  305. self.refs,
  306. [
  307. cond.replace(variable, expression, replace_bound, alpha_convert)
  308. for cond in self.conds
  309. ],
  310. consequent,
  311. )
  312. def free(self):
  313. """:see: Expression.free()"""
  314. conds_free = reduce(operator.or_, [c.free() for c in self.conds], set())
  315. if self.consequent:
  316. conds_free.update(self.consequent.free())
  317. return conds_free - set(self.refs)
  318. def get_refs(self, recursive=False):
  319. """:see: AbstractExpression.get_refs()"""
  320. if recursive:
  321. conds_refs = self.refs + list(
  322. chain(*(c.get_refs(True) for c in self.conds))
  323. )
  324. if self.consequent:
  325. conds_refs.extend(self.consequent.get_refs(True))
  326. return conds_refs
  327. else:
  328. return self.refs
  329. def visit(self, function, combinator):
  330. """:see: Expression.visit()"""
  331. parts = list(map(function, self.conds))
  332. if self.consequent:
  333. parts.append(function(self.consequent))
  334. return combinator(parts)
  335. def visit_structured(self, function, combinator):
  336. """:see: Expression.visit_structured()"""
  337. consequent = function(self.consequent) if self.consequent else None
  338. return combinator(self.refs, list(map(function, self.conds)), consequent)
  339. def eliminate_equality(self):
  340. drs = self
  341. i = 0
  342. while i < len(drs.conds):
  343. cond = drs.conds[i]
  344. if (
  345. isinstance(cond, EqualityExpression)
  346. and isinstance(cond.first, AbstractVariableExpression)
  347. and isinstance(cond.second, AbstractVariableExpression)
  348. ):
  349. drs = DRS(
  350. list(set(drs.refs) - set([cond.second.variable])),
  351. drs.conds[:i] + drs.conds[i + 1 :],
  352. drs.consequent,
  353. )
  354. if cond.second.variable != cond.first.variable:
  355. drs = drs.replace(cond.second.variable, cond.first, False, False)
  356. i = 0
  357. i -= 1
  358. i += 1
  359. conds = []
  360. for cond in drs.conds:
  361. new_cond = cond.eliminate_equality()
  362. new_cond_simp = new_cond.simplify()
  363. if (
  364. not isinstance(new_cond_simp, DRS)
  365. or new_cond_simp.refs
  366. or new_cond_simp.conds
  367. or new_cond_simp.consequent
  368. ):
  369. conds.append(new_cond)
  370. consequent = drs.consequent.eliminate_equality() if drs.consequent else None
  371. return DRS(drs.refs, conds, consequent)
  372. def fol(self):
  373. if self.consequent:
  374. accum = None
  375. if self.conds:
  376. accum = reduce(AndExpression, [c.fol() for c in self.conds])
  377. if accum:
  378. accum = ImpExpression(accum, self.consequent.fol())
  379. else:
  380. accum = self.consequent.fol()
  381. for ref in self.refs[::-1]:
  382. accum = AllExpression(ref, accum)
  383. return accum
  384. else:
  385. if not self.conds:
  386. raise Exception("Cannot convert DRS with no conditions to FOL.")
  387. accum = reduce(AndExpression, [c.fol() for c in self.conds])
  388. for ref in map(Variable, self._order_ref_strings(self.refs)[::-1]):
  389. accum = ExistsExpression(ref, accum)
  390. return accum
  391. def _pretty(self):
  392. refs_line = " ".join(self._order_ref_strings(self.refs))
  393. cond_lines = [
  394. cond
  395. for cond_line in [
  396. filter(lambda s: s.strip(), cond._pretty()) for cond in self.conds
  397. ]
  398. for cond in cond_line
  399. ]
  400. length = max([len(refs_line)] + list(map(len, cond_lines)))
  401. drs = (
  402. [
  403. " _" + "_" * length + "_ ",
  404. "| " + refs_line.ljust(length) + " |",
  405. "|-" + "-" * length + "-|",
  406. ]
  407. + ["| " + line.ljust(length) + " |" for line in cond_lines]
  408. + ["|_" + "_" * length + "_|"]
  409. )
  410. if self.consequent:
  411. return DrtBinaryExpression._assemble_pretty(
  412. drs, DrtTokens.IMP, self.consequent._pretty()
  413. )
  414. return drs
  415. def _order_ref_strings(self, refs):
  416. strings = ["%s" % ref for ref in refs]
  417. ind_vars = []
  418. func_vars = []
  419. event_vars = []
  420. other_vars = []
  421. for s in strings:
  422. if is_indvar(s):
  423. ind_vars.append(s)
  424. elif is_funcvar(s):
  425. func_vars.append(s)
  426. elif is_eventvar(s):
  427. event_vars.append(s)
  428. else:
  429. other_vars.append(s)
  430. return (
  431. sorted(other_vars)
  432. + sorted(event_vars, key=lambda v: int([v[2:], -1][len(v[2:]) == 0]))
  433. + sorted(func_vars, key=lambda v: (v[0], int([v[1:], -1][len(v[1:]) == 0])))
  434. + sorted(ind_vars, key=lambda v: (v[0], int([v[1:], -1][len(v[1:]) == 0])))
  435. )
  436. def __eq__(self, other):
  437. r"""Defines equality modulo alphabetic variance.
  438. If we are comparing \x.M and \y.N, then check equality of M and N[x/y]."""
  439. if isinstance(other, DRS):
  440. if len(self.refs) == len(other.refs):
  441. converted_other = other
  442. for (r1, r2) in zip(self.refs, converted_other.refs):
  443. varex = self.make_VariableExpression(r1)
  444. converted_other = converted_other.replace(r2, varex, True)
  445. if self.consequent == converted_other.consequent and len(
  446. self.conds
  447. ) == len(converted_other.conds):
  448. for c1, c2 in zip(self.conds, converted_other.conds):
  449. if not (c1 == c2):
  450. return False
  451. return True
  452. return False
  453. def __ne__(self, other):
  454. return not self == other
  455. __hash__ = Expression.__hash__
  456. def __str__(self):
  457. drs = "([%s],[%s])" % (
  458. ",".join(self._order_ref_strings(self.refs)),
  459. ", ".join("%s" % cond for cond in self.conds),
  460. ) # map(str, self.conds)))
  461. if self.consequent:
  462. return (
  463. DrtTokens.OPEN
  464. + drs
  465. + " "
  466. + DrtTokens.IMP
  467. + " "
  468. + "%s" % self.consequent
  469. + DrtTokens.CLOSE
  470. )
  471. return drs
  472. def DrtVariableExpression(variable):
  473. """
  474. This is a factory method that instantiates and returns a subtype of
  475. ``DrtAbstractVariableExpression`` appropriate for the given variable.
  476. """
  477. if is_indvar(variable.name):
  478. return DrtIndividualVariableExpression(variable)
  479. elif is_funcvar(variable.name):
  480. return DrtFunctionVariableExpression(variable)
  481. elif is_eventvar(variable.name):
  482. return DrtEventVariableExpression(variable)
  483. else:
  484. return DrtConstantExpression(variable)
  485. class DrtAbstractVariableExpression(DrtExpression, AbstractVariableExpression):
  486. def fol(self):
  487. return self
  488. def get_refs(self, recursive=False):
  489. """:see: AbstractExpression.get_refs()"""
  490. return []
  491. def _pretty(self):
  492. s = "%s" % self
  493. blank = " " * len(s)
  494. return [blank, blank, s, blank]
  495. def eliminate_equality(self):
  496. return self
  497. class DrtIndividualVariableExpression(
  498. DrtAbstractVariableExpression, IndividualVariableExpression
  499. ):
  500. pass
  501. class DrtFunctionVariableExpression(
  502. DrtAbstractVariableExpression, FunctionVariableExpression
  503. ):
  504. pass
  505. class DrtEventVariableExpression(
  506. DrtIndividualVariableExpression, EventVariableExpression
  507. ):
  508. pass
  509. class DrtConstantExpression(DrtAbstractVariableExpression, ConstantExpression):
  510. pass
  511. class DrtProposition(DrtExpression, Expression):
  512. def __init__(self, variable, drs):
  513. self.variable = variable
  514. self.drs = drs
  515. def replace(self, variable, expression, replace_bound=False, alpha_convert=True):
  516. if self.variable == variable:
  517. assert isinstance(
  518. expression, DrtAbstractVariableExpression
  519. ), "Can only replace a proposition label with a variable"
  520. return DrtProposition(
  521. expression.variable,
  522. self.drs.replace(variable, expression, replace_bound, alpha_convert),
  523. )
  524. else:
  525. return DrtProposition(
  526. self.variable,
  527. self.drs.replace(variable, expression, replace_bound, alpha_convert),
  528. )
  529. def eliminate_equality(self):
  530. return DrtProposition(self.variable, self.drs.eliminate_equality())
  531. def get_refs(self, recursive=False):
  532. return self.drs.get_refs(True) if recursive else []
  533. def __eq__(self, other):
  534. return (
  535. self.__class__ == other.__class__
  536. and self.variable == other.variable
  537. and self.drs == other.drs
  538. )
  539. def __ne__(self, other):
  540. return not self == other
  541. __hash__ = Expression.__hash__
  542. def fol(self):
  543. return self.drs.fol()
  544. def _pretty(self):
  545. drs_s = self.drs._pretty()
  546. blank = " " * len("%s" % self.variable)
  547. return (
  548. [blank + " " + line for line in drs_s[:1]]
  549. + ["%s" % self.variable + ":" + line for line in drs_s[1:2]]
  550. + [blank + " " + line for line in drs_s[2:]]
  551. )
  552. def visit(self, function, combinator):
  553. """:see: Expression.visit()"""
  554. return combinator([function(self.drs)])
  555. def visit_structured(self, function, combinator):
  556. """:see: Expression.visit_structured()"""
  557. return combinator(self.variable, function(self.drs))
  558. def __str__(self):
  559. return "prop(%s, %s)" % (self.variable, self.drs)
  560. class DrtNegatedExpression(DrtExpression, NegatedExpression):
  561. def fol(self):
  562. return NegatedExpression(self.term.fol())
  563. def get_refs(self, recursive=False):
  564. """:see: AbstractExpression.get_refs()"""
  565. return self.term.get_refs(recursive)
  566. def _pretty(self):
  567. term_lines = self.term._pretty()
  568. return (
  569. [" " + line for line in term_lines[:2]]
  570. + ["__ " + line for line in term_lines[2:3]]
  571. + [" | " + line for line in term_lines[3:4]]
  572. + [" " + line for line in term_lines[4:]]
  573. )
  574. class DrtLambdaExpression(DrtExpression, LambdaExpression):
  575. def alpha_convert(self, newvar):
  576. """Rename all occurrences of the variable introduced by this variable
  577. binder in the expression to ``newvar``.
  578. :param newvar: ``Variable``, for the new variable
  579. """
  580. return self.__class__(
  581. newvar,
  582. self.term.replace(self.variable, DrtVariableExpression(newvar), True),
  583. )
  584. def fol(self):
  585. return LambdaExpression(self.variable, self.term.fol())
  586. def _pretty(self):
  587. variables = [self.variable]
  588. term = self.term
  589. while term.__class__ == self.__class__:
  590. variables.append(term.variable)
  591. term = term.term
  592. var_string = " ".join("%s" % v for v in variables) + DrtTokens.DOT
  593. term_lines = term._pretty()
  594. blank = " " * len(var_string)
  595. return (
  596. [" " + blank + line for line in term_lines[:1]]
  597. + [" \ " + blank + line for line in term_lines[1:2]]
  598. + [" /\ " + var_string + line for line in term_lines[2:3]]
  599. + [" " + blank + line for line in term_lines[3:]]
  600. )
  601. class DrtBinaryExpression(DrtExpression, BinaryExpression):
  602. def get_refs(self, recursive=False):
  603. """:see: AbstractExpression.get_refs()"""
  604. return (
  605. self.first.get_refs(True) + self.second.get_refs(True) if recursive else []
  606. )
  607. def _pretty(self):
  608. return DrtBinaryExpression._assemble_pretty(
  609. self._pretty_subex(self.first),
  610. self.getOp(),
  611. self._pretty_subex(self.second),
  612. )
  613. @staticmethod
  614. def _assemble_pretty(first_lines, op, second_lines):
  615. max_lines = max(len(first_lines), len(second_lines))
  616. first_lines = _pad_vertically(first_lines, max_lines)
  617. second_lines = _pad_vertically(second_lines, max_lines)
  618. blank = " " * len(op)
  619. first_second_lines = list(zip(first_lines, second_lines))
  620. return (
  621. [
  622. " " + first_line + " " + blank + " " + second_line + " "
  623. for first_line, second_line in first_second_lines[:2]
  624. ]
  625. + [
  626. "(" + first_line + " " + op + " " + second_line + ")"
  627. for first_line, second_line in first_second_lines[2:3]
  628. ]
  629. + [
  630. " " + first_line + " " + blank + " " + second_line + " "
  631. for first_line, second_line in first_second_lines[3:]
  632. ]
  633. )
  634. def _pretty_subex(self, subex):
  635. return subex._pretty()
  636. class DrtBooleanExpression(DrtBinaryExpression, BooleanExpression):
  637. pass
  638. class DrtOrExpression(DrtBooleanExpression, OrExpression):
  639. def fol(self):
  640. return OrExpression(self.first.fol(), self.second.fol())
  641. def _pretty_subex(self, subex):
  642. if isinstance(subex, DrtOrExpression):
  643. return [line[1:-1] for line in subex._pretty()]
  644. return DrtBooleanExpression._pretty_subex(self, subex)
  645. class DrtEqualityExpression(DrtBinaryExpression, EqualityExpression):
  646. def fol(self):
  647. return EqualityExpression(self.first.fol(), self.second.fol())
  648. class DrtConcatenation(DrtBooleanExpression):
  649. """DRS of the form '(DRS + DRS)'"""
  650. def __init__(self, first, second, consequent=None):
  651. DrtBooleanExpression.__init__(self, first, second)
  652. self.consequent = consequent
  653. def replace(self, variable, expression, replace_bound=False, alpha_convert=True):
  654. """Replace all instances of variable v with expression E in self,
  655. where v is free in self."""
  656. first = self.first
  657. second = self.second
  658. consequent = self.consequent
  659. # If variable is bound
  660. if variable in self.get_refs():
  661. if replace_bound:
  662. first = first.replace(
  663. variable, expression, replace_bound, alpha_convert
  664. )
  665. second = second.replace(
  666. variable, expression, replace_bound, alpha_convert
  667. )
  668. if consequent:
  669. consequent = consequent.replace(
  670. variable, expression, replace_bound, alpha_convert
  671. )
  672. else:
  673. if alpha_convert:
  674. # alpha convert every ref that is free in 'expression'
  675. for ref in set(self.get_refs(True)) & expression.free():
  676. v = DrtVariableExpression(unique_variable(ref))
  677. first = first.replace(ref, v, True, alpha_convert)
  678. second = second.replace(ref, v, True, alpha_convert)
  679. if consequent:
  680. consequent = consequent.replace(ref, v, True, alpha_convert)
  681. first = first.replace(variable, expression, replace_bound, alpha_convert)
  682. second = second.replace(variable, expression, replace_bound, alpha_convert)
  683. if consequent:
  684. consequent = consequent.replace(
  685. variable, expression, replace_bound, alpha_convert
  686. )
  687. return self.__class__(first, second, consequent)
  688. def eliminate_equality(self):
  689. # TODO: at some point. for now, simplify.
  690. drs = self.simplify()
  691. assert not isinstance(drs, DrtConcatenation)
  692. return drs.eliminate_equality()
  693. def simplify(self):
  694. first = self.first.simplify()
  695. second = self.second.simplify()
  696. consequent = self.consequent.simplify() if self.consequent else None
  697. if isinstance(first, DRS) and isinstance(second, DRS):
  698. # For any ref that is in both 'first' and 'second'
  699. for ref in set(first.get_refs(True)) & set(second.get_refs(True)):
  700. # alpha convert the ref in 'second' to prevent collision
  701. newvar = DrtVariableExpression(unique_variable(ref))
  702. second = second.replace(ref, newvar, True)
  703. return DRS(first.refs + second.refs, first.conds + second.conds, consequent)
  704. else:
  705. return self.__class__(first, second, consequent)
  706. def get_refs(self, recursive=False):
  707. """:see: AbstractExpression.get_refs()"""
  708. refs = self.first.get_refs(recursive) + self.second.get_refs(recursive)
  709. if self.consequent and recursive:
  710. refs.extend(self.consequent.get_refs(True))
  711. return refs
  712. def getOp(self):
  713. return DrtTokens.DRS_CONC
  714. def __eq__(self, other):
  715. r"""Defines equality modulo alphabetic variance.
  716. If we are comparing \x.M and \y.N, then check equality of M and N[x/y]."""
  717. if isinstance(other, DrtConcatenation):
  718. self_refs = self.get_refs()
  719. other_refs = other.get_refs()
  720. if len(self_refs) == len(other_refs):
  721. converted_other = other
  722. for (r1, r2) in zip(self_refs, other_refs):
  723. varex = self.make_VariableExpression(r1)
  724. converted_other = converted_other.replace(r2, varex, True)
  725. return (
  726. self.first == converted_other.first
  727. and self.second == converted_other.second
  728. and self.consequent == converted_other.consequent
  729. )
  730. return False
  731. def __ne__(self, other):
  732. return not self == other
  733. __hash__ = DrtBooleanExpression.__hash__
  734. def fol(self):
  735. e = AndExpression(self.first.fol(), self.second.fol())
  736. if self.consequent:
  737. e = ImpExpression(e, self.consequent.fol())
  738. return e
  739. def _pretty(self):
  740. drs = DrtBinaryExpression._assemble_pretty(
  741. self._pretty_subex(self.first),
  742. self.getOp(),
  743. self._pretty_subex(self.second),
  744. )
  745. if self.consequent:
  746. drs = DrtBinaryExpression._assemble_pretty(
  747. drs, DrtTokens.IMP, self._pretty(self.consequent)
  748. )
  749. return drs
  750. def _pretty_subex(self, subex):
  751. if isinstance(subex, DrtConcatenation):
  752. return [line[1:-1] for line in subex._pretty()]
  753. return DrtBooleanExpression._pretty_subex(self, subex)
  754. def visit(self, function, combinator):
  755. """:see: Expression.visit()"""
  756. if self.consequent:
  757. return combinator(
  758. [function(self.first), function(self.second), function(self.consequent)]
  759. )
  760. else:
  761. return combinator([function(self.first), function(self.second)])
  762. def __str__(self):
  763. first = self._str_subex(self.first)
  764. second = self._str_subex(self.second)
  765. drs = Tokens.OPEN + first + " " + self.getOp() + " " + second + Tokens.CLOSE
  766. if self.consequent:
  767. return (
  768. DrtTokens.OPEN
  769. + drs
  770. + " "
  771. + DrtTokens.IMP
  772. + " "
  773. + "%s" % self.consequent
  774. + DrtTokens.CLOSE
  775. )
  776. return drs
  777. def _str_subex(self, subex):
  778. s = "%s" % subex
  779. if isinstance(subex, DrtConcatenation) and subex.consequent is None:
  780. return s[1:-1]
  781. return s
  782. class DrtApplicationExpression(DrtExpression, ApplicationExpression):
  783. def fol(self):
  784. return ApplicationExpression(self.function.fol(), self.argument.fol())
  785. def get_refs(self, recursive=False):
  786. """:see: AbstractExpression.get_refs()"""
  787. return (
  788. self.function.get_refs(True) + self.argument.get_refs(True)
  789. if recursive
  790. else []
  791. )
  792. def _pretty(self):
  793. function, args = self.uncurry()
  794. function_lines = function._pretty()
  795. args_lines = [arg._pretty() for arg in args]
  796. max_lines = max(map(len, [function_lines] + args_lines))
  797. function_lines = _pad_vertically(function_lines, max_lines)
  798. args_lines = [_pad_vertically(arg_lines, max_lines) for arg_lines in args_lines]
  799. func_args_lines = list(zip(function_lines, list(zip(*args_lines))))
  800. return (
  801. [
  802. func_line + " " + " ".join(args_line) + " "
  803. for func_line, args_line in func_args_lines[:2]
  804. ]
  805. + [
  806. func_line + "(" + ",".join(args_line) + ")"
  807. for func_line, args_line in func_args_lines[2:3]
  808. ]
  809. + [
  810. func_line + " " + " ".join(args_line) + " "
  811. for func_line, args_line in func_args_lines[3:]
  812. ]
  813. )
  814. def _pad_vertically(lines, max_lines):
  815. pad_line = [" " * len(lines[0])]
  816. return lines + pad_line * (max_lines - len(lines))
  817. class PossibleAntecedents(list, DrtExpression, Expression):
  818. def free(self):
  819. """Set of free variables."""
  820. return set(self)
  821. def replace(self, variable, expression, replace_bound=False, alpha_convert=True):
  822. """Replace all instances of variable v with expression E in self,
  823. where v is free in self."""
  824. result = PossibleAntecedents()
  825. for item in self:
  826. if item == variable:
  827. self.append(expression)
  828. else:
  829. self.append(item)
  830. return result
  831. def _pretty(self):
  832. s = "%s" % self
  833. blank = " " * len(s)
  834. return [blank, blank, s]
  835. def __str__(self):
  836. return "[" + ",".join("%s" % it for it in self) + "]"
  837. class AnaphoraResolutionException(Exception):
  838. pass
  839. def resolve_anaphora(expression, trail=[]):
  840. if isinstance(expression, ApplicationExpression):
  841. if expression.is_pronoun_function():
  842. possible_antecedents = PossibleAntecedents()
  843. for ancestor in trail:
  844. for ref in ancestor.get_refs():
  845. refex = expression.make_VariableExpression(ref)
  846. # ==========================================================
  847. # Don't allow resolution to itself or other types
  848. # ==========================================================
  849. if refex.__class__ == expression.argument.__class__ and not (
  850. refex == expression.argument
  851. ):
  852. possible_antecedents.append(refex)
  853. if len(possible_antecedents) == 1:
  854. resolution = possible_antecedents[0]
  855. else:
  856. resolution = possible_antecedents
  857. return expression.make_EqualityExpression(expression.argument, resolution)
  858. else:
  859. r_function = resolve_anaphora(expression.function, trail + [expression])
  860. r_argument = resolve_anaphora(expression.argument, trail + [expression])
  861. return expression.__class__(r_function, r_argument)
  862. elif isinstance(expression, DRS):
  863. r_conds = []
  864. for cond in expression.conds:
  865. r_cond = resolve_anaphora(cond, trail + [expression])
  866. # if the condition is of the form '(x = [])' then raise exception
  867. if isinstance(r_cond, EqualityExpression):
  868. if isinstance(r_cond.first, PossibleAntecedents):
  869. # Reverse the order so that the variable is on the left
  870. temp = r_cond.first
  871. r_cond.first = r_cond.second
  872. r_cond.second = temp
  873. if isinstance(r_cond.second, PossibleAntecedents):
  874. if not r_cond.second:
  875. raise AnaphoraResolutionException(
  876. "Variable '%s' does not "
  877. "resolve to anything." % r_cond.first
  878. )
  879. r_conds.append(r_cond)
  880. if expression.consequent:
  881. consequent = resolve_anaphora(expression.consequent, trail + [expression])
  882. else:
  883. consequent = None
  884. return expression.__class__(expression.refs, r_conds, consequent)
  885. elif isinstance(expression, AbstractVariableExpression):
  886. return expression
  887. elif isinstance(expression, NegatedExpression):
  888. return expression.__class__(
  889. resolve_anaphora(expression.term, trail + [expression])
  890. )
  891. elif isinstance(expression, DrtConcatenation):
  892. if expression.consequent:
  893. consequent = resolve_anaphora(expression.consequent, trail + [expression])
  894. else:
  895. consequent = None
  896. return expression.__class__(
  897. resolve_anaphora(expression.first, trail + [expression]),
  898. resolve_anaphora(expression.second, trail + [expression]),
  899. consequent,
  900. )
  901. elif isinstance(expression, BinaryExpression):
  902. return expression.__class__(
  903. resolve_anaphora(expression.first, trail + [expression]),
  904. resolve_anaphora(expression.second, trail + [expression]),
  905. )
  906. elif isinstance(expression, LambdaExpression):
  907. return expression.__class__(
  908. expression.variable, resolve_anaphora(expression.term, trail + [expression])
  909. )
  910. class DrsDrawer(object):
  911. BUFFER = 3 # Space between elements
  912. TOPSPACE = 10 # Space above whole DRS
  913. OUTERSPACE = 6 # Space to the left, right, and bottom of the whle DRS
  914. def __init__(self, drs, size_canvas=True, canvas=None):
  915. """
  916. :param drs: ``DrtExpression``, The DRS to be drawn
  917. :param size_canvas: bool, True if the canvas size should be the exact size of the DRS
  918. :param canvas: ``Canvas`` The canvas on which to draw the DRS. If none is given, create a new canvas.
  919. """
  920. master = None
  921. if not canvas:
  922. master = Tk()
  923. master.title("DRT")
  924. font = Font(family="helvetica", size=12)
  925. if size_canvas:
  926. canvas = Canvas(master, width=0, height=0)
  927. canvas.font = font
  928. self.canvas = canvas
  929. (right, bottom) = self._visit(drs, self.OUTERSPACE, self.TOPSPACE)
  930. width = max(right + self.OUTERSPACE, 100)
  931. height = bottom + self.OUTERSPACE
  932. canvas = Canvas(master, width=width, height=height) # , bg='white')
  933. else:
  934. canvas = Canvas(master, width=300, height=300)
  935. canvas.pack()
  936. canvas.font = font
  937. self.canvas = canvas
  938. self.drs = drs
  939. self.master = master
  940. def _get_text_height(self):
  941. """Get the height of a line of text"""
  942. return self.canvas.font.metrics("linespace")
  943. def draw(self, x=OUTERSPACE, y=TOPSPACE):
  944. """Draw the DRS"""
  945. self._handle(self.drs, self._draw_command, x, y)
  946. if self.master and not in_idle():
  947. self.master.mainloop()
  948. else:
  949. return self._visit(self.drs, x, y)
  950. def _visit(self, expression, x, y):
  951. """
  952. Return the bottom-rightmost point without actually drawing the item
  953. :param expression: the item to visit
  954. :param x: the top of the current drawing area
  955. :param y: the left side of the current drawing area
  956. :return: the bottom-rightmost point
  957. """
  958. return self._handle(expression, self._visit_command, x, y)
  959. def _draw_command(self, item, x, y):
  960. """
  961. Draw the given item at the given location
  962. :param item: the item to draw
  963. :param x: the top of the current drawing area
  964. :param y: the left side of the current drawing area
  965. :return: the bottom-rightmost point
  966. """
  967. if isinstance(item, str):
  968. self.canvas.create_text(x, y, anchor="nw", font=self.canvas.font, text=item)
  969. elif isinstance(item, tuple):
  970. # item is the lower-right of a box
  971. (right, bottom) = item
  972. self.canvas.create_rectangle(x, y, right, bottom)
  973. horiz_line_y = (
  974. y + self._get_text_height() + (self.BUFFER * 2)
  975. ) # the line separating refs from conds
  976. self.canvas.create_line(x, horiz_line_y, right, horiz_line_y)
  977. return self._visit_command(item, x, y)
  978. def _visit_command(self, item, x, y):
  979. """
  980. Return the bottom-rightmost point without actually drawing the item
  981. :param item: the item to visit
  982. :param x: the top of the current drawing area
  983. :param y: the left side of the current drawing area
  984. :return: the bottom-rightmost point
  985. """
  986. if isinstance(item, str):
  987. return (x + self.canvas.font.measure(item), y + self._get_text_height())
  988. elif isinstance(item, tuple):
  989. return item
  990. def _handle(self, expression, command, x=0, y=0):
  991. """
  992. :param expression: the expression to handle
  993. :param command: the function to apply, either _draw_command or _visit_command
  994. :param x: the top of the current drawing area
  995. :param y: the left side of the current drawing area
  996. :return: the bottom-rightmost point
  997. """
  998. if command == self._visit_command:
  999. # if we don't need to draw the item, then we can use the cached values
  1000. try:
  1001. # attempt to retrieve cached values
  1002. right = expression._drawing_width + x
  1003. bottom = expression._drawing_height + y
  1004. return (right, bottom)
  1005. except AttributeError:
  1006. # the values have not been cached yet, so compute them
  1007. pass
  1008. if isinstance(expression, DrtAbstractVariableExpression):
  1009. factory = self._handle_VariableExpression
  1010. elif isinstance(expression, DRS):
  1011. factory = self._handle_DRS
  1012. elif isinstance(expression, DrtNegatedExpression):
  1013. factory = self._handle_NegatedExpression
  1014. elif isinstance(expression, DrtLambdaExpression):
  1015. factory = self._handle_LambdaExpression
  1016. elif isinstance(expression, BinaryExpression):
  1017. factory = self._handle_BinaryExpression
  1018. elif isinstance(expression, DrtApplicationExpression):
  1019. factory = self._handle_ApplicationExpression
  1020. elif isinstance(expression, PossibleAntecedents):
  1021. factory = self._handle_VariableExpression
  1022. elif isinstance(expression, DrtProposition):
  1023. factory = self._handle_DrtProposition
  1024. else:
  1025. raise Exception(expression.__class__.__name__)
  1026. (right, bottom) = factory(expression, command, x, y)
  1027. # cache the values
  1028. expression._drawing_width = right - x
  1029. expression._drawing_height = bottom - y
  1030. return (right, bottom)
  1031. def _handle_VariableExpression(self, expression, command, x, y):
  1032. return command("%s" % expression, x, y)
  1033. def _handle_NegatedExpression(self, expression, command, x, y):
  1034. # Find the width of the negation symbol
  1035. right = self._visit_command(DrtTokens.NOT, x, y)[0]
  1036. # Handle term
  1037. (right, bottom) = self._handle(expression.term, command, right, y)
  1038. # Handle variables now that we know the y-coordinate
  1039. command(
  1040. DrtTokens.NOT,
  1041. x,
  1042. self._get_centered_top(y, bottom - y, self._get_text_height()),
  1043. )
  1044. return (right, bottom)
  1045. def _handle_DRS(self, expression, command, x, y):
  1046. left = x + self.BUFFER # indent the left side
  1047. bottom = y + self.BUFFER # indent the top
  1048. # Handle Discourse Referents
  1049. if expression.refs:
  1050. refs = " ".join("%s" % r for r in expression.refs)
  1051. else:
  1052. refs = " "
  1053. (max_right, bottom) = command(refs, left, bottom)
  1054. bottom += self.BUFFER * 2
  1055. # Handle Conditions
  1056. if expression.conds:
  1057. for cond in expression.conds:
  1058. (right, bottom) = self._handle(cond, command, left, bottom)
  1059. max_right = max(max_right, right)
  1060. bottom += self.BUFFER
  1061. else:
  1062. bottom += self._get_text_height() + self.BUFFER
  1063. # Handle Box
  1064. max_right += self.BUFFER
  1065. return command((max_right, bottom), x, y)
  1066. def _handle_ApplicationExpression(self, expression, command, x, y):
  1067. function, args = expression.uncurry()
  1068. if not isinstance(function, DrtAbstractVariableExpression):
  1069. # It's not a predicate expression ("P(x,y)"), so leave arguments curried
  1070. function = expression.function
  1071. args = [expression.argument]
  1072. # Get the max bottom of any element on the line
  1073. function_bottom = self._visit(function, x, y)[1]
  1074. max_bottom = max(
  1075. [function_bottom] + [self._visit(arg, x, y)[1] for arg in args]
  1076. )
  1077. line_height = max_bottom - y
  1078. # Handle 'function'
  1079. function_drawing_top = self._get_centered_top(
  1080. y, line_height, function._drawing_height
  1081. )
  1082. right = self._handle(function, command, x, function_drawing_top)[0]
  1083. # Handle open paren
  1084. centred_string_top = self._get_centered_top(
  1085. y, line_height, self._get_text_height()
  1086. )
  1087. right = command(DrtTokens.OPEN, right, centred_string_top)[0]
  1088. # Handle each arg
  1089. for (i, arg) in enumerate(args):
  1090. arg_drawing_top = self._get_centered_top(
  1091. y, line_height, arg._drawing_height
  1092. )
  1093. right = self._handle(arg, command, right, arg_drawing_top)[0]
  1094. if i + 1 < len(args):
  1095. # since it's not the last arg, add a comma
  1096. right = command(DrtTokens.COMMA + " ", right, centred_string_top)[0]
  1097. # Handle close paren
  1098. right = command(DrtTokens.CLOSE, right, centred_string_top)[0]
  1099. return (right, max_bottom)
  1100. def _handle_LambdaExpression(self, expression, command, x, y):
  1101. # Find the width of the lambda symbol and abstracted variables
  1102. variables = DrtTokens.LAMBDA + "%s" % expression.variable + DrtTokens.DOT
  1103. right = self._visit_command(variables, x, y)[0]
  1104. # Handle term
  1105. (right, bottom) = self._handle(expression.term, command, right, y)
  1106. # Handle variables now that we know the y-coordinate
  1107. command(
  1108. variables, x, self._get_centered_top(y, bottom - y, self._get_text_height())
  1109. )
  1110. return (right, bottom)
  1111. def _handle_BinaryExpression(self, expression, command, x, y):
  1112. # Get the full height of the line, based on the operands
  1113. first_height = self._visit(expression.first, 0, 0)[1]
  1114. second_height = self._visit(expression.second, 0, 0)[1]
  1115. line_height = max(first_height, second_height)
  1116. # Handle open paren
  1117. centred_string_top = self._get_centered_top(
  1118. y, line_height, self._get_text_height()
  1119. )
  1120. right = command(DrtTokens.OPEN, x, centred_string_top)[0]
  1121. # Handle the first operand
  1122. first_height = expression.first._drawing_height
  1123. (right, first_bottom) = self._handle(
  1124. expression.first,
  1125. command,
  1126. right,
  1127. self._get_centered_top(y, line_height, first_height),
  1128. )
  1129. # Handle the operator
  1130. right = command(" %s " % expression.getOp(), right, centred_string_top)[0]
  1131. # Handle the second operand
  1132. second_height = expression.second._drawing_height
  1133. (right, second_bottom) = self._handle(
  1134. expression.second,
  1135. command,
  1136. right,
  1137. self._get_centered_top(y, line_height, second_height),
  1138. )
  1139. # Handle close paren
  1140. right = command(DrtTokens.CLOSE, right, centred_string_top)[0]
  1141. return (right, max(first_bottom, second_bottom))
  1142. def _handle_DrtProposition(self, expression, command, x, y):
  1143. # Find the width of the negation symbol
  1144. right = command(expression.variable, x, y)[0]
  1145. # Handle term
  1146. (right, bottom) = self._handle(expression.term, command, right, y)
  1147. return (right, bottom)
  1148. def _get_centered_top(self, top, full_height, item_height):
  1149. """Get the y-coordinate of the point that a figure should start at if
  1150. its height is 'item_height' and it needs to be centered in an area that
  1151. starts at 'top' and is 'full_height' tall."""
  1152. return top + (full_height - item_height) / 2
  1153. def demo():
  1154. print("=" * 20 + "TEST PARSE" + "=" * 20)
  1155. dexpr = DrtExpression.fromstring
  1156. print(dexpr(r"([x,y],[sees(x,y)])"))
  1157. print(dexpr(r"([x],[man(x), walks(x)])"))
  1158. print(dexpr(r"\x.\y.([],[sees(x,y)])"))
  1159. print(dexpr(r"\x.([],[walks(x)])(john)"))
  1160. print(dexpr(r"(([x],[walks(x)]) + ([y],[runs(y)]))"))
  1161. print(dexpr(r"(([],[walks(x)]) -> ([],[runs(x)]))"))
  1162. print(dexpr(r"([x],[PRO(x), sees(John,x)])"))
  1163. print(dexpr(r"([x],[man(x), -([],[walks(x)])])"))
  1164. print(dexpr(r"([],[(([x],[man(x)]) -> ([],[walks(x)]))])"))
  1165. print("=" * 20 + "Test fol()" + "=" * 20)
  1166. print(dexpr(r"([x,y],[sees(x,y)])").fol())
  1167. print("=" * 20 + "Test alpha conversion and lambda expression equality" + "=" * 20)
  1168. e1 = dexpr(r"\x.([],[P(x)])")
  1169. print(e1)
  1170. e2 = e1.alpha_convert(Variable("z"))
  1171. print(e2)
  1172. print(e1 == e2)
  1173. print("=" * 20 + "Test resolve_anaphora()" + "=" * 20)
  1174. print(resolve_anaphora(dexpr(r"([x,y,z],[dog(x), cat(y), walks(z), PRO(z)])")))
  1175. print(
  1176. resolve_anaphora(dexpr(r"([],[(([x],[dog(x)]) -> ([y],[walks(y), PRO(y)]))])"))
  1177. )
  1178. print(resolve_anaphora(dexpr(r"(([x,y],[]) + ([],[PRO(x)]))")))
  1179. print("=" * 20 + "Test pretty_print()" + "=" * 20)
  1180. dexpr(r"([],[])").pretty_print()
  1181. dexpr(
  1182. r"([],[([x],[big(x), dog(x)]) -> ([],[bark(x)]) -([x],[walk(x)])])"
  1183. ).pretty_print()
  1184. dexpr(r"([x,y],[x=y]) + ([z],[dog(z), walk(z)])").pretty_print()
  1185. dexpr(r"([],[([x],[]) | ([y],[]) | ([z],[dog(z), walk(z)])])").pretty_print()
  1186. dexpr(r"\P.\Q.(([x],[]) + P(x) + Q(x))(\x.([],[dog(x)]))").pretty_print()
  1187. def test_draw():
  1188. try:
  1189. from tkinter import Tk
  1190. except ImportError:
  1191. from nose import SkipTest
  1192. raise SkipTest("tkinter is required, but it's not available.")
  1193. expressions = [
  1194. r"x",
  1195. r"([],[])",
  1196. r"([x],[])",
  1197. r"([x],[man(x)])",
  1198. r"([x,y],[sees(x,y)])",
  1199. r"([x],[man(x), walks(x)])",
  1200. r"\x.([],[man(x), walks(x)])",
  1201. r"\x y.([],[sees(x,y)])",
  1202. r"([],[(([],[walks(x)]) + ([],[runs(x)]))])",
  1203. r"([x],[man(x), -([],[walks(x)])])",
  1204. r"([],[(([x],[man(x)]) -> ([],[walks(x)]))])",
  1205. ]
  1206. for e in expressions:
  1207. d = DrtExpression.fromstring(e)
  1208. d.draw()
  1209. if __name__ == "__main__":
  1210. demo()