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); 
 | 
}; 
 |