@@ -1157,6 +1157,61 @@ export class Parser {
11571157 return this . finish ( node ) ;
11581158 }
11591159
1160+ public _parseBooleanExpression ( parseTest : ( ) => nodes . Node | null ) : nodes . Node | null {
1161+ // <boolean-expr[ <test> ]> = not <boolean-expr-group> | <boolean-expr-group>
1162+ // [ [ and <boolean-expr-group> ]*
1163+ // | [ or <boolean-expr-group> ]* ]
1164+
1165+ const node = this . create ( nodes . Node ) ;
1166+
1167+ if ( this . acceptIdent ( 'not' ) ) {
1168+ if ( ! node . addChild ( this . _parseBooleanExpressionGroup ( parseTest ) ) ) {
1169+ return null ;
1170+ }
1171+ } else {
1172+ if ( ! node . addChild ( this . _parseBooleanExpressionGroup ( parseTest ) ) ) {
1173+ return null ;
1174+ }
1175+ if ( this . peekIdent ( 'and' ) ) {
1176+ while ( this . acceptIdent ( 'and' ) ) {
1177+ if ( ! node . addChild ( this . _parseBooleanExpressionGroup ( parseTest ) ) ) {
1178+ return null ;
1179+ }
1180+ }
1181+ } else if ( this . peekIdent ( 'or' ) ) {
1182+ while ( this . acceptIdent ( 'or' ) ) {
1183+ if ( ! node . addChild ( this . _parseBooleanExpressionGroup ( parseTest ) ) ) {
1184+ return null ;
1185+ }
1186+ }
1187+ }
1188+ }
1189+ return this . finish ( node ) ;
1190+ }
1191+
1192+ public _parseBooleanExpressionGroup ( parseTest : ( ) => nodes . Node | null ) {
1193+ // <boolean-expr-group> = <test> | ( <boolean-expr[ <test> ]> ) | <general-enclosed>
1194+
1195+ const node = this . create ( nodes . Node ) ;
1196+ const pos = this . mark ( ) ;
1197+
1198+ if ( this . accept ( TokenType . ParenthesisL ) ) {
1199+ if ( node . addChild ( this . _parseBooleanExpression ( parseTest ) ) ) {
1200+ if ( ! this . accept ( TokenType . ParenthesisR ) ) {
1201+ return this . finish ( node , ParseError . RightParenthesisExpected , [ ] , [ TokenType . CurlyL ] ) ;
1202+ }
1203+ return this . finish ( node ) ;
1204+ }
1205+ this . restoreAtMark ( pos ) ;
1206+ }
1207+
1208+ if ( ! node . addChild ( parseTest ( ) ) ) {
1209+ return null ;
1210+ } ;
1211+
1212+ return this . finish ( node ) ;
1213+ }
1214+
11601215 public _parseMediaCondition ( ) : nodes . Node | null {
11611216 // <media-condition> = <media-not> | <media-and> | <media-or> | <media-in-parens>
11621217 // <media-not> = not <media-in-parens>
@@ -2071,6 +2126,13 @@ export class Parser {
20712126 const pos = this . mark ( ) ;
20722127 const node = this . create ( nodes . Function ) ;
20732128
2129+ let parseArgument = this . _parseFunctionArgument . bind ( this ) ;
2130+ let separator = TokenType . Comma ;
2131+ if ( this . peekIdent ( "if" ) ) {
2132+ parseArgument = this . _parseIfBranch . bind ( this ) ;
2133+ separator = TokenType . SemiColon ;
2134+ }
2135+
20742136 if ( ! node . setIdentifier ( this . _parseFunctionIdentifier ( ) ) ) {
20752137 return null ;
20762138 }
@@ -2080,12 +2142,12 @@ export class Parser {
20802142 return null ;
20812143 }
20822144
2083- if ( node . getArguments ( ) . addChild ( this . _parseFunctionArgument ( ) ) ) {
2084- while ( this . accept ( TokenType . Comma ) ) {
2145+ if ( node . getArguments ( ) . addChild ( parseArgument ( ) ) ) {
2146+ while ( this . accept ( separator ) ) {
20852147 if ( this . peek ( TokenType . ParenthesisR ) ) {
20862148 break ;
20872149 }
2088- if ( ! node . getArguments ( ) . addChild ( this . _parseFunctionArgument ( ) ) ) {
2150+ if ( ! node . getArguments ( ) . addChild ( parseArgument ( ) ) ) {
20892151 this . markError ( node , ParseError . ExpressionExpected ) ;
20902152 }
20912153 }
@@ -2126,6 +2188,84 @@ export class Parser {
21262188 return null ;
21272189 }
21282190
2191+ public _parseIfBranch ( ) {
2192+ // <if-branch> = <if-condition> : <declaration-value>?
2193+
2194+ const node = this . create ( nodes . Node ) ;
2195+ if ( ! node . addChild ( this . _parseIfCondition ( ) ) ) {
2196+ return this . finish ( node , ParseError . IfConditionExpected , [ ] , [ TokenType . SemiColon ] ) ;
2197+ }
2198+ if ( ! this . accept ( TokenType . Colon ) ) {
2199+ return this . finish ( node , ParseError . ColonExpected , [ ] , [ TokenType . SemiColon ] ) ;
2200+ }
2201+ node . addChild ( this . _parseExpr ( ) ) ;
2202+ return this . finish ( node ) ;
2203+ }
2204+
2205+ public _parseIfCondition ( ) : nodes . Node | null {
2206+ // <if-condition> = <boolean-expr[ <if-test> ]> | else
2207+
2208+ const node = this . create ( nodes . Node ) ;
2209+
2210+ if ( this . peekIdent ( "else" ) ) {
2211+ node . addChild ( this . _parseIdent ( ) ) ;
2212+ return this . finish ( node ) ;
2213+ }
2214+
2215+ return this . _parseBooleanExpression ( this . _parseIfTest . bind ( this ) ) ;
2216+ }
2217+
2218+ public _parseIfTest ( ) : nodes . Node | null {
2219+ // <if-test> =
2220+ // supports( [ <ident> : <declaration-value> ] | <supports-condition> ) |
2221+ // media( <media-feature> | <media-condition> ) |
2222+ // style( <style-query> )
2223+
2224+ const node = this . create ( nodes . Node ) ;
2225+
2226+ if ( this . acceptIdent ( 'supports' ) ) {
2227+ if ( this . hasWhitespace ( ) || ! this . accept ( TokenType . ParenthesisL ) ) {
2228+ return this . finish ( node , ParseError . LeftParenthesisExpected , [ ] , [ TokenType . Colon ] ) ;
2229+ }
2230+ node . addChild ( this . _tryToParseDeclaration ( ) || this . _parseSupportsCondition ( ) ) ;
2231+ if ( ! this . accept ( TokenType . ParenthesisR ) ) {
2232+ return this . finish ( node , ParseError . RightParenthesisExpected , [ ] , [ TokenType . Colon ] ) ;
2233+ }
2234+ return this . finish ( node ) ;
2235+ }
2236+
2237+ if ( this . acceptIdent ( 'media' ) ) {
2238+ if ( this . hasWhitespace ( ) || ! this . accept ( TokenType . ParenthesisL ) ) {
2239+ return this . finish ( node , ParseError . LeftParenthesisExpected , [ ] , [ TokenType . Colon ] ) ;
2240+ }
2241+ const pos = this . mark ( ) ;
2242+ const condition = this . _parseMediaCondition ( ) ;
2243+ if ( condition && ! condition . isErroneous ( ) ) {
2244+ node . addChild ( condition ) ;
2245+ } else {
2246+ this . restoreAtMark ( pos ) ;
2247+ node . addChild ( this . _parseMediaFeature ( ) ) ;
2248+ }
2249+ if ( ! this . accept ( TokenType . ParenthesisR ) ) {
2250+ return this . finish ( node , ParseError . RightParenthesisExpected , [ ] , [ TokenType . Colon ] ) ;
2251+ }
2252+ return this . finish ( node ) ;
2253+ }
2254+
2255+ if ( this . acceptIdent ( 'style' ) ) {
2256+ if ( this . hasWhitespace ( ) || ! this . accept ( TokenType . ParenthesisL ) ) {
2257+ return this . finish ( node , ParseError . LeftParenthesisExpected , [ ] , [ TokenType . Colon ] ) ;
2258+ }
2259+ node . addChild ( this . _parseStyleQuery ( ) ) ;
2260+ if ( ! this . accept ( TokenType . ParenthesisR ) ) {
2261+ return this . finish ( node , ParseError . RightParenthesisExpected , [ ] , [ TokenType . Colon ] ) ;
2262+ }
2263+ return this . finish ( node ) ;
2264+ }
2265+
2266+ return null ;
2267+ }
2268+
21292269 public _parseHexColor ( ) : nodes . Node | null {
21302270 if ( this . peekRegExp ( TokenType . Hash , / ^ # ( [ A - F a - f 0 - 9 ] { 3 } | [ A - F a - f 0 - 9 ] { 4 } | [ A - F a - f 0 - 9 ] { 6 } | [ A - F a - f 0 - 9 ] { 8 } ) $ / g) ) {
21312271 const node = this . create ( nodes . HexColorValue ) ;
0 commit comments