feat: Template types #45
@ -7,6 +7,7 @@ import {typesOfSignature, subsetOfKeys} from './utils.mjs'
|
||||
const anySpec = {} // fixed dummy specification of 'any' type
|
||||
|
||||
const theTemplateParam = 'T' // First pass: only allow this one exact parameter
|
||||
const templateFromParam = 'U' // For defining covariant conversions
|
||||
|
||||
/* Returns a new signature just like sig but with the parameter replaced by
|
||||
* the type
|
||||
@ -803,11 +804,91 @@ export default class PocomathInstance {
|
||||
imps[signature] = does(refs)
|
||||
}
|
||||
|
||||
/* HERE!! This function needs to analyze the template and make sure the
|
||||
/* This function analyzes the template and makes sure the
|
||||
* instantiations of it for type and all prior types of type are present
|
||||
* in the instance
|
||||
* in the instance.
|
||||
*/
|
||||
_ensureTemplateTypes(template, type) {
|
||||
let [base, arg] = template.split('<', 2)
|
||||
arg = arg.slice(0,-1)
|
||||
if (!arg) {
|
||||
throw new Error(
|
||||
'Implementation error in _ensureTemplateTypes', template, type)
|
||||
}
|
||||
let instantiations
|
||||
if (this._templateParam(arg)) { // 1st-level template
|
||||
instantiations = new Set(this._priorTypes(type))
|
||||
instantiations.add(type)
|
||||
} else { // nested template
|
||||
instantiations = this._ensureTemplateTypes(arg, type)
|
||||
}
|
||||
const resultingTypes = new Set()
|
||||
for (const iType of instantiations) {
|
||||
const resultType = this._maybeAddTemplateType(base, iType)
|
||||
if (resultType) resultingTypes.push(resultType)
|
||||
}
|
||||
return resultingTypes
|
||||
}
|
||||
|
||||
/* Maybe add the instantiation of template type base with argument tyoe
|
||||
* instantiator to the Types of this instance, if it hasn't happened already.
|
||||
* Returns the name of the type if added, false otherwise.
|
||||
*/
|
||||
_maybeAddTemplateType(base, instantiator) {
|
||||
const wantsType = `${base}<${instantiator}>`
|
||||
if (wantsType in this.Types) return false
|
||||
// OK, need to generate the type from the template
|
||||
// Set up refines, before, test, and from
|
||||
const newTypeSpec = {}
|
||||
const template = this._templateTypes[base]
|
||||
if (!template) {
|
||||
throw new Error(
|
||||
`Implementor error in _maybeAddTemplateType ${base} ${instantiator}`)
|
||||
}
|
||||
const instantiatorSpec = this.Types[instantiator]
|
||||
if (instantiatorSpec.refines) {
|
||||
// Assuming all templates are covariant, for now
|
||||
newTypeSpec.refines = `${base}<${instantiatorSpec.refines}>`
|
||||
}
|
||||
let beforeTypes = []
|
||||
if (instantiatorSpec.before) {
|
||||
beforeTypes = instantiatorSpec.before.map(type => `${base}<${type}>`)
|
||||
}
|
||||
if (template.before) {
|
||||
for (const beforeTmpl of template.before) {
|
||||
beforeTypes.push(
|
||||
substituteInSig(beforeTmpl, theTemplateParam, instantiator))
|
||||
}
|
||||
}
|
||||
if (beforeTypes.length > 0) {
|
||||
newTypeSpec.before = beforeTypes
|
||||
}
|
||||
newTypeSpec.test = template.test(instantiatorSpec.test)
|
||||
if (template.from) {
|
||||
newTypeSpec.from = {}
|
||||
for (let source in template.from) {
|
||||
source = substituteInSig(source, theTemplateParam, instantiator)
|
||||
const usesFromParam = false
|
||||
for (const word of source.split(/[<>]/)) {
|
||||
if (word === templateFromParam) {
|
||||
usesFromParam = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (usesFromParam) {
|
||||
for (const iFrom in instantiatorSpec.from) {
|
||||
const finalSource = substituteInSig(
|
||||
source, templateFromParam, iFrom)
|
||||
newTypeSpec[finalSource] = template.from[source](
|
||||
instantiatorSpec.from[iFrom])
|
||||
}
|
||||
} else {
|
||||
newTypeSpec[source] = template.from[source]
|
||||
}
|
||||
}
|
||||
}
|
||||
this.installType(wantsType, newTypeSpec)
|
||||
return wantsType
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user