refactor: convert grammar predicate detection to spec table

Replace 21-item hardcoded isKeywordThatCanBePredicate array with
isPredicateName() from PredicateSpec (+ 'size' special case for
fluent API FOL bodies).

Replace 24-item parseRelation relationKinds array with
collectSpatialPredicateNames() from spec. Error message now also
auto-derives from the spec.

595 SDK + 57 E2E tests pass.
This commit is contained in:
John Dvorak
2026-05-22 12:51:20 -07:00
parent 2e27693278
commit 283ab1b39f
+7 -18
View File
@@ -27,6 +27,8 @@ import type {
ToleranceLiteralNode,
} from 'imhotep-core'
import { isPredicateName, collectSpatialPredicateNames } from 'imhotep-core'
import type { Token } from './lexer.js'
// ---------------------------------------------------------------------------
@@ -658,16 +660,9 @@ export class GrammarParser {
private isKeywordThatCanBePredicate(): boolean {
const kind = this.currentToken().kind
const predicateKinds: Token['kind'][] = [
'leftOf', 'rightOf', 'above', 'below',
'alignedWith', 'leftAlignedWith', 'rightAlignedWith', 'topAlignedWith', 'bottomAlignedWith',
'centeredWithin', 'inside', 'contains', 'overlaps', 'intersects', 'touches', 'separatedFrom', 'hasGap',
// Spatial aliases
'beside', 'nextTo', 'adjacent', 'touching', 'near', 'under', 'within',
// Size predicates that can appear in FOL formula bodies
'width', 'height', 'size', 'between',
]
return predicateKinds.includes(kind)
// 'size' is a fluent API keyword for size assertions in FOL bodies;
// all other predicate names derive from the spec table.
return isPredicateName(kind) || kind === 'size'
}
// -------------------------------------------------------------------------
@@ -1142,13 +1137,7 @@ export class GrammarParser {
)
}
const relationKinds: Array<Token['kind']> = [
'leftOf', 'rightOf', 'above', 'below',
'alignedWith', 'leftAlignedWith', 'rightAlignedWith', 'topAlignedWith', 'bottomAlignedWith',
'centeredWithin', 'inside', 'contains', 'overlaps', 'intersects', 'touches', 'separatedFrom', 'hasGap',
// Spatial aliases
'beside', 'nextTo', 'adjacent', 'touching', 'near', 'under', 'within',
]
const relationKinds = collectSpatialPredicateNames() as Array<Token['kind']>
for (const kind of relationKinds) {
if (this.match(kind)) {
@@ -1156,7 +1145,7 @@ export class GrammarParser {
}
}
throw this.error(`Expected relation (leftOf, rightOf, above, below, alignedWith, centeredWithin, inside, contains, overlaps, intersects, touches, separatedFrom, hasGap, beside, nextTo, adjacent, touching, near, under, within)`)
throw this.error(`Expected relation (${relationKinds.join(', ')})`)
}
// -------------------------------------------------------------------------