feat: Template types #45

Merged
glen merged 10 commits from template_types into main 2022-08-05 12:48:57 +00:00
Showing only changes of commit a743337134 - Show all commits

View File

@ -7,6 +7,7 @@ import {typesOfSignature, subsetOfKeys} from './utils.mjs'
const anySpec = {} // fixed dummy specification of 'any' type const anySpec = {} // fixed dummy specification of 'any' type
const theTemplateParam = 'T' // First pass: only allow this one exact parameter 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 /* Returns a new signature just like sig but with the parameter replaced by
* the type * the type
@ -803,11 +804,91 @@ export default class PocomathInstance {
imps[signature] = does(refs) 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 * instantiations of it for type and all prior types of type are present
* in the instance * in the instance.
*/ */
_ensureTemplateTypes(template, type) { _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
} }
} }