1'use strict' 2 3// The ABNF grammar in the spec is totally ambiguous. 4// 5// This parser follows the operator precedence defined in the 6// `Order of Precedence and Parentheses` section. 7 8module.exports = function (tokens) { 9 var index = 0 10 11 function hasMore () { 12 return index < tokens.length 13 } 14 15 function token () { 16 return hasMore() ? tokens[index] : null 17 } 18 19 function next () { 20 if (!hasMore()) { 21 throw new Error() 22 } 23 index++ 24 } 25 26 function parseOperator (operator) { 27 var t = token() 28 if (t && t.type === 'OPERATOR' && operator === t.string) { 29 next() 30 return t.string 31 } 32 } 33 34 function parseWith () { 35 if (parseOperator('WITH')) { 36 var t = token() 37 if (t && t.type === 'EXCEPTION') { 38 next() 39 return t.string 40 } 41 throw new Error('Expected exception after `WITH`') 42 } 43 } 44 45 function parseLicenseRef () { 46 // TODO: Actually, everything is concatenated into one string 47 // for backward-compatibility but it could be better to return 48 // a nice structure. 49 var begin = index 50 var string = '' 51 var t = token() 52 if (t.type === 'DOCUMENTREF') { 53 next() 54 string += 'DocumentRef-' + t.string + ':' 55 if (!parseOperator(':')) { 56 throw new Error('Expected `:` after `DocumentRef-...`') 57 } 58 } 59 t = token() 60 if (t.type === 'LICENSEREF') { 61 next() 62 string += 'LicenseRef-' + t.string 63 return {license: string} 64 } 65 index = begin 66 } 67 68 function parseLicense () { 69 var t = token() 70 if (t && t.type === 'LICENSE') { 71 next() 72 var node = {license: t.string} 73 if (parseOperator('+')) { 74 node.plus = true 75 } 76 var exception = parseWith() 77 if (exception) { 78 node.exception = exception 79 } 80 return node 81 } 82 } 83 84 function parseParenthesizedExpression () { 85 var left = parseOperator('(') 86 if (!left) { 87 return 88 } 89 90 var expr = parseExpression() 91 92 if (!parseOperator(')')) { 93 throw new Error('Expected `)`') 94 } 95 96 return expr 97 } 98 99 function parseAtom () { 100 return ( 101 parseParenthesizedExpression() || 102 parseLicenseRef() || 103 parseLicense() 104 ) 105 } 106 107 function makeBinaryOpParser (operator, nextParser) { 108 return function parseBinaryOp () { 109 var left = nextParser() 110 if (!left) { 111 return 112 } 113 114 if (!parseOperator(operator)) { 115 return left 116 } 117 118 var right = parseBinaryOp() 119 if (!right) { 120 throw new Error('Expected expression') 121 } 122 return { 123 left: left, 124 conjunction: operator.toLowerCase(), 125 right: right 126 } 127 } 128 } 129 130 var parseAnd = makeBinaryOpParser('AND', parseAtom) 131 var parseExpression = makeBinaryOpParser('OR', parseAnd) 132 133 var node = parseExpression() 134 if (!node || hasMore()) { 135 throw new Error('Syntax error') 136 } 137 return node 138} 139