Created
January 2, 2012 16:50
-
-
Save HarryR/1551348 to your computer and use it in GitHub Desktop.
Redis server-side protocol parser for Ragel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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" | |
":[email protected] PRIVMSG #wiremod :No.\r\n" | |
":ccfreak2k@@[email protected] PRIVMSG #wiremod :No.\r\n" | |
":[email protected] 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