forked from diffpy/diffpy.srfit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathswapper.py
More file actions
151 lines (116 loc) · 4.22 KB
/
swapper.py
File metadata and controls
151 lines (116 loc) · 4.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#!/usr/bin/env python
##############################################################################
#
# diffpy.srfit by DANSE Diffraction group
# Simon J. L. Billinge
# (c) 2008 The Trustees of Columbia University
# in the City of New York. All rights reserved.
#
# File coded by: Chris Farrow
#
# See AUTHORS.txt for a list of people who contributed.
# See LICENSE_DANSE.txt for license information.
#
##############################################################################
"""Swapper for replacing a Literal in an equation with another
Literals."""
__all__ = ["Swapper"]
from diffpy.srfit.equation.visitors.visitor import Visitor
class Swapper(Visitor):
"""Swapper for swapping out one literal for another in a literal
tree.
Note that this cannot swap out a root node of a literal tree. This case
must be tested for explicitly.
Attributes
----------
newlit
The literal to be placed into the literal tree.
oldlit
The literal to be replaced.
"""
def __init__(self, oldlit, newlit):
"""Initialize.
Parameters
----------
oldlit
The literal to be replaced.
newlit
The literal to be placed into the literal tree. See the
class for how the replacement takes place.
"""
self.newlit = newlit
self.oldlit = oldlit
self._swap = False
return
def onArgument(self, arg):
"""Process an Argument node.
Tell the parent to swap the old Argument with the replacement
Literal.
"""
if arg is self.oldlit:
self._swap = True
return
def onOperator(self, op):
"""Process an Operator node.
Tell the parent to swap the old Operator with the replacement
Literal.
"""
# Check to see if we need to swap out this Operator. If so, then we
# don't need to traverse into its arguments.
if op is self.oldlit:
self._swap = True
return
# Now traverse into the arguments.
for literal in op.args:
literal.identify(self)
# If we've been told to swap out a literal, then we must do it in-place
# because the order of op.args matters.
if self._swap:
oldlit = self.oldlit
newlit = self.newlit
while oldlit in op.args:
# Record the index
idx = op.args.index(oldlit)
# Remove the literal
del op.args[idx]
# Remove op as an observer. A KeyError will be raised if we
# attempt to remove the same observer more than once, which
# might happen if the oldlit appears multiple times in op.args.
try:
oldlit.removeObserver(op._flush)
except KeyError:
pass
# Validate the new literal. If it fails, we need to restore the
# old one
try:
op._loop_check(newlit)
except ValueError:
# Restore the old literal
op.args.insert(idx, oldlit)
oldlit.addObserver(op._flush)
raise
# If we got here, then go on with replacing the literal
op.args.insert(idx, newlit)
newlit.addObserver(op._flush)
op._flush(other=())
self._swap = False
return
def onEquation(self, eq):
"""Process an Equation node.
This looks at the equation itself as well as the root.
"""
if eq is self.oldlit:
self._swap = True
return
# If the newlit is the root, then swap that out and move on.
if eq.root is self.oldlit:
eq.setRoot(self.newlit)
return
# Now move into the equation. We have to do a _loop_check to make sure
# that we won't have any loops in the equation.
eq._loop_check(self.newlit)
eq.root.identify(self)
# Reset the root in case anything changed underneath.
eq.setRoot(eq.root)
return
# End of file