scan.js 2.7KB

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