/
replace.go
137 lines (129 loc) · 3.78 KB
/
replace.go
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
package expreduce
// This function assumes e and lhs have the same head and that the head is Flat.
func FlatReplace(e *Expression, lhs *Expression, rhs Ex, orderless bool, es *EvalState) Ex {
looseLhs := NewExpression([]Ex{})
looseLhs.Parts = append(looseLhs.Parts, lhs.Parts[0])
if !orderless {
looseLhs.Parts = append(looseLhs.Parts, NewExpression([]Ex{
&Symbol{"System`Pattern"},
&Symbol{"System`Expreduce`start"},
NewExpression([]Ex{&Symbol{"System`BlankNullSequence"}}),
}))
}
looseLhs.Parts = append(looseLhs.Parts, lhs.Parts[1:]...)
looseLhs.Parts = append(looseLhs.Parts, NewExpression([]Ex{
&Symbol{"System`Pattern"},
&Symbol{"System`Expreduce`end"},
NewExpression([]Ex{&Symbol{"System`BlankNullSequence"}}),
}))
pm := EmptyPD()
matchq, newPd := IsMatchQ(e, looseLhs, pm, es)
if matchq {
var tmpEx Ex
if orderless {
tmpEx = ReplacePD(NewExpression([]Ex{
e.Parts[0],
rhs,
&Symbol{"System`Expreduce`end"},
}), es, newPd)
} else {
tmpEx = ReplacePD(NewExpression([]Ex{
e.Parts[0],
&Symbol{"System`Expreduce`start"},
rhs,
&Symbol{"System`Expreduce`end"},
}), es, newPd)
}
return tmpEx
}
return e
}
func ReplacePDInternal(e Ex, pm *PDManager) Ex {
asSym, isSym := e.(*Symbol)
if isSym {
for k, def := range pm.patternDefined {
if k == asSym.Name {
// Shouldn't need the copy
return def
}
}
}
asExpr, isExpr := e.(*Expression)
if isExpr {
asExpr.evaledHash = 0
asExpr.cachedHash = 0
for i := range asExpr.Parts {
asExpr.Parts[i] = ReplacePDInternal(asExpr.Parts[i], pm)
}
}
return e
}
func ReplacePD(this Ex, es *EvalState, pm *PDManager) Ex {
containsAny := false
for k := range pm.patternDefined {
if ContainsSymbol(this, k) {
containsAny = true
break
}
}
if !containsAny {
return this
}
// Expressions are immutable. Any time we change an expression, we must
// first copy it.
return ReplacePDInternal(this.DeepCopy(), pm)
}
// The goal of this function is to replace all matching expressions with the
// RHS upon successful matches. We will NOT substitute any named patterns in
// the RHS. We will merely make sure that the named patterns are added to pm.
// Final named pattern substitution will occur at the last possible time.
func ReplaceAll(this Ex, r *Expression, es *EvalState, pm *PDManager,
stopAtHead string) Ex {
asExpression, isExpression := this.(*Expression)
if isExpression {
_, isRestrictedHead := HeadAssertion(this, stopAtHead)
if isRestrictedHead {
return this
} else {
// Continue recursion
es.Debugf("ReplaceAll(%v, %v, es, %v)", this, r, pm)
return asExpression.ReplaceAll(r, stopAtHead, es)
}
}
if res, matches := IsMatchQ(this, r.Parts[1], pm, es); res {
return ReplacePD(r.Parts[2], es, matches)
}
return this
}
func tryCondWithMatches(asCond *Expression, matches *PDManager, es *EvalState) (Ex, bool) {
condRes := ReplacePD(asCond.Parts[2], es, matches).Eval(es)
condResSymbol, condResIsSymbol := condRes.(*Symbol)
if condResIsSymbol {
if condResSymbol.Name == "System`True" {
toReturn := ReplacePD(asCond.Parts[1], es, matches)
if rCond, isRCond := HeadAssertion(toReturn, "System`Condition"); isRCond {
return tryCondWithMatches(rCond, matches, es)
}
return toReturn, true
}
}
return nil, false
}
func Replace(this Ex, r *Expression, es *EvalState) (Ex, bool) {
if asCond, isCond := HeadAssertion(r.Parts[2], "System`Condition"); isCond {
mi, cont := NewMatchIter(this, r.Parts[1], EmptyPD(), es)
for cont {
res, matches, done := mi.next()
cont = !done
if res {
toReturn, ok := tryCondWithMatches(asCond, matches, es)
if ok {
return toReturn, true
}
}
}
} else if res, matches := IsMatchQ(this, r.Parts[1], EmptyPD(), es); res {
return ReplacePD(r.Parts[2], es, matches), true
}
return this, false
}