theprogrammablemind
Version:
283 lines (269 loc) • 10.6 kB
JavaScript
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 }