diff --git a/src/jsx.erl b/src/jsx.erl index 722f674..ff1bc09 100644 --- a/src/jsx.erl +++ b/src/jsx.erl @@ -148,7 +148,7 @@ single_quotes_test_() -> }, {"single quote in double quoted string", ?_assertEqual( - to_term(<<"[\"a single quote: '\"]">>), + to_term(<<"[\"a single quote: '\"]">>, [single_quotes]), [<<"a single quote: '">>] ) }, @@ -163,6 +163,12 @@ single_quotes_test_() -> badarg, to_term(<<"[\"a single quote: \\'\"]">>) ) + }, + {"mismatched quotes", + ?_assertError( + badarg, + to_term(<<"['mismatched\"]">>, [single_quotes]) + ) } ]. diff --git a/src/jsx_decoder.erl b/src/jsx_decoder.erl index 700ba0a..006cc22 100644 --- a/src/jsx_decoder.erl +++ b/src/jsx_decoder.erl @@ -134,7 +134,7 @@ decoder(Handler, State, Opts) -> value(<>, Handler, Stack, Opts) -> string(Rest, Handler, [?new_seq()|Stack], Opts); value(<>, Handler, Stack, Opts = #opts{single_quotes=true}) -> - string(Rest, Handler, [?new_seq()|Stack], Opts); + string(Rest, Handler, [?new_seq(), single_quote|Stack], Opts); value(<<$t, Rest/binary>>, Handler, Stack, Opts) -> tr(Rest, Handler, Stack, Opts); value(<<$f, Rest/binary>>, Handler, Stack, Opts) -> @@ -162,7 +162,7 @@ value(Bin, Handler, Stack, Opts) -> object(<>, Handler, Stack, Opts) -> string(Rest, Handler, [?new_seq()|Stack], Opts); object(<>, Handler, Stack, Opts = #opts{single_quotes=true}) -> - string(Rest, Handler, [?new_seq()|Stack], Opts); + string(Rest, Handler, [?new_seq(), single_quote|Stack], Opts); object(<>, {Handler, State}, [key|Stack], Opts) -> maybe_done(Rest, {Handler, Handler:handle_event(end_object, State)}, Stack, Opts); object(<>, Handler, Stack, Opts) when ?is_whitespace(S) -> @@ -176,7 +176,7 @@ object(Bin, Handler, Stack, Opts) -> array(<>, Handler, Stack, Opts) -> string(Rest, Handler, [?new_seq()|Stack], Opts); array(<>, Handler, Stack, Opts = #opts{single_quotes=true}) -> - string(Rest, Handler, [?new_seq()|Stack], Opts); + string(Rest, Handler, [?new_seq(), single_quote|Stack], Opts); array(<<$t, Rest/binary>>, Handler, Stack, Opts) -> tr(Rest, Handler, Stack, Opts); array(<<$f, Rest/binary>>, Handler, Stack, Opts) -> @@ -216,7 +216,7 @@ colon(Bin, Handler, Stack, Opts) -> key(<>, Handler, Stack, Opts) -> string(Rest, Handler, [?new_seq()|Stack], Opts); key(<>, Handler, Stack, Opts = #opts{single_quotes=true}) -> - string(Rest, Handler, [?new_seq()|Stack], Opts); + string(Rest, Handler, [?new_seq(), single_quote|Stack], Opts); key(<>, Handler, Stack, Opts) when ?is_whitespace(S) -> key(Rest, Handler, Stack, Opts); key(<<>>, Handler, Stack, Opts) -> @@ -242,30 +242,24 @@ partial_utf(<>) partial_utf(_) -> false. -string(<>, {Handler, State}, [Acc, key|Stack], Opts) -> - colon(Rest, - {Handler, Handler:handle_event({key, ?end_seq(Acc)}, State)}, - [key|Stack], - Opts - ); -string(<>, {Handler, State}, [Acc, key|Stack], Opts = #opts{single_quotes=true}) -> - colon(Rest, - {Handler, Handler:handle_event({key, ?end_seq(Acc)}, State)}, - [key|Stack], - Opts - ); -string(<>, {Handler, State}, [Acc|Stack], Opts) -> - maybe_done(Rest, - {Handler, Handler:handle_event({string, ?end_seq(Acc)}, State)}, - Stack, - Opts - ); -string(<>, {Handler, State}, [Acc|Stack], Opts = #opts{single_quotes=true}) -> - maybe_done(Rest, - {Handler, Handler:handle_event({string, ?end_seq(Acc)}, State)}, - Stack, - Opts - ); +string(<>, {Handler, State}, S, Opts) -> + case S of + [Acc, key|Stack] -> + colon(Rest, {Handler, Handler:handle_event({key, ?end_seq(Acc)}, State)}, [key|Stack], Opts); + [_Acc, single_quote|_Stack] -> + ?error([<>, {Handler, State}, S, Opts]); + [Acc|Stack] -> + maybe_done(Rest, {Handler, Handler:handle_event({string, ?end_seq(Acc)}, State)}, Stack, Opts) + end; +string(<>, {Handler, State}, S, Opts = #opts{single_quotes=true}) -> + case S of + [Acc, single_quote, key|Stack] -> + colon(Rest, {Handler, Handler:handle_event({key, ?end_seq(Acc)}, State)}, [key|Stack], Opts); + [Acc, single_quote|Stack] -> + maybe_done(Rest, {Handler, Handler:handle_event({string, ?end_seq(Acc)}, State)}, Stack, Opts); + [Acc|Stack] -> + string(Rest, {Handler, State}, [?acc_seq(Acc, ?singlequote)|Stack], Opts) + end; string(<>, Handler, Stack, Opts) -> escape(Rest, Handler, Stack, Opts); %% things get dumb here. erlang doesn't properly restrict unicode non-characters