UNPKG

theprogrammablemind

Version:

466 lines (420 loc) • 11.3 kB
const deepEqual = require('deep-equal') const stringify = require('json-stable-stringify') function where (goUp = 2) { const e = new Error() const regexForm1 = /\((.*):(\d+):(\d+)\)$/ const regexForm2 = /at (.*):(\d+):(\d+)$/ const lines = e.stack.split('\n') let line let match for (line of lines.slice(1)) { // if (!(line.includes('config.js:') || line.includes('client.js:') || line.includes('<anonymous>'))) { if (!(line.includes('config.js:') || line.includes('client.js:') || line.includes('helpers.js:'))) { match = regexForm1.exec(line) || regexForm2.exec(line) if (!match) { continue } break } } // const line = e.stack.split("\n")[goUp]; // const match = regexForm1.exec(line) || regexForm2.exec(line) if (match) { return `${match[1]}:${match[2]}` } else { return 'running in browser or in an async call so the stack is broken.' } } function suggestAssociationsFix (esummary, asummary) { for (let isummary = 0; isummary < esummary.length; ++isummary) { if (!deepEqual(esummary[isummary], asummary[isummary])) { return esummary[isummary].operators } } return [] } function suggestAssociationsFixFromSummaries (esummaries, asummaries) { for (let isummaries = 0; isummaries < esummaries.length; ++isummaries) { const esummary = esummaries[isummaries].summaries const asummary = asummaries[isummaries].summaries for (let isummary = 0; isummary < esummary.length; ++isummary) { if (!deepEqual(esummary[isummary], asummary[isummary])) { return esummary[isummary].operators } } } return [] } function w (func) { func.where = where(3) return func } // properties - the properties that correspond to types // types - the expected types of the properties // returns list of properties found matching order of types const args = (context, hierarchy) => ({ types, properties }) => { const orderedByType = [] for (const type of types) { let found = false for (let index = 0; index < properties.length; ++index) { const value = context[properties[index]] if (!value) { return false } if (hierarchy.isA(value.marker, type)) { orderedByType.push(properties[index]) properties.splice(index, 1) found = true break } } if (!found) { return } } if (orderedByType.length === 0) { return } return orderedByType } const appendNoDups = (l1, l2) => { for (const x of l2) { if (l1.includes(x)) { continue } l1.push(x) } } const safeNoDups = (list) => { noDups = [] for (const element of list) { if (!noDups.find((e) => safeEquals(e, element))) { noDups.push(element) } } return noDups } const safeEquals = (v1, v2) => { if (typeof v1 !== typeof v2) { return false } const type = typeof v1 if (type == 'number' || type == 'string') { return v1 == v2 } else if (type == 'function') { return v1.toString() == v2.toString() } else if (v1 == undefined || v2 == undefined) { return v1 == v2 } else { if (v1.length != v2.length) { return false } for (const key in v1) { if (!safeEquals(v1[key], v2[key])) { return false } } return true } } /* const semanticsGenerate = (from, known) => { const marker = from.marker const sources = [] for (let key in Object.keys(from)) { if (from[key].marker) { sources.push(from[key]) } } const mappings = [] for (let key in Object.keys(to)) { const source if (to[keys].marker == } return { match: ({context}) => marker == marker, apply: ({context}) => { }, } } */ const hashIndexesGet = (hash, indexes) => { let value = hash for (const i of indexes) { value = value[i] } return value } const hashIndexesSet = (hash, indexes, value) => { let currentValue = hash for (const i of indexes.slice(0, -1)) { if (!currentValue[i]) { currentValue[i] = {} } currentValue = currentValue[i] } currentValue[indexes[indexes.length - 1]] = value } const translationMapping = (from, to) => { const mappings = [] for (const fkey of Object.keys(from)) { if (from[fkey].value) { let found = false const todo = Object.keys(to).map((key) => [key]) while (!found) { const tkey = todo.shift() const tvalue = hashIndexesGet(to, tkey) const fvalue = hashIndexesGet(from, [fkey]) if (fvalue.value === tvalue.value) { mappings.push({ from: [fkey], to: tkey }) found = true break } else { if (typeof tvalue !== 'string' && typeof tvalue !== 'number') { for (const key of Object.keys(tvalue)) { todo.push(tkey.concat(key)) } } } } } } return mappings } const normalizeGenerator = (generator) => { if (Array.isArray(generator)) { if (generator.length === 2) { return { match: generator[0], apply: generator[1] } } else { return { match: generator[0], apply: generator[1], uuid: generator[2] } } } return generator } const normalizeSemantic = (semantic) => { if (Array.isArray(semantic)) { if (semantic.length === 2) { return { match: semantic[0], apply: semantic[1] } } else { return { match: semantic[0], apply: semantic[1], uuid: semantic[2] } } } return semantic } const isArray = (value) => { return (!!value) && (value.constructor === Array) } const isObject = (value) => { return (!!value) && (value.constructor === Object) } const isCompound = (value) => { return isArray(value) || isObject(value) } class InitCalls { constructor (name) { this.nextCallId = 0 this.nextContextId = 0 this.stack = [] this.name = name } start () { return this.nextCallId } next () { // this.nextContextId += 1 this.nextContextId += 1 } push () { this.nextCallId += 1 // this.nextCallId += 1 // this.stack.push(this.nextCallId) this.stack.push(this.nextCallId) // TODO put the nextContextId in the context for debugging const calls = this.stack.map((call) => `${this.name}#call${call}`) // return `Context#${this.nextContextId}: ${calls}` return `Context#${this.nextContextId}: ${calls}` } current () { return `${this.name}#call${this.stack[this.stack.length - 1]}` } touch (context) { if (!context.touchedBy) { context.touchedBy = [] } if (!context.touchedBy.includes(this.current())) { context.touchedBy.push(this.current()) } } pop () { this.stack.pop() } } const hashCode = (str) => { let hash = 0; let i; let ch if (str.length === 0) return hash for (i = 0; i < str.length; i++) { ch = str.charCodeAt(i) hash = ((hash << 5) - hash) + ch hash |= 0 // Convert to 32bit integer } return hash } const sortJson = (json) => { return json } const validProps = (valids, object, type) => { for (const prop of Object.keys(object)) { let okay = false for (valid of valids) { if (typeof valid === 'string') { okay = prop == valid } else { okay = prop.match(valid) } if (okay) { break } } if (!okay) { throw new Error(`Unknown property "${prop}" in the ${type}. Valid properties are ${valids.join(', ')}. The ${type} is ${JSON.stringify(object)}`) } } } const mapInPlace = (list, fn) => { for (let i = 0; i < list.length; ++i) { list[i] = fn(list[i]) } } const updateQueries = (queryOrConfig) => { if (typeof queryOrConfig === 'string' || queryOrConfig.query) { return queryOrConfig } else if (typeof queryOrConfig === 'function') { return { apply: queryOrConfig.toString() } } else { const config = queryOrConfig return functionsToStrings(config) } } const functionsToStrings = (config) => { config = { ...config } defToStrings = (def) => { if (def.apply) { // return { ...def, match: def.match.toString(), apply: def.apply.toString() } return { match: def.match.toString(), apply: def.apply.toString() } } else { return [def[0].toString(), def[1].toString()] } } if (config.generators) { config.generators = config.generators.map(defToStrings) } if (config.semantics) { config.semantics = config.semantics.map(defToStrings) } if (config.bridges) { config.bridges = config.bridges.map((bridge) => { bridge = { ...bridge } if (bridge.generator) { bridge.generator = bridge.generator.toString() } if (bridge.generators) { bridge.generators = bridge.generators.map(defToStrings) } if (bridge.generatorp) { bridge.generatorp = bridge.generatorp.toString() } if (bridge.generatorr) { bridge.generatorr = bridge.generatorr.toString() } if (bridge.semantic) { bridge.semantic = bridge.semantic.toString() } if (bridge.semantics) { bridge.semantics = bridge.semantics.toString() } if (bridge.evaluator) { bridge.evaluator = bridge.evaluator.toString() } if (bridge.evaluators) { bridge.evaluators = bridge.evaluators.toString() } return bridge }) } return config } const ecatch = (where, call) => { try { return call() } catch (e) { throw new Error(`${where} ${e.stack}`) } } const equalKey = (key1, key2) => { return key1[0] == key2[0] && key1[1] == key2[1] } // matches for { context: ..., [ordered], choose: ... } exactely OR // [ <id1>, <id2> ] - where id1 is chosen const subPriority = (sub, sup) => { if (Array.isArray(sub)) { const subChoosen = sub[0] const subOther = sub[1] const hasChoosen = sup.choose.find((index) => equalKey(sup.context[index], subChoosen)) != undefined const hasOtherChosen = sup.choose.find((index) => equalKey(sup.context[index], subOther)) != undefined const hasOther = sup.context.find((other) => equalKey(other, subOther)) !== undefined return !!(hasChoosen && hasOther) && !hasOtherChosen } if (!safeEquals([...sub.choose].sort(), [...sup.choose].sort())) { return false } const choose = (priority) => { const chosen = [] for (const i of priority.choose) { chosen.push(priority.context[i]) } return chosen } const chosen1 = choose(sub) const chosen2 = choose(sup) const sameId = (id1, id2) => id1[0] == id2[0] && id1[1] == id2[1] // same length so only need one way const missing1 = chosen1.find((id1) => !chosen2.find((id2) => sameId(id1, id2))) if (missing1) { return false } return true } const stableIds = {} const stableId = (tag) => { const id = stableIds[tag] || 1 stableIds[tag] = id + 1 return id } module.exports = { stableId, ecatch, functionsToStrings, updateQueries, mapInPlace, validProps, args, safeNoDups, safeEquals, appendNoDups, hashIndexesGet, hashIndexesSet, translationMapping, normalizeGenerator, normalizeSemantic, isArray, isObject, isCompound, InitCalls, hashCode, sortJson, subPriority, where, w, suggestAssociationsFix, suggestAssociationsFixFromSummaries }