|
'use strict'; |
|
|
|
const types = require('../../tokenizer/types.cjs'); |
|
|
|
const MediaFeatureToken = new Set([types.Colon, types.RightParenthesis, types.EOF]); |
|
const SupportsFeatureToken = new Set([types.Colon, types.EOF]); |
|
|
|
const name = 'Condition'; |
|
const structure = { |
|
kind: String, |
|
children: [[ |
|
'Identifier', |
|
'Feature', |
|
'FeatureRange' |
|
]] |
|
}; |
|
|
|
const conditions = { |
|
media() { |
|
if (this.tokenType === types.LeftParenthesis) { |
|
const firstToken = this.lookupTypeNonSC(1); |
|
if (firstToken === types.Ident && MediaFeatureToken.has(this.lookupTypeNonSC(2))) { |
|
return this.Feature('media'); |
|
} else if (firstToken !== types.LeftParenthesis) { |
|
return this.parseWithFallback(() => this.FeatureRange('media'), (startIndex) => { |
|
this.skip(startIndex - this.tokenIndex); |
|
}); |
|
} |
|
} |
|
}, |
|
supports() { |
|
if (this.tokenType === types.LeftParenthesis) { |
|
if (this.lookupTypeNonSC(1) === types.Ident && SupportsFeatureToken.has(this.lookupTypeNonSC(2))) { |
|
return this.Declaration(); |
|
} |
|
} |
|
}, |
|
container() { |
|
if (this.tokenType === types.LeftParenthesis) { |
|
if (this.lookupTypeNonSC(1) === types.Ident && MediaFeatureToken.has(this.lookupTypeNonSC(2))) { |
|
return this.Feature('size'); |
|
} else if (this.lookupTypeNonSC(1) !== types.LeftParenthesis) { |
|
return this.FeatureRange('size'); |
|
} |
|
} |
|
} |
|
}; |
|
|
|
function parse(kind = 'media') { |
|
const children = this.createList(); |
|
const termParser = conditions[kind]; |
|
|
|
scan: while (!this.eof) { |
|
switch (this.tokenType) { |
|
case types.Comment: |
|
case types.WhiteSpace: |
|
this.next(); |
|
continue; |
|
|
|
case types.Ident: |
|
children.push(this.Identifier()); |
|
break; |
|
|
|
case types.LeftParenthesis: { |
|
let term = termParser.call(this); |
|
|
|
if (!term) { |
|
term = this.parseWithFallback(() => { |
|
this.next(); |
|
const res = this.Condition(kind); |
|
this.eat(types.RightParenthesis); |
|
return res; |
|
}, (startIndex) => { |
|
this.skip(startIndex - this.tokenIndex); |
|
return this.GeneralEnclosed(); |
|
}); |
|
} |
|
|
|
children.push(term); |
|
break; |
|
} |
|
|
|
case types.Function: { |
|
let term = termParser.call(this); |
|
|
|
if (!term) { |
|
term = this.GeneralEnclosed(); |
|
} |
|
|
|
children.push(term); |
|
break; |
|
} |
|
|
|
default: |
|
break scan; |
|
} |
|
} |
|
|
|
if (children.isEmpty) { |
|
this.error('Condition can\'t be empty'); |
|
} |
|
|
|
return { |
|
type: 'Condition', |
|
loc: this.getLocationFromList(children), |
|
kind, |
|
children |
|
}; |
|
} |
|
|
|
function generate(node) { |
|
node.children.forEach(child => { |
|
if (child.type === 'Condition') { |
|
this.token(types.LeftParenthesis, '('); |
|
this.node(child); |
|
this.token(types.RightParenthesis, ')'); |
|
} else { |
|
this.node(child); |
|
} |
|
}); |
|
} |
|
|
|
exports.conditions = conditions; |
|
exports.generate = generate; |
|
exports.name = name; |
|
exports.parse = parse; |
|
exports.structure = structure; |
|
|