UNPKG

babelute

Version:

Internal Domain Specific (Multi)Modeling javascript framework

1,130 lines (900 loc) 34 kB
(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