bottom-up.test.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. var rules = require("../lib/rules");
  2. var bottomUp = require("../lib/bottom-up");
  3. var testing = require("../lib/testing");
  4. var TokenIterator = require("../lib/TokenIterator");
  5. var errors = require("../lib/errors");
  6. var results = require("../lib/parsing-results");
  7. var StringSource = require("../lib/StringSource");
  8. var assertIsSuccess = testing.assertIsSuccess;
  9. var assertIsSuccessWithValue = testing.assertIsSuccessWithValue;
  10. var assertIsFailure = testing.assertIsFailure;
  11. var assertIsFailureWithRemaining = testing.assertIsFailureWithRemaining;
  12. var assertIsError = testing.assertIsError;
  13. var Tokeniser = require("./Tokeniser");
  14. var Token = require("../lib/Token");
  15. var source = function(string, startIndex, endIndex) {
  16. return new StringSource(string).range(startIndex, endIndex);
  17. };
  18. var token = function(tokenType, value, source) {
  19. return new Token(tokenType, value, source);
  20. };
  21. var partialCallRule = bottomUp.infix("call", function(parser) {
  22. return rules.sequence(
  23. rules.token("symbol", "("),
  24. rules.sequence.capture(parser.rule()),
  25. rules.token("symbol", ")")
  26. ).head();
  27. }).map(function(left, arg) {
  28. return [left, arg];
  29. });
  30. var partialAddRule = bottomUp.infix("add", function(parser) {
  31. return rules.sequence(
  32. rules.token("symbol", "+"),
  33. rules.sequence.capture(parser.leftAssociative("add"))
  34. ).head();
  35. }).map(function(left, right) {
  36. return ["+", left, right];
  37. });
  38. var partialMultiplyRule = bottomUp.infix("multiply", function(parser) {
  39. return rules.sequence(
  40. rules.token("symbol", "*"),
  41. rules.sequence.capture(parser.leftAssociative("multiply"))
  42. ).head();
  43. }).map(function(left, right) {
  44. return ["*", left, right];
  45. });
  46. var partialPowerRule = bottomUp.infix("power", function(parser) {
  47. return rules.sequence(
  48. rules.token("symbol", "^"),
  49. rules.sequence.capture(parser.rightAssociative("power"))
  50. ).head();
  51. }).map(function(left, right) {
  52. return ["^", left, right];
  53. });
  54. exports.canParsePrefixExpression = function(test) {
  55. var rule = bottomUp.parser("expression",
  56. [rules.tokenOfType("identifier")],
  57. []
  58. ).rule();
  59. var result = parse(rule, [
  60. token("identifier", "blah", source("blah", 0, 4)),
  61. token("end", null, source("blah", 4, 4))
  62. ]);
  63. assertIsSuccess(test, result, {
  64. value: "blah",
  65. source: source("blah", 0, 4)
  66. });
  67. test.done();
  68. };
  69. exports.canParseSimpleInfixExpression = function(test) {
  70. var rule = bottomUp.parser("expression",
  71. [rules.tokenOfType("identifier")],
  72. [partialCallRule]
  73. ).rule();
  74. var result = parse(rule, [
  75. token("identifier", "print", source("print(name)", 0, 5)),
  76. token("symbol", "(", source("print(name)", 5, 6)),
  77. token("identifier", "name", source("print(name)", 6, 10)),
  78. token("symbol", ")", source("print(name)", 10, 11)),
  79. token("end", null, source("print(name)", 11, 11))
  80. ]);
  81. assertIsSuccess(test, result, {
  82. value: ["print", "name"],
  83. source: source("print(name)", 0, 11)
  84. });
  85. test.done();
  86. };
  87. exports.parsingStopsIfPrefixRuleFails = function(test) {
  88. var rule = bottomUp.parser("expression",
  89. [rules.tokenOfType("identifier")],
  90. [partialCallRule]
  91. ).rule();
  92. var result = parse(rule, [
  93. token("symbol", "(", source("(name)", 0, 1)),
  94. token("identifier", "name", source("(name)", 1, 5)),
  95. token("symbol", ")", source("(name)", 5, 6)),
  96. token("end", null, source("(name)", 6, 6))
  97. ]);
  98. assertIsFailure(test, result, {
  99. remaining: [
  100. token("symbol", "(", source("(name)", 0, 1)),
  101. token("identifier", "name", source("(name)", 1, 5)),
  102. token("symbol", ")", source("(name)", 5, 6)),
  103. token("end", null, source("(name)", 6, 6))
  104. ],
  105. errors: [errors.error({
  106. expected: "expression",
  107. actual: "symbol \"(\"",
  108. location: source("(name)", 0, 1)
  109. })]
  110. });
  111. test.done();
  112. };
  113. exports.canParseExpressionWithTwoLeftAssociativeOperators = function(test) {
  114. var expressionParser = bottomUp.parser("expression",
  115. [rules.tokenOfType("number")],
  116. [
  117. partialMultiplyRule,
  118. partialAddRule
  119. ]
  120. );
  121. var rule = expressionParser.rule();
  122. var result = parse(rule, [
  123. token("number", "1", source("1 * 2 * 3 + 4 * 5", 0, 1)),
  124. token("symbol", "*", source("1 * 2 * 3 + 4 * 5", 2, 3)),
  125. token("number", "2", source("1 * 2 * 3 + 4 * 5", 4, 5)),
  126. token("symbol", "*", source("1 * 2 * 3 + 4 * 5", 6, 7)),
  127. token("number", "3", source("1 * 2 * 3 + 4 * 5", 8, 9)),
  128. token("symbol", "+", source("1 * 2 * 3 + 4 * 5", 10, 11)),
  129. token("number", "4", source("1 * 2 * 3 + 4 * 5", 12, 13)),
  130. token("symbol", "*", source("1 * 2 * 3 + 4 * 5", 14, 15)),
  131. token("number", "5", source("1 * 2 * 3 + 4 * 5", 16, 17)),
  132. token("end", null, source("1 * 2 * 3 + 4 * 5", 17, 17))
  133. ]);
  134. assertIsSuccess(test, result, {
  135. value: ["+", ["*", ["*", "1", "2"], "3"], ["*", "4", "5"]],
  136. source: source("1 * 2 * 3 + 4 * 5", 0, 17)
  137. });
  138. test.done();
  139. };
  140. exports.canParseExpressionWithRightAssociativeOperators = function(test) {
  141. var expressionParser = bottomUp.parser("expression",
  142. [rules.tokenOfType("number")],
  143. [
  144. partialPowerRule,
  145. partialAddRule
  146. ]
  147. );
  148. var rule = expressionParser.rule();
  149. var result = parse(rule, [
  150. token("number", "1", source("1 ^ 2 ^ 3 + 4 ^ 5", 0, 1)),
  151. token("symbol", "^", source("1 ^ 2 ^ 3 + 4 ^ 5", 2, 3)),
  152. token("number", "2", source("1 ^ 2 ^ 3 + 4 ^ 5", 4, 5)),
  153. token("symbol", "^", source("1 ^ 2 ^ 3 + 4 ^ 5", 6, 7)),
  154. token("number", "3", source("1 ^ 2 ^ 3 + 4 ^ 5", 8, 9)),
  155. token("symbol", "+", source("1 ^ 2 ^ 3 + 4 ^ 5", 10, 11)),
  156. token("number", "4", source("1 ^ 2 ^ 3 + 4 ^ 5", 12, 13)),
  157. token("symbol", "^", source("1 ^ 2 ^ 3 + 4 ^ 5", 14, 15)),
  158. token("number", "5", source("1 ^ 2 ^ 3 + 4 ^ 5", 16, 17)),
  159. token("end", null, source("1 ^ 2 ^ 3 + 4 ^ 5", 17, 17))
  160. ]);
  161. assertIsSuccess(test, result, {
  162. value: ["+", ["^", "1", ["^", "2", "3"]], ["^", "4", "5"]],
  163. source: source("1 ^ 2 ^ 3 + 4 ^ 5", 0, 17)
  164. });
  165. test.done();
  166. };
  167. var parse = function(parser, tokens) {
  168. return parser(new TokenIterator(tokens));
  169. };