use regex;
use std::str;
use msg;
use dbg;
macro_rules! static_regex {
($id:ident = $val:expr) => {
lazy_static! {
static ref $id: regex::Regex = regex::Regex::new($val).unwrap();
}
}
}
pub fn parse_line(mut line: &str) -> dbg::Result<msg::Message> {
let mut token = None;
if let Some((tok, rest)) = parse_token(line) {
token = Some(tok);
line = rest;
}
if !line.starts_with("^") {
return Err(dbg::Error::IgnoredOutput);
}
line = line.split_at(1).1;
let class = if let Some((class, rest)) = parse_message_class(line) {
line = rest;
class
} else {
return Err(dbg::Error::ParseError);
};
let mut result = Vec::new();
if line.starts_with("\n") {
return Ok(msg::Message {
token: token,
class: class,
content: result
});
} else if !line.starts_with(",") {
return Err(dbg::Error::ParseError);
}
line = line.split_at(1).1;
if let Some((variable, rest)) = parse_variable(line) {
line = rest;
result.push(variable);
} else {
return Err(dbg::Error::ParseError);
}
while !line.starts_with("\n") {
if !line.starts_with(",") {
return Err(dbg::Error::ParseError);
}
line = line.split_at(1).1;
if let Some((variable, rest)) = parse_variable(line) {
line = rest;
result.push(variable);
} else {
return Err(dbg::Error::ParseError);
}
}
Ok(msg::Message {
token: token,
class: class,
content: result
})
}
fn parse<T: str::FromStr>(data: &str, toklen: usize) -> (T, &str) {
let (x, y) = data.split_at(toklen);
(T::from_str(x).ok().unwrap(), y)
}
fn parse_token(data: &str) -> Option<(String, &str)> {
static_regex!(RE = r"^[0-9]+");
if let Some((_, count)) = RE.find(data) {
Some(parse(data, count))
} else {
None
}
}
fn parse_message_class(data: &str) -> Option<(msg::MessageClass, &str)> {
static_regex!(RE = r"^(done|connected|running|error|exit)");
if let Some((_, count)) = RE.find(data) {
Some(parse(data, count))
} else {
None
}
}
fn parse_varname(data: &str) -> Option<(msg::VarName, &str)> {
static_regex!(RE = r"^[a-zA-Z_][a-zA-Z0-9_]*");
if let Some((_, count)) = RE.find(data) {
Some(parse(data, count))
} else {
None
}
}
fn parse_constant(data: &str) -> Option<(msg::Value, &str)> {
static_regex!(RE = r#"^(".*?[^\\]"|"")"#);
if let Some((_, count)) = RE.find(data) {
let (value, rest) = parse(data, count);
Some((msg::Value::String(value), rest))
} else {
None
}
}
fn parse_variable_list(data: &str) -> Option<(msg::Value, &str)> {
let mut end = "}";
if data.starts_with("[") {
end = "]";
}
if !data.starts_with("{") {
return None;
}
let mut data = data.split_at(1).1;
let mut result = Vec::new();
if data.starts_with(end) {
return Some((msg::Value::VariableList(result), data.split_at(1).1));
}
if let Some((variable, rest)) = parse_variable(data) {
data = rest;
result.push(variable);
} else {
return None;
}
while !data.starts_with(end) {
if !data.starts_with(",") {
return None;
}
data = data.split_at(1).1;
if let Some((variable, rest)) = parse_variable(data) {
data = rest;
result.push(variable);
} else {
return None;
}
}
Some((msg::Value::VariableList(result), data.split_at(1).1))
}
fn parse_value_list(data: &str) -> Option<(msg::Value, &str)> {
if !data.starts_with("[") {
return None;
}
let mut data = data.split_at(1).1;
let mut result = Vec::new();
if data.starts_with("]") {
return Some((msg::Value::ValueList(result), data.split_at(1).1));
}
if let Some((value, rest)) = parse_value(data) {
data = rest;
result.push(value);
} else {
return None;
}
while !data.starts_with("]") {
if !data.starts_with(",") {
return None;
}
data = data.split_at(1).1;
if let Some((value, rest)) = parse_value(data) {
data = rest;
result.push(value);
} else {
return None;
}
}
Some((msg::Value::ValueList(result), data.split_at(1).1))
}
fn parse_value(data: &str) -> Option<(msg::Value, &str)> {
parse_constant(data).or(parse_variable_list(data)).or(parse_value_list(data))
}
fn parse_variable(data: &str) -> Option<(msg::Variable, &str)> {
if let Some((var, rest)) = parse_varname(data) {
match rest.chars().nth(0) {
Some('=') => if let Some((val, rest)) =
parse_value(rest.split_at(1).1) {
Some((msg::Variable { name: var, value: val }, rest))
} else {
None
}
,
_ => None
}
} else {
None
}
}