use crate::tree::*;
use crate::tree::AsmNode::*;
pub fn parse_source(text: &str, meta_path: &str) -> AsmNode {
let mut nodes: Vec<AsmNode> = vec![];
for (parsed_line, meta_line, meta_raw) in parse_text(text) {
let node: AsmNode = Source{code: parsed_line, meta: Metadata{raw: meta_raw, source_file: meta_path.to_string(), line: meta_line}};
nodes.push(node);
}
Inode(nodes)
}
fn parse_line(line: &str) -> Vec<String> {
let mut line_as_char: Vec<char> = vec![];
for c in line.chars() {
if c == ';' {
break;
}
line_as_char.push(c);
}
line_as_char.push('\n'); let mut ret: Vec<String> = vec![];
let mut in_whitespace = true;
let mut in_quote = false;
let mut last_escaped = false;
let mut new_quoted_string = Vec::<char>::new();
let mut word_start = 0;
for i in 0..line_as_char.len() {
if in_quote {
if last_escaped {
new_quoted_string.push(match line_as_char[i] {
'n' => '\n',
'r' => '\r',
't' => '\t',
x => x,
});
last_escaped = false;
} else if line_as_char[i] == '"' {
in_quote = false;
let quoted_string: String = new_quoted_string.iter().collect();
ret.push(quoted_string);
} else if line_as_char[i] == '\\' {
last_escaped = true;
} else {
new_quoted_string.push(line_as_char[i]);
}
} else if in_whitespace {
if line_as_char[i] == '"' {
in_quote = true;
new_quoted_string = Vec::<char>::new();
} else if !line_as_char[i].is_whitespace() {
in_whitespace = false;
word_start = i;
}
} else if line_as_char[i].is_whitespace() {
in_whitespace = true;
let word_to_add = &line_as_char[word_start..i];
ret.push(word_to_add.iter().cloned().collect::<String>());
}
}
ret
}
fn parse_text(text: &str) -> Vec<(Vec<String>, usize, String)> {
let mut ret: Vec<(Vec<String>, usize, String)> = vec![];
let mut line_index = 1;
for line in text.split("\n") {
let parsed = parse_line(line);
if !parsed.is_empty() {
ret.push((parsed, line_index, line.to_string()));
}
line_index += 1;
}
ret
}
#[test]
fn test_parse_line() {
fn test_eq(line: &str, split: Vec<&str>) {
let converted = parse_line(line);
assert_eq!(converted, split);
}
test_eq("abc def", vec!["abc", "def"]);
test_eq(" abc def ", vec!["abc", "def"]);
test_eq(" abc def;bla bla ", vec!["abc", "def"]);
test_eq(" abc def ; bla bla ", vec!["abc", "def"]);
test_eq("abcd \"\\r\\t\" efg", vec!["abcd", "\r\t", "efg"]);
}
#[test]
fn test_parse_text() {
fn test_eq(text: &str, split: Vec<Vec<&str>>) {
let converted = parse_text(text);
let mut converted_vec: Vec<Vec<String>> = vec![];
for (elem, _meta1, _meta2) in converted {
converted_vec.push(elem);
}
assert_eq!(converted_vec, split);
}
test_eq("abc def\na b;lol\n", vec![vec!["abc", "def"], vec!["a", "b"]]);
test_eq("abc def ; lol\na b;lol", vec![vec!["abc", "def"], vec!["a", "b"]]);
test_eq("abc def ; lol\n\n\na b\r\n", vec![vec!["abc", "def"], vec!["a", "b"]]);
test_eq("abc def\na b", vec![vec!["abc", "def"], vec!["a", "b"]]);
}
#[test]
fn test_parse_source() {
assert_eq!(parse_source("a b\nc d", "path"), Inode(vec![
Source{code: vec!["a".to_string(), "b".to_string()], meta: Metadata{raw: "a b".to_string(), source_file: "path".to_string(), line: 1}},
Source{code: vec!["c".to_string(), "d".to_string()], meta: Metadata{raw: "c d".to_string(), source_file: "path".to_string(), line: 2}}]));
}
#[test]
fn test_parse_line_string() {
fn test_eq(line: &str, split: Vec<&str>) {
let converted = parse_line(line);
assert_eq!(converted, split);
}
test_eq("abc \"def hij\"", vec!["abc", "def hij"]);
test_eq("abc \"def \\n hij\"", vec!["abc", "def \n hij"]);
test_eq("abc \"def \\\" hij\"", vec!["abc", "def \" hij"]);
}