Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 12 additions & 13 deletions killerbunny/evaluating/evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
StringValue
)
from killerbunny.parsing.function import Nothing, LogicalType, ValueType, FunctionCallNode, FunctionArgument
from killerbunny.parsing.helper import unescape_string_content
from killerbunny.parsing.node_type import ASTNode, ASTNodeType
from killerbunny.parsing.parser_nodes import (
JsonPathQueryNode,
Expand Down Expand Up @@ -136,12 +137,7 @@ def visit_JsonPathQueryNode(self, node: JsonPathQueryNode, context: Context) ->
segment_output: VNodeList = input_nodelist # default, in case there are no segments

segments: RepetitionNode = node.segments
if segments.is_empty():
# no segments in the absolute query, so allow all input nodes
context.set_symbol(JPATH_QUERY_RESULT_NODE_KEY, input_nodelist)
return rt_res.success(BooleanValue.value_for(True).set_context(context).set_pos(node.position.copy()))



for seg_node in segments:
# creating a new Context provides a local scope for the segment visit
seg_context = Context("<segment>", context, seg_node.position )
Expand Down Expand Up @@ -193,13 +189,14 @@ def _collect_vnodes_and_their_descendants(self,
collected_vnodes.append(cur_node)

# add children to the queue for processing during the next iteration
if isinstance(jvalue, JSON_ARRAY_TYPES):
if isinstance(jvalue, JSON_ARRAY_TYPES) and not isinstance(jvalue, str):
for index, element in enumerate(jvalue):
new_node = VNode(NormalizedJPath(f"{jpath}[{index}]"), element,
cur_node.root_value, cur_node.node_depth + 1 )
node_queue.append((new_node, depth + 1))
elif isinstance(jvalue, JSON_OBJECT_TYPES):
for name, value in jvalue.items():
# noinspection PyUnresolvedReferences
for name, value in jvalue.items(): # type: ignore
new_node = VNode(NormalizedJPath(f"{jpath}['{name}']"), value,
cur_node.root_value, cur_node.node_depth + 1)
node_queue.append((new_node, depth + 1))
Expand All @@ -220,7 +217,7 @@ def _children_of(self, parent_node: VNode) -> VNodeList:
child_nodes: list[VNode] = []
instance_ids: dict[int, VNode] = {id(parent_node.jvalue):parent_node}

if isinstance(parent_node.jvalue, JSON_STRUCTURED_TYPES):
if isinstance(parent_node.jvalue, JSON_STRUCTURED_TYPES) and not isinstance(parent_node.jvalue, str):
base_path = parent_node.jpath
if isinstance(parent_node.jvalue, JSON_ARRAY_TYPES):
for index, element in enumerate(parent_node.jvalue):
Expand Down Expand Up @@ -364,9 +361,11 @@ def visit_NameSelectorNode(self, node: NameSelectorNode, context: Context) -> Ru
jpath = input_node.jpath
jvalue_dict = input_node.jvalue
if node.member_name in jvalue_dict:
# we have to unescape because NormalizedJPath will re-escape the entire path, resulting in a double escaping of the starting path
unescaped_jpath = unescape_string_content(jpath.jpath_str)
output_nodelist.append(
VNode( NormalizedJPath(f"{jpath}['{node.member_name}']"), jvalue_dict[ node.member_name ],
input_node.root_value, input_node.node_depth +1 )
VNode( NormalizedJPath(f"{unescaped_jpath}['{node.member_name}']"), jvalue_dict[ node.member_name ],
input_node.root_value, input_node.node_depth + 1 )
)

return rt_res.success(VNodeList(output_nodelist))
Expand Down Expand Up @@ -404,7 +403,7 @@ def visit_SliceSelectorNode(self, node: SliceSelectorNode, context: Context) ->
input_node: VNode
current_slice_obj = node.slice_op
for input_node in input_nodelist:
if not isinstance(input_node.jvalue, JSON_ARRAY_TYPES):
if not isinstance(input_node.jvalue, JSON_ARRAY_TYPES) or isinstance(input_node.jvalue, str):
continue
jpath = input_node.jpath
jvalue_list = input_node.jvalue
Expand Down Expand Up @@ -440,7 +439,7 @@ def visit_IndexSelectorNode(self, node: IndexSelectorNode, context: Context) ->
ouput_nodes: list[VNode] = []
input_node: VNode
for input_node in input_nodelist:
if not isinstance(input_node.jvalue, JSON_ARRAY_TYPES):
if not isinstance(input_node.jvalue, JSON_ARRAY_TYPES) or isinstance(input_node.jvalue, str):
continue
jpath = input_node.jpath
jvalue = input_node.jvalue
Expand Down
2 changes: 1 addition & 1 deletion killerbunny/evaluating/evaluator_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def normalize_path(jpath_str: str) -> str:
match = NORMALIZED_PATH_RE.match(escaped_path)
if match:
return escaped_path
else: raise ValueError(f"jpath_str: '{jpath_str}' is not in normal format.")
else: raise ValueError(f"jpath_str: '{jpath_str}' (escaped: '{escaped_path}') is not in normal format.")
# todo : to verify that a jpath is well-formed we actualy need to run it through the lexer and parser!
# this produces an AST, however. So we would need a special evalute method in the Evaluator to construct the
# jpath and verify it only contains legitimate jpath syntax.
Expand Down
4 changes: 3 additions & 1 deletion killerbunny/lexing/tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,9 @@ def __str__(self) -> str:

def __testrepr__(self) -> str:
"""Representation of Token for testing purposes. If you change this, you may break unit tests. """
if self.token_type.is_literal() or self.token_type.is_identifier():
if self.token_type == TokenType.SLICE:
string = f"SLICE[{self.value}]"
elif self.token_type.is_literal() or self.token_type.is_identifier():
string = f"{self.token_type}:{self.value}"
elif self.token_type.is_keyword():
string =f"{self.token_type.category.name}:{self.value}"
Expand Down
Loading