1 /*
  2  * 	Copyright (C) 2012-2013 DFKI GmbH
  3  * 	Deutsches Forschungszentrum fuer Kuenstliche Intelligenz
  4  * 	German Research Center for Artificial Intelligence
  5  * 	http://www.dfki.de
  6  * 
  7  * 	Permission is hereby granted, free of charge, to any person obtaining a 
  8  * 	copy of this software and associated documentation files (the 
  9  * 	"Software"), to deal in the Software without restriction, including 
 10  * 	without limitation the rights to use, copy, modify, merge, publish, 
 11  * 	distribute, sublicense, and/or sell copies of the Software, and to 
 12  * 	permit persons to whom the Software is furnished to do so, subject to 
 13  * 	the following conditions:
 14  * 
 15  * 	The above copyright notice and this permission notice shall be included 
 16  * 	in all copies or substantial portions of the Software.
 17  * 
 18  * 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 19  * 	OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 20  * 	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 21  * 	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
 22  * 	CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 23  * 	TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 24  * 	SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 25  */
 26 
 27 
 28 /**
 29  * A Utility class for parsing (eHTML) templates.<br>
 30  * 
 31  * @class ParserUtils
 32  * 
 33  * @see mmir.parser.ParserUtils#constructor
 34  * 
 35  * @example mmir.ParserUtils.parse(str, view)
 36  */
 37 //mmir.parser.ParserUtils = (function(){});
 38 
 39 
 40 //(function ( mmir ) {
 41 
 42 define([ 'parserModule', 'parsingResult', 'templateProcessor'
 43         , 'templateLexer', 'ES3Lexer', 'ES3Parser', 'contentLexer', 'contentParser'
 44         , 'scriptLexer', 'scriptParser', 'antlr3'
 45     ], 
 46     
 47     /**
 48      * Utility functions for parsing templates (and template elements, e.g. JS-parts of template expressions)
 49      * 
 50      * @class
 51      * @name ParserUtils
 52      * @memberOf mmir.parser
 53      * @static
 54      * 
 55      * @public
 56      */
 57     function( parser, ParsingResult, templateProcessor
 58     	, MmirTemplateLexer, ES3Lexer, ES3Parser, MmirScriptContentLexer, MmirScriptContentParser
 59     	, MmirScriptLexer, MmirScriptParser, org
 60 ){
 61 
 62 	////////////////////////////////////helper for debugging / printing error details ////////////////////////
 63 
 64 	/**
 65 	 * -2: internal debug
 66 	 * -1: interanl info
 67 	 *  0: debug
 68 	 *  1: info
 69 	 *  2: warn
 70 	 *  3: error
 71 	 * TODO make this set-able (export getter/setter? use configurationManager?)
 72 	 * 
 73 	 * @private
 74 	 * @memberOf ParserUtils#
 75 	 */
 76 	var errorLevel = 2;
 77 	
 78 
 79 	/**
 80 	 * HELPER print internal debug messages during parsing (VERY VERBOSE)
 81 	 * 
 82 	 * @private
 83 	 * @memberOf ParserUtils#
 84 	 */
 85 	function _print(msg){//FIXME
 86 		if ( errorLevel <= -2 ) console.log(msg);
 87 	};
 88 	parser.print = _print;
 89 
 90 	/**
 91 	 * HELPER print internal, informational messages during parsing (VERBOSE)
 92 	 * 
 93 	 * @private
 94 	 * @memberOf ParserUtils#
 95 	 */
 96 	function _printInfo(prefix, msg){//FIXME
 97 		if (  errorLevel <= -1 ) console.info(parser.parserCreatePrintMessage(prefix,msg));
 98 	};
 99 	parser.printInfo = _printInfo;
100 
101 	/**
102 	 * HELPER print debug messages during parsing
103 	 * 
104 	 * @private
105 	 * @memberOf ParserUtils#
106 	 */
107 	function _parserPrintDebug(prefix, msg, source){//FIXME
108 		if (  errorLevel <= 0  ) console.debug(parser.parserCreatePrintMessage(prefix,msg, source));
109 	};
110 	parser.parserPrintDebug = _parserPrintDebug;
111 
112 	/**
113 	 * HELPER print informational messages during parsing
114 	 * 
115 	 * @private
116 	 * @memberOf ParserUtils#
117 	 */
118 	function _parserPrintInfo(prefix, msg, source){//FIXME
119 		if (  errorLevel <= 1  ) console.info(parser.parserCreatePrintMessage(prefix,msg, source));
120 	};
121 	parser.parserPrintInfo = _parserPrintInfo;
122 	
123 	/**
124 	 * HELPER print warnings during parsing
125 	 * 
126 	 * @private
127 	 * @memberOf ParserUtils#
128 	 */
129 	function _parserPrintWarning(prefix, msg, source){//FIXME
130 		if (  errorLevel <= 2  ) console.warn(parser.parserCreatePrintMessage(prefix,msg, source));
131 	};
132 	parser.parserPrintWarning = _parserPrintWarning;
133 
134 	/**
135 	 * HELPER print errors during parsing
136 	 * 
137 	 * @private
138 	 * @memberOf ParserUtils#
139 	 */
140 	function _parserPrintError(prefix, msg, source){
141 		if (  errorLevel <= 3  ) console.error(parser.parserCreatePrintMessage(prefix,msg, source));
142 	};
143 	parser.parserPrintError = _parserPrintError;
144 	
145 	/**
146 	 * HELPER: attach internal print-functions to all classes (ie. prototypes) in the list
147 	 * 
148 	 * @private
149 	 * @memberOf ParserUtils#
150 	 */
151 	var _attachInternalPrintFunc = function(list){
152 		var _prototype;
153 		for(var i=0, size=list.length; i < size; ++i){
154 			_prototype = list[i].prototype;
155 			_prototype.printInfo  = parser.printInfo;
156 			_prototype.printDebug = parser.print;
157 		}
158 	};
159 	
160 	//attach the internal debug/print functions to all lexers/parsers:
161 	// (-> see import-list in define() above)
162 	_attachInternalPrintFunc([
163 		MmirTemplateLexer, ES3Lexer, ES3Parser, MmirScriptContentLexer, MmirScriptContentParser
164 		, MmirScriptLexer, MmirScriptParser
165 	]);
166 	
167 	/**
168 	 * @type View
169 	 * @private
170 	 * @memberOf ParserUtils#
171 	 */
172 	var _currentParsedView = null;//FIXME make this an argument in the printXXX functions (e.g. the current mechanism will not work, if templates are parsed concurrently/in parallel/using threads)
173 	
174 	/**
175 	 * Creates a message with parsing-information.
176 	 * 
177 	 * In case the <code>msg</code> is an error message containing relative/incorrect location information,
178 	 * an heuristic will be used to fix the location information; in addition the references location
179 	 * will be extracted from the source-String and a "pointer-String" will be generated, e.g.
180 	 * <pre>
181 	 * 	source:   "  	@{  mmmm.['sd']=wer ;}@"
182 	 * 	pointer:  "  	        ^"
183 	 * </pre>
184 	 * 
185 	 * @function
186 	 * @param {String} prefix
187 	 * 					a prefix for the message
188 	 * @param {String} msg
189 	 * 					the original message (may contain location-information "line <line_i>:<position_j>")
190 	 * @param {Object} [tokenSource] OPTIONAL
191 	 * 					the token-source, from where the error/message was triggered
192 	 * 					If the argument has the field <code>tokenSource.offset</code> (Number)
193 	 * 					 if will be used to correct/fix the location information in the original message.
194 	 * 					If the argument has the fields <code>tokenSource.start</code> (Number) and
195 	 * 					 <code>tokenSource.end</code> (Number), then this will be used to correct/fix
196 	 * 					 the location information in the original message text.
197 	 * @param {Object} [viewObj] OPTIONAL
198 	 * 					currently not used!
199 	 * 					(will replace _currentParsedView in the future!)
200 	 * 
201 	 * @private
202 	 * @memberOf ParserUtils#
203 	 */
204 	var parserCreatePrintMessage = (function(){//return function(prefix, msg, tokenSource, viewObj)
205 		
206 		/**
207 		 *
208 		 * Get the index in the String str, where line number lineNo
209 		 * starts.
210 		 * 
211 		 * New lines begin after \n, \r\n, or \r.
212 		 * 
213 		 * If lineNo is <= 1, the function returns always 0.
214 		 * 
215 		 * If the lineNo is greater than the count of lines in str, the string length itself is returned. 
216 		 * 
217 		 * <p>
218 		 * NOTE used by {@link #parserCreatePrintMessage}
219 		 * 
220 		 * @function
221 		 * @param {String} str the string
222 		 * @param {Number} lineNo the line number (first line is 1)
223 		 * 
224 		 * @private
225 		 * @memberOf ParserUtils.parserCreatePrintMessage
226 		 */
227 		var getIndexForLine = (function(){
228 			
229 			var detectLinebreak = /(\r?\n|\r)/igm;
230 			
231 			return function(str, lineNo){
232 				if(lineNo <= 1){
233 					return 0;
234 				}
235 				var match;
236 				var count = 1;
237 				while(match = detectLinebreak.exec(str)){
238 					//ASSERT: lineNo >= 2
239 					if(++count == lineNo){
240 						break;
241 					}
242 				}
243 				
244 				//reset regexpr:
245 				detectLinebreak.lastIndex = 0;
246 				
247 				if(match){
248 					return match.index + match[1].length;
249 				}
250 				
251 				//request line-no. >= 2 AND loop "detect enough" linebreaks => the request line index starts after strings ends => return string's length
252 				return str.length;
253 			};
254 		})();//END getIndexForLine
255 
256 		/**
257 		 *
258 		 * Get the line in the String str, in which the char at index is included.
259 		 * 
260 		 * New lines begin after \n, \r\n, or \r,
261 		 * e.g. for line X: 
262 		 * <pre>
263 		 *  ...\r\n
264 		 *        ^
265 		 * </pre>
266 		 * the line number will be X (i.e. the line-break itself is still included in the current line).
267 		 * <p>
268 		 * If index is < 0, the function returns always 1.
269 		 * <p>
270 		 * If the index is greater than str.length, -1 is returned.
271 		 * <p>
272 		 * NOTE used by {@link #extractErrorPosition}
273 		 * 
274 		 * @function
275 		 * @param {String} str the string
276 		 * @param {Number} index the char index for which to find the line number (first line is 1)
277 		 * 
278 		 * @private
279 		 * 
280 		 * @memberOf ParserUtils.parserCreatePrintMessage
281 		 * 
282 		 */
283 		var getLineForIndex = (function(){
284 			
285 			var detectLinebreak = /(\r?\n|\r)/ig;
286 			
287 			return function(str, index){
288 				if(index < 0){
289 					return 1;
290 				}
291 				if(index >= str.length){
292 					return -1;
293 				}
294 				//ASSERT index is at least within line 1
295 				var match;
296 				var count = 1;
297 				var isNextLineFound = false;
298 	            var currentPos = -1;
299 	            var lastPos = 0;
300 				while(match = detectLinebreak.exec(str)){
301 	                currentPos = match.index + match[1].length;
302 					if(currentPos > index){
303 						isNextLineFound = true;
304 						break;
305 					}
306 	                lastPos = currentPos;
307 					++count;
308 				}
309 				
310 				//reset regexpr:
311 				detectLinebreak.lastIndex = 0;
312 				            
313 				return {
314 					line : count,
315 					index: index - lastPos
316 				};
317 			};
318 		})();//END getLineForIndex
319 
320 		/**
321 		 * 
322 		 * NOTE used by {@link #parserCreatePrintMessage}
323 		 * 
324 		 * @private
325 		 * @function
326 		 * @memberOf ParserUtils.parserCreatePrintMessage
327 		 */
328 		var extractErrorPosition = (function(){
329 			
330 			var detectLineNo = /line (\d+):(-?\d+)/i;
331 			
332 			return function extractErrorPositionImpl(msg, offset, originalContent, tokenSource){
333 //				console.log('\nTEST1_extractErrorPositionImpl with arguments '+arguments.length+'\n');
334 				
335 				var result = detectLineNo.exec(msg);
336 				
337 				//reset regexpr:
338 				detectLineNo.lastIndex = 0;
339 				
340 //				console.log('\nTEST2_result for "'+msg+'": '+result+'\n');
341 				var pos = null;
342 				if(result){
343 					
344 					var line = parseInt(result[1],10);
345 					var index = parseInt(result[2],10);
346 					
347 					var isCorrected = false;
348 					
349 					if(tokenSource){
350 						
351 						//if we have "invalid" position-info.:
352 						//  -> the error probably occured at the very beginning of the parsed expression
353 						//  -> try to extract position from parent parser/lexer
354 						if(line === 0 || index === -1){
355 							line  = tokenSource.getLine();
356 							index = tokenSource.getCharPositionInLine();
357 						}
358 						
359 						//if there is an offest supplied by the tokenSource -> use it:
360 						if(tokenSource.offset){
361 							
362 							var iOffset = tokenSource.offset;
363 //							if(line === 1){
364 //								//
365 //								//this position information is derived from a script-eval (-> ConentElement.ScriptEvalError)
366 //								// -> need to increase offset by 1 or 2, since all script-elements have
367 //								//    an additional, internal offset of 1 or 2 (this is only an heuristical value...)
368 //								// e.g. @( ...
369 //								//      @{ ...
370 //								//   @for( ...
371 //								iOffset += 2;
372 //							}
373 							
374 							var contentOffset = getLineForIndex(originalContent, iOffset);
375 							
376 							//if it is "relatively" the first line, we need to adjust to index 
377 							//   (i.e. the position within the line)
378 							if(line === 1){
379 								index += contentOffset.index;
380 							}
381 							
382 							//adjust the line, i.e. make "relative" -> "absolute" line number
383 							line += contentOffset.line - 1;
384 							
385 							isCorrected = true;
386 						}
387 						
388 					}
389 					
390 					pos = {
391 							line: line,
392 							index: index
393 					};
394 //					console.log('\nTEST3_pos: '+JSON.stringify(pos)+', offset: '+offset+'\n');
395 					
396 					if(offset && offset !== 0){
397 //						console.log('\nTEST4_offset: '+offset+'\n');
398 						
399 						var newLine = line;
400 						var newIndex = index;
401 						if( ! isCorrected){
402 							var lineOffset = getLineForIndex(originalContent, offset);
403 							if(line < 2){
404 								newIndex = lineOffset.index + index;
405 								pos.originalIndex = index;
406 								pos.index = newIndex;
407 							}
408 							newLine = lineOffset.line + line - 1;
409 							pos.originalLine = line;
410 							pos.line = newLine;
411 						}
412 						
413 						var fixed = msg.substring(0,result.index + 'line '.length) + newLine + ':' + newIndex + msg.substring(result.index + result[0].length);
414 						pos.text = fixed;
415 //						pos.originalContent = originalContent;
416 //						pos.offset = offset + pos.index;
417 					}
418 					else {
419 						pos.text = msg;
420 					}
421 				}
422 				else if(tokenSource && tokenSource.start && tokenSource.end){
423 					
424 					pos = getLineForIndex(originalContent, tokenSource.start);
425 					pos.text = ' near /';
426 				}
427 				
428 				return pos;
429 			};
430 		})();//END extractErrorPosition
431 		
432 		/**
433 		 * Create a message for parsing-information.
434 		 * 
435 		 * In case the <code>msg</code> is an error message containing relative/incorrect location information,
436 		 * an heuristic will be used to fix the location information; in addition the references location
437 		 * will be extracted from the source-String and a "pointer-String" will be generated, e.g.
438 		 * <pre>
439 		 * 	source:   "  	@{  mmmm.['sd']=wer ;}@"
440 		 * 	pointer:  "  	        ^"
441 		 * </pre>
442 		 * 
443 		 * @private
444 		 * 
445 		 * @param {String} prefix
446 		 * 					a prefix for the message
447 		 * @param {String} msg
448 		 * 					the original message (may contain location-information "line <line_i>:<position_j>")
449 		 * @param {Object} [tokenSource] OPTIONAL
450 		 * 					the token-source, from where the error/message was triggered
451 		 * 					If the argument has the field <code>tokenSource.offset</code> (Number)
452 		 * 					 if will be used to correct/fix the location information in the original message.
453 		 * 					If the argument has the fields <code>tokenSource.start</code> (Number) and
454 		 * 					 <code>tokenSource.end</code> (Number), then this will be used to correct/fix
455 		 * 					 the location information in the original message text.
456 		 * @param {Object} [viewObj] OPTIONAL
457 		 * 					currently not used!
458 		 * 					(will replace _currentParsedView in the future!)
459 		 */
460 		return function parserCreatePrintMessageImpl(prefix, msg, tokenSource, viewObj){//FIXME
461 			var currentView = _currentParsedView;
462 			if(currentView != null){
463 				
464 				var rootView = null;
465 				var details = '';
466 				if(currentView.getController){
467 					details += 'CTRL("' + currentView.getController().getName() + '")';
468 				}
469 				
470 				if(currentView.getView){
471 					if(details.length > 0){
472 						details += '->';
473 					}
474 					details += 'VIEW("' + currentView.getView().getName() + '")';
475 					rootView = currentView.getView();
476 				}
477 				
478 				if(details.length > 0){
479 					details += '->';
480 				}
481 				details += currentView.constructor.name;
482 				
483 				if(currentView.getName){
484 					details += '("' + currentView.getName() + '")';
485 				}
486 				
487 				if(rootView && typeof currentView.getStart !== 'undefined'){
488 					
489 					var pos = extractErrorPosition(msg, currentView.getOffset(), rootView.getDefinition(), tokenSource);
490 		//			console.log('\nTEST_A_pos: '+JSON.stringify(pos)+', offset: '+currentView.getStart() +'\n');
491 					if(pos){
492 		
493 						msg = pos.text;
494 						
495 						//msg += '\n\t at line '+pos.line+', index '+pos.index;
496 						var content = rootView.getDefinition();
497 						var line = null;
498 						var offset = currentView.getStart();
499 		
500 						
501 						if(content){
502 							var start = getIndexForLine(content, pos.line);
503 							var end = start;
504 							var len = content.length;
505 							while(end < len && (content[end] != '\r' && content[end] != '\n')){
506 								++end;
507 							}
508 							
509 							line = content.substring(start,end);
510 						}
511 						
512 						if(line){
513 							
514 							//marker for "pointing" the error
515 							var marker = [];
516 							for(var i=0; i < pos.index; ++i){
517 								if(line[i] == '\t'){
518 									//need to include tabs themselves, since they
519 									//  take more than 1 char-positions when displayed:
520 									marker.push('\t');
521 								}
522 								else {
523 									marker.push(' ');
524 								}
525 							}
526 							//add marker symbol, that points to error in the line above:
527 							marker.push('^');
528 			
529 							msg += ' at line '+pos.line+':';
530 							msg += '\n "'+line+'"';        //<- the line with the error
531 							msg += '\n  '+marker.join(''); //<- the marker line (will only be correctly aligned for fixed-width fonts)
532 						}
533 					}
534 				}
535 				
536 				return prefix + 'in ' + details + ' - ' + msg;
537 			}
538 			else {
539 				return prefix+msg;
540 			}
541 		};//END parserCreatePrintMessage
542 		
543 	})();//END var parserCreatePrintMessage = ...
544 	
545 	parser.parserCreatePrintMessage = parserCreatePrintMessage; 
546 	
547 	//////////////////////////////////// END: helper for debugging, error details etc. ////////////////////////
548 
549 		/**
550 	     * Object containing the instance of the class ParserUtils 
551 	     * 
552 	     * @type ParserUtils
553 	     * @private
554 	     * 
555 	     * @memberOf ParserUtils#
556 	     */
557 	    var instance = null;
558 
559 	    /**
560 		 * @private
561 	     * @memberOf ParserUtils#
562 	     */
563 	    var isDebug = true;//TODO read/set from configuration
564 	    
565 	    MmirTemplateLexer.prototype.emitErrorMessage = function(msg) {
566 	    	parser.parserPrintError('[ERROR] TemplateLexer: ', msg, this);
567 		};
568 //		MmirTemplateParser.prototype.emitErrorMessage = function(msg) {
569 //			parser.parserPrintError('[ERROR] TemplateParser: ',msg);
570 //		};
571 		
572 		ES3Lexer.prototype.emitErrorMessage = function(msg) {
573 			parser.parserPrintError('[ERROR] JavaScriptLexer_ES3: ', msg, this);
574 		};
575 		ES3Parser.prototype.emitErrorMessage = function(msg) {
576 			parser.parserPrintError('[ERROR] JavaScriptParser_ES3: ', msg, this.getTokenStream().getTokenSource());
577 		};
578 		
579 		MmirScriptLexer.prototype.emitErrorMessage = function(msg) {
580 			var mode = this.isStatementMode()? 'Statement' : 'Block';
581 			parser.parserPrintError('[ERROR] Script'+mode+'Lexer: ',msg, this);
582 		};
583 		MmirScriptParser.prototype.emitErrorMessage = function(msg) {
584 			parser.parserPrintError('[ERROR] ScriptParser: ',msg, this.getTokenStream().getTokenSource());
585 		};
586 		
587 		MmirScriptContentLexer.prototype.emitErrorMessage = function(msg) {
588 			parser.parserPrintError('[ERROR] ContentLexer: ',msg, this);
589 		};
590 		MmirScriptContentParser.prototype.emitErrorMessage = function(msg) {
591 			parser.parserPrintError('[ERROR] ContentParser: ',msg, this.getTokenStream().getTokenSource());
592 		};
593 		
594 		/**
595 		 * @private
596 	     * @memberOf ParserUtils#
597 		 */
598 		function internalParse(text) {
599 
600 		    var input = new org.antlr.runtime.ANTLRStringStream(text);//FIXME change, how dependency 'antlr3' is exported?
601 		  	var lexer = new MmirTemplateLexer(input);
602 		  	
603 		  	lexer.isDebug = isDebug;
604 		  	
605 		  	var tokens = new org.antlr.runtime.CommonTokenStream(lexer);//FIXME change, how dependency 'antlr3' is exported?
606 
607 			var result 				= {};
608 			result.rawTemplateText 	= tokens.toString();
609 			result.scripts 			= lexer.includeScripts;
610 			result.styles 			= lexer.includeStyles;
611 			result.localizations 	= lexer.locales;
612 			result.ifs	 			= lexer.ifs;
613 			result.fors 			= lexer.fors;
614 			result.yields 			= lexer.yields;
615 			result.contentFors 		= lexer.yieldContents;
616 			result.helpers	 		= lexer.helpers;
617 			result.partials 		= lexer.renderPartials;
618 			result.escapes	 		= lexer.escape;
619 			result.scriptStatements	= lexer.scriptStatements;
620 			result.scriptBlocks		= lexer.scriptBlocks;
621 			result.vars				= lexer.vars;
622 			result.comments			= lexer.comments;
623 			//end: parsing results
624 			
625 			
626 			lexer = null;
627 			
628 			return result;
629 		}
630 		
631 		/**
632 		 * @private
633 	     * @memberOf ParserUtils#
634 		 */
635 		function internalParseJS(text, entryRuleName, offset) {
636 		  	
637 		  	var input = new org.antlr.runtime.ANTLRStringStream(text);
638 		  	var lexer = new ES3Lexer(input);
639 		  	lexer.isDebug = isDebug;
640 		  	lexer.offset = offset;
641 		  	
642 		  	var tokens = new org.antlr.runtime.CommonTokenStream(lexer);
643 			var parser = new ES3Parser(tokens);
644 			parser.offset = offset;
645 			
646 			if(!entryRuleName){
647 //			var parseResult = 
648 				parser.program();//<- parse with main rule 'program' in ES3Parser
649 			}
650 			else {
651 //				var parseResult = 
652 					parser[entryRuleName]();//<- parse with main rule 'program' in ES3Parser
653 			}
654 			var result 				= new Object();
655 			result.rawTemplateText 	= tokens.toString();
656 			
657 			var varRefs = parser.getVarReferences();
658 			if(varRefs){
659 				result.varReferences = varRefs;
660 			}
661 			
662 			//TODO handle potentially global var-declaration (i.e. assignments without preceding var, where the variable is undefined yet)
663 			
664 			//end: parsing results
665 			
666 			lexer = null;
667 			parser = null;
668 			
669 			return result;
670 		}
671 		
672 //		var getVarReferences = function(parser){
673 //			
674 //			var size = parser.ampersatIdentifiers.length;
675 //			
676 //			if(size === 0){
677 //				return null;
678 //			}
679 //			
680 //			var varRefs = new Array(size);
681 //			for(var i=0; i < size; ++i){
682 //				var ref = parser.ampersatIdentifiers[i];
683 //				
684 //				var refObj = new mmir.parser.ParsingResult(ref);
685 ////				refObj.start = ref.start;
686 //				
687 //				//correct end-position (token's stop-index is exactly the last char-index, whereas ParsingResult's end-position is token.stopIndex + 1)
688 //				refObj.end = refObj.getEnd() + 1;
689 //				
690 //				refObj.type = mmir.parser.element.VAR_REFERENCE;
691 //				
692 //				varRefs[i] = refObj;
693 //			}
694 //			return varRefs;
695 //		};
696 		
697 	    /**
698 		 * Constructor-Method of Singleton mmir.parser.ParserUtils
699 		 * 
700 		 * @constructs ParserUtils
701 		 * @memberOf ParserUtils.prototype
702 		 * @private
703 		 * @ignore
704 		 * 
705 		 */
706 	    function constructor(){
707 	        //private members (currently none)
708 	    	
709 	    	/** @lends ParserUtils.prototype */
710 	    	return {
711 	        	//public members:
712 
713 	    		/**
714 	    		 * Parse a text as view template (e.g. *.ehtml files). 
715 	    		 * 
716 	    		 * @param {String} rawTemplateString the text that should be parsed
717 	    		 * @param {Object} [view] (optional) the view to which the <tt>rawTemplateString</tt> belongs (only used for error messages)
718 	    		 * @returns {mmir.parser.ParsingResult} the parsing result
719 	    		 * 
720 	    		 * @public
721 	    		 * @memberOf mmir.parser.ParserUtils.prototype
722 	    		 */
723 	    		parse: function(rawTemplateString, view){
724 	    			
725 	    			if(view){
726 	    				_currentParsedView = view;
727 	    			}
728 	    			else {
729 	    				_currentParsedView = null;
730 	    			}
731 	    			
732 	    			return internalParse(rawTemplateString);
733 	    		},
734 	    		
735 	    		/**
736 	    		 * Parse a text as JavaScript.
737 	    		 * 
738 	    		 * @param {String} rawTemplateString the text that should be parsed
739 	    		 * @param {String} [parseEntryRuleName] (optional) specifies the JavaScript element that should be parsed for
740 	    		 * @param {Object} [view] (optional) the view to which the <tt>rawTemplateString</tt> belongs (only used for error messages)
741 	    		 * @returns {mmir.parser.ParsingResult} the parsing result
742 	    		 * 
743 	    		 * @public
744 	    		 */
745 	    		parseJS: function(rawTemplateString, parseEntryRuleName, view, inViewOffset){
746 	    			
747 	    			//in case only 2 or 3 arguments are present: is 2nd the View object?
748 	    			if(!inViewOffset && typeof parseEntryRuleName !== 'string' && parseEntryRuleName !== null && typeof parseEntryRuleName === 'object'){
749 	    				
750 	    				if(typeof view === 'number'){
751 	    					inViewOffset = view;
752 	    				}
753 	    				
754 	    				view = parseEntryRuleName;
755 	    				parseEntryRuleName = null;
756 	    				
757 	    				
758 	    			}
759 	    			
760 	    			if(view){
761 	    				_currentParsedView = view;
762 	    			}
763 	    			else {
764 	    				_currentParsedView = null;
765 	    			}
766 	    			
767 	    			return internalParseJS(rawTemplateString, parseEntryRuleName, inViewOffset);
768 	    		}
769 	    	};//END: return{}
770 	    	
771 	    }//END: constructor()
772 	    
773 	    instance = new constructor();
774 
775 	    /**
776 	     * @deprecated instead, use ParseUtils object directly (i.e. omit getInstance() call)
777 	     * 
778 		 * @function
779 		 * @name getInstance
780 		 * 
781    		 * @public
782 	     * @memberOf ParserUtils#
783 	     */
784 	    instance.getInstance = function(){
785 	    	return this;
786 	    };
787 	    
788 	    //FIXME should the renderer be exported to parser.ParserUtils here?
789 	    parser.ParserUtils = instance;
790 	    
791 	    return instance;
792 		
793 	});//END define(..., function(){
794 
795 
796