bottom-up.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. var rules = require("./rules");
  2. var results = require("./parsing-results");
  3. exports.parser = function(name, prefixRules, infixRuleBuilders) {
  4. var self = {
  5. rule: rule,
  6. leftAssociative: leftAssociative,
  7. rightAssociative: rightAssociative
  8. };
  9. var infixRules = new InfixRules(infixRuleBuilders.map(createInfixRule));
  10. var prefixRule = rules.firstOf(name, prefixRules);
  11. function createInfixRule(infixRuleBuilder) {
  12. return {
  13. name: infixRuleBuilder.name,
  14. rule: lazyRule(infixRuleBuilder.ruleBuilder.bind(null, self))
  15. };
  16. }
  17. function rule() {
  18. return createRule(infixRules);
  19. }
  20. function leftAssociative(name) {
  21. return createRule(infixRules.untilExclusive(name));
  22. }
  23. function rightAssociative(name) {
  24. return createRule(infixRules.untilInclusive(name));
  25. }
  26. function createRule(infixRules) {
  27. return apply.bind(null, infixRules);
  28. }
  29. function apply(infixRules, tokens) {
  30. var leftResult = prefixRule(tokens);
  31. if (leftResult.isSuccess()) {
  32. return infixRules.apply(leftResult);
  33. } else {
  34. return leftResult;
  35. }
  36. }
  37. return self;
  38. };
  39. function InfixRules(infixRules) {
  40. function untilExclusive(name) {
  41. return new InfixRules(infixRules.slice(0, ruleNames().indexOf(name)));
  42. }
  43. function untilInclusive(name) {
  44. return new InfixRules(infixRules.slice(0, ruleNames().indexOf(name) + 1));
  45. }
  46. function ruleNames() {
  47. return infixRules.map(function(rule) {
  48. return rule.name;
  49. });
  50. }
  51. function apply(leftResult) {
  52. var currentResult;
  53. var source;
  54. while (true) {
  55. currentResult = applyToTokens(leftResult.remaining());
  56. if (currentResult.isSuccess()) {
  57. source = leftResult.source().to(currentResult.source());
  58. leftResult = results.success(
  59. currentResult.value()(leftResult.value(), source),
  60. currentResult.remaining(),
  61. source
  62. )
  63. } else if (currentResult.isFailure()) {
  64. return leftResult;
  65. } else {
  66. return currentResult;
  67. }
  68. }
  69. }
  70. function applyToTokens(tokens) {
  71. return rules.firstOf("infix", infixRules.map(function(infix) {
  72. return infix.rule;
  73. }))(tokens);
  74. }
  75. return {
  76. apply: apply,
  77. untilExclusive: untilExclusive,
  78. untilInclusive: untilInclusive
  79. }
  80. }
  81. exports.infix = function(name, ruleBuilder) {
  82. function map(func) {
  83. return exports.infix(name, function(parser) {
  84. var rule = ruleBuilder(parser);
  85. return function(tokens) {
  86. var result = rule(tokens);
  87. return result.map(function(right) {
  88. return function(left, source) {
  89. return func(left, right, source);
  90. };
  91. });
  92. };
  93. });
  94. }
  95. return {
  96. name: name,
  97. ruleBuilder: ruleBuilder,
  98. map: map
  99. };
  100. }
  101. // TODO: move into a sensible place and remove duplication
  102. var lazyRule = function(ruleBuilder) {
  103. var rule;
  104. return function(input) {
  105. if (!rule) {
  106. rule = ruleBuilder();
  107. }
  108. return rule(input);
  109. };
  110. };