112 lines
3.0 KiB
Rust
112 lines
3.0 KiB
Rust
use std::io::BufRead;
|
|
use std::iter::Iterator;
|
|
use std::collections::{HashMap, VecDeque};
|
|
|
|
use lazy_static::lazy_static;
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
pub enum Token {
|
|
Text,
|
|
Comment,
|
|
DHCP,
|
|
Newline,
|
|
Address,
|
|
Auto,
|
|
Gateway,
|
|
Inet,
|
|
Inet6,
|
|
Iface,
|
|
Loopback,
|
|
Manual,
|
|
Netmask,
|
|
Static,
|
|
Attribute,
|
|
MTU,
|
|
EOF,
|
|
}
|
|
|
|
lazy_static! {
|
|
static ref KEYWORDS: HashMap<&'static str, Token> = {
|
|
let mut map = HashMap::new();
|
|
map.insert("address", Token::Address);
|
|
map.insert("auto", Token::Auto);
|
|
map.insert("dhcp", Token::DHCP);
|
|
map.insert("gateway", Token::Gateway);
|
|
map.insert("inet", Token::Inet);
|
|
map.insert("inet6", Token::Inet6);
|
|
map.insert("iface", Token::Iface);
|
|
map.insert("loopback", Token::Loopback);
|
|
map.insert("manual", Token::Manual);
|
|
map.insert("netmask", Token::Netmask);
|
|
map.insert("static", Token::Static);
|
|
map.insert("mtu", Token::MTU);
|
|
map
|
|
};
|
|
}
|
|
|
|
pub struct Lexer<R> {
|
|
input: R,
|
|
eof_count: usize,
|
|
cur_line: Option<VecDeque<(Token, String)>>,
|
|
}
|
|
|
|
impl <R: BufRead> Lexer<R> {
|
|
|
|
pub fn new(input: R) -> Self {
|
|
Self { input, eof_count: 0, cur_line: None }
|
|
}
|
|
|
|
fn split_line(line: &str) -> VecDeque<(Token, String)> {
|
|
if line.starts_with("#") {
|
|
let mut res = VecDeque::new();
|
|
res.push_back((Token::Comment, line[1..].trim().to_string()));
|
|
return res;
|
|
}
|
|
let mut list: VecDeque<(Token, String)> = line.split_ascii_whitespace().map(|text| {
|
|
let token = KEYWORDS.get(text).unwrap_or(&Token::Text);
|
|
(*token, text.to_string())
|
|
}).collect();
|
|
|
|
if line.starts_with(|c: char| c.is_ascii_whitespace() && c != '\n') {
|
|
list.push_front((Token::Attribute, String::from("\t")));
|
|
}
|
|
list
|
|
}
|
|
}
|
|
|
|
impl <R: BufRead> Iterator for Lexer<R> {
|
|
|
|
type Item = Result<(Token, String), std::io::Error>;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.cur_line.is_none() {
|
|
let mut line = String::new();
|
|
match self.input.read_line(&mut line) {
|
|
Err(err) => return Some(Err(err)),
|
|
Ok(0) => {
|
|
self.eof_count += 1;
|
|
if self.eof_count == 1 { return Some(Ok((Token::EOF, String::new()))); }
|
|
return None;
|
|
}
|
|
_ => {}
|
|
}
|
|
self.cur_line = Some(Self::split_line(&line));
|
|
}
|
|
|
|
match self.cur_line {
|
|
Some(ref mut cur_line) => {
|
|
if cur_line.is_empty() {
|
|
self.cur_line = None;
|
|
return Some(Ok((Token::Newline, String::from("\n"))));
|
|
} else {
|
|
let (token, text) = cur_line.pop_front().unwrap();
|
|
return Some(Ok((token, text)));
|
|
}
|
|
}
|
|
None => {
|
|
return None;
|
|
}
|
|
}
|
|
}
|
|
}
|