Skip to content

Latest commit

 

History

History
166 lines (126 loc) · 3.43 KB

json_parser.adoc

File metadata and controls

166 lines (126 loc) · 3.43 KB

JSON 解析器

语法文件

json_parser.yrl
Nonterminals value values object array pair pairs.

Terminals number string true false null '[' ']' '{' '}' ',' ':'.

Rootsymbol value.

value -> object  :  '$1'.
value -> array   :  '$1'.
value -> number  :  get_val('$1').
value -> string  :  get_val('$1').
value -> 'true'  :  get_val('$1').
value -> 'null'  :  get_val('$1').
value -> 'false' :  get_val('$1').

object -> '{' '}' : #{}.
object -> '{' pairs '}' : '$2'.

pairs -> pair : '$1'.
pairs -> pair ',' pairs : maps:merge('$1', '$3').

pair -> string ':' value : #{ get_val('$1') => '$3' }.

array -> '[' ']' : {}.
array -> '[' values ']' : list_to_tuple('$2').

values -> value : [ '$1' ].
values -> value ',' values : [ '$1' | '$3' ].

Erlang code.

-export([demo/0]).

demo() ->
    {ok, Tokens, _} = json_tokenizer:t(),
    case json_parser:parse(Tokens) of
        {ok, Forms} ->
            io:format("Forms: ~w~n", [Forms]);
        {error, Error} ->
            io:format("Error: ~p~n", [Error])
    end.

get_val({_,_,Val}) -> Val;
get_val({Val, _}) -> Val.

词法文件

json_tokenizer.xrl
Definitions.

Digit         = [0-9]
Digit1to9     = [1-9]
HexDigit      = [0-9a-f]
UnescapedChar = [^\"\\]
EscapedChar   = (\\\\)|(\\\")|(\\b)|(\\f)|(\\n)|(\\r)|(\\t)|(\\/)
Unicode       = (\\u{HexDigit}{HexDigit}{HexDigit}{HexDigit})
Quote         = [\"]
Delim         = [\[\]:,{}]
Space         = [\n\s\t\r]

Rules.

{Quote}{Quote} : {token, {string, TokenLine, ""}}.
{Quote}({EscapedChar}|({UnescapedChar})|({Unicode}))+{Quote} :
  {token, {string, TokenLine, drop_quotes(TokenChars)}}.

null  : {token, {null,  TokenLine}}.
true  : {token, {true,  TokenLine}}.
false : {token, {false, TokenLine}}.

{Delim} : {token, {list_to_atom(TokenChars), TokenLine}}.

{Space} : skip_token.

-?{Digit1to9}+{Digit}*\.{Digit}+((E|e)(\+|\-)?{Digit}+)? :
  {token, {number, TokenLine, list_to_float(TokenChars)}}.
-?{Digit1to9}+{Digit}* :
  {token, {number, TokenLine, list_to_integer(TokenChars)+0.0}}.

Erlang code.
-export([t/0]).

drop_quotes([$" | QuotedString]) -> literal(lists:droplast(QuotedString)).
literal([$\\,$" | Rest]) ->
  [$"|literal(Rest)];
literal([$\\,$\\ | Rest]) ->
  [$\\|literal(Rest)];
literal([$\\,$/ | Rest]) ->
  [$/|literal(Rest)];
literal([$\\,$b | Rest]) ->
  [$\b|literal(Rest)];
literal([$\\,$f | Rest]) ->
  [$\f|literal(Rest)];
literal([$\\,$n | Rest]) ->
  [$\n|literal(Rest)];
literal([$\\,$r | Rest]) ->
  [$\r|literal(Rest)];
literal([$\\,$t | Rest]) ->
  [$\t|literal(Rest)];
literal([$\\,$u,D0,D1,D2,D3|Rest]) ->
  Char = list_to_integer([D0,D1,D2,D3],16),
  [Char|literal(Rest)];
literal([C|Rest]) ->
  [C|literal(Rest)];
literal([]) ->[].

t() ->
  {ok,
   [{'{',1},
    {string,2,"no"},
    {':',2},
    {number,2,1.0},
    {'}',3}
   ],
   4}.

构建

Makefile
Q := @

JSON_TOKENIZER := json_tokenizer.erl
JSON_PARSER := json_parser.erl

default: shell

json: $(JSON_PARSER) $(JSON_TOKENIZER)


$(JSON_TOKENIZER): json_tokenizer.xrl
	$(Q) mkdir -p ebin
	$(Q) erlc -o $@ +'{verbose,true}' +'{report,true}' $<
	$(Q) erlc -o ebin $(JSON_TOKENIZER)

$(JSON_PARSER): json_parser.yrl
	$(Q) mkdir -p ebin
	$(Q) erlc -o $@ +'{verbose,true}' +'{report,true}' $<
	$(Q) erlc -o ebin $(JSON_PARSER)


shell: clean $(JSON_TOKENIZER) $(JSON_PARSER)
	$(Q) erl -pa ebin

clean:
	$(Q) rm -fr ebin $(JSON_TOKENIZER) $(JSON_PARSER)

执行

$ make
> json_parser:demo().