Skip to content

Commit 6ed56ee

Browse files
authored
tools: improve the v reduce parser for functions, and add more loops to reduce more (#23694)
1 parent 694cac9 commit 6ed56ee

1 file changed

Lines changed: 122 additions & 82 deletions

File tree

‎cmd/tools/vreduce.v‎

Lines changed: 122 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,14 @@ fn main() {
4444
content := os.read_file(file_path)!
4545
assert string_reproduces(content, error_msg, command)
4646
show_code_stats(content, label: 'Original code size')
47-
mut tree := parse(content)
4847

4948
// start tests
50-
tmp_code := create_code(tree)
49+
tmp_code := create_code(parse(content))
5150
assert string_reproduces(tmp_code, error_msg, command)
5251
show_code_stats(tmp_code, label: 'Code size without comments')
5352

5453
// reduce the code
55-
reduce_scope(mut tree, error_msg, command, do_fmt)
54+
reduce_scope(content, error_msg, command, do_fmt)
5655
}
5756

5857
// Return true if the command ran on the file produces the pattern
@@ -77,6 +76,7 @@ type Elem = string | Scope
7776
@[heap]
7877
struct Scope {
7978
mut:
79+
fn_scope bool // contains a function (string: signature{}, children: function body)
8080
ignored bool // is the scope ignored when creating the file
8181
tmp_ignored bool // used when testing if it can be ignored in the file
8282
children []Elem // code blocks (strings & children scope
@@ -96,6 +96,24 @@ fn parse(file string) Scope { // The parser is surely incomplete for the V synta
9696
for file[i] != `\n` { // comment -> skip until newline
9797
i++
9898
}
99+
} else if file[i] == `\n` && file[i - 1] == `\n` {
100+
i++ // remove excess newlines
101+
} else if file[i] == `\t` {
102+
i++ // remove tabs for easier processing
103+
} else if file[i] == `f` && file[i + 1] == `n` && file[i + 2] == ` ` && file[i - 1] or {
104+
`\n`
105+
} == `\n` {
106+
top.children << current_string
107+
// no increase in scope because not handled with {}
108+
current_string = ''
109+
top.children << &Scope{
110+
fn_scope: true
111+
}
112+
stack << &(top.children[top.children.len - 1] as Scope)
113+
current_string += file[i].ascii_str() // f
114+
i++
115+
current_string += file[i].ascii_str() // n
116+
i++
99117
} else if file[i] == `/` && file[i + 1] == `*` {
100118
i++
101119
i++
@@ -142,12 +160,23 @@ fn parse(file string) Scope { // The parser is surely incomplete for the V synta
142160
} else if file[i] == `}` {
143161
scope_level -= 1
144162
assert scope_level >= 0, 'The scopes are not well detected ${stack[0]}'
145-
top.children << current_string
163+
if current_string != '' {
164+
top.children << current_string
165+
}
166+
if stack.last().children == [] {
167+
stack[stack.len - 2].children.delete(stack[stack.len - 2].children.len - 1) // delete the empty scope (the last children because top of the stack)
168+
}
146169
stack.pop()
147170
top = stack[stack.len - 1]
148171
current_string = ''
149172
current_string += file[i].ascii_str() // }
150173
i++
174+
if scope_level == 0 && stack.len == 2 { // the function and the body scope
175+
top.children << current_string
176+
stack.pop()
177+
top = stack[stack.len - 1]
178+
current_string = ''
179+
}
151180
} else {
152181
current_string += file[i].ascii_str()
153182
i++
@@ -157,7 +186,7 @@ fn parse(file string) Scope { // The parser is surely incomplete for the V synta
157186
top = stack[stack.len - 1]
158187
top.children << current_string // last part of the file
159188
assert scope_level == 0, 'The scopes are not well detected'
160-
assert stack.len == 1, 'The stack should only have the BODY scope'
189+
assert stack.len == 1, 'The stack should only have the body scope'
161190
return *stack[0]
162191
}
163192

@@ -183,101 +212,112 @@ fn create_code(sc Scope) string {
183212
}
184213

185214
// Reduces the code contained in the scope tree and writes the reduced code to `rpdc.v`
186-
fn reduce_scope(mut sc Scope, error_msg string, command string, do_fmt bool) {
215+
fn reduce_scope(content string, error_msg string, command string, do_fmt bool) {
216+
mut sc := parse('') // will get filled in the start of the loop
187217
println('Cleaning the scopes')
188-
mut modified_smth := true // was a modification successful in reducing the code in the last iteration
189-
for modified_smth { // as long as there are successful modifications
190-
modified_smth = false
191-
println('NEXT ITERATION, loop 1')
192-
mut stack := []&Elem{}
193-
for i in 0 .. sc.children.len {
194-
stack << &sc.children[i]
195-
}
196-
for stack.len > 0 { // traverse the tree and disable (ignore) scopes that are not needed for reproduction
197-
mut item := stack.pop()
198-
if mut item is Scope {
199-
if !item.ignored {
200-
item.tmp_ignored = true // try to ignore it
201-
code := create_code(sc)
202-
item.tmp_ignored = false // dont need it anymore
203-
if string_reproduces(code, error_msg, command) {
204-
item.ignored = true
205-
modified_smth = true
206-
show_code_stats(code)
207-
} else { // if can remove it, no need to go though it's children
208-
for i in 0 .. item.children.len {
209-
stack << &item.children[i]
218+
mut text_code := content
219+
mut outer_modified_smth := true
220+
for outer_modified_smth {
221+
sc = parse(text_code)
222+
outer_modified_smth = false
223+
mut modified_smth := true // was a modification successful in reducing the code in the last iteration
224+
for modified_smth { // as long as there are successful modifications
225+
modified_smth = false
226+
println('NEXT ITERATION, loop 1')
227+
mut stack := []&Elem{}
228+
for i in 0 .. sc.children.len {
229+
stack << &sc.children[i]
230+
}
231+
for stack.len > 0 { // traverse the tree and disable (ignore) scopes that are not needed for reproduction
232+
mut item := stack.pop()
233+
if mut item is Scope {
234+
if !item.ignored {
235+
item.tmp_ignored = true // try to ignore it
236+
code := create_code(sc)
237+
item.tmp_ignored = false // dont need it anymore
238+
if string_reproduces(code, error_msg, command) {
239+
item.ignored = true
240+
modified_smth = true
241+
outer_modified_smth = true
242+
show_code_stats(code)
243+
} else { // if can remove it, no need to go though it's children
244+
for i in 0 .. item.children.len {
245+
stack << &item.children[i]
246+
}
210247
}
211248
}
212249
}
213250
}
214251
}
215-
}
216252

217-
println('Processing remaining lines')
218-
tmp_code := create_code(sc).split_into_lines() // dont forget to add back the \n
219-
// Create the binary tree of the lines
220-
depth := int(math.log2(tmp_code.len)) + 1
221-
mut c := 0
222-
mut line_stack := []&Scope{}
223-
line_stack << &Scope{}
224-
for c < tmp_code.len {
225-
l1 := line_stack.len
226-
if l1 <= depth { // or equal because of the first node
227-
if line_stack[l1 - 1].children.len < 2 {
228-
line_stack[l1 - 1].children << &Scope{}
229-
l2 := line_stack[l1 - 1].children.len
230-
line_stack << &(line_stack[l1 - 1].children[l2 - 1] as Scope)
231-
} else {
232-
line_stack.pop()
233-
}
234-
} else {
235-
if line_stack[l1 - 1].children.len != 0 { // if there is already a string
236-
line_stack.pop()
253+
text_code = create_code(sc)
254+
255+
println('Processing remaining lines')
256+
split_code := text_code.split_into_lines() // dont forget to add back the \n
257+
// Create the binary tree of the lines
258+
depth := int(math.log2(split_code.len)) + 1
259+
mut c := 0
260+
mut line_stack := []&Scope{}
261+
line_stack << &Scope{}
262+
for c < split_code.len {
263+
l1 := line_stack.len
264+
if l1 <= depth { // or equal because of the first node
265+
if line_stack[l1 - 1].children.len < 2 {
266+
line_stack[l1 - 1].children << &Scope{}
267+
l2 := line_stack[l1 - 1].children.len
268+
line_stack << &(line_stack[l1 - 1].children[l2 - 1] as Scope)
269+
} else {
270+
line_stack.pop()
271+
}
237272
} else {
238-
line_stack[l1 - 1].children << tmp_code[c] + '\n' // the \n were removed by the split
239-
c++
240-
line_stack.pop() // already a string
273+
if line_stack[l1 - 1].children.len != 0 { // if there is already a string
274+
line_stack.pop()
275+
} else {
276+
line_stack[l1 - 1].children << split_code[c] + '\n' // the \n were removed by the split
277+
c++
278+
line_stack.pop() // already a string
279+
}
241280
}
242281
}
243-
}
244282

245-
// Traverse the tree and prune the useless lines / line groups for the reproduction
246-
mut line_tree := *line_stack[0]
247-
assert string_reproduces(create_code(line_tree), error_msg, command) // should be the same
248-
println('Pruning the lines/line groups')
249-
modified_smth = true
250-
for modified_smth {
251-
modified_smth = false
252-
println('NEXT ITERATION, loop 2')
253-
mut stack := []&Elem{}
254-
for i in 0 .. line_tree.children.len {
255-
stack << &line_tree.children[i]
256-
}
257-
for stack.len > 0 { // traverse the binary tree (of the lines)
258-
mut item := stack.pop()
259-
if mut item is Scope {
260-
if !item.ignored {
261-
item.tmp_ignored = true
262-
code := create_code(line_tree)
263-
item.tmp_ignored = false // dont need it anymore
264-
if string_reproduces(code, error_msg, command) {
265-
item.ignored = true
266-
modified_smth = true
267-
show_code_stats(code)
268-
} else { // if can remove it, can remove it's children
269-
for i in 0 .. item.children.len {
270-
stack << &item.children[i]
283+
// Traverse the tree and prune the useless lines / line groups for the reproduction
284+
mut line_tree := *line_stack[0]
285+
assert string_reproduces(create_code(line_tree), error_msg, command) // should be the same
286+
println('Pruning the lines/line groups')
287+
modified_smth = true
288+
for modified_smth {
289+
modified_smth = false
290+
println('NEXT ITERATION, loop 2')
291+
mut stack := []&Elem{}
292+
for i in 0 .. line_tree.children.len {
293+
stack << &line_tree.children[i]
294+
}
295+
for stack.len > 0 { // traverse the binary tree (of the lines)
296+
mut item := stack.pop()
297+
if mut item is Scope {
298+
if !item.ignored {
299+
item.tmp_ignored = true
300+
code := create_code(line_tree)
301+
item.tmp_ignored = false // dont need it anymore
302+
if string_reproduces(code, error_msg, command) {
303+
item.ignored = true
304+
modified_smth = true
305+
outer_modified_smth = true
306+
show_code_stats(code)
307+
} else { // if can remove it, can remove it's children
308+
for i in 0 .. item.children.len {
309+
stack << &item.children[i]
310+
}
271311
}
272312
}
273313
}
274314
}
275315
}
316+
text_code = create_code(line_tree)
276317
}
277318

278-
mre := create_code(line_tree) // final minimal reproductible example
279-
assert string_reproduces(mre, error_msg, command)
280-
os.write_file('rpdc.v', mre) or { panic(err) }
319+
assert string_reproduces(text_code, error_msg, command)
320+
os.write_file('rpdc.v', text_code) or { panic(err) }
281321
if do_fmt {
282322
os.execute('v fmt -w rpdc.v')
283323
final_content := os.read_file('rpdc.v') or { panic(err) }

0 commit comments

Comments
 (0)