55static VALUE mJSON , eNestingError , eParserError , Encoding_UTF_8 ;
66static VALUE CNaN , CInfinity , CMinusInfinity , JSON_empty_string ;
77
8- static ID i_new , i_try_convert , i_encode , i_at_line , i_at_column ;
8+ static ID i_new , i_try_convert , i_encode , i_at_line , i_at_column , i_at_json_path ;
99#ifndef HAVE_RB_STR_TO_INTERNED_STR
1010static ID i_uminus ;
1111#endif
@@ -651,11 +651,74 @@ 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 void json_path_append_key (VALUE path , VALUE key )
655+ {
656+ if (RB_SYMBOL_P (key )) {
657+ key = rb_sym2str (key );
658+ }
659+
660+ if (!RB_TYPE_P (key , T_STRING )) {
661+ rb_str_catf (path , "[%+" PRIsVALUE "]" , key );
662+ return ;
663+ }
664+
665+ long len = RSTRING_LEN (key );
666+ bool plain = len > 0 ;
667+ for (long i = 0 ; plain && i < len ; i ++ ) {
668+ char c = RSTRING_PTR (key )[i ];
669+ bool alpha = (c >= 'a' && c <= 'z' ) || (c >= 'A' && c <= 'Z' ) || c == '_' ;
670+ plain = alpha || (i > 0 && c >= '0' && c <= '9' );
671+ }
672+
673+ if (plain ) {
674+ rb_str_cat_cstr (path , "." );
675+ rb_str_cat (path , RSTRING_PTR (key ), len );
676+ return ;
677+ }
678+
679+ rb_str_cat_cstr (path , "[\"" );
680+ for (long i = 0 ; i < RSTRING_LEN (key ); i ++ ) {
681+ char c = RSTRING_PTR (key )[i ];
682+ if (c == '"' || c == '\\' ) {
683+ rb_str_cat (path , "\\" , 1 );
684+ }
685+ rb_str_cat (path , & c , 1 );
686+ }
687+ rb_str_cat_cstr (path , "\"]" );
688+ }
689+
690+ static VALUE json_path_new (JSON_ParserState * state , VALUE duplicate_key )
691+ {
692+ VALUE path = rb_utf8_str_new ("$" , 1 );
693+ json_frame_stack * frames = state -> frames ;
694+ rvalue_stack * values = state -> value_stack ;
695+
696+ for (long depth = 1 ; depth < frames -> head ; depth ++ ) {
697+ json_frame * frame = & frames -> ptr [depth ];
698+ bool innermost = depth == frames -> head - 1 ;
699+ long child_head = innermost ? values -> head : frames -> ptr [depth + 1 ].value_stack_head ;
700+ long count = child_head - frame -> value_stack_head ;
701+
702+ if (frame -> type == JSON_FRAME_ARRAY ) {
703+ rb_str_catf (path , "[%ld]" , frame -> phase == JSON_PHASE_ARRAY_COMMA ? count - 1 : count );
704+ } else if (innermost && !UNDEF_P (duplicate_key )) {
705+ json_path_append_key (path , duplicate_key );
706+ } else if (count & 1 ) {
707+ json_path_append_key (path , values -> ptr [child_head - 1 ]);
708+ } else if (frame -> phase == JSON_PHASE_OBJECT_COMMA && count >= 2 ) {
709+ json_path_append_key (path , values -> ptr [child_head - 2 ]);
710+ }
711+ }
712+
713+ return path ;
714+ }
715+
654716static VALUE parse_error_new (JSON_ParserState * state , VALUE message , long line , long column , bool eos )
655717{
656718 VALUE exc = rb_exc_new_str (eParserError , message );
657719 rb_ivar_set (exc , i_at_line , LONG2NUM (line ));
658720 rb_ivar_set (exc , i_at_column , LONG2NUM (column ));
721+ rb_ivar_set (exc , i_at_json_path , json_path_new (state , Qundef ));
659722 return exc ;
660723}
661724
@@ -1199,14 +1262,17 @@ NORETURN(static) void raise_duplicate_key_error(JSON_ParserState *state, VALUE d
11991262 );
12001263
12011264 rb_str_concat (message , build_parse_error_message ("" , state ));
1265+ VALUE exc ;
12021266 if (state -> parser ) { // line and columns can't be accurate in resumable
1203- rb_exc_raise ( parse_error_new (state , message , 0 , 0 , false) );
1267+ exc = parse_error_new (state , message , 0 , 0 , false);
12041268 } else {
12051269 long line , column ;
12061270 cursor_position (state , & line , & column );
12071271 rb_str_catf (message , " at line %ld column %ld" , line , column );
1208- rb_exc_raise ( parse_error_new (state , message , line , column , false) );
1272+ exc = parse_error_new (state , message , line , column , false);
12091273 }
1274+ rb_ivar_set (exc , i_at_json_path , json_path_new (state , duplicate_key ));
1275+ rb_exc_raise (exc );
12101276}
12111277
12121278NOINLINE (static ) void json_on_duplicate_key (JSON_ParserState * state , JSON_ParserConfig * config , size_t count , const VALUE * pairs )
@@ -2122,6 +2188,12 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src)
21222188 // the rvalue stack.
21232189 VALUE result = complete ? * rvalue_stack_peek (state -> value_stack , 1 ) : Qundef ;
21242190
2191+ if (complete ) {
2192+ json_ensure_eof (state , config );
2193+ } else {
2194+ raise_eos_error ("unexpected end of input" , state );
2195+ }
2196+
21252197 // This may be skipped in case of exception, but
21262198 // it won't cause a leak.
21272199 rvalue_stack_eagerly_release (value_stack_handle );
@@ -2130,12 +2202,6 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src)
21302202 RB_GC_GUARD (frame_stack_handle );
21312203 RB_GC_GUARD (Vsource );
21322204
2133- if (complete ) {
2134- json_ensure_eof (state , config );
2135- } else {
2136- raise_eos_error ("unexpected end of input" , state );
2137- }
2138-
21392205 return result ;
21402206}
21412207
@@ -2871,6 +2937,7 @@ void Init_parser(void)
28712937 i_encode = rb_intern ("encode" );
28722938 i_at_line = rb_intern ("@line" );
28732939 i_at_column = rb_intern ("@column" );
2940+ i_at_json_path = rb_intern ("@json_path" );
28742941
28752942 binary_encindex = rb_ascii8bit_encindex ();
28762943 utf8_encindex = rb_utf8_encindex ();
0 commit comments