Skip to content

Commit 107909e

Browse files
reid-spencerclaude
andcommitted
Add Finder.findInParents to search ancestor chain for definitions
New companion object method searches the contents of each parent in a hierarchy chain for definitions matching a type parameter. Returns matches paired with their parent chain for PathIdentifier construction. Useful for finding definitions in scope at a given point in the AST hierarchy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8f170b8 commit 107909e

2 files changed

Lines changed: 67 additions & 1 deletion

File tree

language/shared/src/main/scala/com/ossuminc/riddl/language/Finder.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,4 +207,32 @@ object Finder:
207207
val container = SimpleContainer[CV](contents)
208208
Finder[CV](container)
209209
end apply
210+
211+
/** Search the contents of each parent in a hierarchy chain
212+
* for definitions of type `T`. Walks from nearest parent
213+
* to furthest (e.g., Entity → Context → Domain).
214+
*
215+
* @tparam T
216+
* The type of definition to find
217+
* @param parents
218+
* The parent chain to search, ordered nearest to furthest
219+
* @return
220+
* Each matching definition paired with the parents of the
221+
* container it was found in, suitable for constructing a
222+
* [[AST.PathIdentifier]]
223+
*/
224+
def findInParents[T <: RiddlValue: ClassTag](
225+
parents: Parents
226+
): Seq[(T, Parents)] =
227+
val lookingFor = classTag[T].runtimeClass
228+
val buffer = mutable.ArrayBuffer.empty[(T, Parents)]
229+
parents.zipWithIndex.foreach { case (parent, idx) =>
230+
val parentsOfParent = parents.drop(idx + 1)
231+
parent.contents.toSeq.foreach { child =>
232+
if lookingFor.isAssignableFrom(child.getClass) then
233+
buffer += (child.asInstanceOf[T] -> parentsOfParent)
234+
}
235+
}
236+
buffer.toSeq
237+
end findInParents
210238
end Finder

language/shared/src/test/scala/com/ossuminc/riddl/language/SharedFinderTest.scala

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ package com.ossuminc.riddl.language
88

99
import com.ossuminc.riddl.language.{Contents, *}
1010
import com.ossuminc.riddl.utils.pc
11-
import com.ossuminc.riddl.language.AST.{Root, Parents, RootContents}
11+
import com.ossuminc.riddl.language.AST.{Root, Parents, RootContents, Type, Handler, Entity, Context}
1212
import com.ossuminc.riddl.language.parsing.{
1313
AbstractParsingTest,
1414
RiddlParserInput,
@@ -58,6 +58,44 @@ class SharedFinderTest extends AbstractTestingBasis {
5858
s must be(Parents(c, b, a, root))
5959
}
6060
}
61+
"findInParents finds definitions in ancestor chain" in {
62+
val content2 =
63+
"""domain D {
64+
| type DomainCmd is command { ??? }
65+
| context C {
66+
| type CtxCmd is command { ??? }
67+
| entity E {
68+
| type EntCmd is command { ??? }
69+
| handler H { ??? }
70+
| }
71+
| }
72+
|}
73+
|""".stripMargin
74+
val input2 = RiddlParserInput(content2, "findInParentsTest")
75+
val root2 = TopLevelParser.parseInput(input2, true) match
76+
case Left(messages) => fail(messages.justErrors.format)
77+
case Right(r: Root) => r
78+
val dom = root2.domains.head
79+
val ctx = dom.contexts.head
80+
val ent = ctx.entities.head
81+
val handler = ent.handlers.head
82+
// Parents of handler: Entity, Context, Domain, Root
83+
val handlerParents = Parents(ent, ctx, dom, root2)
84+
val found = Finder.findInParents[Type](handlerParents)
85+
// Should find EntCmd in Entity, CtxCmd in Context,
86+
// DomainCmd in Domain — 3 total
87+
found.size must be(3)
88+
val names = found.map(_._1.id.value)
89+
names must contain("EntCmd")
90+
names must contain("CtxCmd")
91+
names must contain("DomainCmd")
92+
// Verify parents: EntCmd's parents should be
93+
// [Context, Domain, Root] (Entity's parents)
94+
val entCmdParents = found.find(
95+
_._1.id.value == "EntCmd"
96+
).get._2
97+
entCmdParents must be(Parents(ctx, dom, root2))
98+
}
6199
"build path map correctly" in {
62100
val a = root.modules.head
63101
val b = a.domains.head

0 commit comments

Comments
 (0)