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:
parent
340dbd436e
commit
dc6921e768
@ -30,9 +30,12 @@ export default class PocomathInstance {
|
|||||||
'install',
|
'install',
|
||||||
'installType',
|
'installType',
|
||||||
'instantiateTemplate',
|
'instantiateTemplate',
|
||||||
|
'isSubtypeOf',
|
||||||
'joinTypes',
|
'joinTypes',
|
||||||
'name',
|
'name',
|
||||||
|
'returnTypeOf',
|
||||||
'self',
|
'self',
|
||||||
|
'subtypesOf',
|
||||||
'Templates',
|
'Templates',
|
||||||
'typeOf',
|
'typeOf',
|
||||||
'Types',
|
'Types',
|
||||||
@ -56,7 +59,8 @@ export default class PocomathInstance {
|
|||||||
this.Templates = {}
|
this.Templates = {}
|
||||||
// The actual type testing functions
|
// The actual type testing functions
|
||||||
this._typeTests = {}
|
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
|
/* The following gives for each type, a set of all types that could
|
||||||
* match in typed-function's dispatch algorithm before the given type.
|
* match in typed-function's dispatch algorithm before the given type.
|
||||||
* This is important because if we instantiate a template, we must
|
* This is important because if we instantiate a template, we must
|
||||||
@ -334,14 +338,14 @@ export default class PocomathInstance {
|
|||||||
this._typeTests[type] = testFn
|
this._typeTests[type] = testFn
|
||||||
this._typed.addTypes([{name: type, test: testFn}], beforeType)
|
this._typed.addTypes([{name: type, test: testFn}], beforeType)
|
||||||
this.Types[type] = spec
|
this.Types[type] = spec
|
||||||
this._subtypes[type] = new Set()
|
this._subtypes[type] = []
|
||||||
this._priorTypes[type] = new Set()
|
this._priorTypes[type] = new Set()
|
||||||
// Update all the subtype sets of supertypes up the chain
|
// Update all the subtype sets of supertypes up the chain
|
||||||
let nextSuper = spec.refines
|
let nextSuper = spec.refines
|
||||||
while (nextSuper) {
|
while (nextSuper) {
|
||||||
this._invalidateDependents(':' + nextSuper)
|
this._invalidateDependents(':' + nextSuper)
|
||||||
this._priorTypes[nextSuper].add(type)
|
this._priorTypes[nextSuper].add(type)
|
||||||
this._subtypes[nextSuper].add(type)
|
this._addSubtypeTo(nextSuper, type)
|
||||||
nextSuper = this.Types[nextSuper].refines
|
nextSuper = this.Types[nextSuper].refines
|
||||||
}
|
}
|
||||||
/* Now add conversions to this type */
|
/* Now add conversions to this type */
|
||||||
@ -369,8 +373,8 @@ export default class PocomathInstance {
|
|||||||
for (const fromtype in this.Types[to].from) {
|
for (const fromtype in this.Types[to].from) {
|
||||||
if (type == fromtype
|
if (type == fromtype
|
||||||
|| (fromtype in this._subtypes
|
|| (fromtype in this._subtypes
|
||||||
&& this._subtypes[fromtype].has(type))) {
|
&& this.isSubtypeOf(type, fromtype))) {
|
||||||
if (spec.refines == to || spec.refines in this._subtypes[to]) {
|
if (spec.refines == to || this.isSubtypeOf(spec.refines,to)) {
|
||||||
throw new SyntaxError(
|
throw new SyntaxError(
|
||||||
`Conversion of ${type} to its supertype ${to} disallowed.`)
|
`Conversion of ${type} to its supertype ${to} disallowed.`)
|
||||||
}
|
}
|
||||||
@ -398,8 +402,30 @@ export default class PocomathInstance {
|
|||||||
this._installFunctions({typeOf: imp})
|
this._installFunctions({typeOf: imp})
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the most refined type of all the types in the array, with
|
_addSubtypeTo(sup, sub) {
|
||||||
* '' standing for the empty type for convenience. If the second
|
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
|
* argument `convert` is true, a convertible type is considered a
|
||||||
* a subtype (defaults to false).
|
* a subtype (defaults to false).
|
||||||
*/
|
*/
|
||||||
@ -418,19 +444,20 @@ export default class PocomathInstance {
|
|||||||
if (typeA === 'ground' || typeB === 'ground') return 'ground'
|
if (typeA === 'ground' || typeB === 'ground') return 'ground'
|
||||||
if (typeA === typeB) return typeA
|
if (typeA === typeB) return typeA
|
||||||
const subber = convert ? this._priorTypes : this._subtypes
|
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:
|
/* OK, so we need the most refined supertype of A that contains B:
|
||||||
*/
|
*/
|
||||||
let nextSuper = typeA
|
let nextSuper = typeA
|
||||||
while (nextSuper) {
|
while (nextSuper) {
|
||||||
if (subber[nextSuper].has(typeB)) return nextSuper
|
if (subber[nextSuper][pick](typeB)) return nextSuper
|
||||||
nextSuper = this.Types[nextSuper].refines
|
nextSuper = this.Types[nextSuper].refines
|
||||||
}
|
}
|
||||||
/* And if conversions are allowed, we have to search the other way too */
|
/* And if conversions are allowed, we have to search the other way too */
|
||||||
if (convert) {
|
if (convert) {
|
||||||
nextSuper = typeB
|
nextSuper = typeB
|
||||||
while (nextSuper) {
|
while (nextSuper) {
|
||||||
if (subber[nextSuper].has(typeA)) return nextSuper
|
if (subber[nextSuper][pick](typeA)) return nextSuper
|
||||||
nextSuper = this.Types[nextSuper].refines
|
nextSuper = this.Types[nextSuper].refines
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1132,7 +1159,7 @@ export default class PocomathInstance {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
if (myType === otherType
|
if (myType === otherType
|
||||||
|| this._subtypes[otherType].has(myType)) {
|
|| this.isSubtypeOf(myType, otherType)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (otherType in this.Templates) {
|
if (otherType in this.Templates) {
|
||||||
|
@ -22,6 +22,7 @@ describe('The default full pocomath instance "math"', () => {
|
|||||||
|
|
||||||
it('can determine the return types of operations', () => {
|
it('can determine the return types of operations', () => {
|
||||||
assert.strictEqual(math.returnTypeOf('negate', 'number'), 'number')
|
assert.strictEqual(math.returnTypeOf('negate', 'number'), 'number')
|
||||||
|
assert.strictEqual(math.returnTypeOf('negate', 'NumInt'), 'NumInt')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can subtract numbers', () => {
|
it('can subtract numbers', () => {
|
||||||
|
Loading…
Reference in New Issue
Block a user