commonMain.at.asitplus.jsonpath.implementation.AntlrJsonPathSemanticAnalyzerVisitor.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jsonpath4k Show documentation
Show all versions of jsonpath4k Show documentation
Kotlin Multiplatform library for using Json Paths as specified in [RFC9535](https://datatracker.ietf.org/doc/rfc9535/)
The newest version!
package at.asitplus.jsonpath.implementation
import at.asitplus.jsonpath.core.FilterPredicate
import at.asitplus.jsonpath.core.JsonPathFilterExpressionType
import at.asitplus.jsonpath.core.JsonPathFilterExpressionValue
import at.asitplus.jsonpath.core.JsonPathFunctionExtension
import at.asitplus.jsonpath.core.JsonPathSelector
import at.asitplus.jsonpath.core.JsonPathSelectorQuery
import at.asitplus.jsonpath.generated.JsonPathParser
import at.asitplus.jsonpath.generated.JsonPathParserBaseVisitor
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.booleanOrNull
import kotlinx.serialization.json.doubleOrNull
import kotlinx.serialization.json.longOrNull
import org.antlr.v4.kotlinruntime.ParserRuleContext
import org.antlr.v4.kotlinruntime.tree.TerminalNode
/**
* specification: https://datatracker.ietf.org/doc/rfc9535/
* date: 2024-02
* section 2.4.3: Well-Typedness of Function Expressions
*
* This class builds an abstract syntax tree where the nodes contain the logic necessary to be evaluated against an input.
*/
internal class AntlrJsonPathSemanticAnalyzerVisitor(
private val errorListener: AntlrJsonPathSemanticAnalyzerErrorListener?,
private val functionExtensionRetriever: (String) -> JsonPathFunctionExtension<*>?,
) : JsonPathParserBaseVisitor>() {
override fun defaultResult(): AbstractSyntaxTree {
return AbstractSyntaxTree(context = null, value = JsonPathExpression.NoType)
}
override fun visitTerminal(node: TerminalNode): AbstractSyntaxTree {
return AbstractSyntaxTree(token = node.symbol, value = JsonPathExpression.NoType)
}
override fun aggregateResult(
aggregate: AbstractSyntaxTree,
nextResult: AbstractSyntaxTree
): AbstractSyntaxTree {
val children = (aggregate.children) + nextResult
return AbstractSyntaxTree(
context = null,
value = if (children.any { it.value is JsonPathExpression.ErrorType }) {
JsonPathExpression.ErrorType
} else {
when (aggregate.value) {
is JsonPathExpression.ErrorType -> {
JsonPathExpression.ErrorType
}
is JsonPathExpression.NoType -> {
nextResult.value
}
else -> if (nextResult.value is JsonPathExpression.NoType) {
// don't override a value with no value
aggregate.value
} else {
// this is not generalizable anyway and needs to be handled for each node explicitly
nextResult.value
}
}
},
children = children
)
}
// queries
override fun visitJsonpath_query(ctx: JsonPathParser.Jsonpath_queryContext): AbstractSyntaxTree {
return QueryNodeBuilder(
context = ctx,
contextSelectorNode = visitRootIdentifier(ctx.rootIdentifier()),
selectorSegmentTrees = ctx.segments().segment().map { visitSegment(it) }
).build()
}
override fun visitRel_query(ctx: JsonPathParser.Rel_queryContext): AbstractSyntaxTree {
return QueryNodeBuilder(
context = ctx,
contextSelectorNode = visitCurrentNodeIdentifier(ctx.currentNodeIdentifier()),
selectorSegmentTrees = ctx.segments().segment().map { visitSegment(it) }
).build()
}
override fun visitAbs_singular_query(ctx: JsonPathParser.Abs_singular_queryContext): AbstractSyntaxTree {
return QueryNodeBuilder(
context = ctx,
contextSelectorNode = visitRootIdentifier(ctx.rootIdentifier()),
selectorSegmentTrees = ctx.singular_query_segments().singular_query_segment().map {
visitSingular_query_segment(it)
},
).build()
}
override fun visitRel_singular_query(ctx: JsonPathParser.Rel_singular_queryContext): AbstractSyntaxTree {
return QueryNodeBuilder(
context = ctx,
contextSelectorNode = visitCurrentNodeIdentifier(ctx.currentNodeIdentifier()),
selectorSegmentTrees = ctx.singular_query_segments().singular_query_segment().map {
visitSingular_query_segment(it)
},
).build()
}
// selectors
override fun visitRootIdentifier(ctx: JsonPathParser.RootIdentifierContext): AbstractSyntaxTree {
return AbstractSyntaxTree(
context = ctx,
value = JsonPathExpression.SelectorExpression(JsonPathSelector.RootSelector)
)
}
override fun visitCurrentNodeIdentifier(ctx: JsonPathParser.CurrentNodeIdentifierContext): AbstractSyntaxTree {
return AbstractSyntaxTree(
context = ctx,
value = JsonPathExpression.SelectorExpression(JsonPathSelector.CurrentNodeSelector),
)
}
override fun visitMemberNameShorthand(ctx: JsonPathParser.MemberNameShorthandContext): AbstractSyntaxTree {
return AbstractSyntaxTree(
context = ctx,
value = JsonPathExpression.SelectorExpression(
JsonPathSelector.MemberSelector(ctx.MEMBER_NAME_SHORTHAND().text),
),
)
}
override fun visitName_selector(ctx: JsonPathParser.Name_selectorContext): AbstractSyntaxTree {
return AbstractSyntaxTree(
context = ctx,
value = JsonPathExpression.SelectorExpression(
JsonPathSelector.MemberSelector(
ctx.stringLiteral().toUnescapedString()
)
)
)
}
override fun visitIndex_selector(ctx: JsonPathParser.Index_selectorContext): AbstractSyntaxTree {
return AbstractSyntaxTree(
context = ctx,
value = JsonPathExpression.SelectorExpression(
JsonPathSelector.IndexSelector(
ctx.int().INT().text.toInt()
)
)
)
}
override fun visitSlice_selector(ctx: JsonPathParser.Slice_selectorContext): AbstractSyntaxTree {
return AbstractSyntaxTree(
context = ctx,
value = JsonPathExpression.SelectorExpression(
JsonPathSelector.SliceSelector(
startInclusive = ctx.start()?.text?.toInt(),
endExclusive = ctx.end()?.text?.toInt(),
step = ctx.step()?.text?.toInt(),
)
)
)
}
override fun visitWildcardSelector(ctx: JsonPathParser.WildcardSelectorContext): AbstractSyntaxTree {
return AbstractSyntaxTree(
context = ctx,
value = JsonPathExpression.SelectorExpression(
JsonPathSelector.WildCardSelector
)
)
}
override fun visitDescendant_segment(ctx: JsonPathParser.Descendant_segmentContext): AbstractSyntaxTree {
val child = ctx.bracketed_selection()?.let { visitBracketed_selection(it) }
?: ctx.memberNameShorthand()?.let { visitMemberNameShorthand(it) }
?: ctx.wildcardSelector()?.let { visitWildcardSelector(it) }
val childValue = child?.value
return AbstractSyntaxTree(
context = ctx,
value = if (childValue is JsonPathExpression.SelectorExpression) {
JsonPathExpression.SelectorExpression(
JsonPathSelector.DescendantSelector(
childValue.selector
)
)
} else JsonPathExpression.ErrorType,
children = listOfNotNull(child)
)
}
override fun visitBracketed_selection(ctx: JsonPathParser.Bracketed_selectionContext): AbstractSyntaxTree {
val children = ctx.selector().map {
visitSelector(it)
}
val selectorExpressionChildren = children.map {
it.value
}.filterIsInstance()
return AbstractSyntaxTree(
context = ctx,
value = if (selectorExpressionChildren.size == children.size) {
JsonPathExpression.SelectorExpression(
JsonPathSelector.BracketedSelector(
selectorExpressionChildren.map { it.selector }
)
)
} else JsonPathExpression.ErrorType,
children = children
)
}
override fun visitFilter_selector(ctx: JsonPathParser.Filter_selectorContext): AbstractSyntaxTree {
val logicalExpressionNode = visitLogical_expr(ctx.logical_expr())
return AbstractSyntaxTree(
context = ctx,
value = if (logicalExpressionNode.value is JsonPathExpression.FilterExpression.LogicalExpression) {
JsonPathExpression.SelectorExpression(
JsonPathSelector.FilterSelector(
object : FilterPredicate {
override fun invoke(
currentNode: JsonElement,
rootNode: JsonElement
): Boolean = logicalExpressionNode.value.evaluate(
JsonPathExpressionEvaluationContext(
currentNode = currentNode,
rootNode = rootNode,
)
).isTrue
}
)
)
} else JsonPathExpression.ErrorType,
children = listOf(logicalExpressionNode)
)
}
// logical expressions
override fun visitLogical_or_expr(ctx: JsonPathParser.Logical_or_exprContext): AbstractSyntaxTree {
val children = ctx.logical_and_expr().map {
visitLogical_and_expr(it)
}
val logicalChildrenValues = children.map { it.value }
.filterIsInstance()
return AbstractSyntaxTree(
context = ctx,
value = if (logicalChildrenValues.size == children.size) {
JsonPathExpression.FilterExpression.LogicalExpression { context ->
JsonPathFilterExpressionValue.LogicalTypeValue(
logicalChildrenValues.any {
it.evaluate(context).isTrue
}
)
}
} else JsonPathExpression.ErrorType,
children = children,
)
}
override fun visitLogical_and_expr(ctx: JsonPathParser.Logical_and_exprContext): AbstractSyntaxTree {
val children = ctx.basic_expr().map {
visitBasic_expr(it)
}
val logicalChildrenValues = children.map { it.value }
.filterIsInstance()
return AbstractSyntaxTree(
context = ctx,
value = if (logicalChildrenValues.size == children.size) {
JsonPathExpression.FilterExpression.LogicalExpression { context ->
JsonPathFilterExpressionValue.LogicalTypeValue(
logicalChildrenValues.all {
it.evaluate(context).isTrue
}
)
}
} else JsonPathExpression.ErrorType,
children = children,
)
}
override fun visitParen_expr(ctx: JsonPathParser.Paren_exprContext): AbstractSyntaxTree {
val isNotNegated = ctx.LOGICAL_NOT_OP()?.let { false } ?: true
val child = visitLogical_expr(ctx.logical_expr())
return AbstractSyntaxTree(
context = ctx,
value = if (child.value is JsonPathExpression.FilterExpression.LogicalExpression) {
JsonPathExpression.FilterExpression.LogicalExpression { context ->
JsonPathFilterExpressionValue.LogicalTypeValue(
child.value.evaluate(context).isTrue == isNotNegated
)
}
} else JsonPathExpression.ErrorType,
children = listOf(child),
)
}
override fun visitTest_expr(ctx: JsonPathParser.Test_exprContext): AbstractSyntaxTree {
val isNotNegated = (ctx.LOGICAL_NOT_OP() == null)
return ctx.filter_query()?.let {
val filterQueryTree = visitFilter_query(it)
val filterQueryValue = filterQueryTree.value
AbstractSyntaxTree(
context = ctx,
value = if (filterQueryValue is JsonPathExpression.FilterExpression.NodesExpression.FilterQueryExpression) {
JsonPathExpression.FilterExpression.LogicalExpression { context ->
JsonPathFilterExpressionValue.LogicalTypeValue(
filterQueryValue.jsonPathQuery.invoke(
currentNode = context.currentNode,
rootNode = context.rootNode
).isNotEmpty() == isNotNegated
)
}
} else JsonPathExpression.ErrorType,
children = listOf(filterQueryTree)
)
} ?: ctx.function_expr()?.let { functionExpressionContext ->
/**
* specification: https://datatracker.ietf.org/doc/rfc9535/
* date: 2024-02
* section 2.4.3: Well-Typedness of Function Expressions
*
* As a test-expr in a logical expression:
* The function's declared result type is LogicalType or (giving
* rise to conversion as per Section 2.4.2) NodesType.
*/
val child = visitFunction_expr(functionExpressionContext)
val functionResultValue = child.value
AbstractSyntaxTree(
context = ctx,
value = when (functionResultValue) {
is JsonPathExpression.FilterExpression.ValueExpression -> {
JsonPathExpression.ErrorType.also {
errorListener?.invalidFunctionExtensionForTestExpression(
functionExpressionContext.FUNCTION_NAME().text,
)
}
}
is JsonPathExpression.FilterExpression.LogicalExpression -> {
JsonPathExpression.FilterExpression.LogicalExpression { context ->
JsonPathFilterExpressionValue.LogicalTypeValue(
functionResultValue.evaluate(context).isTrue == isNotNegated
)
}
}
is JsonPathExpression.FilterExpression.NodesExpression.NodesFunctionExpression -> {
JsonPathExpression.FilterExpression.LogicalExpression { context ->
JsonPathFilterExpressionValue.LogicalTypeValue(
functionResultValue.evaluate(context).nodeList.isNotEmpty() == isNotNegated
)
}
}
else -> JsonPathExpression.ErrorType
},
children = listOf(child),
)
} ?: AbstractSyntaxTree(
context = ctx,
value = JsonPathExpression.ErrorType,
).also {
errorListener?.invalidTestExpression(ctx.text)
}
}
override fun visitFunction_expr(ctx: JsonPathParser.Function_exprContext): AbstractSyntaxTree {
val functionArgumentNodes = ctx.function_argument().map {
visitFunction_argument(it)
}
val extension = functionExtensionRetriever.invoke(ctx.FUNCTION_NAME().text)
?: return AbstractSyntaxTree(
context = ctx,
value = JsonPathExpression.ErrorType,
children = functionArgumentNodes,
).also {
errorListener?.unknownFunctionExtension(ctx.FUNCTION_NAME().text)
}
val isArglistSizeConsistent = ctx.function_argument().size == extension.argumentTypes.size
val coercedArgumentExpressions =
functionArgumentNodes.map { it.value }.mapIndexed { index, argumentNode ->
when (extension.argumentTypes.getOrNull(index)) {
/**
* specification: https://datatracker.ietf.org/doc/rfc9535/
* date: 2024-02
* section 2.4.3: Well-Typedness of Function Expressions
*
* * When the declared type of the parameter is LogicalType and the
* argument is one of the following:
*
* - A function expression with declared result type NodesType.
* In this case, the argument is converted to LogicalType as
* per Section 2.4.2.
*/
JsonPathFilterExpressionType.LogicalType -> when (argumentNode) {
is JsonPathExpression.FilterExpression.NodesExpression -> {
JsonPathExpression.FilterExpression.LogicalExpression {
JsonPathFilterExpressionValue.LogicalTypeValue(
argumentNode.evaluate(it).nodeList.isNotEmpty()
)
}
}
else -> argumentNode
}
JsonPathFilterExpressionType.NodesType -> argumentNode
/**
* specification: https://datatracker.ietf.org/doc/rfc9535/
* date: 2024-02
* section 2.4.3: Well-Typedness of Function Expressions
*
* * When the declared type of the parameter is ValueType and the
* argument is one of the following:
*
* - A value expressed as a literal.
*
* - A singular query. In this case:
*
* o If the query results in a nodelist consisting of a
* single node, the argument is the value of the node.
*
* o If the query results in an empty nodelist, the argument
* is the special result Nothing.
*/
JsonPathFilterExpressionType.ValueType -> when (argumentNode) {
is JsonPathExpression.FilterExpression.NodesExpression.FilterQueryExpression.SingularQueryExpression -> {
argumentNode.toValueTypeValue()
}
else -> argumentNode
}
null -> argumentNode
}
}
val coercedArgumentTypes = coercedArgumentExpressions.map {
if (it !is JsonPathExpression.FilterExpression) {
null
} else {
it.expressionType
}
}
val isCoercedArgumentTypesMatching =
coercedArgumentTypes.mapIndexed { index, argumentType ->
argumentType == extension.argumentTypes[index]
}.all {
it
}
val isValidFunctionCall =
isArglistSizeConsistent and isCoercedArgumentTypesMatching
if (isValidFunctionCall == false) {
errorListener?.invalidArglistForFunctionExtension(
functionExtensionName = ctx.FUNCTION_NAME().text,
functionExtensionImplementation = extension,
coercedArgumentTypes = coercedArgumentTypes.zip(
functionArgumentNodes.map {
it.text
}
)
)
}
return AbstractSyntaxTree(
context = ctx,
value = if (isValidFunctionCall) {
val coercedArguments =
coercedArgumentExpressions.filterIsInstance()
when (extension) {
is JsonPathFunctionExtension.LogicalTypeFunctionExtension -> {
JsonPathExpression.FilterExpression.LogicalExpression { context ->
extension.evaluate(coercedArguments.map {
it.evaluate(context)
})
}
}
is JsonPathFunctionExtension.NodesTypeFunctionExtension -> {
JsonPathExpression.FilterExpression.NodesExpression.NodesFunctionExpression { context ->
extension.evaluate(coercedArguments.map {
it.evaluate(context)
})
}
}
is JsonPathFunctionExtension.ValueTypeFunctionExtension -> {
JsonPathExpression.FilterExpression.ValueExpression { context ->
extension.evaluate(coercedArguments.map {
it.evaluate(context)
})
}
}
}
} else {
JsonPathExpression.ErrorType
},
children = functionArgumentNodes,
)
}
override fun visitComparison_expr(ctx: JsonPathParser.Comparison_exprContext): AbstractSyntaxTree {
val firstComparable = visitComparable(ctx.firstComparable().comparable())
val secondComparable = visitComparable(ctx.secondComparable().comparable())
val children = listOf(firstComparable, secondComparable)
val firstValue =
if (firstComparable.value is JsonPathExpression.FilterExpression.NodesExpression.FilterQueryExpression.SingularQueryExpression) {
firstComparable.value.toValueTypeValue()
} else firstComparable.value
val secondValue =
if (secondComparable.value is JsonPathExpression.FilterExpression.NodesExpression.FilterQueryExpression.SingularQueryExpression) {
secondComparable.value.toValueTypeValue()
} else secondComparable.value
listOf(
ctx.firstComparable().comparable() to firstValue,
ctx.secondComparable().comparable() to secondValue,
).forEach { (comparableContext, value) ->
val functionExpressionContext = comparableContext.function_expr()
when {
value is JsonPathExpression.ErrorType -> {}
functionExpressionContext != null -> {
/**
* specification: https://datatracker.ietf.org/doc/rfc9535/
* date: 2024-02
* section 2.4.3: Well-Typedness of Function Expressions
*
* As a comparable in a comparison:
* The function's declared result type is ValueType.
*/
if (value !is JsonPathExpression.FilterExpression.ValueExpression) {
errorListener?.invalidFunctionExtensionForComparable(
functionExpressionContext.FUNCTION_NAME().text,
)
}
}
}
}
return AbstractSyntaxTree(
context = ctx,
value = if (firstValue !is JsonPathExpression.FilterExpression.ValueExpression) {
JsonPathExpression.ErrorType
} else if (secondValue !is JsonPathExpression.FilterExpression.ValueExpression) {
JsonPathExpression.ErrorType
} else comparisonExpression(
firstComparable = firstValue.evaluate,
secondComparable = secondValue.evaluate,
ctx.comparisonOp(),
),
children = children,
)
}
private fun comparisonExpression(
firstComparable: (JsonPathExpressionEvaluationContext) -> JsonPathFilterExpressionValue.ValueTypeValue,
secondComparable: (JsonPathExpressionEvaluationContext) -> JsonPathFilterExpressionValue.ValueTypeValue,
comparisonOpContext: JsonPathParser.ComparisonOpContext,
): JsonPathExpression = comparisonOpContext.let {
when {
it.COMPARISON_OP_EQUALS() != null -> JsonPathExpression.FilterExpression.LogicalExpression { context ->
JsonPathFilterExpressionValue.LogicalTypeValue(
this.evaluateComparisonEquals(
firstComparable.invoke(context),
secondComparable.invoke(context),
)
)
}
it.COMPARISON_OP_SMALLER_THAN() != null -> JsonPathExpression.FilterExpression.LogicalExpression { context ->
JsonPathFilterExpressionValue.LogicalTypeValue(
evaluateComparisonSmallerThan(
firstComparable.invoke(context),
secondComparable.invoke(context),
)
)
}
it.COMPARISON_OP_NOT_EQUALS() != null -> JsonPathExpression.FilterExpression.LogicalExpression { context ->
JsonPathFilterExpressionValue.LogicalTypeValue(
!this.evaluateComparisonEquals(
firstComparable.invoke(context),
secondComparable.invoke(context),
)
)
}
it.COMPARISON_OP_SMALLER_THAN_OR_EQUALS() != null -> JsonPathExpression.FilterExpression.LogicalExpression { context ->
JsonPathFilterExpressionValue.LogicalTypeValue(
evaluateComparisonSmallerThan(
firstComparable.invoke(context),
secondComparable.invoke(context),
) or this.evaluateComparisonEquals(
firstComparable.invoke(context),
secondComparable.invoke(context),
)
)
}
it.COMPARISON_OP_GREATER_THAN() != null -> JsonPathExpression.FilterExpression.LogicalExpression { context ->
JsonPathFilterExpressionValue.LogicalTypeValue(
evaluateComparisonSmallerThan(
secondComparable.invoke(context),
firstComparable.invoke(context),
)
)
}
it.COMPARISON_OP_GREATER_THAN_OR_EQUALS() != null -> JsonPathExpression.FilterExpression.LogicalExpression { context ->
JsonPathFilterExpressionValue.LogicalTypeValue(
evaluateComparisonSmallerThan(
secondComparable.invoke(context),
firstComparable.invoke(context),
) or this.evaluateComparisonEquals(
firstComparable.invoke(context),
secondComparable.invoke(context),
)
)
}
else -> JsonPathExpression.ErrorType
}
}
private fun evaluateComparisonEquals(
firstValue: JsonPathFilterExpressionValue.ValueTypeValue,
secondValue: JsonPathFilterExpressionValue.ValueTypeValue,
): Boolean {
if (firstValue is JsonPathFilterExpressionValue.ValueTypeValue.Nothing) {
return secondValue is JsonPathFilterExpressionValue.ValueTypeValue.Nothing
}
if (secondValue is JsonPathFilterExpressionValue.ValueTypeValue.Nothing) {
return false
}
return evaluateComparisonEqualsUnpacked(
firstValue,
secondValue,
)
}
private fun evaluateComparisonEqualsUnpacked(
first: JsonPathFilterExpressionValue.ValueTypeValue,
second: JsonPathFilterExpressionValue.ValueTypeValue,
): Boolean = when (first) {
is JsonPathFilterExpressionValue.ValueTypeValue.JsonValue -> {
if (second !is JsonPathFilterExpressionValue.ValueTypeValue.JsonValue) {
false
} else when (first.jsonElement) {
JsonNull -> {
second.jsonElement == JsonNull
}
is JsonPrimitive -> {
if (second.jsonElement is JsonPrimitive) {
when {
first.jsonElement.isString != second.jsonElement.isString -> false
first.jsonElement.isString -> first.jsonElement.content == second.jsonElement.content
else -> first.jsonElement.booleanOrNull?.let { it == second.jsonElement.booleanOrNull }
?: first.jsonElement.longOrNull?.let { it == second.jsonElement.longOrNull }
?: first.jsonElement.doubleOrNull?.let { it == second.jsonElement.doubleOrNull }
?: false
}
} else false
}
is JsonArray -> {
if (second.jsonElement is JsonArray) {
(first.jsonElement.size == second.jsonElement.size) and first.jsonElement.mapIndexed { index, it ->
index to it
}.all {
this.evaluateComparisonEqualsUnpacked(
JsonPathFilterExpressionValue.ValueTypeValue.JsonValue(it.second),
JsonPathFilterExpressionValue.ValueTypeValue.JsonValue(second.jsonElement[it.first]),
)
}
} else false
}
is JsonObject -> {
if (second.jsonElement is JsonObject) {
(first.jsonElement.keys == second.jsonElement.keys) and first.jsonElement.entries.all {
this.evaluateComparisonEqualsUnpacked(
JsonPathFilterExpressionValue.ValueTypeValue.JsonValue(it.value),
JsonPathFilterExpressionValue.ValueTypeValue.JsonValue(
second.jsonElement[it.key]
?: throw MissingKeyException(
jsonObject = second.jsonElement,
key = it.key
)
)
)
}
} else false
}
}
}
JsonPathFilterExpressionValue.ValueTypeValue.Nothing -> second == JsonPathFilterExpressionValue.ValueTypeValue.Nothing
}
private fun evaluateComparisonSmallerThan(
firstValue: JsonPathFilterExpressionValue.ValueTypeValue,
secondValue: JsonPathFilterExpressionValue.ValueTypeValue,
): Boolean {
if (firstValue is JsonPathFilterExpressionValue.ValueTypeValue.Nothing) {
return false
}
if (secondValue is JsonPathFilterExpressionValue.ValueTypeValue.Nothing) {
return false
}
return evaluateComparisonUnpackedSmallerThan(
firstValue,
secondValue,
)
}
private fun evaluateComparisonUnpackedSmallerThan(
first: JsonPathFilterExpressionValue,
second: JsonPathFilterExpressionValue,
): Boolean {
if (first !is JsonPathFilterExpressionValue.ValueTypeValue.JsonValue) {
return false
}
if (second !is JsonPathFilterExpressionValue.ValueTypeValue.JsonValue) {
return false
}
if (first.jsonElement !is JsonPrimitive) {
return false
}
if (second.jsonElement !is JsonPrimitive) {
return false
}
if (first.jsonElement.isString != second.jsonElement.isString) {
return false
}
if (first.jsonElement.isString) {
return first.jsonElement.content < second.jsonElement.content
}
return first.jsonElement.longOrNull?.let { firstValue ->
second.jsonElement.longOrNull?.let { firstValue < it }
?: second.jsonElement.doubleOrNull?.let { firstValue < it }
} ?: first.jsonElement.doubleOrNull?.let { firstValue ->
second.jsonElement.longOrNull?.let { firstValue < it }
?: second.jsonElement.doubleOrNull?.let { firstValue < it }
} ?: false
}
// primitives
override fun visitStringLiteral(ctx: JsonPathParser.StringLiteralContext): AbstractSyntaxTree {
return AbstractSyntaxTree(
context = ctx,
value = JsonPathExpression.FilterExpression.ValueExpression {
JsonPathFilterExpressionValue.ValueTypeValue.JsonValue(
JsonPrimitive(ctx.toUnescapedString())
)
},
)
}
override fun visitNumber(ctx: JsonPathParser.NumberContext): AbstractSyntaxTree {
return AbstractSyntaxTree(
context = ctx,
value = JsonPathExpression.FilterExpression.ValueExpression {
JsonPathFilterExpressionValue.ValueTypeValue.JsonValue(
JsonPrimitive(ctx.NUMBER().text.toDouble())
)
},
)
}
override fun visitInt(ctx: JsonPathParser.IntContext): AbstractSyntaxTree {
return AbstractSyntaxTree(
context = ctx,
value = JsonPathExpression.FilterExpression.ValueExpression {
JsonPathFilterExpressionValue.ValueTypeValue.JsonValue(
JsonPrimitive(ctx.INT().text.toInt())
)
},
)
}
override fun visitTrue(ctx: JsonPathParser.TrueContext): AbstractSyntaxTree {
return AbstractSyntaxTree(
context = ctx,
value = JsonPathExpression.FilterExpression.ValueExpression {
JsonPathFilterExpressionValue.ValueTypeValue.JsonValue(
JsonPrimitive(true)
)
},
)
}
override fun visitFalse(ctx: JsonPathParser.FalseContext): AbstractSyntaxTree {
return AbstractSyntaxTree(
context = ctx,
value = JsonPathExpression.FilterExpression.ValueExpression {
JsonPathFilterExpressionValue.ValueTypeValue.JsonValue(
JsonPrimitive(false)
)
},
)
}
override fun visitNull(ctx: JsonPathParser.NullContext): AbstractSyntaxTree {
return AbstractSyntaxTree(
context = ctx,
value = JsonPathExpression.FilterExpression.ValueExpression {
JsonPathFilterExpressionValue.ValueTypeValue.JsonValue(
JsonNull
)
},
)
}
}
internal class QueryNodeBuilder(
private val context: ParserRuleContext,
private val contextSelectorNode: AbstractSyntaxTree,
private val selectorSegmentTrees: List>
) {
fun build(): AbstractSyntaxTree {
val children = listOf(
contextSelectorNode
) + selectorSegmentTrees
val childrenValues = children.map { it.value }
val childrenSelectors =
childrenValues.filterIsInstance()
val value = if (childrenValues.size != childrenSelectors.size) {
JsonPathExpression.ErrorType
} else {
val query = JsonPathSelectorQuery(childrenSelectors.map { it.selector })
if (query.isSingularQuery) {
JsonPathExpression.FilterExpression.NodesExpression.FilterQueryExpression.SingularQueryExpression(
query
)
} else {
JsonPathExpression.FilterExpression.NodesExpression.FilterQueryExpression.NonSingularQueryExpression(
query
)
}
}
return AbstractSyntaxTree(
context = context,
value = value,
children = children,
)
}
}