You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

108 lines
2.6 KiB

4 years ago
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. function ignoreFunction() {}
  6. function createReturningFunction(value) {
  7. return function() {
  8. return value;
  9. };
  10. }
  11. function Parser(states) {
  12. this.states = this.compileStates(states);
  13. }
  14. Parser.prototype.compileStates = function(states) {
  15. var result = {};
  16. Object.keys(states).forEach(function(name) {
  17. result[name] = this.compileState(states[name], states);
  18. }, this);
  19. return result;
  20. };
  21. Parser.prototype.compileState = function(state, states) {
  22. var regExps = [];
  23. function iterator(str, value) {
  24. regExps.push({
  25. groups: Parser.getGroupCount(str),
  26. regExp: str,
  27. value: value
  28. });
  29. }
  30. function processState(statePart) {
  31. if(Array.isArray(statePart)) {
  32. statePart.forEach(processState);
  33. } else if(typeof statePart === "object") {
  34. Object.keys(statePart).forEach(function(key) {
  35. iterator(key, statePart[key]);
  36. });
  37. } else if(typeof statePart === "string") {
  38. processState(states[statePart]);
  39. } else {
  40. throw new Error("Unexpected 'state' format");
  41. }
  42. }
  43. processState(state);
  44. var total = regExps.map(function(r) {
  45. return "(" + r.regExp + ")";
  46. }).join("|");
  47. var actions = [];
  48. var pos = 1;
  49. regExps.forEach(function(r) {
  50. var fn;
  51. if(typeof r.value === "function") {
  52. fn = r.value;
  53. } else if(typeof r.value === "string") {
  54. fn = createReturningFunction(r.value);
  55. } else {
  56. fn = ignoreFunction;
  57. }
  58. actions.push({
  59. name: r.regExp,
  60. fn: fn,
  61. pos: pos,
  62. pos2: pos + r.groups + 1
  63. });
  64. pos += r.groups + 1;
  65. });
  66. return {
  67. regExp: new RegExp(total, "g"),
  68. actions: actions
  69. };
  70. };
  71. Parser.getGroupCount = function(regExpStr) {
  72. return new RegExp("(" + regExpStr + ")|^$").exec("").length - 2;
  73. };
  74. Parser.prototype.parse = function(initialState, string, context) {
  75. context = context || {};
  76. var currentState = initialState;
  77. var currentIndex = 0;
  78. for(;;) {
  79. var state = this.states[currentState];
  80. var regExp = state.regExp;
  81. regExp.lastIndex = currentIndex;
  82. var match = regExp.exec(string);
  83. if(!match) return context;
  84. var actions = state.actions;
  85. currentIndex = state.regExp.lastIndex;
  86. for(var i = 0; i < actions.length; i++) {
  87. var action = actions[i];
  88. if(match[action.pos]) {
  89. var ret = action.fn.apply(context, Array.prototype.slice.call(match, action.pos, action.pos2).concat([state.regExp.lastIndex - match[0].length, match[0].length]));
  90. if(ret) {
  91. if(!(ret in this.states))
  92. throw new Error("State '" + ret + "' doesn't exist");
  93. currentState = ret;
  94. }
  95. break;
  96. }
  97. }
  98. }
  99. };
  100. module.exports = Parser;