babelute
Version:
Internal Domain Specific (Multi)Modeling javascript framework
1,130 lines (900 loc) • 34 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.Babelute = factory());
}(this, (function () { 'use strict';
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var inherits = function (subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
};
var possibleConstructorReturn = function (self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
};
var toConsumableArray = function (arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
return arr2;
} else {
return Array.from(arr);
}
};
// removed in production
/**
* Lexem class : a lexem is just an object containing 3 properties { lexicon:String, name:String, args:Arguments|Array }
* You should never construct them directly (but if you do babelute's plugins). And it should never be extended.
* @protected
*/
var Lexem =
/**
* construct a new lexem instance
* @param {String} lexicon the lexicon's name of the lexem
* @param {String} name the lexem's name
* @param {Array|arguments} args the lexem's arguments (an array or the "callee arguments" object)
*/
function Lexem(lexicon, name, args) {
classCallCheck(this, Lexem);
/**
* the lexicon name from where the lexem comes
* @type {String}
*/
this.lexicon = lexicon;
/**
* the lexem's name
* @type {String}
*/
this.name = name;
/**
* The lexem's arguments array (or arguments object)
* @type {Array|arguments}
*/
this.args = args;
}; /*
* @Author: Gilles Coomans
*/
function extend(BaseClass) {
var B = function B() {
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
BaseClass.apply(this, args);
};
B.prototype = Object.create(BaseClass.prototype);
B.prototype.constructor = B;
for (var _len = arguments.length, apis = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
apis[_key - 1] = arguments[_key];
}
apis.forEach(function (api) {
for (var i in api) {
B.prototype[i] = api[i];
}
});
return B;
}
// removed in production
/**
* Babelute subclass(es) instances : for holding array of lexems (i.e. a sentence) written through the DSL's API.
*
* Will be the base class for all DSLs handlers.
*
* Babelute API and lexems Naming Conventions :
*
* - any "meta-language" method (aka any method that handle the sentence it self - appending new lexem, changing current lexicon, sentences translations, ...)
* must start with and underscore : e.g. _append, _lexicon, _if, _each, _eachLexem, _translate...
* - any "pragmatics output related" method should start with a '$' and should be named with followed format : e.g. .$myLexiconToMyOutputType(...)
* - any DSL lexems (so any other "api"'s method) should start with a simple alphabetic char : e.g. .myLexem(), .description(), .title(), ...
*
* @public
*/
var Babelute = function () {
/**
* construct a babelute instance
* @param {?Array} lexems array of lexems for init. (only for internal use)
*/
function Babelute() {
var lexems = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
classCallCheck(this, Babelute);
/**
* the array where lexems are stored
* @type {Array}
*/
this._lexems = lexems || [];
/**
* useful marker for fast instanceof replacement (frame/multiple-js-runtime friendly)
* @type {Boolean}
*/
this.__babelute__ = true;
}
/**
* The absolute Babelute atom method : add a lexem to babelute's array
* @public
* @param {String} lexiconName the current lexicon name
* @param {String} name the lexem's name
* @param {Array|arguments} args the lexem's arguments (either an array or maybe directly the arguments object from when lexem is called)
* @return {Babelute} the current Babelute instance
*/
createClass(Babelute, [{
key: '_append',
value: function _append(lexiconName, name, args) {
this._lexems.push(new Lexem(lexiconName, name, args));
return this;
}
/**
* conditional sentences concatenation.
*
* Apply modification at sentence writing time (aka the babelute does not contains the _if lexems. _if has immediatly been applied).
*
* @public
* @param {*} condition any value that will be casted to Boolean (!!)
* @param {Babelute} babelute which sentence to insert if !!condition === true
* @param {?Babelute} elseBabelute which sentence to insert if !!condition === false
* @return {Babelute} the current Babelute instance
*/
}, {
key: '_if',
value: function _if(condition, babelute) {
var elseBabelute = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
if (condition) this._lexems = this._lexems.concat(babelute._lexems);else if (elseBabelute) this._lexems = this._lexems.concat(elseBabelute._lexems);
return this;
}
/**
* For each item from array : execute function and concatenate returned babelute sentence to current one.
* Provided function must return a babelute.
*
* Apply modification at sentence writing time (aka the babelute does not contains the _each lexems. _each has immediatly been applied).
*
* @public
* @param {Array} array the array to iterate on
* @param {Function} func the function to handle each item. it must return a babelute.
* @return {Babelute} the current Babelute instance
*/
}, {
key: '_each',
value: function _each(array, func) {
var _this = this;
if (array) array.forEach(function (item, index) {
var b = func(item, index);
_this._lexems.push.apply(_this._lexems, b._lexems);
});
return this;
}
/**
* Use a babelute (another sentence) at this point in the current sentence
* @public
* @param {string|Babelute} babelute Either a string formatted as 'mylexicon:myMethod' (which gives the lexem's method to call), or a Babelute instance (which will be inserted in current sentence)
* @param {?...args} args the optional arguments to use when calling lexem (only if first argument is a string)
* @return {Babelute} the current Babelute instance
* @throws {Error} If lexicon not found (when first arg is string)
* @throws {Error} If method not found in lexicon (when first arg is string)
*/
/* istanbul ignore next */
}, {
key: '_use',
value: function _use(babelute) {} // eslint-disable-line no-unused-vars
// will be implemented in lexicon
/**
* Change current lexicon for next lexems
* @public
* @param {string} lexiconName the lexicon to use
* @return {Babelute} a new Babelute from lexicon (i.e. with lexicon's API)
* @throws {Error} If lexicon not found with lexiconName
*/
/* istanbul ignore next */
}, {
key: '_lexicon',
value: function _lexicon(lexiconName) {} // eslint-disable-line no-unused-vars
// will be implemented in lexicon
/**
* transform a sentence through a function. This function must return a new sentence.
* @param {Function} handler a function that receive the current sentence as argument and that return a new sentence.
* @return {Babelute} the new sentence
*/
}, {
key: '_transform',
value: function _transform(handler) {
return handler(this);
}
/**
* translate each lexems
* @param {[type]} handler [description]
* @return {[type]} [description]
*/
}, {
key: '_translateLexems',
value: function _translateLexems(handler) {
return this._transform(function (sentence) {
var b = new Babelute();
sentence._lexems.forEach(function (lexem) {
return b._use(handler(lexem));
});
return b;
});
}
/**
* translate current sentence's lexems through a Lexicon or a map of Lexicon.
*
* @param {Lexicon|Object} lexicon the Lexicon (or a map of Lexicon) to get translation from
* @param {Boolean} firstLevel true if should produce FirstLevel translation (i.e. targetLexicon.FirstLevel). False otherwise.
* @return {Babelute} a new Babelute instance with translated lexems.
*/
/* istanbul ignore next */
}, {
key: '_translateLexemsThrough',
value: function _translateLexemsThrough(lexicon) {
var firstLevel = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
} // eslint-disable-line no-unused-vars
// will be implemented in Lexicon
// static extends(BaseClass, ...apis) {
// assert(BaseClass === Babelute || (BaseClass.prototype instanceof Babelute), 'Babelute.extends accepts only a Babelute Class (or subclass) as first argument');
// const B = function(...args) {
// BaseClass.apply(this, args);
// };
// B.prototype = Object.create(BaseClass.prototype);
// B.prototype.constructor = B;
// apis.forEach((api) => {
// for (var i in api) B.prototype[i] = api[i];
// });
// // Object.assign seems to bug when used on prototype (not investigate enough : so use plain old for-in syntax)
// // investigation gives : babel make prototype not enumerable
// return B;
// }
}]);
return Babelute;
}();
Babelute.extends = extend;
// removed in production
/**
* deserialize json to babelute
* @param {String} json the json string
* @return {Babelute} the deserialized babelute
* @throws {Error} If json is badly formated
*/
function fromJSON(json) {
return JSON.parse(json, function (k, v) {
if (v && v.__babelute__) return new Babelute(v._lexems.map(function (lexem) {
return new Lexem(lexem.lexicon, lexem.name, lexem.args);
}));
return v;
});
}
// removed in production
/**
* A FirstLevel is a Babelute that has exactly same api than its corresponding Babelute (from a DSL) but where every compounds methods has been replaced by its "atomic" equivalent.
* (Same concept than 'first-level of understanding', as if we where stupid by always understanding only first literal sens of words.)
*
* It provides sentences and lexems without any interpretation, and that could be really useful : e.g.
* - to see sentence as "editable document" and/or for allowing meta-writing of sentences
* - to obtain the full AST of babelute sentences
*
* @access protected
*/
var FirstLevel = function (_Babelute) {
inherits(FirstLevel, _Babelute);
/**
* construct a firstlevel babelute instance
* @param {?Array} lexems array of lexems for init. (only for internal use)
*/
function FirstLevel(lexems) {
classCallCheck(this, FirstLevel);
var _this = possibleConstructorReturn(this, (FirstLevel.__proto__ || Object.getPrototypeOf(FirstLevel)).call(this, lexems));
_this.__first_level_babelute__ = true;
return _this;
}
/**
* return a FirstLevelMethod aka a method that only append an atom (lexicon, name, args)
* @param {String} lexiconName the lexicon name of the appended atom
* @param {String} lexemName the lexem name of the appended atom
* @return {Function} a function that append the atom
*/
createClass(FirstLevel, null, [{
key: 'getFirstLevelMethod',
value: function getFirstLevelMethod(lexiconName, lexemName) {
return function () {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
this._lexems.push(new Lexem(lexiconName, lexemName, args));
return this;
};
}
}]);
return FirstLevel;
}(Babelute);
// removed in production
/**
* Initializer Class
* @protected
*/
var Initializer = function Initializer() {
classCallCheck(this, Initializer);
};
/**
* extends Initializer
* @param {Class} BaseInitializer the Initializer to extends
* @return {Class} the extended Initalizer class
*/
Initializer.extends = extend;
/**
* create a Initializer (based on a Babelute subclass) and instanciate it
* @param {Babelute} BabeluteClass a Babelute subclass from where create initializer
* @param {?Initializer} BaseInitializer a parent initializer to be extended (optional)
* @return {Initializer} the Initializer instance
* @protected
*/
function createInitializer(BabeluteClass) {
var BaseInitializer = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
var Init = BabeluteClass.Initializer = BaseInitializer ? Initializer.extends(BaseInitializer) : Initializer;
BabeluteClass.initializer = new Init();
BabeluteClass.initializer._empty = function () {
return new BabeluteClass();
};
BabeluteClass.initializer.Class = BabeluteClass;
Object.keys(BabeluteClass).forEach(function (i) {
return addToInitializer(Init, i);
});
return BabeluteClass.initializer;
}
/**
* add method to initializer
* @protected
* @param {Initializer} Initializer Initializer class where add methods in proto
* @param {string} methodName the name of method to add
*/
function addToInitializer(Initializer, methodName) {
Initializer.prototype[methodName] = function () {
var _ref;
return (_ref = new this.Class())[methodName].apply(_ref, arguments);
};
}
// add base Babelute's api
['_use', '_each', '_if', '_append', '_lexicon'].forEach(function (methodName) {
addToInitializer(Initializer, methodName);
});
// removed in production
/**
* Lexicons dico : where to store public (registered) lexicon
* @type {Object}
* @private
*/
var lexicons = {};
/**
* Lexicon class : helpers to store and manage DSL's API.
*
* A __Lexicon__ is just an object aimed to handle, store and construct easily a DSL (its lexicon - i.e. the bunch of words that compose it)
* and its related Atomic/FirstLevel/SecondLevel Babelute subclasses, and their initializers.
*
* One DSL = One lexicon.
*
* A lexicon could extend another lexicon to manage dialects.
*
* You should never use frontaly the constructor (aka never use new Lexicon in your app). Use createLexicon or createDialect in place.
*
* @public
*/
var Lexicon = function () {
/**
* @param {string} name the lexicon name
* @param {?Lexicon} parent an optional parent lexicon to be extended here
*/
function Lexicon(name, parent) {
var _this = this;
classCallCheck(this, Lexicon); // all assertions will be removed in production
/**
* the parent lexicon (if any)
* @type {Lexicon}
* @public
*/
this.parent = parent;
parent = parent || {};
/**
* the lexicon's name
* @type {String}
*/
this.name = name;
// the three APIs :
/**
* interpretable sentences API (finally always made from syntactic/semantic atoms (aka last level))
* @type {Babelute}
* @protected
*/
this.Atomic = initClass(parent.Atomic || Babelute);
/**
* "document" sentences API (first level : aka all methods has been replaced by fake atomic methods)
* @type {Babelute}
* @protected
*/
this.FirstLevel = initClass(parent.FirstLevel || FirstLevel);
/**
* AST-provider API aka the whole tree between first level and last level. Never use it directly : its used under the hood by {@link developOneLevel} method.
* @type {Babelute}
* @protected
*/
this.SecondLevel = Babelute.extends(parent.SecondLevel || Babelute);
/**
* the secondLevel instance
* @type {Babelute}
* @protected
*/
this.secondLevel = new this.SecondLevel();
if (parent.Atomic) Object.keys(parent.Atomic.initializer).forEach(function (key) {
addToInitializer(_this.Atomic.Initializer, key);
addToInitializer(_this.FirstLevel.Initializer, key);
});
}
/**
* add atomic lexem (atoms) to lexicon
* @param {string[]} atomsArray array of atoms name (as string)
* @return {Lexicon} the lexicon itself
*/
createClass(Lexicon, [{
key: 'addAtoms',
value: function addAtoms(atomsArray) {
var _this2 = this;
atomsArray.forEach(function (name) {
return addAtom(_this2, name);
});
return this;
}
/**
* add compounds lexems to lexicon
* @param {Function} producer a function that take a babelute initializer as argument and that return an object containing methods (lexems) to add to lexicon
* @return {Lexicon} the lexicon itself
*/
}, {
key: 'addCompounds',
value: function addCompounds(producer) {
var _this3 = this;
// Atomic API is produced with Atomic initializer
var atomicMethods = producer(this.Atomic.initializer, false);
for (var i in atomicMethods) {
this.Atomic.prototype[i] = atomicMethods[i];
} // SecondLevel API is simply produced with the related FirstLevel initializer.
// (so same producer method, same api, but different handler for inner composition)
// is the only thing to do to gain capability to handle full AST. (see docs)
var secondLevelCompounds = producer(this.FirstLevel.initializer, true);
for (var j in secondLevelCompounds) {
this.SecondLevel.prototype[j] = secondLevelCompounds[j];
}Object.keys(atomicMethods).forEach(function (key) {
_this3.FirstLevel.prototype[key] = FirstLevel.getFirstLevelMethod(_this3.name, key);
addToInitializer(_this3.Atomic.Initializer, key);
addToInitializer(_this3.FirstLevel.Initializer, key);
});
return this;
}
/**
* add aliases lexems to lexicon (aliases are like shortcuts : they are added as this to Atomic, FirstLevel and SecondLevel API)
* @param {Object} methods an object containing methods (lexems) to add to lexicon
* @return {Lexicon} the lexicon itself
*/
}, {
key: 'addAliases',
value: function addAliases(producer) {
var _this4 = this;
var producerType = typeof producer === 'undefined' ? 'undefined' : _typeof(producer);
var methods = producerType === 'function' ? producer() : producer;
Object.keys(methods).forEach(function (key) {
_this4.Atomic.prototype[key] = _this4.FirstLevel.prototype[key] = _this4.SecondLevel.prototype[key] = methods[key];
addToInitializer(_this4.Atomic.Initializer, key);
addToInitializer(_this4.FirstLevel.Initializer, key);
});
return this;
}
/**
* @protected
*/
}, {
key: 'use',
value: function use(babelute, name, args, firstLevel) {
var instance = firstLevel ? this.FirstLevel.instance : this.Atomic.instance;
if (!instance[name]) throw new Error('Babelute (' + this.name + ') : method not found : ' + name);
instance[name].apply(babelute, args);
}
/**
* return lexicon's initializer instance. (atomic or firstlevel depending on argument)
* @public
* @param {Boolean} firstLevel true if you want firstLevel initializer, false overwise.
* @return {Initializer} the needed initializer instance
*/
}, {
key: 'initializer',
value: function initializer(firstLevel) {
return firstLevel ? this.FirstLevel.initializer : this.Atomic.initializer;
}
/**
* Create a dialect from this lexicon. a dialect is also a Lexicon.
* @param {String} name the new lexicon name
* @return {Lexicon} the new Lexicon that inherit from this one
*/
}, {
key: 'createDialect',
value: function createDialect(name) {
return new Lexicon(name, this);
}
}]);
return Lexicon;
}();
/**
* Add syntactical atom lexem to lexicon (actually to inner classes that reflect API). A syntactical Atom method is a function that only add one lexem.
* @private
*/
function addAtom(lexicon, name) {
lexicon.Atomic.prototype[name] = lexicon.FirstLevel.prototype[name] = lexicon.SecondLevel.prototype[name] = FirstLevel.getFirstLevelMethod(lexicon.name, name);
addToInitializer(lexicon.Atomic.Initializer, name);
addToInitializer(lexicon.FirstLevel.Initializer, name);
}
/**
* babelute lexicon's Classes initialisation
* @private
*/
function initClass(BaseClass) {
var Class = Babelute.extends(BaseClass);
createInitializer(Class, BaseClass.Initializer);
Class.instance = new Class();
return Class;
}
/**
* Way to create lexicon instances
* @public
* @param {string} name the name of the lexicon
* @param {Lexicon} parent a lexicon instance as parent for this one (optional)
* @return {Lexicon} a lexicon instance
*/
function createLexicon(name) {
var parent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
return new Lexicon(name, parent);
}
/**
* getLexicon registred lexicon by name
*
* @param {string} lexiconName the lexicon's name
* @return {Lexicon} the lexicon
* @throws {Error} If lexicon not found with lexiconName
*/
function getLexicon(lexiconName) {
var lexicon = lexicons[lexiconName];
if (!lexicon) throw new Error('lexicon not found : ' + lexiconName);
return lexicon;
}
/**
* registerLexicon lexicon by name
* @param {Lexicon} lexicon the lexicon instance to registerLexicon
* @param {?string} name lexicon name (optional : if not provided : use the one from lexicon itself)
*/
function registerLexicon(lexicon) {
var name = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
lexicons[name || lexicon.name] = lexicon;
}
/*
* _lexicon handeling
*/
// implementation of already declared method in Babelute's proto
Babelute.prototype._lexicon = function (lexiconName) {
return new (getLexicon(lexiconName).Atomic)(this._lexems);
};
FirstLevel.prototype._lexicon = function (lexiconName) {
return new (getLexicon(lexiconName).FirstLevel)(this._lexems);
};
/*
* translation through lexicon (already delcared in Babelute proto)
* @TODO: translation and each and if
* each :
* .each(collec, handler)
* translated to
* .each(collec, wrap(handler, translationInfos))
*
* ==> should translate automatically output from handler
* if ==> same things : wrap handlers
* ==> while translating : when lexem.name === "each" (or "if") (should always be present in target lexicon)
* ==> apply wrapping
*/
Babelute.prototype._translateLexemsThrough = function (lexicon) {
var firstLevel = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var map = lexicon instanceof Lexicon ? null : lexicon;
return this._translateLexems(function (lexem) {
if (map) lexicon = map[lexem.lexicon];
if (!lexicon) return null;
var args = translateArgs(lexem.args, lexicon, firstLevel);
var b = new (firstLevel ? lexicon.FirstLevel : lexicon.Atomic)();
return b[lexem.name] && b[lexem.name].apply(b, toConsumableArray(args));
});
};
function translateArgs(args, lexicon, firstLevel) {
var result = [];
for (var i = 0, len = args.length; i < len; ++i) {
if (args[i] && args[i].__babelute__) result.push(args[i]._translateLexemsThrough(lexicon, firstLevel));else result.push(args[i]);
}return result;
}
/**
* _use handeling
*/
// implementation of already declared method in Babelute's proto
Babelute.prototype._use = function (babelute /* could be a string in "lexiconName:methodName" format */) {
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
return babelute ? use(this, babelute, args, false) : this;
};
// implementation of already declared method in Babelute's proto
FirstLevel.prototype._use = function (babelute /* could be a string in "lexiconName:methodName" format */ /*, ...args */) {
return babelute ? use(this, babelute, [].slice.call(arguments, 1), true) : this;
};
function use(self, babelute, args, firstLevel) {
if (typeof babelute === 'string') {
var splitted = babelute.split(':');
getLexicon(splitted[0]).use(self, splitted[1], args, firstLevel);
} else self._lexems = self._lexems.concat(babelute._lexems);
return self;
}
/**
* return a new babelute from needed lexicon
* @param {string} lexiconName the lexicon from where to take api
* @param {Boolean} asFirstLevel True if it needs to return a FirstLevel instance. False or ommitted : returns an Atomic instance.
* @return {[type]} the babelute instance (either an Atomic or a FirstLevel)
* @throws {Error} If lexicon not found with lexiconName
*/
function init(lexiconName, asFirstLevel) {
if (lexiconName) return new (getLexicon(lexiconName)[asFirstLevel ? 'FirstLevel' : 'Atomic'])();else if (asFirstLevel) return new FirstLevel();
return new Babelute();
}
/**
* develop a FirstLevel compounds-words-lexem through SecondLevel API. It returns the FirstLevel sentence corresponding to lexem's semantic developement.
* @param {Lexem} lexem the lexem to develop
* @param {?Lexicon} lexicon the optional lexicon to use
* @return {FirstLevel} the developed sentence
* @throws {Error} If lexicon not found with lexem.lexicon
* @throws {Error} If method not found in lexicon
*/
function developOneLevel(lexem) {
var lexicon = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
lexicon = lexicon || getLexicon(lexem.lexicon);
return lexicon.secondLevel[lexem.name].apply(new lexicon.FirstLevel(), lexem.args);
}
/**
* develop a FirstLevel lexem through Atomic API. Return the atomic representation of the lexem (in its own language).
* @param {Lexem} lexem the lexem to develop
* @param {?Lexicon} lexicon the optional lexicon to use
* @return {Babelute} the developed sentence
* @throws {Error} If lexicon not found with lexem.lexicon
* @throws {Error} If method not found in lexicon
*/
function developToAtoms(lexem) {
var lexicon = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
lexicon = lexicon || getLexicon(lexem.lexicon);
return lexicon.Atomic.instance[lexem.name].apply(new lexicon.Atomic(), lexem.args);
}
/**
* Provide Babelute Subclass "initializer" object (the one with all the flattened shortcut api for starting sentences easily)
* @param {string} lexiconName The lexiconName where catch the Babelute Class from where getLexicon or create the initializer object.
* @param {boolean} asFirstLevel true if should return a first-level instance. false to return an atomic instance.
* @return {Object} An initializer object with shortcuted API from lexicon's Atomic prototype
* @throws {Error} If lexicon not found with lexiconName
*/
function initializer(lexiconName, asFirstLevel) {
if (!asFirstLevel) return getLexicon(lexiconName).Atomic.initializer;
return getLexicon(lexiconName).FirstLevel.initializer;
}
// removed in production
/**
* Base class to provide homogeneous Pragmatics interface. You should never instanciate a Pragmatics directly with new. use {@link createPragmatics}.
*/
var Pragmatics = function () {
/**
* @param {Object} targets initial targets object
* @param {Object} pragmas pragmatics methods to add
*/
function Pragmatics(targets, pragmas) {
classCallCheck(this, Pragmatics);
/**
* targets holder object
* @type {Object}
* @public
*/
this._targets = targets;
if (pragmas) this.addPragmas(pragmas);
}
/**
* add methods to pragmatics instance
* @param {Object} pragmas an object containing methods to add
*/
createClass(Pragmatics, [{
key: 'addPragmas',
value: function addPragmas(pragmas) {
for (var i in pragmas) {
/**
* @ignore
*/
this[i] = pragmas[i];
}
}
/* istanbul ignore next */
/**
* the method used to output a babelute through this pragmatics instance
* @abstract
*/
}, {
key: '$output',
value: function $output() /* ... */{
// to be overridden
throw new Error('pragmatics.$output should be implemented in subclasses');
}
}]);
return Pragmatics;
}();
/**
* return a new Pragmatics instance. Do not forget to implement $output before usage.
* @param {Object} targets initial targets object
* @param {Object} pragmas pragmatics methods to add
* @return {Pragmatics} the Pragmatics instance
*/
/**
* Pragmatics Class : minimal abstract class for homogeneous pragmatics.
*
* This is the minimal contract that a pragmatics should satisfy.
*
* @author Gilles Coomans
* @licence MIT
* @copyright 2016-2017 Gilles Coomans
*/
function createPragmatics() {
var targets = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var pragmas = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return new Pragmatics(targets, pragmas);
}
// removed in production
/**
* FacadePragmatics : a facade oriented Pragmatics subclass. You should never instanciate a FacadePragmatics directly with new. use {@link createFacadePragmatics}.
* @example
* // Remarque : any lexem's method will be of the following format :
* function(subject, args, ?percolator){
* // return nothing
* }
*/
var FacadePragmatics = function (_Pragmatics) {
inherits(FacadePragmatics, _Pragmatics);
/**
* @param {Object} targets initial targets object
* @param {?Object} pragmas pragmatics methods to add
*/
function FacadePragmatics(targets, pragmas) {
classCallCheck(this, FacadePragmatics);
return possibleConstructorReturn(this, (FacadePragmatics.__proto__ || Object.getPrototypeOf(FacadePragmatics)).call(this, targets, pragmas));
}
/**
* "each" facade implementation
* @param {Object} subject the handled subject
* @param {Array|arguments} args the lexem's args : [ collection:Array, itemHandler:Function ]
* @param {?Object} percolator the sentence's percolator instance
* @return {void} nothing
*/
createClass(FacadePragmatics, [{
key: 'each',
value: function each(subject, args /* collection, itemHandler */, percolator) {
var collec = args[0],
itemHandler = args[1];
if (collec && collec.length) // no supputation on collection kind : use "for"
for (var i = 0, len = collec.length, item, templ; i < len; ++i) {
item = collec[i];
templ = itemHandler(item, i);
if (!templ) throw new Error('.each function should return a sentence.');
this.$output(subject, templ, percolator);
}
}
/**
* "if" facade implementation
* @param {Object} subject the handled subject
* @param {Array|arguments} args the lexem's args : [ conditionIsTrue:Babelute, conditionIsFalse:Babelute ]
* @param {?Object} percolator the sentence's percolator instance
* @return {void} nothing
*/
}, {
key: 'if',
value: function _if(subject, args /* trueBabelute, falseBabelute */, percolator) {
if (args[0]) this.$output(subject, args[1], percolator);else if (args[2]) this.$output(subject, args[2], percolator);
}
/**
*
* @override
* @param {Object} subject the subject handle through interpretation
* @param {Babelute} babelute the babelute "to interpret on" subject
* @param {Scope} percolator the sentence percolator instance (optional)
* @return {Object} the subject
*/
}, {
key: '$output',
value: function $output(subject, babelute) {
var percolator = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
for (var i = 0, lexem, len = babelute._lexems.length; i < len; ++i) {
lexem = babelute._lexems[i];
if (this._targets[lexem.lexicon] && this[lexem.name]) this[lexem.name](subject, lexem.args, percolator);
}
return subject;
}
}]);
return FacadePragmatics;
}(Pragmatics);
/**
* create a FacadePragmatics instance
* @param {Object} targets the pragmatics targets DSL
* @param {?Object} pragmas the methods to add
* @return {FacadePragmatics} the facade pragmatics instance
* @example
* const myPragmas = babelute.createFacadePragmatics({
* 'my-lexicon':true
* }, {
* foo(subject, args, percolator){
* // do something
* },
* bar(subject, args, percolator){
* // do something
* }
* });
*/
function createFacadePragmatics(targets) {
var pragmas = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
return new FacadePragmatics(targets, pragmas);
}
/*
* @author Gilles Coomans
* @licence MIT
* @copyright 2016 Gilles Coomans
*/
var index = {
createLexicon: createLexicon,
createPragmatics: createPragmatics,
createFacadePragmatics: createFacadePragmatics,
init: init,
initializer: initializer,
getLexicon: getLexicon,
registerLexicon: registerLexicon,
developOneLevel: developOneLevel,
developToAtoms: developToAtoms,
fromJSON: fromJSON,
Babelute: Babelute,
Lexem: Lexem,
FirstLevel: FirstLevel,
Pragmatics: Pragmatics,
FacadePragmatics: FacadePragmatics,
Lexicon: Lexicon,
lexicons: lexicons
};
return index;
})));
//# sourceMappingURL=index.js.map