1
2
3
4
5
6
7
8 from nltk.internals import Counter
9 from nltk.sem import logic
10 from nltk.sem.logic import Variable, unique_variable
11 import drt_resolve_anaphora as RA
12
13 from Tkinter import Canvas
14 from Tkinter import Tk
15 from tkFont import Font
16 from nltk.draw import in_idle
17
19 """
20 This is the base abstract abstract DRT Expression from which every DRT
21 Expression extends.
22 """
24 accum = self.applyto(other)
25 for a in additional:
26 accum = accum(a)
27 return accum
28
31
34
37
39 raise NotImplementedError()
40
44
48
52
53 - def tp_equals(self, other, prover_name='tableau'):
62
65
72
75
81
84
85 -class DRS(AbstractDrs, logic.Expression, RA.DRS):
86 """A Discourse Representation Structure."""
88 """
89 @param refs: C{list} of C{DrtVariableExpression} for the discourse referents
90 @param conds: C{list} of C{Expression} for the conditions
91 """
92 self.refs = refs
93 self.conds = conds
94
95 - def replace(self, variable, expression, replace_bound=False):
96 """Replace all instances of variable v with expression E in self,
97 where v is free in self."""
98 try:
99
100 i = self.refs.index(variable)
101 if not replace_bound:
102 return self
103 else:
104 return DRS(self.refs[:i]+[expression.variable]+self.refs[i+1:],
105 [cond.replace(variable, expression, True) for cond in self.conds])
106 except ValueError:
107
108
109
110
111 for ref in (set(self.refs) & expression.free()):
112 newvar = unique_variable()
113 newvarex = DrtIndividualVariableExpression(newvar)
114 i = self.refs.index(ref)
115 self = DRS(self.refs[:i]+[newvar]+self.refs[i+1:],
116 [cond.replace(ref, newvarex, True)
117 for cond in self.conds])
118
119
120 return DRS(self.refs,
121 [cond.replace(variable, expression, replace_bound)
122 for cond in self.conds])
123
125 """
126 @see: logic.Expression.variables()
127 """
128 conds_free = set()
129 for cond in self.conds:
130 conds_free |= cond.variables()
131 return conds_free - set(self.refs)
132
133 - def free(self, indvar_only=True):
134 """
135 @see: logic.Expression.free()
136 """
137 conds_free = set()
138 for cond in self.conds:
139 conds_free |= cond.free(indvar_only)
140 return conds_free - set(self.refs)
141
144
146 return DRS(self.refs, [cond.simplify() for cond in self.conds])
147
155
157 r"""Defines equality modulo alphabetic variance.
158 If we are comparing \x.M and \y.N, then check equality of M and N[x/y]."""
159 if self.__class__ == other.__class__:
160 if len(self.refs) == len(other.refs):
161 converted_other = other
162 for (r1, r2) in zip(self.refs, converted_other.refs):
163 varex = self.make_VariableExpression(r1)
164 converted_other = converted_other.replace(r2, varex, True)
165 return self.conds == converted_other.conds
166 return False
167
169 return Tokens.DRS + '([' + ','.join([str(ref) for ref in self.refs]) + \
170 '],[' + ', '.join([cond.str(syntax) for cond in self.conds]) + '])'
171
179
188
193
198
202
206
208 first_drs = self.first
209 second_drs = self.second
210
211 accum = first_drs.conds[-1].toFol()
212 for cond in first_drs.conds[::-1][1:]:
213 accum = logic.AndExpression(cond.toFol(), accum)
214
215 accum = logic.ImpExpression(accum, second_drs.toFol())
216
217 for ref in first_drs.refs[::-1]:
218 accum = logic.AllExpression(ref, accum)
219
220 return accum
221
225
229
230 -class ConcatenationDRS(AbstractDrs, logic.BooleanExpression, RA.ConcatenationDRS):
231 """DRS of the form '(DRS + DRS)'"""
232 - def replace(self, variable, expression, replace_bound=False):
233 """Replace all instances of variable v with expression E in self,
234 where v is free in self."""
235 first = self.first
236 second = self.second
237
238
239 if isinstance(first, DRS) and isinstance(second, DRS) and \
240 variable in (set(first.get_refs()) & set(second.get_refs())):
241 first = first.replace(variable, expression, True)
242 second = second.replace(variable, expression, True)
243
244
245 elif isinstance(first, DRS) and variable in first.refs:
246 if replace_bound:
247 first = first.replace(variable, expression, replace_bound)
248 second = second.replace(variable, expression, replace_bound)
249
250
251 elif isinstance(second, DRS) and variable in second.refs:
252 if replace_bound:
253 first = first.replace(variable, expression, replace_bound)
254 second = second.replace(variable, expression, replace_bound)
255
256 else:
257
258 for ref in (set(self.get_refs()) & expression.free()):
259 v = DrtIndividualVariableExpression(unique_variable())
260 first = first.replace(ref, v, True)
261 second = second.replace(ref, v, True)
262
263 first = first.replace(variable, expression, replace_bound)
264 second = second.replace(variable, expression, replace_bound)
265
266 return self.__class__(first, second)
267
270
272 first = self.first.simplify()
273 second = self.second.simplify()
274
275 if isinstance(first, DRS) and isinstance(second, DRS):
276
277 for ref in (set(first.refs) & set(second.refs)):
278
279 newvar = DrtIndividualVariableExpression(unique_variable())
280 second = second.replace(ref, newvar, True)
281
282 return DRS(first.refs + second.refs, first.conds + second.conds)
283 else:
284 return self.__class__(first,second)
285
288
290 r"""Defines equality modulo alphabetic variance.
291 If we are comparing \x.M and \y.N, then check equality of M and N[x/y]."""
292 if self.__class__ == other.__class__:
293 self_refs = self.get_refs()
294 other_refs = other.get_refs()
295 if len(self_refs) == len(other_refs):
296 converted_other = other
297 for (r1,r2) in zip(self_refs, other_refs):
298 varex = self.make_VariableExpression(r1)
299 converted_other = converted_other.replace(r2, varex, True)
300 return self.first == converted_other.first and \
301 self.second == converted_other.second
302 return False
303
306
314
316 BUFFER = 3
317
318 - def __init__(self, drs, size_canvas=True, canvas=None):
319 """
320 @param drs: C{AbstractDrs}, The DRS to be drawn
321 @param size_canvas: C{boolean}, True if the canvas size should be the exact size of the DRS
322 @param canvas: C{Canvas} The canvas on which to draw the DRS. If none is given, create a new canvas.
323 """
324 self.syntax = logic.Tokens.NEW_NLTK
325
326 master = None
327 if not canvas:
328 master = Tk()
329 master.title("DRT")
330
331 font = Font(family='helvetica', size=12)
332
333 if size_canvas:
334 canvas = Canvas(master, width=0, height=0)
335 canvas.font = font
336 self.canvas = canvas
337 (right, bottom) = self._visit(drs, self.BUFFER, self.BUFFER)
338 canvas = Canvas(master, width=max(right, 100), height=bottom)
339 else:
340 canvas = Canvas(master, width=300, height=300)
341
342 canvas.pack()
343 canvas.font = font
344
345 self.canvas = canvas
346 self.drs = drs
347 self.master = master
348
350 """Get the height of a line of text"""
351 return self.canvas.font.metrics("linespace")
352
361
362 - def _visit(self, expression, x, y):
363 """
364 Return the bottom-rightmost point without actually drawing the item
365
366 @param expression: the item to visit
367 @param x: the top of the current drawing area
368 @param y: the left side of the current drawing area
369 @return: the bottom-rightmost point
370 """
371 return self._handle(expression, self._visit_command, x, y)
372
374 """
375 Draw the given item at the given location
376
377 @param item: the item to draw
378 @param x: the top of the current drawing area
379 @param y: the left side of the current drawing area
380 @return: the bottom-rightmost point
381 """
382 if isinstance(item, str):
383 self.canvas.create_text(x, y, anchor='nw', font=self.canvas.font, text=item)
384 elif isinstance(item, tuple):
385
386 (right, bottom) = item
387 self.canvas.create_rectangle(x, y, right, bottom)
388 horiz_line_y = y + self._get_text_height() + (self.BUFFER * 2)
389 self.canvas.create_line(x, horiz_line_y, right, horiz_line_y)
390
391 return self._visit_command(item, x, y)
392
394 """
395 Return the bottom-rightmost point without actually drawing the item
396
397 @param item: the item to visit
398 @param x: the top of the current drawing area
399 @param y: the left side of the current drawing area
400 @return: the bottom-rightmost point
401 """
402 if isinstance(item, str):
403 return (x + self.canvas.font.measure(item), y + self._get_text_height())
404 elif isinstance(item, tuple):
405 return item
406
407 - def _handle(self, expression, command, x=0, y=0):
408 """
409 @param expression: the expression to handle
410 @param command: the function to apply, either _draw_command or _visit_command
411 @param x: the top of the current drawing area
412 @param y: the left side of the current drawing area
413 @return: the bottom-rightmost point
414 """
415 if command == self._visit_command:
416
417 try:
418
419 right = expression._drawing_width + x
420 bottom = expression._drawing_height + y
421 return (right, bottom)
422 except AttributeError:
423
424 pass
425
426 if isinstance(expression, DrtVariableExpression):
427 factory = self._handle_VariableExpression
428 elif isinstance(expression, DRS):
429 factory = self._handle_DRS
430 elif isinstance(expression, DrtNegatedExpression):
431 factory = self._handle_NegatedExpression
432 elif isinstance(expression, DrtLambdaExpression):
433 factory = self._handle_LambdaExpression
434 elif isinstance(expression, logic.BooleanExpression):
435 factory = self._handle_BooleanExpression
436 elif isinstance(expression, DrtApplicationExpression):
437 factory = self._handle_ApplicationExpression
438 elif isinstance(expression, RA.PossibleAntecedents):
439 factory = self._handle_VariableExpression
440 else:
441 raise Exception, expression.__class__.__name__
442
443 (right, bottom) = factory(expression, command, x, y)
444
445
446 expression._drawing_width = right - x
447 expression._drawing_height = bottom - y
448
449 return (right, bottom)
450
452 return command(expression.str(self.syntax), x, y)
453
455
456 right = self._visit_command(Tokens.NOT[self.syntax], x, y)[0]
457
458
459 (right, bottom) = self._handle(expression.term, command, right, y)
460
461
462 command(Tokens.NOT[self.syntax], x, self._get_centered_top(y, bottom - y, self._get_text_height()))
463
464 return (right, bottom)
465
467 left = x + self.BUFFER
468 bottom = y + self.BUFFER
469
470
471 if expression.refs:
472 refs = ' '.join([str(ref) for ref in expression.refs])
473 else:
474 refs = ' '
475 (max_right, bottom) = command(refs, left, bottom)
476 bottom += (self.BUFFER * 2)
477
478
479 if expression.conds:
480 for cond in expression.conds:
481 (right, bottom) = self._handle(cond, command, left, bottom)
482 max_right = max(max_right, right)
483 bottom += self.BUFFER
484 else:
485 bottom += self._get_text_height() + self.BUFFER
486
487
488 max_right += self.BUFFER
489 return command((max_right, bottom), x, y)
490
492 function, args = expression.uncurry()
493 if not isinstance(function, DrtVariableExpression):
494
495 function = expression.function
496 args = [expression.argument]
497
498
499 function_bottom = self._visit(function, x, y)[1]
500 max_bottom = max([function_bottom] + [self._visit(arg, x, y)[1] for arg in args])
501
502 line_height = max_bottom - y
503
504
505 function_drawing_top = self._get_centered_top(y, line_height, function._drawing_height)
506 right = self._handle(function, command, x, function_drawing_top)[0]
507
508
509 centred_string_top = self._get_centered_top(y, line_height, self._get_text_height())
510 right = command(Tokens.OPEN, right, centred_string_top)[0]
511
512
513 for (i,arg) in enumerate(args):
514 arg_drawing_top = self._get_centered_top(y, line_height, arg._drawing_height)
515 right = self._handle(arg, command, right, arg_drawing_top)[0]
516
517 if i+1 < len(args):
518
519 right = command(Tokens.COMMA + ' ', right, centred_string_top)[0]
520
521
522 right = command(Tokens.CLOSE, right, centred_string_top)[0]
523
524 return (right, max_bottom)
525
527
528 variables = Tokens.LAMBDA[self.syntax] + str(expression.variable) + Tokens.DOT[self.syntax]
529 right = self._visit_command(variables, x, y)[0]
530
531
532 (right, bottom) = self._handle(expression.term, command, right, y)
533
534
535 command(variables, x, self._get_centered_top(y, bottom - y, self._get_text_height()))
536
537 return (right, bottom)
538
540
541 first_height = self._visit(expression.first, 0, 0)[1]
542 second_height = self._visit(expression.second, 0, 0)[1]
543 line_height = max(first_height, second_height)
544
545
546 centred_string_top = self._get_centered_top(y, line_height, self._get_text_height())
547 right = command(Tokens.OPEN, x, centred_string_top)[0]
548
549
550 first_height = expression.first._drawing_height
551 (right, first_bottom) = self._handle(expression.first, command, right, self._get_centered_top(y, line_height, first_height))
552
553
554 right = command(' %s ' % expression.getOp(self.syntax), right, centred_string_top)[0]
555
556
557 second_height = expression.second._drawing_height
558 (right, second_bottom) = self._handle(expression.second, command, right, self._get_centered_top(y, line_height, second_height))
559
560
561 right = command(Tokens.CLOSE, right, centred_string_top)[0]
562
563 return (right, max(first_bottom, second_bottom))
564
566 """Get the y-coordinate of the point that a figure should start at if
567 its height is 'item_height' and it needs to be centered in an area that
568 starts at 'top' and is 'full_height' tall."""
569 return top + (full_height - item_height) / 2
570
571
584
586 """A lambda calculus expression parser."""
587
590
592 """This method exists to be overridden"""
593 return Tokens.SYMBOLS
594
597
615
618
643
645 """This method serves as a hook for other logic parsers that
646 have different equality expression classes"""
647 return DrtEqualityExpression(first, second)
648
663
666
672
675
677 n = Tokens.NEW_NLTK
678 print '='*20 + 'TEST PARSE' + '='*20
679 parser = DrtParser()
680 print parser.parse(r'DRS([x,y],[sees(x,y)])')
681 print parser.parse(r'DRS([x],[man(x), walks(x)])')
682 print parser.parse(r'\x.\y.DRS([],[sees(x,y)])')
683 print parser.parse(r'\x.DRS([],[walks(x)])(john)')
684 print parser.parse(r'(DRS([x],[walks(x)]) + DRS([y],[runs(y)]))')
685 print parser.parse(r'(DRS([],[walks(x)]) -> DRS([],[runs(x)]))')
686 print parser.parse(r'DRS([x],[PRO(x), sees(John,x)])')
687 print parser.parse(r'DRS([x],[man(x), -DRS([],[walks(x)])])')
688 print parser.parse(r'DRS([],[(DRS([x],[man(x)]) -> DRS([],[walks(x)]))])')
689
690 print '='*20 + 'Test toFol()' + '='*20
691 print parser.parse(r'DRS([x,y],[sees(x,y)])').toFol()
692
693
694 print '='*20 + 'Test alpha conversion and lambda expression equality' + '='*20
695 e1 = parser.parse(r'\x.drs([],[P(x)])')
696 print e1
697 e2 = e1.alpha_convert(DrtVariableExpression('z'))
698 print e2
699 print e1 == e2
700
701 print '='*20 + 'Test resolve_anaphora()' + '='*20
702 print parser.parse(r'DRS([x,y,z],[dog(x), cat(y), walks(z), PRO(z)])').resolve_anaphora()
703 print parser.parse(r'DRS([],[(DRS([x],[dog(x)]) -> DRS([y],[walks(y), PRO(y)]))])').resolve_anaphora()
704 print parser.parse(r'(DRS([x,y],[]) + DRS([],[PRO(x)]))').resolve_anaphora()
705 print parser.parse(r'DRS([x],[walks(x), PRO(x)])').resolve_anaphora()
706
707 print '='*20 + 'Test tp_equals()' + '='*20
708 a = parser.parse(r'DRS([x],[man(x), walks(x)])')
709 b = parser.parse(r'DRS([x],[walks(x), man(x)])')
710 print a.tp_equals(b)
711
712
714 expressions = [
715 r'x',
716 r'DRS([],[])',
717 r'DRS([x],[])',
718 r'DRS([x],[man(x)])',
719
720 r'drs([x,y],[sees(x,y)])',
721 r'drs([x],[man(x), walks(x)])',
722 r'\x.drs([],[man(x), walks(x)])',
723 r'\x y.drs([],[sees(x,y)])',
724 r'drs([],[(drs([],[walks(x)]) + drs([],[runs(x)]))])',
725
726 r'drs([x],[man(x), -drs([],[walks(x)])])',
727 r'drs([],[(drs([x],[man(x)]) -> drs([],[walks(x)]))])'
728 ]
729
730 for e in expressions:
731 d = DrtParser().parse(e)
732 d.draw()
733
734 if __name__ == '__main__':
735 demo()
736