@@ -140,6 +140,11 @@ fn check_expr<'a>(expr: &WithSpan<'a, Expr<'a>>, allowed: Allowed) -> ParseResul
140140 }
141141 Expr :: LetCond ( cond) => check_expr ( & cond. expr , Allowed :: default ( ) ) ,
142142 Expr :: ArgumentPlaceholder => cut_error ! ( "unreachable" , expr. span) ,
143+ Expr :: Conditional ( cond) => {
144+ check_expr ( & cond. then , Allowed :: default ( ) ) ?;
145+ check_expr ( & cond. test , Allowed :: default ( ) ) ?;
146+ check_expr ( & cond. otherwise , Allowed :: default ( ) )
147+ }
143148 Expr :: BoolLit ( _)
144149 | Expr :: NumLit ( _, _)
145150 | Expr :: StrLit ( _)
@@ -225,6 +230,14 @@ pub enum Expr<'a> {
225230 /// This variant should never be used directly.
226231 /// It is used for the handling of named arguments in the generator, esp. with filters.
227232 ArgumentPlaceholder ,
233+ Conditional ( Conditional < ' a > ) ,
234+ }
235+
236+ #[ derive( Clone , Debug , PartialEq ) ]
237+ pub struct Conditional < ' a > {
238+ pub then : Box < WithSpan < ' a , Expr < ' a > > > ,
239+ pub test : Box < WithSpan < ' a , Expr < ' a > > > ,
240+ pub otherwise : Box < WithSpan < ' a , Expr < ' a > > > ,
228241}
229242
230243#[ derive( Clone , Debug , PartialEq ) ]
@@ -316,6 +329,84 @@ impl<'a> Expr<'a> {
316329 allow_underscore : bool ,
317330 ) -> ParseResult < ' a , WithSpan < ' a , Self > > {
318331 let _level_guard = level. nest ( i) ?;
332+ Self :: if_else ( i, level, allow_underscore)
333+ }
334+
335+ /// Like [`Expr::parse()`], but does not parse conditional expressions,
336+ /// i.e. the token `if` is not consumed.
337+ pub ( super ) fn parse_no_if_else (
338+ i : & mut & ' a str ,
339+ level : Level < ' _ > ,
340+ allow_underscore : bool ,
341+ ) -> ParseResult < ' a , WithSpan < ' a , Self > > {
342+ let _level_guard = level. nest ( i) ?;
343+ Self :: range ( i, level, allow_underscore)
344+ }
345+
346+ fn if_else (
347+ i : & mut & ' a str ,
348+ level : Level < ' _ > ,
349+ allow_underscore : bool ,
350+ ) -> ParseResult < ' a , WithSpan < ' a , Self > > {
351+ #[ inline( never) ] // very unlikely case
352+ fn actually_cond < ' a > (
353+ i : & mut & ' a str ,
354+ level : Level < ' _ > ,
355+ allow_underscore : bool ,
356+ then : Box < WithSpan < ' a , Expr < ' a > > > ,
357+ start : & ' a str ,
358+ if_span : & ' a str ,
359+ ) -> ParseResult < ' a , WithSpan < ' a , Expr < ' a > > > {
360+ let Some ( test) =
361+ opt ( |i : & mut _ | Expr :: parse ( i, level, allow_underscore) ) . parse_next ( i) ?
362+ else {
363+ return cut_error ! (
364+ "conditional expression (`then if cond else otherwise`) expects an expression \
365+ after the keyword `if`",
366+ if_span,
367+ ) ;
368+ } ;
369+
370+ let Some ( else_span) = opt ( ws ( keyword ( "else" ) ) . take ( ) ) . parse_next ( i) ? else {
371+ return cut_error ! (
372+ "conditional expression (`then if cond else otherwise`) expects the keyword \
373+ `else` after its condition",
374+ test. span( ) ,
375+ ) ;
376+ } ;
377+
378+ let Some ( otherwise) =
379+ opt ( |i : & mut _ | Expr :: if_else ( i, level, allow_underscore) ) . parse_next ( i) ?
380+ else {
381+ return cut_error ! (
382+ "conditional expression (`then if cond else otherwise`) expects an expression \
383+ after the keyword `else`",
384+ else_span,
385+ ) ;
386+ } ;
387+
388+ let expr = Expr :: Conditional ( Conditional {
389+ test : Box :: new ( test) ,
390+ then,
391+ otherwise : Box :: new ( otherwise) ,
392+ } ) ;
393+ Ok ( WithSpan :: new ( expr, start, i) )
394+ }
395+
396+ let start = * i;
397+ let expr = Self :: range ( i, level, allow_underscore) ?;
398+ if let Some ( if_span) = opt ( ws ( keyword ( "if" ) ) . take ( ) ) . parse_next ( i) ? {
399+ actually_cond ( i, level, allow_underscore, expr. into ( ) , start, if_span)
400+ } else {
401+ Ok ( expr)
402+ }
403+ }
404+
405+ fn range (
406+ i : & mut & ' a str ,
407+ level : Level < ' _ > ,
408+ allow_underscore : bool ,
409+ ) -> ParseResult < ' a , WithSpan < ' a , Self > > {
319410 let range_right = move |i : & mut _ | {
320411 (
321412 ws ( alt ( ( "..=" , ".." ) ) ) ,
@@ -634,6 +725,7 @@ impl<'a> Expr<'a> {
634725 Self :: BinOp ( v) if matches ! ( v. op, "&&" | "||" ) => {
635726 v. lhs . contains_bool_lit_or_is_defined ( ) || v. rhs . contains_bool_lit_or_is_defined ( )
636727 }
728+ Self :: Conditional ( cond) => cond. test . contains_bool_lit_or_is_defined ( ) ,
637729 Self :: NumLit ( _, _)
638730 | Self :: StrLit ( _)
639731 | Self :: CharLit ( _)
0 commit comments