Skip to content

Commit d949a43

Browse files
committed
Initial commit
0 parents  commit d949a43

File tree

2 files changed

+260
-0
lines changed

2 files changed

+260
-0
lines changed

src/lib.rs

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use nfa::NFA;
2+
use nfa::CharacterClass;
3+
use std::hashmap::HashMap;
4+
mod nfa;
5+
6+
pub enum Handler {
7+
StringHandler(~str)
8+
}
9+
10+
struct Router {
11+
nfa: NFA,
12+
handlers: HashMap<uint, Handler>
13+
}
14+
15+
impl Router {
16+
pub fn new() -> Router {
17+
Router{ nfa: NFA::new(), handlers: HashMap::new() }
18+
}
19+
20+
pub fn add(&mut self, mut route: &str, dest: Handler) {
21+
if route.char_at(0) == '/' {
22+
route = route.slice_from(1);
23+
}
24+
25+
let nfa = &mut self.nfa;
26+
let mut state = 0;
27+
28+
for char in route.chars() {
29+
state = nfa.put(state, CharacterClass::valid_char(char));
30+
}
31+
32+
nfa.acceptance(state);
33+
self.handlers.insert(state, dest);
34+
}
35+
36+
pub fn recognize<'a>(&'a self, mut path: &str) -> Result<&'a Handler, ~str> {
37+
if path.char_at(0) == '/' {
38+
path = path.slice_from(1);
39+
}
40+
41+
let states = self.nfa.process(path);
42+
43+
match states {
44+
Err(str) => Err(str),
45+
Ok(states) => Ok(self.handlers.get(&states[0]))
46+
}
47+
}
48+
}
49+
50+
#[test]
51+
fn basic_router() {
52+
let mut router = Router::new();
53+
54+
router.add("/thomas", StringHandler(~"Thomas"));
55+
router.add("/tom", StringHandler(~"Tom"));
56+
router.add("/wycats", StringHandler(~"Yehuda"));
57+
58+
match *router.recognize("/thomas").unwrap() {
59+
StringHandler(ref str) => assert!(str == &~"Thomas", "/thomas matched")
60+
}
61+
}

src/nfa.rs

+199
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
use std::hashmap::HashSet;
2+
3+
#[deriving(Eq)]
4+
pub enum CharacterClass {
5+
ValidChars(HashSet<~char>),
6+
InvalidChars(HashSet<~char>)
7+
}
8+
9+
impl CharacterClass {
10+
pub fn valid(string: &str) -> CharacterClass {
11+
ValidChars(CharacterClass::str_to_set(string))
12+
}
13+
14+
pub fn valid_char(char: char) -> CharacterClass {
15+
ValidChars(CharacterClass::char_to_set(char))
16+
}
17+
18+
pub fn invalid(string: &str) -> CharacterClass {
19+
InvalidChars(CharacterClass::str_to_set(string))
20+
}
21+
22+
pub fn invalid_char(char: char) -> CharacterClass {
23+
InvalidChars(CharacterClass::char_to_set(char))
24+
}
25+
26+
fn char_to_set(char: char) -> HashSet<~char> {
27+
let mut set = HashSet::new();
28+
set.insert(~char);
29+
set
30+
}
31+
32+
fn str_to_set(string: &str) -> HashSet<~char> {
33+
let mut set = HashSet::new();
34+
for char in string.chars() {
35+
set.insert(~char);
36+
}
37+
set
38+
}
39+
}
40+
41+
struct State {
42+
index: uint,
43+
chars: CharacterClass,
44+
next_states: ~[uint],
45+
acceptance: bool
46+
}
47+
48+
impl State {
49+
pub fn new(index: uint, chars: CharacterClass) -> State {
50+
State{ index: index, chars: chars, next_states: ~[], acceptance: false }
51+
}
52+
}
53+
54+
pub struct NFA {
55+
states: ~[State]
56+
}
57+
58+
impl NFA {
59+
pub fn new() -> NFA {
60+
let root = State::new(0, CharacterClass::valid(""));
61+
NFA{ states: ~[root] }
62+
}
63+
64+
pub fn process<'a>(&'a self, string: &str) -> Result<~[uint], ~str> {
65+
let mut current = ~[self.get(0)];
66+
67+
for char in string.chars() {
68+
let next_states = self.process_char(current, &char);
69+
70+
if next_states.is_empty() {
71+
return Err("Couldn't process " + string);
72+
}
73+
74+
current = next_states;
75+
}
76+
77+
let returned = current.iter().filter_map(|&state| {
78+
if state.acceptance { Some(state.index) } else { None }
79+
}).to_owned_vec();
80+
81+
if returned.is_empty() {
82+
Err(~"The string was exhausted before reaching an acceptance state")
83+
} else {
84+
Ok(returned)
85+
}
86+
}
87+
88+
fn process_char<'a>(&'a self, states: ~[&State], char: &char) -> ~[&'a State] {
89+
let mut returned = ~[];
90+
91+
for state in states.iter() {
92+
for index in state.next_states.iter() {
93+
let state = self.get(*index);
94+
match state.chars {
95+
ValidChars(ref valid) => if valid.contains(&~*char) { returned.push(state); },
96+
InvalidChars(ref invalid) => if !invalid.contains(&~*char) { returned.push(state); }
97+
}
98+
}
99+
}
100+
101+
returned
102+
}
103+
104+
pub fn get<'a>(&'a self, state: uint) -> &'a State {
105+
&self.states[state]
106+
}
107+
108+
pub fn get_mut<'a>(&'a mut self, state: uint) -> &'a mut State {
109+
&mut self.states[state]
110+
}
111+
112+
pub fn put(&mut self, index: uint, chars: CharacterClass) -> uint {
113+
{
114+
let state = self.get(index);
115+
116+
for index in state.next_states.iter() {
117+
let state = self.get(*index);
118+
if state.chars == chars {
119+
return *index;
120+
}
121+
}
122+
}
123+
124+
let state = self.new_state(chars);
125+
self.get_mut(index).next_states.push(state);
126+
state
127+
}
128+
129+
pub fn acceptance(&mut self, index: uint) {
130+
self.get_mut(index).acceptance = true;
131+
}
132+
133+
fn new_state(&mut self, chars: CharacterClass) -> uint {
134+
let index = self.states.len();
135+
let state = State::new(index, chars);
136+
self.states.push(state);
137+
index
138+
}
139+
}
140+
141+
#[test]
142+
fn basic_test() {
143+
let mut nfa = NFA::new();
144+
let a = nfa.put(0, CharacterClass::valid("h"));
145+
let b = nfa.put(a, CharacterClass::valid("e"));
146+
let c = nfa.put(b, CharacterClass::valid("l"));
147+
let d = nfa.put(c, CharacterClass::valid("l"));
148+
let e = nfa.put(d, CharacterClass::valid("o"));
149+
nfa.acceptance(e);
150+
151+
let states = nfa.process("hello");
152+
153+
assert!(states.unwrap() == ~[e], "You didn't get the right final state");
154+
}
155+
156+
#[test]
157+
fn multiple_solutions() {
158+
let mut nfa = NFA::new();
159+
let a1 = nfa.put(0, CharacterClass::valid("n"));
160+
let b1 = nfa.put(a1, CharacterClass::valid("e"));
161+
let c1 = nfa.put(b1, CharacterClass::valid("w"));
162+
nfa.acceptance(c1);
163+
164+
let a2 = nfa.put(0, CharacterClass::invalid(""));
165+
let b2 = nfa.put(a2, CharacterClass::invalid(""));
166+
let c2 = nfa.put(b2, CharacterClass::invalid(""));
167+
nfa.acceptance(c2);
168+
169+
let states = nfa.process("new");
170+
171+
assert!(states.unwrap() == ~[c1, c2], "The two states were not found");
172+
}
173+
174+
#[test]
175+
fn multiple_paths() {
176+
let mut nfa = NFA::new();
177+
let a = nfa.put(0, CharacterClass::valid("t")); // t
178+
let b1 = nfa.put(a, CharacterClass::valid("h")); // th
179+
let c1 = nfa.put(b1, CharacterClass::valid("o")); // tho
180+
let d1 = nfa.put(c1, CharacterClass::valid("m")); // thom
181+
let e1 = nfa.put(d1, CharacterClass::valid("a")); // thoma
182+
let f1 = nfa.put(e1, CharacterClass::valid("s")); // thomas
183+
184+
let b2 = nfa.put(a, CharacterClass::valid("o")); // to
185+
let c2 = nfa.put(b2, CharacterClass::valid("m")); // tom
186+
187+
nfa.acceptance(f1);
188+
nfa.acceptance(c2);
189+
190+
let thomas = nfa.process("thomas");
191+
let tom = nfa.process("tom");
192+
let thom = nfa.process("thom");
193+
let nope = nfa.process("nope");
194+
195+
assert!(thomas.unwrap() == ~[f1], "thomas was parsed correctly");
196+
assert!(tom.unwrap() == ~[c2], "tom was parsed correctly");
197+
assert!(thom.is_err(), "thom didn't reach an acceptance state");
198+
assert!(nope.is_err(), "nope wasn't parsed");
199+
}

0 commit comments

Comments
 (0)