1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

tooling: add tools/compare_v_performance_between_commits

easily compare v performance/size across commits.

* fix eprintln on linux (it now uses stderr, and flushes it).

* flag: cleaner usage information.
This commit is contained in:
Delyan Angelov
2019-09-28 14:17:16 +03:00
committed by Alexander Medvednikov
parent 5c79c0e743
commit 366c50674c
6 changed files with 361 additions and 44 deletions

View File

@ -1,3 +1,4 @@
module flag
// module flag for command-line flag parsing
//
@ -43,15 +44,14 @@
// }
// ```
module flag
// data object storing information about a defined flag
struct Flag {
pub:
name string // name as it appears on command line
abbr byte // shortcut
usage string // help message
val_desc string // something like '<arg>' that appears in usage
val_desc string // something like '<arg>' that appears in usage,
// and also the default value, when the flag is not given
}
//
@ -66,12 +66,20 @@ pub mut:
min_free_args int
max_free_args int
args_description string
}
const (
// used for formating usage message
SPACE = ' '
UNDERLINE = '-----------------------------------------------'
MAX_ARGS_NUMBER = 4048
)
// create a new flag set for parsing command line arguments
// TODO use INT_MAX some how
pub fn new_flag_parser(args []string) &FlagParser {
return &FlagParser{args:args, max_free_args: 4048}
return &FlagParser{args:args, max_free_args: MAX_ARGS_NUMBER}
}
// change the application name to be used in 'usage' output
@ -171,7 +179,7 @@ fn (fs mut FlagParser) parse_bool_value(n string, ab byte) ?string {
// version with abbreviation
//TODO error handling for invalid string to bool conversion
pub fn (fs mut FlagParser) bool_(n string, a byte, v bool, u string) bool {
fs.add_flag(n, a, u, '')
fs.add_flag(n, a, u, '<bool>:'+v.str())
parsed := fs.parse_bool_value(n, a) or {
return v
}
@ -196,7 +204,7 @@ pub fn (fs mut FlagParser) bool(n string, v bool, u string) bool {
// version with abbreviation
//TODO error handling for invalid string to int conversion
pub fn (fs mut FlagParser) int_(n string, a byte, i int, u string) int {
fs.add_flag(n, a, u, '<int>')
fs.add_flag(n, a, u, '<int>:$i')
parsed := fs.parse_value(n, a) or {
return i
}
@ -221,7 +229,7 @@ pub fn (fs mut FlagParser) int(n string, i int, u string) int {
// version with abbreviation
//TODO error handling for invalid string to float conversion
pub fn (fs mut FlagParser) float_(n string, a byte, f f32, u string) f32 {
fs.add_flag(n, a, u, '<float>')
fs.add_flag(n, a, u, '<float>:$f')
parsed := fs.parse_value(n, a) or {
return f
}
@ -245,7 +253,7 @@ pub fn (fs mut FlagParser) float(n string, f f32, u string) f32 {
// the default value is returned
// version with abbreviation
pub fn (fs mut FlagParser) string_(n string, a byte, v, u string) string {
fs.add_flag(n, a, u, '<arg>')
fs.add_flag(n, a, u, '<string>:$v')
parsed := fs.parse_value(n, a) or {
return v
}
@ -261,6 +269,27 @@ pub fn (fs mut FlagParser) string(n, v, u string) string {
return fs.string_(n, `\0`, v, u)
}
pub fn (fs mut FlagParser) limit_free_args_to_at_least(n int) {
if n > MAX_ARGS_NUMBER {
panic('flag.limit_free_args_to_at_least expect n to be smaller than $MAX_ARGS_NUMBER')
}
if n <= 0 {
panic('flag.limit_free_args_to_at_least expect n to be a positive number')
}
fs.min_free_args = n
}
pub fn (fs mut FlagParser) limit_free_args_to_exactly(n int) {
if n > MAX_ARGS_NUMBER {
panic('flag.limit_free_args_to_exactly expect n to be smaller than $MAX_ARGS_NUMBER')
}
if n < 0 {
panic('flag.limit_free_args_to_exactly expect n to be a non negative number')
}
fs.min_free_args = n
fs.max_free_args = n
}
// this will cause an error in finalize() if free args are out of range
// (min, ..., max)
pub fn (fs mut FlagParser) limit_free_args(min, max int) {
@ -271,19 +300,50 @@ pub fn (fs mut FlagParser) limit_free_args(min, max int) {
fs.max_free_args = max
}
const (
// used for formating usage message
SPACE = ' '
)
pub fn (fs mut FlagParser) arguments_description(description string){
fs.args_description = description
}
// collect all given information and
pub fn (fs FlagParser) usage() string {
mut use := '\n'
use += 'usage ${fs.application_name} [options] [ARGS]\n'
positive_min_arg := ( fs.min_free_args > 0 )
positive_max_arg := ( fs.max_free_args > 0 && fs.max_free_args != MAX_ARGS_NUMBER )
no_arguments := ( fs.min_free_args == 0 && fs.max_free_args == 0 )
mut adesc := if fs.args_description.len > 0 { fs.args_description } else { '[ARGS]' }
if no_arguments { adesc = '' }
mut use := ''
use += '$fs.application_name $fs.application_version\n'
use += '$UNDERLINE\n'
use += 'Usage: ${fs.application_name} [options] $adesc\n'
use += '\n'
if fs.application_description != '' {
use += 'Description:\n'
use += '$fs.application_description'
use += '\n\n'
}
// show a message about the [ARGS]:
if positive_min_arg || positive_max_arg || no_arguments {
if no_arguments {
use += 'This application does not expect any arguments\n\n'
goto end_of_arguments_handling
}
mut s:= []string
if positive_min_arg { s << 'at least $fs.min_free_args' }
if positive_max_arg { s << 'at most $fs.max_free_args' }
if positive_min_arg && positive_max_arg && fs.min_free_args == fs.max_free_args {
s = ['exactly $fs.min_free_args']
}
sargs := s.join(' and ')
use += 'The arguments should be $sargs in number.\n\n'
}
end_of_arguments_handling:
if fs.flags.len > 0 {
use += 'options:\n'
use += 'Options:\n'
for f in fs.flags {
flag_desc := ' --$f.name $f.val_desc'
space := if flag_desc.len > SPACE.len-2 {
@ -292,17 +352,10 @@ pub fn (fs FlagParser) usage() string {
SPACE.right(flag_desc.len)
}
abbr_desc := if f.abbr == `\0` { '' } else { ' -${tos(f.abbr, 1)}\n' }
use += '$abbr_desc$flag_desc$space$f.usage\n'
use += '${abbr_desc}${flag_desc}${space}${f.usage}\n'
}
}
use += '\n'
use += '$fs.application_name $fs.application_version\n'
if fs.application_description != '' {
use += '\n'
use += 'description:\n'
use += '$fs.application_description'
}
return use
}
@ -319,15 +372,14 @@ pub fn (fs FlagParser) finalize() ?[]string {
return error('Unknown argument \'${a.right(2)}\'')
}
}
if fs.args.len < fs.min_free_args {
return error('Expect at least ${fs.min_free_args} arguments')
if fs.args.len < fs.min_free_args && fs.min_free_args > 0 {
return error('Expected at least ${fs.min_free_args} arguments, but given $fs.args.len')
}
if fs.args.len >= fs.max_free_args {
if fs.max_free_args > 0 {
return error('Expect at most ${fs.max_free_args} arguments')
} else {
return error('Expect no arguments')
}
if fs.args.len > fs.max_free_args && fs.max_free_args > 0 {
return error('Expected at most ${fs.max_free_args} arguments, but given $fs.args.len')
}
if fs.args.len > 0 && fs.max_free_args == 0 && fs.min_free_args == 0 {
return error('Expected no arguments, but given $fs.args.len')
}
return fs.args
}

View File

@ -145,6 +145,7 @@ fn test_finalize_returns_error_for_unknown_flags() {
fn test_allow_to_build_usage_message() {
mut fp := flag.new_flag_parser([]string)
fp.limit_free_args(1, 4)
fp.application('flag_tool')
fp.version('v0.0.0')
fp.description('some short information about this tool')
@ -158,13 +159,14 @@ fn test_allow_to_build_usage_message() {
usage := fp.usage()
mut all_strings_found := true
for s in ['flag_tool', 'v0.0.0',
'an_int <int>', 'a_bool', 'bool_without', 'a_float <float>', 'a_string <arg>',
'an_int <int>', 'a_bool', 'bool_without', 'a_float <float>', 'a_string <string>:not_stuff',
'some int to define',
'some bool to define',
'this should appear on the next line',
'some float as well',
'your credit card number',
'usage', 'options:', 'description:',
'The arguments should be at least 1 and at most 4 in number.',
'Usage', 'Options:', 'Description:',
'some short information about this tool'] {
if !usage.contains(s) {
eprintln(' missing \'$s\' in usage message')
@ -181,7 +183,7 @@ fn test_if_no_description_given_usage_message_does_not_contain_descpription() {
fp.bool('a_bool', false, '')
assert !fp.usage().contains('description:')
assert !fp.usage().contains('Description:')
}
fn test_if_no_options_given_usage_message_does_not_contain_options() {
@ -189,7 +191,7 @@ fn test_if_no_options_given_usage_message_does_not_contain_options() {
fp.application('flag_tool')
fp.version('v0.0.0')
assert !fp.usage().contains('options:')
assert !fp.usage().contains('Options:')
}
fn test_free_args_could_be_limited() {
@ -206,7 +208,7 @@ fn test_error_for_to_few_free_args() {
mut fp1 := flag.new_flag_parser(['a', 'b', 'c'])
fp1.limit_free_args(5, 6)
args := fp1.finalize() or {
assert err == 'Expect at least 5 arguments'
assert err.starts_with('Expected at least 5 arguments')
return
}
assert args.len < 0 // expect an error and need to use args
@ -216,7 +218,7 @@ fn test_error_for_to_much_free_args() {
mut fp1 := flag.new_flag_parser(['a', 'b', 'c'])
fp1.limit_free_args(1, 2)
args := fp1.finalize() or {
assert err == 'Expect at most 2 arguments'
assert err.starts_with('Expected at most 2 arguments')
return
}
assert args.len < 0 // expect an error and need to use args
@ -226,7 +228,7 @@ fn test_could_expect_no_free_args() {
mut fp1 := flag.new_flag_parser(['a'])
fp1.limit_free_args(0, 0)
args := fp1.finalize() or {
assert err == 'Expect no arguments'
assert err.starts_with('Expected no arguments')
return
}
assert args.len < 0 // expect an error and need to use args