tools: improve v reduce output; bump version, warn on failed string_reproduces/3, but continue to run

This commit is contained in:
Delyan Angelov 2025-02-12 10:56:10 +02:00
parent 4baa6cd70e
commit c58563ce5a
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED

View File

@ -1,7 +1,9 @@
import os
import flag
import math
import log
const version = '0.0.2'
const default_command = '${os.quoted_path(@VEXE)} -no-skip-unused' // Command used to compile the program, using -no-skip-unused to ease the reducing
const default_error_msg = 'C compilation error' // the pattern to reproduce
// Temporary files
@ -10,11 +12,12 @@ const tmp_reduced_code_file_name = '__v_reduced_code.v'
const path = '${tmp_folder}/${tmp_reduced_code_file_name}'
fn main() {
log.use_stdout()
mut fp := flag.new_flag_parser(os.args)
fp.skip_executable()
fp.application('v reduce path/to/file_to_reduce.v')
fp.description('This tool will reduce the code file and try to make the smallest one it can that reproduces the error when the command is executed')
fp.version('0.0.1')
fp.version(version)
error_msg := fp.string('error_msg', `e`, default_error_msg, 'the error message you want to reproduce, default: \'${default_error_msg}\'')
command := fp.string('command', `c`, default_command, 'the command used to try to reproduce the error, default: \'${default_command}\'')
@ -25,29 +28,38 @@ fn main() {
return
}
assert file_paths.len == 2, fp.usage() // ['reduce', 'path/to/file.v']
if file_paths.len != 2 {
println(fp.usage())
exit(0)
}
file_path := file_paths[1]
if file_path == '' || !os.exists(file_path) {
eprintln('You need to specify a valid file to reduce')
log.error('You need to specify a valid file to reduce.')
if file_path != '' {
log.error('Path `${file_path}` is not a valid .v file.')
}
println(fp.usage())
exit(1)
}
println("Starting to reduce the file: '${file_path}'\n with command: `${command}`,\n trying to reproduce: `${error_msg}`")
log.info("Starting to reduce the file: '${file_path}'\n with command: `${command}`,\n trying to reproduce: `${error_msg}`")
if do_fmt {
println('Will do `v fmt -w rpdc.v` after the reduction.')
log.info('Will do `v fmt -w rpdc.v` after the reduction.')
} else {
println('Will NOT do `v fmt -w rpdc.v` (use the `--fmt` or `-w` flag to enable it)')
log.info('Will NOT do `v fmt -w rpdc.v` (use the `--fmt` or `-w` flag to enable it)')
}
content := os.read_file(file_path)!
assert string_reproduces(content, error_msg, command)
warn_on_false(string_reproduces(content, error_msg, command), 'string_reproduces',
@LOCATION)
show_code_stats(content, label: 'Original code size')
// start tests
tmp_code := create_code(parse(content))
assert string_reproduces(tmp_code, error_msg, command)
warn_on_false(string_reproduces(tmp_code, error_msg, command), 'string_reproduces',
@LOCATION)
show_code_stats(tmp_code, label: 'Code size without comments')
// reduce the code
@ -185,8 +197,10 @@ fn parse(file string) Scope { // The parser is surely incomplete for the V synta
}
top = stack[stack.len - 1]
top.children << current_string // last part of the file
assert scope_level == 0, 'The scopes are not well detected'
assert stack.len == 1, 'The stack should only have the body scope'
warn_on_false(scope_level == 0, 'scope_level == 0 /* the scopes are not well detected*/',
@LOCATION)
warn_on_false(stack.len == 1, 'stack.len == 1 /* the stack should only have the body scope */',
@LOCATION)
return *stack[0]
}
@ -214,7 +228,7 @@ fn create_code(sc Scope) string {
// Reduces the code contained in the scope tree and writes the reduced code to `rpdc.v`
fn reduce_scope(content string, error_msg string, command string, do_fmt bool) {
mut sc := parse('') // will get filled in the start of the loop
println('Cleaning the scopes')
log.info('Cleaning the scopes')
mut text_code := content
mut outer_modified_smth := true
for outer_modified_smth {
@ -223,7 +237,7 @@ fn reduce_scope(content string, error_msg string, command string, do_fmt bool) {
mut modified_smth := true // was a modification successful in reducing the code in the last iteration
for modified_smth { // as long as there are successful modifications
modified_smth = false
println('NEXT ITERATION, loop 1')
log.info('NEXT ITERATION, loop 1')
mut stack := []&Elem{}
for i in 0 .. sc.children.len {
stack << &sc.children[i]
@ -252,7 +266,7 @@ fn reduce_scope(content string, error_msg string, command string, do_fmt bool) {
text_code = create_code(sc)
println('Processing remaining lines')
log.info('Processing remaining lines')
split_code := text_code.split_into_lines() // dont forget to add back the \n
// Create the binary tree of the lines
depth := int(math.log2(split_code.len)) + 1
@ -282,12 +296,13 @@ fn reduce_scope(content string, error_msg string, command string, do_fmt bool) {
// Traverse the tree and prune the useless lines / line groups for the reproduction
mut line_tree := *line_stack[0]
assert string_reproduces(create_code(line_tree), error_msg, command) // should be the same
println('Pruning the lines/line groups')
warn_on_false(string_reproduces(create_code(line_tree), error_msg, command), 'string_reproduces',
@LOCATION) // should be the same
log.info('Pruning the lines/line groups')
modified_smth = true
for modified_smth {
modified_smth = false
println('NEXT ITERATION, loop 2')
log.info('NEXT ITERATION, loop 2')
mut stack := []&Elem{}
for i in 0 .. line_tree.children.len {
stack << &line_tree.children[i]
@ -316,7 +331,8 @@ fn reduce_scope(content string, error_msg string, command string, do_fmt bool) {
text_code = create_code(line_tree)
}
assert string_reproduces(text_code, error_msg, command)
warn_on_false(string_reproduces(text_code, error_msg, command), 'string_reproduces',
@LOCATION)
os.write_file('rpdc.v', text_code) or { panic(err) }
if do_fmt {
os.execute('v fmt -w rpdc.v')
@ -333,5 +349,9 @@ struct ShowParams {
fn show_code_stats(code string, params ShowParams) {
lines := code.split_into_lines()
println('${params.label}: ${code.len} chars, ${lines.len} lines.')
log.info('${params.label}: ${code.len} chars, ${lines.len} lines.')
}
fn warn_on_false(res bool, what string, loc string) {
log.warn('${what} is false, at ${loc}')
}