possibilities.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. # This file is part of TRS (http://math.kompiler.org)
  2. #
  3. # TRS is free software: you can redistribute it and/or modify it under the
  4. # terms of the GNU Affero General Public License as published by the Free
  5. # Software Foundation, either version 3 of the License, or (at your option) any
  6. # later version.
  7. #
  8. # TRS is distributed in the hope that it will be useful, but WITHOUT ANY
  9. # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  10. # A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  11. # details.
  12. #
  13. # You should have received a copy of the GNU Affero General Public License
  14. # along with TRS. If not, see <http://www.gnu.org/licenses/>.
  15. from node import TYPE_OPERATOR
  16. import re
  17. # Each rule will append its hint message to the following dictionary. The
  18. # function pointer to the apply function of the rule is used as key. The
  19. # corresponding value is a string, which will be used to produce the hint
  20. # message. The string will be processed using string.format().
  21. MESSAGES = {}
  22. class Possibility(object):
  23. def __init__(self, root, handler, args=()):
  24. self.root = root
  25. self.handler = handler
  26. self.args = args
  27. def __str__(self):
  28. if self.handler in MESSAGES:
  29. msg = MESSAGES[self.handler]
  30. if callable(msg):
  31. msg = msg(self.root, self.args)
  32. # Surround math notation with backticks. If there are any backticks
  33. # already, do not add additional backticks. The add_backticks
  34. # lambda is necessary otherwise because \1 and \2 are not matched
  35. # both at the same time.
  36. add_backticks = lambda x: '`%s`' % ''.join(x.groups(''))
  37. msg = re.sub('`([^`]*)`|(\(?{[^. ]+)', add_backticks, msg)
  38. return msg.format(self.root, *self.args)
  39. return repr(self)
  40. def __repr__(self):
  41. return '<Possibility root="%s" handler=%s args=%s>' \
  42. % (self.root, self.handler.func_name, self.args)
  43. def __eq__(self, other):
  44. """
  45. Use node hash comparison when comparing to other Possibility to assert
  46. that its is the same object as in this one.
  47. """
  48. return self.handler == other.handler \
  49. and hash(self.root) == hash(other.root) \
  50. and self.args == other.args
  51. def find_parent_node(root, child):
  52. nodes = [root]
  53. while nodes:
  54. node = nodes.pop()
  55. while node:
  56. if node.type != TYPE_OPERATOR:
  57. break
  58. if child in node:
  59. return node
  60. if len(node) > 1:
  61. nodes.append(node[1])
  62. node = node[0]
  63. def apply_suggestion(root, suggestion):
  64. # TODO: clone the root node before modifying. After deep copying the root
  65. # node, the subtree_map cannot be used since the hash() of each node in the
  66. # deep copied root node has changed.
  67. #root = root.clone()
  68. subtree = suggestion.handler(suggestion.root, suggestion.args)
  69. parent_node = find_parent_node(root, suggestion.root)
  70. # There is either a parent node or the subtree is the root node.
  71. # FIXME: FAIL: test_diagnostic_test_application in tests/test_b1_ch08.py
  72. #try:
  73. # assert bool(parent_node) != (subtree == root)
  74. #except:
  75. # print 'parent_node: %s' % (str(parent_node))
  76. # print 'subtree: %s == %s' % (str(subtree), str(root))
  77. # raise
  78. if parent_node:
  79. parent_node.substitute(suggestion.root, subtree)
  80. return root
  81. return subtree