Rail开发日志_2
上一篇笔记概括了分词方法。
确定一个简单的目标
先实现Comment
类型和Category
类型,因为最接近行和领域的概念本质。
struct Comment {content: String,
}
struct Category {name: String,
}
以上两者只不过是中间结果。
其中,Line_data::Category
携带的String
应当为Element
,但遵循从少到多的思路,先写成String
。(其实是太菜了不会写)
Category
用于创建Domain
。
以下是从分词结果到语法分析结果的桥梁。
enum LineData {Comment(Comment),Category(Category),
}
struct Line {indent: usize,data: LineData,
}
其中Line
用于代表行。
语法分析
输入分词结果和缩进
fn into_lines(line_list: Vec<Vec<String>>, indent_list: Vec<usize>) -> Vec<Line> {let mut local: Vec<Line> = Vec::new();for (i, tokens) in line_list.iter().enumerate() {let indent = indent_list[i];let data = match tokens[0].as_str() {"/" => {LineData::Comment(Comment { content: tokens[1].clone() })}"+" => {LineData::Category(Category { name: tokens[1].clone() })}_ => {continue}};local.push(Line { indent, data });}local
}
检查函数如下
fn check_lines(lines: &Vec<Line>) {for line in lines.iter() {let indent_str = " ".repeat(line.indent);match &line.data {LineData::Comment(comment) => {println!("{}Comment: {}", indent_str, comment.content);}LineData::Category(category) => {println!("{}Category: {}", indent_str, category.name);}}}
}
测试
完整的测试代码如下。
fn main() {let test_input = String::from(r"
/ test_line_for_a_comment
+ test_category/ test_line_for_a_comment/ test_line_for_a_comment+ test_category/ test_line_for_a_comment/ test_line_for_a_comment/ test_line_for_a_comment/ test_line_for_a_comment/ test_line_for_a_comment+ test_category/ test_line_for_a_comment/ test_line_for_a_comment+ test_category
+ test_category/ test_line_for_a_comment
+ test_category_with_an_empty_category+ test_an_empty_category
/ test_line_for_a_comment
/ test_line_for_a_comment
/ test_line_for_a_comment
/ test_line_for_a_comment
");let (line_list, indent_list) = into_tokens(&test_input);check_tokens(&line_list, &indent_list);let lines: Vec<Line> = into_lines(line_list, indent_list);check_lines(&lines)
}fn into_tokens(lines_input: &str) -> (Vec<Vec<String>>, Vec<usize>) {let mut word_current = String::new();let mut line_current = Vec::new();let mut lines_list_splited = Vec::new();let mut indent_current = 0;let mut indent_list = Vec::new();let mut indent_state_in = true;for char in lines_input.chars() {if char == '\n' {if !word_current.is_empty() {line_current.push(word_current.clone());word_current.clear();}if !line_current.is_empty() {lines_list_splited.push(line_current.clone());indent_list.push(indent_current);}line_current.clear();indent_current = 0;indent_state_in = true;} else if char == ' ' {if indent_state_in {indent_current += 1;} else if !word_current.is_empty() {line_current.push(word_current.clone());word_current.clear();}} else {indent_state_in = false;word_current.push(char);}}if !word_current.is_empty() {line_current.push(word_current);}if !line_current.is_empty() {lines_list_splited.push(line_current);indent_list.push(indent_current);}(lines_list_splited, indent_list)
}
fn check_tokens(token_list: &Vec<Vec<String>>, indent_list: &Vec<usize>) {for i in 0..token_list.len() {println!("[{}: {}] {:?}", i + 1, indent_list[i], token_list[i]);}
}struct Comment {content: String,
}
struct Category {name: String,
}
fn into_lines(line_list: Vec<Vec<String>>, indent_list: Vec<usize>) -> Vec<Line> {let mut local: Vec<Line> = Vec::new();for (i, tokens) in line_list.iter().enumerate() {let indent = indent_list[i];let data = match tokens[0].as_str() {"/" => {LineData::Comment(Comment { content: tokens[1].clone() })}"+" => {LineData::Category(Category { name: tokens[1].clone() })}_ => {continue}};local.push(Line { indent, data });}local
}
fn check_lines(lines: &Vec<Line>) {for line in lines.iter() {let indent_str = " ".repeat(line.indent);match &line.data {LineData::Comment(comment) => {println!("{}Comment: {}", indent_str, comment.content);}LineData::Category(category) => {println!("{}Category: {}", indent_str, category.name);}}}
}enum LineData {Comment(Comment),Category(Category),
}
struct Line {indent: usize,data: LineData,
}
测试结果如下。
Comment: test_line_for_a_comment
Category: test_categoryComment: test_line_for_a_commentComment: test_line_for_a_commentCategory: test_categoryComment: test_line_for_a_commentComment: test_line_for_a_commentComment: test_line_for_a_commentComment: test_line_for_a_commentComment: test_line_for_a_commentCategory: test_categoryComment: test_line_for_a_commentComment: test_line_for_a_commentCategory: test_category
Category: test_categoryComment: test_line_for_a_comment
Category: test_category_with_an_empty_categoryCategory: test_an_empty_category
Comment: test_line_for_a_comment
Comment: test_line_for_a_comment
Comment: test_line_for_a_comment
Comment: test_line_for_a_comment