Skip to content

Instantly share code, notes, and snippets.

@op
Created March 18, 2011 22:53
Show Gist options
  • Save op/876997 to your computer and use it in GitHub Desktop.
Save op/876997 to your computer and use it in GitHub Desktop.
go flag parser
diff -r f487d74ff495 src/pkg/flag/export_test.go
--- a/src/pkg/flag/export_test.go Thu Feb 10 23:01:45 2011 +0800
+++ b/src/pkg/flag/export_test.go Fri Feb 18 15:24:20 2011 +0800
@@ -12,7 +12,7 @@
// After calling ResetForTesting, parse errors in flag handling will panic rather
// than exit the program.
func ResetForTesting(usage func()) {
- flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), os.Args[1:]}
+ flags = NewParser()
Usage = usage
panicOnError = true
}
diff -r f487d74ff495 src/pkg/flag/flag.go
--- a/src/pkg/flag/flag.go Thu Feb 10 23:01:45 2011 +0800
+++ b/src/pkg/flag/flag.go Fri Feb 18 15:24:20 2011 +0800
@@ -62,6 +62,14 @@
}
os.Args = flag.Args()
flag.Parse()
+
+ A flag parser can be created separatley from the global flags and can
+ be fed with arguments other than os.Args and will not exit the program in
+ case of errors when parsing the flags.
+
+ var parser = flag.NewParser()
+ var ip *int = parser.Int("flagname", 1234, "help message for flagname")
+ parser.Parse()
*/
package flag
@@ -71,6 +79,42 @@
"strconv"
)
+// Errors introduced by this package.
+
+var (
+ errIllegalSyntax = os.NewError("flag: illegal flag syntax")
+ errInvalidBooleanValue = os.NewError("flag: invalid boolean value")
+ errInvalidValue = os.NewError("flag: invalid value")
+ errMissingArgument = os.NewError("flag: requires an argument")
+ errUndefinedFlag = os.NewError("flag: undefined flag")
+)
+
+type parseError struct {
+ os.Error
+ name string
+ value string
+}
+
+func newParseError(error os.Error, name string, value string) os.Error {
+ return &parseError{error, name, value}
+}
+
+func (err *parseError) String() string {
+ switch err.Error.String() {
+ case errIllegalSyntax.String():
+ return "bad flag syntax: " + err.name
+ case errInvalidBooleanValue.String():
+ return fmt.Sprintf("invalid boolean value %q for flag: -%s", err.value, err.name)
+ case errInvalidValue.String():
+ return fmt.Sprintf("invalid value %q for flag: -%s", err.value, err.name)
+ case errMissingArgument.String():
+ return "flag needs an argument: -" + err.name
+ case errUndefinedFlag.String():
+ return "flag provided but not defined: -" + err.name
+ }
+ panic("unknown flag parse error")
+}
+
// -- Bool Value
type boolValue bool
@@ -197,16 +241,22 @@
DefValue string // default value (as text); for usage message
}
-type allFlags struct {
+
+// Parser represents a flag parser.
+type Parser struct {
actual map[string]*Flag
formal map[string]*Flag
args []string // arguments after flags
}
-var flags *allFlags
+var flags *Parser
// VisitAll visits the flags, calling fn for each. It visits all flags, even those not set.
func VisitAll(fn func(*Flag)) {
+ flags.VisitAll(fn)
+}
+
+func (flags *Parser) VisitAll(fn func(*Flag)) {
for _, f := range flags.formal {
fn(f)
}
@@ -214,6 +264,10 @@
// Visit visits the flags, calling fn for each. It visits only those flags that have been set.
func Visit(fn func(*Flag)) {
+ flags.Visit(fn)
+}
+
+func (flags *Parser) Visit(fn func(*Flag)) {
for _, f := range flags.actual {
fn(f)
}
@@ -221,12 +275,20 @@
// Lookup returns the Flag structure of the named flag, returning nil if none exists.
func Lookup(name string) *Flag {
+ return flags.Lookup(name)
+}
+
+func (flags *Parser) Lookup(name string) *Flag {
return flags.formal[name]
}
// Set sets the value of the named flag. It returns true if the set succeeded; false if
// there is no such flag defined.
func Set(name, value string) bool {
+ return flags.Set(name, value)
+}
+
+func (flags *Parser) Set(name, value string) bool {
f, ok := flags.formal[name]
if !ok {
return false
@@ -241,7 +303,11 @@
// PrintDefaults prints to standard error the default values of all defined flags.
func PrintDefaults() {
- VisitAll(func(f *Flag) {
+ flags.PrintDefaults()
+}
+
+func (flags *Parser) PrintDefaults() {
+ flags.VisitAll(func(f *Flag) {
format := " -%s=%s: %s\n"
if _, ok := f.Value.(*stringValue); ok {
// put quotes on the value
@@ -268,11 +334,14 @@
os.Exit(2)
}
-func NFlag() int { return len(flags.actual) }
+func NFlag() int { return flags.NFlag() }
+func (flags *Parser) NFlag() int { return len(flags.actual) }
// Arg returns the i'th command-line argument. Arg(0) is the first remaining argument
// after flags have been processed.
-func Arg(i int) string {
+func Arg(i int) string { return flags.Arg(i) }
+
+func (flags *Parser) Arg(i int) string {
if i < 0 || i >= len(flags.args) {
return ""
}
@@ -280,62 +349,96 @@
}
// NArg is the number of arguments remaining after flags have been processed.
-func NArg() int { return len(flags.args) }
+func NArg() int { return flags.NArg() }
+func (flags *Parser) NArg() int { return len(flags.args) }
// Args returns the non-flag command-line arguments.
-func Args() []string { return flags.args }
+func Args() []string { return flags.Args() }
+func (flags *Parser) Args() []string { return flags.args }
// BoolVar defines a bool flag with specified name, default value, and usage string.
// The argument p points to a bool variable in which to store the value of the flag.
func BoolVar(p *bool, name string, value bool, usage string) {
- Var(newBoolValue(value, p), name, usage)
+ flags.BoolVar(p, name, value, usage)
+}
+
+func (flags *Parser) BoolVar(p *bool, name string, value bool, usage string) {
+ flags.Var(newBoolValue(value, p), name, usage)
}
// Bool defines a bool flag with specified name, default value, and usage string.
// The return value is the address of a bool variable that stores the value of the flag.
func Bool(name string, value bool, usage string) *bool {
+ return flags.Bool(name, value, usage)
+}
+
+func (flags *Parser) Bool(name string, value bool, usage string) *bool {
p := new(bool)
- BoolVar(p, name, value, usage)
+ flags.BoolVar(p, name, value, usage)
return p
}
// IntVar defines an int flag with specified name, default value, and usage string.
// The argument p points to an int variable in which to store the value of the flag.
func IntVar(p *int, name string, value int, usage string) {
- Var(newIntValue(value, p), name, usage)
+ flags.IntVar(p, name, value, usage)
+}
+
+func (flags *Parser) IntVar(p *int, name string, value int, usage string) {
+ flags.Var(newIntValue(value, p), name, usage)
}
// Int defines an int flag with specified name, default value, and usage string.
// The return value is the address of an int variable that stores the value of the flag.
func Int(name string, value int, usage string) *int {
+ return flags.Int(name, value, usage)
+}
+
+func (flags *Parser) Int(name string, value int, usage string) *int {
p := new(int)
- IntVar(p, name, value, usage)
+ flags.IntVar(p, name, value, usage)
return p
}
// Int64Var defines an int64 flag with specified name, default value, and usage string.
// The argument p points to an int64 variable in which to store the value of the flag.
func Int64Var(p *int64, name string, value int64, usage string) {
- Var(newInt64Value(value, p), name, usage)
+ flags.Int64Var(p, name, value, usage)
+}
+
+func (flags *Parser) Int64Var(p *int64, name string, value int64, usage string) {
+ flags.Var(newInt64Value(value, p), name, usage)
}
// Int64 defines an int64 flag with specified name, default value, and usage string.
// The return value is the address of an int64 variable that stores the value of the flag.
func Int64(name string, value int64, usage string) *int64 {
+ return flags.Int64(name, value, usage)
+}
+
+func (flags *Parser) Int64(name string, value int64, usage string) *int64 {
p := new(int64)
- Int64Var(p, name, value, usage)
+ flags.Int64Var(p, name, value, usage)
return p
}
// UintVar defines a uint flag with specified name, default value, and usage string.
// The argument p points to a uint variable in which to store the value of the flag.
func UintVar(p *uint, name string, value uint, usage string) {
- Var(newUintValue(value, p), name, usage)
+ flags.UintVar(p, name, value, usage)
+}
+
+func (flags *Parser) UintVar(p *uint, name string, value uint, usage string) {
+ flags.Var(newUintValue(value, p), name, usage)
}
// Uint defines a uint flag with specified name, default value, and usage string.
// The return value is the address of a uint variable that stores the value of the flag.
func Uint(name string, value uint, usage string) *uint {
+ return flags.Uint(name, value, usage)
+}
+
+func (flags *Parser) Uint(name string, value uint, usage string) *uint {
p := new(uint)
UintVar(p, name, value, usage)
return p
@@ -344,48 +447,76 @@
// Uint64Var defines a uint64 flag with specified name, default value, and usage string.
// The argument p points to a uint64 variable in which to store the value of the flag.
func Uint64Var(p *uint64, name string, value uint64, usage string) {
- Var(newUint64Value(value, p), name, usage)
+ flags.Uint64Var(p, name, value, usage)
+}
+
+func (flags *Parser) Uint64Var(p *uint64, name string, value uint64, usage string) {
+ flags.Var(newUint64Value(value, p), name, usage)
}
// Uint64 defines a uint64 flag with specified name, default value, and usage string.
// The return value is the address of a uint64 variable that stores the value of the flag.
func Uint64(name string, value uint64, usage string) *uint64 {
+ return flags.Uint64(name, value, usage)
+}
+
+func (flags *Parser) Uint64(name string, value uint64, usage string) *uint64 {
p := new(uint64)
- Uint64Var(p, name, value, usage)
+ flags.Uint64Var(p, name, value, usage)
return p
}
// StringVar defines a string flag with specified name, default value, and usage string.
// The argument p points to a string variable in which to store the value of the flag.
func StringVar(p *string, name, value string, usage string) {
- Var(newStringValue(value, p), name, usage)
+ flags.StringVar(p, name, value, usage)
+}
+
+func (flags *Parser) StringVar(p *string, name, value string, usage string) {
+ flags.Var(newStringValue(value, p), name, usage)
}
// String defines a string flag with specified name, default value, and usage string.
// The return value is the address of a string variable that stores the value of the flag.
func String(name, value string, usage string) *string {
+ return flags.String(name, value, usage)
+}
+
+func (flags *Parser) String(name, value string, usage string) *string {
p := new(string)
- StringVar(p, name, value, usage)
+ flags.StringVar(p, name, value, usage)
return p
}
// Float64Var defines a float64 flag with specified name, default value, and usage string.
// The argument p points to a float64 variable in which to store the value of the flag.
func Float64Var(p *float64, name string, value float64, usage string) {
- Var(newFloat64Value(value, p), name, usage)
+ flags.Float64Var(p, name, value, usage)
+}
+
+func (flags *Parser) Float64Var(p *float64, name string, value float64, usage string) {
+ flags.Var(newFloat64Value(value, p), name, usage)
}
// Float64 defines a float64 flag with specified name, default value, and usage string.
// The return value is the address of a float64 variable that stores the value of the flag.
func Float64(name string, value float64, usage string) *float64 {
+ return flags.Float64(name, value, usage)
+}
+
+func (flags *Parser) Float64(name string, value float64, usage string) *float64 {
p := new(float64)
- Float64Var(p, name, value, usage)
+ flags.Float64Var(p, name, value, usage)
return p
}
// Var defines a user-typed flag with specified name, default value, and usage string.
// The argument p points to a Value variable in which to store the value of the flag.
func Var(value Value, name string, usage string) {
+ flags.Var(value, name, usage)
+}
+
+func (flags *Parser) Var(value Value, name string, usage string) {
// Remember the default value as a string; it won't change.
f := &Flag{name, usage, value, value.String()}
_, alreadythere := flags.formal[name]
@@ -397,30 +528,29 @@
}
-func (f *allFlags) parseOne() (ok bool) {
- if len(f.args) == 0 {
- return false
+func (flags *Parser) parseOne() (ok bool, err os.Error) {
+ if len(flags.args) == 0 {
+ return false, nil
}
- s := f.args[0]
+ s := flags.args[0]
if len(s) == 0 || s[0] != '-' || len(s) == 1 {
- return false
+ return false, nil
}
num_minuses := 1
if s[1] == '-' {
num_minuses++
if len(s) == 2 { // "--" terminates the flags
- f.args = f.args[1:]
- return false
+ flags.args = flags.args[1:]
+ return false, nil
}
}
name := s[num_minuses:]
if len(name) == 0 || name[0] == '-' || name[0] == '=' {
- fmt.Fprintln(os.Stderr, "bad flag syntax:", s)
- fail()
+ return false, newParseError(errIllegalSyntax, s, "")
}
// it's a flag. does it have an argument?
- f.args = f.args[1:]
+ flags.args = flags.args[1:]
has_value := false
value := ""
for i := 1; i < len(name); i++ { // equals cannot be first
@@ -434,47 +564,66 @@
m := flags.formal
flag, alreadythere := m[name] // BUG
if !alreadythere {
- fmt.Fprintf(os.Stderr, "flag provided but not defined: -%s\n", name)
- fail()
+ return false, newParseError(errUndefinedFlag, name, value)
}
if fv, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg
if has_value {
if !fv.Set(value) {
- fmt.Fprintf(os.Stderr, "invalid boolean value %q for flag: -%s\n", value, name)
- fail()
+ return false, newParseError(errInvalidBooleanValue, name, value)
}
} else {
fv.Set("true")
}
} else {
// It must have a value, which might be the next argument.
- if !has_value && len(f.args) > 0 {
+ if !has_value && len(flags.args) > 0 {
// value is the next arg
has_value = true
- value, f.args = f.args[0], f.args[1:]
+ value, flags.args = flags.args[0], flags.args[1:]
}
if !has_value {
- fmt.Fprintf(os.Stderr, "flag needs an argument: -%s\n", name)
- fail()
+ return false, newParseError(errMissingArgument, name, value)
}
ok = flag.Value.Set(value)
if !ok {
- fmt.Fprintf(os.Stderr, "invalid value %q for flag: -%s\n", value, name)
- fail()
+ return false, newParseError(errInvalidValue, name, value)
}
}
flags.actual[name] = flag
- return true
+ return true, nil
}
+// NewParser creates a new flag parser, completely separate from the global
+// flag parser.
+func NewParser() (*Parser) {
+ return &Parser{make(map[string]*Flag), make(map[string]*Flag), nil}
+}
+
+
+func (f *Parser) Parse(args []string) (err os.Error) {
+ var ok bool
+ f.args = args
+ for {
+ ok, err = f.parseOne()
+ if ok != true || err != nil {
+ break
+ }
+ }
+ return
+}
+
+
// Parse parses the command-line flags. Must be called after all flags are defined
// and before any are accessed by the program.
func Parse() {
- flags.args = os.Args[1:]
- for flags.parseOne() {
+ err := flags.Parse(os.Args[1:])
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.String())
+ fail()
}
}
+
func init() {
- flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), os.Args[1:]}
+ flags = NewParser()
}
diff -r f487d74ff495 src/pkg/flag/flag_test.go
--- a/src/pkg/flag/flag_test.go Thu Feb 10 23:01:45 2011 +0800
+++ b/src/pkg/flag/flag_test.go Fri Feb 18 15:24:20 2011 +0800
@@ -192,3 +192,16 @@
t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args)
}
}
+
+func TestFlagSeparation(t *testing.T) {
+ ResetForTesting(func() { t.Fatal("bad parse") })
+ parser := NewParser()
+ args := []string{"cmd", "-foo", "args"}
+ fooGlobal := Bool("foo", false, "")
+ fooLocal := parser.Bool("foo", false, "")
+ err := parser.Parse(args[1:])
+
+ if err != nil || *fooGlobal == true || *fooLocal != true {
+ t.Fatalf("expected nil false true got %v %v %v", err, *fooGlobal, *fooLocal)
+ }
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment