Skip to content

Instantly share code, notes, and snippets.

@HarryR
Created January 2, 2012 16:50
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save HarryR/1551348 to your computer and use it in GitHub Desktop.
Save HarryR/1551348 to your computer and use it in GitHub Desktop.
Redis server-side protocol parser for Ragel
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
%%{
machine redis_parser;
action reset_line {
if(line.argv) {
free(line.argv);
line.argv = NULL;
}
memset(&line, 0, sizeof(struct batchline));
}
action mark {
mark = fpc - data;
}
action write_arg {
if( line.argv == NULL ) {
line.argv = malloc(sizeof(struct slice));
line.argc = 1;
}
else {
line.argc += 1;
line.argv = realloc(line.argv, sizeof(struct slice) * line.argc);
}
line.argv[line.argc-1].data = data + mark;
line.argv[line.argc-1].len = (fpc - data) - mark;
}
action arg_err {
printf("error parsing argument! %.*s %zu/%zu continuing happily\n\n", (int)((fpc - data) - mark), data + mark, fpc - data, mark);
fhold; fgoto gobble_line;
}
action write_message {
if( ! line.ignore ) {
if( line.argc ) {
for(size_t i = 0; i < line.argc; i++) {
printf("arg %zu:%.*s\n", i, (int)line.argv[i].len, line.argv[i].data);
}
}
printf("--------------------\n");
}
}
action message_err {
printf("error parsing message! %zu/%zu continuing happily\n\n", fpc - data, mark);
fhold; fgoto gobble_line;
}
action write_arg_len {
printf("arg len: '%.*s' %zu/%zu\n\n", (fpc - data) - mark, data + mark, fpc - data, mark);
}
action argc_reset {
line.parse_arg_count = 0;
}
action argc_add_digit {
line.parse_arg_count = line.argc * 10 + (fc - '0');
}
action argsz_reset {
line.parse_arg_len = 0;
}
action argsz_add_digit {
line.parse_arg_len = line.parse_arg_len * 10 + (fc - '0');
}
action test_arg_len {
((fpc - data) - mark) < line.parse_arg_len
}
action set_unified {
line.unified = true;
}
action set_inline {
line.unified = false;
}
action arg_count_ok {
( ! line.unified || (line.argc < line.parse_arg_count))
}
action set_ignore {
line.ignore = true;
}
separator = " ";
int = [0-9]+;
crlf = ("\r\n" | "\n");
gobble_line := (any -- crlf)* crlf @{ fcall main; };
uni_arg_len = "$" @argsz_reset (digit @argsz_add_digit)+ crlf >mark;
uni_arg = uni_arg_len (any when test_arg_len)+ >mark %write_arg;
uni_arg_count = "*" @argc_reset (digit @argc_add_digit)+ crlf;
uni_request = uni_arg_count (uni_arg <: crlf)+ @set_unified;
arg_valid = any -- (separator | "\z" | "\r" | crlf);
cmd_valid = any -- ("*" | "$" | "#" | separator | "\z" | "\r" | crlf);
inl_cmd = cmd_valid+ >mark %write_arg $err(arg_err);
inl_arg = arg_valid+ >mark %write_arg $err(arg_err);
inl_request = inl_cmd (separator inl_arg when arg_count_ok)* crlf @set_inline;
comment_line = space* "#" @set_ignore (any -- crlf)+ crlf;
message = (comment_line|inl_request|uni_request) %write_message >reset_line <err(message_err);
main := message+;
}%%
%% write data;
const char *data =
"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$9\r\nmyva\r\nlue\r\n"
"*2\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"
"+ DERP\r\n"
" #test\n"
"#test\n"
"#derp\n"
"PING $(DERP) 3\r\n"
"PING 1234\r\n"
"1234\r\n"
"PIN\rG\r\n"
"PRIVMSG #luahelp :say something\r\n"
"PRIVMSG :say something\r\n"
":user!~ccfreak2k@4chan.fm PRIVMSG #wiremod :No.\r\n"
":ccfreak2k@@!~ccfreak2k@4chan.fm PRIVMSG #wiremod :No.\r\n"
":ccfreak2k!~ccfreak2k@4chan.fm PRIVMSG #wi\rremod :No.\r\n"
"PRIVMSG abc :def hij\r\n";
struct slice
{
const char* data;
int len;
};
struct batchline
{
struct slice* argv;
size_t argc;
size_t parse_arg_count;
size_t parse_arg_len;
bool unified;
bool ignore;
};
int main(int argc, char **argv)
{
int cs;
size_t mark;
struct batchline line;
memset(&line, 0, sizeof(struct batchline));
const char *p = data;
const char *pe = p + strlen(p);
const char *eof = pe;
int stack[10] = {0};
int top = 0;
%% write init;
%% write exec;
printf("Finished at state %i\n", cs);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment