feat(PocomathInstance): Add subtypesOf and isSubtypeOf methods

Note this involves refeactoring the internal subtype tracker to keep
  subtypes in a list sorted in topological order.
This commit is contained in:
Glen Whitney 2022-08-12 09:10:53 -07:00
parent 340dbd436e
commit dc6921e768
2 changed files with 39 additions and 11 deletions

View File

@ -30,9 +30,12 @@ export default class PocomathInstance {
'install',
'installType',
'instantiateTemplate',
'isSubtypeOf',
'joinTypes',
'name',
'returnTypeOf',
'self',
'subtypesOf',
'Templates',
'typeOf',
'Types',
@ -56,7 +59,8 @@ export default class PocomathInstance {
this.Templates = {}
// The actual type testing functions
this._typeTests = {}
this._subtypes = {} // For each type, gives all of its (in)direct subtypes
// For each type, gives all of its (in)direct subtypes in topo order:
this._subtypes = {}
/* The following gives for each type, a set of all types that could
* match in typed-function's dispatch algorithm before the given type.
* This is important because if we instantiate a template, we must
@ -334,14 +338,14 @@ export default class PocomathInstance {
this._typeTests[type] = testFn
this._typed.addTypes([{name: type, test: testFn}], beforeType)
this.Types[type] = spec
this._subtypes[type] = new Set()
this._subtypes[type] = []
this._priorTypes[type] = new Set()
// Update all the subtype sets of supertypes up the chain
let nextSuper = spec.refines
while (nextSuper) {
this._invalidateDependents(':' + nextSuper)
this._priorTypes[nextSuper].add(type)
this._subtypes[nextSuper].add(type)
this._addSubtypeTo(nextSuper, type)
nextSuper = this.Types[nextSuper].refines
}
/* Now add conversions to this type */
@ -369,8 +373,8 @@ export default class PocomathInstance {
for (const fromtype in this.Types[to].from) {
if (type == fromtype
|| (fromtype in this._subtypes
&& this._subtypes[fromtype].has(type))) {
if (spec.refines == to || spec.refines in this._subtypes[to]) {
&& this.isSubtypeOf(type, fromtype))) {
if (spec.refines == to || this.isSubtypeOf(spec.refines,to)) {
throw new SyntaxError(
`Conversion of ${type} to its supertype ${to} disallowed.`)
}
@ -398,8 +402,30 @@ export default class PocomathInstance {
this._installFunctions({typeOf: imp})
}
/* Returns the most refined type of all the types in the array, with
* '' standing for the empty type for convenience. If the second
_addSubtypeTo(sup, sub) {
if (this.isSubtypeOf(sub, sup)) return
const supSubs = this._subtypes[sup]
let i
for (i = 0; i < supSubs.length; ++i) {
if (this.isSubtypeOf(sub, supSubs[i])) break
}
supSubs.splice(i, 0, sub)
}
/* Returns true is typeA is a subtype of type B */
isSubtypeOf(typeA, typeB) {
return this._subtypes[typeB].includes(typeA)
}
/* Returns a list of the subtypes of a given type, in topological sorted
* order (i.e, no type on the list contains one that comes after it).
*/
subtypesOf(type) {
// HERE! For this to work, have to maintain subtypes as a sorted list.
return this._subtypes[type] // should we clone?
}
/* Returns the most refined type containing all the types in the array,
* with '' standing for the empty type for convenience. If the second
* argument `convert` is true, a convertible type is considered a
* a subtype (defaults to false).
*/
@ -418,19 +444,20 @@ export default class PocomathInstance {
if (typeA === 'ground' || typeB === 'ground') return 'ground'
if (typeA === typeB) return typeA
const subber = convert ? this._priorTypes : this._subtypes
if (subber[typeB].has(typeA)) return typeB
const pick = convert ? 'has' : 'includes'
if (subber[typeB][pick](typeA)) return typeB
/* OK, so we need the most refined supertype of A that contains B:
*/
let nextSuper = typeA
while (nextSuper) {
if (subber[nextSuper].has(typeB)) return nextSuper
if (subber[nextSuper][pick](typeB)) return nextSuper
nextSuper = this.Types[nextSuper].refines
}
/* And if conversions are allowed, we have to search the other way too */
if (convert) {
nextSuper = typeB
while (nextSuper) {
if (subber[nextSuper].has(typeA)) return nextSuper
if (subber[nextSuper][pick](typeA)) return nextSuper
nextSuper = this.Types[nextSuper].refines
}
}
@ -1132,7 +1159,7 @@ export default class PocomathInstance {
break
}
if (myType === otherType
|| this._subtypes[otherType].has(myType)) {
|| this.isSubtypeOf(myType, otherType)) {
continue
}
if (otherType in this.Templates) {

View File

@ -22,6 +22,7 @@ describe('The default full pocomath instance "math"', () => {
it('can determine the return types of operations', () => {
assert.strictEqual(math.returnTypeOf('negate', 'number'), 'number')
assert.strictEqual(math.returnTypeOf('negate', 'NumInt'), 'NumInt')
})
it('can subtract numbers', () => {