UNPKG

theprogrammablemind

Version:

283 lines (269 loc) • 10.6 kB
const { args: contextArgs, normalizeGenerator } = require('./helpers') const Lines = require('../lines') const helpers = require('./helpers') class Generator { // constructor ({ match, apply, uuid, index, km, priority, notes }) { constructor (generator) { generator = normalizeGenerator(generator) const { match, apply, uuid, index, km, priority, where, notes, debug, applyWrapped, property } = generator this.match = match this._apply = apply this._applyWrapped = applyWrapped this.property = property this.uuid = uuid this.index = index this.km = km this.priority = priority || 0 this.notes = notes this.where = where this.callId = debug } getAPI (config) { if (config && config.getAPI) { return config.getAPI(this.uuid) } } getAPIs (config) { if (config && config._api && config._api.multiApi) { return config._api.apis } } toLabel () { const where = this.where ? `where: "${this.where}"` : '' if (!this.km) { return where } return `KM '${this.km}' ordinal: ${this.index} ${where}` } toString () { return `Generator(${this.match}, ${this._applyWrapped || this._apply})${this.property ? `\nsee the ${this.property} property` : ''}` } async matches (baseArgs, objects, context, hierarchy, config, options = {}) { if (objects && objects.namespaced) { objects = objects.namespaced[this.uuid] } // return this.match({ ...args, args: contextArgs(context, hierarchy), objects, global: objects, hierarchy, context, api: this.getAPI(config) }) const callId = baseArgs.calls.current() const moreArgs = { uuid: this.uuid, args: contextArgs(context, hierarchy), objects, global: objects, // hierarchy, context, callId, api: this.getAPI(config), apis: this.getAPIs(config) } const args = Object.assign({}, baseArgs, moreArgs, (baseArgs.getUUIDScoped || (() => { return {} }))(this.uuid)) // return this.match(args) const matches = await this.match(args) if ((matches && (options.debug || {}).match) || callId == this.callId) { // next line is the matcher debugger // eslint-disable-line no-debugger await this.match(args) } return matches } // apply (baseArgs, objects, g, gs, context, hierarchy, config, response, log, options = {}) { async apply (baseArgs, objects, context, hierarchy, config, response, log, options = {}) { if (!log) { throw new Error('generators.apply argument log is required') } if (baseArgs.call && config && sbaseArgs.calls.stack.length > config.maxDepth) { throw new Error(`Max depth of ${config.maxDepth} for calls has been exceeded. maxDepth can be set on the config object. To see the calls run with the --dl or set the debugLoops property on the config`) } if (config && config.debugLoops) { console.log('apply', this.toLabel()) } // this.getAPI(config) let n = (id) => id if (config && 'nsToString' in config) { n = (id) => config.nsToString(id) } if (objects && objects.namespaced) { objects = objects.namespaced[this.uuid] } const km = (name) => config.getConfig(name) const callId = baseArgs.calls.current() const moreArgs = { callId, uuid: this.uuid, // km, args: contextArgs(context, hierarchy), objects, log, global: objects, // g, n, hierarchy, context, uuid: this.uuid, // gs, config, response, api: this.getAPI(config), apis: this.getAPIs(config) } const args = Object.assign({}, baseArgs, moreArgs, (baseArgs.getUUIDScoped || (() => { return {} }))(this.uuid)) if (this.property == 'generatorp') { args.g = args.gp } if ((options.debug || {}).apply || callId == this.callId) { debugger // eslint-disable-line no-debugger } return await this._apply(args) } } class Generators { constructor (generators, logs = [], options = {}) { let index = -1 generators = (generators || []).map((generator) => { if (generator instanceof Generator) { return generator } else { index += 1 return new Generator({ km: options.km, index, ...normalizeGenerator(generator) }) } }) this.defaults = [] const priorityToGenerators = {} const add = (priority, value) => { if (!priorityToGenerators[priority]) { priorityToGenerators[priority] = [] } priorityToGenerators[priority].push(value) } for (const generator of generators) { const priority = generator.priority add(priority, generator) } this.generators = [] for (const key of Object.keys(priorityToGenerators).sort()) { this.generators = this.generators.concat(priorityToGenerators[key]) } // this.generators = generators this.logs = logs }; // assumed - properties added to context before the generators are called. For setting paraphrase property async apply (args, context, assumed = {}, options = {}) { if (Array.isArray(context)) { throw new Error('Expected a context not an array') } if (typeof context !== 'object') { return String(context) } const config = args.config const objects = args.objects const hierarchy = args.hierarchy const response = args.response // args = { ...args, ...args.getAssumedScoped(assumed) } args.addAssumedScoped(args, assumed) context = Object.assign({}, context, args.assumed) let generated = '' let applied = false const stack = args.calls.push() for (let igenerator = 0; igenerator < this.generators.length; ++igenerator) { const generator = this.generators[igenerator] if (await generator.matches(args, objects, context, hierarchy, config, options)) { const log = (message) => { this.logs.push(message) } // this.logs.push(`Generators: applied ${generator.toString()}\n to\n ${JSON.stringify(context)}`) let errorMessage = 'The apply function did not return a value' try { generated = await generator.apply(args, objects, context, hierarchy, config, response, log) } catch (e) { // the next if handle this generated = null e.retryCall = () => generator.apply(args, objects, context, hierarchy, config, response, log) const help = 'The error has a retryCall property that will recall the function that failed.' if (e.stack && e.message) { const info = `${generator.notes ? generator.notes : ''}${generator.where ? generator.where : ''}` errorMessage = `Error applying generator '${info}. Error is ${e.toString()} stack is ${e.stack}. Generator is ${generator.toString()}. ${help}` } else if (e.error) { const info = `${generator.notes ? generator.notes : ''}${generator.where ? generator.where : ''}` errorMessage = `Error applying generator '${info}. Error is ${e.error.join()}. Generator is ${generator.toString()}. ${help}` } else { errorMessage = e.toString() } } if (!generated && generated !== '') { const widths = [10, 10, 90] const lines = new Lines(widths) lines.setElement(0, 0, 'Generator') const source = `${generator.km}/#${generator.index}` lines.setElement(0, 2, `ERROR while applying (${source}) ${generator.toLabel()}`) lines.newRow() lines.setElement(0, 2, generator.toString()) lines.newRow() lines.setElement(0, 1, 'TO') lines.setElement(0, 2, `context_id: ${context.context_id}`) lines.setElement(1, 2, JSON.stringify(helpers.sortJson(context, { depth: 25 }), null, 2)) lines.newRow() lines.setElement(0, 1, 'STACK') lines.setElement(0, 2, stack) lines.newRow() lines.setElement(0, 1, 'DEBUG') lines.setElement(0, 2, `To debug this use args.callId == '${args.calls.current()}'`) lines.newRow() lines.setElement(0, 1, 'ERROR') lines.setElement(0, 2, errorMessage) this.logs.push(lines.toString()) const message = `ERROR while applying (${source}) ${generator.toLabel()}\n to\n ${JSON.stringify(context, null, 2)}.\n${errorMessage}'` // this.logs.push(message) // return [message] args.calls.pop() throw { error: [message], logs: this.logs } } if (((config || {}).config || {}).debug) { const widths = [10, 10, 90] const lines = new Lines(widths) lines.setElement(0, 0, 'Generator') if (generator.index > -1 && generator.km) { // lines.setElement(0, 2, `KM '${generator.km}' ordinal: ${generator.index}`) lines.setElement(0, 2, generator.toLabel()) } lines.newRow() lines.setElement(0, 1, 'APPLIED') lines.setElement(0, 2, generator.toLabel()) lines.newRow() lines.setElement(0, 2, generator.toString()) lines.newRow() lines.setElement(0, 1, 'RESULT') lines.setElement(0, 2, generated) lines.newRow() lines.setElement(0, 1, 'STACK') lines.setElement(0, 2, stack) lines.newRow() lines.setElement(0, 1, 'DEBUG') lines.setElement(0, 2, `To debug this use args.callId == '${args.calls.current()}'`) lines.newRow() lines.setElement(0, 1, 'TO') lines.setElement(0, 2, `context_id: ${context.context_id}`) lines.setElement(1, 2, JSON.stringify(helpers.sortJson(context, { depth: 25 }), null, 2)) this.logs.push(lines.toString()) } applied = true break } } args.calls.pop() if (!applied && ((config || {}).config || {}).debug) { const widths = [10, 10, 90] const lines = new Lines(widths) lines.setElement(0, 0, 'Generator') lines.setElement(0, 2, 'No generator applied') lines.newRow() lines.setElement(0, 1, 'STACK') lines.setElement(0, 2, stack) lines.newRow() lines.setElement(0, 1, 'TO') lines.setElement(0, 2, JSON.stringify(context, null, 2)) this.logs.push(lines.toString()) } return ((config || {}).parenthesized ? '(' + generated + ')' : generated) } } module.exports = { Generator, Generators, normalizeGenerator }