diff --git a/experiments/erlang/.gitignore b/experiments/erlang/.gitignore new file mode 100644 index 0000000..c85682e --- /dev/null +++ b/experiments/erlang/.gitignore @@ -0,0 +1,2 @@ +ebin +erl_crash.dump diff --git a/experiments/erlang/Makefile b/experiments/erlang/Makefile new file mode 100644 index 0000000..61a938b --- /dev/null +++ b/experiments/erlang/Makefile @@ -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 diff --git a/experiments/erlang/rebar b/experiments/erlang/rebar new file mode 100755 index 0000000..dc66e87 Binary files /dev/null and b/experiments/erlang/rebar differ diff --git a/experiments/erlang/src/hop.app.src b/experiments/erlang/src/hop.app.src new file mode 100644 index 0000000..314a851 --- /dev/null +++ b/experiments/erlang/src/hop.app.src @@ -0,0 +1,8 @@ +%% -*- erlang -*- +{application, hop, + [{description, "Hop"}, + {vsn, "0.0.1"}, + {registered, []}, + {applications, [kernel, + stdlib]}, + {env, []}]}. diff --git a/experiments/erlang/src/sexp.erl b/experiments/erlang/src/sexp.erl new file mode 100644 index 0000000..9a93989 --- /dev/null +++ b/experiments/erlang/src/sexp.erl @@ -0,0 +1,122 @@ +%% 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], $)].