Erlang SPKI SEXP.
This commit is contained in:
parent
05123a421e
commit
f92a6e19de
|
@ -0,0 +1,2 @@
|
|||
ebin
|
||||
erl_crash.dump
|
|
@ -0,0 +1,17 @@
|
|||
all: compile
|
||||
|
||||
compile:
|
||||
./rebar compile
|
||||
|
||||
clean:
|
||||
./rebar clean
|
||||
-rmdir ebin
|
||||
|
||||
veryclean: clean
|
||||
rm -rf rel
|
||||
rm -f erl_crash.dump
|
||||
|
||||
run: compile
|
||||
erl -pa ebin \
|
||||
-boot start_sasl \
|
||||
-run hop_demo start localhost
|
Binary file not shown.
|
@ -0,0 +1,8 @@
|
|||
%% -*- erlang -*-
|
||||
{application, hop,
|
||||
[{description, "Hop"},
|
||||
{vsn, "0.0.1"},
|
||||
{registered, []},
|
||||
{applications, [kernel,
|
||||
stdlib]},
|
||||
{env, []}]}.
|
|
@ -0,0 +1,122 @@
|
|||
%% Copyright 2010, 2011, 2012 Tony Garnock-Jones <tonygarnockjones@gmail.com>.
|
||||
%%
|
||||
%% This file is part of Hop.
|
||||
%%
|
||||
%% Hop is free software: you can redistribute it and/or modify it
|
||||
%% under the terms of the GNU General Public License as published by
|
||||
%% the Free Software Foundation, either version 3 of the License, or
|
||||
%% (at your option) any later version.
|
||||
%%
|
||||
%% Hop is distributed in the hope that it will be useful, but WITHOUT
|
||||
%% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
%% or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
%% License for more details.
|
||||
%%
|
||||
%% You should have received a copy of the GNU General Public License
|
||||
%% along with Hop. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-module(sexp).
|
||||
%% SPKI SEXP.
|
||||
|
||||
-export([lex_state/0, lex/1, lex/2]).
|
||||
-export([parse_state/0, parse/1, parse/2]).
|
||||
-export([format/1, format_iolist/1]).
|
||||
|
||||
%% Lexer states
|
||||
-record(init, {}).
|
||||
-record(str, {remaining, pieces_rev}).
|
||||
-record(strlen, {value}).
|
||||
|
||||
%% Parser states
|
||||
-record(parser, {lexer, stack}).
|
||||
|
||||
-define(ZERO, 48).
|
||||
-define(NINE, 57).
|
||||
|
||||
lex_state() ->
|
||||
#init{}.
|
||||
|
||||
lex(Chunk) ->
|
||||
lex(Chunk, lex_state()).
|
||||
|
||||
lex(<<"(", Rest/binary>>, State = #init{}) ->
|
||||
{Events, FinalState} = lex(Rest, State),
|
||||
{[open | Events], FinalState};
|
||||
lex(<<")", Rest/binary>>, State = #init{}) ->
|
||||
{Events, FinalState} = lex(Rest, State),
|
||||
{[close | Events], FinalState};
|
||||
lex(<<"[", Rest/binary>>, State = #init{}) ->
|
||||
{Events, FinalState} = lex(Rest, State),
|
||||
{[hint_open | Events], FinalState};
|
||||
lex(<<"]", Rest/binary>>, State = #init{}) ->
|
||||
{Events, FinalState} = lex(Rest, State),
|
||||
{[hint_close | Events], FinalState};
|
||||
lex(<<D, Rest/binary>>, #init{}) when D >= ?ZERO andalso D =< ?NINE ->
|
||||
lex(Rest, #strlen{value = D - ?ZERO});
|
||||
lex(<<X, Rest/binary>>, State = #init{}) when X =< 32 ->
|
||||
lex(Rest, State);
|
||||
lex(<<D, Rest/binary>>, #strlen{value = V}) when D >= ?ZERO andalso D =< ?NINE ->
|
||||
lex(Rest, #strlen{value = V * 10 + D - ?ZERO});
|
||||
lex(<<":", Rest/binary>>, #strlen{value = V}) ->
|
||||
lex(Rest, #str{remaining = V, pieces_rev = []});
|
||||
lex(Chunk, #str{remaining = Len, pieces_rev = PiecesRev}) ->
|
||||
case Chunk of
|
||||
<<Piece:Len/binary, Rest/binary>> ->
|
||||
{Events, FinalState} = lex(Rest, #init{}),
|
||||
{[{str, iolist_to_binary(lists:reverse([Piece | PiecesRev]))} | Events], FinalState};
|
||||
_ ->
|
||||
{[], #str{remaining = Len - size(Chunk), pieces_rev = [Chunk | PiecesRev]}}
|
||||
end;
|
||||
lex(<<>>, State) ->
|
||||
{[], State}.
|
||||
|
||||
parse_state() ->
|
||||
#parser{lexer = lex_state(), stack = []}.
|
||||
|
||||
parse(Chunk) ->
|
||||
Inert = parse_state(),
|
||||
case parse(Chunk, Inert) of
|
||||
{Terms, Inert} -> {ok, Terms, Inert};
|
||||
{Terms, Other} -> {more, Terms, Other}
|
||||
end.
|
||||
|
||||
parse(Chunk, #parser{lexer = Lexer, stack = Stack}) ->
|
||||
{Events, NewLexer} = lex(Chunk, Lexer),
|
||||
{Terms, NewStack} = parse_events(Events, Stack),
|
||||
{Terms, #parser{lexer = NewLexer, stack = NewStack}}.
|
||||
|
||||
parse_events([open | Rest], Stack) ->
|
||||
parse_events(Rest, [[] | Stack]);
|
||||
parse_events([close | Rest], [AccRev]) when is_list(AccRev) ->
|
||||
{Terms, NewStack} = parse_events(Rest, []),
|
||||
{[lists:reverse(AccRev) | Terms], NewStack};
|
||||
parse_events([close | Rest], [AccRev, AccRevOuter | Stack]) when is_list(AccRev) ->
|
||||
parse_events(Rest, [[lists:reverse(AccRev) | AccRevOuter] | Stack]);
|
||||
parse_events([hint_open | Rest], Stack) ->
|
||||
parse_events(Rest, [hint | Stack]);
|
||||
parse_events([{str, Bin} | Rest], [hint | Stack]) ->
|
||||
parse_events(Rest, [{hint, Bin} | Stack]);
|
||||
parse_events([hint_close | Rest], Stack = [{hint, _} | _]) ->
|
||||
parse_events(Rest, Stack);
|
||||
parse_events([{str, BodyBin} | Rest], [{hint, HintBin} | Stack]) ->
|
||||
{Terms, NewStack} = parse_events(Rest, Stack),
|
||||
{[{HintBin, BodyBin} | Terms], NewStack};
|
||||
parse_events([{str, Bin} | Rest], Stack = []) ->
|
||||
{Terms, NewStack} = parse_events(Rest, Stack),
|
||||
{[Bin | Terms], NewStack};
|
||||
parse_events([{str, Bin} | Rest], [AccRev | Stack]) when is_list(AccRev) ->
|
||||
parse_events(Rest, [[Bin | AccRev] | Stack]);
|
||||
parse_events([], Stack) ->
|
||||
{[], Stack}.
|
||||
|
||||
format(Sexp) ->
|
||||
iolist_to_binary(format_iolist(Sexp)).
|
||||
|
||||
format_bin(Bin) -> [integer_to_list(size(Bin)), $:, Bin].
|
||||
|
||||
format_iolist(Bin) when is_binary(Bin) ->
|
||||
format_bin(Bin);
|
||||
format_iolist({H, B}) when is_binary(H) andalso is_binary(B) ->
|
||||
[$[, format_bin(H), $], format_bin(B)];
|
||||
format_iolist(Sexps) when is_list(Sexps) ->
|
||||
[$(, [format_iolist(Sexp) || Sexp <- Sexps], $)].
|
Loading…
Reference in New Issue