UNPKG

elliptical

Version:

Interactive natural-language interfaces

234 lines (179 loc) 8.56 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); exports.default = compile; var _lodash = require('lodash'); var _lodash2 = _interopRequireDefault(_lodash); var _utils = require('./utils'); var _phrases = require('./phrases'); var phrases = _interopRequireWildcard(_phrases); var _option = require('./option'); var _option2 = _interopRequireDefault(_option); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _toConsumableArray(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); } } function applyDefaults(element) { return _lodash2.default.assign({}, element, { props: _lodash2.default.defaults({}, element.props, element.type.defaultProps || {}) }); } function compileAndAddToMap(subElement, process, elementMap, _ref) { let errors = _ref.errors; const traverse = compileNonRoot(subElement, process, { errors: errors }); elementMap.set(subElement, traverse); return traverse; } function next(elementMap, process, subElement, option, _ref2) { let errors = _ref2.errors; let traverser = elementMap.get(subElement); if (traverser) { return traverser(option); } else { const newTraverser = compileAndAddToMap(subElement, process, elementMap, { errors: errors }); return newTraverser(option); } } function compileProp(prop, process, elementMap, _ref3) { let errors = _ref3.errors; if (prop && prop.type && prop.props && prop.children && (_lodash2.default.isPlainObject(prop.type) || _lodash2.default.isString(prop.type)) && _lodash2.default.isPlainObject(prop.props) && _lodash2.default.isArray(prop.children)) { // We can be pretty sure this is an element, return compileAndAddToMap(prop, process, elementMap, { errors: errors }); } else { return prop; } } function getPhrase(element) { return _lodash2.default.isString(element.type) ? phrases[element.type] : element.type; } function tryRunning(func, errors, messages) { let defaultIfError = arguments.length <= 3 || arguments[3] === undefined ? null : arguments[3]; if (errors === 'log') { try { return func(); } catch (e) { var _console; (_console = console).error.apply(_console, _toConsumableArray(messages).concat([e])); return defaultIfError; } } else { return func(); } } function compileNonRoot(element, process, _ref4) { let errors = _ref4.errors; // ignore null elements if (element == null) return () => []; // assign defaultProps element = applyDefaults(element); if (process) { element = tryRunning(() => process(element), errors, ['An error occurred processing', element]); // allow process calls to nullify elements if (element == null) return () => []; } const phrase = getPhrase(element); // call describe if (phrase.describe) { if (phrase.lazy === false) { const description = tryRunning(() => phrase.describe(element), errors, ['An error occurred describing', element]); const traverse = compileNonRoot(description, process, { errors: errors }); return addOutbound(element, traverse, { errors: errors }); } else { let subTraverse; const traverse = input => { if (!subTraverse) { const description = tryRunning(() => phrase.describe(element), errors, ['An error occurred dynamically describing', element]); subTraverse = compileNonRoot(description, process, { errors: errors }); } return subTraverse(input); }; return addOutbound(element, traverse, { errors: errors }); } } const elementMap = new Map(); // if there's no describe, check to see if any props are elements // and compile those _lodash2.default.forEach(element.props, prop => compileProp(prop, process, elementMap, { errors: errors })); // generate the traverse thunk _lodash2.default.forEach(element.children, child => { return compileAndAddToMap(child, process, elementMap, { errors: errors }); }); const subTraverse = (subElem, option) => next(elementMap, process, subElem, option, { errors: errors }); const traverse = option => { return tryRunning(() => phrase.visit(option, element, subTraverse), errors, ['An error occurred visiting', element], []); }; return addOutbound(element, traverse, { errors: errors }); } function addOutbound(element, traverse, _ref5) { let errors = _ref5.errors; return function* (option) { const start = option.words.length; const mods = {}; if (element.props.data != null) { mods.data = _lodash2.default.concat(option.data, [element.props.data]); } const newOption = _lodash2.default.assign({}, option, mods); for (let output of traverse(newOption)) { if ((0, _utils.isComplete)(output)) { if (element.type.mapResult) { const newResult = tryRunning(() => element.type.mapResult(output.result, element), errors, ['An error occurred in mapResult of', element], output.result); output = _lodash2.default.assign({}, output, { result: newResult }); } if (element.type.filterResult) { const filterResultResult = tryRunning(() => element.type.filterResult(output.result, element), errors, ['An error occurred in filterResult of', element], true); if (!filterResultResult) { continue; } } } const end = output.words.length; const mods = {}; if (element.props.value != null) { mods.result = element.props.value; } if (element.props.score != null) { mods.score = element.props.score; } if (element.props.multiplier != null) { mods.score = element.props.multiplier * (element.props.score == null ? output.score : element.props.score); } _lodash2.default.forEach([['qualifiers', 'qualifier'], ['arguments', 'argument'], ['annotations', 'annotation'], ['categories', 'category']], _ref6 => { var _ref7 = _slicedToArray(_ref6, 2); let plural = _ref7[0]; let singular = _ref7[1]; if (element.props[plural] != null || element.props[singular] != null) { const theseAdditions = element.props[plural] || [element.props[singular]]; const outputAdditions = _lodash2.default.map(theseAdditions, addition => { return { value: addition, start: start, end: end }; }); mods[plural] = _lodash2.default.concat(outputAdditions, output[plural]); } }); yield _lodash2.default.assign({}, output, mods); } }; } function compile(element, process) { var _ref8 = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; var _ref8$errors = _ref8.errors; let errors = _ref8$errors === undefined ? 'none' : _ref8$errors; const compiled = compileNonRoot(element, process, { errors: errors }); return function traverse(input) { const postProcessed = postProcess(compiled, input); const allOutputs = Array.from(postProcessed); return _lodash2.default.sortBy(allOutputs, output => -output.score); }; } function* postProcess(compiled, input) { const option = (0, _option2.default)({ text: input }); const outputs = compiled(option); for (let output of outputs) { if (output.text === '' || output.text == null) { _lodash2.default.forEach(output.callbacks, callback => callback()); const newOutput = _lodash2.default.clone(output); delete newOutput.callbacks; yield newOutput; } } }