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 */
import typed from 'typed-function'
import dependencyExtractor from './dependencyExtractor.mjs'
import {dependencyExtractor, generateTypeExtractor} from './extractors.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
@ -461,9 +461,12 @@ export default class PocomathInstance {
this._addAffect(depname, name)
}
for (const type of typesOfSignature(signature)) {
if (this._templateParam(type)) continue
this._usedTypes.add(type)
this._addAffect(':' + type, name)
for (const word of type.split(/[<>]/)) {
if (word.length == 0) continue
if (this._templateParam(word)) continue
this._usedTypes.add(word)
this._addAffect(':' + word, name)
}
}
}
}
@ -526,25 +529,40 @@ export default class PocomathInstance {
if (!imps) {
throw new SyntaxError(`No implementations for ${name}`)
}
const usableEntries = Object.entries(imps).filter(
([signature]) => subsetOfKeys(typesOfSignature(signature), this.Types))
/* Collect the entries we know the types for */
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) {
throw new SyntaxError(
`Every implementation for ${name} uses an undefined type;\n`
+ ` 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'})
const tf_imps = {}
for (const [rawSignature, behavior] of usableEntries) {
/* Check if it's an ordinary non-template signature */
let explicit = true
for (const type of typesOfSignature(rawSignature)) {
if (this._templateParam(type)) { // template types need better check
for (const word of type.split(/[<>]/)) {
if (this._templateParam(word)) {
explicit = false
break
}
}
}
if (explicit) {
this._addTFimplementation(tf_imps, rawSignature, behavior)
continue
@ -591,36 +609,31 @@ export default class PocomathInstance {
innerRefs[dep] = refs[outerName]
}
}
const original = behavior.does(innerRefs)
return behavior.does(innerRefs)
}
this._addTFimplementation(tf_imps, signature, {uses, does: patch})
}
/* 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(
trimSignature, theTemplateParam, 'any')
baseSignature, theTemplateParam, 'any')
/* The catchall signature has to detect the actual type of the call
* and add the new instantiations. We should really be using the
* typed-function parser to do the manipulations below, but we don't
* have access. The firs section prepares the type inference data:
* and add the new instantiations.
* First, prepare the type inference data:
*/
const parTypes = trimSignature.split(',')
const inferences = []
const typer = entity => this.typeOf(entity)
let ambiguous = true
for (let parType of parTypes) {
parType = parType.trim()
if (parType.slice(0,3) === '...') {
parType = parType.slice(3).trim()
}
if (parType === theTemplateParam) {
inferences.push(typer)
ambiguous = false
} else {
inferences.push(false)
}
}
if (ambiguous) {
const topTyper = entity => this.typeOf(entity)
const inferences = parTypes.map(
type => generateTypeExtractor(
type,
theTemplateParam,
topTyper,
this.joinTypes.bind(this),
this._templateTypes))
if (inferences.every(x => !x)) { // all false
throw new SyntaxError(
`Cannot find template parameter in ${rawSignature}`)
}
@ -654,9 +667,24 @@ export default class PocomathInstance {
usedConversions = true
instantiateFor = self.joinTypes(argTypes, usedConversions)
if (instantiateFor === 'any') {
// Need a more informative error message here
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: */
if (usedConversions) {
i = - 1
@ -774,4 +802,12 @@ export default class PocomathInstance {
}
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 dependencyExtractor from '../../src/core/dependencyExtractor.mjs'
import {dependencyExtractor} from '../../src/core/extractors.mjs'
describe('dependencyExtractor', () => {
it('will record the keys of a destructuring function', () => {