%% Copyright 2010, 2011, 2012 Tony Garnock-Jones . %% %% 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 . -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(<>, #init{}) when D >= ?ZERO andalso D =< ?NINE -> lex(Rest, #strlen{value = D - ?ZERO}); lex(<>, State = #init{}) when X =< 32 -> lex(Rest, State); lex(<>, #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 <> -> {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], $)].