/
level_specification.jl
192 lines (160 loc) · 5.13 KB
/
level_specification.jl
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#### LeveSpec types for various types of level specification
## TODO: Heads => True
abstract type LevelSpec end
struct LevelSpecToDepth <: LevelSpec
level::Int
end
struct LevelSpecAtDepth <: LevelSpec
level::Int
end
struct LevelSpecRange <: LevelSpec
start::Int
stop::Int
end
struct LevelSpecAll <: LevelSpec
includezero::Bool
end
LevelSpecAll() = LevelSpecAll(false)
#### Create LevelSpec instances from Mxpr expressions
## Interpretation of negative index is different here than from Mma
## In Mma, a negative index counts up from each leaf.
## TODO: or FIXME
## 1. Implement both depth-first and breadth-first traversal. Only
## breadth-first is implmented now.
## 2. For negative indicies. Traverse the whole tree and build a flat
## Array of of the depth of each leaf. Then count negative levels from
## each leaf.
# Form n. levels 1 through n
function make_level_specification(expr,n::Integer)
if n < 0 n += depth(expr) end
LevelSpecToDepth(n)
end
# Form [n] or [m,n] level n only, or levels m through n
function make_level_specification(expr,spec::Mxpr{:List})
_make_level_specification(expr,spec,margs(spec)...)
end
# Form Infinity or else error. levels 1 through Infinity
function make_level_specification(expr,x)
if x == Infinity # could use singlton type
return LevelSpecRange(1,depth(expr))
# return LevelSpecAll(false)
end
symerror("Level specification $x is not of the form n, {n}, or {m, n}.")
end
# Form [n]
function _make_level_specification(expr,spec::Mxpr{:List}, n::Integer)
if n < 0 n += depth(expr) end
LevelSpecAtDepth(n)
end
# Form [m,n]
function _make_level_specification(expr, spec::Mxpr{:List}, start::Integer, stop::Integer)
if start < 0 start += depth(expr) end
if stop < 0 stop += depth(expr) end
LevelSpecRange(start, stop)
end
function _make_level_specification(expr, spec::Mxpr{:List}, start::Integer, stop)
stop != Infinity && symerror("Level specification $spec is not of the form n, [n], or [m, n].")
d = depth(expr)
if start < 0 start += d end
LevelSpecRange(start,d)
end
function _make_level_specification(expr, spec::Mxpr{:List},args...)
symerror("Level specification $spec is not of the form n, [n], or [m, n].")
end
#### Structure for performing action at levels
mutable struct LevelAction
data::Any # application-specific data
doaction::Function # function takes arguments (data,expr)
levelind::Int
subind::Int
parent::Any
levelbreak::Bool
end
LevelAction(data,doaction) = LevelAction(data,doaction,0,0,Null,false)
has_level_zero(spec::LevelSpecAtDepth) = spec.level == 0
has_level_zero(spec::LevelSpecToDepth) = spec.level == 0
has_level_zero(spec::LevelSpecRange) = spec.start == 0
has_level_zero(spec::LevelSpecAll) = spec.includezero
# This is length for mapping purposes
function length_for_level(x)
return 0
end
function length_for_level(mx::Mxpr)
return length(mx)
end
function checkbreak(action)
if action.levelbreak
true
else
false
end
end
# macro travcode()
# code = quote
# elen = length_for_level(expr)
# if elen > 0
# action.parent = expr
# for i in 1:elen
# action.parent = expr
# action.levelind += 1
# action.subind = i
# traverse_levels!(action,spec,expr[i])
# if checkbreak(action)
# return
# end
# action.levelind -= 1
# end
# # action.parent = Null
# end
# end
# return code
# end
const traversecode =
quote
elen = length_for_level(expr)
if elen > 0
action.parent = expr
for i in 1:elen
action.parent = expr
action.levelind += 1
action.subind = i
traverse_levels!(action,spec,expr[i])
if checkbreak(action)
return
end
action.levelind -= 1
end
# action.parent = Null
end
end
@eval function traverse_levels!(action::LevelAction, spec::LevelSpecAtDepth, expr)
if action.levelind == spec.level
action.doaction(action.data, expr)
action.levelbreak && return
elseif action.levelind < spec.level
$traversecode
end
end
@eval function traverse_levels!(action::LevelAction, spec::LevelSpecToDepth, expr)
if action.levelind <= spec.level && action.levelind > 0
action.doaction(action.data, expr)
checkbreak(action) && return
end
if action.levelind < spec.level
$traversecode
end
end
@eval function traverse_levels!(action::LevelAction, spec::LevelSpecRange, expr)
if action.levelind <= spec.stop && action.levelind >= spec.start
action.doaction(action.data, expr)
action.levelbreak && return
end
if action.levelind < spec.stop
$traversecode
end
end
@eval function traverse_levels!(action::LevelAction, spec::LevelSpecAll, expr)
action.doaction(action.data, expr)
action.levelbreak && return
$traversecode
end