refactor: Include more code that should work for instantiating type templates

In particular, there is now an empty stub for the function that actually
  installs the instantiations into the PocomathInstance
This commit is contained in:
Glen Whitney 2022-08-03 17:55:53 -07:00
parent 27bf23db54
commit e82bcf5a9c
4 changed files with 110 additions and 45 deletions

View File

@ -1,8 +1,8 @@
/* Core of pocomath: create an instance */ /* Core of pocomath: create an instance */
import typed from 'typed-function' import typed from 'typed-function'
import dependencyExtractor from './dependencyExtractor.mjs' import {dependencyExtractor, generateTypeExtractor} from './extractors.mjs'
import {makeChain} from './Chain.mjs' import {makeChain} from './Chain.mjs'
import {subsetOfKeys, typesOfSignature} from './utils.mjs' import {typesOfSignature, subsetOfKeys} from './utils.mjs'
const anySpec = {} // fixed dummy specification of 'any' type const anySpec = {} // fixed dummy specification of 'any' type
@ -461,9 +461,12 @@ export default class PocomathInstance {
this._addAffect(depname, name) this._addAffect(depname, name)
} }
for (const type of typesOfSignature(signature)) { for (const type of typesOfSignature(signature)) {
if (this._templateParam(type)) continue for (const word of type.split(/[<>]/)) {
this._usedTypes.add(type) if (word.length == 0) continue
this._addAffect(':' + type, name) if (this._templateParam(word)) continue
this._usedTypes.add(word)
this._addAffect(':' + word, name)
}
} }
} }
} }
@ -526,23 +529,38 @@ export default class PocomathInstance {
if (!imps) { if (!imps) {
throw new SyntaxError(`No implementations for ${name}`) throw new SyntaxError(`No implementations for ${name}`)
} }
const usableEntries = Object.entries(imps).filter( /* Collect the entries we know the types for */
([signature]) => subsetOfKeys(typesOfSignature(signature), this.Types)) const usableEntries = []
for (const entry of Object.entries(imps)) {
let keep = true
for (const type of typesOfSignature(entry[0])) {
if (type in this.Types) continue
const baseType = type.split('<')[0]
if (baseType in this._templateTypes) continue
keep = false
break
}
if (keep) usableEntries.push(entry)
}
if (usableEntries.length === 0) { if (usableEntries.length === 0) {
throw new SyntaxError( throw new SyntaxError(
`Every implementation for ${name} uses an undefined type;\n` `Every implementation for ${name} uses an undefined type;\n`
+ ` signatures: ${Object.keys(imps)}`) + ` signatures: ${Object.keys(imps)}`)
} }
// Mark this method as being in the midst of being reassembled /* Initial error checking done; mark this method as being
* in the midst of being reassembled
*/
Object.defineProperty(this, name, {configurable: true, value: 'limbo'}) Object.defineProperty(this, name, {configurable: true, value: 'limbo'})
const tf_imps = {} const tf_imps = {}
for (const [rawSignature, behavior] of usableEntries) { for (const [rawSignature, behavior] of usableEntries) {
/* Check if it's an ordinary non-template signature */ /* Check if it's an ordinary non-template signature */
let explicit = true let explicit = true
for (const type of typesOfSignature(rawSignature)) { for (const type of typesOfSignature(rawSignature)) {
if (this._templateParam(type)) { // template types need better check for (const word of type.split(/[<>]/)) {
explicit = false if (this._templateParam(word)) {
break explicit = false
break
}
} }
} }
if (explicit) { if (explicit) {
@ -591,36 +609,31 @@ export default class PocomathInstance {
innerRefs[dep] = refs[outerName] innerRefs[dep] = refs[outerName]
} }
} }
const original = behavior.does(innerRefs)
return behavior.does(innerRefs) return behavior.does(innerRefs)
} }
this._addTFimplementation(tf_imps, signature, {uses, does: patch}) this._addTFimplementation(tf_imps, signature, {uses, does: patch})
} }
/* Now add the catchall signature */ /* Now add the catchall signature */
let templateCall = `<${theTemplateParam}>`
/* Relying here that the base of 'Foo<T>' is 'Foo': */
let baseSignature = substituteInSig(trimSignature, templateCall, '')
/* Any remaining template params are top-level */
const signature = substituteInSig( const signature = substituteInSig(
trimSignature, theTemplateParam, 'any') baseSignature, theTemplateParam, 'any')
/* The catchall signature has to detect the actual type of the call /* The catchall signature has to detect the actual type of the call
* and add the new instantiations. We should really be using the * and add the new instantiations.
* typed-function parser to do the manipulations below, but we don't * First, prepare the type inference data:
* have access. The firs section prepares the type inference data:
*/ */
const parTypes = trimSignature.split(',') const parTypes = trimSignature.split(',')
const inferences = [] const topTyper = entity => this.typeOf(entity)
const typer = entity => this.typeOf(entity) const inferences = parTypes.map(
let ambiguous = true type => generateTypeExtractor(
for (let parType of parTypes) { type,
parType = parType.trim() theTemplateParam,
if (parType.slice(0,3) === '...') { topTyper,
parType = parType.slice(3).trim() this.joinTypes.bind(this),
} this._templateTypes))
if (parType === theTemplateParam) { if (inferences.every(x => !x)) { // all false
inferences.push(typer)
ambiguous = false
} else {
inferences.push(false)
}
}
if (ambiguous) {
throw new SyntaxError( throw new SyntaxError(
`Cannot find template parameter in ${rawSignature}`) `Cannot find template parameter in ${rawSignature}`)
} }
@ -654,9 +667,24 @@ export default class PocomathInstance {
usedConversions = true usedConversions = true
instantiateFor = self.joinTypes(argTypes, usedConversions) instantiateFor = self.joinTypes(argTypes, usedConversions)
if (instantiateFor === 'any') { if (instantiateFor === 'any') {
// Need a more informative error message here
throw TypeError('No common type for arguments to ' + name) throw TypeError('No common type for arguments to ' + name)
} }
} }
/* Generate the list of actual wanted types */
const wantTypes = parTypes.map(type => substituteInSig(
type, theTemplateParam, instantiateFor))
/* Now we have to add any actual types that are relevant
* to this invocation. Namely, that would be every formal parameter
* type in the invocation, with the parameter template instantiated
* by instantiateFor, and for all of instantiateFor's "prior types"
*/
for (j = 0; j < parTypes.length; ++j) {
if (wantTypes[i] !== parTypes[i] && wantTypes.includes('<')) {
// actually used the param and is a template
self._ensureTemplateTypes(parTypes[i], instantiateFor)
}
}
/* Transform the arguments if we used any conversions: */ /* Transform the arguments if we used any conversions: */
if (usedConversions) { if (usedConversions) {
i = - 1 i = - 1
@ -774,4 +802,12 @@ export default class PocomathInstance {
} }
imps[signature] = does(refs) imps[signature] = does(refs)
} }
/* HERE!! This function needs to analyze the template and make sure the
* instantiations of it for type and all prior types of type are present
* in the instance
*/
_ensureTemplateTypes(template, type) {
}
} }

View File

@ -1,12 +0,0 @@
/* Call this with an empty Set object S, and it returns an entity E
* from which properties can be extracted, and at any time S will
* contain all of the property names that have been extracted from E.
*/
export default function dependencyExtractor(destinationSet) {
return new Proxy({}, {
get: (target, property) => {
destinationSet.add(property)
return {}
}
})
}

41
src/core/extractors.mjs Normal file
View File

@ -0,0 +1,41 @@
/* Call this with an empty Set object S, and it returns an entity E
* from which properties can be extracted, and at any time S will
* contain all of the property names that have been extracted from E.
*/
export function dependencyExtractor(destinationSet) {
return new Proxy({}, {
get: (target, property) => {
destinationSet.add(property)
return {}
}
})
}
/* Given a (template) type name, what the template parameter is,
* a top level typer, and a library of templates,
* produces a function that will extract the instantantion type from an
* instance. Currently relies heavily on there being only unary templates.
*
* We should really be using the typed-function parser to do the
* manipulations below, but at the moment we don't have access.
*/
export function generateTypeExtractor(
type, param, topTyper, typeJoiner, templates)
{
type = type.trim()
if (type.slice(0,3) === '...') {
type = type.slice(3).trim()
}
if (type === param) return topTyper
if (!(type.includes('<'))) return false // no template type to extract
const base = type.split('<',1)[0]
if (!(base in templates)) return false // unknown template
const arg = type.slice(base.length+1, -1)
const argExtractor = generateTypeExtractor(
arg, param, topTyper, typeJointer, templates)
if (!argExtractor) return false
return templates[base].infer({
typeOf: argExtractor,
joinTypes: typeJoiner
})
}

View File

@ -1,5 +1,5 @@
import assert from 'assert' import assert from 'assert'
import dependencyExtractor from '../../src/core/dependencyExtractor.mjs' import {dependencyExtractor} from '../../src/core/extractors.mjs'
describe('dependencyExtractor', () => { describe('dependencyExtractor', () => {
it('will record the keys of a destructuring function', () => { it('will record the keys of a destructuring function', () => {