(blog ‘lucindo)

um dia eu aprendo a programar

Arquivo da categoria ‘erlang’

Common Lisp, Erlang e Emacs no Leopard

Passo a passo para instalar e configurar Erlang, SBCL e Emacs no Leopard.

Primeiro instale o xcode que vem no CD do Leopard. Em seguida instale o MacPorts.

O MacPorts está com versões bem atualizadas de Erlang (R12B-0) e SBCL (1.0.13):
$ sudo port selfupdate
$ sudo port install erlang +smp +ssl
$ sudo port install sbcl +threads
$ sudo port clean --all installed

O MacPorts instala tudo no /opt, então coloque o seguinte no seu .profile:
export PATH=/opt/local/bin:/opt/local/sbin:$PATH

Para emacs existem algumas opções. Eu uso o Carbon Emacs (que já vem com slime). Depois de instalar adicione o seguinte no seu .emacs:

(add-to-list 'exec-path “/opt/local/bin”)
(add-to-list ‘load-path “/opt/local/lib/erlang/lib/tools-2.6/emacs”)

(setq erlang-root-dir “/opt/local”)
(requireerlang-start)

(requireslime)
(setq inferior-lisp-program “sbcl –noinform”)
(add-hook ‘lisp-mode-hook (lambda () (slime-mode t)))
(add-hook ‘inferior-lisp-mode-hook (lambda () (inferior-slime-mode t)))

Update: Para passar parâmetros para a Erlang VM use o seguinte no seu .emacs:

(setq inferior-erlang-machine-options
      '(“-pa” “/opt/local/lib/yaws/ebin”
        “-sname” “mini”))
2 comentários »

Erlang Google Tech Talk

Sem comentários »

Video: Concurrency and Erlang

Apresentação feita por Andre Pang no linux.conf.au 2007 destacando algumas características gerais da linguagem e focando na importância de suporte nativo na linguagem a programação concorrente.



Sem comentários »

Primeiros passos com Erlang (toy NNTP client)

Nada melhor do que programar para aprender uma linguagem de programação. Por isso comecei um pequeno projeto em Erlang. Faz parte desse projeto um cliente do protocolo NNTP. Implementei apenas os comandos NNTP que eu precisava.

Essa implementação está bem crua ainda, sem tratamento de erros do protocolo, que se acontecerem vão resultar em crash por badmatch.

-module(nntp).
-vsn(1).
-author({renato, lucindo}).
-purpose(”work :-P “).

%% Implements a little client for:
%%  NNTP: http://tools.ietf.org/html/rfc977
%%  Simple NNTP Auth: http://tools.ietf.org/html/rfc4643

%% protocol functions
-export([connect/2, connect/4, cmd_quit/1]).
-export([cmd_list/1, cmd_group/2, cmd_stat/2, cmd_head/2, cmd_body/2]).
%% utils
-export([get_header_contents/2]).

-include(”nntp.hrl“).

%% Little documentation:
%% warning 1:
%%   You must include “nntp.hrl” to the definition of newsgroup record.
%%
%% Warning 2:
%%   This is a quick and really dirty implementation. Will result on a badmath
%%   error due protocol erros. This will be fixed one day (maybe with wappers
%%   functions with try…catch).
%%
%% Commands (functions exported):
%%
%% nntp:connect(Host, Port)
%%   Simple connection on Host:Port
%%   Returns {ok, Socket} on success. {error, Reason} otherwise
%%
%% nntp:connect(Host, Port, User, Password)
%%   Connect and authenticate on Host:Port. Same return values as connect/2
%%
%% nntp:cmd_quit(Sockt)
%%   Terminate de NNTP session and close the socket.
%%   Aways returns ok.
%%
%% nntp:cmd_list(Socket)
%%   List all groups on server.
%%   Resturns a erlang:list of newsgroups record (see “nntp.hrl”).
%%
%% nntp:cmd_group(Socket, GroupName)
%%   Selects the group GroupName.
%%   Returns a tuple {ok, NMessages, First, Last}, where NMessages is the number of
%%   messages on group, First is the first article number and Last is the last
%%   article number
%%
%% nntp:cmd_stat(Socket, Msgnum)
%%   Selects the message Msgnum (message number) for reading
%%   Returns {ok, MessageID} where MessageID is the message unique ID
%%
%% nntp:cmd_head(Socket, Msgnum)
%%   Gets the headers of article Msgnum.
%%   Returns a tuple {ok, MessageID, Headers} where Headers is a list of strings
%%
%% nntp:get_header_contents(Header, Field)
%%   Gets the contents of a Header’s Field (the header as returned by nntp:cmd_head)
%%   Returns {ok, Filed, Content} on success, error otherwise
%%
%% nntp:cmd_body(Socket, Msgnum)
%%   Gets the body of message Msgnum as string.
%%   Returns {ok, MessageID, Message} on success, error otherwise

connect(Hostname, Port) ->
    case gen_tcp:connect(Hostname, Port, [binary, {packet, line}, {active, false}]) of
        {ok, Socket} -> 
            case read_response(Socket) of
                {ok, Code, _} when Code == 200; Code == 201 ->
                    {ok, Socket};
                _ ->
                    gen_tcp:close(Socket),
                    {error, “Greetings read error“}
            end;
        _ ->
            {error, “Connection error“}
    end.
connect(Hostname, Port, User, Pass) ->
    {ok, Socket} = connect(Hostname, Port),
    auth(Socket, User, Pass).

auth(Socket, User, Pass) ->
    ok = send_request(Socket, “AUTHINFO USER ” ++ User),
    {ok, 381, _} = read_response(Socket),
    ok = send_request(Socket, “AUTHINFO PASS ” ++ Pass),
    {ok, 281, _} = read_response(Socket),
    {ok, Socket}.

cmd_quit(Socket) ->
    ok = send_request(Socket, “QUIT“),
    {ok, 205, _} = read_response(Socket),
    ok = gen_tcp:close(Socket),
    ok.

cmd_list(Socket) ->
    ok = send_request(Socket, “LIST“),
    {ok, 215, _} = read_response(Socket),
    cmd_list(Socket, []).
cmd_list(Socket, Glist) ->
    case gen_tcp:recv(Socket, 0) of
        {ok, Response} ->
            Line = remove_rn(binary_to_list(Response)),
            if
                Line == “.” ->
                    Glist;
                true ->
                    Tk = string:tokens(Line, “ “),
                    Name = lists:nth(1, Tk),
                    Last = list_to_integer(lists:nth(2, Tk)),
                    First = list_to_integer(lists:nth(3, Tk)),
                    CanPost = list_to_atom(lists:nth(4, Tk)),
                    Group = #newsgroup {
                      name = Name,
                      last = Last,
                      first = First,
                      canpost = CanPost
                     },
                    cmd_list(Socket, lists:append(Glist, [Group]))
            end;
        _ ->
            error
    end.

cmd_group(Socket, Group) ->
    ok = send_request(Socket, “GROUP ” ++ Group),
    {ok, 211, GrpInfo} = read_response(Socket),
    Tk = string:tokens(GrpInfo, “ “),
    NMessages = list_to_integer(lists:nth(1, Tk)),
    First = list_to_integer(lists:nth(2, Tk)),
    Last = list_to_integer(lists:nth(3, Tk)),
    {ok, NMessages, First, Last}.

cmd_stat(Socket, Msgnum) ->
    ok = send_request(Socket, “STAT “++ integer_to_list(Msgnum)),
    {ok, 223, Response} = read_response(Socket),
    {ok, lists:nth(2, string:tokens(Response, “ “))}.

cmd_head(Socket, Msgnum) ->
    exec_cmd_response_and_stream(Socket, Msgnum, “HEAD “, 221, fun read_lines_as_list/3).

cmd_body(Socket, Msgnum) ->
    exec_cmd_response_and_stream(Socket, Msgnum, “BODY “, 222, fun read_lines_as_string/3).

exec_cmd_response_and_stream(Socket, Msgnum, Command, RespCode, ReadFunction) when integer(Msgnum) ->
    exec_cmd_response_and_stream(Socket, integer_to_list(Msgnum), Command, RespCode, ReadFunction);
exec_cmd_response_and_stream(Socket, Msgnum, Command, RespCode, ReadFunction) ->
    ok = send_request(Socket, Command ++ Msgnum),
    {ok, RespCode, Response} = read_response(Socket),
    {ok, lists:nth(2, string:tokens(Response, “ “)), apply(ReadFunction, [Socket, “.“, []])}.

read_lines_as_list(Socket, Delim, Str) ->
    case gen_tcp:recv(Socket, 0) of
        {ok, Response} ->
            Line = remove_rn(binary_to_list(Response)),
            if
                Line == Delim ->
                    Str;
                true ->
                    read_lines_as_list(Socket, Delim, lists:append(Str, [Line]))
            end;
        _ ->
            error
    end.    

read_lines_as_string(Socket, Delim, Str) ->
    case gen_tcp:recv(Socket, 0) of
        {ok, Response} ->
            StrR = binary_to_list(Response),
            Line = remove_rn(StrR),
            if
                Line == Delim ->
                    Str;
                true ->
                    read_lines_as_string(Socket, Delim, [Str, StrR])
            end;
        _ ->
            error
    end.    

send_request(Socket, Cmd) ->
    Str = Cmd ++ “rn“,
    case gen_tcp:send(Socket, Str) of
        ok->
            ok;
        {error, Reason} ->
            io:format(”error on send socket: ~w~n“, [Reason]),
            gen_tcp:close(Socket),
            error
    end.

read_response(Socket) ->
    case gen_tcp:recv(Socket, 0) of
        {ok, Response} ->
            parse_response(Response);
        {error, Reason} ->
            io:format(”error on recv socket: ~w~n“, [Reason]),
            gen_tcp:close(Socket),
            error
    end.

parse_response(Response) ->
    <<Code:3/binary, _:1/binary, Message/binary>> = Response,
    {ok, list_to_integer(binary_to_list(Code)), remove_rn(binary_to_list(Message))}.

get_header_contents(Header, Field) ->
    get_header_contents_by_field(Field, Header).

get_header_contents_by_field(Field, [H|T]) ->
    Tk = string:tokens(H, “: “),
    if
        length(Tk) > 1 ->       
            F = lists:nth(1, Tk),
            C = lists:nth(2, Tk),
            if
                F == Field ->
                    {ok, F, C};
                true ->
                    get_header_contents_by_field(Field, T)
            end;
        true ->
            get_header_contents_by_field(Field, T)
    end;
get_header_contents_by_field(_, []) ->
    error.

remove_rn(Str) ->
    string:strip(string:strip(Str, both, $n), both, $r).

O arquivo nntp.hrl tem apenas:

-record(newsgroup,
        {
          name = “”,
          last = 0,
          first = 0,
          canpost = n
         }).

Alguém sabe como exportar records de uma maneira fácil?

1 comentário »

Makefile genério para Erlang

O Makefile a seguir compila os arquivos nntp.erl e crawler.erl

.SUFFIXES: .erl .beam .hrl

.erl.beam:
        erlc -W $<

MODULES = nntp crawler

all: compile

compile: ${MODULES:%=%.beam}

clean:
        rm -rf *.beam erl_crash.dump *~
Sem comentários »

Erlang no Mac OS X

Uma maneira fácil de instalar o sistema Erlang no Mac OS X é pelo Darwin Ports. Com ele instalado vá no terminal e dê os comandos:

$ sudo port selfupdate
$ sudo port install erlang +smp

Se você usa Emacs (Aquamacs) adicione no seu .emacs:

(setq load-path (cons "/opt/local/lib/erlang/lib/tools-2.5.3/emacs”
    load-path))
(setq erlang-root-dir “/opt/local/“)
(setq exec-path (cons “/opt/local/bin” exec-path))
(requireerlang-start)
Sem comentários »

Erlang?

Esse video introduz algumas capacidades da plataforma Erlang. Ver a demonstração de hotswap de código é o suficiente para querer aprender mais sobre essa linguagem.



Sem comentários »