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