#!/bin/env tclsh ################################################################ # prase_arg # ls_arg_definition # one expected argument = "-${arg_name}(type)(default_value)" # arg_name will be defined in up-level proc. # To match defined arguments, we support shortcuts like Synopsys DC/PT/ICC # type = # default is the default value if it's not given in ls_arg. # if default is empty, then this is a must-have argument. # ls_arg can be $args for a proc or $argv for command line script ################################################################ proc parse_arg { ls_arg_definition ls_arg } { # parse ls_arg_definition set arg(name) {} set usage "usage:\n" foreach arg_def $ls_arg_definition { if {[regexp {^-([_0-9a-zA-Z]+)\(([a-z]+)\)\((.*)\)$} $arg_def match arg_name arg_type default_value]} { if {$default_value == ""} { set default_value "NULL" set optional "Must-have" } else { set optional "Optional" } set arg(name) [lappend arg(name) $arg_name] set arg($arg_name,arg_type) $arg_type set arg($arg_name,value) $default_value uplevel set $arg_name $arg($arg_name,value) # usage set usage "$usage -$arg_name ($optional) TYPE=$arg_type; DEFAULT=$default_value\n" } else { error "Error: arg_definition ($arg_def) is not recognized" } } # parse ls_arg if {$ls_arg == "-help"} { puts $usage uplevel exit } if {$ls_arg == ""} { set expect_arg_name -1 } else { set expect_arg_name 1 } while {$expect_arg_name >= 0} { set input [lindex $ls_arg 0] set ls_arg [lrange $ls_arg 1 end] if {$expect_arg_name} { # parse arg name if {[regexp {^-([_0-9a-zA-Z]+)$} $input match input_arg_name]} { set num_match 0 set match_arg_name "" foreach arg_name $arg(name) { if {[string match "${input_arg_name}*" $arg_name]} { incr num_match 1 set match_arg_name $arg_name } } if {$num_match == 0} { # no match found error "Error: argument ($input_arg_name) is not defined in ($ls_arg_definition)." } elseif {$num_match > 1} { # multi matches found error "Error: argument ($input_arg_name) is too ambiguouse. $num_match matches are found." } else { # 1 match found if {$arg($match_arg_name,arg_type) == "bool"} { set arg($match_arg_name,value) 1 set expect_arg_name 1 } else { set arg($match_arg_name,value) "NULL" set expect_arg_name 0 } } } else { error "Error: expecting argument name at ($input)" } } else { # parse arg value if {($arg($match_arg_name,arg_type) == "int" && ![regexp {^[0-9]+$} $input]) \ || ($arg($match_arg_name,arg_type) == "float" && ![regexp {^[0-9]+\.[0-9]+} $input]) \ } { error "Error: argument type is wrong. Expecting $arg($match_arg_name,arg_type) for $match_arg_name at ($input)" } elseif {$arg($match_arg_name,arg_type) == "string"} { regsub -all {\$} $input {\\$} input regsub -all "\n" $input "\\n" input regsub -all "\t" $input "\\t" input regsub -all "\"" $input "\\\"" input set arg($match_arg_name,value) "\"$input\"" } else { set arg($match_arg_name,value) $input } set expect_arg_name 1 } if {[llength $ls_arg] == 0} { break } } foreach arg_name $arg(name) { if {$arg($arg_name,value) == "NULL"} { error "Error: argument ($arg_name) has no value defined" } uplevel set $arg_name $arg($arg_name,value) } } # test if {[info exists argv0]} { if {[file tail $argv0] == [file tail [info script]]} { proc test_parse_arg args { parse_arg "-num(int)(100) -input_file(path)() -output_file(path)() -is_cool(bool)(0) -some_string(string)(.)" $args puts $num puts $input_file puts $output_file puts $is_cool puts "($some_string)" } test_parse_arg -num 123 -in ./input -out ../output -is_cool test_parse_arg -num 123 -in ./input -out ../output } }