package dockerscript import ( "fmt" "github.com/dotcloud/docker/pkg/dockerscript/scanner" "io" "strings" ) type Command struct { Args []string Children []*Command Background bool } type Scanner struct { scanner.Scanner commentLine bool } func Parse(src io.Reader) ([]*Command, error) { s := &Scanner{} s.Init(src) s.Whitespace = 1<<'\t' | 1<<' ' s.Mode = scanner.ScanStrings | scanner.ScanRawStrings | scanner.ScanIdents expr, err := parse(s, "") if err != nil { return nil, fmt.Errorf("line %d:%d: %v\n", s.Pos().Line, s.Pos().Column, err) } return expr, nil } func (cmd *Command) subString(depth int) string { var prefix string for i := 0; i < depth; i++ { prefix += " " } s := prefix + strings.Join(cmd.Args, ", ") if len(cmd.Children) > 0 { s += " {\n" for _, subcmd := range cmd.Children { s += subcmd.subString(depth + 1) } s += prefix + "}" } s += "\n" return s } func (cmd *Command) String() string { return cmd.subString(0) } func parseArgs(s *Scanner) ([]string, rune, error) { var parseError error // FIXME: we overwrite previously set error s.Error = func(s *scanner.Scanner, msg string) { parseError = fmt.Errorf(msg) // parseError = fmt.Errorf("line %d:%d: %s\n", s.Pos().Line, s.Pos().Column, msg) } var args []string tok := s.Scan() for tok != scanner.EOF { if parseError != nil { return args, tok, parseError } text := s.TokenText() // Toggle line comment if strings.HasPrefix(text, "#") { s.commentLine = true } else if text == "\n" || text == "\r" { s.commentLine = false return args, tok, nil } if !s.commentLine { if text == "{" || text == "}" || text == "\n" || text == "\r" || text == ";" || text == "&" { return args, tok, nil } args = append(args, text) } tok = s.Scan() } return args, tok, nil } func parse(s *Scanner, opener string) (expr []*Command, err error) { /* defer func() { fmt.Printf("parse() returned %d commands:\n", len(expr)) for _, c := range expr { fmt.Printf("\t----> %s\n", c) } }() */ for { args, tok, err := parseArgs(s) if err != nil { return nil, err } cmd := &Command{Args: args} afterArgs := s.TokenText() if afterArgs == "{" { children, err := parse(s, "{") if err != nil { return nil, err } cmd.Children = children } else if afterArgs == "}" && opener != "{" { return nil, fmt.Errorf("unexpected end of block '}'") } else if afterArgs == "&" { cmd.Background = true } if len(cmd.Args) > 0 || len(cmd.Children) > 0 { expr = append(expr, cmd) } if tok == scanner.EOF || afterArgs == "}" { break } } return expr, nil }