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:
parent
27bf23db54
commit
e82bcf5a9c
@ -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,23 +529,38 @@ 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
|
||||
explicit = false
|
||||
break
|
||||
for (const word of type.split(/[<>]/)) {
|
||||
if (this._templateParam(word)) {
|
||||
explicit = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (explicit) {
|
||||
@ -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) {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
41
src/core/extractors.mjs
Normal 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
|
||||
})
|
||||
}
|
@ -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', () => {
|
Loading…
Reference in New Issue
Block a user