From 13236736e7ef762255468f2e54dcc712c54966f9 Mon Sep 17 00:00:00 2001 From: ltrujello Date: Fri, 2 Dec 2022 07:50:10 +0000 Subject: [PATCH] Replaces broken tutorial code with working example --- doc/making_a_new_parser_from_scratch.md | 98 +++++++++++++++++++------ 1 file changed, 76 insertions(+), 22 deletions(-) diff --git a/doc/making_a_new_parser_from_scratch.md b/doc/making_a_new_parser_from_scratch.md index 8a87a396c..e7f722371 100644 --- a/doc/making_a_new_parser_from_scratch.md +++ b/doc/making_a_new_parser_from_scratch.md @@ -99,29 +99,83 @@ As an example, here is how we could build a (non spec compliant) HTTP request line parser: ```rust +use nom::{ + bytes::complete::{tag, take_while1}, + character::{is_alphabetic, is_space}, + sequence::{preceded, tuple}, + IResult, +}; +use std::str; + +#[derive(Debug, PartialEq)] +pub struct Request<'a> { + pub method: &'a [u8], + pub url: &'a [u8], + pub version: &'a [u8], +} + +fn is_not_space(c: u8) -> bool { + !is_space(c) +} + +fn is_version(c: u8) -> bool { + c >= b'0' && c <= b'9' || c == b'.' +} + // first implement the basic parsers -let method = take_while1(is_alpha); -let space = take_while1(|c| c == ' '); -let url = take_while1(|c| c!= ' '); -let is_version = |c| c >= b'0' && c <= b'9' || c == b'.'; -let http = tag("HTTP/"); -let version = take_while1(is_version); -let line_ending = tag("\r\n"); - -// combine http and version to extract the version string -// preceded will return the result of the second parser -// if both succeed -let http_version = preceded(http, version); - -// combine all previous parsers in one function -fn request_line(i: &[u8]) -> IResult<&[u8], Request> { - - // tuple takes as argument a tuple of parsers and will return - // a tuple of their results - let (input, (method, _, url, _, version, _)) = - tuple((method, space, url, space, http_version, line_ending))(i)?; - - Ok((input, Request { method, url, version })) +fn method(s: &[u8]) -> IResult<&[u8], &[u8]> { + take_while1(is_alphabetic)(s) +} + +fn space(s: &[u8]) -> IResult<&[u8], &[u8]> { + take_while1(is_space)(s) +} + +fn url(s: &[u8]) -> IResult<&[u8], &[u8]> { + take_while1(is_not_space)(s) +} + +fn http(s: &[u8]) -> IResult<&[u8], &[u8]> { + tag("HTTP/")(s) +} +fn version(s: &[u8]) -> IResult<&[u8], &[u8]> { + take_while1(is_version)(s) +} +fn line_ending(s: &[u8]) -> IResult<&[u8], &[u8]> { + tag("\r\n")(s) +} + +fn http_version(s: &[u8]) -> IResult<&[u8], &[u8]> { + preceded(http, version)(s) +} + +fn main() { + // combine all previous parsers in one function + fn request_line(i: &[u8]) -> IResult<&[u8], Request> { + // tuple takes as argument a tuple of parsers and will return + // a tuple of their results + let (input, (method, _, url, _, version, _)) = + tuple((method, space, url, space, http_version, line_ending))(i)?; + + println!("{:#?}", str::from_utf8(method)); + println!("{:#?}", str::from_utf8(url)); + println!("{:#?}", str::from_utf8(version)); + + Ok(( + input, + Request { + method, + url, + version, + }, + )) + } + + // combine http and version to extract the version string + // preceded will return the result of the second parser + // if both succeed + let s = "GET documents HTTP/1.1\r\n".as_bytes(); + request_line(s); } ```