dockerscript.go 2.5 KB

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