2026-04-06 00:20:51 -05:00

210 lines
4.7 KiB
Go

package axscript
import (
"fmt"
"strconv"
"strings"
)
func Tokenize(cmdline string) []string {
var tokens []string
var token strings.Builder
inQuotes := false
runes := []rune(cmdline)
length := len(runes)
for i := 0; i < length; {
c := runes[i]
if c == ' ' && !inQuotes {
if token.Len() > 0 {
tokens = append(tokens, token.String())
token.Reset()
}
i++
continue
}
if c == '"' {
inQuotes = !inQuotes
i++
continue
}
if c == '\\' {
numBS := 0
for i < length && runes[i] == '\\' {
numBS++
i++
}
if i < length && runes[i] == '"' {
for j := 0; j < numBS/2; j++ {
token.WriteRune('\\')
}
if numBS%2 == 0 {
inQuotes = !inQuotes
} else {
token.WriteRune('"')
}
i++
} else {
for j := 0; j < numBS; j++ {
token.WriteRune('\\')
}
}
continue
}
token.WriteRune(c)
i++
}
if token.Len() > 0 {
tokens = append(tokens, token.String())
}
return tokens
}
func ParseCommand(cmdline string, resolved *ResolvedCommand) (*ParsedCommand, error) {
tokens := Tokenize(cmdline)
if len(tokens) == 0 {
return nil, fmt.Errorf("empty command line")
}
result := &ParsedCommand{
Args: make(map[string]interface{}),
}
commandName := tokens[0]
result.CommandName = commandName
result.Args["command"] = commandName
var cmdDef *CommandDef
argTokens := tokens[1:]
if resolved.Subcommand != nil {
if len(tokens) < 2 {
return nil, fmt.Errorf("subcommand required for '%s'", commandName)
}
result.SubcommandName = tokens[1]
result.Args["subcommand"] = tokens[1]
cmdDef = resolved.Subcommand
argTokens = tokens[2:]
} else {
cmdDef = resolved.Command
}
parsedArgsMap := make(map[string]string)
var lastKey string
for i := 0; i < len(argTokens); i++ {
arg := argTokens[i]
isWideArgs := true
for _, commandArg := range cmdDef.Args {
if commandArg.Flag {
if commandArg.Type == ArgTypeBool && commandArg.Mark == arg {
parsedArgsMap[commandArg.Mark] = "true"
lastKey = commandArg.Mark
isWideArgs = false
break
} else if commandArg.Mark == arg && i+1 < len(argTokens) {
i++
parsedArgsMap[commandArg.Name] = argTokens[i]
lastKey = commandArg.Name
isWideArgs = false
break
}
} else if _, exists := parsedArgsMap[commandArg.Name]; !exists {
parsedArgsMap[commandArg.Name] = arg
lastKey = commandArg.Name
isWideArgs = false
break
}
}
if isWideArgs && lastKey != "" {
var wide strings.Builder
for j := i; j < len(argTokens); j++ {
wide.WriteString(" ")
wide.WriteString(argTokens[j])
}
parsedArgsMap[lastKey] += wide.String()
break
}
}
for _, commandArg := range cmdDef.Args {
nameKey := commandArg.Name
if commandArg.Type == ArgTypeBool {
nameKey = commandArg.Mark
}
if val, exists := parsedArgsMap[nameKey]; exists {
switch commandArg.Type {
case ArgTypeString:
result.Args[commandArg.Name] = val
case ArgTypeInt:
intVal, err := strconv.Atoi(strings.TrimSpace(val))
if err != nil {
return nil, fmt.Errorf("invalid integer value for argument '%s': %s", commandArg.Name, val)
}
result.Args[commandArg.Name] = intVal
case ArgTypeBool:
result.Args[commandArg.Mark] = val == "true"
case ArgTypeFile:
result.FileArgs = append(result.FileArgs, FileArgInfo{
ArgName: commandArg.Name,
OriginalPath: val,
Required: commandArg.Required,
})
result.Args[commandArg.Name] = nil
}
} else if commandArg.Required {
if commandArg.DefaultValue == nil && !commandArg.DefaultUsed {
return nil, fmt.Errorf("missing required argument: %s", commandArg.Name)
}
switch commandArg.Type {
case ArgTypeString:
if s, ok := commandArg.DefaultValue.(string); ok {
result.Args[commandArg.Name] = s
} else {
return nil, fmt.Errorf("missing required argument: %s", commandArg.Name)
}
case ArgTypeInt:
switch v := commandArg.DefaultValue.(type) {
case int:
result.Args[commandArg.Name] = v
case int64:
result.Args[commandArg.Name] = int(v)
case float64:
result.Args[commandArg.Name] = int(v)
default:
return nil, fmt.Errorf("missing required argument: %s", commandArg.Name)
}
case ArgTypeBool:
if b, ok := commandArg.DefaultValue.(bool); ok {
result.Args[commandArg.Mark] = b
} else {
return nil, fmt.Errorf("missing required argument: %s", commandArg.Name)
}
}
}
}
msg := cmdDef.Message
if msg != "" {
for k, v := range parsedArgsMap {
param := "<" + k + ">"
if strings.Contains(msg, param) {
msg = strings.ReplaceAll(msg, param, v)
}
}
result.Message = msg
result.Args["message"] = msg
}
return result, nil
}