1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
//! Gobble is a simple parser combinator system for parsing strings. //! //! For example parsing a function call //! //! ```rust //! use gobble::*; //! let ident = || string_2_parts(Alpha.min_n(1),(Alpha,NumDigit,'_').any()); //! //! let fsig = (ident().then_ig("("),sep(ident(),",",0).then_ig(")")); //! //! let (nm, args) = fsig.parse_s("loadFile1(fname,ref)").unwrap(); //! assert_eq!(nm, "loadFile1"); //! assert_eq!(args, vec!["fname", "ref"]); //! //! //identifiers cant start with numbers, //! assert!(fsig.parse_s("23file(fname,ref)").is_err()); //! //! ``` //! //! To work this library depends the following: //! //! ```rust //! pub enum ParseError { //! //... //! } //! //The LCChars in the result will be a clone of the incoming iterator //! //but having iterated to end of the what the parser required. //! pub type ParseRes<'a, V> = Result<(LCChars<'a>, V), ParseError>; //! //! //implements Iterator and can be cloned relatively cheaply //! pub struct LCChars<'a>{ //! it:std::str::Chars<'a>, //! line:usize, //! col:usize, //! } //! //! pub trait Parser<V> { //! // Takes a non mut pointer to the iterator, so that the caller //! // may try something else if this doesn't work //! // clone it before reading next //! fn parse<'a>(&self,it:&LCChars<'a>)->ParseRes<'a,V>; //! //! //...helper methods //! } //! pub trait BoolChar { //! fn bool_char(&self,c:char)->bool; //! //....helper methods //! } //! ``` //! //! Parser is automatically implemented for: //! * ```Fn<'a>(&LCChars<'a>)->ParseRes<'a,String>``` //! * ```&'static str``` which will return itself if it matches //! * ```char``` which will return itself if it matched the next char //! * Tuples of up to 6 parsers. Returning a tuple of all the //! parsers matched one after the //! other. //! //! Most of the time a parser can be built simply by combining other parsers //! ```rust //! use gobble::*; //! //! // map can be used to convert one result to another //! // keyval is now a function that returns a parser //! let keyval = || (common_ident,":",common_str).map(|(a,_,c)|(a,c)); //! //! //this can also be written as below for better type safety //! fn keyval2()->impl Parser<(String,String)>{ //! (common_ident,":",common_str).map(|(a,_,c)|(a,c)) //! } //! //! //parse_s is a helper on Parsers //! let (k,v) = keyval().parse_s(r#"car:"mini""#).unwrap(); //! assert_eq!(k,"car"); //! assert_eq!(v,"mini"); //! //! //this can now be combined with other parsers. //! // 'ig_then' combines 2 parsers and drops the result of the first //! // 'then_ig' drops the result of the second //! // 'sep_until will repeat the first term into a Vec, separated by the second //! // until the final term. //! let obj = || "{".ig_then(sep_until(keyval(),",","}")); //! //! let obs = obj().parse_s(r#"{cat:"Tiddles",dog:"Spot"}"#).unwrap(); //! assert_eq!(obs[0],("cat".to_string(),"Tiddles".to_string())); //! //! ``` //! ## CharBool //! //! CharBool is the trait for boolean char checks. It is auto implemented for: //! * Fn(char)->bool //! * char -- Returns true if the input matches the char //! * &'static str -- returns true if the str contains the input //! * several zero size types - Alpha,NumDigit,HexDigit,WS,WSL,Any //! * Tuples of up to 6 CharBools -- returning true if any of the members succeed //! //! This means you can combine them in tuples ```(Alpha,NumDigit,"_").char_bool(c)``` //! will be true if any of them match //! //! //! //! CharBool also provides 3 helper methods which each return a parser //! * ```one()``` matches and returns exactly 1 character //! * ```min_n(n)``` requires at least n matches ruturns a string //! * ```any()``` matches any number of chars returning a string //! //! And a helper that returns a CharBool //! * ```except(cb)``` Passes if self does, and cb doesnt //! ```rust //! use gobble::*; //! let s = |c| c > 'w' || c == 'z'; //! let xv = s.one().parse_s("xhello").unwrap(); //! assert_eq!(xv,'x'); //! //! let id = (Alpha,"_*").min_n(4).parse_s("sm*shing_game+you").unwrap(); //! assert_eq!(id,"sm*shing_game"); //! //! // not enough matches //! assert!((NumDigit,"abc").min_n(4).parse_s("23fflr").is_err()); //! //! // any succeeds even with no matches equivilent to min(0) //! assert_eq!((NumDigit,"abc").any().parse_s("23fflr"),Ok("23".to_string())); //! assert_eq!((NumDigit,"abc").any().parse_s("fflr"),Ok("".to_string())); //! //! ``` //! //! ## White Space //! //! White space is pretty straight forward to handle //! //! ```rust //! use gobble::*; //! let my_ws = || " \t".any(); //! // middle takes three parsers and returns the result of the middle //! // this could also be done easily with 'map' or 'then_ig' //! let my_s = |p| middle(my_ws(),p,my_ws()); //! //! let sp_id = my_s(common_ident); //! let v = sp_id.parse_s(" \t doggo ").unwrap(); //! assert_eq!(v,"doggo"); //! ``` //! That said gobble already provides ```ws()``` and ```s_(p)``` //! //! ```rust //! use gobble::*; //! //eoi = end of input //! let p = repeat_until_ig(s_("abc".min_n(1)),eoi); //! let r = p.parse_s("aaa \tbbb bab").unwrap(); //! assert_eq!(r,vec!["aaa","bbb","bab"]); //! ``` //! //! ## Recursive Structures //! //! Some structures like Json, or programming languages need to be able to //! handle recursion. However with the techniques we have used so far //! this would lead to infinitely sized structures. //! //! The way to handle this is to make sure one member of the loop is not //! build into the structure. Instead to create it using the 'Fn' //! //! ```rust //! use gobble::*; //! #[derive(Debug,PartialEq)] //! enum Expr { //! Val(isize), //! Add(Box<Expr>,Box<Expr>), //! Paren(Box<Expr>), //! } //! //! fn expr_l()->impl Parser<Expr>{ //! or( //! middle("(",s_(expr),")").map(|e|Expr::Paren(Box::new(e))), //! common_int.map(|v|Expr::Val(v)) //! ) //! } //! //! // using the full fn def we avoid the recursive structure //! fn expr<'a>(it:&LCChars<'a>)->ParseRes<'a,Expr> { //! //note that expr_l has brackets but expr doesnt. //! //expr is a reference to a static function //! let p = (expr_l(),maybe(s_("+").ig_then(expr))) //! .map(|(l,opr)|match opr{ //! Some(r)=>Expr::Add(Box::new(l),Box::new(r)), //! None=>l, //! }); //! //! //! p.parse(it) //! } //! //! let r = expr.parse_s("45 + (34+3 )").unwrap(); //! //! //recursive structures are never fun to write manually //! assert_eq!(r,Expr::Add( //! Box::new(Expr::Val(45)), //! Box::new(Expr::Paren(Box::new(Expr::Add( //! Box::new(Expr::Val(34)), //! Box::new(Expr::Val(3)) //! )))) //! )); //! //! ``` pub mod chars; pub mod combi; pub mod common; pub mod err; pub mod iter; pub mod ptrait; pub mod reader; pub mod repeater; pub mod skip; pub mod strings; pub mod tuple; pub use chars::*; pub use combi::*; pub use common::*; pub use err::*; pub use iter::*; pub use ptrait::*; pub use reader::*; pub use repeater::*; pub use skip::*; pub use strings::*; pub use tuple::*;