Redis protocol parser and writer.

author
- Can Bican
license
- GPL

*/

    9:- module(resp,[resp_write/2, resp_parse/2]).   10
   11unicode_size(Char,Size) :-
   12	\+ integer(Char),
   13	char_code(Char,Code),
   14	unicode_size(Code,Size),
   15	!.
   16unicode_size(0,0).
   17unicode_size(Code,1) :- Code =< 127.
   18unicode_size(Code,2) :- Code > 127 , Code =< 2047.
   19unicode_size(Code,3) :- Code > 2047 , Code =< 65535.
   20unicode_size(Code,4) :- Code > 65535 , Code =< 1114111.
   21
   22unicode_byte_length(String,Length) :-
   23	string_codes(String,Codes),
   24	unicode_byte_length_list(Codes,Length).
   25
   26unicode_byte_length_list([],0) :- !.
   27unicode_byte_length_list([Code|Codes],Length) :-
   28	unicode_size(Code,L0),
   29	unicode_byte_length_list(Codes,L),
   30	Length is L0 + L.
   31
   32get_chars(_,[],0) :- !.
   33get_chars(Stream,[First|Rest],Count) :-
   34	get_char(Stream,First),
   35	unicode_size(First,SS),
   36	C is Count - SS,
   37	get_chars(Stream,Rest,C),
   38	!.
 resp_write(Stream, List) is nondet
Write contents of the List to the Stream in Redis protocol format.
   45resp_write(Stream,Array) :-
   46	resp_write(Stream,Array,false).
   47
   48resp_write(_,[],true) :-
   49	!.
   50
   51resp_write(Stream,[],_) :-
   52	format(Stream,"*0~w",["\r\n"]),
   53	!.
   54resp_write(Stream,[nil],_) :-
   55	format(Stream,"*-1~w",["\r\n"]),
   56	!.
   57resp_write(Stream,[First|Rest],IsFirst) :-
   58	is_list([First|Rest]),
   59	length([First|Rest],Length),
   60	( IsFirst == true ;
   61	format(Stream,"*~w~w",[Length,"\r\n"])),
   62	resp_write_one(Stream,First),
   63	resp_write(Stream,Rest,true),
   64	!.
   65resp_write(Stream,Term,_) :-
   66	resp_write_one(Stream,Term),
   67	!.
   68
   69resp_write_one(Stream,string(String)) :-
   70	format(Stream,"+~w~w",[String,"\r\n"]).
   71resp_write_one(Stream,error(String)) :-
   72	format(Stream,"-~w~w",[String,"\r\n"]).
   73resp_write_one(Stream,integer(String)) :-
   74	format(Stream,":~w~w",[String,"\r\n"]).
   75resp_write_one(Stream,bulk(nil)) :-
   76	format(Stream,"$~w~w",["-1","\r\n"]).
   77resp_write_one(Stream,bulk(String)) :-
   78	unicode_byte_length(String,Length),
   79	format(Stream,"$~w~w~w~w",[Length,"\r\n",String,"\r\n"]).
   80resp_write_one(Stream,Array) :-
   81	is_list(Array),
   82	resp_write(Stream,Array).
   83
   84resp_writef_array(Stream,Array) :-
   85	format(Stream,"~w",[Array]).
   86
   87resp_write_one_array([],"") :- !.
   88resp_write_one_array([First|Rest],ResultString) :-
   89	new_memory_file(MF),
   90	open_memory_file(MF, write, Stream),
   91	resp_write_one(Stream,First),
   92	close(Stream),
   93	memory_file_to_string(MF,FirstResult,utf8),
   94	free_memory_file(MF),
   95	resp_write_one_array(Rest,ResultRest),
   96	swritef(ResultString,"%w%w",[FirstResult,ResultRest]).
 resp_parse(Stream, List) is nondet
Parse the input from Stream as a List of predicates.
  102resp_parse(Stream,Result) :-
  103	read_line_to_codes(Stream,[TypeCode|ArgumentCodes]),
  104	char_code(Type,TypeCode),
  105	atom_chars(Arguments,ArgumentCodes),
  106	resp_parse(Type,Arguments,Stream,Result).
  107	% resp_parse(Stream,RestResult).
  108resp_parse(_,[]) :- !.
  109
  110resp_parse('+',Argument,_,string(Argument)) :- !.
  111resp_parse(':',Argument,_,integer(ArgumentInteger)) :-
  112	atom_number(Argument,ArgumentInteger),
  113	!.
  114resp_parse('-',Argument,_,error(Argument)) :- !.
  115resp_parse('$',Argument,_,bulk(nil)) :-
  116	atom_number(Argument,Count),
  117	Count < 0,
  118	!.
  119resp_parse('$',Argument,Stream,bulk(Result)) :-
  120	atom_number(Argument,Count),
  121	get_chars(Stream,ResultChars,Count),
  122	string_codes(Result,ResultChars),
  123	get_code(Stream,_),
  124	get_code(Stream,_),
  125	!.
  126resp_parse('*',Argument,Stream,Result) :-
  127	atom_number(Argument,Count),
  128	resp_parse_array(Stream,Count,Result),
  129	!.
  130
  131resp_parse_array(_,-1,[nil]) :- !.
  132resp_parse_array(_,0,[]) :- !.
  133resp_parse_array(Stream,Count,[First|Rest]) :-
  134	read_line_to_codes(Stream,[TypeCode|ArgumentCodes]),
  135	char_code(Type,TypeCode),
  136	atom_chars(Arguments,ArgumentCodes),
  137	resp_parse(Type,Arguments,Stream,First),
  138	Count1 is Count - 1,
  139	resp_parse_array(Stream,Count1,Rest),
  140	!