diff options
Diffstat (limited to 'vanilla/node_modules/css-tree/lib/lexer/match-graph.js')
| -rw-r--r-- | vanilla/node_modules/css-tree/lib/lexer/match-graph.js | 527 |
1 files changed, 0 insertions, 527 deletions
diff --git a/vanilla/node_modules/css-tree/lib/lexer/match-graph.js b/vanilla/node_modules/css-tree/lib/lexer/match-graph.js deleted file mode 100644 index 5d3d800..0000000 --- a/vanilla/node_modules/css-tree/lib/lexer/match-graph.js +++ /dev/null @@ -1,527 +0,0 @@ -import { parse } from '../definition-syntax/parse.js'; - -export const MATCH = { type: 'Match' }; -export const MISMATCH = { type: 'Mismatch' }; -export const DISALLOW_EMPTY = { type: 'DisallowEmpty' }; - -const LEFTPARENTHESIS = 40; // ( -const RIGHTPARENTHESIS = 41; // ) - -function createCondition(match, thenBranch, elseBranch) { - // reduce node count - if (thenBranch === MATCH && elseBranch === MISMATCH) { - return match; - } - - if (match === MATCH && thenBranch === MATCH && elseBranch === MATCH) { - return match; - } - - if (match.type === 'If' && match.else === MISMATCH && thenBranch === MATCH) { - thenBranch = match.then; - match = match.match; - } - - return { - type: 'If', - match, - then: thenBranch, - else: elseBranch - }; -} - -function isFunctionType(name) { - return ( - name.length > 2 && - name.charCodeAt(name.length - 2) === LEFTPARENTHESIS && - name.charCodeAt(name.length - 1) === RIGHTPARENTHESIS - ); -} - -function isEnumCapatible(term) { - return ( - term.type === 'Keyword' || - term.type === 'AtKeyword' || - term.type === 'Function' || - term.type === 'Type' && isFunctionType(term.name) - ); -} - -function groupNode(terms, combinator = ' ', explicit = false) { - return { - type: 'Group', - terms, - combinator, - disallowEmpty: false, - explicit - }; -} - -function replaceTypeInGraph(node, replacements, visited = new Set()) { - if (!visited.has(node)) { - visited.add(node); - - switch (node.type) { - case 'If': - node.match = replaceTypeInGraph(node.match, replacements, visited); - node.then = replaceTypeInGraph(node.then, replacements, visited); - node.else = replaceTypeInGraph(node.else, replacements, visited); - break; - - case 'Type': - return replacements[node.name] || node; - } - } - - return node; -} - -function buildGroupMatchGraph(combinator, terms, atLeastOneTermMatched) { - switch (combinator) { - case ' ': { - // Juxtaposing components means that all of them must occur, in the given order. - // - // a b c - // = - // match a - // then match b - // then match c - // then MATCH - // else MISMATCH - // else MISMATCH - // else MISMATCH - let result = MATCH; - - for (let i = terms.length - 1; i >= 0; i--) { - const term = terms[i]; - - result = createCondition( - term, - result, - MISMATCH - ); - }; - - return result; - } - - case '|': { - // A bar (|) separates two or more alternatives: exactly one of them must occur. - // - // a | b | c - // = - // match a - // then MATCH - // else match b - // then MATCH - // else match c - // then MATCH - // else MISMATCH - - let result = MISMATCH; - let map = null; - - for (let i = terms.length - 1; i >= 0; i--) { - let term = terms[i]; - - // reduce sequence of keywords into a Enum - if (isEnumCapatible(term)) { - if (map === null && i > 0 && isEnumCapatible(terms[i - 1])) { - map = Object.create(null); - result = createCondition( - { - type: 'Enum', - map - }, - MATCH, - result - ); - } - - if (map !== null) { - const key = (isFunctionType(term.name) ? term.name.slice(0, -1) : term.name).toLowerCase(); - if (key in map === false) { - map[key] = term; - continue; - } - } - } - - map = null; - - // create a new conditonal node - result = createCondition( - term, - MATCH, - result - ); - }; - - return result; - } - - case '&&': { - // A double ampersand (&&) separates two or more components, - // all of which must occur, in any order. - - // Use MatchOnce for groups with a large number of terms, - // since &&-groups produces at least N!-node trees - if (terms.length > 5) { - return { - type: 'MatchOnce', - terms, - all: true - }; - } - - // Use a combination tree for groups with small number of terms - // - // a && b && c - // = - // match a - // then [b && c] - // else match b - // then [a && c] - // else match c - // then [a && b] - // else MISMATCH - // - // a && b - // = - // match a - // then match b - // then MATCH - // else MISMATCH - // else match b - // then match a - // then MATCH - // else MISMATCH - // else MISMATCH - let result = MISMATCH; - - for (let i = terms.length - 1; i >= 0; i--) { - const term = terms[i]; - let thenClause; - - if (terms.length > 1) { - thenClause = buildGroupMatchGraph( - combinator, - terms.filter(function(newGroupTerm) { - return newGroupTerm !== term; - }), - false - ); - } else { - thenClause = MATCH; - } - - result = createCondition( - term, - thenClause, - result - ); - }; - - return result; - } - - case '||': { - // A double bar (||) separates two or more options: - // one or more of them must occur, in any order. - - // Use MatchOnce for groups with a large number of terms, - // since ||-groups produces at least N!-node trees - if (terms.length > 5) { - return { - type: 'MatchOnce', - terms, - all: false - }; - } - - // Use a combination tree for groups with small number of terms - // - // a || b || c - // = - // match a - // then [b || c] - // else match b - // then [a || c] - // else match c - // then [a || b] - // else MISMATCH - // - // a || b - // = - // match a - // then match b - // then MATCH - // else MATCH - // else match b - // then match a - // then MATCH - // else MATCH - // else MISMATCH - let result = atLeastOneTermMatched ? MATCH : MISMATCH; - - for (let i = terms.length - 1; i >= 0; i--) { - const term = terms[i]; - let thenClause; - - if (terms.length > 1) { - thenClause = buildGroupMatchGraph( - combinator, - terms.filter(function(newGroupTerm) { - return newGroupTerm !== term; - }), - true - ); - } else { - thenClause = MATCH; - } - - result = createCondition( - term, - thenClause, - result - ); - }; - - return result; - } - } -} - -function buildMultiplierMatchGraph(node) { - let result = MATCH; - let matchTerm = buildMatchGraphInternal(node.term); - - if (node.max === 0) { - // disable repeating of empty match to prevent infinite loop - matchTerm = createCondition( - matchTerm, - DISALLOW_EMPTY, - MISMATCH - ); - - // an occurrence count is not limited, make a cycle; - // to collect more terms on each following matching mismatch - result = createCondition( - matchTerm, - null, // will be a loop - MISMATCH - ); - - result.then = createCondition( - MATCH, - MATCH, - result // make a loop - ); - - if (node.comma) { - result.then.else = createCondition( - { type: 'Comma', syntax: node }, - result, - MISMATCH - ); - } - } else { - // create a match node chain for [min .. max] interval with optional matches - for (let i = node.min || 1; i <= node.max; i++) { - if (node.comma && result !== MATCH) { - result = createCondition( - { type: 'Comma', syntax: node }, - result, - MISMATCH - ); - } - - result = createCondition( - matchTerm, - createCondition( - MATCH, - MATCH, - result - ), - MISMATCH - ); - } - } - - if (node.min === 0) { - // allow zero match - result = createCondition( - MATCH, - MATCH, - result - ); - } else { - // create a match node chain to collect [0 ... min - 1] required matches - for (let i = 0; i < node.min - 1; i++) { - if (node.comma && result !== MATCH) { - result = createCondition( - { type: 'Comma', syntax: node }, - result, - MISMATCH - ); - } - - result = createCondition( - matchTerm, - result, - MISMATCH - ); - } - } - - return result; -} - -function buildMatchGraphInternal(node) { - if (typeof node === 'function') { - return { - type: 'Generic', - fn: node - }; - } - - switch (node.type) { - case 'Group': { - let result = buildGroupMatchGraph( - node.combinator, - node.terms.map(buildMatchGraphInternal), - false - ); - - if (node.disallowEmpty) { - result = createCondition( - result, - DISALLOW_EMPTY, - MISMATCH - ); - } - - return result; - } - - case 'Multiplier': - return buildMultiplierMatchGraph(node); - - // https://drafts.csswg.org/css-values-5/#boolean - case 'Boolean': { - const term = buildMatchGraphInternal(node.term); - // <boolean-expr[ <test> ]> = not <boolean-expr-group> | <boolean-expr-group> [ [ and <boolean-expr-group> ]* | [ or <boolean-expr-group> ]* ] - const matchNode = buildMatchGraphInternal(groupNode([ - groupNode([ - { type: 'Keyword', name: 'not' }, - { type: 'Type', name: '!boolean-group' } - ]), - groupNode([ - { type: 'Type', name: '!boolean-group' }, - groupNode([ - { type: 'Multiplier', comma: false, min: 0, max: 0, term: groupNode([ - { type: 'Keyword', name: 'and' }, - { type: 'Type', name: '!boolean-group' } - ]) }, - { type: 'Multiplier', comma: false, min: 0, max: 0, term: groupNode([ - { type: 'Keyword', name: 'or' }, - { type: 'Type', name: '!boolean-group' } - ]) } - ], '|') - ]) - ], '|')); - // <boolean-expr-group> = <test> | ( <boolean-expr[ <test> ]> ) | <general-enclosed> - const booleanGroup = buildMatchGraphInternal( - groupNode([ - { type: 'Type', name: '!term' }, - groupNode([ - { type: 'Token', value: '(' }, - { type: 'Type', name: '!self' }, - { type: 'Token', value: ')' } - ]), - { type: 'Type', name: 'general-enclosed' } - ], '|') - ); - - replaceTypeInGraph(booleanGroup, { '!term': term, '!self': matchNode }); - replaceTypeInGraph(matchNode, { '!boolean-group': booleanGroup }); - - return matchNode; - } - - case 'Type': - case 'Property': - return { - type: node.type, - name: node.name, - syntax: node - }; - - case 'Keyword': - return { - type: node.type, - name: node.name.toLowerCase(), - syntax: node - }; - - case 'AtKeyword': - return { - type: node.type, - name: '@' + node.name.toLowerCase(), - syntax: node - }; - - case 'Function': - return { - type: node.type, - name: node.name.toLowerCase() + '(', - syntax: node - }; - - case 'String': - // convert a one char length String to a Token - if (node.value.length === 3) { - return { - type: 'Token', - value: node.value.charAt(1), - syntax: node - }; - } - - // otherwise use it as is - return { - type: node.type, - value: node.value.substr(1, node.value.length - 2).replace(/\\'/g, '\''), - syntax: node - }; - - case 'Token': - return { - type: node.type, - value: node.value, - syntax: node - }; - - case 'Comma': - return { - type: node.type, - syntax: node - }; - - default: - throw new Error('Unknown node type:', node.type); - } -} - -export function buildMatchGraph(syntaxTree, ref) { - if (typeof syntaxTree === 'string') { - syntaxTree = parse(syntaxTree); - } - - return { - type: 'MatchGraph', - match: buildMatchGraphInternal(syntaxTree), - syntax: ref || null, - source: syntaxTree - }; -} |
