UNPKG

11.2 kBJavaScriptView Raw
1import Sequence from './sequence';
2import SpecialSequence from './specialsequence';
3import Repetition from './repetition';
4import RuleReference from './rulereference';
5import Choice from './choice';
6import Literal from './literal';
7import Expression from './expression';
8
9
10const ChunkType = {
11 RULE: 'RULE',
12 REPETITION_TOKEN: 'REPETITION_TOKEN',
13 // CONCATENATION: 'CONCATENATION',
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 * @param {Expression} expression
26 * @return {boolean}
27 */
28function isNoop(expression) {
29 return expression instanceof Sequence && expression.getExpressions().length == 0;
30}
31
32export 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 // Case of: 3 * expression
84 if (previousChunk.getType() == ChunkType.RULE) {
85 multiplier = +previousChunk.text;
86 if(isNaN(multiplier)) {
87 multiplier = null;
88 }
89 }
90 if (multiplier != null) {
91 // The current one is removed, so next one is at index i.
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 // For now, nothing to do
134 this.chunkList.splice(i, 1);
135 }
136 case ChunkType.ALTERNATION: {
137 hasAlternation = true;
138 break;
139 }
140 case ChunkType.GROUP: {
141 // Group could be empty
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 // Group is empty.
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}