| '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 | 
| } |