139 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			139 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| '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
 | |
| }
 |