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.
   44resp_write(_,[]) :- !.
   45resp_write(Stream,[First|Rest]) :-
   46	resp_write_one(Stream,First),
   47	resp_write(Stream,Rest).
   48
   49resp_write_one(Stream,string(String)) :-
   50	format(Stream,"+~w~w",[String,"\r\n"]).
   51resp_write_one(Stream,error(String)) :-
   52	format(Stream,"-~w~w",[String,"\r\n"]).
   53resp_write_one(Stream,integer(String)) :-
   54	format(Stream,":~w~w",[String,"\r\n"]).
   55resp_write_one(Stream,bulk(nil)) :-
   56	format(Stream,"$~w~w",["-1","\r\n"]).
   57resp_write_one(Stream,bulk(String)) :-
   58	unicode_byte_length(String,Length),
   59	format(Stream,"$~w~w~w~w",[Length,"\r\n",String,"\r\n"]).
   60resp_write_one(Stream,array(nil)) :-
   61	format(Stream,"*-1~w",["\r\n"]),
   62	!.
   63resp_write_one(Stream,array(Array)) :-
   64	resp_write_one_array(Array,ResultArray),
   65	length(Array,Length),
   66	format(Stream,"*~w~w~w",[Length,"\r\n",ResultArray]).
   67
   68resp_write_one_array([],"") :- !.
   69resp_write_one_array([First|Rest],ResultString) :-
   70	new_memory_file(MF),
   71	open_memory_file(MF, write, Stream),
   72	resp_write_one(Stream,First),
   73	close(Stream),
   74	memory_file_to_string(MF,FirstResult,utf8),
   75	free_memory_file(MF),
   76	resp_write_one_array(Rest,ResultRest),
   77	string_concat(FirstResult,ResultRest,ResultString).
 resp_parse(Stream, List) is nondet
Parse the input from Stream as a List of predicates.
   83resp_parse(Stream,[Result|RestResult]) :-
   84	read_line_to_codes(Stream,[TypeCode|ArgumentCodes]),
   85	char_code(Type,TypeCode),
   86	atom_chars(Arguments,ArgumentCodes),
   87	resp_parse(Type,Arguments,Stream,Result),
   88	resp_parse(Stream,RestResult).
   89resp_parse(_,[]) :- !.
   90
   91resp_parse('+',Argument,_,string(Argument)) :- !.
   92resp_parse(':',Argument,_,integer(ArgumentInteger)) :-
   93	atom_number(Argument,ArgumentInteger),
   94	!.
   95resp_parse('-',Argument,_,error(Argument)) :- !.
   96resp_parse('$',Argument,_,bulk(nil)) :-
   97	atom_number(Argument,Count),
   98	Count < 0,
   99	!.
  100resp_parse('$',Argument,Stream,bulk(Result)) :-
  101	atom_number(Argument,Count),
  102	get_chars(Stream,ResultChars,Count),
  103	string_codes(Result,ResultChars),
  104	seek(Stream,2,current,_),
  105	!.
  106resp_parse('*',Argument,Stream,array(Result)) :-
  107	atom_number(Argument,Count),
  108	resp_parse_array(Stream,Count,Result),
  109	!.
  110
  111resp_parse_array(_,-1,nil) :- !.
  112resp_parse_array(_,0,[]) :- !.
  113resp_parse_array(Stream,Count,[First|Rest]) :-
  114	read_line_to_codes(Stream,[TypeCode|ArgumentCodes]),
  115	char_code(Type,TypeCode),
  116	atom_chars(Arguments,ArgumentCodes),
  117	resp_parse(Type,Arguments,Stream,First),
  118	Count1 is Count - 1,
  119	resp_parse_array(Stream,Count1,Rest),
  120	!