dockerscript.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. package dockerscript
  2. import (
  3. "fmt"
  4. "github.com/dotcloud/docker/pkg/dockerscript/scanner"
  5. "io"
  6. "strings"
  7. )
  8. type Command struct {
  9. Args []string
  10. Children []*Command
  11. Background bool
  12. }
  13. type Scanner struct {
  14. scanner.Scanner
  15. commentLine bool
  16. }
  17. func Parse(src io.Reader) ([]*Command, error) {
  18. s := &Scanner{}
  19. s.Init(src)
  20. s.Whitespace = 1<<'\t' | 1<<' '
  21. s.Mode = scanner.ScanStrings | scanner.ScanRawStrings | scanner.ScanIdents
  22. expr, err := parse(s, "")
  23. if err != nil {
  24. return nil, fmt.Errorf("line %d:%d: %v\n", s.Pos().Line, s.Pos().Column, err)
  25. }
  26. return expr, nil
  27. }
  28. func (cmd *Command) subString(depth int) string {
  29. var prefix string
  30. for i := 0; i < depth; i++ {
  31. prefix += " "
  32. }
  33. s := prefix + strings.Join(cmd.Args, ", ")
  34. if len(cmd.Children) > 0 {
  35. s += " {\n"
  36. for _, subcmd := range cmd.Children {
  37. s += subcmd.subString(depth + 1)
  38. }
  39. s += prefix + "}"
  40. }
  41. s += "\n"
  42. return s
  43. }
  44. func (cmd *Command) String() string {
  45. return cmd.subString(0)
  46. }
  47. func parseArgs(s *Scanner) ([]string, rune, error) {
  48. var parseError error
  49. // FIXME: we overwrite previously set error
  50. s.Error = func(s *scanner.Scanner, msg string) {
  51. parseError = fmt.Errorf(msg)
  52. // parseError = fmt.Errorf("line %d:%d: %s\n", s.Pos().Line, s.Pos().Column, msg)
  53. }
  54. var args []string
  55. tok := s.Scan()
  56. for tok != scanner.EOF {
  57. if parseError != nil {
  58. return args, tok, parseError
  59. }
  60. text := s.TokenText()
  61. // Toggle line comment
  62. if strings.HasPrefix(text, "#") {
  63. s.commentLine = true
  64. } else if text == "\n" || text == "\r" {
  65. s.commentLine = false
  66. return args, tok, nil
  67. }
  68. if !s.commentLine {
  69. if text == "{" || text == "}" || text == "\n" || text == "\r" || text == ";" || text == "&" {
  70. return args, tok, nil
  71. }
  72. args = append(args, text)
  73. }
  74. tok = s.Scan()
  75. }
  76. return args, tok, nil
  77. }
  78. func parse(s *Scanner, opener string) (expr []*Command, err error) {
  79. /*
  80. defer func() {
  81. fmt.Printf("parse() returned %d commands:\n", len(expr))
  82. for _, c := range expr {
  83. fmt.Printf("\t----> %s\n", c)
  84. }
  85. }()
  86. */
  87. for {
  88. args, tok, err := parseArgs(s)
  89. if err != nil {
  90. return nil, err
  91. }
  92. cmd := &Command{Args: args}
  93. afterArgs := s.TokenText()
  94. if afterArgs == "{" {
  95. children, err := parse(s, "{")
  96. if err != nil {
  97. return nil, err
  98. }
  99. cmd.Children = children
  100. } else if afterArgs == "}" && opener != "{" {
  101. return nil, fmt.Errorf("unexpected end of block '}'")
  102. } else if afterArgs == "&" {
  103. cmd.Background = true
  104. }
  105. if len(cmd.Args) > 0 || len(cmd.Children) > 0 {
  106. expr = append(expr, cmd)
  107. }
  108. if tok == scanner.EOF || afterArgs == "}" {
  109. break
  110. }
  111. }
  112. return expr, nil
  113. }