Goofing around with command line flag scanning

cat-town
Dan Buch 9 years ago
parent 43d54aa33b
commit 81f5be99aa
No known key found for this signature in database
GPG Key ID: FAEF12936DD3E3EC

@ -0,0 +1,31 @@
package main
import (
"fmt"
"log"
"os"
farse "../.."
)
func main() {
fl := farse.New()
err := fl.AddKnown(
"-a", "-n", "-q", "-m",
"--azimuth", "--noodle", "--quince",
"whoop", "fizzle", "flim",
)
if err != nil {
log.Fatal(err)
}
fmt.Println("known:")
for key, tokType := range fl.Known() {
fmt.Printf(" %v: %s\n", key, tokType)
}
fmt.Println("scanned:")
for _, tok := range fl.Scan(os.Args) {
fmt.Printf(" %v: %s\n", tok.Value, tok.Type)
}
}

@ -0,0 +1,181 @@
package farse
import "fmt"
type TokenType int
const (
TokenLongFlag TokenType = iota
TokenShortFlag
TokenShortCompoundFlag
TokenIdentifier
TokenArgument
TokenInvalid
TokenSentinel
)
func (t TokenType) String() string {
switch t {
case TokenLongFlag:
return "long flag"
case TokenShortFlag:
return "short flag"
case TokenShortCompoundFlag:
return "short compound"
case TokenIdentifier:
return "identifier"
case TokenArgument:
return "argument"
case TokenInvalid:
return "invalid"
case TokenSentinel:
return "sentinel"
}
return "(x___x)"
}
type Token struct {
Value string
Type TokenType
}
type Farse struct {
known map[string]TokenType
}
func New() *Farse {
return &Farse{
known: map[string]TokenType{},
}
}
func (fl *Farse) Known() map[string]TokenType {
return fl.known
}
func (fl *Farse) AddKnown(knownStrings ...string) error {
for i, tok := range scan(knownStrings) {
if tok.Type == TokenShortCompoundFlag {
return fmt.Errorf("argument %d %q is invalid value for known identifier", i, tok.Value)
}
if len(tok.Value) == 0 {
return fmt.Errorf("argument %d is empty", i)
}
if len(tok.Value) == 1 && tok.Value == "-" {
return fmt.Errorf("argument %d %q is invalid value for known identifier", i, tok.Value)
}
if _, exists := fl.known[tok.Value]; exists {
return fmt.Errorf("argument %d %s conflicts with known", i, tok.Value)
}
fl.known[tok.Value] = tok.Type
}
return nil
}
func (fl *Farse) Scan(args []string) []*Token {
ret := []*Token{}
passedSentinel := false
for _, tok := range scan(args) {
if tok.Type == TokenSentinel {
ret = append(ret, tok)
passedSentinel = true
continue
}
if passedSentinel {
tok.Type = TokenArgument
ret = append(ret, tok)
continue
}
if tok.Type == TokenShortCompoundFlag {
ret = append(ret, fl.extractCompoundTokens(tok.Value)...)
continue
}
if tok.Type == TokenIdentifier {
if _, known := fl.known[tok.Value]; !known {
tok.Type = TokenArgument
}
ret = append(ret, tok)
}
}
return ret
}
func (fl *Farse) extractCompoundTokens(s string) []*Token {
ret := []*Token{}
for j, ch := range s {
last := j == (len(s) - 1)
if j == 0 && ch == '-' {
continue
}
sf := string(append([]rune("-"), ch))
if _, known := fl.known[sf]; known {
ret = append(ret, &Token{Value: sf, Type: TokenShortFlag})
if last {
return ret
}
if _, nextKnown := fl.known[string(append([]rune("-"), rune(s[j+1])))]; !nextKnown {
ret = append(ret, &Token{Value: s[j+1:], Type: TokenArgument})
return ret
}
}
}
return ret
}
func scan(sl []string) []*Token {
ret := []*Token{}
for _, s := range sl {
tok := scanToken(s)
if tok != nil {
ret = append(ret, tok)
}
}
return ret
}
func scanToken(s string) *Token {
switch len(s) {
case 0, 1:
return &Token{Value: s, Type: TokenIdentifier}
case 2:
if s == "--" {
return &Token{Value: s, Type: TokenSentinel}
}
if s[0] == '-' {
return &Token{Value: s, Type: TokenShortFlag}
}
default:
if s[0] == '-' {
if s[1] != '-' {
return &Token{Value: s, Type: TokenShortCompoundFlag}
}
return &Token{Value: s, Type: TokenLongFlag}
}
return &Token{Value: s, Type: TokenIdentifier}
}
return nil
}
Loading…
Cancel
Save