| function noop(value) { | 
|     return value; | 
| } | 
|   | 
| function generateMultiplier(multiplier) { | 
|     if (multiplier.min === 0 && multiplier.max === 0) { | 
|         return '*'; | 
|     } | 
|   | 
|     if (multiplier.min === 0 && multiplier.max === 1) { | 
|         return '?'; | 
|     } | 
|   | 
|     if (multiplier.min === 1 && multiplier.max === 0) { | 
|         return multiplier.comma ? '#' : '+'; | 
|     } | 
|   | 
|     if (multiplier.min === 1 && multiplier.max === 1) { | 
|         return ''; | 
|     } | 
|   | 
|     return ( | 
|         (multiplier.comma ? '#' : '') + | 
|         (multiplier.min === multiplier.max | 
|             ? '{' + multiplier.min + '}' | 
|             : '{' + multiplier.min + ',' + (multiplier.max !== 0 ? multiplier.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) { | 
|     var combinator = node.combinator === ' ' || compact ? node.combinator : ' ' + node.combinator + ' '; | 
|     var result = node.terms.map(function(term) { | 
|         return generate(term, decorate, forceBraces, compact); | 
|     }).join(combinator); | 
|   | 
|     if (node.explicit || forceBraces) { | 
|         result = (compact || result[0] === ',' ? '[' : '[ ') + result + (compact ? ']' : ' ]'); | 
|     } | 
|   | 
|     return result; | 
| } | 
|   | 
| function generate(node, decorate, forceBraces, compact) { | 
|     var result; | 
|   | 
|     switch (node.type) { | 
|         case 'Group': | 
|             result = | 
|                 generateSequence(node, decorate, forceBraces, compact) + | 
|                 (node.disallowEmpty ? '!' : ''); | 
|             break; | 
|   | 
|         case 'Multiplier': | 
|             // return since node is a composition | 
|             return ( | 
|                 generate(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); | 
| } | 
|   | 
| module.exports = function(node, options) { | 
|     var decorate = noop; | 
|     var forceBraces = false; | 
|     var 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 generate(node, decorate, forceBraces, compact); | 
| }; |