2828 * @param {string } source Source to process
2929 * @param {string|Object.<string,string>= } baseDirOrIncludes Source base directory used for includes (node.js only)
3030 * or an object containing all the included sources by filename. Defaults to the current working directory.
31+ * @param {boolean } preserveLineNumbers When removing blocks of code, replace the block with blank lines so that
32+ * line numbers are preserved, as long as #include is not used
3133 * @constructor
3234 */
33- var Preprocessor = function Preprocessor ( source , baseDirOrIncludes ) {
35+ var Preprocessor = function Preprocessor ( source , baseDirOrIncludes , preserveLineNumbers ) {
3436
3537 /**
3638 * Source code to pre-process.
4749 this . baseDir = typeof baseDirOrIncludes == 'string' ? baseDirOrIncludes : "." ;
4850
4951 /**
50- * Current base directory.
51- * @type {string }
52- * @expose
52+ * Included sources by filename.
53+ * @type {Object.<string, string> }
5354 */
54- this . dir = this . baseDir ;
55+ this . includes = typeof baseDirOrIncludes == 'object' ? baseDirOrIncludes : { } ;
5556
5657 /**
57- * Included sources by filename.
58- * @type {!Object.<string, string> }
58+ * Preserve line numbers when removing blocks of code
59+ * @type {boolean }
60+ */
61+ this . preserveLineNumbers = typeof preserveLineNumbers == 'boolean' ? preserveLineNumbers : false ;
62+
63+ /**
64+ * Whether running inside of node.js or not.
65+ * @type {boolean }
5966 * @expose
6067 */
61- this . includes = typeof baseDirOrIncludes == 'object' ? baseDirOrIncludes : { } ;
68+ this . isNode = ( typeof window == 'undefined' || ! window . window ) && typeof require == 'function' ;
6269
6370 /**
6471 * Error reporting source ahead length.
6875 this . errorSourceAhead = 50 ;
6976
7077 /**
71- * Defines.
72- * @type {!Object.<string> }
73- * @expose
78+ * Runtime defines.
79+ * @type {Array.<string> }
7480 */
75- this . defines = { } ;
81+ this . defines = [ ] ;
7682 } ;
7783
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-
8684 /**
8785 * Definition expression
8886 * @type {!RegExp }
117115 * #define EXPRESSION
118116 * @type {!RegExp }
119117 */
120- Preprocessor . DEFINE = / d e f i n e [ ] + ( [ ^ ] + ) [ ] + ( [ ^ \n ] + ) \r ? (?: \n | $ ) / g;
118+ Preprocessor . DEFINE = / d e f i n e [ ] + ( [ ^ \n ] + ) \r ? (?: \n | $ ) / g;
121119
122120 /**
123121 * @type {!RegExp }
124122 * @inner
125123 */
126124 var GLOB_EXP = / (?: ^ | [ ^ \\ ] ) \* / ;
127125
126+ /**
127+ * @type {!RegExp }
128+ * @inner
129+ */
130+ var NOT_LINE_ENDING = / [ ^ \r \n ] / g;
131+
128132 /**
129133 * Strips slashes from an escaped string.
130134 * @param {string } str Escaped string
177181 Preprocessor . nlToStr = function ( str ) {
178182 return '[' + str . replace ( / \r / g, "" ) . replace ( / \n / g, "\\n" ) + ']' ;
179183 } ;
180-
184+
181185 /**
182186 * Evaluates an expression.
187+ * @param {object.<string,string> } runtimeDefines Runtime defines
188+ * @param {Array.<string>|string } inlineDefines Inline defines (optional for backward compatibility)
183189 * @param {string= } expr Expression to evaluate
184190 * @return {* } Expression result
185191 * @throws {Error } If the expression cannot be evaluated
186192 * @expose
187193 */
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 ) ;
194+ Preprocessor . evaluate = function ( runtimeDefines , inlineDefines , expr ) {
195+ if ( typeof inlineDefines === 'string' ) {
196+ expr = inlineDefines ;
197+ inlineDefines = [ ] ;
198+ }
199+ var addSlashes = Preprocessor . addSlashes ;
200+ return ( function ( runtimeDefines , inlineDefines , expr ) {
201+ for ( var key in runtimeDefines ) {
202+ if ( runtimeDefines . hasOwnProperty ( key ) ) {
203+ eval ( "var " + key + " = \"" + addSlashes ( "" + runtimeDefines [ key ] ) + "\";" ) ;
204+ }
205+ }
206+ for ( var i = 0 ; i < inlineDefines . length ; i ++ ) {
207+ var def = inlineDefines [ i ] ;
208+ if ( def . substring ( 0 , 9 ) != 'function ' && def . substring ( 0 , 4 ) != 'var ' ) {
209+ def = "var " + def ; // Enforce local
210+ }
211+ eval ( def ) ;
212+ }
213+ return eval ( expr ) ;
214+ } ) . bind ( null ) ( runtimeDefines , inlineDefines , expr ) ;
206215 } ;
207216
208217 /**
209- * Processes this instances sources .
218+ * Preprocesses .
210219 * @param {object.<string,string> } defines Defines
211220 * @param {function(string)= } verbose Print verbose processing information to the specified function as the first parameter. Defaults to not print debug information.
212221 * @return {string } Processed source
218227 verbose = typeof verbose == 'function' ? verbose : function ( ) { } ;
219228 verbose ( "Defines: " + JSON . stringify ( defines ) ) ;
220229
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 ] ;
228-
229230 var match , match2 , include , p , stack = [ ] ;
230231 while ( ( match = Preprocessor . EXPR . exec ( this . source ) ) !== null ) {
231232 verbose ( match [ 2 ] + " @ " + match . index + "-" + Preprocessor . EXPR . lastIndex ) ;
247248 include = this . includes [ include ] ;
248249 }
249250 } else { // Load it if in node.js...
250- if ( ! Preprocessor . IS_NODE ) {
251+ if ( ! this . isNode ) {
251252 throw ( new Error ( "Failed to resolve include: " + this . baseDir + "/" + include ) ) ;
252253 }
253254 try {
262263 include = '' ;
263264 for ( var i = 0 ; i < files . length ; i ++ ) {
264265 verbose ( ' incl: ' + files [ i ] ) ;
265- var contents = fs . readFileSync ( files [ i ] ) + "\n" ; // One new line between files
266+ var contents = fs . readFileSync ( files [ i ] ) + "" ;
266267 _this . includes [ key ] = contents ;
267268 include += contents ;
268269 }
287288 }
288289 include = match2 [ 1 ] ;
289290 verbose ( " expr: " + match2 [ 1 ] ) ;
290- include = Preprocessor . evaluate ( defs , match2 [ 1 ] ) ;
291+ include = Preprocessor . evaluate ( defines , this . defines , match2 [ 1 ] ) ;
291292 verbose ( " value: " + Preprocessor . nlToStr ( include ) ) ;
292293 this . source = this . source . substring ( 0 , match . index ) + indent + include + this . source . substring ( Preprocessor . PUT . lastIndex ) ;
293294 Preprocessor . EXPR . lastIndex = match . index + include . length ;
302303 }
303304 verbose ( " test: " + match2 [ 2 ] ) ;
304305 if ( match2 [ 1 ] == "ifdef" ) {
305- include = typeof defs [ match2 [ 2 ] ] !== 'undefined' ;
306+ include = ! ! defines [ match2 [ 2 ] ] ;
306307 } else if ( match2 [ 1 ] == "ifndef" ) {
307- include = typeof defs [ match2 [ 2 ] ] === 'undefined' ;
308+ include = ! defines [ match2 [ 2 ] ] ;
308309 } else {
309- include = Preprocessor . evaluate ( defines , match2 [ 2 ] ) ;
310+ include = Preprocessor . evaluate ( defines , this . defines , match2 [ 2 ] ) ;
310311 }
311312 verbose ( " value: " + include ) ;
312313 stack . push ( p = {
328329 }
329330 var before = stack . pop ( ) ;
330331 verbose ( " pop: " + JSON . stringify ( before ) ) ;
331- include = this . source . substring ( before [ "lastIndex" ] , match . index ) ;
332+
333+ if ( this . preserveLineNumbers ) {
334+ include = this . source . substring ( before [ "index" ] , before [ "lastIndex" ] ) . replace ( NOT_LINE_ENDING , "" ) +
335+ this . source . substring ( before [ "lastIndex" ] , match . index ) +
336+ this . source . substring ( match . index , Preprocessor . ENDIF . lastIndex ) . replace ( NOT_LINE_ENDING , "" ) ;
337+ } else {
338+ include = this . source . substring ( before [ "lastIndex" ] , match . index ) ;
339+ }
340+
332341 if ( before [ "include" ] ) {
333342 verbose ( " incl: " + Preprocessor . nlToStr ( include ) + ", 0-" + before [ 'index' ] + " + " + include . length + " bytes + " + Preprocessor . ENDIF . lastIndex + "-" + this . source . length ) ;
334343 this . source = this . source . substring ( 0 , before [ "index" ] ) + include + this . source . substring ( Preprocessor . ENDIF . lastIndex ) ;
344+ } else if ( this . preserveLineNumbers ) {
345+ verbose ( " excl(\\n): " + Preprocessor . nlToStr ( include ) + ", 0-" + before [ 'index' ] + " + " + Preprocessor . ENDIF . lastIndex + "-" + this . source . length ) ;
346+ include = include . replace ( NOT_LINE_ENDING , "" ) ;
347+ this . source = this . source . substring ( 0 , before [ "index" ] ) + include + this . source . substring ( Preprocessor . ENDIF . lastIndex ) ;
335348 } else {
336349 verbose ( " excl: " + Preprocessor . nlToStr ( include ) + ", 0-" + before [ 'index' ] + " + " + Preprocessor . ENDIF . lastIndex + "-" + this . source . length ) ;
337350 include = "" ;
346359 if ( match2 [ 1 ] == 'else' ) {
347360 include = ! before [ "include" ] ;
348361 } else {
349- include = Preprocessor . evaluate ( defs , match2 [ 2 ] ) ;
362+ include = Preprocessor . evaluate ( defines , this . defines , match2 [ 2 ] ) ;
350363 }
351364 stack . push ( p = {
352365 "include" : ! before [ "include" ] ,
362375 if ( ( match2 = Preprocessor . DEFINE . exec ( this . source ) ) === null ) {
363376 throw ( new Error ( "Illegal #" + match [ 2 ] + ": " + this . source . substring ( match . index , match . index + this . errorSourceAhead ) + "..." ) ) ;
364377 }
365- var defineName = match2 [ 1 ] ,
366- defineValue = match2 [ 2 ] ;
367- verbose ( " def: " + defineName + " " + defineValue ) ;
368- defs [ defineName ] = defineValue ;
369- this . source = this . source . substring ( 0 , match . index ) + indent + this . source . substring ( Preprocessor . DEFINE . lastIndex ) ;
378+ var define = match2 [ 1 ] ;
379+ verbose ( " def: " + match2 [ 1 ] ) ;
380+ this . defines . push ( define ) ;
381+ var lineEnding = ""
382+ if ( this . preserveLineNumbers ) {
383+ lineEnding = this . source . substring ( match . index , Preprocessor . DEFINE . lastIndex ) . replace ( NOT_LINE_ENDING , "" ) ;
384+ }
385+ this . source = this . source . substring ( 0 , match . index ) + indent + lineEnding + this . source . substring ( Preprocessor . DEFINE . lastIndex ) ;
370386 Preprocessor . EXPR . lastIndex = match . index ;
371387 verbose ( " continue at " + Preprocessor . EXPR . lastIndex ) ;
372388 }
398414 }
399415 global [ "dcodeIO" ] [ "Preprocessor" ] = Preprocessor ;
400416 }
401-
402- } ) ( this ) ;
417+
418+ } ) ( this ) ;
0 commit comments