Browse code

Move directive out of globals

Signed-off-by: John Howard <jhoward@microsoft.com>
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
(cherry picked from commit 755be795b4e48b3eadcdf1427bf9731b0e97bed1)
Signed-off-by: Tibor Vass <tibor@docker.com>

John Howard authored on 2016/06/28 05:20:47
Showing 9 changed files
... ...
@@ -71,6 +71,7 @@ type Builder struct {
71 71
 	disableCommit    bool
72 72
 	cacheBusted      bool
73 73
 	allowedBuildArgs map[string]bool // list of build-time args that are allowed for expansion/substitution and passing to commands in 'run'.
74
+	directive        parser.Directive
74 75
 
75 76
 	// TODO: remove once docker.Commit can receive a tag
76 77
 	id string
... ...
@@ -130,9 +131,15 @@ func NewBuilder(clientCtx context.Context, config *types.ImageBuildOptions, back
130 130
 		tmpContainers:    map[string]struct{}{},
131 131
 		id:               stringid.GenerateNonCryptoID(),
132 132
 		allowedBuildArgs: make(map[string]bool),
133
+		directive: parser.Directive{
134
+			EscapeSeen:           false,
135
+			LookingForDirectives: true,
136
+		},
133 137
 	}
138
+	parser.SetEscapeToken(parser.DefaultEscapeToken, &b.directive) // Assume the default token for escape
139
+
134 140
 	if dockerfile != nil {
135
-		b.dockerfile, err = parser.Parse(dockerfile)
141
+		b.dockerfile, err = parser.Parse(dockerfile, &b.directive)
136 142
 		if err != nil {
137 143
 			return nil, err
138 144
 		}
... ...
@@ -218,7 +225,7 @@ func (b *Builder) build(stdout io.Writer, stderr io.Writer, out io.Writer) (stri
218 218
 		for k, v := range b.options.Labels {
219 219
 			line += fmt.Sprintf("%q=%q ", k, v)
220 220
 		}
221
-		_, node, err := parser.ParseLine(line)
221
+		_, node, err := parser.ParseLine(line, &b.directive)
222 222
 		if err != nil {
223 223
 			return "", err
224 224
 		}
... ...
@@ -291,7 +298,12 @@ func (b *Builder) Cancel() {
291 291
 //
292 292
 // TODO: Remove?
293 293
 func BuildFromConfig(config *container.Config, changes []string) (*container.Config, error) {
294
-	ast, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")))
294
+	b, err := NewBuilder(context.Background(), nil, nil, nil, nil)
295
+	if err != nil {
296
+		return nil, err
297
+	}
298
+
299
+	ast, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")), &b.directive)
295 300
 	if err != nil {
296 301
 		return nil, err
297 302
 	}
... ...
@@ -303,10 +315,6 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con
303 303
 		}
304 304
 	}
305 305
 
306
-	b, err := NewBuilder(context.Background(), nil, nil, nil, nil)
307
-	if err != nil {
308
-		return nil, err
309
-	}
310 306
 	b.runConfig = config
311 307
 	b.Stdout = ioutil.Discard
312 308
 	b.Stderr = ioutil.Discard
... ...
@@ -173,7 +173,9 @@ func executeTestCase(t *testing.T, testCase dispatchTestCase) {
173 173
 	}()
174 174
 
175 175
 	r := strings.NewReader(testCase.dockerfile)
176
-	n, err := parser.Parse(r)
176
+	d := parser.Directive{}
177
+	parser.SetEscapeToken(parser.DefaultEscapeToken, &d)
178
+	n, err := parser.Parse(r, &d)
177 179
 
178 180
 	if err != nil {
179 181
 		t.Fatalf("Error when parsing Dockerfile: %s", err)
... ...
@@ -427,7 +427,7 @@ func (b *Builder) processImageFrom(img builder.Image) error {
427 427
 
428 428
 	// parse the ONBUILD triggers by invoking the parser
429 429
 	for _, step := range onBuildTriggers {
430
-		ast, err := parser.Parse(strings.NewReader(step))
430
+		ast, err := parser.Parse(strings.NewReader(step), &b.directive)
431 431
 		if err != nil {
432 432
 			return err
433 433
 		}
... ...
@@ -648,7 +648,7 @@ func (b *Builder) parseDockerfile() error {
648 648
 			return fmt.Errorf("The Dockerfile (%s) cannot be empty", b.options.Dockerfile)
649 649
 		}
650 650
 	}
651
-	b.dockerfile, err = parser.Parse(f)
651
+	b.dockerfile, err = parser.Parse(f, &b.directive)
652 652
 	if err != nil {
653 653
 		return err
654 654
 	}
... ...
@@ -22,7 +22,10 @@ func main() {
22 22
 			panic(err)
23 23
 		}
24 24
 
25
-		ast, err := parser.Parse(f)
25
+		d := parser.Directive{LookingForDirectives: true}
26
+		parser.SetEscapeToken(parser.DefaultEscapeToken, &d)
27
+
28
+		ast, err := parser.Parse(f, &d)
26 29
 		if err != nil {
27 30
 			panic(err)
28 31
 		} else {
... ...
@@ -28,7 +28,10 @@ var validJSONArraysOfStrings = map[string][]string{
28 28
 
29 29
 func TestJSONArraysOfStrings(t *testing.T) {
30 30
 	for json, expected := range validJSONArraysOfStrings {
31
-		if node, _, err := parseJSON(json); err != nil {
31
+		d := Directive{}
32
+		SetEscapeToken(DefaultEscapeToken, &d)
33
+
34
+		if node, _, err := parseJSON(json, &d); err != nil {
32 35
 			t.Fatalf("%q should be a valid JSON array of strings, but wasn't! (err: %q)", json, err)
33 36
 		} else {
34 37
 			i := 0
... ...
@@ -48,7 +51,10 @@ func TestJSONArraysOfStrings(t *testing.T) {
48 48
 		}
49 49
 	}
50 50
 	for _, json := range invalidJSONArraysOfStrings {
51
-		if _, _, err := parseJSON(json); err != errDockerfileNotStringArray {
51
+		d := Directive{}
52
+		SetEscapeToken(DefaultEscapeToken, &d)
53
+
54
+		if _, _, err := parseJSON(json, &d); err != errDockerfileNotStringArray {
52 55
 			t.Fatalf("%q should be an invalid JSON array of strings, but wasn't!", json)
53 56
 		}
54 57
 	}
... ...
@@ -21,7 +21,7 @@ var (
21 21
 
22 22
 // ignore the current argument. This will still leave a command parsed, but
23 23
 // will not incorporate the arguments into the ast.
24
-func parseIgnore(rest string) (*Node, map[string]bool, error) {
24
+func parseIgnore(rest string, d *Directive) (*Node, map[string]bool, error) {
25 25
 	return &Node{}, nil, nil
26 26
 }
27 27
 
... ...
@@ -30,12 +30,12 @@ func parseIgnore(rest string) (*Node, map[string]bool, error) {
30 30
 //
31 31
 // ONBUILD RUN foo bar -> (onbuild (run foo bar))
32 32
 //
33
-func parseSubCommand(rest string) (*Node, map[string]bool, error) {
33
+func parseSubCommand(rest string, d *Directive) (*Node, map[string]bool, error) {
34 34
 	if rest == "" {
35 35
 		return nil, nil, nil
36 36
 	}
37 37
 
38
-	_, child, err := ParseLine(rest)
38
+	_, child, err := ParseLine(rest, d)
39 39
 	if err != nil {
40 40
 		return nil, nil, err
41 41
 	}
... ...
@@ -46,7 +46,7 @@ func parseSubCommand(rest string) (*Node, map[string]bool, error) {
46 46
 // helper to parse words (i.e space delimited or quoted strings) in a statement.
47 47
 // The quotes are preserved as part of this function and they are stripped later
48 48
 // as part of processWords().
49
-func parseWords(rest string) []string {
49
+func parseWords(rest string, d *Directive) []string {
50 50
 	const (
51 51
 		inSpaces = iota // looking for start of a word
52 52
 		inWord
... ...
@@ -96,7 +96,7 @@ func parseWords(rest string) []string {
96 96
 				blankOK = true
97 97
 				phase = inQuote
98 98
 			}
99
-			if ch == tokenEscape {
99
+			if ch == d.EscapeToken {
100 100
 				if pos+chWidth == len(rest) {
101 101
 					continue // just skip an escape token at end of line
102 102
 				}
... ...
@@ -115,7 +115,7 @@ func parseWords(rest string) []string {
115 115
 				phase = inWord
116 116
 			}
117 117
 			// The escape token is special except for ' quotes - can't escape anything for '
118
-			if ch == tokenEscape && quote != '\'' {
118
+			if ch == d.EscapeToken && quote != '\'' {
119 119
 				if pos+chWidth == len(rest) {
120 120
 					phase = inWord
121 121
 					continue // just skip the escape token at end
... ...
@@ -133,14 +133,14 @@ func parseWords(rest string) []string {
133 133
 
134 134
 // parse environment like statements. Note that this does *not* handle
135 135
 // variable interpolation, which will be handled in the evaluator.
136
-func parseNameVal(rest string, key string) (*Node, map[string]bool, error) {
136
+func parseNameVal(rest string, key string, d *Directive) (*Node, map[string]bool, error) {
137 137
 	// This is kind of tricky because we need to support the old
138 138
 	// variant:   KEY name value
139 139
 	// as well as the new one:    KEY name=value ...
140 140
 	// The trigger to know which one is being used will be whether we hit
141 141
 	// a space or = first.  space ==> old, "=" ==> new
142 142
 
143
-	words := parseWords(rest)
143
+	words := parseWords(rest, d)
144 144
 	if len(words) == 0 {
145 145
 		return nil, nil, nil
146 146
 	}
... ...
@@ -187,12 +187,12 @@ func parseNameVal(rest string, key string) (*Node, map[string]bool, error) {
187 187
 	return rootnode, nil, nil
188 188
 }
189 189
 
190
-func parseEnv(rest string) (*Node, map[string]bool, error) {
191
-	return parseNameVal(rest, "ENV")
190
+func parseEnv(rest string, d *Directive) (*Node, map[string]bool, error) {
191
+	return parseNameVal(rest, "ENV", d)
192 192
 }
193 193
 
194
-func parseLabel(rest string) (*Node, map[string]bool, error) {
195
-	return parseNameVal(rest, "LABEL")
194
+func parseLabel(rest string, d *Directive) (*Node, map[string]bool, error) {
195
+	return parseNameVal(rest, "LABEL", d)
196 196
 }
197 197
 
198 198
 // parses a statement containing one or more keyword definition(s) and/or
... ...
@@ -203,8 +203,8 @@ func parseLabel(rest string) (*Node, map[string]bool, error) {
203 203
 // In addition, a keyword definition alone is of the form `keyword` like `name1`
204 204
 // above. And the assignments `name2=` and `name3=""` are equivalent and
205 205
 // assign an empty value to the respective keywords.
206
-func parseNameOrNameVal(rest string) (*Node, map[string]bool, error) {
207
-	words := parseWords(rest)
206
+func parseNameOrNameVal(rest string, d *Directive) (*Node, map[string]bool, error) {
207
+	words := parseWords(rest, d)
208 208
 	if len(words) == 0 {
209 209
 		return nil, nil, nil
210 210
 	}
... ...
@@ -229,7 +229,7 @@ func parseNameOrNameVal(rest string) (*Node, map[string]bool, error) {
229 229
 
230 230
 // parses a whitespace-delimited set of arguments. The result is effectively a
231 231
 // linked list of string arguments.
232
-func parseStringsWhitespaceDelimited(rest string) (*Node, map[string]bool, error) {
232
+func parseStringsWhitespaceDelimited(rest string, d *Directive) (*Node, map[string]bool, error) {
233 233
 	if rest == "" {
234 234
 		return nil, nil, nil
235 235
 	}
... ...
@@ -253,7 +253,7 @@ func parseStringsWhitespaceDelimited(rest string) (*Node, map[string]bool, error
253 253
 }
254 254
 
255 255
 // parsestring just wraps the string in quotes and returns a working node.
256
-func parseString(rest string) (*Node, map[string]bool, error) {
256
+func parseString(rest string, d *Directive) (*Node, map[string]bool, error) {
257 257
 	if rest == "" {
258 258
 		return nil, nil, nil
259 259
 	}
... ...
@@ -263,7 +263,7 @@ func parseString(rest string) (*Node, map[string]bool, error) {
263 263
 }
264 264
 
265 265
 // parseJSON converts JSON arrays to an AST.
266
-func parseJSON(rest string) (*Node, map[string]bool, error) {
266
+func parseJSON(rest string, d *Directive) (*Node, map[string]bool, error) {
267 267
 	rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
268 268
 	if !strings.HasPrefix(rest, "[") {
269 269
 		return nil, nil, fmt.Errorf(`Error parsing "%s" as a JSON array`, rest)
... ...
@@ -296,12 +296,12 @@ func parseJSON(rest string) (*Node, map[string]bool, error) {
296 296
 // parseMaybeJSON determines if the argument appears to be a JSON array. If
297 297
 // so, passes to parseJSON; if not, quotes the result and returns a single
298 298
 // node.
299
-func parseMaybeJSON(rest string) (*Node, map[string]bool, error) {
299
+func parseMaybeJSON(rest string, d *Directive) (*Node, map[string]bool, error) {
300 300
 	if rest == "" {
301 301
 		return nil, nil, nil
302 302
 	}
303 303
 
304
-	node, attrs, err := parseJSON(rest)
304
+	node, attrs, err := parseJSON(rest, d)
305 305
 
306 306
 	if err == nil {
307 307
 		return node, attrs, nil
... ...
@@ -318,8 +318,8 @@ func parseMaybeJSON(rest string) (*Node, map[string]bool, error) {
318 318
 // parseMaybeJSONToList determines if the argument appears to be a JSON array. If
319 319
 // so, passes to parseJSON; if not, attempts to parse it as a whitespace
320 320
 // delimited string.
321
-func parseMaybeJSONToList(rest string) (*Node, map[string]bool, error) {
322
-	node, attrs, err := parseJSON(rest)
321
+func parseMaybeJSONToList(rest string, d *Directive) (*Node, map[string]bool, error) {
322
+	node, attrs, err := parseJSON(rest, d)
323 323
 
324 324
 	if err == nil {
325 325
 		return node, attrs, nil
... ...
@@ -328,11 +328,11 @@ func parseMaybeJSONToList(rest string) (*Node, map[string]bool, error) {
328 328
 		return nil, nil, err
329 329
 	}
330 330
 
331
-	return parseStringsWhitespaceDelimited(rest)
331
+	return parseStringsWhitespaceDelimited(rest, d)
332 332
 }
333 333
 
334 334
 // The HEALTHCHECK command is like parseMaybeJSON, but has an extra type argument.
335
-func parseHealthConfig(rest string) (*Node, map[string]bool, error) {
335
+func parseHealthConfig(rest string, d *Directive) (*Node, map[string]bool, error) {
336 336
 	// Find end of first argument
337 337
 	var sep int
338 338
 	for ; sep < len(rest); sep++ {
... ...
@@ -352,7 +352,7 @@ func parseHealthConfig(rest string) (*Node, map[string]bool, error) {
352 352
 	}
353 353
 
354 354
 	typ := rest[:sep]
355
-	cmd, attrs, err := parseMaybeJSON(rest[next:])
355
+	cmd, attrs, err := parseMaybeJSON(rest[next:], d)
356 356
 	if err != nil {
357 357
 		return nil, nil, err
358 358
 	}
... ...
@@ -36,26 +36,32 @@ type Node struct {
36 36
 	EndLine    int             // the line in the original dockerfile where the node ends
37 37
 }
38 38
 
39
-const defaultTokenEscape = "\\"
39
+// Directive is the structure used during a build run to hold the state of
40
+// parsing directives.
41
+type Directive struct {
42
+	EscapeToken           rune           // Current escape token
43
+	LineContinuationRegex *regexp.Regexp // Current line contination regex
44
+	LookingForDirectives  bool           // Whether we are currently looking for directives
45
+	EscapeSeen            bool           // Whether the escape directive has been seen
46
+}
40 47
 
41 48
 var (
42
-	dispatch              map[string]func(string) (*Node, map[string]bool, error)
43
-	tokenWhitespace       = regexp.MustCompile(`[\t\v\f\r ]+`)
44
-	tokenLineContinuation *regexp.Regexp
45
-	tokenEscape           = rune(defaultTokenEscape[0])
46
-	tokenEscapeCommand    = regexp.MustCompile(`^#[ \t]*escape[ \t]*=[ \t]*(?P<escapechar>.).*$`)
47
-	tokenComment          = regexp.MustCompile(`^#.*$`)
48
-	lookingForDirectives  bool
49
-	directiveEscapeSeen   bool
49
+	dispatch           map[string]func(string, *Directive) (*Node, map[string]bool, error)
50
+	tokenWhitespace    = regexp.MustCompile(`[\t\v\f\r ]+`)
51
+	tokenEscapeCommand = regexp.MustCompile(`^#[ \t]*escape[ \t]*=[ \t]*(?P<escapechar>.).*$`)
52
+	tokenComment       = regexp.MustCompile(`^#.*$`)
50 53
 )
51 54
 
52
-// setTokenEscape sets the default token for escaping characters in a Dockerfile.
53
-func setTokenEscape(s string) error {
55
+// DefaultEscapeToken is the default escape token
56
+const DefaultEscapeToken = "\\"
57
+
58
+// SetEscapeToken sets the default token for escaping characters in a Dockerfile.
59
+func SetEscapeToken(s string, d *Directive) error {
54 60
 	if s != "`" && s != "\\" {
55 61
 		return fmt.Errorf("invalid ESCAPE '%s'. Must be ` or \\", s)
56 62
 	}
57
-	tokenEscape = rune(s[0])
58
-	tokenLineContinuation = regexp.MustCompile(`\` + s + `[ \t]*$`)
63
+	d.EscapeToken = rune(s[0])
64
+	d.LineContinuationRegex = regexp.MustCompile(`\` + s + `[ \t]*$`)
59 65
 	return nil
60 66
 }
61 67
 
... ...
@@ -66,7 +72,7 @@ func init() {
66 66
 	// reformulating the arguments according to the rules in the parser
67 67
 	// functions. Errors are propagated up by Parse() and the resulting AST can
68 68
 	// be incorporated directly into the existing AST as a next.
69
-	dispatch = map[string]func(string) (*Node, map[string]bool, error){
69
+	dispatch = map[string]func(string, *Directive) (*Node, map[string]bool, error){
70 70
 		command.Add:         parseMaybeJSONToList,
71 71
 		command.Arg:         parseNameOrNameVal,
72 72
 		command.Cmd:         parseMaybeJSON,
... ...
@@ -89,36 +95,35 @@ func init() {
89 89
 }
90 90
 
91 91
 // ParseLine parse a line and return the remainder.
92
-func ParseLine(line string) (string, *Node, error) {
93
-
92
+func ParseLine(line string, d *Directive) (string, *Node, error) {
94 93
 	// Handle the parser directive '# escape=<char>. Parser directives must precede
95 94
 	// any builder instruction or other comments, and cannot be repeated.
96
-	if lookingForDirectives {
95
+	if d.LookingForDirectives {
97 96
 		tecMatch := tokenEscapeCommand.FindStringSubmatch(strings.ToLower(line))
98 97
 		if len(tecMatch) > 0 {
99
-			if directiveEscapeSeen == true {
98
+			if d.EscapeSeen == true {
100 99
 				return "", nil, fmt.Errorf("only one escape parser directive can be used")
101 100
 			}
102 101
 			for i, n := range tokenEscapeCommand.SubexpNames() {
103 102
 				if n == "escapechar" {
104
-					if err := setTokenEscape(tecMatch[i]); err != nil {
103
+					if err := SetEscapeToken(tecMatch[i], d); err != nil {
105 104
 						return "", nil, err
106 105
 					}
107
-					directiveEscapeSeen = true
106
+					d.EscapeSeen = true
108 107
 					return "", nil, nil
109 108
 				}
110 109
 			}
111 110
 		}
112 111
 	}
113 112
 
114
-	lookingForDirectives = false
113
+	d.LookingForDirectives = false
115 114
 
116 115
 	if line = stripComments(line); line == "" {
117 116
 		return "", nil, nil
118 117
 	}
119 118
 
120
-	if tokenLineContinuation.MatchString(line) {
121
-		line = tokenLineContinuation.ReplaceAllString(line, "")
119
+	if d.LineContinuationRegex.MatchString(line) {
120
+		line = d.LineContinuationRegex.ReplaceAllString(line, "")
122 121
 		return line, nil, nil
123 122
 	}
124 123
 
... ...
@@ -130,7 +135,7 @@ func ParseLine(line string) (string, *Node, error) {
130 130
 	node := &Node{}
131 131
 	node.Value = cmd
132 132
 
133
-	sexp, attrs, err := fullDispatch(cmd, args)
133
+	sexp, attrs, err := fullDispatch(cmd, args, d)
134 134
 	if err != nil {
135 135
 		return "", nil, err
136 136
 	}
... ...
@@ -145,10 +150,7 @@ func ParseLine(line string) (string, *Node, error) {
145 145
 
146 146
 // Parse is the main parse routine.
147 147
 // It handles an io.ReadWriteCloser and returns the root of the AST.
148
-func Parse(rwc io.Reader) (*Node, error) {
149
-	directiveEscapeSeen = false
150
-	lookingForDirectives = true
151
-	setTokenEscape(defaultTokenEscape) // Assume the default token for escape
148
+func Parse(rwc io.Reader, d *Directive) (*Node, error) {
152 149
 	currentLine := 0
153 150
 	root := &Node{}
154 151
 	root.StartLine = -1
... ...
@@ -163,7 +165,7 @@ func Parse(rwc io.Reader) (*Node, error) {
163 163
 		}
164 164
 		scannedLine := strings.TrimLeftFunc(string(scannedBytes), unicode.IsSpace)
165 165
 		currentLine++
166
-		line, child, err := ParseLine(scannedLine)
166
+		line, child, err := ParseLine(scannedLine, d)
167 167
 		if err != nil {
168 168
 			return nil, err
169 169
 		}
... ...
@@ -178,7 +180,7 @@ func Parse(rwc io.Reader) (*Node, error) {
178 178
 					continue
179 179
 				}
180 180
 
181
-				line, child, err = ParseLine(line + newline)
181
+				line, child, err = ParseLine(line+newline, d)
182 182
 				if err != nil {
183 183
 					return nil, err
184 184
 				}
... ...
@@ -188,7 +190,7 @@ func Parse(rwc io.Reader) (*Node, error) {
188 188
 				}
189 189
 			}
190 190
 			if child == nil && line != "" {
191
-				_, child, err = ParseLine(line)
191
+				_, child, err = ParseLine(line, d)
192 192
 				if err != nil {
193 193
 					return nil, err
194 194
 				}
... ...
@@ -39,7 +39,9 @@ func TestTestNegative(t *testing.T) {
39 39
 			t.Fatalf("Dockerfile missing for %s: %v", dir, err)
40 40
 		}
41 41
 
42
-		_, err = Parse(df)
42
+		d := Directive{LookingForDirectives: true}
43
+		SetEscapeToken(DefaultEscapeToken, &d)
44
+		_, err = Parse(df, &d)
43 45
 		if err == nil {
44 46
 			t.Fatalf("No error parsing broken dockerfile for %s", dir)
45 47
 		}
... ...
@@ -59,7 +61,9 @@ func TestTestData(t *testing.T) {
59 59
 		}
60 60
 		defer df.Close()
61 61
 
62
-		ast, err := Parse(df)
62
+		d := Directive{LookingForDirectives: true}
63
+		SetEscapeToken(DefaultEscapeToken, &d)
64
+		ast, err := Parse(df, &d)
63 65
 		if err != nil {
64 66
 			t.Fatalf("Error parsing %s's dockerfile: %v", dir, err)
65 67
 		}
... ...
@@ -119,7 +123,9 @@ func TestParseWords(t *testing.T) {
119 119
 	}
120 120
 
121 121
 	for _, test := range tests {
122
-		words := parseWords(test["input"][0])
122
+		d := Directive{LookingForDirectives: true}
123
+		SetEscapeToken(DefaultEscapeToken, &d)
124
+		words := parseWords(test["input"][0], &d)
123 125
 		if len(words) != len(test["expect"]) {
124 126
 			t.Fatalf("length check failed. input: %v, expect: %q, output: %q", test["input"][0], test["expect"], words)
125 127
 		}
... ...
@@ -138,7 +144,9 @@ func TestLineInformation(t *testing.T) {
138 138
 	}
139 139
 	defer df.Close()
140 140
 
141
-	ast, err := Parse(df)
141
+	d := Directive{LookingForDirectives: true}
142
+	SetEscapeToken(DefaultEscapeToken, &d)
143
+	ast, err := Parse(df, &d)
142 144
 	if err != nil {
143 145
 		t.Fatalf("Error parsing dockerfile %s: %v", testFileLineInfo, err)
144 146
 	}
... ...
@@ -36,7 +36,7 @@ func (node *Node) Dump() string {
36 36
 
37 37
 // performs the dispatch based on the two primal strings, cmd and args. Please
38 38
 // look at the dispatch table in parser.go to see how these dispatchers work.
39
-func fullDispatch(cmd, args string) (*Node, map[string]bool, error) {
39
+func fullDispatch(cmd, args string, d *Directive) (*Node, map[string]bool, error) {
40 40
 	fn := dispatch[cmd]
41 41
 
42 42
 	// Ignore invalid Dockerfile instructions
... ...
@@ -44,7 +44,7 @@ func fullDispatch(cmd, args string) (*Node, map[string]bool, error) {
44 44
 		fn = parseIgnore
45 45
 	}
46 46
 
47
-	sexp, attrs, err := fn(args)
47
+	sexp, attrs, err := fn(args, d)
48 48
 	if err != nil {
49 49
 		return nil, nil, err
50 50
 	}