Skip to content

Instantly share code, notes, and snippets.

@m4rw3r
Last active September 7, 2015 17:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save m4rw3r/4e82c4ee10deb1e141fc to your computer and use it in GitHub Desktop.
Save m4rw3r/4e82c4ee10deb1e141fc to your computer and use it in GitHub Desktop.
Version of the attoparsec example using the parser combinator Combine version 1.0.0.
extern crate combine;
use combine::*;
use combine::primitives::Error;
use std::fs::File;
use std::env;
#[derive(Debug)]
struct Request {
method: Vec<u8>,
uri: Vec<u8>,
version: Vec<u8>,
}
#[derive(Debug)]
struct Header {
name: Vec<u8>,
value: Vec<Vec<u8>>,
}
fn is_token(c: u8) -> bool {
// roughly follows the order of ascii chars: "\"(),/:;<=>?@[\\]{} \t"
c < 128 && c > 32 && c != b'\t' && c != b'"' && c != b'(' && c != b')' &&
c != b',' && c != b'/' && !(c > 57 && c < 65) && !(c > 90 && c < 94) &&
c != b'{' && c != b'}'
}
fn is_horizontal_space(c: u8) -> bool { c == b' ' || c == b'\t' }
fn is_space(c: u8) -> bool { c == b' ' }
fn is_not_space(c: u8) -> bool { c != b' ' }
fn is_http_version(c: u8) -> bool { c >= b'0' && c <= b'9' || c == b'.' }
fn main() {
let mut contents: Vec<u8> = Vec::new();
{
use std::io::Read;
let mut file = File::open(env::args().nth(1).expect("File to read")).ok().expect("Failed to open file");
let _ = file.read_to_end(&mut contents).unwrap();
}
// Making a closure, because parser instances cannot be reused
let end_of_line = || (satisfy(|c| c == b'\r').skip(satisfy(|c| c == b'\n'))).or(satisfy(|c| c == b'\n'));
// let mut http_version = string(&b"HTTP/"[..])
let mut http_version = satisfy(|c| c == b'H')
.with(satisfy(|c| c == b'T'))
.with(satisfy(|c| c == b'T'))
.with(satisfy(|c| c == b'P'))
.with(satisfy(|c| c == b'/'))
.with(many1(satisfy(|c| is_http_version(c))));
let request_line = parser(|input| (
// dereferencing pointers before checking if it is a token
many1(satisfy(|c| is_token(c))),
skip_many1(satisfy(|c| is_space(c))),
many1(satisfy(|c| is_not_space(c))),
skip_many1(satisfy(|c| is_space(c))),
&mut http_version,
).map(|(method, _, uri, _, version)| Request {
method: method,
uri: uri,
version: version,
})
.parse_lazy(input)
);
let message_header = parser(|input| {
let message_header_line = (
skip_many1(satisfy(|c| is_horizontal_space(c))),
many1(satisfy(|c| c != b'\r' && c != b'\n')),
end_of_line()
).map(|(_, line, _)| line);
(
many1(satisfy(|c| is_token(c))),
satisfy(|c| c == b':'),
many1(message_header_line)
).map(|(name, _, value)| Header {
name: name,
value: value,
})
.parse_lazy(input)
});
let mut request = (
request_line,
end_of_line(),
many(message_header),
end_of_line()
).map(|(request, _, headers, _)| (request, headers));
let mut i = 0;
let mut buf = &contents[..];
loop {
// Needed for inferrence for many(message_header)
let r: Result<((Request, Vec<Header>), _), _> = request.parse(buf);
match r {
Ok(((_, _), b)) => {
i = i + 1;
buf = b
},
Err(e) => {
if e.errors[0] == Error::end_of_input() {
return
}
panic!("{:?}", e);
}
}
if buf.is_empty() {
break;
}
}
println!("num: {}", i);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment