UNPKG

2.9 kBJavaScriptView Raw
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}