refactor: separate the generation of a typed-function implementation

This commit is contained in:
Glen Whitney 2022-07-31 09:36:45 -07:00
parent 5c3716ff99
commit 0fbcbf661e
1 changed files with 80 additions and 69 deletions

View File

@ -319,7 +319,8 @@ export default class PocomathInstance {
`Conflicting definitions of ${signature} for ${name}`)
}
} else {
opImps[signature] = behavior
// Must avoid aliasing into another instance:
opImps[signature] = {uses: behavior.uses, does: behavior.does}
for (const dep of behavior.uses) {
const depname = dep.split('(', 1)[0]
if (depname === 'self' || this._templateParam(depname)) {
@ -402,80 +403,90 @@ export default class PocomathInstance {
}
Object.defineProperty(this, name, {configurable: true, value: 'limbo'})
const tf_imps = {}
for (const [rawSignature, {uses, does}] of usableEntries) {
for (const [rawSignature, behavior] of usableEntries) {
/* For now, replace theTemplateParam with 'any' */
const signature = rawSignature.replaceAll(theTemplateParam, 'any')
if (uses.length === 0) {
tf_imps[signature] = does()
} else {
const refs = {}
let full_self_referential = false
let part_self_references = []
for (const dep of uses) {
let [func, needsig] = dep.split(/[()]/)
const needTypes = needsig ? typesOfSignature(needsig) : new Set()
/* For now, punt on template parameters */
if (needTypes.has(theTemplateParam)) needsig = ''
if (func === 'self') {
if (needsig) {
if (full_self_referential) {
throw new SyntaxError(
'typed-function does not support mixed full and '
+ 'partial self-reference')
}
if (subsetOfKeys(typesOfSignature(needsig), this.Types)) {
part_self_references.push(needsig)
}
} else {
if (part_self_references.length) {
throw new SyntaxError(
'typed-function does not support mixed full and '
+ 'partial self-reference')
}
full_self_referential = true
}
} else {
if (this[func] === 'limbo') {
/* We are in the midst of bundling func, so have to use
* an indirect reference to func. And given that, there's
* really no helpful way to extract a specific signature
*/
const self = this
refs[dep] = function () { // is this the most efficient?
return self[func].apply(this, arguments)
}
} else {
// can bundle up func, and grab its signature if need be
let destination = this[func]
if (needsig) {
destination = this._typed.find(destination, needsig)
}
refs[dep] = destination
}
}
}
if (full_self_referential) {
tf_imps[signature] = this._typed.referToSelf(self => {
refs.self = self
return does(refs)
})
} else if (part_self_references.length) {
tf_imps[signature] = this._typed.referTo(
...part_self_references, (...impls) => {
for (let i = 0; i < part_self_references.length; ++i) {
refs[`self(${part_self_references[i]})`] = impls[i]
}
return does(refs)
}
)
} else {
tf_imps[signature] = does(refs)
}
}
this._addTFimplementation(
tf_imps, signature, behavior.uses, behavior.does)
}
const tf = this._typed(name, tf_imps)
Object.defineProperty(this, name, {configurable: true, value: tf})
return tf
}
/* Adapts Pocomath-style behavior specification (uses, does) for signature
* to typed-function implementations and inserts the result into plain object
* imps
*/
_addTFimplementation(imps, signature, uses, does) {
if (uses.length === 0) {
imps[signature] = does()
return
}
const refs = {}
let full_self_referential = false
let part_self_references = []
for (const dep of uses) {
let [func, needsig] = dep.split(/[()]/)
const needTypes = needsig ? typesOfSignature(needsig) : new Set()
/* For now, punt on template parameters */
if (needTypes.has(theTemplateParam)) needsig = ''
if (func === 'self') {
if (needsig) {
if (full_self_referential) {
throw new SyntaxError(
'typed-function does not support mixed full and '
+ 'partial self-reference')
}
if (subsetOfKeys(typesOfSignature(needsig), this.Types)) {
part_self_references.push(needsig)
}
} else {
if (part_self_references.length) {
throw new SyntaxError(
'typed-function does not support mixed full and '
+ 'partial self-reference')
}
full_self_referential = true
}
} else {
if (this[func] === 'limbo') {
/* We are in the midst of bundling func, so have to use
* an indirect reference to func. And given that, there's
* really no helpful way to extract a specific signature
*/
const self = this
refs[dep] = function () { // is this the most efficient?
return self[func].apply(this, arguments)
}
} else {
// can bundle up func, and grab its signature if need be
let destination = this[func]
if (needsig) {
destination = this._typed.find(destination, needsig)
}
refs[dep] = destination
}
}
}
if (full_self_referential) {
imps[signature] = this._typed.referToSelf(self => {
refs.self = self
return does(refs)
})
return
}
if (part_self_references.length) {
imps[signature] = this._typed.referTo(
...part_self_references, (...impls) => {
for (let i = 0; i < part_self_references.length; ++i) {
refs[`self(${part_self_references[i]})`] = impls[i]
}
return does(refs)
}
)
return
}
imps[signature] = does(refs)
}
}