1use crate::lexer::{TokenType, Token, push_token, tokenize};
2
3fn add_table(tokens: &mut Vec<Token>, table: &mut Token) {
4 if !table.subtokens.is_empty() {
5 tokens.push(table.clone());
6 *table = Token::init(TokenType::Table, String::new());
7 }
8}
9
10fn table_parse(input: &String) -> Token {
11 enum CellMode {
12 None,
13 Column,
14 Row,
15 Attr,
16 }
17 let mut nullify = false;
18 let mut out: Vec<Token> = Vec::new();
19 let mut starting_cell = true;
20 let mut current_cell = Token::init(TokenType::TableCell, String::new());
21 let mut cell_mode = CellMode::None;
22 let mut current_cell_col = String::new();
23 let mut current_cell_row = String::new();
24 let mut rowattr = String::new();
25 for ch in input.chars() {
26 if starting_cell {
27 match ch {
29 '*' => {
30 match cell_mode {
31 CellMode::Attr => current_cell.attributes += &ch.to_string(),
32 _ => current_cell.class = TokenType::TableHeader,
33 }
34 },
35 'r' => {
36 match cell_mode {
37 CellMode::Attr => current_cell.attributes += &ch.to_string(),
38 _ => cell_mode = CellMode::Row,
39 }
40 },
41 'c' => {
42 match cell_mode {
43 CellMode::Attr => current_cell.attributes += &ch.to_string(),
44 _ => cell_mode = CellMode::Column,
45 }
46 },
47 '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
48 match cell_mode {
49 CellMode::Column => current_cell_col += &ch.to_string(),
50 CellMode::Row => current_cell_row += &ch.to_string(),
51 CellMode::Attr => current_cell.attributes += &ch.to_string(),
52 _ => panic!("Found a digit in an unexpected position in cell token"),
53 }
54 },
55 '{' => {
56 cell_mode = CellMode::Attr;
57 current_cell.attributes += &ch.to_string();
58 }
59 '}'=> {
60 match cell_mode {
61 CellMode::Attr => {
62 cell_mode = CellMode::None;
63 },
64 _ => panic!("{}", "Found a } outside an attribute sequence"),
65 }
66 },
67 '|' => {
68 nullify = false;
69 starting_cell = true;
70 current_cell = Token::init(TokenType::TableCell, String::new());
71 cell_mode = CellMode::None;
72 current_cell_row = String::new();
73 current_cell_col = String::new();
74 },
75 '-' => nullify = true,
76 ' ' => {
77 match cell_mode {
78 CellMode::Attr => current_cell.attributes += &ch.to_string(),
79 _ => {
80 starting_cell = false;
81 let mut close_atter = !current_cell.attributes.is_empty();
82 if !current_cell_col.is_empty() || !current_cell_row.is_empty() {
83 if !close_atter {
84 current_cell.attributes += "{";
85 close_atter = true;
86 }
87 if !current_cell_row.is_empty() {
88 current_cell.attributes += &(" rowspan=\"".to_owned() + ¤t_cell_row + "\"");
89 }
90 if !current_cell_col.is_empty() {
91 current_cell.attributes += &(" colspan=\"".to_owned() + ¤t_cell_col + "\"");
92 }
93 }
94 if close_atter { current_cell.attributes += "}" }
95 }
96 }
97 },
98 _ => {
99 match cell_mode {
100 CellMode::Attr => current_cell.attributes += &ch.to_string(),
101 _ => panic!("Unexpected character in table cell initiation"),
102 }
103 },
104 }
105 } else {
106 match ch {
108 '|' => {
109 if !nullify {
110 current_cell.subtokens = tokenize(¤t_cell.content.trim_end_matches('\t')).0;
111 out.push(current_cell.clone());
112 }
113 nullify = false;
114 starting_cell = true;
115 current_cell = Token::init(TokenType::TableCell, String::new());
116 cell_mode = CellMode::None;
117 current_cell_row = String::new();
118 current_cell_col = String::new();
119 },
120 _ => current_cell.content += &ch.to_string(),
121 }
122 }
123 }
124 if !current_cell.attributes.is_empty() && current_cell.content.is_empty() {
125 current_cell.attributes += "}";
126 rowattr = current_cell.attributes;
127 }
128 let mut outok = Token::init_sub(TokenType::TableRow, out, String::new());
129 outok.attributes = rowattr;
130 outok
131}
132
133pub(crate) fn block_lexer(lines: &Vec<Vec<Token>>) -> Vec<Token>{
134 let mut blocks: Vec<Token> = Vec::new();
135 let mut current_block: Token;
136 let mut lists: Vec<Token> = Vec::new();
137 let mut table: Token = Token::init(TokenType::Table, String::new());
138 let mut next_attr: String = String::new();
139 for line in lines.iter() {
140 match line.first() {
141 None => add_table(&mut blocks, &mut table),
142 Some(ftoken) => {
143 match ftoken.class {
144 TokenType::TableRow => {
145 if table.subtokens.is_empty() {
146 table.attributes = next_attr.clone();
147 next_attr = String::new();
148 }
149 table.subtokens.push(table_parse(&ftoken.content));
150 },
151 TokenType::ListEl => {
152 add_table(&mut blocks, &mut table);
153 let fltoken = {
154 let mut ft = ftoken.clone();
155 ft.subtokens = line[1..].to_vec();
156 ft
157 };
158 match lists.last_mut() {
159 None => {
160 let mut new_sublist = Token::init_sub(TokenType::UList, vec![fltoken.to_owned()], fltoken.content.to_owned());
161 new_sublist.attributes = next_attr.clone();
162 lists.push(new_sublist);
163 next_attr = String::new();
164 },
165 Some(x) => {
166 match x.class {
167 TokenType::UList => {
168 if get_list_depth(&x) == get_list_depth(&fltoken) {
169 x.subtokens.push(fltoken.to_owned());
170 } else if get_list_depth(&x) < get_list_depth(&fltoken) {
171 let mut new_sublist = Token::init_sub(TokenType::UList, vec![fltoken.to_owned()], fltoken.content.to_owned());
172 new_sublist.attributes = next_attr.clone();
173 lists.push(new_sublist);
174 next_attr = String::new();
175 }
176 else {
177 let new_sublist = Token::init_sub(TokenType::UList, vec![fltoken.to_owned()], fltoken.content.to_owned());
178 lists.push(new_sublist);
179 }
180 },
181 TokenType::OList => {
182 lists.push(Token::init_sub(TokenType::UList, vec![fltoken.to_owned()], fltoken.content.to_owned()));
183 },
184 _ => panic!("This wasn't supposed to be possible at all"),
185 }
186 },
187 }
188 },
189 TokenType::NumberedListEl => {
190 add_table(&mut blocks, &mut table);
191 let fltoken = {
192 let mut ft = ftoken.clone();
193 ft.subtokens = line[1..].to_vec();
194 ft
195 };
196 match lists.last_mut() {
197 None => {
198 let mut new_sublist = Token::init_sub(TokenType::OList, vec![fltoken.to_owned()], fltoken.content.to_owned());
199 new_sublist.attributes = next_attr.clone();
200 next_attr = String::new();
201 lists.push(new_sublist);
202 },
203 Some(x) => {
204 match x.class {
205 TokenType::OList => {
206 if get_list_depth(&x) == get_list_depth(&fltoken) {
207 x.subtokens.push(fltoken.to_owned());
208 } else if get_list_depth(&x) < get_list_depth(&fltoken) {
209 let mut new_sublist = Token::init_sub(TokenType::OList, vec![fltoken.to_owned()], fltoken.content.to_owned());
210 new_sublist.attributes = next_attr.clone();
211 lists.push(new_sublist);
212 next_attr = String::new();
213 }
214 else {
215 let new_sublist = Token::init_sub(TokenType::OList, vec![fltoken.to_owned()], fltoken.content.to_owned());
216 lists.push(new_sublist);
217 }
218 },
219 TokenType::UList => {
220 lists.push(Token::init_sub(TokenType::OList, vec![fltoken.to_owned()], fltoken.content.to_owned()));
221 },
222 _ => panic!("This wasn't supposed to be possible at all"),
223 }
224 },
225 }
226 },
227 TokenType::Html => {
228 add_table(&mut blocks, &mut table);
229 if !lists.is_empty() {
230 push_token(&mut blocks, &Token::init_sub(TokenType::ListBlock, lists.clone(), String::new()));
231 }
232 current_block = ftoken.clone();
233 current_block.subtokens = line[1..].to_vec();
234 push_token(&mut blocks, ¤t_block);
235 }
236 TokenType::Attr => {
237 if !lists.is_empty() {
238 push_token(&mut blocks, &Token::init_sub(TokenType::ListBlock, lists.clone(), String::new()));
239 lists = Vec::new();
240 }
241 if line.len() > 1 {
242 current_block = Token::n_para();
243 current_block.attributes = line[0].content.to_owned();
244 current_block.subtokens = line[1..].to_vec();
245 push_token(&mut blocks, ¤t_block);
246 } else {
247 next_attr = ftoken.content.to_owned();
248 }
249 },
250 TokenType::Header => {
251 add_table(&mut blocks, &mut table);
252 if !lists.is_empty() {
253 push_token(&mut blocks, &Token::init_sub(TokenType::ListBlock, lists.clone(), String::new()));
254 lists = Vec::new();
255 }
256 current_block = ftoken.clone();
257 current_block.subtokens = line[1..].to_vec();
258 push_token(&mut blocks, ¤t_block);
259 },
260 TokenType::Image => {
261 add_table(&mut blocks, &mut table);
262 if !lists.is_empty() {
263 push_token(&mut blocks, &Token::init_sub(TokenType::ListBlock, lists.clone(), String::new()));
264 lists = Vec::new();
265 }
266 if line.len() > 1 {
267 current_block = Token::n_para();
268 current_block.subtokens = line.clone();
269 push_token(&mut blocks, ¤t_block);
270 } else {
271 current_block = ftoken.clone();
272 push_token(&mut blocks, ¤t_block);
273 }
274 }
275 _ => {
276 add_table(&mut blocks, &mut table);
277 if !lists.is_empty() {
278 push_token(&mut blocks, &Token::init_sub(TokenType::ListBlock, lists.clone(), String::new()));
279 lists = Vec::new();
280 }
281 current_block = Token::n_para();
282 current_block.subtokens = line.clone();
283 match blocks.last_mut() {
284 None => push_token(&mut blocks, ¤t_block),
285 Some(x) => {
286 let first_char = current_block.subtokens.first_mut().expect("An empty string got to the paragraph parser.");
287 if &first_char.content[0..1] == " " {
288 x.subtokens.push(Token::init(TokenType::LineBreak, "\n".to_owned()));
289 first_char.content = "\n".to_owned() + &first_char.content[1..];
290 x.subtokens.append(&mut current_block.subtokens);
291 } else {
292 push_token(&mut blocks, ¤t_block);
293 }
294 },
295 }
296 }
297 }
298 }
299 }
300 }
301 add_table(&mut blocks, &mut table);
302 if !lists.is_empty() {
303 push_token(&mut blocks, &Token::init_sub(TokenType::ListBlock, lists.clone(), String::new()));
304 }
305 blocks
306}
307
308pub(crate) fn get_list_depth(token: &Token) -> usize {
309 match token.class {
310 TokenType::UList | TokenType::ListEl | TokenType::OList | TokenType::NumberedListEl => token.content.len() - 1,
311 _ => panic!("Passed a non-list token to get_list_depth"),
312 }
313}