| package parser |
| |
| import ( |
| "strings" |
| "unicode" |
| ) |
| |
| // splitCommand takes a single line of text and parses out the cmd and args, |
| // which are used for dispatching to more exact parsing functions. |
| func splitCommand(line string) (string, []string, string, error) { |
| var args string |
| var flags []string |
| |
| // Make sure we get the same results irrespective of leading/trailing spaces |
| cmdline := tokenWhitespace.Split(strings.TrimSpace(line), 2) |
| cmd := strings.ToLower(cmdline[0]) |
| |
| if len(cmdline) == 2 { |
| var err error |
| args, flags, err = extractBuilderFlags(cmdline[1]) |
| if err != nil { |
| return "", nil, "", err |
| } |
| } |
| |
| return cmd, flags, strings.TrimSpace(args), nil |
| } |
| |
| func extractBuilderFlags(line string) (string, []string, error) { |
| // Parses the BuilderFlags and returns the remaining part of the line |
| |
| const ( |
| inSpaces = iota // looking for start of a word |
| inWord |
| inQuote |
| ) |
| |
| words := []string{} |
| phase := inSpaces |
| word := "" |
| quote := '\000' |
| blankOK := false |
| var ch rune |
| |
| for pos := 0; pos <= len(line); pos++ { |
| if pos != len(line) { |
| ch = rune(line[pos]) |
| } |
| |
| if phase == inSpaces { // Looking for start of word |
| if pos == len(line) { // end of input |
| break |
| } |
| if unicode.IsSpace(ch) { // skip spaces |
| continue |
| } |
| |
| // Only keep going if the next word starts with -- |
| if ch != '-' || pos+1 == len(line) || rune(line[pos+1]) != '-' { |
| return line[pos:], words, nil |
| } |
| |
| phase = inWord // found something with "--", fall through |
| } |
| if (phase == inWord || phase == inQuote) && (pos == len(line)) { |
| if word != "--" && (blankOK || len(word) > 0) { |
| words = append(words, word) |
| } |
| break |
| } |
| if phase == inWord { |
| if unicode.IsSpace(ch) { |
| phase = inSpaces |
| if word == "--" { |
| return line[pos:], words, nil |
| } |
| if blankOK || len(word) > 0 { |
| words = append(words, word) |
| } |
| word = "" |
| blankOK = false |
| continue |
| } |
| if ch == '\'' || ch == '"' { |
| quote = ch |
| blankOK = true |
| phase = inQuote |
| continue |
| } |
| if ch == '\\' { |
| if pos+1 == len(line) { |
| continue // just skip \ at end |
| } |
| pos++ |
| ch = rune(line[pos]) |
| } |
| word += string(ch) |
| continue |
| } |
| if phase == inQuote { |
| if ch == quote { |
| phase = inWord |
| continue |
| } |
| if ch == '\\' { |
| if pos+1 == len(line) { |
| phase = inWord |
| continue // just skip \ at end |
| } |
| pos++ |
| ch = rune(line[pos]) |
| } |
| word += string(ch) |
| } |
| } |
| |
| return "", words, nil |
| } |