lut.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. // lut.js
  2. // MIT licensed, see LICENSE file
  3. // Copyright (c) 2013-2015 Olov Lassus <olov.lassus@gmail.com>
  4. "use strict";
  5. const assert = require("assert");
  6. const traverse = require("ordered-ast-traverse");
  7. const is = require("simple-is");
  8. module.exports = Lut;
  9. function Lut(ast, src) {
  10. assert(this instanceof Lut);
  11. const sparseBegins = new Array(src.length);
  12. const begins = [];
  13. const sparseEnds = new Array(src.length);
  14. const ends = [];
  15. let p = 0;
  16. const t0 = Date.now();
  17. traverse(ast, {pre: function(node) {
  18. // assert (node.range[0] >= p);
  19. if (node.type === "Program") {
  20. return;
  21. }
  22. p = node.range[0];
  23. if (!sparseBegins[p]) {
  24. sparseBegins[p] = node;
  25. }
  26. p = node.range[1];
  27. if (!sparseEnds[p]) {
  28. sparseEnds[p] = node;
  29. }
  30. }});
  31. for (let i in sparseBegins) {
  32. begins.push(sparseBegins[i]);
  33. }
  34. for (let i in sparseEnds) {
  35. ends.push(sparseEnds[i]);
  36. }
  37. const t1 = Date.now();
  38. // console.error(t1-t0)
  39. // begins and ends are compact arrays with nodes,
  40. // sorted on node.range[0/1] (unique)
  41. this.begins = begins;
  42. this.ends = ends;
  43. }
  44. Lut.prototype.findNodeFromPos = findNodeFromPos;
  45. Lut.prototype.findNodeBeforePos = findNodeBeforePos;
  46. // binary search lut to find node beginning at pos
  47. // or as close after pos as possible. null if none
  48. function findNodeFromPos(pos) {
  49. const lut = this.begins;
  50. assert(is.finitenumber(pos) && pos >= 0);
  51. let left = 0;
  52. let right = lut.length - 1;
  53. while (left < right) {
  54. const mid = Math.floor((left + right) / 2);
  55. assert(mid >= 0 && mid < lut.length);
  56. if (pos > lut[mid].range[0]) {
  57. left = mid + 1;
  58. }
  59. else {
  60. right = mid;
  61. }
  62. }
  63. if (left > right) {
  64. assert(last(lut).range[0] < pos);
  65. return null;
  66. }
  67. const found = left;
  68. const foundPos = lut[found].range[0];
  69. assert(foundPos >= pos);
  70. if (found >= 1) {
  71. const prevPos = lut[found - 1].range[0];
  72. assert(prevPos < pos);
  73. }
  74. return lut[found];
  75. }
  76. // binary search lut to find node ending (as in range[1]
  77. // at or before pos. null if none
  78. function findNodeBeforePos(pos) {
  79. const lut = this.ends;
  80. assert(is.finitenumber(pos) && pos >= 0);
  81. let left = 0;
  82. let right = lut.length - 1;
  83. while (left < right) {
  84. const mid = Math.ceil((left + right) / 2);
  85. assert(mid >= 0 && mid < lut.length);
  86. if (pos < lut[mid].range[1]) {
  87. right = mid - 1;
  88. }
  89. else {
  90. left = mid;
  91. }
  92. }
  93. if (left > right) {
  94. assert(lut[0].range[1] > pos);
  95. return null;
  96. }
  97. const found = left;
  98. const foundPos = lut[found].range[1];
  99. if(foundPos > pos) {
  100. return null;
  101. }
  102. if (found <= lut.length - 2) {
  103. const nextPos = lut[found + 1].range[1];
  104. assert(nextPos > pos);
  105. }
  106. return lut[found];
  107. }
  108. function last(arr) {
  109. return arr[arr.length - 1];
  110. }