Skip to content

Commit d77424e

Browse files
committed
Move most of ParserError#json_path building logic in Ruby
This isn't a performance sensitive path, and that logic can be shared with the eventual JRuby implementation.
1 parent 2b7afaa commit d77424e

2 files changed

Lines changed: 45 additions & 40 deletions

File tree

‎ext/json/ext/parser/parser.c‎

Lines changed: 9 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -651,60 +651,30 @@ static VALUE build_parse_error_message(const char *format, JSON_ParserState *sta
651651
return rb_enc_sprintf(enc_utf8, format, ptr);
652652
}
653653

654-
static bool json_path_append_key(VALUE path, VALUE key)
655-
{
656-
if (RB_SYMBOL_P(key)) {
657-
key = rb_sym2str(key);
658-
} else if (!RB_TYPE_P(key, T_STRING)) {
659-
return false;
660-
}
661-
662-
long len = RSTRING_LEN(key);
663-
bool plain = len > 0;
664-
for (long i = 0; plain && i < len; i++) {
665-
char c = RSTRING_PTR(key)[i];
666-
bool alpha = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
667-
plain = alpha || (i > 0 && c >= '0' && c <= '9');
668-
}
669-
670-
if (plain) {
671-
rb_str_cat_cstr(path, ".");
672-
rb_str_cat(path, RSTRING_PTR(key), len);
673-
return true;
674-
}
675-
676-
rb_str_cat_cstr(path, "[\"");
677-
for (long i = 0; i < RSTRING_LEN(key); i++) {
678-
char c = RSTRING_PTR(key)[i];
679-
if (c == '"' || c == '\\') {
680-
rb_str_cat(path, "\\", 1);
681-
}
682-
rb_str_cat(path, &c, 1);
683-
}
684-
rb_str_cat_cstr(path, "\"]");
685-
return true;
686-
}
687-
688654
static VALUE json_path_new(JSON_ParserState *state, VALUE duplicate_key)
689655
{
690-
VALUE path = rb_utf8_str_new("$", 1);
656+
VALUE path = rb_ary_new_capa(state->current_nesting);
657+
691658
json_frame_stack *frames = state->frames;
692659
rvalue_stack *values = state->value_stack;
693660

694661
for (long depth = 1; depth < frames->head; depth++) {
695662
json_frame *frame = &frames->ptr[depth];
663+
696664
bool innermost = depth == frames->head - 1;
697665
long child_head = innermost ? values->head : frames->ptr[depth + 1].value_stack_head;
698666
long count = child_head - frame->value_stack_head;
699667

700668
if (frame->type == JSON_FRAME_ARRAY) {
701-
rb_str_catf(path, "[%ld]", frame->phase == JSON_PHASE_ARRAY_COMMA ? count - 1 : count);
669+
rb_ary_push(path, LONG2NUM(frame->phase == JSON_PHASE_ARRAY_COMMA ? count - 1 : count));
702670
} else if (innermost && !UNDEF_P(duplicate_key)) {
703-
if (!json_path_append_key(path, duplicate_key)) break;
671+
rb_ary_push(path, duplicate_key);
704672
} else if (count & 1) {
705-
if (!json_path_append_key(path, values->ptr[child_head - 1])) break;
673+
rb_ary_push(path, values->ptr[child_head - 1]);
706674
} else if (frame->phase == JSON_PHASE_OBJECT_COMMA && count >= 2) {
707-
if (!json_path_append_key(path, values->ptr[child_head - 2])) break;
675+
rb_ary_push(path, values->ptr[child_head - 2]);
676+
} else {
677+
break;
708678
}
709679
}
710680

‎lib/json/common.rb‎

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,42 @@ class JSONError < StandardError; end
142142

143143
# This exception is raised if a parser error occurs.
144144
class ParserError < JSONError
145-
attr_reader :line, :column, :json_path
145+
attr_reader :line, :column
146+
147+
def json_path
148+
return @json_path if String === @json_path
149+
150+
if Array === @json_path
151+
path = build_json_path(@json_path)
152+
@json_path = path unless frozen?
153+
return path
154+
end
155+
end
156+
157+
private
158+
159+
def build_json_path(segments)
160+
error = false
161+
path = segments.filter_map do |segment|
162+
next if error
163+
164+
case segment
165+
when Integer
166+
"[#{segment}]"
167+
when String, Symbol
168+
if segment.match?(/\A[a-zA-Z\$\_][a-zA-Z\$\_0-9]*\z/)
169+
".#{segment}"
170+
else
171+
segment = segment.to_s.gsub(/["\\]/, { '"' => '\\"', '\\' => '\\\\' })
172+
%{["#{segment}"]}
173+
end
174+
else
175+
error = true
176+
nil
177+
end
178+
end.join
179+
"$#{path}".freeze
180+
end
146181
end
147182

148183
# This exception is raised if the nesting of parsed data structures is too

0 commit comments

Comments
 (0)