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 */
|
/* 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,25 +529,40 @@ 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(/[<>]/)) {
|
||||||
|
if (this._templateParam(word)) {
|
||||||
explicit = false
|
explicit = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (explicit) {
|
if (explicit) {
|
||||||
this._addTFimplementation(tf_imps, rawSignature, behavior)
|
this._addTFimplementation(tf_imps, rawSignature, behavior)
|
||||||
continue
|
continue
|
||||||
@ -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) {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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 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', () => {
|
Loading…
Reference in New Issue
Block a user