define(['mmirf/resources', 'mmirf/grammarConverter', 'mmirf/logger', 'module', 'require'
],
/**
* @name SemanticInterpreter
* @memberOf mmir
* @static
* @class
* @hideconstructor
*
* @requires require
*/
function (
res, GrammarConverter, Logger, module, require
){
/**
* The instance for the singleton SemanticInterpreter
*
* @type SemanticInterpreter
* @private
*
* @memberOf mmir.SemanticInterpreter#
*/
var instance = null;
/**
* @private
* @type mmir.tools.Logger
* @memberOf mmir.SemanticInterpreter#
*/
var logger = Logger.create(module);
/**
* The version number for the format of generated (JavaScript) grammars.
*
* This number is "written into" the generated grammars and then
* used as argument, when the grammar adds itself via
* <code>addGrammar(id, func, versionNumber)</code>.
*
* See generator function build_grammar() within createAndAddGrammar().
*
* NOTE: This version number must be increased, when the way changes, how
* grammars are generated.
* Or more precisely: when previously generated grammars cannot
* be used anymore, after the generation mechanism has been changed.
*
* @constant
* @private
*
* @memberOf mmir.SemanticInterpreter#
*/
var GRAMMAR_FILE_FORMAT_VERSION = 7;
/**
* @constructs SemanticInterpreter
* @memberOf mmir.SemanticInterpreter#
* @private
* @ignore
*/
function constructor(){
/**
* "map" for grammar implementations (e.g. for different languages)
*
* @private
*
* @memberOf mmir.SemanticInterpreter#
*/
var grammarImplMap = {};
/**
* list of IDs for grammar implementations (e.g. for different languages).
*
* This list contains the "keys" of all current entries in <tt>grammarImplMap</tt>.
*
* @private
* @type Array<String>
*
* @memberOf mmir.SemanticInterpreter#
*/
var grammarImplList = [];
/**
* id (i.e. the <em>key</em> for map <tt>grammarImplMap</tt>) for currently used
* grammar.
*
* If for invocations of interpret(..) etc. function the ID/languageCode
* argument is missing/omitted, then this id will be used.
*
* NOTE: if not <tt>NULL</tt>, the grammar must be available, either
* as compiled JS file (which must be already loaded, i.e. already present in <tt>grammarImplMap</tt>), or
* as JSON grammar file (which must be available at <tt>/config/languages/[ID]/grammar.json</tt>
*
* @type String
* @private
*
* @memberOf mmir.SemanticInterpreter#
*/
var currentGrammarId = null;
/**
* @type String
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var currentGrammarEningeId = null;
/**
* If true, the async versions of the grammar engines are loaded,
* i.e. compilation of grammar parsers will be asynchronously done in a WebWorker
*
* @type Boolean
* @private
* @default false
* @memberOf mmir.SemanticInterpreter#
*/
var _isAsyncCompileMode = false;
/**
* If true, strict JavaScript mode will be disabled when generating grammars
*
* @type Boolean
* @private
* @default false
* @memberOf mmir.SemanticInterpreter#
*/
var _disableStrictMode = false;
/**
* If true, pre-processing input-phrase (before running interpretion) will
* include meta-data for changed positions (due to pre-processing) in input-string
*
* E.g. can be used to map semantic-results (matched tokens/utterances where
* e.g. stopwords would be removed) on the raw input-string.
*
* @type Boolean
* @private
* @default true
* @memberOf mmir.SemanticInterpreter#
*/
var _isCalcProcPos = true;
/**
* @type String
* @constant
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var DEFAULT_GRAMMAR_ENGINE = 'jscc';
/**
* @type String
* @constant
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var GRAMMAR_MODULE_ID_PREFIX = 'mmirf/';
/**
* @type String
* @constant
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var GRAMMAR_MODULE_ID_POSTFIX = 'Gen';
/**
* @type String
* @constant
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var GRAMMAR_ASYNC_MODULE_MODIFIER = 'Async';
/**
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var doSetGrammarEngine = function(id, asyncCompileMode, disableStrictMode){
currentGrammarEningeId = id;
if(typeof asyncCompileMode !== 'undefined'){
_isAsyncCompileMode = !!asyncCompileMode;
}
if(typeof disableStrictMode === 'boolean'){
_disableStrictMode = disableStrictMode;
}
};
/**
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var doGetGrammarEngine = function(){
if(currentGrammarEningeId){
return currentGrammarEningeId;
}
return DEFAULT_GRAMMAR_ENGINE;
};
/**
* Flag for enabling/disabling processing of SemanticInterpreter.
*
* If disabled, interpret(), applyPreProcessing() will return <tt>null</tt> values.
*
* NOTE: if no grammar for any language is available, the SemanticInterpreter should be disabled.
*
* Setting a language, automatically enables the the SemanticInterpreter.
*
* @type Boolean
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var _isEnabled = false;
/**
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var doSetEnabled = function(isEnabled){
_isEnabled = isEnabled;
};
/**
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var doCheckIsEnabled = function(){
return _isEnabled;
};
/**
* Add/register grammar for use with {@link #interpret}
*
* NOTE: if no other grammar is available yet, <tt>currentGrammarId</tt> will be set to <tt>id</tt>.
*
* NOTE: if currently disabled, calling this function automatically enables ( setEnabled(TRUE) ),
* the semantic interpreter.
*
* @function
* @param id {String} ID for the grammar (e.g. an ISO-639 language code)
* @param grammarImpl {mmir.grammar.GrammarConverter|Function} the executable JavaScript grammar implementation
* IF {mmir.grammar.GrammarConverter}: the impl. with valid member {Function} {@link mmir.grammar.GrammarConverter.executeGrammar()}
* IF {Function}: the {Function} {@link mmir.grammar.GrammarConverter#executeGrammar()} -
* In this case, if no GrammarConverter instance fo <tt>id</tt> is present, a new one will be created;
* The stopwords must already be set, be part of the options-argument
* (see doc for <code>fileFormatNo</code>), or must additionally be set for the GrammarConverter
* instance (e.g. using {@link mmir.SemanticInterpreter.setStopwords})
* @param {Number|PlainObject} [fileFormatNo] OPTIONAL
* If Number and the number given does not match {@link #GRAMMAR_FILE_FORMAT_VERSION}
* the file format is assumed to be out-dated and an Error will be thrown.
*
* If PlainObject, i.e. an options object, the following properties are evaluated
* (all properties are optional):
* <pre>fileFormat: NUMBER, default: undefined</pre>
* (desc. see above)
* <pre>execMode: 'sync' | 'async', default: 'sync'</pre>
* if 'async' then the grammar is executed asynchronously, i.e. interpret()
* must be invoked with a callback function in order to retrieve the result
* <pre>stopwords: Array<string>, default: null</pre>
* if given, the grammar (GrammarConverter) will be set with this stopword list, i.e. <code>grammar.setStopwords(stopwords)</code>
*
* @throws Error if <code>fileFormatNo</code> is given, but does not match GRAMMAR_FILE_FORMAT_VERSION.
*
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var doAddGrammar = function(id, grammarImpl, fileFormatNo){
var execMode = 'sync';
var stopwords = null;
if(fileFormatNo && typeof fileFormatNo === 'object'){
execMode = fileFormatNo.execMode;
stopwords = fileFormatNo.stopwords;
//lastly: overwrite fileFormatNo with the corresponding property:
fileFormatNo = fileFormatNo.fileFormat;
}
//check if the added grammar has correct format
if(fileFormatNo && fileFormatNo != GRAMMAR_FILE_FORMAT_VERSION){
//grammar has old / out-dated format:
throw new Error('Grammar file has wrong format: need grammar file with format version '
+GRAMMAR_FILE_FORMAT_VERSION+', but got: '+fileFormatNo
+ '. Please update generated grammar (delete '
+ res.getGeneratedGrammarsPath() +' and re-build grammars).'
);
}
//the grammar function must be "wrapped" in a GrammarConverter instance
// ... if not, do so now:
if( ! (grammarImpl instanceof GrammarConverter)){
var gc = doGetGrammar(id, true);
//if for this ID (= language code) no grammar-converter
// exists yet, create a now one
// (otherwise, re-use the existing one)
if(!gc){
gc = new GrammarConverter();
}
gc.setGrammarFunction(grammarImpl, execMode === 'async');
grammarImpl = gc;
}
var isAlreadyPresent = checkHasGrammar(id);
grammarImplMap[id] = grammarImpl;
if( ! isAlreadyPresent){
//DISABLED: this may produce side effects (now: current grammar must be explicitly set using setCurrentGrammar(lang))
// if(grammarImplList.length === 0){
// currentGrammarId = id;
// }
grammarImplList.push(id);
}
if(stopwords){
grammarImpl.setStopWords(stopwords);
}
doSetEnabled(true);
};
/**
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var doSetStopwords = function(id, stopwordArray){
doGetGrammar(id).setStopWords(stopwordArray);
};
/**
* HELPER retrieve the executable grammar:
* if already loaded, return the grammar instance, otherwise load & compile.
*
* @param {String} id
* the ID (e.g. language code) for the grammar
* @param {Boolean} [doNotResolve] OPTIONAL
* if <code>false</code> AND the request grammar is not loaded yet,
* then the grammar will NOT be loaded (if omitted or <code>true</code>
* missing grammars will automatically be loaded and compiled)
* @param {Function} [callback] OPTIONAL
* if grammar has to be loaded (and compiled), the provided callback
* will be called, after completion with the corresponding GrammarConverter instance:
* <code>callback(newGrammarConverter)</code>.
*
* @return {GrammarExecFunction}
* the exectuable grammar (i.e. execution function), if the grammar is
* already loaded (if grammar has to loaded and compiled, you need to
* wait for the callback-call and then re-invoke doGetGrammar()).
*
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var doGetGrammar = function(id, doNotResolve, callback){//NOTE: this should stay private
if(!id){
if(!currentGrammarId){
throw new Error('Could not retrieve grammar: required grammar ID is missing');
}
else {
id = currentGrammarId;
}
}
//shift arguments, if necessary:
if(!callback && typeof doNotResolve === 'function'){
callback = doNotResolve;
doNotResolve = false;
}
var isDefaultCallback = false;
if(!callback && logger.isInfo()){
//create a "debug-info callback"
isDefaultCallback = true;
callback = function(){
if(logger.isInfo()) logger.info('created executable grammar for "'+id+'" from source '+instance.get_json_grammar_url(id));
};
}
if(!doNotResolve && ! checkHasGrammar(id) ){
//DISABLED: check for executable grammar (that was not loaded yet), before trying to compile json-grammar
// -> this would pull in too many dependencies(?) ...
//
// if(instance.exists_gen_grammar(id)){
//
// require('mmirf/commonUtils').loadCompiledGrammars(res.getGeneratedGrammarsPath(), function(){
//
// if(!isDefaultCallback) callback();
// else if(logger.isInfo()) logger.info('initialized executable grammar for "'+id+'".');
//
// }, require('mmirf/languageManager').getLanguages().filter(function(lang){ return lang !== id}))
//
// } else {
//
// var jsonGrammarUrl = instance.get_json_grammar_url(id);
// createAndAddGrammar(jsonGrammarUrl, id, callback);
// }
var jsonGrammarUrl = instance.get_json_grammar_url(id);
createAndAddGrammar(jsonGrammarUrl, id, callback);
}
else if(callback && !isDefaultCallback){
callback(grammarImplMap[id]);
}
return grammarImplMap[id];
};
/**
* Check if grammar is register
*
* @param {string} id the grammar ID
* @return {Boolean} true, if grammar with ID is registered
*
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var checkHasGrammar = function(id){
return typeof grammarImplMap[id] !== 'undefined';
};
/**
* Remove a registered grammar
*
* @param {string} id the grammar ID to remove
*
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var doRemoveGrammar = function(id){
if( checkHasGrammar(id) ){
//remove from impl.-map:
delete grammarImplMap[id];
//remove from ID-list
for(var i=0, size = grammarImplList.length; i < size; ++i){
if(grammarImplList[i]==id){
grammarImplList.splice(i, 1);
break;
}
}
}
};
//TODO move create/build into GrammarConverter
/**
* @param {String|JSONObject} doRecompile
* IF {String}: the String's contents will be used as a String-representation of the JSON grammar
* IF {Object}: the Object will be used as JSON representation for the grammar
*
* @param {String} [generatedParserLanguageCode] OPTIONAL
* if param doRecompile is used, this String specifies the
* language for the generated grammatic-parser. If omitted, the default "de" (German) will be used.
* NOTE: this must be a valid ISO language code!
*
* @param {Function} [callback] OPTIONAL
* a callback that is invoked after the grammar was created and added to the SemanticInterpreter.
* The callback-function will be invoked with corrsponding GrammarConverter instance, i.e. <code>callback(newGrammarConverter);</code>
* @function
*
* @private
* @memberOf mmir.SemanticInterpreter#
*/
function createAndAddGrammar(doRecompile, generatedParserLanguageCode, callback){
var gc = new GrammarConverter();
//callback that will be used after the JSON file for the grammar was loaded:
function build_grammar(theConverterInstance){//<- argument is the GrammarConverter instance
var genId = doGetGrammarEngine();//one of ['jscc' | 'pegjs' | 'jison'];
var genName = GRAMMAR_MODULE_ID_PREFIX + genId + (_isAsyncCompileMode? GRAMMAR_ASYNC_MODULE_MODIFIER : '') + GRAMMAR_MODULE_ID_POSTFIX;
var compileOptions = {
fileVersion: GRAMMAR_FILE_FORMAT_VERSION,
strict: !_disableStrictMode
};
var onModuleLoaded = function onLoad(gen){
//initialize the generator (initialization may be async -> need callback/Promise)
// (-> if already initialized, the then-callback will be invoked immediately)
gen.init().then(function onInit(){
//actually start compilation of the grammar definition:
// usually this involves 2 steps:
// (1) converting the JSON grammar into a specific ParserParser syntax (e.g. JS/CC syntax)
// (2) compiling this syntax using the corresponding Parser-Generator
// -> the resulting parser-function will then be registered on the SemanticInterpreter instance
// (using its addGrammar() function) along with the stopword definition (using the setStopwords() function)
gen.compileGrammar(theConverterInstance, generatedParserLanguageCode, compileOptions, function onCompiled(convertedInstance){
//add the grammar-parser-text and grammar-definition-text to the newly registered Grammar-instance
// (-> registering is done within the compileGrammar() function!)
var registeredGrammarInstance = doGetGrammar(generatedParserLanguageCode, true);
if(registeredGrammarInstance){
registeredGrammarInstance.setGrammarSource(convertedInstance.getGrammarSource());
registeredGrammarInstance.setGrammarDef(convertedInstance.getGrammarDef());
}
else {
logger.error('A problem occured during generation of grammar for "'+generatedParserLanguageCode+'"');
}
//invoke callback if present:
if(callback){
callback(registeredGrammarInstance);
}
});
});
};//END: onModuleLoaded([jsccGen])
//FIXME webpack emits a warning, if normal require() is used -> TODO find other way than using mmir.require() for getting rid of the warning (i.e. avoid adding dependency for mmirf/core!)
var req = typeof WEBPACK_BUILD !== 'undefined' && WEBPACK_BUILD? require('mmirf/core').require : require;
req([genName], onModuleLoaded, function(_err){
//if async-module could not be loaded, try sync-module
if(_isAsyncCompileMode){
logger.warn('Cannot use asynchronous compilation for '+genId+
': no async module available, using sync compilation instead...'
);
genName = GRAMMAR_MODULE_ID_PREFIX + genId + GRAMMAR_MODULE_ID_POSTFIX;
req([genName], onModuleLoaded);
}
});
}//END function build_grammar
if(typeof doRecompile === 'string'){// arg. is URL for JSON grammar definition
//interpret STRING as URL for the JSON grammar:
gc.loadGrammar(build_grammar, function(err){
var errMsg = err;
if(err){
if(err.stack){
errMsg = err.stack;
} else {
try{
errMsg = JSON.stringify(err);
} catch(e){}
}
}
throw new Error('Could not find JSON grammar file at "'+doRecompile+'": '+errMsg);
}, doRecompile, true
);
} else if(typeof doRecompile === 'object'){// arg. is JSONObject (ie. JSON grammar definition)
//ASSERT if doRecompile === null => throws error!
gc.json_grammar_definition = doRecompile;
build_grammar(gc);
} else {
logger.error('__createAndAddGrammar(): could not build grammar due to missing argumens');
}
}
/**
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var process_asr_semantic = function(phrase, langCode, callback){
if(!doCheckIsEnabled()){
logger.warn('interpret(): currently disabled!');
return null;
}
if(langCode && (typeof langCode === 'function' || typeof langCode === 'object')){
callback = langCode;
langCode = void(0);
}
var options;
if(callback && typeof callback === 'object'){
options = callback;
callback = options.callback;
} else {
options = {};
}
if(typeof options.debug === 'undefined'){
options.debug = logger.isDebug();
}
if(typeof options.trace === 'undefined'){
options.trace = logger.isVerbose();
}
var execGrammar = function(grammarConverter, phrase, langCode, parseOptions, callback){
//pre-process pharse (e.g. mask umlauts, remove stopwords)
var positions = _isCalcProcPos? {} : void(0);//<- for storing modification information during pre-processing
var strPreparedPhrase = grammarConverter.preproc( phrase.toLowerCase(), positions );
if(logger.isDebug()) logger.debug('process_asr_semantic('+langCode+'): removed stopwords, now parsing phrase "'+strPreparedPhrase+'"');//debug
if(callback){
grammarConverter.executeGrammar( strPreparedPhrase, parseOptions, function(result){
//post-process result (e.g. unmask umlauts etc)
result = grammarConverter.postproc(result, positions);
result.preproc = positions;
callback(result);//TODO return copy instead of original instance?
});
} else {
var result = grammarConverter.executeGrammar( strPreparedPhrase, parseOptions );
//post-process result (e.g. unmask umlauts etc)
result = grammarConverter.postproc(result, positions);
result.preproc = positions;
return result;//TODO return copy instead of original instance?
}
};//END OF: var execGrammar = function...
var grammarReadyCallback;
if(callback){
grammarReadyCallback = function(){
var grammarConverter = doGetGrammar(langCode);
if(grammarConverter.isAsyncExec()){
execGrammar(grammarConverter, phrase, langCode, options, callback);
} else {
callback(execGrammar(grammarConverter, phrase, langCode, options));
}
};
}
var grammarConverter = doGetGrammar(langCode, grammarReadyCallback);
if(!grammarConverter && ! grammarReadyCallback){
throw new Error('no grammar available for '+(langCode || currentGrammarId)+' (and no callback provided for asnyc invocation)');
}
if(!grammarReadyCallback){
return execGrammar(grammarConverter, phrase, langCode, options);
}
};
/**
* @private
* @memberOf mmir.SemanticInterpreter#
*/
var doApplyPreproc = function(thePhrase, lang, processingSteps){
if(!doCheckIsEnabled()){
logger.warn('doProcessStopwords(): currently disabled!');
return null;
}
var grammarConverter = doGetGrammar(lang);
if(!grammarConverter){
throw new Error('No grammar for ID '+lang);
}
return grammarConverter.preproc(thePhrase, null, processingSteps);
};
/** @lends mmir.SemanticInterpreter.prototype */
var _tmpInstance = { // public members
/**
* @param {String} phrase
* the phrase that will be parsed
* @param {String} langCode
* the language code (identifier) for the parser/grammar
* @param {Function|ParseOptions} [callback] OPTIONAL
* parsing-options or a callback:
* options.callback: FUNCTION the callback function (see below)
* options.debug: BOOLEAN enabling debug output
* (by default the logger's log-level <= 'debug' is used)
* options.trace: BOOLEAN enabling verbose/tracing output;
* may not be supported by all grammar engines
* (by default the logger's log-level <= 'verbose' is used)
* NOTE: some grammar engines may support additional parsing options
* If a callback function: receives the return value
* (instead of receiving the result as return value from
* this function directly).
* The signature for the callback is: <code>callback(result: Object)</code>
* (i.e. the result that would be returned by this function itself is
* passed as argument into the callback function; see also documentation
* for <em>returns</em>).
* NOTE: in case, the grammar for the requested <code>langCode</code>
* is not compiled yet (i.e. not present as executable JavaScript),
* the corresponding JSON definition of the grammar needs to be
* compiled first, before processing the ASR's semantics is possible.
* In this case, a <code>callback</code> function <strong>MUST</strong>
* be supplied in order to receive a result (since compilation of the
* grammar may be <em>asynchronous</em>).
*
* @returns {Object}
* the parsing result (as processed by the parser / grammar;
* usually a JSON-like object).
* WARNING: if a <code>callback</code> function was provided, then
* there is no return object.
*
* @public
* @memberOf mmir.SemanticInterpreter.prototype
*/
interpret: function(phrase, langCode, callback){
return process_asr_semantic(phrase, langCode, callback);
},
/**
* Removes stopwords using the stopword-list from the parser/grammar
* for <code>lang</code>.
*
*
* @deprecated use {@link #applyPreProcessing} instead
*
* @param {String} thePhrase
* the Phrase for which stopwords should be removed
* @param {String} lang
* the language code (identifier) for the parser/grammar
*
* @public
* @see #applyPreProcessing
*/
removeStopwords: function(thePhrase, lang, processingSteps){
logger.warn('using deprecated function removeStopwords(): should use applyPreProcessing() instead.');
return this.applyPreProcessing(thePhrase, lang, processingSteps);
},
/**
* Applies pre-processing for the corresponding parser/grammar
* of <code>lang</code> (e.g. removes stopwords using the stopword-list etc).
*
* NOTE: <code>{@link #interpret}</code> automatically applies pre-processing
* i.e. there is no need to manually do this when using <code>{@link #interpret}</code>).
*
* IMPORTANT: this helper function actually invokes {@link mmir.grammar.GrammarConverter#preproc}
* which by default removes stopwords; if the corresponding GrammarConverter instance
* has been set with a non-default pre-processing chain, results may be differ
* (i.e. may not remove stopwords).
*
* @param {String} thePhrase
* the Phrase for which stopwords should be removed
* @param {String} [lang]
* the language code (identifier) for the parser/grammar
* (if omitted the currently set grammar is used)
* @param {Array<ProcessingStep>} [processingSteps] OPTIONAL
* if given, use <code>processingSteps</code> instead of the
* GrammarConverter's configured pre-processing chain.
* NOTE positional argument (i.e. must specify <code>pos</code> too)
*
* @public
* @see mmir.grammar.GrammarConverter#preproc
*/
applyPreProcessing: function(thePhrase, lang, processingSteps){
return doApplyPreproc(thePhrase, lang, processingSteps);
},
/** NOTE: the grammar must be compiled/registered first
* @param {String} id
* the ID (identifier) / language code for grammar
* @public
* @see mmir.grammar.GrammarConverter#getGrammarDef
*/
getGrammarDefinitionText: function(id){
return doGetGrammar(id).getGrammarDef();
},
/** NOTE: the grammar must be compiled/registered first
* @param {String} id
* the ID (identifier) / language code for grammar
* @public
* @see mmir.grammar.GrammarConverter#getGrammarSource
*/
getGrammarParserText: function(id){
return doGetGrammar(id).getGrammarSource();
},
/**
* Get the grammar converter instance (of registered grammar)
* @public
* @param {String} [id]
* the ID (identifier) / language code for grammar
* if omitted: the currently active grammar
* @returns {mmir.grammar.GrammarConverter} the grammar converter
*
* @see #addGrammar
* @see #setCurrentGrammar
*/
getGrammarConverter: function(id){
return doGetGrammar(id, true);//<- if no grammar is loaded for this ID, do NOT try to load it!
},
/**
* @copydoc #createAndAddGrammar
* @public
* @param {String|JSONObject} rawGrammarSrc
* @param {String} id
* @param {Function} [callback]
* @returns {SemanticInterpreter.prototype}
*/
createGrammar: function(rawGrammarSrc, id, callback){
if(!id){
throw new Error('missing ID for generated grammar');//TODO
}
createAndAddGrammar(rawGrammarSrc, id, callback);
return this;
},
/**
* @copydoc #doAddGrammar
* @public
* @function
*/
addGrammar: doAddGrammar,
/**
* @copydoc #doAddGrammar
* @public
* @function
*/
setStopwords: doSetStopwords,
// getGrammar: doGetGrammar, <- set to private
/**
* @copydoc #checkHasGrammar
* @public
* @function
*/
hasGrammar: checkHasGrammar,
/**
* @copydoc #doRemoveGrammar
* @public
* @function
*/
removeGrammar: doRemoveGrammar,
/**
* Shortcut for {@link mmir.GrammarConverter#addProc}:
* add pre-/post-processing step for running before/after {@link #interpret}
*
* @param {String} langCode the language code, for which to add the (pre- and/or post-) processing step
* @param {ProcessingStep} proc the processing step:
* <pre>
* {
* //the name of the processing step
* name: string,
* //OPTIONAL pre-processing function: pre(input: string | Positions, isCalcPos: boolean)
* pre: Function,
* //OPTIONAL post-processing function: post(result: any, pos: Positions)
* post: Function
* }
* </pre>
* @param {Boolean|Number} [isPrepend] OPTIONAL
* if omitted (or FALSY): appended <code>proc</code> to processing steps
* if number: insert <code>proc</code> at this index into the processing steps-list
* if TRUE: prepend <code>proc</code> to processing steps
* @param {Function} [callback] OPTIONAL
* callback, in case of asnychronous initalization, i.e. if
* grammar is not loaded/compiled yet, and grammar.json is available.
* If omitted, an error is thrown, if the grammar has not been loaded/compiled yet.
*
* @see mmir.GrammarConverter#addProc
* @example
* //poitionUtils:
* var posUtil = mmir.require('mmirf/positionUtils');
* //stemming function
* var stemFunc = ...;
* //add stemming function for pre-processing for "de" as first step
* mmir.semantic.addProcessing('de', {
* name: 'stem',
* pre: posUtil.createWordPosPreProc(stem, this)
* }, true);
*/
addProcessing: function(langCode, processingStep, indexOrIsPrepend, callback){
var cb = callback;
var asyncCb = function(gc){
gc.addProc(processingStep, indexOrIsPrepend);
cb && cb(gc);
};
var gc = doGetGrammar(langCode, !cb, cb? asyncCb : void(0));//<- if no grammar is loaded for this ID, only try to load it, if a callback is provided
if(!cb){
asyncCb(gc);
}
},
/**
* Sets the current grammar.
*
* If in invocations of {@link #interpret} the grammar ID (e.g. language code) is missing,
* then this grammar that is set here is used.
*
* The id must reference either a grammar that was compiled (i.e. generated JavaScript file)
* for this id, or there must exists JSON-grammar file for which the language-dir matches the id parameter,
* e.g. <code>config/languages/[id]/grammar.json</code>.
*
* @param {String} id the ID for the grammar, e.g. an ISO language code
*
* @function
* @public
*/
setCurrentGrammar: function(id){
currentGrammarId = id;
//set semantic-interpreter to enabled
// (this ensures, that JSON-grammars are automatically loaded,
// if no corresponding compiled JS-grammar is available yet)
doSetEnabled(true);
},
/**
* @copydoc #currentGrammarId
* @public
*/
getCurrentGrammar: function(){
return currentGrammarId;
},
/**
* @see #isEnabled
* @public
*/
setEnabled: function(isEnabled){
doSetEnabled(isEnabled);
},
/**
* @copydoc #_isEnabled
* @public
*/
isEnabled: function(){
return doCheckIsEnabled();
},
/**
* Get the ID of the current grammar engine / compiler.
*
* @default "jcss"
* @returns {String}
* the ID of the current grammar engine
* @public
*/
getGrammarEngine: function(){
return doGetGrammarEngine();
},
/**
* Set the grammar engine, i.e. the
* compiler engine for the JSON grammar
*
* NOTE: implementations of the grammar engines are located at env/grammar/
* The file-name for an implementation should follow the convention: ID+"Generator.js"
* and should be registered with requirejs with the module-ID: ID+"Gen"
*
* @param {String} egnineId
* the ID for the engine.
* Possible values: "jscc", "jison", "pegjs"
*
* @param {Boolean} [asyncCompileMode] OPITIONAL
* sets the compile mode (sychronous or asynchronous) when generating new parsers
* with the grammar-engine.
* DEFAULT: VOID (i.e. leave current set compile-mode setting unchanged)
*
* @param {Boolean} [disableStrictMode] OPTIONAL
* disable JavaScript strict mode when generating grammar code
* <br>NOTE: this argument is positional, i.e. <code>asyncCompileMode</code> must also be given when using this argument
*
* @public
*/
setGrammarEngine: function(engineId, asyncCompileMode, disableStrictMode){
doSetGrammarEngine(engineId, asyncCompileMode, disableStrictMode);
},
/**
* Set compile-mode (sychronous or asynchronous) for the grammar engine, i.e. if the
* compiler engine for the JSON grammar should run synchronously or asynchronously.
*
* NOTE: if there is no asynchronous implementation available for the grammar engine,
* the sync-impl. is used by default.
*
* NOTE: asynchronous compile mode requires WebWorkers
*
* @param {Boolean} asyncCompileMode
* sets the compile mode (sychronous or asynchronous) when generating new parsers
* with the grammar-engine.
*
* @param {Boolean} [disableStrictMode] OPTIONAL
* disable JavaScript strict mode when generating grammar code
*
* @public
* @default false (i.e. synchronous compile mode)
* @require WebWorker (if async mode)
*/
setEngineCompileMode: function(asyncCompileMode, disableStrictMode){
_isAsyncCompileMode = !!asyncCompileMode;
if(typeof disableStrictMode === 'boolean'){
_disableStrictMode = disableStrictMode;
}
},
/**
* Get compile-mode (sychronous or asynchronous) for the grammar engine, i.e. if the
* compiler engine for the JSON grammar should run synchronously or asynchronously.
*
* @return {Boolean} the compile mode (sychronous or asynchronous) when generating new parsers
* with the grammar-engine.
* @public
*/
getEngineCompileMode: function(){
return _isAsyncCompileMode;
},
/**
* Get JavaScript strict mode compile-setting for the grammar engine, i.e. if the
* compiler engine should generate code with strict-mode setting.
*
* @return {Boolean} the strict mode setting
* @public
*/
getEngineCompileStrictMode: function(){
return !_disableStrictMode;
},
/**
* @copydoc #GRAMMAR_FILE_FORMAT_VERSION
* @returns {Number} the current version number that this SemanticInterpreter
* instance supports, for the file format of compiled grammars.
*/
getFileVersion: function(){
return GRAMMAR_FILE_FORMAT_VERSION;
},
/**
* Enable / disable calculation of modified positions during pre-processing
*
* @param {Boolean} isEnabled if calculation of modified positions during pre-processing should be enabled
*
* @public
* @see #isPreProcessPositionsEnabled
* @see #_isCalcProcPos
*/
setPreProcessPositionsEnabled: function(isEnabled){
_isCalcProcPos = isEnabled;
},
/**
* If true, pre-processing input-phrase (before running interpretion) will
* include meta-data for changed positions (due to pre-processing) in input-string
*
* E.g. can be used to map semantic-results (matched tokens/utterances where
* e.g. stopwords would be removed) on the raw input-string.
*
* The meta-information will be included in field <code>preproc</code> of the
* interpretation result.
*
* @return {Boolean} if calculation of modified positions during pre-processing is enabled
*
* @public
* @see #setPreProcessPositionsEnabled
* @see #_isCalcProcPos
*/
isPreProcessPositionsEnabled: function(){
return _isCalcProcPos;
},
//FIXME rename/move functions
get_json_grammar_url: function(id){
return res.getGrammarFileUrl(id);
}//,
// exists_gen_grammar: function(id){
// var lang = require('mmirf/languageManager');
// return lang.existsGrammar(id, 'bin');
// }
};//END: var _tmpInstance = {...
return _tmpInstance;
}
instance = new constructor();
return instance;
});//END: define(..., function(){