| 'use strict' | 
 |  | 
 | var licenses = [] | 
 |   .concat(require('spdx-license-ids')) | 
 |   .concat(require('spdx-license-ids/deprecated')) | 
 | var exceptions = require('spdx-exceptions') | 
 |  | 
 | module.exports = function (source) { | 
 |   var index = 0 | 
 |  | 
 |   function hasMore () { | 
 |     return index < source.length | 
 |   } | 
 |  | 
 |   // `value` can be a regexp or a string. | 
 |   // If it is recognized, the matching source string is returned and | 
 |   // the index is incremented. Otherwise `undefined` is returned. | 
 |   function read (value) { | 
 |     if (value instanceof RegExp) { | 
 |       var chars = source.slice(index) | 
 |       var match = chars.match(value) | 
 |       if (match) { | 
 |         index += match[0].length | 
 |         return match[0] | 
 |       } | 
 |     } else { | 
 |       if (source.indexOf(value, index) === index) { | 
 |         index += value.length | 
 |         return value | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   function skipWhitespace () { | 
 |     read(/[ ]*/) | 
 |   } | 
 |  | 
 |   function operator () { | 
 |     var string | 
 |     var possibilities = ['WITH', 'AND', 'OR', '(', ')', ':', '+'] | 
 |     for (var i = 0; i < possibilities.length; i++) { | 
 |       string = read(possibilities[i]) | 
 |       if (string) { | 
 |         break | 
 |       } | 
 |     } | 
 |  | 
 |     if (string === '+' && index > 1 && source[index - 2] === ' ') { | 
 |       throw new Error('Space before `+`') | 
 |     } | 
 |  | 
 |     return string && { | 
 |       type: 'OPERATOR', | 
 |       string: string | 
 |     } | 
 |   } | 
 |  | 
 |   function idstring () { | 
 |     return read(/[A-Za-z0-9-.]+/) | 
 |   } | 
 |  | 
 |   function expectIdstring () { | 
 |     var string = idstring() | 
 |     if (!string) { | 
 |       throw new Error('Expected idstring at offset ' + index) | 
 |     } | 
 |     return string | 
 |   } | 
 |  | 
 |   function documentRef () { | 
 |     if (read('DocumentRef-')) { | 
 |       var string = expectIdstring() | 
 |       return {type: 'DOCUMENTREF', string: string} | 
 |     } | 
 |   } | 
 |  | 
 |   function licenseRef () { | 
 |     if (read('LicenseRef-')) { | 
 |       var string = expectIdstring() | 
 |       return {type: 'LICENSEREF', string: string} | 
 |     } | 
 |   } | 
 |  | 
 |   function identifier () { | 
 |     var begin = index | 
 |     var string = idstring() | 
 |  | 
 |     if (licenses.indexOf(string) !== -1) { | 
 |       return { | 
 |         type: 'LICENSE', | 
 |         string: string | 
 |       } | 
 |     } else if (exceptions.indexOf(string) !== -1) { | 
 |       return { | 
 |         type: 'EXCEPTION', | 
 |         string: string | 
 |       } | 
 |     } | 
 |  | 
 |     index = begin | 
 |   } | 
 |  | 
 |   // Tries to read the next token. Returns `undefined` if no token is | 
 |   // recognized. | 
 |   function parseToken () { | 
 |     // Ordering matters | 
 |     return ( | 
 |       operator() || | 
 |       documentRef() || | 
 |       licenseRef() || | 
 |       identifier() | 
 |     ) | 
 |   } | 
 |  | 
 |   var tokens = [] | 
 |   while (hasMore()) { | 
 |     skipWhitespace() | 
 |     if (!hasMore()) { | 
 |       break | 
 |     } | 
 |  | 
 |     var token = parseToken() | 
 |     if (!token) { | 
 |       throw new Error('Unexpected `' + source[index] + | 
 |                       '` at offset ' + index) | 
 |     } | 
 |  | 
 |     tokens.push(token) | 
 |   } | 
 |   return tokens | 
 | } |