@@ -157,47 +157,61 @@ extension TemplateByteScanner {
157157 case Data ( bytes: [ . forwardSlash, . forwardSlash] ) , Data ( bytes: [ . forwardSlash, . asterisk] ) :
158158 break
159159 default :
160- throw TemplateError . parse ( reason: " Invalid tag name " , source : makeSource ( using: start) )
160+ throw TemplateError . parse ( reason: " Invalid tag name " , template : makeSource ( using: start) , source : . capture ( ) )
161161 }
162162 }
163163
164164 // Extract the tag params.
165165 let params : [ TemplateSyntax ]
166166 guard let name = String ( data: id, encoding: . utf8) else {
167- throw TemplateError . parse ( reason: " Invalid UTF-8 string " , source : makeSource ( using: start) )
167+ throw TemplateError . parse ( reason: " Invalid UTF-8 string " , template : makeSource ( using: start) , source : . capture ( ) )
168168 }
169169
170170 switch name {
171171 case " for " :
172172 try expect ( . leftParenthesis)
173+ if peek ( ) == . space {
174+ throw TemplateError . parse (
175+ reason: " Whitespace not allowed before key in 'for' tag. " ,
176+ template: makeSource ( using: start) ,
177+ source: . capture( )
178+ )
179+ }
173180 let key = try extractIdentifier ( )
174181 try expect ( . space)
175182 try expect ( . i)
176183 try expect ( . n)
177184 try expect ( . space)
178185 guard let val = try extractParameter ( ) else {
179- throw TemplateError . parse ( reason: " Parameter required after `in` in for-loop " , source : makeSource ( using: start) )
186+ throw TemplateError . parse ( reason: " Parameter required after `in` in for-loop " , template : makeSource ( using: start) , source : . capture ( ) )
180187 }
181188
182189 switch val. type {
183190 case . identifier, . tag:
184191 break
185192 default :
186- throw TemplateError . parse ( reason: " Identifier or tag required " , source : makeSource ( using: start) )
193+ throw TemplateError . parse ( reason: " Identifier or tag required " , template : makeSource ( using: start) , source : . capture ( ) )
187194 }
188195
196+ if peek ( by: - 1 ) == . space {
197+ throw TemplateError . parse (
198+ reason: " Whitespace not allowed after value in 'for' tag. " ,
199+ template: makeSource ( using: start) ,
200+ source: . capture( )
201+ )
202+ }
189203 try expect ( . rightParenthesis)
190204
191205 guard case . identifier( let name) = key. type else {
192- throw TemplateError . parse ( reason: " Invalid key type in for-loop " , source : makeSource ( using: start) )
206+ throw TemplateError . parse ( reason: " Invalid key type in for-loop " , template : makeSource ( using: start) , source : . capture ( ) )
193207 }
194208
195209 guard name. path. count == 1 else {
196- throw TemplateError . parse ( reason: " One key required in for-loop " , source : makeSource ( using: start) )
210+ throw TemplateError . parse ( reason: " One key required in for-loop " , template : makeSource ( using: start) , source : . capture ( ) )
197211 }
198212
199213 guard let data = name. path [ 0 ] . stringValue. data ( using: . utf8) else {
200- throw TemplateError . parse ( reason: " Invalid UTF-8 string " , source : makeSource ( using: start) )
214+ throw TemplateError . parse ( reason: " Invalid UTF-8 string " , template : makeSource ( using: start) , source : . capture ( ) )
201215 }
202216
203217 let raw = TemplateSyntax (
@@ -272,7 +286,7 @@ extension TemplateByteScanner {
272286 switch name {
273287 case " if " :
274288 guard params.count == 1 else {
275- throw TemplateError.parse(reason: " One parameter required for if tag. " , source : makeSource(using: start))
289+ throw TemplateError.parse(reason: " One parameter required for if tag. " , template : makeSource(using: start), source: .capture( ))
276290 }
277291
278292 let cond = try TemplateConditional(
@@ -283,13 +297,13 @@ extension TemplateByteScanner {
283297 type = .conditional(cond)
284298 case " embed" :
285299 guard params.count == 1 else {
286- throw TemplateError.parse(reason: " One parameter required for embed tag. " , source : makeSource(using: start))
300+ throw TemplateError.parse(reason: " One parameter required for embed tag. " , template : makeSource(using: start), source: .capture( ))
287301 }
288302 let embed = TemplateEmbed(path: params[0])
289303 type = .embed(embed)
290304 case " for" :
291305 guard params.count == 2 else {
292- throw TemplateError.parse(reason: " Two parameters required for for- loop. " , source : makeSource(using: start))
306+ throw TemplateError.parse(reason: " Two parameters required for for- loop. " , template : makeSource(using: start), source: .capture( ))
293307 }
294308 let iterator = TemplateIterator(key: params[1], data: params[0], body: body ?? [])
295309 type = .iterator(iterator)
@@ -392,7 +406,7 @@ extension TemplateByteScanner {
392406 try extractSpaces ( )
393407
394408 guard params. count == 1 else {
395- throw TemplateError . parse ( reason: " One parameter required for else tag. " , source : makeSource ( using: start) )
409+ throw TemplateError . parse ( reason: " One parameter required for else tag. " , template : makeSource ( using: start) , source : . capture ( ) )
396410 }
397411
398412 return try TemplateConditional (
@@ -568,16 +582,16 @@ extension TemplateByteScanner {
568582
569583 let bytes = data [ start. offset..< offset]
570584 guard let string = String ( data: bytes, encoding: . utf8) else {
571- throw TemplateError . parse ( reason: " Invalid UTF8 string " , source : makeSource ( using: start) )
585+ throw TemplateError . parse ( reason: " Invalid UTF8 string " , template : makeSource ( using: start) , source : . capture ( ) )
572586 }
573587 if bytes. contains ( . period) {
574588 guard let double = Double ( string) else {
575- throw TemplateError . parse ( reason: " Invalid double " , source : makeSource ( using: start) )
589+ throw TemplateError . parse ( reason: " Invalid double " , template : makeSource ( using: start) , source : . capture ( ) )
576590 }
577591 return . double( double)
578592 } else {
579593 guard let int = Int ( string) else {
580- throw TemplateError . parse ( reason: " Invalid integer " , source : makeSource ( using: start) )
594+ throw TemplateError . parse ( reason: " Invalid integer " , template : makeSource ( using: start) , source : . capture ( ) )
581595 }
582596 return . int( int)
583597 }
@@ -590,7 +604,7 @@ extension TemplateByteScanner {
590604 let start = makeSourceStart ( )
591605
592606 guard let byte = peek ( ) else {
593- throw TemplateError . parse ( reason: " Unexpected EOF " , source : makeSource ( using: start) )
607+ throw TemplateError . parse ( reason: " Unexpected EOF " , template : makeSource ( using: start) , source : . capture ( ) )
594608 }
595609
596610 let kind : TemplateSyntaxType
@@ -609,7 +623,7 @@ extension TemplateByteScanner {
609623 case . exclamation:
610624 try expect ( . exclamation)
611625 guard let param = try extractParameter ( ) else {
612- throw TemplateError . parse ( reason: " Parameter required after not `!` " , source : makeSource ( using: start) )
626+ throw TemplateError . parse ( reason: " Parameter required after not `!` " , template : makeSource ( using: start) , source : . capture ( ) )
613627 }
614628 kind = . expression( . prefix( operator: . not, right: param) )
615629 default :
@@ -687,7 +701,7 @@ extension TemplateByteScanner {
687701 }
688702
689703 guard let right = try extractParameter ( ) else {
690- throw TemplateError . parse ( reason: " Parameter required after infix operator " , source : makeSource ( using: start) )
704+ throw TemplateError . parse ( reason: " Parameter required after infix operator " , template : makeSource ( using: start) , source : . capture ( ) )
691705 }
692706
693707 // FIXME: allow for () grouping and proper PEMDAS
@@ -716,11 +730,13 @@ extension TemplateByteScanner {
716730 let start = makeSourceStart ( )
717731
718732 guard let byte = peek ( ) else {
719- throw TemplateError . parse ( reason: " Unexpected EOF " , source : makeSource ( using: start) )
733+ throw TemplateError . parse ( reason: " Unexpected EOF " , template : makeSource ( using: start) , source : . capture ( ) )
720734 }
721735
722736 guard byte == expect else {
723- throw TemplateError . parse ( reason: " Expected \( expect) got \( byte) " , source: makeSource ( using: start) )
737+ let expectedChar = Character ( Unicode . Scalar. init ( expect) )
738+ let char = Character ( Unicode . Scalar. init ( byte) )
739+ throw TemplateError . parse ( reason: " Expected ' \( expectedChar) ' got ' \( char) ' " , template: makeSource ( using: start) , source: . capture( ) )
724740 }
725741
726742 try requirePop ( )
0 commit comments