1 | import Sequence from './sequence';
|
2 | import SpecialSequence from './specialsequence';
|
3 | import Repetition from './repetition';
|
4 | import RuleReference from './rulereference';
|
5 | import Choice from './choice';
|
6 | import Literal from './literal';
|
7 | import Expression from './expression';
|
8 |
|
9 |
|
10 | const ChunkType = {
|
11 | RULE: 'RULE',
|
12 | REPETITION_TOKEN: 'REPETITION_TOKEN',
|
13 |
|
14 | ALTERNATION: 'ALTERNATION',
|
15 | GROUP: 'GROUP',
|
16 | COMMENT: 'COMMENT',
|
17 | SPECIAL_SEQUENCE: 'SPECIAL_SEQUENCE',
|
18 | LITERAL: 'LITERAL',
|
19 | OPTION: 'OPTION',
|
20 | REPETITION: 'REPETITION',
|
21 | CHOICE: 'CHOICE',
|
22 | };
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | function isNoop(expression) {
|
29 | return expression instanceof Sequence && expression.getExpressions().length == 0;
|
30 | }
|
31 |
|
32 | export default class Chunk {
|
33 |
|
34 | static get ChunkType() {
|
35 | return ChunkType;
|
36 | }
|
37 |
|
38 | constructor(type, text) {
|
39 | this.type = type;
|
40 | this.text = text;
|
41 | this.minCount = 0;
|
42 | this.maxCount = null;
|
43 | this.chunkList = null;
|
44 | }
|
45 |
|
46 | getType() {
|
47 | return this.type;
|
48 | }
|
49 |
|
50 | setType(type) {
|
51 | this.type = type;
|
52 | }
|
53 |
|
54 | setText(text) {
|
55 | this.text = text;
|
56 | }
|
57 |
|
58 | setMinCount(minCount) {
|
59 | this.minCount = minCount;
|
60 | }
|
61 |
|
62 | setMaxCount(maxCount) {
|
63 | this.maxCount = maxCount;
|
64 | }
|
65 |
|
66 | addChunk(chunk) {
|
67 | if (this.chunkList == null) {
|
68 | this.chunkList = [];
|
69 | }
|
70 | this.chunkList.push(chunk);
|
71 | }
|
72 |
|
73 | prune() {
|
74 | let hasAlternation = false;
|
75 | for (let i = this.chunkList.length - 1; i >= 0; i--) {
|
76 | const chunk = this.chunkList[i];
|
77 | switch (chunk.getType()) {
|
78 | case ChunkType.REPETITION_TOKEN: {
|
79 | if ("*" === chunk.text) {
|
80 | this.chunkList.splice(i, 1);
|
81 | const previousChunk = this.chunkList[i - 1];
|
82 | let multiplier = null;
|
83 |
|
84 | if (previousChunk.getType() == ChunkType.RULE) {
|
85 | multiplier = +previousChunk.text;
|
86 | if(isNaN(multiplier)) {
|
87 | multiplier = null;
|
88 | }
|
89 | }
|
90 | if (multiplier != null) {
|
91 |
|
92 | const nextChunk = this.chunkList[i];
|
93 | if (nextChunk.getType() == ChunkType.OPTION) {
|
94 | const newChunk = new Chunk(ChunkType.REPETITION);
|
95 | newChunk.setMinCount(0);
|
96 | newChunk.setMaxCount(multiplier);
|
97 | for (const c of nextChunk.chunkList) {
|
98 | newChunk.addChunk(c);
|
99 | }
|
100 | this.chunkList.splice(i, 1);
|
101 | this.chunkList[i - 1] = newChunk;
|
102 | } else {
|
103 | const newChunk = new Chunk(ChunkType.REPETITION);
|
104 | newChunk.setMinCount(multiplier);
|
105 | newChunk.setMaxCount(multiplier);
|
106 | newChunk.addChunk(nextChunk);
|
107 | this.chunkList.splice(i, 1);
|
108 | this.chunkList[i - 1] = newChunk;
|
109 | }
|
110 | } else {
|
111 | const newChunk = new Chunk(ChunkType.REPETITION);
|
112 | newChunk.setMinCount(0);
|
113 | newChunk.addChunk(previousChunk);
|
114 | this.chunkList[i - 1] = newChunk;
|
115 | }
|
116 | } else if ("+" === chunk.text) {
|
117 | this.chunkList.splice(i, 1);
|
118 | const newChunk = new Chunk(ChunkType.REPETITION);
|
119 | newChunk.setMinCount(1);
|
120 | const previousChunk = this.chunkList[i - 1];
|
121 | newChunk.addChunk(previousChunk);
|
122 | this.chunkList[i - 1] = newChunk;
|
123 | } else if ("?" === chunk.text) {
|
124 | this.chunkList.splice(i, 1);
|
125 | const newChunk = new Chunk(ChunkType.OPTION);
|
126 | const previousChunk = this.chunkList[i - 1];
|
127 | newChunk.addChunk(previousChunk);
|
128 | this.chunkList[i - 1] = newChunk;
|
129 | }
|
130 | break;
|
131 | }
|
132 | case ChunkType.COMMENT: {
|
133 |
|
134 | this.chunkList.splice(i, 1);
|
135 | }
|
136 | case ChunkType.ALTERNATION: {
|
137 | hasAlternation = true;
|
138 | break;
|
139 | }
|
140 | case ChunkType.GROUP: {
|
141 |
|
142 | if (chunk.chunkList != null) {
|
143 | chunk.prune();
|
144 | if (chunk.chunkList.length == 1) {
|
145 | this.chunkList[i] = chunk.chunkList[0];
|
146 | }
|
147 | }
|
148 | break;
|
149 | }
|
150 | case ChunkType.OPTION:
|
151 | case ChunkType.REPETITION: {
|
152 | chunk.prune();
|
153 | break;
|
154 | }
|
155 | }
|
156 | }
|
157 | if (hasAlternation) {
|
158 | const alternationSequenceList = [];
|
159 | alternationSequenceList.push([]);
|
160 | for (const chunk of this.chunkList) {
|
161 | if (chunk.getType() == ChunkType.ALTERNATION) {
|
162 | alternationSequenceList.push([]);
|
163 | } else {
|
164 | const list = alternationSequenceList[alternationSequenceList.length - 1];
|
165 | list.push(chunk);
|
166 | }
|
167 | }
|
168 | const choiceChunk = new Chunk(ChunkType.CHOICE);
|
169 | for (const subList of alternationSequenceList) {
|
170 | if (subList.length == 1) {
|
171 | choiceChunk.addChunk(subList[0]);
|
172 | } else {
|
173 | const groupChunk = new Chunk(ChunkType.GROUP);
|
174 | for (const c of subList) {
|
175 | groupChunk.addChunk(c);
|
176 | }
|
177 | choiceChunk.addChunk(groupChunk);
|
178 | }
|
179 | }
|
180 | this.chunkList.length = 0;
|
181 | this.chunkList.push(choiceChunk);
|
182 | }
|
183 | }
|
184 |
|
185 | getExpression() {
|
186 | switch (this.type) {
|
187 | case ChunkType.GROUP: {
|
188 | if (this.chunkList == null) {
|
189 |
|
190 | return new Sequence();
|
191 | }
|
192 | if (this.chunkList.length == 1) {
|
193 | return this.chunkList[0].getExpression();
|
194 | }
|
195 | const expressionList = [];
|
196 | for (const chunk of this.chunkList) {
|
197 | expressionList.push(chunk.getExpression());
|
198 | }
|
199 | return new Sequence(expressionList);
|
200 | }
|
201 | case ChunkType.CHOICE: {
|
202 | if (this.chunkList.length == 1) {
|
203 | return this.chunkList[0].getExpression();
|
204 | }
|
205 | const expressionList = [];
|
206 | let hasLine = false;
|
207 | for (const chunk of this.chunkList) {
|
208 | let expression = chunk.getExpression();
|
209 | if (expression instanceof Repetition) {
|
210 | const repetition = expression;
|
211 | if (repetition.getMinRepetitionCount() == 0) {
|
212 | if (repetition.getMaxRepetitionCount() == null || repetition.getMaxRepetitionCount() != 1) {
|
213 | expression = new Repetition(repetition.getExpression(), 1, repetition.getMaxRepetitionCount());
|
214 | } else {
|
215 | expression = repetition.getExpression();
|
216 | }
|
217 | hasLine = true;
|
218 | }
|
219 | }
|
220 | if (expression instanceof Choice) {
|
221 | for (const exp of expression.getExpressions()) {
|
222 | expressionList.push(exp);
|
223 | }
|
224 | } else {
|
225 | expressionList.push(expression);
|
226 | }
|
227 | }
|
228 | if (hasLine && (expressionList.length == 0 || !isNoop(expressionList[expressionList.length - 1]))) {
|
229 | expressionList.push(new Sequence());
|
230 | }
|
231 | return new Choice(expressionList);
|
232 | }
|
233 | case ChunkType.RULE: {
|
234 | return new RuleReference(this.text);
|
235 | }
|
236 | case ChunkType.LITERAL: {
|
237 | return new Literal(this.text);
|
238 | }
|
239 | case ChunkType.SPECIAL_SEQUENCE: {
|
240 | return new SpecialSequence(this.text);
|
241 | }
|
242 | case ChunkType.OPTION: {
|
243 | if (this.chunkList.length == 1) {
|
244 | const subChunk = this.chunkList[0];
|
245 | if (subChunk.getType() == ChunkType.CHOICE) {
|
246 | const newChunk = new Chunk(ChunkType.CHOICE);
|
247 | for (const cChunk of subChunk.chunkList) {
|
248 | newChunk.addChunk(cChunk);
|
249 | }
|
250 | newChunk.addChunk(new Chunk(ChunkType.GROUP));
|
251 | return newChunk.getExpression();
|
252 | }
|
253 | return new Repetition(subChunk.getExpression(), 0, 1);
|
254 | }
|
255 | const expressionList = [];
|
256 | for (const chunk of this.chunkList) {
|
257 | expressionList.push(chunk.getExpression());
|
258 | }
|
259 | return new Repetition(new Sequence(expressionList), 0, 1);
|
260 | }
|
261 | case ChunkType.REPETITION: {
|
262 | if (this.chunkList.length == 1) {
|
263 | return new Repetition(this.chunkList[0].getExpression(), this.minCount, this.maxCount);
|
264 | }
|
265 | const expressionList = [];
|
266 | for (const chunk of this.chunkList) {
|
267 | expressionList.push(chunk.getExpression());
|
268 | }
|
269 | return new Repetition(new Sequence(expressionList), this.minCount, this.maxCount);
|
270 | }
|
271 | }
|
272 | throw "Type should not be reachable: " + this.type;
|
273 | }
|
274 |
|
275 | toString() {
|
276 | let s = "" + this.type;
|
277 | if (this.text != null) {
|
278 | s += " (" + this.text + ")";
|
279 | }
|
280 | return s;
|
281 | }
|
282 |
|
283 | }
|