rules.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. var _ = require("underscore");
  2. var options = require("option");
  3. var results = require("./parsing-results");
  4. var errors = require("./errors");
  5. var lazyIterators = require("./lazy-iterators");
  6. exports.token = function(tokenType, value) {
  7. var matchValue = value !== undefined;
  8. return function(input) {
  9. var token = input.head();
  10. if (token && token.name === tokenType && (!matchValue || token.value === value)) {
  11. return results.success(token.value, input.tail(), token.source);
  12. } else {
  13. var expected = describeToken({name: tokenType, value: value});
  14. return describeTokenMismatch(input, expected);
  15. }
  16. };
  17. };
  18. exports.tokenOfType = function(tokenType) {
  19. return exports.token(tokenType);
  20. };
  21. exports.firstOf = function(name, parsers) {
  22. if (!_.isArray(parsers)) {
  23. parsers = Array.prototype.slice.call(arguments, 1);
  24. }
  25. return function(input) {
  26. return lazyIterators
  27. .fromArray(parsers)
  28. .map(function(parser) {
  29. return parser(input);
  30. })
  31. .filter(function(result) {
  32. return result.isSuccess() || result.isError();
  33. })
  34. .first() || describeTokenMismatch(input, name);
  35. };
  36. };
  37. exports.then = function(parser, func) {
  38. return function(input) {
  39. var result = parser(input);
  40. if (!result.map) {
  41. console.log(result);
  42. }
  43. return result.map(func);
  44. };
  45. };
  46. exports.sequence = function() {
  47. var parsers = Array.prototype.slice.call(arguments, 0);
  48. var rule = function(input) {
  49. var result = _.foldl(parsers, function(memo, parser) {
  50. var result = memo.result;
  51. var hasCut = memo.hasCut;
  52. if (!result.isSuccess()) {
  53. return {result: result, hasCut: hasCut};
  54. }
  55. var subResult = parser(result.remaining());
  56. if (subResult.isCut()) {
  57. return {result: result, hasCut: true};
  58. } else if (subResult.isSuccess()) {
  59. var values;
  60. if (parser.isCaptured) {
  61. values = result.value().withValue(parser, subResult.value());
  62. } else {
  63. values = result.value();
  64. }
  65. var remaining = subResult.remaining();
  66. var source = input.to(remaining);
  67. return {
  68. result: results.success(values, remaining, source),
  69. hasCut: hasCut
  70. };
  71. } else if (hasCut) {
  72. return {result: results.error(subResult.errors(), subResult.remaining()), hasCut: hasCut};
  73. } else {
  74. return {result: subResult, hasCut: hasCut};
  75. }
  76. }, {result: results.success(new SequenceValues(), input), hasCut: false}).result;
  77. var source = input.to(result.remaining());
  78. return result.map(function(values) {
  79. return values.withValue(exports.sequence.source, source);
  80. });
  81. };
  82. rule.head = function() {
  83. var firstCapture = _.find(parsers, isCapturedRule);
  84. return exports.then(
  85. rule,
  86. exports.sequence.extract(firstCapture)
  87. );
  88. };
  89. rule.map = function(func) {
  90. return exports.then(
  91. rule,
  92. function(result) {
  93. return func.apply(this, result.toArray());
  94. }
  95. );
  96. };
  97. function isCapturedRule(subRule) {
  98. return subRule.isCaptured;
  99. }
  100. return rule;
  101. };
  102. var SequenceValues = function(values, valuesArray) {
  103. this._values = values || {};
  104. this._valuesArray = valuesArray || [];
  105. };
  106. SequenceValues.prototype.withValue = function(rule, value) {
  107. if (rule.captureName && rule.captureName in this._values) {
  108. throw new Error("Cannot add second value for capture \"" + rule.captureName + "\"");
  109. } else {
  110. var newValues = _.clone(this._values);
  111. newValues[rule.captureName] = value;
  112. var newValuesArray = this._valuesArray.concat([value]);
  113. return new SequenceValues(newValues, newValuesArray);
  114. }
  115. };
  116. SequenceValues.prototype.get = function(rule) {
  117. if (rule.captureName in this._values) {
  118. return this._values[rule.captureName];
  119. } else {
  120. throw new Error("No value for capture \"" + rule.captureName + "\"");
  121. }
  122. };
  123. SequenceValues.prototype.toArray = function() {
  124. return this._valuesArray;
  125. };
  126. exports.sequence.capture = function(rule, name) {
  127. var captureRule = function() {
  128. return rule.apply(this, arguments);
  129. };
  130. captureRule.captureName = name;
  131. captureRule.isCaptured = true;
  132. return captureRule;
  133. };
  134. exports.sequence.extract = function(rule) {
  135. return function(result) {
  136. return result.get(rule);
  137. };
  138. };
  139. exports.sequence.applyValues = function(func) {
  140. // TODO: check captureName doesn't conflict with source or other captures
  141. var rules = Array.prototype.slice.call(arguments, 1);
  142. return function(result) {
  143. var values = rules.map(function(rule) {
  144. return result.get(rule);
  145. });
  146. return func.apply(this, values);
  147. };
  148. };
  149. exports.sequence.source = {
  150. captureName: "☃source☃"
  151. };
  152. exports.sequence.cut = function() {
  153. return function(input) {
  154. return results.cut(input);
  155. };
  156. };
  157. exports.optional = function(rule) {
  158. return function(input) {
  159. var result = rule(input);
  160. if (result.isSuccess()) {
  161. return result.map(options.some);
  162. } else if (result.isFailure()) {
  163. return results.success(options.none, input);
  164. } else {
  165. return result;
  166. }
  167. };
  168. };
  169. exports.zeroOrMoreWithSeparator = function(rule, separator) {
  170. return repeatedWithSeparator(rule, separator, false);
  171. };
  172. exports.oneOrMoreWithSeparator = function(rule, separator) {
  173. return repeatedWithSeparator(rule, separator, true);
  174. };
  175. var zeroOrMore = exports.zeroOrMore = function(rule) {
  176. return function(input) {
  177. var values = [];
  178. var result;
  179. while ((result = rule(input)) && result.isSuccess()) {
  180. input = result.remaining();
  181. values.push(result.value());
  182. }
  183. if (result.isError()) {
  184. return result;
  185. } else {
  186. return results.success(values, input);
  187. }
  188. };
  189. };
  190. exports.oneOrMore = function(rule) {
  191. return exports.oneOrMoreWithSeparator(rule, noOpRule);
  192. };
  193. function noOpRule(input) {
  194. return results.success(null, input);
  195. }
  196. var repeatedWithSeparator = function(rule, separator, isOneOrMore) {
  197. return function(input) {
  198. var result = rule(input);
  199. if (result.isSuccess()) {
  200. var mainRule = exports.sequence.capture(rule, "main");
  201. var remainingRule = zeroOrMore(exports.then(
  202. exports.sequence(separator, mainRule),
  203. exports.sequence.extract(mainRule)
  204. ));
  205. var remainingResult = remainingRule(result.remaining());
  206. return results.success([result.value()].concat(remainingResult.value()), remainingResult.remaining());
  207. } else if (isOneOrMore || result.isError()) {
  208. return result;
  209. } else {
  210. return results.success([], input);
  211. }
  212. };
  213. };
  214. exports.leftAssociative = function(leftRule, rightRule, func) {
  215. var rights;
  216. if (func) {
  217. rights = [{func: func, rule: rightRule}];
  218. } else {
  219. rights = rightRule;
  220. }
  221. rights = rights.map(function(right) {
  222. return exports.then(right.rule, function(rightValue) {
  223. return function(leftValue, source) {
  224. return right.func(leftValue, rightValue, source);
  225. };
  226. });
  227. });
  228. var repeatedRule = exports.firstOf.apply(null, ["rules"].concat(rights));
  229. return function(input) {
  230. var start = input;
  231. var leftResult = leftRule(input);
  232. if (!leftResult.isSuccess()) {
  233. return leftResult;
  234. }
  235. var repeatedResult = repeatedRule(leftResult.remaining());
  236. while (repeatedResult.isSuccess()) {
  237. var remaining = repeatedResult.remaining();
  238. var source = start.to(repeatedResult.remaining());
  239. var right = repeatedResult.value();
  240. leftResult = results.success(
  241. right(leftResult.value(), source),
  242. remaining,
  243. source
  244. );
  245. repeatedResult = repeatedRule(leftResult.remaining());
  246. }
  247. if (repeatedResult.isError()) {
  248. return repeatedResult;
  249. }
  250. return leftResult;
  251. };
  252. };
  253. exports.leftAssociative.firstOf = function() {
  254. return Array.prototype.slice.call(arguments, 0);
  255. };
  256. exports.nonConsuming = function(rule) {
  257. return function(input) {
  258. return rule(input).changeRemaining(input);
  259. };
  260. };
  261. var describeToken = function(token) {
  262. if (token.value) {
  263. return token.name + " \"" + token.value + "\"";
  264. } else {
  265. return token.name;
  266. }
  267. };
  268. function describeTokenMismatch(input, expected) {
  269. var error;
  270. var token = input.head();
  271. if (token) {
  272. error = errors.error({
  273. expected: expected,
  274. actual: describeToken(token),
  275. location: token.source
  276. });
  277. } else {
  278. error = errors.error({
  279. expected: expected,
  280. actual: "end of tokens"
  281. });
  282. }
  283. return results.failure([error], input);
  284. }