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.
138 lines
2.8 KiB
138 lines
2.8 KiB
'use strict' |
|
|
|
// The ABNF grammar in the spec is totally ambiguous. |
|
// |
|
// This parser follows the operator precedence defined in the |
|
// `Order of Precedence and Parentheses` section. |
|
|
|
module.exports = function (tokens) { |
|
var index = 0 |
|
|
|
function hasMore () { |
|
return index < tokens.length |
|
} |
|
|
|
function token () { |
|
return hasMore() ? tokens[index] : null |
|
} |
|
|
|
function next () { |
|
if (!hasMore()) { |
|
throw new Error() |
|
} |
|
index++ |
|
} |
|
|
|
function parseOperator (operator) { |
|
var t = token() |
|
if (t && t.type === 'OPERATOR' && operator === t.string) { |
|
next() |
|
return t.string |
|
} |
|
} |
|
|
|
function parseWith () { |
|
if (parseOperator('WITH')) { |
|
var t = token() |
|
if (t && t.type === 'EXCEPTION') { |
|
next() |
|
return t.string |
|
} |
|
throw new Error('Expected exception after `WITH`') |
|
} |
|
} |
|
|
|
function parseLicenseRef () { |
|
// TODO: Actually, everything is concatenated into one string |
|
// for backward-compatibility but it could be better to return |
|
// a nice structure. |
|
var begin = index |
|
var string = '' |
|
var t = token() |
|
if (t.type === 'DOCUMENTREF') { |
|
next() |
|
string += 'DocumentRef-' + t.string + ':' |
|
if (!parseOperator(':')) { |
|
throw new Error('Expected `:` after `DocumentRef-...`') |
|
} |
|
} |
|
t = token() |
|
if (t.type === 'LICENSEREF') { |
|
next() |
|
string += 'LicenseRef-' + t.string |
|
return { license: string } |
|
} |
|
index = begin |
|
} |
|
|
|
function parseLicense () { |
|
var t = token() |
|
if (t && t.type === 'LICENSE') { |
|
next() |
|
var node = { license: t.string } |
|
if (parseOperator('+')) { |
|
node.plus = true |
|
} |
|
var exception = parseWith() |
|
if (exception) { |
|
node.exception = exception |
|
} |
|
return node |
|
} |
|
} |
|
|
|
function parseParenthesizedExpression () { |
|
var left = parseOperator('(') |
|
if (!left) { |
|
return |
|
} |
|
|
|
var expr = parseExpression() |
|
|
|
if (!parseOperator(')')) { |
|
throw new Error('Expected `)`') |
|
} |
|
|
|
return expr |
|
} |
|
|
|
function parseAtom () { |
|
return ( |
|
parseParenthesizedExpression() || |
|
parseLicenseRef() || |
|
parseLicense() |
|
) |
|
} |
|
|
|
function makeBinaryOpParser (operator, nextParser) { |
|
return function parseBinaryOp () { |
|
var left = nextParser() |
|
if (!left) { |
|
return |
|
} |
|
|
|
if (!parseOperator(operator)) { |
|
return left |
|
} |
|
|
|
var right = parseBinaryOp() |
|
if (!right) { |
|
throw new Error('Expected expression') |
|
} |
|
return { |
|
left: left, |
|
conjunction: operator.toLowerCase(), |
|
right: right |
|
} |
|
} |
|
} |
|
|
|
var parseAnd = makeBinaryOpParser('AND', parseAtom) |
|
var parseExpression = makeBinaryOpParser('OR', parseAnd) |
|
|
|
var node = parseExpression() |
|
if (!node || hasMore()) { |
|
throw new Error('Syntax error') |
|
} |
|
return node |
|
}
|
|
|