Last active
May 18, 2017 22:45
-
-
Save syusui-s/95a67a4336e28f5635e30c1cc8548235 to your computer and use it in GitHub Desktop.
「Java パーサコンビネータ 超入門」Rust version( http://qiita.com/7shi/items/68228e19552c271bea81 )
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
use std::error::Error; | |
use std::iter::FromIterator; | |
use std::fmt; | |
#[derive(Debug)] | |
struct ParseError { | |
message: &'static str, | |
} | |
impl ParseError { | |
fn new(message: &'static str) -> Self { | |
ParseError { message } | |
} | |
} | |
impl Error for ParseError { | |
fn description(&self) -> &str { self.message } | |
} | |
impl fmt::Display for ParseError { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
write!(f, "parse error: {}", self.message) | |
} | |
} | |
struct Source { | |
src: String, | |
pos: usize, | |
} | |
impl Source { | |
fn new(src: String) -> Self { | |
Self { src: src, pos: 0, } | |
} | |
fn from_str(src: &str) -> Self { | |
Self::new(src.into()) | |
} | |
} | |
impl Iterator for Source { | |
type Item = char; | |
fn next(&mut self) -> Option<Self::Item> { | |
let result = self.src.chars().nth(self.pos); | |
self.pos += 1; | |
result | |
} | |
} | |
///////////////////////////////////////////////////////////////////// | |
type ParserResult<T> = Result<T, ParseError>; | |
type Parser<T> = fn(&mut Source) -> ParserResult<T>; | |
macro_rules! satisfy { | |
( $n:ident, $x:path $(, $a:expr )* ) => { | |
fn $n(src: &mut Source) -> ParserResult<char> { | |
match src.next() { | |
Some(ch) => { | |
if ($x)(ch$(, $a)*) { Ok(ch) } | |
else { Err(ParseError::new("not satisfy")) } | |
}, | |
None => Err(ParseError::new("too short")), | |
} | |
} | |
}; | |
} | |
macro_rules! or { | |
( $n:ident, $t:ty, $x:path, $y:path) => { | |
fn $n(src: &mut Source) -> ParserResult<$t> { | |
let pos = src.pos() | |
if let Ok(result) = ($x)(src) { | |
return Ok(result); | |
} | |
src.set_pos(pos); | |
if let Ok(result) = ($y)(src) { | |
return Ok(result); | |
} else { | |
return Err(ParseError::Ok); | |
} | |
} | |
}; | |
} | |
fn any_char(s: &mut Source) -> ParserResult<char> { | |
s.next().ok_or(ParseError::new("too short")) | |
} | |
fn test1(s: &mut Source) -> ParserResult<String> { | |
let x1 = any_char(s)?; | |
let x2 = any_char(s)?; | |
let vec = [x1, x2]; | |
Ok(String::from_iter(vec.iter())) | |
} | |
fn test2(s: &mut Source) -> ParserResult<String> { | |
let mut x1 = test1(s)?; | |
x1.push(any_char(s)?); | |
Ok(x1) | |
} | |
satisfy!(digit, char::is_digit, 10); | |
satisfy!(alpha, char::is_alphabetic); | |
or!(digit_or_alpha, char, digit, alpha); | |
fn parse_test<T>(func: Parser<T>, s: &str) -> ParserResult<T> { | |
let mut source = Source::from_str(s); | |
func(&mut source) | |
} | |
///////////////////////////////////////////////////////////////////// | |
fn main() { | |
println!("{:?}", parse_test(any_char, "abc")); | |
println!("{:?}", parse_test(test1, "abc")); | |
println!("{:?}", parse_test(test2, "abc")); | |
println!("{:?}", parse_test(digit, "123")); | |
println!("{:?}", parse_test(digit_or_alpha, "1")); | |
println!("{:?}", parse_test(digit_or_alpha, "a")); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment