All new
alle new
This commit is contained in:
1 parent
da64472251
commit
5b20adee48
1605 files changed
+396517
-1674
No files matched your search
+21
@@ -0,0 +1,21 @@
|
||||
var StringSource = require("../lib/StringSource");
|
||||
|
||||
exports.stringSourceRangeDescriptionIncludesLineAndCharacterNumber = function(test) {
|
||||
test.equal(
|
||||
"Line number: 1\nCharacter number: 3",
|
||||
new StringSource("blah").range(2, 3).describe()
|
||||
);
|
||||
test.equal(
|
||||
"Line number: 3\nCharacter number: 5",
|
||||
new StringSource("one\ntwo\nthree\nfour").range(12, 15).describe()
|
||||
);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.stringSourceRangeDescriptionIncludesDescriptionIfProvided = function(test) {
|
||||
test.equal(
|
||||
"File: /some/file\nLine number: 1\nCharacter number: 3",
|
||||
new StringSource("blah", "File: /some/file").range(2, 3).describe()
|
||||
);
|
||||
test.done();
|
||||
};
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
var TokenIterator = require("../lib/TokenIterator");
|
||||
var Token = require("../lib/Token");
|
||||
var StringSource = require("../lib/StringSource");
|
||||
|
||||
exports.canCreateSourceRangeToIteratorBeyondEnd = function(test) {
|
||||
var source = function(startIndex, endIndex) {
|
||||
return new StringSource("blah").range(startIndex, endIndex);
|
||||
};
|
||||
var startIterator = new TokenIterator([
|
||||
new Token("identifier", "blah", source(0, 4)),
|
||||
new Token("end", null, source(4, 4))
|
||||
]);
|
||||
var endIterator = startIterator.tail().tail();
|
||||
var range = startIterator.to(endIterator);
|
||||
test.deepEqual(source(0, 4), range);
|
||||
test.done();
|
||||
};
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
var Token = require("../lib/Token");
|
||||
var StringSource = require("../lib/StringSource");
|
||||
|
||||
var Tokeniser = module.exports = function(options) {
|
||||
var keywords = options.keywords;
|
||||
var tokenise = function(input) {
|
||||
var source = new StringSource(input);
|
||||
|
||||
var createToken = function(startIndex, endIndex) {
|
||||
var value = input.substring(startIndex, endIndex);
|
||||
var tokenType = keywords.indexOf(value) === -1 ? "identifier" : "keyword";
|
||||
return new Token(
|
||||
tokenType,
|
||||
value,
|
||||
source.range(startIndex, endIndex)
|
||||
);
|
||||
};
|
||||
|
||||
var position = 0;
|
||||
var tokens = [];
|
||||
var done = input === "";
|
||||
|
||||
while (!done) {
|
||||
var nextWhitespace = indexOfRegex(input, /\s/, position);
|
||||
if (nextWhitespace === -1) {
|
||||
done = true;
|
||||
tokens.push(createToken(position, input.length));
|
||||
} else {
|
||||
tokens.push(createToken(position, nextWhitespace));
|
||||
position = indexOfRegex(input, /\S/, nextWhitespace);
|
||||
}
|
||||
}
|
||||
tokens.push(new Token("end", null, source.range(input.length, input.length)));
|
||||
return tokens;
|
||||
};
|
||||
|
||||
var indexOfRegex = function(string, regex, startIndex) {
|
||||
startIndex = startIndex || 0;
|
||||
var index = string.substring(startIndex).search(regex);
|
||||
if (index === -1) {
|
||||
return -1;
|
||||
} else {
|
||||
return index + startIndex;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
tokenise: tokenise
|
||||
};
|
||||
};
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
var Tokeniser = require("./Tokeniser");
|
||||
var Token = require("../lib/Token");
|
||||
var StringSource = require("../lib/StringSource");
|
||||
|
||||
exports.stringIsSingleIdentifier = stringIsTokenisedTo("blah", [
|
||||
new Token("identifier", "blah", stringSourceRange("blah", 0, 4)),
|
||||
new Token("end", null, stringSourceRange("blah", 4, 4))
|
||||
]);
|
||||
|
||||
exports.identifiersAreSeparatedByWhitespace = stringIsTokenisedTo("one two", [
|
||||
new Token("identifier", "one", stringSourceRange("one two", 0, 3)),
|
||||
new Token("identifier", "two", stringSourceRange("one two", 4, 7)),
|
||||
new Token("end", null, stringSourceRange("one two", 7, 7))
|
||||
]);
|
||||
|
||||
exports.canDetectKeywords = stringIsTokenisedTo("true", [
|
||||
new Token("keyword", "true", stringSourceRange("true", 0, 4)),
|
||||
new Token("end", null, stringSourceRange("true", 4, 4))
|
||||
]);
|
||||
|
||||
exports.emptyStringIsTokenisedToSingleEndToken = stringIsTokenisedTo("", [
|
||||
new Token("end", null, stringSourceRange("", 0, 0))
|
||||
]);
|
||||
|
||||
function stringIsTokenisedTo(input, expected) {
|
||||
return function(test) {
|
||||
test.deepEqual(expected, tokenise(input));
|
||||
test.done();
|
||||
};
|
||||
};
|
||||
|
||||
function stringSourceRange(string, startIndex, endIndex) {
|
||||
return new StringSource(string).range(startIndex, endIndex);
|
||||
};
|
||||
|
||||
function tokenise(input) {
|
||||
return new Tokeniser({keywords: ["true"]}).tokenise(input);
|
||||
};
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
var rules = require("../lib/rules");
|
||||
var bottomUp = require("../lib/bottom-up");
|
||||
var testing = require("../lib/testing");
|
||||
var TokenIterator = require("../lib/TokenIterator");
|
||||
var errors = require("../lib/errors");
|
||||
var results = require("../lib/parsing-results");
|
||||
var StringSource = require("../lib/StringSource");
|
||||
var assertIsSuccess = testing.assertIsSuccess;
|
||||
var assertIsSuccessWithValue = testing.assertIsSuccessWithValue;
|
||||
var assertIsFailure = testing.assertIsFailure;
|
||||
var assertIsFailureWithRemaining = testing.assertIsFailureWithRemaining;
|
||||
var assertIsError = testing.assertIsError;
|
||||
var Tokeniser = require("./Tokeniser");
|
||||
var Token = require("../lib/Token");
|
||||
|
||||
var source = function(string, startIndex, endIndex) {
|
||||
return new StringSource(string).range(startIndex, endIndex);
|
||||
};
|
||||
|
||||
var token = function(tokenType, value, source) {
|
||||
return new Token(tokenType, value, source);
|
||||
};
|
||||
|
||||
var partialCallRule = bottomUp.infix("call", function(parser) {
|
||||
return rules.sequence(
|
||||
rules.token("symbol", "("),
|
||||
rules.sequence.capture(parser.rule()),
|
||||
rules.token("symbol", ")")
|
||||
).head();
|
||||
}).map(function(left, arg) {
|
||||
return [left, arg];
|
||||
});
|
||||
|
||||
var partialAddRule = bottomUp.infix("add", function(parser) {
|
||||
return rules.sequence(
|
||||
rules.token("symbol", "+"),
|
||||
rules.sequence.capture(parser.leftAssociative("add"))
|
||||
).head();
|
||||
}).map(function(left, right) {
|
||||
return ["+", left, right];
|
||||
});
|
||||
|
||||
var partialMultiplyRule = bottomUp.infix("multiply", function(parser) {
|
||||
return rules.sequence(
|
||||
rules.token("symbol", "*"),
|
||||
rules.sequence.capture(parser.leftAssociative("multiply"))
|
||||
).head();
|
||||
}).map(function(left, right) {
|
||||
return ["*", left, right];
|
||||
});
|
||||
|
||||
var partialPowerRule = bottomUp.infix("power", function(parser) {
|
||||
return rules.sequence(
|
||||
rules.token("symbol", "^"),
|
||||
rules.sequence.capture(parser.rightAssociative("power"))
|
||||
).head();
|
||||
}).map(function(left, right) {
|
||||
return ["^", left, right];
|
||||
});
|
||||
|
||||
exports.canParsePrefixExpression = function(test) {
|
||||
var rule = bottomUp.parser("expression",
|
||||
[rules.tokenOfType("identifier")],
|
||||
[]
|
||||
).rule();
|
||||
var result = parse(rule, [
|
||||
token("identifier", "blah", source("blah", 0, 4)),
|
||||
token("end", null, source("blah", 4, 4))
|
||||
]);
|
||||
assertIsSuccess(test, result, {
|
||||
value: "blah",
|
||||
source: source("blah", 0, 4)
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.canParseSimpleInfixExpression = function(test) {
|
||||
var rule = bottomUp.parser("expression",
|
||||
[rules.tokenOfType("identifier")],
|
||||
[partialCallRule]
|
||||
).rule();
|
||||
|
||||
var result = parse(rule, [
|
||||
token("identifier", "print", source("print(name)", 0, 5)),
|
||||
token("symbol", "(", source("print(name)", 5, 6)),
|
||||
token("identifier", "name", source("print(name)", 6, 10)),
|
||||
token("symbol", ")", source("print(name)", 10, 11)),
|
||||
token("end", null, source("print(name)", 11, 11))
|
||||
]);
|
||||
assertIsSuccess(test, result, {
|
||||
value: ["print", "name"],
|
||||
source: source("print(name)", 0, 11)
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.parsingStopsIfPrefixRuleFails = function(test) {
|
||||
var rule = bottomUp.parser("expression",
|
||||
[rules.tokenOfType("identifier")],
|
||||
[partialCallRule]
|
||||
).rule();
|
||||
|
||||
var result = parse(rule, [
|
||||
token("symbol", "(", source("(name)", 0, 1)),
|
||||
token("identifier", "name", source("(name)", 1, 5)),
|
||||
token("symbol", ")", source("(name)", 5, 6)),
|
||||
token("end", null, source("(name)", 6, 6))
|
||||
]);
|
||||
assertIsFailure(test, result, {
|
||||
remaining: [
|
||||
token("symbol", "(", source("(name)", 0, 1)),
|
||||
token("identifier", "name", source("(name)", 1, 5)),
|
||||
token("symbol", ")", source("(name)", 5, 6)),
|
||||
token("end", null, source("(name)", 6, 6))
|
||||
],
|
||||
errors: [errors.error({
|
||||
expected: "expression",
|
||||
actual: "symbol \"(\"",
|
||||
location: source("(name)", 0, 1)
|
||||
})]
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.canParseExpressionWithTwoLeftAssociativeOperators = function(test) {
|
||||
var expressionParser = bottomUp.parser("expression",
|
||||
[rules.tokenOfType("number")],
|
||||
[
|
||||
partialMultiplyRule,
|
||||
partialAddRule
|
||||
]
|
||||
);
|
||||
|
||||
var rule = expressionParser.rule();
|
||||
|
||||
var result = parse(rule, [
|
||||
token("number", "1", source("1 * 2 * 3 + 4 * 5", 0, 1)),
|
||||
token("symbol", "*", source("1 * 2 * 3 + 4 * 5", 2, 3)),
|
||||
token("number", "2", source("1 * 2 * 3 + 4 * 5", 4, 5)),
|
||||
token("symbol", "*", source("1 * 2 * 3 + 4 * 5", 6, 7)),
|
||||
token("number", "3", source("1 * 2 * 3 + 4 * 5", 8, 9)),
|
||||
token("symbol", "+", source("1 * 2 * 3 + 4 * 5", 10, 11)),
|
||||
token("number", "4", source("1 * 2 * 3 + 4 * 5", 12, 13)),
|
||||
token("symbol", "*", source("1 * 2 * 3 + 4 * 5", 14, 15)),
|
||||
token("number", "5", source("1 * 2 * 3 + 4 * 5", 16, 17)),
|
||||
token("end", null, source("1 * 2 * 3 + 4 * 5", 17, 17))
|
||||
]);
|
||||
assertIsSuccess(test, result, {
|
||||
value: ["+", ["*", ["*", "1", "2"], "3"], ["*", "4", "5"]],
|
||||
source: source("1 * 2 * 3 + 4 * 5", 0, 17)
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.canParseExpressionWithRightAssociativeOperators = function(test) {
|
||||
var expressionParser = bottomUp.parser("expression",
|
||||
[rules.tokenOfType("number")],
|
||||
[
|
||||
partialPowerRule,
|
||||
partialAddRule
|
||||
]
|
||||
);
|
||||
|
||||
var rule = expressionParser.rule();
|
||||
|
||||
var result = parse(rule, [
|
||||
token("number", "1", source("1 ^ 2 ^ 3 + 4 ^ 5", 0, 1)),
|
||||
token("symbol", "^", source("1 ^ 2 ^ 3 + 4 ^ 5", 2, 3)),
|
||||
token("number", "2", source("1 ^ 2 ^ 3 + 4 ^ 5", 4, 5)),
|
||||
token("symbol", "^", source("1 ^ 2 ^ 3 + 4 ^ 5", 6, 7)),
|
||||
token("number", "3", source("1 ^ 2 ^ 3 + 4 ^ 5", 8, 9)),
|
||||
token("symbol", "+", source("1 ^ 2 ^ 3 + 4 ^ 5", 10, 11)),
|
||||
token("number", "4", source("1 ^ 2 ^ 3 + 4 ^ 5", 12, 13)),
|
||||
token("symbol", "^", source("1 ^ 2 ^ 3 + 4 ^ 5", 14, 15)),
|
||||
token("number", "5", source("1 ^ 2 ^ 3 + 4 ^ 5", 16, 17)),
|
||||
token("end", null, source("1 ^ 2 ^ 3 + 4 ^ 5", 17, 17))
|
||||
]);
|
||||
assertIsSuccess(test, result, {
|
||||
value: ["+", ["^", "1", ["^", "2", "3"]], ["^", "4", "5"]],
|
||||
source: source("1 ^ 2 ^ 3 + 4 ^ 5", 0, 17)
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
var parse = function(parser, tokens) {
|
||||
return parser(new TokenIterator(tokens));
|
||||
};
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
var errors = require("../lib/errors");
|
||||
var StringSource = require("../lib/StringSource");
|
||||
|
||||
exports.errorDescriptionIncludesLocationAndActualValueAndExpectedValue = function(test) {
|
||||
var error = errors.error({
|
||||
expected: "Nothing",
|
||||
actual: "Something",
|
||||
location: {
|
||||
describe: function() {
|
||||
return "Here"
|
||||
}
|
||||
}
|
||||
});
|
||||
test.equal("Here:\nExpected Nothing\nbut got Something", error.describe());
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.canDescribeErrorWithoutLocation = function(test) {
|
||||
var error = errors.error({
|
||||
expected: "Nothing",
|
||||
actual: "Something"
|
||||
});
|
||||
test.equal("Expected Nothing\nbut got Something", error.describe());
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.canGetPositionFromError = function(test) {
|
||||
var error = errors.error({
|
||||
expected: "Nothing",
|
||||
actual: "Something",
|
||||
location: new StringSource("abc\ndef\nghi\n", "").range(6, 8)
|
||||
});
|
||||
test.equal(2, error.lineNumber());
|
||||
test.equal(3, error.characterNumber());
|
||||
test.done();
|
||||
};
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
var lazyIterators = require("../lib/lazy-iterators");
|
||||
|
||||
exports.convertingArrayToIteratorAndBack = function(test) {
|
||||
var original = ["apple", "banana", "coconut"];
|
||||
var iterator = lazyIterators.fromArray(original);
|
||||
var reconstituted = iterator.toArray();
|
||||
test.deepEqual(original, reconstituted);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.mapAppliesFunctionToEveryElement = function(test) {
|
||||
var original = [1, 2, 3];
|
||||
var squared = lazyIterators.fromArray(original).map(function(i) {
|
||||
return i * i;
|
||||
}).toArray();
|
||||
test.deepEqual([1, 4, 9], squared);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.mappingFunctionIsOnlyCalledWhenNecessary = function(test) {
|
||||
var original = [1, 2, 3];
|
||||
var faultySquared = lazyIterators.fromArray(original).map(function(i) {
|
||||
if (i === 1) {
|
||||
return i;
|
||||
} else {
|
||||
throw new Error("Can only calculate the square of one");
|
||||
}
|
||||
}).first();
|
||||
test.deepEqual(1, faultySquared);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.filterRetainsElementsThatSatisfyPredicate = function(test) {
|
||||
var original = [1, 2, 3, 4];
|
||||
var even = lazyIterators.fromArray(original).filter(function(i) {
|
||||
return i % 2 === 0;
|
||||
}).toArray();
|
||||
test.deepEqual([2, 4], even);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.firstReturnsFirstElementOfArray = function(test) {
|
||||
var original = [1, 2, 3, 4];
|
||||
var first = lazyIterators.fromArray(original).first();
|
||||
test.deepEqual(1, first);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.firstIsNullIfArrayIsEmpty = function(test) {
|
||||
var first = lazyIterators.fromArray([]).first();
|
||||
test.same(null, first);
|
||||
test.done();
|
||||
};
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
var lop = require("../");
|
||||
var Parser = lop.Parser;
|
||||
var rules = lop.rules;
|
||||
var testing = require("../lib/testing");
|
||||
var Tokeniser = require("./Tokeniser");
|
||||
|
||||
exports.canParseUsingParser = function(test) {
|
||||
var tokens = new Tokeniser({keywords: []}).tokenise("! blah");
|
||||
|
||||
var name = rules.sequence.capture(rules.token("identifier"), "name");
|
||||
var rule = rules.sequence(
|
||||
rules.token("identifier", "!"),
|
||||
name
|
||||
);
|
||||
|
||||
var parser = new Parser();
|
||||
var result = parser.parseTokens(rule, tokens);
|
||||
|
||||
testing.assertIsSuccess(test, result);
|
||||
test.deepEqual(result.value().get(name), "blah");
|
||||
|
||||
test.done();
|
||||
};
|
||||
|
||||
var parseString = function(parser, string) {
|
||||
return parser(new TokenIterator(tokens));
|
||||
};
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
var RegexTokeniser = require("../lib/regex-tokeniser").RegexTokeniser;
|
||||
var Token = require("../lib/Token");
|
||||
var StringSource = require("../lib/StringSource");
|
||||
|
||||
exports.emptyStringIsTokenisedToEndToken = stringIsTokenisedTo("", [
|
||||
endToken("")
|
||||
]);
|
||||
|
||||
exports.canMatchSingleToken = stringIsTokenisedTo("blah", [
|
||||
new Token("identifier", "blah", stringSourceRange("blah", 0, 4)),
|
||||
endToken("blah")
|
||||
]);
|
||||
|
||||
exports.canMatchMultipleTokens = stringIsTokenisedTo("a.btn", [
|
||||
new Token("identifier", "a", stringSourceRange("a.btn", 0, 1)),
|
||||
new Token("dot", ".", stringSourceRange("a.btn", 1, 2)),
|
||||
new Token("identifier", "btn", stringSourceRange("a.btn", 2, 5)),
|
||||
endToken("a.btn")
|
||||
]);
|
||||
|
||||
exports.unrecognisedCharactersAreTokenised = stringIsTokenisedTo("!btn", [
|
||||
new Token("unrecognisedCharacter", "!", stringSourceRange("!btn", 0, 1)),
|
||||
new Token("identifier", "btn", stringSourceRange("!btn", 1, 4)),
|
||||
endToken("!btn")
|
||||
]);
|
||||
|
||||
exports.firstMatchingRuleIsUsed = stringIsTokenisedTo(":", [
|
||||
new Token("colon1", ":", stringSourceRange(":", 0, 1)),
|
||||
endToken(":")
|
||||
]);
|
||||
|
||||
exports.valuesOfZeroLengthAreIgnored = function(test) {
|
||||
var expectedTokens = [
|
||||
new Token("unrecognisedCharacter", "!", stringSourceRange("!btn", 0, 1)),
|
||||
new Token("identifier", "btn", stringSourceRange("!btn", 1, 4)),
|
||||
endToken("!btn")
|
||||
];
|
||||
|
||||
var rules = [
|
||||
{
|
||||
name: "identifier",
|
||||
regex: /([a-z]*)/
|
||||
}
|
||||
];
|
||||
var tokeniser = new RegexTokeniser(rules);
|
||||
test.deepEqual(expectedTokens, tokeniser.tokenise("!btn"));
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.tokenValueIsFirstCaptureOfRegex = stringIsTokenisedTo('"a"', [
|
||||
new Token("string", "a", stringSourceRange('"a"', 0, 3)),
|
||||
endToken('"a"')
|
||||
]);
|
||||
|
||||
exports.tokenWithNoCaptureHasUndefinedValue = function(test) {
|
||||
var expectedTokens = [
|
||||
new Token("bang", undefined, stringSourceRange("!", 0, 1)),
|
||||
endToken("!")
|
||||
];
|
||||
|
||||
var rules = [
|
||||
{
|
||||
name: "bang",
|
||||
regex: /!/
|
||||
}
|
||||
];
|
||||
var tokeniser = new RegexTokeniser(rules);
|
||||
test.deepEqual(expectedTokens, tokeniser.tokenise("!"));
|
||||
test.done();
|
||||
};
|
||||
|
||||
function endToken(input) {
|
||||
var source = stringSourceRange(input, input.length, input.length);
|
||||
return new Token("end", null, source);
|
||||
}
|
||||
|
||||
function stringIsTokenisedTo(input, expected) {
|
||||
return function(test) {
|
||||
test.deepEqual(expected, tokenise(input));
|
||||
test.done();
|
||||
};
|
||||
};
|
||||
|
||||
function stringSourceRange(string, startIndex, endIndex) {
|
||||
return new StringSource(string).range(startIndex, endIndex);
|
||||
};
|
||||
|
||||
function tokenise(input) {
|
||||
var rules = [
|
||||
{
|
||||
name: "identifier",
|
||||
regex: /([a-z]+)/
|
||||
},
|
||||
{
|
||||
name: "dot",
|
||||
regex: /(\.)/
|
||||
},
|
||||
{
|
||||
name: "colon1",
|
||||
regex: /(:)/
|
||||
},
|
||||
{
|
||||
name: "colon2",
|
||||
regex: /(:)/
|
||||
},
|
||||
{
|
||||
name: "string",
|
||||
regex: /"([a-z]*)"/
|
||||
}
|
||||
];
|
||||
var tokeniser = new RegexTokeniser(rules);
|
||||
return tokeniser.tokenise(input);
|
||||
};
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
var results = require("../lib/parsing-results");
|
||||
|
||||
exports.errorIsThrownIfCreatingFailureWithoutAnyErrors = function(test) {
|
||||
test.throws(function() {
|
||||
results.failure([]);
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
+617
@@ -0,0 +1,617 @@
|
||||
var options = require("option");
|
||||
|
||||
var rules = require("../lib/rules");
|
||||
var testing = require("../lib/testing");
|
||||
var TokenIterator = require("../lib/TokenIterator");
|
||||
var errors = require("../lib/errors");
|
||||
var results = require("../lib/parsing-results");
|
||||
var StringSource = require("../lib/StringSource");
|
||||
var assertIsSuccess = testing.assertIsSuccess;
|
||||
var assertIsSuccessWithValue = testing.assertIsSuccessWithValue;
|
||||
var assertIsFailure = testing.assertIsFailure;
|
||||
var assertIsFailureWithRemaining = testing.assertIsFailureWithRemaining;
|
||||
var assertIsError = testing.assertIsError;
|
||||
var Tokeniser = require("./Tokeniser");
|
||||
var Token = require("../lib/Token");
|
||||
|
||||
var stringSourceRange = function(string, startIndex, endIndex) {
|
||||
return new StringSource(string).range(startIndex, endIndex);
|
||||
};
|
||||
|
||||
var token = function(tokenType, value, source) {
|
||||
return new Token(tokenType, value, source);
|
||||
};
|
||||
|
||||
var keyword = function(value) {
|
||||
return rules.token("keyword", value);
|
||||
};
|
||||
|
||||
var identifier = function(value) {
|
||||
return rules.token("identifier", value);
|
||||
};
|
||||
|
||||
exports.tokenRuleFailsIfInputIsEmpty = function(test) {
|
||||
var tokens = [];
|
||||
var rule = rules.token("keyword", "true");
|
||||
var result = rule(new TokenIterator(tokens));
|
||||
assertIsFailure(test, result, {
|
||||
remaining: [],
|
||||
errors: [errors.error({
|
||||
expected: "keyword \"true\"",
|
||||
actual: "end of tokens"
|
||||
})]
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.tokenRuleConsumeTokenWhenTokenIsOfCorrectType = function(test) {
|
||||
var parser = rules.token("keyword", "true");
|
||||
var result = parseString(parser, "true");
|
||||
assertIsSuccess(test, result, {
|
||||
value: "true",
|
||||
source: stringSourceRange("true", 0, 4)
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.parsingTokenFailsIfTokenIsOfWrongType = function(test) {
|
||||
var parser = rules.token("keyword", "true");
|
||||
var result = parseString(parser, "blah");
|
||||
assertIsFailure(test, result, {
|
||||
remaining: [
|
||||
token("identifier", "blah", stringSourceRange("blah", 0, 4)),
|
||||
token("end", null, stringSourceRange("blah", 4, 4))
|
||||
],
|
||||
errors: [errors.error({
|
||||
expected: "keyword \"true\"",
|
||||
actual: "identifier \"blah\"",
|
||||
location: stringSourceRange("blah", 0, 4)
|
||||
})]
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.parsingTokenFailsIfTokenIsOfWrongValue = function(test) {
|
||||
var parser = rules.token("keyword", "true");
|
||||
var result = parseString(parser, "false");
|
||||
assertIsFailure(test, result, {
|
||||
remaining: [
|
||||
token("keyword", "false", stringSourceRange("false", 0, 5)),
|
||||
token("end", null, stringSourceRange("false", 5, 5))
|
||||
],
|
||||
errors: [errors.error({
|
||||
expected: "keyword \"true\"",
|
||||
actual: "keyword \"false\"",
|
||||
location: stringSourceRange("false", 0, 5)
|
||||
})]
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.anyValueIsAcceptedIfValueOfTokenIsNotSpecified = function(test) {
|
||||
var parser = rules.token("keyword");
|
||||
var result = parseString(parser, "true");
|
||||
assertIsSuccess(test, result, {
|
||||
value: "true",
|
||||
source: stringSourceRange("true", 0, 4)
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.firstSuccessIsReturnedByFirstOf = function(test) {
|
||||
var trueParser = keyword("true");
|
||||
var falseParser = keyword("false");
|
||||
var evilParser = function() {
|
||||
throw new Error("Hahaha!");
|
||||
};
|
||||
var result = parseString(rules.firstOf("Boolean", trueParser, falseParser, evilParser), "false");
|
||||
assertIsSuccessWithValue(test, result, "false");
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.firstOfFailsIfNoParsersMatch = function(test) {
|
||||
var trueParser = keyword("true");
|
||||
var falseParser = keyword("false");
|
||||
var result = parseString(rules.firstOf("Boolean", trueParser, falseParser), "blah");
|
||||
assertIsFailure(test, result, {
|
||||
remaining:[
|
||||
identifier("blah", stringSourceRange("blah", 0, 4)),
|
||||
token("end", null, stringSourceRange("blah", 4, 4))
|
||||
],
|
||||
errors: [errors.error({
|
||||
expected: "Boolean",
|
||||
actual: "identifier \"blah\"",
|
||||
location: stringSourceRange("blah", 0, 4)
|
||||
})]
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.firstOfReturnsErrorIfSubRuleReturnsErrorEvenIfLaterRuleSucceeds = function(test) {
|
||||
var trueParser = rules.sequence(rules.sequence.cut(), keyword("true"));
|
||||
var falseParser = keyword("false");
|
||||
var result = parseString(rules.firstOf("Boolean", trueParser, falseParser), "false");
|
||||
assertIsError(test, result, {
|
||||
remaining:[
|
||||
keyword("false", stringSourceRange("false", 0, 5)),
|
||||
token("end", null, stringSourceRange("false", 5, 5))
|
||||
],
|
||||
errors: [errors.error({
|
||||
expected: "keyword \"true\"",
|
||||
actual: "keyword \"false\"",
|
||||
location: stringSourceRange("false", 0, 5)
|
||||
})]
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.thenReturnsFailureIfOriginalResultIsFailure = function(test) {
|
||||
var parser = rules.then(keyword("true"), function() { return true; });
|
||||
var result = parseString(parser, "blah");
|
||||
assertIsFailure(test, result, {
|
||||
remaining:[
|
||||
identifier("blah", stringSourceRange("blah", 0, 4)),
|
||||
token("end", null, stringSourceRange("blah", 4, 4))
|
||||
],
|
||||
errors: [errors.error({
|
||||
expected: "keyword \"true\"",
|
||||
actual: "identifier \"blah\"",
|
||||
location: stringSourceRange("blah", 0, 4)
|
||||
})]
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.thenMapsOverValueIfOriginalResultIsSuccess = function(test) {
|
||||
var parser = rules.then(keyword("true"), function() { return true; });
|
||||
var result = parseString(parser, "true");
|
||||
assertIsSuccessWithValue(test, result, true);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.sequenceSucceedsIfSubParsersCanBeAppliedInOrder = function(test) {
|
||||
var parser = rules.sequence(identifier("one"), identifier("two"));
|
||||
var result = parseString(parser, "one two");
|
||||
assertIsSuccess(test, result, {
|
||||
source: stringSourceRange("one two", 0, 7)
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.sequenceFailIfSubParserFails = function(test) {
|
||||
var parser = rules.sequence(identifier("("), identifier(")"));
|
||||
var result = parseString(parser, "(");
|
||||
assertIsFailure(test, result, {
|
||||
remaining:[token("end", null, stringSourceRange("(", 1, 1))],
|
||||
errors: [errors.error({
|
||||
expected: "identifier \")\"",
|
||||
actual: "end",
|
||||
location: stringSourceRange("(", 1, 1)
|
||||
})]
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.sequenceFailIfSubParserFailsAndFinalParserSucceeds = function(test) {
|
||||
var parser = rules.sequence(identifier("("), identifier(")"));
|
||||
var result = parseString(parser, ")");
|
||||
assertIsFailure(test, result, {
|
||||
remaining:[
|
||||
identifier(")", stringSourceRange(")", 0, 1)),
|
||||
token("end", null, stringSourceRange(")", 1, 1))
|
||||
],
|
||||
errors: [errors.error({
|
||||
expected: "identifier \"(\"",
|
||||
actual: "identifier \")\"",
|
||||
location: stringSourceRange(")", 0, 1)
|
||||
})]
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.sequenceReturnsMapOfCapturedValues = function(test) {
|
||||
var name = rules.sequence.capture(identifier(), "name");
|
||||
var parser = rules.sequence(identifier("("), name, identifier(")"));
|
||||
var result = parseString(parser, "( bob )");
|
||||
assertIsSuccess(test, result);
|
||||
test.deepEqual(result.value().get(name), "bob");
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.failureInSubRuleInSequenceBeforeCutCausesSequenceToFail = function(test) {
|
||||
var parser = rules.sequence(identifier("("), rules.sequence.cut(), identifier(), identifier(")"));
|
||||
var result = parseString(parser, "bob");
|
||||
assertIsFailure(test, result);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.failureInSubRuleInSequenceAfterCutCausesError = function(test) {
|
||||
var parser = rules.sequence(identifier("("), rules.sequence.cut(), identifier(), identifier(")"));
|
||||
var result = parseString(parser, "( true");
|
||||
assertIsError(test, result, {
|
||||
remaining: [
|
||||
token("keyword", "true", stringSourceRange("( true", 2, 6)),
|
||||
token("end", null, stringSourceRange("( true", 6, 6))
|
||||
],
|
||||
errors: [errors.error({
|
||||
expected: "identifier",
|
||||
actual: "keyword \"true\"",
|
||||
location: stringSourceRange("( true", 2, 6)
|
||||
})]
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.canPullSingleValueOutOfCapturedValuesUsingExtract = function(test) {
|
||||
var name = rules.sequence.capture(identifier(), "name");
|
||||
var parser = rules.then(
|
||||
rules.sequence(identifier("("), name, identifier(")")),
|
||||
rules.sequence.extract(name)
|
||||
);
|
||||
var result = parseString(parser, "( bob )");
|
||||
assertIsSuccessWithValue(test, result, "bob");
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.canPullSingleValueOutOfCapturedValuesUsingHeadOnSequenceRule = function(test) {
|
||||
var name = rules.sequence.capture(identifier(), "name");
|
||||
var parser =
|
||||
rules.sequence(identifier("("), name, identifier(")"))
|
||||
.head();
|
||||
var result = parseString(parser, "( bob )");
|
||||
assertIsSuccessWithValue(test, result, "bob");
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.canApplyValuesFromSequenceToFunction = function(test) {
|
||||
var firstName = rules.sequence.capture(identifier(), "firstName");
|
||||
var secondName = rules.sequence.capture(identifier(), "secondName");
|
||||
var parser = rules.then(
|
||||
rules.sequence(
|
||||
secondName,
|
||||
identifier(","),
|
||||
firstName
|
||||
),
|
||||
rules.sequence.applyValues(function(firstName, secondName) {
|
||||
return {first: firstName, second: secondName};
|
||||
}, firstName, secondName)
|
||||
);
|
||||
var result = parseString(parser, "Bobertson , Bob");
|
||||
assertIsSuccessWithValue(test, result, {first: "Bob", second: "Bobertson"});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.canApplyValuesAndSourceFromSequenceToFunctionUsingMapOnSequenceRule = function(test) {
|
||||
var firstName = rules.sequence.capture(identifier(), "firstName");
|
||||
var secondName = rules.sequence.capture(identifier());
|
||||
var parser = rules.sequence(
|
||||
secondName,
|
||||
identifier(","),
|
||||
firstName
|
||||
).map(function(secondName, firstName, source) {
|
||||
return {first: firstName, second: secondName, source: source};
|
||||
});
|
||||
var result = parseString(parser, "Bobertson , Bob");
|
||||
assertIsSuccessWithValue(test, result, {
|
||||
first: "Bob",
|
||||
second: "Bobertson",
|
||||
source: stringSourceRange("Bobertson , Bob", 0, 15)
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.canApplyValuesWithSourceFromSequenceToFunction = function(test) {
|
||||
var firstName = rules.sequence.capture(identifier(), "firstName");
|
||||
var secondName = rules.sequence.capture(identifier(), "secondName");
|
||||
var parser = rules.then(
|
||||
rules.sequence(
|
||||
secondName,
|
||||
identifier(","),
|
||||
firstName
|
||||
),
|
||||
rules.sequence.applyValues(function(firstName, secondName, source) {
|
||||
return {first: firstName, second: secondName, source: source};
|
||||
}, firstName, secondName, rules.sequence.source)
|
||||
);
|
||||
var result = parseString(parser, "Bobertson , Bob");
|
||||
assertIsSuccessWithValue(test, result, {
|
||||
first: "Bob",
|
||||
second: "Bobertson",
|
||||
source: stringSourceRange("Bobertson , Bob", 0, 15)
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.exceptionIfTryingToReadAValueThatHasntBeenCaptured = function(test) {
|
||||
var name = rules.sequence.capture(identifier(), "name");
|
||||
var parser = rules.sequence(identifier("("), identifier(")"));
|
||||
var result = parseString(parser, "( )");
|
||||
assertIsSuccess(test, result);
|
||||
try {
|
||||
result.value().get(name);
|
||||
test.ok(false, "Expected exception");
|
||||
} catch (error) {
|
||||
test.equal(error.message, "No value for capture \"name\"");
|
||||
}
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.exceptionIfTryingToCaptureValueWithUsedName = function(test) {
|
||||
var firstName = rules.sequence.capture(identifier(), "name");
|
||||
var secondName = rules.sequence.capture(identifier(), "name");
|
||||
var parser = rules.sequence(secondName, identifier(","), firstName);
|
||||
try {
|
||||
parseString(parser, "Bobertson , Bob")
|
||||
test.ok(false, "Expected exception");
|
||||
} catch (error) {
|
||||
test.equal(error.message, "Cannot add second value for capture \"name\"");
|
||||
}
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.optionalRuleDoesNothingIfValueDoesNotMatch = function(test) {
|
||||
var parser = rules.optional(identifier("("));
|
||||
var result = parseString(parser, "");
|
||||
assertIsSuccess(test, result);
|
||||
test.deepEqual(result.value(), options.none);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.optionalRuleConsumesInputIfPossible = function(test) {
|
||||
var parser = rules.optional(identifier("("));
|
||||
var result = parseString(parser, "(");
|
||||
assertIsSuccess(test, result);
|
||||
test.deepEqual(result.value(), options.some("("));
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.optionalRulePreservesErrors = function(test) {
|
||||
var error = results.error([errors.error({
|
||||
expected: "something",
|
||||
actual: "something else"
|
||||
})]);
|
||||
var parser = rules.optional(function(input) {
|
||||
return error;
|
||||
});
|
||||
var result = parseString(parser, "");
|
||||
test.deepEqual(result, error);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.zeroOrMoreWithSeparatorParsesEmptyStringAndReturnsEmptyArray = function(test) {
|
||||
var parser = rules.zeroOrMoreWithSeparator(identifier(), identifier(","));
|
||||
var result = parseString(parser, "");
|
||||
assertIsSuccessWithValue(test, result, []);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.zeroOrMoreWithSeparatorParsesSingleInstanceOfRuleAndReturnsSingleElementArray = function(test) {
|
||||
var parser = rules.zeroOrMoreWithSeparator(identifier(), identifier(","));
|
||||
var result = parseString(parser, "blah");
|
||||
assertIsSuccessWithValue(test, result, ["blah"]);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.zeroOrMoreWithSeparatorParsesMultipleInstanceOfRuleAndReturnsArray = function(test) {
|
||||
var parser = rules.zeroOrMoreWithSeparator(identifier(), identifier(","));
|
||||
var result = parseString(parser, "apple , banana , coconut");
|
||||
assertIsSuccessWithValue(test, result, ["apple", "banana", "coconut"]);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.zeroOrMoreWithSeparatorDoesNotConsumeFinalSeparatorIfItIsNotFollowedByMainRule = function(test) {
|
||||
var parser = rules.zeroOrMoreWithSeparator(identifier(), identifier(","));
|
||||
var result = parseString(parser, "apple , banana ,");
|
||||
assertIsSuccess(test, result, {
|
||||
remaining: [
|
||||
token("identifier", ",", stringSourceRange("apple , banana ,", 15, 16)),
|
||||
token("end", null, stringSourceRange("apple , banana ,", 16, 16))
|
||||
],
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.zeroOrMoreReturnsErrorIfFirstUseOfRuleReturnsError = function(test) {
|
||||
var parser = rules.zeroOrMoreWithSeparator(
|
||||
rules.sequence(identifier(), rules.sequence.cut(), identifier()),
|
||||
identifier(",")
|
||||
);
|
||||
var result = parseString(parser, "apple");
|
||||
assertIsError(test, result);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.zeroOrMoreParsesEmptyStringAndReturnsEmptyArray = function(test) {
|
||||
var parser = rules.zeroOrMore(identifier());
|
||||
var result = parseString(parser, "");
|
||||
assertIsSuccessWithValue(test, result, []);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.zeroOrMoreParsesSingleInstanceOfRuleAndReturnsSingleElementArray = function(test) {
|
||||
var parser = rules.zeroOrMore(identifier());
|
||||
var result = parseString(parser, "blah");
|
||||
assertIsSuccessWithValue(test, result, ["blah"]);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.zeroOrMoreParsesMultipleInstanceOfRuleAndReturnsArray = function(test) {
|
||||
var parser = rules.zeroOrMore(identifier());
|
||||
var result = parseString(parser, "( , )");
|
||||
assertIsSuccessWithValue(test, result, ["(", ",", ")"]);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.zeroOrMoreReturnsErrorIfSubRuleReturnsError = function(test) {
|
||||
var parser = rules.zeroOrMore(
|
||||
rules.sequence(identifier(), rules.sequence.cut(), identifier(";"))
|
||||
);
|
||||
var result = parseString(parser, "blah");
|
||||
assertIsError(test, result, {
|
||||
remaining:[
|
||||
token("end", null, stringSourceRange("blah", 4, 4))
|
||||
],
|
||||
errors: [errors.error({
|
||||
expected: "identifier \";\"",
|
||||
actual: "end",
|
||||
location: stringSourceRange("blah", 4, 4)
|
||||
})]
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.oneOrMoreWithSeparatorFailsOnEmptyString = function(test) {
|
||||
var parser = rules.oneOrMoreWithSeparator(identifier(), identifier(","));
|
||||
var result = parseString(parser, "");
|
||||
assertIsFailure(test, result, {
|
||||
remaining:[
|
||||
token("end", null, stringSourceRange("", 0, 0))
|
||||
],
|
||||
errors: [errors.error({
|
||||
expected: "identifier",
|
||||
actual: "end",
|
||||
location: stringSourceRange("", 0, 0)
|
||||
})]
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.oneOrMoreWithSeparatorParsesSingleInstanceOfRuleAndReturnsSingleElementArray = function(test) {
|
||||
var parser = rules.oneOrMoreWithSeparator(identifier(), identifier(","));
|
||||
var result = parseString(parser, "blah");
|
||||
assertIsSuccessWithValue(test, result, ["blah"]);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.oneOrMoreWithSeparatorParsesMultipleInstanceOfRuleAndReturnsArray = function(test) {
|
||||
var parser = rules.oneOrMoreWithSeparator(identifier(), identifier(","));
|
||||
var result = parseString(parser, "apple , banana , coconut");
|
||||
assertIsSuccessWithValue(test, result, ["apple", "banana", "coconut"]);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.oneOrMoreFailsOnEmptyString = function(test) {
|
||||
var parser = rules.oneOrMore(identifier());
|
||||
var result = parseString(parser, "");
|
||||
assertIsFailure(test, result, {
|
||||
remaining:[
|
||||
token("end", null, stringSourceRange("", 0, 0))
|
||||
],
|
||||
errors: [errors.error({
|
||||
expected: "identifier",
|
||||
actual: "end",
|
||||
location: stringSourceRange("", 0, 0)
|
||||
})]
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.oneOrMoreParsesSingleInstanceOfRuleAndReturnsSingleElementArray = function(test) {
|
||||
var parser = rules.oneOrMore(identifier());
|
||||
var result = parseString(parser, "blah");
|
||||
assertIsSuccessWithValue(test, result, ["blah"]);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.oneOrMoreParsesMultipleInstanceOfRuleAndReturnsArray = function(test) {
|
||||
var parser = rules.oneOrMore(identifier());
|
||||
var result = parseString(parser, "apple banana coconut");
|
||||
assertIsSuccessWithValue(test, result, ["apple", "banana", "coconut"]);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.leftAssociativeConsumesNothingIfLeftHandSideDoesntMatch = function(test) {
|
||||
var parser = rules.leftAssociative(
|
||||
keyword(),
|
||||
identifier("+"),
|
||||
function(left, right) {
|
||||
return [left, right];
|
||||
}
|
||||
);
|
||||
var result = parseString(parser, "+ +");
|
||||
assertIsFailure(test, result, {
|
||||
remaining:[
|
||||
token("identifier", "+", stringSourceRange("+ +", 0, 1)),
|
||||
token("identifier", "+", stringSourceRange("+ +", 2, 3)),
|
||||
token("end", null, stringSourceRange("+ +", 3, 3))
|
||||
],
|
||||
errors: [errors.error({
|
||||
expected: "keyword",
|
||||
actual: "identifier \"+\"",
|
||||
location: stringSourceRange("+ +", 0, 1)
|
||||
})]
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.leftAssociativeReturnsValueOfLeftHandSideIfRightHandSideDoesntMatch = function(test) {
|
||||
var parser = rules.leftAssociative(
|
||||
identifier(),
|
||||
identifier("+"),
|
||||
function(left, right) {
|
||||
return [left, right];
|
||||
}
|
||||
);
|
||||
var result = parseString(parser, "apple");
|
||||
assertIsSuccessWithValue(test, result, "apple");
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.leftAssociativeAllowsLeftAssociativeRules = function(test) {
|
||||
var parser = rules.leftAssociative(
|
||||
identifier(),
|
||||
identifier("+"),
|
||||
function(left, right) {
|
||||
return [left, right];
|
||||
}
|
||||
);
|
||||
var result = parseString(parser, "apple + +");
|
||||
assertIsSuccessWithValue(test, result, [["apple", "+"], "+"]);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.leftAssociativeCanHaveMultipleChoicesForRight = function(test) {
|
||||
var parser = rules.leftAssociative(
|
||||
identifier(),
|
||||
rules.leftAssociative.firstOf(
|
||||
{rule: identifier("+"), func: function(left, right) { return [left, right]; }},
|
||||
{rule: identifier(","), func: function(left, right) { return [left]; }}
|
||||
)
|
||||
);
|
||||
var result = parseString(parser, "apple + ,");
|
||||
assertIsSuccessWithValue(test, result, [["apple", "+"]]);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.leftAssociativeReturnsErrorIfRightHandSideReturnsError = function(test) {
|
||||
var parser = rules.leftAssociative(
|
||||
identifier(),
|
||||
rules.leftAssociative.firstOf(
|
||||
{rule: rules.sequence(rules.sequence.cut(), identifier("+")), func: function() {}}
|
||||
)
|
||||
);
|
||||
var result = parseString(parser, "apple");
|
||||
assertIsError(test, result);
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.nonConsumingRuleDoesNotConsumeInput = function(test) {
|
||||
var parser = rules.nonConsuming(rules.token("keyword", "true"));
|
||||
var result = parseString(parser, "true");
|
||||
assertIsSuccess(test, result, {
|
||||
value: "true",
|
||||
source: stringSourceRange("true", 0, 4),
|
||||
remaining: [token("keyword", "true"), token("end", null)]
|
||||
});
|
||||
test.done();
|
||||
};
|
||||
|
||||
var parseString = function(parser, string) {
|
||||
var keywords = ["true", "false"];
|
||||
var tokens = new Tokeniser({keywords: keywords}).tokenise(string);
|
||||
return parser(new TokenIterator(tokens));
|
||||
};
|
||||
|
||||
var parseTokens = function(parser, tokens) {
|
||||
return parser(new TokenIterator(tokens));
|
||||
};
|
||||
Reference in new issue
Block a user