feat: Template types #45
@ -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