Skip to content

Instantly share code, notes, and snippets.

@tera3939
Created January 25, 2017 08:55
Show Gist options
  • Save tera3939/5a4c66b628d201c9b954785fe98b5781 to your computer and use it in GitHub Desktop.
Save tera3939/5a4c66b628d201c9b954785fe98b5781 to your computer and use it in GitHub Desktop.
Java 再帰下降構文解析 超入門 - http://qiita.com/7shi/items/64261a67081d49f941e3 をRustでやってみた
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