Goofing around with command line flag scanning
This commit is contained in:
parent
43d54aa33b
commit
81f5be99aa
31
farse/cmd/farse-test/main.go
Normal file
31
farse/cmd/farse-test/main.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
181
farse/farse.go
Normal file
181
farse/farse.go
Normal file
@ -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…
Reference in New Issue
Block a user