---input--- -module(test). -export([listen/1, handle_client/1, maintain_clients/1, start/1, stop/0, controller/1]). -author("jerith"). -define(TCP_OPTIONS,[list, {packet, 0}, {active, false}, {reuseaddr, true}]). -record(player, {name=none, socket, mode}). %% To allow incoming connections, we need to listen on a TCP port. %% This is also the entry point for our server as a whole, so it %% starts the client_manager process and gives it a name so the rest %% of the code can get to it easily. listen(Port) -> {ok, LSocket} = gen_tcp:listen(Port, ?TCP_OPTIONS), register(client_manager, spawn(?MODULE, maintain_clients, [[]])), do_accept(LSocket). %% Accepting a connection gives us a connection socket with the %% newly-connected client on the other end. Since we want to accept %% more than one client, we spawn a new process for each and then wait %% for another connection on our listening socket. do_accept(LSocket) -> case gen_tcp:accept(LSocket) of {ok, Socket} -> spawn(?MODULE, handle_client, [Socket]), client_manager ! {connect, Socket}; {error, Reason} -> io:format("Socket accept error: ~s~n", [Reason]) end, do_accept(LSocket). %% All the client-socket process needs to do is wait for data and %% forward it to the client_manager process which decides what to do %% with it. If the client disconnects, we let client_manager know and %% then quietly go away. handle_client(Socket) -> case gen_tcp:recv(Socket, 0) of {ok, Data} -> client_manager ! {data, Socket, Data}, handle_client(Socket); {error, closed} -> client_manager ! {disconnect, Socket} end. %% This is the main loop of the client_manager process. It maintains %% the list of "players" and calls the handler for client input. maintain_clients(Players) -> io:format("Players:~n", []), lists:foreach(fun(P) -> io:format(">>> ~w~n", [P]) end, Players), receive {connect, Socket} -> Player = #player{socket=Socket, mode=connect}, send_prompt(Player), io:format("client connected: ~w~n", [Player]), NewPlayers = [Player | Players]; {disconnect, Socket} -> Player = find_player(Socket, Players), io:format("client disconnected: ~w~n", [Player]), NewPlayers = lists:delete(Player, Players); {data, Socket, Data} -> Player = find_player(Socket, Players), NewPlayers = parse_data(Player, Players, Data), NewPlayer = find_player(Socket, NewPlayers), send_prompt(NewPlayer) end, maintain_clients(NewPlayers). %% find_player is a utility function to get a player record associated %% with a particular socket out of the player list. find_player(Socket, Players) -> {value, Player} = lists:keysearch(Socket, #player.socket, Players), Player. %% delete_player returns the player list without the given player. It %% deletes the player from the list based on the socket rather than %% the whole record because the list might hold a different version. delete_player(Player, Players) -> lists:keydelete(Player#player.socket, #player.socket, Players). %% Sends an appropriate prompt to the player. Currently the only %% prompt we send is the initial "Name: " when the player connects. send_prompt(Player) -> case Player#player.mode of connect -> gen_tcp:send(Player#player.socket, "Name: "); active -> ok end. %% Sends the given data to all players in active mode. send_to_active(Prefix, Players, Data) -> ActivePlayers = lists:filter(fun(P) -> P#player.mode == active end, Players), lists:foreach(fun(P) -> gen_tcp:send(P#player.socket, Prefix ++ Data) end, ActivePlayers), ok. %% We don't really do much parsing, but that will probably change as %% more features are added. Currently this handles naming the player %% when he first connects and treats everything else as a message to %% send. parse_data(Player, Players, Data) -> case Player#player.mode of active -> send_to_active(Player#player.name ++ ": ", delete_player(Player, Players), Data), Players; connect -> UPlayer = Player#player{name=bogostrip(Data), mode=active}, [UPlayer | delete_player(Player, Players)] end. %% Utility methods to clean up the name before we apply it. Called %% bogostrip rather than strip because it returns the first continuous %% block of non-matching characters rather stripping matching %% characters off the front and back. bogostrip(String) -> bogostrip(String, "\r\n\t "). bogostrip(String, Chars) -> LStripped = string:substr(String, string:span(String, Chars)+1), string:substr(LStripped, 1, string:cspan(LStripped, Chars)). %% Here we have some extra code to test other bits of pygments' Erlang %% lexer. get_timestamp() -> {{Year,Month,Day},{Hour,Min,Sec}} = erlang:universaltime(), lists:flatten(io_lib:format( "~4.10.0B-~2.10.0B-~2.10.0BT~2.10.0B:~2.10.0B:~2.10.0BZ", [Year, Month, Day, Hour, Min, Sec])). a_binary() -> << 100:16/integer, 16#7f >>. a_list_comprehension() -> [X*2 || X <- [1,2,3]]. a_map() -> M0 = #{ a => 1, b => 2 }, M1 = M0#{ b := 200 }. escape_sequences() -> [ "\b\d\e\f\n\r\s\t\v\'\"\\" , "\1\12\123" % octal , "\x01" % short hex , "\x{fff}" % long hex , "\^a\^A" % control characters ]. map(Fun, [H|T]) -> [Fun(H) | map(Fun, T)]; map(Fun, []) -> []. %% pmap, just because it's cool. pmap(F, L) -> Parent = self(), [receive {Pid, Result} -> Result end || Pid <- [spawn(fun() -> Parent ! {self(), F(X)} end) || X <- L]]. ---tokens--- '-' Punctuation 'module' Name.Entity '(' Punctuation 'test' Name ')' Punctuation '.' Punctuation '\n' Text '-' Punctuation 'export' Name.Entity '(' Punctuation '[' Punctuation 'listen' Name '/' Operator '1' Literal.Number.Integer ',' Punctuation '\n ' Text 'handle_client' Name '/' Operator '1' Literal.Number.Integer ',' Punctuation '\n ' Text 'maintain_clients' Name '/' Operator '1' Literal.Number.Integer ',' Punctuation '\n ' Text 'start' Name '/' Operator '1' Literal.Number.Integer ',' Punctuation '\n ' Text 'stop' Name '/' Operator '0' Literal.Number.Integer ',' Punctuation '\n ' Text 'controller' Name '/' Operator '1' Literal.Number.Integer ']' Punctuation ')' Punctuation '.' Punctuation '\n\n' Text '-' Punctuation 'author' Name.Entity '(' Punctuation '"' Literal.String 'jerith' Literal.String '"' Literal.String ')' Punctuation '.' Punctuation '\n\n' Text '-' Punctuation 'define' Name.Entity '(' Punctuation 'TCP_OPTIONS' Name.Constant ',' Punctuation '[' Punctuation 'list' Name ',' Punctuation ' ' Text '{' Punctuation 'packet' Name ',' Punctuation ' ' Text '0' Literal.Number.Integer '}' Punctuation ',' Punctuation ' ' Text '{' Punctuation 'active' Name ',' Punctuation ' ' Text 'false' Name '}' Punctuation ',' Punctuation ' ' Text '{' Punctuation 'reuseaddr' Name ',' Punctuation ' ' Text 'true' Name '}' Punctuation ']' Punctuation ')' Punctuation '.' Punctuation '\n\n' Text '-' Punctuation 'record' Name.Entity '(' Punctuation 'player' Name.Label ',' Punctuation ' ' Text '{' Punctuation 'name' Name '=' Operator 'none' Name ',' Punctuation ' ' Text 'socket' Name ',' Punctuation ' ' Text 'mode' Name '}' Punctuation ')' Punctuation '.' Punctuation '\n\n' Text '%% To allow incoming connections, we need to listen on a TCP port.\n' Comment '%% This is also the entry point for our server as a whole, so it\n' Comment '%% starts the client_manager process and gives it a name so the rest\n' Comment '%% of the code can get to it easily.\n' Comment '\n' Text 'listen' Name.Function '(' Punctuation 'Port' Name.Variable ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text '{' Punctuation 'ok' Name ',' Punctuation ' ' Text 'LSocket' Name.Variable '}' Punctuation ' ' Text '=' Operator ' ' Text 'gen_tcp' Name.Namespace ':' Punctuation 'listen' Name.Function '(' Punctuation 'Port' Name.Variable ',' Punctuation ' ' Text '?' Operator 'TCP_OPTIONS' Name.Variable ')' Punctuation ',' Punctuation '\n ' Text 'register' Name.Builtin '(' Punctuation 'client_manager' Name ',' Punctuation ' ' Text 'spawn' Name.Builtin '(' Punctuation '?' Operator 'MODULE' Name.Variable ',' Punctuation ' ' Text 'maintain_clients' Name ',' Punctuation ' ' Text '[' Punctuation '[' Punctuation ']' Punctuation ']' Punctuation ')' Punctuation ')' Punctuation ',' Punctuation '\n ' Text 'do_accept' Name '(' Punctuation 'LSocket' Name.Variable ')' Punctuation '.' Punctuation '\n\n' Text '%% Accepting a connection gives us a connection socket with the\n' Comment '%% newly-connected client on the other end. Since we want to accept\n' Comment '%% more than one client, we spawn a new process for each and then wait\n' Comment '%% for another connection on our listening socket.\n' Comment '\n' Text 'do_accept' Name.Function '(' Punctuation 'LSocket' Name.Variable ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'case' Keyword ' ' Text 'gen_tcp' Name.Namespace ':' Punctuation 'accept' Name.Function '(' Punctuation 'LSocket' Name.Variable ')' Punctuation ' ' Text 'of' Keyword '\n ' Text '{' Punctuation 'ok' Name ',' Punctuation ' ' Text 'Socket' Name.Variable '}' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'spawn' Name.Builtin '(' Punctuation '?' Operator 'MODULE' Name.Variable ',' Punctuation ' ' Text 'handle_client' Name ',' Punctuation ' ' Text '[' Punctuation 'Socket' Name.Variable ']' Punctuation ')' Punctuation ',' Punctuation '\n ' Text 'client_manager' Name ' ' Text '!' Operator ' ' Text '{' Punctuation 'connect' Name ',' Punctuation ' ' Text 'Socket' Name.Variable '}' Punctuation ';' Punctuation '\n ' Text '{' Punctuation 'error' Name ',' Punctuation ' ' Text 'Reason' Name.Variable '}' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'io' Name.Namespace ':' Punctuation 'format' Name.Function '(' Punctuation '"' Literal.String 'Socket accept error: ' Literal.String '~s' Literal.String.Interpol '~n' Literal.String.Interpol '"' Literal.String ',' Punctuation ' ' Text '[' Punctuation 'Reason' Name.Variable ']' Punctuation ')' Punctuation '\n ' Text 'end' Keyword ',' Punctuation '\n ' Text 'do_accept' Name '(' Punctuation 'LSocket' Name.Variable ')' Punctuation '.' Punctuation '\n\n' Text '%% All the client-socket process needs to do is wait for data and\n' Comment '%% forward it to the client_manager process which decides what to do\n' Comment '%% with it. If the client disconnects, we let client_manager know and\n' Comment '%% then quietly go away.\n' Comment '\n' Text 'handle_client' Name.Function '(' Punctuation 'Socket' Name.Variable ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'case' Keyword ' ' Text 'gen_tcp' Name.Namespace ':' Punctuation 'recv' Name.Function '(' Punctuation 'Socket' Name.Variable ',' Punctuation ' ' Text '0' Literal.Number.Integer ')' Punctuation ' ' Text 'of' Keyword '\n ' Text '{' Punctuation 'ok' Name ',' Punctuation ' ' Text 'Data' Name.Variable '}' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'client_manager' Name ' ' Text '!' Operator ' ' Text '{' Punctuation 'data' Name ',' Punctuation ' ' Text 'Socket' Name.Variable ',' Punctuation ' ' Text 'Data' Name.Variable '}' Punctuation ',' Punctuation '\n ' Text 'handle_client' Name '(' Punctuation 'Socket' Name.Variable ')' Punctuation ';' Punctuation '\n ' Text '{' Punctuation 'error' Name ',' Punctuation ' ' Text 'closed' Name '}' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'client_manager' Name ' ' Text '!' Operator ' ' Text '{' Punctuation 'disconnect' Name ',' Punctuation ' ' Text 'Socket' Name.Variable '}' Punctuation '\n ' Text 'end' Keyword '.' Punctuation '\n\n' Text '%% This is the main loop of the client_manager process. It maintains\n' Comment '%% the list of "players" and calls the handler for client input.\n' Comment '\n' Text 'maintain_clients' Name.Function '(' Punctuation 'Players' Name.Variable ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'io' Name.Namespace ':' Punctuation 'format' Name.Function '(' Punctuation '"' Literal.String 'Players:' Literal.String '~n' Literal.String.Interpol '"' Literal.String ',' Punctuation ' ' Text '[' Punctuation ']' Punctuation ')' Punctuation ',' Punctuation '\n ' Text 'lists' Name.Namespace ':' Punctuation 'foreach' Name.Function '(' Punctuation 'fun' Keyword '(' Punctuation 'P' Name.Variable ')' Punctuation ' ' Text '-' Operator '>' Operator ' ' Text 'io' Name.Namespace ':' Punctuation 'format' Name.Function '(' Punctuation '"' Literal.String '>>> ' Literal.String '~w' Literal.String.Interpol '~n' Literal.String.Interpol '"' Literal.String ',' Punctuation ' ' Text '[' Punctuation 'P' Name.Variable ']' Punctuation ')' Punctuation ' ' Text 'end' Keyword ',' Punctuation ' ' Text 'Players' Name.Variable ')' Punctuation ',' Punctuation '\n ' Text 'receive' Keyword '\n ' Text '{' Punctuation 'connect' Name ',' Punctuation ' ' Text 'Socket' Name.Variable '}' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'Player' Name.Variable ' ' Text '=' Operator ' ' Text '#player' Name.Label '{' Punctuation 'socket' Name '=' Operator 'Socket' Name.Variable ',' Punctuation ' ' Text 'mode' Name '=' Operator 'connect' Name '}' Punctuation ',' Punctuation '\n ' Text 'send_prompt' Name '(' Punctuation 'Player' Name.Variable ')' Punctuation ',' Punctuation '\n ' Text 'io' Name.Namespace ':' Punctuation 'format' Name.Function '(' Punctuation '"' Literal.String 'client connected: ' Literal.String '~w' Literal.String.Interpol '~n' Literal.String.Interpol '"' Literal.String ',' Punctuation ' ' Text '[' Punctuation 'Player' Name.Variable ']' Punctuation ')' Punctuation ',' Punctuation '\n ' Text 'NewPlayers' Name.Variable ' ' Text '=' Operator ' ' Text '[' Punctuation 'Player' Name.Variable ' ' Text '|' Punctuation ' ' Text 'Players' Name.Variable ']' Punctuation ';' Punctuation '\n ' Text '{' Punctuation 'disconnect' Name ',' Punctuation ' ' Text 'Socket' Name.Variable '}' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'Player' Name.Variable ' ' Text '=' Operator ' ' Text 'find_player' Name '(' Punctuation 'Socket' Name.Variable ',' Punctuation ' ' Text 'Players' Name.Variable ')' Punctuation ',' Punctuation '\n ' Text 'io' Name.Namespace ':' Punctuation 'format' Name.Function '(' Punctuation '"' Literal.String 'client disconnected: ' Literal.String '~w' Literal.String.Interpol '~n' Literal.String.Interpol '"' Literal.String ',' Punctuation ' ' Text '[' Punctuation 'Player' Name.Variable ']' Punctuation ')' Punctuation ',' Punctuation '\n ' Text 'NewPlayers' Name.Variable ' ' Text '=' Operator ' ' Text 'lists' Name.Namespace ':' Punctuation 'delete' Name.Function '(' Punctuation 'Player' Name.Variable ',' Punctuation ' ' Text 'Players' Name.Variable ')' Punctuation ';' Punctuation '\n ' Text '{' Punctuation 'data' Name ',' Punctuation ' ' Text 'Socket' Name.Variable ',' Punctuation ' ' Text 'Data' Name.Variable '}' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'Player' Name.Variable ' ' Text '=' Operator ' ' Text 'find_player' Name '(' Punctuation 'Socket' Name.Variable ',' Punctuation ' ' Text 'Players' Name.Variable ')' Punctuation ',' Punctuation '\n ' Text 'NewPlayers' Name.Variable ' ' Text '=' Operator ' ' Text 'parse_data' Name '(' Punctuation 'Player' Name.Variable ',' Punctuation ' ' Text 'Players' Name.Variable ',' Punctuation ' ' Text 'Data' Name.Variable ')' Punctuation ',' Punctuation '\n ' Text 'NewPlayer' Name.Variable ' ' Text '=' Operator ' ' Text 'find_player' Name '(' Punctuation 'Socket' Name.Variable ',' Punctuation ' ' Text 'NewPlayers' Name.Variable ')' Punctuation ',' Punctuation '\n ' Text 'send_prompt' Name '(' Punctuation 'NewPlayer' Name.Variable ')' Punctuation '\n ' Text 'end' Keyword ',' Punctuation '\n ' Text 'maintain_clients' Name '(' Punctuation 'NewPlayers' Name.Variable ')' Punctuation '.' Punctuation '\n\n' Text '%% find_player is a utility function to get a player record associated\n' Comment '%% with a particular socket out of the player list.\n' Comment '\n' Text 'find_player' Name.Function '(' Punctuation 'Socket' Name.Variable ',' Punctuation ' ' Text 'Players' Name.Variable ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text '{' Punctuation 'value' Name ',' Punctuation ' ' Text 'Player' Name.Variable '}' Punctuation ' ' Text '=' Operator ' ' Text 'lists' Name.Namespace ':' Punctuation 'keysearch' Name.Function '(' Punctuation 'Socket' Name.Variable ',' Punctuation ' ' Text '#player.socket' Name.Label ',' Punctuation ' ' Text 'Players' Name.Variable ')' Punctuation ',' Punctuation '\n ' Text 'Player' Name.Variable '.' Punctuation '\n\n' Text '%% delete_player returns the player list without the given player. It\n' Comment '%% deletes the player from the list based on the socket rather than\n' Comment '%% the whole record because the list might hold a different version.\n' Comment '\n' Text 'delete_player' Name.Function '(' Punctuation 'Player' Name.Variable ',' Punctuation ' ' Text 'Players' Name.Variable ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'lists' Name.Namespace ':' Punctuation 'keydelete' Name.Function '(' Punctuation 'Player' Name.Variable '#player.socket' Name.Label ',' Punctuation ' ' Text '#player.socket' Name.Label ',' Punctuation ' ' Text 'Players' Name.Variable ')' Punctuation '.' Punctuation '\n\n' Text '%% Sends an appropriate prompt to the player. Currently the only\n' Comment '%% prompt we send is the initial "Name: " when the player connects.\n' Comment '\n' Text 'send_prompt' Name.Function '(' Punctuation 'Player' Name.Variable ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'case' Keyword ' ' Text 'Player' Name.Variable '#player.mode' Name.Label ' ' Text 'of' Keyword '\n ' Text 'connect' Name ' ' Text '-' Operator '>' Operator '\n ' Text 'gen_tcp' Name.Namespace ':' Punctuation 'send' Name.Builtin '(' Punctuation 'Player' Name.Variable '#player.socket' Name.Label ',' Punctuation ' ' Text '"' Literal.String 'Name: ' Literal.String '"' Literal.String ')' Punctuation ';' Punctuation '\n ' Text 'active' Name ' ' Text '-' Operator '>' Operator '\n ' Text 'ok' Name '\n ' Text 'end' Keyword '.' Punctuation '\n\n' Text '%% Sends the given data to all players in active mode.\n' Comment '\n' Text 'send_to_active' Name.Function '(' Punctuation 'Prefix' Name.Variable ',' Punctuation ' ' Text 'Players' Name.Variable ',' Punctuation ' ' Text 'Data' Name.Variable ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'ActivePlayers' Name.Variable ' ' Text '=' Operator ' ' Text 'lists' Name.Namespace ':' Punctuation 'filter' Name.Function '(' Punctuation 'fun' Keyword '(' Punctuation 'P' Name.Variable ')' Punctuation ' ' Text '-' Operator '>' Operator ' ' Text 'P' Name.Variable '#player.mode' Name.Label ' ' Text '==' Operator ' ' Text 'active' Name ' ' Text 'end' Keyword ',' Punctuation '\n ' Text 'Players' Name.Variable ')' Punctuation ',' Punctuation '\n ' Text 'lists' Name.Namespace ':' Punctuation 'foreach' Name.Function '(' Punctuation 'fun' Keyword '(' Punctuation 'P' Name.Variable ')' Punctuation ' ' Text '-' Operator '>' Operator ' ' Text 'gen_tcp' Name.Namespace ':' Punctuation 'send' Name.Builtin '(' Punctuation 'P' Name.Variable '#player.socket' Name.Label ',' Punctuation ' ' Text 'Prefix' Name.Variable ' ' Text '++' Operator ' ' Text 'Data' Name.Variable ')' Punctuation ' ' Text 'end' Keyword ',' Punctuation '\n ' Text 'ActivePlayers' Name.Variable ')' Punctuation ',' Punctuation '\n ' Text 'ok' Name '.' Punctuation '\n\n' Text "%% We don't really do much parsing, but that will probably change as\n" Comment '%% more features are added. Currently this handles naming the player\n' Comment '%% when he first connects and treats everything else as a message to\n' Comment '%% send.\n' Comment '\n' Text 'parse_data' Name.Function '(' Punctuation 'Player' Name.Variable ',' Punctuation ' ' Text 'Players' Name.Variable ',' Punctuation ' ' Text 'Data' Name.Variable ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'case' Keyword ' ' Text 'Player' Name.Variable '#player.mode' Name.Label ' ' Text 'of' Keyword '\n ' Text 'active' Name ' ' Text '-' Operator '>' Operator '\n ' Text 'send_to_active' Name '(' Punctuation 'Player' Name.Variable '#player.name' Name.Label ' ' Text '++' Operator ' ' Text '"' Literal.String ': ' Literal.String '"' Literal.String ',' Punctuation '\n ' Text 'delete_player' Name '(' Punctuation 'Player' Name.Variable ',' Punctuation ' ' Text 'Players' Name.Variable ')' Punctuation ',' Punctuation ' ' Text 'Data' Name.Variable ')' Punctuation ',' Punctuation '\n ' Text 'Players' Name.Variable ';' Punctuation '\n ' Text 'connect' Name ' ' Text '-' Operator '>' Operator '\n ' Text 'UPlayer' Name.Variable ' ' Text '=' Operator ' ' Text 'Player' Name.Variable '#player' Name.Label '{' Punctuation 'name' Name '=' Operator 'bogostrip' Name '(' Punctuation 'Data' Name.Variable ')' Punctuation ',' Punctuation ' ' Text 'mode' Name '=' Operator 'active' Name '}' Punctuation ',' Punctuation '\n ' Text '[' Punctuation 'UPlayer' Name.Variable ' ' Text '|' Punctuation ' ' Text 'delete_player' Name '(' Punctuation 'Player' Name.Variable ',' Punctuation ' ' Text 'Players' Name.Variable ')' Punctuation ']' Punctuation '\n ' Text 'end' Keyword '.' Punctuation '\n\n' Text '%% Utility methods to clean up the name before we apply it. Called\n' Comment '%% bogostrip rather than strip because it returns the first continuous\n' Comment '%% block of non-matching characters rather stripping matching\n' Comment '%% characters off the front and back.\n' Comment '\n' Text 'bogostrip' Name.Function '(' Punctuation 'String' Name.Variable ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'bogostrip' Name '(' Punctuation 'String' Name.Variable ',' Punctuation ' ' Text '"' Literal.String '\\r' Literal.String.Escape '\\n' Literal.String.Escape '\\t' Literal.String.Escape ' ' Literal.String '"' Literal.String ')' Punctuation '.' Punctuation '\n\n' Text 'bogostrip' Name.Function '(' Punctuation 'String' Name.Variable ',' Punctuation ' ' Text 'Chars' Name.Variable ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'LStripped' Name.Variable ' ' Text '=' Operator ' ' Text 'string' Name.Namespace ':' Punctuation 'substr' Name.Function '(' Punctuation 'String' Name.Variable ',' Punctuation ' ' Text 'string' Name.Namespace ':' Punctuation 'span' Name.Function '(' Punctuation 'String' Name.Variable ',' Punctuation ' ' Text 'Chars' Name.Variable ')' Punctuation '+' Operator '1' Literal.Number.Integer ')' Punctuation ',' Punctuation '\n ' Text 'string' Name.Namespace ':' Punctuation 'substr' Name.Function '(' Punctuation 'LStripped' Name.Variable ',' Punctuation ' ' Text '1' Literal.Number.Integer ',' Punctuation ' ' Text 'string' Name.Namespace ':' Punctuation 'cspan' Name.Function '(' Punctuation 'LStripped' Name.Variable ',' Punctuation ' ' Text 'Chars' Name.Variable ')' Punctuation ')' Punctuation '.' Punctuation '\n\n' Text "%% Here we have some extra code to test other bits of pygments' Erlang\n" Comment '%% lexer.\n' Comment '\n' Text 'get_timestamp' Name.Function '(' Punctuation ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text '{' Punctuation '{' Punctuation 'Year' Name.Variable ',' Punctuation 'Month' Name.Variable ',' Punctuation 'Day' Name.Variable '}' Punctuation ',' Punctuation '{' Punctuation 'Hour' Name.Variable ',' Punctuation 'Min' Name.Variable ',' Punctuation 'Sec' Name.Variable '}' Punctuation '}' Punctuation ' ' Text '=' Operator ' ' Text 'erlang' Name.Namespace ':' Punctuation 'universaltime' Name.Function '(' Punctuation ')' Punctuation ',' Punctuation '\n ' Text 'lists' Name.Namespace ':' Punctuation 'flatten' Name.Function '(' Punctuation 'io_lib' Name.Namespace ':' Punctuation 'format' Name.Function '(' Punctuation '\n ' Text '"' Literal.String '~4.10.0B' Literal.String.Interpol '-' Literal.String '~2.10.0B' Literal.String.Interpol '-' Literal.String '~2.10.0B' Literal.String.Interpol 'T' Literal.String '~2.10.0B' Literal.String.Interpol ':' Literal.String '~2.10.0B' Literal.String.Interpol ':' Literal.String '~2.10.0B' Literal.String.Interpol 'Z' Literal.String '"' Literal.String ',' Punctuation '\n ' Text '[' Punctuation 'Year' Name.Variable ',' Punctuation ' ' Text 'Month' Name.Variable ',' Punctuation ' ' Text 'Day' Name.Variable ',' Punctuation ' ' Text 'Hour' Name.Variable ',' Punctuation ' ' Text 'Min' Name.Variable ',' Punctuation ' ' Text 'Sec' Name.Variable ']' Punctuation ')' Punctuation ')' Punctuation '.' Punctuation '\n\n' Text 'a_binary' Name.Function '(' Punctuation ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text '<' Operator '<' Operator ' ' Text '100' Literal.Number.Integer ':' Punctuation '16' Literal.Number.Integer '/' Operator 'integer' Name ',' Punctuation ' ' Text '16#7f' Literal.Number.Integer ' ' Text '>' Operator '>' Operator '.' Punctuation '\n\n' Text 'a_list_comprehension' Name.Function '(' Punctuation ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text '[' Punctuation 'X' Name.Variable '*' Operator '2' Literal.Number.Integer ' ' Text '|' Punctuation '|' Punctuation ' ' Text 'X' Name.Variable ' ' Text '<' Operator '-' Operator ' ' Text '[' Punctuation '1' Literal.Number.Integer ',' Punctuation '2' Literal.Number.Integer ',' Punctuation '3' Literal.Number.Integer ']' Punctuation ']' Punctuation '.' Punctuation '\n\n' Text 'a_map' Name.Function '(' Punctuation ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'M0' Name.Variable ' ' Text '=' Operator ' ' Text '#{' Punctuation ' ' Text 'a' Name ' ' Text '=' Operator '>' Operator ' ' Text '1' Literal.Number.Integer ',' Punctuation ' ' Text 'b' Name ' ' Text '=' Operator '>' Operator ' ' Text '2' Literal.Number.Integer ' ' Text '}' Punctuation ',' Punctuation '\n ' Text 'M1' Name.Variable ' ' Text '=' Operator ' ' Text 'M0' Name.Variable '#{' Punctuation ' ' Text 'b' Name ' ' Text ':' Punctuation '=' Operator ' ' Text '200' Literal.Number.Integer ' ' Text '}' Punctuation '.' Punctuation '\n\n' Text 'escape_sequences' Name.Function '(' Punctuation ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text '[' Punctuation ' ' Text '"' Literal.String '\\b' Literal.String.Escape '\\d' Literal.String.Escape '\\e' Literal.String.Escape '\\f' Literal.String.Escape '\\n' Literal.String.Escape '\\r' Literal.String.Escape '\\s' Literal.String.Escape '\\t' Literal.String.Escape '\\v' Literal.String.Escape "\\'" Literal.String.Escape '\\"' Literal.String.Escape '\\\\' Literal.String.Escape '"' Literal.String '\n ' Text ',' Punctuation ' ' Text '"' Literal.String '\\1' Literal.String.Escape '\\12' Literal.String.Escape '\\123' Literal.String.Escape '"' Literal.String ' ' Text '% octal\n' Comment ' ' Text ',' Punctuation ' ' Text '"' Literal.String '\\x01' Literal.String.Escape '"' Literal.String ' ' Text '% short hex\n' Comment ' ' Text ',' Punctuation ' ' Text '"' Literal.String '\\x{fff}' Literal.String.Escape '"' Literal.String ' ' Text '% long hex\n' Comment ' ' Text ',' Punctuation ' ' Text '"' Literal.String '\\^a' Literal.String.Escape '\\^A' Literal.String.Escape '"' Literal.String ' ' Text '% control characters\n' Comment ' ' Text ']' Punctuation '.' Punctuation '\n\n' Text 'map' Name.Function '(' Punctuation 'Fun' Name.Variable ',' Punctuation ' ' Text '[' Punctuation 'H' Name.Variable '|' Punctuation 'T' Name.Variable ']' Punctuation ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text '[' Punctuation 'Fun' Name.Variable '(' Punctuation 'H' Name.Variable ')' Punctuation ' ' Text '|' Punctuation ' ' Text 'map' Name '(' Punctuation 'Fun' Name.Variable ',' Punctuation ' ' Text 'T' Name.Variable ')' Punctuation ']' Punctuation ';' Punctuation '\n\n' Text 'map' Name.Function '(' Punctuation 'Fun' Name.Variable ',' Punctuation ' ' Text '[' Punctuation ']' Punctuation ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text '[' Punctuation ']' Punctuation '.' Punctuation '\n\n' Text "%% pmap, just because it's cool.\n" Comment '\n' Text 'pmap' Name.Function '(' Punctuation 'F' Name.Variable ',' Punctuation ' ' Text 'L' Name.Variable ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'Parent' Name.Variable ' ' Text '=' Operator ' ' Text 'self' Name '(' Punctuation ')' Punctuation ',' Punctuation '\n ' Text '[' Punctuation 'receive' Keyword ' ' Text '{' Punctuation 'Pid' Name.Variable ',' Punctuation ' ' Text 'Result' Name.Variable '}' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'Result' Name.Variable '\n ' Text 'end' Keyword ' ' Text '|' Punctuation '|' Punctuation ' ' Text 'Pid' Name.Variable ' ' Text '<' Operator '-' Operator ' ' Text '[' Punctuation 'spawn' Name.Builtin '(' Punctuation 'fun' Keyword '(' Punctuation ')' Punctuation ' ' Text '-' Operator '>' Operator '\n ' Text 'Parent' Name.Variable ' ' Text '!' Operator ' ' Text '{' Punctuation 'self' Name '(' Punctuation ')' Punctuation ',' Punctuation ' ' Text 'F' Name.Variable '(' Punctuation 'X' Name.Variable ')' Punctuation '}' Punctuation ' \n ' Text 'end' Keyword ')' Punctuation ' ' Text '|' Punctuation '|' Punctuation ' ' Text 'X' Name.Variable ' ' Text '<' Operator '-' Operator ' ' Text 'L' Name.Variable ']' Punctuation ']' Punctuation '.' Punctuation '\n' Text