1 | var options = require("option");
|
2 |
|
3 | var rules = require("../lib/rules");
|
4 | var testing = require("../lib/testing");
|
5 | var TokenIterator = require("../lib/TokenIterator");
|
6 | var errors = require("../lib/errors");
|
7 | var results = require("../lib/parsing-results");
|
8 | var StringSource = require("../lib/StringSource");
|
9 | var assertIsSuccess = testing.assertIsSuccess;
|
10 | var assertIsSuccessWithValue = testing.assertIsSuccessWithValue;
|
11 | var assertIsFailure = testing.assertIsFailure;
|
12 | var assertIsFailureWithRemaining = testing.assertIsFailureWithRemaining;
|
13 | var assertIsError = testing.assertIsError;
|
14 | var Tokeniser = require("./Tokeniser");
|
15 | var Token = require("../lib/Token");
|
16 |
|
17 | var stringSourceRange = function(string, startIndex, endIndex) {
|
18 | return new StringSource(string).range(startIndex, endIndex);
|
19 | };
|
20 |
|
21 | var token = function(tokenType, value, source) {
|
22 | return new Token(tokenType, value, source);
|
23 | };
|
24 |
|
25 | var keyword = function(value) {
|
26 | return rules.token("keyword", value);
|
27 | };
|
28 |
|
29 | var identifier = function(value) {
|
30 | return rules.token("identifier", value);
|
31 | };
|
32 |
|
33 | exports.tokenRuleFailsIfInputIsEmpty = function(test) {
|
34 | var tokens = [];
|
35 | var rule = rules.token("keyword", "true");
|
36 | var result = rule(new TokenIterator(tokens));
|
37 | assertIsFailure(test, result, {
|
38 | remaining: [],
|
39 | errors: [errors.error({
|
40 | expected: "keyword \"true\"",
|
41 | actual: "end of tokens"
|
42 | })]
|
43 | });
|
44 | test.done();
|
45 | };
|
46 |
|
47 | exports.tokenRuleConsumeTokenWhenTokenIsOfCorrectType = function(test) {
|
48 | var parser = rules.token("keyword", "true");
|
49 | var result = parseString(parser, "true");
|
50 | assertIsSuccess(test, result, {
|
51 | value: "true",
|
52 | source: stringSourceRange("true", 0, 4)
|
53 | });
|
54 | test.done();
|
55 | };
|
56 |
|
57 | exports.parsingTokenFailsIfTokenIsOfWrongType = function(test) {
|
58 | var parser = rules.token("keyword", "true");
|
59 | var result = parseString(parser, "blah");
|
60 | assertIsFailure(test, result, {
|
61 | remaining: [
|
62 | token("identifier", "blah", stringSourceRange("blah", 0, 4)),
|
63 | token("end", null, stringSourceRange("blah", 4, 4))
|
64 | ],
|
65 | errors: [errors.error({
|
66 | expected: "keyword \"true\"",
|
67 | actual: "identifier \"blah\"",
|
68 | location: stringSourceRange("blah", 0, 4)
|
69 | })]
|
70 | });
|
71 | test.done();
|
72 | };
|
73 |
|
74 | exports.parsingTokenFailsIfTokenIsOfWrongValue = function(test) {
|
75 | var parser = rules.token("keyword", "true");
|
76 | var result = parseString(parser, "false");
|
77 | assertIsFailure(test, result, {
|
78 | remaining: [
|
79 | token("keyword", "false", stringSourceRange("false", 0, 5)),
|
80 | token("end", null, stringSourceRange("false", 5, 5))
|
81 | ],
|
82 | errors: [errors.error({
|
83 | expected: "keyword \"true\"",
|
84 | actual: "keyword \"false\"",
|
85 | location: stringSourceRange("false", 0, 5)
|
86 | })]
|
87 | });
|
88 | test.done();
|
89 | };
|
90 |
|
91 | exports.anyValueIsAcceptedIfValueOfTokenIsNotSpecified = function(test) {
|
92 | var parser = rules.token("keyword");
|
93 | var result = parseString(parser, "true");
|
94 | assertIsSuccess(test, result, {
|
95 | value: "true",
|
96 | source: stringSourceRange("true", 0, 4)
|
97 | });
|
98 | test.done();
|
99 | };
|
100 |
|
101 | exports.firstSuccessIsReturnedByFirstOf = function(test) {
|
102 | var trueParser = keyword("true");
|
103 | var falseParser = keyword("false");
|
104 | var evilParser = function() {
|
105 | throw new Error("Hahaha!");
|
106 | };
|
107 | var result = parseString(rules.firstOf("Boolean", trueParser, falseParser, evilParser), "false");
|
108 | assertIsSuccessWithValue(test, result, "false");
|
109 | test.done();
|
110 | };
|
111 |
|
112 | exports.firstOfFailsIfNoParsersMatch = function(test) {
|
113 | var trueParser = keyword("true");
|
114 | var falseParser = keyword("false");
|
115 | var result = parseString(rules.firstOf("Boolean", trueParser, falseParser), "blah");
|
116 | assertIsFailure(test, result, {
|
117 | remaining:[
|
118 | identifier("blah", stringSourceRange("blah", 0, 4)),
|
119 | token("end", null, stringSourceRange("blah", 4, 4))
|
120 | ],
|
121 | errors: [errors.error({
|
122 | expected: "Boolean",
|
123 | actual: "identifier \"blah\"",
|
124 | location: stringSourceRange("blah", 0, 4)
|
125 | })]
|
126 | });
|
127 | test.done();
|
128 | };
|
129 |
|
130 | exports.firstOfReturnsErrorIfSubRuleReturnsErrorEvenIfLaterRuleSucceeds = function(test) {
|
131 | var trueParser = rules.sequence(rules.sequence.cut(), keyword("true"));
|
132 | var falseParser = keyword("false");
|
133 | var result = parseString(rules.firstOf("Boolean", trueParser, falseParser), "false");
|
134 | assertIsError(test, result, {
|
135 | remaining:[
|
136 | keyword("false", stringSourceRange("false", 0, 5)),
|
137 | token("end", null, stringSourceRange("false", 5, 5))
|
138 | ],
|
139 | errors: [errors.error({
|
140 | expected: "keyword \"true\"",
|
141 | actual: "keyword \"false\"",
|
142 | location: stringSourceRange("false", 0, 5)
|
143 | })]
|
144 | });
|
145 | test.done();
|
146 | };
|
147 |
|
148 | exports.thenReturnsFailureIfOriginalResultIsFailure = function(test) {
|
149 | var parser = rules.then(keyword("true"), function() { return true; });
|
150 | var result = parseString(parser, "blah");
|
151 | assertIsFailure(test, result, {
|
152 | remaining:[
|
153 | identifier("blah", stringSourceRange("blah", 0, 4)),
|
154 | token("end", null, stringSourceRange("blah", 4, 4))
|
155 | ],
|
156 | errors: [errors.error({
|
157 | expected: "keyword \"true\"",
|
158 | actual: "identifier \"blah\"",
|
159 | location: stringSourceRange("blah", 0, 4)
|
160 | })]
|
161 | });
|
162 | test.done();
|
163 | };
|
164 |
|
165 | exports.thenMapsOverValueIfOriginalResultIsSuccess = function(test) {
|
166 | var parser = rules.then(keyword("true"), function() { return true; });
|
167 | var result = parseString(parser, "true");
|
168 | assertIsSuccessWithValue(test, result, true);
|
169 | test.done();
|
170 | };
|
171 |
|
172 | exports.sequenceSucceedsIfSubParsersCanBeAppliedInOrder = function(test) {
|
173 | var parser = rules.sequence(identifier("one"), identifier("two"));
|
174 | var result = parseString(parser, "one two");
|
175 | assertIsSuccess(test, result, {
|
176 | source: stringSourceRange("one two", 0, 7)
|
177 | });
|
178 | test.done();
|
179 | };
|
180 |
|
181 | exports.sequenceFailIfSubParserFails = function(test) {
|
182 | var parser = rules.sequence(identifier("("), identifier(")"));
|
183 | var result = parseString(parser, "(");
|
184 | assertIsFailure(test, result, {
|
185 | remaining:[token("end", null, stringSourceRange("(", 1, 1))],
|
186 | errors: [errors.error({
|
187 | expected: "identifier \")\"",
|
188 | actual: "end",
|
189 | location: stringSourceRange("(", 1, 1)
|
190 | })]
|
191 | });
|
192 | test.done();
|
193 | };
|
194 |
|
195 | exports.sequenceFailIfSubParserFailsAndFinalParserSucceeds = function(test) {
|
196 | var parser = rules.sequence(identifier("("), identifier(")"));
|
197 | var result = parseString(parser, ")");
|
198 | assertIsFailure(test, result, {
|
199 | remaining:[
|
200 | identifier(")", stringSourceRange(")", 0, 1)),
|
201 | token("end", null, stringSourceRange(")", 1, 1))
|
202 | ],
|
203 | errors: [errors.error({
|
204 | expected: "identifier \"(\"",
|
205 | actual: "identifier \")\"",
|
206 | location: stringSourceRange(")", 0, 1)
|
207 | })]
|
208 | });
|
209 | test.done();
|
210 | };
|
211 |
|
212 | exports.sequenceReturnsMapOfCapturedValues = function(test) {
|
213 | var name = rules.sequence.capture(identifier(), "name");
|
214 | var parser = rules.sequence(identifier("("), name, identifier(")"));
|
215 | var result = parseString(parser, "( bob )");
|
216 | assertIsSuccess(test, result);
|
217 | test.deepEqual(result.value().get(name), "bob");
|
218 | test.done();
|
219 | };
|
220 |
|
221 | exports.failureInSubRuleInSequenceBeforeCutCausesSequenceToFail = function(test) {
|
222 | var parser = rules.sequence(identifier("("), rules.sequence.cut(), identifier(), identifier(")"));
|
223 | var result = parseString(parser, "bob");
|
224 | assertIsFailure(test, result);
|
225 | test.done();
|
226 | };
|
227 |
|
228 | exports.failureInSubRuleInSequenceAfterCutCausesError = function(test) {
|
229 | var parser = rules.sequence(identifier("("), rules.sequence.cut(), identifier(), identifier(")"));
|
230 | var result = parseString(parser, "( true");
|
231 | assertIsError(test, result, {
|
232 | remaining: [
|
233 | token("keyword", "true", stringSourceRange("( true", 2, 6)),
|
234 | token("end", null, stringSourceRange("( true", 6, 6))
|
235 | ],
|
236 | errors: [errors.error({
|
237 | expected: "identifier",
|
238 | actual: "keyword \"true\"",
|
239 | location: stringSourceRange("( true", 2, 6)
|
240 | })]
|
241 | });
|
242 | test.done();
|
243 | };
|
244 |
|
245 | exports.canPullSingleValueOutOfCapturedValuesUsingExtract = function(test) {
|
246 | var name = rules.sequence.capture(identifier(), "name");
|
247 | var parser = rules.then(
|
248 | rules.sequence(identifier("("), name, identifier(")")),
|
249 | rules.sequence.extract(name)
|
250 | );
|
251 | var result = parseString(parser, "( bob )");
|
252 | assertIsSuccessWithValue(test, result, "bob");
|
253 | test.done();
|
254 | };
|
255 |
|
256 | exports.canPullSingleValueOutOfCapturedValuesUsingHeadOnSequenceRule = function(test) {
|
257 | var name = rules.sequence.capture(identifier(), "name");
|
258 | var parser =
|
259 | rules.sequence(identifier("("), name, identifier(")"))
|
260 | .head();
|
261 | var result = parseString(parser, "( bob )");
|
262 | assertIsSuccessWithValue(test, result, "bob");
|
263 | test.done();
|
264 | };
|
265 |
|
266 | exports.canApplyValuesFromSequenceToFunction = function(test) {
|
267 | var firstName = rules.sequence.capture(identifier(), "firstName");
|
268 | var secondName = rules.sequence.capture(identifier(), "secondName");
|
269 | var parser = rules.then(
|
270 | rules.sequence(
|
271 | secondName,
|
272 | identifier(","),
|
273 | firstName
|
274 | ),
|
275 | rules.sequence.applyValues(function(firstName, secondName) {
|
276 | return {first: firstName, second: secondName};
|
277 | }, firstName, secondName)
|
278 | );
|
279 | var result = parseString(parser, "Bobertson , Bob");
|
280 | assertIsSuccessWithValue(test, result, {first: "Bob", second: "Bobertson"});
|
281 | test.done();
|
282 | };
|
283 |
|
284 | exports.canApplyValuesAndSourceFromSequenceToFunctionUsingMapOnSequenceRule = function(test) {
|
285 | var firstName = rules.sequence.capture(identifier(), "firstName");
|
286 | var secondName = rules.sequence.capture(identifier());
|
287 | var parser = rules.sequence(
|
288 | secondName,
|
289 | identifier(","),
|
290 | firstName
|
291 | ).map(function(secondName, firstName, source) {
|
292 | return {first: firstName, second: secondName, source: source};
|
293 | });
|
294 | var result = parseString(parser, "Bobertson , Bob");
|
295 | assertIsSuccessWithValue(test, result, {
|
296 | first: "Bob",
|
297 | second: "Bobertson",
|
298 | source: stringSourceRange("Bobertson , Bob", 0, 15)
|
299 | });
|
300 | test.done();
|
301 | };
|
302 |
|
303 | exports.canApplyValuesWithSourceFromSequenceToFunction = function(test) {
|
304 | var firstName = rules.sequence.capture(identifier(), "firstName");
|
305 | var secondName = rules.sequence.capture(identifier(), "secondName");
|
306 | var parser = rules.then(
|
307 | rules.sequence(
|
308 | secondName,
|
309 | identifier(","),
|
310 | firstName
|
311 | ),
|
312 | rules.sequence.applyValues(function(firstName, secondName, source) {
|
313 | return {first: firstName, second: secondName, source: source};
|
314 | }, firstName, secondName, rules.sequence.source)
|
315 | );
|
316 | var result = parseString(parser, "Bobertson , Bob");
|
317 | assertIsSuccessWithValue(test, result, {
|
318 | first: "Bob",
|
319 | second: "Bobertson",
|
320 | source: stringSourceRange("Bobertson , Bob", 0, 15)
|
321 | });
|
322 | test.done();
|
323 | };
|
324 |
|
325 | exports.exceptionIfTryingToReadAValueThatHasntBeenCaptured = function(test) {
|
326 | var name = rules.sequence.capture(identifier(), "name");
|
327 | var parser = rules.sequence(identifier("("), identifier(")"));
|
328 | var result = parseString(parser, "( )");
|
329 | assertIsSuccess(test, result);
|
330 | try {
|
331 | result.value().get(name);
|
332 | test.ok(false, "Expected exception");
|
333 | } catch (error) {
|
334 | test.equal(error.message, "No value for capture \"name\"");
|
335 | }
|
336 | test.done();
|
337 | };
|
338 |
|
339 | exports.exceptionIfTryingToCaptureValueWithUsedName = function(test) {
|
340 | var firstName = rules.sequence.capture(identifier(), "name");
|
341 | var secondName = rules.sequence.capture(identifier(), "name");
|
342 | var parser = rules.sequence(secondName, identifier(","), firstName);
|
343 | try {
|
344 | parseString(parser, "Bobertson , Bob")
|
345 | test.ok(false, "Expected exception");
|
346 | } catch (error) {
|
347 | test.equal(error.message, "Cannot add second value for capture \"name\"");
|
348 | }
|
349 | test.done();
|
350 | };
|
351 |
|
352 | exports.optionalRuleDoesNothingIfValueDoesNotMatch = function(test) {
|
353 | var parser = rules.optional(identifier("("));
|
354 | var result = parseString(parser, "");
|
355 | assertIsSuccess(test, result);
|
356 | test.deepEqual(result.value(), options.none);
|
357 | test.done();
|
358 | };
|
359 |
|
360 | exports.optionalRuleConsumesInputIfPossible = function(test) {
|
361 | var parser = rules.optional(identifier("("));
|
362 | var result = parseString(parser, "(");
|
363 | assertIsSuccess(test, result);
|
364 | test.deepEqual(result.value(), options.some("("));
|
365 | test.done();
|
366 | };
|
367 |
|
368 | exports.optionalRulePreservesErrors = function(test) {
|
369 | var error = results.error([errors.error({
|
370 | expected: "something",
|
371 | actual: "something else"
|
372 | })]);
|
373 | var parser = rules.optional(function(input) {
|
374 | return error;
|
375 | });
|
376 | var result = parseString(parser, "");
|
377 | test.deepEqual(result, error);
|
378 | test.done();
|
379 | };
|
380 |
|
381 | exports.zeroOrMoreWithSeparatorParsesEmptyStringAndReturnsEmptyArray = function(test) {
|
382 | var parser = rules.zeroOrMoreWithSeparator(identifier(), identifier(","));
|
383 | var result = parseString(parser, "");
|
384 | assertIsSuccessWithValue(test, result, []);
|
385 | test.done();
|
386 | };
|
387 |
|
388 | exports.zeroOrMoreWithSeparatorParsesSingleInstanceOfRuleAndReturnsSingleElementArray = function(test) {
|
389 | var parser = rules.zeroOrMoreWithSeparator(identifier(), identifier(","));
|
390 | var result = parseString(parser, "blah");
|
391 | assertIsSuccessWithValue(test, result, ["blah"]);
|
392 | test.done();
|
393 | };
|
394 |
|
395 | exports.zeroOrMoreWithSeparatorParsesMultipleInstanceOfRuleAndReturnsArray = function(test) {
|
396 | var parser = rules.zeroOrMoreWithSeparator(identifier(), identifier(","));
|
397 | var result = parseString(parser, "apple , banana , coconut");
|
398 | assertIsSuccessWithValue(test, result, ["apple", "banana", "coconut"]);
|
399 | test.done();
|
400 | };
|
401 |
|
402 | exports.zeroOrMoreWithSeparatorDoesNotConsumeFinalSeparatorIfItIsNotFollowedByMainRule = function(test) {
|
403 | var parser = rules.zeroOrMoreWithSeparator(identifier(), identifier(","));
|
404 | var result = parseString(parser, "apple , banana ,");
|
405 | assertIsSuccess(test, result, {
|
406 | remaining: [
|
407 | token("identifier", ",", stringSourceRange("apple , banana ,", 15, 16)),
|
408 | token("end", null, stringSourceRange("apple , banana ,", 16, 16))
|
409 | ],
|
410 | });
|
411 | test.done();
|
412 | };
|
413 |
|
414 | exports.zeroOrMoreReturnsErrorIfFirstUseOfRuleReturnsError = function(test) {
|
415 | var parser = rules.zeroOrMoreWithSeparator(
|
416 | rules.sequence(identifier(), rules.sequence.cut(), identifier()),
|
417 | identifier(",")
|
418 | );
|
419 | var result = parseString(parser, "apple");
|
420 | assertIsError(test, result);
|
421 | test.done();
|
422 | };
|
423 |
|
424 | exports.zeroOrMoreParsesEmptyStringAndReturnsEmptyArray = function(test) {
|
425 | var parser = rules.zeroOrMore(identifier());
|
426 | var result = parseString(parser, "");
|
427 | assertIsSuccessWithValue(test, result, []);
|
428 | test.done();
|
429 | };
|
430 |
|
431 | exports.zeroOrMoreParsesSingleInstanceOfRuleAndReturnsSingleElementArray = function(test) {
|
432 | var parser = rules.zeroOrMore(identifier());
|
433 | var result = parseString(parser, "blah");
|
434 | assertIsSuccessWithValue(test, result, ["blah"]);
|
435 | test.done();
|
436 | };
|
437 |
|
438 | exports.zeroOrMoreParsesMultipleInstanceOfRuleAndReturnsArray = function(test) {
|
439 | var parser = rules.zeroOrMore(identifier());
|
440 | var result = parseString(parser, "( , )");
|
441 | assertIsSuccessWithValue(test, result, ["(", ",", ")"]);
|
442 | test.done();
|
443 | };
|
444 |
|
445 | exports.zeroOrMoreReturnsErrorIfSubRuleReturnsError = function(test) {
|
446 | var parser = rules.zeroOrMore(
|
447 | rules.sequence(identifier(), rules.sequence.cut(), identifier(";"))
|
448 | );
|
449 | var result = parseString(parser, "blah");
|
450 | assertIsError(test, result, {
|
451 | remaining:[
|
452 | token("end", null, stringSourceRange("blah", 4, 4))
|
453 | ],
|
454 | errors: [errors.error({
|
455 | expected: "identifier \";\"",
|
456 | actual: "end",
|
457 | location: stringSourceRange("blah", 4, 4)
|
458 | })]
|
459 | });
|
460 | test.done();
|
461 | };
|
462 |
|
463 | exports.oneOrMoreWithSeparatorFailsOnEmptyString = function(test) {
|
464 | var parser = rules.oneOrMoreWithSeparator(identifier(), identifier(","));
|
465 | var result = parseString(parser, "");
|
466 | assertIsFailure(test, result, {
|
467 | remaining:[
|
468 | token("end", null, stringSourceRange("", 0, 0))
|
469 | ],
|
470 | errors: [errors.error({
|
471 | expected: "identifier",
|
472 | actual: "end",
|
473 | location: stringSourceRange("", 0, 0)
|
474 | })]
|
475 | });
|
476 | test.done();
|
477 | };
|
478 |
|
479 | exports.oneOrMoreWithSeparatorParsesSingleInstanceOfRuleAndReturnsSingleElementArray = function(test) {
|
480 | var parser = rules.oneOrMoreWithSeparator(identifier(), identifier(","));
|
481 | var result = parseString(parser, "blah");
|
482 | assertIsSuccessWithValue(test, result, ["blah"]);
|
483 | test.done();
|
484 | };
|
485 |
|
486 | exports.oneOrMoreWithSeparatorParsesMultipleInstanceOfRuleAndReturnsArray = function(test) {
|
487 | var parser = rules.oneOrMoreWithSeparator(identifier(), identifier(","));
|
488 | var result = parseString(parser, "apple , banana , coconut");
|
489 | assertIsSuccessWithValue(test, result, ["apple", "banana", "coconut"]);
|
490 | test.done();
|
491 | };
|
492 |
|
493 | exports.oneOrMoreFailsOnEmptyString = function(test) {
|
494 | var parser = rules.oneOrMore(identifier());
|
495 | var result = parseString(parser, "");
|
496 | assertIsFailure(test, result, {
|
497 | remaining:[
|
498 | token("end", null, stringSourceRange("", 0, 0))
|
499 | ],
|
500 | errors: [errors.error({
|
501 | expected: "identifier",
|
502 | actual: "end",
|
503 | location: stringSourceRange("", 0, 0)
|
504 | })]
|
505 | });
|
506 | test.done();
|
507 | };
|
508 |
|
509 | exports.oneOrMoreParsesSingleInstanceOfRuleAndReturnsSingleElementArray = function(test) {
|
510 | var parser = rules.oneOrMore(identifier());
|
511 | var result = parseString(parser, "blah");
|
512 | assertIsSuccessWithValue(test, result, ["blah"]);
|
513 | test.done();
|
514 | };
|
515 |
|
516 | exports.oneOrMoreParsesMultipleInstanceOfRuleAndReturnsArray = function(test) {
|
517 | var parser = rules.oneOrMore(identifier());
|
518 | var result = parseString(parser, "apple banana coconut");
|
519 | assertIsSuccessWithValue(test, result, ["apple", "banana", "coconut"]);
|
520 | test.done();
|
521 | };
|
522 |
|
523 | exports.leftAssociativeConsumesNothingIfLeftHandSideDoesntMatch = function(test) {
|
524 | var parser = rules.leftAssociative(
|
525 | keyword(),
|
526 | identifier("+"),
|
527 | function(left, right) {
|
528 | return [left, right];
|
529 | }
|
530 | );
|
531 | var result = parseString(parser, "+ +");
|
532 | assertIsFailure(test, result, {
|
533 | remaining:[
|
534 | token("identifier", "+", stringSourceRange("+ +", 0, 1)),
|
535 | token("identifier", "+", stringSourceRange("+ +", 2, 3)),
|
536 | token("end", null, stringSourceRange("+ +", 3, 3))
|
537 | ],
|
538 | errors: [errors.error({
|
539 | expected: "keyword",
|
540 | actual: "identifier \"+\"",
|
541 | location: stringSourceRange("+ +", 0, 1)
|
542 | })]
|
543 | });
|
544 | test.done();
|
545 | };
|
546 |
|
547 | exports.leftAssociativeReturnsValueOfLeftHandSideIfRightHandSideDoesntMatch = function(test) {
|
548 | var parser = rules.leftAssociative(
|
549 | identifier(),
|
550 | identifier("+"),
|
551 | function(left, right) {
|
552 | return [left, right];
|
553 | }
|
554 | );
|
555 | var result = parseString(parser, "apple");
|
556 | assertIsSuccessWithValue(test, result, "apple");
|
557 | test.done();
|
558 | };
|
559 |
|
560 | exports.leftAssociativeAllowsLeftAssociativeRules = function(test) {
|
561 | var parser = rules.leftAssociative(
|
562 | identifier(),
|
563 | identifier("+"),
|
564 | function(left, right) {
|
565 | return [left, right];
|
566 | }
|
567 | );
|
568 | var result = parseString(parser, "apple + +");
|
569 | assertIsSuccessWithValue(test, result, [["apple", "+"], "+"]);
|
570 | test.done();
|
571 | };
|
572 |
|
573 | exports.leftAssociativeCanHaveMultipleChoicesForRight = function(test) {
|
574 | var parser = rules.leftAssociative(
|
575 | identifier(),
|
576 | rules.leftAssociative.firstOf(
|
577 | {rule: identifier("+"), func: function(left, right) { return [left, right]; }},
|
578 | {rule: identifier(","), func: function(left, right) { return [left]; }}
|
579 | )
|
580 | );
|
581 | var result = parseString(parser, "apple + ,");
|
582 | assertIsSuccessWithValue(test, result, [["apple", "+"]]);
|
583 | test.done();
|
584 | };
|
585 |
|
586 | exports.leftAssociativeReturnsErrorIfRightHandSideReturnsError = function(test) {
|
587 | var parser = rules.leftAssociative(
|
588 | identifier(),
|
589 | rules.leftAssociative.firstOf(
|
590 | {rule: rules.sequence(rules.sequence.cut(), identifier("+")), func: function() {}}
|
591 | )
|
592 | );
|
593 | var result = parseString(parser, "apple");
|
594 | assertIsError(test, result);
|
595 | test.done();
|
596 | };
|
597 |
|
598 | exports.nonConsumingRuleDoesNotConsumeInput = function(test) {
|
599 | var parser = rules.nonConsuming(rules.token("keyword", "true"));
|
600 | var result = parseString(parser, "true");
|
601 | assertIsSuccess(test, result, {
|
602 | value: "true",
|
603 | source: stringSourceRange("true", 0, 4),
|
604 | remaining: [token("keyword", "true"), token("end", null)]
|
605 | });
|
606 | test.done();
|
607 | };
|
608 |
|
609 | var parseString = function(parser, string) {
|
610 | var keywords = ["true", "false"];
|
611 | var tokens = new Tokeniser({keywords: keywords}).tokenise(string);
|
612 | return parser(new TokenIterator(tokens));
|
613 | };
|
614 |
|
615 | var parseTokens = function(parser, tokens) {
|
616 | return parser(new TokenIterator(tokens));
|
617 | };
|