@@ -21,6 +21,8 @@ import {
2121 SliceExpression ,
2222 KeywordArgumentExpression ,
2323 TupleLiteral ,
24+ Macro ,
25+ SelectExpression ,
2426} from "./ast" ;
2527
2628/**
@@ -90,6 +92,14 @@ export function parse(tokens: Token[]): Program {
9092 expect ( TOKEN_TYPES . CloseStatement , "Expected %} token" ) ;
9193 break ;
9294
95+ case TOKEN_TYPES . Macro :
96+ ++ current ;
97+ result = parseMacroStatement ( ) ;
98+ expect ( TOKEN_TYPES . OpenStatement , "Expected {% token" ) ;
99+ expect ( TOKEN_TYPES . EndMacro , "Expected endmacro token" ) ;
100+ expect ( TOKEN_TYPES . CloseStatement , "Expected %} token" ) ;
101+ break ;
102+
93103 case TOKEN_TYPES . For :
94104 ++ current ;
95105 result = parseForStatement ( ) ;
@@ -173,6 +183,25 @@ export function parse(tokens: Token[]): Program {
173183 return new If ( test , body , alternate ) ;
174184 }
175185
186+ function parseMacroStatement ( ) : Macro {
187+ const name = parsePrimaryExpression ( ) ;
188+ if ( name . type !== "Identifier" ) {
189+ throw new SyntaxError ( `Expected identifier following macro statement` ) ;
190+ }
191+ const args = parseArgs ( ) ;
192+ expect ( TOKEN_TYPES . CloseStatement , "Expected closing statement token" ) ;
193+
194+ // Body of macro
195+ const body : Statement [ ] = [ ] ;
196+
197+ // Keep going until we hit {% endmacro
198+ while ( not ( TOKEN_TYPES . OpenStatement , TOKEN_TYPES . EndMacro ) ) {
199+ body . push ( parseAny ( ) ) ;
200+ }
201+
202+ return new Macro ( name as Identifier , args , body ) ;
203+ }
204+
176205 function parseExpressionSequence ( primary = false ) : Statement {
177206 const fn = primary ? parsePrimaryExpression : parseExpression ;
178207 const expressions = [ fn ( ) ] ;
@@ -189,7 +218,7 @@ export function parse(tokens: Token[]): Program {
189218
190219 function parseForStatement ( ) : For {
191220 // e.g., `message` in `for message in messages`
192- const loopVariable = parseExpressionSequence ( true ) ; // should be an identifier
221+ const loopVariable = parseExpressionSequence ( true ) ; // should be an identifier/tuple
193222 if ( ! ( loopVariable instanceof Identifier || loopVariable instanceof TupleLiteral ) ) {
194223 throw new SyntaxError ( `Expected identifier/tuple for the loop variable, got ${ loopVariable . type } instead` ) ;
195224 }
@@ -204,28 +233,48 @@ export function parse(tokens: Token[]): Program {
204233 // Body of for loop
205234 const body : Statement [ ] = [ ] ;
206235
207- // Keep going until we hit {% endfor
208- while ( not ( TOKEN_TYPES . OpenStatement , TOKEN_TYPES . EndFor ) ) {
236+ // Keep going until we hit {% endfor or {% else
237+ while ( not ( TOKEN_TYPES . OpenStatement , TOKEN_TYPES . EndFor ) && not ( TOKEN_TYPES . OpenStatement , TOKEN_TYPES . Else ) ) {
209238 body . push ( parseAny ( ) ) ;
210239 }
211240
212- return new For ( loopVariable , iterable , body ) ;
241+ // (Optional) else block
242+ const alternative : Statement [ ] = [ ] ;
243+ if ( is ( TOKEN_TYPES . OpenStatement , TOKEN_TYPES . Else ) ) {
244+ ++ current ; // consume {%
245+ ++ current ; // consume else
246+ expect ( TOKEN_TYPES . CloseStatement , "Expected closing statement token" ) ;
247+
248+ // keep going until we hit {% endfor
249+ while ( not ( TOKEN_TYPES . OpenStatement , TOKEN_TYPES . EndFor ) ) {
250+ alternative . push ( parseAny ( ) ) ;
251+ }
252+ }
253+
254+ return new For ( loopVariable , iterable , body , alternative ) ;
213255 }
214256
215257 function parseExpression ( ) : Statement {
216258 // Choose parse function with lowest precedence
217- return parseTernaryExpression ( ) ;
259+ return parseIfExpression ( ) ;
218260 }
219261
220- function parseTernaryExpression ( ) : Statement {
262+ function parseIfExpression ( ) : Statement {
221263 const a = parseLogicalOrExpression ( ) ;
222264 if ( is ( TOKEN_TYPES . If ) ) {
223265 // Ternary expression
224266 ++ current ; // consume if
225267 const predicate = parseLogicalOrExpression ( ) ;
226- expect ( TOKEN_TYPES . Else , "Expected else token" ) ;
227- const b = parseLogicalOrExpression ( ) ;
228- return new If ( predicate , [ a ] , [ b ] ) ;
268+
269+ if ( is ( TOKEN_TYPES . Else ) ) {
270+ // Ternary expression with else
271+ ++ current ; // consume else
272+ const b = parseLogicalOrExpression ( ) ;
273+ return new If ( predicate , [ a ] , [ b ] ) ;
274+ } else {
275+ // Select expression on iterable
276+ return new SelectExpression ( a , predicate ) ;
277+ }
229278 }
230279 return a ;
231280 }
@@ -477,7 +526,7 @@ export function parse(tokens: Token[]): Program {
477526 return new StringLiteral ( token . value ) ;
478527 case TOKEN_TYPES . BooleanLiteral :
479528 ++ current ;
480- return new BooleanLiteral ( token . value === "true" ) ;
529+ return new BooleanLiteral ( token . value . toLowerCase ( ) === "true" ) ;
481530 case TOKEN_TYPES . Identifier :
482531 ++ current ;
483532 return new Identifier ( token . value ) ;
0 commit comments