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 define(['constants', 'grammarConverter', 'logger', 'module'
 28         ], 
 29 	/**
 30 	 * @name SemanticInterpreter
 31 	 * @memberOf mmir
 32 	 * @static
 33 	 * @class
 34 	 */
 35 	function (
 36 		constants, GrammarConverter, Logger, module
 37 ){
 38 	
 39 	/**
 40 	 * The instance for the singleton SemanticInterpreter
 41 	 * 
 42 	 * @type SemanticInterpreter
 43 	 * @private
 44 	 * 
 45      * @memberOf SemanticInterpreter#
 46 	 */
 47 	var instance = null;
 48 
 49 	/**
 50 	 * @private
 51 	 * @type Logger
 52      * @memberOf SemanticInterpreter#
 53 	 */
 54 	var logger = Logger.create(module);
 55 	
 56 	/**
 57      * The version number for the format of generated (JavaScript) grammars.
 58      * 
 59      * This number is "written into" the generated grammars and then
 60      * used as argument, when the grammar adds itself via
 61      * <code>addGrammar(id, func, versionNumber)</code>.
 62      * 
 63      * See generator function build_grammar() within createAndAddGrammar().
 64      * 
 65      * NOTE: This version number must be increased, when way changes, how 
 66      *       grammars are generated.
 67      *       Or more precisely: when previously generated grammars cannot
 68      *       be used anymore, after the generation mechanism has been changed.
 69      * 
 70      * @constant
 71      * @private
 72      * 
 73      * @memberOf SemanticInterpreter#
 74      */
 75     var GRAMMAR_FILE_FORMAT_VERSION = 4;
 76     
 77     
 78     /**
 79      * @constructs SemanticInterpreter
 80      * @memberOf SemanticInterpreter#
 81      * @private
 82      * @ignore
 83      */
 84     function constructor(){
 85     	
 86 	    /**
 87 	     * "map" for grammar implementations (e.g. for different languages)
 88 	     * 
 89 	     * @private
 90 	     * 
 91 	     * @memberOf SemanticInterpreter#
 92 	     */
 93 	    var grammarImplMap = {};
 94 	    /**
 95 	     * list of IDs for grammar implementations (e.g. for different languages).
 96 	     * 
 97 	     * This list contains the "keys" of all current entries in <tt>grammarImplMap</tt>.
 98 	     * 
 99 	     * @private
100 	     * @type Array<String>
101 	     * 
102 	     * @memberOf SemanticInterpreter#
103 	     */
104 	    var grammarImplList = [];
105 	    
106 	    /**
107 	     * id (i.e. the <em>key</em> for map <tt>grammarImplMap</tt>) for currently used
108 	     * grammar.
109 	     * 
110 	     * If for invocations of getASRSemantic(..) etc. function the ID/languageCode
111 	     * argument is missing/omitted, then this id will be used.
112 	     * 
113 	     * NOTE: if not <tt>NULL</tt>, the grammar must be available, either
114 	     * 		 as compiled JS file (which must be already loaded, i.e. already present in <tt>grammarImplMap</tt>), or
115 	     * 		 as JSON grammar file (which must be available at <tt>/config/languages/[ID]/grammar.json</tt>
116 	     * 
117 	     * @type String
118 	     * @private
119 	     * 
120 	     * @memberOf SemanticInterpreter#
121 	     */
122 	    var currentGrammarId = null;
123 	    
124 	    /**
125 	     * @type String
126 	     * @private
127 	     * @memberOf SemanticInterpreter#
128 	     */
129 	    var currentGrammarEningeId = null;
130 
131 	    /**
132 	     * If true, the async versions of the grammar engines are loaded,
133 	     * i.e. compilation of grammar parsers will be asynchronously done in a WebWorker
134 	     * 
135 	     * @type Boolean
136 	     * @private
137 	     * @default false
138 	     * @memberOf SemanticInterpreter#
139 	     */
140 	    var _isAsyncCompileMode = false;
141 	    
142 	    /**
143 	     * @type String
144 	     * @constant
145 	     * @private
146 	     * @memberOf SemanticInterpreter#
147 	     */
148 	    var DEFAULT_GRAMMAR_ENGINE = 'jscc';
149 	    /**
150 	     * @type String
151 	     * @constant
152 	     * @private
153 	     * @memberOf SemanticInterpreter#
154 	     */
155 	    var GRAMMAR_MODULE_ID_POSTFIX = 'Gen';
156 	    /**
157 	     * @type String
158 	     * @constant
159 	     * @private
160 	     * @memberOf SemanticInterpreter#
161 	     */
162 	    var GRAMMAR_ASYNC_MODULE_MODIFIER = 'Async';
163 	    
164 	    /**
165 	     * @private
166 	     * @memberOf SemanticInterpreter#
167 	     */
168 	    var doSetGrammarEngine = function(id, asyncCompileMode){
169 	    	
170 	    	currentGrammarEningeId = id;
171 	    	
172 	    	if(typeof asyncCompileMode !== 'undefined'){
173 	    		_isAsyncCompileMode = !!asyncCompileMode;
174 	    	}
175 	    };
176 	    /**
177 	     * @private
178 	     * @memberOf SemanticInterpreter#
179 	     */
180 	    var doGetGrammarEngine = function(){
181 	    	if(currentGrammarEningeId){
182 		    	return currentGrammarEningeId;	
183 	    	}
184 	    	return DEFAULT_GRAMMAR_ENGINE;
185 	    };
186     	
187 	    /**
188 	     * Flag for enabling/disabling processing of SemanticInterpreter.
189 	     * 
190 	     * If disabled, getASRSemantic(), removeStopwords() etc. (+ <tt>_alt</tt> versions) will return <tt>null</tt> values. 
191 	     * 
192 	     * NOTE: if no grammar for any language is available, the SemanticInterpreter should be disabled.
193 	     * 
194 	     *  Setting a language, automatically enables the the SemanticInterpreter.
195 	     * 
196 	     * @type Boolean
197 	     * @private
198 	     * @memberOf SemanticInterpreter#
199 	     */
200 	    var _isEnabled = false;
201 	    
202 
203 	    /**
204 	     * @private
205 	     * @memberOf SemanticInterpreter#
206 	     */
207 	    var doSetEnabled = function(isEnabled){
208         	_isEnabled = isEnabled;
209         };
210 
211 	    /**
212 	     * @private
213 	     * @memberOf SemanticInterpreter#
214 	     */
215         var doCheckIsEnabled = function(){
216         	return _isEnabled;
217         };
218 	    
219 	    /**
220 	     * 
221 	     * NOTE: if no other grammar is available yet, <tt>currentGrammarId</tt> will be set to <tt>id</tt>.
222 	     * 
223 	     * NOTE: if currently disabled, calling this function automatically enables ( setEnabled(TRUE) ),
224 	     * 		 the semantic interpreter.
225 	     * 
226 	     * @function
227 	     * @param id {String} ID for the grammar (e.g. an ISO-639 language code)
228 	     * @param grammarImpl {GrammarConverter|Function} the executable JavaScript grammar implementation
229 	     * 					IF {GrammarConverter}: the impl. with valid member {Function} {@link GrammarConverter.executeGrammar()}
230 	     * 					IF {Function}: the {Function} {@link GrammarConverter.executeGrammar()} - 
231 	     * 									In this case, if no GrammarConverter instance fo <tt>id</tt> is present, a new one will be created; 
232 	     * 									The stopwords must already be set, or must additionally be set for the GrammarConverter instance
233 	     * 									  (e.g. using {@link mmir.SemanticInterpreter.setStopwords})
234 	     * @param {Number|PlainObject} [fileFormatNo] OPTIONAL
235 		 * 					If Number and the number given does not match {@link #GRAMMAR_FILE_FORMAT_VERSION}
236 		 * 					the file format is assumed to be out-dated and an Error will be thrown.
237 		 * 					
238 		 * 					If PlainObject, i.e. an options object, the following properties are evaluated
239 		 * 					(all properties are opitional):
240 		 * 					<code>fileFormat: NUMBER, default: undefined</code>
241 		 * 						(desc. see above)
242 		 * 					<code>execMode: 'sync' | 'async', default: 'sync'</code>
243 		 * 						if 'async' then the grammar is executed asynchronously, i.e. getASRSemantic()
244 		 * 						must be invoked with a callback function in order to retrieve the result
245 		 * 
246 	     * @throws Error if <code>fileFormatNo</code> is given, but does not match GRAMMAR_FILE_FORMAT_VERSION.
247 	     * 
248 	     * @private
249 	     * @memberOf SemanticInterpreter#
250 	     */
251     	var doAddGrammar = function(id, grammarImpl, fileFormatNo){
252     		
253     		var execMode = 'sync';
254     		if(fileFormatNo && typeof fileFormatNo === 'object'){
255     			
256     			execMode = fileFormatNo.execMode;
257     			
258     			//lastly: overwrite fileFormatNo with the corresponding property:
259     			fileFormatNo = fileFormatNo.fileFormat;
260     		}
261     		
262     		//check if the added grammar has correct format
263     		if(fileFormatNo && fileFormatNo != GRAMMAR_FILE_FORMAT_VERSION){
264     			
265     			//grammar has old / out-dated format:
266     			throw new Error('Grammar file has wrong format: need grammar file with format version '
267     					+GRAMMAR_FILE_FORMAT_VERSION+', but got: '+fileFormatNo
268     					+ '. Please update generated grammar (delete '
269     					+ constants.getGeneratedGrammarsPath() +' and re-build grammars).'
270     			);
271     		}
272         	
273     		//the grammar function must be "wrapped" in a GrammarConverter instance
274     		// ... if not, do so now:
275         	if( ! (grammarImpl instanceof GrammarConverter)){
276         		var gc = doGetGrammar(id, true);
277         		
278         		//if for this ID (= language code) no grammar-converter
279         		// exists yet, create a now one
280         		// (otherwise, re-use the existing one)
281         		if(!gc){
282         			gc = new GrammarConverter();
283         		}
284         		gc.setGrammarFunction(grammarImpl, execMode === 'async');
285         		grammarImpl = gc;
286         	}
287         	
288         	var isAlreadyPresent = checkHasGrammar(id);
289         	grammarImplMap[id] = grammarImpl;
290         	
291         	if( ! isAlreadyPresent){
292         		
293         		//DISABLED: this may produce side effects (now: current grammar must be explicitly set using setCurrentGrammar(lang))
294 //	        	if(grammarImplList.length === 0){
295 //	        		currentGrammarId = id;
296 //	        	}
297 	        	grammarImplList.push(id);
298         	}
299         	
300         	doSetEnabled(true);
301         };
302         
303         /**
304 	     * @private
305 	     * @memberOf SemanticInterpreter#
306          */
307         var doSetStopwords = function(id, stopwordArray){
308         	doGetGrammar(id).setStopWords(stopwordArray);
309         };
310         /**
311          * HELPER retrieve the executable grammar:
312          * if already loaded, return the grammar instance, otherwise load & compile.
313          * 
314          * @param {String} id
315          * 		the ID (e.g. language code) for the grammar
316          * @param {Boolean} [doNotResolve] OPTIONAL
317          * 		if <code>false</code> AND the request grammar is not loaded yet,
318          * 		then the grammar will NOT be loaded (if omitted or <code>true</code>
319          * 		missing grammars will automatically be loaded and compiled)
320          * @param {Function} [callback] OPTIONAL
321          * 		if grammar has to be loaded (and compiled), the provided callback
322          * 		will be called, after completion.
323          * 
324          * @return {GrammarExecFunction}
325          * 			the exectuable grammar (i.e. execution function), if the grammar is
326          * 			already loaded (if grammar has to loaded and compiled, you need to
327          * 			wait for the callback-call and then re-invoke doGetGrammar()).
328          * 
329 	     * @private
330 	     * @memberOf SemanticInterpreter#
331          */
332         var doGetGrammar = function(id, doNotResolve, callback){//NOTE: this should stay private
333         	
334         	if(!id){
335         		if(!currentGrammarId){
336         			throw 'Could not retrieve grammar: required grammar ID is missing';
337         		}
338         		else {
339         			id = currentGrammarId;
340         		}
341         	}
342         	
343         	//shift arguments, if necessary:
344         	if(!callback && typeof doNotResolve === 'function'){
345         		callback = doNotResolve;
346         		doNotResolve = false;
347         	}
348         	
349         	var isDefaultCallback = false;
350         	if(!callback && logger.isInfo()){
351         		//create a "debug-info callback"
352         		isDefaultCallback = true;
353         		callback = function(){
354         			if(logger.isInfo()) logger.info('created executable grammar for "'+id+'" from source '+jsonGrammarUrl);
355         		};
356         	}
357         	
358         	if(!doNotResolve && ! checkHasGrammar(id) ){
359         		var jsonGrammarUrl = instance.get_json_grammar_url(id);
360         		
361         		createAndAddGrammar(jsonGrammarUrl, id, callback);
362         	}
363         	else if(callback && !isDefaultCallback){
364         		callback();
365         	}
366         	
367         	return grammarImplMap[id];
368         };
369         /**
370          * @private
371 	     * @memberOf SemanticInterpreter#
372          */
373         var checkHasGrammar = function(id){
374         	return typeof grammarImplMap[id] !== 'undefined';
375         };
376         /**
377 	     * @private
378 	     * @memberOf SemanticInterpreter#
379          */
380         var doRemoveGrammar = function(id){
381         	
382         	if( checkHasGrammar(id) ){
383         		
384         		//remove from impl.-map:
385 	        	delete grammarImplMap[id];
386 	        	
387 	        	//remove from ID-list
388 	        	for(var i=0, size = grammarImplList.length; i < size; ++i){
389 	        		if(grammarImplList[i]==id){
390 		        		grammarImplList.splice(i, 1);
391 	        			break;
392 	        		}
393 	        	}
394         	}
395         };
396         
397         
398         //TODO move create/build into GrammarConverter
399     	/**
400          * @param {String|JSONObject} doRecompile
401          * 					IF {String}: the String's contents will be used as a String-representation of the JSON grammar
402          * 					IF {Object}: the Object will be used as JSON representation for the grammar 
403          * 
404          * @param {String} [generatedParserLanguageCode] OPTIONAL 
405          * 					if param doRecompile is used, this String specifies the 
406          * 					   language for the generated grammatic-parser. If omitted, the default "de" (German) will be used.
407          * 					NOTE: this must be a valid ISO language code!
408          * 
409          * @param {Function} [callback] OPTIONAL
410          * 							a callback that is invoked after the grammar was created and added to the SemanticInterpreter. 
411          * 							The callback-function will be invoked without arguments, i.e. <code>callback();</code>
412          * @function
413          * 
414 	     * @private
415 	     * @memberOf SemanticInterpreter#
416          */
417         function createAndAddGrammar(doRecompile, generatedParserLanguageCode, callback){
418         	
419         	var gc = new GrammarConverter();
420         	
421         	//callback that will be used after the JSON file for the grammar was loaded:
422         	function build_grammar(theConverterInstance){//<- argument is the GrammarConverter instance
423         	    
424         		var genId   = doGetGrammarEngine();//one of ['jscc' | 'pegjs' | 'jison'];
425         		var genName = genId + (_isAsyncCompileMode? GRAMMAR_ASYNC_MODULE_MODIFIER : '') + GRAMMAR_MODULE_ID_POSTFIX;
426         		
427         		var onModuleLoaded = function onLoad(gen){ 
428         			
429         			//initialize the generator (initialization may be async -> need callback/Promise)
430         			// (-> if already initialized, the then-callback will be invoked immediately)
431         			gen.init().then(function onInit(){
432         				
433         				//actually start compilation of the grammar definition:
434         				// usually this involves 2 steps: 
435         				//    (1) converting the JSON grammar into a specific ParserParser syntax (e.g. JS/CC syntax)
436         				//    (2) compiling this syntax using the corresponding Parser-Generator
437         				// -> the resulting parser-function will then be registered on the SemanticInterpreter instance
438         				//    (using its addGrammar() function) along with the stopword definition (using the setStopwords() function)
439         				gen.compileGrammar(theConverterInstance, generatedParserLanguageCode, GRAMMAR_FILE_FORMAT_VERSION, function onCompiled(convertedInstance){
440 	        		        
441         					//add the grammar-parser-text and grammar-definition-text to the newly registered Grammar-instance
442         					// (-> registering is done within the compileGrammar() function!)
443 	        		        var registeredGrammarInstance = doGetGrammar(generatedParserLanguageCode, true);
444 	        		        if(registeredGrammarInstance){
445 		        		        registeredGrammarInstance.setGrammarSource(convertedInstance.getGrammarSource());
446 		        		        registeredGrammarInstance.setGrammarDef(convertedInstance.getGrammarDef());
447 	        		        }
448 	        		        else {
449 	        		        	logger.error('A problem occured during generation of grammar for "'+generatedParserLanguageCode+'"');
450 	        		        }
451 	        		        
452 	        		        //invoke callback if present:
453 	        		        if(callback){
454 	        		        	callback(registeredGrammarInstance);
455 	        		        }
456         				});
457         			});
458         			
459         		};//END: onModuleLoaded([jsccGen])
460         		
461         		require([genName], onModuleLoaded, function(err){
462         			
463         			//if async-module could not be loaded, try sync-module
464         			if(_isAsyncCompileMode){
465         				
466         				logger.warn('Cannot use asynchronous compilation for '+genId+
467         						': no async module available, using sync compilation instead...'
468         				);
469         				genName = genId + GRAMMAR_MODULE_ID_POSTFIX;
470         				
471         				require([genName], onModuleLoaded);
472         			}
473         		});
474                 
475             }//END function build_grammar
476         	
477             if(typeof doRecompile === 'string'){// arg. is URL for JSON grammar definition
478             	
479             	//interpret STRING as URL for the JSON grammar:
480             	gc.loadGrammar(build_grammar, function(err){
481             			throw 'Could not find JSON grammar file at "'+doRecompile+'": '+err;
482             		}, doRecompile, true
483             	);
484             } else if(typeof doRecompile === 'object'){// arg. is JSONObject (ie. JSON grammar defintion)
485             	
486             	//ASSERT if doRecompile === null => throws error!
487             	
488             	gc.json_grammar_definition = doRecompile;
489             	build_grammar(gc);
490             	
491             } else {
492             	logger.error('SemanticInterpreter.__createAndAddGrammar: could not build grammar due to missing argumens');
493             }
494         }
495         
496         /**
497 	     * @private
498 	     * @memberOf SemanticInterpreter#
499          */
500         var process_asr_semantic = function(phrase, stopwordFunc, langCode, callback){
501 
502 			if(!doCheckIsEnabled()){
503 				logger.warn('SemanticInterpreter.getASRSemantic: currently disabled!');
504 				return null;
505 			}
506 			
507 			if(typeof langCode === 'function'){
508 				callback = langCode;
509 				langCode = void(0);
510 			}
511         	
512 			var execGrammar = function(grammarConverter, phrase, stopwordFunc, langCode, callback){
513         		
514 	            var strPreparedPhrase = grammarConverter.maskString( phrase.toLowerCase() );
515 	            strPreparedPhrase = stopwordFunc(strPreparedPhrase, langCode, grammarConverter);
516 	           
517 	            if(logger.isDebug()) logger.debug('SemanticInterpreter.process_asr_semantic('+langCode+'): removed stopwords, now parsing phrase "'+strPreparedPhrase+'"');//debug
518 	            
519 	            if(callback){
520 
521 		    		grammarConverter.executeGrammar( strPreparedPhrase, function(result){
522 		    			
523 		    			//unmask previously mask non-ASCII chars in all Strings of the returned result:
524 			    		result = grammarConverter.unmaskJSON(
525 			    				result
526 			    		);
527 			            
528 			            callback(result);//TODO return copy instead of original instance?
529 			            
530 		    		} );
531 		            
532 		    		
533 		            
534 	            } else {
535 	            	
536 		    		var result = grammarConverter.executeGrammar( strPreparedPhrase );
537 		            
538 		    		//unmask previously mask non-ASCII chars in all Strings of the returned result:
539 		    		result = grammarConverter.unmaskJSON(
540 		    				result
541 		    		);
542 		            
543 		            return result;//TODO return copy instead of original instance?
544 	            }
545 	            
546         	};
547         	
548 			var grammarReadyCallback;
549 			if(callback){
550 				
551 				grammarReadyCallback = function(){
552 					
553 					var grammarConverter = doGetGrammar(langCode);
554 					
555 					if(grammarConverter.isAsyncExec()){
556 						execGrammar(grammarConverter, phrase, stopwordFunc, langCode, callback);	
557 					} else {
558 						callback(execGrammar(grammarConverter, phrase, stopwordFunc, langCode));
559 					}
560 				};
561 			}
562 			
563         	var grammarConverter = doGetGrammar(langCode, grammarReadyCallback);
564         	
565         	if(!grammarConverter && ! grammarReadyCallback){
566     			throw 'NoGrammar_'+langCode;
567     		}
568         	
569         	if(!grammarReadyCallback){
570         		return execGrammar(grammarConverter, phrase, stopwordFunc, langCode);
571         	}
572         };
573         
574         /**
575 	     * @private
576 	     * @memberOf SemanticInterpreter#
577          */
578 		var removeStopwordsFunc =  function removeStopwords(thePhrase, lang, gc){
579 			if(!gc){
580 				gc = doGetGrammar(lang);
581 			}
582     		var stop_words_regexp = gc.getStopWordsRegExpr();
583     		
584     		var str = thePhrase;
585     		var encoded_stop_words_regexp = gc.getStopWordsEncRegExpr();
586     		if(encoded_stop_words_regexp){
587     			str = str.replace(gc.stop_words_regexp_enc, ' ').trim();
588     		}
589     		
590         	return str.replace(stop_words_regexp, '').trim();
591     	};
592     	
593     	/**
594 	     * @private
595 	     * @memberOf SemanticInterpreter#
596     	 */
597 		var removeStopwordsAltFunc = function removeStopwords_alt(thePhrase, lang, gc){
598 			if(!gc){
599 				gc = doGetGrammar(lang);
600 			}
601     		var stop_words_regexp = gc.getStopWordsRegExpr_alt();
602         	
603 			while (thePhrase.match(stop_words_regexp)) {
604 				thePhrase = thePhrase.replace(stop_words_regexp, ' ');
605 				thePhrase = thePhrase.trim();
606 			}
607 			
608 			return thePhrase;
609 		};
610         /**
611 	     * @private
612 	     * @memberOf SemanticInterpreter#
613          */
614 		var doRemoveStopWords = function(thePhrase, lang, func){
615 			if(!doCheckIsEnabled()){
616 				logger.warn('SemanticInterpreter.'+func.name+': currently disabled!');
617 				return null;
618 			}
619 			
620 			var grammarConverter = doGetGrammar(lang);
621         	
622         	if(!grammarConverter){
623     			throw 'NoGrammar_'+lang;
624     		}
625         	
626             var str = grammarConverter.maskString( thePhrase );
627             
628 //			var str = grammarConverter.encodeUmlauts(thePhrase, true);
629 			str = func(str, lang, grammarConverter);
630 			return grammarConverter.unmaskString( str );//grammarConverter.decodeUmlauts(str, true);
631 		};
632 		
633         var _tmpInstance = { // public members
634         		
635         	/**  @scope SemanticInterpreter# *///for jsdoc2
636 
637 			/**
638 			 * @deprecated use {@link #removeStopwords} instead
639              * @memberOf SemanticInterpreter.prototype
640 	         * @public
641 			 */
642 			removeStopwords_alt: function(thePhrase, lang){
643 				return doRemoveStopWords(thePhrase, lang, removeStopwordsAltFunc);
644 			},
645         	/**
646              * @param {String} phrase
647              * 					the phrase that will be parsed
648              * @param {String} langCode
649              * 					the language code (identifier) for the parser/grammar
650              * @param {Function} [callback] OPTIONAL
651              * 					a callback function that receives the return value
652              * 					(instead of receiving the result as return value from
653              * 					 this function directly).
654              * 					The signature for the callback is: <code>callback(result: Object)</code>
655              * 					  (i.e. the result that would be returned by this function itself is
656              * 					   passed as argument into the callback function; see also documentation
657              * 					   for <em>returns</em>).
658              * 					NOTE: in case, the grammar for the requested <code>langCode</code>
659              * 						  is not compiled yet (i.e. not present as executable JavaScript),
660              * 						  the corresponding JSON definition of the grammar needs to be
661              * 					      compiled first, before processing the ASR's semantics is possible.
662              * 						  In this case, a <code>callback</code> function <strong>MUST</strong>
663              * 						  be supplied in order to receive a result (since compilation of the
664              * 					      grammar may be <em>asynchronous</em>).  
665              * 
666              * @returns {Object}
667              * 				the parsing result (as processed by the parser / grammar;
668              * 				usually a JSON-like object).
669              * 				WARNING: if a <code>callback</code> function was provided, then
670              * 						 there is no return object.
671              * 
672 	         * @public
673              */
674             getASRSemantic: function(phrase, langCode, callback){
675             	
676             	return process_asr_semantic(phrase, removeStopwordsFunc, langCode, callback);
677             	
678             },
679             /**
680 	         * @public
681              * @deprecated use {@link #getASRSemantic} instead
682              */
683             getASRSemantic_alt: function(phrase, langCode){
684             	
685             	return process_asr_semantic(phrase, removeStopwordsAltFunc, langCode);
686             	
687             },
688             /**
689              * Removes stopwords using the stopword-list from the parser/grammar
690              * for <code>lang</code>.
691              * 
692              * NOTE: <code>{@link #getASRSemantic}</code> automatically applies stopword-removal
693              * 		 (i.e. there is no need to manually remove stopwords using this function
694              * 		  when using <code>{@link #getASRSemantic}</code>).
695              * 
696              * @param {String} thePhrase
697              * 					the Phrase for which stopwords should be removed
698              * @param {String} lang
699              * 					the language code (identifier) for the parser/grammar
700              * 
701 	         * @public
702              */
703 			removeStopwords: function(thePhrase, lang){
704 				return doRemoveStopWords(thePhrase, lang, removeStopwordsFunc);
705 			},
706 			/** NOTE: the grammar must be compiled first, see getNewInstance(true)  @public */
707 			getGrammarDefinitionText: function(id){
708 				return doGetGrammar(id).getGrammarDef();//grammarDefinitionText;
709 			},
710 			/** NOTE: the grammar must be compiled first, see getNewInstance(true)  @public*/
711 			getGrammarParserText: function(id){
712 				return doGetGrammar(id).getGrammarSource();//grammarParser;
713 			},
714 			/**
715 			 * 
716 	         * @public
717 			 * @param {String} id
718 			 * @returns {GrammarConverter}
719 			 */
720 			getGrammarConverter: function(id){
721 				return doGetGrammar(id, true);//<- if no grammar is loaded for this ID, do NOT try to load it!
722 			},
723 			/**
724 			 * 
725 	         * @public
726 			 * @param {String|JSONObject} rawGrammarSrc
727 			 * @param {String} id
728 			 * @param {Function} [callback]
729 			 * @returns {SemanticInterpreter.prototype}
730 			 */
731 			createGrammar: function(rawGrammarSrc, id, callback){
732 				
733 				if(!id){
734 					throw 'missing ID for generated grammar';//TODO
735 				}
736 		        
737 				createAndAddGrammar(rawGrammarSrc, id, callback);
738 				
739 				return this;
740 			},
741 			/**  
742 			 * @public
743 			 * @function
744 			 */
745 	        addGrammar: doAddGrammar,
746 			/**  
747 			 * @public
748 			 * @function
749 			 */
750 	        setStopwords: doSetStopwords,
751 //	        getGrammar: doGetGrammar, <- set to private
752 			/**  
753 			 * @public
754 			 * @function
755 			 */
756 	        hasGrammar: checkHasGrammar,
757 			/**  
758 			 * @public
759 			 * @function
760 			 */
761 	        removeGrammar: doRemoveGrammar,
762 
763 	        /**
764 	         * Sets the current grammar.
765 	         * 
766 	         * If in invocations of {@link #getASRSemantic} the grammar ID (e.g. language code) is missing,
767 	         * then this grammar that is set here is used.
768 	         * 
769 	         * The id must reference either a grammar that was compiled (i.e. generated JavaScript file)
770 	         * for this id, or there must exists JSON-grammar file for which the language-dir matches the id parameter,
771 	         * e.g. <code>config/languages/[id]/grammar.json</code>.
772 	         * 
773 	         * @param {String} id the ID for the grammar, e.g. an ISO language code
774 	         * 
775 	         * @function
776 	         * @public
777 	         */
778 	        setCurrentGrammar: function(id){
779 	        	currentGrammarId = id;
780 	        	
781 	        	//set semantic-interpreter to enabled
782 	        	//  (this ensures, that JSON-grammars are automatically loaded,
783 	        	//   if no corresponding compiled JS-grammar is available yet) 
784 	        	doSetEnabled(true);
785 	        },
786 			/**  @public  */
787 	        getCurrentGrammar: function(){
788 	        	return currentGrammarId;
789 	        },
790 
791 			/**  @public  */
792 	        setEnabled: function(isEnabled){
793 	        	doSetEnabled(isEnabled);
794 	        },
795 			/**  @public  */
796 	        isEnabled: function(){
797 	        	return doCheckIsEnabled();
798 	        },
799 	        
800 	        /**
801 	         * Get the ID of the current grammar engine / compiler.
802 	         * 
803 	         * @default "jcss"
804 	         * @returns {String}
805 	         * 			the ID of the current grammar engine
806 	         * @public
807 	         */
808 	        getGrammarEngine: function(){
809 	        	return doGetGrammarEngine();
810 	        },
811 	        /**
812 	         * Set the grammar engine, i.e. the
813 	         * compiler engine for the JSON grammar
814 	         * 
815 	         * NOTE: implementations of the grammar engines are located at env/grammar/
816 	         *       The file-name for an implementation should follow the convention: ID+"Generator.js"
817 	         *       and should be registered with requirejs with the module-ID: ID+"Gen"
818 	         * 
819 	         * @param {String} egnineId
820 	         * 			the ID for the engine.
821 	         * 			Possible values: "jscc", "jison", "pegjs"
822 	         * 
823 	         * @param {Boolean} [asyncCompileMode] OPITIONAL
824 	         * 			sets the compile mode (sychronous or asynchronous) when generating new parsers
825 	         * 			with the grammar-engine.
826 	         * 			DEFAULT: VOID (i.e. leave current set compile-mode setting unchanged)
827 	         * 
828 	         * @public
829 	         */
830 	        setGrammarEngine: function(engineId, asyncCompileMode){
831 	        	doSetGrammarEngine(engineId, asyncCompileMode);
832 	        },
833 	        
834 	        /**
835 	         * Set compile-mode (sychronous or asynchronous) for the grammar engine, i.e. if the
836 	         * compiler engine for the JSON grammar should run synchronously or asynchronously.
837 	         * 
838 	         * NOTE: if there is no asynchronous implementation available for the grammar engine,
839 	         * 		 the sync-impl. is used by default.
840 	         * 
841 	         * NOTE: asynchronous compile mode requires WebWorkers
842 	         * 
843 	         * @param {Boolean} asyncCompileMode
844 	         * 			sets the compile mode (sychronous or asynchronous) when generating new parsers
845 	         * 			with the grammar-engine.
846 	         * 
847 	         * @public
848 	         * @default false (i.e. synchronous compile mode)
849 	         * @require WebWorker (if async mode)
850 	         */
851 	        setEngineCompileMode: function(asyncCompileMode){
852 	        	_isAsyncCompileMode = !!asyncCompileMode;
853 	        },
854 	        /**
855 	         * @returns {Number} the current version number that this SemanticInterpreter
856 	         * 				instance supports, for the file format of compiled grammars.
857 	         */
858 	        getFileVersion: function(){
859 	        	return GRAMMAR_FILE_FORMAT_VERSION;
860 	        },
861 	        
862 	        //FIXME rename/move functions
863 	        get_json_grammar_url: function(id){
864 	        	var configLangPath = constants.getLanguagePath();
865 	        	var jsonGrammarFileName = constants.getGrammarFileName();
866 	        	
867 	        	return configLangPath + id + '/' +jsonGrammarFileName;
868 	        }
869         };//END: var _tmpInstance = {...
870         
871         return _tmpInstance;
872     }
873     
874     instance = new constructor();
875     
876     /**
877 	 * @deprecated instead: use <code>mmir.SemanticInterpreter</code> directly
878 	 * 
879 	 * @function
880 	 * @name getInstance
881 	 * @public
882      * @memberOf SemanticInterpreter.prototype
883 	 */
884 	instance.getInstance = function(){
885 		return instance;
886 	};
887     
888     return instance;
889     
890 });//END: define(..., function(){
891