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

Last change on this file since cdcff72 was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

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