UNPKG

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