• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict'
2
3var licenses = []
4  .concat(require('spdx-license-ids'))
5  .concat(require('spdx-license-ids/deprecated'))
6var exceptions = require('spdx-exceptions')
7
8module.exports = function (source) {
9  var index = 0
10
11  function hasMore () {
12    return index < source.length
13  }
14
15  // `value` can be a regexp or a string.
16  // If it is recognized, the matching source string is returned and
17  // the index is incremented. Otherwise `undefined` is returned.
18  function read (value) {
19    if (value instanceof RegExp) {
20      var chars = source.slice(index)
21      var match = chars.match(value)
22      if (match) {
23        index += match[0].length
24        return match[0]
25      }
26    } else {
27      if (source.indexOf(value, index) === index) {
28        index += value.length
29        return value
30      }
31    }
32  }
33
34  function skipWhitespace () {
35    read(/[ ]*/)
36  }
37
38  function operator () {
39    var string
40    var possibilities = ['WITH', 'AND', 'OR', '(', ')', ':', '+']
41    for (var i = 0; i < possibilities.length; i++) {
42      string = read(possibilities[i])
43      if (string) {
44        break
45      }
46    }
47
48    if (string === '+' && index > 1 && source[index - 2] === ' ') {
49      throw new Error('Space before `+`')
50    }
51
52    return string && {
53      type: 'OPERATOR',
54      string: string
55    }
56  }
57
58  function idstring () {
59    return read(/[A-Za-z0-9-.]+/)
60  }
61
62  function expectIdstring () {
63    var string = idstring()
64    if (!string) {
65      throw new Error('Expected idstring at offset ' + index)
66    }
67    return string
68  }
69
70  function documentRef () {
71    if (read('DocumentRef-')) {
72      var string = expectIdstring()
73      return {type: 'DOCUMENTREF', string: string}
74    }
75  }
76
77  function licenseRef () {
78    if (read('LicenseRef-')) {
79      var string = expectIdstring()
80      return {type: 'LICENSEREF', string: string}
81    }
82  }
83
84  function identifier () {
85    var begin = index
86    var string = idstring()
87
88    if (licenses.indexOf(string) !== -1) {
89      return {
90        type: 'LICENSE',
91        string: string
92      }
93    } else if (exceptions.indexOf(string) !== -1) {
94      return {
95        type: 'EXCEPTION',
96        string: string
97      }
98    }
99
100    index = begin
101  }
102
103  // Tries to read the next token. Returns `undefined` if no token is
104  // recognized.
105  function parseToken () {
106    // Ordering matters
107    return (
108      operator() ||
109      documentRef() ||
110      licenseRef() ||
111      identifier()
112    )
113  }
114
115  var tokens = []
116  while (hasMore()) {
117    skipWhitespace()
118    if (!hasMore()) {
119      break
120    }
121
122    var token = parseToken()
123    if (!token) {
124      throw new Error('Unexpected `' + source[index] +
125                      '` at offset ' + index)
126    }
127
128    tokens.push(token)
129  }
130  return tokens
131}
132