elliptical
Version:
Interactive natural-language interfaces
234 lines (179 loc) • 8.56 kB
JavaScript
;
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;
}
}
}