Created
January 25, 2017 08:55
-
-
Save tera3939/5a4c66b628d201c9b954785fe98b5781 to your computer and use it in GitHub Desktop.
Java 再帰下降構文解析 超入門 - http://qiita.com/7shi/items/64261a67081d49f941e3 をRustでやってみた
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::cell::Cell; | |
struct Source<'a> { | |
string: &'a str, | |
pos: Cell<usize>, | |
} | |
impl<'a> Source<'a> { | |
fn new(string: &str) -> Source{ | |
Source { | |
string: string, | |
pos: Cell::new(0), | |
} | |
} | |
fn peek(&self) -> Option<char> { | |
let pos = self.pos.get(); | |
if pos < self.string.len() { | |
self.string.chars().nth(pos) | |
} else { | |
None | |
} | |
} | |
fn next(&self) { | |
self.pos.set(self.pos.get() + 1); | |
} | |
} | |
trait Parser { | |
fn number(&self) -> isize; | |
fn expr(&self) -> isize; | |
fn term(&self) -> isize; | |
fn factor(&self) -> isize; | |
fn spases(&self); | |
} | |
impl<'a> Parser for Source<'a> { | |
fn number(&self) -> isize { | |
let mut num = String::new(); | |
while let Some(ch) = self.peek() { | |
if ch.is_digit(10) { | |
num.push(ch); | |
self.next(); | |
} else { | |
break; | |
} | |
} | |
isize::from_str_radix(&num, 10).unwrap() | |
} | |
// expr = term, {("+", term) | ("-", term)} | |
fn expr(&self) -> isize { | |
let mut x = self.term(); | |
while let Some(p) = self.peek() { | |
match p { | |
'+' => { | |
self.next(); | |
x += self.term(); | |
}, | |
'-' => { | |
self.next(); | |
x -= self.term(); | |
}, | |
_ => { break; }, | |
} | |
} | |
x | |
} | |
// term = number, {("*", number) | ("/", number)} | |
fn term(&self) -> isize { | |
let mut x = self.factor(); | |
while let Some(p) = self.peek() { | |
match p { | |
'*' => { | |
self.next(); | |
x *= self.factor(); | |
}, | |
'/' => { | |
self.next(); | |
x /= self.factor(); | |
}, | |
_ => { break; }, | |
} | |
} | |
x | |
} | |
// factor = factor = [spaces], ("(", expr, ")") | number, [spaces] | |
fn factor(&self) -> isize { | |
self.spases(); | |
let ret = if let Some(p) = self.peek() { | |
if p == '(' { | |
self.next(); | |
let result = self.expr(); | |
if self.peek().unwrap() == ')' { | |
self.next(); | |
} | |
result | |
} else { | |
self.number() | |
} | |
} else { | |
self.number() | |
}; | |
self.spases(); | |
ret | |
} | |
fn spases(&self) { | |
while let Some(p) = self.peek() { | |
if p == ' ' { | |
self.next(); | |
} else { | |
break; | |
} | |
} | |
} | |
} | |
macro_rules! expr_test { | |
( $( ($e:expr, $r: expr) ),* ) => { | |
$({ | |
let a = Source::new($e); | |
assert_eq!($r, a.expr()); | |
})* | |
} | |
} | |
#[test] | |
fn expr_test() { | |
expr_test!( | |
("1 + 2", 3 ), | |
("123", 123), | |
("1 + 2 + 3", 6 ), | |
("1 - 2 - 3", -4 ), | |
("1 - 2 + 3", 2 ), | |
("2 * 3 + 4", 10 ), | |
("2 + 3 * 4", 14 ), | |
("100 / 10 / 2", 5 ), | |
("( 2 + 3 ) * 4", 20 ) | |
); | |
} | |
fn main() { | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment