Created
February 4, 2017 14:23
-
-
Save tera3939/d5128350bc18091f20966a00d73d3416 to your computer and use it in GitHub Desktop.
パーサーコンビネータになりたかったもの
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::ascii::AsciiExt; | |
use std::cell::{Cell, RefCell}; | |
use std::error::Error; | |
use std::fmt; | |
#[derive(Debug)] | |
enum ParseError { | |
Parse, | |
} | |
impl fmt::Display for ParseError { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
match *self { | |
ParseError::Parse => {write!(f, "Could not parse source in Source struct.")}, | |
} | |
} | |
} | |
impl Error for ParseError { | |
fn description(&self) -> &str { | |
match *self { | |
ParseError::Parse => "Could not parse this string.", | |
} | |
} | |
} | |
type ParseResult<T> = Result<T, ParseError>; | |
#[derive(Debug)] | |
struct Source { | |
source: String, | |
part: RefCell<Option<String>>, | |
pos: Cell<usize>, | |
} | |
impl Source { | |
fn new<P: Into<String>> (s: P) -> Source { | |
Source { | |
source: s.into(), | |
part: RefCell::new(Some(String::with_capacity(1024))), | |
pos: Cell::new(0), | |
} | |
} | |
fn peek(&self) -> Option<char> { | |
self.source.chars().nth(self.pos.get()) | |
} | |
fn next(&self) { | |
self.pos.set(self.pos.get() + 1); | |
} | |
} | |
trait Parser { | |
fn any_char(&self) -> &Source; | |
fn satisfy<F>(&self, f: F) -> &Source where F: FnOnce(char) -> bool; | |
fn replicate<F>(&self, n: usize, f: F) -> &Source where F: Fn(&Source) -> &Source; | |
fn many<F>(&self, f: F) -> &Source where F: Fn(&Source) -> &Source; | |
fn digit(&self) -> &Source; | |
fn upper(&self) -> &Source; | |
fn lower(&self) -> &Source; | |
fn alpha(&self) -> &Source; | |
fn alpha_num(&self) -> &Source; | |
fn letter(&self) -> &Source; | |
fn done(&self) -> ParseResult<String>; | |
} | |
impl Parser for Source { | |
fn satisfy<F>(&self, f: F) -> &Source | |
where F: FnOnce(char) -> bool{ | |
/* | |
* 現在位置に文字があるか確認 | |
* もしあるならその文字を関数fに渡す | |
* 真が帰ってきたときSource.partに追加 | |
* 偽が帰ってきたときは何もしない // ここがよくない | |
* 文字がないならば何もしない | |
* その後Source.posを一つ進めてselfを返す | |
*/ | |
match self.peek() { | |
Some(ch) => { | |
if f(ch) { | |
self.part | |
.borrow_mut() | |
.as_mut() | |
.map(|x| x.push(ch)); | |
} else { | |
*self.part.borrow_mut() = None; | |
return self; | |
} | |
}, | |
_ => { | |
*self.part.borrow_mut() = None; | |
return self; | |
}, | |
}; | |
self.next(); | |
self | |
} | |
fn any_char(&self) -> &Source { self.satisfy(|x| true) } | |
fn digit(&self) -> &Source { self.satisfy(|x| x.is_digit(10)) } | |
fn upper(&self) -> &Source { self.satisfy(|x| x.is_uppercase()) } | |
fn lower(&self) -> &Source { self.satisfy(|x| x.is_lowercase()) } | |
fn alpha(&self) -> &Source { self.satisfy(|x| x.is_ascii()) } | |
fn alpha_num(&self) -> &Source { self.satisfy(|x| x.is_digit(10) || x.is_ascii()) } | |
fn letter(&self) -> &Source { self.satisfy(|x| x.is_alphabetic()) } | |
fn done(&self)-> ParseResult<String> { | |
self.part | |
.borrow() | |
.as_ref() | |
.ok_or(ParseError::Parse) | |
.map(|p|{ | |
let mut s = String::with_capacity(p.len()); | |
s.push_str(p); | |
s | |
}) | |
} | |
fn replicate<F>(&self, n: usize, f: F) -> &Source | |
where F: Fn(&Source) -> &Source { | |
for i in 0..n { | |
f(self); | |
} | |
self | |
} | |
fn many<F>(&self, f: F) -> &Source where F: Fn(&Source) -> &Source { | |
} | |
} | |
macro_rules! char_to_string { | |
($ch_vec:expr) => {{ | |
let mut s = String::new(); | |
for ch in $ch_vec.iter() { | |
s.push(*ch); | |
} | |
s | |
}} | |
} | |
fn test(s: &str) -> ParseResult<String> { | |
let s = Source::new(s); | |
s.many(|x| x.alpha()).done() | |
} | |
fn main() { | |
let a = test("a12"); | |
let b = test("ab1"); | |
let c = test("abc"); | |
println!("{:?}", a); | |
println!("{:?}", b); | |
println!("{:?}", c); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment