line_parsers.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. package parser
  2. // line parsers are dispatch calls that parse a single unit of text into a
  3. // Node object which contains the whole statement. Dockerfiles have varied
  4. // (but not usually unique, see ONBUILD for a unique example) parsing rules
  5. // per-command, and these unify the processing in a way that makes it
  6. // manageable.
  7. import (
  8. "encoding/json"
  9. "strconv"
  10. "strings"
  11. )
  12. // ignore the current argument. This will still leave a command parsed, but
  13. // will not incorporate the arguments into the ast.
  14. func parseIgnore(rest string) (*Node, error) {
  15. return blankNode(), nil
  16. }
  17. // used for onbuild. Could potentially be used for anything that represents a
  18. // statement with sub-statements.
  19. //
  20. // ONBUILD RUN foo bar -> (onbuild (run foo bar))
  21. //
  22. func parseSubCommand(rest string) (*Node, error) {
  23. _, child, err := parseLine(rest)
  24. if err != nil {
  25. return nil, err
  26. }
  27. return &Node{Children: []*Node{child}}, nil
  28. }
  29. // parse environment like statements. Note that this does *not* handle
  30. // variable interpolation, which will be handled in the evaluator.
  31. func parseEnv(rest string) (*Node, error) {
  32. node := blankNode()
  33. rootnode := node
  34. strs := TOKEN_WHITESPACE.Split(rest, 2)
  35. node.Value = strs[0]
  36. node.Next = blankNode()
  37. node.Next.Value = strs[1]
  38. return rootnode, nil
  39. }
  40. // parses a whitespace-delimited set of arguments. The result is effectively a
  41. // linked list of string arguments.
  42. func parseStringsWhitespaceDelimited(rest string) (*Node, error) {
  43. node := blankNode()
  44. rootnode := node
  45. prevnode := node
  46. for _, str := range TOKEN_WHITESPACE.Split(rest, -1) { // use regexp
  47. prevnode = node
  48. node.Value = str
  49. node.Next = blankNode()
  50. node = node.Next
  51. }
  52. // XXX to get around regexp.Split *always* providing an empty string at the
  53. // end due to how our loop is constructed, nil out the last node in the
  54. // chain.
  55. prevnode.Next = nil
  56. return rootnode, nil
  57. }
  58. // parsestring just wraps the string in quotes and returns a working node.
  59. func parseString(rest string) (*Node, error) {
  60. return &Node{rest, nil, nil}, nil
  61. }
  62. // parseJSON converts JSON arrays to an AST.
  63. func parseJSON(rest string) (*Node, error) {
  64. var (
  65. myJson []interface{}
  66. next = blankNode()
  67. orignext = next
  68. prevnode = next
  69. )
  70. if err := json.Unmarshal([]byte(rest), &myJson); err != nil {
  71. return nil, err
  72. }
  73. for _, str := range myJson {
  74. switch str.(type) {
  75. case float64:
  76. str = strconv.FormatFloat(str.(float64), 'G', -1, 64)
  77. }
  78. next.Value = str.(string)
  79. next.Next = blankNode()
  80. prevnode = next
  81. next = next.Next
  82. }
  83. prevnode.Next = nil
  84. return orignext, nil
  85. }
  86. // parseMaybeJSON determines if the argument appears to be a JSON array. If
  87. // so, passes to parseJSON; if not, quotes the result and returns a single
  88. // node.
  89. func parseMaybeJSON(rest string) (*Node, error) {
  90. rest = strings.TrimSpace(rest)
  91. if strings.HasPrefix(rest, "[") {
  92. node, err := parseJSON(rest)
  93. if err == nil {
  94. return node, nil
  95. }
  96. }
  97. node := blankNode()
  98. node.Value = rest
  99. return node, nil
  100. }