| package parser |
| |
| import ( |
| "fmt" |
| "strconv" |
| "strings" |
| "unicode" |
| ) |
| |
| // Dump dumps the AST defined by `node` as a list of sexps. |
| // Returns a string suitable for printing. |
| func (node *Node) Dump() string { |
| str := "" |
| str += node.Value |
| |
| if len(node.Flags) > 0 { |
| str += fmt.Sprintf(" %q", node.Flags) |
| } |
| |
| for _, n := range node.Children { |
| str += "(" + n.Dump() + ")\n" |
| } |
| |
| if node.Next != nil { |
| for n := node.Next; n != nil; n = n.Next { |
| if len(n.Children) > 0 { |
| str += " " + n.Dump() |
| } else { |
| str += " " + strconv.Quote(n.Value) |
| } |
| } |
| } |
| |
| return strings.TrimSpace(str) |
| } |
| |
| // performs the dispatch based on the two primal strings, cmd and args. Please |
| // look at the dispatch table in parser.go to see how these dispatchers work. |
| func fullDispatch(cmd, args string, d *Directive) (*Node, map[string]bool, error) { |
| fn := dispatch[cmd] |
| |
| // Ignore invalid Dockerfile instructions |
| if fn == nil { |
| fn = parseIgnore |
| } |
| |
| sexp, attrs, err := fn(args, d) |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| return sexp, attrs, nil |
| } |
| |
| // 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 |
| } |
| |
| // covers comments and empty lines. Lines should be trimmed before passing to |
| // this function. |
| func stripComments(line string) string { |
| // string is already trimmed at this point |
| if tokenComment.MatchString(line) { |
| return tokenComment.ReplaceAllString(line, "") |
| } |
| |
| return line |
| } |
| |
| 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 someting 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 |
| } |