|
47 | 47 | this.baseDir = typeof baseDirOrIncludes == 'string' ? baseDirOrIncludes : ".";
|
48 | 48 |
|
49 | 49 | /**
|
50 |
| - * Included sources by filename. |
51 |
| - * @type {Object.<string, string>} |
| 50 | + * Current base directory. |
| 51 | + * @type {string} |
| 52 | + * @expose |
52 | 53 | */
|
53 |
| - this.includes = typeof baseDirOrIncludes == 'object' ? baseDirOrIncludes : {}; |
| 54 | + this.dir = this.baseDir; |
54 | 55 |
|
55 | 56 | /**
|
56 |
| - * Whether running inside of node.js or not. |
57 |
| - * @type {boolean} |
| 57 | + * Included sources by filename. |
| 58 | + * @type {!Object.<string, string>} |
58 | 59 | * @expose
|
59 | 60 | */
|
60 |
| - this.isNode = (typeof window == 'undefined' || !window.window) && typeof require == 'function'; |
| 61 | + this.includes = typeof baseDirOrIncludes == 'object' ? baseDirOrIncludes : {}; |
61 | 62 |
|
62 | 63 | /**
|
63 | 64 | * Error reporting source ahead length.
|
|
67 | 68 | this.errorSourceAhead = 50;
|
68 | 69 |
|
69 | 70 | /**
|
70 |
| - * Runtime defines. |
71 |
| - * @type {Array.<string>} |
| 71 | + * Defines. |
| 72 | + * @type {!Object.<string>} |
| 73 | + * @expose |
72 | 74 | */
|
73 |
| - this.defines = []; |
| 75 | + this.defines = {}; |
74 | 76 | };
|
75 | 77 |
|
| 78 | + /** |
| 79 | + * Whether running under node.js or not. |
| 80 | + * @type {boolean} |
| 81 | + * @const |
| 82 | + * @expose |
| 83 | + */ |
| 84 | + Preprocessor.IS_NODE = (typeof window === 'undefined' || !window.window) && typeof require === 'function' && typeof process === 'object' && typeof process.nextTick === 'function'; |
| 85 | + |
76 | 86 | /**
|
77 | 87 | * Definition expression
|
78 | 88 | * @type {!RegExp}
|
|
107 | 117 | * #define EXPRESSION
|
108 | 118 | * @type {!RegExp}
|
109 | 119 | */
|
110 |
| - Preprocessor.DEFINE = /define[ ]+([^\n]+)\r?(?:\n|$)/g; |
| 120 | + Preprocessor.DEFINE = /define[ ]+([^ ]+)[ ]+([^\n]+)\r?(?:\n|$)/g; |
111 | 121 |
|
112 | 122 | /**
|
113 | 123 | * @type {!RegExp}
|
|
170 | 180 |
|
171 | 181 | /**
|
172 | 182 | * Evaluates an expression.
|
173 |
| - * @param {object.<string,string>} runtimeDefines Runtime defines |
174 |
| - * @param {Array.<string>|string} inlineDefines Inline defines (optional for backward compatibility) |
175 | 183 | * @param {string=} expr Expression to evaluate
|
176 | 184 | * @return {*} Expression result
|
177 | 185 | * @throws {Error} If the expression cannot be evaluated
|
178 | 186 | * @expose
|
179 | 187 | */
|
180 |
| - Preprocessor.evaluate = function(runtimeDefines, inlineDefines, expr) { |
181 |
| - if (typeof inlineDefines === 'string') { |
182 |
| - expr = inlineDefines; |
183 |
| - inlineDefines = []; |
184 |
| - } |
185 |
| - var addSlashes = Preprocessor.addSlashes; |
186 |
| - return (function(runtimeDefines, inlineDefines, expr) { |
187 |
| - for (var key in runtimeDefines) { |
188 |
| - if (runtimeDefines.hasOwnProperty(key)) { |
189 |
| - eval("var "+key+" = \""+addSlashes(""+runtimeDefines[key])+"\";"); |
190 |
| - } |
191 |
| - } |
192 |
| - for (var i=0; i<inlineDefines.length; i++) { |
193 |
| - var def = inlineDefines[i]; |
194 |
| - if (def.substring(0,9) != 'function ' && def.substring(0,4) != 'var ') { |
195 |
| - def = "var "+def; // Enforce local |
196 |
| - } |
197 |
| - eval(def); |
198 |
| - } |
199 |
| - return eval(expr); |
200 |
| - }).bind(null)(runtimeDefines, inlineDefines, expr); |
| 188 | + Preprocessor.evaluate = function(expr) { |
| 189 | + // Potentially this is dangerous but as we don't want to write a special interpreter, it must be good enough. |
| 190 | + if (expr.indexOf(";") >= 0) |
| 191 | + throw(new Error("Illegal expression: "+expr)); |
| 192 | + return eval("(function() { return "+expr+" }()"); // May also throw |
| 193 | + }; |
| 194 | + |
| 195 | + /** |
| 196 | + * Processes the specified sources using the given parameters. |
| 197 | + * @param {string} source Source to process |
| 198 | + * @param {string|Object.<string,string>=} baseDirOrIncludes Source base directory used for includes (node.js only) |
| 199 | + * or an object containing all the included sources by filename. Defaults to the current working directory. |
| 200 | + * @param {object.<string,string>} defines Defines |
| 201 | + * @param {function(string)=} verbose Print verbose processing information to the specified function as the first parameter. Defaults to not print debug information. |
| 202 | + * @returns {string} |
| 203 | + */ |
| 204 | + Preprocessor.process = function(source, baseDirOrIncludes, defines, verbose) { |
| 205 | + return new Preprocessor(source, baseDirOrIncludes).process(defines, verbose); |
201 | 206 | };
|
202 | 207 |
|
203 | 208 | /**
|
204 |
| - * Preprocesses. |
| 209 | + * Processes this instances sources. |
205 | 210 | * @param {object.<string,string>} defines Defines
|
206 | 211 | * @param {function(string)=} verbose Print verbose processing information to the specified function as the first parameter. Defaults to not print debug information.
|
207 | 212 | * @return {string} Processed source
|
|
212 | 217 | defines = defines || {};
|
213 | 218 | verbose = typeof verbose == 'function' ? verbose : function() {};
|
214 | 219 | verbose("Defines: "+JSON.stringify(defines));
|
| 220 | + |
| 221 | + var defs = {}; |
| 222 | + for (var i in this.defines) // Inline defines |
| 223 | + if (this.defines.hasOwnProperty(i)) |
| 224 | + defs[i] = this.defines[i]; |
| 225 | + for (i in defines) // Runtime defines |
| 226 | + if (defines.hasOwnProperty(i)) |
| 227 | + defs[i] = this.defines[i]; |
215 | 228 |
|
216 | 229 | var match, match2, include, p, stack = [];
|
217 | 230 | while ((match = Preprocessor.EXPR.exec(this.source)) !== null) {
|
|
234 | 247 | include = this.includes[include];
|
235 | 248 | }
|
236 | 249 | } else { // Load it if in node.js...
|
237 |
| - if (!this.isNode) { |
| 250 | + if (!Preprocessor.IS_NODE) { |
238 | 251 | throw(new Error("Failed to resolve include: "+this.baseDir+"/"+include));
|
239 | 252 | }
|
240 | 253 | try {
|
|
249 | 262 | include = '';
|
250 | 263 | for (var i=0; i<files.length; i++) {
|
251 | 264 | verbose(' incl: '+files[i]);
|
252 |
| - var contents = fs.readFileSync(files[i])+""; |
| 265 | + var contents = fs.readFileSync(files[i])+"\n"; // One new line between files |
253 | 266 | _this.includes[key] = contents;
|
254 | 267 | include += contents;
|
255 | 268 | }
|
|
274 | 287 | }
|
275 | 288 | include = match2[1];
|
276 | 289 | verbose(" expr: "+match2[1]);
|
277 |
| - include = Preprocessor.evaluate(defines, this.defines, match2[1]); |
| 290 | + include = Preprocessor.evaluate(defs, match2[1]); |
278 | 291 | verbose(" value: "+Preprocessor.nlToStr(include));
|
279 | 292 | this.source = this.source.substring(0, match.index)+indent+include+this.source.substring(Preprocessor.PUT.lastIndex);
|
280 | 293 | Preprocessor.EXPR.lastIndex = match.index + include.length;
|
|
289 | 302 | }
|
290 | 303 | verbose(" test: "+match2[2]);
|
291 | 304 | if (match2[1] == "ifdef") {
|
292 |
| - include = !!defines[match2[2]]; |
| 305 | + include = typeof defs[match2[2]] !== 'undefined'; |
293 | 306 | } else if (match2[1] == "ifndef") {
|
294 |
| - include = !defines[match2[2]]; |
| 307 | + include = typeof defs[match2[2]] === 'undefined'; |
295 | 308 | } else {
|
296 |
| - include = Preprocessor.evaluate(defines, this.defines, match2[2]); |
| 309 | + include = Preprocessor.evaluate(defines, match2[2]); |
297 | 310 | }
|
298 | 311 | verbose(" value: "+include);
|
299 | 312 | stack.push(p={
|
|
333 | 346 | if (match2[1] == 'else') {
|
334 | 347 | include = !before["include"];
|
335 | 348 | } else {
|
336 |
| - include = Preprocessor.evaluate(defines, this.defines, match2[2]); |
| 349 | + include = Preprocessor.evaluate(defs, match2[2]); |
337 | 350 | }
|
338 | 351 | stack.push(p={
|
339 | 352 | "include": !before["include"],
|
|
349 | 362 | if ((match2 = Preprocessor.DEFINE.exec(this.source)) === null) {
|
350 | 363 | throw(new Error("Illegal #"+match[2]+": "+this.source.substring(match.index, match.index+this.errorSourceAhead)+"..."));
|
351 | 364 | }
|
352 |
| - var define = match2[1]; |
353 |
| - verbose(" def: "+match2[1]); |
354 |
| - this.defines.push(define); |
| 365 | + var defineName = match2[1], |
| 366 | + defineValue = match2[2]; |
| 367 | + verbose(" def: "+defineName+" "+defineValue); |
| 368 | + defs[defineName] = defineValue; |
355 | 369 | this.source = this.source.substring(0, match.index)+indent+this.source.substring(Preprocessor.DEFINE.lastIndex);
|
356 | 370 | Preprocessor.EXPR.lastIndex = match.index;
|
357 | 371 | verbose(" continue at "+Preprocessor.EXPR.lastIndex);
|
|
0 commit comments