source: imaps-frontend/node_modules/css-tree/lib/parser/create.js@ d565449

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

Update repo after prototype presentation

  • Property mode set to 100644
File size: 9.5 KB
RevLine 
[d565449]1var OffsetToLocation = require('../common/OffsetToLocation');
2var SyntaxError = require('../common/SyntaxError');
3var TokenStream = require('../common/TokenStream');
4var List = require('../common/List');
5var tokenize = require('../tokenizer');
6var constants = require('../tokenizer/const');
7var { findWhiteSpaceStart, cmpStr } = require('../tokenizer/utils');
8var sequence = require('./sequence');
9var noop = function() {};
10
11var TYPE = constants.TYPE;
12var NAME = constants.NAME;
13var WHITESPACE = TYPE.WhiteSpace;
14var COMMENT = TYPE.Comment;
15var IDENT = TYPE.Ident;
16var FUNCTION = TYPE.Function;
17var URL = TYPE.Url;
18var HASH = TYPE.Hash;
19var PERCENTAGE = TYPE.Percentage;
20var NUMBER = TYPE.Number;
21var NUMBERSIGN = 0x0023; // U+0023 NUMBER SIGN (#)
22var NULL = 0;
23
24function createParseContext(name) {
25 return function() {
26 return this[name]();
27 };
28}
29
30function processConfig(config) {
31 var parserConfig = {
32 context: {},
33 scope: {},
34 atrule: {},
35 pseudo: {}
36 };
37
38 if (config.parseContext) {
39 for (var name in config.parseContext) {
40 switch (typeof config.parseContext[name]) {
41 case 'function':
42 parserConfig.context[name] = config.parseContext[name];
43 break;
44
45 case 'string':
46 parserConfig.context[name] = createParseContext(config.parseContext[name]);
47 break;
48 }
49 }
50 }
51
52 if (config.scope) {
53 for (var name in config.scope) {
54 parserConfig.scope[name] = config.scope[name];
55 }
56 }
57
58 if (config.atrule) {
59 for (var name in config.atrule) {
60 var atrule = config.atrule[name];
61
62 if (atrule.parse) {
63 parserConfig.atrule[name] = atrule.parse;
64 }
65 }
66 }
67
68 if (config.pseudo) {
69 for (var name in config.pseudo) {
70 var pseudo = config.pseudo[name];
71
72 if (pseudo.parse) {
73 parserConfig.pseudo[name] = pseudo.parse;
74 }
75 }
76 }
77
78 if (config.node) {
79 for (var name in config.node) {
80 parserConfig[name] = config.node[name].parse;
81 }
82 }
83
84 return parserConfig;
85}
86
87module.exports = function createParser(config) {
88 var parser = {
89 scanner: new TokenStream(),
90 locationMap: new OffsetToLocation(),
91
92 filename: '<unknown>',
93 needPositions: false,
94 onParseError: noop,
95 onParseErrorThrow: false,
96 parseAtrulePrelude: true,
97 parseRulePrelude: true,
98 parseValue: true,
99 parseCustomProperty: false,
100
101 readSequence: sequence,
102
103 createList: function() {
104 return new List();
105 },
106 createSingleNodeList: function(node) {
107 return new List().appendData(node);
108 },
109 getFirstListNode: function(list) {
110 return list && list.first();
111 },
112 getLastListNode: function(list) {
113 return list.last();
114 },
115
116 parseWithFallback: function(consumer, fallback) {
117 var startToken = this.scanner.tokenIndex;
118
119 try {
120 return consumer.call(this);
121 } catch (e) {
122 if (this.onParseErrorThrow) {
123 throw e;
124 }
125
126 var fallbackNode = fallback.call(this, startToken);
127
128 this.onParseErrorThrow = true;
129 this.onParseError(e, fallbackNode);
130 this.onParseErrorThrow = false;
131
132 return fallbackNode;
133 }
134 },
135
136 lookupNonWSType: function(offset) {
137 do {
138 var type = this.scanner.lookupType(offset++);
139 if (type !== WHITESPACE) {
140 return type;
141 }
142 } while (type !== NULL);
143
144 return NULL;
145 },
146
147 eat: function(tokenType) {
148 if (this.scanner.tokenType !== tokenType) {
149 var offset = this.scanner.tokenStart;
150 var message = NAME[tokenType] + ' is expected';
151
152 // tweak message and offset
153 switch (tokenType) {
154 case IDENT:
155 // when identifier is expected but there is a function or url
156 if (this.scanner.tokenType === FUNCTION || this.scanner.tokenType === URL) {
157 offset = this.scanner.tokenEnd - 1;
158 message = 'Identifier is expected but function found';
159 } else {
160 message = 'Identifier is expected';
161 }
162 break;
163
164 case HASH:
165 if (this.scanner.isDelim(NUMBERSIGN)) {
166 this.scanner.next();
167 offset++;
168 message = 'Name is expected';
169 }
170 break;
171
172 case PERCENTAGE:
173 if (this.scanner.tokenType === NUMBER) {
174 offset = this.scanner.tokenEnd;
175 message = 'Percent sign is expected';
176 }
177 break;
178
179 default:
180 // when test type is part of another token show error for current position + 1
181 // e.g. eat(HYPHENMINUS) will fail on "-foo", but pointing on "-" is odd
182 if (this.scanner.source.charCodeAt(this.scanner.tokenStart) === tokenType) {
183 offset = offset + 1;
184 }
185 }
186
187 this.error(message, offset);
188 }
189
190 this.scanner.next();
191 },
192
193 consume: function(tokenType) {
194 var value = this.scanner.getTokenValue();
195
196 this.eat(tokenType);
197
198 return value;
199 },
200 consumeFunctionName: function() {
201 var name = this.scanner.source.substring(this.scanner.tokenStart, this.scanner.tokenEnd - 1);
202
203 this.eat(FUNCTION);
204
205 return name;
206 },
207
208 getLocation: function(start, end) {
209 if (this.needPositions) {
210 return this.locationMap.getLocationRange(
211 start,
212 end,
213 this.filename
214 );
215 }
216
217 return null;
218 },
219 getLocationFromList: function(list) {
220 if (this.needPositions) {
221 var head = this.getFirstListNode(list);
222 var tail = this.getLastListNode(list);
223 return this.locationMap.getLocationRange(
224 head !== null ? head.loc.start.offset - this.locationMap.startOffset : this.scanner.tokenStart,
225 tail !== null ? tail.loc.end.offset - this.locationMap.startOffset : this.scanner.tokenStart,
226 this.filename
227 );
228 }
229
230 return null;
231 },
232
233 error: function(message, offset) {
234 var location = typeof offset !== 'undefined' && offset < this.scanner.source.length
235 ? this.locationMap.getLocation(offset)
236 : this.scanner.eof
237 ? this.locationMap.getLocation(findWhiteSpaceStart(this.scanner.source, this.scanner.source.length - 1))
238 : this.locationMap.getLocation(this.scanner.tokenStart);
239
240 throw new SyntaxError(
241 message || 'Unexpected input',
242 this.scanner.source,
243 location.offset,
244 location.line,
245 location.column
246 );
247 }
248 };
249
250 config = processConfig(config || {});
251 for (var key in config) {
252 parser[key] = config[key];
253 }
254
255 return function(source, options) {
256 options = options || {};
257
258 var context = options.context || 'default';
259 var onComment = options.onComment;
260 var ast;
261
262 tokenize(source, parser.scanner);
263 parser.locationMap.setSource(
264 source,
265 options.offset,
266 options.line,
267 options.column
268 );
269
270 parser.filename = options.filename || '<unknown>';
271 parser.needPositions = Boolean(options.positions);
272 parser.onParseError = typeof options.onParseError === 'function' ? options.onParseError : noop;
273 parser.onParseErrorThrow = false;
274 parser.parseAtrulePrelude = 'parseAtrulePrelude' in options ? Boolean(options.parseAtrulePrelude) : true;
275 parser.parseRulePrelude = 'parseRulePrelude' in options ? Boolean(options.parseRulePrelude) : true;
276 parser.parseValue = 'parseValue' in options ? Boolean(options.parseValue) : true;
277 parser.parseCustomProperty = 'parseCustomProperty' in options ? Boolean(options.parseCustomProperty) : false;
278
279 if (!parser.context.hasOwnProperty(context)) {
280 throw new Error('Unknown context `' + context + '`');
281 }
282
283 if (typeof onComment === 'function') {
284 parser.scanner.forEachToken((type, start, end) => {
285 if (type === COMMENT) {
286 const loc = parser.getLocation(start, end);
287 const value = cmpStr(source, end - 2, end, '*/')
288 ? source.slice(start + 2, end - 2)
289 : source.slice(start + 2, end);
290
291 onComment(value, loc);
292 }
293 });
294 }
295
296 ast = parser.context[context].call(parser, options);
297
298 if (!parser.scanner.eof) {
299 parser.error();
300 }
301
302 return ast;
303 };
304};
Note: See TracBrowser for help on using the repository browser.