Differences
This shows you the differences between two versions of the page.
| lib:parser [2019/01/08 08:37] – created sprowell | lib:parser [2019/01/08 09:42] (current) – sprowell | ||
|---|---|---|---|
| Line 21: | Line 21: | ||
| <code Ada> | <code Ada> | ||
| -- The best pseudocode is written in Ada for some reason. | -- The best pseudocode is written in Ada for some reason. | ||
| - | while peek is a digit loop | + | while is_digit(peek) loop |
| consume; | consume; | ||
| end loop; | end loop; | ||
| Line 27: | Line 27: | ||
| Composites of these primitives are available to simplify things. | Composites of these primitives are available to simplify things. | ||
| - | the implementation of the above pseudocode might look like the code below, | + | the implementation of the above pseudocode might look like the code below, |
| + | |||
| + | <code Rust> | ||
| + | fn parse_unsigned_integer< | ||
| + | let mut result = String:: | ||
| + | while let Some(ch) = parser.peek()? | ||
| + | if ch.is_digit(10) { | ||
| + | result.push(ch); | ||
| + | parser.consume(); | ||
| + | } else { | ||
| + | break; | ||
| + | } | ||
| + | } | ||
| + | match result.parse::< | ||
| + | Ok(number) => Ok(number), | ||
| + | Err(err) => Err(parser.error(err.to_string())), | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | This is terrible. | ||
| + | |||
| + | Here the integer parsing is broken out into a separate function so we can use the ''?'' | ||
| <code Rust> | <code Rust> | ||
| Line 59: | Line 81: | ||
| If nothing is entered we get '' | If nothing is entered we get '' | ||
| + | Of course, this probably isn't what we want. Suppose we have a stream of identifiers and numbers, and we want to parse these. | ||
| + | |||
| + | We already know how to parse unsigned integers. | ||
| + | |||
| + | <code Rust> | ||
| + | fn parse_identifier< | ||
| + | let mut result = parser.take_while(|ch| ch.is_alphabetic())?; | ||
| + | result = result + parser.take_while(|ch| ch.is_alphanumeric())? | ||
| + | Ok(result) | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Okay, now let's create a type for our tokens. | ||
| + | |||
| + | <code Rust> | ||
| + | # | ||
| + | enum Thing { | ||
| + | Number(u64), | ||
| + | Id(String), | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Now we can parse the sequence, and create a vector of '' | ||
| + | |||
| + | <code Rust> | ||
| + | fn parse_sequence< | ||
| + | let mut things = Vec::new(); | ||
| + | let _ = parser.consume_whitespace(); | ||
| + | while !parser.at_eof() { | ||
| + | match parser.peek() { | ||
| + | Ok(Some(ch)) => { | ||
| + | if ch.is_digit(10) { | ||
| + | things.push(Thing:: | ||
| + | } else if ch.is_alphabetic() { | ||
| + | things.push(Thing:: | ||
| + | } else { | ||
| + | return Err(parser.unexpected_char(" | ||
| + | } | ||
| + | } | ||
| + | Ok(None) => break, | ||
| + | Err(err) => return Err(err), | ||
| + | } | ||
| + | let _ = parser.consume_whitespace(); | ||
| + | } | ||
| + | Ok(things) | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Now we just need a main function to pull this all together. | ||
| + | |||
| + | <code Rust> | ||
| + | fn main() { | ||
| + | let mut parser = parser:: | ||
| + | match parse_sequence(& | ||
| + | Err(err) => { | ||
| + | println!(" | ||
| + | } | ||
| + | Ok(seq) => { | ||
| + | println!(" | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | That's pretty much it. If it seems like there' | ||