source: imaps-frontend/node_modules/css-tree/lib/lexer/Lexer.js

main
Last change on this file was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 14.6 KB
Line 
1var SyntaxReferenceError = require('./error').SyntaxReferenceError;
2var SyntaxMatchError = require('./error').SyntaxMatchError;
3var names = require('../utils/names');
4var generic = require('./generic');
5var parse = require('../definition-syntax/parse');
6var generate = require('../definition-syntax/generate');
7var walk = require('../definition-syntax/walk');
8var prepareTokens = require('./prepare-tokens');
9var buildMatchGraph = require('./match-graph').buildMatchGraph;
10var matchAsTree = require('./match').matchAsTree;
11var trace = require('./trace');
12var search = require('./search');
13var getStructureFromConfig = require('./structure').getStructureFromConfig;
14var cssWideKeywords = buildMatchGraph('inherit | initial | unset');
15var cssWideKeywordsWithExpression = buildMatchGraph('inherit | initial | unset | <-ms-legacy-expression>');
16
17function dumpMapSyntax(map, compact, syntaxAsAst) {
18 var result = {};
19
20 for (var name in map) {
21 if (map[name].syntax) {
22 result[name] = syntaxAsAst
23 ? map[name].syntax
24 : generate(map[name].syntax, { compact: compact });
25 }
26 }
27
28 return result;
29}
30
31function dumpAtruleMapSyntax(map, compact, syntaxAsAst) {
32 const result = {};
33
34 for (const [name, atrule] of Object.entries(map)) {
35 result[name] = {
36 prelude: atrule.prelude && (
37 syntaxAsAst
38 ? atrule.prelude.syntax
39 : generate(atrule.prelude.syntax, { compact })
40 ),
41 descriptors: atrule.descriptors && dumpMapSyntax(atrule.descriptors, compact, syntaxAsAst)
42 };
43 }
44
45 return result;
46}
47
48function valueHasVar(tokens) {
49 for (var i = 0; i < tokens.length; i++) {
50 if (tokens[i].value.toLowerCase() === 'var(') {
51 return true;
52 }
53 }
54
55 return false;
56}
57
58function buildMatchResult(match, error, iterations) {
59 return {
60 matched: match,
61 iterations: iterations,
62 error: error,
63 getTrace: trace.getTrace,
64 isType: trace.isType,
65 isProperty: trace.isProperty,
66 isKeyword: trace.isKeyword
67 };
68}
69
70function matchSyntax(lexer, syntax, value, useCommon) {
71 var tokens = prepareTokens(value, lexer.syntax);
72 var result;
73
74 if (valueHasVar(tokens)) {
75 return buildMatchResult(null, new Error('Matching for a tree with var() is not supported'));
76 }
77
78 if (useCommon) {
79 result = matchAsTree(tokens, lexer.valueCommonSyntax, lexer);
80 }
81
82 if (!useCommon || !result.match) {
83 result = matchAsTree(tokens, syntax.match, lexer);
84 if (!result.match) {
85 return buildMatchResult(
86 null,
87 new SyntaxMatchError(result.reason, syntax.syntax, value, result),
88 result.iterations
89 );
90 }
91 }
92
93 return buildMatchResult(result.match, null, result.iterations);
94}
95
96var Lexer = function(config, syntax, structure) {
97 this.valueCommonSyntax = cssWideKeywords;
98 this.syntax = syntax;
99 this.generic = false;
100 this.atrules = {};
101 this.properties = {};
102 this.types = {};
103 this.structure = structure || getStructureFromConfig(config);
104
105 if (config) {
106 if (config.types) {
107 for (var name in config.types) {
108 this.addType_(name, config.types[name]);
109 }
110 }
111
112 if (config.generic) {
113 this.generic = true;
114 for (var name in generic) {
115 this.addType_(name, generic[name]);
116 }
117 }
118
119 if (config.atrules) {
120 for (var name in config.atrules) {
121 this.addAtrule_(name, config.atrules[name]);
122 }
123 }
124
125 if (config.properties) {
126 for (var name in config.properties) {
127 this.addProperty_(name, config.properties[name]);
128 }
129 }
130 }
131};
132
133Lexer.prototype = {
134 structure: {},
135 checkStructure: function(ast) {
136 function collectWarning(node, message) {
137 warns.push({
138 node: node,
139 message: message
140 });
141 }
142
143 var structure = this.structure;
144 var warns = [];
145
146 this.syntax.walk(ast, function(node) {
147 if (structure.hasOwnProperty(node.type)) {
148 structure[node.type].check(node, collectWarning);
149 } else {
150 collectWarning(node, 'Unknown node type `' + node.type + '`');
151 }
152 });
153
154 return warns.length ? warns : false;
155 },
156
157 createDescriptor: function(syntax, type, name, parent = null) {
158 var ref = {
159 type: type,
160 name: name
161 };
162 var descriptor = {
163 type: type,
164 name: name,
165 parent: parent,
166 syntax: null,
167 match: null
168 };
169
170 if (typeof syntax === 'function') {
171 descriptor.match = buildMatchGraph(syntax, ref);
172 } else {
173 if (typeof syntax === 'string') {
174 // lazy parsing on first access
175 Object.defineProperty(descriptor, 'syntax', {
176 get: function() {
177 Object.defineProperty(descriptor, 'syntax', {
178 value: parse(syntax)
179 });
180
181 return descriptor.syntax;
182 }
183 });
184 } else {
185 descriptor.syntax = syntax;
186 }
187
188 // lazy graph build on first access
189 Object.defineProperty(descriptor, 'match', {
190 get: function() {
191 Object.defineProperty(descriptor, 'match', {
192 value: buildMatchGraph(descriptor.syntax, ref)
193 });
194
195 return descriptor.match;
196 }
197 });
198 }
199
200 return descriptor;
201 },
202 addAtrule_: function(name, syntax) {
203 if (!syntax) {
204 return;
205 }
206
207 this.atrules[name] = {
208 type: 'Atrule',
209 name: name,
210 prelude: syntax.prelude ? this.createDescriptor(syntax.prelude, 'AtrulePrelude', name) : null,
211 descriptors: syntax.descriptors
212 ? Object.keys(syntax.descriptors).reduce((res, descName) => {
213 res[descName] = this.createDescriptor(syntax.descriptors[descName], 'AtruleDescriptor', descName, name);
214 return res;
215 }, {})
216 : null
217 };
218 },
219 addProperty_: function(name, syntax) {
220 if (!syntax) {
221 return;
222 }
223
224 this.properties[name] = this.createDescriptor(syntax, 'Property', name);
225 },
226 addType_: function(name, syntax) {
227 if (!syntax) {
228 return;
229 }
230
231 this.types[name] = this.createDescriptor(syntax, 'Type', name);
232
233 if (syntax === generic['-ms-legacy-expression']) {
234 this.valueCommonSyntax = cssWideKeywordsWithExpression;
235 }
236 },
237
238 checkAtruleName: function(atruleName) {
239 if (!this.getAtrule(atruleName)) {
240 return new SyntaxReferenceError('Unknown at-rule', '@' + atruleName);
241 }
242 },
243 checkAtrulePrelude: function(atruleName, prelude) {
244 let error = this.checkAtruleName(atruleName);
245
246 if (error) {
247 return error;
248 }
249
250 var atrule = this.getAtrule(atruleName);
251
252 if (!atrule.prelude && prelude) {
253 return new SyntaxError('At-rule `@' + atruleName + '` should not contain a prelude');
254 }
255
256 if (atrule.prelude && !prelude) {
257 return new SyntaxError('At-rule `@' + atruleName + '` should contain a prelude');
258 }
259 },
260 checkAtruleDescriptorName: function(atruleName, descriptorName) {
261 let error = this.checkAtruleName(atruleName);
262
263 if (error) {
264 return error;
265 }
266
267 var atrule = this.getAtrule(atruleName);
268 var descriptor = names.keyword(descriptorName);
269
270 if (!atrule.descriptors) {
271 return new SyntaxError('At-rule `@' + atruleName + '` has no known descriptors');
272 }
273
274 if (!atrule.descriptors[descriptor.name] &&
275 !atrule.descriptors[descriptor.basename]) {
276 return new SyntaxReferenceError('Unknown at-rule descriptor', descriptorName);
277 }
278 },
279 checkPropertyName: function(propertyName) {
280 var property = names.property(propertyName);
281
282 // don't match syntax for a custom property
283 if (property.custom) {
284 return new Error('Lexer matching doesn\'t applicable for custom properties');
285 }
286
287 if (!this.getProperty(propertyName)) {
288 return new SyntaxReferenceError('Unknown property', propertyName);
289 }
290 },
291
292 matchAtrulePrelude: function(atruleName, prelude) {
293 var error = this.checkAtrulePrelude(atruleName, prelude);
294
295 if (error) {
296 return buildMatchResult(null, error);
297 }
298
299 if (!prelude) {
300 return buildMatchResult(null, null);
301 }
302
303 return matchSyntax(this, this.getAtrule(atruleName).prelude, prelude, false);
304 },
305 matchAtruleDescriptor: function(atruleName, descriptorName, value) {
306 var error = this.checkAtruleDescriptorName(atruleName, descriptorName);
307
308 if (error) {
309 return buildMatchResult(null, error);
310 }
311
312 var atrule = this.getAtrule(atruleName);
313 var descriptor = names.keyword(descriptorName);
314
315 return matchSyntax(this, atrule.descriptors[descriptor.name] || atrule.descriptors[descriptor.basename], value, false);
316 },
317 matchDeclaration: function(node) {
318 if (node.type !== 'Declaration') {
319 return buildMatchResult(null, new Error('Not a Declaration node'));
320 }
321
322 return this.matchProperty(node.property, node.value);
323 },
324 matchProperty: function(propertyName, value) {
325 var error = this.checkPropertyName(propertyName);
326
327 if (error) {
328 return buildMatchResult(null, error);
329 }
330
331 return matchSyntax(this, this.getProperty(propertyName), value, true);
332 },
333 matchType: function(typeName, value) {
334 var typeSyntax = this.getType(typeName);
335
336 if (!typeSyntax) {
337 return buildMatchResult(null, new SyntaxReferenceError('Unknown type', typeName));
338 }
339
340 return matchSyntax(this, typeSyntax, value, false);
341 },
342 match: function(syntax, value) {
343 if (typeof syntax !== 'string' && (!syntax || !syntax.type)) {
344 return buildMatchResult(null, new SyntaxReferenceError('Bad syntax'));
345 }
346
347 if (typeof syntax === 'string' || !syntax.match) {
348 syntax = this.createDescriptor(syntax, 'Type', 'anonymous');
349 }
350
351 return matchSyntax(this, syntax, value, false);
352 },
353
354 findValueFragments: function(propertyName, value, type, name) {
355 return search.matchFragments(this, value, this.matchProperty(propertyName, value), type, name);
356 },
357 findDeclarationValueFragments: function(declaration, type, name) {
358 return search.matchFragments(this, declaration.value, this.matchDeclaration(declaration), type, name);
359 },
360 findAllFragments: function(ast, type, name) {
361 var result = [];
362
363 this.syntax.walk(ast, {
364 visit: 'Declaration',
365 enter: function(declaration) {
366 result.push.apply(result, this.findDeclarationValueFragments(declaration, type, name));
367 }.bind(this)
368 });
369
370 return result;
371 },
372
373 getAtrule: function(atruleName, fallbackBasename = true) {
374 var atrule = names.keyword(atruleName);
375 var atruleEntry = atrule.vendor && fallbackBasename
376 ? this.atrules[atrule.name] || this.atrules[atrule.basename]
377 : this.atrules[atrule.name];
378
379 return atruleEntry || null;
380 },
381 getAtrulePrelude: function(atruleName, fallbackBasename = true) {
382 const atrule = this.getAtrule(atruleName, fallbackBasename);
383
384 return atrule && atrule.prelude || null;
385 },
386 getAtruleDescriptor: function(atruleName, name) {
387 return this.atrules.hasOwnProperty(atruleName) && this.atrules.declarators
388 ? this.atrules[atruleName].declarators[name] || null
389 : null;
390 },
391 getProperty: function(propertyName, fallbackBasename = true) {
392 var property = names.property(propertyName);
393 var propertyEntry = property.vendor && fallbackBasename
394 ? this.properties[property.name] || this.properties[property.basename]
395 : this.properties[property.name];
396
397 return propertyEntry || null;
398 },
399 getType: function(name) {
400 return this.types.hasOwnProperty(name) ? this.types[name] : null;
401 },
402
403 validate: function() {
404 function validate(syntax, name, broken, descriptor) {
405 if (broken.hasOwnProperty(name)) {
406 return broken[name];
407 }
408
409 broken[name] = false;
410 if (descriptor.syntax !== null) {
411 walk(descriptor.syntax, function(node) {
412 if (node.type !== 'Type' && node.type !== 'Property') {
413 return;
414 }
415
416 var map = node.type === 'Type' ? syntax.types : syntax.properties;
417 var brokenMap = node.type === 'Type' ? brokenTypes : brokenProperties;
418
419 if (!map.hasOwnProperty(node.name) || validate(syntax, node.name, brokenMap, map[node.name])) {
420 broken[name] = true;
421 }
422 }, this);
423 }
424 }
425
426 var brokenTypes = {};
427 var brokenProperties = {};
428
429 for (var key in this.types) {
430 validate(this, key, brokenTypes, this.types[key]);
431 }
432
433 for (var key in this.properties) {
434 validate(this, key, brokenProperties, this.properties[key]);
435 }
436
437 brokenTypes = Object.keys(brokenTypes).filter(function(name) {
438 return brokenTypes[name];
439 });
440 brokenProperties = Object.keys(brokenProperties).filter(function(name) {
441 return brokenProperties[name];
442 });
443
444 if (brokenTypes.length || brokenProperties.length) {
445 return {
446 types: brokenTypes,
447 properties: brokenProperties
448 };
449 }
450
451 return null;
452 },
453 dump: function(syntaxAsAst, pretty) {
454 return {
455 generic: this.generic,
456 types: dumpMapSyntax(this.types, !pretty, syntaxAsAst),
457 properties: dumpMapSyntax(this.properties, !pretty, syntaxAsAst),
458 atrules: dumpAtruleMapSyntax(this.atrules, !pretty, syntaxAsAst)
459 };
460 },
461 toString: function() {
462 return JSON.stringify(this.dump());
463 }
464};
465
466module.exports = Lexer;
Note: See TracBrowser for help on using the repository browser.