@@ -395,6 +395,23 @@ static void raise_parse_error(const char *format, JSON_ParserState *state)
395395{
396396 unsigned char buffer [PARSE_ERROR_FRAGMENT_LEN + 1 ];
397397
398+ const char * cursor = state -> cursor ;
399+ long column = 0 ;
400+ long line = 1 ;
401+
402+ while (cursor >= state -> start ) {
403+ if (* cursor -- == '\n' ) {
404+ break ;
405+ }
406+ column ++ ;
407+ }
408+
409+ while (cursor >= state -> start ) {
410+ if (* cursor -- == '\n' ) {
411+ line ++ ;
412+ }
413+ }
414+
398415 const char * ptr = state -> cursor ;
399416 size_t len = ptr ? strnlen (ptr , PARSE_ERROR_FRAGMENT_LEN ) : 0 ;
400417
@@ -413,7 +430,14 @@ static void raise_parse_error(const char *format, JSON_ParserState *state)
413430 ptr = (const char * )buffer ;
414431 }
415432
416- rb_enc_raise (enc_utf8 , rb_path2class ("JSON::ParserError" ), format , ptr );
433+ VALUE msg = rb_sprintf (format , ptr );
434+ VALUE message = rb_enc_sprintf (enc_utf8 , "%s at line %ld column %ld" , RSTRING_PTR (msg ), line , column );
435+ RB_GC_GUARD (msg );
436+
437+ VALUE exc = rb_exc_new_str (rb_path2class ("JSON::ParserError" ), message );
438+ rb_ivar_set (exc , rb_intern ("@line" ), LONG2NUM (line ));
439+ rb_ivar_set (exc , rb_intern ("@column" ), LONG2NUM (column ));
440+ rb_exc_raise (exc );
417441}
418442
419443#ifdef RBIMPL_ATTR_NORETURN
@@ -508,11 +532,11 @@ json_eat_comments(JSON_ParserState *state)
508532 break ;
509533 }
510534 default :
511- raise_parse_error ("unexpected token at '%s'" , state );
535+ raise_parse_error ("unexpected token '%s'" , state );
512536 break ;
513537 }
514538 } else {
515- raise_parse_error ("unexpected token at '%s'" , state );
539+ raise_parse_error ("unexpected token '%s'" , state );
516540 }
517541}
518542
@@ -870,15 +894,15 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
870894 return json_push_value (state , config , Qnil );
871895 }
872896
873- raise_parse_error ("unexpected token at '%s'" , state );
897+ raise_parse_error ("unexpected token '%s'" , state );
874898 break ;
875899 case 't' :
876900 if ((state -> end - state -> cursor >= 4 ) && (memcmp (state -> cursor , "true" , 4 ) == 0 )) {
877901 state -> cursor += 4 ;
878902 return json_push_value (state , config , Qtrue );
879903 }
880904
881- raise_parse_error ("unexpected token at '%s'" , state );
905+ raise_parse_error ("unexpected token '%s'" , state );
882906 break ;
883907 case 'f' :
884908 // Note: memcmp with a small power of two compile to an integer comparison
@@ -887,7 +911,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
887911 return json_push_value (state , config , Qfalse );
888912 }
889913
890- raise_parse_error ("unexpected token at '%s'" , state );
914+ raise_parse_error ("unexpected token '%s'" , state );
891915 break ;
892916 case 'N' :
893917 // Note: memcmp with a small power of two compile to an integer comparison
@@ -896,15 +920,15 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
896920 return json_push_value (state , config , CNaN );
897921 }
898922
899- raise_parse_error ("unexpected token at '%s'" , state );
923+ raise_parse_error ("unexpected token '%s'" , state );
900924 break ;
901925 case 'I' :
902926 if (config -> allow_nan && (state -> end - state -> cursor >= 8 ) && (memcmp (state -> cursor , "Infinity" , 8 ) == 0 )) {
903927 state -> cursor += 8 ;
904928 return json_push_value (state , config , CInfinity );
905929 }
906930
907- raise_parse_error ("unexpected token at '%s'" , state );
931+ raise_parse_error ("unexpected token '%s'" , state );
908932 break ;
909933 case '-' :
910934 // Note: memcmp with a small power of two compile to an integer comparison
@@ -913,7 +937,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
913937 state -> cursor += 9 ;
914938 return json_push_value (state , config , CMinusInfinity );
915939 } else {
916- raise_parse_error ("unexpected token at '%s'" , state );
940+ raise_parse_error ("unexpected token '%s'" , state );
917941 }
918942 }
919943 // Fallthrough
0 commit comments