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.
		
		
		
		
			
				
					139 lines
				
				2.8 KiB
			
		
		
			
		
	
	
					139 lines
				
				2.8 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								'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
							 | 
						||
| 
								 | 
							
								}
							 |