You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
135 lines
3.2 KiB
135 lines
3.2 KiB
'use strict'; |
|
|
|
function noop(value) { |
|
return value; |
|
} |
|
|
|
function generateMultiplier(multiplier) { |
|
const { min, max, comma } = multiplier; |
|
|
|
if (min === 0 && max === 0) { |
|
return comma ? '#?' : '*'; |
|
} |
|
|
|
if (min === 0 && max === 1) { |
|
return '?'; |
|
} |
|
|
|
if (min === 1 && max === 0) { |
|
return comma ? '#' : '+'; |
|
} |
|
|
|
if (min === 1 && max === 1) { |
|
return ''; |
|
} |
|
|
|
return ( |
|
(comma ? '#' : '') + |
|
(min === max |
|
? '{' + min + '}' |
|
: '{' + min + ',' + (max !== 0 ? max : '') + '}' |
|
) |
|
); |
|
} |
|
|
|
function generateTypeOpts(node) { |
|
switch (node.type) { |
|
case 'Range': |
|
return ( |
|
' [' + |
|
(node.min === null ? '-∞' : node.min) + |
|
',' + |
|
(node.max === null ? '∞' : node.max) + |
|
']' |
|
); |
|
|
|
default: |
|
throw new Error('Unknown node type `' + node.type + '`'); |
|
} |
|
} |
|
|
|
function generateSequence(node, decorate, forceBraces, compact) { |
|
const combinator = node.combinator === ' ' || compact ? node.combinator : ' ' + node.combinator + ' '; |
|
const result = node.terms |
|
.map(term => internalGenerate(term, decorate, forceBraces, compact)) |
|
.join(combinator); |
|
|
|
if (node.explicit || forceBraces) { |
|
return (compact || result[0] === ',' ? '[' : '[ ') + result + (compact ? ']' : ' ]'); |
|
} |
|
|
|
return result; |
|
} |
|
|
|
function internalGenerate(node, decorate, forceBraces, compact) { |
|
let result; |
|
|
|
switch (node.type) { |
|
case 'Group': |
|
result = |
|
generateSequence(node, decorate, forceBraces, compact) + |
|
(node.disallowEmpty ? '!' : ''); |
|
break; |
|
|
|
case 'Multiplier': |
|
// return since node is a composition |
|
return ( |
|
internalGenerate(node.term, decorate, forceBraces, compact) + |
|
decorate(generateMultiplier(node), node) |
|
); |
|
|
|
case 'Type': |
|
result = '<' + node.name + (node.opts ? decorate(generateTypeOpts(node.opts), node.opts) : '') + '>'; |
|
break; |
|
|
|
case 'Property': |
|
result = '<\'' + node.name + '\'>'; |
|
break; |
|
|
|
case 'Keyword': |
|
result = node.name; |
|
break; |
|
|
|
case 'AtKeyword': |
|
result = '@' + node.name; |
|
break; |
|
|
|
case 'Function': |
|
result = node.name + '('; |
|
break; |
|
|
|
case 'String': |
|
case 'Token': |
|
result = node.value; |
|
break; |
|
|
|
case 'Comma': |
|
result = ','; |
|
break; |
|
|
|
default: |
|
throw new Error('Unknown node type `' + node.type + '`'); |
|
} |
|
|
|
return decorate(result, node); |
|
} |
|
|
|
function generate(node, options) { |
|
let decorate = noop; |
|
let forceBraces = false; |
|
let compact = false; |
|
|
|
if (typeof options === 'function') { |
|
decorate = options; |
|
} else if (options) { |
|
forceBraces = Boolean(options.forceBraces); |
|
compact = Boolean(options.compact); |
|
if (typeof options.decorate === 'function') { |
|
decorate = options.decorate; |
|
} |
|
} |
|
|
|
return internalGenerate(node, decorate, forceBraces, compact); |
|
} |
|
|
|
exports.generate = generate;
|
|
|