Name matching

This commit is contained in:
Alexander Nozik 2021-01-24 22:11:25 +03:00
parent ac8631e3a0
commit 366d32a04a
2 changed files with 60 additions and 0 deletions

View File

@ -1,5 +1,6 @@
package hep.dataforge.names package hep.dataforge.names
import hep.dataforge.meta.DFExperimental
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.descriptors.PrimitiveKind
@ -39,6 +40,18 @@ public class Name(public val tokens: List<NameToken>) {
public companion object : KSerializer<Name> { public companion object : KSerializer<Name> {
public const val NAME_SEPARATOR: String = "." public const val NAME_SEPARATOR: String = "."
/**
* Match any single token (both body and index)
*/
@DFExperimental
public val MATCH_ANY_TOKEN: NameToken = NameToken("*")
/**
* Token that allows to match the whole tail or the whole head of the name. Must match at least one token.
*/
@DFExperimental
public val MATCH_ALL_TOKEN: NameToken = NameToken("**")
public val EMPTY: Name = Name(emptyList()) public val EMPTY: Name = Name(emptyList())
override val descriptor: SerialDescriptor = override val descriptor: SerialDescriptor =

View File

@ -0,0 +1,47 @@
package hep.dataforge.names
import hep.dataforge.meta.DFExperimental
/**
* Checks if this token matches a given [NameToken]. The match successful if:
* * Token body matches pattern body as a regex
* * Index body matches pattern body as a regex of both are null
*/
@DFExperimental
public fun NameToken.matches(pattern: NameToken): Boolean {
if (pattern == Name.MATCH_ANY_TOKEN) return true
val bodyMatches = body.matches(pattern.body.toRegex())
val indexMatches = (index == null && pattern.index == null) || pattern.index?.let { patternIndex ->
(index ?: "").matches(patternIndex.toRegex())
} ?: false
return bodyMatches && indexMatches
}
/**
* Matches all names in pattern according to [NameToken.matches] rules.
*/
@DFExperimental
public fun Name.matches(pattern: Name): Boolean = when {
pattern.endsWith(Name.MATCH_ALL_TOKEN) -> {
length >= pattern.length
&& Name(tokens.subList(0, pattern.length - 1)).matches(pattern.cutLast())
}
pattern.startsWith(Name.MATCH_ALL_TOKEN) -> {
length >= pattern.length
&& Name(tokens.subList(tokens.size - pattern.length + 1, tokens.size)).matches(pattern.cutFirst())
}
else -> {
tokens.indices.forEach {
val thisToken = tokens.getOrNull(it) ?: return false
if (thisToken == Name.MATCH_ALL_TOKEN) error("Match-all token in the middle of the name is not supported yet")
val patternToken = pattern.tokens.getOrNull(it) ?: return false
if (!thisToken.matches(patternToken)) return false
}
true
}
}
@OptIn(DFExperimental::class)
public fun Name.matches(pattern: String): Boolean = matches(pattern.toName())